diff options
-rw-r--r-- | ranger/core/actions.py | 14 | ||||
-rw-r--r-- | ranger/defaults/commands.py | 2 | ||||
-rw-r--r-- | ranger/defaults/rifle.conf | 46 | ||||
-rwxr-xr-x | ranger/ext/rifle.py | 103 |
4 files changed, 113 insertions, 52 deletions
diff --git a/ranger/core/actions.py b/ranger/core/actions.py index d5d38de9..f701c08f 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -17,6 +17,7 @@ from ranger.ext.relative_symlink import relative_symlink from ranger.ext.keybinding_parser import key_to_string, construct_keybinding from ranger.ext.shell_escape import shell_quote from ranger.ext.next_available_filename import next_available_filename +from ranger.ext.rifle import squash_flags from ranger.core.shared import FileManagerAware, EnvironmentAware, \ SettingsAware from ranger.fsobject import File @@ -292,19 +293,16 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): elif type(files) not in (list, tuple): files = [files] - if 'flags' in kw: - from ranger.core.runner import Context - context = Context(files=list(files), flags=kw['flags']) - context.squash_flags() - if 'c' in context.flags: - files = [self.fm.env.cf] + flags = squash_flags(kw.get('flags', '')) + if 'c' in flags: + files = [self.fm.env.cf] self.signal_emit('execute.before', keywords=kw) filenames = [f.path for f in files] mimetype = files[0].mimetype if files else None - label = kw['label'] if 'label' in kw else None + label = kw.get('app', None) try: - return self.rifle.execute(filenames, mode, label, mimetype) + return self.rifle.execute(filenames, mode, label, flags, mimetype) finally: self.signal_emit('execute.after') diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index 054bb020..52a1d30e 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -312,7 +312,7 @@ class open_with(Command): return app, flags, int(mode) def _is_app(self, arg): - return (not self._is_flags(arg) and arg in get_executables()) + return not self._is_flags(arg) and not arg.isdigit() def _is_flags(self, arg): return all(x in ALLOWED_FLAGS for x in arg) diff --git a/ranger/defaults/rifle.conf b/ranger/defaults/rifle.conf index 5851f447..2e86d463 100644 --- a/ranger/defaults/rifle.conf +++ b/ranger/defaults/rifle.conf @@ -7,12 +7,11 @@ # Syntax: # <condition1> , <condition2> , ... = command # -# Prefixing a condition with a "!" will negate its result. -# # The command can contain these environment variables: # $1-$9 | The n-th selected file # $@ | All selected files # +# Prefixing a condition with "!" will negate its result. # These conditions are currently supported: # ext <regexp> | The regexp matches the extension of $1 # mime <regexp> | The regexp matches the mime type of $1 @@ -24,28 +23,39 @@ # # There are also pseudo-conditions which have a "side effect": # flag <flags> | Change how the program is run. See below. -# label <label> | Assign a label or name to the rule. +# label <label> | Assign a label or name to the command so it can +# | be started with :open_with <label> in ranger +# | or `rifle -p <label>` in the standalone executable. # else | Always true. # -# These flags are currently supported for the "flag" condition: -# f | fork the program to the background -# p | pipe the output into "$PAGER" or "less" -# w | ask the user to press enter after the process has been executed +# Flags are single characters which slightly transform the command: +# f | Fork the program, make it run in the background. +# | New command = nohup $command >& /dev/null & +# r | Execute the command with root permissions +# | New command = sudo $command +# t | Run the program in a new terminal. If $TERMCMD is not defined, +# | rifle will attempt to extract it from $TERM. +# | New command = $TERMCMD -e $command +# +# NOTE: FLAGS DO NOT WORK PROPERLY WHEN PIPES ARE IN THE COMMAND #------------------------------------------- # Websites #------------------------------------------- +# Rarely installed browsers get higher priority; It is assumed that if you +# install a rare browser, you probably use it. Firefox/konqueror/w3m on the +# other hand are often only installed as fallback browsers. ext x?html?, has surf, X, flag f = surf -- "$@" ext x?html?, has vimprobable, X, flag f = vimprobable -- "$@" ext x?html?, has vimprobable2, X, flag f = vimprobable2 -- "$@" ext x?html?, has jumanji, X, flag f = jumanji -- "$@" ext x?html?, has luakit, X, flag f = luakit -- "$@" ext x?html?, has uzbl, X, flag f = uzbl -- "$@" +ext x?html?, has midori, X, flag f = midori -- "$@" +ext x?html?, has opera, X, flag f = opera -- "$@" ext x?html?, has firefox, X, flag f = firefox -- "$@" ext x?html?, has seamonkey, X, flag f = seamonkey -- "$@" ext x?html?, has iceweasel, X, flag f = iceweasel -- "$@" -ext x?html?, has opera, X, flag f = opera -- "$@" -ext x?html?, has midori, X, flag f = midori -- "$@" ext x?html?, has epiphany, X, flag f = epiphany -- "$@" ext x?html?, has konqueror, X, flag f = konqueror -- "$@" ext x?html?, has elinks, terminal = elinks "$@" @@ -89,9 +99,9 @@ ext midi?, terminal, has wildmidi = wildmidi -- "$@" mime ^video|audio, has gmplayer, X, flag f = gmplayer -- "$@" mime ^video|audio, has smplayer, X, flag f = smplayer -- "$@" mime ^video, has mplayer2, X, flag f = mplayer2 -- "$@" +mime ^video, has mplayer2, X, flag f = mplayer2 -fs -- "$@" mime ^video, has mplayer, X, flag f = mplayer -- "$@" mime ^video, has mplayer, X, flag f = mplayer -fs -- "$@" -mime ^video, has mplayer, X, flag f = mplayer -mixer software -- "$@" mime ^video|audio, has vlc, X, flag f = vlc -- "$@" mime ^video|audio, has totem, X, flag f = totem -- "$@" mime ^video|audio, has totem, X, flag f = totem --fullscreen -- "$@" @@ -99,8 +109,8 @@ mime ^video|audio, has totem, X, flag f = totem --fullscreen -- "$@" #-------------------------------------------- # Video without X: #------------------------------------------- -mime ^video, terminal, has mplayer2 = mplayer2 -- "$@" -mime ^video, terminal, has mplayer = mplayer -- "$@" +mime ^video, terminal, !X, has mplayer2 = mplayer2 -- "$@" +mime ^video, terminal, !X, has mplayer = mplayer -- "$@" #------------------------------------------- # Image Viewing: @@ -135,10 +145,10 @@ ext djvu, has evince, X, flag f = evince -- "$@" # Archives #------------------------------------------- # This requires atool -ext 7z|ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz, has als, flag p = als -- "$@" -ext iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has als, flag p = als -- "$@" -ext 7z|ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz, has aunpack, flag p = aunpack -- "$@" -ext iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has aunpack, flag p = aunpack -- "$@" +ext 7z|ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz, has als = als -- "$@" | "$PAGER" +ext iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has als = als -- "$@" | "$PAGER" +ext 7z|ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz, has aunpack = aunpack -- "$@" +ext iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has aunpack = aunpack -- "$@" # Fallback: ext tar|gz, has tar = tar vvtf "$@" @@ -152,5 +162,7 @@ label wallpaper, mime ^image, X = feh --bg-tile "$1" label wallpaper, mime ^image, X = feh --bg-center "$1" label wallpaper, mime ^image, X = feh --bg-fill "$1" -# Define the editor for non-text files as last action +# Define the editor for non-text files + pager as last action label editor, !mime ^text, !ext xml|csv|tex = "$EDITOR" -- "$@" +label pager, mime ^text = "$PAGER" -- "$@" +label pager, ext xml|csv|tex = "$PAGER" -- "$@" diff --git a/ranger/ext/rifle.py b/ranger/ext/rifle.py index ea2c113b..4870c36e 100755 --- a/ranger/ext/rifle.py +++ b/ranger/ext/rifle.py @@ -37,6 +37,22 @@ def _is_terminal(): return False return True + +def squash_flags(flags): + """ + Remove lowercase flags if the respective uppercase flag exists + + >>> squash_flags('abc') + 'abc' + >>> squash_flags('abcC') + 'ab' + >>> squash_flags('CabcAd') + 'bd' + """ + exclude = ''.join(f.upper() + f.lower() for f in flags if f == f.upper()) + return ''.join(f for f in flags if f not in exclude) + + class Rifle(object): delimiter1 = '=' delimiter2 = ',' @@ -61,7 +77,8 @@ class Rifle(object): def __init__(self, config_file): self.config_file = config_file - self._app_flags = False + self._app_flags = '' + self._app_label = None def reload_config(self, config_file=None): """Replace the current configuration with the one in config_file""" @@ -93,6 +110,8 @@ class Rifle(object): # Handle the negation of conditions starting with an exclamation mark, # then pass on the arguments to _eval_condition2(). + if not condition: + return True if condition[0].startswith('!'): new_condition = tuple([condition[0][1:]]) + tuple(condition[1:]) return not self._eval_condition2(new_condition, files, label) @@ -107,9 +126,6 @@ class Rifle(object): if not files: return False - self._app_flags = '' - self._app_label = None - if function == 'ext': extension = os.path.basename(files[0]).rsplit('.', 1)[-1] return bool(re.search('^' + argument + '$', extension)) @@ -144,17 +160,38 @@ class Rifle(object): self._mimetype = mimetype return mimetype - def _build_command(self, files, action): + def _build_command(self, files, action, flags): + # Get the flags + if isinstance(flags, str): + self._app_flags += flags + self._app_flags = squash_flags(self._app_flags) flags = self._app_flags + _filenames = "' '".join(f.replace("'", "'\\\''") for f in files) command = "set -- '%s'" % _filenames + '\n' - if 'p' in flags and not 'f' in flags and _is_terminal(): - action += '| less' - if 'f' in flags: - action = "nohup %s >&/dev/null &" % action - command += action + + # Apply flags + command += self._apply_flags(action, flags) return command + def _apply_flags(self, action, flags): + # FIXME: Flags do not work properly when pipes are in the command. + if 'r' in flags: + action = 'sudo ' + action + if 't' in flags: + if 'TERMCMD' not in os.environ: + term = os.environ['TERM'] + if term.startswith('rxvt-unicode'): + term = 'urxvt' + if term not in get_executables(): + self.hook_logger("Can not determine terminal command. " + "Please set $TERMCMD manually.") + os.environ['TERMCMD'] = term + action = "$TERMCMD -e %s" % action + if 'f' in flags: + action = "nohup %s >& /dev/null &" % action + return action + def list_commands(self, files, mimetype=None): """ Returns one 4-tuple for all currently applicable commands @@ -168,6 +205,8 @@ class Rifle(object): result = [] t = time.time() for cmd, tests in self.rules: + self._app_flags = '' + self._app_label = None for test in tests: if not self._eval_condition(test, files, None): break @@ -196,23 +235,28 @@ class Rifle(object): count = 0 # Determine command for cmd, tests in self.rules: + self._app_flags = '' + self._app_label = None for test in tests: if not self._eval_condition(test, files, label): break else: - if count != way: - count += 1 - else: - command = self.hook_command_preprocessing(command) - command = self._build_command(files, cmd) + if label and label == self._app_label or \ + not label and count == way: + cmd = self.hook_command_preprocessing(cmd) + command = self._build_command(files, cmd, flags) break + else: + count += 1 # Execute command if command is None: - if count <= 0: + if count <= 0 or way <= 0: self.hook_logger("No action found.") else: self.hook_logger("Method number %d is undefined." % way) else: + if 'PAGER' not in os.environ: + os.environ['PAGER'] = 'less' command = self.hook_command_postprocessing(command) self.hook_before_executing(command, self._mimetype, self._app_flags) try: @@ -221,8 +265,9 @@ class Rifle(object): finally: self.hook_after_executing(command, self._mimetype, self._app_flags) + def main(): - """The main function, which is run when you start this program direectly.""" + """The main function which is run when you start this program direectly.""" import sys # Find configuration file path @@ -236,16 +281,22 @@ def main(): # Evaluate arguments from optparse import OptionParser - parser = OptionParser(usage="%prog [-hlpw] [files]") - parser.add_option('-p', type='string', default='0', metavar="KEYWORD", - help="pick a method to open the files. KEYWORD is either the number" - " listed by 'rifle -l' or a string that matches a label in the" - " configuration file") + parser = OptionParser(usage="%prog [-fhlpw] [files]") + parser.add_option('-f', type="string", default=None, metavar="FLAGS", + help="use additional flags: f=fork, r=root, t=terminal. " + "Uppercase flag negates respective lowercase flags.") parser.add_option('-l', action="store_true", - help="list possible ways to open the files") + help="list possible ways to open the files (id:label:flags:command)") + parser.add_option('-p', type='string', default='0', metavar="KEYWORD", + help="pick a method to open the files. KEYWORD is either the " + "number listed by 'rifle -l' or a string that matches a label in " + "the configuration file") parser.add_option('-w', type='string', default=None, metavar="PROGRAM", help="open the files with PROGRAM") options, positional = parser.parse_args() + if not positional: + parser.print_help() + raise SystemExit(1) if options.p.isdigit(): way = int(options.p) @@ -263,10 +314,10 @@ def main(): rifle.reload_config() #print(rifle.list_commands(sys.argv[1:])) if options.l: - for count, cmd, label, flags in rifle.list_commands(sys.argv[1:]): - print("%d: %s" % (count, cmd)) + for count, cmd, label, flags in rifle.list_commands(positional): + print("%d:%s:%s:%s" % (count, label or '', flags, cmd)) else: - rifle.execute(sys.argv[1:], way=way, label=label) + rifle.execute(positional, way=way, label=label, flags=options.f) if __name__ == '__main__': |