Merge branch 'refactor_command_completion' of https://github.com/rcorre/qutebrowser into rcorre-refactor_command_completion

This commit is contained in:
Florian Bruhin 2016-08-07 11:29:50 +02:00
commit 26e0383d45
7 changed files with 70 additions and 54 deletions

View File

@ -75,7 +75,6 @@ class Command:
deprecated: False, or a string to describe why a command is deprecated. deprecated: False, or a string to describe why a command is deprecated.
desc: The description of the command. desc: The description of the command.
handler: The handler function to call. handler: The handler function to call.
completion: Completions to use for arguments, as a list of strings.
debug: Whether this is a debugging command (only shown with --debug). debug: Whether this is a debugging command (only shown with --debug).
parser: The ArgumentParser to use to parse this command. parser: The ArgumentParser to use to parse this command.
flags_with_args: A list of flags which take an argument. flags_with_args: A list of flags which take an argument.
@ -148,13 +147,7 @@ class Command:
self._qute_args = getattr(self.handler, 'qute_args', {}) self._qute_args = getattr(self.handler, 'qute_args', {})
self.handler.qute_args = None self.handler.qute_args = None
args = self._inspect_func() self._inspect_func()
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): def _check_prerequisites(self, win_id):
"""Check if the command is permitted to run currently. """Check if the command is permitted to run currently.
@ -208,6 +201,11 @@ class Command:
"""Get an ArgInfo tuple for the given inspect.Parameter.""" """Get an ArgInfo tuple for the given inspect.Parameter."""
return self._qute_args.get(param.name, ArgInfo()) return self._qute_args.get(param.name, ArgInfo())
def get_pos_arg_info(self, pos):
"""Get an ArgInfo tuple for the given positional parameter."""
name = self.pos_args[pos][0]
return self._qute_args.get(name, ArgInfo())
def _inspect_special_param(self, param): def _inspect_special_param(self, param):
"""Check if the given parameter is a special one. """Check if the given parameter is a special one.

View File

@ -204,25 +204,18 @@ class Completer(QObject):
return sortfilter.CompletionFilterModel(source=model, parent=self) return sortfilter.CompletionFilterModel(source=model, parent=self)
# delegate completion to command # delegate completion to command
try: try:
completions = cmdutils.cmd_dict[parts[0]].completion cmd = cmdutils.cmd_dict[parts[0]]
except KeyError: except KeyError:
# entering an unknown command # entering an unknown command
return None return None
if completions is None:
# command without any available completions
return None
dbg_completions = [c.name for c in completions]
try: try:
idx = cursor_part - 1 idx = cursor_part - 1
completion = completions[idx] completion = cmd.get_pos_arg_info(idx).completion
except IndexError: except IndexError:
# More arguments than completions # user provided more positional arguments than the command takes
log.completion.debug("completions: {}".format( return None
', '.join(dbg_completions))) if completion is None:
return None return None
dbg_completions[idx] = '*' + dbg_completions[idx] + '*'
log.completion.debug("completions: {}".format(
', '.join(dbg_completions)))
model = self._get_completion_model(completion, parts, cursor_part) model = self._get_completion_model(completion, parts, cursor_part)
return model return model

View File

