diff options
-rwxr-xr-x | ranger/config/commands.py | 93 |
1 files changed, 75 insertions, 18 deletions
diff --git a/ranger/config/commands.py b/ranger/config/commands.py index 1bd6f96f..5af41723 100755 --- a/ranger/config/commands.py +++ b/ranger/config/commands.py @@ -482,36 +482,93 @@ class set_(Command): return None -class setlocal(set_): - """:setlocal path=<regular expression> <option name>=<python expression> +class _setlocal(set_): + """Shared class for setinpath and setinregex - Gives an option a new value. + By implementing the _arg abstract propery you can affect what the name of + the pattern/path/regex argument can be, this should be a regular expression + without match groups. + + By implementing the _format_arg abstract method you can affect how the + argument is formatted as a regular expression. """ - PATH_RE_DQUOTED = re.compile(r'^setlocal\s+path="(.*?)"') - PATH_RE_SQUOTED = re.compile(r"^setlocal\s+path='(.*?)'") - PATH_RE_UNQUOTED = re.compile(r'^path=(.*?)$') + from abc import (ABCMeta, abstractmethod, abstractproperty) + + __metaclass__ = ABCMeta + + @abstractproperty + def _arg(self): + """The name of the option for the path/regex""" + raise NotImplementedError + + def __init__(self, *args, **kwargs): + super(set_, self).__init__(*args, **kwargs) + # We require quoting of paths with whitespace so we have to take care + # not to match escaped quotes. + self.PATH_RE_DQUOTED = re.compile( + r'^set.+?\s+{arg}="(.*?[^\\])"'.format(arg=self._arg()) + ) + self.PATH_RE_SQUOTED = re.compile( + r"^set.+?\s+{arg}='(.*?[^\\])'".format(arg=self._arg()) + ) + self.PATH_RE_UNQUOTED = re.compile( + r'^{arg}=(.+?)$'.format(arg=self._arg()) + ) def _re_shift(self, match): if not match: return None - path = os.path.expanduser(match.group(1)) - for _ in range(len(path.split())): + path = match.group(1) + for _ in range(1 + min(1, len(path.split()))): self.shift() - return path + return os.path.expanduser(path) + + @abstractmethod + def _format_arg(self, arg): + """How to format the argument as a regular expression""" + raise NotImplementedError def execute(self): - path = self._re_shift(self.PATH_RE_DQUOTED.match(self.line)) - if path is None: - path = self._re_shift(self.PATH_RE_SQUOTED.match(self.line)) - if path is None: - path = self._re_shift(self.PATH_RE_UNQUOTED.match(self.arg(1))) - if path is None and self.fm.thisdir: - path = "^" + re.escape(self.fm.thisdir.path) + "$" - if not path: + arg = self._re_shift(self.PATH_RE_DQUOTED.match(self.line)) + branch = 0 + if arg is None: + arg = self._re_shift(self.PATH_RE_SQUOTED.match(self.line)) + branch = 1 + if arg is None: + arg = self._re_shift(self.PATH_RE_UNQUOTED.match(self.arg(1))) + branch = 2 + if arg is None and self.fm.thisdir: + arg = self.fm.thisdir.path + branch = 3 + if arg is None: return + else: + arg = self._format_arg(arg) name, value, _ = self.parse_setting_line() - self.fm.set_option_from_string(name, value, localpath=path) + self.fm.set_option_from_string(name, value, localpath=arg) + + +class setinpath(_setlocal): + """:setinpath path=<path> <option name>=<python expression> + + Sets an option when in a directory that matches <path>, relative paths can + match multiple directories, for example, ``path=build`` would match a build + directory in any directory. If the <path> contains whitespace it needs to + be quoted and nested quotes need to be backslash-escaped. The "path" + argument can also be named "pattern" to allow for easier switching with + ``setinregex``. + """ + def _arg(self): + return "(?:path|pattern)" + + def _format_arg(self, arg): + return "{0}$".format(re.escape(arg)) + + +class setlocal(setinpath): + """:setlocal is an alias for :setinpath""" + pass class setintag(set_): |