1515
1616from textual_inputs .events import InputOnChange , InputOnFocus , make_message_class
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 conceal_text (segment : str ) -> str :
28+ """Produce the segment concealed like a password."""
29+ return "•" * len (segment )
30+
31+
32+ def syntax_highlight_text (code : str , syntax : str ) -> Text :
33+ """Produces highlighted text based on the syntax."""
34+ syntax_obj = Syntax (code , syntax )
35+ with CONSOLE .capture () as capture :
36+ CONSOLE .print (syntax_obj )
37+ return Text .from_ansi (capture .get ())
38+
2139
2240class TextInput (Widget ):
2341 """
@@ -33,12 +51,14 @@ class TextInput(Widget):
3351 of the widget's border.
3452 password (bool, optional): Defaults to False. Hides the text
3553 input, replacing it with bullets.
54+ syntax (Optional[str]): the name of the language for syntax highlighting.
3655
3756 Attributes:
3857 value (str): the value of the text field
3958 placeholder (str): The placeholder message.
4059 title (str): The displayed title of the widget.
4160 has_password (bool): True if the text field masks the input.
61+ syntax (Optional[str]): the name of the language for syntax highlighting.
4262 has_focus (bool): True if the widget is focused.
4363 cursor (Tuple[str, Style]): The character used for the cursor
4464 and a rich Style object defining its appearance.
@@ -87,12 +107,15 @@ def __init__(
87107 placeholder : str = "" ,
88108 title : str = "" ,
89109 password : bool = False ,
110+ syntax : Optional [str ] = None ,
111+ ** kwargs : Any ,
90112 ) -> None :
91113 super ().__init__ (name )
92114 self .value = value
93115 self .placeholder = placeholder
94116 self .title = title
95117 self .has_password = password
118+ self .syntax = syntax
96119 self ._on_change_message_class = InputOnChange
97120 self ._on_focus_message_class = InputOnFocus
98121 self ._cursor_position = len (self .value )
@@ -153,7 +176,7 @@ def render(self) -> RenderableType:
153176 else :
154177 segments = [self .placeholder ]
155178 else :
156- segments = [self ._conceal_or_reveal (self .value )]
179+ segments = [self ._modify_text (self .value )]
157180
158181 text = Text .assemble (* segments )
159182
@@ -177,33 +200,26 @@ def render(self) -> RenderableType:
177200 box = rich .box .DOUBLE if self .has_focus else rich .box .SQUARE ,
178201 )
179202
180- def _conceal_or_reveal (self , segment : str ) -> str :
203+ def _modify_text (self , segment : str ) -> Union [ str , Text ] :
181204 """
182- Produce the segment either concealed like a password or as it
183- was passed.
205+ Produces the text with modifications, such as password concealing.
184206 """
185207 if self .has_password :
186- return "" .join ("•" for _ in segment )
208+ return conceal_text (segment )
209+ if self .syntax :
210+ return syntax_highlight_text (segment , self .syntax )
187211 return segment
188212
189- def _render_text_with_cursor (self ) -> List [Union [str , Tuple [str , Style ]]]:
213+ def _render_text_with_cursor (self ) -> List [Union [str , Text , Tuple [str , Style ]]]:
190214 """
191215 Produces the renderable Text object combining value and cursor
192216 """
193- if len (self .value ) == 0 :
194- segments = [self .cursor ]
195- elif self ._cursor_position == 0 :
196- segments = [self .cursor , self ._conceal_or_reveal (self .value )]
197- elif self ._cursor_position == len (self .value ):
198- segments = [self ._conceal_or_reveal (self .value ), self .cursor ]
199- else :
200- segments = [
201- self ._conceal_or_reveal (self .value [: self ._cursor_position ]),
202- self .cursor ,
203- self ._conceal_or_reveal (self .value [self ._cursor_position :]),
204- ]
205-
206- return segments
217+ text = self ._modify_text (self .value )
218+ return [
219+ text [: self ._cursor_position ],
220+ self .cursor ,
221+ text [self ._cursor_position :],
222+ ]
207223
208224 async def on_focus (self , event : events .Focus ) -> None :
209225 self ._has_focus = True
0 commit comments