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()
href='#n217'>217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380