Add :config-clear and :config-unset

See #2794
This commit is contained in:
Florian Bruhin 2017-10-03 12:44:22 +02:00
parent b06a38ce7e
commit ed8c3f4aa2
9 changed files with 216 additions and 3 deletions

View File

@ -60,7 +60,10 @@ Added
Together with the previous setting, this should make wrapper scripts
unnecessary.
- Proxy authentication is now supported with the QtWebEngine backend.
- New `:config-cycle` command to cycle an option between multiple values.
- New config commands:
- `:config-cycle` to cycle an option between multiple values.
- `:config-unset` to remove a configured option
- `:config-clear` to remove all configured options
Changed
~~~~~~~

View File

@ -31,7 +31,9 @@ It is possible to run or bind multiple commands by separating them with `;;`.
|<<bookmark-load,bookmark-load>>|Load a bookmark.
|<<buffer,buffer>>|Select tab by index or url/title best match.
|<<close,close>>|Close the current window.
|<<config-clear,config-clear>>|Set all settings back to their default.
|<<config-cycle,config-cycle>>|Cycle an option between multiple values.
|<<config-unset,config-unset>>|Unset an option.
|<<download,download>>|Download a given URL, or current page if no URL given.
|<<download-cancel,download-cancel>>|Cancel the last/[count]th download.
|<<download-clear,download-clear>>|Remove all finished downloads from the list.
@ -202,6 +204,16 @@ The tab index to focus, starting with 1.
=== close
Close the current window.
[[config-clear]]
=== config-clear
Syntax: +:config-clear [*--save*]+
Set all settings back to their default.
==== optional arguments
* +*-s*+, +*--save*+: If given, all configuration in autoconfig.yml is also removed.
[[config-cycle]]
=== config-cycle
Syntax: +:config-cycle [*--temp*] [*--print*] 'option' 'values' ['values' ...]+
@ -216,6 +228,20 @@ Cycle an option between multiple values.
* +*-t*+, +*--temp*+: Set value temporarily until qutebrowser is closed.
* +*-p*+, +*--print*+: Print the value after setting.
[[config-unset]]
=== config-unset
Syntax: +:config-unset [*--temp*] 'option'+
Unset an option.
This sets an option back to its default and removes it from autoconfig.yml.
==== positional arguments
* +'option'+: The name of the option.
==== optional arguments
* +*-t*+, +*--temp*+: Don't touch autoconfig.yml.
[[download]]
=== download
Syntax: +:download [*--mhtml*] [*--dest* 'dest'] ['url'] ['dest-old']+

View File

@ -319,6 +319,32 @@ class Config(QObject):
if save_yaml:
self._yaml[name] = converted
def unset(self, name, *, save_yaml=False):
"""Set the given setting back to its default."""
self.get_opt(name)
try:
del self._values[name]
except KeyError:
return
self.changed.emit(name)
if save_yaml:
self._yaml.unset(name)
def clear(self, *, save_yaml=False):
"""Clear all settings in the config.
If save_yaml=True is given, also remove all customization from the YAML
file.
"""
old_values = self._values
self._values = {}
for name in old_values:
self.changed.emit(name)
if save_yaml:
self._yaml.clear()
def update_mutables(self, *, save_yaml=False):
"""Update mutable settings if they changed.

View File

@ -178,3 +178,28 @@ class ConfigCommands:
if print_:
self._print_value(option)
@cmdutils.register(instance='config-commands')
@cmdutils.argument('option', completion=configmodel.option)
def config_unset(self, option, temp=False):
"""Unset an option.
This sets an option back to its default and removes it from
autoconfig.yml.
Args:
option: The name of the option.
temp: Don't touch autoconfig.yml.
"""
with self._handle_config_error():
self._config.unset(option, save_yaml=not temp)
@cmdutils.register(instance='config-commands')
def config_clear(self, save=False):
"""Set all settings back to their default.
Args:
save: If given, all configuration in autoconfig.yml is also
removed.
"""
self._config.clear(save_yaml=save)

View File

@ -102,9 +102,8 @@ class YamlConfig(QObject):
return self._values[name]
def __setitem__(self, name, value):
self.changed.emit()
self._dirty = True
self._values[name] = value
self._mark_changed()
def __contains__(self, name):
return name in self._values
@ -112,6 +111,11 @@ class YamlConfig(QObject):
def __iter__(self):
return iter(self._values.items())
def _mark_changed(self):
"""Mark the YAML config as changed."""
self._dirty = True
self.changed.emit()
def _save(self):
"""Save the settings to the YAML file if they've changed."""
if not self._dirty:
@ -167,6 +171,19 @@ class YamlConfig(QObject):
self._values = global_obj
self._dirty = False
def unset(self, name):
"""Remove the given option name if it's configured."""
try:
del self._values[name]
except KeyError:
return
self._mark_changed()
def clear(self):
"""Clear all values from the YAML file."""
self._values = []
self._mark_changed()
class ConfigAPI:

