diff options
-rw-r--r-- | ranger/api/keys.py | 2 | ||||
-rw-r--r-- | ranger/container/keymap.py | 32 | ||||
-rw-r--r-- | ranger/defaults/keys.py | 101 | ||||
-rw-r--r-- | 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 "<dir>". + +Example scenario +---------------- +If this keys are defined: +map("dd", "d<dir>", 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('<C-L>', fm.redraw_window()) map('<backspace2>', alias='<backspace>') # Backspace is bugged sometimes +#map('<dir>', wdg.move()) @map('<dir>') # move around with direction keys def move(arg): arg.wdg.move(narg=arg.n, **arg.direction) +# -------------------------------------------------- direction keys +map.dir('<down>', down=1) +map.dir('<up>', down=-1) +map.dir('<left>', right=-1) +map.dir('<right>', right=1) +map.dir('<home>', down=0, absolute=True) +map.dir('<end>', down=-1, absolute=True) +map.dir('<pagedown>', down=1, pages=True) +map.dir('<pageup>', down=-1, pages=True) +map.dir('%', down=1, percentage=True, absolute=True) + # =================================================================== # == Define aliases # =================================================================== -map = vim_aliases = KeyMap() -map('j', alias='<down>') -map('k', alias='<up>') -map('h', alias='<left>') -map('l', alias='<right>') -map('gg', alias='<home>') -map('G', alias='<end>') -map('<C-F>', alias='<pagedown>') -map('<C-B>', alias='<pageup>') - -map = readline_aliases = KeyMap() -map('<C-B>', alias='<left>') -map('<C-F>', alias='<right>') -map('<C-A>', alias='<home>') -map('<C-E>', alias='<end>') -map('<C-D>', alias='<delete>') -map('<C-H>', alias='<backspace>') +map = vim_aliases = KeyMapWithDirections() +map.dir('j', alias='<down>') +map.dir('k', alias='<up>') +map.dir('h', alias='<left>') +map.dir('l', alias='<right>') +map.dir('gg', alias='<home>') +map.dir('G', alias='<end>') +map.dir('<C-F>', alias='<pagedown>') +map.dir('<C-B>', alias='<pageup>') + +map = readline_aliases = KeyMapWithDirections() +map.dir('<C-B>', alias='<left>') +map.dir('<C-F>', alias='<right>') +map.dir('<C-A>', alias='<home>') +map.dir('<C-E>', alias='<end>') +map.dir('<C-D>', alias='<delete>') +map.dir('<C-H>', alias='<backspace>') # =================================================================== @@ -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<dir>', fm.copy()) +map('dd', 'd<dir>', 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('<C-Y>', 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('<down>', dir=Direction(down=1)) -map('<up>', dir=Direction(down=-1)) -map('<left>', dir=Direction(right=-1)) -map('<right>', dir=Direction(right=1)) -map('<home>', dir=Direction(down=0, absolute=True)) -map('<end>', dir=Direction(down=-1, absolute=True)) -map('<pagedown>', dir=Direction(down=1, pages=True)) -map('<pageup>', 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('<dir>', 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('<dir>', func) km.map('d<dir>', 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<dir>', func) + km.dir('j', down=42) + self.assertEqual(42, press('abcj')) if __name__ == '__main__': main() |