diff options
-rw-r--r-- | doc/ranger.pod | 5 | ||||
-rw-r--r-- | ranger/api/commands.py | 50 | ||||
-rw-r--r-- | ranger/core/actions.py | 8 | ||||
-rw-r--r-- | ranger/core/helper.py | 4 | ||||
-rw-r--r-- | ranger/defaults/commands.py | 63 | ||||
-rw-r--r-- | ranger/defaults/rc.conf | 293 | ||||
-rw-r--r-- | ranger/ext/keybinding_parser.py | 2 | ||||
-rw-r--r-- | ranger/ext/keybindings.py | 13 | ||||
-rw-r--r-- | ranger/gui/ui.py | 2 | ||||
-rw-r--r-- | ranger/gui/widgets/console.py | 3 |
10 files changed, 407 insertions, 36 deletions
diff --git a/doc/ranger.pod b/doc/ranger.pod index 5945ad7c..6625c4e4 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -660,10 +660,11 @@ word starts with a `y'. Edit the current file or the file in the argument. -=item eval I<python_code> +=item eval [I<-q>] I<python_code> Evaluates the python code. `fm' is a reference to the FM instance. To display -text, use the function `p'. +text, use the function `p'. The result is displayed on the screen unless you +use the "-q" option. Examples: :eval fm diff --git a/ranger/api/commands.py b/ranger/api/commands.py index 130906b6..1e2cf912 100644 --- a/ranger/api/commands.py +++ b/ranger/api/commands.py @@ -52,6 +52,18 @@ class CommandContainer(object): except: pass + def load_commands_from_object(self, obj, filtr): + for attribute_name in dir(obj): + if attribute_name[0] == '_' or attribute_name not in filtr: + continue + attribute = getattr(obj, attribute_name) + if hasattr(attribute, '__call__'): + cmd = type(attribute_name, (FunctionCommand, ), dict()) + cmd._based_function = attribute + cmd._function_name = attribute.__name__ + cmd._object_name = obj.__class__.__name__ + self.commands[attribute_name] = cmd + def get_command(self, name, abbrev=True): if abbrev: lst = [cls for cmd, cls in self.commands.items() \ @@ -240,3 +252,41 @@ class Command(FileManagerAware): # more than one result. append no slash, so the user can # manually type in the slash to advance into that directory return (line.start(1) + join(rel_dirname, name) for name in names) + + +class FunctionCommand(Command): + _based_function = None + _object_name = "" + _function_name = "unknown" + def execute(self): + if not self._based_function: + return + if len(self.args) == 1: + return self._based_function() + + args, keywords = list(), dict() + for arg in self.args[1:]: + equal_sign = arg.find("=") + value = arg if (equal_sign is -1) else arg[equal_sign + 1:] + try: + value = int(value) + except: + if value in ('True', 'False'): + value = (value == 'True') + else: + try: + value = float(value) + except: + pass + + if equal_sign == -1: + args.append(value) + else: + keywords[arg[:equal_sign]] = value + + try: + return self._based_function(*args, **keywords) + except TypeError: + self.fm.notify("Bad arguments for %s.%s: %s, %s" % + (self._object_name, self._function_name, + repr(args), repr(keywords)), bad=True) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 3427af7f..44b5eac8 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -269,7 +269,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): def history_go(self, relative): """Move back and forth in the history""" - self.env.history_go(relative) + self.env.history_go(int(relative)) def scroll(self, relative): """Scroll down by <relative> lines""" @@ -329,7 +329,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): def hint(self, text): self.ui.hint(text) - def toggle_boolean_option(self, string): + def toggle_option(self, string): """Toggle a boolean option named <string>""" if isinstance(self.env.settings[string], bool): self.env.settings[string] ^= True @@ -418,9 +418,9 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): except: return False self.env.last_search = text - self.search(order='search', offset=offset) + self.search_next(order='search', offset=offset) - def search(self, order=None, offset=1, forward=True): + def search_next(self, order=None, offset=1, forward=True): original_order = order if self.search_forward: direction = bool(forward) diff --git a/ranger/core/helper.py b/ranger/core/helper.py index 56b8e5ad..a02cb0c0 100644 --- a/ranger/core/helper.py +++ b/ranger/core/helper.py @@ -87,6 +87,7 @@ def parse_arguments(): def load_settings(fm, clean): + from ranger.core.actions import Actions import ranger.core.shared import ranger.api.commands import ranger.api.keys @@ -95,6 +96,7 @@ def load_settings(fm, clean): # Load commands comcont = ranger.api.commands.CommandContainer() + comcont.load_commands_from_object(fm, dir(Actions)) ranger.api.commands.alias = comcont.alias try: import commands @@ -149,8 +151,6 @@ def load_settings(fm, clean): else: comcont = ranger.api.commands.CommandContainer() ranger.api.commands.alias = comcont.alias - keymanager = ranger.core.shared.EnvironmentAware.env.keymanager - ranger.api.keys.keymanager = keymanager from ranger.defaults import commands, keys, apps comcont.load_commands_from_module(commands) fm.commands = comcont diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index a40d447d..34214ab7 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -62,19 +62,26 @@ from ranger.core.runner import ALLOWED_FLAGS alias('e', 'edit') alias('q', 'quit') alias('q!', 'quitall') +alias('console!', 'console_bang') alias('qall', 'quitall') class cd(Command): """ - :cd <dirname> + :cd [-r] <dirname> The cd command changes the directory. The command 'cd -' is equivalent to typing ``. + Using the option "-r" will get you to the real path. """ def execute(self): - line = parse(self.line) - destination = line.rest(1) + if self.arg(1) == '-r': + import os.path + self.shift() + destination = os.path.realpath(self.rest(1)) + else: + destination = self.rest(1) + if not destination: destination = '~' @@ -137,6 +144,16 @@ class cd(Command): return (line.start(1) + join(rel_dirname, dirname) for dirname in dirnames) +class chain(Command): + """ + :chain <command1>; <command2>; ... + Calls multiple commands at once, separated by semicolons. + """ + def execute(self): + for command in self.rest(1).split(";"): + self.fm.execute_console(command) + + class search(Command): def execute(self): self.fm.search_file(parse(self.line).rest(1), regexp=True) @@ -492,6 +509,33 @@ class mark(Command): self.fm.ui.need_redraw = True +class console(Command): + """ + :console <command> + + Open the console with the given command. + """ + def execute(self): + position = None + if self.arg(1)[0:2] == '-p': + try: + position = int(self.arg(1)[2:]) + self.shift() + except: + pass + self.fm.open_console(self.rest(1), position=position) + + +class console_bang(Command): + """ + :console! <command> + + Execute the given command in the console. + """ + def execute(self): + self.fm.execute_console(self.rest(1)) + + class load_copy_buffer(Command): """ :load_copy_buffer @@ -600,7 +644,7 @@ class edit(Command): class eval_(Command): """ - :eval <python code> + :eval [-q] <python code> Evaluates the python code. `fm' is a reference to the FM instance. @@ -614,8 +658,15 @@ class eval_(Command): name = 'eval' def execute(self): - code = parse(self.line).rest(1) + if self.arg(1) == '-q': + code = self.rest(2) + quiet = True + else: + code = self.rest(1) + quiet = False + import ranger fm = self.fm + cmd = self.fm.execute_console p = fm.notify try: try: @@ -623,7 +674,7 @@ class eval_(Command): except SyntaxError: exec(code) else: - if result: + if result and not quiet: p(result) except Exception as err: p(err) diff --git a/ranger/defaults/rc.conf b/ranger/defaults/rc.conf index 59401e3b..cb90a38f 100644 --- a/ranger/defaults/rc.conf +++ b/ranger/defaults/rc.conf @@ -1,21 +1,280 @@ -# VIM -map x break_shit -map gg eval fm.move(to=0) -map G eval fm.move(to=-1) -map : eval fm.open_console() -map j eval fm.move(down=1) -map k eval fm.move(up=1) -map h eval fm.move(left=1) -map l eval fm.move(right=1) -map q quit +# =================================================================== +# == Define keys for the browser +# =================================================================== + +# Basic map Q quit +map q quit +map ZZ quit +map ZQ quit -cmap <ESC> quit +map R reload_cwd +map <C-r> reset +map <C-l> redraw_window +map H history_go -1 +map L history_go 1 +map <C-c> abort -map R eval fm.reload_cwd() -map <c-r> eval fm.reset() -map <c-l> eval fm.redraw_window() -map H eval fm.history_go(-1) -map L eval fm.history_go(1) -map <c-c> eval fm.abort() +map i display_file map ? help +map W display_log +map w eval fm.ui.open_taskview() +map S shell $SHELL + +map : console +map ; console +map ! console shell +map @ console -p6 shell %s +map # console shell -p +map s console shell +map r console open_with +map f console find +map cd console cd + +# Tagging / Marking +map t tag_toggle +map T tag_remove + +# VIM-like +map gg move to=0 +map G move to=-1 +map j move down=1 +map k move up=1 +map h move left=1 +map l move right=1 +map J move down=0.5 pages=True +map K move up=0.5 pages=True +map <C-d> move down=0.5 pages=True +map <C-u> move up=0.5 pages=True +map <C-f> move down=1 pages=True +map <C-b> move up=1 pages=True + +# For the nostalgics: Midnight Commander bindings +map <F1> help +map <F3> display_file +map <F4> edit +map <F5> copy +map <F6> cut +map <F7> console mkdir +map <F8> console delete seriously? +map <F10> exit + +# In case you work on a keyboard with dvorak layout +map <UP> move down=1 +map <LEFT> move up=1 +map <LEFT> move left=1 +map <RIGHT> move right=1 +map <HOME> move to=0 +map <END> move to=-1 +map <PAGEDOWN> move down=1 pages=True +map <PAGEUP> move up=1 pages=True +map <CR> move right=1 +map <DEL> console delete +map <INS> console touch + +# Jumping around +map ] move_parent 1 +map [ move_parent -1 +map } traverse + +map gh cd ~ +map ge cd /etc +map gu cd /usr +map gd cd /dev +map gl cd -r . +map gL cd -r %f +map go cd /opt +map gv cd /var +map gm cd /media +map gM cd /mnt +map gs cd /srv +map gR eval fm.cd(ranger.RANGERDIR) + +# External Programs +map du shell -p du --max-depth=1 -h --apparent-size +map dU shell -p du --max-depth=1 -h --apparent-size | sort -rh +map yp shell -d echo -n %d/%f | xsel -i +map yd shell -d echo -n %d | xsel -i +map yn shell -d echo -n %%f | xsel -i + +# Filesystem Operations +map = chmod + +map cw console rename +map A eval fm.open_console('rename ' + fm.env.cf.basename) +map I eval fm.open_console('rename ' + fm.env.cf.basename, position=7) + +map pp paste +map po paste overwrite=True +map pl paste_symlink relative=False +map pL paste_symlink relative=True +map phl paste_hardlink + +map dd cut +map ud uncut +map da cut mode=add +map dr cut mode=remove + +map yy copy +map uy uncut +map ya copy mode=add +map yr copy mode=remove + +# Temporary workarounds +map dgg eval fm.cut(dirarg=dict(to=0)) +map dG eval fm.cut(dirarg=dict(to=-1)) +map dj eval fm.cut(dirarg=dict(down=1)) +map dk eval fm.cut(dirarg=dict(up=1)) +map ygg eval fm.copy(dirarg=dict(to=0)) +map yG eval fm.copy(dirarg=dict(to=-1)) +map yj eval fm.copy(dirarg=dict(down=1)) +map yk eval fm.copy(dirarg=dict(up=1)) + +# Searching +map / console search +map n search_next +map N search_next forward=False +map ct search_next order=tag +map cs search_next order=size +map ci search_next order=mimetype +map cc search_next order=ctime +map cm search_next order=mtime +map ca search_next order=atime + +# Tabs +map <C-n> chain tab_new; cd ~ +map <C-w> tab_close +map <TAB> tab_move 1 +map <S-TAB> tab_move -1 +map gn chain tab_new; cd ~ +map gc tab_close +map gt tab_move 1 +map gT tab_move -1 +map <a-1> tab_open 1 +map <a-2> tab_open 2 +map <a-3> tab_open 3 +map <a-4> tab_open 4 +map <a-5> tab_open 5 +map <a-6> tab_open 6 +map <a-7> tab_open 7 +map <a-8> tab_open 8 +map <a-9> tab_open 9 + +# Sorting +map or eval fm.settings.sort_reverse ^= True +map os chain set sort=size; set sort_reverse=False +map ob chain set sort=basename; set sort_reverse=False +map on chain set sort=natural; set sort_reverse=False +map om chain set sort=mtime; set sort_reverse=False +map oc chain set sort=ctime; set sort_reverse=False +map oa chain set sort=atime; set sort_reverse=False +map ot chain set sort=type; set sort_reverse=False + +map oS chain set sort=size; set sort_reverse=True +map oB chain set sort=basename; set sort_reverse=True +map oN chain set sort=natural; set sort_reverse=True +map oM chain set sort=mtime; set sort_reverse=True +map oC chain set sort=ctime; set sort_reverse=True +map oA chain set sort=atime; set sort_reverse=True +map oT chain set sort=type; set sort_reverse=True + +# Settings +map zc toggle_option collapse_preview +map zd toggle_option sort_directories_first +map zh toggle_option show_hidden +map <C-h> toggle_option show_hidden +map zi toggle_option flushinput +map zm toggle_option mouse_enabled +map zp toggle_option preview_files +map zP toggle_option preview_directories +map zs toggle_option sort_case_insensitive +map zv toggle_option use_preview_script +map zf console filter + +# Beware. I haven't figured out how to make these keybindings pretty yet: + +# map +ow shell -d chmod o+w (one mapping for each combination) +eval import itertools; [cmd("map +%s%s shell -d chmod %s+%s %%s" % (mode+mode)) for mode in itertools.product("ugoa", "rwxXst")] + +# map -ow shell -d chmod o+w (one mapping for each combination) +eval import itertools; [cmd("map -%s%s shell -d chmod %s-%s %%s" % (mode+mode)) for mode in itertools.product("ugoa", "rwxXst")] + +# map "x tag_toggle tag=x (one mapping for each key) +eval -q [cmd('map "%s eval fm.tag_toggle(tag=chr(%d))' % (chr(s),s)) for s in range(33, 127)] + + +# =================================================================== +# == Define keys for the console +# =================================================================== + +# Basic +cmap <tab> eval fm.ui.console.tab() +cmap <s-tab> eval fm.ui.console.tab(-1) +cmap <C-c> eval fm.ui.console.close() +cmap <C-d> eval fm.ui.console.close() +cmap <ESC> eval fm.ui.console.close() +cmap <CR> eval fm.ui.console.execute() +cmap <C-j> eval fm.ui.console.execute() +cmap <C-l> redraw_window + +# This special expression allows typing in numerals: +cmap <allow_quantifiers> false + +# Move around +cmap <left> eval fm.ui.console.move(left=1) +cmap <right> eval fm.ui.console.move(right=1) +cmap <home> eval fm.ui.console.move(right=0, absolute=True) +cmap <end> eval fm.ui.console.move(right=-1, absolute=True) +cmap <up> eval fm.ui.console.history_move(-1) +cmap <down> eval fm.ui.console.history_move(1) + +# And of course the emacs way +cmap <C-b> eval fm.ui.console.move(left=1) +cmap <C-f> eval fm.ui.console.move(right=1) +cmap <C-a> eval fm.ui.console.move(right=0, absolute=True) +cmap <C-e> eval fm.ui.console.move(right=-1, absolute=True) +cmap <C-p> eval fm.ui.console.history_move(-1) +cmap <C-n> eval fm.ui.console.history_move(1) + +# Line Editing +# Note: There are multiple ways to express backspaces. <backspace> (code 263) +# and <backspace2> (code 127). To be sure, use both. +cmap <backspace> eval fm.ui.console.delete(-1) +cmap <backspace2> eval fm.ui.console.delete(-1) +cmap <delete> eval fm.ui.console.delete(0) +cmap <C-h> eval fm.ui.console.delete(-1) +cmap <C-d> eval fm.ui.console.delete(0) +cmap <C-w> eval fm.ui.console.delete_word() +cmap <C-k> eval fm.ui.console.delete_rest(1) +cmap <C-u> eval fm.ui.console.delete_rest(-1) +cmap <C-y> eval fm.ui.console.paste() + + +# =================================================================== +# == Taskview Keybindings +# =================================================================== + +# Movement +tmap j eval -q fm.ui.taskview.move(down=1) +tmap k eval -q fm.ui.taskview.move(up=1) +tmap gg eval -q fm.ui.taskview.move(down=0, absolute=True) +tmap G eval -q fm.ui.taskview.move(down=-1, absolute=True) +tmap <down> eval -q fm.ui.taskview.move(down=1) +tmap <up> eval -q fm.ui.taskview.move(up=1) +tmap <home> eval -q fm.ui.taskview.move(down=0, absolute=True) +tmap <end> eval -q fm.ui.taskview.move(down=-1, absolute=True) + +# Changing priority and deleting tasks +tmap J eval -q fm.ui.taskview.task_move(-1) +tmap K eval -q fm.ui.taskview.task_move(0) +tmap dd eval -q fm.ui.taskview.task_remove() +tmap <pagedown> eval -q fm.ui.taskview.task_move(-1) +tmap <pageup> eval -q fm.ui.taskview.task_move(0) +tmap <delete> eval -q fm.ui.taskview.task_remove() + +# A bunch of aliases for closing +tmap w eval -q fm.ui.close_taskview() +tmap q eval -q fm.ui.close_taskview() +tmap Q eval -q fm.ui.close_taskview() +tmap <esc> eval -q fm.ui.close_taskview() +tmap <c-c> eval -q fm.ui.close_taskview() diff --git a/ranger/ext/keybinding_parser.py b/ranger/ext/keybinding_parser.py index bfd1b6d4..855904ba 100644 --- a/ranger/ext/keybinding_parser.py +++ b/ranger/ext/keybinding_parser.py @@ -68,11 +68,13 @@ DIRKEY = 9001 ANYKEY = 9002 PASSIVE_ACTION = 9003 ALT_KEY = 9004 +QUANT_KEY = 9005 very_special_keys = { 'dir': DIRKEY, 'any': ANYKEY, 'bg': PASSIVE_ACTION, + 'allow_quantifiers': QUANT_KEY, } special_keys = { diff --git a/ranger/ext/keybindings.py b/ranger/ext/keybindings.py index 36b13ad0..d40094d3 100644 --- a/ranger/ext/keybindings.py +++ b/ranger/ext/keybindings.py @@ -14,7 +14,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. from ranger.ext.keybinding_parser import (parse_keybinding, - ANYKEY, PASSIVE_ACTION) + ANYKEY, PASSIVE_ACTION, QUANT_KEY) digits = set(range(ord('0'), ord('9')+1)) @@ -48,8 +48,9 @@ class KeyMaps(dict): class KeyBuffer(object): - any_key = ANYKEY - passive_key = PASSIVE_ACTION + any_key = ANYKEY + passive_key = PASSIVE_ACTION + quantifier_key = QUANT_KEY def __init__(self, keymap=None): self.keymap = keymap @@ -64,6 +65,10 @@ class KeyBuffer(object): self.finished_parsing = False self.parse_error = False + if self.keymap and self.quantifier_key in self.keymap: + if self.keymap[self.quantifier_key] == 'false': + self.finished_parsing_quantifier = True + def add(self, key): self.keys.append(key) if not self.finished_parsing_quantifier and key in digits: @@ -93,4 +98,4 @@ class KeyBuffer(object): self.parse_error = True def __str__(self): - return repr(self.keys) + return "".join("{0:c}".format(c) for c in self.keys) diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index a2c5c9a1..0b124be1 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -156,6 +156,8 @@ class UI(DisplayableContainer): keybuffer.clear() elif keybuffer.finished_parsing: keybuffer.clear() + return False + return True def handle_keys(self, *keys): for key in keys: diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index f50a5f3c..c1371040 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -152,7 +152,8 @@ class Console(Widget): def press(self, key): self.env.keymaps.use_keymap('console') - self.fm.ui.press(key) + if not self.fm.ui.press(key): + self.type_key(key) def type_key(self, key): self.tab_deque = None |