1from __future__ import annotations
2
3from collections.abc import Callable
4from enum import Enum
5from typing import TYPE_CHECKING
6
7from prompt_toolkit.clipboard import ClipboardData
8
9if TYPE_CHECKING:
10 from .bindings.vi import TextObject
11 from .key_processor import KeyPressEvent
12
13__all__ = [
14 "InputMode",
15 "CharacterFind",
16 "ViState",
17]
18
19
20class InputMode(str, Enum):
21 value: str
22
23 INSERT = "vi-insert"
24 INSERT_MULTIPLE = "vi-insert-multiple"
25 NAVIGATION = "vi-navigation" # Normal mode.
26 REPLACE = "vi-replace"
27 REPLACE_SINGLE = "vi-replace-single"
28
29
30class CharacterFind:
31 def __init__(self, character: str, backwards: bool = False) -> None:
32 self.character = character
33 self.backwards = backwards
34
35
36class ViState:
37 """
38 Mutable class to hold the state of the Vi navigation.
39 """
40
41 def __init__(self) -> None:
42 #: None or CharacterFind instance. (This is used to repeat the last
43 #: search in Vi mode, by pressing the 'n' or 'N' in navigation mode.)
44 self.last_character_find: CharacterFind | None = None
45
46 # When an operator is given and we are waiting for text object,
47 # -- e.g. in the case of 'dw', after the 'd' --, an operator callback
48 # is set here.
49 self.operator_func: None | (Callable[[KeyPressEvent, TextObject], None]) = None
50 self.operator_arg: int | None = None
51
52 #: Named registers. Maps register name (e.g. 'a') to
53 #: :class:`ClipboardData` instances.
54 self.named_registers: dict[str, ClipboardData] = {}
55
56 #: The Vi mode we're currently in to.
57 self.__input_mode = InputMode.INSERT
58
59 #: Waiting for digraph.
60 self.waiting_for_digraph = False
61 self.digraph_symbol1: str | None = None # (None or a symbol.)
62
63 #: When true, make ~ act as an operator.
64 self.tilde_operator = False
65
66 #: Register in which we are recording a macro.
67 #: `None` when not recording anything.
68 # Note that the recording is only stored in the register after the
69 # recording is stopped. So we record in a separate `current_recording`
70 # variable.
71 self.recording_register: str | None = None
72 self.current_recording: str = ""
73
74 # Temporary navigation (normal) mode.
75 # This happens when control-o has been pressed in insert or replace
76 # mode. The user can now do one navigation action and we'll return back
77 # to insert/replace.
78 self.temporary_navigation_mode = False
79
80 @property
81 def input_mode(self) -> InputMode:
82 "Get `InputMode`."
83 return self.__input_mode
84
85 @input_mode.setter
86 def input_mode(self, value: InputMode) -> None:
87 "Set `InputMode`."
88 if value == InputMode.NAVIGATION:
89 self.waiting_for_digraph = False
90 self.operator_func = None
91 self.operator_arg = None
92
93 self.__input_mode = value
94
95 def reset(self) -> None:
96 """
97 Reset state, go back to the given mode. INSERT by default.
98 """
99 # Go back to insert mode.
100 self.input_mode = InputMode.INSERT
101
102 self.waiting_for_digraph = False
103 self.operator_func = None
104 self.operator_arg = None
105
106 # Reset recording state.
107 self.recording_register = None
108 self.current_recording = ""