1515
1616from textual_inputs .events import InputOnChange , InputOnFocus
1717
18+ from rich .console import Console
19+ from rich .syntax import Syntax
20+
1821if TYPE_CHECKING :
1922 from rich .console import RenderableType
2023
24+ CONSOLE = Console ()
25+
26+
27+ def get_syntax (code , syntax ):
28+ syntax = Syntax (code , syntax )
29+ with CONSOLE .capture () as capture :
30+ CONSOLE .print (syntax )
31+ return capture .get ()
32+
2133
2234class TextInput (Widget ):
2335 """
@@ -39,6 +51,7 @@ class TextInput(Widget):
3951 placeholder (str): The placeholder message.
4052 title (str): The displayed title of the widget.
4153 has_password (bool): True if the text field masks the input.
54+ syntax (Optional[str]): the name of the language for syntax highlighting.
4255 has_focus (bool): True if the widget is focused.
4356 cursor (Tuple[str, Style]): The character used for the cursor
4457 and a rich Style object defining its appearance.
@@ -81,13 +94,15 @@ def __init__(
8194 placeholder : str = "" ,
8295 title : str = "" ,
8396 password : bool = False ,
97+ syntax : Optional [str ] = None ,
8498 ** kwargs : Any ,
8599 ) -> None :
86100 super ().__init__ (name , ** kwargs )
87101 self .value = value
88102 self .placeholder = placeholder
89103 self .title = title
90104 self .has_password = password
105+ self .syntax = syntax
91106 self ._cursor_position = len (self .value )
92107
93108 def __rich_repr__ (self ):
@@ -142,13 +157,15 @@ def render(self) -> RenderableType:
142157 box = rich .box .DOUBLE if self .has_focus else rich .box .SQUARE ,
143158 )
144159
145- def _conceal_or_reveal (self , segment : str ) -> str :
160+ def _conceal_or_reveal (self , segment : str ) -> Union [ str , Text ] :
146161 """
147- Produce the segment either concealed like a password or as it
148- was passed .
162+ Produce the segment concealed like a password, as it was passed,
163+ or syntax highlighted .
149164 """
150165 if self .has_password :
151- return "" .join ("•" for _ in segment )
166+ return "•" * len (segment )
167+ if self .syntax :
168+ return Text .from_ansi (get_syntax (segment , self .syntax ))
152169 return segment
153170
154171 def _render_text_with_cursor (self ) -> List [Union [str , Tuple [str , Style ]]]:
@@ -157,16 +174,18 @@ def _render_text_with_cursor(self) -> List[Union[str, Tuple[str, Style]]]:
157174 """
158175 if len (self .value ) == 0 :
159176 segments = [self .cursor ]
160- elif self ._cursor_position == 0 :
161- segments = [self .cursor , self ._conceal_or_reveal (self .value )]
162- elif self ._cursor_position == len (self .value ):
163- segments = [self ._conceal_or_reveal (self .value ), self .cursor ]
164177 else :
165- segments = [
166- self ._conceal_or_reveal (self .value [: self ._cursor_position ]),
167- self .cursor ,
168- self ._conceal_or_reveal (self .value [self ._cursor_position :]),
169- ]
178+ text = self ._conceal_or_reveal (self .value )
179+ if self ._cursor_position == 0 :
180+ segments = [self .cursor , text ]
181+ elif self ._cursor_position == len (self .value ):
182+ segments = [text , self .cursor ]
183+ else :
184+ segments = [
185+ text [: self ._cursor_position ],
186+ self .cursor ,
187+ text [self ._cursor_position :],
188+ ]
170189
171190 return segments
172191
0 commit comments