Add a keyhint blacklist.

Replace the setting ui.show-keyhints with ui.keyhint-blacklist, which
is a list of globs for keychains that shouldn't be hinted. This allows
users to prevent showing keyhints for keychains they already know.

keyhint-blacklist='*' is equivalent to show-keyhints=False.

Resolves #1515.
This commit is contained in:
Ryan Roden-Corrent 2016-05-23 21:12:52 -04:00
parent b1aaf0f10f
commit f58e2d91dc
4 changed files with 52 additions and 27 deletions

View File

@ -341,6 +341,7 @@ class ConfigManager(QObject):
('colors', 'tab.indicator.system'): 'tabs.indicator.system',
('completion', 'history-length'): 'cmd-history-max-items',
('colors', 'downloads.fg'): 'downloads.fg.start',
('ui', 'show-keyhints'): 'keyhint-blacklist',
}
DELETED_OPTIONS = [
('colors', 'tab.separator'),
@ -360,6 +361,8 @@ class ConfigManager(QObject):
_get_value_transformer({'false': '-1', 'true': '1000'}),
('general', 'log-javascript-console'):
_get_value_transformer({'false': 'none', 'true': 'debug'}),
('ui', 'keyhint-blacklist'):
_get_value_transformer({'false': '*', 'true': ''}),
}
changed = pyqtSignal(str, str)

View File

@ -355,9 +355,11 @@ def data(readonly=False):
"Hide the window decoration when using wayland "
"(requires restart)"),
('show-keyhints',
SettingValue(typ.Bool(), 'true'),
"Show possible keychains based on the current keystring"),
('keyhint-blacklist',
SettingValue(typ.List(none_ok=True), ''),
"Keychains that shouldn't be shown in the keyhint dialog\n\n"
"Globs are supported, so ';*' will blacklist all keychains"
"starting with ';'. Use '*' to disable keyhints"),
readonly=readonly
)),

View File

@ -25,6 +25,7 @@ It is intended to help discoverability of keybindings.
"""
import html
import fnmatch
from PyQt5.QtWidgets import QLabel, QSizePolicy
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt
@ -39,7 +40,6 @@ class KeyHintView(QLabel):
Attributes:
_win_id: Window ID of parent.
_enabled: If False, do not show the window at all
Signals:
reposition_keyhint: Emitted when this widget should be resized.
@ -61,9 +61,6 @@ class KeyHintView(QLabel):
super().__init__(parent)
self.setTextFormat(Qt.RichText)
self._win_id = win_id
self.set_enabled()
cfg = objreg.get('config')
cfg.changed.connect(self.set_enabled)
style.set_register_stylesheet(self)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Minimum)
self.hide()
@ -74,13 +71,6 @@ class KeyHintView(QLabel):
def __repr__(self):
return utils.get_repr(self, win_id=self._win_id)
@config.change_filter('ui', 'show-keyhints')
def set_enabled(self):
"""Update self._enabled when the config changed."""
self._enabled = config.get('ui', 'show-keyhints')
if not self._enabled:
self.hide()
def showEvent(self, e):
"""Adjust the keyhint size when it's freshly shown."""
self.reposition_keyhint.emit()
@ -93,15 +83,22 @@ class KeyHintView(QLabel):
Args:
prefix: The current partial keystring.
"""
if not prefix or not self._enabled:
if not prefix:
self._show_timer.stop()
self.hide()
return
blacklist = config.get('ui', 'keyhint-blacklist') or []
keyconf = objreg.get('key-config')
def blacklisted(keychain):
return any(fnmatch.fnmatchcase(keychain, glob)
for glob in blacklist)
bindings = [(k, v) for (k, v)
in keyconf.get_bindings_for(modename).items()
if k.startswith(prefix) and not utils.is_special_key(k)]
if k.startswith(prefix) and not utils.is_special_key(k) and
not blacklisted(k)]
if not bindings:
self._show_timer.stop()

View File

@ -34,7 +34,6 @@ def expected_text(*args):
"""
text = '<table>'
for group in args:
print("group = {}".format(group))
text += ("<tr>"
"<td>{}</td>"
"<td style='color: {}'>{}</td>"
@ -54,7 +53,7 @@ def keyhint(qtbot, config_stub, key_config_stub):
'keyhint.bg': 'black'
},
'fonts': {'keyhint': 'Comic Sans'},
'ui': {'show-keyhints': True},
'ui': {'keyhint-blacklist': ''},
}
keyhint = KeyHintView(0, None)
qtbot.add_widget(keyhint)
@ -63,7 +62,7 @@ def keyhint(qtbot, config_stub, key_config_stub):
def test_suggestions(keyhint, key_config_stub):
"""Test cursor position based on the prompt."""
"""Test that keyhints are shown based on a prefix."""
# we want the dict to return sorted items() for reliable testing
key_config_stub.set_bindings_for('normal', OrderedDict([
('aa', 'cmd-aa'),
@ -95,14 +94,6 @@ def test_special_bindings(keyhint, key_config_stub):
('&lt;', 'yellow', 'b', 'cmd-&lt;b'))
def test_disable(keyhint, config_stub):
"""Ensure the widget isn't visible if disabled."""
config_stub.set('ui', 'show-keyhints', False)
keyhint.update_keyhint('normal', 'a')
assert not keyhint.text()
assert not keyhint.isVisible()
def test_color_switch(keyhint, config_stub, key_config_stub):
"""Ensure the the keyhint suffix color can be updated at runtime."""
config_stub.set('colors', 'keyhint.fg.suffix', '#ABCDEF')
@ -120,3 +111,35 @@ def test_no_matches(keyhint, key_config_stub):
keyhint.update_keyhint('normal', 'z')
assert not keyhint.text()
assert not keyhint.isVisible()
def test_blacklist(keyhint, config_stub, key_config_stub):
"""Test that blacklisted keychaints aren't hinted."""
config_stub.set('ui', 'keyhint-blacklist', ['ab*'])
# we want the dict to return sorted items() for reliable testing
key_config_stub.set_bindings_for('normal', OrderedDict([
('aa', 'cmd-aa'),
('ab', 'cmd-ab'),
('aba', 'cmd-aba'),
('abb', 'cmd-abb'),
('xd', 'cmd-xd'),
('xe', 'cmd-xe')]))
keyhint.update_keyhint('normal', 'a')
assert keyhint.text() == expected_text(('a', 'yellow', 'a', 'cmd-aa'))
def test_blacklist_all(keyhint, config_stub, key_config_stub):
"""Test that setting the blacklist to * disables keyhints."""
config_stub.set('ui', 'keyhint-blacklist', ['*'])
# we want the dict to return sorted items() for reliable testing
key_config_stub.set_bindings_for('normal', OrderedDict([
('aa', 'cmd-aa'),
('ab', 'cmd-ab'),
('aba', 'cmd-aba'),
('abb', 'cmd-abb'),
('xd', 'cmd-xd'),
('xe', 'cmd-xe')]))
keyhint.update_keyhint('normal', 'a')
assert not keyhint.text()