Refactor command argument splitting.
This uses split=True/False as cmdutils.register argument again (because we would never want to set maxsplit ourselves, only not splitting the *last* argument makes any sense. Also this uses shlex for splitting again, like earlier.
This commit is contained in:
parent
de5f85982f
commit
be467d5e50
1
TODO
1
TODO
@ -40,7 +40,6 @@ Ctrl+A/X to increase/decrease last number in URL
|
||||
command completion gets hidden when doing a new ValueList value
|
||||
logging contexts
|
||||
catch import errors for PyQt and QtWebKit
|
||||
How do we handle empty values in input bar?
|
||||
- Add more element-selection-detection code (with options?) based on:
|
||||
-> javascript: http://stackoverflow.com/a/2848120/2085149
|
||||
-> microFocusChanged and check active element via:
|
||||
|
@ -420,7 +420,7 @@ class QuteBrowser(QApplication):
|
||||
logging.debug("maybe_quit quitting.")
|
||||
self.quit()
|
||||
|
||||
@cmdutils.register(instance='', maxsplit=0)
|
||||
@cmdutils.register(instance='', split=False)
|
||||
def pyeval(self, s):
|
||||
"""Evaluate a python string and display the results as a webpage.
|
||||
|
||||
|
@ -86,7 +86,8 @@ class CurCommandDispatcher(QObject):
|
||||
return
|
||||
widget.hintmanager.follow_prevnext(frame, widget.url(), prev, newtab)
|
||||
|
||||
@cmdutils.register(instance='mainwindow.tabs.cur', name='open', maxsplit=0)
|
||||
@cmdutils.register(instance='mainwindow.tabs.cur', name='open',
|
||||
split=False)
|
||||
def openurl(self, url, count=None):
|
||||
"""Open an url in the current/[count]th tab.
|
||||
|
||||
|
@ -32,10 +32,7 @@ class Command(QObject):
|
||||
|
||||
Attributes:
|
||||
name: The main name of the command.
|
||||
maxsplit: Maximum count of splits to be made.
|
||||
-1: Split everything (default)
|
||||
0: Don't split.
|
||||
n: Split a maximum of n times.
|
||||
split: Whether to split the arguments.
|
||||
hide: Whether to hide the arguments or not.
|
||||
nargs: A (minargs, maxargs) tuple, maxargs = None if there's no limit.
|
||||
count: Whether the command supports a count, or not.
|
||||
@ -57,13 +54,13 @@ class Command(QObject):
|
||||
|
||||
signal = pyqtSignal(tuple)
|
||||
|
||||
def __init__(self, name, maxsplit, hide, nargs, count, desc, instance,
|
||||
def __init__(self, name, split, hide, nargs, count, desc, instance,
|
||||
handler, completion, modes, not_modes, needs_js):
|
||||
# I really don't know how to solve this in a better way, I tried.
|
||||
# pylint: disable=too-many-arguments
|
||||
super().__init__()
|
||||
self.name = name
|
||||
self.maxsplit = maxsplit
|
||||
self.split = split
|
||||
self.hide = hide
|
||||
self.nargs = nargs
|
||||
self.count = count
|
||||
|
@ -26,6 +26,7 @@ import qutebrowser.config.config as config
|
||||
import qutebrowser.commands.utils as cmdutils
|
||||
import qutebrowser.utils.message as message
|
||||
from qutebrowser.commands._exceptions import NoSuchCommandError, CommandError
|
||||
from qutebrowser.utils.misc import safe_shlex_split
|
||||
|
||||
|
||||
def split_cmdline(text):
|
||||
@ -168,15 +169,15 @@ class CommandManager:
|
||||
|
||||
if len(parts) == 1:
|
||||
args = []
|
||||
elif cmd.split:
|
||||
args = safe_shlex_split(parts[1])
|
||||
else:
|
||||
logging.debug("Splitting '{}' with max {} splits".format(
|
||||
parts[1], cmd.maxsplit))
|
||||
args = parts[1].split(maxsplit=cmd.maxsplit)
|
||||
args = parts[1].split(maxsplit=cmd.nargs[0] - 1)
|
||||
self._cmd = cmd
|
||||
self._args = args
|
||||
retargs = args[:]
|
||||
if text.endswith(' ') and (cmd.maxsplit == -1 or
|
||||
len(args) <= cmd.maxsplit):
|
||||
if text.endswith(' ') and (cmd.split is True or
|
||||
len(args) < cmd.args[0]):
|
||||
retargs.append('')
|
||||
return [cmdstr] + retargs
|
||||
|
||||
|
@ -40,10 +40,7 @@ class register: # pylint: disable=invalid-name
|
||||
instance: The instance to be used as "self", as a dotted string.
|
||||
name: The name (as string) or names (as list) of the command.
|
||||
nargs: A (minargs, maxargs) tuple of valid argument counts, or an int.
|
||||
maxsplit: Maximum count of splits to be made.
|
||||
-1: Split everything (default)
|
||||
0: Don't split.
|
||||
n: Split a maximum of n times.
|
||||
split: Whether to split the arguments.
|
||||
hide: Whether to hide the command or not.
|
||||
completion: Which completion to use for arguments, as a list of
|
||||
strings.
|
||||
@ -51,7 +48,7 @@ class register: # pylint: disable=invalid-name
|
||||
needs_js: If javascript is needed for this command.
|
||||
"""
|
||||
|
||||
def __init__(self, instance=None, name=None, nargs=None, maxsplit=-1,
|
||||
def __init__(self, instance=None, name=None, nargs=None, split=True,
|
||||
hide=False, completion=None, modes=None, not_modes=None,
|
||||
needs_js=False):
|
||||
"""Save decorator arguments.
|
||||
@ -64,7 +61,7 @@ class register: # pylint: disable=invalid-name
|
||||
if modes is not None and not_modes is not None:
|
||||
raise ValueError("Only modes or not_modes can be given!")
|
||||
self.name = name
|
||||
self.maxsplit = maxsplit
|
||||
self.split = split
|
||||
self.hide = hide
|
||||
self.nargs = nargs
|
||||
self.instance = instance
|
||||
@ -105,7 +102,7 @@ class register: # pylint: disable=invalid-name
|
||||
desc = func.__doc__.splitlines()[0].strip().rstrip('.')
|
||||
else:
|
||||
desc = ""
|
||||
cmd = Command(name=mainname, maxsplit=self.maxsplit,
|
||||
cmd = Command(name=mainname, split=self.split,
|
||||
hide=self.hide, nargs=nargs, count=count, desc=desc,
|
||||
instance=self.instance, handler=func,
|
||||
completion=self.completion, modes=self.modes,
|
||||
|
@ -310,7 +310,7 @@ class ConfigManager(QObject):
|
||||
newval = val.typ.transform(newval)
|
||||
return newval
|
||||
|
||||
@cmdutils.register(name='set', instance='config', maxsplit=2,
|
||||
@cmdutils.register(name='set', instance='config',
|
||||
completion=['section', 'option', 'value'])
|
||||
def set_wrapper(self, section, option, value):
|
||||
"""Set an option.
|
||||
@ -322,7 +322,7 @@ class ConfigManager(QObject):
|
||||
except (NoOptionError, NoSectionError, ValidationError) as e:
|
||||
message.error("set: {} - {}".format(e.__class__.__name__, e))
|
||||
|
||||
@cmdutils.register(name='set_temp', instance='config', maxsplit=2,
|
||||
@cmdutils.register(name='set_temp', instance='config',
|
||||
completion=['section', 'option', 'value'])
|
||||
def set_temp_wrapper(self, section, option, value):
|
||||
"""Set a temporary option.
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
"""Other utilities which don't fit anywhere else."""
|
||||
|
||||
import shlex
|
||||
from functools import reduce
|
||||
from pkg_resources import resource_string
|
||||
|
||||
@ -46,3 +47,32 @@ def dotted_getattr(obj, path):
|
||||
The object at path.
|
||||
"""
|
||||
return reduce(getattr, path.split('.'), obj)
|
||||
|
||||
|
||||
def safe_shlex_split(s):
|
||||
r"""Split a string via shlex safely (don't bail out on unbalanced quotes).
|
||||
|
||||
We split while the user is typing (for completion), and as
|
||||
soon as " or \ is typed, the string is invalid for shlex,
|
||||
because it encounters EOF while in quote/escape state.
|
||||
|
||||
Here we fix this error temporarely so shlex doesn't blow up,
|
||||
and then retry splitting again.
|
||||
|
||||
Since shlex raises ValueError in both cases we unfortunately
|
||||
have to parse the exception string...
|
||||
"""
|
||||
try:
|
||||
return shlex.split(s)
|
||||
except ValueError as e:
|
||||
if str(e) == "No closing quotation":
|
||||
# e.g. eggs "bacon ham
|
||||
# -> we fix this as eggs "bacon ham"
|
||||
s += '"'
|
||||
elif str(e) == "No escaped character":
|
||||
# e.g. eggs\
|
||||
# -> we fix this as eggs\\
|
||||
s += '\\'
|
||||
else:
|
||||
raise
|
||||
return shlex.split(s)
|
||||
|
@ -222,12 +222,12 @@ class TabbedBrowser(TabWidget):
|
||||
elif last_close == 'blank':
|
||||
tab.openurl('about:blank')
|
||||
|
||||
@cmdutils.register(instance='mainwindow.tabs', maxsplit=0)
|
||||
@cmdutils.register(instance='mainwindow.tabs', split=False)
|
||||
def tabopen(self, url):
|
||||
"""Open a new tab with a given url."""
|
||||
self._tabopen(url, background=False)
|
||||
|
||||
@cmdutils.register(instance='mainwindow.tabs', maxsplit=0)
|
||||
@cmdutils.register(instance='mainwindow.tabs', split=False)
|
||||
def backtabopen(self, url):
|
||||
"""Open a new tab in background."""
|
||||
self._tabopen(url, background=True)
|
||||
|
Loading…
Reference in New Issue
Block a user