Completed
Push — master ( 82760b...c05ef7 )
by Lambda
02:32
created

Action.call()   B

Complexity

Conditions 2

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
dl 0
loc 28
rs 8.8571
c 0
b 0
f 0
1
"""Action module."""
2
from typing import Callable, Optional, Dict, Tuple, Sequence    # noqa: F401
3
from .prompt import Prompt, Status
4
5
ActionCallback = Callable[[Prompt], Optional[int]]
6
ActionRules = Sequence[Tuple[str, ActionCallback]]
7
8
9
class Action:
10
    """Action class which holds action callbacks.
11
12
    Attributes:
13
        registry (dict): An action dictionary.
14
    """
15
16
    __slots__ = ('registry',)
17
18
    def __init__(self) -> None:
19
        """Constructor."""
20
        self.registry = {}  # type: Dict[str, ActionCallback]
21
22
    def register(self, name: str, callback: ActionCallback=None) -> None:
23
        """Register action callback to a specified name.
24
25
        Args:
26
            name (str): An action name which follow {namespace}:{action name}
27
            callback (Callable): An action callback which take a
28
                ``prompt.prompt.Prompt`` instance and return None or int.
29
30
        Example:
31
            >>> from neovim_prompt.prompt import Status
32
            >>> action = Action()
33
            >>> action.register('prompt:accept', lambda prompt: Status.accept)
34
        """
35
        self.registry[name] = callback
36
37
    def register_from_rules(self, rules: ActionRules) -> None:
38
        """Register action callbacks from rules.
39
40
        Args:
41
            rules (Iterable): An iterator which returns rules. A rule is a
42
                (name, callback) tuple.
43
44
        Example:
45
            >>> from neovim_prompt.prompt import Status
46
            >>> action = Action()
47
            >>> action.register_from_rules([
48
            ...     ('prompt:accept', lambda prompt: Status.accept),
49
            ...     ('prompt:cancel', lambda prompt: Status.cancel),
50
            ... ])
51
        """
52
        for rule in rules:
53
            self.register(*rule)
54
55
    def call(self, prompt: Prompt, name: str) -> Optional[Status]:
56
        """Call a callback of specified action.
57
58
        Args:
59
            prompt (Prompt): A ``prompt.prompt.Prompt`` instance.
60
            name (str): An action name.
61
62
        Example:
63
            >>> from unittest.mock import MagicMock
64
            >>> from neovim_prompt.prompt import Status
65
            >>> prompt = MagicMock()
66
            >>> action = Action()
67
            >>> action.register_from_rules([
68
            ...     ('prompt:accept', lambda prompt: Status.accept),
69
            ...     ('prompt:cancel', lambda prompt: Status.cancel),
70
            ... ])
71
            >>> action.call(prompt, 'prompt:accept')
72
            <Status.accept: 1>
73
74
        Returns:
75
            None or Status: None or int which represent the prompt status.
76
        """
77
        if name not in self.registry:
78
            raise AttributeError(
79
                'No action "%s" has registered.' % name
80
            )
81
        fn = self.registry[name]
82
        return fn(prompt)
83
84
    @classmethod
85
    def from_rules(cls, rules: ActionRules) -> 'Action':
86
        """Create a new action instance from rules.
87
88
        Args:
89
            rules (Iterable): An iterator which returns rules. A rule is a
90
                (name, callback) tuple.
91
92
        Example:
93
            >>> from neovim_prompt.prompt import Status
94
            >>> Action.from_rules([
95
            ...     ('prompt:accept', lambda prompt: Status.accept),
96
            ...     ('prompt:cancel', lambda prompt: Status.cancel),
97
            ... ])
98
            <neovim_prompt.action.Action object at ...>
99
100
        Returns:
101
            Action: An action instance.
102
        """
103
        action = cls()
104
        action.register_from_rules(rules)
105
        return action
106
107
108
# Default actions -------------------------------------------------------------
109
def _accept(prompt):
110
    from .prompt import Status
111
    return Status.accept
112
113
114
def _cancel(prompt):
115
    from .prompt import Status
116
    return Status.cancel
117
118
119
def _toggle_insert_mode(prompt):
120
    from .prompt import InsertMode
121
    if prompt.insert_mode == InsertMode.insert:
122
        prompt.insert_mode = InsertMode.replace
123
    else:
124
        prompt.insert_mode = InsertMode.insert
125
126
127
def _delete_char_before_caret(prompt):
128
    if prompt.caret.locus == 0:
129
        return
130
    prompt.context.text = ''.join([
131
        prompt.caret.get_backward_text()[:-1],
132
        prompt.caret.get_selected_text(),
133
        prompt.caret.get_forward_text(),
134
    ])
135
    prompt.caret.locus -= 1
136
137
138
def _delete_char_under_caret(prompt):
139
    prompt.context.text = ''.join([
140
        prompt.caret.get_backward_text(),
141
        prompt.caret.get_forward_text(),
142
    ])
143
144
145
def _delete_text_after_caret(prompt):
146
    prompt.context.text = prompt.caret.get_backward_text()
147
    prompt.caret.locus = prompt.caret.tail
148
149
150
def _delete_entire_text(prompt):
151
    prompt.context.text = ''
152
    prompt.caret.locus = prompt.caret.tail
153
154
155
def _move_caret_to_left(prompt):
156
    prompt.caret.locus -= 1
157
158
159
def _move_caret_to_right(prompt):
160
    prompt.caret.locus += 1
161
162
163
def _move_caret_to_head(prompt):
164
    prompt.caret.locus = prompt.caret.head
165
166
167
def _move_caret_to_lead(prompt):
168
    prompt.caret.locus = prompt.caret.lead
169
170
171
def _move_caret_to_tail(prompt):
172
    prompt.caret.locus = prompt.caret.tail
173
174
175
def _assign_previous_text(prompt):
176
    prompt.text = prompt.history.previous()
177
178
179
def _assign_next_text(prompt):
180
    prompt.text = prompt.history.next()
181
182
183
def _assign_previous_matched_text(prompt):
184
    prompt.text = prompt.history.previous_match()
185
186
187
def _assign_next_matched_text(prompt):
188
    prompt.text = prompt.history.next_match()
189
190
191
def _paste_from_register(prompt):
192
    prompt.nvim.command(r'echon "\""')
193
    reg = prompt.nvim.eval('nr2char(getchar())')
194
    val = prompt.nvim.call('getreg', reg)
195
    prompt.update_text(val)
196
197
198
def _paste_from_default_register(prompt):
199
    val = prompt.nvim.call('getreg', prompt.nvim.vvars['register'])
200
    prompt.update_text(val)
201
202
203
def _yank_to_register(prompt):
204
    prompt.nvim.command(r'echon "\""')
205
    reg = prompt.nvim.eval('nr2char(getchar())')
206
    prompt.nvim.call('setreg', reg, prompt.text)
207
208
209
def _yank_to_default_register(prompt):
210
    prompt.nvim.call('setreg', prompt.nvim.vvars['register'], prompt.text)
211
212
213
DEFAULT_ACTION = Action.from_rules([
214
    ('prompt:accept', _accept),
215
    ('prompt:cancel', _cancel),
216
    ('prompt:toggle_insert_mode', _toggle_insert_mode),
217
    ('prompt:delete_char_before_caret', _delete_char_before_caret),
218
    ('prompt:delete_char_under_caret', _delete_char_under_caret),
219
    ('prompt:delete_text_after_caret', _delete_text_after_caret),
220
    ('prompt:delete_entire_text', _delete_entire_text),
221
    ('prompt:move_caret_to_left', _move_caret_to_left),
222
    ('prompt:move_caret_to_right', _move_caret_to_right),
223
    ('prompt:move_caret_to_head', _move_caret_to_head),
224
    ('prompt:move_caret_to_lead', _move_caret_to_lead),
225
    ('prompt:move_caret_to_tail', _move_caret_to_tail),
226
    ('prompt:assign_previous_text', _assign_previous_text),
227
    ('prompt:assign_next_text', _assign_next_text),
228
    ('prompt:assign_previous_matched_text', _assign_previous_matched_text),
229
    ('prompt:assign_next_matched_text', _assign_next_matched_text),
230
    ('prompt:paste_from_register', _paste_from_register),
231
    ('prompt:paste_from_default_register', _paste_from_default_register),
232
    ('prompt:yank_to_register', _yank_to_register),
233
    ('prompt:yank_to_default_register', _yank_to_default_register),
234
])
235