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
import qutebrowser.resources 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.commands import cmdutils, runners, cmdexc
from qutebrowser.config import style, config, websettings, configexc from qutebrowser.config import style, config, websettings, configexc
from qutebrowser.browser import (urlmarks, adblock, history, browsertab, 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) diskcache = cache.DiskCache(standarddir.cache(), parent=qApp)
objreg.register('cache', diskcache) objreg.register('cache', diskcache)
log.init.debug("Initializing completions...")
completionmodels.init()
log.init.debug("Misc initialization...") log.init.debug("Misc initialization...")
if config.get('ui', 'hide-wayland-decoration'): if config.get('ui', 'hide-wayland-decoration'):
os.environ['QT_WAYLAND_DISABLE_WINDOWDECORATION'] = '1' os.environ['QT_WAYLAND_DISABLE_WINDOWDECORATION'] = '1'
@ -753,7 +750,7 @@ class Quitter:
QTimer.singleShot(0, functools.partial(qApp.exit, status)) QTimer.singleShot(0, functools.partial(qApp.exit, status))
@cmdutils.register(instance='quitter', name='wq') @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): def save_and_quit(self, name=sessions.default):
"""Save open pages and quit. """Save open pages and quit.

View File

@ -41,7 +41,7 @@ from qutebrowser.utils import (message, usertypes, log, qtutils, urlutils,
objreg, utils, typing) objreg, utils, typing)
from qutebrowser.utils.usertypes import KeyMode from qutebrowser.utils.usertypes import KeyMode
from qutebrowser.misc import editor, guiprocess from qutebrowser.misc import editor, guiprocess
from qutebrowser.completion.models import instances, sortfilter from qutebrowser.completion.models import sortfilter, urlmodel, miscmodels
class CommandDispatcher: class CommandDispatcher:
@ -284,7 +284,7 @@ class CommandDispatcher:
@cmdutils.register(instance='command-dispatcher', name='open', @cmdutils.register(instance='command-dispatcher', name='open',
maxsplit=0, scope='window') maxsplit=0, scope='window')
@cmdutils.argument('url', completion=usertypes.Completion.url) @cmdutils.argument('url', completion=urlmodel.url)
@cmdutils.argument('count', count=True) @cmdutils.argument('count', count=True)
def openurl(self, url=None, implicit=False, def openurl(self, url=None, implicit=False,
bg=False, tab=False, window=False, count=None, secure=False, bg=False, tab=False, window=False, count=None, secure=False,
@ -1007,7 +1007,7 @@ class CommandDispatcher:
self._open(url, tab, bg, window) self._open(url, tab, bg, window)
@cmdutils.register(instance='command-dispatcher', scope='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): def buffer(self, index):
"""Select tab by index or url/title best match. """Select tab by index or url/title best match.
@ -1023,7 +1023,7 @@ class CommandDispatcher:
for part in index_parts: for part in index_parts:
int(part) int(part)
except ValueError: except ValueError:
model = instances.get(usertypes.Completion.tab) model = miscmodels.buffer()
sf = sortfilter.CompletionFilterModel(source=model) sf = sortfilter.CompletionFilterModel(source=model)
sf.set_pattern(index) sf.set_pattern(index)
if sf.count() > 0: if sf.count() > 0:
@ -1229,8 +1229,7 @@ class CommandDispatcher:
@cmdutils.register(instance='command-dispatcher', scope='window', @cmdutils.register(instance='command-dispatcher', scope='window',
maxsplit=0) maxsplit=0)
@cmdutils.argument('name', @cmdutils.argument('name', completion=miscmodels.quickmark)
completion=usertypes.Completion.quickmark_by_name)
def quickmark_load(self, name, tab=False, bg=False, window=False): def quickmark_load(self, name, tab=False, bg=False, window=False):
"""Load a quickmark. """Load a quickmark.
@ -1248,8 +1247,7 @@ class CommandDispatcher:
@cmdutils.register(instance='command-dispatcher', scope='window', @cmdutils.register(instance='command-dispatcher', scope='window',
maxsplit=0) maxsplit=0)
@cmdutils.argument('name', @cmdutils.argument('name', completion=miscmodels.quickmark)
completion=usertypes.Completion.quickmark_by_name)
def quickmark_del(self, name=None): def quickmark_del(self, name=None):
"""Delete a quickmark. """Delete a quickmark.
@ -1311,7 +1309,7 @@ class CommandDispatcher:
@cmdutils.register(instance='command-dispatcher', scope='window', @cmdutils.register(instance='command-dispatcher', scope='window',
maxsplit=0) 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, def bookmark_load(self, url, tab=False, bg=False, window=False,
delete=False): delete=False):
"""Load a bookmark. """Load a bookmark.
@ -1333,7 +1331,7 @@ class CommandDispatcher:
@cmdutils.register(instance='command-dispatcher', scope='window', @cmdutils.register(instance='command-dispatcher', scope='window',
maxsplit=0) maxsplit=0)
@cmdutils.argument('url', completion=usertypes.Completion.bookmark_by_url) @cmdutils.argument('url', completion=miscmodels.bookmark)
def bookmark_del(self, url=None): def bookmark_del(self, url=None):
"""Delete a bookmark. """Delete a bookmark.
@ -1507,7 +1505,7 @@ class CommandDispatcher:
@cmdutils.register(instance='command-dispatcher', name='help', @cmdutils.register(instance='command-dispatcher', name='help',
scope='window') 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): def show_help(self, tab=False, bg=False, window=False, topic=None):
r"""Show help about a command or setting. 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.config import config
from qutebrowser.commands import cmdutils, runners from qutebrowser.commands import cmdutils, runners
from qutebrowser.utils import usertypes, log, utils from qutebrowser.utils import log, utils
from qutebrowser.completion.models import instances, sortfilter from qutebrowser.completion.models import sortfilter, miscmodels
class Completer(QObject): class Completer(QObject):
@ -72,7 +72,7 @@ class Completer(QObject):
Return: Return:
A completion model or None. A completion model or None.
""" """
model = instances.get(completion)(*pos_args) model = completion(*pos_args)
if model is None: if model is None:
return None return None
else: else:
@ -96,7 +96,7 @@ class Completer(QObject):
log.completion.debug("After removing flags: {}".format(before_cursor)) log.completion.debug("After removing flags: {}".format(before_cursor))
if not before_cursor: if not before_cursor:
# '|' or 'set|' # '|' or 'set|'
model = instances.get(usertypes.Completion.command)() model = miscmodels.command()
return sortfilter.CompletionFilterModel(source=model, parent=self) return sortfilter.CompletionFilterModel(source=model, parent=self)
try: try:
cmd = cmdutils.cmd_dict[before_cursor[0]] 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, from qutebrowser.utils import (message, objreg, utils, standarddir, log,
qtutils, error, usertypes) qtutils, error, usertypes)
from qutebrowser.misc import objects from qutebrowser.misc import objects
from qutebrowser.utils.usertypes import Completion from qutebrowser.completion.models import configmodel
UNSET = object() UNSET = object()
@ -794,9 +794,9 @@ class ConfigManager(QObject):
e.__class__.__name__, e)) e.__class__.__name__, e))
@cmdutils.register(name='set', instance='config', star_args_optional=True) @cmdutils.register(name='set', instance='config', star_args_optional=True)
@cmdutils.argument('section_', completion=Completion.section) @cmdutils.argument('section_', completion=configmodel.section)
@cmdutils.argument('option', completion=Completion.option) @cmdutils.argument('option', completion=configmodel.option)
@cmdutils.argument('values', completion=Completion.value) @cmdutils.argument('values', completion=configmodel.value)
@cmdutils.argument('win_id', win_id=True) @cmdutils.argument('win_id', win_id=True)
def set_command(self, win_id, section_=None, option=None, *values, def set_command(self, win_id, section_=None, option=None, *values,
temp=False, print_=False): temp=False, print_=False):

View File

@ -27,7 +27,8 @@ from PyQt5.QtCore import pyqtSignal, QObject
from qutebrowser.config import configdata, textwrapper from qutebrowser.config import configdata, textwrapper
from qutebrowser.commands import cmdutils, cmdexc 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): class KeyConfigError(Exception):
@ -153,7 +154,7 @@ 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,
no_replace_variables=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): def bind(self, key, command=None, *, mode='normal', force=False):
"""Bind a key to a command. """Bind a key to a command.

View File

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

View File

@ -236,13 +236,6 @@ KeyMode = enum('KeyMode', ['normal', 'hint', 'command', 'yesno', 'prompt',
'jump_mark', 'record_macro', 'run_macro']) '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 statuses for errors. Needs to be an int for sys.exit.
Exit = enum('Exit', ['ok', 'reserved', 'exception', 'err_ipc', 'err_init', Exit = enum('Exit', ['ok', 'reserved', 'exception', 'err_ipc', 'err_init',
'err_config', 'err_key_config'], is_int=True, start=0) '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 PyQt5.QtGui import QStandardItemModel
from qutebrowser.completion import completer from qutebrowser.completion import completer
from qutebrowser.utils import usertypes
from qutebrowser.commands import command, cmdutils 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) @pytest.fixture(autouse=True)
def instances(monkeypatch): def miscmodels_patch(mocker):
"""Mock the instances module so get returns a fake completion model.""" """Patch the miscmodels module to provide fake completion functions.
# populate a model for each completion type, with a nested structure for
# option and value completion Technically some of these are not part of miscmodels, but rolling them into
get = lambda kind: lambda *args: FakeCompletionModel(kind, *args) one module is easier and sufficient for mocking. The only one referenced
monkeypatch.setattr(completer, 'instances', get) 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) @pytest.fixture(autouse=True)
def cmdutils_patch(monkeypatch, stubs): def cmdutils_patch(monkeypatch, stubs, miscmodels_patch):
"""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('section_', completion=miscmodels_patch.section)
@cmdutils.argument('option', completion=usertypes.Completion.option) @cmdutils.argument('option', completion=miscmodels_patch.option)
@cmdutils.argument('value', completion=usertypes.Completion.value) @cmdutils.argument('value', completion=miscmodels_patch.value)
def set_command(section_=None, option=None, value=None): def set_command(section_=None, option=None, value=None):
"""docstring.""" """docstring."""
pass 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): def show_help(tab=False, bg=False, window=False, topic=None):
"""docstring.""" """docstring."""
pass pass
@cmdutils.argument('url', completion=usertypes.Completion.url) @cmdutils.argument('url', completion=miscmodels_patch.url)
@cmdutils.argument('count', count=True) @cmdutils.argument('count', count=True)
def openurl(url=None, implicit=False, bg=False, tab=False, window=False, def openurl(url=None, implicit=False, bg=False, tab=False, window=False,
count=None): count=None):
@ -101,7 +115,7 @@ def cmdutils_patch(monkeypatch, stubs):
pass pass
@cmdutils.argument('win_id', win_id=True) @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): def bind(key, win_id, command=None, *, mode='normal', force=False):
"""docstring.""" """docstring."""
pass pass
@ -132,49 +146,45 @@ def _set_cmd_prompt(cmd, txt):
@pytest.mark.parametrize('txt, kind, pattern, pos_args', [ @pytest.mark.parametrize('txt, kind, pattern, pos_args', [
(':nope|', usertypes.Completion.command, 'nope', []), (':nope|', 'command', 'nope', []),
(':nope |', None, '', []), (':nope |', None, '', []),
(':set |', usertypes.Completion.section, '', []), (':set |', 'section', '', []),
(':set gen|', usertypes.Completion.section, 'gen', []), (':set gen|', 'section', 'gen', []),
(':set general |', usertypes.Completion.option, '', ['general']), (':set general |', 'option', '', ['general']),
(':set what |', usertypes.Completion.option, '', ['what']), (':set what |', 'option', '', ['what']),
(':set general editor |', usertypes.Completion.value, '', (':set general editor |', 'value', '', ['general', 'editor']),
(':set general editor gv|', 'value', 'gv', ['general', 'editor']),
(':set general editor "gvim -f"|', 'value', 'gvim -f',
['general', 'editor']), ['general', 'editor']),
(':set general editor gv|', usertypes.Completion.value, 'gv', (':set general editor "gvim |', 'value', 'gvim', ['general', 'editor']),
['general', 'editor']), (':set general huh |', 'value', '', ['general', 'huh']),
(':set general editor "gvim -f"|', usertypes.Completion.value, 'gvim -f', (':help |', 'helptopic', '', []),
['general', 'editor']), (':help |', 'helptopic', '', []),
(':set general editor "gvim |', usertypes.Completion.value, 'gvim', (':open |', 'url', '', []),
['general', 'editor']),
(':set general huh |', usertypes.Completion.value, '', ['general', 'huh']),
(':help |', usertypes.Completion.helptopic, '', []),
(':help |', usertypes.Completion.helptopic, '', []),
(':open |', usertypes.Completion.url, '', []),
(':bind |', None, '', []), (':bind |', None, '', []),
(':bind <c-x> |', usertypes.Completion.command, '', ['<c-x>']), (':bind <c-x> |', 'command', '', ['<c-x>']),
(':bind <c-x> foo|', usertypes.Completion.command, 'foo', ['<c-x>']), (':bind <c-x> foo|', 'command', 'foo', ['<c-x>']),
(':bind <c-x>| foo', None, '<c-x>', []), (':bind <c-x>| foo', None, '<c-x>', []),
(':set| general ', usertypes.Completion.command, 'set', []), (':set| general ', 'command', 'set', []),
(':|set general ', usertypes.Completion.command, 'set', []), (':|set general ', 'command', 'set', []),
(':set gene|ral ignore-case', usertypes.Completion.section, 'general', []), (':set gene|ral ignore-case', 'section', 'general', []),
(':|', usertypes.Completion.command, '', []), (':|', 'command', '', []),
(': |', usertypes.Completion.command, '', []), (': |', 'command', '', []),
('/|', None, '', []), ('/|', None, '', []),
(':open -t|', None, '', []), (':open -t|', None, '', []),
(':open --tab|', None, '', []), (':open --tab|', None, '', []),
(':open -t |', usertypes.Completion.url, '', []), (':open -t |', 'url', '', []),
(':open --tab |', usertypes.Completion.url, '', []), (':open --tab |', 'url', '', []),
(':open | -t', usertypes.Completion.url, '', []), (':open | -t', 'url', '', []),
(':tab-detach |', None, '', []), (':tab-detach |', None, '', []),
(':bind --mode=caret <c-x> |', usertypes.Completion.command, '', (':bind --mode=caret <c-x> |', 'command', '', ['<c-x>']),
['<c-x>']), pytest.param(':bind --mode caret <c-x> |', 'command', '', [],
pytest.param(':bind --mode caret <c-x> |', usertypes.Completion.command, marks=pytest.mark.xfail(reason='issue #74')),
'', [], marks=pytest.mark.xfail(reason='issue #74')), (':set -t -p |', 'section', '', []),
(':set -t -p |', usertypes.Completion.section, '', []),
(':open -- |', None, '', []), (':open -- |', None, '', []),
(':gibberish nonesense |', None, '', []), (':gibberish nonesense |', None, '', []),
('/:help|', None, '', []), ('/:help|', None, '', []),
('::bind|', usertypes.Completion.command, ':bind'), ('::bind|', 'command', ':bind', []),
]) ])
def test_update_completion(txt, kind, pattern, pos_args, status_command_stub, def test_update_completion(txt, kind, pattern, pos_args, status_command_stub,
completer_obj, completion_widget_stub): completer_obj, completion_widget_stub):