Pass CompletionInfo to completion functions.
In python3.4, there is a circular dependency between the config module and configmodel.bind. This is resolved by dependency injection. The config/keyconfig instances are embedded in a struct passed to every completion function, so the functions no longer depend on the modules. This will also enable completion functions to access other previously inaccessible info, such as the window id. See #2814.
This commit is contained in:
parent
6a292f9d56
commit
3bfa01f0d0
@ -19,6 +19,8 @@
|
||||
|
||||
"""Completer attached to a CompletionView."""
|
||||
|
||||
import collections
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot, QObject, QTimer
|
||||
|
||||
from qutebrowser.config import config
|
||||
@ -27,6 +29,11 @@ from qutebrowser.utils import log, utils, debug
|
||||
from qutebrowser.completion.models import miscmodels
|
||||
|
||||
|
||||
# Context passed into all completion functions
|
||||
CompletionInfo = collections.namedtuple('CompletionInfo',
|
||||
['config', 'keyconf'])
|
||||
|
||||
|
||||
class Completer(QObject):
|
||||
|
||||
"""Completer which manages completions in a CompletionView.
|
||||
@ -231,7 +238,9 @@ class Completer(QObject):
|
||||
args = (x for x in before_cursor[1:] if not x.startswith('-'))
|
||||
with debug.log_time(log.completion,
|
||||
'Starting {} completion'.format(func.__name__)):
|
||||
model = func(*args)
|
||||
info = CompletionInfo(config=config.instance,
|
||||
keyconf=config.key_instance)
|
||||
model = func(*args, info=info)
|
||||
with debug.log_time(log.completion, 'Set completion model'):
|
||||
completion.set_model(model)
|
||||
|
||||
|
@ -19,31 +19,32 @@
|
||||
|
||||
"""Functions that return config-related completion models."""
|
||||
|
||||
from qutebrowser.config import configdata, configexc, config
|
||||
from qutebrowser.config import configdata, configexc
|
||||
from qutebrowser.completion.models import completionmodel, listcategory, util
|
||||
from qutebrowser.commands import cmdutils
|
||||
|
||||
|
||||
def option():
|
||||
def option(*, info):
|
||||
"""A CompletionModel filled with settings and their descriptions."""
|
||||
model = completionmodel.CompletionModel(column_widths=(20, 70, 10))
|
||||
options = ((x.name, x.description, config.instance.get_str(x.name))
|
||||
options = ((x.name, x.description, info.config.get_str(x.name))
|
||||
for x in configdata.DATA.values())
|
||||
model.add_category(listcategory.ListCategory("Options", sorted(options)))
|
||||
return model
|
||||
|
||||
|
||||
def value(optname, *_values):
|
||||
def value(optname, *_values, info):
|
||||
"""A CompletionModel filled with setting values.
|
||||
|
||||
Args:
|
||||
optname: The name of the config option this model shows.
|
||||
_values: The values already provided on the command line.
|
||||
info: A CompletionInfo instance.
|
||||
"""
|
||||
model = completionmodel.CompletionModel(column_widths=(30, 70, 0))
|
||||
|
||||
try:
|
||||
current = str(config.instance.get(optname) or '""')
|
||||
current = str(info.config.get(optname) or '""')
|
||||
except configexc.NoOptionError:
|
||||
return None
|
||||
|
||||
@ -60,14 +61,14 @@ def value(optname, *_values):
|
||||
return model
|
||||
|
||||
|
||||
def bind(key):
|
||||
def bind(key, *, info):
|
||||
"""A CompletionModel filled with all bindable commands and descriptions.
|
||||
|
||||
Args:
|
||||
key: the key being bound.
|
||||
"""
|
||||
model = completionmodel.CompletionModel(column_widths=(20, 60, 20))
|
||||
cmd_text = config.key_instance.get_bindings_for('normal').get(key)
|
||||
cmd_text = info.keyconf.get_bindings_for('normal').get(key)
|
||||
|
||||
if cmd_text:
|
||||
cmd_name = cmd_text.split(' ')[0]
|
||||
|
@ -24,7 +24,7 @@ from qutebrowser.utils import objreg, log
|
||||
from qutebrowser.completion.models import completionmodel, listcategory, util
|
||||
|
||||
|
||||
def command():
|
||||
def command(*, info=None): # pylint: disable=unused-argument
|
||||
"""A CompletionModel filled with non-hidden commands and descriptions."""
|
||||
model = completionmodel.CompletionModel(column_widths=(20, 60, 20))
|
||||
cmdlist = util.get_cmd_completions(include_aliases=True,
|
||||
@ -33,7 +33,7 @@ def command():
|
||||
return model
|
||||
|
||||
|
||||
def helptopic():
|
||||
def helptopic(*, info=None): # pylint: disable=unused-argument
|
||||
"""A CompletionModel filled with help topics."""
|
||||
model = completionmodel.CompletionModel()
|
||||
|
||||
@ -47,7 +47,7 @@ def helptopic():
|
||||
return model
|
||||
|
||||
|
||||
def quickmark():
|
||||
def quickmark(*, info=None): # pylint: disable=unused-argument
|
||||
"""A CompletionModel filled with all quickmarks."""
|
||||
def delete(data):
|
||||
"""Delete a quickmark from the completion menu."""
|
||||
@ -63,7 +63,7 @@ def quickmark():
|
||||
return model
|
||||
|
||||
|
||||
def bookmark():
|
||||
def bookmark(*, info=None): # pylint: disable=unused-argument
|
||||
"""A CompletionModel filled with all bookmarks."""
|
||||
def delete(data):
|
||||
"""Delete a bookmark from the completion menu."""
|
||||
@ -79,7 +79,7 @@ def bookmark():
|
||||
return model
|
||||
|
||||
|
||||
def session():
|
||||
def session(*, info=None): # pylint: disable=unused-argument
|
||||
"""A CompletionModel filled with session names."""
|
||||
model = completionmodel.CompletionModel()
|
||||
try:
|
||||
@ -92,7 +92,7 @@ def session():
|
||||
return model
|
||||
|
||||
|
||||
def buffer():
|
||||
def buffer(*, info=None): # pylint: disable=unused-argument
|
||||
"""A model to complete on open tabs across all windows.
|
||||
|
||||
Used for switching the buffer command.
|
||||
|
@ -22,7 +22,6 @@
|
||||
from qutebrowser.completion.models import (completionmodel, listcategory,
|
||||
histcategory)
|
||||
from qutebrowser.utils import log, objreg
|
||||
from qutebrowser.config import config
|
||||
|
||||
|
||||
_URLCOL = 0
|
||||
@ -50,7 +49,7 @@ def _delete_quickmark(data):
|
||||
quickmark_manager.delete(name)
|
||||
|
||||
|
||||
def url():
|
||||
def url(*, info):
|
||||
"""A model which combines bookmarks, quickmarks and web history URLs.
|
||||
|
||||
Used for the `open` command.
|
||||
@ -66,7 +65,7 @@ def url():
|
||||
model.add_category(listcategory.ListCategory(
|
||||
'Bookmarks', bookmarks, delete_func=_delete_bookmark))
|
||||
|
||||
if config.val.completion.web_history_max_items != 0:
|
||||
if info.config.get('completion.web_history_max_items') != 0:
|
||||
hist_cat = histcategory.HistoryCategory(delete_func=_delete_history)
|
||||
model.add_category(hist_cat)
|
||||
return model
|
||||
|
@ -33,10 +33,11 @@ class FakeCompletionModel(QStandardItemModel):
|
||||
|
||||
"""Stub for a completion model."""
|
||||
|
||||
def __init__(self, kind, *pos_args, parent=None):
|
||||
def __init__(self, kind, *pos_args, info, parent=None):
|
||||
super().__init__(parent)
|
||||
self.kind = kind
|
||||
self.pos_args = list(pos_args)
|
||||
self.info = info
|
||||
|
||||
|
||||
class CompletionWidgetStub(QObject):
|
||||
@ -77,17 +78,21 @@ def miscmodels_patch(mocker):
|
||||
"""
|
||||
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)
|
||||
|
||||
def func(name):
|
||||
return lambda *args, info: FakeCompletionModel(name, *args, info=info)
|
||||
|
||||
m.command = func('command')
|
||||
m.helptopic = func('helptopic')
|
||||
m.quickmark = func('quickmark')
|
||||
m.bookmark = func('bookmark')
|
||||
m.session = func('session')
|
||||
m.buffer = func('buffer')
|
||||
m.bind = func('bind')
|
||||
m.url = func('url')
|
||||
m.section = func('section')
|
||||
m.option = func('option')
|
||||
m.value = func('value')
|
||||
return m
|
||||
|
||||
|
||||
@ -186,7 +191,8 @@ def _set_cmd_prompt(cmd, txt):
|
||||
('::bind|', 'command', ':bind', []),
|
||||
])
|
||||
def test_update_completion(txt, kind, pattern, pos_args, status_command_stub,
|
||||
completer_obj, completion_widget_stub):
|
||||
completer_obj, completion_widget_stub, config_stub,
|
||||
key_config_stub):
|
||||
"""Test setting the completion widget's model based on command text."""
|
||||
# this test uses | as a placeholder for the current cursor position
|
||||
_set_cmd_prompt(status_command_stub, txt)
|
||||
@ -198,6 +204,8 @@ def test_update_completion(txt, kind, pattern, pos_args, status_command_stub,
|
||||
model = completion_widget_stub.set_model.call_args[0][0]
|
||||
assert model.kind == kind
|
||||
assert model.pos_args == pos_args
|
||||
assert model.info.config == config_stub
|
||||
assert model.info.keyconf == key_config_stub
|
||||
completion_widget_stub.set_pattern.assert_called_once_with(pattern)
|
||||
|
||||
|
||||
|
@ -27,6 +27,7 @@ from datetime import datetime
|
||||
import pytest
|
||||
from PyQt5.QtCore import QUrl
|
||||
|
||||
from qutebrowser.completion import completer
|
||||
from qutebrowser.completion.models import miscmodels, urlmodel, configmodel
|
||||
from qutebrowser.config import configdata, configtypes
|
||||
from qutebrowser.utils import objreg
|
||||
@ -181,6 +182,12 @@ def web_history_populated(web_history):
|
||||
return web_history
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def info(config_stub, key_config_stub):
|
||||
return completer.CompletionInfo(config=config_stub,
|
||||
keyconf=key_config_stub)
|
||||
|
||||
|
||||
def test_command_completion(qtmodeltester, cmdutils_stub, configdata_stub,
|
||||
key_config_stub):
|
||||
"""Test the results of command completion.
|
||||
@ -310,7 +317,7 @@ def test_bookmark_completion_delete(qtmodeltester, bookmarks, row, removed):
|
||||
|
||||
|
||||
def test_url_completion(qtmodeltester, web_history_populated,
|
||||
quickmarks, bookmarks):
|
||||
quickmarks, bookmarks, info):
|
||||
"""Test the results of url completion.
|
||||
|
||||
Verify that:
|
||||
@ -318,7 +325,7 @@ def test_url_completion(qtmodeltester, web_history_populated,
|
||||
- entries are sorted by access time
|
||||
- only the most recent entry is included for each url
|
||||
"""
|
||||
model = urlmodel.url()
|
||||
model = urlmodel.url(info=info)
|
||||
model.set_pattern('')
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
@ -361,20 +368,20 @@ def test_url_completion(qtmodeltester, web_history_populated,
|
||||
('foobar', '', '%', 0),
|
||||
])
|
||||
def test_url_completion_pattern(web_history, quickmark_manager_stub,
|
||||
bookmark_manager_stub, url, title, pattern,
|
||||
rowcount):
|
||||
bookmark_manager_stub, info,
|
||||
url, title, pattern, rowcount):
|
||||
"""Test that url completion filters by url and title."""
|
||||
web_history.add_url(QUrl(url), title)
|
||||
model = urlmodel.url()
|
||||
model = urlmodel.url(info=info)
|
||||
model.set_pattern(pattern)
|
||||
# 2, 0 is History
|
||||
assert model.rowCount(model.index(2, 0)) == rowcount
|
||||
|
||||
|
||||
def test_url_completion_delete_bookmark(qtmodeltester, bookmarks,
|
||||
web_history, quickmarks):
|
||||
web_history, quickmarks, info):
|
||||
"""Test deleting a bookmark from the url completion model."""
|
||||
model = urlmodel.url()
|
||||
model = urlmodel.url(info=info)
|
||||
model.set_pattern('')
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
@ -393,11 +400,10 @@ def test_url_completion_delete_bookmark(qtmodeltester, bookmarks,
|
||||
assert len_before == len(bookmarks.marks) + 1
|
||||
|
||||
|
||||
def test_url_completion_delete_quickmark(qtmodeltester,
|
||||
quickmarks, web_history, bookmarks,
|
||||
qtbot):
|
||||
def test_url_completion_delete_quickmark(qtmodeltester, info, qtbot,
|
||||
quickmarks, web_history, bookmarks):
|
||||
"""Test deleting a bookmark from the url completion model."""
|
||||
model = urlmodel.url()
|
||||
model = urlmodel.url(info=info)
|
||||
model.set_pattern('')
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
@ -416,11 +422,11 @@ def test_url_completion_delete_quickmark(qtmodeltester,
|
||||
assert len_before == len(quickmarks.marks) + 1
|
||||
|
||||
|
||||
def test_url_completion_delete_history(qtmodeltester,
|
||||
def test_url_completion_delete_history(qtmodeltester, info,
|
||||
web_history_populated,
|
||||
quickmarks, bookmarks):
|
||||
"""Test deleting a history entry."""
|
||||
model = urlmodel.url()
|
||||
model = urlmodel.url(info=info)
|
||||
model.set_pattern('')
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
@ -437,11 +443,11 @@ def test_url_completion_delete_history(qtmodeltester,
|
||||
assert 'https://python.org' not in web_history_populated
|
||||
|
||||
|
||||
def test_url_completion_zero_limit(config_stub, web_history, quickmarks,
|
||||
def test_url_completion_zero_limit(config_stub, web_history, quickmarks, info,
|
||||
bookmarks):
|
||||
"""Make sure there's no history if the limit was set to zero."""
|
||||
config_stub.val.completion.web_history_max_items = 0
|
||||
model = urlmodel.url()
|
||||
model = urlmodel.url(info=info)
|
||||
model.set_pattern('')
|
||||
category = model.index(2, 0) # "History" normally
|
||||
assert model.data(category) is None
|
||||
@ -518,8 +524,8 @@ def test_tab_completion_delete(qtmodeltester, fake_web_tab, app_stub,
|
||||
|
||||
|
||||
def test_setting_option_completion(qtmodeltester, config_stub,
|
||||
configdata_stub):
|
||||
model = configmodel.option()
|
||||
configdata_stub, info):
|
||||
model = configmodel.option(info=info)
|
||||
model.set_pattern('')
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
@ -536,7 +542,7 @@ def test_setting_option_completion(qtmodeltester, config_stub,
|
||||
|
||||
|
||||
def test_bind_completion(qtmodeltester, cmdutils_stub, config_stub,
|
||||
key_config_stub, configdata_stub):
|
||||
key_config_stub, configdata_stub, info):
|
||||
"""Test the results of keybinding command completion.
|
||||
|
||||
Validates that:
|
||||
@ -545,7 +551,7 @@ def test_bind_completion(qtmodeltester, cmdutils_stub, config_stub,
|
||||
- the binding (if any) is shown in the misc column
|
||||
- aliases are included
|
||||
"""
|
||||
model = configmodel.bind('ZQ')
|
||||
model = configmodel.bind('ZQ', info=info)
|
||||
model.set_pattern('')
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
@ -563,7 +569,7 @@ def test_bind_completion(qtmodeltester, cmdutils_stub, config_stub,
|
||||
})
|
||||
|
||||
|
||||
def test_url_completion_benchmark(benchmark,
|
||||
def test_url_completion_benchmark(benchmark, info,
|
||||
quickmark_manager_stub,
|
||||
bookmark_manager_stub,
|
||||
web_history):
|
||||
@ -586,7 +592,7 @@ def test_url_completion_benchmark(benchmark,
|
||||
for i in range(1000)])
|
||||
|
||||
def bench():
|
||||
model = urlmodel.url()
|
||||
model = urlmodel.url(info=info)
|
||||
model.set_pattern('')
|
||||
model.set_pattern('e')
|
||||
model.set_pattern('ex')
|
||||
|
Loading…
Reference in New Issue
Block a user