summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--ranger/actions.py15
-rw-r--r--ranger/defaults/keys.py60
-rw-r--r--ranger/ext/accumulator.py15
-rw-r--r--ranger/gui/widgets/pager.py15
-rw-r--r--ranger/keyapi.py94
5 files changed, 116 insertions, 83 deletions
diff --git a/ranger/actions.py b/ranger/actions.py
index bc6da7b8..8209c9ab 100644
--- a/ranger/actions.py
+++ b/ranger/actions.py
@@ -137,10 +137,10 @@ class Actions(EnvironmentAware, SettingsAware):
 		"""Delete the bookmark with the name <key>"""
 		self.bookmarks.delete(key)
 
-	def move_left(self, n=1):
+	def move_left(self, narg=1):
 		"""Enter the parent directory"""
 		try:
-			directory = os.path.join(*(['..'] * n))
+			directory = os.path.join(*(['..'] * narg))
 		except:
 			return
 		self.env.enter_dir(directory)
@@ -237,20 +237,25 @@ class Actions(EnvironmentAware, SettingsAware):
 		if hasattr(self.ui, 'open_console'):
 			self.ui.open_console(mode, string)
 
-	def move_pointer(self, relative = 0, absolute = None):
+	def move_pointer(self, relative = 0, absolute = None, narg=None):
 		"""Move the pointer down by <relative> or to <absolute>"""
-		self.env.pwd.move(relative, absolute)
+		self.env.pwd.move(relative=relative,
+				absolute=absolute, narg=narg)
 
 	def move_pointer_by_pages(self, relative):
 		"""Move the pointer down by <relative> pages"""
 		self.env.pwd.move(relative=int(relative * self.env.termsize[0]))
 
-	def move_pointer_by_percentage(self, relative=0, absolute=None):
+	def move_pointer_by_percentage(self, relative=0, absolute=None, narg=None):
 		"""Move the pointer down by <relative>% or to <absolute>%"""
 		try:
 			factor = len(self.env.pwd) / 100.0
 		except:
 			return
+
+		if narg is not None:
+			absolute = narg
+
 		self.env.pwd.move(
 				relative=int(relative * factor),
 				absolute=int(absolute * factor))
diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py
index 5f7691e2..02438cbc 100644
--- a/ranger/defaults/keys.py
+++ b/ranger/defaults/keys.py
@@ -26,6 +26,7 @@ def system_functions(command_list):
 
 	bind(KEY_RESIZE, fm.resize())
 	bind(KEY_MOUSE, fm.handle_mouse())
+	bind('Q', fm.exit())
 	bind(ctrl('L'), fm.redraw_window())
 
 def initialize_commands(command_list):
@@ -33,7 +34,15 @@ def initialize_commands(command_list):
 
 	bind, hint = make_abbreviations(command_list)
 
+	bind('j', KEY_DOWN, fm.move_pointer(relative=1))
+	bind('k', KEY_UP, fm.move_pointer(relative=-1))
 	bind('l', KEY_RIGHT, fm.move_right())
+	bind('h', KEY_LEFT, KEY_BACKSPACE, DEL, fm.move_left(1))
+
+	bind('gg', fm.move_pointer(absolute=0))
+	bind('G', fm.move_pointer(absolute=-1))
+	bind('%', fm.move_pointer_by_percentage(absolute=50))
+
 	bind(KEY_END, fm.move_pointer(absolute=-1))
 	bind(KEY_HOME, fm.move_pointer(absolute=0))
 	bind(KEY_ENTER, ctrl('j'), fm.move_right(mode=1))
@@ -132,7 +141,7 @@ def initialize_commands(command_list):
 
 	# system functions
 	system_functions(command_list)
-	bind('Q', 'ZZ', fm.exit())
+	bind('ZZ', fm.exit())
 	bind(ctrl('R'), fm.reset())
 	bind(ctrl('C'), fm.interrupt())
 	bind(':', ';', fm.open_console(cmode.COMMAND))
@@ -143,29 +152,6 @@ def initialize_commands(command_list):
 	bind('r', fm.open_console(cmode.OPEN_QUICK))
 
 	# definitions which require their own function:
-	def ggG(default):
-		# moves to an absolute point, or to a predefined default
-		# if no number is specified.
-		return lambda arg: \
-				arg.fm.move_pointer(absolute=(arg.n or default)-1)
-
-	bind('gg', ggG(1))
-	bind('G', ggG(0))
-
-	bind('%', lambda arg: \
-			arg.fm.move_pointer_by_percentage(absolute=arg.n or 50))
-
-	def jk(direction):
-		# moves up or down by the specified number or one, in
-		# the predefined direction
-		return lambda arg: \
-				arg.fm.move_pointer(relative=(arg.n or 1) * direction)
-
-	bind('j', KEY_DOWN, jk(1))
-	bind('k', KEY_UP, jk(-1))
-
-	bind('h', KEY_LEFT, KEY_BACKSPACE, DEL, lambda arg: \
-			arg.fm.move_left(arg.n or 1))
 
 	bind('w', lambda arg: arg.fm.ui.open_taskview())
 
