@@ -219,77 +219,84 @@ def __init__(
219219
220220 def lex_document (self , document : Document ) -> Callable [[int ], Any ]:
221221 """Lex the document."""
222+ # Get redirection tokens and terminators to avoid highlighting them as values
223+ exclude_tokens = set (constants .REDIRECTION_TOKENS )
224+ exclude_tokens .update (self .cmd_app .statement_parser .terminators )
225+ arg_pattern = re .compile (r'(\s+)|(--?[^\s\'"]+)|("[^"]*"?|\'[^\']*\'?)|([^\s\'"]+)' )
226+
227+ def highlight_args (text : str , tokens : list [tuple [str , str ]]) -> None :
228+ """Highlight arguments in a string."""
229+ for m in arg_pattern .finditer (text ):
230+ space , flag , quoted , word = m .groups ()
231+ match_text = m .group (0 )
232+
233+ if space :
234+ tokens .append (('' , match_text ))
235+ elif flag :
236+ tokens .append ((self .flag_color , match_text ))
237+ elif (quoted or word ) and match_text not in exclude_tokens :
238+ tokens .append ((self .argument_color , match_text ))
239+ else :
240+ tokens .append (('' , match_text ))
222241
223242 def get_line (lineno : int ) -> list [tuple [str , str ]]:
224243 """Return the tokens for the given line number."""
225244 line = document .lines [lineno ]
226245 tokens : list [tuple [str , str ]] = []
227246
228- # Use cmd2's command pattern to find the first word (the command)
229- if ru .ALLOW_STYLE != ru .AllowStyle .NEVER and (
230- match := self .cmd_app .statement_parser ._command_pattern .search (line )
231- ):
232- # Group 1 is the command, Group 2 is the character(s) that terminated the command match
233- command = match .group (1 )
234- cmd_start = match .start (1 )
235- cmd_end = match .end (1 )
236-
237- # Add any leading whitespace
238- if cmd_start > 0 :
239- tokens .append (('' , line [:cmd_start ]))
240-
241- if command :
242- # Determine the style for the command
243- shortcut_found = False
244- for shortcut , _ in self .cmd_app .statement_parser .shortcuts :
245- if command .startswith (shortcut ):
246- # Add the shortcut with the command style
247- tokens .append ((self .command_color , shortcut ))
248-
249- # If there's more in the command word, it's an argument
250- if len (command ) > len (shortcut ):
251- tokens .append ((self .argument_color , command [len (shortcut ) :]))
252-
253- shortcut_found = True
254- break
255-
256- if not shortcut_found :
257- style = ''
258- if command in self .cmd_app .get_all_commands ():
259- style = self .command_color
260- elif command in self .cmd_app .aliases :
261- style = self .alias_color
262- elif command in self .cmd_app .macros :
263- style = self .macro_color
264-
265- # Add the command with the determined style
266- tokens .append ((style , command ))
267-
268- # Add the rest of the line
269- if cmd_end < len (line ):
270- rest = line [cmd_end :]
271- # Regex to match whitespace, flags, quoted strings, or other words
272- arg_pattern = re .compile (r'(\s+)|(--?[^\s\'"]+)|("[^"]*"?|\'[^\']*\'?)|([^\s\'"]+)' )
273-
274- # Get redirection tokens and terminators to avoid highlighting them as values
275- exclude_tokens = set (constants .REDIRECTION_TOKENS )
276- exclude_tokens .update (self .cmd_app .statement_parser .terminators )
277-
278- for m in arg_pattern .finditer (rest ):
279- space , flag , quoted , word = m .groups ()
280- text = m .group (0 )
281-
282- if space :
283- tokens .append (('' , text ))
284- elif flag :
285- tokens .append ((self .flag_color , text ))
286- elif (quoted or word ) and text not in exclude_tokens :
287- tokens .append ((self .argument_color , text ))
288- else :
289- tokens .append (('' , text ))
290- elif line :
291- # No command match found or colors aren't allowed, add the entire line unstyled
292- tokens .append (('' , line ))
247+ # Only attempt to match a command on the first line
248+ if lineno == 0 and ru .ALLOW_STYLE != ru .AllowStyle .NEVER :
249+ # Use cmd2's command pattern to find the first word (the command)
250+ match = self .cmd_app .statement_parser ._command_pattern .search (line )
251+ if match :
252+ # Group 1 is the command, Group 2 is the character(s) that terminated the command match
253+ command = match .group (1 )
254+ cmd_start = match .start (1 )
255+ cmd_end = match .end (1 )
256+
257+ # Add any leading whitespace
258+ if cmd_start > 0 :
259+ tokens .append (('' , line [:cmd_start ]))
260+
261+ if command :
262+ # Determine the style for the command
263+ shortcut_found = False
264+ for shortcut , _ in self .cmd_app .statement_parser .shortcuts :
265+ if command .startswith (shortcut ):
266+ # Add the shortcut with the command style
267+ tokens .append ((self .command_color , shortcut ))
268+
269+ # If there's more in the command word, it's an argument
270+ if len (command ) > len (shortcut ):
271+ tokens .append ((self .argument_color , command [len (shortcut ) :]))
272+
273+ shortcut_found = True
274+ break
275+
276+ if not shortcut_found :
277+ style = ''
278+ if command in self .cmd_app .get_all_commands ():
279+ style = self .command_color
280+ elif command in self .cmd_app .aliases :
281+ style = self .alias_color
282+ elif command in self .cmd_app .macros :
283+ style = self .macro_color
284+
285+ # Add the command with the determined style
286+ tokens .append ((style , command ))
287+
288+ # Add the rest of the line as arguments
289+ if cmd_end < len (line ):
290+ highlight_args (line [cmd_end :], tokens )
291+ else :
292+ # No command match found on the first line
293+ tokens .append (('' , line ))
294+ else :
295+ # All other lines are unstyled or treated as arguments
296+ if ru .ALLOW_STYLE != ru .AllowStyle .NEVER :
297+ highlight_args (line , tokens )
298+ else :
299+ tokens .append (('' , line ))
293300
294301 return tokens
295302
0 commit comments