qutebrowser / qutebrowser

A keyboard-driven, vim-like browser based on Python and Qt.
https://www.qutebrowser.org/
GNU General Public License v3.0
9.6k stars 1.01k forks source link

Support URL patterns for more :config-* commands #5856

Open The-Compiler opened 3 years ago

The-Compiler commented 3 years ago
The-Compiler commented 3 years ago

Here's a patch attempting to add pattern support for :config-{list,dict}-{add,remove}:

diff --git i/doc/help/commands.asciidoc w/doc/help/commands.asciidoc
index 8eca20caf..179906cb4 100644
--- i/doc/help/commands.asciidoc
+++ w/doc/help/commands.asciidoc
@@ -300,7 +300,7 @@ Cycle an option between multiple values.

 [[config-dict-add]]
 === config-dict-add
-Syntax: +:config-dict-add [*--temp*] [*--replace*] 'option' 'key' 'value'+
+Syntax: +:config-dict-add [*--pattern* 'pattern'] [*--temp*] [*--replace*] 'option' 'key' 'value'+

 Add a key/value pair to a dictionary option.

@@ -310,13 +310,14 @@ Add a key/value pair to a dictionary option.
 * +'value'+: The value to place in the dictionary.

 ==== optional arguments
+* +*-u*+, +*--pattern*+: The URL pattern to use.
 * +*-t*+, +*--temp*+: Add value temporarily until qutebrowser is closed.
 * +*-r*+, +*--replace*+: Replace existing values. By default, existing values are not overwritten.

 [[config-dict-remove]]
 === config-dict-remove
-Syntax: +:config-dict-remove [*--temp*] 'option' 'key'+
+Syntax: +:config-dict-remove [*--pattern* 'pattern'] [*--temp*] 'option' 'key'+

 Remove a key from a dict.

@@ -325,6 +326,7 @@ Remove a key from a dict.
 * +'key'+: The key to remove from the dict.

 ==== optional arguments
+* +*-u*+, +*--pattern*+: The URL pattern to use.
 * +*-t*+, +*--temp*+: Remove value temporarily until qutebrowser is closed.

 [[config-diff]]
@@ -342,7 +344,7 @@ Open the config.py file in the editor.

 [[config-list-add]]
 === config-list-add
-Syntax: +:config-list-add [*--temp*] 'option' 'value'+
+Syntax: +:config-list-add [*--pattern* 'pattern'] [*--temp*] 'option' 'value'+

 Append a value to a config option that is a list.

@@ -351,11 +353,12 @@ Append a value to a config option that is a list.
 * +'value'+: The value to append to the end of the list.

 ==== optional arguments
+* +*-u*+, +*--pattern*+: The URL pattern to use.
 * +*-t*+, +*--temp*+: Add value temporarily until qutebrowser is closed.

 [[config-list-remove]]
 === config-list-remove
-Syntax: +:config-list-remove [*--temp*] 'option' 'value'+
+Syntax: +:config-list-remove [*--pattern* 'pattern'] [*--temp*] 'option' 'value'+

 Remove a value from a list.

@@ -364,6 +367,7 @@ Remove a value from a list.
 * +'value'+: The value to remove from the list.

 ==== optional arguments
+* +*-u*+, +*--pattern*+: The URL pattern to use.
 * +*-t*+, +*--temp*+: Remove value temporarily until qutebrowser is closed.

 [[config-source]]
@@ -381,7 +385,7 @@ Read a config.py file.

 [[config-unset]]
 === config-unset
-Syntax: +:config-unset [*--temp*] 'option'+
+Syntax: +:config-unset [*--pattern* 'pattern'] [*--temp*] 'option'+

 Unset an option.

@@ -391,6 +395,7 @@ This sets an option back to its default and removes it from autoconfig.yml.
 * +'option'+: The name of the option.

 ==== optional arguments
+* +*-u*+, +*--pattern*+: The URL pattern to use.
 * +*-t*+, +*--temp*+: Set value temporarily until qutebrowser is closed.

 [[config-write-py]]
