Merge branch 'complete-hidden-commands' of https://github.com/rcorre/qutebrowser into rcorre-complete-hidden-commands
This commit is contained in:
commit
f3c32308d3
@ -114,6 +114,13 @@ def init_session_completion():
|
||||
_instances[usertypes.Completion.sessions] = model
|
||||
|
||||
|
||||
def _init_bind_completion():
|
||||
"""Initialize the command completion model."""
|
||||
log.completion.debug("Initializing bind completion.")
|
||||
model = miscmodels.BindCompletionModel()
|
||||
_instances[usertypes.Completion.bind] = model
|
||||
|
||||
|
||||
INITIALIZERS = {
|
||||
usertypes.Completion.command: _init_command_completion,
|
||||
usertypes.Completion.helptopic: _init_helptopic_completion,
|
||||
@ -125,6 +132,7 @@ INITIALIZERS = {
|
||||
usertypes.Completion.quickmark_by_name: init_quickmark_completions,
|
||||
usertypes.Completion.bookmark_by_url: init_bookmark_completions,
|
||||
usertypes.Completion.sessions: init_session_completion,
|
||||
usertypes.Completion.bind: _init_bind_completion,
|
||||
}
|
||||
|
||||
|
||||
@ -182,5 +190,7 @@ def init():
|
||||
keyconf = objreg.get('key-config')
|
||||
keyconf.changed.connect(
|
||||
functools.partial(update, [usertypes.Completion.command]))
|
||||
keyconf.changed.connect(
|
||||
functools.partial(update, [usertypes.Completion.bind]))
|
||||
|
||||
objreg.get('config').changed.connect(_update_aliases)
|
||||
|
@ -30,7 +30,7 @@ from qutebrowser.completion.models import base
|
||||
|
||||
class CommandCompletionModel(base.BaseCompletionModel):
|
||||
|
||||
"""A CompletionModel filled with all commands and descriptions."""
|
||||
"""A CompletionModel filled with non-hidden commands and descriptions."""
|
||||
|
||||
# https://github.com/The-Compiler/qutebrowser/issues/545
|
||||
# pylint: disable=abstract-method
|
||||
@ -39,23 +39,11 @@ class CommandCompletionModel(base.BaseCompletionModel):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
assert cmdutils.cmd_dict
|
||||
cmdlist = []
|
||||
for obj in set(cmdutils.cmd_dict.values()):
|
||||
if (obj.hide or (obj.debug and not objreg.get('args').debug) or
|
||||
obj.deprecated):
|
||||
pass
|
||||
else:
|
||||
cmdlist.append((obj.name, obj.desc))
|
||||
for name, cmd in config.section('aliases').items():
|
||||
cmdlist.append((name, "Alias for '{}'".format(cmd)))
|
||||
cmdlist = _get_cmd_completions(include_aliases=True,
|
||||
include_hidden=False)
|
||||
cat = self.new_category("Commands")
|
||||
|
||||
# map each command to its bound keys and show these in the misc column
|
||||
key_config = objreg.get('key-config')
|
||||
cmd_to_keys = key_config.get_reverse_bindings_for('normal')
|
||||
for (name, desc) in sorted(cmdlist):
|
||||
self.new_item(cat, name, desc, ', '.join(cmd_to_keys[name]))
|
||||
for (name, desc, misc) in cmdlist:
|
||||
self.new_item(cat, name, desc, misc)
|
||||
|
||||
|
||||
class HelpCompletionModel(base.BaseCompletionModel):
|
||||
@ -72,17 +60,11 @@ class HelpCompletionModel(base.BaseCompletionModel):
|
||||
|
||||
def _init_commands(self):
|
||||
"""Fill completion with :command entries."""
|
||||
assert cmdutils.cmd_dict
|
||||
cmdlist = []
|
||||
for obj in set(cmdutils.cmd_dict.values()):
|
||||
if (obj.hide or (obj.debug and not objreg.get('args').debug) or
|
||||
obj.deprecated):
|
||||
pass
|
||||
else:
|
||||
cmdlist.append((':' + obj.name, obj.desc))
|
||||
cmdlist = _get_cmd_completions(include_aliases=False,
|
||||
include_hidden=True, prefix=':')
|
||||
cat = self.new_category("Commands")
|
||||
for (name, desc) in sorted(cmdlist):
|
||||
self.new_item(cat, name, desc)
|
||||
for (name, desc, misc) in cmdlist:
|
||||
self.new_item(cat, name, desc, misc)
|
||||
|
||||
def _init_settings(self):
|
||||
"""Fill completion with section->option entries."""
|
||||
@ -259,3 +241,49 @@ class TabCompletionModel(base.BaseCompletionModel):
|
||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
window=int(win_id))
|
||||
tabbed_browser.on_tab_close_requested(int(tab_index) - 1)
|
||||
|
||||
|
||||
class BindCompletionModel(base.BaseCompletionModel):
|
||||
|
||||
"""A CompletionModel filled with all bindable commands and descriptions."""
|
||||
|
||||
# https://github.com/The-Compiler/qutebrowser/issues/545
|
||||
# pylint: disable=abstract-method
|
||||
|
||||
COLUMN_WIDTHS = (20, 60, 20)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
cmdlist = _get_cmd_completions(include_hidden=True,
|
||||
include_aliases=True)
|
||||
cat = self.new_category("Commands")
|
||||
for (name, desc, misc) in cmdlist:
|
||||
self.new_item(cat, name, desc, misc)
|
||||
|
||||
|
||||
def _get_cmd_completions(include_hidden, include_aliases, prefix=''):
|
||||
"""Get a list of completions info for commands, sorted by name.
|
||||
|
||||
Args:
|
||||
include_hidden: True to include commands annotated with hide=True.
|
||||
include_aliases: True to include command aliases.
|
||||
prefix: String to append to the command name.
|
||||
|
||||
Return: A list of tuples of form (name, description, bindings).
|
||||
"""
|
||||
assert cmdutils.cmd_dict
|
||||
cmdlist = []
|
||||
cmd_to_keys = objreg.get('key-config').get_reverse_bindings_for('normal')
|
||||
for obj in set(cmdutils.cmd_dict.values()):
|
||||
hide_debug = obj.debug and not objreg.get('args').debug
|
||||
hide_hidden = obj.hide and not include_hidden
|
||||
if not (hide_debug or hide_hidden or obj.deprecated):
|
||||
bindings = ', '.join(cmd_to_keys.get(obj.name, []))
|
||||
cmdlist.append((prefix + obj.name, obj.desc, bindings))
|
||||
|
||||
if include_aliases:
|
||||
for name, cmd in config.section('aliases').items():
|
||||
bindings = ', '.join(cmd_to_keys.get(name, []))
|
||||
cmdlist.append((name, "Alias for '{}'".format(cmd), bindings))
|
||||
|
||||
return cmdlist
|
||||
|
@ -153,7 +153,7 @@ class KeyConfigParser(QObject):
|
||||
@cmdutils.register(instance='key-config', maxsplit=1, no_cmd_split=True,
|
||||
no_replace_variables=True)
|
||||
@cmdutils.argument('win_id', win_id=True)
|
||||
@cmdutils.argument('command', completion=usertypes.Completion.command)
|
||||
@cmdutils.argument('command', completion=usertypes.Completion.bind)
|
||||
def bind(self, key, win_id, command=None, *, mode='normal', force=False):
|
||||
"""Bind a key to a command.
|
||||
|
||||
@ -424,8 +424,9 @@ class KeyConfigParser(QObject):
|
||||
|
||||
def get_reverse_bindings_for(self, section):
|
||||
"""Get a dict of commands to a list of bindings for the section."""
|
||||
cmd_to_keys = collections.defaultdict(list)
|
||||
cmd_to_keys = {}
|
||||
for key, cmd in self.get_bindings_for(section).items():
|
||||
cmd_to_keys.setdefault(cmd, [])
|
||||
# put special bindings last
|
||||
if utils.is_special_key(key):
|
||||
cmd_to_keys[cmd].append(key)
|
||||
|
@ -238,7 +238,8 @@ KeyMode = enum('KeyMode', ['normal', 'hint', 'command', 'yesno', 'prompt',
|
||||
# Available command completions
|
||||
Completion = enum('Completion', ['command', 'section', 'option', 'value',
|
||||
'helptopic', 'quickmark_by_name',
|
||||
'bookmark_by_url', 'url', 'tab', 'sessions'])
|
||||
'bookmark_by_url', 'url', 'tab', 'sessions',
|
||||
'bind'])
|
||||
|
||||
|
||||
# Exit statuses for errors. Needs to be an int for sys.exit.
|
||||
|
@ -31,17 +31,18 @@ from qutebrowser.browser import history
|
||||
from qutebrowser.config import sections, value
|
||||
|
||||
|
||||
def _get_completions(model):
|
||||
"""Collect all the completion entries of a model, organized by category.
|
||||
def _check_completions(model, expected):
|
||||
"""Check that a model contains the expected items in any order.
|
||||
|
||||
The result is a list of form:
|
||||
[
|
||||
(CategoryName: [(name, desc, misc), ...]),
|
||||
(CategoryName: [(name, desc, misc), ...]),
|
||||
...
|
||||
]
|
||||
Args:
|
||||
expected: A dict of form
|
||||
{
|
||||
CategoryName: [(name, desc, misc), ...],
|
||||
CategoryName: [(name, desc, misc), ...],
|
||||
...
|
||||
}
|
||||
"""
|
||||
completions = []
|
||||
actual = {}
|
||||
for i in range(0, model.rowCount()):
|
||||
category = model.item(i)
|
||||
entries = []
|
||||
@ -50,8 +51,12 @@ def _get_completions(model):
|
||||
desc = category.child(j, 1)
|
||||
misc = category.child(j, 2)
|
||||
entries.append((name.text(), desc.text(), misc.text()))
|
||||
completions.append((category.text(), entries))
|
||||
return completions
|
||||
actual[category.text()] = entries
|
||||
for cat_name, expected_entries in expected.items():
|
||||
assert cat_name in actual
|
||||
actual_items = actual[cat_name]
|
||||
for expected_item in expected_entries:
|
||||
assert expected_item in actual_items
|
||||
|
||||
|
||||
def _patch_cmdutils(monkeypatch, stubs, symbol):
|
||||
@ -165,7 +170,6 @@ def test_command_completion(qtmodeltester, monkeypatch, stubs, config_stub,
|
||||
|
||||
Validates that:
|
||||
- only non-hidden and non-deprecated commands are included
|
||||
- commands are sorted by name
|
||||
- the command description is shown in the desc column
|
||||
- the binding (if any) is shown in the misc column
|
||||
- aliases are included
|
||||
@ -173,55 +177,56 @@ def test_command_completion(qtmodeltester, monkeypatch, stubs, config_stub,
|
||||
_patch_cmdutils(monkeypatch, stubs,
|
||||
'qutebrowser.completion.models.miscmodels.cmdutils')
|
||||
config_stub.data['aliases'] = {'rock': 'roll'}
|
||||
key_config_stub.set_bindings_for('normal', {'s': 'stop', 'rr': 'roll'})
|
||||
key_config_stub.set_bindings_for('normal', {'s': 'stop',
|
||||
'rr': 'roll',
|
||||
'ro': 'rock'})
|
||||
model = miscmodels.CommandCompletionModel()
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
|
||||
actual = _get_completions(model)
|
||||
assert actual == [
|
||||
("Commands", [
|
||||
_check_completions(model, {
|
||||
"Commands": [
|
||||
('stop', 'stop qutebrowser', 's'),
|
||||
('drop', 'drop all user data', ''),
|
||||
('rock', "Alias for 'roll'", ''),
|
||||
('roll', 'never gonna give you up', 'rr'),
|
||||
('stop', 'stop qutebrowser', 's')
|
||||
])
|
||||
]
|
||||
('rock', "Alias for 'roll'", 'ro'),
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
def test_help_completion(qtmodeltester, monkeypatch, stubs):
|
||||
def test_help_completion(qtmodeltester, monkeypatch, stubs, key_config_stub):
|
||||
"""Test the results of command completion.
|
||||
|
||||
Validates that:
|
||||
- only non-hidden and non-deprecated commands are included
|
||||
- commands are sorted by name
|
||||
- only non-deprecated commands are included
|
||||
- the command description is shown in the desc column
|
||||
- the binding (if any) is shown in the misc column
|
||||
- aliases are included
|
||||
- only the first line of a multiline description is shown
|
||||
"""
|
||||
module = 'qutebrowser.completion.models.miscmodels'
|
||||
key_config_stub.set_bindings_for('normal', {'s': 'stop', 'rr': 'roll'})
|
||||
_patch_cmdutils(monkeypatch, stubs, module + '.cmdutils')
|
||||
_patch_configdata(monkeypatch, stubs, module + '.configdata.DATA')
|
||||
model = miscmodels.HelpCompletionModel()
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
|
||||
actual = _get_completions(model)
|
||||
assert actual == [
|
||||
("Commands", [
|
||||
_check_completions(model, {
|
||||
"Commands": [
|
||||
(':stop', 'stop qutebrowser', 's'),
|
||||
(':drop', 'drop all user data', ''),
|
||||
(':roll', 'never gonna give you up', ''),
|
||||
(':stop', 'stop qutebrowser', '')
|
||||
]),
|
||||
("Settings", [
|
||||
(':roll', 'never gonna give you up', 'rr'),
|
||||
(':hide', '', ''),
|
||||
],
|
||||
"Settings": [
|
||||
('general->time', 'Is an illusion.', ''),
|
||||
('general->volume', 'Goes to 11', ''),
|
||||
('ui->gesture', 'Waggle your hands to control qutebrowser', ''),
|
||||
('ui->mind', 'Enable mind-control ui (experimental)', ''),
|
||||
('ui->voice', 'Whether to respond to voice commands', ''),
|
||||
])
|
||||
]
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
def test_quickmark_completion(qtmodeltester, quickmarks):
|
||||
@ -230,14 +235,13 @@ def test_quickmark_completion(qtmodeltester, quickmarks):
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
|
||||
actual = _get_completions(model)
|
||||
assert actual == [
|
||||
("Quickmarks", [
|
||||
_check_completions(model, {
|
||||
"Quickmarks": [
|
||||
('aw', 'https://wiki.archlinux.org', ''),
|
||||
('ddg', 'https://duckduckgo.com', ''),
|
||||
('wiki', 'https://wikipedia.org', ''),
|
||||
])
|
||||
]
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
def test_bookmark_completion(qtmodeltester, bookmarks):
|
||||
@ -246,14 +250,13 @@ def test_bookmark_completion(qtmodeltester, bookmarks):
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
|
||||
actual = _get_completions(model)
|
||||
assert actual == [
|
||||
("Bookmarks", [
|
||||
_check_completions(model, {
|
||||
"Bookmarks": [
|
||||
('https://github.com', 'GitHub', ''),
|
||||
('https://python.org', 'Welcome to Python.org', ''),
|
||||
('http://qutebrowser.org', 'qutebrowser | qutebrowser', ''),
|
||||
])
|
||||
]
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
def test_url_completion(qtmodeltester, config_stub, web_history, quickmarks,
|
||||
@ -271,23 +274,22 @@ def test_url_completion(qtmodeltester, config_stub, web_history, quickmarks,
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
|
||||
actual = _get_completions(model)
|
||||
assert actual == [
|
||||
("Quickmarks", [
|
||||
_check_completions(model, {
|
||||
"Quickmarks": [
|
||||
('https://wiki.archlinux.org', 'aw', ''),
|
||||
('https://duckduckgo.com', 'ddg', ''),
|
||||
('https://wikipedia.org', 'wiki', ''),
|
||||
]),
|
||||
("Bookmarks", [
|
||||
],
|
||||
"Bookmarks": [
|
||||
('https://github.com', 'GitHub', ''),
|
||||
('https://python.org', 'Welcome to Python.org', ''),
|
||||
('http://qutebrowser.org', 'qutebrowser | qutebrowser', ''),
|
||||
]),
|
||||
("History", [
|
||||
],
|
||||
"History": [
|
||||
('https://python.org', 'Welcome to Python.org', '2016-03-08'),
|
||||
('https://github.com', 'GitHub', '2016-05-01'),
|
||||
]),
|
||||
]
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
def test_url_completion_delete_bookmark(qtmodeltester, config_stub,
|
||||
@ -332,10 +334,9 @@ def test_session_completion(qtmodeltester, session_manager_stub):
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
|
||||
actual = _get_completions(model)
|
||||
assert actual == [
|
||||
("Sessions", [('default', '', ''), ('1', '', ''), ('2', '', '')])
|
||||
]
|
||||
_check_completions(model, {
|
||||
"Sessions": [('default', '', ''), ('1', '', ''), ('2', '', '')]
|
||||
})
|
||||
|
||||
|
||||
def test_tab_completion(qtmodeltester, fake_web_tab, app_stub, win_registry,
|
||||
@ -352,17 +353,16 @@ def test_tab_completion(qtmodeltester, fake_web_tab, app_stub, win_registry,
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
|
||||
actual = _get_completions(model)
|
||||
assert actual == [
|
||||
('0', [
|
||||
_check_completions(model, {
|
||||
'0': [
|
||||
('0/1', 'https://github.com', 'GitHub'),
|
||||
('0/2', 'https://wikipedia.org', 'Wikipedia'),
|
||||
('0/3', 'https://duckduckgo.com', 'DuckDuckGo')
|
||||
]),
|
||||
('1', [
|
||||
],
|
||||
'1': [
|
||||
('1/1', 'https://wiki.archlinux.org', 'ArchWiki'),
|
||||
])
|
||||
]
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
def test_tab_completion_delete(qtmodeltester, fake_web_tab, qtbot, app_stub,
|
||||
@ -397,13 +397,12 @@ def test_setting_section_completion(qtmodeltester, monkeypatch, stubs):
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
|
||||
actual = _get_completions(model)
|
||||
assert actual == [
|
||||
("Sections", [
|
||||
_check_completions(model, {
|
||||
"Sections": [
|
||||
('general', 'General/miscellaneous options.', ''),
|
||||
('ui', 'General options related to the user interface.', ''),
|
||||
])
|
||||
]
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
def test_setting_option_completion(qtmodeltester, monkeypatch, stubs,
|
||||
@ -417,14 +416,13 @@ def test_setting_option_completion(qtmodeltester, monkeypatch, stubs,
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
|
||||
actual = _get_completions(model)
|
||||
assert actual == [
|
||||
("ui", [
|
||||
_check_completions(model, {
|
||||
"ui": [
|
||||
('gesture', 'Waggle your hands to control qutebrowser', 'off'),
|
||||
('mind', 'Enable mind-control ui (experimental)', 'on'),
|
||||
('voice', 'Whether to respond to voice commands', 'sometimes'),
|
||||
])
|
||||
]
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
def test_setting_value_completion(qtmodeltester, monkeypatch, stubs,
|
||||
@ -436,14 +434,43 @@ def test_setting_value_completion(qtmodeltester, monkeypatch, stubs,
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
|
||||
actual = _get_completions(model)
|
||||
assert actual == [
|
||||
("Current/Default", [
|
||||
_check_completions(model, {
|
||||
"Current/Default": [
|
||||
('0', 'Current value', ''),
|
||||
('11', 'Default value', ''),
|
||||
]),
|
||||
("Completions", [
|
||||
],
|
||||
"Completions": [
|
||||
('0', '', ''),
|
||||
('11', '', ''),
|
||||
])
|
||||
]
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
def test_bind_completion(qtmodeltester, monkeypatch, stubs, config_stub,
|
||||
key_config_stub):
|
||||
"""Test the results of keybinding command completion.
|
||||
|
||||
Validates that:
|
||||
- only non-hidden and non-deprecated commands are included
|
||||
- the command description is shown in the desc column
|
||||
- the binding (if any) is shown in the misc column
|
||||
- aliases are included
|
||||
"""
|
||||
_patch_cmdutils(monkeypatch, stubs,
|
||||
'qutebrowser.completion.models.miscmodels.cmdutils')
|
||||
config_stub.data['aliases'] = {'rock': 'roll'}
|
||||
key_config_stub.set_bindings_for('normal', {'s': 'stop',
|
||||
'rr': 'roll',
|
||||
'ro': 'rock'})
|
||||
model = miscmodels.BindCompletionModel()
|
||||
qtmodeltester.data_display_may_return_none = True
|
||||
qtmodeltester.check(model)
|
||||
|
||||
_check_completions(model, {
|
||||
"Commands": [
|
||||
('stop', 'stop qutebrowser', 's'),
|
||||
('drop', 'drop all user data', ''),
|
||||
('hide', '', ''),
|
||||
('rock', "Alias for 'roll'", 'ro'),
|
||||
]
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user