Merge branch 'rcorre-keyhint_fix'

This commit is contained in:
Florian Bruhin 2016-05-20 13:07:47 +02:00
commit d67bfc8c45
6 changed files with 56 additions and 23 deletions

View File

@ -24,7 +24,7 @@ from PyQt5.QtCore import Qt, QTimer, pyqtSlot
from qutebrowser.browser import webview from qutebrowser.browser import webview
from qutebrowser.config import config, configdata from qutebrowser.config import config, configdata
from qutebrowser.utils import objreg, log, qtutils from qutebrowser.utils import objreg, log, qtutils, utils
from qutebrowser.commands import cmdutils from qutebrowser.commands import cmdutils
from qutebrowser.completion.models import base from qutebrowser.completion.models import base
@ -57,7 +57,7 @@ class CommandCompletionModel(base.BaseCompletionModel):
cmd_to_keys = defaultdict(list) cmd_to_keys = defaultdict(list)
for key, cmd in keyconf.get_bindings_for('normal').items(): for key, cmd in keyconf.get_bindings_for('normal').items():
# put special bindings last # put special bindings last
if key.startswith('<') and key.endswith('>'): if utils.is_special_key(key):
cmd_to_keys[cmd].append(key) cmd_to_keys[cmd].append(key)
else: else:
cmd_to_keys[cmd].insert(0, key) cmd_to_keys[cmd].insert(0, key)

View File

@ -335,7 +335,7 @@ class BaseKeyParser(QObject):
def _parse_key_command(self, modename, key, cmd): def _parse_key_command(self, modename, key, cmd):
"""Parse the keys and their command and store them in the object.""" """Parse the keys and their command and store them in the object."""
if key.startswith('<') and key.endswith('>'): if utils.is_special_key(key):
keystr = utils.normalize_keystr(key[1:-1]) keystr = utils.normalize_keystr(key[1:-1])
self.special_bindings[keystr] = cmd self.special_bindings[keystr] = cmd
elif self._supports_chains: elif self._supports_chains:

View File

@ -94,28 +94,31 @@ class KeyHintView(QLabel):
self.hide() self.hide()
return return
keyconf = objreg.get('key-config')
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 not bindings:
return
self.show() self.show()
suffix_color = html.escape(config.get('colors', 'keyhint.fg.suffix')) suffix_color = html.escape(config.get('colors', 'keyhint.fg.suffix'))
text = '' text = ''
keyconf = objreg.get('key-config') for key, cmd in bindings:
# this is only fired in normal mode text += (
for key, cmd in keyconf.get_bindings_for(modename).items(): "<tr>"
# for now, special keys can't be part of keychains, so ignore them "<td>{}</td>"
is_special_binding = key.startswith('<') and key.endswith('>') "<td style='color: {}'>{}</td>"
if key.startswith(prefix) and not is_special_binding: "<td style='padding-left: 2ex'>{}</td>"
text += ( "</tr>"
"<tr>" ).format(
"<td>{}</td>" html.escape(prefix),
"<td style='color: {}'>{}</td>" suffix_color,
"<td style='padding-left: 2ex'>{}</td>" html.escape(key[len(prefix):]),
"</tr>" html.escape(cmd)
).format( )
html.escape(prefix),
suffix_color,
html.escape(key[len(prefix):]),
html.escape(cmd)
)
text = '<table>{}</table>'.format(text) text = '<table>{}</table>'.format(text)
self.setText(text) self.setText(text)

View File

@ -440,9 +440,14 @@ class KeyParseError(Exception):
super().__init__("Could not parse {!r}: {}".format(keystr, error)) super().__init__("Could not parse {!r}: {}".format(keystr, error))
def is_special_key(keystr):
"""True if keystr is a 'special' keystring (e.g. <ctrl-x> or <space>)."""
return keystr.startswith('<') and keystr.endswith('>')
def _parse_single_key(keystr): def _parse_single_key(keystr):
"""Convert a single key string to a (Qt.Key, Qt.Modifiers, text) tuple.""" """Convert a single key string to a (Qt.Key, Qt.Modifiers, text) tuple."""
if keystr.startswith('<') and keystr.endswith('>'): if is_special_key(keystr):
# Special key # Special key
keystr = keystr[1:-1] keystr = keystr[1:-1]
elif len(keystr) == 1: elif len(keystr) == 1:
@ -489,7 +494,7 @@ def _parse_single_key(keystr):
def parse_keystring(keystr): def parse_keystring(keystr):
"""Parse a keystring like <Ctrl-x> or xyz and return a KeyInfo list.""" """Parse a keystring like <Ctrl-x> or xyz and return a KeyInfo list."""
if keystr.startswith('<') and keystr.endswith('>'): if is_special_key(keystr):
return [_parse_single_key(keystr)] return [_parse_single_key(keystr)]
else: else:
return [_parse_single_key(char) for char in keystr] return [_parse_single_key(char) for char in keystr]

View File

@ -110,3 +110,12 @@ def test_color_switch(keyhint, config_stub, key_config_stub):
('aa', 'cmd-aa')])) ('aa', 'cmd-aa')]))
keyhint.update_keyhint('normal', 'a') keyhint.update_keyhint('normal', 'a')
assert keyhint.text() == expected_text(('a', '#ABCDEF', 'a', 'cmd-aa')) assert keyhint.text() == expected_text(('a', '#ABCDEF', 'a', 'cmd-aa'))
def test_no_matches(keyhint, key_config_stub):
"""Ensure the widget isn't visible if there are no keystrings to show."""
key_config_stub.set_bindings_for('normal', OrderedDict([
('aa', 'cmd-aa'),
('ab', 'cmd-ab')]))
keyhint.update_keyhint('normal', 'z')
assert not keyhint.isVisible()

View File

@ -984,3 +984,19 @@ class TestGetSetClipboard:
def test_supports_selection(self, clipboard_mock, selection): def test_supports_selection(self, clipboard_mock, selection):
clipboard_mock.supportsSelection.return_value = selection clipboard_mock.supportsSelection.return_value = selection
assert utils.supports_selection() == selection assert utils.supports_selection() == selection
@pytest.mark.parametrize('keystr, expected', [
('<Control-x>', True),
('<Meta-x>', True),
('<Ctrl-Alt-y>', True),
('x', False),
('X', False),
('<Escape>', True),
('foobar', False),
('foo>', False),
('<foo', False),
('<<', False),
])
def test_is_special_key(keystr, expected):
assert utils.is_special_key(keystr) == expected