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
>718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892