diff options
-rw-r--r-- | ranger/container/keymap.py | 19 | ||||
-rw-r--r-- | ranger/defaults/keys.py | 224 | ||||
-rw-r--r-- | ranger/gui/widgets/console.py | 48 | ||||
-rw-r--r-- | 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 <http://www.gnu.org/licenses/>. -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", "<C-J><A-4>", "<tab>", "<down><up><right>" -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('<C-L>', fm.redraw_window()) +map('<backspace2>', alias='<backspace>') # Backspace is bugged sometimes -# --------------------------------------------------------- -# Define keys for everywhere: -map = keymanager['general'] -@map('<dir>') +@map('<dir>') # move around with direction keys def move(arg): arg.wdg.move(narg=arg.n, **arg.direction) -map('Q', fm.exit()) -map('<C-L>', fm.redraw_window()) - -# --------------------------------------------------------- -# Define keys in "general" context: -map = keymanager['general'] +# =================================================================== +# == 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('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<bg>', 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<bg>', 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<bg>', 'O<bg>', 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<bg>', 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', '<C-W>', fm.tab_close()) +map('gt', '<TAB>', fm.tab_move(1)) +map('gT', '<S-TAB>', fm.tab_move(-1)) +map('gn', '<C-N>', fm.tab_new()) +for n in range(10): + map('g' + str(n), fm.tab_open(n)) + map('<A-' + 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<bg>', 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("`<bg>", "'<bg>", "m<bg>", fm.draw_bookmarks()) + +# ---------------------------------------------------- change views +map('i', fm.display_file()) +map('<C-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('<C-R>', fm.reset()) +map('R', fm.reload_cwd()) +@map('<C-C>') +def ctrl_c(arg): + try: + item = arg.fm.loader.queue[0] + except: + arg.fm.notify("Type Q or :quit<Enter> 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('<dir>') # define my own direction keys + +map('a', wdg.type_key('a')) +map('<up>', wdg.history_move(-1)) +map('<down>', wdg.history_move(1)) +map('<home>', wdg.move(right=0, absolute=True)) +map('<end>', wdg.move(right=-1, absolute=True)) +map('<tab>', wdg.tab()) +map('<s-tab>', wdg.tab(-1)) +map('<c-c>', wdg.close()) +map('<CR>', '<c-j>', wdg.execute()) +map('<F1>', lambda arg: arg.fm.display_command_help(arg.wdg)) + +map('<backspace>', wdg.delete(-1)) +map('<delete>', wdg.delete(1)) +map('<C-W>', wdg.delete_word()) +map('<C-K>', wdg.delete_rest(1)) +map('<C-U>', wdg.delete_rest(-1)) +map('<C-Y>', wdg.paste()) + +def type_key(arg): + log('x') + arg.wdg.type_key(arg.match) +map('<any>', type_key) +log(map._tree) + + +# =================================================================== +# == Define direction keys +# =================================================================== map = keymanager.get_context('directions') map('<down>', dir=Direction(down=1)) map('<up>', 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 |