summary refs log tree commit diff stats
path: root/ranger
diff options
context:
space:
mode:
authorhut <hut@lavabit.com>2010-04-16 17:40:55 +0200
committerhut <hut@lavabit.com>2010-04-16 17:40:55 +0200
commitfacb13de27e6aa2ade05e69b8d7d7222b8715a68 (patch)
tree93df3394814341107f44c7a5f13fb20d326898a1 /ranger
parentf8e3b704d2f468336e8efc93f50c268b992a1ed1 (diff)
parent175290dbbba5e7bc5d416bc0ba87b687238976c4 (diff)
downloadranger-facb13de27e6aa2ade05e69b8d7d7222b8715a68.tar.gz
Merge branch 'devel' of ssh://repo.or.cz/srv/git/ranger into devel
Diffstat (limited to 'ranger')
-rw-r--r--ranger/container/__init__.py3
-rw-r--r--ranger/container/keybuffer.py176
-rw-r--r--ranger/container/keymap.py289
-rw-r--r--ranger/defaults/keys.py1
-rw-r--r--ranger/ext/keybinding_parser.py96
5 files changed, 290 insertions, 275 deletions
diff --git a/ranger/container/__init__.py b/ranger/container/__init__.py
index c1bb8194..3351cc63 100644
--- a/ranger/container/__init__.py
+++ b/ranger/container/__init__.py
@@ -17,5 +17,6 @@
 used to manage stored data
 """
 from ranger.container.history import History
-from .keymap import KeyMap, KeyBuffer, KeyManager
+from .keymap import KeyMap, KeyManager
+from .keybuffer import KeyBuffer
 from .bookmarks import Bookmarks
diff --git a/ranger/container/keybuffer.py b/ranger/container/keybuffer.py
new file mode 100644
index 00000000..50914f84
--- /dev/null
+++ b/ranger/container/keybuffer.py
@@ -0,0 +1,176 @@
+# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import curses.ascii
+from collections import deque
+from string import digits
+from ranger.ext.keybinding_parser import parse_keybinding, \
+		DIRKEY, ANYKEY, PASSIVE_ACTION
+from ranger.container.keymap import Binding, KeyMap
+
+MAX_ALIAS_RECURSION = 20
+
+class KeyBuffer(object):
+	"""The evaluator and storage for pressed keys"""
+	def __init__(self, keymap, direction_keys):
+		self.assign(keymap, direction_keys)
+
+	def assign(self, keymap, direction_keys):
+		self.keymap = keymap
+		self.direction_keys = direction_keys
+
+	def add(self, key):
+		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 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 direction keys {j,k,gg,pagedown,...}
+			if not self.eval_command:
+				self._do_eval_direction(key)
+
+	def _do_eval_direction(self, key):
+		try:
+			assert isinstance(self.dir_tree_pointer, dict)
+			self.dir_tree_pointer = self.dir_tree_pointer[key]
+		except KeyError:
+			self.failure = True
+		else:
+			self._direction_try_to_finish()
+
+	def _direction_try_to_finish(self):
+		if self.max_alias_recursion <= 0:
+			self.failure = True
+			return None
+		match = self.dir_tree_pointer
+		assert isinstance(match, (Binding, dict, KeyMap))
+		if isinstance(match, KeyMap):
+			self.dir_tree_pointer = self.dir_tree_pointer._tree
+			match = self.dir_tree_pointer
+		if isinstance(self.dir_tree_pointer, Binding):
+			if match.alias:
+				self.key_queue.extend(parse_keybinding(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:
+					direction.multiply(self.direction_quant)
+				self.directions.append(direction)
+				self.direction_quant = None
+				self.eval_command = True
+				self._try_to_finish()
+
+	def _do_eval_quantifier(self, key):
+		if self.eval_command:
+			tree = self.tree_pointer
+		else:
+			tree = self.dir_tree_pointer
+		if chr(key) in digits and ANYKEY not in tree:
+			attr = self.eval_command and 'quant' or 'direction_quant'
+			if getattr(self, attr) is None:
+				setattr(self, attr, 0)
+			setattr(self, attr, getattr(self, attr) * 10 + key - 48)
+		else:
+			self.eval_quantifier = False
+			return None
+		return True
+
+	def _do_eval_command(self, key):
+		assert isinstance(self.tree_pointer, dict), self.tree_pointer
+		try:
+			self.tree_pointer = self.tree_pointer[key]
+		except TypeError:
+			self.failure = True
+			return None
+		except KeyError:
+			try:
+				chr(key) in digits or self.direction_keys._tree[key]
+				self.tree_pointer = self.tree_pointer[DIRKEY]
+			except KeyError:
+				try:
+					self.tree_pointer = self.tree_pointer[ANYKEY]
+				except KeyError:
+					self.failure = True
+					return None
+				else:
+					self.matches.append(key)
+					assert isinstance(self.tree_pointer, (Binding, dict))
+					self._try_to_finish()
+			else:
+				assert isinstance(self.tree_pointer, (Binding, dict))
+				self.eval_command = False
+				self.eval_quantifier = True
+				self.dir_tree_pointer = self.direction_keys._tree
+		else:
+			if isinstance(self.tree_pointer, dict):
+				try:
+					self.command = self.tree_pointer[PASSIVE_ACTION]
+				except (KeyError, TypeError):
+					self.command = None
+			self._try_to_finish()
+
+	def _try_to_finish(self):
+		if self.max_alias_recursion <= 0:
+			self.failure = True
+			return None
+		assert isinstance(self.tree_pointer, (Binding, dict, KeyMap))
+		if isinstance(self.tree_pointer, KeyMap):
+			self.tree_pointer = self.tree_pointer._tree
+		if isinstance(self.tree_pointer, Binding):
+			if self.tree_pointer.alias:
+				keys = parse_keybinding(self.tree_pointer.alias)
+				self.key_queue.extend(keys)
+				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
+		self.matches = []
+		self.command = None
+		self.direction_quant = None
+		self.directions = []
+		self.all_keys = []
+		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
+
+	def __str__(self):
+		"""returns a concatenation of all characters"""
+		return "".join("{0:c}".format(c) for c in self.all_keys)
diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py
index f09d9cfb..167ba160 100644
--- a/ranger/container/keymap.py
+++ b/ranger/container/keymap.py
@@ -13,45 +13,27 @@
 # You should have received a copy of the GNU General Public License
 # 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
 from ranger.ext.direction import Direction
+from ranger.ext.keybinding_parser import parse_keybinding, DIRKEY, ANYKEY
 
-MAX_ALIAS_RECURSION = 20
-PASSIVE_ACTION = 9003
-DIRKEY = 9001
-ANYKEY = 9002
 FUNC = 'func'
-DIRECTION = 'direction'
 DIRARG = 'dir'
 ALIASARG = 'alias'
 
-def to_string(i):
-	"""convert a ord'd integer to a string"""
-	try:
-		return chr(i)
-	except ValueError:
-		return '?'
-
-def is_ascii_digit(n):
-	return n >= 48 and n <= 57
-
 class CommandArgs(object):
 	"""The arguments which are passed to a keybinding function"""
