Source code for prompt.action

"""Action module."""
import re
from .digraph import Digraph
from .util import int2char, int2repr, getchar


[docs]class Action: """Action class which holds action callbacks. Attributes: registry (dict): An action dictionary. """ __slots__ = ('registry',) def __init__(self): """Constructor.""" self.registry = {}
[docs] def register(self, name, callback): """Register action callback to a specified name. Args: name (str): An action name which follow {namespace}:{action name} callback (Callable): An action callback which take a ``prompt.prompt.Prompt`` instance and return None or int. Example: >>> from .prompt import STATUS_ACCEPT >>> action = Action() >>> action.register('prompt:accept', lambda prompt: STATUS_ACCEPT) """ self.registry[name] = callback
[docs] def register_from_rules(self, rules) -> None: """Register action callbacks from rules. Args: rules (Iterable): An iterator which returns rules. A rule is a (name, callback) tuple. Example: >>> from .prompt import STATUS_ACCEPT, STATUS_CANCEL >>> action = Action() >>> action.register_from_rules([ ... ('prompt:accept', lambda prompt: STATUS_ACCEPT), ... ('prompt:cancel', lambda prompt: STATUS_CANCEL), ... ]) """ for rule in rules: self.register(*rule)
[docs] def call(self, prompt, name): """Call a callback of specified action. Args: prompt (Prompt): A ``prompt.prompt.Prompt`` instance. name (str): An action name. Example: >>> from unittest.mock import MagicMock >>> from .prompt import STATUS_ACCEPT, STATUS_CANCEL >>> prompt = MagicMock() >>> action = Action() >>> action.register_from_rules([ ... ('prompt:accept', lambda prompt: STATUS_ACCEPT), ... ('prompt:cancel', lambda prompt: STATUS_CANCEL), ... ]) >>> action.call(prompt, 'prompt:accept') 1 >>> action.call(prompt, 'unknown:accept') 1 >>> action.call(prompt, 'unknown:unknown') Traceback (most recent call last): ... AttributeError: No action "unknown:unknown" has registered. Returns: None or int: None or int which represent the prompt status. """ alternative_name = re.sub(r'[^:]+:(.*)', r'prompt:\1', name) if name not in self.registry and alternative_name in self.registry: # fallback to the prompt's builtin action name = alternative_name if name in self.registry: fn = self.registry[name] return fn(prompt) raise AttributeError( 'No action "%s" has registered.' % name )
@classmethod
[docs] def from_rules(cls, rules): """Create a new action instance from rules. Args: rules (Iterable): An iterator which returns rules. A rule is a (name, callback) tuple. Example: >>> from .prompt import STATUS_ACCEPT, STATUS_CANCEL >>> Action.from_rules([ ... ('prompt:accept', lambda prompt: STATUS_ACCEPT), ... ('prompt:cancel', lambda prompt: STATUS_CANCEL), ... ]) <....action.Action object at ...> Returns: Action: An action instance. """ action = cls() action.register_from_rules(rules) return action
# Default actions ------------------------------------------------------------- def _accept(prompt): from .prompt import STATUS_ACCEPT return STATUS_ACCEPT def _cancel(prompt): from .prompt import STATUS_CANCEL return STATUS_CANCEL def _toggle_insert_mode(prompt): from .prompt import INSERT_MODE_INSERT, INSERT_MODE_REPLACE if prompt.insert_mode == INSERT_MODE_INSERT: prompt.insert_mode = INSERT_MODE_REPLACE else: prompt.insert_mode = INSERT_MODE_INSERT def _delete_char_before_caret(prompt): if prompt.caret.locus == 0: return prompt.context.text = ''.join([ prompt.caret.get_backward_text()[:-1], prompt.caret.get_selected_text(), prompt.caret.get_forward_text(), ]) prompt.caret.locus -= 1 def _delete_word_before_caret(prompt): if prompt.caret.locus == 0: return # Use vim's substitute to respect 'iskeyword' original_backward_text = prompt.caret.get_backward_text() backward_text = prompt.nvim.call( 'substitute', original_backward_text, '\k\+\s*$', '', '', ) prompt.context.text = ''.join([ backward_text, prompt.caret.get_selected_text(), prompt.caret.get_forward_text(), ]) prompt.caret.locus -= len(original_backward_text) - len(backward_text) def _delete_char_under_caret(prompt): prompt.context.text = ''.join([ prompt.caret.get_backward_text(), prompt.caret.get_forward_text(), ]) def _delete_text_after_caret(prompt): prompt.context.text = prompt.caret.get_backward_text() prompt.caret.locus = prompt.caret.tail def _delete_entire_text(prompt): prompt.context.text = '' prompt.caret.locus = prompt.caret.tail def _move_caret_to_left(prompt): prompt.caret.locus -= 1 def _move_caret_to_one_word_left(prompt): # Use vim's substitute to respect 'iskeyword' original_text = prompt.caret.get_backward_text() substituted_text = prompt.nvim.call( 'substitute', original_text, '\k\+\s\?$', '', '', ) offset = len(original_text) - len(substituted_text) prompt.caret.locus -= 1 if not offset else offset def _move_caret_to_right(prompt): prompt.caret.locus += 1 def _move_caret_to_one_word_right(prompt): # Use vim's substitute to respect 'iskeyword' original_text = prompt.caret.get_forward_text() substituted_text = prompt.nvim.call( 'substitute', original_text, '^\k\+', '', '', ) prompt.caret.locus += 1 + len(original_text) - len(substituted_text) def _move_caret_to_head(prompt): prompt.caret.locus = prompt.caret.head def _move_caret_to_lead(prompt): prompt.caret.locus = prompt.caret.lead def _move_caret_to_tail(prompt): prompt.caret.locus = prompt.caret.tail def _assign_previous_text(prompt): prompt.text = prompt.history.previous() def _assign_next_text(prompt): prompt.text = prompt.history.next() def _assign_previous_matched_text(prompt): prompt.text = prompt.history.previous_match() def _assign_next_matched_text(prompt): prompt.text = prompt.history.next_match() def _paste_from_register(prompt): context = prompt.context.to_dict() prompt.update_text('"') prompt.redraw_prompt() reg = int2char(prompt.nvim, getchar(prompt.nvim)) prompt.context.extend(context) val = prompt.nvim.call('getreg', reg) prompt.update_text(val) def _paste_from_default_register(prompt): val = prompt.nvim.call('getreg', prompt.nvim.vvars['register']) prompt.update_text(val) def _yank_to_register(prompt): context = prompt.context.to_dict() prompt.update_text("'") prompt.redraw_prompt() reg = int2char(prompt.nvim, getchar(prompt.nvim)) prompt.context.extend(context) prompt.nvim.call('setreg', reg, prompt.text) def _yank_to_default_register(prompt): prompt.nvim.call('setreg', prompt.nvim.vvars['register'], prompt.text) def _insert_special(prompt): context = prompt.context.to_dict() prompt.update_text('^') prompt.redraw_prompt() code = getchar(prompt.nvim) prompt.context.extend(context) # Substitute special keys into control char if code == b'\x80kb': code = 0x08 # ^H char = int2repr(prompt.nvim, code) prompt.update_text(char) def _insert_digraph(prompt): context = prompt.context.to_dict() prompt.update_text('?') prompt.redraw_prompt() digraph = Digraph() char = digraph.retrieve(prompt.nvim) prompt.context.extend(context) prompt.update_text(char) DEFAULT_ACTION = Action.from_rules([ ('prompt:accept', _accept), ('prompt:cancel', _cancel), ('prompt:toggle_insert_mode', _toggle_insert_mode), ('prompt:delete_char_before_caret', _delete_char_before_caret), ('prompt:delete_word_before_caret', _delete_word_before_caret), ('prompt:delete_char_under_caret', _delete_char_under_caret), ('prompt:delete_text_after_caret', _delete_text_after_caret), ('prompt:delete_entire_text', _delete_entire_text), ('prompt:move_caret_to_left', _move_caret_to_left), ('prompt:move_caret_to_one_word_left', _move_caret_to_one_word_left), ('prompt:move_caret_to_right', _move_caret_to_right), ('prompt:move_caret_to_one_word_right', _move_caret_to_one_word_right), ('prompt:move_caret_to_head', _move_caret_to_head), ('prompt:move_caret_to_lead', _move_caret_to_lead), ('prompt:move_caret_to_tail', _move_caret_to_tail), ('prompt:assign_previous_text', _assign_previous_text), ('prompt:assign_next_text', _assign_next_text), ('prompt:assign_previous_matched_text', _assign_previous_matched_text), ('prompt:assign_next_matched_text', _assign_next_matched_text), ('prompt:paste_from_register', _paste_from_register), ('prompt:paste_from_default_register', _paste_from_default_register), ('prompt:yank_to_register', _yank_to_register), ('prompt:yank_to_default_register', _yank_to_default_register), ('prompt:insert_special', _insert_special), ('prompt:insert_digraph', _insert_digraph), ])