diff options
author | toonn <toonn@toonn.io> | 2021-09-07 19:20:56 +0200 |
---|---|---|
committer | toonn <toonn@toonn.io> | 2021-09-07 19:20:56 +0200 |
commit | 6f3a6006a0cd074a550e645383c7240d2d97f1ef (patch) | |
tree | dddf80df892f850736f585703549ed6db1dbc64c /ranger | |
parent | 7e6a0aa6a144f6733a8000a2822c107c93e60c1a (diff) | |
parent | 99771df4d7ec71b45976bdc85be66c30b725d14e (diff) | |
download | ranger-6f3a6006a0cd074a550e645383c7240d2d97f1ef.tar.gz |
Merge branch 'jakanaka-eva-setlocal'
Diffstat (limited to 'ranger')
-rwxr-xr-x | ranger/config/commands.py | 106 | ||||
-rw-r--r-- | ranger/container/settings.py | 2 |
2 files changed, 89 insertions, 19 deletions
diff --git a/ranger/config/commands.py b/ranger/config/commands.py index d1d84e91..43c4993f 100755 --- a/ranger/config/commands.py +++ b/ranger/config/commands.py @@ -482,36 +482,106 @@ 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 + + @property + @abstractmethod + def _arg(self): + """The name of the option for the path/regex""" + raise NotImplementedError + + def __init__(self, *args, **kwargs): + super(setlocal_, 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) + # Prepend something that behaves like "path=" in case path starts with + # whitespace + for _ in "={0}".format(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 = self.fm.thisdir.path - if not path: + arg = self._re_shift(self.path_re_dquoted.match(self.line)) + if arg is None: + arg = self._re_shift(self.path_re_squoted.match(self.line)) + if arg is None: + arg = self._re_shift(self.path_re_unquoted.match(self.arg(1))) + if arg is None and self.fm.thisdir: + arg = self.fm.thisdir.path + 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``. + """ + _arg = "(?:path|pattern)" + + def _format_arg(self, arg): + return "{0}$".format(re.escape(arg)) + + +class setlocal(setinpath): + """:setlocal is an alias for :setinpath""" + + +class setinregex(setlocal_): + """:setinregex re=<regex> <option name>=<python expression> + + Sets an option when in a specific directory. If the <regex> contains + whitespace it needs to be quoted and nested quotes need to be + backslash-escaped. Special characters need to be escaped if they are + intended to match literally as documented in the ``re`` library + documentation. The "re" argument can also be named "regex" or "pattern," + which allows for easier switching with ``setinpath``. + """ + _arg = "(?:re(?:gex)?|pattern)" + + def _format_arg(self, arg): + return arg class setintag(set_): diff --git a/ranger/container/settings.py b/ranger/container/settings.py index e083c5ac..80250d4f 100644 --- a/ranger/container/settings.py +++ b/ranger/container/settings.py @@ -281,7 +281,7 @@ class Settings(SignalDispatcher, FileManagerAware): if path: if path not in self._localsettings: try: - regex = re.compile(re.escape(path)) + regex = re.compile(path) except re.error: # Bad regular expression return self._localregexes[path] = regex |