Skip to content

Commit 4482a79

Browse files
committed
Update Cmd2Lexer to recognize shortcuts and color them like a command
1 parent b6f86be commit 4482a79

File tree

3 files changed

+75
-16
lines changed

3 files changed

+75
-16
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ repos:
1111
exclude: ^examples/transcripts/
1212

1313
- repo: https://github.com/astral-sh/ruff-pre-commit
14-
rev: "v0.15.0"
14+
rev: "v0.15.1"
1515
hooks:
1616
- id: ruff-format
1717
args: [--config=ruff.toml]

cmd2/pt_utils.py

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@
3737
class Cmd2Completer(Completer):
3838
"""Completer that delegates to cmd2's completion logic."""
3939

40-
def __init__(self, cmd_app: 'Cmd', custom_settings: utils.CustomCompletionSettings | None = None) -> None:
40+
def __init__(
41+
self,
42+
cmd_app: 'Cmd',
43+
custom_settings: utils.CustomCompletionSettings | None = None,
44+
) -> None:
4145
"""Initialize prompt_toolkit based completer class."""
4246
self.cmd_app = cmd_app
4347
self.custom_settings = custom_settings
@@ -155,10 +159,31 @@ def store_string(self, string: str) -> None:
155159
class Cmd2Lexer(Lexer):
156160
"""Lexer that highlights cmd2 command names, aliases, and macros."""
157161

158-
def __init__(self, cmd_app: 'Cmd') -> None:
159-
"""Initialize the lexer."""
162+
def __init__(
163+
self,
164+
cmd_app: 'Cmd',
165+
command_color: str = 'ansigreen',
166+
alias_color: str = 'ansicyan',
167+
macro_color: str = 'ansimagenta',
168+
flag_color: str = 'ansired',
169+
argument_color: str = 'ansiyellow',
170+
) -> None:
171+
"""Initialize the Lexer.
172+
173+
:param cmd_app: cmd2.Cmd instance
174+
:param command_color: color to use for commands, defaults to 'ansigreen'
175+
:param alias_color: color to use for aliases, defaults to 'ansicyan'
176+
:param macro_color: color to use for macros, defaults to 'ansimagenta'
177+
:param flag_color: color to use for flags, defaults to 'ansired'
178+
:param argument_color: color to use for arguments, defaults to 'ansiyellow'
179+
"""
160180
super().__init__()
161181
self.cmd_app = cmd_app
182+
self.command_color = command_color
183+
self.alias_color = alias_color
184+
self.macro_color = macro_color
185+
self.flag_color = flag_color
186+
self.argument_color = argument_color
162187

163188
def lex_document(self, document: Document) -> Callable[[int], Any]:
164189
"""Lex the document."""
@@ -182,16 +207,30 @@ def get_line(lineno: int) -> list[tuple[str, str]]:
182207

183208
if command:
184209
# Determine the style for the command
185-
style = ''
186-
if command in self.cmd_app.get_all_commands():
187-
style = 'ansigreen'
188-
elif command in self.cmd_app.aliases:
189-
style = 'ansicyan'
190-
elif command in self.cmd_app.macros:
191-
style = 'ansimagenta'
192-
193-
# Add the command with the determined style
194-
tokens.append((style, command))
210+
shortcut_found = False
211+
for shortcut, _ in self.cmd_app.statement_parser.shortcuts:
212+
if command.startswith(shortcut):
213+
# Add the shortcut with the command style
214+
tokens.append((self.command_color, shortcut))
215+
216+
# If there's more in the command word, it's an argument
217+
if len(command) > len(shortcut):
218+
tokens.append((self.argument_color, command[len(shortcut) :]))
219+
220+
shortcut_found = True
221+
break
222+
223+
if not shortcut_found:
224+
style = ''
225+
if command in self.cmd_app.get_all_commands():
226+
style = self.command_color
227+
elif command in self.cmd_app.aliases:
228+
style = self.alias_color
229+
elif command in self.cmd_app.macros:
230+
style = self.macro_color
231+
232+
# Add the command with the determined style
233+
tokens.append((style, command))
195234

196235
# Add the rest of the line
197236
if cmd_end < len(line):
@@ -211,9 +250,9 @@ def get_line(lineno: int) -> list[tuple[str, str]]:
211250
if space:
212251
tokens.append(('', text))
213252
elif flag:
214-
tokens.append(('ansired', text))
253+
tokens.append((self.flag_color, text))
215254
elif (quoted or word) and text not in exclude_tokens:
216-
tokens.append(('ansiyellow', text))
255+
tokens.append((self.argument_color, text))
217256
else:
218257
tokens.append(('', text))
219258
elif line:

tests/test_pt_utils.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ def __init__(self):
2525
self.completion_header = ''
2626
self.statement_parser = Mock()
2727
self.statement_parser.terminators = [';']
28+
self.statement_parser.shortcuts = []
2829
self.statement_parser._command_pattern = re.compile(r'\A\s*(\S*?)(\s|\Z)')
2930
self.aliases = {}
3031
self.macros = {}
@@ -146,6 +147,25 @@ def test_lex_document_unclosed_quote(self, mock_cmd_app):
146147

147148
assert tokens == [('ansigreen', 'echo'), ('', ' '), ('ansiyellow', '"hello')]
148149

150+
def test_lex_document_shortcut(self, mock_cmd_app):
151+
"""Test lexing a shortcut."""
152+
mock_cmd_app.statement_parser.shortcuts = [('!', 'shell')]
153+
lexer = pt_utils.Cmd2Lexer(cast(Any, mock_cmd_app))
154+
155+
# Case 1: Shortcut glued to argument
156+
line = "!ls"
157+
document = Document(line)
158+
get_line = lexer.lex_document(document)
159+
tokens = get_line(0)
160+
assert tokens == [('ansigreen', '!'), ('ansiyellow', 'ls')]
161+
162+
# Case 2: Shortcut with space
163+
line = "! ls"
164+
document = Document(line)
165+
get_line = lexer.lex_document(document)
166+
tokens = get_line(0)
167+
assert tokens == [('ansigreen', '!'), ('', ' '), ('ansiyellow', 'ls')]
168+
149169

150170
class TestCmd2Completer:
151171
def test_get_completions_basic(self, mock_cmd_app):

0 commit comments

Comments
 (0)