summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--ranger/container/keymap.py62
-rw-r--r--ranger/defaults/keys.py16
-rw-r--r--ranger/ext/tree.py1
-rw-r--r--test/tc_newkeys.py54
4 files changed, 72 insertions, 61 deletions
diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py
index 930800ff..29d6e629 100644
--- a/ranger/container/keymap.py
+++ b/ranger/container/keymap.py
@@ -14,6 +14,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import curses.ascii
+from collections import deque
 from string import ascii_lowercase
 from inspect import isfunction, getargspec
 from ranger.ext.tree import Tree
@@ -155,27 +156,28 @@ class KeyBuffer(object):
 		self.direction_keys = direction_keys
 
 	def add(self, key):
-		if self.failure:
-			return None
 		assert isinstance(key, int)
 		assert key >= 0
 		self.all_keys.append(key)
+		self.key_queue.append(key)
+		while self.key_queue:
+			key = self.key_queue.popleft()
 
-		# evaluate quantifiers
-		if self.eval_quantifier and self._do_eval_quantifier(key):
-			return
+			# evaluate quantifiers
+			if self.eval_quantifier and self._do_eval_quantifier(key):
+				return
 
-		# evaluate the command
-		if self.eval_command and self._do_eval_command(key):
-			return
+			# evaluate the command
+			if self.eval_command and self._do_eval_command(key):
+				return
 
-		# evaluate (the first number of) the direction-quantifier
-		if self.eval_quantifier and self._do_eval_quantifier(key):
-			return
+			# evaluate (the first number of) the direction-quantifier
+			if self.eval_quantifier and self._do_eval_quantifier(key):
+				return
 
-		# evaluate direction keys {j,k,gg,pagedown,...}
-		if not self.eval_command:
-			self._do_eval_direction(key)
+			# evaluate direction keys {j,k,gg,pagedown,...}
+			if not self.eval_command:
+				self._do_eval_direction(key)
 
 	def _do_eval_direction(self, key):
 		try:
@@ -186,8 +188,8 @@ class KeyBuffer(object):
 		else:
 			self._direction_try_to_finish()
 
-	def _direction_try_to_finish(self, rec=MAX_ALIAS_RECURSION):
-		if rec <= 0:
+	def _direction_try_to_finish(self):
+		if self.max_alias_recursion <= 0:
 			self.failure = True
 			return None
 		match = self.dir_tree_pointer
@@ -197,12 +199,9 @@ class KeyBuffer(object):
 			match = self.dir_tree_pointer
 		if isinstance(self.dir_tree_pointer, Binding):
 			if match.alias:
-				try:
-					self.dir_tree_pointer = self.direction_keys[match.alias]
-					self._direction_try_to_finish(rec - 1)
-				except KeyError:
-					self.failure = True
-					return None
+				self.key_queue.extend(translate_keys(match.alias))
+				self.dir_tree_pointer = self.direction_keys._tree
+				self.max_alias_recursion -= 1
 			else:
 				direction = match.actions['dir'].copy()
 				if self.direction_quant is not None:
@@ -232,11 +231,11 @@ class KeyBuffer(object):
 		try:
 			self.tree_pointer = self.tree_pointer[key]
 		except TypeError:
-			print(self.tree_pointer)
 			self.failure = True
 			return None
 		except KeyError:
 			try:
+				is_ascii_digit(key) or self.direction_keys._tree[key]
 				self.tree_pointer = self.tree_pointer[DIRKEY]
 			except KeyError:
 				try:
@@ -261,8 +260,8 @@ class KeyBuffer(object):
 					self.command = None
 			self._try_to_finish()
 
-	def _try_to_finish(self, rec=MAX_ALIAS_RECURSION):
-		if rec <= 0:
+	def _try_to_finish(self):
+		if self.max_alias_recursion <= 0:
 			self.failure = True
 			return None
 		assert isinstance(self.tree_pointer, (Binding, dict, KeyMap))
@@ -270,17 +269,15 @@ class KeyBuffer(object):
 			self.tree_pointer = self.tree_pointer._tree
 		if isinstance(self.tree_pointer, Binding):
 			if self.tree_pointer.alias:
-				try:
-					self.tree_pointer = self.keymap[self.tree_pointer.alias]
-					self._try_to_finish(rec - 1)
-				except KeyError:
-					self.failure = True
-					return None
+				self.key_queue.extend(translate_keys(self.tree_pointer.alias))
+				self.tree_pointer = self.keymap._tree
+				self.max_alias_recursion -= 1
 			else:
 				self.command = self.tree_pointer
 				self.done = True
 
 	def clear(self):
+		self.max_alias_recursion = MAX_ALIAS_RECURSION
 		self.failure = False
 		self.done = False
 		self.quant = None
@@ -292,6 +289,8 @@ class KeyBuffer(object):
 		self.tree_pointer = self.keymap._tree
 		self.dir_tree_pointer = self.direction_keys._tree
 
+		self.key_queue = deque()
+
 		self.eval_quantifier = True
 		self.eval_command = True
 
