Remove --force for :bind and config.bind(...)

Turns out --force is just in the way for most people, and at least for default
bindings it's easy to reset them.

Also, it makes :config-source fail when config.py contains keybindings.

Closes #3049
This commit is contained in:
Florian Bruhin 2017-10-03 19:03:03 +02:00
parent 727580d1f4
commit 22088d9f7b
12 changed files with 22 additions and 72 deletions

View File

@ -120,7 +120,7 @@ How many pages to go back.
[[bind]]
=== bind
Syntax: +:bind [*--mode* 'mode'] [*--force*] 'key' ['command']+
Syntax: +:bind [*--mode* 'mode'] 'key' ['command']+
Bind a key to a command.
@ -133,7 +133,6 @@ Bind a key to a command.
* +*-m*+, +*--mode*+: A comma-separated list of modes to bind the key in (default: `normal`). See `:help bindings.commands` for the
available modes.
* +*-f*+, +*--force*+: Rebind the key if it is already bound.
==== note
* This command does not split arguments after the last argument and handles quotes literally.

View File

@ -61,8 +61,6 @@ link:commands.html#unbind[`:unbind`] commands:
- Binding the key chain `,v` to the `:spawn mpv {url}` command:
`:bind ,v spawn mpv {url}`
- Unbinding the same key chain: `:unbind ,v`
- Changing an existing binding: `bind --force ,v message-info foo`. Without
`--force`, qutebrowser will show an error because `,v` is already bound.
Key chains starting with a comma are ideal for custom bindings, as the comma key
will never be used in a default keybinding.
@ -179,13 +177,6 @@ To bind a key in a mode other than `'normal'`, add a `mode` argument:
config.bind('<Ctrl-y>', 'prompt-yes', mode='prompt')
----
If the key is already bound, `force=True` needs to be given to rebind it:
[source,python]
----
config.bind('<Ctrl-v>', 'message-info foo', force=True)
----
To unbind a key (either a key which has been bound before, or a default binding):
[source,python]
@ -339,10 +330,10 @@ to do so:
[source,python]
----
def bind_chained(key, *commands, force=False):
config.bind(key, ' ;; '.join(commands), force=force)
def bind_chained(key, *commands):
config.bind(key, ' ;; '.join(commands))
bind_chained('<Escape>', 'clear-keychain', 'search', force=True)
bind_chained('<Escape>', 'clear-keychain', 'search')
----
Avoiding flake8 errors

View File

@ -168,13 +168,11 @@ class KeyConfig:
bindings = self.get_bindings_for(mode)
return bindings.get(key, None)
def bind(self, key, command, *, mode, force=False, save_yaml=False):
def bind(self, key, command, *, mode, save_yaml=False):
"""Add a new binding from key to command."""
key = self._prepare(key, mode)
log.keyboard.vdebug("Adding binding {} -> {} in mode {}.".format(
key, command, mode))
if key in self.get_bindings_for(mode) and not force:
raise configexc.DuplicateKeyError(key)
bindings = self._config.get_obj('bindings.commands')
if mode not in bindings:

View File

@ -92,7 +92,7 @@ class ConfigCommands:
@cmdutils.register(instance='config-commands', maxsplit=1,
no_cmd_split=True, no_replace_variables=True)
@cmdutils.argument('command', completion=configmodel.bind)
def bind(self, key, command=None, *, mode='normal', force=False):
def bind(self, key, command=None, *, mode='normal'):
"""Bind a key to a command.
Args:
@ -102,7 +102,6 @@ class ConfigCommands:
mode: A comma-separated list of modes to bind the key in
(default: `normal`). See `:help bindings.commands` for the
available modes.
force: Rebind the key if it is already bound.
"""
if command is None:
if utils.is_special_key(key):
@ -118,11 +117,7 @@ class ConfigCommands:
return
try:
self._keyconfig.bind(key, command, mode=mode, force=force,
save_yaml=True)
except configexc.DuplicateKeyError as e:
raise cmdexc.CommandError("bind: {} - use --force to override!"
.format(e))
self._keyconfig.bind(key, command, mode=mode, save_yaml=True)
except configexc.KeybindingError as e:
raise cmdexc.CommandError("bind: {}".format(e))

View File

@ -59,14 +59,6 @@ class KeybindingError(Error):
"""Raised for issues with keybindings."""
class DuplicateKeyError(KeybindingError):
"""Raised when there was a duplicate key."""
def __init__(self, key):
super().__init__("Duplicate key {}".format(key))
class NoOptionError(Error):
"""Raised when an option was not found."""

View File

@ -236,13 +236,9 @@ class ConfigAPI:
with self._handle_error('setting', name):
self._config.set_obj(name, value)
def bind(self, key, command, mode='normal', *, force=False):
def bind(self, key, command, mode='normal'):
with self._handle_error('binding', key):
try:
self._keyconfig.bind(key, command, mode=mode, force=force)
except configexc.DuplicateKeyError as e:
raise configexc.KeybindingError('{} - use force=True to '
'override!'.format(e))
self._keyconfig.bind(key, command, mode=mode)
def unbind(self, key, mode='normal'):
with self._handle_error('unbinding', key):

