Skip to content
Permalink
Browse files

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
  • Loading branch information
The-Compiler committed Oct 3, 2017
1 parent 727580d commit 22088d9f7bf6912f4a0b66d317dd99fce9cde636
@@ -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.
@@ -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
@@ -168,13 +168,11 @@ def get_command(self, key, mode):
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:
@@ -92,7 +92,7 @@ def set(self, win_id, option=None, value=None, temp=False, print_=False):
@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 @@ def bind(self, key, command=None, *, mode='normal', force=False):
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 @@ def bind(self, key, command=None, *, mode='normal', force=False):
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))

@@ -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."""
@@ -236,13 +236,9 @@ def set(self, name, value):
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):
@@ -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"
@@ -120,7 +120,7 @@ def openurl(url=None, related=False, bg=False, tab=False, window=False,

@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

@@ -172,19 +172,13 @@ def test_get_reverse_bindings_for(self, keyconf, config_stub, no_bindings,
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', [
@@ -418,9 +418,8 @@ def test_bind_invalid_mode(self, commands):
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 @@ def test_bind_duplicate(self, commands, config_stub, keyconf, force, key):
'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
@@ -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"))
@@ -403,12 +403,11 @@ def test_bind_freshly_defined_alias(self, confpy):
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",

0 comments on commit 22088d9

Please sign in to comment.
You can’t perform that action at this time.