Refactor keyparser
This commit is contained in:
parent
aedf1889dd
commit
0f29526f91
@ -38,7 +38,7 @@ class KeyParser(QObject):
|
|||||||
MATCH_PARTIAL: Constant for a partial match (no keychain matched yet,
|
MATCH_PARTIAL: Constant for a partial match (no keychain matched yet,
|
||||||
but it's still possible in the future.
|
but it's still possible in the future.
|
||||||
MATCH_DEFINITIVE: Constant for a full match (keychain matches exactly).
|
MATCH_DEFINITIVE: Constant for a full match (keychain matches exactly).
|
||||||
MATCH_BOTH: There are both a partial and a definitive match.
|
MATCH_AMBIGUOUS: There are both a partial and a definitive match.
|
||||||
MATCH_NONE: Constant for no match (no more matches possible).
|
MATCH_NONE: Constant for no match (no more matches possible).
|
||||||
supports_count: If the keyparser should support counts.
|
supports_count: If the keyparser should support counts.
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ class KeyParser(QObject):
|
|||||||
|
|
||||||
MATCH_PARTIAL = 0
|
MATCH_PARTIAL = 0
|
||||||
MATCH_DEFINITIVE = 1
|
MATCH_DEFINITIVE = 1
|
||||||
MATCH_BOTH = 2
|
MATCH_AMBIGUOUS = 2
|
||||||
MATCH_NONE = 3
|
MATCH_NONE = 3
|
||||||
|
|
||||||
supports_count = False
|
supports_count = False
|
||||||
@ -123,54 +123,27 @@ class KeyParser(QObject):
|
|||||||
logging.debug('Ignoring, no text')
|
logging.debug('Ignoring, no text')
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._timer is not None:
|
self._stop_delayed_exec()
|
||||||
logging.debug("Stopping delayed execution.")
|
|
||||||
self._timer.stop()
|
|
||||||
self._timer = None
|
|
||||||
|
|
||||||
self._keystring += txt
|
self._keystring += txt
|
||||||
|
|
||||||
if self.supports_count:
|
if self.supports_count:
|
||||||
(countstr, cmdstr_needle) = re.match(r'^(\d*)(.*)',
|
(countstr, cmd_input) = re.match(r'^(\d*)(.*)',
|
||||||
self._keystring).groups()
|
self._keystring).groups()
|
||||||
|
count = int(countstr) if countstr else None
|
||||||
else:
|
else:
|
||||||
countstr = None
|
cmd_input = self._keystring
|
||||||
cmdstr_needle = self._keystring
|
count = None
|
||||||
|
|
||||||
if not cmdstr_needle:
|
if not cmd_input:
|
||||||
return
|
return
|
||||||
|
|
||||||
# FIXME this doesn't handle ambigious keys correctly.
|
(match, binding) = self._match_key(cmd_input)
|
||||||
#
|
|
||||||
# If a keychain is ambigious, we probably should set up a QTimer with a
|
|
||||||
# configurable timeout, which triggers cmd.run() when expiring. Then
|
|
||||||
# when we enter _handle() again in time we stop the timer.
|
|
||||||
|
|
||||||
(match, cmdstr_hay) = self._match_key(cmdstr_needle)
|
|
||||||
|
|
||||||
if match == self.MATCH_DEFINITIVE:
|
if match == self.MATCH_DEFINITIVE:
|
||||||
self._keystring = ''
|
self._keystring = ''
|
||||||
count = int(countstr) if countstr else None
|
self.execute(binding, count)
|
||||||
self.execute(cmdstr_hay, count=count)
|
elif match == self.MATCH_AMBIGUOUS:
|
||||||
elif match == self.MATCH_BOTH:
|
self._handle_ambiguous_match(binding, count)
|
||||||
logging.debug("Partial and definitive match for \"{}\"".format(
|
|
||||||
self._keystring))
|
|
||||||
time = config.get('general', 'cmd_timeout')
|
|
||||||
count = int(countstr) if countstr else None
|
|
||||||
if time == 0:
|
|
||||||
# execute immediately
|
|
||||||
self._keystring = ''
|
|
||||||
self.execute(cmdstr_hay, count=count)
|
|
||||||
else:
|
|
||||||
# execute in `time' ms
|
|
||||||
logging.debug("Scheduling execution of {} in {}ms".format(
|
|
||||||
cmdstr_hay, time))
|
|
||||||
self._timer = QTimer(self)
|
|
||||||
self._timer.setSingleShot(True)
|
|
||||||
self._timer.setInterval(time)
|
|
||||||
self._timer.timeout.connect(
|
|
||||||
partial(self.delayed_exec, cmdstr_hay, count))
|
|
||||||
self._timer.start()
|
|
||||||
elif match == self.MATCH_PARTIAL:
|
elif match == self.MATCH_PARTIAL:
|
||||||
logging.debug('No match for "{}" (added {})'.format(
|
logging.debug('No match for "{}" (added {})'.format(
|
||||||
self._keystring, txt))
|
self._keystring, txt))
|
||||||
@ -179,41 +152,41 @@ class KeyParser(QObject):
|
|||||||
self._keystring))
|
self._keystring))
|
||||||
self._keystring = ''
|
self._keystring = ''
|
||||||
|
|
||||||
def _match_key(self, cmdstr_needle):
|
def _match_key(self, cmd_input):
|
||||||
"""Try to match a given keystring with any bound keychain.
|
"""Try to match a given keystring with any bound keychain.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cmdstr_needle: The command string to find.
|
cmd_input: The command string to find.
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
A tuple (matchtype, hay) where matchtype is MATCH_DEFINITIVE,
|
A tuple (matchtype, binding).
|
||||||
MATCH_PARTIAL or MATCH_NONE and hay is the long keystring where the
|
matchtype: MATCH_DEFINITIVE, MATCH_AMBIGUOUS, MATCH_PARTIAL or
|
||||||
part was found in.
|
MATCH_NONE
|
||||||
|
binding: - None with MATCH_PARTIAL/MATCH_NONE
|
||||||
|
- The found binding with MATCH_DEFINITIVE/
|
||||||
|
MATCH_AMBIGUOUS
|
||||||
"""
|
"""
|
||||||
# Check definitive match
|
# A (cmd_input, binding) tuple (k, v of bindings) or None.
|
||||||
definitive_match = None
|
definitive_match = None
|
||||||
partial_match = False
|
partial_match = False
|
||||||
|
# Check definitive match
|
||||||
try:
|
try:
|
||||||
cmdstr_hay = self.bindings[cmdstr_needle]
|
definitive_match = (cmd_input, self.bindings[cmd_input])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
else:
|
|
||||||
definitive_match = (cmdstr_needle, cmdstr_hay)
|
|
||||||
# Check partial match
|
# Check partial match
|
||||||
for hay in self.bindings:
|
for binding in self.bindings:
|
||||||
if definitive_match is not None and hay == definitive_match[0]:
|
if definitive_match is not None and binding == definitive_match[0]:
|
||||||
# We already matched that one
|
# We already matched that one
|
||||||
continue
|
continue
|
||||||
try:
|
if len(binding) < len(cmd_input):
|
||||||
if cmdstr_needle[-1] == hay[len(cmdstr_needle) - 1]:
|
# binding is shorter than cmd_input, so it can't possibly match
|
||||||
partial_match = True
|
|
||||||
break
|
|
||||||
except IndexError:
|
|
||||||
# current cmd is shorter than our cmdstr_needle, so it
|
|
||||||
# won't match
|
|
||||||
continue
|
continue
|
||||||
|
elif cmd_input[-1] == binding[len(cmd_input) - 1]:
|
||||||
|
partial_match = True
|
||||||
|
break
|
||||||
if definitive_match is not None and partial_match:
|
if definitive_match is not None and partial_match:
|
||||||
return (self.MATCH_BOTH, definitive_match[1])
|
return (self.MATCH_AMBIGUOUS, definitive_match[1])
|
||||||
elif definitive_match is not None:
|
elif definitive_match is not None:
|
||||||
return (self.MATCH_DEFINITIVE, definitive_match[1])
|
return (self.MATCH_DEFINITIVE, definitive_match[1])
|
||||||
elif partial_match:
|
elif partial_match:
|
||||||
@ -221,6 +194,37 @@ class KeyParser(QObject):
|
|||||||
else:
|
else:
|
||||||
return (self.MATCH_NONE, None)
|
return (self.MATCH_NONE, None)
|
||||||
|
|
||||||
|
def _stop_delayed_exec(self):
|
||||||
|
"""Stop a delayed execution if any is running."""
|
||||||
|
if self._timer is not None:
|
||||||
|
logging.debug("Stopping delayed execution.")
|
||||||
|
self._timer.stop()
|
||||||
|
self._timer = None
|
||||||
|
|
||||||
|
def _handle_ambiguous_match(self, binding, count):
|
||||||
|
"""Handle an ambiguous match.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
binding: The command-string to execute.
|
||||||
|
count: The count to pass.
|
||||||
|
"""
|
||||||
|
logging.debug("Ambiguous match for \"{}\"".format(self._keystring))
|
||||||
|
time = config.get('general', 'cmd_timeout')
|
||||||
|
if time == 0:
|
||||||
|
# execute immediately
|
||||||
|
self._keystring = ''
|
||||||
|
self.execute(binding, count)
|
||||||
|
else:
|
||||||
|
# execute in `time' ms
|
||||||
|
logging.debug("Scheduling execution of {} in {}ms".format(binding,
|
||||||
|
time))
|
||||||
|
self._timer = QTimer(self)
|
||||||
|
self._timer.setSingleShot(True)
|
||||||
|
self._timer.setInterval(time)
|
||||||
|
self._timer.timeout.connect(partial(self.delayed_exec, binding,
|
||||||
|
count))
|
||||||
|
self._timer.start()
|
||||||
|
|
||||||
def _normalize_keystr(self, keystr):
|
def _normalize_keystr(self, keystr):
|
||||||
"""Normalize a keystring like Ctrl-Q to a keystring like Ctrl+Q.
|
"""Normalize a keystring like Ctrl-Q to a keystring like Ctrl+Q.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user