Merge branch 'NoctuaNivalis-issue-1393'

This commit is contained in:
Florian Bruhin 2016-04-30 14:10:37 +02:00
commit e9da3ef8bb
8 changed files with 86 additions and 7 deletions

View File

@ -146,8 +146,8 @@ Contributors, sorted by the number of commits in descending order:
* Lamar Pavel
* Bruno Oliveira
* Alexander Cogneau
* Martin Tournoij
* Felix Van der Jeugt
* Martin Tournoij
* Raphael Pierzina
* Joel Torstensson
* Ryan Roden-Corrent

View File

@ -1004,11 +1004,14 @@ class WordHinter:
def __init__(self):
# will be initialized on first use.
self.words = set()
self.dictionary = None
def ensure_initialized(self):
"""Generate the used words if yet uninialized."""
if not self.words:
dictionary = config.get("hints", "dictionary")
dictionary = config.get("hints", "dictionary")
if not self.words or self.dictionary != dictionary:
self.words.clear()
self.dictionary = dictionary
try:
with open(dictionary, encoding="UTF-8") as wordfile:
alphabet = set(string.ascii_lowercase)
@ -1065,12 +1068,17 @@ class WordHinter:
def any_prefix(self, hint, existing):
return any(hint.startswith(e) or e.startswith(hint) for e in existing)
def new_hint_for(self, elem, existing):
def filter_prefixes(self, hints, existing):
return (h for h in hints if not self.any_prefix(h, existing))
def new_hint_for(self, elem, existing, fallback):
"""Return a hint for elem, not conflicting with the existing."""
new = self.tag_words_to_hints(self.extract_tag_words(elem))
no_prefixes = (h for h in new if not self.any_prefix(h, existing))
new_no_prefixes = self.filter_prefixes(new, existing)
fallback_no_prefixes = self.filter_prefixes(fallback, existing)
# either the first good, or None
return next(no_prefixes, None)
return (next(new_no_prefixes, None) or
next(fallback_no_prefixes, None))
def hint(self, elems):
"""Produce hint labels based on the html tags.
@ -1090,7 +1098,9 @@ class WordHinter:
used_hints = set()
words = iter(self.words)
for elem in elems:
hint = self.new_hint_for(elem, used_hints) or next(words)
hint = self.new_hint_for(elem, used_hints, words)
if not hint:
raise WordHintingError("Not enough words in the dictionary.")
used_hints.add(hint)
hints.append(hint)
return hints

View File

@ -0,0 +1 @@
hinting

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Let's Hint some words</title>
</head>
<body>
<h1>Word hints</h1>
<h2>Smart hints</h2>
<p>In qutebrowser, urls can not only be hinted with letters and
numbers, but also with <a href="../words.txt">words</a>. When there is
a sensible url text available, qutebrowser will even use that
text to create a <a href="../smart.txt">smart</a> hint.</p>
<h2>Filled hints</h2>
<p>When no smart hints are available, because the hint text is
<a href="../l33t.txt">too</a> short or <a href="../l33t.txt">l33t</a> to
use, words from a dictionary will be used.</p>
<h2>Hint conflicts</h2>
<p>Of course, hints have to be unique. For instance, all hints
below should get a different hint, whether they're smart or
not:</p>
<ul>
<li><a href="../hinting.txt">hinting</a> should be a smart hint</li>
<li><a href="../l33t.txt">word</a> is a prefix of words</li>
<li><a href="../l33t.txt">3</a> is too 1337</li>
<li><a href="../l33t.txt">4</a> is too 1337</li>
</ul>
</body>
</html>

View File

@ -0,0 +1 @@
l33t

View File

@ -0,0 +1 @@
smart

View File

@ -0,0 +1 @@
words

View File

@ -25,6 +25,7 @@ import os.path
import yaml
import pytest
import bs4
import textwrap
def collect_tests():
@ -54,3 +55,35 @@ def test_hints(test_name, quteproc):
quteproc.wait_for(message='hints: a', category='hints')
quteproc.send_cmd(':follow-hint a')
quteproc.wait_for_load_finished('data/' + parsed['target'])
def test_word_hints_issue1393(quteproc, tmpdir):
dict_file = tmpdir / 'dict'
dict_file.write(textwrap.dedent("""
alph
beta
gamm
delt
epsi
"""))
targets = [
('words', 'words.txt'),
('smart', 'smart.txt'),
('hinting', 'hinting.txt'),
('alph', 'l33t.txt'),
('beta', 'l33t.txt'),
('gamm', 'l33t.txt'),
('delt', 'l33t.txt'),
('epsi', 'l33t.txt'),
]
quteproc.set_setting('hints', 'mode', 'word')
quteproc.set_setting('hints', 'dictionary', str(dict_file))
for hint, target in targets:
quteproc.open_path('data/hints/issue1393.html')
quteproc.wait_for_load_finished('data/hints/issue1393.html')
quteproc.send_cmd(':hint')
quteproc.wait_for(message='hints: *', category='hints')
quteproc.send_cmd(':follow-hint {}'.format(hint))
quteproc.wait_for_load_finished('data/{}'.format(target))