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.
|<<config-clear,config-clear>>|Set all settings back to their default.
|<<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-unset,config-unset>>|Unset an option.
|<<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.
* +*-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
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.utils import objreg, utils, message, standarddir
from qutebrowser.config import configtypes, configexc, configfiles
from qutebrowser.misc import editor
class ConfigCommands:
@ -226,3 +227,27 @@ class ConfigCommands:
configfiles.read_config_py(filename)
except configexc.ConfigFileErrors as 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."""
import logging
import pytest
from PyQt5.QtCore import QUrl
from PyQt5.QtCore import QUrl, QProcess
from qutebrowser.config import configcommands
from qutebrowser.commands import cmdexc
@ -302,6 +304,53 @@ class TestSource:
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:
"""Tests for :bind and :unbind."""

View File

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