-	def __init__(self, fm, widget, keybuffer):
+	def __init__(self, fm, widget, keybuf):
 		self.fm = fm
 		self.wdg = widget
-		self.keybuffer = keybuffer
-		self.n = keybuffer.quant
-		self.direction = keybuffer.directions and keybuffer.directions[0] or None
-		self.directions = keybuffer.directions
-		self.keys = str(keybuffer)
-		self.matches = keybuffer.matches
-		self.match = keybuffer.matches and keybuffer.matches[0] or None
-		self.binding = keybuffer.command
+		self.keybuffer = keybuf
+		self.n = keybuf.quant
+		self.direction = keybuf.directions and keybuf.directions[0] or None
+		self.directions = keybuf.directions
+		self.keys = str(keybuf)
+		self.matches = keybuf.matches
+		self.match = keybuf.matches and keybuf.matches[0] or None
+		self.binding = keybuf.command
 
 	@staticmethod
 	def from_widget(widget):
@@ -64,7 +46,7 @@ class KeyMap(Tree):
 		if keywords:
 			return self._add_binding(*args, **keywords)
 		firstarg = args[-1]
-		if isfunction(firstarg):
+		if hasattr(firstarg, '__call__'):
 			keywords[FUNC] = firstarg
 			return self._add_binding(*args[:-1], **keywords)
 		def decorator_function(func):
@@ -79,14 +61,14 @@ class KeyMap(Tree):
 		assert keys
 		bind = Binding(keys, actions)
 		for key in keys:
-			self.set(translate_keys(key), bind)
+			self.set(parse_keybinding(key), bind)
 
 	def unmap(self, *keys):
 		for key in keys:
-			self.unset(translate_keys(key))
+			self.unset(parse_keybinding(key))
 
 	def __getitem__(self, key):
-		return self.traverse(translate_keys(key))
+		return self.traverse(parse_keybinding(key))
 
 
 class KeyMapWithDirections(KeyMap):
@@ -153,13 +135,6 @@ class Binding(object):
 			self.function = self.actions[FUNC]
 		except KeyError:
 			self.function = None
-			self.has_direction = False
-		else:
-			argnames = getargspec(self.function)[0]
-			try:
-				self.has_direction = actions['with_direction']
-			except KeyError:
-				self.has_direction = DIRECTION in argnames
 		try:
 			self.direction = self.actions[DIRARG]
 		except KeyError:
