diff options
-rw-r--r-- | qutebrowser/commands/runners.py | 22 | ||||
-rw-r--r-- | qutebrowser/keyinput/modeman.py | 85 | ||||
-rw-r--r-- | qutebrowser/keyinput/modeparsers.py | 30 | ||||
-rw-r--r-- | tests/helpers/stubs.py | 11 | ||||
-rw-r--r-- | tests/unit/keyinput/test_modeparsers.py | 31 |
5 files changed, 117 insertions, 62 deletions
diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py index 7462eff51..c12c2668e 100644 --- a/qutebrowser/commands/runners.py +++ b/qutebrowser/commands/runners.py @@ -303,7 +303,21 @@ class CommandParser: return split_args -class CommandRunner(QObject): +class AbstractCommandRunner(QObject): + + """Abstract base class for CommandRunner.""" + + def run(self, text, count=None, *, safely=False): + raise NotImplementedError + + @pyqtSlot(str, int) + @pyqtSlot(str) + def run_safely(self, text, count=None): + """Run a command and display exceptions in the statusbar.""" + self.run(text, count, safely=True) + + +class CommandRunner(AbstractCommandRunner): """Parse and run qutebrowser commandline commands. @@ -371,9 +385,3 @@ class CommandRunner(QObject): if record_macro and cur_mode == usertypes.KeyMode.normal: macro_recorder = objreg.get('macro-recorder') macro_recorder.record_command(text, count) - - @pyqtSlot(str, int) - @pyqtSlot(str) - def run_safely(self, text, count=None): - """Run a command and display exceptions in the statusbar.""" - self.run(text, count, safely=True) diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index f4b2bf9ac..eb11926c3 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -25,6 +25,7 @@ import attr from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QObject, QEvent from PyQt5.QtWidgets import QApplication +from qutebrowser.commands import runners from qutebrowser.keyinput import modeparsers from qutebrowser.config import config from qutebrowser.api import cmdutils @@ -64,34 +65,68 @@ class NotInModeError(Exception): def init(win_id, parent): """Initialize the mode manager and the keyparsers for the given win_id.""" - KM = usertypes.KeyMode # noqa: N806 modeman = ModeManager(win_id, parent) objreg.register('mode-manager', modeman, scope='window', window=win_id) + + commandrunner = runners.CommandRunner(win_id) + keyparsers = { - KM.normal: - modeparsers.NormalKeyParser(win_id, modeman), - KM.hint: - modeparsers.HintKeyParser(win_id, modeman), - KM.insert: - modeparsers.PassthroughKeyParser(win_id, 'insert', modeman), - KM.passthrough: - modeparsers.PassthroughKeyParser(win_id, 'passthrough', modeman), - KM.command: - modeparsers.PassthroughKeyParser(win_id, 'command', modeman), - KM.prompt: - modeparsers.PassthroughKeyParser(win_id, 'prompt', modeman), - KM.yesno: - modeparsers.PromptKeyParser(win_id, modeman), - KM.caret: - modeparsers.CaretKeyParser(win_id, modeman), - KM.set_mark: - modeparsers.RegisterKeyParser(win_id, KM.set_mark, modeman), - KM.jump_mark: - modeparsers.RegisterKeyParser(win_id, KM.jump_mark, modeman), - KM.record_macro: - modeparsers.RegisterKeyParser(win_id, KM.record_macro, modeman), - KM.run_macro: - modeparsers.RegisterKeyParser(win_id, KM.run_macro, modeman), + usertypes.KeyMode.normal: + modeparsers.NormalKeyParser(win_id=win_id, + commandrunner=commandrunner, + parent=modeman), + usertypes.KeyMode.hint: + modeparsers.HintKeyParser(win_id=win_id, + commandrunner=commandrunner, + parent=modeman), + usertypes.KeyMode.insert: + modeparsers.PassthroughKeyParser(win_id=win_id, + mode='insert', + commandrunner=commandrunner, + parent=modeman), + usertypes.KeyMode.passthrough: + modeparsers.PassthroughKeyParser(win_id=win_id, + mode='passthrough', + commandrunner=commandrunner, + parent=modeman), + usertypes.KeyMode.command: + modeparsers.PassthroughKeyParser(win_id=win_id, + mode='command', + commandrunner=commandrunner, + parent=modeman), + usertypes.KeyMode.prompt: + modeparsers.PassthroughKeyParser(win_id=win_id, + mode='prompt', + commandrunner=commandrunner, + parent=modeman), + usertypes.KeyMode.yesno: + modeparsers.PromptKeyParser(win_id=win_id, + commandrunner=commandrunner, + parent=modeman), + usertypes.KeyMode.caret: + modeparsers.CaretKeyParser(win_id=win_id, + commandrunner=commandrunner, + parent=modeman), + usertypes.KeyMode.set_mark: + modeparsers.RegisterKeyParser(win_id=win_id, + mode=usertypes.KeyMode.set_mark, + commandrunner=commandrunner, + parent=modeman), + usertypes.KeyMode.jump_mark: + modeparsers.RegisterKeyParser(win_id=win_id, + mode=usertypes.KeyMode.jump_mark, + commandrunner=commandrunner, + parent=modeman), + usertypes.KeyMode.record_macro: + modeparsers.RegisterKeyParser(win_id=win_id, + mode=usertypes.KeyMode.record_macro, + commandrunner=commandrunner, + parent=modeman), + usertypes.KeyMode.run_macro: + modeparsers.RegisterKeyParser(win_id=win_id, + mode=usertypes.KeyMode.run_macro, + commandrunner=commandrunner, + parent=modeman), } objreg.register('keyparsers', keyparsers, scope='window', window=win_id) modeman.destroyed.connect( diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index cbfb9a500..bbb393a8f 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -29,7 +29,7 @@ import enum from PyQt5.QtCore import pyqtSlot, Qt from PyQt5.QtGui import QKeySequence -from qutebrowser.commands import runners, cmdexc +from qutebrowser.commands import cmdexc from qutebrowser.config import config from qutebrowser.keyinput import basekeyparser, keyutils from qutebrowser.utils import usertypes, log, message, objreg, utils @@ -47,9 +47,9 @@ class CommandKeyParser(basekeyparser.BaseKeyParser): _commandrunner: CommandRunner instance. """ - def __init__(self, win_id, parent=None): + def __init__(self, win_id, commandrunner, parent=None): super().__init__(win_id, parent) - self._commandrunner = runners.CommandRunner(win_id) + self._commandrunner = commandrunner def execute(self, cmdstr, count=None): try: @@ -66,8 +66,8 @@ class NormalKeyParser(CommandKeyParser): _partial_timer: Timer to clear partial keypresses. """ - def __init__(self, win_id, parent=None): - super().__init__(win_id, parent) + def __init__(self, win_id, commandrunner, parent=None): + super().__init__(win_id, commandrunner, parent) self._read_config('normal') self._partial_timer = usertypes.Timer(self, 'partial-match') self._partial_timer.setSingleShot(True) @@ -144,7 +144,7 @@ class PassthroughKeyParser(CommandKeyParser): passthrough = True supports_count = False - def __init__(self, win_id, mode, parent=None): + def __init__(self, win_id, mode, commandrunner, parent=None): """Constructor. Args: @@ -152,7 +152,7 @@ class PassthroughKeyParser(CommandKeyParser): parent: Qt parent. warn: Whether to warn if an ignored key was bound. """ - super().__init__(win_id, parent) + super().__init__(win_id, commandrunner, parent) self._read_config(mode) self._mode = mode @@ -166,8 +166,8 @@ class PromptKeyParser(CommandKeyParser): supports_count = False - def __init__(self, win_id, parent=None): - super().__init__(win_id, parent) + def __init__(self, win_id, commandrunner, parent=None): + super().__init__(win_id, commandrunner, parent) self._read_config('yesno') def __repr__(self): @@ -185,8 +185,8 @@ class HintKeyParser(CommandKeyParser): supports_count = False - def __init__(self, win_id, parent=None): - super().__init__(win_id, parent) + def __init__(self, win_id, commandrunner, parent=None): + super().__init__(win_id, commandrunner, parent) self._filtertext = '' self._last_press = LastPress.none self._read_config('hint') @@ -298,8 +298,8 @@ class CaretKeyParser(CommandKeyParser): passthrough = True - def __init__(self, win_id, parent=None): - super().__init__(win_id, parent) + def __init__(self, win_id, commandrunner, parent=None): + super().__init__(win_id, commandrunner, parent) self._read_config('caret') @@ -314,8 +314,8 @@ class RegisterKeyParser(CommandKeyParser): supports_count = False - def __init__(self, win_id, mode, parent=None): - super().__init__(win_id, parent) + def __init__(self, win_id, mode, commandrunner, parent=None): + super().__init__(win_id, commandrunner, parent) self._mode = mode self._read_config('register') diff --git a/tests/helpers/stubs.py b/tests/helpers/stubs.py index cabd606b6..8e8851dba 100644 --- a/tests/helpers/stubs.py +++ b/tests/helpers/stubs.py @@ -35,6 +35,7 @@ from PyQt5.QtWidgets import QCommonStyle, QLineEdit, QWidget, QTabBar from qutebrowser.browser import browsertab, downloads from qutebrowser.utils import usertypes from qutebrowser.mainwindow import mainwindow +from qutebrowser.commands import runners class FakeNetworkCache(QAbstractNetworkCache): @@ -639,3 +640,13 @@ class FakeHistoryProgress: def finish(self): self._finished = True + + +class FakeCommandRunner(runners.AbstractCommandRunner): + + def __init__(self, parent=None): + super().__init__(parent) + self.commands = [] + + def run(self, text, count=None, *, safely=False): + self.commands.append((text, count)) diff --git a/tests/unit/keyinput/test_modeparsers.py b/tests/unit/keyinput/test_modeparsers.py index bdda244e4..513b5c67c 100644 --- a/tests/unit/keyinput/test_modeparsers.py +++ b/tests/unit/keyinput/test_modeparsers.py @@ -19,8 +19,6 @@ """Tests for mode parsers.""" -from unittest import mock - from PyQt5.QtCore import Qt from PyQt5.QtGui import QKeySequence @@ -29,6 +27,11 @@ import pytest from qutebrowser.keyinput import modeparsers, keyutils +@pytest.fixture +def commandrunner(stubs): + return stubs.FakeCommandRunner() + + class TestsNormalKeyParser: @pytest.fixture(autouse=True) @@ -39,23 +42,22 @@ class TestsNormalKeyParser: stubs.FakeTimer) @pytest.fixture - def keyparser(self): - kp = modeparsers.NormalKeyParser(0) - kp.execute = mock.Mock() + def keyparser(self, commandrunner): + kp = modeparsers.NormalKeyParser(win_id=0, commandrunner=commandrunner) return kp - def test_keychain(self, keyparser, fake_keyevent): + def test_keychain(self, keyparser, fake_keyevent, commandrunner): """Test valid keychain.""" # Press 'x' which is ignored because of no match keyparser.handle(fake_keyevent(Qt.Key_X)) # Then start the real chain keyparser.handle(fake_keyevent(Qt.Key_B)) keyparser.handle(fake_keyevent(Qt.Key_A)) - keyparser.execute.assert_called_with('message-info ba', None) + assert commandrunner.commands == [('message-info ba', None)] assert not keyparser._sequence def test_partial_keychain_timeout(self, keyparser, config_stub, - fake_keyevent, qtbot): + fake_keyevent, qtbot, commandrunner): """Test partial keychain timeout.""" config_stub.val.input.partial_timeout = 100 timer = keyparser._partial_timer @@ -68,14 +70,14 @@ class TestsNormalKeyParser: assert timer.interval() == 100 assert timer.isActive() - assert not keyparser.execute.called + assert not commandrunner.commands assert keyparser._sequence == keyutils.KeySequence.parse('b') # Now simulate a timeout and check the keystring has been cleared. with qtbot.wait_signal(keyparser.keystring_updated) as blocker: timer.timeout.emit() - assert not keyparser.execute.called + assert not commandrunner.commands assert not keyparser._sequence assert blocker.args == [''] @@ -83,13 +85,12 @@ class TestsNormalKeyParser: class TestHintKeyParser: @pytest.fixture - def keyparser(self, config_stub, key_config_stub): - kp = modeparsers.HintKeyParser(0) - kp.execute = mock.Mock() + def keyparser(self, config_stub, key_config_stub, commandrunner): + kp = modeparsers.HintKeyParser(win_id=0, commandrunner=commandrunner) kp.keystring_updated.disconnect() # Don't try to update HintManager return kp - def test_simple_hint_match(self, keyparser, fake_keyevent): + def test_simple_hint_match(self, keyparser, fake_keyevent, commandrunner): keyparser.update_bindings(['aa', 'as']) match = keyparser.handle(fake_keyevent(Qt.Key_A)) @@ -97,7 +98,7 @@ class TestHintKeyParser: match = keyparser.handle(fake_keyevent(Qt.Key_S)) assert match == QKeySequence.ExactMatch - keyparser.execute.assert_called_with('follow-hint -s as', None) + assert commandrunner.commands == [('follow-hint -s as', None)] def test_numberkey_hint_match(self, keyparser, fake_keyevent): keyparser.update_bindings(['21', '22']) |