View File

@ -246,7 +246,7 @@ Feature: Using hints
Scenario: Ignoring key presses after auto-following hints
When I set hints.auto_follow_timeout to 1000
And I set hints.mode to number
And I run :bind --force , message-error "This error message was triggered via a keybinding which should have been inhibited"
And I run :bind , message-error "This error message was triggered via a keybinding which should have been inhibited"
And I open data/hints/html/simple.html
And I hint with args "all"
And I press the key "f"
@ -259,7 +259,7 @@ Feature: Using hints
Scenario: Turning off auto_follow_timeout
When I set hints.auto_follow_timeout to 0
And I set hints.mode to number
And I run :bind --force , message-info "Keypress worked!"
And I run :bind , message-info "Keypress worked!"
And I open data/hints/html/simple.html
And I hint with args "all"
And I press the key "f"

View File

@ -120,7 +120,7 @@ def cmdutils_patch(monkeypatch, stubs, miscmodels_patch):
@cmdutils.argument('win_id', win_id=True)
@cmdutils.argument('command', completion=miscmodels_patch.command)
def bind(key, win_id, command=None, *, mode='normal', force=False):
def bind(key, win_id, command=None, *, mode='normal'):
"""docstring."""
pass

View File

@ -172,19 +172,13 @@ class TestKeyConfig:
config_stub.val.bindings.commands = {'normal': bindings}
assert keyconf.get_reverse_bindings_for('normal') == expected
@pytest.mark.parametrize('force', [True, False])
@pytest.mark.parametrize('key', ['a', '<Ctrl-X>', 'b'])
def test_bind_duplicate(self, keyconf, config_stub, force, key):
def test_bind_duplicate(self, keyconf, config_stub, key):
config_stub.val.bindings.default = {'normal': {'a': 'nop',
'<Ctrl+x>': 'nop'}}
config_stub.val.bindings.commands = {'normal': {'b': 'nop'}}
if force:
keyconf.bind(key, 'message-info foo', mode='normal', force=True)
assert keyconf.get_command(key, 'normal') == 'message-info foo'
else:
with pytest.raises(configexc.DuplicateKeyError):
keyconf.bind(key, 'message-info foo', mode='normal')
assert keyconf.get_command(key, 'normal') == 'nop'
keyconf.bind(key, 'message-info foo', mode='normal')
assert keyconf.get_command(key, 'normal') == 'message-info foo'
@pytest.mark.parametrize('mode', ['normal', 'caret'])
@pytest.mark.parametrize('command', [

View File

@ -418,9 +418,8 @@ class TestBind:
match='bind: Invalid mode wrongmode!'):
commands.bind('a', 'nop', mode='wrongmode')
@pytest.mark.parametrize('force', [True, False])
@pytest.mark.parametrize('key', ['a', 'b', '<Ctrl-X>'])
def test_bind_duplicate(self, commands, config_stub, keyconf, force, key):
def test_bind_duplicate(self, commands, config_stub, keyconf, key):
"""Run ':bind' with a key which already has been bound.'.
Also tests for https://github.com/qutebrowser/qutebrowser/issues/1544
@ -432,15 +431,8 @@ class TestBind:
'normal': {'b': 'nop'},
}
if force:
commands.bind(key, 'message-info foo', mode='normal', force=True)
assert keyconf.get_command(key, 'normal') == 'message-info foo'
else:
with pytest.raises(cmdexc.CommandError,
match="bind: Duplicate key .* - use --force to "
"override"):
commands.bind(key, 'message-info foo', mode='normal')
assert keyconf.get_command(key, 'normal') == 'nop'
commands.bind(key, 'message-info foo', mode='normal')
assert keyconf.get_command(key, 'normal') == 'message-info foo'
def test_bind_none(self, commands, config_stub):
config_stub.val.bindings.commands = None

View File

@ -43,12 +43,6 @@ def test_backend_error():
assert str(e) == "This setting is not available with the QtWebKit backend!"
def test_duplicate_key_error():
e = configexc.DuplicateKeyError('asdf')
assert isinstance(e, configexc.KeybindingError)
assert str(e) == "Duplicate key asdf"
def test_desc_with_text():
"""Test ConfigErrorDesc.with_text."""
old = configexc.ConfigErrorDesc("Error text", Exception("Exception text"))

View File

@ -403,12 +403,11 @@ class TestConfigPy:
confpy.read()
def test_bind_duplicate_key(self, confpy):
"""Make sure we get a nice error message on duplicate key bindings."""
"""Make sure overriding a keybinding works."""
confpy.write("config.bind('H', 'message-info back')")
error = confpy.read(error=True)
expected = "Duplicate key H - use force=True to override!"
assert str(error.exception) == expected
confpy.read()
expected = {'normal': {'H': 'message-info back'}}
assert config.instance._values['bindings.commands'] == expected
def test_bind_none(self, confpy):
confpy.write("c.bindings.commands = None",