Don't highlight html escapes in completion.

Resolves #4199.

To avoid accidentally highlighting characters that were introduced by
html escaping the text before feeding it to setHtml, we can't just
escape the whole string before adding the highlighting. Instead, we need
to break the string up on the pattern, format and escape the individual
parts, then join them back together.

re.escape includes empty strings if there is a match at the start/end,
which ensures that matches always land on odd indices:

https://docs.python.org/3/library/re.html#re.split

> If there are capturing groups in the separator and it matches at the
> start of the string, the result will start with an empty string. The
> same holds for the end of the string
This commit is contained in:
Ryan Roden-Corrent 2018-09-15 14:06:28 -04:00
parent 4f99af5876
commit 102c6b99dd
No known key found for this signature in database
GPG Key ID: 4E5072F68872BC04
2 changed files with 11 additions and 5 deletions

View File

@ -202,11 +202,16 @@ class CompletionItemDelegate(QStyledItemDelegate):
pattern = view.pattern pattern = view.pattern
columns_to_filter = index.model().columns_to_filter(index) columns_to_filter = index.model().columns_to_filter(index)
if index.column() in columns_to_filter and pattern: if index.column() in columns_to_filter and pattern:
repl = r'<span class="highlight">\g<0></span>' pat = '({})'.format(re.escape(pattern).replace(r'\ ', r'|'))
pat = html.escape(re.escape(pattern), quote=False).replace( parts = re.split(pat, self._opt.text, flags=re.IGNORECASE)
r'\ ', r'|') fmt = '<span class="highlight">{}</span>'
txt = html.escape(self._opt.text, quote=False) escape = lambda s: html.escape(s, quote=False)
text = re.sub(pat, repl, txt, flags=re.IGNORECASE) highlight = lambda s: fmt.format(escape(s))
# matches are at every odd index
text = ''.join([
highlight(s) if i % 2 == 1 else escape(s)
for i, s in enumerate(parts)
])
self._doc.setHtml(text) self._doc.setHtml(text)
else: else:
self._doc.setPlainText(self._opt.text) self._doc.setPlainText(self._opt.text)

View File

@ -90,6 +90,7 @@ def delegate(mock_style_option, mock_text_document, config_stub, mocker, view):
# https://github.com/qutebrowser/qutebrowser/issues/4199 # https://github.com/qutebrowser/qutebrowser/issues/4199
('foo', "'foo'", "'{foo}'"), ('foo', "'foo'", "'{foo}'"),
('x', "'x'", "'{x}'"), ('x', "'x'", "'{x}'"),
('lt', "<lt", "&lt;{lt}"),
]) ])
def test_paint(delegate, painter, view, mock_style_option, mock_text_document, def test_paint(delegate, painter, view, mock_style_option, mock_text_document,
pat, txt_in, txt_out): pat, txt_in, txt_out):