Simplify text marking in completion.
Also improves performance, see #190.
This commit is contained in:
parent
1917911dd8
commit
0a5cee6ea2
@ -29,8 +29,7 @@ from PyQt5.QtGui import QStandardItemModel, QStandardItem
|
||||
from qutebrowser.utils import usertypes, qtutils
|
||||
|
||||
|
||||
Role = usertypes.enum('Role', ['marks', 'sort'], start=Qt.UserRole,
|
||||
is_int=True)
|
||||
Role = usertypes.enum('Role', ['sort'], start=Qt.UserRole, is_int=True)
|
||||
|
||||
|
||||
class BaseCompletionModel(QStandardItemModel):
|
||||
@ -45,44 +44,6 @@ class BaseCompletionModel(QStandardItemModel):
|
||||
super().__init__(parent)
|
||||
self.setColumnCount(3)
|
||||
|
||||
def _get_marks(self, needle, haystack):
|
||||
"""Return the marks for needle in haystack.
|
||||
|
||||
Args:
|
||||
needle: The substring which should match.
|
||||
haystack: The string where the matches should be in.
|
||||
|
||||
Return:
|
||||
A list of (startidx, endidx) tuples.
|
||||
"""
|
||||
pos1 = pos2 = 0
|
||||
marks = []
|
||||
if not needle:
|
||||
return marks
|
||||
needle = needle.casefold()
|
||||
haystack = haystack.casefold()
|
||||
while True:
|
||||
pos1 = haystack.find(needle, pos2)
|
||||
if pos1 == -1:
|
||||
break
|
||||
pos2 = pos1 + len(needle)
|
||||
marks.append((pos1, pos2))
|
||||
return marks
|
||||
|
||||
def mark_item(self, index, needle):
|
||||
"""Mark a string in the givem item.
|
||||
|
||||
Args:
|
||||
index: A QModelIndex of the item to mark.
|
||||
needle: The string to mark.
|
||||
"""
|
||||
qtutils.ensure_valid(index)
|
||||
haystack = self.data(index)
|
||||
marks = self._get_marks(needle, haystack)
|
||||
ok = self.setData(index, marks, Role.marks)
|
||||
if not ok:
|
||||
raise ValueError("Error while setting data!")
|
||||
|
||||
def new_category(self, name, sort=None):
|
||||
"""Add a new category to the model.
|
||||
|
||||
|
@ -100,20 +100,6 @@ class CompletionFilterModel(QSortFilterProxyModel):
|
||||
return index
|
||||
return QModelIndex()
|
||||
|
||||
def mark_all_items(self, text):
|
||||
"""Mark the given text in all visible items."""
|
||||
if not text:
|
||||
return
|
||||
for i in range(self.rowCount()):
|
||||
cat = self.index(i, 0)
|
||||
qtutils.ensure_valid(cat)
|
||||
for k in range(self.rowCount(cat)):
|
||||
index = self.index(k, 0, cat)
|
||||
qtutils.ensure_valid(index)
|
||||
index = self.mapToSource(index)
|
||||
qtutils.ensure_valid(index)
|
||||
self.srcmodel.mark_item(index, text)
|
||||
|
||||
def setSourceModel(self, model):
|
||||
"""Override QSortFilterProxyModel's setSourceModel to clear pattern."""
|
||||
log.completion.debug("Setting source model: {}".format(model))
|
||||
|
@ -253,6 +253,5 @@ class Completer(QObject):
|
||||
completion.hide()
|
||||
return
|
||||
|
||||
self._model().mark_all_items(pattern)
|
||||
if completion.enabled:
|
||||
completion.show()
|
||||
|
@ -39,8 +39,6 @@ class CompletionView(QTreeView):
|
||||
Based on QTreeView but heavily customized so root elements show as category
|
||||
headers, and children show as flat list.
|
||||
|
||||
Highlights completions based on marks in the Role.marks data.
|
||||
|
||||
Class attributes:
|
||||
COLUMN_WIDTHS: A list of column widths, in percent.
|
||||
|
||||
|
@ -184,10 +184,6 @@ class CompletionItemDelegate(QStyledItemDelegate):
|
||||
self._opt.direction, self._opt.displayAlignment))
|
||||
|
||||
self._doc = QTextDocument(self)
|
||||
if index.parent().isValid():
|
||||
self._doc.setPlainText(self._opt.text)
|
||||
else:
|
||||
self._doc.setHtml('<b>{}</b>'.format(html.escape(self._opt.text)))
|
||||
self._doc.setDefaultFont(self._opt.font)
|
||||
self._doc.setDefaultTextOption(text_option)
|
||||
self._doc.setDefaultStyleSheet(style.get_stylesheet("""
|
||||
@ -197,21 +193,17 @@ class CompletionItemDelegate(QStyledItemDelegate):
|
||||
"""))
|
||||
self._doc.setDocumentMargin(2)
|
||||
|
||||
if index.column() == 0 and index.model().pattern:
|
||||
# We take a shortcut here by not checking anything if the models
|
||||
# pattern is empty. This is required because the marks data is
|
||||
# invalid in that case (for performance reasons).
|
||||
marks = index.data(basecompletion.Role.marks)
|
||||
if marks is None:
|
||||
return
|
||||
for mark in marks:
|
||||
cur = QTextCursor(self._doc)
|
||||
cur.setPosition(mark[0])
|
||||
cur.setPosition(mark[1], QTextCursor.KeepAnchor)
|
||||
txt = cur.selectedText()
|
||||
cur.removeSelectedText()
|
||||
cur.insertHtml('<span class="highlight">{}</span>'.format(
|
||||
html.escape(txt)))
|
||||
if index.parent().isValid():
|
||||
pattern = index.model().pattern
|
||||
if index.column() == 0 and pattern:
|
||||
text = self._opt.text.replace(
|
||||
pattern,
|
||||
'<span class="highlight">{}</span>'.format(pattern))
|
||||
self._doc.setHtml(text)
|
||||
else:
|
||||
self._doc.setPlainText(self._opt.text)
|
||||
else:
|
||||
self._doc.setHtml('<b>{}</b>'.format(html.escape(self._opt.text)))
|
||||
|
||||
def _draw_focus_rect(self):
|
||||
"""Draw the focus rectangle of an ItemViewItem."""
|
||||
|
Loading…
Reference in New Issue
Block a user