Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/prompt_toolkit/cursor_shapes.py: 60%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

50 statements  

1from __future__ import annotations 

2 

3from abc import ABC, abstractmethod 

4from collections.abc import Callable 

5from enum import Enum 

6from typing import TYPE_CHECKING, Any 

7 

8from prompt_toolkit.enums import EditingMode 

9from prompt_toolkit.key_binding.vi_state import InputMode 

10 

11if TYPE_CHECKING: 

12 from .application import Application 

13 

14__all__ = [ 

15 "CursorShape", 

16 "CursorShapeConfig", 

17 "SimpleCursorShapeConfig", 

18 "ModalCursorShapeConfig", 

19 "DynamicCursorShapeConfig", 

20 "to_cursor_shape_config", 

21] 

22 

23 

24class CursorShape(Enum): 

25 # Default value that should tell the output implementation to never send 

26 # cursor shape escape sequences. This is the default right now, because 

27 # before this `CursorShape` functionality was introduced into 

28 # prompt_toolkit itself, people had workarounds to send cursor shapes 

29 # escapes into the terminal, by monkey patching some of prompt_toolkit's 

30 # internals. We don't want the default prompt_toolkit implementation to 

31 # interfere with that. E.g., IPython patches the `ViState.input_mode` 

32 # property. See: https://github.com/ipython/ipython/pull/13501/files 

33 _NEVER_CHANGE = "_NEVER_CHANGE" 

34 

35 BLOCK = "BLOCK" 

36 BEAM = "BEAM" 

37 UNDERLINE = "UNDERLINE" 

38 BLINKING_BLOCK = "BLINKING_BLOCK" 

39 BLINKING_BEAM = "BLINKING_BEAM" 

40 BLINKING_UNDERLINE = "BLINKING_UNDERLINE" 

41 

42 

43class CursorShapeConfig(ABC): 

44 @abstractmethod 

45 def get_cursor_shape(self, application: Application[Any]) -> CursorShape: 

46 """ 

47 Return the cursor shape to be used in the current state. 

48 """ 

49 

50 

51AnyCursorShapeConfig = CursorShape | CursorShapeConfig | None 

52 

53 

54class SimpleCursorShapeConfig(CursorShapeConfig): 

55 """ 

56 Always show the given cursor shape. 

57 """ 

58 

59 def __init__(self, cursor_shape: CursorShape = CursorShape._NEVER_CHANGE) -> None: 

60 self.cursor_shape = cursor_shape 

61 

62 def get_cursor_shape(self, application: Application[Any]) -> CursorShape: 

63 return self.cursor_shape 

64 

65 

66class ModalCursorShapeConfig(CursorShapeConfig): 

67 """ 

68 Show cursor shape according to the current input mode. 

69 """ 

70 

71 def get_cursor_shape(self, application: Application[Any]) -> CursorShape: 

72 if application.editing_mode == EditingMode.VI: 

73 if application.vi_state.input_mode in { 

74 InputMode.NAVIGATION, 

75 }: 

76 return CursorShape.BLOCK 

77 if application.vi_state.input_mode in { 

78 InputMode.INSERT, 

79 InputMode.INSERT_MULTIPLE, 

80 }: 

81 return CursorShape.BEAM 

82 if application.vi_state.input_mode in { 

83 InputMode.REPLACE, 

84 InputMode.REPLACE_SINGLE, 

85 }: 

86 return CursorShape.UNDERLINE 

87 elif application.editing_mode == EditingMode.EMACS: 

88 # like vi's INSERT 

89 return CursorShape.BEAM 

90 

91 # Default 

92 return CursorShape.BLOCK 

93 

94 

95class DynamicCursorShapeConfig(CursorShapeConfig): 

96 def __init__( 

97 self, get_cursor_shape_config: Callable[[], AnyCursorShapeConfig] 

98 ) -> None: 

99 self.get_cursor_shape_config = get_cursor_shape_config 

100 

101 def get_cursor_shape(self, application: Application[Any]) -> CursorShape: 

102 return to_cursor_shape_config(self.get_cursor_shape_config()).get_cursor_shape( 

103 application 

104 ) 

105 

106 

107def to_cursor_shape_config(value: AnyCursorShapeConfig) -> CursorShapeConfig: 

108 """ 

109 Take a `CursorShape` instance or `CursorShapeConfig` and turn it into a 

110 `CursorShapeConfig`. 

111 """ 

112 if value is None: 

113 return SimpleCursorShapeConfig() 

114 

115 if isinstance(value, CursorShape): 

116 return SimpleCursorShapeConfig(value) 

117 

118 return value