Merge branch 'fix_hints_autofollow' of https://github.com/lahwaacz/qutebrowser into lahwaacz-fix_hints_autofollow
This commit is contained in:
commit
8e6d784fd7
qutebrowser
tests/end2end/features
@ -62,7 +62,6 @@ class HintContext:
|
|||||||
"""Context namespace used for hinting.
|
"""Context namespace used for hinting.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
frames: The QWebFrames to use.
|
|
||||||
all_elems: A list of all (elem, label) namedtuples ever created.
|
all_elems: A list of all (elem, label) namedtuples ever created.
|
||||||
elems: A mapping from key strings to (elem, label) namedtuples.
|
elems: A mapping from key strings to (elem, label) namedtuples.
|
||||||
May contain less elements than `all_elems` due to filtering.
|
May contain less elements than `all_elems` due to filtering.
|
||||||
@ -79,6 +78,7 @@ class HintContext:
|
|||||||
to_follow: The link to follow when enter is pressed.
|
to_follow: The link to follow when enter is pressed.
|
||||||
args: Custom arguments for userscript/spawn
|
args: Custom arguments for userscript/spawn
|
||||||
rapid: Whether to do rapid hinting.
|
rapid: Whether to do rapid hinting.
|
||||||
|
filterstr: Used to save the filter string for restoring in rapid mode.
|
||||||
tab: The WebTab object we started hinting in.
|
tab: The WebTab object we started hinting in.
|
||||||
group: The group of web elements to hint.
|
group: The group of web elements to hint.
|
||||||
"""
|
"""
|
||||||
@ -90,7 +90,7 @@ class HintContext:
|
|||||||
self.baseurl = None
|
self.baseurl = None
|
||||||
self.to_follow = None
|
self.to_follow = None
|
||||||
self.rapid = False
|
self.rapid = False
|
||||||
self.frames = []
|
self.filterstr = None
|
||||||
self.args = []
|
self.args = []
|
||||||
self.tab = None
|
self.tab = None
|
||||||
self.group = None
|
self.group = None
|
||||||
@ -309,7 +309,6 @@ class HintManager(QObject):
|
|||||||
_context: The HintContext for the current invocation.
|
_context: The HintContext for the current invocation.
|
||||||
_win_id: The window ID this HintManager is associated with.
|
_win_id: The window ID this HintManager is associated with.
|
||||||
_tab_id: The tab ID this HintManager is associated with.
|
_tab_id: The tab ID this HintManager is associated with.
|
||||||
_filterstr: Used to save the filter string for restoring in rapid mode.
|
|
||||||
|
|
||||||
Signals:
|
Signals:
|
||||||
See HintActions
|
See HintActions
|
||||||
@ -342,7 +341,6 @@ class HintManager(QObject):
|
|||||||
self._win_id = win_id
|
self._win_id = win_id
|
||||||
self._tab_id = tab_id
|
self._tab_id = tab_id
|
||||||
self._context = None
|
self._context = None
|
||||||
self._filterstr = None
|
|
||||||
self._word_hinter = WordHinter()
|
self._word_hinter = WordHinter()
|
||||||
|
|
||||||
self._actions = HintActions(win_id)
|
self._actions = HintActions(win_id)
|
||||||
@ -381,7 +379,6 @@ class HintManager(QObject):
|
|||||||
window=self._win_id)
|
window=self._win_id)
|
||||||
message_bridge.maybe_reset_text(text)
|
message_bridge.maybe_reset_text(text)
|
||||||
self._context = None
|
self._context = None
|
||||||
self._filterstr = None
|
|
||||||
|
|
||||||
def _hint_strings(self, elems):
|
def _hint_strings(self, elems):
|
||||||
"""Calculate the hint strings for elems.
|
"""Calculate the hint strings for elems.
|
||||||
@ -394,6 +391,8 @@ class HintManager(QObject):
|
|||||||
Return:
|
Return:
|
||||||
A list of hint strings, in the same order as the elements.
|
A list of hint strings, in the same order as the elements.
|
||||||
"""
|
"""
|
||||||
|
if not elems:
|
||||||
|
return []
|
||||||
hint_mode = self._context.hint_mode
|
hint_mode = self._context.hint_mode
|
||||||
if hint_mode == 'word':
|
if hint_mode == 'word':
|
||||||
try:
|
try:
|
||||||
@ -630,6 +629,15 @@ class HintManager(QObject):
|
|||||||
# Do multi-word matching
|
# Do multi-word matching
|
||||||
return all(word in elemstr for word in filterstr.split())
|
return all(word in elemstr for word in filterstr.split())
|
||||||
|
|
||||||
|
def _filter_matches_exactly(self, filterstr, elemstr):
|
||||||
|
"""Return True if `filterstr` exactly matches `elemstr`."""
|
||||||
|
# Empty string and None never match
|
||||||
|
if not filterstr:
|
||||||
|
return False
|
||||||
|
filterstr = filterstr.casefold()
|
||||||
|
elemstr = elemstr.casefold()
|
||||||
|
return filterstr == elemstr
|
||||||
|
|
||||||
def _start_cb(self, elems):
|
def _start_cb(self, elems):
|
||||||
"""Initialize the elements and labels based on the context set."""
|
"""Initialize the elements and labels based on the context set."""
|
||||||
filterfunc = webelem.FILTERS.get(self._context.group, lambda e: True)
|
filterfunc = webelem.FILTERS.get(self._context.group, lambda e: True)
|
||||||
@ -656,6 +664,9 @@ class HintManager(QObject):
|
|||||||
modeman.enter(self._win_id, usertypes.KeyMode.hint,
|
modeman.enter(self._win_id, usertypes.KeyMode.hint,
|
||||||
'HintManager.start')
|
'HintManager.start')
|
||||||
|
|
||||||
|
# to make auto-follow == 'always' work
|
||||||
|
self._handle_auto_follow()
|
||||||
|
|
||||||
@cmdutils.register(instance='hintmanager', scope='tab', name='hint',
|
@cmdutils.register(instance='hintmanager', scope='tab', name='hint',
|
||||||
star_args_optional=True, maxsplit=2,
|
star_args_optional=True, maxsplit=2,
|
||||||
backend=usertypes.Backend.QtWebKit)
|
backend=usertypes.Backend.QtWebKit)
|
||||||
@ -753,7 +764,6 @@ class HintManager(QObject):
|
|||||||
self._context.baseurl = tabbed_browser.current_url()
|
self._context.baseurl = tabbed_browser.current_url()
|
||||||
except qtutils.QtValueError:
|
except qtutils.QtValueError:
|
||||||
raise cmdexc.CommandError("No URL set for this page yet!")
|
raise cmdexc.CommandError("No URL set for this page yet!")
|
||||||
self._context.tab = tab
|
|
||||||
self._context.args = args
|
self._context.args = args
|
||||||
self._context.group = group
|
self._context.group = group
|
||||||
selector = webelem.SELECTORS[self._context.group]
|
selector = webelem.SELECTORS[self._context.group]
|
||||||
@ -767,6 +777,51 @@ class HintManager(QObject):
|
|||||||
|
|
||||||
return self._context.hint_mode
|
return self._context.hint_mode
|
||||||
|
|
||||||
|
def _get_visible_hints(self):
|
||||||
|
"""Get elements which are currently visible."""
|
||||||
|
visible = {}
|
||||||
|
for string, elem in self._context.elems.items():
|
||||||
|
try:
|
||||||
|
if not self._is_hidden(elem.label):
|
||||||
|
visible[string] = elem
|
||||||
|
except webelem.Error:
|
||||||
|
pass
|
||||||
|
return visible
|
||||||
|
|
||||||
|
def _handle_auto_follow(self, keystr="", filterstr="", visible=None):
|
||||||
|
"""Handle the auto-follow option."""
|
||||||
|
if visible is None:
|
||||||
|
visible = self._get_visible_hints()
|
||||||
|
|
||||||
|
if len(visible) != 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
auto_follow = config.get('hints', 'auto-follow')
|
||||||
|
|
||||||
|
if auto_follow == "always":
|
||||||
|
follow = True
|
||||||
|
elif auto_follow == "unique-match":
|
||||||
|
follow = keystr or filterstr
|
||||||
|
elif auto_follow == "full-match":
|
||||||
|
elemstr = str(list(visible.values())[0].elem)
|
||||||
|
filter_match = self._filter_matches_exactly(filterstr, elemstr)
|
||||||
|
follow = (keystr in visible) or filter_match
|
||||||
|
else:
|
||||||
|
follow = False
|
||||||
|
# save the keystr of the only one visible hint to be picked up
|
||||||
|
# later by self.follow_hint
|
||||||
|
self._context.to_follow = list(visible.keys())[0]
|
||||||
|
|
||||||
|
if follow:
|
||||||
|
# apply auto-follow-timeout
|
||||||
|
timeout = config.get('hints', 'auto-follow-timeout')
|
||||||
|
keyparsers = objreg.get('keyparsers', scope='window',
|
||||||
|
window=self._win_id)
|
||||||
|
normal_parser = keyparsers[usertypes.KeyMode.normal]
|
||||||
|
normal_parser.set_inhibited_timeout(timeout)
|
||||||
|
# unpacking gets us the first (and only) key in the dict.
|
||||||
|
self._fire(*visible)
|
||||||
|
|
||||||
def handle_partial_key(self, keystr):
|
def handle_partial_key(self, keystr):
|
||||||
"""Handle a new partial keypress."""
|
"""Handle a new partial keypress."""
|
||||||
log.hints.debug("Handling new keystring: '{}'".format(keystr))
|
log.hints.debug("Handling new keystring: '{}'".format(keystr))
|
||||||
@ -790,57 +845,7 @@ class HintManager(QObject):
|
|||||||
self._hide_elem(elem.label)
|
self._hide_elem(elem.label)
|
||||||
except webelem.Error:
|
except webelem.Error:
|
||||||
pass
|
pass
|
||||||
|
self._handle_auto_follow(keystr=keystr)
|
||||||
def _filter_number_hints(self):
|
|
||||||
"""Apply filters for numbered hints and renumber them.
|
|
||||||
|
|
||||||
Return:
|
|
||||||
Elements which are still visible
|
|
||||||
"""
|
|
||||||
# renumber filtered hints
|
|
||||||
elems = []
|
|
||||||
for e in self._context.all_elems:
|
|
||||||
try:
|
|
||||||
if not self._is_hidden(e.label):
|
|
||||||
elems.append(e)
|
|
||||||
except webelem.Error:
|
|
||||||
pass
|
|
||||||
if not elems:
|
|
||||||
# Whoops, filtered all hints
|
|
||||||
modeman.leave(self._win_id, usertypes.KeyMode.hint,
|
|
||||||
'all filtered')
|
|
||||||
return {}
|
|
||||||
|
|
||||||
strings = self._hint_strings(elems)
|
|
||||||
self._context.elems = {}
|
|
||||||
for elem, string in zip(elems, strings):
|
|
||||||
elem.label.set_inner_xml(string)
|
|
||||||
self._context.elems[string] = elem
|
|
||||||
keyparsers = objreg.get('keyparsers', scope='window',
|
|
||||||
window=self._win_id)
|
|
||||||
keyparser = keyparsers[usertypes.KeyMode.hint]
|
|
||||||
keyparser.update_bindings(strings, preserve_filter=True)
|
|
||||||
|
|
||||||
return self._context.elems
|
|
||||||
|
|
||||||
def _filter_non_number_hints(self):
|
|
||||||
"""Apply filters for letter/word hints.
|
|
||||||
|
|
||||||
Return:
|
|
||||||
Elements which are still visible
|
|
||||||
"""
|
|
||||||
visible = {}
|
|
||||||
for string, elem in self._context.elems.items():
|
|
||||||
try:
|
|
||||||
if not self._is_hidden(elem.label):
|
|
||||||
visible[string] = elem
|
|
||||||
except webelem.Error:
|
|
||||||
pass
|
|
||||||
if not visible:
|
|
||||||
# Whoops, filtered all hints
|
|
||||||
modeman.leave(self._win_id, usertypes.KeyMode.hint,
|
|
||||||
'all filtered')
|
|
||||||
return visible
|
|
||||||
|
|
||||||
def filter_hints(self, filterstr):
|
def filter_hints(self, filterstr):
|
||||||
"""Filter displayed hints according to a text.
|
"""Filter displayed hints according to a text.
|
||||||
@ -852,13 +857,15 @@ class HintManager(QObject):
|
|||||||
and `self._filterstr` are None, all hints are shown.
|
and `self._filterstr` are None, all hints are shown.
|
||||||
"""
|
"""
|
||||||
if filterstr is None:
|
if filterstr is None:
|
||||||
filterstr = self._filterstr
|
filterstr = self._context.filterstr
|
||||||
else:
|
else:
|
||||||
self._filterstr = filterstr
|
self._context.filterstr = filterstr
|
||||||
|
|
||||||
|
visible = []
|
||||||
for elem in self._context.all_elems:
|
for elem in self._context.all_elems:
|
||||||
try:
|
try:
|
||||||
if self._filter_matches(filterstr, str(elem.elem)):
|
if self._filter_matches(filterstr, str(elem.elem)):
|
||||||
|
visible.append(elem)
|
||||||
if self._is_hidden(elem.label):
|
if self._is_hidden(elem.label):
|
||||||
# hidden element which matches again -> show it
|
# hidden element which matches again -> show it
|
||||||
self._show_elem(elem.label)
|
self._show_elem(elem.label)
|
||||||
@ -868,35 +875,37 @@ class HintManager(QObject):
|
|||||||
except webelem.Error:
|
except webelem.Error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if self._context.hint_mode == 'number':
|
if not visible:
|
||||||
visible = self._filter_number_hints()
|
# Whoops, filtered all hints
|
||||||
else:
|
modeman.leave(self._win_id, usertypes.KeyMode.hint,
|
||||||
visible = self._filter_non_number_hints()
|
'all filtered')
|
||||||
|
return
|
||||||
|
|
||||||
if (len(visible) == 1 and
|
if self._context.hint_mode == 'number':
|
||||||
config.get('hints', 'auto-follow') and
|
# renumber filtered hints
|
||||||
filterstr is not None):
|
strings = self._hint_strings(visible)
|
||||||
# apply auto-follow-timeout
|
self._context.elems = {}
|
||||||
timeout = config.get('hints', 'auto-follow-timeout')
|
for elem, string in zip(visible, strings):
|
||||||
|
elem.label.set_inner_xml(string)
|
||||||
|
self._context.elems[string] = elem
|
||||||
keyparsers = objreg.get('keyparsers', scope='window',
|
keyparsers = objreg.get('keyparsers', scope='window',
|
||||||
window=self._win_id)
|
window=self._win_id)
|
||||||
normal_parser = keyparsers[usertypes.KeyMode.normal]
|
keyparser = keyparsers[usertypes.KeyMode.hint]
|
||||||
normal_parser.set_inhibited_timeout(timeout)
|
keyparser.update_bindings(strings, preserve_filter=True)
|
||||||
# unpacking gets us the first (and only) key in the dict.
|
|
||||||
self.fire(*visible)
|
|
||||||
|
|
||||||
def fire(self, keystr, force=False):
|
# Note: filter_hints can be called with non-None filterstr only
|
||||||
|
# when number mode is active
|
||||||
|
if filterstr is not None:
|
||||||
|
# pass self._context.elems as the dict of visible hints
|
||||||
|
self._handle_auto_follow(filterstr=filterstr,
|
||||||
|
visible=self._context.elems)
|
||||||
|
|
||||||
|
def _fire(self, keystr):
|
||||||
"""Fire a completed hint.
|
"""Fire a completed hint.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
keystr: The keychain string to follow.
|
keystr: The keychain string to follow.
|
||||||
force: When True, follow even when auto-follow is false.
|
|
||||||
"""
|
"""
|
||||||
if not (force or config.get('hints', 'auto-follow')):
|
|
||||||
self.handle_partial_key(keystr)
|
|
||||||
self._context.to_follow = keystr
|
|
||||||
return
|
|
||||||
|
|
||||||
# Handlers which take a QWebElement
|
# Handlers which take a QWebElement
|
||||||
elem_handlers = {
|
elem_handlers = {
|
||||||
Target.normal: self._actions.click,
|
Target.normal: self._actions.click,
|
||||||
@ -969,7 +978,7 @@ class HintManager(QObject):
|
|||||||
keystring = self._context.to_follow
|
keystring = self._context.to_follow
|
||||||
elif keystring not in self._context.elems:
|
elif keystring not in self._context.elems:
|
||||||
raise cmdexc.CommandError("No hint {}!".format(keystring))
|
raise cmdexc.CommandError("No hint {}!".format(keystring))
|
||||||
self.fire(keystring, force=True)
|
self._fire(keystring)
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def on_contents_size_changed(self):
|
def on_contents_size_changed(self):
|
||||||
|
@ -365,6 +365,8 @@ class ConfigManager(QObject):
|
|||||||
_get_value_transformer({'false': 'none', 'true': 'debug'}),
|
_get_value_transformer({'false': 'none', 'true': 'debug'}),
|
||||||
('ui', 'keyhint-blacklist'):
|
('ui', 'keyhint-blacklist'):
|
||||||
_get_value_transformer({'false': '*', 'true': ''}),
|
_get_value_transformer({'false': '*', 'true': ''}),
|
||||||
|
('hints', 'auto-follow'):
|
||||||
|
_get_value_transformer({'false': 'never', 'true': 'unique-match'}),
|
||||||
}
|
}
|
||||||
|
|
||||||
changed = pyqtSignal(str, str)
|
changed = pyqtSignal(str, str)
|
||||||
|
@ -936,9 +936,21 @@ def data(readonly=False):
|
|||||||
"The dictionary file to be used by the word hints."),
|
"The dictionary file to be used by the word hints."),
|
||||||
|
|
||||||
('auto-follow',
|
('auto-follow',
|
||||||
SettingValue(typ.Bool(), 'true'),
|
SettingValue(typ.String(
|
||||||
"Follow a hint immediately when the hint text is completely "
|
valid_values=typ.ValidValues(
|
||||||
"matched."),
|
('always', "Auto-follow whenever there is only a single "
|
||||||
|
"hint on a page."),
|
||||||
|
('unique-match', "Auto-follow whenever there is a unique "
|
||||||
|
"non-empty match in either the hint string (word mode) "
|
||||||
|
"or filter (number mode)."),
|
||||||
|
('full-match', "Follow the hint when the user typed the "
|
||||||
|
"whole hint (letter, word or number mode) or the "
|
||||||
|
"element's text (only in number mode)."),
|
||||||
|
('never', "The user will always need to press Enter to "
|
||||||
|
"follow a hint."),
|
||||||
|
)), 'unique-match'),
|
||||||
|
"Controls when a hint can be automatically followed without the "
|
||||||
|
"user pressing Enter."),
|
||||||
|
|
||||||
('auto-follow-timeout',
|
('auto-follow-timeout',
|
||||||
SettingValue(typ.Int(), '0'),
|
SettingValue(typ.Int(), '0'),
|
||||||
|
@ -231,7 +231,7 @@ class HintKeyParser(keyparser.CommandKeyParser):
|
|||||||
if keytype == self.Type.chain:
|
if keytype == self.Type.chain:
|
||||||
hintmanager = objreg.get('hintmanager', scope='tab',
|
hintmanager = objreg.get('hintmanager', scope='tab',
|
||||||
window=self._win_id, tab='current')
|
window=self._win_id, tab='current')
|
||||||
hintmanager.fire(cmdstr)
|
hintmanager.handle_partial_key(cmdstr)
|
||||||
else:
|
else:
|
||||||
# execute as command
|
# execute as command
|
||||||
super().execute(cmdstr, keytype, count)
|
super().execute(cmdstr, keytype, count)
|
||||||
|
@ -225,7 +225,7 @@ Feature: Using hints
|
|||||||
Scenario: Multi-word matching
|
Scenario: Multi-word matching
|
||||||
When I open data/hints/number.html
|
When I open data/hints/number.html
|
||||||
And I set hints -> mode to number
|
And I set hints -> mode to number
|
||||||
And I set hints -> auto-follow to true
|
And I set hints -> auto-follow to unique-match
|
||||||
And I set hints -> auto-follow-timeout to 0
|
And I set hints -> auto-follow-timeout to 0
|
||||||
And I run :hint all
|
And I run :hint all
|
||||||
And I press the keys "ten pos"
|
And I press the keys "ten pos"
|
||||||
@ -265,3 +265,133 @@ Feature: Using hints
|
|||||||
And I press the key "s"
|
And I press the key "s"
|
||||||
And I run :follow-hint 1
|
And I run :follow-hint 1
|
||||||
Then data/numbers/7.txt should be loaded
|
Then data/numbers/7.txt should be loaded
|
||||||
|
|
||||||
|
### auto-follow option
|
||||||
|
|
||||||
|
Scenario: Using hints -> auto-follow == 'always' in letter mode
|
||||||
|
When I open data/hints/html/simple.html
|
||||||
|
And I set hints -> mode to letter
|
||||||
|
And I set hints -> auto-follow to always
|
||||||
|
And I run :hint
|
||||||
|
Then data/hello.txt should be loaded
|
||||||
|
|
||||||
|
# unique-match is actually the same as full-match in letter mode
|
||||||
|
Scenario: Using hints -> auto-follow == 'unique-match' in letter mode
|
||||||
|
When I open data/hints/html/simple.html
|
||||||
|
And I set hints -> mode to letter
|
||||||
|
And I set hints -> auto-follow to unique-match
|
||||||
|
And I run :hint
|
||||||
|
And I press the key "a"
|
||||||
|
Then data/hello.txt should be loaded
|
||||||
|
|
||||||
|
Scenario: Using hints -> auto-follow == 'full-match' in letter mode
|
||||||
|
When I open data/hints/html/simple.html
|
||||||
|
And I set hints -> mode to letter
|
||||||
|
And I set hints -> auto-follow to full-match
|
||||||
|
And I run :hint
|
||||||
|
And I press the key "a"
|
||||||
|
Then data/hello.txt should be loaded
|
||||||
|
|
||||||
|
Scenario: Using hints -> auto-follow == 'never' without Enter in letter mode
|
||||||
|
When I open data/hints/html/simple.html
|
||||||
|
And I set hints -> mode to letter
|
||||||
|
And I set hints -> auto-follow to never
|
||||||
|
And I run :hint
|
||||||
|
And I press the key "a"
|
||||||
|
Then "Leaving mode KeyMode.hint (reason: followed)" should not be logged
|
||||||
|
|
||||||
|
Scenario: Using hints -> auto-follow == 'never' in letter mode
|
||||||
|
When I open data/hints/html/simple.html
|
||||||
|
And I set hints -> mode to letter
|
||||||
|
And I set hints -> auto-follow to never
|
||||||
|
And I run :hint
|
||||||
|
And I press the key "a"
|
||||||
|
And I press the key "<Enter>"
|
||||||
|
Then data/hello.txt should be loaded
|
||||||
|
|
||||||
|
Scenario: Using hints -> auto-follow == 'always' in number mode
|
||||||
|
When I open data/hints/html/simple.html
|
||||||
|
And I set hints -> mode to number
|
||||||
|
And I set hints -> auto-follow to always
|
||||||
|
And I run :hint
|
||||||
|
Then data/hello.txt should be loaded
|
||||||
|
|
||||||
|
Scenario: Using hints -> auto-follow == 'unique-match' in number mode
|
||||||
|
When I open data/hints/html/simple.html
|
||||||
|
And I set hints -> mode to number
|
||||||
|
And I set hints -> auto-follow to unique-match
|
||||||
|
And I run :hint
|
||||||
|
And I press the key "f"
|
||||||
|
Then data/hello.txt should be loaded
|
||||||
|
|
||||||
|
Scenario: Using hints -> auto-follow == 'full-match' in number mode
|
||||||
|
When I open data/hints/html/simple.html
|
||||||
|
And I set hints -> mode to number
|
||||||
|
And I set hints -> auto-follow to full-match
|
||||||
|
And I run :hint
|
||||||
|
# this actually presses the keys one by one
|
||||||
|
And I press the key "follow me!"
|
||||||
|
Then data/hello.txt should be loaded
|
||||||
|
|
||||||
|
Scenario: Using hints -> auto-follow == 'never' without Enter in number mode
|
||||||
|
When I open data/hints/html/simple.html
|
||||||
|
And I set hints -> mode to number
|
||||||
|
And I set hints -> auto-follow to never
|
||||||
|
And I run :hint
|
||||||
|
# this actually presses the keys one by one
|
||||||
|
And I press the key "follow me!"
|
||||||
|
Then "Leaving mode KeyMode.hint (reason: followed)" should not be logged
|
||||||
|
|
||||||
|
Scenario: Using hints -> auto-follow == 'never' in number mode
|
||||||
|
When I open data/hints/html/simple.html
|
||||||
|
And I set hints -> mode to number
|
||||||
|
And I set hints -> auto-follow to never
|
||||||
|
And I run :hint
|
||||||
|
# this actually presses the keys one by one
|
||||||
|
And I press the key "follow me!"
|
||||||
|
And I press the key "<Enter>"
|
||||||
|
Then data/hello.txt should be loaded
|
||||||
|
|
||||||
|
Scenario: Using hints -> auto-follow == 'always' in word mode
|
||||||
|
When I open data/hints/html/simple.html
|
||||||
|
And I set hints -> mode to word
|
||||||
|
And I set hints -> auto-follow to always
|
||||||
|
And I run :hint
|
||||||
|
Then data/hello.txt should be loaded
|
||||||
|
|
||||||
|
Scenario: Using hints -> auto-follow == 'unique-match' in word mode
|
||||||
|
When I open data/hints/html/simple.html
|
||||||
|
And I set hints -> mode to word
|
||||||
|
And I set hints -> auto-follow to unique-match
|
||||||
|
And I run :hint
|
||||||
|
# the link gets "hello" as the hint
|
||||||
|
And I press the key "h"
|
||||||
|
Then data/hello.txt should be loaded
|
||||||
|
|
||||||
|
Scenario: Using hints -> auto-follow == 'full-match' in word mode
|
||||||
|
When I open data/hints/html/simple.html
|
||||||
|
And I set hints -> mode to word
|
||||||
|
And I set hints -> auto-follow to full-match
|
||||||
|
And I run :hint
|
||||||
|
# this actually presses the keys one by one
|
||||||
|
And I press the key "hello"
|
||||||
|
Then data/hello.txt should be loaded
|
||||||
|
|
||||||
|
Scenario: Using hints -> auto-follow == 'never' without Enter in word mode
|
||||||
|
When I open data/hints/html/simple.html
|
||||||
|
And I set hints -> mode to word
|
||||||
|
And I set hints -> auto-follow to never
|
||||||
|
And I run :hint
|
||||||
|
# this actually presses the keys one by one
|
||||||
|
And I press the key "hello"
|
||||||
|
Then "Leaving mode KeyMode.hint (reason: followed)" should not be logged
|
||||||
|
|
||||||
|
Scenario: Using hints -> auto-follow == 'never' in word mode
|
||||||
|
When I open data/hints/html/simple.html
|
||||||
|
And I set hints -> mode to word
|
||||||
|
And I set hints -> auto-follow to never
|
||||||
|
And I run :hint
|
||||||
|
# this actually presses the keys one by one
|
||||||
|
And I press the key "hello"
|
||||||
|
And I press the key "<Enter>"
|
||||||
|
Then data/hello.txt should be loaded
|
||||||
|
@ -17,5 +17,30 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
import pytest_bdd as bdd
|
import pytest_bdd as bdd
|
||||||
bdd.scenarios('hints.feature')
|
bdd.scenarios('hints.feature')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def set_up_word_hints(tmpdir, quteproc):
|
||||||
|
dict_file = tmpdir / 'dict'
|
||||||
|
dict_file.write(textwrap.dedent("""
|
||||||
|
one
|
||||||
|
two
|
||||||
|
three
|
||||||
|
four
|
||||||
|
five
|
||||||
|
six
|
||||||
|
seven
|
||||||
|
eight
|
||||||
|
nine
|
||||||
|
ten
|
||||||
|
eleven
|
||||||
|
twelve
|
||||||
|
thirteen
|
||||||
|
"""))
|
||||||
|
quteproc.set_setting('hints', 'dictionary', str(dict_file))
|
||||||
|
Loading…
Reference in New Issue
Block a user