diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index 4151eb892..5d9207dff 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -837,8 +837,7 @@ If neither count nor index are given, it behaves like tab-next. If both are give ==== count -The tab index to focus, starting with 1. The special value 0 focuses the rightmost tab. - +The tab index to focus, starting with 1. [[tab-move]] === tab-move diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 0458e18ed..c13cde990 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -959,7 +959,7 @@ class CommandDispatcher: @cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.argument('index', choices=['last']) - @cmdutils.argument('count', count=True, zero_count=True) + @cmdutils.argument('count', count=True) def tab_focus(self, index: typing.Union[str, int]=None, count=None): """Select the tab given as argument/[count]. @@ -972,7 +972,6 @@ class CommandDispatcher: Negative indices count from the end, such that -1 is the last tab. count: The tab index to focus, starting with 1. - The special value 0 focuses the rightmost tab. """ if index == 'last': self._tab_focus_last() @@ -982,9 +981,8 @@ class CommandDispatcher: if index is None: self.tab_next() return - elif index == 0: - index = self._count() - elif index < 0: + + if index < 0: index = self._count() + index + 1 if 1 <= index <= self._count(): diff --git a/qutebrowser/commands/command.py b/qutebrowser/commands/command.py index a2bc3b50d..a7404fb3a 100644 --- a/qutebrowser/commands/command.py +++ b/qutebrowser/commands/command.py @@ -34,14 +34,11 @@ class ArgInfo: """Information about an argument.""" def __init__(self, win_id=False, count=False, hide=False, metavar=None, - zero_count=False, flag=None, completion=None, choices=None): + flag=None, completion=None, choices=None): if win_id and count: raise TypeError("Argument marked as both count/win_id!") - if zero_count and not count: - raise TypeError("zero_count argument cannot exist without count!") self.win_id = win_id self.count = count - self.zero_count = zero_count self.flag = flag self.hide = hide self.metavar = metavar @@ -51,7 +48,6 @@ class ArgInfo: def __eq__(self, other): return (self.win_id == other.win_id and self.count == other.count and - self.zero_count == other.zero_count and self.flag == other.flag and self.hide == other.hide and self.metavar == other.metavar and @@ -61,7 +57,6 @@ class ArgInfo: def __repr__(self): return utils.get_repr(self, win_id=self.win_id, count=self.count, flag=self.flag, hide=self.hide, - zero_count=self.zero_count, metavar=self.metavar, completion=self.completion, choices=self.choices, constructor=True) @@ -142,7 +137,6 @@ class Command: self.opt_args = collections.OrderedDict() self.namespace = None self._count = None - self._zero_count = None self.pos_args = [] self.desc = None self.flags_with_args = [] @@ -154,7 +148,7 @@ class Command: self._inspect_func() - def _check_prerequisites(self, win_id, count): + def _check_prerequisites(self, win_id): """Check if the command is permitted to run currently. Args: @@ -170,11 +164,6 @@ class Command: "{}: Only available with {} " "backend.".format(self.name, self.backend.name)) - if count == 0 and not self._zero_count: - raise cmdexc.PrerequisitesError( - "{}: A zero count is not allowed for this command!" - .format(self.name)) - if self.deprecated: message.warning('{} is deprecated - {}'.format(self.name, self.deprecated)) @@ -246,9 +235,6 @@ class Command: assert param.kind != inspect.Parameter.POSITIONAL_ONLY if param.name == 'self': continue - arg_info = self.get_arg_info(param) - if arg_info.count: - self._zero_count = arg_info.zero_count if self._inspect_special_param(param): continue if (param.kind == inspect.Parameter.KEYWORD_ONLY and @@ -532,7 +518,7 @@ class Command: e.status, e)) return self._count = count - self._check_prerequisites(win_id, count) + self._check_prerequisites(win_id) posargs, kwargs = self._get_call_args(win_id) log.commands.debug('Calling {}'.format( debug_utils.format_call(self.handler, posargs, kwargs))) diff --git a/qutebrowser/keyinput/basekeyparser.py b/qutebrowser/keyinput/basekeyparser.py index 3114f4663..7325223a3 100644 --- a/qutebrowser/keyinput/basekeyparser.py +++ b/qutebrowser/keyinput/basekeyparser.py @@ -147,6 +147,9 @@ class BaseKeyParser(QObject): (countstr, cmd_input) = re.match(r'^(\d*)(.*)', self._keystring).groups() count = int(countstr) if countstr else None + if count == 0 and not cmd_input: + cmd_input = self._keystring + count = None else: cmd_input = self._keystring count = None diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index 20ace1959..143988c64 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -529,10 +529,6 @@ Feature: Various utility commands. And I set general -> private-browsing to false Then the page should contain the plaintext "Local storage status: not working" - Scenario: Using 0 as count - When I run :scroll down with count 0 - Then the error "scroll: A zero count is not allowed for this command!" should be shown - @no_xvfb Scenario: :window-only Given I run :tab-only diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index 1663dd8d7..143e9f2d4 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -255,6 +255,7 @@ Feature: Tab management - data/numbers/2.txt (active) - data/numbers/3.txt + Scenario: :tab-focus with invalid negative index Scenario: :tab-focus with count 0 When I open data/numbers/1.txt And I open data/numbers/2.txt in a new tab @@ -266,7 +267,6 @@ Feature: Tab management - data/numbers/2.txt - data/numbers/3.txt (active) - Scenario: :tab-focus with invalid negative index When I open data/numbers/1.txt And I open data/numbers/2.txt in a new tab And I open data/numbers/3.txt in a new tab diff --git a/tests/unit/commands/test_cmdutils.py b/tests/unit/commands/test_cmdutils.py index aaf63014e..28e7d5fed 100644 --- a/tests/unit/commands/test_cmdutils.py +++ b/tests/unit/commands/test_cmdutils.py @@ -423,16 +423,6 @@ class TestArgument: assert str(excinfo.value) == "Argument marked as both count/win_id!" - def test_count_and_zero_count_arg(self): - with pytest.raises(TypeError) as excinfo: - @cmdutils.argument('arg', count=False, zero_count=True) - def fun(arg=0): - """Blah.""" - pass - - expected = "zero_count argument cannot exist without count!" - assert str(excinfo.value) == expected - def test_no_docstring(self, caplog): with caplog.at_level(logging.WARNING): @cmdutils.register() diff --git a/tests/unit/keyinput/test_basekeyparser.py b/tests/unit/keyinput/test_basekeyparser.py index 3e55d0366..da1ecfbdf 100644 --- a/tests/unit/keyinput/test_basekeyparser.py +++ b/tests/unit/keyinput/test_basekeyparser.py @@ -245,6 +245,12 @@ class TestKeyChain: 'ba', keyparser.Type.chain, None) assert keyparser._keystring == '' + def test_0_press(self, handle_text, keyparser): + handle_text((Qt.Key_0, '0')) + keyparser.execute.assert_called_once_with( + '0', keyparser.Type.chain, None) + assert keyparser._keystring == '' + def test_ambiguous_keychain(self, qapp, handle_text, config_stub, keyparser): config_stub.data = CONFIG @@ -308,8 +314,9 @@ class TestCount: def test_count_0(self, handle_text, keyparser): handle_text((Qt.Key_0, '0'), (Qt.Key_B, 'b'), (Qt.Key_A, 'a')) - keyparser.execute.assert_called_once_with( - 'ba', keyparser.Type.chain, 0) + calls = [mock.call('0', keyparser.Type.chain, None), + mock.call('ba', keyparser.Type.chain, None)] + keyparser.execute.assert_has_calls(calls) assert keyparser._keystring == '' def test_count_42(self, handle_text, keyparser):