diff --git a/CONTRIBUTING.asciidoc b/CONTRIBUTING.asciidoc index 9f65c5d7f..d9f6a7015 100644 --- a/CONTRIBUTING.asciidoc +++ b/CONTRIBUTING.asciidoc @@ -453,6 +453,7 @@ The following arguments are supported for `@cmdutils.argument`: - `win_id=True`: Mark the argument as special window ID argument - `count=True`: Mark the argument as special count argument - `hide=True`: Hide the argument from the documentation +- `completion`: A `usertypes.Completion` member to use as completion. The name of an argument will always be the parameter name, with any trailing underscores stripped. diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 459c2f76b..7afc114ff 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -722,8 +722,8 @@ class Quitter: # segfaults. QTimer.singleShot(0, functools.partial(qApp.exit, status)) - @cmdutils.register(instance='quitter', name='wq', - completion=[usertypes.Completion.sessions]) + @cmdutils.register(instance='quitter', name='wq') + @cmdutils.argument('name', completion=usertypes.Completion.sessions) def save_and_quit(self, name=sessions.default): """Save open pages and quit. diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 8ab4614ee..7b2f302dd 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -226,8 +226,8 @@ class CommandDispatcher: tabbar.setSelectionBehaviorOnRemove(old_selection_behavior) @cmdutils.register(instance='command-dispatcher', name='open', - maxsplit=0, scope='window', - completion=[usertypes.Completion.url]) + maxsplit=0, scope='window') + @cmdutils.argument('url', completion=usertypes.Completion.url) @cmdutils.argument('count', count=True) def openurl(self, url=None, bg=False, tab=False, window=False, count=None): """Open a URL in the current/[count]th tab. @@ -863,8 +863,8 @@ class CommandDispatcher: raise cmdexc.CommandError(e) self._open(url, tab, bg, window) - @cmdutils.register(instance='command-dispatcher', scope='window', - completion=[usertypes.Completion.tab]) + @cmdutils.register(instance='command-dispatcher', scope='window') + @cmdutils.argument('index', completion=usertypes.Completion.tab) def buffer(self, index): """Select tab by index or url/title best match. @@ -1087,8 +1087,8 @@ class CommandDispatcher: quickmark_manager.prompt_save(self._win_id, self._current_url()) @cmdutils.register(instance='command-dispatcher', scope='window', - maxsplit=0, - completion=[usertypes.Completion.quickmark_by_name]) + maxsplit=0) + @cmdutils.argument('name', completion=usertypes.Completion.quickmark_by_name) def quickmark_load(self, name, tab=False, bg=False, window=False): """Load a quickmark. @@ -1118,8 +1118,8 @@ class CommandDispatcher: "Bookmarked {}!".format(url.toDisplayString())) @cmdutils.register(instance='command-dispatcher', scope='window', - maxsplit=0, - completion=[usertypes.Completion.bookmark_by_url]) + maxsplit=0) + @cmdutils.argument('url', completion=usertypes.Completion.bookmark_by_url) def bookmark_load(self, url, tab=False, bg=False, window=False): """Load a bookmark. @@ -1303,8 +1303,8 @@ class CommandDispatcher: message.info(self._win_id, "Dumped page to {}.".format(dest)) @cmdutils.register(instance='command-dispatcher', name='help', - completion=[usertypes.Completion.helptopic], scope='window') + @cmdutils.argument('topic', completion=usertypes.Completion.helptopic) def show_help(self, tab=False, bg=False, window=False, topic=None): r"""Show help about a command or setting. diff --git a/qutebrowser/browser/urlmarks.py b/qutebrowser/browser/urlmarks.py index 7ba1cfb2f..5d6dc5cd6 100644 --- a/qutebrowser/browser/urlmarks.py +++ b/qutebrowser/browser/urlmarks.py @@ -204,8 +204,8 @@ class QuickmarkManager(UrlMarkManager): else: set_mark() - @cmdutils.register(instance='quickmark-manager', maxsplit=0, - completion=[usertypes.Completion.quickmark_by_name]) + @cmdutils.register(instance='quickmark-manager', maxsplit=0) + @cmdutils.argument('name', completion=usertypes.Completion.quickmark_by_name) def quickmark_del(self, name): """Delete a quickmark. @@ -284,8 +284,8 @@ class BookmarkManager(UrlMarkManager): self.changed.emit() self.added.emit(title, urlstr) - @cmdutils.register(instance='bookmark-manager', maxsplit=0, - completion=[usertypes.Completion.bookmark_by_url]) + @cmdutils.register(instance='bookmark-manager', maxsplit=0) + @cmdutils.argument('url', completion=usertypes.Completion.bookmark_by_url) def bookmark_del(self, url): """Delete a bookmark. diff --git a/qutebrowser/commands/command.py b/qutebrowser/commands/command.py index 87566ad38..621ef060b 100644 --- a/qutebrowser/commands/command.py +++ b/qutebrowser/commands/command.py @@ -40,7 +40,7 @@ class ArgInfo: """Information about an argument.""" def __init__(self, win_id=False, count=False, flag=None, hide=False, - metavar=None): + metavar=None, completion=None): if win_id and count: raise TypeError("Argument marked as both count/win_id!") self.win_id = win_id @@ -48,18 +48,21 @@ class ArgInfo: self.flag = flag self.hide = hide self.metavar = metavar + self.completion = completion def __eq__(self, other): return (self.win_id == other.win_id and self.count == other.count and self.flag == other.flag and self.hide == other.hide and - self.metavar == other.metavar) + self.metavar == other.metavar and + self.completion == other.completion) def __repr__(self): return utils.get_repr(self, win_id=self.win_id, count=self.count, flag=self.flag, hide=self.hide, - metavar=self.metavar, constructor=True) + metavar=self.metavar, completion=self.completion, + constructor=True) class Command: @@ -90,10 +93,9 @@ class Command: """ def __init__(self, *, handler, name, instance=None, maxsplit=None, - hide=False, completion=None, modes=None, not_modes=None, - needs_js=False, debug=False, ignore_args=False, - deprecated=False, no_cmd_split=False, - star_args_optional=False, scope='global'): + hide=False, modes=None, not_modes=None, needs_js=False, + debug=False, ignore_args=False, deprecated=False, + no_cmd_split=False, star_args_optional=False, scope='global'): # I really don't know how to solve this in a better way, I tried. # pylint: disable=too-many-locals if modes is not None and not_modes is not None: @@ -115,7 +117,6 @@ class Command: self.hide = hide self.deprecated = deprecated self._instance = instance - self.completion = completion self._modes = modes self._not_modes = not_modes self._scope = scope @@ -148,10 +149,11 @@ class Command: args = self._inspect_func() - if self.completion is not None and len(self.completion) > len(args): - raise ValueError("Got {} completions, but only {} " - "arguments!".format(len(self.completion), - len(args))) + self.completion = [] + for arg in args: + arg_completion = self.get_arg_info(arg).completion + if arg_completion is not None: + self.completion.append(arg_completion) def _check_prerequisites(self, win_id): """Check if the command is permitted to run currently. @@ -264,7 +266,7 @@ class Command: log.commands.vdebug('Adding arg {} of type {} -> {}'.format( param.name, typ, callsig)) self.parser.add_argument(*args, **kwargs) - return signature.parameters.keys() + return signature.parameters.values() def _param_to_argparse_kwargs(self, param, typ): """Get argparse keyword arguments for a parameter. diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index 19440c275..5068283d4 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -686,9 +686,10 @@ class ConfigManager(QObject): raise cmdexc.CommandError("set: {} - {}".format( e.__class__.__name__, e)) - @cmdutils.register(name='set', instance='config', - completion=[Completion.section, Completion.option, - Completion.value]) + @cmdutils.register(name='set', instance='config') + @cmdutils.argument('section_', completion=Completion.section) + @cmdutils.argument('option', completion=Completion.option) + @cmdutils.argument('value', completion=Completion.value) @cmdutils.argument('win_id', win_id=True) def set_command(self, win_id, section_=None, option=None, value=None, temp=False, print_=False): diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index 4ebee8c44..420c808af 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -356,8 +356,8 @@ class SessionManager(QObject): sessions.append(base) return sessions - @cmdutils.register(completion=[usertypes.Completion.sessions], - instance='session-manager') + @cmdutils.register(instance='session-manager') + @cmdutils.argument('name', completion=usertypes.Completion.sessions) def session_load(self, name, clear=False, temp=False, force=False): """Load a session. @@ -384,10 +384,9 @@ class SessionManager(QObject): for win in old_windows: win.close() - @cmdutils.register(name=['session-save', 'w'], - completion=[usertypes.Completion.sessions], - instance='session-manager') + @cmdutils.register(name=['session-save', 'w'], instance='session-manager') @cmdutils.argument('win_id', win_id=True) + @cmdutils.argument('name', completion=usertypes.Completion.sessions) def session_save(self, win_id, name: str=default, current=False, quiet=False, force=False): """Save a session. @@ -420,8 +419,8 @@ class SessionManager(QObject): message.info(win_id, "Saved session {}.".format(name), immediately=True) - @cmdutils.register(completion=[usertypes.Completion.sessions], - instance='session-manager') + @cmdutils.register(instance='session-manager') + @cmdutils.argument('name', completion=usertypes.Completion.sessions) def session_delete(self, name, force=False): """Delete a session. diff --git a/tests/unit/commands/test_cmdutils.py b/tests/unit/commands/test_cmdutils.py index 04cabfe0a..711632b3e 100644 --- a/tests/unit/commands/test_cmdutils.py +++ b/tests/unit/commands/test_cmdutils.py @@ -174,13 +174,6 @@ class TestRegister: pass assert cmdutils.cmd_dict['fun'].hide - def test_wrong_completion_count(self): - with pytest.raises(ValueError): - @cmdutils.register(completion=['one', 'two']) - def fun(arg): - """Blah.""" - pass - def test_star_args(self): """Check handling of *args.""" @cmdutils.register()