Permalink
Browse files

Remove support for ambiguous keybindings

  • Loading branch information...
The-Compiler committed Sep 13, 2017
1 parent bf9d401 commit 1fc9817cd43fbebce7592f9bd4993c31e34f839f
@@ -191,7 +191,6 @@
|<<hints.uppercase,hints.uppercase>>|Make chars in hint strings uppercase.
|<<history_gap_interval,history_gap_interval>>|The maximum time in minutes between two history items for them to be considered being from the same browsing session.
|<<ignore_case,ignore_case>>|Find text on a page case-insensitively.
|<<input.ambiguous_timeout,input.ambiguous_timeout>>|Timeout (in milliseconds) for ambiguous key bindings.
|<<input.forward_unbound_keys,input.forward_unbound_keys>>|Forward unbound keys to the webview in normal mode.
|<<input.insert_mode.auto_leave,input.insert_mode.auto_leave>>|Leave insert mode if a non-editable element is clicked.
|<<input.insert_mode.auto_load,input.insert_mode.auto_load>>|Automatically enter insert mode if an editable element is focused after loading the page.
@@ -2050,13 +2049,6 @@ Valid values:
Default: +pass:[smart]+
[[input.ambiguous_timeout]]
== input.ambiguous_timeout
Timeout (in milliseconds) for ambiguous key bindings.
If the current input forms both a complete match and a partial match, the complete match will be executed after this time.
Default: +pass:[500]+
[[input.forward_unbound_keys]]
== input.forward_unbound_keys
Forward unbound keys to the webview in normal mode.
@@ -794,19 +794,6 @@ hints.uppercase:
## input
# FIXME:conf get rid of this?
input.ambiguous_timeout:
default: 500
type:
name: Int
minval: 0
maxval: maxint
desc: >-
Timeout (in milliseconds) for ambiguous key bindings.
If the current input forms both a complete match and a partial match, the complete
match will be executed after this time.
input.forward_unbound_keys:
default: auto
type:
@@ -20,7 +20,6 @@
"""Base class for vim-like key sequence parser."""
import re
import functools
import unicodedata
from PyQt5.QtCore import pyqtSignal, QObject
@@ -41,7 +40,6 @@ class BaseKeyParser(QObject):
partial: No keychain matched yet, but it's still possible in the
future.
definitive: Keychain matches exactly.
ambiguous: There are both a partial and a definitive match.
none: No more matches possible.
Types: type of a key binding.
@@ -59,7 +57,6 @@ class BaseKeyParser(QObject):
_warn_on_keychains: Whether a warning should be logged when binding
keychains in a section which does not support them.
_keystring: The currently entered key sequence
_ambiguous_timer: Timer for delayed execution with ambiguous bindings.
_modename: The name of the input mode associated with this keyparser.
_supports_count: Whether count is supported
_supports_chains: Whether keychains are supported
@@ -78,16 +75,13 @@ class BaseKeyParser(QObject):
do_log = True
passthrough = False
Match = usertypes.enum('Match', ['partial', 'definitive', 'ambiguous',
'other', 'none'])
Match = usertypes.enum('Match', ['partial', 'definitive', 'other', 'none'])
Type = usertypes.enum('Type', ['chain', 'special'])
def __init__(self, win_id, parent=None, supports_count=None,
supports_chains=False):
super().__init__(parent)
self._win_id = win_id
self._ambiguous_timer = usertypes.Timer(self, 'ambiguous-match')
self._ambiguous_timer.setSingleShot(True)
self._modename = None
self._keystring = ''
if supports_count is None:
@@ -189,7 +183,6 @@ def _handle_single_key(self, e):
self._debug_log("Ignoring, no text char")
return self.Match.none
self._stop_timers()
key_mappings = config.val.bindings.key_mappings
txt = key_mappings.get(txt, txt)
self._keystring += txt
@@ -207,10 +200,6 @@ def _handle_single_key(self, e):
self._keystring))
self.clear_keystring()
self.execute(binding, self.Type.chain, count)
elif match == self.Match.ambiguous:
self._debug_log("Ambiguous match for '{}'.".format(
self._keystring))
self._handle_ambiguous_match(binding, count)
elif match == self.Match.partial:
self._debug_log("No match for '{}' (added {})".format(
self._keystring, txt))
@@ -230,11 +219,9 @@ def _match_key(self, cmd_input):
Return:
A tuple (matchtype, binding).
matchtype: Match.definitive, Match.ambiguous, Match.partial or
Match.none
binding: - None with Match.partial/Match.none
- The found binding with Match.definitive/
Match.ambiguous
matchtype: Match.definitive, Match.partial or Match.none.
binding: - None with Match.partial/Match.none.
- The found binding with Match.definitive.
"""
# A (cmd_input, binding) tuple (k, v of bindings) or None.
definitive_match = None
@@ -252,58 +239,13 @@ def _match_key(self, cmd_input):
elif binding.startswith(cmd_input):
partial_match = True
break
if definitive_match is not None and partial_match:
return (self.Match.ambiguous, definitive_match[1])
elif definitive_match is not None:
if definitive_match is not None:
return (self.Match.definitive, definitive_match[1])
elif partial_match:
return (self.Match.partial, None)
else:
return (self.Match.none, None)
def _stop_timers(self):
"""Stop a delayed execution if any is running."""
if self._ambiguous_timer.isActive() and self.do_log:
log.keyboard.debug("Stopping delayed execution.")
self._ambiguous_timer.stop()
try:
self._ambiguous_timer.timeout.disconnect()
except TypeError:
# no connections
pass
def _handle_ambiguous_match(self, binding, count):
"""Handle an ambiguous match.
Args:
binding: The command-string to execute.
count: The count to pass.
"""
self._debug_log("Ambiguous match for '{}'".format(self._keystring))
time = config.val.input.ambiguous_timeout
if time == 0:
# execute immediately
self.clear_keystring()
self.execute(binding, self.Type.chain, count)
else:
# execute in `time' ms
self._debug_log("Scheduling execution of {} in {}ms".format(
binding, time))
self._ambiguous_timer.setInterval(time)
self._ambiguous_timer.timeout.connect(
functools.partial(self.delayed_exec, binding, count))
self._ambiguous_timer.start()
def delayed_exec(self, command, count):
"""Execute a delayed command.
Args:
command/count: As if passed to self.execute()
"""
self._debug_log("Executing delayed command now!")
self.clear_keystring()
self.execute(command, self.Type.chain, count)
def handle(self, e):
"""Handle a new keypress and call the respective handlers.
@@ -7,11 +7,13 @@ Feature: Keyboard input
# :clear-keychain
Scenario: Clearing the keychain
When I run :bind foo message-error test12
And I run :bind bar message-info test12-2
And I press the keys "fo"
When I run :bind ,foo message-error test12
And I run :bind ,bar message-info test12-2
And I press the keys ",fo"
And I run :clear-keychain
And I press the keys "bar"
And I press the keys ",bar"
And I run :unbind ,foo
And I run :unbind ,bar
Then the message "test12-2" should be shown
# input.forward_unbound_keys
@@ -37,7 +37,6 @@ def keyparser(key_config_stub):
0, supports_count=True, supports_chains=True)
kp.execute = mock.Mock()
yield kp
assert not kp._ambiguous_timer.isActive()
@pytest.fixture
@@ -242,51 +241,15 @@ def test_0_press(self, handle_text, keyparser):
'message-info 0', keyparser.Type.chain, None)
assert keyparser._keystring == ''
def test_ambiguous_keychain(self, qapp, handle_text, config_stub,
keyparser):
config_stub.val.input.ambiguous_timeout = 100
timer = keyparser._ambiguous_timer
assert not timer.isActive()
# We start with 'a' where the keychain gives us an ambiguous result.
# Then we check if the timer has been set up correctly
handle_text((Qt.Key_A, 'a'))
assert not keyparser.execute.called
assert timer.isSingleShot()
assert timer.interval() == 100
assert timer.isActive()
# Now we type an 'x' and check 'ax' has been executed and the timer
# stopped.
handle_text((Qt.Key_X, 'x'))
keyparser.execute.assert_called_once_with(
'message-info ax', keyparser.Type.chain, None)
assert not timer.isActive()
assert keyparser._keystring == ''
def test_ambiguous_keychain_no_timeout(self, handle_text, config_stub,
keyparser):
config_stub.val.input.ambiguous_timeout = 0
def test_ambiguous_keychain(self, handle_text, keyparser):
handle_text((Qt.Key_A, 'a'))
assert keyparser.execute.called
assert not keyparser._ambiguous_timer.isActive()
def test_invalid_keychain(self, handle_text, keyparser):
handle_text((Qt.Key_B, 'b'))
handle_text((Qt.Key_C, 'c'))
assert keyparser._keystring == ''
def test_ambiguous_delayed_exec(self, handle_text, config_stub, qtbot,
keyparser):
config_stub.val.input.ambiguous_timeout = 100
# 'a' is an ambiguous result.
handle_text((Qt.Key_A, 'a'))
assert not keyparser.execute.called
assert keyparser._ambiguous_timer.isActive()
# We wait for the timeout to occur.
with qtbot.waitSignal(keyparser.keystring_updated):
pass
assert keyparser.execute.called
class TestCount:

0 comments on commit 1fc9817

Please sign in to comment.