diff --git a/qutebrowser/keyinput/basekeyparser.py b/qutebrowser/keyinput/basekeyparser.py index ec1004316..27d760d9a 100644 --- a/qutebrowser/keyinput/basekeyparser.py +++ b/qutebrowser/keyinput/basekeyparser.py @@ -88,69 +88,6 @@ class BaseKeyParser(QObject): if self.do_log: log.keyboard.debug(message) - def _handle_key(self, e): - """Handle a new keypress. - - Separate the keypress into count/command, then check if it matches - any possible command, and either run the command, ignore it, or - display an error. - - Args: - e: the KeyPressEvent from Qt. - - Return: - A QKeySequence match or None. - """ - key = e.key() - txt = keyutils.keyevent_to_string(e) - self._debug_log("Got key: 0x{:x} / text: '{}'".format(key, txt)) - - if not txt: - self._debug_log("Ignoring, no text char") - return QKeySequence.NoMatch - - # if len(txt) == 1: - # category = unicodedata.category(txt) - # is_control_char = (category == 'Cc') - # else: - # is_control_char = False - - # if (not txt) or is_control_char: - # self._debug_log("Ignoring, no text char") - # return QKeySequence.NoMatch - - if (txt.isdigit() and self._supports_count and not - (not self._count and txt == '0')): - assert len(txt) == 1, txt - self._count += txt - return None - - sequence = self._sequence.append_event(e) - match, binding = self._match_key(sequence) - if match == QKeySequence.NoMatch: - mappings = config.val.bindings.key_mappings - mapped = mappings.get(sequence, None) - if mapped is not None: - match, binding = self._match_key(mapped) - - self._sequence = self._sequence.append_event(e) - if match == QKeySequence.ExactMatch: - self._debug_log("Definitive match for '{}'.".format( - self._sequence)) - count = int(self._count) if self._count else None - self.clear_keystring() - self.execute(binding, count) - elif match == QKeySequence.PartialMatch: - self._debug_log("No match for '{}' (added {})".format( - self._sequence, txt)) - elif match == QKeySequence.NoMatch: - self._debug_log("Giving up with '{}', no matches".format( - self._sequence)) - self.clear_keystring() - else: - raise utils.Unreachable("Invalid match value {!r}".format(match)) - return match - def _match_key(self, sequence): """Try to match a given keystring with any bound keychain. @@ -175,21 +112,70 @@ class BaseKeyParser(QObject): return (QKeySequence.NoMatch, None) def handle(self, e): - """Handle a new keypress and call the respective handlers. + """Handle a new keypress. + + Separate the keypress into count/command, then check if it matches + any possible command, and either run the command, ignore it, or + display an error. Args: - e: the KeyPressEvent from Qt + e: the KeyPressEvent from Qt. Return: - True if the event was handled, False otherwise. + A QKeySequence match. """ - match = self._handle_key(e) + key = e.key() + txt = keyutils.keyevent_to_string(e) + self._debug_log("Got key: 0x{:x} / text: '{}'".format(key, txt)) - # don't emit twice if the keystring was cleared in self.clear_keystring - if self._sequence: + if not txt: + self._debug_log("Ignoring, no text char") + return QKeySequence.NoMatch + + # if len(txt) == 1: + # category = unicodedata.category(txt) + # is_control_char = (category == 'Cc') + # else: + # is_control_char = False + + # if (not txt) or is_control_char: + # self._debug_log("Ignoring, no text char") + # return QKeySequence.NoMatch + + if (txt.isdigit() and self._supports_count and not + (not self._count and txt == '0')): + assert len(txt) == 1, txt + self._count += txt + return QKeySequence.ExactMatch + + sequence = self._sequence.append_event(e) + match, binding = self._match_key(sequence) + if match == QKeySequence.NoMatch: + mappings = config.val.bindings.key_mappings + mapped = mappings.get(sequence, None) + if mapped is not None: + match, binding = self._match_key(mapped) + + self._sequence = self._sequence.append_event(e) + + if match == QKeySequence.ExactMatch: + self._debug_log("Definitive match for '{}'.".format( + self._sequence)) + count = int(self._count) if self._count else None + self.clear_keystring() + self.execute(binding, count) + elif match == QKeySequence.PartialMatch: + self._debug_log("No match for '{}' (added {})".format( + self._sequence, txt)) self.keystring_updated.emit(self._count + str(self._sequence)) + elif match == QKeySequence.NoMatch: + self._debug_log("Giving up with '{}', no matches".format( + self._sequence)) + self.clear_keystring() + else: + raise utils.Unreachable("Invalid match value {!r}".format(match)) - return match != QKeySequence.NoMatch + return match @config.change_filter('bindings') def _on_config_changed(self): diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index 75e3af367..94d76832d 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -157,7 +157,7 @@ class ModeManager(QObject): if curmode != usertypes.KeyMode.insert: log.modes.debug("got keypress in mode {} - delegating to " "{}".format(curmode, utils.qualname(parser))) - handled = parser.handle(event) + match = parser.handle(event) is_non_alnum = ( event.modifiers() not in [Qt.NoModifier, Qt.ShiftModifier] or @@ -165,7 +165,7 @@ class ModeManager(QObject): forward_unbound_keys = config.val.input.forward_unbound_keys - if handled: + if match: filter_this = True elif (parser.passthrough or forward_unbound_keys == 'all' or (forward_unbound_keys == 'auto' and is_non_alnum)): @@ -178,10 +178,10 @@ class ModeManager(QObject): if curmode != usertypes.KeyMode.insert: focus_widget = QApplication.instance().focusWidget() - log.modes.debug("handled: {}, forward_unbound_keys: {}, " + log.modes.debug("match: {}, forward_unbound_keys: {}, " "passthrough: {}, is_non_alnum: {} --> " "filter: {} (focused: {!r})".format( - handled, forward_unbound_keys, + match, forward_unbound_keys, parser.passthrough, is_non_alnum, filter_this, focus_widget)) return filter_this diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index 9c44e4818..f397b9fd5 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -153,8 +153,8 @@ class HintKeyParser(keyparser.CommandKeyParser): self._read_config('hint') self.keystring_updated.connect(self.on_keystring_updated) - def _handle_special_key(self, e): - """Override _handle_special_key to handle string filtering. + def _handle_filter_key(self, e): + """Handle keys for string filtering. Return True if the keypress has been handled, and False if not. @@ -162,10 +162,8 @@ class HintKeyParser(keyparser.CommandKeyParser): e: the KeyPressEvent from Qt. Return: - True if event has been handled, False otherwise. + A QKeySequence match. """ - # FIXME rewrite this - # FIXME should backspacing be a more generic hint feature? log.keyboard.debug("Got special key 0x{:x} text {}".format( e.key(), e.text())) hintmanager = objreg.get('hintmanager', scope='tab', @@ -178,7 +176,7 @@ class HintKeyParser(keyparser.CommandKeyParser): if self._last_press == LastPress.filtertext and self._filtertext: self._filtertext = self._filtertext[:-1] hintmanager.filter_hints(self._filtertext) - return True + return QKeySequence.ExactMatch elif self._last_press == LastPress.keystring and self._sequence: self._sequence = self._sequence[:-1] self.keystring_updated.emit(str(self._sequence)) @@ -187,18 +185,18 @@ class HintKeyParser(keyparser.CommandKeyParser): # in numeric mode after the number has been deleted). hintmanager.filter_hints(self._filtertext) self._last_press = LastPress.filtertext - return True + return QKeySequence.ExactMatch else: - return False + return QKeySequence.NoMatch elif hintmanager.current_mode() != 'number': - return False + return QKeySequence.NoMatch elif not e.text(): - return False + return QKeySequence.NoMatch else: self._filtertext += e.text() hintmanager.filter_hints(self._filtertext) self._last_press = LastPress.filtertext - return True + return QKeySequence.ExactMatch def handle(self, e): """Handle a new keypress and call the respective handlers. @@ -209,25 +207,18 @@ class HintKeyParser(keyparser.CommandKeyParser): Returns: True if the match has been handled, False otherwise. """ - # FIXME rewrite this - match = self._handle_key(e) + match = super().handle(e) if match == QKeySequence.PartialMatch: - # FIXME do we need to check self._sequence here? - self.keystring_updated.emit(str(self._sequence)) self._last_press = LastPress.keystring - return True elif match == QKeySequence.ExactMatch: self._last_press = LastPress.none - return True - elif match is None: # FIXME - return None elif match == QKeySequence.NoMatch: # We couldn't find a keychain so we check if it's a special key. - return self._handle_special_key(e) + return self._handle_filter_key(e) else: raise ValueError("Got invalid match type {}!".format(match)) - return match != QKeySequence.NoMatch + return match def update_bindings(self, strings, preserve_filter=False): """Update bindings when the hint strings changed. @@ -285,15 +276,16 @@ class RegisterKeyParser(keyparser.CommandKeyParser): Return: True if event has been handled, False otherwise. """ - # FIXME rewrite this - if super().handle(e): - return True + match = super().handle(e) + if match: + return match key = e.text() if key == '' or keyutils.keyevent_to_string(e) is None: # this is not a proper register key, let it pass and keep going - return False + # FIXME can we simplify this when we refactor keyutils.py? + return QKeySequence.NoMatch tabbed_browser = objreg.get('tabbed-browser', scope='window', window=self._win_id) @@ -315,5 +307,4 @@ class RegisterKeyParser(keyparser.CommandKeyParser): message.error(str(err), stack=traceback.format_exc()) self.request_leave.emit(self._mode, "valid register key", True) - - return True + return QKeySequence.ExactMatch