@ -27,8 +27,7 @@ Module attributes:
import functools import functools
from qutebrowser.completion.models import (miscmodels, urlmodel, configmodel, from qutebrowser.completion.models import miscmodels, urlmodel, configmodel
base)
from qutebrowser.utils import objreg, usertypes, log, debug from qutebrowser.utils import objreg, usertypes, log, debug
from qutebrowser.config import configdata from qutebrowser.config import configdata
@ -115,13 +114,6 @@ def init_session_completion():
_instances[usertypes.Completion.sessions] = model _instances[usertypes.Completion.sessions] = model
def _init_empty_completion():
"""Initialize empty completion model."""
log.completion.debug("Initializing empty completion.")
if usertypes.Completion.empty not in _instances:
_instances[usertypes.Completion.empty] = base.BaseCompletionModel()
INITIALIZERS = { INITIALIZERS = {
usertypes.Completion.command: _init_command_completion, usertypes.Completion.command: _init_command_completion,
usertypes.Completion.helptopic: _init_helptopic_completion, usertypes.Completion.helptopic: _init_helptopic_completion,
@ -133,7 +125,6 @@ INITIALIZERS = {
usertypes.Completion.quickmark_by_name: init_quickmark_completions, usertypes.Completion.quickmark_by_name: init_quickmark_completions,
usertypes.Completion.bookmark_by_url: init_bookmark_completions, usertypes.Completion.bookmark_by_url: init_bookmark_completions,
usertypes.Completion.sessions: init_session_completion, usertypes.Completion.sessions: init_session_completion,
usertypes.Completion.empty: _init_empty_completion,
} }

View File

@ -152,7 +152,6 @@ class KeyConfigParser(QObject):
@cmdutils.register(instance='key-config', maxsplit=1, no_cmd_split=True) @cmdutils.register(instance='key-config', maxsplit=1, no_cmd_split=True)
@cmdutils.argument('win_id', win_id=True) @cmdutils.argument('win_id', win_id=True)
@cmdutils.argument('key', completion=usertypes.Completion.empty)
@cmdutils.argument('command', completion=usertypes.Completion.command) @cmdutils.argument('command', completion=usertypes.Completion.command)
def bind(self, key, win_id, command=None, *, mode='normal', force=False): def bind(self, key, win_id, command=None, *, mode='normal', force=False):
"""Bind a key to a command. """Bind a key to a command.

View File

@ -238,8 +238,7 @@ KeyMode = enum('KeyMode', ['normal', 'hint', 'command', 'yesno', 'prompt',
# Available command completions # Available command completions
Completion = enum('Completion', ['command', 'section', 'option', 'value', Completion = enum('Completion', ['command', 'section', 'option', 'value',
'helptopic', 'quickmark_by_name', 'helptopic', 'quickmark_by_name',
'bookmark_by_url', 'url', 'tab', 'sessions', 'bookmark_by_url', 'url', 'tab', 'sessions'])
'empty'])
# Exit statuses for errors. Needs to be an int for sys.exit. # Exit statuses for errors. Needs to be an int for sys.exit.

View File

@ -291,6 +291,21 @@ class TestRegister:
else: else:
assert cmd._get_call_args(win_id=0) == ([expected], {}) assert cmd._get_call_args(win_id=0) == ([expected], {})
def test_pos_arg_info(self):
@cmdutils.register()
@cmdutils.argument('foo', choices=('a', 'b'))
@cmdutils.argument('bar', choices=('x', 'y'))
@cmdutils.argument('opt')
def fun(foo, bar, opt=False):
"""Blah."""
pass
cmd = cmdutils.cmd_dict['fun']
assert cmd.get_pos_arg_info(0) == command.ArgInfo(choices=('a', 'b'))
assert cmd.get_pos_arg_info(1) == command.ArgInfo(choices=('x', 'y'))
with pytest.raises(IndexError):
cmd.get_pos_arg_info(2)
class TestArgument: class TestArgument:

View File

@ -27,6 +27,7 @@ from PyQt5.QtGui import QStandardItemModel
from qutebrowser.completion import completer from qutebrowser.completion import completer
from qutebrowser.utils import usertypes from qutebrowser.utils import usertypes
from qutebrowser.commands import command, cmdutils
class FakeCompletionModel(QStandardItemModel): class FakeCompletionModel(QStandardItemModel):
@ -91,24 +92,48 @@ def instances(monkeypatch):
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def cmdutils_patch(monkeypatch, stubs): def cmdutils_patch(monkeypatch, stubs):
"""Patch the cmdutils module to provide fake commands.""" """Patch the cmdutils module to provide fake commands."""
@cmdutils.argument('section_', completion=usertypes.Completion.section)
@cmdutils.argument('option', completion=usertypes.Completion.option)
@cmdutils.argument('value', completion=usertypes.Completion.value)
def set_command(section_=None, option=None, value=None):
"""docstring!"""
pass
@cmdutils.argument('topic', completion=usertypes.Completion.helptopic)
def show_help(tab=False, bg=False, window=False, topic=None):
"""docstring!"""
pass
@cmdutils.argument('url', completion=usertypes.Completion.url)
@cmdutils.argument('count', count=True)
def openurl(url=None, implicit=False, bg=False, tab=False, window=False,
count=None):
"""docstring!"""
pass
@cmdutils.argument('win_id', win_id=True)
@cmdutils.argument('command', completion=usertypes.Completion.command)
def bind(key, win_id, command=None, *, mode='normal', force=False):
"""docstring!"""
# pylint: disable=unused-variable
pass
def tab_detach():
"""docstring!"""
pass
cmds = { cmds = {
'set': [usertypes.Completion.section, usertypes.Completion.option, 'set': set_command,
usertypes.Completion.value], 'help': show_help,
'help': [usertypes.Completion.helptopic], 'open': openurl,
'quickmark-load': [usertypes.Completion.quickmark_by_name], 'bind': bind,
'bookmark-load': [usertypes.Completion.bookmark_by_url], 'tab-detach': tab_detach,
'open': [usertypes.Completion.url],
'buffer': [usertypes.Completion.tab],
'session-load': [usertypes.Completion.sessions],
'bind': [usertypes.Completion.empty, usertypes.Completion.command],
'tab-detach': None,
} }
cmd_utils = stubs.FakeCmdUtils({ cmd_utils = stubs.FakeCmdUtils({
name: stubs.FakeCommand(completion=compl) name: command.Command(name=name, handler=fn)
for name, compl in cmds.items() for name, fn in cmds.items()
}) })
monkeypatch.setattr('qutebrowser.completion.completer.cmdutils', monkeypatch.setattr('qutebrowser.completion.completer.cmdutils', cmd_utils)
cmd_utils)
def _set_cmd_prompt(cmd, txt): def _set_cmd_prompt(cmd, txt):
@ -143,21 +168,17 @@ def _validate_cmd_prompt(cmd, txt):
(':set general ignore-case |', usertypes.Completion.value), (':set general ignore-case |', usertypes.Completion.value),
(':set general huh |', None), (':set general huh |', None),
(':help |', usertypes.Completion.helptopic), (':help |', usertypes.Completion.helptopic),
(':quickmark-load |', usertypes.Completion.quickmark_by_name), (':help |', usertypes.Completion.helptopic),
(':bookmark-load |', usertypes.Completion.bookmark_by_url),
(':open |', usertypes.Completion.url), (':open |', usertypes.Completion.url),
(':buffer |', usertypes.Completion.tab), (':bind |', None),
(':session-load |', usertypes.Completion.sessions),
(':bind |', usertypes.Completion.empty),
(':bind <c-x> |', usertypes.Completion.command), (':bind <c-x> |', usertypes.Completion.command),
(':bind <c-x> foo|', usertypes.Completion.command), (':bind <c-x> foo|', usertypes.Completion.command),
(':bind <c-x>| foo', usertypes.Completion.empty), (':bind <c-x>| foo', None),
(':set| general ', usertypes.Completion.command), (':set| general ', usertypes.Completion.command),
(':|set general ', usertypes.Completion.command), (':|set general ', usertypes.Completion.command),
(':set gene|ral ignore-case', usertypes.Completion.section), (':set gene|ral ignore-case', usertypes.Completion.section),
(':|', usertypes.Completion.command), (':|', usertypes.Completion.command),
(': |', usertypes.Completion.command), (': |', usertypes.Completion.command),
(':bookmark-load |', usertypes.Completion.bookmark_by_url),
('/|', None), ('/|', None),
(':open -t|', None), (':open -t|', None),
(':open --tab|', None), (':open --tab|', None),