@@ -169,238 +144,4 @@ class Binding(object):
 		except KeyError:
 			self.alias = None
 		else:
-			self.alias = tuple(translate_keys(alias))
-
-class KeyBuffer(object):
-	"""The evaluator and storage for pressed keys"""
-	def __init__(self, keymap, direction_keys):
-		self.assign(keymap, direction_keys)
-
-	def assign(self, keymap, direction_keys):
-		self.keymap = keymap
-		self.direction_keys = direction_keys
-
-	def add(self, key):
-		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 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 direction keys {j,k,gg,pagedown,...}
-			if not self.eval_command:
-				self._do_eval_direction(key)
-
-	def _do_eval_direction(self, key):
-		try:
-			assert isinstance(self.dir_tree_pointer, dict)
-			self.dir_tree_pointer = self.dir_tree_pointer[key]
-		except KeyError:
-			self.failure = True
-		else:
-			self._direction_try_to_finish()
-
-	def _direction_try_to_finish(self):
-		if self.max_alias_recursion <= 0:
-			self.failure = True
-			return None
-		match = self.dir_tree_pointer
-		assert isinstance(match, (Binding, dict, KeyMap))
-		if isinstance(match, KeyMap):
-			self.dir_tree_pointer = self.dir_tree_pointer._tree
-			match = self.dir_tree_pointer
-		if isinstance(self.dir_tree_pointer, Binding):
-			if match.alias:
-				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:
-					direction.multiply(self.direction_quant)
-				self.directions.append(direction)
-				self.direction_quant = None
-				self.eval_command = True
-				self._try_to_finish()
-
-	def _do_eval_quantifier(self, key):
-		if self.eval_command:
-			tree = self.tree_pointer
-		else:
-			tree = self.dir_tree_pointer
-		if is_ascii_digit(key) and ANYKEY not in tree:
-			attr = self.eval_command and 'quant' or 'direction_quant'
-			if getattr(self, attr) is None:
-				setattr(self, attr, 0)
-			setattr(self, attr, getattr(self, attr) * 10 + key - 48)
-		else:
-			self.eval_quantifier = False
-			return None
-		return True
-
-	def _do_eval_command(self, key):
-		assert isinstance(self.tree_pointer, dict), self.tree_pointer
-		try:
-			self.tree_pointer = self.tree_pointer[key]
-		except TypeError:
-			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:
-					self.tree_pointer = self.tree_pointer[ANYKEY]
-				except KeyError:
-					self.failure = True
-					return None
-				else:
-					self.matches.append(key)
-					assert isinstance(self.tree_pointer, (Binding, dict))
-					self._try_to_finish()
-			else:
-				assert isinstance(self.tree_pointer, (Binding, dict))
-				self.eval_command = False
-				self.eval_quantifier = True
-				self.dir_tree_pointer = self.direction_keys._tree
-		else:
-			if isinstance(self.tree_pointer, dict):
-				try:
-					self.command = self.tree_pointer[PASSIVE_ACTION]
-				except (KeyError, TypeError):
-					self.command = None
-			self._try_to_finish()
-
-	def _try_to_finish(self):
-		if self.max_alias_recursion <= 0:
-			self.failure = True
-			return None
-		assert isinstance(self.tree_pointer, (Binding, dict, KeyMap))
-		if isinstance(self.tree_pointer, KeyMap):
-			self.tree_pointer = self.tree_pointer._tree
-		if isinstance(self.tree_pointer, Binding):
-			if self.tree_pointer.alias:
-				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
-		self.matches = []
-		self.command = None
-		self.direction_quant = None
-		self.directions = []
-		self.all_keys = []
-		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
-
-	def __str__(self):
-		"""returns a concatenation of all characters"""
-		return "".join(to_string(c) for c in self.all_keys)
-
-	def simulate_press(self, string):
-		for char in translate_keys(string):
-			self.add(char)
-			if self.done:
-				return self.command
-			if self.failure:
-				break
-		return self.command
-
-special_keys = {
-	'dir': DIRKEY,
-	'any': ANYKEY,
-	'bg': PASSIVE_ACTION,
-	'backspace': curses.KEY_BACKSPACE,
-	'backspace2': curses.ascii.DEL,
-	'delete': curses.KEY_DC,
-	'cr': ord("\n"),
-	'enter': ord("\n"),
-	'space': ord(" "),
-	'esc': curses.ascii.ESC,
-	'down': curses.KEY_DOWN,
-	'up': curses.KEY_UP,
-	'left': curses.KEY_LEFT,
-	'right': curses.KEY_RIGHT,
-	'pagedown': curses.KEY_NPAGE,
-	'pageup': curses.KEY_PPAGE,
-	'home': curses.KEY_HOME,
-	'end': curses.KEY_END,
-	'tab': ord('\t'),
-	's-tab': curses.KEY_BTAB,
-}
-for char in ascii_lowercase:
-	special_keys['c-' + char] = ord(char) - 96
-
-for char in (ascii_lowercase + '0123456789'):
-	special_keys['a-' + char] = (27, ord(char))
-
-def translate_keys(obj):
-	"""
-	Translate a keybinding to a sequence of integers
-
-	Example:
-	lol<CR>   =>   (108, 111, 108, 10)
-	"""
-	assert isinstance(obj, (tuple, int, str))
-	if isinstance(obj, tuple):
-		for char in obj:
-			yield char
-	elif isinstance(obj, int):
-		yield obj
-	elif isinstance(obj, str):
-		in_brackets = False
-		bracket_content = None
-		for char in obj:
-			if in_brackets:
-				if char == '>':
-					in_brackets = False
-					string = ''.join(bracket_content).lower()
-					try:
-						keys = special_keys[string]
-						for key in keys:
-							yield key
-					except KeyError:
-						yield ord('<')
-						for c in bracket_content:
-							yield ord(c)
-						yield ord('>')
-					except TypeError:
-						yield keys  # it was no tuple, just an int
-				else:
-					bracket_content.append(char)
-			else:
-				if char == '<':
-					in_brackets = True
-					bracket_content = []
-				else:
-					yield ord(char)
-		if in_brackets:
-			yield ord('<')
-			for c in bracket_content:
-				yield ord(c)
+			self.alias = tuple(parse_keybinding(alias))
diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py
index 9e8e19e2..61138007 100644
--- a/ranger/defaults/keys.py
+++ b/ranger/defaults/keys.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 # Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
 #
 # This program is free software: you can redistribute it and/or modify
