diff options
-rw-r--r-- | doc/ranger.1 | 39 | ||||
-rw-r--r-- | doc/ranger.pod | 39 | ||||
-rwxr-xr-x | ranger/config/commands.py | 106 | ||||
-rw-r--r-- | ranger/container/settings.py | 2 |
4 files changed, 146 insertions, 40 deletions
diff --git a/doc/ranger.1 b/doc/ranger.1 index 5ce8566a..c005afee 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -133,7 +133,7 @@ .\" ======================================================================== .\" .IX Title "RANGER 1" -.TH RANGER 1 "ranger-1.9.3" "2021-09-01" "ranger manual" +.TH RANGER 1 "ranger-1.9.3" "2021-09-05" "ranger manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -1288,6 +1288,8 @@ ranger. For your convenience, this is a list of the \*(L"public\*(R" commands i \& search pattern \& search_inc pattern \& set option value +\& setinpath [path=<path>] option value +\& setinregex [re=<regex>] option value \& setintag tags option value \& setlocal [path=<path>] option value \& shell [\-FLAGS...] command @@ -1693,6 +1695,30 @@ doesn't work for functions and regular expressions. Valid values are: \& list | 1,2,3,4 \& none | none .Ve +.IP "setinpath [path=\fIpath\fR] \fIoption\fR \fIvalue\fR" 2 +.IX Item "setinpath [path=path] option value" +Assigns a new value to an option, but locally for the directory given by +\&\fIpath\fR. This means, that this option only takes effect when visiting that +directory. If no path is given, uses the current directory. +.Sp +\&\fIpath\fR can be quoted with either single or double quotes to prevent unwanted +splitting, \fIpath='~/dl dl'\fR or \fIpath=\*(L"~/dl dl\*(R"\fR. You can use \*(L"pattern\*(R" rather +than \*(L"path\*(R" for consistency with \f(CW\*(C`setinregex\*(C'\fR. +.IP "setinregex [re=\fIregex\fR] \fIoption\fR \fIvalue\fR" 2 +.IX Item "setinregex [re=regex] option value" +Assigns a new value to an option, but locally for directories matching +\&\fIregex\fR. This means, that this option only takes effect when visiting such +directories. If no regular expression is given, uses the current directory. +.Sp +\&\fIregex\fR is a regular expression. This means that \f(CW\*(C`re=~/dl\*(C'\fR applies to all +paths that start with \fI~/dl\fR, e.g. \fI~/dl2\fR and \fI~/dl/foo\fR. To avoid this, +use \f(CW\*(C`path=~/dl$\*(C'\fR. To specify a folder with special characters +(.^$\e*+?(){}[]|), escape them with a backslash. +.Sp +\&\fIregex\fR can be quoted with either single or double quotes to prevent unwanted +splitting,. \fIre='~/dl dl$'\fR or \fIre=\*(L"~/dl dl$\*(R"\fR. You can use \*(L"regex\*(R" rather +than \*(L"re\*(R" to avoid having to remember the spelling and you can use \*(L"pattern\*(R" +for consistency with \f(CW\*(C`setinpath\*(C'\fR. .IP "setintag \fItags\fR \fIoption\fR \fIvalue\fR" 2 .IX Item "setintag tags option value" Assigns a new value to an option, but locally for the directories that are @@ -1707,16 +1733,7 @@ with the \fIv\fR tag by typing \fI"v\fR, then use this command: .Ve .IP "setlocal [path=\fIpath\fR] \fIoption\fR \fIvalue\fR" 2 .IX Item "setlocal [path=path] option value" -Assigns a new value to an option, but locally for the directory given by -\&\fIpath\fR. This means, that this option only takes effect when visiting that -directory. If no path is given, uses the current directory. -.Sp -\&\fIpath\fR is a regular expression. This means that \f(CW\*(C`path=~/dl\*(C'\fR applies to all -paths that start with \fI~/dl\fR, e.g. \fI~/dl2\fR and \fI~/dl/foo\fR. To avoid this, -use \f(CW\*(C`path=~/dl$\*(C'\fR. -.Sp -\&\fIpath\fR can be quoted with either single or double quotes to prevent unwanted -splitting. \fIpath='~/dl dl$'\fR or \fIpath=\*(L"~/dl dl$\*(R"\fR +Alias for \f(CW\*(C`setinpath\*(C'\fR. .IP "shell [\-\fIflags\fR] \fIcommand\fR" 2 .IX Item "shell [-flags] command" Run a shell command. \fIflags\fR are discussed in their own section. diff --git a/doc/ranger.pod b/doc/ranger.pod index 3fdc37d3..bf92d1d4 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -1388,6 +1388,8 @@ ranger. For your convenience, this is a list of the "public" commands including search pattern search_inc pattern set option value + setinpath [path=<path>] option value + setinregex [re=<regex>] option value setintag tags option value setlocal [path=<path>] option value shell [-FLAGS...] command @@ -1837,6 +1839,32 @@ doesn't work for functions and regular expressions. Valid values are: list | 1,2,3,4 none | none +=item setinpath [path=I<path>] I<option> I<value> + +Assigns a new value to an option, but locally for the directory given by +I<path>. This means, that this option only takes effect when visiting that +directory. If no path is given, uses the current directory. + +I<path> can be quoted with either single or double quotes to prevent unwanted +splitting, I<path='~/dl dl'> or I<path="~/dl dl">. You can use "pattern" rather +than "path" for consistency with C<setinregex>. + +=item setinregex [re=I<regex>] I<option> I<value> + +Assigns a new value to an option, but locally for directories matching +I<regex>. This means, that this option only takes effect when visiting such +directories. If no regular expression is given, uses the current directory. + +I<regex> is a regular expression. This means that C<re=~/dl> applies to all +paths that start with I<~/dl>, e.g. I<~/dl2> and I<~/dl/foo>. To avoid this, +use C<path=~/dl$>. To specify a folder with special characters +(.^$\*+?(){}[]|), escape them with a backslash. + +I<regex> can be quoted with either single or double quotes to prevent unwanted +splitting,. I<re='~/dl dl$'> or I<re="~/dl dl$">. You can use "regex" rather +than "re" to avoid having to remember the spelling and you can use "pattern" +for consistency with C<setinpath>. + =item setintag I<tags> I<option> I<value> Assigns a new value to an option, but locally for the directories that are @@ -1850,16 +1878,7 @@ with the I<v> tag by typing I<"v>, then use this command: =item setlocal [path=I<path>] I<option> I<value> -Assigns a new value to an option, but locally for the directory given by -I<path>. This means, that this option only takes effect when visiting that -directory. If no path is given, uses the current directory. - -I<path> is a regular expression. This means that C<path=~/dl> applies to all -paths that start with I<~/dl>, e.g. I<~/dl2> and I<~/dl/foo>. To avoid this, -use C<path=~/dl$>. - -I<path> can be quoted with either single or double quotes to prevent unwanted -splitting. I<path='~/dl dl$'> or I<path="~/dl dl$"> +Alias for C<setinpath>. =item shell [-I<flags>] I<command> 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 |