We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
q<Fn> tries to record macro
I'm not sure if this is solved in #2113, but currently when doing `q<Fn>` I get `Recording macro ''...`

cc @blyxxyz

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `qutebrowser/keyinput/modeparsers.py`
Content:
```
1 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
2 
3 # Copyright 2014-2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
4 #
5 # This file is part of qutebrowser.
6 #
7 # qutebrowser is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # qutebrowser is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.
19 
20 """KeyChainParser for "hint" and "normal" modes.
21 
22 Module attributes:
23     STARTCHARS: Possible chars for starting a commandline input.
24 """
25 
26 import traceback
27 
28 from PyQt5.QtCore import pyqtSlot, Qt
29 
30 from qutebrowser.commands import cmdexc
31 from qutebrowser.config import config
32 from qutebrowser.keyinput import keyparser
33 from qutebrowser.utils import usertypes, log, message, objreg, utils
34 
35 
36 STARTCHARS = ":/?"
37 LastPress = usertypes.enum('LastPress', ['none', 'filtertext', 'keystring'])
38 
39 
40 class NormalKeyParser(keyparser.CommandKeyParser):
41 
42     """KeyParser for normal mode with added STARTCHARS detection and more.
43 
44     Attributes:
45         _partial_timer: Timer to clear partial keypresses.
46     """
47 
48     def __init__(self, win_id, parent=None):
49         super().__init__(win_id, parent, supports_count=True,
50                          supports_chains=True)
51         self.read_config('normal')
52         self._partial_timer = usertypes.Timer(self, 'partial-match')
53         self._partial_timer.setSingleShot(True)
54         self._inhibited = False
55         self._inhibited_timer = usertypes.Timer(self, 'normal-inhibited')
56         self._inhibited_timer.setSingleShot(True)
57 
58     def __repr__(self):
59         return utils.get_repr(self)
60 
61     def _handle_single_key(self, e):
62         """Override _handle_single_key to abort if the key is a startchar.
63 
64         Args:
65             e: the KeyPressEvent from Qt.
66 
67         Return:
68             A self.Match member.
69         """
70         txt = e.text().strip()
71         if self._inhibited:
72             self._debug_log("Ignoring key '{}', because the normal mode is "
73                 "currently inhibited.".format(txt))
74             return self.Match.none
75         match = super()._handle_single_key(e)
76         if match == self.Match.partial:
77             timeout = config.get('input', 'partial-timeout')
78             if timeout != 0:
79                 self._partial_timer.setInterval(timeout)
80                 self._partial_timer.timeout.connect(self._clear_partial_match)
81                 self._partial_timer.start()
82         return match
83 
84     def set_inhibited_timeout(self, timeout):
85         if timeout != 0:
86             self._debug_log("Inhibiting the normal mode for {}ms.".format(
87                 timeout))
88             self._inhibited = True
89             self._inhibited_timer.setInterval(timeout)
90             self._inhibited_timer.timeout.connect(self._clear_inhibited)
91             self._inhibited_timer.start()
92 
93     @pyqtSlot()
94     def _clear_partial_match(self):
95         """Clear a partial keystring after a timeout."""
96         self._debug_log("Clearing partial keystring {}".format(
97             self._keystring))
98         self._keystring = ''
99         self.keystring_updated.emit(self._keystring)
100 
101     @pyqtSlot()
102     def _clear_inhibited(self):
103         """Reset inhibition state after a timeout."""
104         self._debug_log("Releasing inhibition state of normal mode.")
105         self._inhibited = False
106 
107     @pyqtSlot()
108     def _stop_timers(self):
109         super()._stop_timers()
110         self._partial_timer.stop()
111         try:
112             self._partial_timer.timeout.disconnect(self._clear_partial_match)
113         except TypeError:
114             # no connections
115             pass
116         self._inhibited_timer.stop()
117         try:
118             self._inhibited_timer.timeout.disconnect(self._clear_inhibited)
119         except TypeError:
120             # no connections
121             pass
122 
123 
124 class PromptKeyParser(keyparser.CommandKeyParser):
125 
126     """KeyParser for yes/no prompts."""
127 
128     def __init__(self, win_id, parent=None):
129         super().__init__(win_id, parent, supports_count=False,
130                          supports_chains=True)
131         # We don't want an extra section for this in the config, so we just
132         # abuse the prompt section.
133         self.read_config('prompt')
134 
135     def __repr__(self):
136         return utils.get_repr(self)
137 
138 
139 class HintKeyParser(keyparser.CommandKeyParser):
140 
141     """KeyChainParser for hints.
142 
143     Attributes:
144         _filtertext: The text to filter with.
145         _last_press: The nature of the last keypress, a LastPress member.
146     """
147 
148     def __init__(self, win_id, parent=None):
149         super().__init__(win_id, parent, supports_count=False,
150                          supports_chains=True)
151         self._filtertext = ''
152         self._last_press = LastPress.none
153         self.read_config('hint')
154         self.keystring_updated.connect(self.on_keystring_updated)
155 
156     def _handle_special_key(self, e):
157         """Override _handle_special_key to handle string filtering.
158 
159         Return True if the keypress has been handled, and False if not.
160 
161         Args:
162             e: the KeyPressEvent from Qt.
163 
164         Return:
165             True if event has been handled, False otherwise.
166         """
167         log.keyboard.debug("Got special key 0x{:x} text {}".format(
168             e.key(), e.text()))
169         hintmanager = objreg.get('hintmanager', scope='tab',
170                                  window=self._win_id, tab='current')
171         if e.key() == Qt.Key_Backspace:
172             log.keyboard.debug("Got backspace, mode {}, filtertext '{}', "
173                                "keystring '{}'".format(self._last_press,
174                                                        self._filtertext,
175                                                        self._keystring))
176             if self._last_press == LastPress.filtertext and self._filtertext:
177                 self._filtertext = self._filtertext[:-1]
178                 hintmanager.filter_hints(self._filtertext)
179                 return True
180             elif self._last_press == LastPress.keystring and self._keystring:
181                 self._keystring = self._keystring[:-1]
182                 self.keystring_updated.emit(self._keystring)
183                 if not self._keystring and self._filtertext:
184                     # Switch back to hint filtering mode (this can happen only
185                     # in numeric mode after the number has been deleted).
186                     hintmanager.filter_hints(self._filtertext)
187                     self._last_press = LastPress.filtertext
188                 return True
189             else:
190                 return super()._handle_special_key(e)
191         elif hintmanager.current_mode() != 'number':
192             return super()._handle_special_key(e)
193         elif not e.text():
194             return super()._handle_special_key(e)
195         else:
196             self._filtertext += e.text()
197             hintmanager.filter_hints(self._filtertext)
198             self._last_press = LastPress.filtertext
199             return True
200 
201     def handle(self, e):
202         """Handle a new keypress and call the respective handlers.
203 
204         Args:
205             e: the KeyPressEvent from Qt
206 
207         Returns:
208             True if the match has been handled, False otherwise.
209         """
210         match = self._handle_single_key(e)
211         if match == self.Match.partial:
212             self.keystring_updated.emit(self._keystring)
213             self._last_press = LastPress.keystring
214             return True
215         elif match == self.Match.definitive:
216             self._last_press = LastPress.none
217             return True
218         elif match == self.Match.other:
219             pass
220         elif match == self.Match.none:
221             # We couldn't find a keychain so we check if it's a special key.
222             return self._handle_special_key(e)
223         else:
224             raise ValueError("Got invalid match type {}!".format(match))
225 
226     def execute(self, cmdstr, keytype, count=None):
227         """Handle a completed keychain."""
228         if not isinstance(keytype, self.Type):
229             raise TypeError("Type {} is no Type member!".format(keytype))
230         if keytype == self.Type.chain:
231             hintmanager = objreg.get('hintmanager', scope='tab',
232                                      window=self._win_id, tab='current')
233             hintmanager.handle_partial_key(cmdstr)
234         else:
235             # execute as command
236             super().execute(cmdstr, keytype, count)
237 
238     def update_bindings(self, strings, preserve_filter=False):
239         """Update bindings when the hint strings changed.
240 
241         Args:
242             strings: A list of hint strings.
243             preserve_filter: Whether to keep the current value of
244                              `self._filtertext`.
245         """
246         self.bindings = {s: s for s in strings}
247         if not preserve_filter:
248             self._filtertext = ''
249 
250     @pyqtSlot(str)
251     def on_keystring_updated(self, keystr):
252         """Update hintmanager when the keystring was updated."""
253         hintmanager = objreg.get('hintmanager', scope='tab',
254                                  window=self._win_id, tab='current')
255         hintmanager.handle_partial_key(keystr)
256 
257 
258 class CaretKeyParser(keyparser.CommandKeyParser):
259 
260     """KeyParser for caret mode."""
261 
262     passthrough = True
263 
264     def __init__(self, win_id, parent=None):
265         super().__init__(win_id, parent, supports_count=True,
266                          supports_chains=True)
267         self.read_config('caret')
268 
269 
270 class RegisterKeyParser(keyparser.CommandKeyParser):
271 
272     """KeyParser for modes that record a register key.
273 
274     Attributes:
275         _mode: One of KeyMode.set_mark, KeyMode.jump_mark, KeyMode.record_macro
276         and KeyMode.run_macro.
277     """
278 
279     def __init__(self, win_id, mode, parent=None):
280         super().__init__(win_id, parent, supports_count=False,
281                          supports_chains=False)
282         self._mode = mode
283         self.read_config('register')
284 
285     def handle(self, e):
286         """Override handle to always match the next key and use the register.
287 
288         Args:
289             e: the KeyPressEvent from Qt.
290 
291         Return:
292             True if event has been handled, False otherwise.
293         """
294         if super().handle(e):
295             return True
296 
297         if utils.keyevent_to_string(e) is None:
298             # this is a modifier key, let it pass and keep going
299             return False
300 
301         key = e.text()
302 
303         tabbed_browser = objreg.get('tabbed-browser', scope='window',
304                                     window=self._win_id)
305         macro_recorder = objreg.get('macro-recorder')
306 
307         try:
308             if self._mode == usertypes.KeyMode.set_mark:
309                 tabbed_browser.set_mark(key)
310             elif self._mode == usertypes.KeyMode.jump_mark:
311                 tabbed_browser.jump_mark(key)
312             elif self._mode == usertypes.KeyMode.record_macro:
313                 macro_recorder.record_macro(key)
314             elif self._mode == usertypes.KeyMode.run_macro:
315                 macro_recorder.run_macro(self._win_id, key)
316             else:
317                 raise ValueError(
318                     "{} is not a valid register mode".format(self._mode))
319         except (cmdexc.CommandMetaError, cmdexc.CommandError) as err:
320             message.error(str(err), stack=traceback.format_exc())
321 
322         self.request_leave.emit(self._mode, "valid register key", True)
323 
324         return True
325 
326     @pyqtSlot(str)
327     def on_keyconfig_changed(self, mode):
328         """RegisterKeyParser has no config section (no bindable keys)."""
329         pass
330
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py
--- a/qutebrowser/keyinput/modeparsers.py
+++ b/qutebrowser/keyinput/modeparsers.py
@@ -294,12 +294,12 @@
         if super().handle(e):
             return True
 
-        if utils.keyevent_to_string(e) is None:
-            # this is a modifier key, let it pass and keep going
-            return False
-
         key = e.text()
 
+        if key == '' or utils.keyevent_to_string(e) is None:
+            # this is not a proper register key, let it pass and keep going
+            return False
+
         tabbed_browser = objreg.get('tabbed-browser', scope='window',
                                     window=self._win_id)
         macro_recorder = objreg.get('macro-recorder')

{"golden_diff": "diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py\n--- a/qutebrowser/keyinput/modeparsers.py\n+++ b/qutebrowser/keyinput/modeparsers.py\n@@ -294,12 +294,12 @@\n         if super().handle(e):\n             return True\n \n-        if utils.keyevent_to_string(e) is None:\n-            # this is a modifier key, let it pass and keep going\n-            return False\n-\n         key = e.text()\n \n+        if key == '' or utils.keyevent_to_string(e) is None:\n+            # this is not a proper register key, let it pass and keep going\n+            return False\n+\n         tabbed_browser = objreg.get('tabbed-browser', scope='window',\n                                     window=self._win_id)\n         macro_recorder = objreg.get('macro-recorder')\n", "issue": "q<Fn> tries to record macro\nI'm not sure if this is solved in #2113, but currently when doing `q<Fn>` I get `Recording macro ''...`\r\n\r\ncc @blyxxyz\n", "before_files": [{"content": "# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:\n\n# Copyright 2014-2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>\n#\n# This file is part of qutebrowser.\n#\n# qutebrowser is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# qutebrowser is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.\n\n\"\"\"KeyChainParser for \"hint\" and \"normal\" modes.\n\nModule attributes:\n    STARTCHARS: Possible chars for starting a commandline input.\n\"\"\"\n\nimport traceback\n\nfrom PyQt5.QtCore import pyqtSlot, Qt\n\nfrom qutebrowser.commands import cmdexc\nfrom qutebrowser.config import config\nfrom qutebrowser.keyinput import keyparser\nfrom qutebrowser.utils import usertypes, log, message, objreg, utils\n\n\nSTARTCHARS = \":/?\"\nLastPress = usertypes.enum('LastPress', ['none', 'filtertext', 'keystring'])\n\n\nclass NormalKeyParser(keyparser.CommandKeyParser):\n\n    \"\"\"KeyParser for normal mode with added STARTCHARS detection and more.\n\n    Attributes:\n        _partial_timer: Timer to clear partial keypresses.\n    \"\"\"\n\n    def __init__(self, win_id, parent=None):\n        super().__init__(win_id, parent, supports_count=True,\n                         supports_chains=True)\n        self.read_config('normal')\n        self._partial_timer = usertypes.Timer(self, 'partial-match')\n        self._partial_timer.setSingleShot(True)\n        self._inhibited = False\n        self._inhibited_timer = usertypes.Timer(self, 'normal-inhibited')\n        self._inhibited_timer.setSingleShot(True)\n\n    def __repr__(self):\n        return utils.get_repr(self)\n\n    def _handle_single_key(self, e):\n        \"\"\"Override _handle_single_key to abort if the key is a startchar.\n\n        Args:\n            e: the KeyPressEvent from Qt.\n\n        Return:\n            A self.Match member.\n        \"\"\"\n        txt = e.text().strip()\n        if self._inhibited:\n            self._debug_log(\"Ignoring key '{}', because the normal mode is \"\n                \"currently inhibited.\".format(txt))\n            return self.Match.none\n        match = super()._handle_single_key(e)\n        if match == self.Match.partial:\n            timeout = config.get('input', 'partial-timeout')\n            if timeout != 0:\n                self._partial_timer.setInterval(timeout)\n                self._partial_timer.timeout.connect(self._clear_partial_match)\n                self._partial_timer.start()\n        return match\n\n    def set_inhibited_timeout(self, timeout):\n        if timeout != 0:\n            self._debug_log(\"Inhibiting the normal mode for {}ms.\".format(\n                timeout))\n            self._inhibited = True\n            self._inhibited_timer.setInterval(timeout)\n            self._inhibited_timer.timeout.connect(self._clear_inhibited)\n            self._inhibited_timer.start()\n\n    @pyqtSlot()\n    def _clear_partial_match(self):\n        \"\"\"Clear a partial keystring after a timeout.\"\"\"\n        self._debug_log(\"Clearing partial keystring {}\".format(\n            self._keystring))\n        self._keystring = ''\n        self.keystring_updated.emit(self._keystring)\n\n    @pyqtSlot()\n    def _clear_inhibited(self):\n        \"\"\"Reset inhibition state after a timeout.\"\"\"\n        self._debug_log(\"Releasing inhibition state of normal mode.\")\n        self._inhibited = False\n\n    @pyqtSlot()\n    def _stop_timers(self):\n        super()._stop_timers()\n        self._partial_timer.stop()\n        try:\n            self._partial_timer.timeout.disconnect(self._clear_partial_match)\n        except TypeError:\n            # no connections\n            pass\n        self._inhibited_timer.stop()\n        try:\n            self._inhibited_timer.timeout.disconnect(self._clear_inhibited)\n        except TypeError:\n            # no connections\n            pass\n\n\nclass PromptKeyParser(keyparser.CommandKeyParser):\n\n    \"\"\"KeyParser for yes/no prompts.\"\"\"\n\n    def __init__(self, win_id, parent=None):\n        super().__init__(win_id, parent, supports_count=False,\n                         supports_chains=True)\n        # We don't want an extra section for this in the config, so we just\n        # abuse the prompt section.\n        self.read_config('prompt')\n\n    def __repr__(self):\n        return utils.get_repr(self)\n\n\nclass HintKeyParser(keyparser.CommandKeyParser):\n\n    \"\"\"KeyChainParser for hints.\n\n    Attributes:\n        _filtertext: The text to filter with.\n        _last_press: The nature of the last keypress, a LastPress member.\n    \"\"\"\n\n    def __init__(self, win_id, parent=None):\n        super().__init__(win_id, parent, supports_count=False,\n                         supports_chains=True)\n        self._filtertext = ''\n        self._last_press = LastPress.none\n        self.read_config('hint')\n        self.keystring_updated.connect(self.on_keystring_updated)\n\n    def _handle_special_key(self, e):\n        \"\"\"Override _handle_special_key to handle string filtering.\n\n        Return True if the keypress has been handled, and False if not.\n\n        Args:\n            e: the KeyPressEvent from Qt.\n\n        Return:\n            True if event has been handled, False otherwise.\n        \"\"\"\n        log.keyboard.debug(\"Got special key 0x{:x} text {}\".format(\n            e.key(), e.text()))\n        hintmanager = objreg.get('hintmanager', scope='tab',\n                                 window=self._win_id, tab='current')\n        if e.key() == Qt.Key_Backspace:\n            log.keyboard.debug(\"Got backspace, mode {}, filtertext '{}', \"\n                               \"keystring '{}'\".format(self._last_press,\n                                                       self._filtertext,\n                                                       self._keystring))\n            if self._last_press == LastPress.filtertext and self._filtertext:\n                self._filtertext = self._filtertext[:-1]\n                hintmanager.filter_hints(self._filtertext)\n                return True\n            elif self._last_press == LastPress.keystring and self._keystring:\n                self._keystring = self._keystring[:-1]\n                self.keystring_updated.emit(self._keystring)\n                if not self._keystring and self._filtertext:\n                    # Switch back to hint filtering mode (this can happen only\n                    # in numeric mode after the number has been deleted).\n                    hintmanager.filter_hints(self._filtertext)\n                    self._last_press = LastPress.filtertext\n                return True\n            else:\n                return super()._handle_special_key(e)\n        elif hintmanager.current_mode() != 'number':\n            return super()._handle_special_key(e)\n        elif not e.text():\n            return super()._handle_special_key(e)\n        else:\n            self._filtertext += e.text()\n            hintmanager.filter_hints(self._filtertext)\n            self._last_press = LastPress.filtertext\n            return True\n\n    def handle(self, e):\n        \"\"\"Handle a new keypress and call the respective handlers.\n\n        Args:\n            e: the KeyPressEvent from Qt\n\n        Returns:\n            True if the match has been handled, False otherwise.\n        \"\"\"\n        match = self._handle_single_key(e)\n        if match == self.Match.partial:\n            self.keystring_updated.emit(self._keystring)\n            self._last_press = LastPress.keystring\n            return True\n        elif match == self.Match.definitive:\n            self._last_press = LastPress.none\n            return True\n        elif match == self.Match.other:\n            pass\n        elif match == self.Match.none:\n            # We couldn't find a keychain so we check if it's a special key.\n            return self._handle_special_key(e)\n        else:\n            raise ValueError(\"Got invalid match type {}!\".format(match))\n\n    def execute(self, cmdstr, keytype, count=None):\n        \"\"\"Handle a completed keychain.\"\"\"\n        if not isinstance(keytype, self.Type):\n            raise TypeError(\"Type {} is no Type member!\".format(keytype))\n        if keytype == self.Type.chain:\n            hintmanager = objreg.get('hintmanager', scope='tab',\n                                     window=self._win_id, tab='current')\n            hintmanager.handle_partial_key(cmdstr)\n        else:\n            # execute as command\n            super().execute(cmdstr, keytype, count)\n\n    def update_bindings(self, strings, preserve_filter=False):\n        \"\"\"Update bindings when the hint strings changed.\n\n        Args:\n            strings: A list of hint strings.\n            preserve_filter: Whether to keep the current value of\n                             `self._filtertext`.\n        \"\"\"\n        self.bindings = {s: s for s in strings}\n        if not preserve_filter:\n            self._filtertext = ''\n\n    @pyqtSlot(str)\n    def on_keystring_updated(self, keystr):\n        \"\"\"Update hintmanager when the keystring was updated.\"\"\"\n        hintmanager = objreg.get('hintmanager', scope='tab',\n                                 window=self._win_id, tab='current')\n        hintmanager.handle_partial_key(keystr)\n\n\nclass CaretKeyParser(keyparser.CommandKeyParser):\n\n    \"\"\"KeyParser for caret mode.\"\"\"\n\n    passthrough = True\n\n    def __init__(self, win_id, parent=None):\n        super().__init__(win_id, parent, supports_count=True,\n                         supports_chains=True)\n        self.read_config('caret')\n\n\nclass RegisterKeyParser(keyparser.CommandKeyParser):\n\n    \"\"\"KeyParser for modes that record a register key.\n\n    Attributes:\n        _mode: One of KeyMode.set_mark, KeyMode.jump_mark, KeyMode.record_macro\n        and KeyMode.run_macro.\n    \"\"\"\n\n    def __init__(self, win_id, mode, parent=None):\n        super().__init__(win_id, parent, supports_count=False,\n                         supports_chains=False)\n        self._mode = mode\n        self.read_config('register')\n\n    def handle(self, e):\n        \"\"\"Override handle to always match the next key and use the register.\n\n        Args:\n            e: the KeyPressEvent from Qt.\n\n        Return:\n            True if event has been handled, False otherwise.\n        \"\"\"\n        if super().handle(e):\n            return True\n\n        if utils.keyevent_to_string(e) is None:\n            # this is a modifier key, let it pass and keep going\n            return False\n\n        key = e.text()\n\n        tabbed_browser = objreg.get('tabbed-browser', scope='window',\n                                    window=self._win_id)\n        macro_recorder = objreg.get('macro-recorder')\n\n        try:\n            if self._mode == usertypes.KeyMode.set_mark:\n                tabbed_browser.set_mark(key)\n            elif self._mode == usertypes.KeyMode.jump_mark:\n                tabbed_browser.jump_mark(key)\n            elif self._mode == usertypes.KeyMode.record_macro:\n                macro_recorder.record_macro(key)\n            elif self._mode == usertypes.KeyMode.run_macro:\n                macro_recorder.run_macro(self._win_id, key)\n            else:\n                raise ValueError(\n                    \"{} is not a valid register mode\".format(self._mode))\n        except (cmdexc.CommandMetaError, cmdexc.CommandError) as err:\n            message.error(str(err), stack=traceback.format_exc())\n\n        self.request_leave.emit(self._mode, \"valid register key\", True)\n\n        return True\n\n    @pyqtSlot(str)\n    def on_keyconfig_changed(self, mode):\n        \"\"\"RegisterKeyParser has no config section (no bindable keys).\"\"\"\n        pass\n", "path": "qutebrowser/keyinput/modeparsers.py"}], "after_files": [{"content": "# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:\n\n# Copyright 2014-2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>\n#\n# This file is part of qutebrowser.\n#\n# qutebrowser is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# qutebrowser is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.\n\n\"\"\"KeyChainParser for \"hint\" and \"normal\" modes.\n\nModule attributes:\n    STARTCHARS: Possible chars for starting a commandline input.\n\"\"\"\n\nimport traceback\n\nfrom PyQt5.QtCore import pyqtSlot, Qt\n\nfrom qutebrowser.commands import cmdexc\nfrom qutebrowser.config import config\nfrom qutebrowser.keyinput import keyparser\nfrom qutebrowser.utils import usertypes, log, message, objreg, utils\n\n\nSTARTCHARS = \":/?\"\nLastPress = usertypes.enum('LastPress', ['none', 'filtertext', 'keystring'])\n\n\nclass NormalKeyParser(keyparser.CommandKeyParser):\n\n    \"\"\"KeyParser for normal mode with added STARTCHARS detection and more.\n\n    Attributes:\n        _partial_timer: Timer to clear partial keypresses.\n    \"\"\"\n\n    def __init__(self, win_id, parent=None):\n        super().__init__(win_id, parent, supports_count=True,\n                         supports_chains=True)\n        self.read_config('normal')\n        self._partial_timer = usertypes.Timer(self, 'partial-match')\n        self._partial_timer.setSingleShot(True)\n        self._inhibited = False\n        self._inhibited_timer = usertypes.Timer(self, 'normal-inhibited')\n        self._inhibited_timer.setSingleShot(True)\n\n    def __repr__(self):\n        return utils.get_repr(self)\n\n    def _handle_single_key(self, e):\n        \"\"\"Override _handle_single_key to abort if the key is a startchar.\n\n        Args:\n            e: the KeyPressEvent from Qt.\n\n        Return:\n            A self.Match member.\n        \"\"\"\n        txt = e.text().strip()\n        if self._inhibited:\n            self._debug_log(\"Ignoring key '{}', because the normal mode is \"\n                \"currently inhibited.\".format(txt))\n            return self.Match.none\n        match = super()._handle_single_key(e)\n        if match == self.Match.partial:\n            timeout = config.get('input', 'partial-timeout')\n            if timeout != 0:\n                self._partial_timer.setInterval(timeout)\n                self._partial_timer.timeout.connect(self._clear_partial_match)\n                self._partial_timer.start()\n        return match\n\n    def set_inhibited_timeout(self, timeout):\n        if timeout != 0:\n            self._debug_log(\"Inhibiting the normal mode for {}ms.\".format(\n                timeout))\n            self._inhibited = True\n            self._inhibited_timer.setInterval(timeout)\n            self._inhibited_timer.timeout.connect(self._clear_inhibited)\n            self._inhibited_timer.start()\n\n    @pyqtSlot()\n    def _clear_partial_match(self):\n        \"\"\"Clear a partial keystring after a timeout.\"\"\"\n        self._debug_log(\"Clearing partial keystring {}\".format(\n            self._keystring))\n        self._keystring = ''\n        self.keystring_updated.emit(self._keystring)\n\n    @pyqtSlot()\n    def _clear_inhibited(self):\n        \"\"\"Reset inhibition state after a timeout.\"\"\"\n        self._debug_log(\"Releasing inhibition state of normal mode.\")\n        self._inhibited = False\n\n    @pyqtSlot()\n    def _stop_timers(self):\n        super()._stop_timers()\n        self._partial_timer.stop()\n        try:\n            self._partial_timer.timeout.disconnect(self._clear_partial_match)\n        except TypeError:\n            # no connections\n            pass\n        self._inhibited_timer.stop()\n        try:\n            self._inhibited_timer.timeout.disconnect(self._clear_inhibited)\n        except TypeError:\n            # no connections\n            pass\n\n\nclass PromptKeyParser(keyparser.CommandKeyParser):\n\n    \"\"\"KeyParser for yes/no prompts.\"\"\"\n\n    def __init__(self, win_id, parent=None):\n        super().__init__(win_id, parent, supports_count=False,\n                         supports_chains=True)\n        # We don't want an extra section for this in the config, so we just\n        # abuse the prompt section.\n        self.read_config('prompt')\n\n    def __repr__(self):\n        return utils.get_repr(self)\n\n\nclass HintKeyParser(keyparser.CommandKeyParser):\n\n    \"\"\"KeyChainParser for hints.\n\n    Attributes:\n        _filtertext: The text to filter with.\n        _last_press: The nature of the last keypress, a LastPress member.\n    \"\"\"\n\n    def __init__(self, win_id, parent=None):\n        super().__init__(win_id, parent, supports_count=False,\n                         supports_chains=True)\n        self._filtertext = ''\n        self._last_press = LastPress.none\n        self.read_config('hint')\n        self.keystring_updated.connect(self.on_keystring_updated)\n\n    def _handle_special_key(self, e):\n        \"\"\"Override _handle_special_key to handle string filtering.\n\n        Return True if the keypress has been handled, and False if not.\n\n        Args:\n            e: the KeyPressEvent from Qt.\n\n        Return:\n            True if event has been handled, False otherwise.\n        \"\"\"\n        log.keyboard.debug(\"Got special key 0x{:x} text {}\".format(\n            e.key(), e.text()))\n        hintmanager = objreg.get('hintmanager', scope='tab',\n                                 window=self._win_id, tab='current')\n        if e.key() == Qt.Key_Backspace:\n            log.keyboard.debug(\"Got backspace, mode {}, filtertext '{}', \"\n                               \"keystring '{}'\".format(self._last_press,\n                                                       self._filtertext,\n                                                       self._keystring))\n            if self._last_press == LastPress.filtertext and self._filtertext:\n                self._filtertext = self._filtertext[:-1]\n                hintmanager.filter_hints(self._filtertext)\n                return True\n            elif self._last_press == LastPress.keystring and self._keystring:\n                self._keystring = self._keystring[:-1]\n                self.keystring_updated.emit(self._keystring)\n                if not self._keystring and self._filtertext:\n                    # Switch back to hint filtering mode (this can happen only\n                    # in numeric mode after the number has been deleted).\n                    hintmanager.filter_hints(self._filtertext)\n                    self._last_press = LastPress.filtertext\n                return True\n            else:\n                return super()._handle_special_key(e)\n        elif hintmanager.current_mode() != 'number':\n            return super()._handle_special_key(e)\n        elif not e.text():\n            return super()._handle_special_key(e)\n        else:\n            self._filtertext += e.text()\n            hintmanager.filter_hints(self._filtertext)\n            self._last_press = LastPress.filtertext\n            return True\n\n    def handle(self, e):\n        \"\"\"Handle a new keypress and call the respective handlers.\n\n        Args:\n            e: the KeyPressEvent from Qt\n\n        Returns:\n            True if the match has been handled, False otherwise.\n        \"\"\"\n        match = self._handle_single_key(e)\n        if match == self.Match.partial:\n            self.keystring_updated.emit(self._keystring)\n            self._last_press = LastPress.keystring\n            return True\n        elif match == self.Match.definitive:\n            self._last_press = LastPress.none\n            return True\n        elif match == self.Match.other:\n            pass\n        elif match == self.Match.none:\n            # We couldn't find a keychain so we check if it's a special key.\n            return self._handle_special_key(e)\n        else:\n            raise ValueError(\"Got invalid match type {}!\".format(match))\n\n    def execute(self, cmdstr, keytype, count=None):\n        \"\"\"Handle a completed keychain.\"\"\"\n        if not isinstance(keytype, self.Type):\n            raise TypeError(\"Type {} is no Type member!\".format(keytype))\n        if keytype == self.Type.chain:\n            hintmanager = objreg.get('hintmanager', scope='tab',\n                                     window=self._win_id, tab='current')\n            hintmanager.handle_partial_key(cmdstr)\n        else:\n            # execute as command\n            super().execute(cmdstr, keytype, count)\n\n    def update_bindings(self, strings, preserve_filter=False):\n        \"\"\"Update bindings when the hint strings changed.\n\n        Args:\n            strings: A list of hint strings.\n            preserve_filter: Whether to keep the current value of\n                             `self._filtertext`.\n        \"\"\"\n        self.bindings = {s: s for s in strings}\n        if not preserve_filter:\n            self._filtertext = ''\n\n    @pyqtSlot(str)\n    def on_keystring_updated(self, keystr):\n        \"\"\"Update hintmanager when the keystring was updated.\"\"\"\n        hintmanager = objreg.get('hintmanager', scope='tab',\n                                 window=self._win_id, tab='current')\n        hintmanager.handle_partial_key(keystr)\n\n\nclass CaretKeyParser(keyparser.CommandKeyParser):\n\n    \"\"\"KeyParser for caret mode.\"\"\"\n\n    passthrough = True\n\n    def __init__(self, win_id, parent=None):\n        super().__init__(win_id, parent, supports_count=True,\n                         supports_chains=True)\n        self.read_config('caret')\n\n\nclass RegisterKeyParser(keyparser.CommandKeyParser):\n\n    \"\"\"KeyParser for modes that record a register key.\n\n    Attributes:\n        _mode: One of KeyMode.set_mark, KeyMode.jump_mark, KeyMode.record_macro\n        and KeyMode.run_macro.\n    \"\"\"\n\n    def __init__(self, win_id, mode, parent=None):\n        super().__init__(win_id, parent, supports_count=False,\n                         supports_chains=False)\n        self._mode = mode\n        self.read_config('register')\n\n    def handle(self, e):\n        \"\"\"Override handle to always match the next key and use the register.\n\n        Args:\n            e: the KeyPressEvent from Qt.\n\n        Return:\n            True if event has been handled, False otherwise.\n        \"\"\"\n        if super().handle(e):\n            return True\n\n        key = e.text()\n\n        if key == '' or utils.keyevent_to_string(e) is None:\n            # this is not a proper register key, let it pass and keep going\n            return False\n\n        tabbed_browser = objreg.get('tabbed-browser', scope='window',\n                                    window=self._win_id)\n        macro_recorder = objreg.get('macro-recorder')\n\n        try:\n            if self._mode == usertypes.KeyMode.set_mark:\n                tabbed_browser.set_mark(key)\n            elif self._mode == usertypes.KeyMode.jump_mark:\n                tabbed_browser.jump_mark(key)\n            elif self._mode == usertypes.KeyMode.record_macro:\n                macro_recorder.record_macro(key)\n            elif self._mode == usertypes.KeyMode.run_macro:\n                macro_recorder.run_macro(self._win_id, key)\n            else:\n                raise ValueError(\n                    \"{} is not a valid register mode\".format(self._mode))\n        except (cmdexc.CommandMetaError, cmdexc.CommandError) as err:\n            message.error(str(err), stack=traceback.format_exc())\n\n        self.request_leave.emit(self._mode, \"valid register key\", True)\n\n        return True\n\n    @pyqtSlot(str)\n    def on_keyconfig_changed(self, mode):\n        \"\"\"RegisterKeyParser has no config section (no bindable keys).\"\"\"\n        pass\n", "path": "qutebrowser/keyinput/modeparsers.py"}]}