Remove completion.instances, usertypes.Completion.

The new completion API no longer needs either of these. Instead of
referencing an enum member, cmdutils.argument.completion now points to
a function that returnsthe desired completion model.
This vastly simplifies the addition of new completion types. Previously
it was necessary to define the new model as well as editing usertypes
and completion.models.instances. Now it is only necessary to define a
single function under completion.models.

This is the next step of Completion Model/View Revamping (#74).
This commit is contained in:
Ryan Roden-Corrent 2016-09-20 06:52:52 -04:00
parent b36cf0572d
commit 3b30b42211
9 changed files with 84 additions and 137 deletions

View File

@ -42,7 +42,7 @@ except ImportError:
import qutebrowser
import qutebrowser.resources
from qutebrowser.completion.models import instances as completionmodels
from qutebrowser.completion.models import miscmodels
from qutebrowser.commands import cmdutils, runners, cmdexc
from qutebrowser.config import style, config, websettings, configexc
from qutebrowser.browser import (urlmarks, adblock, history, browsertab,
@ -459,9 +459,6 @@ def _init_modules(args, crash_handler):
diskcache = cache.DiskCache(standarddir.cache(), parent=qApp)
objreg.register('cache', diskcache)
log.init.debug("Initializing completions...")
completionmodels.init()
log.init.debug("Misc initialization...")
if config.get('ui', 'hide-wayland-decoration'):
os.environ['QT_WAYLAND_DISABLE_WINDOWDECORATION'] = '1'
@ -753,7 +750,7 @@ class Quitter:
QTimer.singleShot(0, functools.partial(qApp.exit, status))
@cmdutils.register(instance='quitter', name='wq')
@cmdutils.argument('name', completion=usertypes.Completion.sessions)
@cmdutils.argument('name', completion=miscmodels.session)
def save_and_quit(self, name=sessions.default):
"""Save open pages and quit.

View File

@ -41,7 +41,7 @@ from qutebrowser.utils import (message, usertypes, log, qtutils, urlutils,
objreg, utils, typing)
from qutebrowser.utils.usertypes import KeyMode
from qutebrowser.misc import editor, guiprocess
from qutebrowser.completion.models import instances, sortfilter
from qutebrowser.completion.models import sortfilter, urlmodel, miscmodels
class CommandDispatcher:
@ -284,7 +284,7 @@ class CommandDispatcher:
@cmdutils.register(instance='command-dispatcher', name='open',
maxsplit=0, scope='window')
@cmdutils.argument('url', completion=usertypes.Completion.url)
@cmdutils.argument('url', completion=urlmodel.url)
@cmdutils.argument('count', count=True)
def openurl(self, url=None, implicit=False,
bg=False, tab=False, window=False, count=None, secure=False,
@ -1007,7 +1007,7 @@ class CommandDispatcher:
self._open(url, tab, bg, window)
@cmdutils.register(instance='command-dispatcher', scope='window')
@cmdutils.argument('index', completion=usertypes.Completion.tab)
@cmdutils.argument('index', completion=miscmodels.buffer)
def buffer(self, index):
"""Select tab by index or url/title best match.
@ -1023,7 +1023,7 @@ class CommandDispatcher:
for part in index_parts:
int(part)
except ValueError:
model = instances.get(usertypes.Completion.tab)
model = miscmodels.buffer()
sf = sortfilter.CompletionFilterModel(source=model)
sf.set_pattern(index)
if sf.count() > 0:
@ -1229,8 +1229,7 @@ class CommandDispatcher:
@cmdutils.register(instance='command-dispatcher', scope='window',
maxsplit=0)
@cmdutils.argument('name',
completion=usertypes.Completion.quickmark_by_name)
@cmdutils.argument('name', completion=miscmodels.quickmark)
def quickmark_load(self, name, tab=False, bg=False, window=False):
"""Load a quickmark.
@ -1248,8 +1247,7 @@ class CommandDispatcher:
@cmdutils.register(instance='command-dispatcher', scope='window',
maxsplit=0)
@cmdutils.argument('name',
completion=usertypes.Completion.quickmark_by_name)
@cmdutils.argument('name', completion=miscmodels.quickmark)
def quickmark_del(self, name=None):
"""Delete a quickmark.
@ -1311,7 +1309,7 @@ class CommandDispatcher:
@cmdutils.register(instance='command-dispatcher', scope='window',
maxsplit=0)
@cmdutils.argument('url', completion=usertypes.Completion.bookmark_by_url)
@cmdutils.argument('url', completion=miscmodels.bookmark)
def bookmark_load(self, url, tab=False, bg=False, window=False,
delete=False):
"""Load a bookmark.
@ -1333,7 +1331,7 @@ class CommandDispatcher:
@cmdutils.register(instance='command-dispatcher', scope='window',
maxsplit=0)
@cmdutils.argument('url', completion=usertypes.Completion.bookmark_by_url)
@cmdutils.argument('url', completion=miscmodels.bookmark)
def bookmark_del(self, url=None):
"""Delete a bookmark.
@ -1507,7 +1505,7 @@ class CommandDispatcher:
@cmdutils.register(instance='command-dispatcher', name='help',
scope='window')
@cmdutils.argument('topic', completion=usertypes.Completion.helptopic)
@cmdutils.argument('topic', completion=miscmodels.helptopic)
def show_help(self, tab=False, bg=False, window=False, topic=None):
r"""Show help about a command or setting.

View File

@ -23,8 +23,8 @@ from PyQt5.QtCore import pyqtSlot, QObject, QTimer
from qutebrowser.config import config
from qutebrowser.commands import cmdutils, runners
from qutebrowser.utils import usertypes, log, utils
from qutebrowser.completion.models import instances, sortfilter
from qutebrowser.utils import log, utils
from qutebrowser.completion.models import sortfilter, miscmodels
class Completer(QObject):
@ -72,7 +72,7 @@ class Completer(QObject):
Return:
A completion model or None.
"""
model = instances.get(completion)(*pos_args)
model = completion(*pos_args)
if model is None:
return None
else:
@ -96,7 +96,7 @@ class Completer(QObject):
log.completion.debug("After removing flags: {}".format(before_cursor))
if not before_cursor:
# '|' or 'set|'
model = instances.get(usertypes.Completion.command)()
model = miscmodels.command()
return sortfilter.CompletionFilterModel(source=model, parent=self)
try:
cmd = cmdutils.cmd_dict[before_cursor[0]]

View File

@ -1,53 +0,0 @@
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2015-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Global instances of the completion models."""
from qutebrowser.utils import usertypes
from qutebrowser.completion.models import miscmodels, urlmodel, configmodel
def get(completion):
"""Get a certain completion. Initializes the completion if needed."""
if completion == usertypes.Completion.command:
return miscmodels.command
if completion == usertypes.Completion.helptopic:
return miscmodels.helptopic
if completion == usertypes.Completion.tab:
return miscmodels.buffer
if completion == usertypes.Completion.quickmark_by_name:
return miscmodels.quickmark
if completion == usertypes.Completion.bookmark_by_url:
return miscmodels.bookmark
if completion == usertypes.Completion.sessions:
return miscmodels.session
if completion == usertypes.Completion.bind:
return miscmodels.bind
if completion == usertypes.Completion.section:
return configmodel.section
if completion == usertypes.Completion.option:
return configmodel.option
if completion == usertypes.Completion.value:
return configmodel.value
if completion == usertypes.Completion.url:
return urlmodel.url
def init():
pass

View File

@ -43,7 +43,7 @@ from qutebrowser.commands import cmdexc, cmdutils
from qutebrowser.utils import (message, objreg, utils, standarddir, log,
qtutils, error, usertypes)
from qutebrowser.misc import objects
from qutebrowser.utils.usertypes import Completion
from qutebrowser.completion.models import configmodel
UNSET = object()
@ -794,9 +794,9 @@ class ConfigManager(QObject):
e.__class__.__name__, e))
@cmdutils.register(name='set', instance='config', star_args_optional=True)
@cmdutils.argument('section_', completion=Completion.section)
@cmdutils.argument('option', completion=Completion.option)
@cmdutils.argument('values', completion=Completion.value)
@cmdutils.argument('section_', completion=configmodel.section)
@cmdutils.argument('option', completion=configmodel.option)
@cmdutils.argument('values', completion=configmodel.value)
@cmdutils.argument('win_id', win_id=True)
def set_command(self, win_id, section_=None, option=None, *values,
temp=False, print_=False):

View File

@ -27,7 +27,8 @@ from PyQt5.QtCore import pyqtSignal, QObject
from qutebrowser.config import configdata, textwrapper
from qutebrowser.commands import cmdutils, cmdexc
from qutebrowser.utils import log, utils, qtutils, message, usertypes
from qutebrowser.utils import log, utils, qtutils, message
from qutebrowser.completion.models import miscmodels
class KeyConfigError(Exception):
@ -153,7 +154,7 @@ class KeyConfigParser(QObject):
@cmdutils.register(instance='key-config', maxsplit=1, no_cmd_split=True,
no_replace_variables=True)
@cmdutils.argument('command', completion=usertypes.Completion.bind)
@cmdutils.argument('command', completion=miscmodels.bind)
def bind(self, key, command=None, *, mode='normal', force=False):
"""Bind a key to a command.

View File

@ -31,10 +31,11 @@ try:
except ImportError: # pragma: no cover
from yaml import SafeLoader as YamlLoader, SafeDumper as YamlDumper
from qutebrowser.utils import (standarddir, objreg, qtutils, log, usertypes,
message, utils)
from qutebrowser.utils import (standarddir, objreg, qtutils, log, message,
utils)
from qutebrowser.commands import cmdexc, cmdutils
from qutebrowser.config import config
from qutebrowser.completion.models import miscmodels
default = object() # Sentinel value
@ -433,7 +434,7 @@ class SessionManager(QObject):
return sessions
@cmdutils.register(instance='session-manager')
@cmdutils.argument('name', completion=usertypes.Completion.sessions)
@cmdutils.argument('name', completion=miscmodels.session)
def session_load(self, name, clear=False, temp=False, force=False):
"""Load a session.
@ -461,7 +462,7 @@ class SessionManager(QObject):
win.close()
@cmdutils.register(name=['session-save', 'w'], instance='session-manager')
@cmdutils.argument('name', completion=usertypes.Completion.sessions)
@cmdutils.argument('name', completion=miscmodels.session)
@cmdutils.argument('win_id', win_id=True)
@cmdutils.argument('with_private', flag='p')
def session_save(self, name: str = default, current=False, quiet=False,
@ -500,7 +501,7 @@ class SessionManager(QObject):
message.info("Saved session {}.".format(name))
@cmdutils.register(instance='session-manager')
@cmdutils.argument('name', completion=usertypes.Completion.sessions)
@cmdutils.argument('name', completion=miscmodels.session)
def session_delete(self, name, force=False):
"""Delete a session.

View File

@ -236,13 +236,6 @@ KeyMode = enum('KeyMode', ['normal', 'hint', 'command', 'yesno', 'prompt',
'jump_mark', 'record_macro', 'run_macro'])
# Available command completions
Completion = enum('Completion', ['command', 'section', 'option', 'value',
'helptopic', 'quickmark_by_name',
'bookmark_by_url', 'url', 'tab', 'sessions',
'bind'])
# Exit statuses for errors. Needs to be an int for sys.exit.
Exit = enum('Exit', ['ok', 'reserved', 'exception', 'err_ipc', 'err_init',
'err_config', 'err_key_config'], is_int=True, start=0)

View File

@ -26,7 +26,6 @@ from PyQt5.QtCore import QObject
from PyQt5.QtGui import QStandardItemModel
from qutebrowser.completion import completer
from qutebrowser.utils import usertypes
from qutebrowser.commands import command, cmdutils
@ -70,30 +69,45 @@ def completer_obj(qtbot, status_command_stub, config_stub, monkeypatch, stubs,
@pytest.fixture(autouse=True)
def instances(monkeypatch):
"""Mock the instances module so get returns a fake completion model."""
# populate a model for each completion type, with a nested structure for
# option and value completion
get = lambda kind: lambda *args: FakeCompletionModel(kind, *args)
monkeypatch.setattr(completer, 'instances', get)
def miscmodels_patch(mocker):
"""Patch the miscmodels module to provide fake completion functions.
Technically some of these are not part of miscmodels, but rolling them into
one module is easier and sufficient for mocking. The only one referenced
directly by Completer is miscmodels.command.
"""
m = mocker.patch('qutebrowser.completion.completer.miscmodels',
autospec=True)
m.command = lambda *args: FakeCompletionModel('command', *args)
m.helptopic = lambda *args: FakeCompletionModel('helptopic', *args)
m.quickmark = lambda *args: FakeCompletionModel('quickmark', *args)
m.bookmark = lambda *args: FakeCompletionModel('bookmark', *args)
m.session = lambda *args: FakeCompletionModel('session', *args)
m.buffer = lambda *args: FakeCompletionModel('buffer', *args)
m.bind = lambda *args: FakeCompletionModel('bind', *args)
m.url = lambda *args: FakeCompletionModel('url', *args)
m.section = lambda *args: FakeCompletionModel('section', *args)
m.option = lambda *args: FakeCompletionModel('option', *args)
m.value = lambda *args: FakeCompletionModel('value', *args)
return m
@pytest.fixture(autouse=True)
def cmdutils_patch(monkeypatch, stubs):
def cmdutils_patch(monkeypatch, stubs, miscmodels_patch):
"""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)
@cmdutils.argument('section_', completion=miscmodels_patch.section)
@cmdutils.argument('option', completion=miscmodels_patch.option)
@cmdutils.argument('value', completion=miscmodels_patch.value)
def set_command(section_=None, option=None, value=None):
"""docstring."""
pass
@cmdutils.argument('topic', completion=usertypes.Completion.helptopic)
@cmdutils.argument('topic', completion=miscmodels_patch.helptopic)
def show_help(tab=False, bg=False, window=False, topic=None):
"""docstring."""
pass
@cmdutils.argument('url', completion=usertypes.Completion.url)
@cmdutils.argument('url', completion=miscmodels_patch.url)
@cmdutils.argument('count', count=True)
def openurl(url=None, implicit=False, bg=False, tab=False, window=False,
count=None):
@ -101,7 +115,7 @@ def cmdutils_patch(monkeypatch, stubs):
pass
@cmdutils.argument('win_id', win_id=True)
@cmdutils.argument('command', completion=usertypes.Completion.command)
@cmdutils.argument('command', completion=miscmodels_patch.command)
def bind(key, win_id, command=None, *, mode='normal', force=False):
"""docstring."""
pass
@ -132,49 +146,45 @@ def _set_cmd_prompt(cmd, txt):
@pytest.mark.parametrize('txt, kind, pattern, pos_args', [
(':nope|', usertypes.Completion.command, 'nope', []),
(':nope|', 'command', 'nope', []),
(':nope |', None, '', []),
(':set |', usertypes.Completion.section, '', []),
(':set gen|', usertypes.Completion.section, 'gen', []),
(':set general |', usertypes.Completion.option, '', ['general']),
(':set what |', usertypes.Completion.option, '', ['what']),
(':set general editor |', usertypes.Completion.value, '',
(':set |', 'section', '', []),
(':set gen|', 'section', 'gen', []),
(':set general |', 'option', '', ['general']),
(':set what |', 'option', '', ['what']),
(':set general editor |', 'value', '', ['general', 'editor']),
(':set general editor gv|', 'value', 'gv', ['general', 'editor']),
(':set general editor "gvim -f"|', 'value', 'gvim -f',
['general', 'editor']),
(':set general editor gv|', usertypes.Completion.value, 'gv',
['general', 'editor']),
(':set general editor "gvim -f"|', usertypes.Completion.value, 'gvim -f',
['general', 'editor']),
(':set general editor "gvim |', usertypes.Completion.value, 'gvim',
['general', 'editor']),
(':set general huh |', usertypes.Completion.value, '', ['general', 'huh']),
(':help |', usertypes.Completion.helptopic, '', []),
(':help |', usertypes.Completion.helptopic, '', []),
(':open |', usertypes.Completion.url, '', []),
(':set general editor "gvim |', 'value', 'gvim', ['general', 'editor']),
(':set general huh |', 'value', '', ['general', 'huh']),
(':help |', 'helptopic', '', []),
(':help |', 'helptopic', '', []),
(':open |', 'url', '', []),
(':bind |', None, '', []),
(':bind <c-x> |', usertypes.Completion.command, '', ['<c-x>']),
(':bind <c-x> foo|', usertypes.Completion.command, 'foo', ['<c-x>']),
(':bind <c-x> |', 'command', '', ['<c-x>']),
(':bind <c-x> foo|', 'command', 'foo', ['<c-x>']),
(':bind <c-x>| foo', None, '<c-x>', []),
(':set| general ', usertypes.Completion.command, 'set', []),
(':|set general ', usertypes.Completion.command, 'set', []),
(':set gene|ral ignore-case', usertypes.Completion.section, 'general', []),
(':|', usertypes.Completion.command, '', []),
(': |', usertypes.Completion.command, '', []),
(':set| general ', 'command', 'set', []),
(':|set general ', 'command', 'set', []),
(':set gene|ral ignore-case', 'section', 'general', []),
(':|', 'command', '', []),
(': |', 'command', '', []),
('/|', None, '', []),
(':open -t|', None, '', []),
(':open --tab|', None, '', []),
(':open -t |', usertypes.Completion.url, '', []),
(':open --tab |', usertypes.Completion.url, '', []),
(':open | -t', usertypes.Completion.url, '', []),
(':open -t |', 'url', '', []),
(':open --tab |', 'url', '', []),
(':open | -t', 'url', '', []),
(':tab-detach |', None, '', []),
(':bind --mode=caret <c-x> |', usertypes.Completion.command, '',
['<c-x>']),
pytest.param(':bind --mode caret <c-x> |', usertypes.Completion.command,
'', [], marks=pytest.mark.xfail(reason='issue #74')),
(':set -t -p |', usertypes.Completion.section, '', []),
(':bind --mode=caret <c-x> |', 'command', '', ['<c-x>']),
pytest.param(':bind --mode caret <c-x> |', 'command', '', [],
marks=pytest.mark.xfail(reason='issue #74')),
(':set -t -p |', 'section', '', []),
(':open -- |', None, '', []),
(':gibberish nonesense |', None, '', []),
('/:help|', None, '', []),
('::bind|', usertypes.Completion.command, ':bind'),
('::bind|', 'command', ':bind', []),
])
def test_update_completion(txt, kind, pattern, pos_args, status_command_stub,
completer_obj, completion_widget_stub):