@@ -318,6 +317,7 @@ special_keys = {
 	'cr': ord("\n"),
 	'enter': ord("\n"),
 	'space': ord(" "),
+	'esc': curses.ascii.ESC,
 	'down': curses.KEY_DOWN,
 	'up': curses.KEY_UP,
 	'left': curses.KEY_LEFT,
diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py
index 70521e1e..57a0d415 100644
--- a/ranger/defaults/keys.py
+++ b/ranger/defaults/keys.py
@@ -265,8 +265,6 @@ map('w', 'q', ESC, ctrl('d'), ctrl('c'),
 map = keymanager.get_context('console')
 map.merge(global_keys)
 map.merge(readline_aliases)
-map.unmap('Q')  # don't quit with Q in console, so we can type it
-map.unmap('<dir>')  # define my own direction keys
 
 map('<up>', wdg.history_move(-1))
 map('<down>', wdg.history_move(1))
@@ -274,7 +272,7 @@ map('<home>', wdg.move(right=0, absolute=True))
 map('<end>', wdg.move(right=-1, absolute=True))
 map('<tab>', wdg.tab())
 map('<s-tab>', wdg.tab(-1))
-map('<c-c>', wdg.close())
+map('<c-c>', '<esc>', wdg.close())
 map('<CR>', '<c-j>', wdg.execute())
 map('<F1>', lambda arg: arg.fm.display_command_help(arg.wdg))
 
@@ -285,14 +283,24 @@ map('<C-K>', wdg.delete_rest(1))
 map('<C-U>', wdg.delete_rest(-1))
 map('<C-Y>', wdg.paste())
 
-map('<any>')
+# Any key which is still undefined will simply be typed in.
+@map('<any>')
 def type_key(arg):
 	arg.wdg.type_key(arg.match)
 
+# Override some global keys so we can type them:
+override = ('Q', '%')
+for key in override:
+	map(key, wdg.type_key(key))
+
 
 # ===================================================================
 # == Define direction keys
 # ===================================================================
+# Note that direction keys point to no functions, but Direction objects.
+# Direction keys are completely independent and can not be merged into
+# other keymaps.  You can't define or unmap direction keys on
+# a per-context-basis, instead use aliases.
 map = keymanager.get_context('directions')
 map('<down>', dir=Direction(down=1))
 map('<up>', dir=Direction(down=-1))
diff --git a/ranger/ext/tree.py b/ranger/ext/tree.py
index 6d841c2a..a954136b 100644
--- a/ranger/ext/tree.py
+++ b/ranger/ext/tree.py
@@ -81,6 +81,7 @@ class Tree(object):
 			if first or isinstance(subtree, Tree) and subtree.empty():
 				top = chars.pop()
 				subtree = self.traverse(chars)
+				assert top in subtree._tree, "no such key: " + chr(top)
 				del subtree._tree[top]
 			else:
 				break
diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py
index fc17aeda..9a7f10c7 100644
--- a/test/tc_newkeys.py
+++ b/test/tc_newkeys.py
@@ -83,31 +83,6 @@ class Test(PressTestCase):
 		self.assert_(match.function)
 		self.assertEqual(8, match.function(args))
 
-	def test_map_collision(self):
-		def add_dirs(arg):
-			return sum(dir.down() for dir in arg.directions)
-		def return5(_):
-			return 5
-
-
-		directions = KeyMap()
-		directions.map('gg', dir=Direction(down=1))
-
-
-		km = KeyMap()
-		km.map('gh', return5)
-		km.map('agh', return5)
-		km.map('a<dir>', add_dirs)
-
-		kb = KeyBuffer(km, directions)
-		press = self._mkpress(kb, km)
-
-		self.assertEqual(5, press('gh'))
-		self.assertEqual(5, press('agh'))
-#		self.assertPressFails(kb, 'agh')
-		self.assertEqual(1, press('agg'))
-
-
 	def test_translate_keys(self):
 		def test(string, *args):
 			if not args:
@@ -187,7 +162,7 @@ class Test(PressTestCase):
 		self.assertEqual(press('c<CR>'), press('c@'))
 		self.assertEqual(press('c<CR>'), press('c@'))
 
-		for n in range(1, 50):
+		for n in range(1, 10):
 			self.assertPressIncomplete(kb, 'y' * n)
 
 		for n in range(1, 5):
@@ -540,11 +515,13 @@ class Test(PressTestCase):
 		press = self._mkpress(kb)
 
 		km.map('<dir>', func)
+		km.map('d<dir>', func)
 		directions.map('j', dir=Direction(down=42))
 		self.assertEqual(42, press('j'))
 
 		km.map('o', alias='j')
 		self.assertEqual(42, press('o'))
+		self.assertEqual(42, press('do'))
 
 	def test_both_directory_and_any_key(self):
 		def func(arg):
@@ -571,4 +548,29 @@ class Test(PressTestCase):
 		km.map('abc<any>', func2)
 		self.assertEqual("yay", press('abcd'))
 
+	def test_map_collision(self):
+		def add_dirs(arg):
+			return sum(dir.down() for dir in arg.directions)
+		def return5(_):
+			return 5
+
+
+		directions = KeyMap()
+		directions.map('gg', dir=Direction(down=1))
+
+
+		km = KeyMap()
+		km.map('gh', return5)
+		km.map('agh', return5)
+		km.map('a<dir>', add_dirs)
+
+		kb = KeyBuffer(km, directions)
+		press = self._mkpress(kb, km)
+
+		self.assertEqual(5, press('gh'))
+		self.assertEqual(5, press('agh'))
+#		self.assertPressFails(kb, 'agh')
+		self.assertEqual(1, press('agg'))
+
+
 if __name__ == '__main__': main()