View File

@ -432,6 +432,12 @@ class FakeYamlConfig:
def __getitem__(self, key):
return self._values[key]
def unset(self, name):
self._values.pop(name, None)
def clear(self):
self._values = []
class StatusBarCommandStub(QLineEdit):

View File

@ -279,6 +279,56 @@ class TestConfig:
conf._set_value(opt, 'never')
assert conf._values['tabs.show'] == 'never'
@pytest.mark.parametrize('save_yaml', [True, False])
def test_unset(self, conf, qtbot, save_yaml):
name = 'tabs.show'
conf.set_obj(name, 'never', save_yaml=True)
assert conf.get(name) == 'never'
with qtbot.wait_signal(conf.changed):
conf.unset(name, save_yaml=save_yaml)
assert conf.get(name) == 'always'
if save_yaml:
assert name not in conf._yaml
else:
assert conf._yaml[name] == 'never'
def test_unset_never_set(self, conf, qtbot):
name = 'tabs.show'
assert conf.get(name) == 'always'
with qtbot.assert_not_emitted(conf.changed):
conf.unset(name)
assert conf.get(name) == 'always'
def test_unset_unknown(self, conf):
with pytest.raises(configexc.NoOptionError):
conf.unset('tabs')
@pytest.mark.parametrize('save_yaml', [True, False])
def test_clear(self, conf, qtbot, save_yaml):
name1 = 'tabs.show'
name2 = 'content.plugins'
conf.set_obj(name1, 'never', save_yaml=True)
conf.set_obj(name2, True, save_yaml=True)
assert conf._values[name1] == 'never'
assert conf._values[name2] is True
with qtbot.waitSignals([conf.changed, conf.changed]) as blocker:
conf.clear(save_yaml=save_yaml)
options = [e.args[0] for e in blocker.all_signals_and_args]
assert options == [name1, name2]
if save_yaml:
assert name1 not in conf._yaml
assert name2 not in conf._yaml
else:
assert conf._yaml[name1] == 'never'
assert conf._yaml[name2] is True
def test_read_yaml(self, conf):
assert not conf._yaml.loaded
conf._yaml['content.plugins'] = True

View File

@ -228,6 +228,41 @@ class TestCycle:
assert msg.text == 'auto_save.session = true'
class TestUnsetAndClear:
"""Test :config-unset and :config-clear."""
@pytest.mark.parametrize('temp', [True, False])
def test_unset(self, commands, config_stub, temp):
name = 'tabs.show'
config_stub.set_obj(name, 'never', save_yaml=True)
commands.config_unset(name, temp=temp)
assert config_stub.get(name) == 'always'
if temp:
assert config_stub._yaml[name] == 'never'
else:
assert name not in config_stub._yaml
def test_unset_unknown_option(self, commands):
with pytest.raises(cmdexc.CommandError, match="No option 'tabs'"):
commands.config_unset('tabs')
@pytest.mark.parametrize('save', [True, False])
def test_clear(self, commands, config_stub, save):
name = 'tabs.show'
config_stub.set_obj(name, 'never', save_yaml=True)
commands.config_clear(save=save)
assert config_stub.get(name) == 'always'
if save:
assert name not in config_stub._yaml
else:
assert config_stub._yaml[name] == 'never'
class TestBind:
"""Tests for :bind and :unbind."""

View File

@ -209,6 +209,31 @@ class TestYaml:
assert isinstance(error.exception, OSError)
assert error.traceback is None
def test_unset(self, qtbot, config_tmpdir):
name = 'tabs.show'
yaml = configfiles.YamlConfig()
yaml[name] = 'never'
with qtbot.wait_signal(yaml.changed):
yaml.unset(name)
assert name not in yaml
def test_unset_never_set(self, qtbot, config_tmpdir):
yaml = configfiles.YamlConfig()
with qtbot.assert_not_emitted(yaml.changed):
yaml.unset('tabs.show')
def test_clear(self, qtbot, config_tmpdir):
name = 'tabs.show'
yaml = configfiles.YamlConfig()
yaml[name] = 'never'
with qtbot.wait_signal(yaml.changed):
yaml.clear()
assert name not in yaml
class ConfPy: