about summary refs log tree commit diff stats
path: root/ranger
diff options
context:
space:
mode:
Diffstat (limited to 'ranger')
-rwxr-xr-xranger/config/commands.py93
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_):