Add a :config-edit command

See #2794
This commit is contained in:
Florian Bruhin 2017-10-03 18:54:40 +02:00
parent 555930791f
commit 727580d1f4
4 changed files with 86 additions and 1 deletions

View File

@ -33,6 +33,7 @@ It is possible to run or bind multiple commands by separating them with `;;`.
|<<close,close>>|Close the current window. |<<close,close>>|Close the current window.
|<<config-clear,config-clear>>|Set all settings back to their default. |<<config-clear,config-clear>>|Set all settings back to their default.
|<<config-cycle,config-cycle>>|Cycle an option between multiple values. |<<config-cycle,config-cycle>>|Cycle an option between multiple values.
|<<config-edit,config-edit>>|Open the config.py file in the editor.
|<<config-source,config-source>>|Read a config.py file. |<<config-source,config-source>>|Read a config.py file.
|<<config-unset,config-unset>>|Unset an option. |<<config-unset,config-unset>>|Unset an option.
|<<download,download>>|Download a given URL, or current page if no URL given. |<<download,download>>|Download a given URL, or current page if no URL given.
@ -229,6 +230,15 @@ Cycle an option between multiple values.
* +*-t*+, +*--temp*+: Set value temporarily until qutebrowser is closed. * +*-t*+, +*--temp*+: Set value temporarily until qutebrowser is closed.
* +*-p*+, +*--print*+: Print the value after setting. * +*-p*+, +*--print*+: Print the value after setting.
[[config-edit]]
=== config-edit
Syntax: +:config-edit [*--no-source*]+
Open the config.py file in the editor.
==== optional arguments
* +*-n*+, +*--no-source*+: Don't re-source the config file after editing.
[[config-source]] [[config-source]]
=== config-source === config-source
Syntax: +:config-source [*--clear*] ['filename']+ Syntax: +:config-source [*--clear*] ['filename']+

View File

@ -28,6 +28,7 @@ from qutebrowser.commands import cmdexc, cmdutils
from qutebrowser.completion.models import configmodel from qutebrowser.completion.models import configmodel
from qutebrowser.utils import objreg, utils, message, standarddir from qutebrowser.utils import objreg, utils, message, standarddir
from qutebrowser.config import configtypes, configexc, configfiles from qutebrowser.config import configtypes, configexc, configfiles
from qutebrowser.misc import editor
class ConfigCommands: class ConfigCommands:
@ -226,3 +227,27 @@ class ConfigCommands:
configfiles.read_config_py(filename) configfiles.read_config_py(filename)
except configexc.ConfigFileErrors as e: except configexc.ConfigFileErrors as e:
raise cmdexc.CommandError(e) raise cmdexc.CommandError(e)
@cmdutils.register(instance='config-commands')
def config_edit(self, no_source=False):
"""Open the config.py file in the editor.
Args:
no_source: Don't re-source the config file after editing.
"""
def on_editing_finished():
"""Source the new config when editing finished.
This can't use cmdexc.CommandError as it's run async.
"""
try:
configfiles.read_config_py(filename)
except configexc.ConfigFileErrors as e:
message.error(str(e))
ed = editor.ExternalEditor(self._config)
if not no_source:
ed.editing_finished.connect(on_editing_finished)
filename = os.path.join(standarddir.config(), 'config.py')
ed.edit_file(filename)

View File

@ -18,8 +18,10 @@
"""Tests for qutebrowser.config.configcommands.""" """Tests for qutebrowser.config.configcommands."""
import logging
import pytest import pytest
from PyQt5.QtCore import QUrl from PyQt5.QtCore import QUrl, QProcess
from qutebrowser.config import configcommands from qutebrowser.config import configcommands
from qutebrowser.commands import cmdexc from qutebrowser.commands import cmdexc
@ -302,6 +304,53 @@ class TestSource:
assert str(excinfo.value) == expected assert str(excinfo.value) == expected
class TestEdit:
"""Tests for :config-edit."""
def test_no_source(self, commands, mocker, config_tmpdir):
mock = mocker.patch('qutebrowser.config.configcommands.editor.'
'ExternalEditor._start_editor', autospec=True)
commands.config_edit(no_source=True)
mock.assert_called_once()
@pytest.fixture
def patch_editor(self, mocker, config_tmpdir, data_tmpdir):
"""Write a config.py file."""
def do_patch(text):
def _write_file(editor_self):
with open(editor_self._filename, 'w', encoding='utf-8') as f:
f.write(text)
editor_self.on_proc_closed(0, QProcess.NormalExit)
return mocker.patch('qutebrowser.config.configcommands.editor.'
'ExternalEditor._start_editor', autospec=True,
side_effect=_write_file)
return do_patch
def test_with_sourcing(self, commands, config_stub, patch_editor):
assert config_stub.val.content.javascript.enabled
mock = patch_editor('c.content.javascript.enabled = False')
commands.config_edit()
mock.assert_called_once()
assert not config_stub.val.content.javascript.enabled
def test_error(self, commands, config_stub, patch_editor, message_mock,
caplog):
patch_editor('c.foo = 42')
with caplog.at_level(logging.ERROR):
commands.config_edit()
msg = message_mock.getmsg()
expected = ("Errors occurred while reading config.py:\n"
" While setting 'foo': No option 'foo'")
assert msg.text == expected
class TestBind: class TestBind:
"""Tests for :bind and :unbind.""" """Tests for :bind and :unbind."""

View File

@ -43,6 +43,7 @@ def editor(caplog):
ed.editing_finished = mock.Mock() ed.editing_finished = mock.Mock()
yield ed yield ed
with caplog.at_level(logging.ERROR): with caplog.at_level(logging.ERROR):
ed._remove_file = True
ed._cleanup() ed._cleanup()