diff --git a/ranger/ext/keybinding_parser.py b/ranger/ext/keybinding_parser.py
new file mode 100644
index 00000000..c33ac12f
--- /dev/null
+++ b/ranger/ext/keybinding_parser.py
@@ -0,0 +1,96 @@
+# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import curses.ascii
+from string import ascii_lowercase
+
+def parse_keybinding(obj):
+	"""
+	Translate a keybinding to a sequence of integers
+
+	Example:
+	lol<CR>   =>   (108, 111, 108, 10)
+	"""
+	assert isinstance(obj, (tuple, int, str))
+	if isinstance(obj, tuple):
+		for char in obj:
+			yield char
+	elif isinstance(obj, int):
+		yield obj
+	elif isinstance(obj, str):
+		in_brackets = False
+		bracket_content = None
+		for char in obj:
+			if in_brackets:
+				if char == '>':
+					in_brackets = False
+					string = ''.join(bracket_content).lower()
+					try:
+						keys = special_keys[string]
+						for key in keys:
+							yield key
+					except KeyError:
+						yield ord('<')
+						for c in bracket_content:
+							yield ord(c)
+						yield ord('>')
+					except TypeError:
+						yield keys  # it was no tuple, just an int
+				else:
+					bracket_content.append(char)
+			else:
+				if char == '<':
+					in_brackets = True
+					bracket_content = []
+				else:
+					yield ord(char)
+		if in_brackets:
+			yield ord('<')
+			for c in bracket_content:
+				yield ord(c)
+
+# Arbitrary numbers which are not used with curses.KEY_XYZ
+DIRKEY = 9001
+ANYKEY = 9002
+PASSIVE_ACTION = 9003
+
+special_keys = {
+	'dir': DIRKEY,
+	'any': ANYKEY,
+	'bg': PASSIVE_ACTION,
+	'backspace': curses.KEY_BACKSPACE,
+	'backspace2': curses.ascii.DEL,
+	'delete': curses.KEY_DC,
+	'cr': ord("\n"),
+	'enter': ord("\n"),
+	'space': ord(" "),
+	'esc': curses.ascii.ESC,
+	'down': curses.KEY_DOWN,
+	'up': curses.KEY_UP,
+	'left': curses.KEY_LEFT,
+	'right': curses.KEY_RIGHT,
+	'pagedown': curses.KEY_NPAGE,
+	'pageup': curses.KEY_PPAGE,
+	'home': curses.KEY_HOME,
+	'end': curses.KEY_END,
+	'tab': ord('\t'),
+	's-tab': curses.KEY_BTAB,
+}
+
+for char in ascii_lowercase:
+	special_keys['c-' + char] = ord(char) - 96
+
+for char in (ascii_lowercase + '0123456789'):
+	special_keys['a-' + char] = (27, ord(char))