# -*- coding: utf-8 -*- # This file is part of ranger, the console file manager. # This configuration file is licensed under the same terms as ranger. # =================================================================== # # NOTE: If you copied this file to ~/.config/ranger/commands_full.py, # then it will NOT be loaded by ranger, and only serve as a reference. # # =================================================================== # This file contains ranger's commands. # It's all in python; lines beginning with # are comments. # # Note that additional commands are automatically generated from the methods # of the class ranger.core.actions.Actions. # # You can customize commands in the file ~/.config/ranger/commands.py. # It has the same syntax as this file. In fact, you can just copy this # file there with `ranger --copy-config=commands' and make your modifications. # But make sure you update your configs when you update ranger. # # =================================================================== # Every class defined here which is a subclass of `Command' will be used as a # command in ranger. Several methods are defined to interface with ranger: # execute(): called when the command is executed. # cancel(): called when closing the console. # tab(): called when is pressed. # quick(): called after each keypress. # # The return values for tab() can be either: # None: There is no tab completion # A string: Change the console to this string # A list/tuple/generator: cycle through every item in it # # The return value for quick() can be: # False: Nothing happens # True: Execute the command afterwards # # The return value for execute() and cancel() doesn't matter. # # =================================================================== # Commands have certain attributes and methods that facilitate parsing of # the arguments: # # self.line: The whole line that was written in the console. # self.args: A list of all (space-separated) arguments to the command. # self.quantifier: If this command was mapped to the key "X" and # the user pressed 6X, self.quantifier will be 6. # self.arg(n): The n-th argument, or an empty string if it doesn't exist. # self.rest(n): The n-th argument plus everything that followed. For example, # if the command was "search foo bar a b c", rest(2) will be "bar a b c" # self.start(n): Anything before the n-th argument. For example, if the # command was "search foo bar a b c", start(2) will be "search foo" # # =================================================================== # And this is a little reference for common ranger functions and objects: # # self.fm: A reference to the "fm" object which contains most information # about ranger. # self.fm.notify(string): Print the given string on the screen. # self.fm.notify(string, bad=True): Print the given string in RED. # self.fm.reload_cwd(): Reload the current working directory. # self.fm.thisdir: The current working directory. (A File object.) # self.fm.thisfile: The current file. (A File object too.) # self.fm.thistab.get_selection(): A list of all selected files. # self.fm.execute_console(string): Execute the string as a ranger command. # self.fm.open_console(string): Open the console with the given string # already typed in for you. # self.fm.move(direction): Moves the cursor in the given direction, which # can be something like down=3, up=5, right=1, left=1, to=6, ... # # File objects (for example self.fm.thisfile) have these useful attributes and # methods: # # cf.path: The path to the file. # cf.basename: The base name only. # cf.load_content(): Force a loading of the directories content (which # obviously works with directories only) # cf.is_directory: True/False depending on whether it's a directory. # # For advanced commands it is unavoidable to dive a bit into the source code # of ranger. # =================================================================== from ranger.api.commands import * class alias(Command): """:alias Copies the oldcommand as newcommand. """ context = 'browser' resolve_macros = False def execute(self): if not self.arg(1) or not self.arg(2): self.fm.notify('Syntax: alias ', bad=True) else: self.fm.commands.alias(self.arg(1), self.rest(2)) class cd(Command): """:cd [-r] 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): import os.path if self.arg(1) == '-r': self.shift() destination = os.path.realpath(self.rest(1)) if os.path.isfile(destination): self.fm.select_file(destination) return else: destination = self.rest(1) if not destination: destination = '~' if destination == '-': self.fm.enter_bookmark('`') else: self.fm.cd(destination) def tab(self): import os from os.path import dirname, basename, expanduser, join cwd = self.fm.thisdir.path rel_dest = self.rest(1) bookmarks = [v.path for v in self.fm.bookmarks.dct.values() if rel_dest in v.path ] # expand the tilde into the user directory if rel_dest.startswith('~'): rel_dest = expanduser(rel_dest) # define some shortcuts abs_dest = join(cwd, rel_dest) abs_dirname = dirname(abs_dest) rel_basename = basename(rel_dest) rel_dirname = dirname(rel_dest) try: # are we at the end of a directory? if rel_dest.endswith('/') or rel_dest == '': _, dirnames, _ = next(os.walk(abs_dest)) # are we in the middle of the filename? else: _, dirnames, _ = next(os.walk(abs_dirname)) dirnames = [dn for dn in dirnames \ if dn.startswith(rel_basename)] except (OSError, StopIteration): # os.walk found nothing pass else: dirnames.sort() if self.fm.settings.cd_bookmarks: dirnames = bookmarks + dirnames # no results, return None if len(dirnames) == 0: return # one result. since it must be a directory, append a slash. if len(dirnames) == 1: return self.start(1) + join(rel_dirname, dirnames[0]) + '/' # more than one result. append no slash, so the user can # manually type in the slash to advance into that directory return (self.start(1) + join(rel_dirname, dirname) for dirname in dirnames) class chain(Command): """:chain ; ; ... Calls multiple commands at once, separated by semicolons. """ def execute(self): for command in self.rest(1).split(";"): self.fm.execute_console(command) class shell(Command): escape_macros_for_shell = True def execute(self): if self.arg(1) and self.arg(1)[0] == '-': flags = self.arg(1)[1:] command = self.rest(2) else: flags = '' command = self.rest(1) if not command and 'p' in flags: command = 'cat %f' if command: if '%' in command: command = self.fm.substitute_macros(command, escape=True) self.fm.execute_command(command, flags=flags) def tab(self): from ranger.ext.get_executables import get_executables if self.arg(1) and self.arg(1)[0] == '-': command = self.rest(2) else: command = self.rest(1) start = self.line[0:len(self.line) - len(command)] try: position_of_last_space = command.rindex(" ") except ValueError: return (start + program + ' ' for program \ in get_executables() if program.startswith(command)) if position_of_last_space == len(command) - 1: selection = self.fm.thistab.get_selection() if len(selection) == 1: return self.line + selection[0].shell_escaped_basename + ' ' else: return self.line + '%s ' else: before_word, start_of_word = self.line.rsplit(' ', 1) return (before_word + ' ' + file.shell_escaped_basename \ for file in self.fm.thisdir.files \ if file.shell_escaped_basename.startswith(start_of_word)) class open_with(Command): def execute(self): app, flags, mode = self._get_app_flags_mode(self.rest(1)) self.fm.execute_file( files = [f for f in self.fm.thistab.get_selection()], app = app, flags = flags, mode = mode) def tab(self): return self._tab_through_executables() def _get_app_flags_mode(self, string): """Extracts the application, flags and mode from a string. examples: "mplayer f 1" => ("mplayer", "f", 1) "aunpack 4" => ("aunpack", "", 4) "p" => ("", "p", 0) "" => None """ app = '' flags = '' mode = 0 split = string.split() if len(split) == 0: pass elif len(split) == 1: part = split[0] if self._is_app(part): app = part elif self._is_flags(part): flags = part elif self._is_mode(part): mode = part elif len(split) == 2: part0 = split[0] part1 = split[1] if self._is_app(part0): app = part0 if self._is_flags(part1): flags = part1 elif self._is_mode(part1): mode = part1 elif self._is_flags(part0): flags = part0 if self._is_mode(part1): mode = part1 elif self._is_mode(part0): mode = part0 if self._is_flags(part1): flags = part1 elif len(split) >= 3: part0 = split[0] part1 = split[1] part2 = split[2] if self._is_app(part0): app = part0 if self._is_flags(part1): flags = part1 if self._is_mode(part2): mode = part2 elif self._is_mode(part1): mode = part1 if self._is_flags(part2): flags = part2 elif self._is_flags(part0): flags = part0 if self._is_mode(part1): mode = part1 elif self._is_mode(part0): mode = part0 if self._is_flags(part1): flags = part1 return app, flags, int(mode) def _is_app(self, arg): return not self._is_flags(arg) and not arg.isdigit() def _is_flags(self, arg): from ranger.core.runner import ALLOWED_FLAGS return all(x in ALLOWED_FLAGS for x in arg) def _is_mode(self, arg): return all(x in '0123456789' for x in arg) class set_(Command): """:set