From dd249864e9f2d0a80655c64b23d7b074740798a7 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 14 Jan 2010 19:56:58 +0100 Subject: custom: added ] key: tag next and run --- ranger/defaults/keys.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index ee6a7802..802574c2 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -185,6 +185,18 @@ def initialize_commands(command_list): bind('?', KEY_F1, fm.display_help()) bind('w', lambda arg: arg.fm.ui.open_taskview()) + # ---------------------------------------------------------- custom + # This is useful to track watched episode of a series. + @bind(']') + def tag_next_and_run(arg): + fm = arg.fm + fm.tag_remove() + fm.tag_remove(movedown=False) + fm.tag_toggle() + fm.move_pointer(relative=-2) + fm.move_right() + fm.move_pointer(relative=1) + # ------------------------------------------------ system functions _system_functions(command_list) bind('ZZ', fm.exit()) -- cgit 1.4.1-2-gfad0 From 1a1ca6dea05867d832517346ea8e49f8cd1e41f0 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 14 Jan 2010 21:01:26 +0100 Subject: custom: keys: added aliases (J => ^D, K => ^U) for quick movement --- ranger/defaults/keys.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 802574c2..023f84a4 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -50,6 +50,10 @@ def _vimlike_aliases(command_list): alias(KEY_HOME, 'gg') alias(KEY_END, 'G') + # I like to move quickly with J/K + alias(ctrl('d'), 'J') + alias(ctrl('u'), 'K') + def initialize_commands(command_list): """Initialize the commands for the main user interface""" -- cgit 1.4.1-2-gfad0 From cf81a11ada43078a68960f9118c5879f1acf8f61 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 4 Feb 2010 22:06:31 +0100 Subject: custom: added info about this branch --- ABOUT_THIS_BRANCH | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 ABOUT_THIS_BRANCH diff --git a/ABOUT_THIS_BRANCH b/ABOUT_THIS_BRANCH new file mode 100644 index 00000000..ae09d54d --- /dev/null +++ b/ABOUT_THIS_BRANCH @@ -0,0 +1,14 @@ +I put my personal branch online, maybe you find some of the +parts useful. To see whats different, type: + +git diff master..hut + +This branch is being regularily rebased on the master branch, +which rewrites history, so maybe its better to pick single commits +from this branch into your own branch rather than working directly +on this one: + +git log master..hut +# search for a commit you like, write down SHA1 identifier +git checkout +git cherry-pick -- cgit 1.4.1-2-gfad0 From a32c6a1f417d249beaa02c416a790642bd94a96d Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 16 Feb 2010 01:59:49 +0100 Subject: custom: "enter" = shortcut for "1l" --- ranger/defaults/keys.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 023f84a4..260ba568 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -201,6 +201,9 @@ def initialize_commands(command_list): fm.move_right() fm.move_pointer(relative=1) + # "enter" = shortcut for "1l" + bind(KEY_ENTER, ctrl('j'), fm.move_right(mode=1)) + # ------------------------------------------------ system functions _system_functions(command_list) bind('ZZ', fm.exit()) -- cgit 1.4.1-2-gfad0 From d8cd2b7573a43d94ac28a83d3ff4ffca14cde11a Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 9 Feb 2010 10:23:08 +0100 Subject: started new key parser --- test/tc_newkeys.py | 331 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 331 insertions(+) create mode 100644 test/tc_newkeys.py diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py new file mode 100644 index 00000000..4b46b0f8 --- /dev/null +++ b/test/tc_newkeys.py @@ -0,0 +1,331 @@ +if __name__ == '__main__': from __init__ import init; init() +from unittest import TestCase, main + +from inspect import isfunction, getargspec +import inspect +from sys import intern + +FUNC = 'func' +DIRECTION = 'direction' +QUANTIFIER = 'n' +MATCH = intern('!!') + +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 + +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.quant1 + self.direction = keybuffer.direction + self.keys = str(keybuffer) + +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 first quantifier + if self.level == 0: + if is_ascii_digit(key): + if self.quant1 is None: + self.quant1 = 0 + self.quant1 = self.quant1 * 10 + key - 48 + else: + self.level = 1 + + # evaluate the command and the second quantifier. + # it's possible to jump between them. "x3xj" is equivalent to "xx3j" + if self.level == 1: + try: + self.tree_pointer = self.tree_pointer[key] + except KeyError: + try: + match = self.tree_pointer[MATCH] + except KeyError: + self.failure = True + return None + # self.command = match + if is_ascii_digit(key): + if self.quant2 is None: + self.quant2 = 0 + self.quant2 = self.quant2 * 10 + key - 48 + else: + self.level = 2 + self.tree_pointer = self.direction_keys._tree + else: + try: + match = self.tree_pointer[MATCH] + except KeyError: + pass + else: + self.command = match + if not match.has_direction: + if self.quant2 is not None: + self.direction = self.direction * self.quant2 + self.done = True + + # evaluate direction keys {j,k,gg,pagedown,...} + if self.level == 2: + try: + self.tree_pointer = self.tree_pointer[key] + except KeyError: + self.failure = True + else: + try: + match = self.tree_pointer[MATCH] + except KeyError: + pass + else: + self.direction = match.actions['dir'] * self.quant2 + self.done = True + + + def clear(self): + self.failure = False + self.done = False + self.quant1 = None + self.quant2 = None + self.command = None + self.direction = Direction(down=1) + self.all_keys = [] + self.tree_pointer = self.keymap._tree + self.direction_tree_pointer = self.direction_keys._tree + self.level = 0 + # level 0 = parsing quantifier 1 + # 1 = parsing command or quantifier 2 + # 2 = parsing direction + + 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 string: + self.add(ord(char)) + if self.done: + return self.command + if self.failure: + break + +class Keymap(object): + """Contains a tree with all the keybindings""" + def __init__(self): + self._tree = dict() + + 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 _split(self, key): + assert isinstance(key, (tuple, int, str)) + if isinstance(key, tuple): + return key + if isinstance(key, str): + return (ord(k) for k in key) + if isinstance(key, int): + return (key, ) + raise TypeError(key) + + def add_binding(self, *keys, **actions): + assert keys + bind = binding(keys, actions) + + for key in keys: + assert key + chars = tuple(self._split(key)) + tree = self.traverse_tree(chars) + tree[MATCH] = bind + + def traverse_tree(self, generator): + tree = self._tree + for char in generator: + try: + tree = tree[char] + except KeyError: + tree[char] = dict() + tree = tree[char] + except TypeError: + raise TypeError("Attempting to override existing entry") + return tree + + def __getitem__(self, key): + tree = self._tree + for char in self._split(key): + try: + tree = tree[char] + except TypeError: + raise KeyError("trying to enter leaf") + except KeyError: + raise KeyError(str(char) + " not in tree " + str(tree)) + try: + return tree[MATCH] + except KeyError: + raise KeyError(str(char) + " not in tree " + str(tree)) + +class binding(object): + """The keybinding object""" + def __init__(self, keys, actions): + assert hasattr(keys, '__iter__') + assert isinstance(actions, dict) + self.keys = set(keys) + 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 + + def add_keys(self, keys): + assert isinstance(keys, set) + self.keys |= keys + + def has(self, action): + return action in self.actions + + def action(self, key): + return self.actions[key] + +def n(value): + """ return n or value """ + def fnc(n=None): + if n is None: + return value + return n + return fnc + +def nd(n=1, direction=Direction()): + """ n * direction """ + if n is None: + n = 1 + return n * direction.down + +class Test(TestCase): + """The test cases""" + def test_add(self): + c = Keymap() + c.add(lambda *_: 'lolz', 'aa', 'b') + self.assert_(c['aa'].actions[FUNC](), 'lolz') + @c.add('a', 'c') + def test(): + return 5 + self.assert_(c['b'].actions[FUNC](), 'lolz') + self.assert_(c['c'].actions[FUNC](), 5) + self.assert_(c['a'].actions[FUNC](), 5) + + def test_quantifier(self): + km = Keymap() + directions = Keymap() + kb = KeyBuffer(km, directions) + km.add(n(5), 'd') + match = kb.simulate_press('3d') + self.assertEqual(3, match.function(kb.quant1)) + kb.clear() + match = kb.simulate_press('6223d') + self.assertEqual(6223, match.function(kb.quant1)) + kb.clear() + + def test_direction(self): + km = Keymap() + directions = Keymap() + kb = KeyBuffer(km, directions) + directions.add('j', dir=Direction(down=1)) + directions.add('k', dir=Direction(down=-1)) + km.add(nd, 'd') + km.add('dd', func=nd, with_direction=False) + + match = kb.simulate_press('3d5j') + self.assertEqual(15, match.function(n=kb.quant1, direction=kb.direction)) + kb.clear() + + match = kb.simulate_press('3d5k') + self.assertEqual(-15, match.function(n=kb.quant1, direction=kb.direction)) + kb.clear() + + match = kb.simulate_press('3d5d') + self.assertEqual(15, match.function(n=kb.quant1, direction=kb.direction)) + kb.clear() + + match = kb.simulate_press('3dd') + self.assertEqual(3, match.function(n=kb.quant1, direction=kb.direction)) + kb.clear() + + match = kb.simulate_press('dd') + self.assertEqual(1, match.function(n=kb.quant1, direction=kb.direction)) + kb.clear() + + km.add(nd, 'x') + km.add('xxxx', func=nd, with_direction=False) + + match = kb.simulate_press('xxxxj') + self.assertEqual(1, match.function(n=kb.quant1, direction=kb.direction)) + kb.clear() + + match = kb.simulate_press('xxxxjsomeinvalidchars') + self.assertEqual(1, match.function(n=kb.quant1, direction=kb.direction)) + kb.clear() + + self.assertEqual(None, kb.simulate_press('xxxj')) + kb.clear() + self.assertEqual(None, kb.simulate_press('xxj')) + kb.clear() + self.assertEqual(None, kb.simulate_press('xxkldfjalksdjklsfsldkj')) + kb.clear() + self.assertEqual(None, kb.simulate_press('xyj')) + kb.clear() + self.assertEqual(None, kb.simulate_press('x')) #direction missing + kb.clear() + + +if __name__ == '__main__': main() -- cgit 1.4.1-2-gfad0 From 8d56a585f6c0cbda42b8b3c2278309b67e46ad7d Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 10 Feb 2010 00:20:41 +0100 Subject: keyparser: some improvements --- test/tc_newkeys.py | 103 +++++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 4b46b0f8..e32ddbd5 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -1,5 +1,6 @@ if __name__ == '__main__': from __init__ import init; init() from unittest import TestCase, main +from pprint import pprint as print from inspect import isfunction, getargspec import inspect @@ -7,6 +8,8 @@ from sys import intern FUNC = 'func' DIRECTION = 'direction' +DIRKEY = 9999 +ANYKEY = 'any' QUANTIFIER = 'n' MATCH = intern('!!') @@ -75,26 +78,24 @@ class KeyBuffer(object): if self.level == 1: try: self.tree_pointer = self.tree_pointer[key] + except TypeError: + self.failure = True + return None except KeyError: - try: - match = self.tree_pointer[MATCH] - except KeyError: - self.failure = True - return None - # self.command = match if is_ascii_digit(key): if self.quant2 is None: self.quant2 = 0 self.quant2 = self.quant2 * 10 + key - 48 - else: + elif DIRKEY in self.tree_pointer: self.level = 2 + self.command = self.tree_pointer[DIRKEY] self.tree_pointer = self.direction_keys._tree - else: - try: - match = self.tree_pointer[MATCH] - except KeyError: - pass else: + self.failure = True + return None + else: + if not isinstance(self.tree_pointer, dict): + match = self.tree_pointer self.command = match if not match.has_direction: if self.quant2 is not None: @@ -108,11 +109,8 @@ class KeyBuffer(object): except KeyError: self.failure = True else: - try: - match = self.tree_pointer[MATCH] - except KeyError: - pass - else: + if not isinstance(self.tree_pointer, dict): + match = self.tree_pointer self.direction = match.actions['dir'] * self.quant2 self.done = True @@ -165,12 +163,20 @@ class Keymap(object): def _split(self, key): assert isinstance(key, (tuple, int, str)) if isinstance(key, tuple): - return key - if isinstance(key, str): - return (ord(k) for k in key) - if isinstance(key, int): - return (key, ) - raise TypeError(key) + for char in key: + yield char + elif isinstance(key, str): + for char in key: + if char == '.': + yield ANYKEY + elif char == '}': + yield DIRKEY + else: + yield ord(char) + elif isinstance(key, int): + yield key + else: + raise TypeError(key) def add_binding(self, *keys, **actions): assert keys @@ -179,8 +185,9 @@ class Keymap(object): for key in keys: assert key chars = tuple(self._split(key)) - tree = self.traverse_tree(chars) - tree[MATCH] = bind + tree = self.traverse_tree(chars[:-1]) + if isinstance(tree, dict): + tree[chars[-1]] = bind def traverse_tree(self, generator): tree = self._tree @@ -204,7 +211,7 @@ class Keymap(object): except KeyError: raise KeyError(str(char) + " not in tree " + str(tree)) try: - return tree[MATCH] + return tree except KeyError: raise KeyError(str(char) + " not in tree " + str(tree)) @@ -282,40 +289,34 @@ class Test(TestCase): kb = KeyBuffer(km, directions) directions.add('j', dir=Direction(down=1)) directions.add('k', dir=Direction(down=-1)) - km.add(nd, 'd') + km.add(nd, 'd}') km.add('dd', func=nd, with_direction=False) - match = kb.simulate_press('3d5j') - self.assertEqual(15, match.function(n=kb.quant1, direction=kb.direction)) - kb.clear() - - match = kb.simulate_press('3d5k') - self.assertEqual(-15, match.function(n=kb.quant1, direction=kb.direction)) - kb.clear() - - match = kb.simulate_press('3d5d') - self.assertEqual(15, match.function(n=kb.quant1, direction=kb.direction)) - kb.clear() - match = kb.simulate_press('3dd') - self.assertEqual(3, match.function(n=kb.quant1, direction=kb.direction)) - kb.clear() + def press(keys): + kb.clear() + match = kb.simulate_press(keys) + self.assertFalse(kb.failure, "parsing keys '"+keys+"' did fail!") + self.assertTrue(kb.done, "parsing keys '"+keys+ \ + "' did not complete!") + dic = {QUANTIFIER:kb.quant1, DIRECTION:kb.direction} + return match.function(**dic) - match = kb.simulate_press('dd') - self.assertEqual(1, match.function(n=kb.quant1, direction=kb.direction)) - kb.clear() + self.assertEqual( 3, press('3ddj')) + self.assertEqual( 15, press('3d5j')) + self.assertEqual(-15, press('3d5k')) + self.assertEqual( 15, press('3d5d')) + self.assertEqual( 3, press('3dd')) + self.assertEqual( 1, press('dd')) - km.add(nd, 'x') + km.add(nd, 'x}') km.add('xxxx', func=nd, with_direction=False) - match = kb.simulate_press('xxxxj') - self.assertEqual(1, match.function(n=kb.quant1, direction=kb.direction)) - kb.clear() + self.assertEqual(1, press('xxxxj')) + self.assertEqual(1, press('xxxxjsomeinvalitchars')) - match = kb.simulate_press('xxxxjsomeinvalidchars') - self.assertEqual(1, match.function(n=kb.quant1, direction=kb.direction)) + # these combinations should break: kb.clear() - self.assertEqual(None, kb.simulate_press('xxxj')) kb.clear() self.assertEqual(None, kb.simulate_press('xxj')) -- cgit 1.4.1-2-gfad0 From 39db95d920dbd42c082d57f9ce9f299fba70802c Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 10 Feb 2010 01:25:34 +0100 Subject: keyparser: "." matches any character --- test/tc_newkeys.py | 94 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 71 insertions(+), 23 deletions(-) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index e32ddbd5..39d6b23a 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -8,8 +8,8 @@ from sys import intern FUNC = 'func' DIRECTION = 'direction' -DIRKEY = 9999 -ANYKEY = 'any' +DIRKEY = 9001 +ANYKEY = 9002 QUANTIFIER = 'n' MATCH = intern('!!') @@ -50,6 +50,7 @@ class CommandArgs(object): self.n = keybuffer.quant1 self.direction = keybuffer.direction self.keys = str(keybuffer) + self.moo = keybuffer.moo class KeyBuffer(object): """The evaluator and storage for pressed keys""" @@ -66,7 +67,7 @@ class KeyBuffer(object): # evaluate first quantifier if self.level == 0: - if is_ascii_digit(key): + if is_ascii_digit(key) and ANYKEY not in self.tree_pointer: if self.quant1 is None: self.quant1 = 0 self.quant1 = self.quant1 * 10 + key - 48 @@ -82,7 +83,7 @@ class KeyBuffer(object): self.failure = True return None except KeyError: - if is_ascii_digit(key): + if is_ascii_digit(key) and ANYKEY not in self.tree_pointer: if self.quant2 is None: self.quant2 = 0 self.quant2 = self.quant2 * 10 + key - 48 @@ -90,17 +91,15 @@ class KeyBuffer(object): self.level = 2 self.command = self.tree_pointer[DIRKEY] self.tree_pointer = self.direction_keys._tree + elif ANYKEY in self.tree_pointer: + self.moo.append(key) + self.tree_pointer = self.tree_pointer[ANYKEY] + self._try_to_finish() else: self.failure = True return None else: - if not isinstance(self.tree_pointer, dict): - match = self.tree_pointer - self.command = match - if not match.has_direction: - if self.quant2 is not None: - self.direction = self.direction * self.quant2 - self.done = True + self._try_to_finish() # evaluate direction keys {j,k,gg,pagedown,...} if self.level == 2: @@ -114,11 +113,20 @@ class KeyBuffer(object): self.direction = match.actions['dir'] * self.quant2 self.done = True + def _try_to_finish(self): + if not isinstance(self.tree_pointer, dict): + match = self.tree_pointer + self.command = match + if not match.has_direction: + if self.quant2 is not None: + self.direction = self.direction * self.quant2 + self.done = True def clear(self): self.failure = False self.done = False self.quant1 = None + self.moo = [] self.quant2 = None self.command = None self.direction = Direction(down=1) @@ -252,14 +260,32 @@ def n(value): return n return fnc -def nd(n=1, direction=Direction()): +def nd(arg): """ n * direction """ - if n is None: + if arg.n is None: n = 1 - return n * direction.down + else: + n = arg.n + if arg.direction is None: + dir = Direction(down=1) + else: + dir = arg.direction + return n * dir.down class Test(TestCase): """The test cases""" + def _mkpress(self, keybuffer, keymap): + def press(keys): + keybuffer.clear() + match = keybuffer.simulate_press(keys) + self.assertFalse(keybuffer.failure, + "parsing keys '"+keys+"' did fail!") + self.assertTrue(keybuffer.done, + "parsing keys '"+keys+"' did not complete!") + arg = CommandArgs(None, None, keybuffer) + return match.function(arg) + return press + def test_add(self): c = Keymap() c.add(lambda *_: 'lolz', 'aa', 'b') @@ -292,15 +318,7 @@ class Test(TestCase): km.add(nd, 'd}') km.add('dd', func=nd, with_direction=False) - - def press(keys): - kb.clear() - match = kb.simulate_press(keys) - self.assertFalse(kb.failure, "parsing keys '"+keys+"' did fail!") - self.assertTrue(kb.done, "parsing keys '"+keys+ \ - "' did not complete!") - dic = {QUANTIFIER:kb.quant1, DIRECTION:kb.direction} - return match.function(**dic) + press = self._mkpress(kb, km) self.assertEqual( 3, press('3ddj')) self.assertEqual( 15, press('3d5j')) @@ -328,5 +346,35 @@ class Test(TestCase): self.assertEqual(None, kb.simulate_press('x')) #direction missing kb.clear() + def test_any_key(self): + km = Keymap() + directions = Keymap() + kb = KeyBuffer(km, directions) + directions.add('j', dir=Direction(down=1)) + directions.add('k', dir=Direction(down=-1)) + + def cat(arg): + n = arg.n is None and 1 or arg.n + return ''.join(chr(c) for c in arg.moo) * n + + km.add(cat, 'return.') + km.add(cat, 'cat4....') + + press = self._mkpress(kb, km) + + self.assertEqual('x', press('returnx')) + self.assertEqual('abcd', press('cat4abcd')) + self.assertEqual('abcdabcd', press('2cat4abcd')) + self.assertEqual('55555', press('5return5')) + + km.add(lambda _: Ellipsis, '.') + self.assertEqual('x', press('returnx')) + self.assertEqual('abcd', press('cat4abcd')) + self.assertEqual(Ellipsis, press('2cat4abcd')) + self.assertEqual(Ellipsis, press('5return5')) + self.assertEqual(Ellipsis, press('f')) + self.assertEqual(Ellipsis, press('ß')) + self.assertEqual(Ellipsis, press('ア')) + self.assertEqual(Ellipsis, press('9')) if __name__ == '__main__': main() -- cgit 1.4.1-2-gfad0 From 95e3dfb1d6cdbb4b3501236d44be6ba1afe47462 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 10 Feb 2010 02:16:48 +0100 Subject: keyparser: reworked --- test/tc_newkeys.py | 112 +++++++++++++++++++++++++++++------------------------ 1 file changed, 61 insertions(+), 51 deletions(-) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 39d6b23a..2db9b160 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -47,8 +47,9 @@ class CommandArgs(object): self.fm = fm self.wdg = widget self.keybuffer = keybuffer - self.n = keybuffer.quant1 - self.direction = keybuffer.direction + self.n = keybuffer.quant + self.direction = keybuffer.directions and keybuffer.directions[0] or None + self.directions = keybuffer.directions self.keys = str(keybuffer) self.moo = keybuffer.moo @@ -65,32 +66,24 @@ class KeyBuffer(object): assert isinstance(key, int) assert key >= 0 - # evaluate first quantifier - if self.level == 0: - if is_ascii_digit(key) and ANYKEY not in self.tree_pointer: - if self.quant1 is None: - self.quant1 = 0 - self.quant1 = self.quant1 * 10 + key - 48 - else: - self.level = 1 + # evaluate quantifiers + if self.eval_quantifier and self._do_eval_quantifier(key): + return - # evaluate the command and the second quantifier. - # it's possible to jump between them. "x3xj" is equivalent to "xx3j" - if self.level == 1: + # evaluate the command + if self.eval_command: try: self.tree_pointer = self.tree_pointer[key] except TypeError: + print(self.tree_pointer) self.failure = True return None except KeyError: - if is_ascii_digit(key) and ANYKEY not in self.tree_pointer: - if self.quant2 is None: - self.quant2 = 0 - self.quant2 = self.quant2 * 10 + key - 48 - elif DIRKEY in self.tree_pointer: - self.level = 2 - self.command = self.tree_pointer[DIRKEY] - self.tree_pointer = self.direction_keys._tree + if DIRKEY in self.tree_pointer: + self.eval_command = False + self.eval_quantifier = True + self.tree_pointer = self.tree_pointer[DIRKEY] + self.dir_tree_pointer = self.direction_keys._tree elif ANYKEY in self.tree_pointer: self.moo.append(key) self.tree_pointer = self.tree_pointer[ANYKEY] @@ -101,42 +94,58 @@ class KeyBuffer(object): else: self._try_to_finish() + if self.eval_quantifier and self._do_eval_quantifier(key): + return + # evaluate direction keys {j,k,gg,pagedown,...} - if self.level == 2: + if not self.eval_command: try: - self.tree_pointer = self.tree_pointer[key] + self.dir_tree_pointer = self.dir_tree_pointer[key] except KeyError: self.failure = True else: - if not isinstance(self.tree_pointer, dict): - match = self.tree_pointer - self.direction = match.actions['dir'] * self.quant2 - self.done = True + if not isinstance(self.dir_tree_pointer, dict): + match = self.dir_tree_pointer + direction = match.actions['dir'] * self.direction_quant + self.directions.append(direction) + self.direction_quant = None + 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 False + return True def _try_to_finish(self): if not isinstance(self.tree_pointer, dict): - match = self.tree_pointer - self.command = match - if not match.has_direction: - if self.quant2 is not None: - self.direction = self.direction * self.quant2 - self.done = True + self.command = self.tree_pointer + self.done = True def clear(self): self.failure = False self.done = False - self.quant1 = None + self.quant = None self.moo = [] self.quant2 = None self.command = None - self.direction = Direction(down=1) + self.direction_quant = None + self.directions = [] self.all_keys = [] self.tree_pointer = self.keymap._tree - self.direction_tree_pointer = self.direction_keys._tree - self.level = 0 - # level 0 = parsing quantifier 1 - # 1 = parsing command or quantifier 2 - # 2 = parsing direction + 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""" @@ -254,10 +263,10 @@ class binding(object): def n(value): """ return n or value """ - def fnc(n=None): - if n is None: + def fnc(arg=None): + if arg is None or arg.n is None: return value - return n + return arg.n return fnc def nd(arg): @@ -283,6 +292,7 @@ class Test(TestCase): self.assertTrue(keybuffer.done, "parsing keys '"+keys+"' did not complete!") arg = CommandArgs(None, None, keybuffer) + self.assert_(match.function, match.__dict__) return match.function(arg) return press @@ -301,13 +311,10 @@ class Test(TestCase): km = Keymap() directions = Keymap() kb = KeyBuffer(km, directions) - km.add(n(5), 'd') - match = kb.simulate_press('3d') - self.assertEqual(3, match.function(kb.quant1)) - kb.clear() - match = kb.simulate_press('6223d') - self.assertEqual(6223, match.function(kb.quant1)) - kb.clear() + km.add(n(5), 'p') + press = self._mkpress(kb, km) + self.assertEqual(3, press('3p')) + self.assertEqual(6223, press('6223p')) def test_direction(self): km = Keymap() @@ -320,11 +327,14 @@ class Test(TestCase): press = self._mkpress(kb, km) + self.assertEqual( 1, press('dj')) self.assertEqual( 3, press('3ddj')) self.assertEqual( 15, press('3d5j')) self.assertEqual(-15, press('3d5k')) - self.assertEqual( 15, press('3d5d')) + # supporting this kind of key combination would be too confusing: + # self.assertEqual( 15, press('3d5d')) self.assertEqual( 3, press('3dd')) + self.assertEqual( 33, press('33dd')) self.assertEqual( 1, press('dd')) km.add(nd, 'x}') -- cgit 1.4.1-2-gfad0 From 4ba2112b6db4cd556708324473f34f12f16063ec Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 10 Feb 2010 02:30:11 +0100 Subject: keyparser: allow arbitrary number/order of directions --- test/tc_newkeys.py | 100 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 66 insertions(+), 34 deletions(-) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 2db9b160..8f6be3f5 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -71,45 +71,30 @@ class KeyBuffer(object): return # evaluate the command - if self.eval_command: - try: - 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] - self.dir_tree_pointer = self.direction_keys._tree - elif ANYKEY in self.tree_pointer: - self.moo.append(key) - self.tree_pointer = self.tree_pointer[ANYKEY] - self._try_to_finish() - else: - self.failure = True - return None - else: - self._try_to_finish() + 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: - try: - self.dir_tree_pointer = self.dir_tree_pointer[key] - except KeyError: - self.failure = True - else: - if not isinstance(self.dir_tree_pointer, dict): - match = self.dir_tree_pointer - direction = match.actions['dir'] * self.direction_quant - self.directions.append(direction) - self.direction_quant = None - self._try_to_finish() + self._do_eval_direction(key) + + def _do_eval_direction(self, key): + try: + self.dir_tree_pointer = self.dir_tree_pointer[key] + except KeyError: + self.failure = True + else: + if not isinstance(self.dir_tree_pointer, dict): + match = self.dir_tree_pointer + 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: @@ -123,9 +108,32 @@ class KeyBuffer(object): setattr(self, attr, getattr(self, attr) * 10 + key - 48) else: self.eval_quantifier = False - return False + return None return True + def _do_eval_command(self, key): + try: + 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] + self.dir_tree_pointer = self.direction_keys._tree + elif ANYKEY in self.tree_pointer: + self.moo.append(key) + self.tree_pointer = self.tree_pointer[ANYKEY] + self._try_to_finish() + else: + self.failure = True + return None + else: + self._try_to_finish() + def _try_to_finish(self): if not isinstance(self.tree_pointer, dict): self.command = self.tree_pointer @@ -387,4 +395,28 @@ class Test(TestCase): self.assertEqual(Ellipsis, press('ア')) self.assertEqual(Ellipsis, press('9')) + def test_multiple_directions(self): + km = Keymap() + directions = Keymap() + kb = KeyBuffer(km, directions) + directions.add('j', dir=Direction(down=1)) + directions.add('k', dir=Direction(down=-1)) + + def add_dirs(arg): + n = 0 + for dir in arg.directions: + n += dir.down + return n + + km.add(add_dirs, 'x}y}') + km.add(add_dirs, 'four}}}}') + + press = self._mkpress(kb, km) + + self.assertEqual(2, press('xjyj')) + self.assertEqual(0, press('fourjkkj')) + self.assertEqual(2, press('four2j4k2j2j')) + self.assertEqual(10, press('four1j2j3j4j')) + self.assertEqual(10, press('four1j2j3j4jafslkdfjkldj')) + if __name__ == '__main__': main() -- cgit 1.4.1-2-gfad0 From ca909b9a3b638d25092bd3990919f61fb677a502 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 10 Feb 2010 15:33:50 +0100 Subject: keyparser: some additions --- test/tc_newkeys.py | 76 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 60 insertions(+), 16 deletions(-) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 8f6be3f5..1326677f 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -1,17 +1,20 @@ +# coding=utf-8 if __name__ == '__main__': from __init__ import init; init() from unittest import TestCase, main -from pprint import pprint as print +#from pprint import pprint as print from inspect import isfunction, getargspec import inspect -from sys import intern +try: + from sys import intern +except: + pass FUNC = 'func' DIRECTION = 'direction' DIRKEY = 9001 ANYKEY = 9002 QUANTIFIER = 'n' -MATCH = intern('!!') def to_string(i): """convert a ord'd integer to a string""" @@ -40,6 +43,7 @@ class Direction(object): copy.down *= other copy.right *= other return copy + __rmul__ = __mul__ class CommandArgs(object): """The arguments which are passed to a keybinding function""" @@ -84,12 +88,14 @@ class KeyBuffer(object): 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: if not isinstance(self.dir_tree_pointer, dict): match = self.dir_tree_pointer + assert isinstance(match, binding) direction = match.actions['dir'] * self.direction_quant self.directions.append(direction) self.direction_quant = None @@ -113,6 +119,7 @@ class KeyBuffer(object): 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) @@ -123,10 +130,12 @@ class KeyBuffer(object): 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.moo.append(key) self.tree_pointer = self.tree_pointer[ANYKEY] + assert isinstance(self.tree_pointer, (binding, dict)) self._try_to_finish() else: self.failure = True @@ -135,6 +144,7 @@ class KeyBuffer(object): self._try_to_finish() def _try_to_finish(self): + assert isinstance(self.tree_pointer, (binding, dict)) if not isinstance(self.tree_pointer, dict): self.command = self.tree_pointer self.done = True @@ -289,8 +299,8 @@ def nd(arg): dir = arg.direction return n * dir.down -class Test(TestCase): - """The test cases""" +class PressTestCase(TestCase): + """Some useful methods for the actual test""" def _mkpress(self, keybuffer, keymap): def press(keys): keybuffer.clear() @@ -304,7 +314,23 @@ class Test(TestCase): return match.function(arg) return press + def assertPressFails(self, kb, keys): + kb.clear() + kb.simulate_press(keys) + self.assertTrue(kb.failure, "Keypress did not fail as expected") + kb.clear() + + def assertPressIncomplete(self, kb, keys): + kb.clear() + kb.simulate_press(keys) + self.assertFalse(kb.failure, "Keypress failed, expected incomplete") + self.assertFalse(kb.done, "Keypress done which was unexpected") + kb.clear() + +class Test(PressTestCase): + """The test cases""" def test_add(self): + # depends on internals c = Keymap() c.add(lambda *_: 'lolz', 'aa', 'b') self.assert_(c['aa'].actions[FUNC](), 'lolz') @@ -335,6 +361,7 @@ class Test(TestCase): press = self._mkpress(kb, km) + self.assertPressIncomplete(kb, 'd') self.assertEqual( 1, press('dj')) self.assertEqual( 3, press('3ddj')) self.assertEqual( 15, press('3d5j')) @@ -352,17 +379,11 @@ class Test(TestCase): self.assertEqual(1, press('xxxxjsomeinvalitchars')) # these combinations should break: - kb.clear() - self.assertEqual(None, kb.simulate_press('xxxj')) - kb.clear() - self.assertEqual(None, kb.simulate_press('xxj')) - kb.clear() - self.assertEqual(None, kb.simulate_press('xxkldfjalksdjklsfsldkj')) - kb.clear() - self.assertEqual(None, kb.simulate_press('xyj')) - kb.clear() - self.assertEqual(None, kb.simulate_press('x')) #direction missing - kb.clear() + self.assertPressFails(kb, 'xxxj') + self.assertPressFails(kb, 'xxj') + self.assertPressFails(kb, 'xxkldfjalksdjklsfsldkj') + self.assertPressFails(kb, 'xyj') + self.assertPressIncomplete(kb, 'x') # direction missing def test_any_key(self): km = Keymap() @@ -419,4 +440,27 @@ class Test(TestCase): self.assertEqual(10, press('four1j2j3j4j')) self.assertEqual(10, press('four1j2j3j4jafslkdfjkldj')) + def test_corruptions(self): + km = Keymap() + directions = Keymap() + kb = KeyBuffer(km, directions) + press = self._mkpress(kb, km) + directions.add('j', dir=Direction(down=1)) + directions.add('k', dir=Direction(down=-1)) + km.add('xxx', func=lambda _: 1) + + self.assertEqual(1, press('xxx')) + + # corrupt the tree + tup = tuple(km._split('xxx')) + subtree = km.traverse_tree(tup[:-1]) + subtree[tup[-1]] = "Boo" + + self.assertPressFails(kb, 'xxy') + self.assertPressFails(kb, 'xzy') + self.assertPressIncomplete(kb, 'xx') + self.assertPressIncomplete(kb, 'x') + self.assertRaises(AssertionError, kb.simulate_press, 'xxx') + kb.clear() + if __name__ == '__main__': main() -- cgit 1.4.1-2-gfad0 From 2feb26dea6e9e55cf6bc0933baefd9fad99f5507 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 10 Feb 2010 18:27:41 +0100 Subject: keyparser: cleanups --- test/tc_newkeys.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 1326677f..56a3303e 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -55,7 +55,7 @@ class CommandArgs(object): self.direction = keybuffer.directions and keybuffer.directions[0] or None self.directions = keybuffer.directions self.keys = str(keybuffer) - self.moo = keybuffer.moo + self.matches = keybuffer.matches class KeyBuffer(object): """The evaluator and storage for pressed keys""" @@ -133,7 +133,7 @@ class KeyBuffer(object): assert isinstance(self.tree_pointer, (binding, dict)) self.dir_tree_pointer = self.direction_keys._tree elif ANYKEY in self.tree_pointer: - self.moo.append(key) + self.matches.append(key) self.tree_pointer = self.tree_pointer[ANYKEY] assert isinstance(self.tree_pointer, (binding, dict)) self._try_to_finish() @@ -153,8 +153,7 @@ class KeyBuffer(object): self.failure = False self.done = False self.quant = None - self.moo = [] - self.quant2 = None + self.matches = [] self.command = None self.direction_quant = None self.directions = [] @@ -210,8 +209,6 @@ class Keymap(object): yield ord(char) elif isinstance(key, int): yield key - else: - raise TypeError(key) def add_binding(self, *keys, **actions): assert keys @@ -394,7 +391,7 @@ class Test(PressTestCase): def cat(arg): n = arg.n is None and 1 or arg.n - return ''.join(chr(c) for c in arg.moo) * n + return ''.join(chr(c) for c in arg.matches) * n km.add(cat, 'return.') km.add(cat, 'cat4....') -- cgit 1.4.1-2-gfad0 From e5c4477536790b8cd10d5ffd204f226ea4d2f73e Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 11 Feb 2010 00:07:29 +0100 Subject: keyparser: added test for directions as functions, cleanups --- test/tc_newkeys.py | 93 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 27 deletions(-) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 56a3303e..613def5c 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -1,7 +1,6 @@ # coding=utf-8 if __name__ == '__main__': from __init__ import init; init() from unittest import TestCase, main -#from pprint import pprint as print from inspect import isfunction, getargspec import inspect @@ -12,9 +11,9 @@ except: FUNC = 'func' DIRECTION = 'direction' +DIRARG = 'dir' DIRKEY = 9001 ANYKEY = 9002 -QUANTIFIER = 'n' def to_string(i): """convert a ord'd integer to a string""" @@ -56,6 +55,7 @@ class CommandArgs(object): 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""" @@ -87,6 +87,12 @@ class KeyBuffer(object): self._do_eval_direction(key) def _do_eval_direction(self, key): + # swap quant and direction_quant in bindings like '' + 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] @@ -225,12 +231,13 @@ class Keymap(object): tree = self._tree for char in generator: try: - tree = tree[char] + newtree = tree[char] + if not isinstance(newtree, dict): + raise KeyError() except KeyError: - tree[char] = dict() - tree = tree[char] - except TypeError: - raise TypeError("Attempting to override existing entry") + newtree = dict() + tree[char] = newtree + tree = newtree return tree def __getitem__(self, key): @@ -265,6 +272,10 @@ class binding(object): 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 def add_keys(self, keys): assert isinstance(keys, set) @@ -276,25 +287,6 @@ class binding(object): def action(self, key): return self.actions[key] -def n(value): - """ return n or value """ - def fnc(arg=None): - if arg is None or arg.n is None: - return value - return arg.n - return fnc - -def nd(arg): - """ n * direction """ - if arg.n is None: - n = 1 - else: - n = arg.n - if arg.direction is None: - dir = Direction(down=1) - else: - dir = arg.direction - return n * dir.down class PressTestCase(TestCase): """Some useful methods for the actual test""" @@ -342,6 +334,13 @@ class Test(PressTestCase): km = Keymap() directions = Keymap() kb = KeyBuffer(km, directions) + def n(value): + """return n or value""" + def fnc(arg=None): + if arg is None or arg.n is None: + return value + return arg.n + return fnc km.add(n(5), 'p') press = self._mkpress(kb, km) self.assertEqual(3, press('3p')) @@ -353,6 +352,12 @@ class Test(PressTestCase): kb = KeyBuffer(km, directions) directions.add('j', dir=Direction(down=1)) directions.add('k', dir=Direction(down=-1)) + def nd(arg): + """ n * direction """ + n = arg.n is None and 1 or arg.n + dir = arg.direction is None and Direction(down=1) \ + or arg.direction + return n * dir.down km.add(nd, 'd}') km.add('dd', func=nd, with_direction=False) @@ -389,12 +394,15 @@ class Test(PressTestCase): directions.add('j', dir=Direction(down=1)) directions.add('k', dir=Direction(down=-1)) + directions.add('g.', dir=Direction(down=-1)) + def cat(arg): n = arg.n is None and 1 or arg.n return ''.join(chr(c) for c in arg.matches) * n km.add(cat, 'return.') km.add(cat, 'cat4....') + km.add(cat, 'foo}.') press = self._mkpress(kb, km) @@ -403,12 +411,15 @@ class Test(PressTestCase): self.assertEqual('abcdabcd', press('2cat4abcd')) self.assertEqual('55555', press('5return5')) + self.assertEqual('x', press('foojx')) + self.assertPressFails(kb, 'fooggx') # ANYKEY forbidden in DIRECTION + km.add(lambda _: Ellipsis, '.') self.assertEqual('x', press('returnx')) self.assertEqual('abcd', press('cat4abcd')) self.assertEqual(Ellipsis, press('2cat4abcd')) self.assertEqual(Ellipsis, press('5return5')) - self.assertEqual(Ellipsis, press('f')) + self.assertEqual(Ellipsis, press('g')) self.assertEqual(Ellipsis, press('ß')) self.assertEqual(Ellipsis, press('ア')) self.assertEqual(Ellipsis, press('9')) @@ -460,4 +471,32 @@ class Test(PressTestCase): self.assertRaises(AssertionError, kb.simulate_press, 'xxx') kb.clear() + def test_directions_as_functions(self): + km = Keymap() + directions = Keymap() + kb = KeyBuffer(km, directions) + press = self._mkpress(kb, km) + + def move(arg): + return arg.direction.down + + directions.add('j', dir=Direction(down=1)) + directions.add('k', dir=Direction(down=-1)) + km.add('}', func=move) + + self.assertEqual(1, press('j')) + self.assertEqual(-1, press('k')) + + km.add('k', func=lambda _: 'love') + + self.assertEqual(1, press('j')) + self.assertEqual('love', press('k')) + + self.assertEqual(40, press('40j')) + + km.add('}}..', func=move) + + self.assertEqual(40, press('40jkhl')) + + if __name__ == '__main__': main() -- cgit 1.4.1-2-gfad0 From cfe081ec178da88d6adb7edc651a05dd83810f70 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 11 Feb 2010 03:04:02 +0100 Subject: keyparser: proper parsing of in keybindings --- test/tc_newkeys.py | 169 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 118 insertions(+), 51 deletions(-) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 613def5c..c8b560ee 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -4,6 +4,8 @@ from unittest import TestCase, main from inspect import isfunction, getargspec import inspect +import sys +from string import ascii_lowercase try: from sys import intern except: @@ -101,7 +103,7 @@ class KeyBuffer(object): else: if not isinstance(self.dir_tree_pointer, dict): match = self.dir_tree_pointer - assert isinstance(match, binding) + assert isinstance(match, Binding) direction = match.actions['dir'] * self.direction_quant self.directions.append(direction) self.direction_quant = None @@ -136,12 +138,12 @@ class KeyBuffer(object): self.eval_command = False self.eval_quantifier = True self.tree_pointer = self.tree_pointer[DIRKEY] - assert isinstance(self.tree_pointer, (binding, dict)) + 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)) + assert isinstance(self.tree_pointer, (Binding, dict)) self._try_to_finish() else: self.failure = True @@ -150,7 +152,7 @@ class KeyBuffer(object): self._try_to_finish() def _try_to_finish(self): - assert isinstance(self.tree_pointer, (binding, dict)) + assert isinstance(self.tree_pointer, (Binding, dict)) if not isinstance(self.tree_pointer, dict): self.command = self.tree_pointer self.done = True @@ -182,7 +184,54 @@ class KeyBuffer(object): if self.failure: break -class Keymap(object): +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): + 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 KeyMap(object): """Contains a tree with all the keybindings""" def __init__(self): self._tree = dict() @@ -200,29 +249,13 @@ class Keymap(object): return func return decorator_function - def _split(self, key): - assert isinstance(key, (tuple, int, str)) - if isinstance(key, tuple): - for char in key: - yield char - elif isinstance(key, str): - for char in key: - if char == '.': - yield ANYKEY - elif char == '}': - yield DIRKEY - else: - yield ord(char) - elif isinstance(key, int): - yield key - def add_binding(self, *keys, **actions): assert keys - bind = binding(keys, actions) + bind = Binding(keys, actions) for key in keys: assert key - chars = tuple(self._split(key)) + chars = tuple(translate_keys(key)) tree = self.traverse_tree(chars[:-1]) if isinstance(tree, dict): tree[chars[-1]] = bind @@ -242,7 +275,7 @@ class Keymap(object): def __getitem__(self, key): tree = self._tree - for char in self._split(key): + for char in translate_keys(key): try: tree = tree[char] except TypeError: @@ -254,7 +287,7 @@ class Keymap(object): except KeyError: raise KeyError(str(char) + " not in tree " + str(tree)) -class binding(object): +class Binding(object): """The keybinding object""" def __init__(self, keys, actions): assert hasattr(keys, '__iter__') @@ -318,9 +351,41 @@ class PressTestCase(TestCase): class Test(PressTestCase): """The test cases""" + def test_translate_keys(self): + def test(string, *args): + if not args: + args = (string, ) + self.assertEqual(ordtuple(*args), tuple(translate_keys(string))) + + def ordtuple(*args): + lst = [] + for arg in args: + if isinstance(arg, str): + lst.extend(ord(c) for c in arg) + else: + lst.append(arg) + return tuple(lst) + + test('k') + test('kj') + test('k', 'k', DIRKEY) + test('kz', 'k', ANYKEY, 'z', ANYKEY) + test('kz', 'k', ANYKEY, 'z', DIRKEY) + test('', "\n") + test('', "\t\t\n") + test('<') + test('>') + test('', 1) + test('k') + test('k') + test('k') + test('knz>') + test('>nz>') + def test_add(self): # depends on internals - c = Keymap() + c = KeyMap() c.add(lambda *_: 'lolz', 'aa', 'b') self.assert_(c['aa'].actions[FUNC](), 'lolz') @c.add('a', 'c') @@ -331,8 +396,8 @@ class Test(PressTestCase): self.assert_(c['a'].actions[FUNC](), 5) def test_quantifier(self): - km = Keymap() - directions = Keymap() + km = KeyMap() + directions = KeyMap() kb = KeyBuffer(km, directions) def n(value): """return n or value""" @@ -343,12 +408,13 @@ class Test(PressTestCase): return fnc km.add(n(5), 'p') press = self._mkpress(kb, km) + self.assertEqual(5, press('p')) self.assertEqual(3, press('3p')) self.assertEqual(6223, press('6223p')) def test_direction(self): - km = Keymap() - directions = Keymap() + km = KeyMap() + directions = KeyMap() kb = KeyBuffer(km, directions) directions.add('j', dir=Direction(down=1)) directions.add('k', dir=Direction(down=-1)) @@ -358,7 +424,7 @@ class Test(PressTestCase): dir = arg.direction is None and Direction(down=1) \ or arg.direction return n * dir.down - km.add(nd, 'd}') + km.add(nd, 'd') km.add('dd', func=nd, with_direction=False) press = self._mkpress(kb, km) @@ -374,7 +440,7 @@ class Test(PressTestCase): self.assertEqual( 33, press('33dd')) self.assertEqual( 1, press('dd')) - km.add(nd, 'x}') + km.add(nd, 'x') km.add('xxxx', func=nd, with_direction=False) self.assertEqual(1, press('xxxxj')) @@ -388,21 +454,21 @@ class Test(PressTestCase): self.assertPressIncomplete(kb, 'x') # direction missing def test_any_key(self): - km = Keymap() - directions = Keymap() + km = KeyMap() + directions = KeyMap() kb = KeyBuffer(km, directions) directions.add('j', dir=Direction(down=1)) directions.add('k', dir=Direction(down=-1)) - directions.add('g.', dir=Direction(down=-1)) + directions.add('g', dir=Direction(down=-1)) def cat(arg): n = arg.n is None and 1 or arg.n return ''.join(chr(c) for c in arg.matches) * n - km.add(cat, 'return.') - km.add(cat, 'cat4....') - km.add(cat, 'foo}.') + km.add(cat, 'return') + km.add(cat, 'cat4') + km.add(cat, 'foo') press = self._mkpress(kb, km) @@ -414,7 +480,7 @@ class Test(PressTestCase): self.assertEqual('x', press('foojx')) self.assertPressFails(kb, 'fooggx') # ANYKEY forbidden in DIRECTION - km.add(lambda _: Ellipsis, '.') + km.add(lambda _: Ellipsis, '') self.assertEqual('x', press('returnx')) self.assertEqual('abcd', press('cat4abcd')) self.assertEqual(Ellipsis, press('2cat4abcd')) @@ -425,8 +491,8 @@ class Test(PressTestCase): self.assertEqual(Ellipsis, press('9')) def test_multiple_directions(self): - km = Keymap() - directions = Keymap() + km = KeyMap() + directions = KeyMap() kb = KeyBuffer(km, directions) directions.add('j', dir=Direction(down=1)) directions.add('k', dir=Direction(down=-1)) @@ -437,8 +503,8 @@ class Test(PressTestCase): n += dir.down return n - km.add(add_dirs, 'x}y}') - km.add(add_dirs, 'four}}}}') + km.add(add_dirs, 'xy') + km.add(add_dirs, 'four') press = self._mkpress(kb, km) @@ -449,8 +515,8 @@ class Test(PressTestCase): self.assertEqual(10, press('four1j2j3j4jafslkdfjkldj')) def test_corruptions(self): - km = Keymap() - directions = Keymap() + km = KeyMap() + directions = KeyMap() kb = KeyBuffer(km, directions) press = self._mkpress(kb, km) directions.add('j', dir=Direction(down=1)) @@ -460,7 +526,7 @@ class Test(PressTestCase): self.assertEqual(1, press('xxx')) # corrupt the tree - tup = tuple(km._split('xxx')) + tup = tuple(translate_keys('xxx')) subtree = km.traverse_tree(tup[:-1]) subtree[tup[-1]] = "Boo" @@ -468,12 +534,13 @@ class Test(PressTestCase): self.assertPressFails(kb, 'xzy') self.assertPressIncomplete(kb, 'xx') self.assertPressIncomplete(kb, 'x') - self.assertRaises(AssertionError, kb.simulate_press, 'xxx') + if not sys.flags.optimize: + self.assertRaises(AssertionError, kb.simulate_press, 'xxx') kb.clear() def test_directions_as_functions(self): - km = Keymap() - directions = Keymap() + km = KeyMap() + directions = KeyMap() kb = KeyBuffer(km, directions) press = self._mkpress(kb, km) @@ -482,7 +549,7 @@ class Test(PressTestCase): directions.add('j', dir=Direction(down=1)) directions.add('k', dir=Direction(down=-1)) - km.add('}', func=move) + km.add('', func=move) self.assertEqual(1, press('j')) self.assertEqual(-1, press('k')) @@ -494,7 +561,7 @@ class Test(PressTestCase): self.assertEqual(40, press('40j')) - km.add('}}..', func=move) + km.add('', func=move) self.assertEqual(40, press('40jkhl')) -- cgit 1.4.1-2-gfad0 From 0975223399cb7559659f3c04f4f46defe70865cc Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 11 Feb 2010 20:27:49 +0100 Subject: keyparser: added seperate Tree class --- test/tc_newkeys.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index c8b560ee..5c40e260 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -231,6 +231,54 @@ def translate_keys(obj): for c in bracket_content: yield ord(c) +Nothing = type('nothing', (object, ), {}) + +class Tree(object): + def __init__(self): + self._tree = dict() + + def plow(self, iterable, append=Nothing): + """ + Move along a path, creating nonexistant subtrees + The additional argument allows you to define + one element which will be appended at the end + """ + tree = self._tree + last_tree = tree + char = Nothing + 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 append is not Nothing: + if char is not Nothing: + last_tree[char] = append + else: + self._tree = append + return tree + + def traverse(self, iterable): + """Move along a path, raising exceptions when failed""" + tree = self._tree + for char in iterable: + try: + tree = tree[char] + except TypeError: + raise KeyError("trying to enter leaf") + except KeyError: + raise KeyError(str(char) + " not in tree " + str(tree)) + try: + return tree + except KeyError: + raise KeyError(str(char) + " not in tree " + str(tree)) + + class KeyMap(object): """Contains a tree with all the keybindings""" def __init__(self): @@ -383,6 +431,11 @@ class Test(PressTestCase): test('knz>') test('>nz>') + def test_tree(self): + t = Tree() + subtree = t.plow('abcd', "Yes") + self.assertEqual("Yes", t.traverse('abcd')) + def test_add(self): # depends on internals c = KeyMap() -- cgit 1.4.1-2-gfad0 From d6429e27ffb57df0240572588704969afd492b3c Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 12 Feb 2010 00:15:00 +0100 Subject: keyparser: turned KeyMap into a tree --- test/tc_newkeys.py | 135 +++++++++++++++++++++++------------------------------ 1 file changed, 59 insertions(+), 76 deletions(-) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 5c40e260..d5c4e18f 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -231,21 +231,37 @@ def translate_keys(obj): for c in bracket_content: yield ord(c) -Nothing = type('nothing', (object, ), {}) - class Tree(object): - def __init__(self): - self._tree = dict() - - def plow(self, iterable, append=Nothing): - """ - Move along a path, creating nonexistant subtrees - The additional argument allows you to define - one element which will be appended at the end - """ + def __init__(self, dictionary=None, parent=None, key=None): + assert dictionary is None or isinstance(dictionary, dict) + if dictionary is None: + self._tree = dict() + else: + self._tree = dictionary + self.key = key + self.parent = parent + + def set(self, keys, value, force=True): + """Sets the element at the end of the path to .""" + 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 = tree - char = Nothing + last_tree = None + char = None for char in iterable: try: newtree = tree[char] @@ -256,34 +272,32 @@ class Tree(object): tree[char] = newtree last_tree = tree tree = newtree - if append is not Nothing: - if char is not Nothing: - last_tree[char] = append - else: - self._tree = append - return tree + if isinstance(tree, dict): + return Tree(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)) - try: + if isinstance(tree, dict): + return Tree(tree, parent=last_tree, key=char) + else: return tree - except KeyError: - raise KeyError(str(char) + " not in tree " + str(tree)) -class KeyMap(object): +class KeyMap(Tree): """Contains a tree with all the keybindings""" - def __init__(self): - self._tree = dict() - def add(self, *args, **keywords): if keywords: return self.add_binding(*args, **keywords) @@ -300,47 +314,17 @@ class KeyMap(object): def add_binding(self, *keys, **actions): assert keys bind = Binding(keys, actions) - for key in keys: - assert key - chars = tuple(translate_keys(key)) - tree = self.traverse_tree(chars[:-1]) - if isinstance(tree, dict): - tree[chars[-1]] = bind - - def traverse_tree(self, generator): - tree = self._tree - for char in generator: - try: - newtree = tree[char] - if not isinstance(newtree, dict): - raise KeyError() - except KeyError: - newtree = dict() - tree[char] = newtree - tree = newtree - return tree + self.set(translate_keys(key), bind) def __getitem__(self, key): - tree = self._tree - for char in translate_keys(key): - try: - tree = tree[char] - except TypeError: - raise KeyError("trying to enter leaf") - except KeyError: - raise KeyError(str(char) + " not in tree " + str(tree)) - try: - return tree - except KeyError: - raise KeyError(str(char) + " not in tree " + str(tree)) + 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.keys = set(keys) self.actions = actions try: self.function = self.actions[FUNC] @@ -358,16 +342,6 @@ class Binding(object): except KeyError: self.direction = None - def add_keys(self, keys): - assert isinstance(keys, set) - self.keys |= keys - - def has(self, action): - return action in self.actions - - def action(self, key): - return self.actions[key] - class PressTestCase(TestCase): """Some useful methods for the actual test""" @@ -433,20 +407,29 @@ class Test(PressTestCase): def test_tree(self): t = Tree() - subtree = t.plow('abcd', "Yes") + t.set('abcd', "Yes") + self.assertEqual("Yes", t.traverse('abcd')) + self.assertRaises(KeyError, t.traverse, 'abcde') + self.assertRaises(KeyError, t.traverse, 'xyz') + self.assert_(isinstance(t.traverse('abc'), Tree)) + + t2 = Tree() + self.assertRaises(KeyError, t2.set, 'axy', "Lol", force=False) + subtree = t2.set('axy', "Lol") self.assertEqual("Yes", t.traverse('abcd')) + self.assertRaises(KeyError, t2.traverse, 'abcd') + self.assertEqual("Lol", t2.traverse('axy')) def test_add(self): - # depends on internals c = KeyMap() c.add(lambda *_: 'lolz', 'aa', 'b') - self.assert_(c['aa'].actions[FUNC](), 'lolz') + self.assert_(c['aa'].function(), 'lolz') @c.add('a', 'c') def test(): return 5 - self.assert_(c['b'].actions[FUNC](), 'lolz') - self.assert_(c['c'].actions[FUNC](), 5) - self.assert_(c['a'].actions[FUNC](), 5) + self.assert_(c['b'].function(), 'lolz') + self.assert_(c['c'].function(), 5) + self.assert_(c['a'].function(), 5) def test_quantifier(self): km = KeyMap() @@ -580,8 +563,8 @@ class Test(PressTestCase): # corrupt the tree tup = tuple(translate_keys('xxx')) - subtree = km.traverse_tree(tup[:-1]) - subtree[tup[-1]] = "Boo" + x = ord('x') + km._tree[x][x][x] = "Boo" self.assertPressFails(kb, 'xxy') self.assertPressFails(kb, 'xzy') -- cgit 1.4.1-2-gfad0 From 705eabe249d07dd569ea4c5b6af134a718a65db0 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 12 Feb 2010 20:33:01 +0100 Subject: keyparser: minor changes --- test/tc_newkeys.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index d5c4e18f..d29450d9 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -273,7 +273,7 @@ class Tree(object): last_tree = tree tree = newtree if isinstance(tree, dict): - return Tree(tree, parent=last_tree, key=char) + return type(self)(tree, parent=last_tree, key=char) else: return tree @@ -291,7 +291,7 @@ class Tree(object): except KeyError: raise KeyError(str(char) + " not in tree " + str(tree)) if isinstance(tree, dict): - return Tree(tree, parent=last_tree, key=char) + return type(self)(tree, parent=last_tree, key=char) else: return tree @@ -342,7 +342,6 @@ class Binding(object): except KeyError: self.direction = None - class PressTestCase(TestCase): """Some useful methods for the actual test""" def _mkpress(self, keybuffer, keymap): @@ -398,6 +397,9 @@ class Test(PressTestCase): test('<') test('>') test('', 1) + test('', 2) + for i in range(1, 26): + test('', i) test('k') test('k') -- cgit 1.4.1-2-gfad0 From c70c915b5ec5a03f0f5b8641322d5c60a1235df5 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 13 Feb 2010 16:52:54 +0100 Subject: keyparser: added tree.copy() for deep copies --- test/tc_newkeys.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index d29450d9..7bdbccad 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -241,6 +241,21 @@ class Tree(object): 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 set(self, keys, value, force=True): """Sets the element at the end of the path to .""" if not isinstance(keys, (list, tuple)): @@ -603,5 +618,15 @@ class Test(PressTestCase): self.assertEqual(40, press('40jkhl')) + def test_tree_deep_copy(self): + t = Tree() + s = t.plow('abc') + s['d'] = "X" + u = t.copy() + self.assertEqual(t._tree, u._tree) + s = t.traverse('ab') + s['c'] = 'Y' + self.assertNotEqual(t._tree, u._tree) + if __name__ == '__main__': main() -- cgit 1.4.1-2-gfad0 From 933463fd93425228e2c1e0439fde75b8167cdbc9 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 17 Feb 2010 22:44:28 +0100 Subject: keyparser: added test case for tree emrge --- test/tc_newkeys.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 7bdbccad..e8a8dbee 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -437,6 +437,42 @@ class Test(PressTestCase): self.assertRaises(KeyError, t2.traverse, 'abcd') self.assertEqual("Lol", t2.traverse('axy')) + def test_merge_trees(self): + t = Tree() + t.set('aaaX', 1) + t.set('aaaY', 2) + t.set('aaaZ', 3) + t.set('bbbA', 11) + t.set('bbbB', 12) + t.set('bbbC', 13) + t.set('bbbD', 14) + t.set('bP', 21) + t.set('bQ', 22) + + u = Tree() + u.set('aaaX', 0) + u.set('bbbC', 'Yes') + u.set('bbbD', 14) + u.set('bbbE', 15) + u.set('bbbF', 16) + u.set('bQ', 22) + u.set('bR', 23) + + v = t.merge(u) + + self.assertEqual(0, t['aaaX']) + self.assertEqual(1, t['aaaY']) + self.assertEqual(2, t['aaaZ']) + self.assertEqual(11, t['bbbA']) + self.assertEqual('Yes', t['bbbC']) + self.assertEqual(14, t['bbbD']) + self.assertEqual(15, t['bbbE']) + self.assertEqual(16, t['bbbF']) + self.assertRaises(KeyError, t.__getitem__, 'bbbG') + self.assertEqual(21, t['bP']) + self.assertEqual(22, t['bQ']) + self.assertEqual(21, t['bR']) + def test_add(self): c = KeyMap() c.add(lambda *_: 'lolz', 'aa', 'b') -- cgit 1.4.1-2-gfad0 From e975b52f9e83bf2ca3fbb6cf066ccd662518a7df Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 17 Feb 2010 22:56:19 +0100 Subject: keyparser: updated test_tree_deep_copy --- test/tc_newkeys.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index e8a8dbee..10519dbf 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -310,7 +310,6 @@ class Tree(object): else: return tree - class KeyMap(Tree): """Contains a tree with all the keybindings""" def add(self, *args, **keywords): @@ -656,12 +655,12 @@ class Test(PressTestCase): def test_tree_deep_copy(self): t = Tree() - s = t.plow('abc') - s['d'] = "X" + s = t.plow('abcd') + s.replace('X') u = t.copy() self.assertEqual(t._tree, u._tree) - s = t.traverse('ab') - s['c'] = 'Y' + s = t.traverse('abc') + s.replace('Y') self.assertNotEqual(t._tree, u._tree) -- cgit 1.4.1-2-gfad0 From 895e04b312544515549fe953e0264d572837ae55 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 18 Feb 2010 01:05:36 +0100 Subject: keyparser: Remove the restriction that a root can't be a leaf --- test/tc_newkeys.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 10519dbf..df32c14a 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -233,7 +233,6 @@ def translate_keys(obj): class Tree(object): def __init__(self, dictionary=None, parent=None, key=None): - assert dictionary is None or isinstance(dictionary, dict) if dictionary is None: self._tree = dict() else: -- cgit 1.4.1-2-gfad0 From 228f3f571f571b93065210ca5db930beb9731b20 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 18 Feb 2010 01:09:04 +0100 Subject: keyparse: implemented Tree.merge --- test/tc_newkeys.py | 123 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 92 insertions(+), 31 deletions(-) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index df32c14a..cd9430db 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -255,6 +255,31 @@ class Tree(object): 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 .""" if not isinstance(keys, (list, tuple)): @@ -309,6 +334,8 @@ class Tree(object): else: return tree + __getitem__ = traverse + class KeyMap(Tree): """Contains a tree with all the keybindings""" def add(self, *args, **keywords): @@ -436,40 +463,74 @@ class Test(PressTestCase): self.assertEqual("Lol", t2.traverse('axy')) def test_merge_trees(self): - t = Tree() - t.set('aaaX', 1) - t.set('aaaY', 2) - t.set('aaaZ', 3) - t.set('bbbA', 11) - t.set('bbbB', 12) - t.set('bbbC', 13) - t.set('bbbD', 14) - t.set('bP', 21) - t.set('bQ', 22) - - u = Tree() - u.set('aaaX', 0) - u.set('bbbC', 'Yes') - u.set('bbbD', 14) - u.set('bbbE', 15) - u.set('bbbF', 16) - u.set('bQ', 22) - u.set('bR', 23) - + def makeTreeA(): + t = Tree() + t.set('aaaX', 1) + t.set('aaaY', 2) + t.set('aaaZ', 3) + t.set('bbbA', 11) + t.set('bbbB', 12) + t.set('bbbC', 13) + t.set('bbbD', 14) + t.set('bP', 21) + t.set('bQ', 22) + return t + + def makeTreeB(): + u = Tree() + u.set('aaaX', 0) + u.set('bbbC', 'Yes') + u.set('bbbD', 14) + u.set('bbbE', 15) + u.set('bbbF', 16) + u.set('bQ', 22) + u.set('bR', 23) + u.set('ffff', 1337) + return u + + # test 1 + t = Tree('a') + u = Tree('b') + merged = t.merge(u) + self.assertEqual('b', merged._tree) + + # test 2 + t = Tree('a') + u = makeTreeA() + merged = t.merge(u) + self.assertEqual(u._tree, merged._tree) + + # test 3 + t = makeTreeA() + u = makeTreeB() v = t.merge(u) - self.assertEqual(0, t['aaaX']) - self.assertEqual(1, t['aaaY']) - self.assertEqual(2, t['aaaZ']) - self.assertEqual(11, t['bbbA']) - self.assertEqual('Yes', t['bbbC']) - self.assertEqual(14, t['bbbD']) - self.assertEqual(15, t['bbbE']) - self.assertEqual(16, t['bbbF']) + self.assertEqual(0, v['aaaX']) + self.assertEqual(2, v['aaaY']) + self.assertEqual(3, v['aaaZ']) + self.assertEqual(11, v['bbbA']) + self.assertEqual('Yes', v['bbbC']) + self.assertEqual(14, v['bbbD']) + self.assertEqual(15, v['bbbE']) + self.assertEqual(16, v['bbbF']) self.assertRaises(KeyError, t.__getitem__, 'bbbG') - self.assertEqual(21, t['bP']) - self.assertEqual(22, t['bQ']) - self.assertEqual(21, t['bR']) + self.assertEqual(21, v['bP']) + self.assertEqual(22, v['bQ']) + self.assertEqual(23, v['bR']) + self.assertEqual(1337, v['ffff']) + + # merge shouldn't be destructive + self.assertEqual(makeTreeA()._tree, t._tree) + self.assertEqual(makeTreeB()._tree, u._tree) + + v['fff'].replace('Lolz') + self.assertEqual('Lolz', v['fff']) + + v['aaa'].replace('Very bad') + v.plow('qqqqqqq').replace('eww.') + + self.assertEqual(makeTreeA()._tree, t._tree) + self.assertEqual(makeTreeB()._tree, u._tree) def test_add(self): c = KeyMap() -- cgit 1.4.1-2-gfad0 From 3f388ce51c55b242e46824808c858aa28ece5ce2 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 18 Feb 2010 13:06:02 +0100 Subject: keyparser: minor updates --- test/tc_newkeys.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index cd9430db..aca6ba98 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -197,6 +197,12 @@ 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 => (108, 111, 108, 10) + """ assert isinstance(obj, (tuple, int, str)) if isinstance(obj, tuple): for char in obj: @@ -480,7 +486,7 @@ class Test(PressTestCase): u = Tree() u.set('aaaX', 0) u.set('bbbC', 'Yes') - u.set('bbbD', 14) + u.set('bbbD', None) u.set('bbbE', 15) u.set('bbbF', 16) u.set('bQ', 22) @@ -510,7 +516,7 @@ class Test(PressTestCase): self.assertEqual(3, v['aaaZ']) self.assertEqual(11, v['bbbA']) self.assertEqual('Yes', v['bbbC']) - self.assertEqual(14, v['bbbD']) + self.assertEqual(None, v['bbbD']) self.assertEqual(15, v['bbbE']) self.assertEqual(16, v['bbbF']) self.assertRaises(KeyError, t.__getitem__, 'bbbG') -- cgit 1.4.1-2-gfad0 From 9588a0fb1fd45951eb8e640ad6c8bccaf7707586 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 18 Feb 2010 15:20:43 +0100 Subject: keyparser: implemented aliases --- test/tc_newkeys.py | 98 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 87 insertions(+), 11 deletions(-) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index aca6ba98..2b040954 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -14,8 +14,10 @@ except: 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""" @@ -101,9 +103,20 @@ class KeyBuffer(object): except KeyError: self.failure = True else: - if not isinstance(self.dir_tree_pointer, dict): - match = self.dir_tree_pointer - assert isinstance(match, Binding) + 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 @@ -151,11 +164,21 @@ class KeyBuffer(object): else: self._try_to_finish() - def _try_to_finish(self): - assert isinstance(self.tree_pointer, (Binding, dict)) - if not isinstance(self.tree_pointer, dict): - self.command = self.tree_pointer - self.done = True + 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 @@ -177,8 +200,8 @@ class KeyBuffer(object): return "".join(to_string(c) for c in self.all_keys) def simulate_press(self, string): - for char in string: - self.add(ord(char)) + for char in translate_keys(string): + self.add(char) if self.done: return self.command if self.failure: @@ -387,6 +410,12 @@ class Binding(object): 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""" @@ -399,7 +428,8 @@ class PressTestCase(TestCase): self.assertTrue(keybuffer.done, "parsing keys '"+keys+"' did not complete!") arg = CommandArgs(None, None, keybuffer) - self.assert_(match.function, match.__dict__) + self.assert_(match.function, "No function found! " + \ + str(match.__dict__)) return match.function(arg) return press @@ -453,6 +483,52 @@ class Test(PressTestCase): test('knz>') test('>nz>') + def test_alias(self): + def add_dirs(arg): + n = 0 + for dir in arg.directions: + n += dir.down + return n + def return5(_): + return 5 + + directions = KeyMap() + directions.add('j', dir=Direction(down=1)) + directions.add('k', dir=Direction(down=-1)) + directions.add('', alias='j') + + base = KeyMap() + base.add(add_dirs, 'a') + base.add(add_dirs, 'b') + base.add(add_dirs, 'xx') + base.add(return5, 'f') + base.add('yy', alias='y') + base.add('!', alias='!') + + other = KeyMap() + other.add('bb', alias='xx') + other.add(add_dirs, 'c') + other.add('g', alias='f') + + km = base.merge(other) + kb = KeyBuffer(km, directions) + + press = self._mkpress(kb, km) + + self.assertEqual(1, press('aj')) + self.assertEqual(2, press('bjbj')) + self.assertEqual(1, press('cj')) + self.assertEqual(1, press('c')) + + self.assertEqual(5, press('f')) + self.assertEqual(5, press('g')) + + for n in range(1, 50): + self.assertPressIncomplete(kb, 'y' * n) + + for n in range(1, 5): + self.assertPressFails(kb, '!' * n) + def test_tree(self): t = Tree() t.set('abcd', "Yes") -- cgit 1.4.1-2-gfad0 From dfd2ef35060ab1e4a6a3dab91db25e48696114da Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 18 Feb 2010 16:42:51 +0100 Subject: keyparser: moved classes from test/ to ranger/ --- ranger/container/__init__.py | 2 +- ranger/container/commandlist.py | 208 ------------------- ranger/container/keybuffer.py | 71 ------- ranger/container/keymap.py | 323 ++++++++++++++++++++++++++++++ ranger/ext/tree.py | 134 +++++++++++++ test/tc_newkeys.py | 432 ++-------------------------------------- 6 files changed, 475 insertions(+), 695 deletions(-) delete mode 100644 ranger/container/commandlist.py delete mode 100644 ranger/container/keybuffer.py create mode 100644 ranger/container/keymap.py create mode 100644 ranger/ext/tree.py diff --git a/ranger/container/__init__.py b/ranger/container/__init__.py index 51122291..b3fe9aff 100644 --- a/ranger/container/__init__.py +++ b/ranger/container/__init__.py @@ -18,5 +18,5 @@ used to manage stored data """ from ranger.container.history import History from ranger.container.keybuffer import KeyBuffer -from .commandlist import CommandList +from .keymap import KeyMap from .bookmarks import Bookmarks diff --git a/ranger/container/commandlist.py b/ranger/container/commandlist.py deleted file mode 100644 index f50603f2..00000000 --- a/ranger/container/commandlist.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (C) 2009, 2010 Roman Zimbelmann -# -# 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 . - -from ranger.ext.openstruct import OpenStruct - -class CommandArgument(object): - def __init__(self, fm, displayable, keybuffer): - self.fm = fm - self.wdg = displayable - self.keybuffer = keybuffer - self.n = keybuffer.number - self.keys = str(keybuffer) - -def cmdarg(displayable): - return CommandArgument(displayable.fm, \ - displayable, displayable.env.keybuffer) - -class CommandList(object): - """ - CommandLists are dictionary-like objects which give you a command - for a given key combination. CommandLists must be filled before use. - """ - - dummy_object = None - dummies_in_paths = False - paths = {} - commandlist = [] - - def __init__(self): - self.commandlist = [] - self.paths = {} - - def __getitem__(self, key): - """Returns the command with the given key combination""" - if isinstance(key, str): - key = self._str_to_tuple(key) - return self.paths[key] - - def rebuild_paths(self): - """ - Fill the path dictionary with dummie objects. - We need to know when to clear the keybuffer (when a wrong key is pressed) - and when to wait for the rest of the key combination. For "gg" we - will assign "g" to a dummy which tells the program to do the latter - and wait. - """ - if self.dummies_in_paths: - self.remove_dummies() - - for cmd in self.commandlist: - for key in cmd.keys: - for path in self._keypath(key): - if path not in self.paths: - self.paths[path] = self.dummy_object - - self.dummies_in_paths = True - - def _keypath(self, tup): - """split a tuple like (a,b,c,d) into [(a,), (a,b), (a,b,c)]""" - length = len(tup) - - if length == 0: - return () - if length == 1: - return (tup, ) - - current = [] - all = [] - - for i in range(len(tup) - 1): - current.append(tup[i]) - all.append(tuple(current)) - - return all - - def remove_dummies(self): - """ - Remove dummie objects in case you have to rebuild a path dictionary - which already contains dummie objects. - """ - for k in tuple(self.paths.keys()): - if self.paths[k] == self.dummy_object: del self.paths[k] - self.dummies_in_paths = False - - - def _str_to_tuple(self, obj): - """splits a string into a tuple of integers""" - if isinstance(obj, tuple): - return obj - elif isinstance(obj, str): - return tuple(map(ord, obj)) - elif isinstance(obj, int): - return (obj, ) - else: - raise TypeError('need a str, int or tuple for str_to_tuple') - - def bind(self, fnc, *keys): - """create a Command object and assign it to the given key combinations.""" - if len(keys) == 0: return - - keys = tuple(map(self._str_to_tuple, keys)) - - cmd = Command(fnc, keys) - - self.commandlist.append(cmd) - for key in cmd.keys: - self.paths[key] = cmd - - def show(self, *keys, **keywords): - """create a Show object and assign it to the given key combinations.""" - if len(keys) == 0: return - - keys = tuple(map(self._str_to_tuple, keys)) - - obj = Show(keywords, keys) - - self.commandlist.append(obj) - for key in obj.keys: - self.paths[key] = obj - - def alias(self, existing, *new): - """bind the keys to the command of the key""" - existing = self._str_to_tuple(existing) - new = tuple(map(self._str_to_tuple, new)) - - obj = AliasedCommand(_make_getter(self.paths, existing), new) - - self.commandlist.append(obj) - for key in new: - self.paths[key] = obj - - def unbind(self, *keys): - i = len(self.commandlist) - keys = set(map(self._str_to_tuple, keys)) - - while i > 0: - i -= 1 - cmd = self.commandlist[i] - cmd.keys -= keys - if not cmd.keys: - del self.commandlist[i] - - for k in keys: - del self.paths[k] - - def clear(self): - """remove all bindings""" - self.paths.clear() - del self.commandlist[:] - - -class Command(object): - """Command objects store information about a command""" - - keys = [] - - def __init__(self, fnc, keys): - self.keys = set(keys) - self.execute = fnc - - def execute(self, *args): - """Execute the command""" - - def execute_wrap(self, displayable): - self.execute(cmdarg(displayable)) - - -class AliasedCommand(Command): - def __init__(self, getter, keys): - self.getter = getter - self.keys = set(keys) - - def get_execute(self): - return self.getter() - - execute = property(get_execute) - - -class Show(object): - """Show objects do things without clearing the keybuffer""" - - keys = [] - text = '' - - def __init__(self, dictionary, keys): - self.keys = set(keys) - self.show_obj = OpenStruct(dictionary) - - -def _make_getter(paths, key): - def getter(): - try: - return paths[key].execute - except: - return lambda: None - return getter diff --git a/ranger/container/keybuffer.py b/ranger/container/keybuffer.py deleted file mode 100644 index 2992aea2..00000000 --- a/ranger/container/keybuffer.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (C) 2009, 2010 Roman Zimbelmann -# -# 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 . - -def to_string(i): - try: - return chr(i) - except ValueError: - return '?' - -from collections import deque -from curses.ascii import ascii - -ZERO = ord('0') -NINE = ord('9') - -class KeyBuffer(object): - def __init__(self): - self.number = None - self.queue = deque() - self.queue_with_numbers = deque() - - def clear(self): - """Clear the keybuffer and restore the initial state""" - self.number = None - self.queue.clear() - self.queue_with_numbers.clear() - - def append(self, key): - """ - Append a key to the keybuffer, or initial numbers to - the number attribute. - """ - self.queue_with_numbers.append(key) - - if not self.queue and key >= ZERO and key <= NINE: - if self.number is None: - self.number = 0 - try: - self.number = self.number * 10 + int(chr(key)) - except ValueError: - return - else: - self.queue.append(key) - - def tuple_with_numbers(self): - """Get a tuple of ascii codes.""" - return tuple(self.queue_with_numbers) - - def tuple_without_numbers(self): - """ - Get a tuple of ascii codes. - If the keybuffer starts with numbers, those will - be left out. To access them, use keybuffer.number - """ - return tuple(self.queue) - - def __str__(self): - """returns a concatenation of all characters""" - return "".join( map( to_string, self.queue_with_numbers ) ) diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py new file mode 100644 index 00000000..c2aa344f --- /dev/null +++ b/ranger/container/keymap.py @@ -0,0 +1,323 @@ +# Copyright (c) 2009, 2010 hut +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +from string import ascii_lowercase +from inspect import isfunction, getargspec +from ranger.ext.tree import Tree + +MAX_ALIAS_RECURSION = 20 +DIRKEY = 9001 +ANYKEY = 9002 +FUNC = 'func' +DIRECTION = 'direction' +DIRARG = 'dir' +ALIASARG = 'alias' + +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__ + +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): + 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 + + @staticmethod + def from_widget(self, widget): + return CommandArgs(displayable.fm, \ + displayable, displayable.env.keybuffer) + +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 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 '' + 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 => (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) diff --git a/ranger/ext/tree.py b/ranger/ext/tree.py new file mode 100644 index 00000000..d7b08cd7 --- /dev/null +++ b/ranger/ext/tree.py @@ -0,0 +1,134 @@ +# Copyright (c) 2009, 2010 hut +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +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 .""" + 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 unset(self, iterable): + chars = list(iterable) + first = True + + while chars: + if first or isinstance(subtree, Tree) and subtree.empty(): + top = chars.pop() + subtree = self.traverse(chars) + del subtree._tree[top] + else: + break + first = False + + def empty(self): + return len(self._tree) == 0 + + 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 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 '' - 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 => (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 .""" - 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(): -- cgit 1.4.1-2-gfad0 From 91e5b9437e96b067860523c93b7c47998f184161 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 18 Feb 2010 17:39:06 +0100 Subject: working on the implementation of the new key parser --- ranger/api/keys.py | 1 + ranger/container/__init__.py | 3 +-- ranger/container/environment.py | 4 ++-- ranger/container/keymap.py | 10 ++++++---- ranger/defaults/keys.py | 28 ++++++++++++++++++++++++++++ ranger/gui/ui.py | 34 +++++++++++++++++----------------- ranger/gui/widgets/console.py | 5 ++--- ranger/gui/widgets/pager.py | 11 +++-------- ranger/gui/widgets/taskview.py | 4 +--- 9 files changed, 61 insertions(+), 39 deletions(-) diff --git a/ranger/api/keys.py b/ranger/api/keys.py index 4ac2e18e..a08c57b3 100644 --- a/ranger/api/keys.py +++ b/ranger/api/keys.py @@ -21,6 +21,7 @@ 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 +from ranger.container.keymap import KeyMap, Direction def make_abbreviations(command_list): def bind(*args, **keywords): diff --git a/ranger/container/__init__.py b/ranger/container/__init__.py index b3fe9aff..4c8f08ba 100644 --- a/ranger/container/__init__.py +++ b/ranger/container/__init__.py @@ -17,6 +17,5 @@ used to manage stored data """ from ranger.container.history import History -from ranger.container.keybuffer import KeyBuffer -from .keymap import KeyMap +from .keymap import KeyMap, KeyBuffer from .bookmarks import Bookmarks diff --git a/ranger/container/environment.py b/ranger/container/environment.py index be1d96c5..b08b357f 100644 --- a/ranger/container/environment.py +++ b/ranger/container/environment.py @@ -42,7 +42,7 @@ class Environment(SettingsAware): self.path = abspath(expanduser(path)) self.pathway = () self.directories = {} - self.keybuffer = KeyBuffer() + self.keybuffer = KeyBuffer(None, None) self.copy = set() self.history = History(self.settings.max_history_size) @@ -56,7 +56,7 @@ class Environment(SettingsAware): if key == curses.KEY_RESIZE: self.keybuffer.clear() - self.keybuffer.append(key) + self.keybuffer.add(key) def key_clear(self): """Clear the keybuffer""" diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index c2aa344f..e1ff06da 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -67,9 +67,9 @@ class CommandArgs(object): self.binding = keybuffer.command @staticmethod - def from_widget(self, widget): - return CommandArgs(displayable.fm, \ - displayable, displayable.env.keybuffer) + def from_widget(widget): + return CommandArgs(widget.fm, \ + widget, widget.env.keybuffer) class KeyMap(Tree): """Contains a tree with all the keybindings""" @@ -126,9 +126,11 @@ class Binding(object): 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 - self.clear() def add(self, key): if self.failure: diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 260ba568..6c24d6d7 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -333,3 +333,31 @@ def _basic_movement(command_list): bind(KEY_UP, wdg.move(relative=-1)) bind(KEY_HOME, wdg.move(absolute=0)) bind(KEY_END, wdg.move(absolute=-1)) + +def get_directions(): + k = KeyMap() + map = k.add + + map('j', dir=Direction(down=1)) + map('k', dir=Direction(down=-1)) + map('h', dir=Direction(right=-1)) + map('l', dir=Direction(right=1)) + return k + +def move(arg): + arg.fm.move_pointer(relative=arg.direction.down) + +def get_ui_keys(): + k = KeyMap() + map = k.add + + map('', func=move) + map('', func=fm.exit()) + return k + +ui_keys = get_ui_keys() +taskview_keys = ui_keys +pager_keys = ui_keys +embedded_pager_keys = ui_keys +console_keys = ui_keys +directions = get_directions() diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index 69acda4b..79552bf2 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -19,14 +19,14 @@ import curses import _curses from .displayable import DisplayableContainer +from ranger.container.keymap import CommandArgs from .mouse_event import MouseEvent -from ranger.container import CommandList class UI(DisplayableContainer): is_set_up = False mousemask = curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION load_mode = False - def __init__(self, commandlist=None, env=None, fm=None): + def __init__(self, keymap=None, env=None, fm=None): import os os.environ['ESCDELAY'] = '25' # don't know a cleaner way @@ -35,12 +35,13 @@ class UI(DisplayableContainer): if fm is not None: self.fm = fm - if commandlist is None: - self.commandlist = CommandList() - self.settings.keys.initialize_commands(self.commandlist) + if keymap is None: + self.keymap = self.settings.keys.ui_keys else: - self.commandlist = commandlist + self.keymap = keymap self.win = curses.initscr() + self.env.keybuffer.assign(self.keymap, self.settings.keys.directions) + self.env.keybuffer.clear() DisplayableContainer.__init__(self, None) @@ -132,15 +133,14 @@ class UI(DisplayableContainer): if DisplayableContainer.press(self, key): return - try: - tup = self.env.keybuffer.tuple_without_numbers() + kbuf = self.env.keybuffer - if tup: - cmd = self.commandlist[tup] - else: - return - except KeyError: - self.env.key_clear() + if kbuf.done: + cmd = kbuf.command + elif kbuf.failure: + kbuf.clear() + return + else: return self.env.cmd = cmd @@ -148,12 +148,12 @@ class UI(DisplayableContainer): if hasattr(cmd, 'show_obj') and hasattr(cmd.show_obj, 'hint'): if hasattr(self, 'hint'): self.hint(cmd.show_obj.hint) - elif hasattr(cmd, 'execute'): + elif cmd.function: try: - cmd.execute_wrap(self) + cmd.function(CommandArgs.from_widget(self)) except Exception as error: self.fm.notify(error) - self.env.key_clear() + kbuf.clear() def get_next_key(self): """Waits for key input and returns the pressed key""" diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 87e5a7b5..7ed00a7e 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -54,10 +54,9 @@ class Console(Widget): allow_close = False def __init__(self, win): - from ranger.container import CommandList, History + from ranger.container import History Widget.__init__(self, win) - self.commandlist = CommandList() - self.settings.keys.initialize_console_commands(self.commandlist) + self.keymap = self.settings.keys.console_keys self.clear() self.histories = [None] * 4 self.histories[DEFAULT_HISTORY] = History() diff --git a/ranger/gui/widgets/pager.py b/ranger/gui/widgets/pager.py index b3e826e0..03a421cf 100644 --- a/ranger/gui/widgets/pager.py +++ b/ranger/gui/widgets/pager.py @@ -18,7 +18,6 @@ The pager displays text and allows you to scroll inside it. """ import re from . import Widget -from ranger.container.commandlist import CommandList from ranger.ext.move import move_between from ranger import log @@ -42,14 +41,10 @@ class Pager(Widget): self.markup = None self.lines = [] - self.commandlist = CommandList() - if embedded: - keyfnc = self.settings.keys.initialize_embedded_pager_commands + self.keymap = self.settings.keys.embedded_pager_keys else: - keyfnc = self.settings.keys.initialize_pager_commands - - keyfnc(self.commandlist) + self.keymap = self.settings.keys.pager_keys def open(self): self.scroll_begin = 0 @@ -166,7 +161,7 @@ class Pager(Widget): try: tup = self.env.keybuffer.tuple_without_numbers() if tup: - cmd = self.commandlist[tup] + cmd = self.keymap[tup] else: return diff --git a/ranger/gui/widgets/taskview.py b/ranger/gui/widgets/taskview.py index 6e86465c..f7937e11 100644 --- a/ranger/gui/widgets/taskview.py +++ b/ranger/gui/widgets/taskview.py @@ -22,7 +22,6 @@ from collections import deque from . import Widget from ranger.ext.accumulator import Accumulator -from ranger.container import CommandList class TaskView(Widget, Accumulator): old_lst = None @@ -31,8 +30,7 @@ class TaskView(Widget, Accumulator): Widget.__init__(self, win) Accumulator.__init__(self) self.scroll_begin = 0 - self.commandlist = CommandList() - self.settings.keys.initialize_taskview_commands(self.commandlist) + self.keymap = self.settings.keys.taskview_keys def draw(self): base_clr = deque() -- cgit 1.4.1-2-gfad0 From 6710b7e18c7a74477375f4425fa4ca1bd2ab0927 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 18 Feb 2010 17:39:19 +0100 Subject: deleted old testcase --- test/tc_commandlist.py | 100 ------------------------------------------------- 1 file changed, 100 deletions(-) delete mode 100644 test/tc_commandlist.py diff --git a/test/tc_commandlist.py b/test/tc_commandlist.py deleted file mode 100644 index 9af2cf05..00000000 --- a/test/tc_commandlist.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright (C) 2009, 2010 Roman Zimbelmann -# -# 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 . - -if __name__ == '__main__': from __init__ import init; init() - -from unittest import TestCase, main -from ranger.container.commandlist import CommandList as CL - -class Test(TestCase): - def assertKeyError(self, obj, key): - self.assertRaises(KeyError, obj.__getitem__, key) - - def test_commandist(self): - cl = CL() - fnc = lambda arg: 1 - fnc2 = lambda arg: 2 - dmy = cl.dummy_object - - cl.bind(fnc, 'aaaa') - cl.rebuild_paths() - - self.assertEqual(dmy, cl['a']) - self.assertEqual(dmy, cl['aa']) - self.assertEqual(dmy, cl['aaa']) - self.assertEqual(fnc, cl['aaaa'].execute) - self.assertKeyError(cl, 'aabb') - self.assertKeyError(cl, 'aaaaa') - - cl.bind(fnc, 'aabb') - cl.rebuild_paths() - - self.assertEqual(dmy, cl['a']) - self.assertEqual(dmy, cl['aa']) - self.assertEqual(dmy, cl['aab']) - self.assertEqual(fnc, cl['aabb'].execute) - self.assertEqual(dmy, cl['aaa']) - self.assertEqual(fnc, cl['aaaa'].execute) - - cl.unbind('aabb') - cl.rebuild_paths() - - self.assertEqual(dmy, cl['a']) - self.assertEqual(dmy, cl['aa']) - self.assertKeyError(cl, 'aabb') - self.assertKeyError(cl, 'aab') - self.assertEqual(dmy, cl['aaa']) - self.assertEqual(fnc, cl['aaaa'].execute) - - # Hints work different now. Since a rework of this system - # is planned anyway, there is no need to fix the test. - # hint_text = 'some tip blablablba' - # cl.hint(hint_text, 'aa') - # cl.rebuild_paths() - - self.assertEqual(dmy, cl['a']) - # self.assertEqual(hint_text, cl['aa'].text) - self.assertEqual(dmy, cl['aaa']) - self.assertEqual(fnc, cl['aaaa'].execute) - - # ------------------------ test aliases - cl.alias('aaaa', 'cc') - cl.rebuild_paths() - - self.assertEqual(dmy, cl['c']) - self.assertEqual(cl['cc'].execute, cl['aaaa'].execute) - - cl.bind(fnc2, 'aaaa') - cl.rebuild_paths() - - self.assertEqual(cl['cc'].execute, cl['aaaa'].execute) - - cl.unbind('cc') - cl.rebuild_paths() - - self.assertEqual(fnc2, cl['aaaa'].execute) - self.assertKeyError(cl, 'cc') - - # ----------------------- test clearing - cl.clear() - self.assertKeyError(cl, 'a') - self.assertKeyError(cl, 'aa') - self.assertKeyError(cl, 'aaa') - self.assertKeyError(cl, 'aaaa') - self.assertKeyError(cl, 'aab') - self.assertKeyError(cl, 'aabb') - - -if __name__ == '__main__': main() -- cgit 1.4.1-2-gfad0 From 13ecffe7ffa5c80cd69d55419f230c97d06ab23e Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 18 Feb 2010 20:47:46 +0100 Subject: integrating keyparser... --- ranger/container/keymap.py | 22 +++++++++++++++---- ranger/defaults/keys.py | 53 +++++++++++++++++++++++++++++++++++++++++----- test/tc_newkeys.py | 12 +++++++++++ 3 files changed, 78 insertions(+), 9 deletions(-) diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index e1ff06da..70faa0d0 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -12,6 +12,7 @@ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +import curses from string import ascii_lowercase from inspect import isfunction, getargspec from ranger.ext.tree import Tree @@ -121,7 +122,7 @@ class Binding(object): except KeyError: self.alias = None else: - self.alias = translate_keys(alias) + self.alias = tuple(translate_keys(alias)) class KeyBuffer(object): """The evaluator and storage for pressed keys""" @@ -173,9 +174,12 @@ class KeyBuffer(object): 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, dict, KeyMap)) + if isinstance(match, KeyMap): + self.dir_tree_pointer = self.dir_tree_pointer._tree match = self.dir_tree_pointer - assert isinstance(match, Binding) + if isinstance(self.dir_tree_pointer, Binding): if 'alias' in match.actions: self.dir_tree_pointer = self.direction_keys.traverse( match.alias) @@ -204,7 +208,7 @@ class KeyBuffer(object): def _do_eval_command(self, key): try: - assert isinstance(self.tree_pointer, dict) + assert isinstance(self.tree_pointer, dict), self.tree_pointer self.tree_pointer = self.tree_pointer[key] except TypeError: print(self.tree_pointer) @@ -278,6 +282,16 @@ key_map = { 'enter': ord("\n"), 'space': ord(" "), 'space': ord(" "), + 'down': curses.KEY_DOWN, + 'up': curses.KEY_UP, + 'left': curses.KEY_LEFT, + 'right': curses.KEY_RIGHT, + 'mouse': curses.KEY_MOUSE, + 'resize': curses.KEY_RESIZE, + 'pagedown': curses.KEY_NPAGE, + 'pageup': curses.KEY_PPAGE, + 'home': curses.KEY_HOME, + 'end': curses.KEY_END, 'tab': ord('\t'), } for char in ascii_lowercase: diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 6c24d6d7..e6d2b0cc 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -338,10 +338,15 @@ def get_directions(): k = KeyMap() map = k.add - map('j', dir=Direction(down=1)) - map('k', dir=Direction(down=-1)) - map('h', dir=Direction(right=-1)) - map('l', dir=Direction(right=1)) + map('', dir=Direction(down=1)) + map('', dir=Direction(down=-1)) + map('', dir=Direction(right=-1)) + map('', dir=Direction(right=1)) + + map('j', alias='') + map('k', alias='') + map('h', alias='') + map('l', alias='') return k def move(arg): @@ -349,12 +354,50 @@ def move(arg): def get_ui_keys(): k = KeyMap() + k.merge(system_keys()) map = k.add map('', func=move) - map('', func=fm.exit()) + map('', 'Q', func=fm.exit()) + + # --------------------------------------------------------- history + map('H', func=fm.history_go(-1)) + map('L', func=fm.history_go(1)) + + # ----------------------------------------------- tagging / marking + map('t', func=fm.tag_toggle()) + map('T', func=fm.tag_remove()) + + map(' ', func=fm.mark(toggle=True)) + map('v', func=fm.mark(all=True, toggle=True)) + map('V', func=fm.mark(all=True, val=False)) + + # ------------------------------------------ file system operations + map('yy', func=fm.copy()) + map('dd', func=fm.cut()) + map('pp', func=fm.paste()) + map('po', func=fm.paste(overwrite=True)) + map('pl', func=fm.paste_symlink()) + map('p', hint='press //p// once again to confirm pasting' \ + ', func=or //l// to create symlinks') + + # ---------------------------------------------------- run programs + map('s', func=fm.execute_command(os.environ['SHELL'])) + map('E', func=fm.edit_file()) + map('term', func=fm.execute_command('x-terminal-emulator', flags='d')) + map('du', func=fm.execute_command('du --max-depth=1 -h | less')) + return k +def system_keys(): + k = KeyMap() + k.map(fm.exit(), 'Q') + k.map(fm.handle_mouse(), '') + k.map(fm.redraw_window(), '') + k.map(fm.resize(), '') + return k + + ui_keys = get_ui_keys() taskview_keys = ui_keys pager_keys = ui_keys diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 8f5422a0..0922029e 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -380,10 +380,22 @@ class Test(PressTestCase): return arg.direction.down directions.add('j', dir=Direction(down=1)) + directions.add('s', alias='j') directions.add('k', dir=Direction(down=-1)) km.add('', func=move) self.assertEqual(1, press('j')) + self.assertEqual(1, press('j')) + self.assertEqual(1, press('j')) + self.assertEqual(1, press('j')) + self.assertEqual(1, press('j')) + self.assertEqual(1, press('s')) + self.assertEqual(1, press('s')) + self.assertEqual(1, press('s')) + self.assertEqual(1, press('s')) + self.assertEqual(1, press('s')) + self.assertEqual(-1, press('k')) + self.assertEqual(-1, press('k')) self.assertEqual(-1, press('k')) km.add('k', func=lambda _: 'love') -- cgit 1.4.1-2-gfad0 From ea3b13663ea3bbf42cd3472750ee7e00e9093ca4 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 18 Feb 2010 21:58:37 +0100 Subject: keyparser: allow passive actions (for hints, show bookmarks) --- ranger/container/keymap.py | 10 +++++++++- test/tc_newkeys.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index 70faa0d0..23f26448 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -18,6 +18,7 @@ from inspect import isfunction, getargspec from ranger.ext.tree import Tree MAX_ALIAS_RECURSION = 20 +PASSIVE_ACTION = 9003 DIRKEY = 9001 ANYKEY = 9002 FUNC = 'func' @@ -207,8 +208,8 @@ class KeyBuffer(object): return True def _do_eval_command(self, key): + assert isinstance(self.tree_pointer, dict), self.tree_pointer try: - assert isinstance(self.tree_pointer, dict), self.tree_pointer self.tree_pointer = self.tree_pointer[key] except TypeError: print(self.tree_pointer) @@ -230,6 +231,11 @@ class KeyBuffer(object): self.failure = True return None 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, rec=MAX_ALIAS_RECURSION): @@ -274,10 +280,12 @@ class KeyBuffer(object): return self.command if self.failure: break + return self.command key_map = { 'dir': DIRKEY, 'any': ANYKEY, + 'psv': PASSIVE_ACTION, 'cr': ord("\n"), 'enter': ord("\n"), 'space': ord(" "), diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 0922029e..697bfdcb 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -38,6 +38,36 @@ class PressTestCase(TestCase): class Test(PressTestCase): """The test cases""" + def test_passive_action(self): + km = KeyMap() + directions = KeyMap() + kb = KeyBuffer(km, directions) + def n(value): + """return n or value""" + def fnc(arg=None): + if arg is None or arg.n is None: + return value + return arg.n + return fnc + + km.add(n(5), 'ppp') + km.add(n(8), 'pp') + km.add(n(2), 'pp') + directions.add('j', dir=Direction(down=1)) + + press = self._mkpress(kb, km) + self.assertEqual(5, press('ppp')) + self.assertEqual(3, press('3ppp')) + + self.assertEqual(2, press('ppj')) + + kb.clear() + match = kb.simulate_press('pp') + args = CommandArgs(0, 0, kb) + self.assert_(match) + self.assert_(match.function) + self.assertEqual(8, match.function(args)) + def test_translate_keys(self): def test(string, *args): if not args: -- cgit 1.4.1-2-gfad0 From 6ae8cb053b78effdeda1d9ba5966086e02befc9a Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 18 Feb 2010 22:11:49 +0100 Subject: keyparser: swapped argument order in KeyMap.add() --- ranger/container/keymap.py | 4 ++-- test/tc_newkeys.py | 42 +++++++++++++++++++++--------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index 23f26448..abc73d4d 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -78,10 +78,10 @@ class KeyMap(Tree): def add(self, *args, **keywords): if keywords: return self.add_binding(*args, **keywords) - firstarg = args[0] + firstarg = args[-1] if isfunction(firstarg): keywords[FUNC] = firstarg - return self.add_binding(*args[1:], **keywords) + return self.add_binding(*args[:-1], **keywords) def decorator_function(func): keywords = {FUNC:func} self.add(*args, **keywords) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 697bfdcb..0bc99de1 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -50,9 +50,9 @@ class Test(PressTestCase): return arg.n return fnc - km.add(n(5), 'ppp') - km.add(n(8), 'pp') - km.add(n(2), 'pp') + km.add('ppp', n(5)) + km.add('pp', n(8)) + km.add('pp', n(2)) directions.add('j', dir=Direction(down=1)) press = self._mkpress(kb, km) @@ -118,16 +118,16 @@ class Test(PressTestCase): directions.add('', alias='j') base = KeyMap() - base.add(add_dirs, 'a') - base.add(add_dirs, 'b') - base.add(add_dirs, 'xx') - base.add(return5, 'f') + base.add('a', add_dirs) + base.add('b', add_dirs) + base.add('xx', add_dirs) + base.add('f', return5) base.add('yy', alias='y') base.add('!', alias='!') other = KeyMap() other.add('bb', alias='xx') - other.add(add_dirs, 'c') + other.add('c', add_dirs) other.add('g', alias='f') km = base.merge(other) @@ -248,7 +248,7 @@ class Test(PressTestCase): def test_add(self): c = KeyMap() - c.add(lambda *_: 'lolz', 'aa', 'b') + c.add('aa', 'b', lambda *_: 'lolz') self.assert_(c['aa'].function(), 'lolz') @c.add('a', 'c') def test(): @@ -268,7 +268,7 @@ class Test(PressTestCase): return value return arg.n return fnc - km.add(n(5), 'p') + km.add('p', n(5)) press = self._mkpress(kb, km) self.assertEqual(5, press('p')) self.assertEqual(3, press('3p')) @@ -286,8 +286,8 @@ class Test(PressTestCase): dir = arg.direction is None and Direction(down=1) \ or arg.direction return n * dir.down - km.add(nd, 'd') - km.add('dd', func=nd, with_direction=False) + km.add('d', nd) + km.add('dd', func=nd) press = self._mkpress(kb, km) @@ -302,8 +302,8 @@ class Test(PressTestCase): self.assertEqual( 33, press('33dd')) self.assertEqual( 1, press('dd')) - km.add(nd, 'x') - km.add('xxxx', func=nd, with_direction=False) + km.add('x', nd) + km.add('xxxx', func=nd) self.assertEqual(1, press('xxxxj')) self.assertEqual(1, press('xxxxjsomeinvalitchars')) @@ -328,9 +328,9 @@ class Test(PressTestCase): n = arg.n is None and 1 or arg.n return ''.join(chr(c) for c in arg.matches) * n - km.add(cat, 'return') - km.add(cat, 'cat4') - km.add(cat, 'foo') + km.add('return', cat) + km.add('cat4', cat) + km.add('foo', cat) press = self._mkpress(kb, km) @@ -342,7 +342,7 @@ class Test(PressTestCase): self.assertEqual('x', press('foojx')) self.assertPressFails(kb, 'fooggx') # ANYKEY forbidden in DIRECTION - km.add(lambda _: Ellipsis, '') + km.add('', lambda _: Ellipsis) self.assertEqual('x', press('returnx')) self.assertEqual('abcd', press('cat4abcd')) self.assertEqual(Ellipsis, press('2cat4abcd')) @@ -365,8 +365,8 @@ class Test(PressTestCase): n += dir.down return n - km.add(add_dirs, 'xy') - km.add(add_dirs, 'four') + km.add('xy', add_dirs) + km.add('four', add_dirs) press = self._mkpress(kb, km) @@ -383,7 +383,7 @@ class Test(PressTestCase): press = self._mkpress(kb, km) directions.add('j', dir=Direction(down=1)) directions.add('k', dir=Direction(down=-1)) - km.add('xxx', func=lambda _: 1) + km.add('xxx', lambda _: 1) self.assertEqual(1, press('xxx')) -- cgit 1.4.1-2-gfad0 From ee4687c32e105a69d49c023e8f3a695b4b407ce2 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 18 Feb 2010 22:41:15 +0100 Subject: keyparser: renamed KeyMap.add to KeyMap.map --- ranger/api/keys.py | 28 --------------- ranger/container/keymap.py | 4 +-- ranger/defaults/keys.py | 44 +++++++++++------------ ranger/gui/ui.py | 10 +++--- test/tc_newkeys.py | 90 +++++++++++++++++++++++----------------------- 5 files changed, 74 insertions(+), 102 deletions(-) diff --git a/ranger/api/keys.py b/ranger/api/keys.py index a08c57b3..86911569 100644 --- a/ranger/api/keys.py +++ b/ranger/api/keys.py @@ -23,34 +23,6 @@ from ranger.gui.widgets import console_mode as cmode from ranger.container.bookmarks import ALLOWED_KEYS as ALLOWED_BOOKMARK_KEYS from ranger.container.keymap import KeyMap, Direction -def make_abbreviations(command_list): - def bind(*args, **keywords): - if keywords: - command_list.show(*args, **keywords) - else: - lastarg = args[-1] - if hasattr(lastarg, '__call__'): - # do the binding - command_list.bind(lastarg, *args[:-1]) - else: - # act as a decorator. eg: - # @bind('a') - # def do_stuff(arg): - # arg.fm.ui.do_stuff() - # - # is equivalent to: - # bind('a', lambda arg: arg.fm.ui.do_stuff()) - return lambda fnc: command_list.bind(fnc, *args) - - def show(*args, **keywords): - command_list.show(*args, **keywords) - - def alias(*args): - command_list.alias(*args) - - return bind, alias - - class Wrapper(object): def __init__(self, firstattr): self.__firstattr__ = firstattr diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index abc73d4d..dae8955a 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -75,7 +75,7 @@ class CommandArgs(object): class KeyMap(Tree): """Contains a tree with all the keybindings""" - def add(self, *args, **keywords): + def map(self, *args, **keywords): if keywords: return self.add_binding(*args, **keywords) firstarg = args[-1] @@ -84,7 +84,7 @@ class KeyMap(Tree): return self.add_binding(*args[:-1], **keywords) def decorator_function(func): keywords = {FUNC:func} - self.add(*args, **keywords) + self.map(*args, **keywords) return func return decorator_function diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index e6d2b0cc..aaa332b9 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -336,7 +336,7 @@ def _basic_movement(command_list): def get_directions(): k = KeyMap() - map = k.add + map = k.map map('', dir=Direction(down=1)) map('', dir=Direction(down=-1)) @@ -355,37 +355,37 @@ def move(arg): def get_ui_keys(): k = KeyMap() k.merge(system_keys()) - map = k.add + map = k.map - map('', func=move) - map('', 'Q', func=fm.exit()) + map('', move) + map('', 'Q', fm.exit()) # --------------------------------------------------------- history - map('H', func=fm.history_go(-1)) - map('L', func=fm.history_go(1)) + map('H', fm.history_go(-1)) + map('L', fm.history_go(1)) # ----------------------------------------------- tagging / marking - map('t', func=fm.tag_toggle()) - map('T', func=fm.tag_remove()) + map('t', fm.tag_toggle()) + map('T', fm.tag_remove()) - map(' ', func=fm.mark(toggle=True)) - map('v', func=fm.mark(all=True, toggle=True)) - map('V', func=fm.mark(all=True, val=False)) + map(' ', fm.mark(toggle=True)) + map('v', fm.mark(all=True, toggle=True)) + map('V', fm.mark(all=True, val=False)) # ------------------------------------------ file system operations - map('yy', func=fm.copy()) - map('dd', func=fm.cut()) - map('pp', func=fm.paste()) - map('po', func=fm.paste(overwrite=True)) - map('pl', func=fm.paste_symlink()) - map('p', hint='press //p// once again to confirm pasting' \ - ', func=or //l// to create symlinks') + map('yy', fm.copy()) + map('dd', fm.cut()) + map('pp', fm.paste()) + map('po', fm.paste(overwrite=True)) + map('pl', fm.paste_symlink()) + map('p', fm.notify('press //p// once again to confirm pasting' \ + ', or //l// to create symlinks')) # ---------------------------------------------------- run programs - map('s', func=fm.execute_command(os.environ['SHELL'])) - map('E', func=fm.edit_file()) - map('term', func=fm.execute_command('x-terminal-emulator', flags='d')) - map('du', func=fm.execute_command('du --max-depth=1 -h | less')) + map('s', fm.execute_command(os.environ['SHELL'])) + map('E', fm.edit_file()) + map('term', fm.execute_command('x-terminal-emulator', flags='d')) + map('du', fm.execute_command('du --max-depth=1 -h | less')) return k diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index 79552bf2..eb7c26fa 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -134,13 +134,12 @@ class UI(DisplayableContainer): return kbuf = self.env.keybuffer + cmd = kbuf.command - if kbuf.done: - cmd = kbuf.command - elif kbuf.failure: + if kbuf.failure: kbuf.clear() return - else: + elif not cmd: return self.env.cmd = cmd @@ -153,7 +152,8 @@ class UI(DisplayableContainer): cmd.function(CommandArgs.from_widget(self)) except Exception as error: self.fm.notify(error) - kbuf.clear() + if kbuf.done: + kbuf.clear() def get_next_key(self): """Waits for key input and returns the pressed key""" diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 0bc99de1..45ac4e33 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -50,10 +50,10 @@ class Test(PressTestCase): return arg.n return fnc - km.add('ppp', n(5)) - km.add('pp', n(8)) - km.add('pp', n(2)) - directions.add('j', dir=Direction(down=1)) + km.map('ppp', n(5)) + km.map('pp', n(8)) + km.map('pp', n(2)) + directions.map('j', dir=Direction(down=1)) press = self._mkpress(kb, km) self.assertEqual(5, press('ppp')) @@ -113,22 +113,22 @@ class Test(PressTestCase): return 5 directions = KeyMap() - directions.add('j', dir=Direction(down=1)) - directions.add('k', dir=Direction(down=-1)) - directions.add('', alias='j') + directions.map('j', dir=Direction(down=1)) + directions.map('k', dir=Direction(down=-1)) + directions.map('', alias='j') base = KeyMap() - base.add('a', add_dirs) - base.add('b', add_dirs) - base.add('xx', add_dirs) - base.add('f', return5) - base.add('yy', alias='y') - base.add('!', alias='!') + base.map('a', add_dirs) + base.map('b', add_dirs) + base.map('xx', add_dirs) + base.map('f', return5) + base.map('yy', alias='y') + base.map('!', alias='!') other = KeyMap() - other.add('bb', alias='xx') - other.add('c', add_dirs) - other.add('g', alias='f') + other.map('bb', alias='xx') + other.map('c', add_dirs) + other.map('g', alias='f') km = base.merge(other) kb = KeyBuffer(km, directions) @@ -248,9 +248,9 @@ class Test(PressTestCase): def test_add(self): c = KeyMap() - c.add('aa', 'b', lambda *_: 'lolz') + c.map('aa', 'b', lambda *_: 'lolz') self.assert_(c['aa'].function(), 'lolz') - @c.add('a', 'c') + @c.map('a', 'c') def test(): return 5 self.assert_(c['b'].function(), 'lolz') @@ -268,7 +268,7 @@ class Test(PressTestCase): return value return arg.n return fnc - km.add('p', n(5)) + km.map('p', n(5)) press = self._mkpress(kb, km) self.assertEqual(5, press('p')) self.assertEqual(3, press('3p')) @@ -278,16 +278,16 @@ class Test(PressTestCase): km = KeyMap() directions = KeyMap() kb = KeyBuffer(km, directions) - directions.add('j', dir=Direction(down=1)) - directions.add('k', dir=Direction(down=-1)) + directions.map('j', dir=Direction(down=1)) + directions.map('k', dir=Direction(down=-1)) def nd(arg): """ n * direction """ n = arg.n is None and 1 or arg.n dir = arg.direction is None and Direction(down=1) \ or arg.direction return n * dir.down - km.add('d', nd) - km.add('dd', func=nd) + km.map('d', nd) + km.map('dd', func=nd) press = self._mkpress(kb, km) @@ -302,8 +302,8 @@ class Test(PressTestCase): self.assertEqual( 33, press('33dd')) self.assertEqual( 1, press('dd')) - km.add('x', nd) - km.add('xxxx', func=nd) + km.map('x', nd) + km.map('xxxx', func=nd) self.assertEqual(1, press('xxxxj')) self.assertEqual(1, press('xxxxjsomeinvalitchars')) @@ -319,18 +319,18 @@ class Test(PressTestCase): km = KeyMap() directions = KeyMap() kb = KeyBuffer(km, directions) - directions.add('j', dir=Direction(down=1)) - directions.add('k', dir=Direction(down=-1)) + directions.map('j', dir=Direction(down=1)) + directions.map('k', dir=Direction(down=-1)) - directions.add('g', dir=Direction(down=-1)) + directions.map('g', dir=Direction(down=-1)) def cat(arg): n = arg.n is None and 1 or arg.n return ''.join(chr(c) for c in arg.matches) * n - km.add('return', cat) - km.add('cat4', cat) - km.add('foo', cat) + km.map('return', cat) + km.map('cat4', cat) + km.map('foo', cat) press = self._mkpress(kb, km) @@ -342,7 +342,7 @@ class Test(PressTestCase): self.assertEqual('x', press('foojx')) self.assertPressFails(kb, 'fooggx') # ANYKEY forbidden in DIRECTION - km.add('', lambda _: Ellipsis) + km.map('', lambda _: Ellipsis) self.assertEqual('x', press('returnx')) self.assertEqual('abcd', press('cat4abcd')) self.assertEqual(Ellipsis, press('2cat4abcd')) @@ -356,8 +356,8 @@ class Test(PressTestCase): km = KeyMap() directions = KeyMap() kb = KeyBuffer(km, directions) - directions.add('j', dir=Direction(down=1)) - directions.add('k', dir=Direction(down=-1)) + directions.map('j', dir=Direction(down=1)) + directions.map('k', dir=Direction(down=-1)) def add_dirs(arg): n = 0 @@ -365,8 +365,8 @@ class Test(PressTestCase): n += dir.down return n - km.add('xy', add_dirs) - km.add('four', add_dirs) + km.map('xy', add_dirs) + km.map('four', add_dirs) press = self._mkpress(kb, km) @@ -381,9 +381,9 @@ class Test(PressTestCase): directions = KeyMap() kb = KeyBuffer(km, directions) press = self._mkpress(kb, km) - directions.add('j', dir=Direction(down=1)) - directions.add('k', dir=Direction(down=-1)) - km.add('xxx', lambda _: 1) + directions.map('j', dir=Direction(down=1)) + directions.map('k', dir=Direction(down=-1)) + km.map('xxx', lambda _: 1) self.assertEqual(1, press('xxx')) @@ -409,10 +409,10 @@ class Test(PressTestCase): def move(arg): return arg.direction.down - directions.add('j', dir=Direction(down=1)) - directions.add('s', alias='j') - directions.add('k', dir=Direction(down=-1)) - km.add('', func=move) + directions.map('j', dir=Direction(down=1)) + directions.map('s', alias='j') + directions.map('k', dir=Direction(down=-1)) + km.map('', func=move) self.assertEqual(1, press('j')) self.assertEqual(1, press('j')) @@ -428,14 +428,14 @@ class Test(PressTestCase): self.assertEqual(-1, press('k')) self.assertEqual(-1, press('k')) - km.add('k', func=lambda _: 'love') + km.map('k', func=lambda _: 'love') self.assertEqual(1, press('j')) self.assertEqual('love', press('k')) self.assertEqual(40, press('40j')) - km.add('', func=move) + km.map('', func=move) self.assertEqual(40, press('40jkhl')) -- cgit 1.4.1-2-gfad0 From 06152bdc5e20cbece35dd4709509b0bf024b428d Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 18 Feb 2010 23:19:59 +0100 Subject: keyparser: fixes --- ranger/container/keymap.py | 6 +++--- ranger/gui/ui.py | 7 +++---- test/tc_ui.py | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index dae8955a..e49da6ee 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -282,7 +282,7 @@ class KeyBuffer(object): break return self.command -key_map = { +special_keys = { 'dir': DIRKEY, 'any': ANYKEY, 'psv': PASSIVE_ACTION, @@ -303,7 +303,7 @@ key_map = { 'tab': ord('\t'), } for char in ascii_lowercase: - key_map['c-' + char] = ord(char) - 96 + special_keys['c-' + char] = ord(char) - 96 def translate_keys(obj): """ @@ -327,7 +327,7 @@ def translate_keys(obj): in_brackets = False string = ''.join(bracket_content).lower() try: - yield key_map[string] + yield special_keys[string] except KeyError: yield ord('<') for c in bracket_content: diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index eb7c26fa..a972eca9 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -144,16 +144,15 @@ class UI(DisplayableContainer): self.env.cmd = cmd - if hasattr(cmd, 'show_obj') and hasattr(cmd.show_obj, 'hint'): - if hasattr(self, 'hint'): - self.hint(cmd.show_obj.hint) - elif cmd.function: + if cmd.function: try: cmd.function(CommandArgs.from_widget(self)) except Exception as error: self.fm.notify(error) if kbuf.done: kbuf.clear() + else: + kbuf.clear() def get_next_key(self): """Waits for key input and returns the pressed key""" diff --git a/test/tc_ui.py b/test/tc_ui.py index affec907..98ddff93 100644 --- a/test/tc_ui.py +++ b/test/tc_ui.py @@ -28,7 +28,7 @@ class Test(unittest.TestCase): def setUp(self): self.fm = Fake() - self.ui = ui.UI(env=Fake(), fm=self.fm, commandlist=Fake()) + self.ui = ui.UI(env=Fake(), fm=self.fm, keymap=Fake()) def fakesetup(): self.ui.widget = Fake() -- cgit 1.4.1-2-gfad0 From 09d8404c62454a08dffc99d0a79d46ac51d8aab5 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 19 Feb 2010 00:39:18 +0100 Subject: keyparser: lots of stuff --- ranger/container/keymap.py | 27 +++++++++++------ ranger/defaults/keys.py | 72 ++++++++++++++++++++++++++++++---------------- ranger/ext/tree.py | 4 +-- ranger/gui/ui.py | 2 +- test/tc_newkeys.py | 14 +++++---- 5 files changed, 77 insertions(+), 42 deletions(-) diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index e49da6ee..f9be5e95 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -66,6 +66,7 @@ class CommandArgs(object): 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 @staticmethod @@ -88,6 +89,8 @@ class KeyMap(Tree): return func return decorator_function + __call__ = map + def add_binding(self, *keys, **actions): assert keys bind = Binding(keys, actions) @@ -181,10 +184,13 @@ class KeyBuffer(object): self.dir_tree_pointer = self.dir_tree_pointer._tree match = self.dir_tree_pointer if isinstance(self.dir_tree_pointer, Binding): - if 'alias' in match.actions: - self.dir_tree_pointer = self.direction_keys.traverse( - match.alias) - self._direction_try_to_finish(rec - 1) + 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 else: direction = match.actions['dir'] * self.direction_quant self.directions.append(direction) @@ -246,10 +252,13 @@ class KeyBuffer(object): 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) + 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 else: self.command = self.tree_pointer self.done = True @@ -285,7 +294,7 @@ class KeyBuffer(object): special_keys = { 'dir': DIRKEY, 'any': ANYKEY, - 'psv': PASSIVE_ACTION, + 'bg': PASSIVE_ACTION, 'cr': ord("\n"), 'enter': ord("\n"), 'space': ord(" "), diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index aaa332b9..f2d93c58 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -36,6 +36,7 @@ Check ranger.keyapi for more information from ranger.api.keys import * + def _vimlike_aliases(command_list): bind, alias = make_abbreviations(command_list) @@ -334,31 +335,43 @@ def _basic_movement(command_list): bind(KEY_HOME, wdg.move(absolute=0)) bind(KEY_END, wdg.move(absolute=-1)) -def get_directions(): - k = KeyMap() - map = k.map +def base_directions(): + map = KeyMap() map('', dir=Direction(down=1)) map('', dir=Direction(down=-1)) map('', dir=Direction(right=-1)) map('', dir=Direction(right=1)) + return map + +def vim(): + map = KeyMap() + map.merge(base_directions()) map('j', alias='') map('k', alias='') map('h', alias='') map('l', alias='') - return k -def move(arg): - arg.fm.move_pointer(relative=arg.direction.down) + return map + +def system_keys(): + map = KeyMap() + map('Q', fm.exit()) + map('', fm.handle_mouse()) + map('', fm.redraw_window()) + map('', fm.resize()) + + return map -def get_ui_keys(): - k = KeyMap() - k.merge(system_keys()) - map = k.map +def browser_keys(): + map = KeyMap() + map.merge(system_keys()) - map('', move) - map('', 'Q', fm.exit()) + @map('') + def move(arg): + arg.fm.move_pointer(relative=arg.direction.down) + map(fm.exit(), 'Q') # --------------------------------------------------------- history map('H', fm.history_go(-1)) @@ -378,29 +391,38 @@ def get_ui_keys(): map('pp', fm.paste()) map('po', fm.paste(overwrite=True)) map('pl', fm.paste_symlink()) - map('p', fm.notify('press //p// once again to confirm pasting' \ + map('p', fm.notify('press //p// once again to confirm pasting' \ ', or //l// to create symlinks')) # ---------------------------------------------------- run programs map('s', fm.execute_command(os.environ['SHELL'])) map('E', fm.edit_file()) - map('term', fm.execute_command('x-terminal-emulator', flags='d')) + map('.term', fm.execute_command('x-terminal-emulator', flags='d')) map('du', fm.execute_command('du --max-depth=1 -h | less')) - return k + map(':', ';', fm.open_console(cmode.COMMAND)) -def system_keys(): - k = KeyMap() - k.map(fm.exit(), 'Q') - k.map(fm.handle_mouse(), '') - k.map(fm.redraw_window(), '') - k.map(fm.resize(), '') - return k + return map + +def console_keys(): + map = KeyMap() + map.merge(system_keys()) + + @map('') + def type_key(arg): + arg.wdg.type_key(arg.match) + + map('', wdg.history_move(-1)) + map('', wdg.history_move(1)) + map('', wdg.tab()) +#from pprint import pprint +#pprint(browser_keys()._tree[106].__dict__) +#raise SystemExit() -ui_keys = get_ui_keys() +ui_keys = browser_keys() taskview_keys = ui_keys pager_keys = ui_keys embedded_pager_keys = ui_keys -console_keys = ui_keys -directions = get_directions() +console_keys = console_keys() +directions = vim() diff --git a/ranger/ext/tree.py b/ranger/ext/tree.py index d7b08cd7..54505f06 100644 --- a/ranger/ext/tree.py +++ b/ranger/ext/tree.py @@ -36,7 +36,7 @@ class Tree(object): newtree._tree = self._tree return newtree - def merge(self, other, copy=True): + def merge(self, other, copy=False): """Merge another Tree into a copy of self""" def deep_merge(branch, otherbranch): assert isinstance(otherbranch, dict) @@ -125,7 +125,7 @@ class Tree(object): except TypeError: raise KeyError("trying to enter leaf") except KeyError: - raise KeyError(str(char) + " not in tree " + str(tree)) + raise KeyError(repr(char) + " not in tree " + str(tree)) if isinstance(tree, dict): return type(self)(tree, parent=last_tree, key=char) else: diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index a972eca9..2b406113 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -36,7 +36,7 @@ class UI(DisplayableContainer): self.fm = fm if keymap is None: - self.keymap = self.settings.keys.ui_keys + self.keymap = self.settings.keys.browser_keys() else: self.keymap = keymap self.win = curses.initscr() diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 45ac4e33..a4d69805 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -51,7 +51,7 @@ class Test(PressTestCase): return fnc km.map('ppp', n(5)) - km.map('pp', n(8)) + km.map('pp', n(8)) km.map('pp', n(2)) directions.map('j', dir=Direction(down=1)) @@ -116,6 +116,7 @@ class Test(PressTestCase): directions.map('j', dir=Direction(down=1)) directions.map('k', dir=Direction(down=-1)) directions.map('', alias='j') + directions.map('@', alias='') base = KeyMap() base.map('a', add_dirs) @@ -130,7 +131,7 @@ class Test(PressTestCase): other.map('c', add_dirs) other.map('g', alias='f') - km = base.merge(other) + km = base.merge(other, copy=True) kb = KeyBuffer(km, directions) press = self._mkpress(kb, km) @@ -142,6 +143,9 @@ class Test(PressTestCase): self.assertEqual(5, press('f')) self.assertEqual(5, press('g')) + self.assertEqual(press('c'), press('c@')) + self.assertEqual(press('c'), press('c@')) + self.assertEqual(press('c'), press('c@')) for n in range(1, 50): self.assertPressIncomplete(kb, 'y' * n) @@ -205,19 +209,19 @@ class Test(PressTestCase): # test 1 t = Tree('a') u = Tree('b') - merged = t.merge(u) + merged = t.merge(u, copy=True) self.assertEqual('b', merged._tree) # test 2 t = Tree('a') u = makeTreeA() - merged = t.merge(u) + merged = t.merge(u, copy=True) self.assertEqual(u._tree, merged._tree) # test 3 t = makeTreeA() u = makeTreeB() - v = t.merge(u) + v = t.merge(u, copy=True) self.assertEqual(0, v['aaaX']) self.assertEqual(2, v['aaaY']) -- cgit 1.4.1-2-gfad0 From 4070e6c93a9e24a5c1bb919668e3124fbd3161de Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 24 Feb 2010 12:42:21 +0100 Subject: keyparser: stuff --- ranger/actions.py | 50 ++++++++++++++- ranger/container/keymap.py | 27 +++----- ranger/defaults/keys.py | 128 +++++++++++++++++++++++++++++++++++++- ranger/ext/direction.py | 120 +++++++++++++++++++++++++++++++++++ ranger/gui/ui.py | 2 + ranger/gui/widgets/browserview.py | 8 +-- 6 files changed, 307 insertions(+), 28 deletions(-) create mode 100644 ranger/ext/direction.py diff --git a/ranger/actions.py b/ranger/actions.py index c4b75aca..754fd857 100644 --- a/ranger/actions.py +++ b/ranger/actions.py @@ -148,8 +148,10 @@ class Actions(EnvironmentAware, SettingsAware): """Delete the bookmark with the name """ self.bookmarks.delete(key) - def move_left(self, narg=1): + def move_left(self, narg=None): """Enter the parent directory""" + if narg is None: + narg = 1 try: directory = os.path.join(*(['..'] * narg)) except: @@ -274,6 +276,44 @@ class Actions(EnvironmentAware, SettingsAware): self.env.pwd.move(relative=relative, absolute=absolute, narg=narg) + def move(self, dir, narg=None): + if narg is not None: + dir = dir * narg + + self.notify(str(dir)) + + if dir.right is not None: + if dir.right >= 0: + if dir.has_explicit_direction: + self.move_right(narg=dir.right) + else: + self.move_right(narg=dir.original_right - 1) + elif dir.right < 0: + self.move_left(narg=dir.left) + else: + if dir.percent: + if dir.absolute: + self.move_pointer_by_percentage( \ + absolute=dir.down, narg=narg) + else: + self.move_pointer_by_percentage( \ + relative=dir.down, narg=narg) + elif dir.pages: + self.move_pointer_by_pages(dir.down) + elif dir.absolute: + if dir.has_explicit_direction: + self.move_pointer(absolute=dir.down) + else: + self.move_pointer(absolute=dir.original_down) + else: + self.move_pointer(relative=dir.down) + + def draw_bookmarks(self): + self.ui.browser.draw_bookmarks = True + + def hide_bookmarks(self): + self.ui.browser.draw_bookmarks = False + def move_pointer_by_pages(self, relative): """Move the pointer down by pages""" self.env.pwd.move(relative=int(relative * self.env.termsize[0])) @@ -288,9 +328,12 @@ class Actions(EnvironmentAware, SettingsAware): if narg is not None: absolute = narg + if absolute is not None: + absolute = int(absolute * factor) + self.env.pwd.move( relative=int(relative * factor), - absolute=int(absolute * factor)) + absolute=absolute) def scroll(self, relative): """Scroll down by lines""" @@ -369,6 +412,9 @@ class Actions(EnvironmentAware, SettingsAware): if hasattr(self.ui, 'notify'): self.ui.notify(text, duration=duration, bad=bad) + def hint(self, text): + self.notify(text) + def mark(self, all=False, toggle=False, val=None, movedown=None, narg=1): """ A wrapper for the directory.mark_xyz functions. diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index f9be5e95..50b70b33 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -16,6 +16,7 @@ import curses from string import ascii_lowercase from inspect import isfunction, getargspec from ranger.ext.tree import Tree +from ranger.ext.direction import Direction MAX_ALIAS_RECURSION = 20 PASSIVE_ACTION = 9003 @@ -26,25 +27,6 @@ DIRECTION = 'direction' DIRARG = 'dir' ALIASARG = 'alias' -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__ - def to_string(i): """convert a ord'd integer to a string""" try: @@ -142,6 +124,7 @@ class KeyBuffer(object): return None assert isinstance(key, int) assert key >= 0 + self.all_keys.append(key) # evaluate quantifiers if self.eval_quantifier and self._do_eval_quantifier(key): @@ -192,7 +175,11 @@ class KeyBuffer(object): self.failure = True return None else: - direction = match.actions['dir'] * self.direction_quant + if self.direction_quant is not None: + direction = match.actions['dir'] * self.direction_quant + direction.has_explicit_direction = True + else: + direction = match.actions['dir'].copy() self.directions.append(direction) self.direction_quant = None self.eval_command = True diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index f2d93c58..7a17c831 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -337,21 +337,34 @@ def _basic_movement(command_list): def base_directions(): + # Direction Keys map = KeyMap() map('', dir=Direction(down=1)) map('', dir=Direction(down=-1)) map('', dir=Direction(right=-1)) map('', dir=Direction(right=1)) + map('', dir=Direction(down=0, absolute=True)) + map('', dir=Direction(down=-1, absolute=True)) + map('', dir=Direction(down=1, pages=True)) + map('', dir=Direction(down=-1, pages=True)) + map('%', dir=Direction(down=1, percent=True, absolute=True)) + map('', dir=Direction(down=1, pages=True)) + map('', dir=Direction(down=1)) return map def vim(): + # Direction Keys map = KeyMap() map.merge(base_directions()) map('j', alias='') map('k', alias='') map('h', alias='') map('l', alias='') + map('gg', alias='') + map('G', alias='') + map('J', dir=Direction(down=20)) + map('K', dir=Direction(down=-20)) return map @@ -370,9 +383,11 @@ def browser_keys(): @map('') def move(arg): - arg.fm.move_pointer(relative=arg.direction.down) + arg.fm.move(dir=arg.direction, narg=arg.n) map(fm.exit(), 'Q') + map('', fm.move(dir=Direction(right=1))) + # --------------------------------------------------------- history map('H', fm.history_go(-1)) map('L', fm.history_go(1)) @@ -391,7 +406,7 @@ def browser_keys(): map('pp', fm.paste()) map('po', fm.paste(overwrite=True)) map('pl', fm.paste_symlink()) - map('p', fm.notify('press //p// once again to confirm pasting' \ + map('p', fm.hint('press //p// once again to confirm pasting' \ ', or //l// to create symlinks')) # ---------------------------------------------------- run programs @@ -400,7 +415,116 @@ def browser_keys(): map('.term', fm.execute_command('x-terminal-emulator', flags='d')) map('du', fm.execute_command('du --max-depth=1 -h | less')) + # -------------------------------------------------- toggle options + map('b', fm.hint("bind_//h//idden //p//review_files" \ + "//d//irectories_first //c//ollapse_preview flush//i//nput")) + map('bh', fm.toggle_boolean_option('show_hidden')) + map('bp', fm.toggle_boolean_option('preview_files')) + map('bi', fm.toggle_boolean_option('flushinput')) + map('bd', fm.toggle_boolean_option('directories_first')) + map('bc', fm.toggle_boolean_option('collapse_preview')) + + # ------------------------------------------------------------ sort + map('o', 'O', fm.hint("//s//ize //b//ase//n//ame //m//time" \ + " //t//ype //r//everse")) + sort_dict = { + 's': 'size', + 'b': 'basename', + 'n': 'basename', + 'm': 'mtime', + 't': 'type', + } + + for key, val in sort_dict.items(): + for key, is_capital in ((key, False), (key.upper(), True)): + # reverse if any of the two letters is capital + map('o' + key, fm.sort(func=val, reverse=is_capital)) + map('O' + key, fm.sort(func=val, reverse=True)) + + map('or', 'Or', 'oR', 'OR', lambda arg: \ + arg.fm.sort(reverse=not arg.fm.settings.reverse)) + + # ----------------------------------------------- console shortcuts + @map("A") + def append_to_filename(arg): + command = 'rename ' + arg.fm.env.cf.basename + arg.fm.open_console(cmode.COMMAND, command) + + map('cw', fm.open_console(cmode.COMMAND, 'rename ')) + map('cd', fm.open_console(cmode.COMMAND, 'cd ')) + map('f', fm.open_console(cmode.COMMAND_QUICK, 'find ')) + map('bf', fm.open_console(cmode.COMMAND, 'filter ')) + map('d', fm.hint('d//u// (disk usage) d//d// (cut)')) + + + # --------------------------------------------- jump to directories + map('gh', fm.cd('~')) + map('ge', fm.cd('/etc')) + map('gu', fm.cd('/usr')) + map('gd', fm.cd('/dev')) + map('gl', fm.cd('/lib')) + map('go', fm.cd('/opt')) + map('gv', fm.cd('/var')) + map('gr', 'g/', fm.cd('/')) + map('gm', fm.cd('/media')) + map('gn', fm.cd('/mnt')) + map('gt', fm.cd('/tmp')) + map('gs', fm.cd('/srv')) + map('gR', fm.cd(RANGERDIR)) + + # ------------------------------------------------------- searching + map('/', fm.open_console(cmode.SEARCH)) + + map('n', fm.search()) + map('N', fm.search(forward=False)) + + map(TAB, fm.search(order='tag')) + map('cc', fm.search(order='ctime')) + map('cm', fm.search(order='mimetype')) + map('cs', fm.search(order='size')) + map('c', fm.hint('//c//time //m//imetype //s//ize')) + + # ------------------------------------------------------- bookmarks + for key in ALLOWED_BOOKMARK_KEYS: + map("`" + key, "'" + key, fm.enter_bookmark(key)) + map("m" + key, fm.set_bookmark(key)) + map("um" + key, fm.unset_bookmark(key)) + map("`", "'", "m", fm.draw_bookmarks()) + + + map(':', ';', fm.open_console(cmode.COMMAND)) + + # ---------------------------------------------------- change views + map('i', fm.display_file()) + map(ctrl('p'), fm.display_log()) + map('?', KEY_F1, fm.display_help()) + map('w', lambda arg: arg.fm.ui.open_taskview()) + + # ---------------------------------------------------------- custom + # This is useful to track watched episode of a series. + @map(']') + def tag_next_and_run(arg): + fm = arg.fm + fm.tag_remove() + fm.tag_remove(movedown=False) + fm.tag_toggle() + fm.move_pointer(relative=-2) + fm.move_right() + fm.move_pointer(relative=1) + + # "enter" = shortcut for "1l" + map('', fm.move(Direction(right=2))) + + # ------------------------------------------------ system functions + map('ZZ', fm.exit()) + map(ctrl('R'), fm.reset()) + map('R', fm.reload_cwd()) + map(ctrl('C'), fm.exit()) + map(':', ';', fm.open_console(cmode.COMMAND)) + map('>', fm.open_console(cmode.COMMAND_QUICK)) + map('!', fm.open_console(cmode.OPEN)) + map('r', fm.open_console(cmode.OPEN_QUICK)) return map diff --git a/ranger/ext/direction.py b/ranger/ext/direction.py new file mode 100644 index 00000000..28003941 --- /dev/null +++ b/ranger/ext/direction.py @@ -0,0 +1,120 @@ +# Copyright (c) 2009, 2010 hut +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +class NoDefault(object): + pass + +class Direction(object): + """An object with a down and right method""" + def __init__(self, right=None, down=None, absolute=False, + percent=False, pages=False, **keywords): + self.has_explicit_direction = False + + if 'up' in keywords: + self.down = -keywords['up'] + else: + self.down = down + + if 'left' in keywords: + self.right = -keywords['left'] + else: + self.right = right + + if 'relative' in keywords: + self.absolute = not relative + else: + self.absolute = absolute + + if 'default' in keywords: + self.default = keywords['default'] + else: + self.default = NoDefault + + self.original_down = self.down + self.original_right = self.right + + self.percent = percent + self.pages = pages + + @property + def up(self): + if self.down is None: + return None + return -self.down + + @property + def left(self): + if self.right is None: + return None + return -self.right + + @property + def relative(self): + return not self.absolute + + def down_or_default(self, default): + if self.has_been_modified: + return self.down + return default + + def steps_down(self, page_length=10): + if self.pages: + return self.down * page_length + else: + return self.down + + def steps_right(self, page_length=10): + if self.pages: + return self.right * page_length + else: + return self.right + + def copy(self): + new = type(self)() + new.__dict__.update(self.__dict__) + return new + + def __mul__(self, other): + copy = self.copy() + if self.absolute: + if self.down is not None: + copy.down = other + if self.right is not None: + copy.right = other + else: + if self.down is not None: + copy.down *= other + if self.right is not None: + copy.right *= other + copy.original_down = self.original_down + copy.original_right = self.original_right + return copy + __rmul__ = __mul__ + + def __str__(self): + s = ['') + return ''.join(s) diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index 2b406113..05efa639 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -136,6 +136,8 @@ class UI(DisplayableContainer): kbuf = self.env.keybuffer cmd = kbuf.command + self.fm.hide_bookmarks() + if kbuf.failure: kbuf.clear() return diff --git a/ranger/gui/widgets/browserview.py b/ranger/gui/widgets/browserview.py index 54f21fa9..080f1be0 100644 --- a/ranger/gui/widgets/browserview.py +++ b/ranger/gui/widgets/browserview.py @@ -23,6 +23,7 @@ class BrowserView(Widget, DisplayableContainer): ratios = None preview = True preview_available = True + draw_bookmarks = False stretch_ratios = None need_clear = False @@ -60,10 +61,9 @@ class BrowserView(Widget, DisplayableContainer): self.add_child(self.pager) def draw(self): - try: - if self.env.cmd.show_obj.draw_bookmarks: - self._draw_bookmarks() - except AttributeError: + if self.draw_bookmarks: + self._draw_bookmarks() + else: if self.need_clear: self.win.erase() self.need_redraw = True -- cgit 1.4.1-2-gfad0 From f885ace0d94ec1c2d9aa504020b3457954153b98 Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 28 Feb 2010 14:41:21 +0100 Subject: keyparser: updaded copying notice in new files --- ranger/container/keymap.py | 23 ++++++++++++----------- ranger/ext/direction.py | 23 ++++++++++++----------- ranger/ext/tree.py | 23 ++++++++++++----------- 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index 50b70b33..7f72a282 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -1,16 +1,17 @@ -# Copyright (c) 2009, 2010 hut +# Copyright (C) 2009, 2010 Roman Zimbelmann # -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. +# 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. # -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# 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 . import curses from string import ascii_lowercase diff --git a/ranger/ext/direction.py b/ranger/ext/direction.py index 28003941..30eb87ce 100644 --- a/ranger/ext/direction.py +++ b/ranger/ext/direction.py @@ -1,16 +1,17 @@ -# Copyright (c) 2009, 2010 hut +# Copyright (C) 2009, 2010 Roman Zimbelmann # -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. +# 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. # -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# 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 . class NoDefault(object): pass diff --git a/ranger/ext/tree.py b/ranger/ext/tree.py index 54505f06..6d841c2a 100644 --- a/ranger/ext/tree.py +++ b/ranger/ext/tree.py @@ -1,16 +1,17 @@ -# Copyright (c) 2009, 2010 hut +# Copyright (C) 2009, 2010 Roman Zimbelmann # -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. +# 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. # -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# 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 . class Tree(object): def __init__(self, dictionary=None, parent=None, key=None): -- cgit 1.4.1-2-gfad0 From 4a435286452a8a0177994a6f27f3806cdb74fa82 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 8 Mar 2010 21:13:22 +0100 Subject: keyparser: added copying info to testcase --- test/tc_newkeys.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index a4d69805..92bfb513 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -1,4 +1,19 @@ # coding=utf-8 +# Copyright (C) 2009, 2010 Roman Zimbelmann +# +# 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 . + if __name__ == '__main__': from __init__ import init; init() from unittest import TestCase, main -- cgit 1.4.1-2-gfad0 From efbde17048b14d43895e4cf91e798fb97702b68f Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 9 Mar 2010 12:40:51 +0100 Subject: keyparser: test for collisions with directories of a lenght > 1 --- ranger/container/keymap.py | 28 +++++++++++++++------------- test/tc_newkeys.py | 35 +++++++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index 7f72a282..e8cf6119 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -115,7 +115,7 @@ 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 @@ -210,20 +210,23 @@ class KeyBuffer(object): self.failure = True return None except KeyError: - if DIRKEY in self.tree_pointer: - self.eval_command = False - self.eval_quantifier = True + try: 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 - 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: if isinstance(self.tree_pointer, dict): try: @@ -286,7 +289,6 @@ special_keys = { 'cr': ord("\n"), 'enter': ord("\n"), 'space': ord(" "), - 'space': ord(" "), 'down': curses.KEY_DOWN, 'up': curses.KEY_UP, 'left': curses.KEY_LEFT, diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 92bfb513..0c810af5 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -83,6 +83,31 @@ 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', 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: @@ -120,10 +145,7 @@ class Test(PressTestCase): def test_alias(self): def add_dirs(arg): - n = 0 - for dir in arg.directions: - n += dir.down - return n + return sum(dir.down for dir in arg.directions) def return5(_): return 5 @@ -379,10 +401,7 @@ class Test(PressTestCase): directions.map('k', dir=Direction(down=-1)) def add_dirs(arg): - n = 0 - for dir in arg.directions: - n += dir.down - return n + return sum(dir.down for dir in arg.directions) km.map('xy', add_dirs) km.map('four', add_dirs) -- cgit 1.4.1-2-gfad0 From 713bd1eee932b3cd192fd1b09611be7679f3dbb8 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 7 Apr 2010 18:17:22 +0200 Subject: core.action: fixes --- ranger/core/actions.py | 8 +++++++- ranger/defaults/keys.py | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 6910871b..fb2973dd 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -140,7 +140,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): self.move(to=1, percentage=True) # moves to 80% """ direction = Direction(kw) - if 'left' in direction: + if 'left' in direction or direction.left() > 0: steps = direction.left() if narg is not None: steps *= narg @@ -411,6 +411,12 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): """Delete the bookmark with the name """ self.bookmarks.delete(key) + def draw_bookmarks(self): + self.ui.browser.draw_bookmarks = True + + def hide_bookmarks(self): + self.ui.browser.draw_bookmarks = False + # -------------------------- # -- Pager # -------------------------- diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index a236ad9f..55f8331d 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -392,7 +392,8 @@ def browser_keys(): @map('') def move(arg): - arg.fm.move(dir=arg.direction, narg=arg.n) + arg.fm.move(narg=arg.n, **arg.direction) + map('gg', fm.move(to=0)) map(fm.exit(), 'Q') map('', fm.move(dir=Direction(right=1))) -- cgit 1.4.1-2-gfad0 From 894665269984ebf9f07bd9cae681ba2057715e25 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 7 Apr 2010 18:18:02 +0200 Subject: updated container.keymap + testcase to work with new ext.direction --- ranger/container/keymap.py | 12 ++---------- test/tc_newkeys.py | 16 +++++++++------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index e8cf6119..41875cb2 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -144,12 +144,6 @@ class KeyBuffer(object): self._do_eval_direction(key) def _do_eval_direction(self, key): - # swap quant and direction_quant in bindings like '' - 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] @@ -176,11 +170,9 @@ class KeyBuffer(object): self.failure = True return None else: + direction = match.actions['dir'].copy() if self.direction_quant is not None: - direction = match.actions['dir'] * self.direction_quant - direction.has_explicit_direction = True - else: - direction = match.actions['dir'].copy() + direction.multiply(self.direction_quant) self.directions.append(direction) self.direction_quant = None self.eval_command = True diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 0c810af5..bcf08e5f 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -85,7 +85,7 @@ class Test(PressTestCase): def test_map_collision(self): def add_dirs(arg): - return sum(dir.down for dir in arg.directions) + return sum(dir.down() for dir in arg.directions) def return5(_): return 5 @@ -145,7 +145,7 @@ class Test(PressTestCase): def test_alias(self): def add_dirs(arg): - return sum(dir.down for dir in arg.directions) + return sum(dir.down() for dir in arg.directions) def return5(_): return 5 @@ -326,7 +326,7 @@ class Test(PressTestCase): n = arg.n is None and 1 or arg.n dir = arg.direction is None and Direction(down=1) \ or arg.direction - return n * dir.down + return n * dir.down() km.map('d', nd) km.map('dd', func=nd) @@ -401,7 +401,7 @@ class Test(PressTestCase): directions.map('k', dir=Direction(down=-1)) def add_dirs(arg): - return sum(dir.down for dir in arg.directions) + return sum(dir.down() for dir in arg.directions) km.map('xy', add_dirs) km.map('four', add_dirs) @@ -445,7 +445,7 @@ class Test(PressTestCase): press = self._mkpress(kb, km) def move(arg): - return arg.direction.down + return arg.direction.down() directions.map('j', dir=Direction(down=1)) directions.map('s', alias='j') @@ -471,11 +471,13 @@ class Test(PressTestCase): self.assertEqual(1, press('j')) self.assertEqual('love', press('k')) - self.assertEqual(40, press('40j')) + self.assertEqual(1, press('40j')) + self.assertEqual(40, kb.quant) km.map('', func=move) - self.assertEqual(40, press('40jkhl')) + self.assertEqual(1, press('40jkhl')) + self.assertEqual(40, kb.quant) def test_tree_deep_copy(self): t = Tree() -- cgit 1.4.1-2-gfad0 From eacd742bd40013d8868fc75a29a85a14f1ee1e81 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 7 Apr 2010 18:50:50 +0200 Subject: container.keymap: support for Alt key --- ranger/container/keymap.py | 7 ++++++- test/tc_newkeys.py | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index 41875cb2..dee54430 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -295,6 +295,7 @@ special_keys = { } for char in ascii_lowercase: special_keys['c-' + char] = ord(char) - 96 + special_keys['a-' + char] = (27, ord(char)) def translate_keys(obj): """ @@ -318,12 +319,16 @@ def translate_keys(obj): in_brackets = False string = ''.join(bracket_content).lower() try: - yield special_keys[string] + 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: diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index bcf08e5f..bbfc74e9 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -136,6 +136,8 @@ class Test(PressTestCase): test('', 2) for i in range(1, 26): test('', i) + test('', 27, ord('x')) + test('', 27, ord('o')) test('k') test('k') -- cgit 1.4.1-2-gfad0 From bf4a79dba297d9d5b3801c5785fdf0458de7873c Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 7 Apr 2010 18:53:11 +0200 Subject: tc_newkey: added comment --- test/tc_newkeys.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index bbfc74e9..c953e88b 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -123,6 +123,7 @@ class Test(PressTestCase): lst.append(arg) return tuple(lst) + # 1 argument means: assume nothing is translated. test('k') test('kj') test('k', 'k', DIRKEY) -- cgit 1.4.1-2-gfad0 From 7626fd1d08ccb597ef25eaebf37ffc7be33c6df2 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 7 Apr 2010 19:22:26 +0200 Subject: Implement alt keys in core --- ranger/container/keymap.py | 2 ++ ranger/core/fm.py | 14 +------------- ranger/defaults/keys.py | 12 ++++++++++-- ranger/gui/ui.py | 19 +++++++++++++++---- 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index dee54430..1e634f1d 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -295,6 +295,8 @@ special_keys = { } 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): diff --git a/ranger/core/fm.py b/ranger/core/fm.py index 25e66407..a408139a 100644 --- a/ranger/core/fm.py +++ b/ranger/core/fm.py @@ -141,19 +141,7 @@ class FM(Actions, SignalDispatcher): ui.set_load_mode(loader.has_work()) - key = ui.get_next_key() - - if key > 0: - if key == KEY_MOUSE: - ui.handle_mouse() - elif key == KEY_RESIZE: - ui.update_size() - else: - if self.input_blocked and \ - time() > self.input_blocked_until: - self.input_blocked = False - if not self.input_blocked: - ui.handle_key(key) + ui.handle_input() gc_tick += 1 if gc_tick > TICKS_BEFORE_COLLECTING_GARBAGE: diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 55f8331d..467d26e6 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -478,17 +478,25 @@ def browser_keys(): map('gr', 'g/', fm.cd('/')) map('gm', fm.cd('/media')) map('gn', fm.cd('/mnt')) - map('gt', fm.cd('/tmp')) map('gs', fm.cd('/srv')) map('gR', fm.cd(RANGERDIR)) + # ------------------------------------------------------------ tabs + map('gc', ctrl('W'), fm.tab_close()) + map('gt', TAB, fm.tab_move(1)) + map('gT', KEY_BTAB, fm.tab_move(-1)) + map('gn', ctrl('N'), fm.tab_new()) + for n in range(10): + map('g' + str(n), fm.tab_open(n)) + map('', fm.tab_open(n)) + # ------------------------------------------------------- searching map('/', fm.open_console(cmode.SEARCH)) map('n', fm.search()) map('N', fm.search(forward=False)) - map(TAB, fm.search(order='tag')) + map('ct', fm.search(order='tag')) map('cc', fm.search(order='ctime')) map('cm', fm.search(order='mimetype')) map('cs', fm.search(order='size')) diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index c7c2090a..c1a4bb60 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -155,13 +155,24 @@ class UI(DisplayableContainer): else: kbuf.clear() - def get_next_key(self): - """Waits for key input and returns the pressed key""" + def handle_input(self): key = self.win.getch() - if key is not -1: + if key in (27, 195): # 27: alt+X, 195: unicode + keys = [key] + previous_load_mode = self.load_mode + self.set_load_mode(True) + for n in range(8): + getkey = self.win.getch() + if getkey is not -1: + keys.append(getkey) + for key in keys: + self.handle_key(key) + self.set_load_mode(previous_load_mode) + else: if self.settings.flushinput: curses.flushinp() - return key + if key > 0: + self.handle_key(key) def setup(self): """ -- cgit 1.4.1-2-gfad0 From be7c282c2d73472254946f5043bf835f6bbb038a Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 7 Apr 2010 19:42:45 +0200 Subject: gui.ui: improvements in handling of special keys --- ranger/core/fm.py | 6 +++++- ranger/gui/ui.py | 22 +++++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/ranger/core/fm.py b/ranger/core/fm.py index a408139a..ae815fbf 100644 --- a/ranger/core/fm.py +++ b/ranger/core/fm.py @@ -19,7 +19,6 @@ The File Manager, putting the pieces together from time import time from collections import deque -from curses import KEY_MOUSE, KEY_RESIZE import os import sys @@ -105,6 +104,11 @@ class FM(Actions, SignalDispatcher): self.input_blocked = sec != 0 self.input_blocked_until = time() + sec + def input_is_blocked(self): + if self.input_blocked and time() > self.input_blocked_until: + self.input_blocked = False + return self.input_blocked + def loop(self): """ The main loop consists of: diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index c1a4bb60..8d355665 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -127,6 +127,10 @@ class UI(DisplayableContainer): if hasattr(self, 'hint'): self.hint() + if key < 0: + self.env.keybuffer.clear() + return + self.env.key_append(key) if DisplayableContainer.press(self, key): @@ -157,22 +161,34 @@ class UI(DisplayableContainer): def handle_input(self): key = self.win.getch() - if key in (27, 195): # 27: alt+X, 195: unicode + if key is 27 or key >= 128 and key < 256: + # Handle special keys like ALT+X or unicode here: keys = [key] previous_load_mode = self.load_mode self.set_load_mode(True) - for n in range(8): + for n in range(4): getkey = self.win.getch() if getkey is not -1: keys.append(getkey) + if len(keys) == 1: + keys.append(-1) for key in keys: self.handle_key(key) self.set_load_mode(previous_load_mode) + if self.settings.flushinput: + curses.flushinp() else: + # Handle simple key presses, CTRL+X, etc here: if self.settings.flushinput: curses.flushinp() if key > 0: - self.handle_key(key) + if key == curses.KEY_MOUSE: + self.handle_mouse() + elif key == curses.KEY_RESIZE: + self.update_size() + else: + if not self.fm.input_is_blocked(): + self.handle_key(key) def setup(self): """ -- cgit 1.4.1-2-gfad0 From 0b306138950baef339164481d93aaabb882027bc Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 7 Apr 2010 21:04:37 +0200 Subject: added KeyManager --- ranger/__main__.py | 1 + ranger/container/__init__.py | 2 +- ranger/container/keymap.py | 33 ++- ranger/core/environment.py | 7 +- ranger/defaults/keys.py | 557 ++++------------------------------------- ranger/defaults/oldkeys.py | 555 ++++++++++++++++++++++++++++++++++++++++ ranger/gui/ui.py | 12 +- ranger/gui/widgets/console.py | 3 - ranger/gui/widgets/pager.py | 5 - ranger/gui/widgets/taskview.py | 1 - ranger/shared/settings.py | 12 +- test/tc_newkeys.py | 39 ++- test/tc_ui.py | 2 +- 13 files changed, 689 insertions(+), 540 deletions(-) create mode 100644 ranger/defaults/oldkeys.py diff --git a/ranger/__main__.py b/ranger/__main__.py index 674ad8f6..863eadd5 100644 --- a/ranger/__main__.py +++ b/ranger/__main__.py @@ -112,6 +112,7 @@ def main(): path = '.' Environment(path) + SettingsAware._setup_keys() try: my_ui = UI() diff --git a/ranger/container/__init__.py b/ranger/container/__init__.py index 4c8f08ba..c1bb8194 100644 --- a/ranger/container/__init__.py +++ b/ranger/container/__init__.py @@ -17,5 +17,5 @@ used to manage stored data """ from ranger.container.history import History -from .keymap import KeyMap, KeyBuffer +from .keymap import KeyMap, KeyBuffer, KeyManager from .bookmarks import Bookmarks diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index 1e634f1d..62cf0e7a 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -61,11 +61,11 @@ class KeyMap(Tree): """Contains a tree with all the keybindings""" def map(self, *args, **keywords): if keywords: - return self.add_binding(*args, **keywords) + return self._add_binding(*args, **keywords) firstarg = args[-1] if isfunction(firstarg): keywords[FUNC] = firstarg - return self.add_binding(*args[:-1], **keywords) + return self._add_binding(*args[:-1], **keywords) def decorator_function(func): keywords = {FUNC:func} self.map(*args, **keywords) @@ -74,7 +74,7 @@ class KeyMap(Tree): __call__ = map - def add_binding(self, *keys, **actions): + def _add_binding(self, *keys, **actions): assert keys bind = Binding(keys, actions) for key in keys: @@ -83,6 +83,33 @@ class KeyMap(Tree): def __getitem__(self, key): return self.traverse(translate_keys(key)) + +class KeyManager(object): + def __init__(self, keybuffer, contexts): + self._keybuffer = keybuffer + self._contexts = { + 'any': KeyMap(), + 'directions': KeyMap(), + } + for context in contexts: + self._contexts[context] = KeyMap() + + def map(self, context, *args, **keywords): + self.get_context(context).map(*args, **keywords) + + def get_context(self, context): + assert isinstance(context, str) + assert context in self._contexts, "no such context!" + return self._contexts[context] + __getitem__ = get_context + + def use_context(self, context, directions='directions'): + context = self.get_context(context) + if self._keybuffer.keymap is not context: + directions = self.get_context(directions) + self._keybuffer.assign(context, directions) + self._keybuffer.clear() + class Binding(object): """The keybinding object""" def __init__(self, keys, actions): diff --git a/ranger/core/environment.py b/ranger/core/environment.py index 0b38c475..d83003b1 100644 --- a/ranger/core/environment.py +++ b/ranger/core/environment.py @@ -20,10 +20,13 @@ import socket from os.path import abspath, normpath, join, expanduser, isdir from ranger.fsobject.directory import Directory, NoDirectoryGiven -from ranger.container import KeyBuffer, History +from ranger.container import KeyBuffer, KeyManager, History from ranger.ext.signal_dispatcher import SignalDispatcher from ranger.shared import SettingsAware +ALLOWED_CONTEXTS = ('general', 'pager', 'embedded_pager', 'taskview', + 'console') + class Environment(SettingsAware, SignalDispatcher): """A collection of data which is relevant for more than one class. @@ -40,6 +43,7 @@ class Environment(SettingsAware, SignalDispatcher): pathway = None path = None keybuffer = None + keymanager = None def __init__(self, path): SignalDispatcher.__init__(self) @@ -48,6 +52,7 @@ class Environment(SettingsAware, SignalDispatcher): self.pathway = () self.directories = {} self.keybuffer = KeyBuffer(None, None) + self.keymanager = KeyManager(self.keybuffer, ALLOWED_CONTEXTS) self.copy = set() self.history = History(self.settings.max_history_size) diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 467d26e6..72558a23 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -39,517 +39,48 @@ Check ranger.keyapi for more information from ranger.api.keys import * -def _vimlike_aliases(map): - alias = map.alias - # the key 'k' will always do the same as KEY_UP, etc. - alias(KEY_UP, 'k') - alias(KEY_DOWN, 'j') - alias(KEY_LEFT, 'h') - alias(KEY_RIGHT, 'l') - - alias(KEY_NPAGE, ctrl('f')) - alias(KEY_PPAGE, ctrl('b')) - alias(KEY_HOME, 'gg') - alias(KEY_END, 'G') - - -def _emacs_aliases(map): - alias = map.alias - alias(KEY_LEFT, ctrl('b')) - alias(KEY_RIGHT, ctrl('f')) - alias(KEY_HOME, ctrl('a')) - alias(KEY_END, ctrl('e')) - alias(KEY_DC, ctrl('d')) - alias(DEL, ctrl('h')) - - -def initialize_commands(map): - """Initialize the commands for the main user interface""" - - # -------------------------------------------------------- movement - _vimlike_aliases(map) - _basic_movement(map) - - map.alias(KEY_LEFT, KEY_BACKSPACE, DEL) - map.alias(KEY_RIGHT, KEY_ENTER, ctrl('j')) - - map('%', fm.move(to=50, percentage=True)) - map(KEY_NPAGE, ctrl('f'), fm.move(down=1, pages=True)) - map(KEY_PPAGE, ctrl('b'), fm.move(up=1, pages=True)) - map(ctrl('d'), 'J', fm.move(down=0.5, pages=True)) - map(ctrl('u'), 'K', fm.move(up=0.5, pages=True)) - - map(']', fm.traverse()) - map('[', fm.history_go(-1)) - - # --------------------------------------------------------- history - map('H', fm.history_go(-1)) - map('L', fm.history_go(1)) - - # ----------------------------------------------- tagging / marking - map('t', fm.tag_toggle()) - map('T', fm.tag_remove()) - - map(' ', fm.mark(toggle=True)) - map('v', fm.mark(all=True, toggle=True)) - map('V', fm.mark(all=True, val=False)) - - # ------------------------------------------ file system operations - map('yy', fm.copy()) - map('dd', fm.cut()) - map('pp', fm.paste()) - map('po', fm.paste(overwrite=True)) - map('pl', fm.paste_symlink()) - map('p', hint='press //p// once again to confirm pasting' \ - ', or //l// to create symlinks') - - # ---------------------------------------------------- run programs - map('s', fm.execute_command(os.environ['SHELL'])) - map('E', fm.edit_file()) - map(',term', fm.execute_command('x-terminal-emulator', flags='d')) - map('du', fm.execute_command('du --max-depth=1 -h | less')) - - # -------------------------------------------------- toggle options - map('b', fm.notify('Warning: settings are now changed with z!', bad=True)) - map('z', hint="show_//h//idden //p//review_files //d//irectories_first " \ - "//c//ollapse_preview flush//i//nput ca//s//e_insensitive") - map('zh', fm.toggle_boolean_option('show_hidden')) - map('zp', fm.toggle_boolean_option('preview_files')) - map('zP', fm.toggle_boolean_option('preview_directories')) - map('zi', fm.toggle_boolean_option('flushinput')) - map('zd', fm.toggle_boolean_option('sort_directories_first')) - map('zc', fm.toggle_boolean_option('collapse_preview')) - map('zs', fm.toggle_boolean_option('sort_case_insensitive')) - - # ------------------------------------------------------------ sort - map('o', 'O', hint="//s//ize //b//ase//n//ame //m//time //t//ype //r//everse") - sort_dict = { - 's': 'size', - 'b': 'basename', - 'n': 'basename', - 'm': 'mtime', - 't': 'type', - } - - for key, val in sort_dict.items(): - for key, is_capital in ((key, False), (key.upper(), True)): - # reverse if any of the two letters is capital - map('o' + key, fm.sort(func=val, reverse=is_capital)) - map('O' + key, fm.sort(func=val, reverse=True)) - - map('or', 'Or', 'oR', 'OR', lambda arg: \ - arg.fm.sort(reverse=not arg.fm.settings.sort_reverse)) - - # ----------------------------------------------- console shortcuts - @map("A") - def append_to_filename(arg): - command = 'rename ' + arg.fm.env.cf.basename - arg.fm.open_console(cmode.COMMAND, command) - - map('cw', fm.open_console(cmode.COMMAND, 'rename ')) - map('cd', fm.open_console(cmode.COMMAND, 'cd ')) - map('f', fm.open_console(cmode.COMMAND_QUICK, 'find ')) - map('tf', fm.open_console(cmode.COMMAND, 'filter ')) - map('d', hint='d//u// (disk usage) d//d// (cut)') - map('@', fm.open_console(cmode.OPEN, '@')) - map('#', fm.open_console(cmode.OPEN, 'p!')) - - # --------------------------------------------- jump to directories - map('gh', fm.cd('~')) - map('ge', fm.cd('/etc')) - map('gu', fm.cd('/usr')) - map('gd', fm.cd('/dev')) - map('gl', fm.cd('/lib')) - map('go', fm.cd('/opt')) - map('gv', fm.cd('/var')) - map('gr', 'g/', fm.cd('/')) - map('gm', fm.cd('/media')) - map('gn', fm.cd('/mnt')) - map('gt', fm.cd('/tmp')) - map('gs', fm.cd('/srv')) - map('gR', fm.cd(RANGERDIR)) - - # ------------------------------------------------------------ tabs - map('gc', ctrl('W'), fm.tab_close()) - map('gt', TAB, fm.tab_move(1)) - map('gT', KEY_BTAB, fm.tab_move(-1)) - map('gn', ctrl('N'), fm.tab_new()) - for n in range(10): - map('g' + str(n), fm.tab_open(n)) - - # ------------------------------------------------------- searching - map('/', fm.open_console(cmode.SEARCH)) - - map('n', fm.search()) - map('N', fm.search(forward=False)) - - map('ct', fm.search(order='tag')) - map('cc', fm.search(order='ctime')) - map('cm', fm.search(order='mimetype')) - map('cs', fm.search(order='size')) - map('c', hint='//c//time //m//imetype //s//ize //t//agged') - - # ------------------------------------------------------- bookmarks - for key in ALLOWED_BOOKMARK_KEYS: - map("`" + key, "'" + key, fm.enter_bookmark(key)) - map("m" + key, fm.set_bookmark(key)) - map("um" + key, fm.unset_bookmark(key)) - map("`", "'", "m", "um", draw_bookmarks=True) - - # ---------------------------------------------------- change views - map('i', fm.display_file()) - map(ctrl('p'), fm.display_log()) - map('?', KEY_F1, fm.display_help()) - map('w', lambda arg: arg.fm.ui.open_taskview()) - - # ---------------------------------------------------------- custom - # This is useful to track watched episode of a series. - @bind(']') - def tag_next_and_run(arg): - fm = arg.fm - fm.tag_remove() - fm.tag_remove(movedown=False) - fm.tag_toggle() - fm.move_pointer(relative=-2) - fm.move_right() - fm.move_pointer(relative=1) - - # "enter" = shortcut for "1l" - bind(KEY_ENTER, ctrl('j'), fm.move_right(mode=1)) - - # ------------------------------------------------ system functions - _system_functions(map) - map('ZZ', 'ZQ', fm.exit()) - map(ctrl('R'), fm.reset()) - map('R', fm.reload_cwd()) - @map(ctrl('C')) - def ctrl_c(arg): - try: - item = arg.fm.loader.queue[0] - except: - arg.fm.notify("Type Q or :quit to exit Ranger") - else: - arg.fm.notify("Aborting: " + item.get_description()) - arg.fm.loader.remove(index=0) - - map(':', ';', fm.open_console(cmode.COMMAND)) - map('>', fm.open_console(cmode.COMMAND_QUICK)) - map('!', fm.open_console(cmode.OPEN)) - map('r', fm.open_console(cmode.OPEN_QUICK)) - - map.rebuild_paths() - - -def initialize_console_commands(map): - """Initialize the commands for the console widget only""" - - _basic_movement(map) - _emacs_aliases(map) - - # -------------------------------------------------------- movement - map(KEY_UP, wdg.history_move(-1)) - map(KEY_DOWN, wdg.history_move(1)) - map(KEY_HOME, wdg.move(right=0, absolute=True)) - map(KEY_END, wdg.move(right=-1, absolute=True)) - - # ----------------------------------------- deleting / pasting text - map(KEY_DC, wdg.delete(0)) - map(KEY_BACKSPACE, DEL, wdg.delete(-1)) - map(ctrl('w'), wdg.delete_word()) - map(ctrl('k'), wdg.delete_rest(1)) - map(ctrl('u'), wdg.delete_rest(-1)) - map(ctrl('y'), wdg.paste()) - - # ------------------------------------------------ system functions - map(KEY_F1, lambda arg: arg.fm.display_command_help(arg.wdg)) - map(ctrl('c'), ESC, wdg.close()) - map(ctrl('j'), KEY_ENTER, wdg.execute()) - map(TAB, wdg.tab()) - map(KEY_BTAB, wdg.tab(-1)) - - map.rebuild_paths() - - -def initialize_taskview_commands(map): - """Initialize the commands for the TaskView widget""" - _basic_movement(map) - _vimlike_aliases(map) - _system_functions(map) - - # -------------------------------------------------- (re)move tasks - map('K', wdg.task_move(0)) - map('J', wdg.task_move(-1)) - map('dd', wdg.task_remove()) - - # ------------------------------------------------ system functions - map('?', fm.display_help()) - map('w', 'q', ESC, ctrl('d'), ctrl('c'), - lambda arg: arg.fm.ui.close_taskview()) - - map.rebuild_paths() - - -def initialize_pager_commands(map): - _base_pager_commands(map) - map('q', 'i', ESC, KEY_F1, lambda arg: arg.fm.ui.close_pager()) - map.rebuild_paths() - - -def initialize_embedded_pager_commands(map): - _base_pager_commands(map) - map('q', 'i', ESC, lambda arg: arg.fm.ui.close_embedded_pager()) - map.rebuild_paths() - - -def _base_pager_commands(map): - _basic_movement(map) - _vimlike_aliases(map) - _system_functions(map) - - # -------------------------------------------------------- movement - map(KEY_LEFT, wdg.move(left=4)) - map(KEY_RIGHT, wdg.move(right=4)) - map(KEY_NPAGE, ctrl('f'), wdg.move(down=1, pages=True)) - map(KEY_PPAGE, ctrl('b'), wdg.move(up=1, pages=True)) - map(ctrl('d'), wdg.move(down=0.5, pages=True)) - map(ctrl('u'), wdg.move(up=0.5, pages=True)) - map(' ', wdg.move(down=0.8, pages=True)) - - # ---------------------------------------------------------- others - map('E', fm.edit_file()) - map('?', fm.display_help()) - - # --------------------------------------------- less-like shortcuts - map.alias(KEY_NPAGE, 'f') - map.alias(KEY_PPAGE, 'b') - map.alias(ctrl('d'), 'd') - map.alias(ctrl('u'), 'u') - - -def _system_functions(map): - map('Q', fm.exit()) - map(ctrl('L'), fm.redraw_window()) - - -def _basic_movement(map): - map(KEY_DOWN, wdg.move(down=1)) - map(KEY_UP, wdg.move(up=1)) - map(KEY_RIGHT, wdg.move(right=1)) - map(KEY_LEFT, wdg.move(left=1)) - map(KEY_HOME, wdg.move(to=0)) - map(KEY_END, wdg.move(to=-1)) - - - -# ------ newkey: - - -def base_directions(): - # Direction Keys - map = KeyMap() - map('', dir=Direction(down=1)) - map('', dir=Direction(down=-1)) - map('', dir=Direction(right=-1)) - map('', dir=Direction(right=1)) - map('', dir=Direction(down=0, absolute=True)) - map('', dir=Direction(down=-1, absolute=True)) - map('', dir=Direction(down=1, pages=True)) - map('', dir=Direction(down=-1, pages=True)) - map('%', dir=Direction(down=1, percentage=True, absolute=True)) - map('', dir=Direction(down=1, pages=True)) - map('', dir=Direction(down=1)) - - return map - -def vim(): - # Direction Keys - map = KeyMap() - map.merge(base_directions()) - map('j', alias='') - map('k', alias='') - map('h', alias='') - map('l', alias='') - map('gg', alias='') - map('G', alias='') - map('J', dir=Direction(down=20)) - map('K', dir=Direction(down=-20)) - - return map - -def system_keys(): - map = KeyMap() - map('Q', fm.exit()) - map('', fm.handle_mouse()) - map('', fm.redraw_window()) - map('', fm.resize()) - - return map - -def browser_keys(): - map = KeyMap() - map.merge(system_keys()) - - @map('') - def move(arg): - arg.fm.move(narg=arg.n, **arg.direction) - map('gg', fm.move(to=0)) - map(fm.exit(), 'Q') - - map('', fm.move(dir=Direction(right=1))) - - # --------------------------------------------------------- history - map('H', fm.history_go(-1)) - map('L', fm.history_go(1)) - - # ----------------------------------------------- tagging / marking - map('t', fm.tag_toggle()) - map('T', fm.tag_remove()) - - map(' ', fm.mark(toggle=True)) - map('v', fm.mark(all=True, toggle=True)) - map('V', fm.mark(all=True, val=False)) - - # ------------------------------------------ file system operations - map('yy', fm.copy()) - map('dd', fm.cut()) - map('pp', fm.paste()) - map('po', fm.paste(overwrite=True)) - map('pl', fm.paste_symlink()) - map('p', fm.hint('press //p// once again to confirm pasting' \ - ', or //l// to create symlinks')) - - # ---------------------------------------------------- run programs - map('s', fm.execute_command(os.environ['SHELL'])) - map('E', fm.edit_file()) - map('.term', fm.execute_command('x-terminal-emulator', flags='d')) - map('du', fm.execute_command('du --max-depth=1 -h | less')) - - # -------------------------------------------------- toggle options - map('b', fm.hint("bind_//h//idden //p//review_files" \ - "//d//irectories_first //c//ollapse_preview flush//i//nput")) - map('bh', fm.toggle_boolean_option('show_hidden')) - map('bp', fm.toggle_boolean_option('preview_files')) - map('bi', fm.toggle_boolean_option('flushinput')) - map('bd', fm.toggle_boolean_option('directories_first')) - map('bc', fm.toggle_boolean_option('collapse_preview')) - - # ------------------------------------------------------------ sort - map('o', 'O', fm.hint("//s//ize //b//ase//n//ame //m//time" \ - " //t//ype //r//everse")) - sort_dict = { - 's': 'size', - 'b': 'basename', - 'n': 'basename', - 'm': 'mtime', - 't': 'type', - } - - for key, val in sort_dict.items(): - for key, is_capital in ((key, False), (key.upper(), True)): - # reverse if any of the two letters is capital - map('o' + key, fm.sort(func=val, reverse=is_capital)) - map('O' + key, fm.sort(func=val, reverse=True)) - - map('or', 'Or', 'oR', 'OR', lambda arg: \ - arg.fm.sort(reverse=not arg.fm.settings.reverse)) - - # ----------------------------------------------- console shortcuts - @map("A") - def append_to_filename(arg): - command = 'rename ' + arg.fm.env.cf.basename - arg.fm.open_console(cmode.COMMAND, command) - - map('cw', fm.open_console(cmode.COMMAND, 'rename ')) - map('cd', fm.open_console(cmode.COMMAND, 'cd ')) - map('f', fm.open_console(cmode.COMMAND_QUICK, 'find ')) - map('bf', fm.open_console(cmode.COMMAND, 'filter ')) - map('d', fm.hint('d//u// (disk usage) d//d// (cut)')) - - - # --------------------------------------------- jump to directories - map('gh', fm.cd('~')) - map('ge', fm.cd('/etc')) - map('gu', fm.cd('/usr')) - map('gd', fm.cd('/dev')) - map('gl', fm.cd('/lib')) - map('go', fm.cd('/opt')) - map('gv', fm.cd('/var')) - map('gr', 'g/', fm.cd('/')) - map('gm', fm.cd('/media')) - map('gn', fm.cd('/mnt')) - map('gs', fm.cd('/srv')) - map('gR', fm.cd(RANGERDIR)) - - # ------------------------------------------------------------ tabs - map('gc', ctrl('W'), fm.tab_close()) - map('gt', TAB, fm.tab_move(1)) - map('gT', KEY_BTAB, fm.tab_move(-1)) - map('gn', ctrl('N'), fm.tab_new()) - for n in range(10): - map('g' + str(n), fm.tab_open(n)) - map('', fm.tab_open(n)) - - # ------------------------------------------------------- searching - map('/', fm.open_console(cmode.SEARCH)) - - map('n', fm.search()) - map('N', fm.search(forward=False)) - - map('ct', fm.search(order='tag')) - map('cc', fm.search(order='ctime')) - map('cm', fm.search(order='mimetype')) - map('cs', fm.search(order='size')) - map('c', fm.hint('//c//time //m//imetype //s//ize')) - - # ------------------------------------------------------- bookmarks - for key in ALLOWED_BOOKMARK_KEYS: - map("`" + key, "'" + key, fm.enter_bookmark(key)) - map("m" + key, fm.set_bookmark(key)) - map("um" + key, fm.unset_bookmark(key)) - map("`", "'", "m", fm.draw_bookmarks()) - - - map(':', ';', fm.open_console(cmode.COMMAND)) - - # ---------------------------------------------------- change views - map('i', fm.display_file()) - map(ctrl('p'), fm.display_log()) - map('?', KEY_F1, fm.display_help()) - map('w', lambda arg: arg.fm.ui.open_taskview()) - - # ------------------------------------------------ system functions - map('ZZ', fm.exit()) - map(ctrl('R'), fm.reset()) - map('R', fm.reload_cwd()) - map(ctrl('C'), fm.exit()) - - map(':', ';', fm.open_console(cmode.COMMAND)) - map('>', fm.open_console(cmode.COMMAND_QUICK)) - map('!', fm.open_console(cmode.OPEN)) - map('r', fm.open_console(cmode.OPEN_QUICK)) - - return map - -def console_keys(): - map = KeyMap() - map.merge(system_keys()) - - @map('') - def type_key(arg): - arg.wdg.type_key(arg.match) - - map('', wdg.history_move(-1)) - map('', wdg.history_move(1)) - map('', wdg.tab()) - -#from pprint import pprint -#pprint(browser_keys()._tree[106].__dict__) -#raise SystemExit() - -ui_keys = browser_keys() -taskview_keys = ui_keys -pager_keys = ui_keys -embedded_pager_keys = ui_keys -console_keys = console_keys() -directions = vim() +# --------------------------------------------------------- +# Define keys for everywhere: +map = keymanager['general'] +@map('') +def move(arg): + arg.wdg.move(narg=arg.n, **arg.direction) + +map('Q', fm.exit()) +map('', fm.redraw_window()) + +# --------------------------------------------------------- +# Define keys in "general" context: +map = keymanager['general'] + + +map('j', fm.move(down=1)) +map('Q', fm.exit()) + +# --------------------------------------------------------- history +map('H', fm.history_go(-1)) +map('L', fm.history_go(1)) + +# ----------------------------------------------- tagging / marking +map('t', fm.tag_toggle()) +map('T', fm.tag_remove()) + +map(' ', fm.mark(toggle=True)) +map('v', fm.mark(all=True, toggle=True)) +map('V', fm.mark(all=True, val=False)) + +# --------------------------------------------------------- +# Define direction keys +map = keymanager.get_context('directions') +map('', dir=Direction(down=1)) +map('', dir=Direction(down=-1)) +map('', dir=Direction(right=-1)) +map('', dir=Direction(right=1)) +map('', dir=Direction(down=0, absolute=True)) +map('', dir=Direction(down=-1, absolute=True)) +map('', dir=Direction(down=1, pages=True)) +map('', dir=Direction(down=-1, pages=True)) +map('%', dir=Direction(down=1, percentage=True, absolute=True)) +map('', dir=Direction(down=1, pages=True)) +map('', dir=Direction(down=1)) diff --git a/ranger/defaults/oldkeys.py b/ranger/defaults/oldkeys.py new file mode 100644 index 00000000..467d26e6 --- /dev/null +++ b/ranger/defaults/oldkeys.py @@ -0,0 +1,555 @@ +# Copyright (C) 2009, 2010 Roman Zimbelmann +# +# 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 . + +""" +This is the default key configuration file of ranger. +Syntax for binding keys: map(*keys, fnc) + +keys are one or more key-combinations which are either: +* a string +* an integer which represents an ascii code +* a tuple of integers + +fnc is a function which is called with the CommandArgument object. + +The CommandArgument object has these attributes: +arg.fm: the file manager instance +arg.wdg: the widget or ui instance +arg.n: the number typed before the key combination (if allowed) +arg.keys: the string representation of the used key combination +arg.keybuffer: the keybuffer instance + +Check ranger.keyapi for more information +""" + +# NOTE: The "map" object used below is a callable CommandList +# object and NOT the builtin python map function! + +from ranger.api.keys import * + +def _vimlike_aliases(map): + alias = map.alias + + # the key 'k' will always do the same as KEY_UP, etc. + alias(KEY_UP, 'k') + alias(KEY_DOWN, 'j') + alias(KEY_LEFT, 'h') + alias(KEY_RIGHT, 'l') + + alias(KEY_NPAGE, ctrl('f')) + alias(KEY_PPAGE, ctrl('b')) + alias(KEY_HOME, 'gg') + alias(KEY_END, 'G') + + +def _emacs_aliases(map): + alias = map.alias + alias(KEY_LEFT, ctrl('b')) + alias(KEY_RIGHT, ctrl('f')) + alias(KEY_HOME, ctrl('a')) + alias(KEY_END, ctrl('e')) + alias(KEY_DC, ctrl('d')) + alias(DEL, ctrl('h')) + + +def initialize_commands(map): + """Initialize the commands for the main user interface""" + + # -------------------------------------------------------- movement + _vimlike_aliases(map) + _basic_movement(map) + + map.alias(KEY_LEFT, KEY_BACKSPACE, DEL) + map.alias(KEY_RIGHT, KEY_ENTER, ctrl('j')) + + map('%', fm.move(to=50, percentage=True)) + map(KEY_NPAGE, ctrl('f'), fm.move(down=1, pages=True)) + map(KEY_PPAGE, ctrl('b'), fm.move(up=1, pages=True)) + map(ctrl('d'), 'J', fm.move(down=0.5, pages=True)) + map(ctrl('u'), 'K', fm.move(up=0.5, pages=True)) + + map(']', fm.traverse()) + map('[', fm.history_go(-1)) + + # --------------------------------------------------------- history + map('H', fm.history_go(-1)) + map('L', fm.history_go(1)) + + # ----------------------------------------------- tagging / marking + map('t', fm.tag_toggle()) + map('T', fm.tag_remove()) + + map(' ', fm.mark(toggle=True)) + map('v', fm.mark(all=True, toggle=True)) + map('V', fm.mark(all=True, val=False)) + + # ------------------------------------------ file system operations + map('yy', fm.copy()) + map('dd', fm.cut()) + map('pp', fm.paste()) + map('po', fm.paste(overwrite=True)) + map('pl', fm.paste_symlink()) + map('p', hint='press //p// once again to confirm pasting' \ + ', or //l// to create symlinks') + + # ---------------------------------------------------- run programs + map('s', fm.execute_command(os.environ['SHELL'])) + map('E', fm.edit_file()) + map(',term', fm.execute_command('x-terminal-emulator', flags='d')) + map('du', fm.execute_command('du --max-depth=1 -h | less')) + + # -------------------------------------------------- toggle options + map('b', fm.notify('Warning: settings are now changed with z!', bad=True)) + map('z', hint="show_//h//idden //p//review_files //d//irectories_first " \ + "//c//ollapse_preview flush//i//nput ca//s//e_insensitive") + map('zh', fm.toggle_boolean_option('show_hidden')) + map('zp', fm.toggle_boolean_option('preview_files')) + map('zP', fm.toggle_boolean_option('preview_directories')) + map('zi', fm.toggle_boolean_option('flushinput')) + map('zd', fm.toggle_boolean_option('sort_directories_first')) + map('zc', fm.toggle_boolean_option('collapse_preview')) + map('zs', fm.toggle_boolean_option('sort_case_insensitive')) + + # ------------------------------------------------------------ sort + map('o', 'O', hint="//s//ize //b//ase//n//ame //m//time //t//ype //r//everse") + sort_dict = { + 's': 'size', + 'b': 'basename', + 'n': 'basename', + 'm': 'mtime', + 't': 'type', + } + + for key, val in sort_dict.items(): + for key, is_capital in ((key, False), (key.upper(), True)): + # reverse if any of the two letters is capital + map('o' + key, fm.sort(func=val, reverse=is_capital)) + map('O' + key, fm.sort(func=val, reverse=True)) + + map('or', 'Or', 'oR', 'OR', lambda arg: \ + arg.fm.sort(reverse=not arg.fm.settings.sort_reverse)) + + # ----------------------------------------------- console shortcuts + @map("A") + def append_to_filename(arg): + command = 'rename ' + arg.fm.env.cf.basename + arg.fm.open_console(cmode.COMMAND, command) + + map('cw', fm.open_console(cmode.COMMAND, 'rename ')) + map('cd', fm.open_console(cmode.COMMAND, 'cd ')) + map('f', fm.open_console(cmode.COMMAND_QUICK, 'find ')) + map('tf', fm.open_console(cmode.COMMAND, 'filter ')) + map('d', hint='d//u// (disk usage) d//d// (cut)') + map('@', fm.open_console(cmode.OPEN, '@')) + map('#', fm.open_console(cmode.OPEN, 'p!')) + + # --------------------------------------------- jump to directories + map('gh', fm.cd('~')) + map('ge', fm.cd('/etc')) + map('gu', fm.cd('/usr')) + map('gd', fm.cd('/dev')) + map('gl', fm.cd('/lib')) + map('go', fm.cd('/opt')) + map('gv', fm.cd('/var')) + map('gr', 'g/', fm.cd('/')) + map('gm', fm.cd('/media')) + map('gn', fm.cd('/mnt')) + map('gt', fm.cd('/tmp')) + map('gs', fm.cd('/srv')) + map('gR', fm.cd(RANGERDIR)) + + # ------------------------------------------------------------ tabs + map('gc', ctrl('W'), fm.tab_close()) + map('gt', TAB, fm.tab_move(1)) + map('gT', KEY_BTAB, fm.tab_move(-1)) + map('gn', ctrl('N'), fm.tab_new()) + for n in range(10): + map('g' + str(n), fm.tab_open(n)) + + # ------------------------------------------------------- searching + map('/', fm.open_console(cmode.SEARCH)) + + map('n', fm.search()) + map('N', fm.search(forward=False)) + + map('ct', fm.search(order='tag')) + map('cc', fm.search(order='ctime')) + map('cm', fm.search(order='mimetype')) + map('cs', fm.search(order='size')) + map('c', hint='//c//time //m//imetype //s//ize //t//agged') + + # ------------------------------------------------------- bookmarks + for key in ALLOWED_BOOKMARK_KEYS: + map("`" + key, "'" + key, fm.enter_bookmark(key)) + map("m" + key, fm.set_bookmark(key)) + map("um" + key, fm.unset_bookmark(key)) + map("`", "'", "m", "um", draw_bookmarks=True) + + # ---------------------------------------------------- change views + map('i', fm.display_file()) + map(ctrl('p'), fm.display_log()) + map('?', KEY_F1, fm.display_help()) + map('w', lambda arg: arg.fm.ui.open_taskview()) + + # ---------------------------------------------------------- custom + # This is useful to track watched episode of a series. + @bind(']') + def tag_next_and_run(arg): + fm = arg.fm + fm.tag_remove() + fm.tag_remove(movedown=False) + fm.tag_toggle() + fm.move_pointer(relative=-2) + fm.move_right() + fm.move_pointer(relative=1) + + # "enter" = shortcut for "1l" + bind(KEY_ENTER, ctrl('j'), fm.move_right(mode=1)) + + # ------------------------------------------------ system functions + _system_functions(map) + map('ZZ', 'ZQ', fm.exit()) + map(ctrl('R'), fm.reset()) + map('R', fm.reload_cwd()) + @map(ctrl('C')) + def ctrl_c(arg): + try: + item = arg.fm.loader.queue[0] + except: + arg.fm.notify("Type Q or :quit to exit Ranger") + else: + arg.fm.notify("Aborting: " + item.get_description()) + arg.fm.loader.remove(index=0) + + map(':', ';', fm.open_console(cmode.COMMAND)) + map('>', fm.open_console(cmode.COMMAND_QUICK)) + map('!', fm.open_console(cmode.OPEN)) + map('r', fm.open_console(cmode.OPEN_QUICK)) + + map.rebuild_paths() + + +def initialize_console_commands(map): + """Initialize the commands for the console widget only""" + + _basic_movement(map) + _emacs_aliases(map) + + # -------------------------------------------------------- movement + map(KEY_UP, wdg.history_move(-1)) + map(KEY_DOWN, wdg.history_move(1)) + map(KEY_HOME, wdg.move(right=0, absolute=True)) + map(KEY_END, wdg.move(right=-1, absolute=True)) + + # ----------------------------------------- deleting / pasting text + map(KEY_DC, wdg.delete(0)) + map(KEY_BACKSPACE, DEL, wdg.delete(-1)) + map(ctrl('w'), wdg.delete_word()) + map(ctrl('k'), wdg.delete_rest(1)) + map(ctrl('u'), wdg.delete_rest(-1)) + map(ctrl('y'), wdg.paste()) + + # ------------------------------------------------ system functions + map(KEY_F1, lambda arg: arg.fm.display_command_help(arg.wdg)) + map(ctrl('c'), ESC, wdg.close()) + map(ctrl('j'), KEY_ENTER, wdg.execute()) + map(TAB, wdg.tab()) + map(KEY_BTAB, wdg.tab(-1)) + + map.rebuild_paths() + + +def initialize_taskview_commands(map): + """Initialize the commands for the TaskView widget""" + _basic_movement(map) + _vimlike_aliases(map) + _system_functions(map) + + # -------------------------------------------------- (re)move tasks + map('K', wdg.task_move(0)) + map('J', wdg.task_move(-1)) + map('dd', wdg.task_remove()) + + # ------------------------------------------------ system functions + map('?', fm.display_help()) + map('w', 'q', ESC, ctrl('d'), ctrl('c'), + lambda arg: arg.fm.ui.close_taskview()) + + map.rebuild_paths() + + +def initialize_pager_commands(map): + _base_pager_commands(map) + map('q', 'i', ESC, KEY_F1, lambda arg: arg.fm.ui.close_pager()) + map.rebuild_paths() + + +def initialize_embedded_pager_commands(map): + _base_pager_commands(map) + map('q', 'i', ESC, lambda arg: arg.fm.ui.close_embedded_pager()) + map.rebuild_paths() + + +def _base_pager_commands(map): + _basic_movement(map) + _vimlike_aliases(map) + _system_functions(map) + + # -------------------------------------------------------- movement + map(KEY_LEFT, wdg.move(left=4)) + map(KEY_RIGHT, wdg.move(right=4)) + map(KEY_NPAGE, ctrl('f'), wdg.move(down=1, pages=True)) + map(KEY_PPAGE, ctrl('b'), wdg.move(up=1, pages=True)) + map(ctrl('d'), wdg.move(down=0.5, pages=True)) + map(ctrl('u'), wdg.move(up=0.5, pages=True)) + map(' ', wdg.move(down=0.8, pages=True)) + + # ---------------------------------------------------------- others + map('E', fm.edit_file()) + map('?', fm.display_help()) + + # --------------------------------------------- less-like shortcuts + map.alias(KEY_NPAGE, 'f') + map.alias(KEY_PPAGE, 'b') + map.alias(ctrl('d'), 'd') + map.alias(ctrl('u'), 'u') + + +def _system_functions(map): + map('Q', fm.exit()) + map(ctrl('L'), fm.redraw_window()) + + +def _basic_movement(map): + map(KEY_DOWN, wdg.move(down=1)) + map(KEY_UP, wdg.move(up=1)) + map(KEY_RIGHT, wdg.move(right=1)) + map(KEY_LEFT, wdg.move(left=1)) + map(KEY_HOME, wdg.move(to=0)) + map(KEY_END, wdg.move(to=-1)) + + + +# ------ newkey: + + +def base_directions(): + # Direction Keys + map = KeyMap() + map('', dir=Direction(down=1)) + map('', dir=Direction(down=-1)) + map('', dir=Direction(right=-1)) + map('', dir=Direction(right=1)) + map('', dir=Direction(down=0, absolute=True)) + map('', dir=Direction(down=-1, absolute=True)) + map('', dir=Direction(down=1, pages=True)) + map('', dir=Direction(down=-1, pages=True)) + map('%', dir=Direction(down=1, percentage=True, absolute=True)) + map('', dir=Direction(down=1, pages=True)) + map('', dir=Direction(down=1)) + + return map + +def vim(): + # Direction Keys + map = KeyMap() + map.merge(base_directions()) + map('j', alias='') + map('k', alias='') + map('h', alias='') + map('l', alias='') + map('gg', alias='') + map('G', alias='') + map('J', dir=Direction(down=20)) + map('K', dir=Direction(down=-20)) + + return map + +def system_keys(): + map = KeyMap() + map('Q', fm.exit()) + map('', fm.handle_mouse()) + map('', fm.redraw_window()) + map('', fm.resize()) + + return map + +def browser_keys(): + map = KeyMap() + map.merge(system_keys()) + + @map('') + def move(arg): + arg.fm.move(narg=arg.n, **arg.direction) + map('gg', fm.move(to=0)) + map(fm.exit(), 'Q') + + map('', fm.move(dir=Direction(right=1))) + + # --------------------------------------------------------- history + map('H', fm.history_go(-1)) + map('L', fm.history_go(1)) + + # ----------------------------------------------- tagging / marking + map('t', fm.tag_toggle()) + map('T', fm.tag_remove()) + + map(' ', fm.mark(toggle=True)) + map('v', fm.mark(all=True, toggle=True)) + map('V', fm.mark(all=True, val=False)) + + # ------------------------------------------ file system operations + map('yy', fm.copy()) + map('dd', fm.cut()) + map('pp', fm.paste()) + map('po', fm.paste(overwrite=True)) + map('pl', fm.paste_symlink()) + map('p', fm.hint('press //p// once again to confirm pasting' \ + ', or //l// to create symlinks')) + + # ---------------------------------------------------- run programs + map('s', fm.execute_command(os.environ['SHELL'])) + map('E', fm.edit_file()) + map('.term', fm.execute_command('x-terminal-emulator', flags='d')) + map('du', fm.execute_command('du --max-depth=1 -h | less')) + + # -------------------------------------------------- toggle options + map('b', fm.hint("bind_//h//idden //p//review_files" \ + "//d//irectories_first //c//ollapse_preview flush//i//nput")) + map('bh', fm.toggle_boolean_option('show_hidden')) + map('bp', fm.toggle_boolean_option('preview_files')) + map('bi', fm.toggle_boolean_option('flushinput')) + map('bd', fm.toggle_boolean_option('directories_first')) + map('bc', fm.toggle_boolean_option('collapse_preview')) + + # ------------------------------------------------------------ sort + map('o', 'O', fm.hint("//s//ize //b//ase//n//ame //m//time" \ + " //t//ype //r//everse")) + sort_dict = { + 's': 'size', + 'b': 'basename', + 'n': 'basename', + 'm': 'mtime', + 't': 'type', + } + + for key, val in sort_dict.items(): + for key, is_capital in ((key, False), (key.upper(), True)): + # reverse if any of the two letters is capital + map('o' + key, fm.sort(func=val, reverse=is_capital)) + map('O' + key, fm.sort(func=val, reverse=True)) + + map('or', 'Or', 'oR', 'OR', lambda arg: \ + arg.fm.sort(reverse=not arg.fm.settings.reverse)) + + # ----------------------------------------------- console shortcuts + @map("A") + def append_to_filename(arg): + command = 'rename ' + arg.fm.env.cf.basename + arg.fm.open_console(cmode.COMMAND, command) + + map('cw', fm.open_console(cmode.COMMAND, 'rename ')) + map('cd', fm.open_console(cmode.COMMAND, 'cd ')) + map('f', fm.open_console(cmode.COMMAND_QUICK, 'find ')) + map('bf', fm.open_console(cmode.COMMAND, 'filter ')) + map('d', fm.hint('d//u// (disk usage) d//d// (cut)')) + + + # --------------------------------------------- jump to directories + map('gh', fm.cd('~')) + map('ge', fm.cd('/etc')) + map('gu', fm.cd('/usr')) + map('gd', fm.cd('/dev')) + map('gl', fm.cd('/lib')) + map('go', fm.cd('/opt')) + map('gv', fm.cd('/var')) + map('gr', 'g/', fm.cd('/')) + map('gm', fm.cd('/media')) + map('gn', fm.cd('/mnt')) + map('gs', fm.cd('/srv')) + map('gR', fm.cd(RANGERDIR)) + + # ------------------------------------------------------------ tabs + map('gc', ctrl('W'), fm.tab_close()) + map('gt', TAB, fm.tab_move(1)) + map('gT', KEY_BTAB, fm.tab_move(-1)) + map('gn', ctrl('N'), fm.tab_new()) + for n in range(10): + map('g' + str(n), fm.tab_open(n)) + map('', fm.tab_open(n)) + + # ------------------------------------------------------- searching + map('/', fm.open_console(cmode.SEARCH)) + + map('n', fm.search()) + map('N', fm.search(forward=False)) + + map('ct', fm.search(order='tag')) + map('cc', fm.search(order='ctime')) + map('cm', fm.search(order='mimetype')) + map('cs', fm.search(order='size')) + map('c', fm.hint('//c//time //m//imetype //s//ize')) + + # ------------------------------------------------------- bookmarks + for key in ALLOWED_BOOKMARK_KEYS: + map("`" + key, "'" + key, fm.enter_bookmark(key)) + map("m" + key, fm.set_bookmark(key)) + map("um" + key, fm.unset_bookmark(key)) + map("`", "'", "m", fm.draw_bookmarks()) + + + map(':', ';', fm.open_console(cmode.COMMAND)) + + # ---------------------------------------------------- change views + map('i', fm.display_file()) + map(ctrl('p'), fm.display_log()) + map('?', KEY_F1, fm.display_help()) + map('w', lambda arg: arg.fm.ui.open_taskview()) + + # ------------------------------------------------ system functions + map('ZZ', fm.exit()) + map(ctrl('R'), fm.reset()) + map('R', fm.reload_cwd()) + map(ctrl('C'), fm.exit()) + + map(':', ';', fm.open_console(cmode.COMMAND)) + map('>', fm.open_console(cmode.COMMAND_QUICK)) + map('!', fm.open_console(cmode.OPEN)) + map('r', fm.open_console(cmode.OPEN_QUICK)) + + return map + +def console_keys(): + map = KeyMap() + map.merge(system_keys()) + + @map('') + def type_key(arg): + arg.wdg.type_key(arg.match) + + map('', wdg.history_move(-1)) + map('', wdg.history_move(1)) + map('', wdg.tab()) + +#from pprint import pprint +#pprint(browser_keys()._tree[106].__dict__) +#raise SystemExit() + +ui_keys = browser_keys() +taskview_keys = ui_keys +pager_keys = ui_keys +embedded_pager_keys = ui_keys +console_keys = console_keys() +directions = vim() diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index 8d355665..f81f1c5d 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -31,7 +31,7 @@ class UI(DisplayableContainer): is_set_up = False mousemask = curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION load_mode = False - def __init__(self, keymap=None, env=None, fm=None): + def __init__(self, env=None, fm=None): self._draw_title = os.environ["TERM"] in TERMINALS_WITH_TITLE os.environ['ESCDELAY'] = '25' # don't know a cleaner way @@ -40,12 +40,8 @@ class UI(DisplayableContainer): if fm is not None: self.fm = fm - if keymap is None: - self.keymap = self.settings.keys.browser_keys() - else: - self.keymap = keymap self.win = curses.initscr() - self.env.keybuffer.assign(self.keymap, self.settings.keys.directions) + self.env.keymanager.use_context('general') self.env.keybuffer.clear() DisplayableContainer.__init__(self, None) @@ -131,11 +127,11 @@ class UI(DisplayableContainer): self.env.keybuffer.clear() return - self.env.key_append(key) - if DisplayableContainer.press(self, key): return + self.env.keymanager.use_context('general') + self.env.key_append(key) kbuf = self.env.keybuffer cmd = kbuf.command diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index aaa85d8e..67bd7893 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -59,7 +59,6 @@ class Console(Widget): def __init__(self, win): from ranger.container import History Widget.__init__(self, win) - self.keymap = self.settings.keys.console_keys self.clear() self.histories = [] # load histories from files @@ -154,8 +153,6 @@ class Console(Widget): self.line = '' def press(self, key): - from curses.ascii import ctrl, ESC - keytuple = self.env.keybuffer.tuple_with_numbers() try: cmd = self.commandlist[keytuple] diff --git a/ranger/gui/widgets/pager.py b/ranger/gui/widgets/pager.py index e915f790..b99bf5db 100644 --- a/ranger/gui/widgets/pager.py +++ b/ranger/gui/widgets/pager.py @@ -40,11 +40,6 @@ class Pager(Widget): self.markup = None self.lines = [] - if embedded: - self.keymap = self.settings.keys.embedded_pager_keys - else: - self.keymap = self.settings.keys.pager_keys - def move_horizontal(self, *a, **k): """For compatibility""" self.fm.notify("Your keys.py is out of date. Can't scroll!", bad=True) diff --git a/ranger/gui/widgets/taskview.py b/ranger/gui/widgets/taskview.py index f7937e11..fe31646d 100644 --- a/ranger/gui/widgets/taskview.py +++ b/ranger/gui/widgets/taskview.py @@ -30,7 +30,6 @@ class TaskView(Widget, Accumulator): Widget.__init__(self, win) Accumulator.__init__(self) self.scroll_begin = 0 - self.keymap = self.settings.keys.taskview_keys def draw(self): base_clr = deque() diff --git a/ranger/shared/settings.py b/ranger/shared/settings.py index a4a58e6e..e4a58f33 100644 --- a/ranger/shared/settings.py +++ b/ranger/shared/settings.py @@ -154,10 +154,16 @@ class SettingsAware(object): except ImportError: from ranger.defaults import apps settings._raw_set('apps', apps) + + SettingsAware.settings = settings + + @staticmethod + def _setup_keys(): # ugly! but works. + import ranger.api.keys + import ranger.shared + env = ranger.shared.EnvironmentAware.env + ranger.api.keys.keymanager = env.keymanager try: import keys except ImportError: from ranger.defaults import keys - settings._raw_set('keys', keys) - - SettingsAware.settings = settings diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index c953e88b..b1cb42fb 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -24,7 +24,7 @@ import sys class PressTestCase(TestCase): """Some useful methods for the actual test""" - def _mkpress(self, keybuffer, keymap): + def _mkpress(self, keybuffer, _=0): def press(keys): keybuffer.clear() match = keybuffer.simulate_press(keys) @@ -492,5 +492,42 @@ class Test(PressTestCase): s.replace('Y') self.assertNotEqual(t._tree, u._tree) + def test_keymap_with_context(self): + def func(arg): + return 5 + def getdown(arg): + return arg.direction.down() + + buffer = KeyBuffer(None, None) + press = self._mkpress(buffer) + kmc = KeyManager(buffer, ['foo', 'bar']) + + map = kmc.get_context('foo') + map('a', func) + map('b', func) + map = kmc.get_context('bar') + map('c', func) + map('', getdown) + + kmc.map('directions', 'j', dir=Direction(down=1)) + + kmc.use_context('foo') + self.assertEqual(5, press('a')) + self.assertEqual(5, press('b')) + self.assertPressFails(buffer, 'c') + + kmc.use_context('bar') + self.assertPressFails(buffer, 'a') + self.assertPressFails(buffer, 'b') + self.assertEqual(5, press('c')) + self.assertEqual(1, press('j')) + kmc.use_context('foo') + kmc.use_context('foo') + kmc.use_context('foo') + kmc.use_context('bar') + kmc.use_context('foo') + kmc.use_context('bar') + kmc.use_context('bar') + self.assertEqual(1, press('j')) if __name__ == '__main__': main() diff --git a/test/tc_ui.py b/test/tc_ui.py index 98ddff93..3c659459 100644 --- a/test/tc_ui.py +++ b/test/tc_ui.py @@ -28,7 +28,7 @@ class Test(unittest.TestCase): def setUp(self): self.fm = Fake() - self.ui = ui.UI(env=Fake(), fm=self.fm, keymap=Fake()) + self.ui = ui.UI(env=Fake(), fm=self.fm) def fakesetup(): self.ui.widget = Fake() -- cgit 1.4.1-2-gfad0 From 2bd8353d66338153267084b7c0fb79e21b62e914 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 7 Apr 2010 22:46:56 +0200 Subject: start to integrate keybindings --- ranger/container/keymap.py | 19 +++- ranger/defaults/keys.py | 224 +++++++++++++++++++++++++++++++++++++----- ranger/gui/widgets/console.py | 48 ++++----- ranger/shared/settings.py | 3 +- 4 files changed, 241 insertions(+), 53 deletions(-) diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index 62cf0e7a..930800ff 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -13,7 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import curses +import curses.ascii from string import ascii_lowercase from inspect import isfunction, getargspec from ranger.ext.tree import Tree @@ -80,6 +80,10 @@ class KeyMap(Tree): for key in keys: self.set(translate_keys(key), bind) + def unmap(self, *keys): + for key in keys: + self.unset(translate_keys(key)) + def __getitem__(self, key): return self.traverse(translate_keys(key)) @@ -87,11 +91,14 @@ class KeyMap(Tree): class KeyManager(object): def __init__(self, keybuffer, contexts): self._keybuffer = keybuffer + self._list_of_contexts = contexts + self.clear() + + def clear(self): self._contexts = { - 'any': KeyMap(), 'directions': KeyMap(), } - for context in contexts: + for context in self._list_of_contexts: self._contexts[context] = KeyMap() def map(self, context, *args, **keywords): @@ -305,6 +312,9 @@ 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(" "), @@ -312,13 +322,12 @@ special_keys = { 'up': curses.KEY_UP, 'left': curses.KEY_LEFT, 'right': curses.KEY_RIGHT, - 'mouse': curses.KEY_MOUSE, - 'resize': curses.KEY_RESIZE, '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 diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 72558a23..031bc402 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -17,46 +17,60 @@ This is the default key configuration file of ranger. Syntax for binding keys: map(*keys, fnc) -keys are one or more key-combinations which are either: -* a string -* an integer which represents an ascii code -* a tuple of integers +Examples for keys: "x", "gg", "", "", "" -fnc is a function which is called with the CommandArgument object. +fnc is a function which is called with the CommandArgs object. -The CommandArgument object has these attributes: +The CommandArgs object has these attributes: arg.fm: the file manager instance arg.wdg: the widget or ui instance arg.n: the number typed before the key combination (if allowed) arg.keys: the string representation of the used key combination arg.keybuffer: the keybuffer instance - -Check ranger.keyapi for more information """ -# NOTE: The "map" object used below is a callable CommandList -# object and NOT the builtin python map function! - from ranger.api.keys import * +from ranger import log +# =================================================================== +# == Define keys for everywhere: +# =================================================================== +map = global_keys = KeyMap() +map('Q', fm.exit()) +map('', fm.redraw_window()) +map('', alias='') # Backspace is bugged sometimes -# --------------------------------------------------------- -# Define keys for everywhere: -map = keymanager['general'] -@map('') +@map('') # move around with direction keys def move(arg): arg.wdg.move(narg=arg.n, **arg.direction) -map('Q', fm.exit()) -map('', fm.redraw_window()) - -# --------------------------------------------------------- -# Define keys in "general" context: -map = keymanager['general'] +# =================================================================== +# == Define aliases +# =================================================================== +map = vim_aliases = KeyMap() +map('j', alias='') +map('k', alias='') +map('h', alias='') +map('l', alias='') +map('gg', alias='') +map('G', alias='') +map('', alias='') +map('', alias='') +map = readline_aliases = KeyMap() +map('', alias='') +map('', alias='') +map('', alias='') +map('', alias='') +map('', alias='') +map('', alias='') -map('j', fm.move(down=1)) -map('Q', fm.exit()) +# =================================================================== +# == Define keys in "general" context: +# =================================================================== +map = keymanager['general'] +map.merge(global_keys) +map.merge(vim_aliases) # --------------------------------------------------------- history map('H', fm.history_go(-1)) @@ -70,8 +84,168 @@ map(' ', fm.mark(toggle=True)) map('v', fm.mark(all=True, toggle=True)) map('V', fm.mark(all=True, val=False)) -# --------------------------------------------------------- -# Define direction keys +# ------------------------------------------ file system operations +map('yy', fm.copy()) +map('dd', fm.cut()) +map('pp', fm.paste()) +map('po', fm.paste(overwrite=True)) +map('pl', fm.paste_symlink()) +map('p', fm.hint('press //p// once again to confirm pasting' \ + ', or //l// to create symlinks')) + +# ---------------------------------------------------- run programs +map('s', fm.execute_command(os.environ['SHELL'])) +map('E', fm.edit_file()) +map('.term', fm.execute_command('x-terminal-emulator', flags='d')) +map('du', fm.execute_command('du --max-depth=1 -h | less')) + +# -------------------------------------------------- toggle options +map('z', fm.hint("bind_//h//idden //p//review_files" \ + "//d//irectories_first //c//ollapse_preview flush//i//nput")) +map('zh', fm.toggle_boolean_option('show_hidden')) +map('zp', fm.toggle_boolean_option('preview_files')) +map('zi', fm.toggle_boolean_option('flushinput')) +map('zd', fm.toggle_boolean_option('directories_first')) +map('zc', fm.toggle_boolean_option('collapse_preview')) + +# ------------------------------------------------------------ sort +map('o', 'O', fm.hint("//s//ize //b//ase//n//ame //m//time" \ + " //t//ype //r//everse")) +sort_dict = { + 's': 'size', + 'b': 'basename', + 'n': 'basename', + 'm': 'mtime', + 't': 'type', +} + +for key, val in sort_dict.items(): + for key, is_capital in ((key, False), (key.upper(), True)): + # reverse if any of the two letters is capital + map('o' + key, fm.sort(func=val, reverse=is_capital)) + map('O' + key, fm.sort(func=val, reverse=True)) + +map('or', 'Or', 'oR', 'OR', lambda arg: \ + arg.fm.sort(reverse=not arg.fm.settings.sort_reverse)) + +# ----------------------------------------------- console shortcuts +@map("A") +def append_to_filename(arg): + command = 'rename ' + arg.fm.env.cf.basename + arg.fm.open_console(cmode.COMMAND, command) + +map('cw', fm.open_console(cmode.COMMAND, 'rename ')) +map('cd', fm.open_console(cmode.COMMAND, 'cd ')) +map('f', fm.open_console(cmode.COMMAND_QUICK, 'find ')) +map('bf', fm.open_console(cmode.COMMAND, 'filter ')) +map('d', fm.hint('d//u// (disk usage) d//d// (cut)')) +map('@', fm.open_console(cmode.OPEN, '@')) +map('#', fm.open_console(cmode.OPEN, 'p!')) + +# --------------------------------------------- jump to directories +map('gh', fm.cd('~')) +map('ge', fm.cd('/etc')) +map('gu', fm.cd('/usr')) +map('gd', fm.cd('/dev')) +map('gl', fm.cd('/lib')) +map('go', fm.cd('/opt')) +map('gv', fm.cd('/var')) +map('gr', 'g/', fm.cd('/')) +map('gm', fm.cd('/media')) +map('gn', fm.cd('/mnt')) +map('gs', fm.cd('/srv')) +map('gR', fm.cd(RANGERDIR)) + +# ------------------------------------------------------------ tabs +map('gc', '', fm.tab_close()) +map('gt', '', fm.tab_move(1)) +map('gT', '', fm.tab_move(-1)) +map('gn', '', fm.tab_new()) +for n in range(10): + map('g' + str(n), fm.tab_open(n)) + map('', fm.tab_open(n)) + +# ------------------------------------------------------- searching +map('/', fm.open_console(cmode.SEARCH)) + +map('n', fm.search()) +map('N', fm.search(forward=False)) + +map('ct', fm.search(order='tag')) +map('cc', fm.search(order='ctime')) +map('cm', fm.search(order='mimetype')) +map('cs', fm.search(order='size')) +map('c', fm.hint('//c//time //m//imetype //s//ize')) + +# ------------------------------------------------------- bookmarks +for key in ALLOWED_BOOKMARK_KEYS: + map("`" + key, "'" + key, fm.enter_bookmark(key)) + map("m" + key, fm.set_bookmark(key)) + map("um" + key, fm.unset_bookmark(key)) +map("`", "'", "m", fm.draw_bookmarks()) + +# ---------------------------------------------------- change views +map('i', fm.display_file()) +map('', fm.display_log()) +map('?', KEY_F1, fm.display_help()) +map('w', lambda arg: arg.fm.ui.open_taskview()) + +# ------------------------------------------------ system functions +map('ZZ', fm.exit()) +map('', fm.reset()) +map('R', fm.reload_cwd()) +@map('') +def ctrl_c(arg): + try: + item = arg.fm.loader.queue[0] + except: + arg.fm.notify("Type Q or :quit to exit Ranger") + else: + arg.fm.notify("Aborting: " + item.get_description()) + arg.fm.loader.remove(index=0) + +map(':', ';', fm.open_console(cmode.COMMAND)) +map('>', fm.open_console(cmode.COMMAND_QUICK)) +map('!', fm.open_console(cmode.OPEN)) +map('r', fm.open_console(cmode.OPEN_QUICK)) + +# =================================================================== +# == Define keys for the console +# =================================================================== +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('') # define my own direction keys + +map('a', wdg.type_key('a')) +map('', wdg.history_move(-1)) +map('', wdg.history_move(1)) +map('', wdg.move(right=0, absolute=True)) +map('', wdg.move(right=-1, absolute=True)) +map('', wdg.tab()) +map('', wdg.tab(-1)) +map('', wdg.close()) +map('', '', wdg.execute()) +map('', lambda arg: arg.fm.display_command_help(arg.wdg)) + +map('', wdg.delete(-1)) +map('', wdg.delete(1)) +map('', wdg.delete_word()) +map('', wdg.delete_rest(1)) +map('', wdg.delete_rest(-1)) +map('', wdg.paste()) + +def type_key(arg): + log('x') + arg.wdg.type_key(arg.match) +map('', type_key) +log(map._tree) + + +# =================================================================== +# == Define direction keys +# =================================================================== map = keymanager.get_context('directions') map('', dir=Direction(down=1)) map('', dir=Direction(down=-1)) diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 67bd7893..77ba4424 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -28,6 +28,7 @@ from ranger.gui.widgets.console_mode import is_valid_mode, mode_to_class from ranger import log, relpath_conf from ranger.ext.shell_escape import shell_quote from ranger.ext.direction import Direction +from ranger.container.keymap import CommandArgs import ranger DEFAULT_HISTORY = 0 @@ -153,36 +154,39 @@ class Console(Widget): self.line = '' def press(self, key): - keytuple = self.env.keybuffer.tuple_with_numbers() - try: - cmd = self.commandlist[keytuple] - except KeyError: - # An unclean hack to allow unicode input. - # This whole part should be replaced. - try: - chrkey = chr(keytuple[0]) - except: - pass - else: - self.type_key(chrkey) - finally: - self.env.key_clear() - return + self.env.keymanager.use_context('console') + self.env.key_append(key) + kbuf = self.env.keybuffer + cmd = kbuf.command - if cmd == self.commandlist.dummy_object: + self.fm.hide_bookmarks() + + if kbuf.failure: + kbuf.clear() + return + elif not cmd: return - try: - cmd.execute_wrap(self) - except Exception as error: - self.fm.notify(error) - self.env.key_clear() + self.env.cmd = cmd + + if cmd.function: + try: + cmd.function(CommandArgs.from_widget(self)) + except Exception as error: + self.fm.notify(error) + if kbuf.done: + kbuf.clear() + else: + kbuf.clear() def type_key(self, key): self.tab_deque = None if isinstance(key, int): - key = chr(key) + try: + key = chr(key) + except ValueError: + return if self.pos == len(self.line): self.line += key diff --git a/ranger/shared/settings.py b/ranger/shared/settings.py index e4a58f33..4c01f570 100644 --- a/ranger/shared/settings.py +++ b/ranger/shared/settings.py @@ -163,7 +163,8 @@ class SettingsAware(object): import ranger.shared env = ranger.shared.EnvironmentAware.env ranger.api.keys.keymanager = env.keymanager + from ranger.defaults import keys try: import keys except ImportError: - from ranger.defaults import keys + pass -- cgit 1.4.1-2-gfad0 From 3ec6f466744a45135c4e592e003f99f775da5e3e Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 7 Apr 2010 23:05:23 +0200 Subject: more keys, fixed hint --- ranger/core/actions.py | 1 + ranger/defaults/keys.py | 59 ++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index fb2973dd..99df7875 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -95,6 +95,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): self.log.appendleft(text) if hasattr(self.ui, 'notify'): self.ui.notify(text, duration=duration, bad=bad) + hint = notify def redraw_window(self): """Redraw the window""" diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 031bc402..62cd46c9 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -44,6 +44,7 @@ map('', alias='') # Backspace is bugged sometimes def move(arg): arg.wdg.move(narg=arg.n, **arg.direction) + # =================================================================== # == Define aliases # =================================================================== @@ -65,6 +66,7 @@ map('', alias='') map('', alias='') map('', alias='') + # =================================================================== # == Define keys in "general" context: # =================================================================== @@ -100,7 +102,7 @@ map('.term', fm.execute_command('x-terminal-emulator', flags='d')) map('du', fm.execute_command('du --max-depth=1 -h | less')) # -------------------------------------------------- toggle options -map('z', fm.hint("bind_//h//idden //p//review_files" \ +map('z', fm.hint("show_//h//idden //p//review_files" \ "//d//irectories_first //c//ollapse_preview flush//i//nput")) map('zh', fm.toggle_boolean_option('show_hidden')) map('zp', fm.toggle_boolean_option('preview_files')) @@ -191,7 +193,7 @@ map('?', KEY_F1, fm.display_help()) map('w', lambda arg: arg.fm.ui.open_taskview()) # ------------------------------------------------ system functions -map('ZZ', fm.exit()) +map('ZZ', 'ZQ', fm.exit()) map('', fm.reset()) map('R', fm.reload_cwd()) @map('') @@ -209,6 +211,53 @@ map('>', fm.open_console(cmode.COMMAND_QUICK)) map('!', fm.open_console(cmode.OPEN)) map('r', fm.open_console(cmode.OPEN_QUICK)) + +# =================================================================== +# == Define keys for the pager +# =================================================================== +map = pager_keys = KeyMap() +map.merge(global_keys) +map.merge(vim_aliases) + +# -------------------------------------------------------- movement +map('', wdg.move(left=4)) +map('', wdg.move(right=4)) +map('', 'd', wdg.move(down=0.5, pages=True)) +map('', 'u', wdg.move(up=0.5, pages=True)) +map('', 'f', '', wdg.move(down=1, pages=True)) +map('', 'b', '', wdg.move(up=1, pages=True)) +map('', wdg.move(down=0.8, pages=True)) + +# ---------------------------------------------------------- others +map('E', fm.edit_file()) +map('?', fm.display_help()) + +# --------------------------------------------------- bind the keys +# There are two different kinds of pagers, each have a different +# method for exiting: + +map = keymanager.get_context('pager') +map.merge(pager_keys) +map('q', 'i', '', lambda arg: arg.fm.ui.close_pager()) + +map = keymanager.get_context('embedded_pager') +map.merge(pager_keys) +map('q', 'i', '', lambda arg: arg.fm.ui.close_embedded_pager()) + + +# =================================================================== +# == Define keys for the taskview +# =================================================================== +map = keymanager.get_context('taskview') +map('K', wdg.task_move(0)) +map('J', wdg.task_move(-1)) +map('dd', wdg.task_remove()) + +map('?', fm.display_help()) +map('w', 'q', ESC, ctrl('d'), ctrl('c'), + lambda arg: arg.fm.ui.close_taskview()) + + # =================================================================== # == Define keys for the console # =================================================================== @@ -236,11 +285,9 @@ map('', wdg.delete_rest(1)) map('', wdg.delete_rest(-1)) map('', wdg.paste()) +map('') def type_key(arg): - log('x') arg.wdg.type_key(arg.match) -map('', type_key) -log(map._tree) # =================================================================== @@ -255,6 +302,6 @@ map('', dir=Direction(down=0, absolute=True)) map('', dir=Direction(down=-1, absolute=True)) map('', dir=Direction(down=1, pages=True)) map('', dir=Direction(down=-1, pages=True)) -map('%', dir=Direction(down=1, percentage=True, absolute=True)) +map('%', dir=Direction(down=1, percentage=True, absolute=True)) map('', dir=Direction(down=1, pages=True)) map('', dir=Direction(down=1)) -- cgit 1.4.1-2-gfad0 From c4e869ddc99c6737a5429705ed949c54c37f9ece Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 7 Apr 2010 23:13:57 +0200 Subject: integrated new keyparser in pager + taskview --- ranger/gui/widgets/console.py | 2 -- ranger/gui/widgets/pager.py | 35 +++++++++++++++++++++-------------- ranger/gui/widgets/taskview.py | 37 ++++++++++++++++++++++--------------- 3 files changed, 43 insertions(+), 31 deletions(-) diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 77ba4424..0e949d3b 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -159,8 +159,6 @@ class Console(Widget): kbuf = self.env.keybuffer cmd = kbuf.command - self.fm.hide_bookmarks() - if kbuf.failure: kbuf.clear() return diff --git a/ranger/gui/widgets/pager.py b/ranger/gui/widgets/pager.py index b99bf5db..e478c7be 100644 --- a/ranger/gui/widgets/pager.py +++ b/ranger/gui/widgets/pager.py @@ -19,6 +19,7 @@ The pager displays text and allows you to scroll inside it. import re from . import Widget from ranger.ext.direction import Direction +from ranger.container.keymap import CommandArgs BAR_REGEXP = re.compile(r'\|\d+\?\|') QUOTES_REGEXP = re.compile(r'"[^"]+?"') @@ -132,22 +133,28 @@ class Pager(Widget): offset=-self.hei) def press(self, key): - try: - tup = self.env.keybuffer.tuple_without_numbers() - if tup: - cmd = self.keymap[tup] - else: - return + self.env.keymanager.use_context(self.embedded and 'embedded_pager' or 'pager') + self.env.key_append(key) + kbuf = self.env.keybuffer + cmd = kbuf.command + + if kbuf.failure: + kbuf.clear() + return + elif not cmd: + return - except KeyError: - self.env.key_clear() + self.env.cmd = cmd + + if cmd.function: + try: + cmd.function(CommandArgs.from_widget(self)) + except Exception as error: + self.fm.notify(error) + if kbuf.done: + kbuf.clear() else: - if hasattr(cmd, 'execute'): - try: - cmd.execute_wrap(self) - except Exception as error: - self.fm.notify(error) - self.env.key_clear() + kbuf.clear() def set_source(self, source, strip=False): if self.source and self.source_is_stream: diff --git a/ranger/gui/widgets/taskview.py b/ranger/gui/widgets/taskview.py index fe31646d..9ef86b07 100644 --- a/ranger/gui/widgets/taskview.py +++ b/ranger/gui/widgets/taskview.py @@ -22,6 +22,7 @@ from collections import deque from . import Widget from ranger.ext.accumulator import Accumulator +from ranger.container.keymap import CommandArgs class TaskView(Widget, Accumulator): old_lst = None @@ -94,22 +95,28 @@ class TaskView(Widget, Accumulator): self.fm.loader.move(_from=i, to=absolute) def press(self, key): - try: - tup = self.env.keybuffer.tuple_without_numbers() - if tup: - cmd = self.commandlist[tup] - else: - return - - except KeyError: - self.env.key_clear() + self.env.keymanager.use_context('taskview') + self.env.key_append(key) + kbuf = self.env.keybuffer + cmd = kbuf.command + + if kbuf.failure: + kbuf.clear() + return + elif not cmd: + return + + self.env.cmd = cmd + + if cmd.function: + try: + cmd.function(CommandArgs.from_widget(self)) + except Exception as error: + self.fm.notify(error) + if kbuf.done: + kbuf.clear() else: - if hasattr(cmd, 'execute'): - try: - cmd.execute_wrap(self) - except Exception as error: - self.fm.notify(error) - self.env.key_clear() + kbuf.clear() def get_list(self): return self.fm.loader.queue -- cgit 1.4.1-2-gfad0 From 4655f56ba803b5a302e7724288efb02e39f3caeb Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 7 Apr 2010 23:15:11 +0200 Subject: removed ABOUT_THIS_BRANCH file --- ABOUT_THIS_BRANCH | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 ABOUT_THIS_BRANCH diff --git a/ABOUT_THIS_BRANCH b/ABOUT_THIS_BRANCH deleted file mode 100644 index ae09d54d..00000000 --- a/ABOUT_THIS_BRANCH +++ /dev/null @@ -1,14 +0,0 @@ -I put my personal branch online, maybe you find some of the -parts useful. To see whats different, type: - -git diff master..hut - -This branch is being regularily rebased on the master branch, -which rewrites history, so maybe its better to pick single commits -from this branch into your own branch rather than working directly -on this one: - -git log master..hut -# search for a commit you like, write down SHA1 identifier -git checkout -git cherry-pick -- cgit 1.4.1-2-gfad0 From cd5b1c41724103bce26ab2f66908a5f86cdc04d5 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 8 Apr 2010 00:35:38 +0200 Subject: defaults.keys: remove useless key definition --- ranger/defaults/keys.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 62cd46c9..c3a2d475 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -267,7 +267,6 @@ map.merge(readline_aliases) map.unmap('Q') # don't quit with Q in console, so we can type it map.unmap('') # define my own direction keys -map('a', wdg.type_key('a')) map('', wdg.history_move(-1)) map('', wdg.history_move(1)) map('', wdg.move(right=0, absolute=True)) -- cgit 1.4.1-2-gfad0 From 4e9384fd9ebcf1c17909dbf2b1e94f0b3ec9100b Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 8 Apr 2010 01:05:03 +0200 Subject: clean up --- ranger/defaults/keys.py | 3 +-- test/tc_newkeys.py | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index c3a2d475..70521e1e 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -227,6 +227,7 @@ map('', 'u', wdg.move(up=0.5, pages=True)) map('', 'f', '', wdg.move(down=1, pages=True)) map('', 'b', '', wdg.move(up=1, pages=True)) map('', wdg.move(down=0.8, pages=True)) +map('', wdg.move(down=1)) # ---------------------------------------------------------- others map('E', fm.edit_file()) @@ -302,5 +303,3 @@ map('', dir=Direction(down=-1, absolute=True)) map('', dir=Direction(down=1, pages=True)) map('', dir=Direction(down=-1, pages=True)) map('%', dir=Direction(down=1, percentage=True, absolute=True)) -map('', dir=Direction(down=1, pages=True)) -map('', dir=Direction(down=1)) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index b1cb42fb..a79ba83f 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -492,7 +492,7 @@ class Test(PressTestCase): s.replace('Y') self.assertNotEqual(t._tree, u._tree) - def test_keymap_with_context(self): + def test_keymanager(self): def func(arg): return 5 def getdown(arg): @@ -530,4 +530,5 @@ class Test(PressTestCase): kmc.use_context('bar') self.assertEqual(1, press('j')) + if __name__ == '__main__': main() -- cgit 1.4.1-2-gfad0 From ef011c914dcf6061af7b30c85db53336594cab81 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 8 Apr 2010 01:26:27 +0200 Subject: tc_newkey: added 2 failing test cases. Fix it! --- test/tc_newkeys.py | 66 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index a79ba83f..fc17aeda 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -500,35 +500,75 @@ class Test(PressTestCase): buffer = KeyBuffer(None, None) press = self._mkpress(buffer) - kmc = KeyManager(buffer, ['foo', 'bar']) + keymanager = KeyManager(buffer, ['foo', 'bar']) - map = kmc.get_context('foo') + map = keymanager.get_context('foo') map('a', func) map('b', func) - map = kmc.get_context('bar') + map = keymanager.get_context('bar') map('c', func) map('', getdown) - kmc.map('directions', 'j', dir=Direction(down=1)) + keymanager.map('directions', 'j', dir=Direction(down=1)) - kmc.use_context('foo') + keymanager.use_context('foo') self.assertEqual(5, press('a')) self.assertEqual(5, press('b')) self.assertPressFails(buffer, 'c') - kmc.use_context('bar') + keymanager.use_context('bar') self.assertPressFails(buffer, 'a') self.assertPressFails(buffer, 'b') self.assertEqual(5, press('c')) self.assertEqual(1, press('j')) - kmc.use_context('foo') - kmc.use_context('foo') - kmc.use_context('foo') - kmc.use_context('bar') - kmc.use_context('foo') - kmc.use_context('bar') - kmc.use_context('bar') + keymanager.use_context('foo') + keymanager.use_context('foo') + keymanager.use_context('foo') + keymanager.use_context('bar') + keymanager.use_context('foo') + keymanager.use_context('bar') + keymanager.use_context('bar') self.assertEqual(1, press('j')) + def test_alias_to_direction(self): + def func(arg): + return arg.direction.down() + + km = KeyMap() + directions = KeyMap() + kb = KeyBuffer(km, directions) + press = self._mkpress(kb) + + km.map('', func) + directions.map('j', dir=Direction(down=42)) + self.assertEqual(42, press('j')) + + km.map('o', alias='j') + self.assertEqual(42, press('o')) + + def test_both_directory_and_any_key(self): + def func(arg): + return arg.direction.down() + def func2(arg): + return "yay" + + km = KeyMap() + directions = KeyMap() + kb = KeyBuffer(km, directions) + press = self._mkpress(kb) + + km.map('abc', func) + directions.map('j', dir=Direction(down=42)) + self.assertEqual(42, press('abcj')) + + km.unmap('abc') + + km.map('abc', func2) + self.assertEqual("yay", press('abcd')) + + km.map('abc', func) + + km.map('abc', func2) + self.assertEqual("yay", press('abcd')) if __name__ == '__main__': main() -- cgit 1.4.1-2-gfad0 From 4537ca72633b0c60e6fd4700adb6b96c0a823c7b Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 8 Apr 2010 19:56:29 +0200 Subject: tc_direction: fixed maximum=X means that the maximum value is set to X-1. Works for lists: d.move(maximum=len(lst)) is this counter intuitive? --- test/tc_direction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tc_direction.py b/test/tc_direction.py index dd7e94ab..18f9eb4c 100644 --- a/test/tc_direction.py +++ b/test/tc_direction.py @@ -71,7 +71,7 @@ class TestDirections(unittest.TestCase): self.assertEqual(3, d.move(direction=3)) self.assertEqual(5, d.move(direction=3, current=2)) self.assertEqual(15, d.move(direction=3, pagesize=5)) - self.assertEqual(10, d.move(direction=3, pagesize=5, maximum=10)) + self.assertEqual(9, d.move(direction=3, pagesize=5, maximum=10)) self.assertEqual(18, d.move(direction=9, override=2)) d2 = Direction(absolute=True) self.assertEqual(5, d2.move(direction=9, override=5)) -- cgit 1.4.1-2-gfad0 From 93ecd88d9ad4231ab4926c2df43e00047900cd99 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 9 Apr 2010 01:29:09 +0200 Subject: partially fix tc_newkeys --- ranger/container/keymap.py | 62 +++++++++++++++++++++++----------------------- ranger/defaults/keys.py | 16 +++++++++--- ranger/ext/tree.py | 1 + test/tc_newkeys.py | 54 +++++++++++++++++++++------------------- 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 . 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('') # define my own direction keys map('', wdg.history_move(-1)) map('', wdg.history_move(1)) @@ -274,7 +272,7 @@ map('', wdg.move(right=0, absolute=True)) map('', wdg.move(right=-1, absolute=True)) map('', wdg.tab()) map('', wdg.tab(-1)) -map('', wdg.close()) +map('', '', wdg.close()) map('', '', wdg.execute()) map('', lambda arg: arg.fm.display_command_help(arg.wdg)) @@ -285,14 +283,24 @@ map('', wdg.delete_rest(1)) map('', wdg.delete_rest(-1)) map('', wdg.paste()) -map('') +# Any key which is still undefined will simply be typed in. +@map('') 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('', dir=Direction(down=1)) map('', 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', 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'), press('c@')) self.assertEqual(press('c'), 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('', func) + km.map('d', 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', 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', 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() -- cgit 1.4.1-2-gfad0 From 0fcbf6dcc044709ec240edfcb5a72147b7bbb4c4 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 9 Apr 2010 04:07:21 +0200 Subject: defaults.apps: edit xml files with editor --- ranger/defaults/apps.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ranger/defaults/apps.py b/ranger/defaults/apps.py index 9543badb..b3500c0d 100644 --- a/ranger/defaults/apps.py +++ b/ranger/defaults/apps.py @@ -58,9 +58,11 @@ class CustomApplications(Applications): f = c.file if f.extension is not None: - if f.extension in ('pdf'): + if f.extension in ('pdf' ): c.flags += 'd' return self.either(c, 'evince', 'zathura', 'apvlv') + if f.extension in ('xml', ): + return self.app_editor(c) if f.extension in ('html', 'htm', 'xhtml', 'swf'): return self.either(c, 'firefox', 'opera', 'elinks') if f.extension in ('swc', 'smc'): -- cgit 1.4.1-2-gfad0 From feaf9e0a81cf2506b18ad9dbb9732e0be45c10fc Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 9 Apr 2010 04:14:41 +0200 Subject: tc_newkeys: fixed all but one test --- ranger/api/keys.py | 2 +- ranger/container/keymap.py | 32 ++++++++++---- ranger/defaults/keys.py | 101 +++++++++++++++++++++++++-------------------- test/tc_newkeys.py | 30 +++++++++++--- 4 files changed, 107 insertions(+), 58 deletions(-) diff --git a/ranger/api/keys.py b/ranger/api/keys.py index d1011d22..97e07ae5 100644 --- a/ranger/api/keys.py +++ b/ranger/api/keys.py @@ -21,7 +21,7 @@ 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 -from ranger.container.keymap import KeyMap, Direction +from ranger.container.keymap import KeyMap, Direction, KeyMapWithDirections class Wrapper(object): def __init__(self, firstattr): diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index 29d6e629..d39df381 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -89,6 +89,23 @@ class KeyMap(Tree): return self.traverse(translate_keys(key)) +class KeyMapWithDirections(KeyMap): + def __init__(self, *args, **keywords): + Tree.__init__(self, *args, **keywords) + self.directions = KeyMap() + + def merge(self, other): + assert hasattr(other, 'directions'), 'Merging with wrong type?' + Tree.merge(self, other) + Tree.merge(self.directions, other.directions) + + def dir(self, *args, **keywords): + if ALIASARG in keywords: + self.directions.map(*args, **keywords) + else: + self.directions.map(*args, dir=Direction(**keywords)) + + class KeyManager(object): def __init__(self, keybuffer, contexts): self._keybuffer = keybuffer @@ -96,28 +113,29 @@ class KeyManager(object): self.clear() def clear(self): - self._contexts = { - 'directions': KeyMap(), - } + self._contexts = dict() for context in self._list_of_contexts: - self._contexts[context] = KeyMap() + self._contexts[context] = KeyMapWithDirections() def map(self, context, *args, **keywords): self.get_context(context).map(*args, **keywords) + def dir(self, context, *args, **keywords): + self.get_context(context).dir(*args, **keywords) + def get_context(self, context): assert isinstance(context, str) assert context in self._contexts, "no such context!" return self._contexts[context] __getitem__ = get_context - def use_context(self, context, directions='directions'): + def use_context(self, context): context = self.get_context(context) if self._keybuffer.keymap is not context: - directions = self.get_context(directions) - self._keybuffer.assign(context, directions) + self._keybuffer.assign(context, context.directions) self._keybuffer.clear() + class Binding(object): """The keybinding object""" def __init__(self, keys, actions): diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 57a0d415..061e9091 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -27,6 +27,25 @@ arg.wdg: the widget or ui instance arg.n: the number typed before the key combination (if allowed) arg.keys: the string representation of the used key combination arg.keybuffer: the keybuffer instance + +Direction keys are special. They must be mapped with: map.dir(*keys, **args) +where args is a dict of values such as up, left, down, right, absolute, +relative, pages, etc... +Example: map.dir('gg', to=0) +Direction keys can be accessed in a mapping that contians "". + +Example scenario +---------------- +If this keys are defined: +map("dd", "d", fm.cut(foo=bar)) +map.dir("gg", to=0) + +Type in the keys on the left and the function on the right will be executed: +dd => fm.cut(foo=bar) +5dd => fm.cut(foo=bar, narg=5) +dgg => fm.cut(foo=bar, dirarg=Direction(to=0)) +5dgg => fm.cut(foo=bar, narg=5, dirarg=Direction(to=0)) +5d3gg => fm.cut(foo=bar, narg=5, dirarg=Direction(to=3)) """ from ranger.api.keys import * @@ -35,36 +54,48 @@ from ranger import log # =================================================================== # == Define keys for everywhere: # =================================================================== -map = global_keys = KeyMap() +map = global_keys = KeyMapWithDirections() map('Q', fm.exit()) map('', fm.redraw_window()) map('', alias='') # Backspace is bugged sometimes +#map('', wdg.move()) @map('') # move around with direction keys def move(arg): arg.wdg.move(narg=arg.n, **arg.direction) +# -------------------------------------------------- direction keys +map.dir('', down=1) +map.dir('', down=-1) +map.dir('', right=-1) +map.dir('', right=1) +map.dir('', down=0, absolute=True) +map.dir('', down=-1, absolute=True) +map.dir('', down=1, pages=True) +map.dir('', down=-1, pages=True) +map.dir('%', down=1, percentage=True, absolute=True) + # =================================================================== # == Define aliases # =================================================================== -map = vim_aliases = KeyMap() -map('j', alias='') -map('k', alias='') -map('h', alias='') -map('l', alias='') -map('gg', alias='') -map('G', alias='') -map('', alias='') -map('', alias='') - -map = readline_aliases = KeyMap() -map('', alias='') -map('', alias='') -map('', alias='') -map('', alias='') -map('', alias='') -map('', alias='') +map = vim_aliases = KeyMapWithDirections() +map.dir('j', alias='') +map.dir('k', alias='') +map.dir('h', alias='') +map.dir('l', alias='') +map.dir('gg', alias='') +map.dir('G', alias='') +map.dir('', alias='') +map.dir('', alias='') + +map = readline_aliases = KeyMapWithDirections() +map.dir('', alias='') +map.dir('', alias='') +map.dir('', alias='') +map.dir('', alias='') +map.dir('', alias='') +map.dir('', alias='') # =================================================================== @@ -74,6 +105,8 @@ map = keymanager['general'] map.merge(global_keys) map.merge(vim_aliases) +map('gg', fm.move(to=0)) + # --------------------------------------------------------- history map('H', fm.history_go(-1)) map('L', fm.history_go(1)) @@ -87,8 +120,8 @@ map('v', fm.mark(all=True, toggle=True)) map('V', fm.mark(all=True, val=False)) # ------------------------------------------ file system operations -map('yy', fm.copy()) -map('dd', fm.cut()) +map('yy', 'y', fm.copy()) +map('dd', 'd', fm.cut()) map('pp', fm.paste()) map('po', fm.paste(overwrite=True)) map('pl', fm.paste_symlink()) @@ -215,7 +248,7 @@ map('r', fm.open_console(cmode.OPEN_QUICK)) # =================================================================== # == Define keys for the pager # =================================================================== -map = pager_keys = KeyMap() +map = pager_keys = KeyMapWithDirections() map.merge(global_keys) map.merge(vim_aliases) @@ -288,26 +321,6 @@ map('', wdg.paste()) 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('', dir=Direction(down=1)) -map('', dir=Direction(down=-1)) -map('', dir=Direction(right=-1)) -map('', dir=Direction(right=1)) -map('', dir=Direction(down=0, absolute=True)) -map('', dir=Direction(down=-1, absolute=True)) -map('', dir=Direction(down=1, pages=True)) -map('', dir=Direction(down=-1, pages=True)) -map('%', dir=Direction(down=1, percentage=True, absolute=True)) +# Unmap some global keys so we can type them: +map.unmap('Q') +map.directions.unmap('%') diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index 9a7f10c7..c06c2894 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -484,7 +484,8 @@ class Test(PressTestCase): map('c', func) map('', getdown) - keymanager.map('directions', 'j', dir=Direction(down=1)) + keymanager.dir('foo', 'j', down=1) + keymanager.dir('bar', 'j', down=1) keymanager.use_context('foo') self.assertEqual(5, press('a')) @@ -509,19 +510,24 @@ class Test(PressTestCase): def func(arg): return arg.direction.down() - km = KeyMap() - directions = KeyMap() - kb = KeyBuffer(km, directions) + km = KeyMapWithDirections() + kb = KeyBuffer(km, km.directions) press = self._mkpress(kb) km.map('', func) km.map('d', func) - directions.map('j', dir=Direction(down=42)) + km.dir('j', down=42) + km.dir('k', alias='j') self.assertEqual(42, press('j')) - km.map('o', alias='j') + km.dir('o', alias='j') + km.dir('ick', alias='j') self.assertEqual(42, press('o')) + self.assertEqual(42, press('dj')) + self.assertEqual(42, press('dk')) self.assertEqual(42, press('do')) + self.assertEqual(42, press('dick')) + self.assertPressFails(kb, 'dioo') def test_both_directory_and_any_key(self): def func(arg): @@ -572,5 +578,17 @@ class Test(PressTestCase): # self.assertPressFails(kb, 'agh') self.assertEqual(1, press('agg')) + def test_keymap_with_dir(self): + def func(arg): + return arg.direction.down() + + km = KeyMapWithDirections() + kb = KeyBuffer(km, km.directions) + + press = self._mkpress(kb) + + km.map('abc', func) + km.dir('j', down=42) + self.assertEqual(42, press('abcj')) if __name__ == '__main__': main() -- cgit 1.4.1-2-gfad0 From 00b627921c0a493e064501f444fa69e147bbb59d Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 9 Apr 2010 04:31:45 +0200 Subject: defaults.keys: improved --- ranger/defaults/keys.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 061e9091..81ebfea0 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -25,15 +25,23 @@ The CommandArgs object has these attributes: arg.fm: the file manager instance arg.wdg: the widget or ui instance arg.n: the number typed before the key combination (if allowed) +arg.direction: the direction object (if applicable) arg.keys: the string representation of the used key combination arg.keybuffer: the keybuffer instance Direction keys are special. They must be mapped with: map.dir(*keys, **args) -where args is a dict of values such as up, left, down, right, absolute, -relative, pages, etc... +where args is a dict of values such as up, down, to, absolute, relative... Example: map.dir('gg', to=0) Direction keys can be accessed in a mapping that contians "". +Additionally, there are shortcuts for accessing methods of the current +file manager and widget instance: +map('xyz', fm.method(foo=bar)) +will be translated to: +map('xyz', lamdba arg: arg.fm.method(foo=bar)) +If possible, arg.n and arg.direction are automatically inserted. + + Example scenario ---------------- If this keys are defined: @@ -105,7 +113,10 @@ map = keymanager['general'] map.merge(global_keys) map.merge(vim_aliases) +# -------------------------------------------------------- movement map('gg', fm.move(to=0)) +map('', 'J', fm.move(down=0.5, pages=True)) +map('', 'K', fm.move(up=0.5, pages=True)) # --------------------------------------------------------- history map('H', fm.history_go(-1)) -- cgit 1.4.1-2-gfad0 From f66939e58bb54828fe9ef9de919e4f039400472f Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 12 Apr 2010 18:18:49 +0200 Subject: added Direction.select() --- ranger/ext/direction.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ranger/ext/direction.py b/ranger/ext/direction.py index 0b3f55f7..f96ee90f 100644 --- a/ranger/ext/direction.py +++ b/ranger/ext/direction.py @@ -133,3 +133,10 @@ class Direction(dict): else: pos += current return int(max(min(pos, maximum + offset - 1), minimum)) + + def select(self, lst, override, current, pagesize): + destination = self.move(direction=self.down(), override=override, + current=current, pagesize=pagesize, minimum=0, maximum=len(lst)) + if destination > current: + return destination, lst[current:destination] + return destination, lst[destination:current] -- cgit 1.4.1-2-gfad0 From 11616e7211d50fc0856242c7fb2adc08279c5f60 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 12 Apr 2010 19:02:44 +0200 Subject: ranger.py: removed whitespace --- ranger.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ranger.py b/ranger.py index 636bd384..06b86531 100755 --- a/ranger.py +++ b/ranger.py @@ -34,13 +34,11 @@ return 1 # embed a shellscript. __doc__ = """Ranger - file browser for the unix terminal""" - # Importing the main method may fail if the ranger directory # is neither in the same directory as this file, nor in one of # pythons global import paths. try: from ranger.__main__ import main - except ImportError: import sys if '-d' not in sys.argv and '--debug' not in sys.argv: @@ -49,7 +47,5 @@ except ImportError: print("launch ranger.py in the top directory.") else: raise - else: main() - -- cgit 1.4.1-2-gfad0 From d9d3243bdfeae03e7c4641bde73103748992b3dc Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 12 Apr 2010 20:42:58 +0200 Subject: fix alt key for xterm --- ranger/defaults/options.py | 4 ++++ ranger/gui/ui.py | 10 ++++++++-- ranger/shared/settings.py | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index d96955b7..46723aa8 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -118,3 +118,7 @@ def colorscheme_overlay(context, fg, bg, attr): # The above function was just an example, let's set it back to None colorscheme_overlay = None + +# Enable this if key combinations with the Alt Key don't work for you. +# (Especially on xterm) +xterm_alt_key = False diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index 41acb39f..983cffc8 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -164,6 +164,10 @@ class UI(DisplayableContainer): else: kbuf.clear() + def handle_keys(self, *keys): + for key in keys: + self.handle_key(key) + def handle_input(self): key = self.win.getch() if key is 27 or key >= 128 and key < 256: @@ -177,8 +181,10 @@ class UI(DisplayableContainer): keys.append(getkey) if len(keys) == 1: keys.append(-1) - for key in keys: - self.handle_key(key) + if self.settings.xterm_alt_key: + if len(keys) == 2 and keys[1] in range(127, 256): + keys = [27, keys[1] - 128] + self.handle_keys(*keys) self.set_load_mode(previous_load_mode) if self.settings.flushinput: curses.flushinp() diff --git a/ranger/shared/settings.py b/ranger/shared/settings.py index e7098792..ebbe12c7 100644 --- a/ranger/shared/settings.py +++ b/ranger/shared/settings.py @@ -44,6 +44,7 @@ ALLOWED_SETTINGS = { 'colorscheme': str, 'colorscheme_overlay': (type(None), type(lambda:0)), 'hidden_filter': lambda x: isinstance(x, str) or hasattr(x, 'match'), + 'xterm_alt_key': bool, } -- cgit 1.4.1-2-gfad0 From 12ddb42454baf3dbe620d2f6c13d6c0f0ce83423 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 13 Apr 2010 12:17:46 +0200 Subject: Fixed suggested cd-after-exit-script for zsh --- README | 2 +- doc/ranger.1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index 07837688..429aefa1 100644 --- a/README +++ b/README @@ -130,7 +130,7 @@ Tips Change the directory of your parent shell when you exit ranger: ranger() { - $(which ranger) $@ && + command ranger $@ && cd "$(grep \^\' ~/.ranger/bookmarks | cut -b3-)" } diff --git a/doc/ranger.1 b/doc/ranger.1 index 9e1ab5a0..6adaf43f 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -169,7 +169,7 @@ of your parent shell after exiting ranger: .nf ranger() { - $(which ranger) $@ && + command ranger $@ && cd "$(grep \\^\\' ~/.ranger/bookmarks | cut -b3-)" } .\"----------------------------------------- -- cgit 1.4.1-2-gfad0 From 465dd890060c96f2ff8badd46a9f7fb75d29fc68 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 13 Apr 2010 12:19:10 +0200 Subject: Abbreviate HOME with ~ in the titlebar --- ranger/defaults/options.py | 3 +++ ranger/gui/bar.py | 4 +++- ranger/gui/widgets/titlebar.py | 15 +++++++++++---- ranger/shared/settings.py | 1 + 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index 46723aa8..0593bae3 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -75,6 +75,9 @@ update_title = True # directories are displayed at once, False turns off this feature. shorten_title = 3 +# Abbreviate $HOME with ~ in the titlebar (first line) of ranger? +tilde_in_titlebar = True + # How many directory-changes or console-commands should be kept in history? max_history_size = 20 diff --git a/ranger/gui/bar.py b/ranger/gui/bar.py index c06a201e..0ef840a4 100644 --- a/ranger/gui/bar.py +++ b/ranger/gui/bar.py @@ -95,8 +95,10 @@ class BarSide(list): def add(self, string, *lst, **kw): cs = ColoredString(string, self.base_color_tag, *lst) + cs.__dict__.update(kw) if 'fixedsize' in kw: - cs.fixed = kw['fixedsize'] + kw['fixed'] = kw['fixedsize'] + del kw['fixedsize'] self.append(cs) def add_space(self, n=1): diff --git a/ranger/gui/widgets/titlebar.py b/ranger/gui/widgets/titlebar.py index 62740e2d..ddcd03c3 100644 --- a/ranger/gui/widgets/titlebar.py +++ b/ranger/gui/widgets/titlebar.py @@ -84,7 +84,7 @@ class TitleBar(Widget): self.fm.enter_dir("/") else: try: - self.fm.env.enter_dir(self.env.pathway[(i-3)/2]) + self.fm.enter_dir(part.directory) except: pass return True @@ -109,15 +109,22 @@ class TitleBar(Widget): bar.add(self.env.username, 'hostname', clr, fixedsize=True) bar.add('@', 'hostname', clr, fixedsize=True) bar.add(self.env.hostname, 'hostname', clr, fixedsize=True) + bar.add(':', 'hostname', clr, fixedsize=True) - for path in self.env.pathway: + pathway = self.env.pathway + if self.settings.tilde_in_titlebar and \ + self.fm.env.cwd.path.startswith(self.env.home_path): + pathway = pathway[self.env.home_path.count('/')+1:] + bar.add('~/', 'directory', fixedsize=True) + + for path in pathway: if path.islink: clr = 'link' else: clr = 'directory' - bar.add(path.basename, clr) - bar.add('/', clr, fixedsize=True) + bar.add(path.basename, clr, directory=path) + bar.add('/', clr, fixedsize=True, directory=path) if self.env.cf is not None: bar.add(self.env.cf.basename, 'file', fixedsize=True) diff --git a/ranger/shared/settings.py b/ranger/shared/settings.py index ebbe12c7..01d4caf6 100644 --- a/ranger/shared/settings.py +++ b/ranger/shared/settings.py @@ -34,6 +34,7 @@ ALLOWED_SETTINGS = { 'sort_directories_first': bool, 'update_title': bool, 'shorten_title': int, # Note: False is an instance of int + 'tilde_in_titlebar': bool, 'max_filesize_for_preview': (int, type(None)), 'max_history_size': (int, type(None)), 'scroll_offset': int, -- cgit 1.4.1-2-gfad0 From 30785f2bdbfbaed60037d598561e7fed60c64256 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 13 Apr 2010 12:24:36 +0200 Subject: gui.bar: clean up --- ranger/gui/bar.py | 6 +----- ranger/gui/widgets/titlebar.py | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/ranger/gui/bar.py b/ranger/gui/bar.py index 0ef840a4..8b8eb33c 100644 --- a/ranger/gui/bar.py +++ b/ranger/gui/bar.py @@ -96,9 +96,6 @@ class BarSide(list): def add(self, string, *lst, **kw): cs = ColoredString(string, self.base_color_tag, *lst) cs.__dict__.update(kw) - if 'fixedsize' in kw: - kw['fixed'] = kw['fixedsize'] - del kw['fixedsize'] self.append(cs) def add_space(self, n=1): @@ -121,11 +118,10 @@ class BarSide(list): class ColoredString(object): - fixed = False - def __init__(self, string, *lst): self.string = string self.lst = lst + self.fixed = False def cut_off(self, n): n = max(n, min(len(self.string), 1)) diff --git a/ranger/gui/widgets/titlebar.py b/ranger/gui/widgets/titlebar.py index ddcd03c3..b815a07e 100644 --- a/ranger/gui/widgets/titlebar.py +++ b/ranger/gui/widgets/titlebar.py @@ -106,16 +106,16 @@ class TitleBar(Widget): else: clr = 'good' - bar.add(self.env.username, 'hostname', clr, fixedsize=True) - bar.add('@', 'hostname', clr, fixedsize=True) - bar.add(self.env.hostname, 'hostname', clr, fixedsize=True) - bar.add(':', 'hostname', clr, fixedsize=True) + bar.add(self.env.username, 'hostname', clr, fixed=True) + bar.add('@', 'hostname', clr, fixed=True) + bar.add(self.env.hostname, 'hostname', clr, fixed=True) + bar.add(':', 'hostname', clr, fixed=True) pathway = self.env.pathway if self.settings.tilde_in_titlebar and \ self.fm.env.cwd.path.startswith(self.env.home_path): pathway = pathway[self.env.home_path.count('/')+1:] - bar.add('~/', 'directory', fixedsize=True) + bar.add('~/', 'directory', fixed=True) for path in pathway: if path.islink: @@ -124,22 +124,22 @@ class TitleBar(Widget): clr = 'directory' bar.add(path.basename, clr, directory=path) - bar.add('/', clr, fixedsize=True, directory=path) + bar.add('/', clr, fixed=True, directory=path) if self.env.cf is not None: - bar.add(self.env.cf.basename, 'file', fixedsize=True) + bar.add(self.env.cf.basename, 'file', fixed=True) def _get_right_part(self, bar): kb = str(self.env.keybuffer) self.old_keybuffer = kb - bar.addright(kb, 'keybuffer', fixedsize=True) - bar.addright(' ', 'space', fixedsize=True) + bar.addright(kb, 'keybuffer', fixed=True) + bar.addright(' ', 'space', fixed=True) self.tab_width = 0 if len(self.fm.tabs) > 1: for tabname in self.fm._get_tab_list(): self.tab_width += len(str(tabname)) + 1 clr = 'good' if tabname == self.fm.current_tab else 'bad' - bar.addright(' '+str(tabname), 'tab', clr, fixedsize=True) + bar.addright(' '+str(tabname), 'tab', clr, fixed=True) def _print_result(self, result): import _curses -- cgit 1.4.1-2-gfad0 From 62e5fdf30596c76e4b9551964e5af8ea21c9741c Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 13 Apr 2010 12:32:28 +0200 Subject: gui.bar: Fixed Zero Division Error --- ranger/gui/bar.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ranger/gui/bar.py b/ranger/gui/bar.py index 8b8eb33c..f5e34eb1 100644 --- a/ranger/gui/bar.py +++ b/ranger/gui/bar.py @@ -68,7 +68,8 @@ class Bar(object): rightsize = self.right.sumsize() nonfixed_items = self.left.nonfixed_items() - itemsize = int(float(wid - rightsize - fixedsize) / nonfixed_items) + 1 + itemsize = int(float(wid - rightsize - fixedsize) / \ + (nonfixed_items + 1)) + 1 for item in self.left: if not item.fixed: -- cgit 1.4.1-2-gfad0 From bec7df57bbf04c8798328cd3b45db2d6c9e54f95 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 13 Apr 2010 12:37:36 +0200 Subject: Don't use tab 0 by default --- ranger/core/actions.py | 3 +-- ranger/defaults/keys.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index a07b4ae0..ac6cfc95 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -521,8 +521,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): self.tab_open(newtab) def tab_new(self): - for i in range(10): - i = (i + 1) % 10 + for i in range(1, 10): if not i in self.tabs: self.tab_open(i) break diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index c181857e..80985274 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -208,7 +208,7 @@ map('gc', '', fm.tab_close()) map('gt', '', fm.tab_move(1)) map('gT', '', fm.tab_move(-1)) map('gn', '', fm.tab_new()) -for n in range(10): +for n in range(1, 10): map('g' + str(n), fm.tab_open(n)) map('', fm.tab_open(n)) -- cgit 1.4.1-2-gfad0 From 62974cad55cc556009568c869d4abb8270260747 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 13 Apr 2010 13:34:42 +0200 Subject: container.keymap: Extended KeyManager --- ranger/container/keymap.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index d39df381..f09d9cfb 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -113,9 +113,9 @@ class KeyManager(object): self.clear() def clear(self): - self._contexts = dict() + self.contexts = dict() for context in self._list_of_contexts: - self._contexts[context] = KeyMapWithDirections() + self.contexts[context] = KeyMapWithDirections() def map(self, context, *args, **keywords): self.get_context(context).map(*args, **keywords) @@ -123,10 +123,17 @@ class KeyManager(object): def dir(self, context, *args, **keywords): self.get_context(context).dir(*args, **keywords) + def unmap(self, context, *args, **keywords): + self.get_context(context).unmap(*args, **keywords) + + def merge_all(self, keymapwithdirection): + for context, keymap in self.contexts.items(): + keymap.merge(keymapwithdirection) + def get_context(self, context): assert isinstance(context, str) - assert context in self._contexts, "no such context!" - return self._contexts[context] + assert context in self.contexts, "no such context: " + context + return self.contexts[context] __getitem__ = get_context def use_context(self, context): -- cgit 1.4.1-2-gfad0 From d492db527bd0a610bef85682615de2c25d5145d6 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 13 Apr 2010 17:47:34 +0200 Subject: Started implementing dirarg --- ranger/api/keys.py | 22 +++++++++++++++++----- ranger/core/actions.py | 16 +++++++++++----- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/ranger/api/keys.py b/ranger/api/keys.py index d7a688b9..92a0269c 100644 --- a/ranger/api/keys.py +++ b/ranger/api/keys.py @@ -34,10 +34,11 @@ class Wrapper(object): def function(command_argument): args, kws = real_args, real_keywords number = command_argument.n + direction = command_argument.direction obj = getattr(command_argument, self.__firstattr__) fnc = getattr(obj, attr) - if number is not None: - args, kws = replace_narg(number, fnc, args, kws) + if number is not None or direction is not None: + args, kws = replace_narg(number, direction, fnc, args, kws) return fnc(*args, **kws) return function return wrapper @@ -63,6 +64,7 @@ fm = Wrapper('fm') wdg = Wrapper('wdg') +DIRARG_KEYWORD = 'dirarg' NARG_KEYWORD = 'narg' def narg(number_, function_, *args_, **keywords_): @@ -78,7 +80,7 @@ def narg(number_, function_, *args_, **keywords_): args, keywords = replace_narg(number_, function_, args_, keywords_) return function_(*args, **keywords) -def replace_narg(number, function, args, keywords): +def replace_narg(number, direction, function, args, keywords): """ This function returns (args, keywords) with one little change: if has a named argument called "narg", args and keywords @@ -93,10 +95,10 @@ def replace_narg(number, function, args, keywords): => (1, 666), {} """ argspec = getargspec(function).args - if NARG_KEYWORD in argspec: + args = list(args) + if number is not None and 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' @@ -105,4 +107,14 @@ def replace_narg(number, function, args, keywords): # is narg in keywords? keywords = dict(keywords) keywords[NARG_KEYWORD] = number + if direction is not None and DIRARG_KEYWORD in argspec: + try: + index = argspec.index(DIRARG_KEYWORD) + if ismethod(function): + index -= 1 # because of 'self' + args[index] = direction + except (ValueError, IndexError): + # is narg in keywords? + keywords = dict(keywords) + keywords[DIRARG_KEYWORD] = direction return args, keywords diff --git a/ranger/core/actions.py b/ranger/core/actions.py index ac6cfc95..7b8d192d 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -542,16 +542,22 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): self.env.cut = False self.ui.browser.main_column.request_redraw() - def copy(self): + def copy(self, narg=None, dirarg=None): """Copy the selected items""" - - selected = self.env.get_selection() + direction = Direction(dirarg or {}) + selected = direction.select( + override=narg, + lst=self.env.cwd.files, + current=self.env.cwd.pointer, + pagesize=self.env.termsize[0]) + + selected = selected or self.env.get_selection() self.env.copy = set(f for f in selected if f in self.env.cwd.files) self.env.cut = False self.ui.browser.main_column.request_redraw() - def cut(self): - self.copy() + def cut(self, narg=None, dirarg=None): + self.copy(narg=narg, dirarg=dirarg) self.env.cut = True self.ui.browser.main_column.request_redraw() -- cgit 1.4.1-2-gfad0 From 447cbcd65588eafdb85b407750602c0fdd591ef4 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 13 Apr 2010 17:57:46 +0200 Subject: dirarg: added unit test and almost working implementaiton --- ranger/core/actions.py | 6 +++++- test/tc_direction.py | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 7b8d192d..df623c77 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -545,7 +545,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): def copy(self, narg=None, dirarg=None): """Copy the selected items""" direction = Direction(dirarg or {}) - selected = direction.select( + pos, selected = direction.select( override=narg, lst=self.env.cwd.files, current=self.env.cwd.pointer, @@ -553,7 +553,11 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): selected = selected or self.env.get_selection() self.env.copy = set(f for f in selected if f in self.env.cwd.files) + self.env.copy.add(self.env.cwd.pointed_obj) self.env.cut = False + self.env.cwd.pointer = pos + self.env.cwd.correct_pointer() + self.env.copy.add(self.env.cwd.pointed_obj) self.ui.browser.main_column.request_redraw() def cut(self, narg=None, dirarg=None): diff --git a/test/tc_direction.py b/test/tc_direction.py index 18f9eb4c..124a7001 100644 --- a/test/tc_direction.py +++ b/test/tc_direction.py @@ -76,6 +76,13 @@ class TestDirections(unittest.TestCase): d2 = Direction(absolute=True) self.assertEqual(5, d2.move(direction=9, override=5)) + def test_select(self): + d = Direction(down=3) + lst = list(range(100)) + self.assertEqual((6, [3,4,5]), d.select(current=3, pagesize=10, override=None, lst=lst)) + d = Direction(down=3, pages=True) + self.assertEqual((9, [3,4,5,6,7,8]), d.select(current=3, pagesize=2, override=None, lst=lst)) + if __name__ == '__main__': unittest.main() -- cgit 1.4.1-2-gfad0 From 661ab7a384bbff355c3ee44c824ba8205afc23f5 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 13 Apr 2010 19:32:11 +0200 Subject: dirarg: tmp --- ranger/core/actions.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index df623c77..929dec31 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -544,15 +544,16 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): def copy(self, narg=None, dirarg=None): """Copy the selected items""" + cwd = self.env.cwd direction = Direction(dirarg or {}) - pos, selected = direction.select( - override=narg, - lst=self.env.cwd.files, - current=self.env.cwd.pointer, - pagesize=self.env.termsize[0]) - - selected = selected or self.env.get_selection() - self.env.copy = set(f for f in selected if f in self.env.cwd.files) + if direction.vertical(): + pos, selected = direction.select( + override=narg, lst=cwd.files, current=currentpos, + pagesize=self.env.termsize[0]) + else: + pos = currentpos + (narg or 0) + selected = (f for f in self.env.get_selection() if f in cwd.files) + self.env.copy = set(selected) self.env.copy.add(self.env.cwd.pointed_obj) self.env.cut = False self.env.cwd.pointer = pos -- cgit 1.4.1-2-gfad0 From daa224d86348f0e0fa39d09feef43e34df56d780 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 14 Apr 2010 00:17:40 +0200 Subject: synchronize key combinations with master branch --- ranger/defaults/keys.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index b298f9c0..72b13051 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -145,7 +145,7 @@ map('p', fm.hint('press //p// once again to confirm pasting' \ ', or //l// to create symlinks')) # ---------------------------------------------------- run programs -map('s', fm.execute_command(os.environ['SHELL'])) +map('S', fm.execute_command(os.environ['SHELL'])) map('E', fm.edit_file()) map('.term', fm.execute_command('x-terminal-emulator', flags='d')) map('du', fm.execute_command('du --max-depth=1 -h | less')) @@ -257,7 +257,7 @@ def ctrl_c(arg): map(':', ';', fm.open_console(cmode.COMMAND)) map('>', fm.open_console(cmode.COMMAND_QUICK)) -map('!', fm.open_console(cmode.OPEN)) +map('!', 's', fm.open_console(cmode.OPEN)) map('r', fm.open_console(cmode.OPEN_QUICK)) -- cgit 1.4.1-2-gfad0 From 656c2bf6deb5dfbbd971880154f109ceb2be87c3 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 14 Apr 2010 00:39:48 +0200 Subject: dirarg: fully functioning now --- ranger/core/actions.py | 31 ++++++++++++++++++------------- ranger/ext/direction.py | 4 +++- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 929dec31..e8634099 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -545,20 +545,25 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): def copy(self, narg=None, dirarg=None): """Copy the selected items""" cwd = self.env.cwd - direction = Direction(dirarg or {}) - if direction.vertical(): - pos, selected = direction.select( - override=narg, lst=cwd.files, current=currentpos, - pagesize=self.env.termsize[0]) - else: - pos = currentpos + (narg or 0) + if not narg and not dirarg: selected = (f for f in self.env.get_selection() if f in cwd.files) - self.env.copy = set(selected) - self.env.copy.add(self.env.cwd.pointed_obj) - self.env.cut = False - self.env.cwd.pointer = pos - self.env.cwd.correct_pointer() - self.env.copy.add(self.env.cwd.pointed_obj) + self.env.copy = set(selected) + self.env.cut = False + else: + direction = Direction(dirarg or {}) + offset = 0 + if not direction.vertical(): + direction = Direction(down=1) + offset = -1 + pos, selected = direction.select( + override=narg, lst=cwd.files, current=cwd.pointer, + pagesize=self.env.termsize[0], offset=offset) + self.env.copy = set(selected) + self.env.copy.add(self.env.cwd.pointed_obj) + self.env.cut = False + self.env.cwd.pointer = pos + self.env.cwd.correct_pointer() + self.env.copy.add(self.env.cwd.pointed_obj) self.ui.browser.main_column.request_redraw() def cut(self, narg=None, dirarg=None): diff --git a/ranger/ext/direction.py b/ranger/ext/direction.py index f96ee90f..5a22d553 100644 --- a/ranger/ext/direction.py +++ b/ranger/ext/direction.py @@ -134,9 +134,11 @@ class Direction(dict): pos += current return int(max(min(pos, maximum + offset - 1), minimum)) - def select(self, lst, override, current, pagesize): + def select(self, lst, override, current, pagesize, offset=0): destination = self.move(direction=self.down(), override=override, current=current, pagesize=pagesize, minimum=0, maximum=len(lst)) if destination > current: + destination += offset return destination, lst[current:destination] + destination -= offset return destination, lst[destination:current] -- cgit 1.4.1-2-gfad0 From f99b82c1ae6fc7bc927ad0f216579162574d4fbe Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 14 Apr 2010 00:43:10 +0200 Subject: dirarg: cleanup --- ranger/core/actions.py | 5 +---- ranger/ext/direction.py | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index e8634099..15d0d017 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -548,7 +548,6 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): if not narg and not dirarg: selected = (f for f in self.env.get_selection() if f in cwd.files) self.env.copy = set(selected) - self.env.cut = False else: direction = Direction(dirarg or {}) offset = 0 @@ -559,11 +558,9 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): override=narg, lst=cwd.files, current=cwd.pointer, pagesize=self.env.termsize[0], offset=offset) self.env.copy = set(selected) - self.env.copy.add(self.env.cwd.pointed_obj) - self.env.cut = False self.env.cwd.pointer = pos self.env.cwd.correct_pointer() - self.env.copy.add(self.env.cwd.pointed_obj) + self.env.cut = False self.ui.browser.main_column.request_redraw() def cut(self, narg=None, dirarg=None): diff --git a/ranger/ext/direction.py b/ranger/ext/direction.py index 5a22d553..cefd32bc 100644 --- a/ranger/ext/direction.py +++ b/ranger/ext/direction.py @@ -139,6 +139,6 @@ class Direction(dict): current=current, pagesize=pagesize, minimum=0, maximum=len(lst)) if destination > current: destination += offset - return destination, lst[current:destination] + return destination, lst[current:destination + 1] destination -= offset - return destination, lst[destination:current] + return destination, lst[destination:current + 1] -- cgit 1.4.1-2-gfad0 From 3dd1793a9f60b318c73e1a0d5eade6349ad322fa Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 14 Apr 2010 00:53:39 +0200 Subject: dirarg: improved --- ranger/core/actions.py | 12 ++++++------ ranger/ext/direction.py | 11 ++++------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 15d0d017..b320396a 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -547,19 +547,19 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): cwd = self.env.cwd if not narg and not dirarg: selected = (f for f in self.env.get_selection() if f in cwd.files) - self.env.copy = set(selected) else: - direction = Direction(dirarg or {}) - offset = 0 - if not direction.vertical(): + if not dirarg and narg: direction = Direction(down=1) - offset = -1 + offset = 0 + else: + direction = Direction(dirarg) + offset = 1 pos, selected = direction.select( override=narg, lst=cwd.files, current=cwd.pointer, pagesize=self.env.termsize[0], offset=offset) - self.env.copy = set(selected) self.env.cwd.pointer = pos self.env.cwd.correct_pointer() + self.env.copy = set(selected) self.env.cut = False self.ui.browser.main_column.request_redraw() diff --git a/ranger/ext/direction.py b/ranger/ext/direction.py index cefd32bc..b9fbcac9 100644 --- a/ranger/ext/direction.py +++ b/ranger/ext/direction.py @@ -134,11 +134,8 @@ class Direction(dict): pos += current return int(max(min(pos, maximum + offset - 1), minimum)) - def select(self, lst, override, current, pagesize, offset=0): - destination = self.move(direction=self.down(), override=override, + def select(self, lst, override, current, pagesize, offset=1): + dest = self.move(direction=self.down(), override=override, current=current, pagesize=pagesize, minimum=0, maximum=len(lst)) - if destination > current: - destination += offset - return destination, lst[current:destination + 1] - destination -= offset - return destination, lst[destination:current + 1] + selection = lst[min(current, dest):max(current, dest) + offset] + return dest + offset - 1, selection -- cgit 1.4.1-2-gfad0 From 951aedd4a63ed53c4fe523f57e6f4cfe6d519d89 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 14 Apr 2010 02:09:51 +0200 Subject: defaults.keys: minor cleanup --- ranger/defaults/keys.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 72b13051..866aab87 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -74,8 +74,8 @@ def move(arg): # -------------------------------------------------- direction keys map.dir('', down=1) -map.dir('', down=-1) -map.dir('', right=-1) +map.dir('', up=1) +map.dir('', left=1) map.dir('', right=1) map.dir('', down=0, absolute=True) map.dir('', down=-1, absolute=True) -- cgit 1.4.1-2-gfad0