Remove :<count>:cmd syntax support.
CommandRunner.parse had some logic for handling commands of form :<count>:cmd. However, this complicated the parsing logic for something that appears to only be used in tests. One could use it in a userscript, but this is unlikely as it is undocumented. Removing support for this simplifies the logic of parse. The commnd `run-with-count` is added to provide this functionality. It works like `repeat` but passes the count along to the command instead of running the command multiple times. This resolves #1997: Qutebrowser crashes when pasting commands. This bug was caused by excess stripping of ':' from the command string by _parse_count.
This commit is contained in:
parent
aba67d0822
commit
fbc084e416
@ -31,8 +31,7 @@ from qutebrowser.utils import message, objreg, qtutils, utils
|
||||
from qutebrowser.misc import split
|
||||
|
||||
|
||||
ParseResult = collections.namedtuple('ParseResult', ['cmd', 'args', 'cmdline',
|
||||
'count'])
|
||||
ParseResult = collections.namedtuple('ParseResult', ['cmd', 'args', 'cmdline'])
|
||||
last_command = {}
|
||||
|
||||
|
||||
@ -154,26 +153,6 @@ class CommandRunner(QObject):
|
||||
for sub in sub_texts:
|
||||
yield self.parse(sub, *args, **kwargs)
|
||||
|
||||
def _parse_count(self, cmdstr):
|
||||
"""Split a count prefix off from a command for parse().
|
||||
|
||||
Args:
|
||||
cmdstr: The command/args including the count.
|
||||
|
||||
Return:
|
||||
A (count, cmdstr) tuple, with count being None or int.
|
||||
"""
|
||||
if ':' not in cmdstr:
|
||||
return (None, cmdstr)
|
||||
|
||||
count, cmdstr = cmdstr.split(':', maxsplit=1)
|
||||
try:
|
||||
count = int(count)
|
||||
except ValueError:
|
||||
# We just ignore invalid prefixes
|
||||
count = None
|
||||
return (count, cmdstr)
|
||||
|
||||
def parse(self, text, *, fallback=False, keep=False):
|
||||
"""Split the commandline text into command and arguments.
|
||||
|
||||
@ -187,7 +166,6 @@ class CommandRunner(QObject):
|
||||
A ParseResult tuple.
|
||||
"""
|
||||
cmdstr, sep, argstr = text.partition(' ')
|
||||
count, cmdstr = self._parse_count(cmdstr)
|
||||
|
||||
if not cmdstr and not fallback:
|
||||
raise cmdexc.NoSuchCommandError("No command given")
|
||||
@ -202,8 +180,7 @@ class CommandRunner(QObject):
|
||||
raise cmdexc.NoSuchCommandError(
|
||||
'{}: no such command'.format(cmdstr))
|
||||
cmdline = split.split(text, keep=keep)
|
||||
return ParseResult(cmd=None, args=None, cmdline=cmdline,
|
||||
count=count)
|
||||
return ParseResult(cmd=None, args=None, cmdline=cmdline)
|
||||
|
||||
args = self._split_args(cmd, argstr, keep)
|
||||
if keep and args:
|
||||
@ -213,7 +190,7 @@ class CommandRunner(QObject):
|
||||
else:
|
||||
cmdline = [cmdstr] + args[:]
|
||||
|
||||
return ParseResult(cmd=cmd, args=args, cmdline=cmdline, count=count)
|
||||
return ParseResult(cmd=cmd, args=args, cmdline=cmdline)
|
||||
|
||||
def _completion_match(self, cmdstr):
|
||||
"""Replace cmdstr with a matching completion if there's only one match.
|
||||
@ -291,20 +268,10 @@ class CommandRunner(QObject):
|
||||
args = result.args
|
||||
else:
|
||||
args = replace_variables(self._win_id, result.args)
|
||||
if count is not None:
|
||||
if result.count is not None:
|
||||
raise cmdexc.CommandMetaError("Got count via command and "
|
||||
"prefix!")
|
||||
result.cmd.run(self._win_id, args, count=count)
|
||||
elif result.count is not None:
|
||||
result.cmd.run(self._win_id, args, count=result.count)
|
||||
else:
|
||||
result.cmd.run(self._win_id, args)
|
||||
result.cmd.run(self._win_id, args, count=count)
|
||||
|
||||
if result.cmdline[0] != 'repeat-command':
|
||||
last_command[cur_mode] = (
|
||||
self._parse_count(text)[1],
|
||||
count if count is not None else result.count)
|
||||
last_command[cur_mode] = (text, count)
|
||||
|
||||
@pyqtSlot(str, int)
|
||||
@pyqtSlot(str)
|
||||
|
@ -86,6 +86,23 @@ def repeat(times: int, command, win_id):
|
||||
commandrunner.run_safely(command)
|
||||
|
||||
|
||||
@cmdutils.register(maxsplit=1, hide=True, no_cmd_split=True,
|
||||
no_replace_variables=True)
|
||||
@cmdutils.argument('win_id', win_id=True)
|
||||
@cmdutils.argument('count', count=True)
|
||||
def run_with_count(count_arg: int, command, win_id, count=1):
|
||||
"""Run a command with the given count.
|
||||
|
||||
If run_with_count itself is run with a count, it multiplies count_arg.
|
||||
|
||||
Args:
|
||||
count_arg: The count to pass to the command.
|
||||
command: The command to run, with optional args.
|
||||
count: The count that run_with_count itself received.
|
||||
"""
|
||||
runners.CommandRunner(win_id).run(command, count_arg * count)
|
||||
|
||||
|
||||
@cmdutils.register(hide=True)
|
||||
def message_error(text):
|
||||
"""Show an error message in the statusbar.
|
||||
|
@ -3,7 +3,7 @@ Feature: Caret mode
|
||||
|
||||
Background:
|
||||
Given I open data/caret.html
|
||||
And I run :tab-only ;; :enter-mode caret
|
||||
And I run :tab-only ;; enter-mode caret
|
||||
|
||||
# document
|
||||
|
||||
|
@ -499,7 +499,7 @@ Feature: Various utility commands.
|
||||
Scenario: Using :debug-log-capacity
|
||||
When I run :debug-log-capacity 100
|
||||
And I run :message-info oldstuff
|
||||
And I run :repeat 20 :message-info otherstuff
|
||||
And I run :repeat 20 message-info otherstuff
|
||||
And I run :message-info newstuff
|
||||
And I open qute:log
|
||||
Then the page should contain the plaintext "newstuff"
|
||||
@ -723,3 +723,13 @@ Feature: Various utility commands.
|
||||
And I run :command-accept
|
||||
And I set general -> private-browsing to false
|
||||
Then the message "blah" should be shown
|
||||
|
||||
## :run-with-count
|
||||
|
||||
Scenario: :run-with-count
|
||||
When I run :run-with-count 2 scroll down
|
||||
Then "command called: scroll ['down'] (count=2)" should be logged
|
||||
|
||||
Scenario: :run-with-count with count
|
||||
When I run :run-with-count 2 scroll down with count 3
|
||||
Then "command called: scroll ['down'] (count=6)" should be logged
|
||||
|
@ -987,13 +987,13 @@ Feature: Tab management
|
||||
Scenario: Using :tab-next after closing last tab (#1448)
|
||||
When I set tabs -> last-close to close
|
||||
And I run :tab-only
|
||||
And I run :tab-close ;; :tab-next
|
||||
And I run :tab-close ;; tab-next
|
||||
Then qutebrowser should quit
|
||||
And no crash should happen
|
||||
|
||||
Scenario: Using :tab-prev after closing last tab (#1448)
|
||||
When I set tabs -> last-close to close
|
||||
And I run :tab-only
|
||||
And I run :tab-close ;; :tab-prev
|
||||
And I run :tab-close ;; tab-prev
|
||||
Then qutebrowser should quit
|
||||
And no crash should happen
|
||||
|
@ -437,7 +437,8 @@ class QuteProc(testprocess.Process):
|
||||
command = command.replace('\\', r'\\')
|
||||
|
||||
if count is not None:
|
||||
command = ':{}:{}'.format(count, command.lstrip(':'))
|
||||
command = ':run-with-count {} {}'.format(count,
|
||||
command.lstrip(':'))
|
||||
|
||||
self.send_ipc([command])
|
||||
if not invalid:
|
||||
|
@ -64,15 +64,6 @@ class TestCommandRunner:
|
||||
with pytest.raises(cmdexc.NoSuchCommandError):
|
||||
list(cr.parse_all(command))
|
||||
|
||||
def test_parse_with_count(self):
|
||||
"""Test parsing of commands with a count."""
|
||||
cr = runners.CommandRunner(0)
|
||||
result = cr.parse('20:scroll down')
|
||||
assert result.cmd.name == 'scroll'
|
||||
assert result.count == 20
|
||||
assert result.args == ['down']
|
||||
assert result.cmdline == ['scroll', 'down']
|
||||
|
||||
def test_partial_parsing(self):
|
||||
"""Test partial parsing with a runner where it's enabled.
|
||||
|
||||
|
@ -181,6 +181,7 @@ def _set_cmd_prompt(cmd, txt):
|
||||
(':open -- |', None, ''),
|
||||
(':gibberish nonesense |', None, ''),
|
||||
('/:help|', None, ''),
|
||||
('::bind|', usertypes.Completion.command, ':bind'),
|
||||
])
|
||||
def test_update_completion(txt, kind, pattern, status_command_stub,
|
||||
completer_obj, completion_widget_stub):
|
||||
|
Loading…
Reference in New Issue
Block a user