Merge branch 'hints'
This commit is contained in:
commit
b972acf20c
@ -32,6 +32,8 @@ Added
|
||||
- New `:messages` command to show error messages
|
||||
- New pop-up showing possible keybinding when the first key of a keychain is
|
||||
pressed. This can be turned off using `:set ui keyhint-blacklist *`.
|
||||
- New `hints -> auto-follow-timeout` setting to ignore keypresses after
|
||||
following a hint when filtering in number mode.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
@ -60,6 +62,10 @@ Changed
|
||||
- `:zoom-in` or `:zoom-out` (`+`/`-`) with a too large count now zooms to the
|
||||
smallest/largest zoom instead of doing nothing.
|
||||
- The commandline now accepts partially typed commands if they're unique.
|
||||
- Number hints are now kept filtered after following a hint in rapid mode.
|
||||
- Number hints are now renumbered after filtering
|
||||
- Number hints can now be filtered with multiple space-separated search terms
|
||||
- `hints -> scatter` is now ignored for number hints
|
||||
|
||||
Fixed
|
||||
-----
|
||||
@ -80,6 +86,7 @@ Fixed
|
||||
- Fixed crash when cancelling a download which belongs to a MHTML download
|
||||
- Fixed rebinding of keybindings being case-sensitive
|
||||
- Fix for tab indicators getting lost when moving tabs
|
||||
- Fixed handling of backspace in number hinting mode
|
||||
|
||||
v0.6.2
|
||||
------
|
||||
|
@ -148,9 +148,9 @@ Contributors, sorted by the number of commits in descending order:
|
||||
* Alexander Cogneau
|
||||
* Felix Van der Jeugt
|
||||
* Martin Tournoij
|
||||
* Jakub Klinkovský
|
||||
* Raphael Pierzina
|
||||
* Joel Torstensson
|
||||
* Jakub Klinkovský
|
||||
* Tarcisio Fedrizzi
|
||||
* Patric Schmitz
|
||||
* Claude
|
||||
|
@ -179,10 +179,11 @@
|
||||
|<<hints-mode,mode>>|Mode to use for hints.
|
||||
|<<hints-chars,chars>>|Chars used for hint strings.
|
||||
|<<hints-min-chars,min-chars>>|Minimum number of chars used for hint strings.
|
||||
|<<hints-scatter,scatter>>|Whether to scatter hint key chains (like Vimium) or not (like dwb).
|
||||
|<<hints-scatter,scatter>>|Whether to scatter hint key chains (like Vimium) or not (like dwb). Ignored for number hints.
|
||||
|<<hints-uppercase,uppercase>>|Make chars in hint strings uppercase.
|
||||
|<<hints-dictionary,dictionary>>|The dictionary file to be used by the word hints.
|
||||
|<<hints-auto-follow,auto-follow>>|Follow a hint immediately when the hint text is completely matched.
|
||||
|<<hints-auto-follow-timeout,auto-follow-timeout>>|A timeout to inhibit normal-mode key bindings after a successfulauto-follow.
|
||||
|<<hints-next-regexes,next-regexes>>|A comma-separated list of regexes to use for 'next' links.
|
||||
|<<hints-prev-regexes,prev-regexes>>|A comma-separated list of regexes to use for 'prev' links.
|
||||
|==============
|
||||
@ -1581,7 +1582,7 @@ Default: +pass:[1]+
|
||||
|
||||
[[hints-scatter]]
|
||||
=== scatter
|
||||
Whether to scatter hint key chains (like Vimium) or not (like dwb).
|
||||
Whether to scatter hint key chains (like Vimium) or not (like dwb). Ignored for number hints.
|
||||
|
||||
Valid values:
|
||||
|
||||
@ -1618,6 +1619,12 @@ Valid values:
|
||||
|
||||
Default: +pass:[true]+
|
||||
|
||||
[[hints-auto-follow-timeout]]
|
||||
=== auto-follow-timeout
|
||||
A timeout to inhibit normal-mode key bindings after a successfulauto-follow.
|
||||
|
||||
Default: +pass:[0]+
|
||||
|
||||
[[hints-next-regexes]]
|
||||
=== next-regexes
|
||||
A comma-separated list of regexes to use for 'next' links.
|
||||
|
@ -23,7 +23,7 @@ import collections
|
||||
import functools
|
||||
import math
|
||||
import re
|
||||
import string
|
||||
from string import ascii_lowercase
|
||||
|
||||
from PyQt5.QtCore import (pyqtSignal, pyqtSlot, QObject, QEvent, Qt, QUrl,
|
||||
QTimer)
|
||||
@ -67,7 +67,9 @@ class HintContext:
|
||||
frames: The QWebFrames to use.
|
||||
destroyed_frames: id()'s of QWebFrames which have been destroyed.
|
||||
(Workaround for https://github.com/The-Compiler/qutebrowser/issues/152)
|
||||
all_elems: A list of all (elem, label) namedtuples ever created.
|
||||
elems: A mapping from key strings to (elem, label) namedtuples.
|
||||
May contain less elements than `all_elems` due to filtering.
|
||||
baseurl: The URL of the current page.
|
||||
target: What to do with the opened links.
|
||||
normal/current/tab/tab_fg/tab_bg/window: Get passed to
|
||||
@ -86,6 +88,7 @@ class HintContext:
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.all_elems = []
|
||||
self.elems = {}
|
||||
self.target = None
|
||||
self.baseurl = None
|
||||
@ -117,6 +120,7 @@ class HintManager(QObject):
|
||||
_context: The HintContext for the current invocation.
|
||||
_win_id: The window 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:
|
||||
mouse_event: Mouse event to be posted in the web view.
|
||||
@ -153,6 +157,7 @@ class HintManager(QObject):
|
||||
self._win_id = win_id
|
||||
self._tab_id = tab_id
|
||||
self._context = None
|
||||
self._filterstr = None
|
||||
self._word_hinter = WordHinter()
|
||||
mode_manager = objreg.get('mode-manager', scope='window',
|
||||
window=win_id)
|
||||
@ -168,7 +173,7 @@ class HintManager(QObject):
|
||||
|
||||
def _cleanup(self):
|
||||
"""Clean up after hinting."""
|
||||
for elem in self._context.elems.values():
|
||||
for elem in self._context.all_elems:
|
||||
try:
|
||||
elem.label.removeFromDocument()
|
||||
except webelem.IsNullError:
|
||||
@ -218,7 +223,7 @@ class HintManager(QObject):
|
||||
else:
|
||||
chars = config.get('hints', 'chars')
|
||||
min_chars = config.get('hints', 'min-chars')
|
||||
if config.get('hints', 'scatter'):
|
||||
if config.get('hints', 'scatter') and hint_mode != 'number':
|
||||
return self._hint_scattered(min_chars, chars, elems)
|
||||
else:
|
||||
return self._hint_linear(min_chars, chars, elems)
|
||||
@ -384,7 +389,7 @@ class HintManager(QObject):
|
||||
label.setStyleProperty('left', '{}px !important'.format(left))
|
||||
label.setStyleProperty('top', '{}px !important'.format(top))
|
||||
|
||||
def _draw_label(self, elem, text):
|
||||
def _draw_label(self, elem, string):
|
||||
"""Draw a hint label over an element.
|
||||
|
||||
Args:
|
||||
@ -409,7 +414,7 @@ class HintManager(QObject):
|
||||
label = webelem.WebElementWrapper(parent.lastChild())
|
||||
label['class'] = 'qutehint'
|
||||
self._set_style_properties(elem, label)
|
||||
label.setPlainText(text)
|
||||
label.setPlainText(string)
|
||||
return label
|
||||
|
||||
def _show_url_error(self):
|
||||
@ -691,15 +696,27 @@ class HintManager(QObject):
|
||||
elems = [e for e in elems if filterfunc(e)]
|
||||
if not elems:
|
||||
raise cmdexc.CommandError("No elements found.")
|
||||
hints = self._hint_strings(elems)
|
||||
log.hints.debug("hints: {}".format(', '.join(hints)))
|
||||
for e, hint in zip(elems, hints):
|
||||
label = self._draw_label(e, hint)
|
||||
self._context.elems[hint] = ElemTuple(e, label)
|
||||
strings = self._hint_strings(elems)
|
||||
log.hints.debug("hints: {}".format(', '.join(strings)))
|
||||
for e, string in zip(elems, strings):
|
||||
label = self._draw_label(e, string)
|
||||
elem = ElemTuple(e, label)
|
||||
self._context.all_elems.append(elem)
|
||||
self._context.elems[string] = elem
|
||||
keyparsers = objreg.get('keyparsers', scope='window',
|
||||
window=self._win_id)
|
||||
keyparser = keyparsers[usertypes.KeyMode.hint]
|
||||
keyparser.update_bindings(hints)
|
||||
keyparser.update_bindings(strings)
|
||||
|
||||
def _filter_matches(self, filterstr, elemstr):
|
||||
"""Return True if `filterstr` matches `elemstr`."""
|
||||
# Empty string and None always match
|
||||
if not filterstr:
|
||||
return True
|
||||
filterstr = filterstr.casefold()
|
||||
elemstr = elemstr.casefold()
|
||||
# Do multi-word matching
|
||||
return all(word in elemstr for word in filterstr.split())
|
||||
|
||||
def follow_prevnext(self, frame, baseurl, prev=False, tab=False,
|
||||
background=False, window=False):
|
||||
@ -844,55 +861,115 @@ class HintManager(QObject):
|
||||
def handle_partial_key(self, keystr):
|
||||
"""Handle a new partial keypress."""
|
||||
log.hints.debug("Handling new keystring: '{}'".format(keystr))
|
||||
for (text, elems) in self._context.elems.items():
|
||||
for string, elem in self._context.elems.items():
|
||||
try:
|
||||
if text.startswith(keystr):
|
||||
matched = text[:len(keystr)]
|
||||
rest = text[len(keystr):]
|
||||
if string.startswith(keystr):
|
||||
matched = string[:len(keystr)]
|
||||
rest = string[len(keystr):]
|
||||
match_color = config.get('colors', 'hints.fg.match')
|
||||
elems.label.setInnerXml(
|
||||
elem.label.setInnerXml(
|
||||
'<font color="{}">{}</font>{}'.format(
|
||||
match_color, matched, rest))
|
||||
if self._is_hidden(elems.label):
|
||||
if self._is_hidden(elem.label):
|
||||
# hidden element which matches again -> show it
|
||||
self._show_elem(elems.label)
|
||||
self._show_elem(elem.label)
|
||||
else:
|
||||
# element doesn't match anymore -> hide it
|
||||
self._hide_elem(elems.label)
|
||||
self._hide_elem(elem.label)
|
||||
except webelem.IsNullError:
|
||||
pass
|
||||
|
||||
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.IsNullError:
|
||||
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.setInnerXml(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.IsNullError:
|
||||
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):
|
||||
"""Filter displayed hints according to a text.
|
||||
|
||||
Args:
|
||||
filterstr: The string to filter with, or None to show all.
|
||||
filterstr: The string to filter with, or None to use the filter
|
||||
from previous call (saved in `self._filterstr`). If
|
||||
`filterstr` is an empty string or if both `filterstr`
|
||||
and `self._filterstr` are None, all hints are shown.
|
||||
"""
|
||||
for elems in self._context.elems.values():
|
||||
if filterstr is None:
|
||||
filterstr = self._filterstr
|
||||
else:
|
||||
self._filterstr = filterstr
|
||||
|
||||
for elem in self._context.all_elems:
|
||||
try:
|
||||
if (filterstr is None or
|
||||
filterstr.casefold() in str(elems.elem).casefold()):
|
||||
if self._is_hidden(elems.label):
|
||||
if self._filter_matches(filterstr, str(elem.elem)):
|
||||
if self._is_hidden(elem.label):
|
||||
# hidden element which matches again -> show it
|
||||
self._show_elem(elems.label)
|
||||
self._show_elem(elem.label)
|
||||
else:
|
||||
# element doesn't match anymore -> hide it
|
||||
self._hide_elem(elems.label)
|
||||
self._hide_elem(elem.label)
|
||||
except webelem.IsNullError:
|
||||
pass
|
||||
visible = {}
|
||||
for k, e in self._context.elems.items():
|
||||
try:
|
||||
if not self._is_hidden(e.label):
|
||||
visible[k] = e
|
||||
except webelem.IsNullError:
|
||||
pass
|
||||
if not visible:
|
||||
# Whoops, filtered all hints
|
||||
modeman.leave(self._win_id, usertypes.KeyMode.hint, 'all filtered')
|
||||
elif (len(visible) == 1 and
|
||||
config.get('hints', 'auto-follow') and
|
||||
filterstr is not None):
|
||||
|
||||
if config.get('hints', 'mode') == 'number':
|
||||
visible = self._filter_number_hints()
|
||||
else:
|
||||
visible = self._filter_non_number_hints()
|
||||
|
||||
if (len(visible) == 1 and
|
||||
config.get('hints', 'auto-follow') and
|
||||
filterstr is not None):
|
||||
# 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)
|
||||
|
||||
@ -950,11 +1027,11 @@ class HintManager(QObject):
|
||||
modeman.maybe_leave(self._win_id, usertypes.KeyMode.hint,
|
||||
'followed')
|
||||
else:
|
||||
# Show all hints again
|
||||
# Reset filtering
|
||||
self.filter_hints(None)
|
||||
# Undo keystring highlighting
|
||||
for (text, elems) in self._context.elems.items():
|
||||
elems.label.setInnerXml(text)
|
||||
for string, elem in self._context.elems.items():
|
||||
elem.label.setInnerXml(string)
|
||||
handler()
|
||||
|
||||
@cmdutils.register(instance='hintmanager', scope='tab', hide=True,
|
||||
@ -978,13 +1055,13 @@ class HintManager(QObject):
|
||||
def on_contents_size_changed(self, _size):
|
||||
"""Reposition hints if contents size changed."""
|
||||
log.hints.debug("Contents size changed...!")
|
||||
for elems in self._context.elems.values():
|
||||
for e in self._context.all_elems:
|
||||
try:
|
||||
if elems.elem.webFrame() is None:
|
||||
if e.elem.webFrame() is None:
|
||||
# This sometimes happens for some reason...
|
||||
elems.label.removeFromDocument()
|
||||
e.label.removeFromDocument()
|
||||
continue
|
||||
self._set_style_position(elems.elem, elems.label)
|
||||
self._set_style_position(e.elem, e.label)
|
||||
except webelem.IsNullError:
|
||||
pass
|
||||
|
||||
@ -1021,7 +1098,7 @@ class WordHinter:
|
||||
self.dictionary = dictionary
|
||||
try:
|
||||
with open(dictionary, encoding="UTF-8") as wordfile:
|
||||
alphabet = set(string.ascii_lowercase)
|
||||
alphabet = set(ascii_lowercase)
|
||||
hints = set()
|
||||
lines = (line.rstrip().lower() for line in wordfile)
|
||||
for word in lines:
|
||||
|
@ -898,7 +898,7 @@ def data(readonly=False):
|
||||
('scatter',
|
||||
SettingValue(typ.Bool(), 'true'),
|
||||
"Whether to scatter hint key chains (like Vimium) or not (like "
|
||||
"dwb)."),
|
||||
"dwb). Ignored for number hints."),
|
||||
|
||||
('uppercase',
|
||||
SettingValue(typ.Bool(), 'false'),
|
||||
@ -913,6 +913,11 @@ def data(readonly=False):
|
||||
"Follow a hint immediately when the hint text is completely "
|
||||
"matched."),
|
||||
|
||||
('auto-follow-timeout',
|
||||
SettingValue(typ.Int(), '0'),
|
||||
"A timeout to inhibit normal-mode key bindings after a successful"
|
||||
"auto-follow."),
|
||||
|
||||
('next-regexes',
|
||||
SettingValue(typ.RegexList(flags=re.IGNORECASE),
|
||||
r'\bnext\b,\bmore\b,\bnewer\b,\b[>→≫]\b,\b(>>|»)\b,'
|
||||
|
@ -49,6 +49,9 @@ class NormalKeyParser(keyparser.CommandKeyParser):
|
||||
self.read_config('normal')
|
||||
self._partial_timer = usertypes.Timer(self, 'partial-match')
|
||||
self._partial_timer.setSingleShot(True)
|
||||
self._inhibited = False
|
||||
self._inhibited_timer = usertypes.Timer(self, 'normal-inhibited')
|
||||
self._inhibited_timer.setSingleShot(True)
|
||||
|
||||
def __repr__(self):
|
||||
return utils.get_repr(self)
|
||||
@ -63,6 +66,10 @@ class NormalKeyParser(keyparser.CommandKeyParser):
|
||||
A self.Match member.
|
||||
"""
|
||||
txt = e.text().strip()
|
||||
if self._inhibited:
|
||||
self._debug_log("Ignoring key '{}', because the normal mode is "
|
||||
"currently inhibited.".format(txt))
|
||||
return self.Match.none
|
||||
if not self._keystring and any(txt == c for c in STARTCHARS):
|
||||
message.set_cmd_text(self._win_id, txt)
|
||||
return self.Match.definitive
|
||||
@ -75,6 +82,15 @@ class NormalKeyParser(keyparser.CommandKeyParser):
|
||||
self._partial_timer.start()
|
||||
return match
|
||||
|
||||
def set_inhibited_timeout(self, timeout):
|
||||
if timeout != 0:
|
||||
self._debug_log("Inhibiting the normal mode for {}ms.".format(
|
||||
timeout))
|
||||
self._inhibited = True
|
||||
self._inhibited_timer.setInterval(timeout)
|
||||
self._inhibited_timer.timeout.connect(self._clear_inhibited)
|
||||
self._inhibited_timer.start()
|
||||
|
||||
@pyqtSlot()
|
||||
def _clear_partial_match(self):
|
||||
"""Clear a partial keystring after a timeout."""
|
||||
@ -83,6 +99,12 @@ class NormalKeyParser(keyparser.CommandKeyParser):
|
||||
self._keystring = ''
|
||||
self.keystring_updated.emit(self._keystring)
|
||||
|
||||
@pyqtSlot()
|
||||
def _clear_inhibited(self):
|
||||
"""Reset inhibition state after a timeout."""
|
||||
self._debug_log("Releasing inhibition state of normal mode.")
|
||||
self._inhibited = False
|
||||
|
||||
@pyqtSlot()
|
||||
def _stop_timers(self):
|
||||
super()._stop_timers()
|
||||
@ -92,6 +114,12 @@ class NormalKeyParser(keyparser.CommandKeyParser):
|
||||
except TypeError:
|
||||
# no connections
|
||||
pass
|
||||
self._inhibited_timer.stop()
|
||||
try:
|
||||
self._inhibited_timer.timeout.disconnect(self._clear_inhibited)
|
||||
except TypeError:
|
||||
# no connections
|
||||
pass
|
||||
|
||||
|
||||
class PromptKeyParser(keyparser.CommandKeyParser):
|
||||
@ -153,6 +181,11 @@ class HintKeyParser(keyparser.CommandKeyParser):
|
||||
elif self._last_press == LastPress.keystring and self._keystring:
|
||||
self._keystring = self._keystring[:-1]
|
||||
self.keystring_updated.emit(self._keystring)
|
||||
if not self._keystring and self._filtertext:
|
||||
# Switch back to hint filtering mode (this can happen only
|
||||
# in numeric mode after the number has been deleted).
|
||||
hintmanager.filter_hints(self._filtertext)
|
||||
self._last_press = LastPress.filtertext
|
||||
return True
|
||||
else:
|
||||
return super()._handle_special_key(e)
|
||||
@ -203,14 +236,17 @@ class HintKeyParser(keyparser.CommandKeyParser):
|
||||
# execute as command
|
||||
super().execute(cmdstr, keytype, count)
|
||||
|
||||
def update_bindings(self, strings):
|
||||
def update_bindings(self, strings, preserve_filter=False):
|
||||
"""Update bindings when the hint strings changed.
|
||||
|
||||
Args:
|
||||
strings: A list of hint strings.
|
||||
preserve_filter: Whether to keep the current value of
|
||||
`self._filtertext`.
|
||||
"""
|
||||
self.bindings = {s: s for s in strings}
|
||||
self._filtertext = ''
|
||||
if not preserve_filter:
|
||||
self._filtertext = ''
|
||||
|
||||
@pyqtSlot(str)
|
||||
def on_keystring_updated(self, keystr):
|
||||
|
35
tests/end2end/data/hints/issue1186.html
Normal file
35
tests/end2end/data/hints/issue1186.html
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Issue 1186</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
This page contains 10 hints to test backspace handling, see #1186.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This requires setting hints -> mode to number.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
When pressing f, x, 0, Backspace, only hints starting with x should be
|
||||
shown.
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="/data/numbers/1.txt">x one</a></li>
|
||||
<li><a href="/data/numbers/2.txt">x two</a></li>
|
||||
<li><a href="/data/numbers/3.txt">x three</a></li>
|
||||
<li><a href="/data/numbers/4.txt">x four</a></li>
|
||||
<li><a href="/data/numbers/5.txt">x five</a></li>
|
||||
<li><a href="/data/numbers/6.txt">x six</a></li>
|
||||
<li><a href="/data/numbers/7.txt">x seven</a></li>
|
||||
<li><a href="/data/numbers/8.txt">x eight</a></li>
|
||||
<li><a href="/data/numbers/9.txt">x nine</a></li>
|
||||
<li><a href="/data/numbers/10.txt">x ten</a></li>
|
||||
<li><a href="/data/numbers/11.txt">x eleven</a></li>
|
||||
<li><a href="/data/numbers/12.txt">twelve</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
35
tests/end2end/data/hints/number.html
Normal file
35
tests/end2end/data/hints/number.html
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Numbered hints</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
This page contains various links to test numbered hints. This requires setting hints -> mode to number.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Related issues:
|
||||
<ul>
|
||||
<li>#308 - Renumber hints when filtering them in number mode.</li>
|
||||
<li>#576 - Keep hint filtering when rapid hinting in number mode</li>
|
||||
<li>#674 (comment) - Multi-word matching for hints </li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="/data/numbers/1.txt">one</a></li>
|
||||
<li><a href="/data/numbers/2.txt">two</a></li>
|
||||
<li><a href="/data/numbers/3.txt">three</a></li>
|
||||
<li><a href="/data/numbers/4.txt">four</a></li>
|
||||
<li><a href="/data/numbers/5.txt">five</a></li>
|
||||
<li><a href="/data/numbers/6.txt">six</a></li>
|
||||
<li><a href="/data/numbers/7.txt">seven</a></li>
|
||||
<li><a href="/data/numbers/8.txt">eight</a></li>
|
||||
<li><a href="/data/numbers/9.txt">nine</a></li>
|
||||
<li><a href="/data/numbers/10.txt">ten</a></li>
|
||||
<li><a href="/data/numbers/11.txt">tenorhorn posaune (eleven)</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
@ -140,3 +140,80 @@ Feature: Using hints
|
||||
Then the following tabs should be open:
|
||||
- data/hints/iframe_target.html
|
||||
- data/hello.txt (active)
|
||||
|
||||
### hints -> auto-follow-timeout
|
||||
|
||||
Scenario: Ignoring key presses after auto-following hints
|
||||
When I set hints -> auto-follow-timeout to 200
|
||||
And I set hints -> mode to number
|
||||
And I run :bind --force , message-error "This should not happen"
|
||||
And I open data/hints/html/simple.html
|
||||
And I run :hint all
|
||||
And I press the key "f"
|
||||
And I wait until data/hello.txt is loaded
|
||||
And I press the key ","
|
||||
# Waiting here so we don't affect the next test
|
||||
And I wait for "Releasing inhibition state of normal mode." in the log
|
||||
Then "Ignoring key ',', because the normal mode is currently inhibited." should be logged
|
||||
|
||||
Scenario: Turning off auto-follow-timeout
|
||||
When I set hints -> auto-follow-timeout to 0
|
||||
And I set hints -> mode to number
|
||||
And I run :bind --force , message-info "Keypress worked!"
|
||||
And I open data/hints/html/simple.html
|
||||
And I run :hint all
|
||||
And I press the key "f"
|
||||
And I wait until data/hello.txt is loaded
|
||||
And I press the key ","
|
||||
Then the message "Keypress worked!" should be shown
|
||||
|
||||
### Number hint mode
|
||||
|
||||
# https://github.com/The-Compiler/qutebrowser/issues/308
|
||||
Scenario: Renumbering hints when filtering
|
||||
When I open data/hints/number.html
|
||||
And I set hints -> mode to number
|
||||
And I run :hint all
|
||||
And I press the key "s"
|
||||
And I run :follow-hint 1
|
||||
Then data/numbers/7.txt should be loaded
|
||||
|
||||
# https://github.com/The-Compiler/qutebrowser/issues/576
|
||||
Scenario: Keeping hint filter in rapid mode
|
||||
When I open data/hints/number.html
|
||||
And I set hints -> mode to number
|
||||
And I run :hint all tab-bg --rapid
|
||||
And I press the key "t"
|
||||
And I run :follow-hint 0
|
||||
And I run :follow-hint 1
|
||||
Then data/numbers/2.txt should be loaded
|
||||
And data/numbers/3.txt should be loaded
|
||||
|
||||
# https://github.com/The-Compiler/qutebrowser/issues/1186
|
||||
Scenario: Keeping hints filter when using backspace
|
||||
When I open data/hints/issue1186.html
|
||||
And I set hints -> mode to number
|
||||
And I run :hint all
|
||||
And I press the key "x"
|
||||
And I press the key "0"
|
||||
And I press the key "<Backspace>"
|
||||
And I run :follow-hint 11
|
||||
Then the error "No hint 11!" should be shown
|
||||
|
||||
# https://github.com/The-Compiler/qutebrowser/issues/674#issuecomment-165096744
|
||||
Scenario: Multi-word matching
|
||||
When I open data/hints/number.html
|
||||
And I set hints -> mode to number
|
||||
And I set hints -> auto-follow to true
|
||||
And I set hints -> auto-follow-timeout to 0
|
||||
And I run :hint all
|
||||
And I press the keys "ten pos"
|
||||
Then data/numbers/11.txt should be loaded
|
||||
|
||||
Scenario: Scattering is ignored with number hints
|
||||
When I open data/hints/number.html
|
||||
And I set hints -> mode to number
|
||||
And I set hints -> scatter to true
|
||||
And I run :hint all
|
||||
And I run :follow-hint 00
|
||||
Then data/numbers/1.txt should be loaded
|
||||
|
Loading…
Reference in New Issue
Block a user