Repository: qutebrowser/qutebrowser

Issue:
<Esc> should probably break the keychain
I have `partial-timeout` set to `0` (no timeout)

If I type: `g<Esc>`, if will not "break" the keychain, the `g` wll stay in the bottom-right corner.
If I then type `gt` again, I've effectivly typed `ggt`, and end up with a "partial" `t` in the bottom-right corner.

I found this rather unexpected; in Vim, this _does_ break the keychain. I would expect `g<Esc>gt` to execute `gt`.



Discussion:
This is actually implemented (but broken)...

From `keyinput/basekeyparser.py`:

``` python
def _handle_single_key(self, e):
    // [...]

    if key == Qt.Key_Escape:
        self._debug_log("Escape pressed, discarding '{}'.".format(
            self._keystring))
        self._keystring = ''
        return self.Match.none
```

This doesn't work anymore because `<Escape>` was bound to clear the current search in 75ac9cce49271967ca3042bd7b368d55c6785a9a.

I guess that hardcoded binding should be removed, and instead a new hidden `clear-keychain` command should be added - then the default binding can be changed to do both.

I also just noticed that the keys are persists when navigating. So if I press `y`, and then click a link in the current tab, the `y` will stay in the bottom-right.

I would expect this to be cleared by the mouse action?


Resolving patch:
diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py
--- a/qutebrowser/config/configdata.py
+++ b/qutebrowser/config/configdata.py
@@ -1143,7 +1143,7 @@ def data(readonly=False):
     ])),
 
     ('normal', collections.OrderedDict([
-        ('search', ['<Escape>']),
+        ('search ;; clear-keychain', ['<Escape>']),
         ('set-cmd-text -s :open', ['o']),
         ('set-cmd-text :open {url}', ['go']),
         ('set-cmd-text -s :open -t', ['O']),
@@ -1354,4 +1354,6 @@ def data(readonly=False):
     (re.compile(r'^scroll 0 -50$'), r'scroll up'),
     (re.compile(r'^scroll 50 0$'), r'scroll right'),
     (re.compile(r'^scroll ([-\d]+ [-\d]+)$'), r'scroll-px \1'),
+
+    (re.compile(r'^search$'), r'search ;; clear-keychain'),
 ]
diff --git a/qutebrowser/keyinput/basekeyparser.py b/qutebrowser/keyinput/basekeyparser.py
--- a/qutebrowser/keyinput/basekeyparser.py
+++ b/qutebrowser/keyinput/basekeyparser.py
@@ -165,12 +165,6 @@ def _handle_single_key(self, e):
         key = e.key()
         self._debug_log("Got key: 0x{:x} / text: '{}'".format(key, txt))
 
-        if key == Qt.Key_Escape:
-            self._debug_log("Escape pressed, discarding '{}'.".format(
-                self._keystring))
-            self._keystring = ''
-            return self.Match.none
-
         if len(txt) == 1:
             category = unicodedata.category(txt)
             is_control_char = (category == 'Cc')
@@ -306,6 +300,7 @@ def handle(self, e):
             True if the event was handled, False otherwise.
         """
         handled = self._handle_special_key(e)
+
         if handled or not self._supports_chains:
             return handled
         match = self._handle_single_key(e)
@@ -362,3 +357,9 @@ def on_keyconfig_changed(self, mode):
                                  "defined!")
         if mode == self._modename:
             self.read_config()
+
+    def clear_keystring(self):
+        """Clear the currently entered key sequence."""
+        self._debug_log("discarding keystring '{}'.".format(self._keystring))
+        self._keystring = ''
+        self.keystring_updated.emit(self._keystring)
diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py
--- a/qutebrowser/keyinput/modeman.py
+++ b/qutebrowser/keyinput/modeman.py
@@ -317,3 +317,8 @@ def eventFilter(self, event):
             return self._eventFilter_keypress(event)
         else:
             return self._eventFilter_keyrelease(event)
+
+    @cmdutils.register(instance='mode-manager', scope='window', hide=True)
+    def clear_keychain(self):
+        """Clear the currently entered key chain."""
+        self._parsers[self.mode].clear_keystring()


Test patch: