1from __future__ import annotations 
    2 
    3from abc import ABC, abstractmethod 
    4from enum import Enum 
    5from typing import TYPE_CHECKING, Any, Callable, Union 
    6 
    7from prompt_toolkit.enums import EditingMode 
    8from prompt_toolkit.key_binding.vi_state import InputMode 
    9 
    10if TYPE_CHECKING: 
    11    from .application import Application 
    12 
    13__all__ = [ 
    14    "CursorShape", 
    15    "CursorShapeConfig", 
    16    "SimpleCursorShapeConfig", 
    17    "ModalCursorShapeConfig", 
    18    "DynamicCursorShapeConfig", 
    19    "to_cursor_shape_config", 
    20] 
    21 
    22 
    23class CursorShape(Enum): 
    24    # Default value that should tell the output implementation to never send 
    25    # cursor shape escape sequences. This is the default right now, because 
    26    # before this `CursorShape` functionality was introduced into 
    27    # prompt_toolkit itself, people had workarounds to send cursor shapes 
    28    # escapes into the terminal, by monkey patching some of prompt_toolkit's 
    29    # internals. We don't want the default prompt_toolkit implementation to 
    30    # interfere with that. E.g., IPython patches the `ViState.input_mode` 
    31    # property. See: https://github.com/ipython/ipython/pull/13501/files 
    32    _NEVER_CHANGE = "_NEVER_CHANGE" 
    33 
    34    BLOCK = "BLOCK" 
    35    BEAM = "BEAM" 
    36    UNDERLINE = "UNDERLINE" 
    37    BLINKING_BLOCK = "BLINKING_BLOCK" 
    38    BLINKING_BEAM = "BLINKING_BEAM" 
    39    BLINKING_UNDERLINE = "BLINKING_UNDERLINE" 
    40 
    41 
    42class CursorShapeConfig(ABC): 
    43    @abstractmethod 
    44    def get_cursor_shape(self, application: Application[Any]) -> CursorShape: 
    45        """ 
    46        Return the cursor shape to be used in the current state. 
    47        """ 
    48 
    49 
    50AnyCursorShapeConfig = Union[CursorShape, CursorShapeConfig, None] 
    51 
    52 
    53class SimpleCursorShapeConfig(CursorShapeConfig): 
    54    """ 
    55    Always show the given cursor shape. 
    56    """ 
    57 
    58    def __init__(self, cursor_shape: CursorShape = CursorShape._NEVER_CHANGE) -> None: 
    59        self.cursor_shape = cursor_shape 
    60 
    61    def get_cursor_shape(self, application: Application[Any]) -> CursorShape: 
    62        return self.cursor_shape 
    63 
    64 
    65class ModalCursorShapeConfig(CursorShapeConfig): 
    66    """ 
    67    Show cursor shape according to the current input mode. 
    68    """ 
    69 
    70    def get_cursor_shape(self, application: Application[Any]) -> CursorShape: 
    71        if application.editing_mode == EditingMode.VI: 
    72            if application.vi_state.input_mode in { 
    73                InputMode.NAVIGATION, 
    74            }: 
    75                return CursorShape.BLOCK 
    76            if application.vi_state.input_mode in { 
    77                InputMode.INSERT, 
    78                InputMode.INSERT_MULTIPLE, 
    79            }: 
    80                return CursorShape.BEAM 
    81            if application.vi_state.input_mode in { 
    82                InputMode.REPLACE, 
    83                InputMode.REPLACE_SINGLE, 
    84            }: 
    85                return CursorShape.UNDERLINE 
    86        elif application.editing_mode == EditingMode.EMACS: 
    87            # like vi's INSERT 
    88            return CursorShape.BEAM 
    89 
    90        # Default 
    91        return CursorShape.BLOCK 
    92 
    93 
    94class DynamicCursorShapeConfig(CursorShapeConfig): 
    95    def __init__( 
    96        self, get_cursor_shape_config: Callable[[], AnyCursorShapeConfig] 
    97    ) -> None: 
    98        self.get_cursor_shape_config = get_cursor_shape_config 
    99 
    100    def get_cursor_shape(self, application: Application[Any]) -> CursorShape: 
    101        return to_cursor_shape_config(self.get_cursor_shape_config()).get_cursor_shape( 
    102            application 
    103        ) 
    104 
    105 
    106def to_cursor_shape_config(value: AnyCursorShapeConfig) -> CursorShapeConfig: 
    107    """ 
    108    Take a `CursorShape` instance or `CursorShapeConfig` and turn it into a 
    109    `CursorShapeConfig`. 
    110    """ 
    111    if value is None: 
    112        return SimpleCursorShapeConfig() 
    113 
    114    if isinstance(value, CursorShape): 
    115        return SimpleCursorShapeConfig(value) 
    116 
    117    return value