diff --git a/qutebrowser/commands/command.py b/qutebrowser/commands/command.py index fd9b2382f..9baa4efe7 100644 --- a/qutebrowser/commands/command.py +++ b/qutebrowser/commands/command.py @@ -515,3 +515,7 @@ class Command: raise cmdexc.PrerequisitesError( "{}: This command is only allowed in {} mode, not {}.".format( self.name, mode_names, mode.name)) + + def takes_count(self): + """Return true iff this command can take a count argument.""" + return any(arg.count for arg in self._qute_args) diff --git a/qutebrowser/misc/keyhintwidget.py b/qutebrowser/misc/keyhintwidget.py index 63b8f017c..5626ce4ae 100644 --- a/qutebrowser/misc/keyhintwidget.py +++ b/qutebrowser/misc/keyhintwidget.py @@ -26,12 +26,14 @@ It is intended to help discoverability of keybindings. import html import fnmatch +import re from PyQt5.QtWidgets import QLabel, QSizePolicy from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt from qutebrowser.config import config from qutebrowser.utils import utils, usertypes +from qutebrowser.commands import cmdutils class KeyHintView(QLabel): @@ -85,6 +87,7 @@ class KeyHintView(QLabel): Args: prefix: The current partial keystring. """ + countstr, prefix = re.match(r'^(\d*)(.*)', prefix).groups() if not prefix: self._show_timer.stop() self.hide() @@ -94,11 +97,17 @@ class KeyHintView(QLabel): return any(fnmatch.fnmatchcase(keychain, glob) for glob in config.val.keyhint.blacklist) + def takes_count(cmdstr): + cmdname = cmdstr.split(' ')[0] + cmd = cmdutils.cmd_dict.get(cmdname) + return cmd and cmd.takes_count() + bindings_dict = config.key_instance.get_bindings_for(modename) bindings = [(k, v) for (k, v) in sorted(bindings_dict.items()) if k.startswith(prefix) and not utils.is_special_key(k) and - not blacklisted(k)] + not blacklisted(k) and + (takes_count(v) or not countstr)] if not bindings: self._show_timer.stop() diff --git a/tests/helpers/stubs.py b/tests/helpers/stubs.py index e68b7cc15..15d2ce8a0 100644 --- a/tests/helpers/stubs.py +++ b/tests/helpers/stubs.py @@ -336,6 +336,7 @@ class FakeCommand: deprecated = attr.ib(False) completion = attr.ib(None) maxsplit = attr.ib(None) + takes_count = attr.ib(lambda: False) class FakeTimer(QObject): diff --git a/tests/unit/misc/test_keyhints.py b/tests/unit/misc/test_keyhints.py index c40958c86..3e94f1271 100644 --- a/tests/unit/misc/test_keyhints.py +++ b/tests/unit/misc/test_keyhints.py @@ -92,6 +92,23 @@ def test_suggestions(keyhint, config_stub): ('a', 'yellow', 'c', 'message-info cmd-ac')) +def test_suggestions_with_count(keyhint, config_stub, monkeypatch, stubs): + """Test that a count prefix filters out commands that take no count.""" + monkeypatch.setattr('qutebrowser.commands.cmdutils.cmd_dict', { + 'foo': stubs.FakeCommand(name='foo', takes_count=lambda: False), + 'bar': stubs.FakeCommand(name='bar', takes_count=lambda: True), + }) + + bindings = {'normal': {'aa': 'foo', 'ab': 'bar'}} + config_stub.val.bindings.default = bindings + config_stub.val.bindings.commands = bindings + + keyhint.update_keyhint('normal', '2a') + assert keyhint.text() == expected_text( + ('a', 'yellow', 'b', 'bar'), + ) + + def test_special_bindings(keyhint, config_stub): """Ensure a prefix of '<' doesn't suggest special keys.""" bindings = {'normal': {