Add a utils.parse_keystring.

This commit is contained in:
Florian Bruhin 2015-11-19 07:35:14 +01:00
parent 9a310dd1fb
commit 1ec03462c8
2 changed files with 134 additions and 0 deletions

View File

@ -391,6 +391,100 @@ def keyevent_to_string(e):
return '+'.join(parts)
class KeyInfo:
"""Stores information about a key, like used in a QKeyEvent.
Attributes:
key: Qt::Key
modifiers: Qt::KeyboardModifiers
text: str
"""
def __init__(self, key, modifiers, text):
self.key = key
self.modifiers = modifiers
self.text = text
def __repr__(self):
# Meh, dependency cycle...
from qutebrowser.utils.debug import qenum_key
if self.modifiers is None:
modifiers = None
else:
#modifiers = qflags_key(Qt, self.modifiers)
modifiers = hex(int(self.modifiers))
return get_repr(self, constructor=True, key=qenum_key(Qt, self.key),
modifiers=modifiers, text=self.text)
def __eq__(self, other):
return (self.key == other.key and self.modifiers == other.modifiers and
self.text == other.text)
class KeyParseError(Exception):
"""Raised by _parse_single_key/parse_keystring on parse errors."""
def __init__(self, keystr, error):
super().__init__("Could not parse {!r}: {}".format(keystr, error))
def _parse_single_key(keystr):
"""Convert a single key string to a (Qt.Key, Qt.Modifiers, text) tuple."""
if keystr.startswith('<') and keystr.endswith('>'):
# Special key
keystr = keystr[1:-1]
elif len(keystr) == 1:
# vim-like key
pass
else:
raise KeyParseError(keystr, "Expecting either a single key or a "
"<Ctrl-x> like keybinding.")
seq = QKeySequence(normalize_keystr(keystr), QKeySequence.PortableText)
if len(seq) != 1:
raise KeyParseError(keystr, "Got {} keys instead of 1.".format(
len(seq)))
result = seq[0]
if result == Qt.Key_unknown:
raise KeyParseError(keystr, "Got unknown key.")
modifier_mask = int(Qt.ShiftModifier | Qt.ControlModifier |
Qt.AltModifier | Qt.MetaModifier | Qt.KeypadModifier |
Qt.GroupSwitchModifier)
assert Qt.Key_unknown & ~modifier_mask == Qt.Key_unknown
modifiers = result & modifier_mask
key = result & ~modifier_mask
if len(keystr) == 1 and keystr.isupper():
modifiers |= Qt.ShiftModifier
assert key != 0, key
key = Qt.Key(key)
modifiers = Qt.KeyboardModifiers(modifiers)
# Let's hope this is accurate...
if len(keystr) == 1 and not modifiers:
text = keystr
elif len(keystr) == 1 and modifiers == Qt.ShiftModifier:
text = keystr.upper()
else:
text = ''
return KeyInfo(key, modifiers, text)
def parse_keystring(keystr):
"""Parse a keystring like <Ctrl-x> or xyz and return a KeyInfo list."""
if keystr.startswith('<') and keystr.endswith('>'):
return [_parse_single_key(keystr)]
else:
return [_parse_single_key(char) for char in keystr]
def normalize_keystr(keystr):
"""Normalize a keystring like Ctrl-Q to a keystring like Ctrl+Q.

View File

@ -500,6 +500,46 @@ class TestKeyEventToString:
assert utils.keyevent_to_string(evt) == 'Meta+A'
@pytest.mark.parametrize('keystr, expected', [
('<Control-x>', utils.KeyInfo(Qt.Key_X, Qt.ControlModifier, '')),
('<Meta-x>', utils.KeyInfo(Qt.Key_X, Qt.MetaModifier, '')),
('<Ctrl-Alt-y>',
utils.KeyInfo(Qt.Key_Y, Qt.ControlModifier | Qt.AltModifier, '')),
('x', utils.KeyInfo(Qt.Key_X, Qt.NoModifier, 'x')),
('X', utils.KeyInfo(Qt.Key_X, Qt.ShiftModifier, 'X')),
('<Escape>', utils.KeyInfo(Qt.Key_Escape, Qt.NoModifier, '')),
('foobar', utils.KeyParseError),
('x, y', utils.KeyParseError),
('xyz', utils.KeyParseError),
('Escape', utils.KeyParseError),
('<Ctrl-x>, <Ctrl-y>', utils.KeyParseError),
])
def test_parse_single_key(keystr, expected):
if expected is utils.KeyParseError:
with pytest.raises(utils.KeyParseError):
utils._parse_single_key(keystr)
else:
assert utils._parse_single_key(keystr) == expected
@pytest.mark.parametrize('keystr, expected', [
('<Control-x>', [utils.KeyInfo(Qt.Key_X, Qt.ControlModifier, '')]),
('x', [utils.KeyInfo(Qt.Key_X, Qt.NoModifier, 'x')]),
('xy', [utils.KeyInfo(Qt.Key_X, Qt.NoModifier, 'x'),
utils.KeyInfo(Qt.Key_Y, Qt.NoModifier, 'y')]),
('<Control-x><Meta-x>', utils.KeyParseError),
])
def test_parse_keystring(keystr, expected):
if expected is utils.KeyParseError:
with pytest.raises(utils.KeyParseError):
utils.parse_keystring(keystr)
else:
assert utils.parse_keystring(keystr) == expected
@pytest.mark.parametrize('orig, repl', [
('Control+x', 'ctrl+x'),
('Windows+x', 'meta+x'),