summaryrefslogtreecommitdiffstats
path: root/prompt_toolkit
diff options
context:
space:
mode:
authorJonathan Slenders <jonathan@slenders.be>2018-05-15 04:25:28 +0200
committerJonathan Slenders <jonathan@slenders.be>2018-05-15 05:00:14 +0200
commitacaa0860d2d17686d307c610ed7c2890a80d5911 (patch)
tree3b55a22ed2e411b49ecc8ccd754f479764badb6f /prompt_toolkit
parent5a3935caab0bf720db6707bb7974eec2400f3701 (diff)
Fix issues with nested macros. Added 'record_in_macro' parameter for key bindings.
Diffstat (limited to 'prompt_toolkit')
-rw-r--r--prompt_toolkit/key_binding/bindings/named_commands.py10
-rw-r--r--prompt_toolkit/key_binding/bindings/vi.py12
-rw-r--r--prompt_toolkit/key_binding/emacs_state.py16
-rw-r--r--prompt_toolkit/key_binding/key_bindings.py40
-rw-r--r--prompt_toolkit/key_binding/key_processor.py13
5 files changed, 61 insertions, 30 deletions
diff --git a/prompt_toolkit/key_binding/bindings/named_commands.py b/prompt_toolkit/key_binding/bindings/named_commands.py
index 2b46e3a8..7b70fcf8 100644
--- a/prompt_toolkit/key_binding/bindings/named_commands.py
+++ b/prompt_toolkit/key_binding/bindings/named_commands.py
@@ -11,6 +11,7 @@ from .completion import generate_completions, display_completions_like_readline
from prompt_toolkit.document import Document
from prompt_toolkit.enums import EditingMode
from prompt_toolkit.key_binding.key_processor import KeyPress
+from prompt_toolkit.key_binding.key_bindings import key_binding
from prompt_toolkit.keys import Keys
from prompt_toolkit.search import SearchDirection
from prompt_toolkit.selection import PasteMode
@@ -31,8 +32,7 @@ def register(name):
assert isinstance(name, six.text_type)
def decorator(handler):
- assert callable(handler)
-
+ " `handler` is a callable or _Binding. "
_readline_commands[name] = handler
return handler
return decorator
@@ -480,10 +480,16 @@ def end_kbd_macro(event):
@register('call-last-kbd-macro')
+@key_binding(record_in_macro=False)
def call_last_kbd_macro(event):
"""
Re-execute the last keyboard macro defined, by making the characters in the
macro appear as if typed at the keyboard.
+
+ Notice that we pass `record_in_macro=False`. This ensures that the 'c-x e'
+ key sequence doesn't appear in the recording itself. This function inserts
+ the body of the called macro back into the KeyProcessor, so these keys will
+ be added later on to the macro of their handlers have `record_in_macro=True`.
"""
# Insert the macro.
event.app.key_processor.feed_multiple(
diff --git a/prompt_toolkit/key_binding/bindings/vi.py b/prompt_toolkit/key_binding/bindings/vi.py
index 7e401e9c..568e4ae4 100644
--- a/prompt_toolkit/key_binding/bindings/vi.py
+++ b/prompt_toolkit/key_binding/bindings/vi.py
@@ -1703,9 +1703,17 @@ def load_vi_bindings():
vi_state.recording_register = None
vi_state.current_recording = ''
- @handle('@', Keys.Any, filter=vi_navigation_mode)
+ @handle('@', Keys.Any, filter=vi_navigation_mode, record_in_macro=False)
def _(event):
- " Execute macro. "
+ """
+ Execute macro.
+
+ Notice that we pass `record_in_macro=False`. This ensures that the `@x`
+ keys don't appear in the recording itself. This function inserts the
+ body of the called macro back into the KeyProcessor, so these keys will
+ be added later on to the macro of their handlers have
+ `record_in_macro=True`.
+ """
# Retrieve macro.
c = event.key_sequence[1].data
try:
diff --git a/prompt_toolkit/key_binding/emacs_state.py b/prompt_toolkit/key_binding/emacs_state.py
index 63ffc8b1..6a96c0a0 100644
--- a/prompt_toolkit/key_binding/emacs_state.py
+++ b/prompt_toolkit/key_binding/emacs_state.py
@@ -12,18 +12,22 @@ class EmacsState(object):
def __init__(self):
# Simple macro recording. (Like Readline does.)
# (For Emacs mode.)
- self.record_macro = False
self.macro = []
+ self.current_recording = None
def reset(self):
- self.record_macro = False
- self.macro = []
+ self.current_recording = None
+
+ @property
+ def is_recording(self):
+ " Tell whether we are recording a macro. "
+ return self.current_recording is not None
def start_macro(self):
" Start recording macro. "
- self.record_macro = True
- self.macro = []
+ self.current_recording = []
def end_macro(self):
" End recording macro. "
- self.record_macro = False
+ self.macro = self.current_recording
+ self.current_recording = None
diff --git a/prompt_toolkit/key_binding/key_bindings.py b/prompt_toolkit/key_binding/key_bindings.py
index ab7d39d0..0941ab20 100644
--- a/prompt_toolkit/key_binding/key_bindings.py
+++ b/prompt_toolkit/key_binding/key_bindings.py
@@ -38,7 +38,7 @@ from __future__ import unicode_literals
from abc import ABCMeta, abstractmethod, abstractproperty
from prompt_toolkit.cache import SimpleCache
-from prompt_toolkit.filters import Filter, to_filter, Never
+from prompt_toolkit.filters import to_filter, Never
from prompt_toolkit.keys import Keys, ALL_KEYS, KEY_ALIASES
from six import text_type, with_metaclass
@@ -56,21 +56,22 @@ __all__ = [
class _Binding(object):
"""
(Immutable binding class.)
+
+ :param record_in_macro: When True, don't record this key binding when a macro is recorded.
"""
- def __init__(self, keys, handler, filter=None, eager=None, is_global=None, save_before=None):
+ def __init__(self, keys, handler, filter=True, eager=False,
+ is_global=False, save_before=None, record_in_macro=True):
assert isinstance(keys, tuple)
assert callable(handler)
- assert isinstance(filter, Filter)
- assert isinstance(eager, Filter)
- assert isinstance(is_global, Filter)
assert callable(save_before)
self.keys = keys
self.handler = handler
- self.filter = filter
- self.eager = eager
- self.is_global = is_global
+ self.filter = to_filter(filter)
+ self.eager = to_filter(eager)
+ self.is_global = to_filter(is_global)
self.save_before = save_before
+ self.record_in_macro = to_filter(record_in_macro)
def call(self, event):
return self.handler(event)
@@ -170,11 +171,14 @@ class KeyBindings(KeyBindingsBase):
:param save_before: Callable that takes an `Event` and returns True if
we should save the current buffer, before handling the event.
(That's the default.)
+ :param record_in_macro: Record these key bindings when a macro is
+ being recorded. (True by default.)
"""
filter = to_filter(kwargs.pop('filter', True))
eager = to_filter(kwargs.pop('eager', False))
is_global = to_filter(kwargs.pop('is_global', False))
save_before = kwargs.pop('save_before', lambda e: True)
+ record_in_macro = to_filter(kwargs.pop('record_in_macro', True))
assert not kwargs
assert keys
@@ -198,11 +202,13 @@ class KeyBindings(KeyBindingsBase):
filter=func.filter & filter,
eager=eager | func.eager,
is_global = is_global | func.is_global,
- save_before=func.save_before))
+ save_before=func.save_before,
+ record_in_macro=func.record_in_macro))
else:
self.bindings.append(
_Binding(keys, func, filter=filter, eager=eager,
- is_global=is_global, save_before=save_before))
+ is_global=is_global, save_before=save_before,
+ record_in_macro=record_in_macro))
self._clear_cache()
return func
@@ -332,20 +338,25 @@ def _check_and_expand_key(key):
return key
-def key_binding(filter=True, eager=False, is_global=False, save_before=None):
+def key_binding(filter=True, eager=False, is_global=False, save_before=None,
+ record_in_macro=True):
"""
Decorator that turn a function into a `_Binding` object. This can be added
to a `KeyBindings` object when a key binding is assigned.
"""
+ assert save_before is None or callable(save_before)
+
filter = to_filter(filter)
eager = to_filter(eager)
is_global = to_filter(is_global)
- save_before = save_before or (lambda e: True)
+ save_before = save_before or (lambda event: True)
+ record_in_macro = to_filter(record_in_macro)
keys = ()
def decorator(function):
return _Binding(keys, function, filter=filter, eager=eager,
- is_global=is_global, save_before=save_before)
+ is_global=is_global, save_before=save_before,
+ record_in_macro=record_in_macro)
return decorator
@@ -427,7 +438,8 @@ class ConditionalKeyBindings(_Proxy):
filter=self.filter & b.filter,
eager=b.eager,
is_global=b.is_global,
- save_before=b.save_before))
+ save_before=b.save_before,
+ record_in_macro=b.record_in_macro))
self._bindings2 = bindings2
self._last_version = expected_version
diff --git a/prompt_toolkit/key_binding/key_processor.py b/prompt_toolkit/key_binding/key_processor.py
index 862f96d2..80dc0891 100644
--- a/prompt_toolkit/key_binding/key_processor.py
+++ b/prompt_toolkit/key_binding/key_processor.py
@@ -302,7 +302,7 @@ class KeyProcessor(object):
def _call_handler(self, handler, key_sequence=None):
app = get_app()
- was_recording_emacs = app.emacs_state.record_macro
+ was_recording_emacs = app.emacs_state.is_recording
was_recording_vi = bool(app.vi_state.recording_register)
arg = self.arg
self.arg = None
@@ -331,12 +331,13 @@ class KeyProcessor(object):
# Record the key sequence in our macro. (Only if we're in macro mode
# before and after executing the key.)
- if app.emacs_state.record_macro and was_recording_emacs:
- app.emacs_state.macro.extend(key_sequence)
+ if handler.record_in_macro():
+ if app.emacs_state.is_recording and was_recording_emacs:
+ app.emacs_state.current_recording.extend(key_sequence)
- if app.vi_state.recording_register and was_recording_vi:
- for k in key_sequence:
- app.vi_state.current_recording += k.data
+ if app.vi_state.recording_register and was_recording_vi:
+ for k in key_sequence:
+ app.vi_state.current_recording += k.data
def _fix_vi_cursor_position(self, event):
"""