Initialize completions lazily and only once.

Before, we initialized the completions once for every window spawned, which was
a waste of CPU-time and RAM.

Now we only initialize them once, when the user uses the completion for the
first time.
This commit is contained in:
Florian Bruhin 2015-03-13 16:25:13 +01:00
parent 389feab1df
commit 74892ac8e4
4 changed files with 165 additions and 79 deletions

View File

@ -38,6 +38,7 @@ from PyQt5.QtCore import (pyqtSlot, qInstallMessageHandler, QTimer, QUrl,
import qutebrowser
import qutebrowser.resources # pylint: disable=unused-import
from qutebrowser.completion.models import instances as completionmodels
from qutebrowser.commands import cmdutils, runners
from qutebrowser.config import style, config, websettings, configexc
from qutebrowser.browser import quickmarks, cookies, cache, adblock, history
@ -163,6 +164,7 @@ class Application(QApplication):
def _init_modules(self):
"""Initialize all 'modules' which need to be initialized."""
# pylint: disable=too-many-statements
log.init.debug("Initializing save manager...")
save_manager = savemanager.SaveManager(self)
objreg.register('save-manager', save_manager)
@ -205,6 +207,8 @@ class Application(QApplication):
log.init.debug("Initializing cache...")
diskcache = cache.DiskCache(self)
objreg.register('cache', diskcache)
log.init.debug("Initializing completions...")
completionmodels.init()
if not session_manager.exists(self._args.session):
log.init.debug("Initializing main window...")
window = mainwindow.MainWindow()

View File

@ -19,14 +19,12 @@
"""Completer attached to a CompletionView."""
from PyQt5.QtCore import pyqtSlot, QObject, QTimer, Qt
from PyQt5.QtCore import pyqtSlot, QObject, QTimer
from qutebrowser.config import config, configdata
from qutebrowser.config import config
from qutebrowser.commands import cmdutils, runners
from qutebrowser.utils import usertypes, log, objreg, utils
from qutebrowser.completion.models import miscmodels, urlmodel, configmodel
from qutebrowser.completion.models.sortfilter import (
CompletionFilterModel as CFM)
from qutebrowser.completion.models import instances
class Completer(QObject):
@ -34,7 +32,6 @@ class Completer(QObject):
"""Completer which manages completions in a CompletionView.
Attributes:
models: dict of available completion models.
_cmd: The statusbar Command object this completer belongs to.
_ignore_change: Whether to ignore the next completion update.
_win_id: The window ID this completer is in.
@ -53,15 +50,6 @@ class Completer(QObject):
self._cmd.textEdited.connect(self.on_text_edited)
self._ignore_change = False
self._empty_item_idx = None
self._models = {
usertypes.Completion.option: {},
usertypes.Completion.value: {},
}
self._init_static_completions()
self._init_setting_completions()
self.init_quickmark_completions()
self.init_session_completion()
self._timer = QTimer()
self._timer.setSingleShot(True)
self._timer.setInterval(0)
@ -79,56 +67,6 @@ class Completer(QObject):
window=self._win_id)
return completion.model()
def _init_static_completions(self):
"""Initialize the static completion models."""
self._models[usertypes.Completion.command] = CFM(
miscmodels.CommandCompletionModel(self), self)
self._models[usertypes.Completion.helptopic] = CFM(
miscmodels.HelpCompletionModel(self), self)
self._models[usertypes.Completion.url] = CFM(
urlmodel.UrlCompletionModel(self), self,
dumb_sort=Qt.DescendingOrder)
def _init_setting_completions(self):
"""Initialize setting completion models."""
self._models[usertypes.Completion.section] = CFM(
configmodel.SettingSectionCompletionModel(self), self)
self._models[usertypes.Completion.option] = {}
self._models[usertypes.Completion.value] = {}
for sectname in configdata.DATA:
model = configmodel.SettingOptionCompletionModel(sectname, self)
self._models[usertypes.Completion.option][sectname] = CFM(
model, self)
self._models[usertypes.Completion.value][sectname] = {}
for opt in configdata.DATA[sectname].keys():
model = configmodel.SettingValueCompletionModel(sectname, opt,
self)
self._models[usertypes.Completion.value][sectname][opt] = CFM(
model, self)
@pyqtSlot()
def init_quickmark_completions(self):
"""Initialize quickmark completion models."""
try:
self._models[usertypes.Completion.quickmark_by_url].deleteLater()
self._models[usertypes.Completion.quickmark_by_name].deleteLater()
except KeyError:
pass
self._models[usertypes.Completion.quickmark_by_url] = CFM(
miscmodels.QuickmarkCompletionModel('url', self), self)
self._models[usertypes.Completion.quickmark_by_name] = CFM(
miscmodels.QuickmarkCompletionModel('name', self), self)
@pyqtSlot()
def init_session_completion(self):
"""Initialize session completion model."""
try:
self._models[usertypes.Completion.sessions].deleteLater()
except KeyError:
pass
self._models[usertypes.Completion.sessions] = CFM(
miscmodels.SessionCompletionModel(self), self)
def _get_completion_model(self, completion, parts, cursor_part):
"""Get a completion model based on an enum member.
@ -142,17 +80,17 @@ class Completer(QObject):
"""
if completion == usertypes.Completion.option:
section = parts[cursor_part - 1]
model = self._models[completion].get(section)
model = instances.get(completion).get(section)
elif completion == usertypes.Completion.value:
section = parts[cursor_part - 2]
option = parts[cursor_part - 1]
try:
model = self._models[completion][section][option]
model = instances.get(completion)[section][option]
except KeyError:
# No completion model for this section/option.
model = None
else:
model = self._models.get(completion)
model = instances.get(completion)
return model
def _filter_cmdline_parts(self, parts, cursor_part):
@ -202,7 +140,7 @@ class Completer(QObject):
"{}".format(parts, cursor_part))
if cursor_part == 0:
# '|' or 'set|'
return self._models[usertypes.Completion.command]
return instances.get(usertypes.Completion.command)
# delegate completion to command
try:
completions = cmdutils.cmd_dict[parts[0]].completion

View File

@ -0,0 +1,154 @@
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Global instances of the completion models.
Module attributes:
_instances: An dict of available completions.
INITIALIZERS: A {usertypes.Completion: callable} dict of functions to
initialize completions.
"""
import functools
from PyQt5.QtCore import pyqtSlot, Qt
from qutebrowser.completion.models import miscmodels, urlmodel, configmodel
from qutebrowser.completion.models.sortfilter import (
CompletionFilterModel as CFM)
from qutebrowser.utils import objreg, usertypes, log
from qutebrowser.config import configdata
_instances = {}
def _init_command_completion():
"""Initialize the command completion model."""
log.completion.debug("Initializing command completion.")
_instances[usertypes.Completion.command] = CFM(
miscmodels.CommandCompletionModel())
def _init_helptopic_completion():
"""Initialize the helptopic completion model."""
log.completion.debug("Initializing helptopic completion.")
_instances[usertypes.Completion.helptopic] = CFM(
miscmodels.HelpCompletionModel())
def _init_url_completion():
"""Initialize the URL completion model."""
log.completion.debug("Initializing URL completion.")
_instances[usertypes.Completion.url] = CFM(
urlmodel.UrlCompletionModel(), dumb_sort=Qt.DescendingOrder)
def _init_setting_completions():
"""Initialize setting completion models."""
log.completion.debug("Initializing setting completion.")
_instances[usertypes.Completion.section] = CFM(
configmodel.SettingSectionCompletionModel())
_instances[usertypes.Completion.option] = {}
_instances[usertypes.Completion.value] = {}
for sectname in configdata.DATA:
model = configmodel.SettingOptionCompletionModel(sectname)
_instances[usertypes.Completion.option][sectname] = CFM(model)
_instances[usertypes.Completion.value][sectname] = {}
for opt in configdata.DATA[sectname].keys():
model = configmodel.SettingValueCompletionModel(sectname, opt)
_instances[usertypes.Completion.value][sectname][opt] = CFM(model)
@pyqtSlot()
def init_quickmark_completions():
"""Initialize quickmark completion models."""
log.completion.debug("Initializing quickmark completion.")
try:
_instances[usertypes.Completion.quickmark_by_url].deleteLater()
_instances[usertypes.Completion.quickmark_by_name].deleteLater()
except KeyError:
pass
_instances[usertypes.Completion.quickmark_by_url] = CFM(
miscmodels.QuickmarkCompletionModel('url'))
_instances[usertypes.Completion.quickmark_by_name] = CFM(
miscmodels.QuickmarkCompletionModel('name'))
@pyqtSlot()
def init_session_completion():
"""Initialize session completion model."""
log.completion.debug("Initializing session completion.")
try:
_instances[usertypes.Completion.sessions].deleteLater()
except KeyError:
pass
_instances[usertypes.Completion.sessions] = CFM(
miscmodels.SessionCompletionModel())
INITIALIZERS = {
usertypes.Completion.command: _init_command_completion,
usertypes.Completion.helptopic: _init_helptopic_completion,
usertypes.Completion.url: _init_url_completion,
usertypes.Completion.section: _init_setting_completions,
usertypes.Completion.option: _init_setting_completions,
usertypes.Completion.value: _init_setting_completions,
usertypes.Completion.quickmark_by_url: init_quickmark_completions,
usertypes.Completion.quickmark_by_name: init_quickmark_completions,
usertypes.Completion.sessions: init_session_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)
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_url,
usertypes.Completion.quickmark_by_name]))
session_manager = objreg.get('session-manager')
session_manager.update_completion.connect(
functools.partial(update, [usertypes.Completion.sessions]))

View File

@ -191,7 +191,6 @@ class MainWindow(QWidget):
completion_obj = self._get_object('completion')
tabs = self._get_object('tabbed-browser')
cmd = self._get_object('status-command')
completer = self._get_object('completer')
search_runner = self._get_object('search-runner')
message_bridge = self._get_object('message-bridge')
mode_manager = self._get_object('mode-manager')
@ -258,15 +257,6 @@ class MainWindow(QWidget):
completion_obj.on_clear_completion_selection)
cmd.hide_completion.connect(completion_obj.hide)
# quickmark completion
quickmark_manager = objreg.get('quickmark-manager')
quickmark_manager.changed.connect(completer.init_quickmark_completions)
# sessions completion
session_manager = objreg.get('session-manager')
session_manager.update_completion.connect(
completer.init_session_completion)
@pyqtSlot()
def resize_completion(self):
"""Adjust completion according to config."""