diff options
author | Jonathan Slenders <jonathan@slenders.be> | 2018-05-15 04:25:28 +0200 |
---|---|---|
committer | Jonathan Slenders <jonathan@slenders.be> | 2018-05-15 05:00:14 +0200 |
commit | acaa0860d2d17686d307c610ed7c2890a80d5911 (patch) | |
tree | 3b55a22ed2e411b49ecc8ccd754f479764badb6f /prompt_toolkit | |
parent | 5a3935caab0bf720db6707bb7974eec2400f3701 (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.py | 10 | ||||
-rw-r--r-- | prompt_toolkit/key_binding/bindings/vi.py | 12 | ||||
-rw-r--r-- | prompt_toolkit/key_binding/emacs_state.py | 16 | ||||
-rw-r--r-- | prompt_toolkit/key_binding/key_bindings.py | 40 | ||||
-rw-r--r-- | prompt_toolkit/key_binding/key_processor.py | 13 |
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): """ |