diff --git a/qutebrowser/app.py b/qutebrowser/app.py index a3a855f9a..2b216ca44 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -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. diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 97a764c68..8914d4ac2 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -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. diff --git a/qutebrowser/completion/completer.py b/qutebrowser/completion/completer.py index 21af74da5..bd4f43009 100644 --- a/qutebrowser/completion/completer.py +++ b/qutebrowser/completion/completer.py @@ -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]] diff --git a/qutebrowser/completion/models/instances.py b/qutebrowser/completion/models/instances.py deleted file mode 100644 index ce109ae7a..000000000 --- a/qutebrowser/completion/models/instances.py +++ /dev/null @@ -1,53 +0,0 @@ -# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: - -# Copyright 2015-2017 Florian Bruhin (The Compiler) -# -# 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 . - -"""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 diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index bb52a2417..dd71f5333 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -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): diff --git a/qutebrowser/config/parsers/keyconf.py b/qutebrowser/config/parsers/keyconf.py index 53f23d7c0..e4adb8676 100644 --- a/qutebrowser/config/parsers/keyconf.py +++ b/qutebrowser/config/parsers/keyconf.py @@ -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. diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index aa9a1be97..124b78053 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -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. diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 7d31ba6ac..31f2f79cb 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -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) diff --git a/tests/unit/completion/test_completer.py b/tests/unit/completion/test_completer.py index 937107f75..48f619104 100644 --- a/tests/unit/completion/test_completer.py +++ b/tests/unit/completion/test_completer.py @@ -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 |', usertypes.Completion.command, '', ['']), - (':bind foo|', usertypes.Completion.command, 'foo', ['']), + (':bind |', 'command', '', ['']), + (':bind foo|', 'command', 'foo', ['']), (':bind | foo', None, '', []), - (':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 |', usertypes.Completion.command, '', - ['']), - pytest.param(':bind --mode caret |', usertypes.Completion.command, - '', [], marks=pytest.mark.xfail(reason='issue #74')), - (':set -t -p |', usertypes.Completion.section, '', []), + (':bind --mode=caret |', 'command', '', ['']), + pytest.param(':bind --mode caret |', '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):