vi_state.py 3.3 KB

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