diff --git a/qutebrowser/completion/completer.py b/qutebrowser/completion/completer.py
index 96d937829..21af74da5 100644
--- a/qutebrowser/completion/completer.py
+++ b/qutebrowser/completion/completer.py
@@ -72,20 +72,7 @@ class Completer(QObject):
Return:
A completion model or None.
"""
- if completion == usertypes.Completion.option:
- section = pos_args[0]
- model = instances.get(completion).get(section)
- elif completion == usertypes.Completion.value:
- section = pos_args[0]
- option = pos_args[1]
- try:
- model = instances.get(completion)[section][option]
- except KeyError:
- # No completion model for this section/option.
- model = None
- else:
- model = instances.get(completion)
-
+ model = instances.get(completion)(*pos_args)
if model is None:
return None
else:
@@ -109,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 = instances.get(usertypes.Completion.command)()
return sortfilter.CompletionFilterModel(source=model, parent=self)
try:
cmd = cmdutils.cmd_dict[before_cursor[0]]
diff --git a/qutebrowser/completion/completionwidget.py b/qutebrowser/completion/completionwidget.py
index 490fcd6c0..f12027340 100644
--- a/qutebrowser/completion/completionwidget.py
+++ b/qutebrowser/completion/completionwidget.py
@@ -28,7 +28,6 @@ from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QItemSelectionModel, QSize
from qutebrowser.config import config, style
from qutebrowser.completion import completiondelegate
-from qutebrowser.completion.models import base
from qutebrowser.utils import utils, usertypes, objreg
from qutebrowser.commands import cmdexc, cmdutils
@@ -112,7 +111,7 @@ class CompletionView(QTreeView):
# objreg.get('config').changed.connect(self.init_command_completion)
objreg.get('config').changed.connect(self._on_config_changed)
- self._column_widths = base.BaseCompletionModel.COLUMN_WIDTHS
+ self._column_widths = (30, 70, 0)
self._active = False
self._delegate = completiondelegate.CompletionItemDelegate(self)
@@ -300,7 +299,7 @@ class CompletionView(QTreeView):
if pattern is not None:
model.set_pattern(pattern)
- self._column_widths = model.srcmodel.COLUMN_WIDTHS
+ self._column_widths = model.srcmodel.column_widths
self._resize_columns()
self._maybe_update_geometry()
diff --git a/qutebrowser/completion/models/base.py b/qutebrowser/completion/models/base.py
index b1cad276a..43f3a1b48 100644
--- a/qutebrowser/completion/models/base.py
+++ b/qutebrowser/completion/models/base.py
@@ -33,26 +33,27 @@ Role = usertypes.enum('Role', ['sort', 'userdata'], start=Qt.UserRole,
is_int=True)
-class BaseCompletionModel(QStandardItemModel):
+class CompletionModel(QStandardItemModel):
"""A simple QStandardItemModel adopted for completions.
Used for showing completions later in the CompletionView. Supports setting
marks and adding new categories/items easily.
- Class Attributes:
- COLUMN_WIDTHS: The width percentages of the columns used in the
- completion view.
- DUMB_SORT: the dumb sorting used by the model
+ Attributes:
+ column_widths: The width percentages of the columns used in the
+ completion view.
+ dumb_sort: the dumb sorting used by the model
"""
- COLUMN_WIDTHS = (30, 70, 0)
- DUMB_SORT = None
-
- def __init__(self, parent=None):
+ def __init__(self, dumb_sort=None, column_widths=(30, 70, 0),
+ columns_to_filter=None, delete_cur_item=None, parent=None):
super().__init__(parent)
self.setColumnCount(3)
- self.columns_to_filter = [0]
+ self.columns_to_filter = columns_to_filter or [0]
+ self.dumb_sort = dumb_sort
+ self.column_widths = column_widths
+ self.delete_cur_item = delete_cur_item
def new_category(self, name, sort=None):
"""Add a new category to the model.
@@ -103,10 +104,6 @@ class BaseCompletionModel(QStandardItemModel):
nameitem.setData(userdata, Role.userdata)
return nameitem, descitem, miscitem
- def delete_cur_item(self, completion):
- """Delete the selected item."""
- raise NotImplementedError
-
def flags(self, index):
"""Return the item flags for index.
diff --git a/qutebrowser/completion/models/configmodel.py b/qutebrowser/completion/models/configmodel.py
index c9e9850d1..5ed2a47d0 100644
--- a/qutebrowser/completion/models/configmodel.py
+++ b/qutebrowser/completion/models/configmodel.py
@@ -17,142 +17,75 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see .
-"""CompletionModels for the config."""
+"""Functions that return config-related completion models."""
-from PyQt5.QtCore import pyqtSlot, Qt
-
-from qutebrowser.config import config, configdata
-from qutebrowser.utils import log, qtutils, objreg
+from qutebrowser.config import config, configdata, configexc
from qutebrowser.completion.models import base
-class SettingSectionCompletionModel(base.BaseCompletionModel):
-
+def section():
"""A CompletionModel filled with settings sections."""
-
- # https://github.com/qutebrowser/qutebrowser/issues/545
- # pylint: disable=abstract-method
-
- COLUMN_WIDTHS = (20, 70, 10)
-
- def __init__(self, parent=None):
- super().__init__(parent)
- cat = self.new_category("Sections")
- for name in configdata.DATA:
- desc = configdata.SECTION_DESC[name].splitlines()[0].strip()
- self.new_item(cat, name, desc)
+ model = base.CompletionModel(column_widths=(20, 70, 10))
+ cat = model.new_category("Sections")
+ for name in configdata.DATA:
+ desc = configdata.SECTION_DESC[name].splitlines()[0].strip()
+ model.new_item(cat, name, desc)
+ return model
-class SettingOptionCompletionModel(base.BaseCompletionModel):
-
+def option(sectname):
"""A CompletionModel filled with settings and their descriptions.
- Attributes:
- _misc_items: A dict of the misc. column items which will be set later.
- _section: The config section this model shows.
+ Args:
+ sectname: The name of the config section this model shows.
"""
-
- # https://github.com/qutebrowser/qutebrowser/issues/545
- # pylint: disable=abstract-method
-
- COLUMN_WIDTHS = (20, 70, 10)
-
- def __init__(self, section, parent=None):
- super().__init__(parent)
- cat = self.new_category(section)
- sectdata = configdata.DATA[section]
- self._misc_items = {}
- self._section = section
- objreg.get('config').changed.connect(self.update_misc_column)
- for name in sectdata:
- try:
- desc = sectdata.descriptions[name]
- except (KeyError, AttributeError):
- # Some stuff (especially ValueList items) don't have a
- # description.
- desc = ""
- else:
- desc = desc.splitlines()[0]
- value = config.get(section, name, raw=True)
- _valitem, _descitem, miscitem = self.new_item(cat, name, desc,
- value)
- self._misc_items[name] = miscitem
-
- @pyqtSlot(str, str)
- def update_misc_column(self, section, option):
- """Update misc column when config changed."""
- if section != self._section:
- return
+ model = base.CompletionModel(column_widths=(20, 70, 10))
+ cat = model.new_category(sectname)
+ try:
+ sectdata = configdata.DATA[sectname]
+ except KeyError:
+ return None
+ for name in sectdata:
try:
- item = self._misc_items[option]
- except KeyError:
- log.completion.debug("Couldn't get item {}.{} from model!".format(
- section, option))
- # changed before init
- return
- val = config.get(section, option, raw=True)
- idx = item.index()
- qtutils.ensure_valid(idx)
- ok = self.setData(idx, val, Qt.DisplayRole)
- if not ok:
- raise ValueError("Setting data failed! (section: {}, option: {}, "
- "value: {})".format(section, option, val))
+ desc = sectdata.descriptions[name]
+ except (KeyError, AttributeError):
+ # Some stuff (especially ValueList items) don't have a
+ # description.
+ desc = ""
+ else:
+ desc = desc.splitlines()[0]
+ val = config.get(sectname, name, raw=True)
+ model.new_item(cat, name, desc, val)
+ return model
-class SettingValueCompletionModel(base.BaseCompletionModel):
-
+def value(sectname, optname):
"""A CompletionModel filled with setting values.
- Attributes:
- _section: The config section this model shows.
- _option: The config option this model shows.
+ Args:
+ sectname: The name of the config section this model shows.
+ optname: The name of the config option this model shows.
"""
-
- # https://github.com/qutebrowser/qutebrowser/issues/545
- # pylint: disable=abstract-method
-
- COLUMN_WIDTHS = (20, 70, 10)
-
- def __init__(self, section, option, parent=None):
- super().__init__(parent)
- self._section = section
- self._option = option
- objreg.get('config').changed.connect(self.update_current_value)
- cur_cat = self.new_category("Current/Default", sort=0)
- value = config.get(section, option, raw=True)
- if not value:
- value = '""'
- self.cur_item, _descitem, _miscitem = self.new_item(cur_cat, value,
- "Current value")
- default_value = configdata.DATA[section][option].default()
- if not default_value:
- default_value = '""'
- self.new_item(cur_cat, default_value, "Default value")
- if hasattr(configdata.DATA[section], 'valtype'):
- # Same type for all values (ValueList)
- vals = configdata.DATA[section].valtype.complete()
- else:
- if option is None:
- raise ValueError("option may only be None for ValueList "
- "sections, but {} is not!".format(section))
- # Different type for each value (KeyValue)
- vals = configdata.DATA[section][option].typ.complete()
- if vals is not None:
- cat = self.new_category("Completions", sort=1)
- for (val, desc) in vals:
- self.new_item(cat, val, desc)
-
- @pyqtSlot(str, str)
- def update_current_value(self, section, option):
- """Update current value when config changed."""
- if (section, option) != (self._section, self._option):
- return
- value = config.get(section, option, raw=True)
- if not value:
- value = '""'
- idx = self.cur_item.index()
- qtutils.ensure_valid(idx)
- ok = self.setData(idx, value, Qt.DisplayRole)
- if not ok:
- raise ValueError("Setting data failed! (section: {}, option: {}, "
- "value: {})".format(section, option, value))
+ model = base.CompletionModel(column_widths=(20, 70, 10))
+ cur_cat = model.new_category("Current/Default", sort=0)
+ try:
+ val = config.get(sectname, optname, raw=True) or '""'
+ except (configexc.NoSectionError, configexc.NoOptionError):
+ return None
+ model.new_item(cur_cat, val, "Current value")
+ default_value = configdata.DATA[sectname][optname].default() or '""'
+ model.new_item(cur_cat, default_value, "Default value")
+ if hasattr(configdata.DATA[sectname], 'valtype'):
+ # Same type for all values (ValueList)
+ vals = configdata.DATA[sectname].valtype.complete()
+ else:
+ if optname is None:
+ raise ValueError("optname may only be None for ValueList "
+ "sections, but {} is not!".format(sectname))
+ # Different type for each value (KeyValue)
+ vals = configdata.DATA[sectname][optname].typ.complete()
+ if vals is not None:
+ cat = model.new_category("Completions", sort=1)
+ for (val, desc) in vals:
+ model.new_item(cat, val, desc)
+ return model
diff --git a/qutebrowser/completion/models/instances.py b/qutebrowser/completion/models/instances.py
index f7eaaca86..ce109ae7a 100644
--- a/qutebrowser/completion/models/instances.py
+++ b/qutebrowser/completion/models/instances.py
@@ -17,180 +17,37 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see .
-"""Global instances of the completion models.
-
-Module attributes:
- _instances: A dict of available completions.
- INITIALIZERS: A {usertypes.Completion: callable} dict of functions to
- initialize completions.
-"""
-
-import functools
+"""Global instances of the completion models."""
+from qutebrowser.utils import usertypes
from qutebrowser.completion.models import miscmodels, urlmodel, configmodel
-from qutebrowser.utils import objreg, usertypes, log, debug
-from qutebrowser.config import configdata, config
-
-
-_instances = {}
-
-
-def _init_command_completion():
- """Initialize the command completion model."""
- log.completion.debug("Initializing command completion.")
- model = miscmodels.CommandCompletionModel()
- _instances[usertypes.Completion.command] = model
-
-
-def _init_helptopic_completion():
- """Initialize the helptopic completion model."""
- log.completion.debug("Initializing helptopic completion.")
- model = miscmodels.HelpCompletionModel()
- _instances[usertypes.Completion.helptopic] = model
-
-
-def _init_url_completion():
- """Initialize the URL completion model."""
- log.completion.debug("Initializing URL completion.")
- with debug.log_time(log.completion, 'URL completion init'):
- model = urlmodel.UrlCompletionModel()
- _instances[usertypes.Completion.url] = model
-
-
-def _init_tab_completion():
- """Initialize the tab completion model."""
- log.completion.debug("Initializing tab completion.")
- with debug.log_time(log.completion, 'tab completion init'):
- model = miscmodels.TabCompletionModel()
- _instances[usertypes.Completion.tab] = model
-
-
-def _init_setting_completions():
- """Initialize setting completion models."""
- log.completion.debug("Initializing setting completion.")
- _instances[usertypes.Completion.section] = (
- configmodel.SettingSectionCompletionModel())
- _instances[usertypes.Completion.option] = {}
- _instances[usertypes.Completion.value] = {}
- for sectname in configdata.DATA:
- opt_model = configmodel.SettingOptionCompletionModel(sectname)
- _instances[usertypes.Completion.option][sectname] = opt_model
- _instances[usertypes.Completion.value][sectname] = {}
- for opt in configdata.DATA[sectname]:
- val_model = configmodel.SettingValueCompletionModel(sectname, opt)
- _instances[usertypes.Completion.value][sectname][opt] = val_model
-
-
-def init_quickmark_completions():
- """Initialize quickmark completion models."""
- log.completion.debug("Initializing quickmark completion.")
- try:
- _instances[usertypes.Completion.quickmark_by_name].deleteLater()
- except KeyError:
- pass
- model = miscmodels.QuickmarkCompletionModel()
- _instances[usertypes.Completion.quickmark_by_name] = model
-
-
-def init_bookmark_completions():
- """Initialize bookmark completion models."""
- log.completion.debug("Initializing bookmark completion.")
- try:
- _instances[usertypes.Completion.bookmark_by_url].deleteLater()
- except KeyError:
- pass
- model = miscmodels.BookmarkCompletionModel()
- _instances[usertypes.Completion.bookmark_by_url] = model
-
-
-def init_session_completion():
- """Initialize session completion model."""
- log.completion.debug("Initializing session completion.")
- try:
- _instances[usertypes.Completion.sessions].deleteLater()
- except KeyError:
- pass
- model = miscmodels.SessionCompletionModel()
- _instances[usertypes.Completion.sessions] = model
-
-
-def _init_bind_completion():
- """Initialize the command completion model."""
- log.completion.debug("Initializing bind completion.")
- model = miscmodels.BindCompletionModel()
- _instances[usertypes.Completion.bind] = model
-
-
-INITIALIZERS = {
- usertypes.Completion.command: _init_command_completion,
- usertypes.Completion.helptopic: _init_helptopic_completion,
- usertypes.Completion.url: _init_url_completion,
- usertypes.Completion.tab: _init_tab_completion,
- usertypes.Completion.section: _init_setting_completions,
- usertypes.Completion.option: _init_setting_completions,
- usertypes.Completion.value: _init_setting_completions,
- usertypes.Completion.quickmark_by_name: init_quickmark_completions,
- usertypes.Completion.bookmark_by_url: init_bookmark_completions,
- usertypes.Completion.sessions: init_session_completion,
- usertypes.Completion.bind: _init_bind_completion,
-}
def get(completion):
"""Get a certain completion. Initializes the completion if needed."""
- try:
- return _instances[completion]
- except KeyError:
- if completion in INITIALIZERS:
- INITIALIZERS[completion]()
- return _instances[completion]
- else:
- raise
-
-
-def update(completions):
- """Update an already existing completion.
-
- Args:
- completions: An iterable of usertypes.Completions.
- """
- did_run = []
- for completion in completions:
- if completion in _instances:
- func = INITIALIZERS[completion]
- if func not in did_run:
- func()
- did_run.append(func)
-
-
-@config.change_filter('aliases', function=True)
-def _update_aliases():
- """Update completions that include command aliases."""
- update([usertypes.Completion.command])
+ 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():
- """Initialize completions. Note this only connects signals."""
- quickmark_manager = objreg.get('quickmark-manager')
- quickmark_manager.changed.connect(
- functools.partial(update, [usertypes.Completion.quickmark_by_name]))
-
- bookmark_manager = objreg.get('bookmark-manager')
- bookmark_manager.changed.connect(
- functools.partial(update, [usertypes.Completion.bookmark_by_url]))
-
- session_manager = objreg.get('session-manager')
- session_manager.update_completion.connect(
- functools.partial(update, [usertypes.Completion.sessions]))
-
- history = objreg.get('web-history')
- history.async_read_done.connect(
- functools.partial(update, [usertypes.Completion.url]))
-
- keyconf = objreg.get('key-config')
- keyconf.changed.connect(
- functools.partial(update, [usertypes.Completion.command]))
- keyconf.changed.connect(
- functools.partial(update, [usertypes.Completion.bind]))
-
- objreg.get('config').changed.connect(_update_aliases)
+ pass
diff --git a/qutebrowser/completion/models/miscmodels.py b/qutebrowser/completion/models/miscmodels.py
index 5ab381c43..bcbb94177 100644
--- a/qutebrowser/completion/models/miscmodels.py
+++ b/qutebrowser/completion/models/miscmodels.py
@@ -17,252 +17,139 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see .
-"""Misc. CompletionModels."""
+"""Functions that return miscellaneous completion models."""
-from PyQt5.QtCore import Qt, QTimer, pyqtSlot
+from PyQt5.QtCore import Qt
-from qutebrowser.browser import browsertab
from qutebrowser.config import config, configdata
from qutebrowser.utils import objreg, log, qtutils
from qutebrowser.commands import cmdutils
from qutebrowser.completion.models import base
-class CommandCompletionModel(base.BaseCompletionModel):
-
+def command():
"""A CompletionModel filled with non-hidden commands and descriptions."""
-
- # https://github.com/qutebrowser/qutebrowser/issues/545
- # pylint: disable=abstract-method
-
- COLUMN_WIDTHS = (20, 60, 20)
-
- def __init__(self, parent=None):
- super().__init__(parent)
- cmdlist = _get_cmd_completions(include_aliases=True,
- include_hidden=False)
- cat = self.new_category("Commands")
- for (name, desc, misc) in cmdlist:
- self.new_item(cat, name, desc, misc)
+ model = base.CompletionModel(column_widths=(20, 60, 20))
+ cmdlist = _get_cmd_completions(include_aliases=True, include_hidden=False)
+ cat = model.new_category("Commands")
+ for (name, desc, misc) in cmdlist:
+ model.new_item(cat, name, desc, misc)
+ return model
-class HelpCompletionModel(base.BaseCompletionModel):
-
+def helptopic():
"""A CompletionModel filled with help topics."""
+ model = base.CompletionModel()
- # https://github.com/qutebrowser/qutebrowser/issues/545
- # pylint: disable=abstract-method
+ cmdlist = _get_cmd_completions(include_aliases=False, include_hidden=True,
+ prefix=':')
+ cat = model.new_category("Commands")
+ for (name, desc, misc) in cmdlist:
+ model.new_item(cat, name, desc, misc)
- COLUMN_WIDTHS = (20, 60, 20)
-
- def __init__(self, parent=None):
- super().__init__(parent)
- self._init_commands()
- self._init_settings()
-
- def _init_commands(self):
- """Fill completion with :command entries."""
- cmdlist = _get_cmd_completions(include_aliases=False,
- include_hidden=True, prefix=':')
- cat = self.new_category("Commands")
- for (name, desc, misc) in cmdlist:
- self.new_item(cat, name, desc, misc)
-
- def _init_settings(self):
- """Fill completion with section->option entries."""
- cat = self.new_category("Settings")
- for sectname, sectdata in configdata.DATA.items():
- for optname in sectdata:
- try:
- desc = sectdata.descriptions[optname]
- except (KeyError, AttributeError):
- # Some stuff (especially ValueList items) don't have a
- # description.
- desc = ""
- else:
- desc = desc.splitlines()[0]
- name = '{}->{}'.format(sectname, optname)
- self.new_item(cat, name, desc)
+ cat = model.new_category("Settings")
+ for sectname, sectdata in configdata.DATA.items():
+ for optname in sectdata:
+ try:
+ desc = sectdata.descriptions[optname]
+ except (KeyError, AttributeError):
+ # Some stuff (especially ValueList items) don't have a
+ # description.
+ desc = ""
+ else:
+ desc = desc.splitlines()[0]
+ name = '{}->{}'.format(sectname, optname)
+ model.new_item(cat, name, desc)
+ return model
-class QuickmarkCompletionModel(base.BaseCompletionModel):
-
+def quickmark():
"""A CompletionModel filled with all quickmarks."""
-
- # https://github.com/qutebrowser/qutebrowser/issues/545
- # pylint: disable=abstract-method
-
- def __init__(self, parent=None):
- super().__init__(parent)
- cat = self.new_category("Quickmarks")
- quickmarks = objreg.get('quickmark-manager').marks.items()
- for qm_name, qm_url in quickmarks:
- self.new_item(cat, qm_name, qm_url)
+ model = base.CompletionModel()
+ cat = model.new_category("Quickmarks")
+ quickmarks = objreg.get('quickmark-manager').marks.items()
+ for qm_name, qm_url in quickmarks:
+ model.new_item(cat, qm_name, qm_url)
+ return model
-class BookmarkCompletionModel(base.BaseCompletionModel):
-
+def bookmark():
"""A CompletionModel filled with all bookmarks."""
-
- # https://github.com/qutebrowser/qutebrowser/issues/545
- # pylint: disable=abstract-method
-
- def __init__(self, parent=None):
- super().__init__(parent)
- cat = self.new_category("Bookmarks")
- bookmarks = objreg.get('bookmark-manager').marks.items()
- for bm_url, bm_title in bookmarks:
- self.new_item(cat, bm_url, bm_title)
+ model = base.CompletionModel()
+ cat = model.new_category("Bookmarks")
+ bookmarks = objreg.get('bookmark-manager').marks.items()
+ for bm_url, bm_title in bookmarks:
+ model.new_item(cat, bm_url, bm_title)
+ return model
-class SessionCompletionModel(base.BaseCompletionModel):
-
+def session():
"""A CompletionModel filled with session names."""
-
- # https://github.com/qutebrowser/qutebrowser/issues/545
- # pylint: disable=abstract-method
-
- def __init__(self, parent=None):
- super().__init__(parent)
- cat = self.new_category("Sessions")
- try:
- for name in objreg.get('session-manager').list_sessions():
- if not name.startswith('_'):
- self.new_item(cat, name)
- except OSError:
- log.completion.exception("Failed to list sessions!")
+ model = base.CompletionModel()
+ cat = model.new_category("Sessions")
+ try:
+ for name in objreg.get('session-manager').list_sessions():
+ if not name.startswith('_'):
+ model.new_item(cat, name)
+ except OSError:
+ log.completion.exception("Failed to list sessions!")
+ return model
-class TabCompletionModel(base.BaseCompletionModel):
-
+def buffer():
"""A model to complete on open tabs across all windows.
Used for switching the buffer command.
"""
+ idx_column = 0
+ url_column = 1
+ text_column = 2
- IDX_COLUMN = 0
- URL_COLUMN = 1
- TEXT_COLUMN = 2
-
- COLUMN_WIDTHS = (6, 40, 54)
- DUMB_SORT = Qt.DescendingOrder
-
- def __init__(self, parent=None):
- super().__init__(parent)
-
- self.columns_to_filter = [self.IDX_COLUMN, self.URL_COLUMN,
- self.TEXT_COLUMN]
-
- for win_id in objreg.window_registry:
- tabbed_browser = objreg.get('tabbed-browser', scope='window',
- window=win_id)
- for i in range(tabbed_browser.count()):
- tab = tabbed_browser.widget(i)
- tab.url_changed.connect(self.rebuild)
- tab.title_changed.connect(self.rebuild)
- tab.shutting_down.connect(self.delayed_rebuild)
- tabbed_browser.new_tab.connect(self.on_new_tab)
- tabbed_browser.tabBar().tabMoved.connect(self.rebuild)
- objreg.get("app").new_window.connect(self.on_new_window)
- self.rebuild()
-
- def on_new_window(self, window):
- """Add hooks to new windows."""
- window.tabbed_browser.new_tab.connect(self.on_new_tab)
-
- @pyqtSlot(browsertab.AbstractTab)
- def on_new_tab(self, tab):
- """Add hooks to new tabs."""
- tab.url_changed.connect(self.rebuild)
- tab.title_changed.connect(self.rebuild)
- tab.shutting_down.connect(self.delayed_rebuild)
- self.rebuild()
-
- @pyqtSlot()
- def delayed_rebuild(self):
- """Fire a rebuild indirectly so widgets get a chance to update."""
- QTimer.singleShot(0, self.rebuild)
-
- @pyqtSlot()
- def rebuild(self):
- """Rebuild completion model from current tabs.
-
- Very lazy method of keeping the model up to date. We could connect to
- signals for new tab, tab url/title changed, tab close, tab moved and
- make sure we handled background loads too ... but iterating over a
- few/few dozen/few hundred tabs doesn't take very long at all.
- """
- window_count = 0
- for win_id in objreg.window_registry:
- tabbed_browser = objreg.get('tabbed-browser', scope='window',
- window=win_id)
- if not tabbed_browser.shutting_down:
- window_count += 1
-
- if window_count < self.rowCount():
- self.removeRows(window_count, self.rowCount() - window_count)
-
- for i, win_id in enumerate(objreg.window_registry):
- tabbed_browser = objreg.get('tabbed-browser', scope='window',
- window=win_id)
- if tabbed_browser.shutting_down:
- continue
- if i >= self.rowCount():
- c = self.new_category("{}".format(win_id))
- else:
- c = self.item(i, 0)
- c.setData("{}".format(win_id), Qt.DisplayRole)
- if tabbed_browser.count() < c.rowCount():
- c.removeRows(tabbed_browser.count(),
- c.rowCount() - tabbed_browser.count())
- for idx in range(tabbed_browser.count()):
- tab = tabbed_browser.widget(idx)
- if idx >= c.rowCount():
- self.new_item(c, "{}/{}".format(win_id, idx + 1),
- tab.url().toDisplayString(),
- tabbed_browser.page_title(idx))
- else:
- c.child(idx, 0).setData("{}/{}".format(win_id, idx + 1),
- Qt.DisplayRole)
- c.child(idx, 1).setData(tab.url().toDisplayString(),
- Qt.DisplayRole)
- c.child(idx, 2).setData(tabbed_browser.page_title(idx),
- Qt.DisplayRole)
-
- def delete_cur_item(self, completion):
- """Delete the selected item.
-
- Args:
- completion: The Completion object to use.
- """
+ def delete_buffer(completion):
+ """Close the selected tab."""
index = completion.currentIndex()
qtutils.ensure_valid(index)
category = index.parent()
qtutils.ensure_valid(category)
- index = category.child(index.row(), self.IDX_COLUMN)
+ index = category.child(index.row(), idx_column)
win_id, tab_index = index.data().split('/')
-
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=int(win_id))
tabbed_browser.on_tab_close_requested(int(tab_index) - 1)
+ model = base.CompletionModel(
+ column_widths=(6, 40, 54),
+ dumb_sort=Qt.DescendingOrder,
+ delete_cur_item=delete_buffer,
+ columns_to_filter=[idx_column, url_column, text_column])
-class BindCompletionModel(base.BaseCompletionModel):
+ for win_id in objreg.window_registry:
+ tabbed_browser = objreg.get('tabbed-browser', scope='window',
+ window=win_id)
+ if tabbed_browser.shutting_down:
+ continue
+ c = model.new_category("{}".format(win_id))
+ for idx in range(tabbed_browser.count()):
+ tab = tabbed_browser.widget(idx)
+ model.new_item(c, "{}/{}".format(win_id, idx + 1),
+ tab.url().toDisplayString(),
+ tabbed_browser.page_title(idx))
+ return model
- """A CompletionModel filled with all bindable commands and descriptions."""
- # https://github.com/qutebrowser/qutebrowser/issues/545
- # pylint: disable=abstract-method
+def bind(_):
+ """A CompletionModel filled with all bindable commands and descriptions.
- COLUMN_WIDTHS = (20, 60, 20)
-
- def __init__(self, parent=None):
- super().__init__(parent)
- cmdlist = _get_cmd_completions(include_hidden=True,
- include_aliases=True)
- cat = self.new_category("Commands")
- for (name, desc, misc) in cmdlist:
- self.new_item(cat, name, desc, misc)
+ Args:
+ _: the key being bound.
+ """
+ # TODO: offer a 'Current binding' completion based on the key.
+ model = base.CompletionModel(column_widths=(20, 60, 20))
+ cmdlist = _get_cmd_completions(include_hidden=True, include_aliases=True)
+ cat = model.new_category("Commands")
+ for (name, desc, misc) in cmdlist:
+ model.new_item(cat, name, desc, misc)
+ return model
def _get_cmd_completions(include_hidden, include_aliases, prefix=''):
diff --git a/qutebrowser/completion/models/sortfilter.py b/qutebrowser/completion/models/sortfilter.py
index e2db88b9e..92dd1b2a0 100644
--- a/qutebrowser/completion/models/sortfilter.py
+++ b/qutebrowser/completion/models/sortfilter.py
@@ -50,7 +50,7 @@ class CompletionFilterModel(QSortFilterProxyModel):
self.pattern = ''
self.pattern_re = None
- dumb_sort = self.srcmodel.DUMB_SORT
+ dumb_sort = self.srcmodel.dumb_sort
if dumb_sort is None:
# pylint: disable=invalid-name
self.lessThan = self.intelligentLessThan
diff --git a/qutebrowser/completion/models/urlmodel.py b/qutebrowser/completion/models/urlmodel.py
index 98f68c08c..d8b7f42a2 100644
--- a/qutebrowser/completion/models/urlmodel.py
+++ b/qutebrowser/completion/models/urlmodel.py
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see .
-"""CompletionModels for URLs."""
+"""Function to return the url completion model for the `open` command."""
import datetime
@@ -27,166 +27,172 @@ from qutebrowser.utils import objreg, utils, qtutils, log
from qutebrowser.completion.models import base
from qutebrowser.config import config
+_URL_COLUMN = 0
+_TEXT_COLUMN = 1
+_TIME_COLUMN = 2
+_model = None
+_history_cat = None
+_quickmark_cat = None
+_bookmark_cat = None
-class UrlCompletionModel(base.BaseCompletionModel):
- """A model which combines bookmarks, quickmarks and web history URLs.
+def _delete_url(completion):
+ index = completion.currentIndex()
+ qtutils.ensure_valid(index)
+ category = index.parent()
+ index = category.child(index.row(), _URL_COLUMN)
+ qtutils.ensure_valid(category)
+ if category.data() == 'Bookmarks':
+ bookmark_manager = objreg.get('bookmark-manager')
+ bookmark_manager.delete(index.data())
+ elif category.data() == 'Quickmarks':
+ quickmark_manager = objreg.get('quickmark-manager')
+ sibling = index.sibling(index.row(), _TEXT_COLUMN)
+ qtutils.ensure_valid(sibling)
+ name = sibling.data()
+ quickmark_manager.delete(name)
+
+
+def _remove_oldest_history():
+ """Remove the oldest history entry."""
+ _history_cat.removeRow(0)
+
+
+def _add_history_entry(entry):
+ """Add a new history entry to the completion."""
+ _model.new_item(_history_cat, entry.url.toDisplayString(),
+ entry.title, _fmt_atime(entry.atime),
+ sort=int(entry.atime), userdata=entry.url)
+
+ max_history = config.get('completion', 'web-history-max-items')
+ if max_history != -1 and _history_cat.rowCount() > max_history:
+ _remove_oldest_history()
+
+
+@config.change_filter('completion', 'timestamp-format')
+def _reformat_timestamps():
+ """Reformat the timestamps if the config option was changed."""
+ for i in range(_history_cat.rowCount()):
+ url_item = _history_cat.child(i, _URL_COLUMN)
+ atime_item = _history_cat.child(i, _TIME_COLUMN)
+ atime = url_item.data(base.Role.sort)
+ atime_item.setText(_fmt_atime(atime))
+
+
+@pyqtSlot(object)
+def _on_history_item_added(entry):
+ """Slot called when a new history item was added."""
+ for i in range(_history_cat.rowCount()):
+ url_item = _history_cat.child(i, _URL_COLUMN)
+ atime_item = _history_cat.child(i, _TIME_COLUMN)
+ title_item = _history_cat.child(i, _TEXT_COLUMN)
+ if url_item.data(base.Role.userdata) == entry.url:
+ atime_item.setText(_fmt_atime(entry.atime))
+ title_item.setText(entry.title)
+ url_item.setData(int(entry.atime), base.Role.sort)
+ break
+ else:
+ _add_history_entry(entry)
+
+
+@pyqtSlot()
+def _on_history_cleared():
+ _history_cat.removeRows(0, _history_cat.rowCount())
+
+
+def _remove_item(data, category, column):
+ """Helper function for on_quickmark_removed and on_bookmark_removed.
+
+ Args:
+ data: The item to search for.
+ category: The category to search in.
+ column: The column to use for matching.
+ """
+ for i in range(category.rowCount()):
+ item = category.child(i, column)
+ if item.data(Qt.DisplayRole) == data:
+ category.removeRow(i)
+ break
+
+
+@pyqtSlot(str)
+def _on_quickmark_removed(name):
+ """Called when a quickmark has been removed by the user.
+
+ Args:
+ name: The name of the quickmark which has been removed.
+ """
+ _remove_item(name, _quickmark_cat, _TEXT_COLUMN)
+
+
+@pyqtSlot(str)
+def _on_bookmark_removed(urltext):
+ """Called when a bookmark has been removed by the user.
+
+ Args:
+ urltext: The url of the bookmark which has been removed.
+ """
+ _remove_item(urltext, _bookmark_cat, _URL_COLUMN)
+
+
+def _fmt_atime(atime):
+ """Format an atime to a human-readable string."""
+ fmt = config.get('completion', 'timestamp-format')
+ if fmt is None:
+ return ''
+ try:
+ dt = datetime.datetime.fromtimestamp(atime)
+ except (ValueError, OSError, OverflowError):
+ # Different errors which can occur for too large values...
+ log.misc.error("Got invalid timestamp {}!".format(atime))
+ return '(invalid)'
+ else:
+ return dt.strftime(fmt)
+
+
+def _init():
+ global _model, _quickmark_cat, _bookmark_cat, _history_cat
+ _model = base.CompletionModel(column_widths=(40, 50, 10),
+ dumb_sort=Qt.DescendingOrder,
+ delete_cur_item=_delete_url,
+ columns_to_filter=[_URL_COLUMN,
+ _TEXT_COLUMN])
+ _quickmark_cat = _model.new_category("Quickmarks")
+ _bookmark_cat = _model.new_category("Bookmarks")
+ _history_cat = _model.new_category("History")
+
+ quickmark_manager = objreg.get('quickmark-manager')
+ quickmarks = quickmark_manager.marks.items()
+ for qm_name, qm_url in quickmarks:
+ _model.new_item(_quickmark_cat, qm_url, qm_name)
+ quickmark_manager.added.connect(
+ lambda name, url: _model.new_item(_quickmark_cat, url, name))
+ quickmark_manager.removed.connect(_on_quickmark_removed)
+
+ bookmark_manager = objreg.get('bookmark-manager')
+ bookmarks = bookmark_manager.marks.items()
+ for bm_url, bm_title in bookmarks:
+ _model.new_item(_bookmark_cat, bm_url, bm_title)
+ bookmark_manager.added.connect(
+ lambda name, url: _model.new_item(_bookmark_cat, url, name))
+ bookmark_manager.removed.connect(_on_bookmark_removed)
+
+ history = objreg.get('web-history')
+ max_history = config.get('completion', 'web-history-max-items')
+ for entry in utils.newest_slice(history, max_history):
+ if not entry.redirect:
+ _add_history_entry(entry)
+ history.add_completion_item.connect(_on_history_item_added)
+ history.cleared.connect(_on_history_cleared)
+
+ objreg.get('config').changed.connect(_reformat_timestamps)
+
+
+def url():
+ """A _model which combines bookmarks, quickmarks and web history URLs.
Used for the `open` command.
"""
-
- URL_COLUMN = 0
- TEXT_COLUMN = 1
- TIME_COLUMN = 2
-
- COLUMN_WIDTHS = (40, 50, 10)
- DUMB_SORT = Qt.DescendingOrder
-
- def __init__(self, parent=None):
- super().__init__(parent)
-
- self.columns_to_filter = [self.URL_COLUMN, self.TEXT_COLUMN]
-
- self._quickmark_cat = self.new_category("Quickmarks")
- self._bookmark_cat = self.new_category("Bookmarks")
- self._history_cat = self.new_category("History")
-
- quickmark_manager = objreg.get('quickmark-manager')
- quickmarks = quickmark_manager.marks.items()
- for qm_name, qm_url in quickmarks:
- self.new_item(self._quickmark_cat, qm_url, qm_name)
- quickmark_manager.added.connect(
- lambda name, url: self.new_item(self._quickmark_cat, url, name))
- quickmark_manager.removed.connect(self.on_quickmark_removed)
-
- bookmark_manager = objreg.get('bookmark-manager')
- bookmarks = bookmark_manager.marks.items()
- for bm_url, bm_title in bookmarks:
- self.new_item(self._bookmark_cat, bm_url, bm_title)
- bookmark_manager.added.connect(
- lambda name, url: self.new_item(self._bookmark_cat, url, name))
- bookmark_manager.removed.connect(self.on_bookmark_removed)
-
- self._history = objreg.get('web-history')
- self._max_history = config.get('completion', 'web-history-max-items')
- history = utils.newest_slice(self._history, self._max_history)
- for entry in history:
- if not entry.redirect:
- self._add_history_entry(entry)
- self._history.add_completion_item.connect(self.on_history_item_added)
- self._history.cleared.connect(self.on_history_cleared)
-
- objreg.get('config').changed.connect(self.reformat_timestamps)
-
- def _fmt_atime(self, atime):
- """Format an atime to a human-readable string."""
- fmt = config.get('completion', 'timestamp-format')
- if fmt is None:
- return ''
- try:
- dt = datetime.datetime.fromtimestamp(atime)
- except (ValueError, OSError, OverflowError):
- # Different errors which can occur for too large values...
- log.misc.error("Got invalid timestamp {}!".format(atime))
- return '(invalid)'
- else:
- return dt.strftime(fmt)
-
- def _remove_oldest_history(self):
- """Remove the oldest history entry."""
- self._history_cat.removeRow(0)
-
- def _add_history_entry(self, entry):
- """Add a new history entry to the completion."""
- self.new_item(self._history_cat, entry.url.toDisplayString(),
- entry.title,
- self._fmt_atime(entry.atime), sort=int(entry.atime),
- userdata=entry.url)
-
- if (self._max_history != -1 and
- self._history_cat.rowCount() > self._max_history):
- self._remove_oldest_history()
-
- @config.change_filter('completion', 'timestamp-format')
- def reformat_timestamps(self):
- """Reformat the timestamps if the config option was changed."""
- for i in range(self._history_cat.rowCount()):
- url_item = self._history_cat.child(i, self.URL_COLUMN)
- atime_item = self._history_cat.child(i, self.TIME_COLUMN)
- atime = url_item.data(base.Role.sort)
- atime_item.setText(self._fmt_atime(atime))
-
- @pyqtSlot(object)
- def on_history_item_added(self, entry):
- """Slot called when a new history item was added."""
- for i in range(self._history_cat.rowCount()):
- url_item = self._history_cat.child(i, self.URL_COLUMN)
- atime_item = self._history_cat.child(i, self.TIME_COLUMN)
- title_item = self._history_cat.child(i, self.TEXT_COLUMN)
- url = url_item.data(base.Role.userdata)
- if url == entry.url:
- atime_item.setText(self._fmt_atime(entry.atime))
- title_item.setText(entry.title)
- url_item.setData(int(entry.atime), base.Role.sort)
- break
- else:
- self._add_history_entry(entry)
-
- @pyqtSlot()
- def on_history_cleared(self):
- self._history_cat.removeRows(0, self._history_cat.rowCount())
-
- def _remove_item(self, data, category, column):
- """Helper function for on_quickmark_removed and on_bookmark_removed.
-
- Args:
- data: The item to search for.
- category: The category to search in.
- column: The column to use for matching.
- """
- for i in range(category.rowCount()):
- item = category.child(i, column)
- if item.data(Qt.DisplayRole) == data:
- category.removeRow(i)
- break
-
- @pyqtSlot(str)
- def on_quickmark_removed(self, name):
- """Called when a quickmark has been removed by the user.
-
- Args:
- name: The name of the quickmark which has been removed.
- """
- self._remove_item(name, self._quickmark_cat, self.TEXT_COLUMN)
-
- @pyqtSlot(str)
- def on_bookmark_removed(self, url):
- """Called when a bookmark has been removed by the user.
-
- Args:
- url: The url of the bookmark which has been removed.
- """
- self._remove_item(url, self._bookmark_cat, self.URL_COLUMN)
-
- def delete_cur_item(self, completion):
- """Delete the selected item.
-
- Args:
- completion: The Completion object to use.
- """
- index = completion.currentIndex()
- qtutils.ensure_valid(index)
- category = index.parent()
- index = category.child(index.row(), self.URL_COLUMN)
- url = index.data()
- qtutils.ensure_valid(category)
-
- if category.data() == 'Bookmarks':
- bookmark_manager = objreg.get('bookmark-manager')
- bookmark_manager.delete(url)
- elif category.data() == 'Quickmarks':
- quickmark_manager = objreg.get('quickmark-manager')
- sibling = index.sibling(index.row(), self.TEXT_COLUMN)
- qtutils.ensure_valid(sibling)
- name = sibling.data()
- quickmark_manager.delete(name)
+ if not _model:
+ _init()
+ return _model
diff --git a/tests/unit/completion/test_column_widths.py b/tests/unit/completion/test_column_widths.py
deleted file mode 100644
index 21456ed37..000000000
--- a/tests/unit/completion/test_column_widths.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
-
-# Copyright 2015-2017 Alexander Cogneau
-#
-# 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 .
-
-"""Tests for qutebrowser.completion.models column widths."""
-
-import pytest
-
-from qutebrowser.completion.models.base import BaseCompletionModel
-from qutebrowser.completion.models.configmodel import (
- SettingOptionCompletionModel, SettingSectionCompletionModel,
- SettingValueCompletionModel)
-from qutebrowser.completion.models.miscmodels import (
- CommandCompletionModel, HelpCompletionModel, QuickmarkCompletionModel,
- BookmarkCompletionModel, SessionCompletionModel)
-from qutebrowser.completion.models.urlmodel import UrlCompletionModel
-
-
-CLASSES = [BaseCompletionModel, SettingOptionCompletionModel,
- SettingOptionCompletionModel, SettingSectionCompletionModel,
- SettingValueCompletionModel, CommandCompletionModel,
- HelpCompletionModel, QuickmarkCompletionModel,
- BookmarkCompletionModel, SessionCompletionModel, UrlCompletionModel]
-
-
-@pytest.mark.parametrize("model", CLASSES)
-def test_list_size(model):
- """Test if there are 3 items in the COLUMN_WIDTHS property."""
- assert len(model.COLUMN_WIDTHS) == 3
-
-
-@pytest.mark.parametrize("model", CLASSES)
-def test_column_width_sum(model):
- """Test if the sum of the widths asserts to 100."""
- assert sum(model.COLUMN_WIDTHS) == 100
diff --git a/tests/unit/completion/test_completer.py b/tests/unit/completion/test_completer.py
index d18e6c125..937107f75 100644
--- a/tests/unit/completion/test_completer.py
+++ b/tests/unit/completion/test_completer.py
@@ -34,11 +34,11 @@ class FakeCompletionModel(QStandardItemModel):
"""Stub for a completion model."""
- DUMB_SORT = None
-
- def __init__(self, kind, parent=None):
+ def __init__(self, kind, *pos_args, parent=None):
super().__init__(parent)
self.kind = kind
+ self.pos_args = [*pos_args]
+ self.dumb_sort = None
class CompletionWidgetStub(QObject):
@@ -74,17 +74,8 @@ 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
- instances = {kind: FakeCompletionModel(kind)
- for kind in usertypes.Completion}
- instances[usertypes.Completion.option] = {
- 'general': FakeCompletionModel(usertypes.Completion.option),
- }
- instances[usertypes.Completion.value] = {
- 'general': {
- 'editor': FakeCompletionModel(usertypes.Completion.value),
- }
- }
- monkeypatch.setattr(completer, 'instances', instances)
+ get = lambda kind: lambda *args: FakeCompletionModel(kind, *args)
+ monkeypatch.setattr(completer, 'instances', get)
@pytest.fixture(autouse=True)
@@ -140,47 +131,52 @@ def _set_cmd_prompt(cmd, txt):
cmd.setCursorPosition(txt.index('|'))
-@pytest.mark.parametrize('txt, kind, pattern', [
- (':nope|', usertypes.Completion.command, 'nope'),
- (':nope |', None, ''),
- (':set |', usertypes.Completion.section, ''),
- (':set gen|', usertypes.Completion.section, 'gen'),
- (':set general |', usertypes.Completion.option, ''),
- (':set what |', None, ''),
- (':set general editor |', usertypes.Completion.value, ''),
- (':set general editor gv|', usertypes.Completion.value, 'gv'),
- (':set general editor "gvim -f"|', usertypes.Completion.value, 'gvim -f'),
- (':set general editor "gvim |', usertypes.Completion.value, 'gvim'),
- (':set general huh |', None, ''),
- (':help |', usertypes.Completion.helptopic, ''),
- (':help |', usertypes.Completion.helptopic, ''),
- (':open |', usertypes.Completion.url, ''),
- (':bind |', None, ''),
- (':bind |', usertypes.Completion.command, ''),
- (':bind foo|', usertypes.Completion.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, ''),
- ('/|', None, ''),
- (':open -t|', None, ''),
- (':open --tab|', None, ''),
- (':open -t |', usertypes.Completion.url, ''),
- (':open --tab |', usertypes.Completion.url, ''),
- (':open | -t', usertypes.Completion.url, ''),
- (':tab-detach |', None, ''),
- (':bind --mode=caret |', usertypes.Completion.command, ''),
+@pytest.mark.parametrize('txt, kind, pattern, pos_args', [
+ (':nope|', usertypes.Completion.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, '',
+ ['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, '', []),
+ (':bind |', None, '', []),
+ (':bind |', usertypes.Completion.command, '', ['']),
+ (':bind foo|', usertypes.Completion.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, '', []),
+ ('/|', None, '', []),
+ (':open -t|', None, '', []),
+ (':open --tab|', None, '', []),
+ (':open -t |', usertypes.Completion.url, '', []),
+ (':open --tab |', usertypes.Completion.url, '', []),
+ (':open | -t', usertypes.Completion.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, ''),
- (':open -- |', None, ''),
- (':gibberish nonesense |', None, ''),
- ('/:help|', None, ''),
+ '', [], marks=pytest.mark.xfail(reason='issue #74')),
+ (':set -t -p |', usertypes.Completion.section, '', []),
+ (':open -- |', None, '', []),
+ (':gibberish nonesense |', None, '', []),
+ ('/:help|', None, '', []),
('::bind|', usertypes.Completion.command, ':bind'),
])
-def test_update_completion(txt, kind, pattern, status_command_stub,
+def test_update_completion(txt, kind, pattern, pos_args, status_command_stub,
completer_obj, completion_widget_stub):
"""Test setting the completion widget's model based on command text."""
# this test uses | as a placeholder for the current cursor position
@@ -192,7 +188,9 @@ def test_update_completion(txt, kind, pattern, status_command_stub,
if kind is None:
assert args[0] is None
else:
- assert args[0].srcmodel.kind == kind
+ model = args[0].srcmodel
+ assert model.kind == kind
+ assert model.pos_args == pos_args
assert args[1] == pattern
diff --git a/tests/unit/completion/test_completionwidget.py b/tests/unit/completion/test_completionwidget.py
index 9a8de3cad..88f4aed20 100644
--- a/tests/unit/completion/test_completionwidget.py
+++ b/tests/unit/completion/test_completionwidget.py
@@ -71,7 +71,7 @@ def completionview(qtbot, status_command_stub, config_stub, win_registry,
def test_set_model(completionview):
"""Ensure set_model actually sets the model and expands all categories."""
- model = base.BaseCompletionModel()
+ model = base.CompletionModel()
filtermodel = sortfilter.CompletionFilterModel(model)
for i in range(3):
model.appendRow(QStandardItem(str(i)))
@@ -82,7 +82,7 @@ def test_set_model(completionview):
def test_set_pattern(completionview):
- model = sortfilter.CompletionFilterModel(base.BaseCompletionModel())
+ model = sortfilter.CompletionFilterModel(base.CompletionModel())
model.set_pattern = unittest.mock.Mock()
completionview.set_model(model, 'foo')
model.set_pattern.assert_called_with('foo')
@@ -148,7 +148,7 @@ def test_completion_item_focus(which, tree, expected, completionview, qtbot):
successive movement. None implies no signal should be
emitted.
"""
- model = base.BaseCompletionModel()
+ model = base.CompletionModel()
for catdata in tree:
cat = QStandardItem()
model.appendRow(cat)
@@ -176,7 +176,7 @@ def test_completion_item_focus_no_model(which, completionview, qtbot):
"""
with qtbot.assertNotEmitted(completionview.selection_changed):
completionview.completion_item_focus(which)
- model = base.BaseCompletionModel()
+ model = base.CompletionModel()
filtermodel = sortfilter.CompletionFilterModel(model,
parent=completionview)
completionview.set_model(filtermodel)
@@ -200,7 +200,7 @@ def test_completion_show(show, rows, quick_complete, completionview,
config_stub.data['completion']['show'] = show
config_stub.data['completion']['quick-complete'] = quick_complete
- model = base.BaseCompletionModel()
+ model = base.CompletionModel()
for name in rows:
cat = QStandardItem()
model.appendRow(cat)
diff --git a/tests/unit/completion/test_models.py b/tests/unit/completion/test_models.py
index ff00a11a9..9870bb4db 100644
--- a/tests/unit/completion/test_models.py
+++ b/tests/unit/completion/test_models.py
@@ -56,6 +56,9 @@ def _check_completions(model, expected):
misc = actual_cat.child(j, 2)
actual_item = (name.text(), desc.text(), misc.text())
assert actual_item in expected_cat
+ # sanity-check the column_widths
+ assert len(model.column_widths) == 3
+ assert sum(model.column_widths) == 100
def _patch_cmdutils(monkeypatch, stubs, symbol):
@@ -184,7 +187,7 @@ def test_command_completion(qtmodeltester, monkeypatch, stubs, config_stub,
key_config_stub.set_bindings_for('normal', {'s': 'stop',
'rr': 'roll',
'ro': 'rock'})
- model = miscmodels.CommandCompletionModel()
+ model = miscmodels.command()
qtmodeltester.data_display_may_return_none = True
qtmodeltester.check(model)
@@ -212,7 +215,7 @@ def test_help_completion(qtmodeltester, monkeypatch, stubs, key_config_stub):
key_config_stub.set_bindings_for('normal', {'s': 'stop', 'rr': 'roll'})
_patch_cmdutils(monkeypatch, stubs, module + '.cmdutils')
_patch_configdata(monkeypatch, stubs, module + '.configdata.DATA')
- model = miscmodels.HelpCompletionModel()
+ model = miscmodels.helptopic()
qtmodeltester.data_display_may_return_none = True
qtmodeltester.check(model)
@@ -236,7 +239,7 @@ def test_help_completion(qtmodeltester, monkeypatch, stubs, key_config_stub):
def test_quickmark_completion(qtmodeltester, quickmarks):
"""Test the results of quickmark completion."""
- model = miscmodels.QuickmarkCompletionModel()
+ model = miscmodels.quickmark()
qtmodeltester.data_display_may_return_none = True
qtmodeltester.check(model)
@@ -251,7 +254,7 @@ def test_quickmark_completion(qtmodeltester, quickmarks):
def test_bookmark_completion(qtmodeltester, bookmarks):
"""Test the results of bookmark completion."""
- model = miscmodels.BookmarkCompletionModel()
+ model = miscmodels.bookmark()
qtmodeltester.data_display_may_return_none = True
qtmodeltester.check(model)
@@ -275,7 +278,7 @@ def test_url_completion(qtmodeltester, config_stub, web_history, quickmarks,
"""
config_stub.data['completion'] = {'timestamp-format': '%Y-%m-%d',
'web-history-max-items': 2}
- model = urlmodel.UrlCompletionModel()
+ model = urlmodel.url()
qtmodeltester.data_display_may_return_none = True
qtmodeltester.check(model)
@@ -303,7 +306,7 @@ def test_url_completion_delete_bookmark(qtmodeltester, config_stub,
"""Test deleting a bookmark from the url completion model."""
config_stub.data['completion'] = {'timestamp-format': '%Y-%m-%d',
'web-history-max-items': 2}
- model = urlmodel.UrlCompletionModel()
+ model = urlmodel.url()
qtmodeltester.data_display_may_return_none = True
qtmodeltester.check(model)
@@ -321,7 +324,7 @@ def test_url_completion_delete_quickmark(qtmodeltester, config_stub,
"""Test deleting a bookmark from the url completion model."""
config_stub.data['completion'] = {'timestamp-format': '%Y-%m-%d',
'web-history-max-items': 2}
- model = urlmodel.UrlCompletionModel()
+ model = urlmodel.url()
qtmodeltester.data_display_may_return_none = True
qtmodeltester.check(model)
@@ -335,7 +338,7 @@ def test_url_completion_delete_quickmark(qtmodeltester, config_stub,
def test_session_completion(qtmodeltester, session_manager_stub):
session_manager_stub.sessions = ['default', '1', '2']
- model = miscmodels.SessionCompletionModel()
+ model = miscmodels.session()
qtmodeltester.data_display_may_return_none = True
qtmodeltester.check(model)
@@ -354,7 +357,7 @@ def test_tab_completion(qtmodeltester, fake_web_tab, app_stub, win_registry,
tabbed_browser_stubs[1].tabs = [
fake_web_tab(QUrl('https://wiki.archlinux.org'), 'ArchWiki', 0),
]
- model = miscmodels.TabCompletionModel()
+ model = miscmodels.buffer()
qtmodeltester.data_display_may_return_none = True
qtmodeltester.check(model)
@@ -381,7 +384,7 @@ def test_tab_completion_delete(qtmodeltester, fake_web_tab, qtbot, app_stub,
tabbed_browser_stubs[1].tabs = [
fake_web_tab(QUrl('https://wiki.archlinux.org'), 'ArchWiki', 0),
]
- model = miscmodels.TabCompletionModel()
+ model = miscmodels.buffer()
qtmodeltester.data_display_may_return_none = True
qtmodeltester.check(model)
@@ -398,7 +401,7 @@ def test_setting_section_completion(qtmodeltester, monkeypatch, stubs):
_patch_configdata(monkeypatch, stubs, module + '.configdata.DATA')
_patch_config_section_desc(monkeypatch, stubs,
module + '.configdata.SECTION_DESC')
- model = configmodel.SettingSectionCompletionModel()
+ model = configmodel.section()
qtmodeltester.data_display_may_return_none = True
qtmodeltester.check(model)
@@ -418,7 +421,7 @@ def test_setting_option_completion(qtmodeltester, monkeypatch, stubs,
config_stub.data = {'ui': {'gesture': 'off',
'mind': 'on',
'voice': 'sometimes'}}
- model = configmodel.SettingOptionCompletionModel('ui')
+ model = configmodel.option('ui')
qtmodeltester.data_display_may_return_none = True
qtmodeltester.check(model)
@@ -440,7 +443,7 @@ def test_setting_option_completion_valuelist(qtmodeltester, monkeypatch, stubs,
'DEFAULT': 'https://duckduckgo.com/?q={}'
}
}
- model = configmodel.SettingOptionCompletionModel('searchengines')
+ model = configmodel.option('searchengines')
qtmodeltester.data_display_may_return_none = True
qtmodeltester.check(model)
@@ -454,7 +457,7 @@ def test_setting_value_completion(qtmodeltester, monkeypatch, stubs,
module = 'qutebrowser.completion.models.configmodel'
_patch_configdata(monkeypatch, stubs, module + '.configdata.DATA')
config_stub.data = {'general': {'volume': '0'}}
- model = configmodel.SettingValueCompletionModel('general', 'volume')
+ model = configmodel.value('general', 'volume')
qtmodeltester.data_display_may_return_none = True
qtmodeltester.check(model)
@@ -486,7 +489,7 @@ def test_bind_completion(qtmodeltester, monkeypatch, stubs, config_stub,
key_config_stub.set_bindings_for('normal', {'s': 'stop',
'rr': 'roll',
'ro': 'rock'})
- model = miscmodels.BindCompletionModel()
+ model = miscmodels.bind('s')
qtmodeltester.data_display_may_return_none = True
qtmodeltester.check(model)
diff --git a/tests/unit/completion/test_sortfilter.py b/tests/unit/completion/test_sortfilter.py
index 2d4a4e25d..c972a5ec9 100644
--- a/tests/unit/completion/test_sortfilter.py
+++ b/tests/unit/completion/test_sortfilter.py
@@ -33,7 +33,7 @@ def _create_model(data):
tuple in the sub-list represents an item, and each value in the
tuple represents the item data for that column
"""
- model = base.BaseCompletionModel()
+ model = base.CompletionModel()
for catdata in data:
cat = model.new_category('')
for itemdata in catdata:
@@ -74,7 +74,7 @@ def _extract_model_data(model):
('4', 'blah', False),
])
def test_filter_accepts_row(pattern, data, expected):
- source_model = base.BaseCompletionModel()
+ source_model = base.CompletionModel()
cat = source_model.new_category('test')
source_model.new_item(cat, data)
@@ -119,8 +119,8 @@ def test_first_last_item(tree, first, last):
def test_set_source_model():
"""Ensure setSourceModel sets source_model and clears the pattern."""
- model1 = base.BaseCompletionModel()
- model2 = base.BaseCompletionModel()
+ model1 = base.CompletionModel()
+ model2 = base.CompletionModel()
filter_model = sortfilter.CompletionFilterModel(model1)
filter_model.set_pattern('foo')
# sourceModel() is cached as srcmodel, so make sure both match
@@ -202,7 +202,7 @@ def test_count(tree, expected):
def test_set_pattern(pattern, dumb_sort, filter_cols, before, after):
"""Validate the filtering and sorting results of set_pattern."""
model = _create_model(before)
- model.DUMB_SORT = dumb_sort
+ model.dumb_sort = dumb_sort
model.columns_to_filter = filter_cols
filter_model = sortfilter.CompletionFilterModel(model)
filter_model.set_pattern(pattern)