@@ -214,15 +200,15 @@ def initialize_taskview_commands(command_list):
 	system_functions(command_list)
 	bind, hint = make_abbreviations(command_list)
 
-	bind('j', KEY_DOWN, nwrap.move(relative=1))
-	bind('k', KEY_UP, nwrap.move(relative=-1))
-	bind('gg', nwrap.move(absolute=0))
-	bind('G', nwrap.move(absolute=-1))
+	bind('j', KEY_DOWN, wdg.move(relative=1))
+	bind('k', KEY_UP, wdg.move(relative=-1))
+	bind('gg', wdg.move(absolute=0))
+	bind('G', wdg.move(absolute=-1))
 	bind('K', wdg.task_move(0))
 	bind('J', wdg.task_move(-1))
 
 	bind('dd', wdg.task_remove())
-	bind('w', ESC, ctrl('d'), ctrl('c'),
+	bind('w', 'q', ESC, ctrl('d'), ctrl('c'),
 			lambda arg: arg.fm.ui.close_taskview())
 
 	command_list.rebuild_paths()
@@ -238,14 +224,14 @@ def initialize_embedded_pager_commands(command_list):
 	system_functions(command_list)
 	bind, hint = make_abbreviations(command_list)
 
-	bind('j', KEY_DOWN, nwrap.move(relative=1))
-	bind('k', KEY_UP, nwrap.move(relative=-1))
-	bind('gg', KEY_HOME, nwrap.move(absolute=0))
-	bind('G', KEY_END, nwrap.move(absolute=-1))
-	bind('J', ctrl('d'), nwrap.move(relative=0.5, pages=True))
-	bind('K', ctrl('u'), nwrap.move(relative=-0.5, pages=True))
-	bind(KEY_NPAGE, ctrl('f'), nwrap.move(relative=1, pages=True))
-	bind(KEY_PPAGE, ctrl('b'), nwrap.move(relative=-1, pages=True))
+	bind('j', KEY_DOWN, wdg.move(relative=1))
+	bind('k', KEY_UP, wdg.move(relative=-1))
+	bind('gg', KEY_HOME, wdg.move(absolute=0))
+	bind('G', KEY_END, wdg.move(absolute=-1))
+	bind('J', ctrl('d'), wdg.move(relative=0.5, pages=True))
+	bind('K', ctrl('u'), wdg.move(relative=-0.5, pages=True))
+	bind(KEY_NPAGE, ctrl('f'), wdg.move(relative=1, pages=True))
+	bind(KEY_PPAGE, ctrl('b'), wdg.move(relative=-1, pages=True))
 	bind('E', fm.edit_file())
 
 	bind('h', wdg.move_horizontal(relative=-4))
diff --git a/ranger/ext/accumulator.py b/ranger/ext/accumulator.py
index 6513bee2..5aee54de 100644
--- a/ranger/ext/accumulator.py
+++ b/ranger/ext/accumulator.py
@@ -3,7 +3,7 @@ class Accumulator(object):
 		self.pointer = 0
 		self.pointed_obj = None
 
-	def move(self, relative=0, absolute=None, pages=False):
+	def move(self, relative=0, absolute=None, pages=None, narg=None):
 		i = self.pointer
 		lst = self.get_list()
 		if not lst:
@@ -11,16 +11,19 @@ class Accumulator(object):
 		length = len(lst)
 
 		if isinstance(absolute, int):
+			if isinstance(narg, int):
+				absolute = narg
 			if absolute < 0: # wrap
 				i = absolute + length
 			else:
 				i = absolute
 
-		if pages:
-			i += relative * self.get_height()
-		else:
-			i += relative
-		i = int(i)
+		if relative != 0:
+			if isinstance(pages, int):
+				relative *= pages * self.get_height()
+			if isinstance(narg, int):
+				relative *= narg
+		i = int(i + relative)
 
 		if i >= length:
 			i = length - 1
diff --git a/ranger/gui/widgets/pager.py b/ranger/gui/widgets/pager.py
index 0e13ecbd..5e9ef3c8 100644
--- a/ranger/gui/widgets/pager.py
+++ b/ranger/gui/widgets/pager.py
@@ -60,19 +60,22 @@ class Pager(Widget):
 					pass
 			self.need_redraw = False
 	
-	def move(self, relative=0, absolute=None, pages=False):
+	def move(self, relative=0, absolute=None, pages=None, narg=None):
 		i = self.scroll_begin
 		if isinstance(absolute, int):
+			if isinstance(narg, int):
+				absolute = narg
 			if absolute < 0:
 				i = absolute + len(self.lines)
 			else:
 				i = absolute
 
-		if pages:
-			i += relative * self.hei
-		else:
-			i += relative
-		i = int(i)
+		if relative != 0:
+			if isinstance(pages, int):
+				relative *= pages * self.hei
+			if isinstance(narg, int):
+				relative *= narg
+		i = int(i + relative)
 
 		length = len(self.lines) - self.hei
 		if i >= length:
diff --git a/ranger/keyapi.py b/ranger/keyapi.py
index 82f1495d..e5d6f367 100644
--- a/ranger/keyapi.py
+++ b/ranger/keyapi.py
@@ -1,5 +1,7 @@
 from curses import *
 from curses.ascii import *
+from inspect import getargspec, ismethod
+
 from ranger import RANGERDIR
 from ranger.gui.widgets import console_mode as cmode
 from ranger.container.bookmarks import ALLOWED_KEYS as ALLOWED_BOOKMARK_KEYS
@@ -18,18 +20,18 @@ class Wrapper(object):
 		self.__firstattr__ = firstattr
 
 	def __getattr__(self, attr):
-		def wrapper(*args, **keywords):
-			def bla(command_argument):
+		def wrapper(*real_args, **real_keywords):
+			def function(command_argument):
+				args, kws = real_args, real_keywords
+				number = command_argument.n
 				obj = getattr(command_argument, self.__firstattr__)
-				if obj is None:
-					return
-				return getattr(obj, attr)(*args, **keywords)
-			return bla
+				fnc = getattr(obj, attr)
+				if number is not None:
+					args, kws = replace_narg(number, fnc, args, kws)
+				return fnc(*args, **kws)
+			return function
 		return wrapper
 
-fm = Wrapper('fm')
-wdg = Wrapper('wdg')
-
 # fm.enter_dir('~') is translated into lambda arg: arg.fm.enter_dir('~')
 # this makes things like this possible:
 # bind('gh', fm.enter_dir('~'))
@@ -39,25 +41,59 @@ wdg = Wrapper('wdg')
 #
 # for something like that, use the long version:
 # bind('H', lambda arg: arg.fm.history.go(-1))
+#
+# If the method has an argument named "narg", pressing a number before
+# the key will pass that number as the narg argument. If you want the
+# same behaviour in a custom lambda function, you can write:
+# bind('gg', fm.move_pointer(absolute=0))
+# as:
+# bind('gg', lambda arg: narg(arg.n, arg.fm.move_pointer, absolute=0))
+
+fm = Wrapper('fm')
+wdg = Wrapper('wdg')
+
+
+NARG_KEYWORD = 'narg'
+
+def narg(number_, function_, *args_, **keywords_):
+	"""
+	This applies the replace_narg function to the arguments and keywords
+	and directly runs this function.
+
+	Example:
+	def foo(xyz, narg): return hash((xyz, narg))
+
+	narg(50, foo, 123) == foo(123, narg=50)
+	"""
+	args, keywords = replace_narg(number_, function_, args_, keywords_)
+	print(args, keywords)
+	return function_(*args, **keywords)
+
+def replace_narg(number, function, args, keywords):
+	"""
+	This function returns (args, keywords) with one little change:
+	if <function> has a named argument called "narg", args and keywords
+	will be modified so that the value of "narg" will be <number>.
+
+	def foo(xyz, narg): pass
+
+	replace_narg(666, foo, (), {'narg': 10, 'xyz': 5})
+	=> (), {'narg': 666, 'xyz': 5}
 
+	replace_narg(666, foo, (1, 2), {})
+	=> (1, 666), {}
+	"""
+	argspec = getargspec(function).args
+	if NARG_KEYWORD in argspec:
+		try:
+			# is narg in args?
+			args = list(args)
+			index = argspec.index(NARG_KEYWORD)
+			if ismethod(function):
+				index -= 1  # because of 'self'
+			args[index] = number
+		except (ValueError, IndexError):
+			# is narg in keywords?
+			keywords[NARG_KEYWORD] = number
+	return args, keywords
 
-# Another wrapper for common actions which use a numerical argument:
-class nwrap(object):
-	@staticmethod
-	def move(relative=0, absolute=None, pages=False):
-		if absolute is None:
-			def fnc(arg):
-				if arg.n is not None:
-					if relative >= 0:
-						arg.wdg.move(relative=arg.n, pages=pages)
-					else:
-						arg.wdg.move(relative=-arg.n, pages=pages)
-				else:
-					arg.wdg.move(relative=relative, pages=pages)
-		else:
-			def fnc(arg):
-				if arg.n is not None:
-					arg.wdg.move(absolute=arg.n, relative=relative, pages=pages)
-				else:
-					arg.wdg.move(absolute=absolute, relative=relative, pages=pages)
-		return fnc