diff --git i/qutebrowser/config/configcommands.py w/qutebrowser/config/configcommands.py
index afec3d5aa..ae0cbb0dc 100644
--- i/qutebrowser/config/configcommands.py
+++ w/qutebrowser/config/configcommands.py
@@ -244,7 +244,14 @@ def config_cycle(self, option: str, *values: str,

     @cmdutils.register(instance='config-commands')
     @cmdutils.argument('option', completion=configmodel.customized_option)
-    def config_unset(self, option: str, temp: bool = False) -> None:
+    @cmdutils.argument('pattern', flag='u')
+    def config_unset(
+        self,
+        option: str,
+        *,
+        pattern: str = None,
+        temp: bool = False,
+    ) -> None:
         """Unset an option.

         This sets an option back to its default and removes it from
@@ -252,10 +259,12 @@ def config_unset(self, option: str, temp: bool = False) -> None:

         Args:
             option: The name of the option.
+            pattern: The URL pattern to use.
             temp: Set value temporarily until qutebrowser is closed.
         """
+        parsed_pattern = self._parse_pattern(pattern)
         with self._handle_config_error():
-            self._config.unset(option, save_yaml=not temp)
+            self._config.unset(option, save_yaml=not temp, pattern=parsed_pattern)

     @cmdutils.register(instance='config-commands')
     @cmdutils.argument('win_id', value=cmdutils.Value.win_id)
@@ -268,13 +277,21 @@ def config_diff(self, win_id: int) -> None:

     @cmdutils.register(instance='config-commands')
     @cmdutils.argument('option', completion=configmodel.list_option)
-    def config_list_add(self, option: str, value: str,
-                        temp: bool = False) -> None:
+    @cmdutils.argument('pattern', flag='u')
+    def config_list_add(
+        self,
+        option: str,
+        value: str,
+        *,
+        pattern: str = None,
+        temp: bool = False,
+    ) -> None:
         """Append a value to a config option that is a list.

         Args:
             option: The name of the option.
             value: The value to append to the end of the list.
+            pattern: The URL pattern to use.
             temp: Add value temporarily until qutebrowser is closed.
         """
         with self._handle_config_error():
@@ -283,22 +300,33 @@ def config_list_add(self, option: str, value: str,
         if not isinstance(opt.typ, valid_list_types):
             raise cmdutils.CommandError(":config-list-add can only be used "
                                         "for lists")
+        parsed_pattern = self._parse_pattern(pattern)

         with self._handle_config_error():
-            option_value = self._config.get_mutable_obj(option)
+            option_value = self._config.get_mutable_obj(option, pattern=parsed_pattern)
             option_value.append(value)
             self._config.update_mutables(save_yaml=not temp)

     @cmdutils.register(instance='config-commands')
     @cmdutils.argument('option', completion=configmodel.dict_option)
-    def config_dict_add(self, option: str, key: str, value: str,
-                        temp: bool = False, replace: bool = False) -> None:
+    @cmdutils.argument('pattern', flag='u')
+    def config_dict_add(
+        self,
+        option: str,
+        key: str,
+        value: str,
+        *,
+        pattern: str = None,
+        temp: bool = False,
+        replace: bool = False,
+    ) -> None:
         """Add a key/value pair to a dictionary option.

         Args:
             option: The name of the option.
             key: The key to use.
             value: The value to place in the dictionary.
+            pattern: The URL pattern to use.
             temp: Add value temporarily until qutebrowser is closed.
             replace: Replace existing values. By default, existing values are
                      not overwritten.
@@ -308,9 +336,10 @@ def config_dict_add(self, option: str, key: str, value: str,
         if not isinstance(opt.typ, configtypes.Dict):
             raise cmdutils.CommandError(":config-dict-add can only be used "
                                         "for dicts")
+        parsed_pattern = self._parse_pattern(pattern)

         with self._handle_config_error():
-            option_value = self._config.get_mutable_obj(option)
+            option_value = self._config.get_mutable_obj(option, pattern=parsed_pattern)

             if key in option_value and not replace:
                 raise cmdutils.CommandError("{} already exists in {} - use "
@@ -322,13 +351,21 @@ def config_dict_add(self, option: str, key: str, value: str,

     @cmdutils.register(instance='config-commands')
     @cmdutils.argument('option', completion=configmodel.list_option)
-    def config_list_remove(self, option: str, value: str,
-                           temp: bool = False) -> None:
+    @cmdutils.argument('pattern', flag='u')
+    def config_list_remove(
+        self,
+        option: str,
+        value: str,
+        *,
+        pattern: str = None,
+        temp: bool = False,
+    ) -> None:
         """Remove a value from a list.

         Args:
             option: The name of the option.
             value: The value to remove from the list.
+            pattern: The URL pattern to use.
             temp: Remove value temporarily until qutebrowser is closed.
         """
         with self._handle_config_error():
@@ -337,9 +374,10 @@ def config_list_remove(self, option: str, value: str,
         if not isinstance(opt.typ, valid_list_types):
             raise cmdutils.CommandError(":config-list-remove can only be used "
                                         "for lists")
+        parsed_pattern = self._parse_pattern(pattern)

         with self._handle_config_error():
-            option_value = self._config.get_mutable_obj(option)
+            option_value = self._config.get_mutable_obj(option, pattern=parsed_pattern)

             if value not in option_value:
                 raise cmdutils.CommandError("{} is not in {}!".format(
@@ -351,13 +389,21 @@ def config_list_remove(self, option: str, value: str,

     @cmdutils.register(instance='config-commands')
     @cmdutils.argument('option', completion=configmodel.dict_option)
-    def config_dict_remove(self, option: str, key: str,
-                           temp: bool = False) -> None:
+    @cmdutils.argument('pattern', flag='u')
+    def config_dict_remove(
+        self,
+        option: str,
+        key: str,
+        *,
+        pattern: str = None,
+        temp: bool = False,
+    ) -> None:
         """Remove a key from a dict.

         Args:
             option: The name of the option.
             key: The key to remove from the dict.
+            pattern: The URL pattern to use.
             temp: Remove value temporarily until qutebrowser is closed.
         """
         with self._handle_config_error():
@@ -365,9 +411,10 @@ def config_dict_remove(self, option: str, key: str,
         if not isinstance(opt.typ, configtypes.Dict):
             raise cmdutils.CommandError(":config-dict-remove can only be used "
                                         "for dicts")
+        parsed_pattern = self._parse_pattern(pattern)

         with self._handle_config_error():
-            option_value = self._config.get_mutable_obj(option)
+            option_value = self._config.get_mutable_obj(option, pattern=parsed_pattern)

             if key not in option_value:
                 raise cmdutils.CommandError("{} is not in {}!".format(
diff --git i/tests/unit/config/test_configcommands.py w/tests/unit/config/test_configcommands.py
index 72af2ad3e..451eed09b 100644
--- i/tests/unit/config/test_configcommands.py
+++ w/tests/unit/config/test_configcommands.py
@@ -356,6 +356,18 @@ def test_dict_add_replace(self, commands, config_stub, replace):
                           "overwrite!"):
                 commands.config_dict_add(name, key, value, replace=False)

+    def test_dict_add_pattern(self, commands, config_stub):
+        name = 'content.headers.custom'
+        pattern = '*://example.com/*'
+        key = 'X-qutebrowser'
+        value = 'test'
+
+        commands.config_dict_add(name, key, value, pattern=pattern)
+
+        assert not config_stub.get(name)
+        config_value = config_stub.get(name, pattern=urlmatch.UrlPattern(pattern))
+        assert config_value == {key: value}
+
     def test_dict_add_invalid_option(self, commands):
         with pytest.raises(
                 cmdutils.CommandError,

doesn't seem to work properly though - for some reason, the option is still set globally.