diff options
Diffstat (limited to 'test/tc_newkeys.py')
-rw-r--r-- | test/tc_newkeys.py | 432 |
1 files changed, 17 insertions, 415 deletions
diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 2b040954..8f5422a0 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -2,420 +2,10 @@ if __name__ == '__main__': from __init__ import init; init() from unittest import TestCase, main -from inspect import isfunction, getargspec -import inspect +from ranger.ext.tree import Tree +from ranger.container.keymap import * + import sys -from string import ascii_lowercase -try: - from sys import intern -except: - pass - -FUNC = 'func' -DIRECTION = 'direction' -DIRARG = 'dir' -ALIASARG = 'alias' -DIRKEY = 9001 -ANYKEY = 9002 -MAX_ALIAS_RECURSION = 20 - -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 Direction(object): - """An object with a down and right method""" - def __init__(self, down=0, right=0): - self.down = down - self.right = right - - def copy(self): - new = type(self)() - new.__dict__.update(self.__dict__) - return new - - def __mul__(self, other): - copy = self.copy() - if other is not None: - copy.down *= other - copy.right *= other - return copy - __rmul__ = __mul__ - -class CommandArgs(object): - """The arguments which are passed to a keybinding function""" - def __init__(self, fm, widget, keybuffer): - 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.binding = keybuffer.command - -class KeyBuffer(object): - """The evaluator and storage for pressed keys""" - def __init__(self, keymap, direction_keys): - self.keymap = keymap - self.direction_keys = direction_keys - self.clear() - - def add(self, key): - if self.failure: - return None - assert isinstance(key, int) - assert key >= 0 - - # 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): - # swap quant and direction_quant in bindings like '<dir>' - if self.quant is not None and self.command is None \ - and self.direction_quant is None: - self.direction_quant = self.quant - self.quant = None - - 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, rec=MAX_ALIAS_RECURSION): - if rec <= 0: - self.failure = True - return None - if not isinstance(self.dir_tree_pointer, dict): - match = self.dir_tree_pointer - assert isinstance(match, Binding) - if 'alias' in match.actions: - self.dir_tree_pointer = self.direction_keys.traverse( - match.alias) - self._direction_try_to_finish(rec - 1) - else: - direction = match.actions['dir'] * 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): - try: - assert isinstance(self.tree_pointer, dict) - self.tree_pointer = self.tree_pointer[key] - except TypeError: - print(self.tree_pointer) - self.failure = True - return None - except KeyError: - if DIRKEY in self.tree_pointer: - self.eval_command = False - self.eval_quantifier = True - self.tree_pointer = self.tree_pointer[DIRKEY] - assert isinstance(self.tree_pointer, (Binding, dict)) - self.dir_tree_pointer = self.direction_keys._tree - elif ANYKEY in self.tree_pointer: - self.matches.append(key) - self.tree_pointer = self.tree_pointer[ANYKEY] - assert isinstance(self.tree_pointer, (Binding, dict)) - self._try_to_finish() - else: - self.failure = True - return None - else: - self._try_to_finish() - - def _try_to_finish(self, rec=MAX_ALIAS_RECURSION): - if rec <= 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 'alias' in self.tree_pointer.actions: - self.tree_pointer = self.keymap.traverse( - translate_keys(self.tree_pointer.actions['alias'])) - self._try_to_finish(rec - 1) - else: - self.command = self.tree_pointer - self.done = True - - def clear(self): - 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.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 - -key_map = { - 'dir': DIRKEY, - 'any': ANYKEY, - 'cr': ord("\n"), - 'enter': ord("\n"), - 'space': ord(" "), - 'space': ord(" "), - 'tab': ord('\t'), -} -for char in ascii_lowercase: - key_map['c-' + char] = ord(char) - 96 - -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: - yield key_map[string] - except KeyError: - yield ord('<') - for c in bracket_content: - yield ord(c) - yield ord('>') - 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) - -class Tree(object): - def __init__(self, dictionary=None, parent=None, key=None): - if dictionary is None: - self._tree = dict() - else: - self._tree = dictionary - self.key = key - self.parent = parent - - def copy(self): - """Create a deep copy""" - def deep_copy_dict(dct): - dct = dct.copy() - for key, val in dct.items(): - if isinstance(val, dict): - dct[key] = deep_copy_dict(val) - return dct - newtree = Tree() - if isinstance(self._tree, dict): - newtree._tree = deep_copy_dict(self._tree) - else: - newtree._tree = self._tree - return newtree - - def merge(self, other, copy=True): - """Merge another Tree into a copy of self""" - def deep_merge(branch, otherbranch): - assert isinstance(otherbranch, dict) - if not isinstance(branch, dict): - branch = dict() - elif copy: - branch = branch.copy() - for key, val in otherbranch.items(): - if isinstance(val, dict): - if key not in branch: - branch[key] = None - branch[key] = deep_merge(branch[key], val) - else: - branch[key] = val - return branch - - if isinstance(self._tree, dict) and isinstance(other._tree, dict): - content = deep_merge(self._tree, other._tree) - elif copy and hasattr(other._tree, 'copy'): - content = other._tree.copy() - else: - content = other._tree - return type(self)(content) - - def set(self, keys, value, force=True): - """Sets the element at the end of the path to <value>.""" - if not isinstance(keys, (list, tuple)): - keys = tuple(keys) - if len(keys) == 0: - self.replace(value) - else: - fnc = force and self.plow or self.traverse - subtree = fnc(keys) - subtree.replace(value) - - def replace(self, value): - if self.parent: - self.parent[self.key] = value - self._tree = value - - def plow(self, iterable): - """Move along a path, creating nonexistant subtrees""" - tree = self._tree - last_tree = None - char = None - for char in iterable: - try: - newtree = tree[char] - if not isinstance(newtree, dict): - raise KeyError() - except KeyError: - newtree = dict() - tree[char] = newtree - last_tree = tree - tree = newtree - if isinstance(tree, dict): - return type(self)(tree, parent=last_tree, key=char) - else: - return tree - - def traverse(self, iterable): - """Move along a path, raising exceptions when failed""" - tree = self._tree - last_tree = tree - char = None - for char in iterable: - last_tree = tree - try: - tree = tree[char] - except TypeError: - raise KeyError("trying to enter leaf") - except KeyError: - raise KeyError(str(char) + " not in tree " + str(tree)) - if isinstance(tree, dict): - return type(self)(tree, parent=last_tree, key=char) - else: - return tree - - __getitem__ = traverse - -class KeyMap(Tree): - """Contains a tree with all the keybindings""" - def add(self, *args, **keywords): - if keywords: - return self.add_binding(*args, **keywords) - firstarg = args[0] - if isfunction(firstarg): - keywords[FUNC] = firstarg - return self.add_binding(*args[1:], **keywords) - def decorator_function(func): - keywords = {FUNC:func} - self.add(*args, **keywords) - return func - return decorator_function - - def add_binding(self, *keys, **actions): - assert keys - bind = Binding(keys, actions) - for key in keys: - self.set(translate_keys(key), bind) - - def __getitem__(self, key): - return self.traverse(translate_keys(key)) - -class Binding(object): - """The keybinding object""" - def __init__(self, keys, actions): - assert hasattr(keys, '__iter__') - assert isinstance(actions, dict) - self.actions = actions - try: - 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: - self.direction = None - try: - alias = self.actions[ALIASARG] - except KeyError: - self.alias = None - else: - self.alias = translate_keys(alias) class PressTestCase(TestCase): """Some useful methods for the actual test""" @@ -539,10 +129,22 @@ class Test(PressTestCase): t2 = Tree() self.assertRaises(KeyError, t2.set, 'axy', "Lol", force=False) - subtree = t2.set('axy', "Lol") + t2.set('axx', 'ololol') + t2.set('axyy', "Lol") self.assertEqual("Yes", t.traverse('abcd')) self.assertRaises(KeyError, t2.traverse, 'abcd') - self.assertEqual("Lol", t2.traverse('axy')) + self.assertEqual("Lol", t2.traverse('axyy')) + self.assertEqual("ololol", t2.traverse('axx')) + + t2.unset('axyy') + self.assertEqual("ololol", t2.traverse('axx')) + self.assertRaises(KeyError, t2.traverse, 'axyy') + self.assertRaises(KeyError, t2.traverse, 'axy') + + t2.unset('a') + self.assertRaises(KeyError, t2.traverse, 'abcd') + self.assertRaises(KeyError, t2.traverse, 'a') + self.assert_(t2.empty()) def test_merge_trees(self): def makeTreeA(): |