summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--qutebrowser/commands/runners.py22
-rw-r--r--qutebrowser/keyinput/modeman.py85
-rw-r--r--qutebrowser/keyinput/modeparsers.py30
-rw-r--r--tests/helpers/stubs.py11
-rw-r--r--tests/unit/keyinput/test_modeparsers.py31
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'])