Repository: qutebrowser/qutebrowser

Issue:
Show the default binding in :bind completion
I found a forgotten branch on my local repo that shows the default binding along with the current in :bind completion.

If it seems useful I could add/fix some tests and submit a PR, or I could ditch it since there is already `:bind --default`.

Right now the 'current' or 'default' tag is shown in parentheses in the description as seen [here](https://imgur.com/a/FaGdh), but it could also replace the description completely (like :set), or replace the existing binding in the rightmost column.


Discussion:
Seems like a nice idea for discoverability! The output in the screenshot seems fine to me. Please submit a PR! 👍 

cc @rcorre 
LGTM, I think `(Current/Default) <description...>` looks fine. Also, nice completion color theme :grin:
Thanks! It's [gruvbox](https://github.com/morhetz/gruvbox) btw.

Resolving patch:
diff --git a/qutebrowser/completion/models/configmodel.py b/qutebrowser/completion/models/configmodel.py
--- a/qutebrowser/completion/models/configmodel.py
+++ b/qutebrowser/completion/models/configmodel.py
@@ -77,17 +77,26 @@ def bind(key, *, info):
         key: the key being bound.
     """
     model = completionmodel.CompletionModel(column_widths=(20, 60, 20))
-    cmd_text = info.keyconf.get_command(key, 'normal')
+    data = []
 
+    cmd_text = info.keyconf.get_command(key, 'normal')
     if cmd_text:
         parser = runners.CommandParser()
         try:
             cmd = parser.parse(cmd_text).cmd
         except cmdexc.NoSuchCommandError:
-            data = [(cmd_text, 'Invalid command!', key)]
+            data.append((cmd_text, '(Current) Invalid command!', key))
         else:
-            data = [(cmd_text, cmd.desc, key)]
-        model.add_category(listcategory.ListCategory("Current", data))
+            data.append((cmd_text, '(Current) {}'.format(cmd.desc), key))
+
+    cmd_text = info.keyconf.get_command(key, 'normal', default=True)
+    if cmd_text:
+        parser = runners.CommandParser()
+        cmd = parser.parse(cmd_text).cmd
+        data.append((cmd_text, '(Default) {}'.format(cmd.desc), key))
+
+    if data:
+        model.add_category(listcategory.ListCategory("Current/Default", data))
 
     cmdlist = util.get_cmd_completions(info, include_hidden=True,
                                        include_aliases=True)
diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py
--- a/qutebrowser/config/config.py
+++ b/qutebrowser/config/config.py
@@ -162,10 +162,13 @@ def get_reverse_bindings_for(self, mode):
                     cmd_to_keys[cmd].insert(0, key)
         return cmd_to_keys
 
-    def get_command(self, key, mode):
+    def get_command(self, key, mode, default=False):
         """Get the command for a given key (or None)."""
         key = self._prepare(key, mode)
-        bindings = self.get_bindings_for(mode)
+        if default:
+            bindings = dict(val.bindings.default[mode])
+        else:
+            bindings = self.get_bindings_for(mode)
         return bindings.get(key, None)
 
     def bind(self, key, command, *, mode, save_yaml=False):


Test patch:
diff --git a/tests/unit/completion/test_models.py b/tests/unit/completion/test_models.py
--- a/tests/unit/completion/test_models.py
+++ b/tests/unit/completion/test_models.py
@@ -73,6 +73,9 @@ def cmdutils_stub(monkeypatch, stubs):
             name='scroll',
             desc='Scroll the current tab in the given direction.',
             modes=()),
+        'tab-close': stubs.FakeCommand(
+            name='tab-close',
+            desc='Close the current tab.'),
     })
 
 
@@ -101,9 +104,10 @@ def configdata_stub(monkeypatch, configdata_init):
                 ),
             ),
             default={
-                'normal': {
-                    '<ctrl+q>': 'quit'
-                }
+                'normal': collections.OrderedDict([
+                    ('<ctrl+q>', 'quit'),
+                    ('d', 'tab-close'),
+                ])
             },
             backends=[],
             raw_backends=None)),
@@ -122,6 +126,7 @@ def configdata_stub(monkeypatch, configdata_init):
                     ('<ctrl+q>', 'quit'),
                     ('ZQ', 'quit'),
                     ('I', 'invalid'),
+                    ('d', 'scroll down'),
                 ])
             },
             backends=[],
@@ -209,6 +214,7 @@ def test_command_completion(qtmodeltester, cmdutils_stub, configdata_stub,
             ('open', 'open a url', ''),
             ('q', "Alias for 'quit'", ''),
             ('quit', 'quit qutebrowser', 'ZQ, <ctrl+q>'),
+            ('tab-close', 'Close the current tab.', ''),
         ]
     })
 
@@ -233,7 +239,8 @@ def test_help_completion(qtmodeltester, cmdutils_stub, key_config_stub,
         "Commands": [
             (':open', 'open a url', ''),
             (':quit', 'quit qutebrowser', 'ZQ, <ctrl+q>'),
-            (':scroll', 'Scroll the current tab in the given direction.', '')
+            (':scroll', 'Scroll the current tab in the given direction.', ''),
+            (':tab-close', 'Close the current tab.', ''),
         ],
         "Settings": [
             ('aliases', 'Aliases for commands.', None),
@@ -566,9 +573,9 @@ def test_setting_option_completion(qtmodeltester, config_stub,
             ('aliases', 'Aliases for commands.', '{"q": "quit"}'),
             ('bindings.commands', 'Default keybindings',
                 '{"normal": {"<ctrl+q>": "quit", "ZQ": "quit", '
-                '"I": "invalid"}}'),
+                '"I": "invalid", "d": "scroll down"}}'),
             ('bindings.default', 'Default keybindings',
-                '{"normal": {"<ctrl+q>": "quit"}}'),
+                '{"normal": {"<ctrl+q>": "quit", "d": "tab-close"}}'),
         ]
     })
 
@@ -589,14 +596,15 @@ def test_bind_completion(qtmodeltester, cmdutils_stub, config_stub,
     qtmodeltester.check(model)
 
     _check_completions(model, {
-        "Current": [
-            ('quit', 'quit qutebrowser', 'ZQ'),
+        "Current/Default": [
+            ('quit', '(Current) quit qutebrowser', 'ZQ'),
         ],
         "Commands": [
             ('open', 'open a url', ''),
             ('q', "Alias for 'quit'", ''),
             ('quit', 'quit qutebrowser', 'ZQ, <ctrl+q>'),
-            ('scroll', 'Scroll the current tab in the given direction.', '')
+            ('scroll', 'Scroll the current tab in the given direction.', ''),
+            ('tab-close', 'Close the current tab.', ''),
         ],
     })
 
@@ -608,21 +616,22 @@ def test_bind_completion_invalid(cmdutils_stub, config_stub, key_config_stub,
     model.set_pattern('')
 
     _check_completions(model, {
-        "Current": [
-            ('invalid', 'Invalid command!', 'I'),
+        "Current/Default": [
+            ('invalid', '(Current) Invalid command!', 'I'),
         ],
         "Commands": [
             ('open', 'open a url', ''),
             ('q', "Alias for 'quit'", ''),
             ('quit', 'quit qutebrowser', 'ZQ, <ctrl+q>'),
-            ('scroll', 'Scroll the current tab in the given direction.', '')
+            ('scroll', 'Scroll the current tab in the given direction.', ''),
+            ('tab-close', 'Close the current tab.', ''),
         ],
     })
 
 
-def test_bind_completion_no_current(qtmodeltester, cmdutils_stub, config_stub,
+def test_bind_completion_no_binding(qtmodeltester, cmdutils_stub, config_stub,
                                     key_config_stub, configdata_stub, info):
-    """Test keybinding completion with no current binding."""
+    """Test keybinding completion with no current or default binding."""
     model = configmodel.bind('x', info=info)
     model.set_pattern('')
     qtmodeltester.data_display_may_return_none = True
@@ -633,7 +642,30 @@ def test_bind_completion_no_current(qtmodeltester, cmdutils_stub, config_stub,
             ('open', 'open a url', ''),
             ('q', "Alias for 'quit'", ''),
             ('quit', 'quit qutebrowser', 'ZQ, <ctrl+q>'),
-            ('scroll', 'Scroll the current tab in the given direction.', '')
+            ('scroll', 'Scroll the current tab in the given direction.', ''),
+            ('tab-close', 'Close the current tab.', ''),
+        ],
+    })
+
+
+def test_bind_completion_changed(cmdutils_stub, config_stub, key_config_stub,
+                                 configdata_stub, info):
+    """Test command completion with a non-default command bound."""
+    model = configmodel.bind('d', info=info)
+    model.set_pattern('')
+
+    _check_completions(model, {
+        "Current/Default": [
+            ('scroll down',
+             '(Current) Scroll the current tab in the given direction.', 'd'),
+            ('tab-close', '(Default) Close the current tab.', 'd'),
+        ],
+        "Commands": [
+            ('open', 'open a url', ''),
+            ('q', "Alias for 'quit'", ''),
+            ('quit', 'quit qutebrowser', 'ZQ, <ctrl+q>'),
+            ('scroll', 'Scroll the current tab in the given direction.', ''),
+            ('tab-close', 'Close the current tab.', ''),
         ],
     })