Skip to content

Commit 0828920

Browse files
committed
Colorize commands separately in the REPL
1 parent a700732 commit 0828920

3 files changed

Lines changed: 34 additions & 0 deletions

File tree

Lib/_colorize.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ class Syntax(ThemeSection):
407407
keyword: str = ANSIColors.BOLD_BLUE
408408
keyword_constant: str = ANSIColors.BOLD_BLUE
409409
builtin: str = ANSIColors.CYAN
410+
command: str = ANSIColors.BOLD_CYAN
410411
comment: str = ANSIColors.RED
411412
string: str = ANSIColors.GREEN
412413
number: str = ANSIColors.YELLOW

Lib/_pyrepl/utils.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
IDENTIFIERS_AFTER = frozenset({"def", "class"})
2525
KEYWORD_CONSTANTS = frozenset({"True", "False", "None"})
2626
BUILTINS = frozenset({str(name) for name in dir(builtins) if not name.startswith('_')})
27+
COMMANDS = frozenset({"exit", "quit", "copyright", "help", "clear"})
2728

2829

2930
def THEME(**kwargs):
@@ -235,6 +236,13 @@ def gen_colors_from_token_stream(
235236
):
236237
span = Span.from_token(token, line_lengths)
237238
yield ColorSpan(span, "soft_keyword")
239+
elif (
240+
token.string in COMMANDS
241+
and not prev_token
242+
and next_token.type == T.NEWLINE
243+
):
244+
span = Span.from_token(token, line_lengths)
245+
yield ColorSpan(span, "command")
238246
elif (
239247
token.string in BUILTINS
240248
and not (prev_token and prev_token.exact_type == T.DOT)

Lib/test/test_pyrepl/test_utils.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,28 @@ def test_gen_colors_keyword_highlighting(self):
159159
span_text = code[color.span.start:color.span.end + 1]
160160
actual_highlights.append((span_text, color.tag))
161161
self.assertEqual(actual_highlights, expected_highlights)
162+
163+
def test_gen_colors_command_highlighting(self):
164+
cases = [
165+
# highlights bare command names
166+
("exit", [("exit", "command")]),
167+
("quit", [("quit", "command")]),
168+
("copyright", [("copyright", "command")]),
169+
("help", [("help", "command")]),
170+
("clear", [("clear", "command")]),
171+
# no highlight when not the only token on the line
172+
("x = exit", [("=", "op"), ("exit", "builtin")]),
173+
("obj.exit", [(".", "op")]),
174+
# falls through to builtin when called as function or used in expression
175+
("exit()", [("exit", "builtin"), ("(", "op"), (")", "op")]),
176+
("quit(0)", [("quit", "builtin"), ("(", "op"), ("0", "number"), (")", "op")]),
177+
("print(exit)", [("print", "builtin"), ("(", "op"), ("exit", "builtin"), (")", "op")]),
178+
]
179+
for code, expected_highlights in cases:
180+
with self.subTest(code=code):
181+
colors = list(gen_colors(code))
182+
actual_highlights = []
183+
for color in colors:
184+
span_text = code[color.span.start:color.span.end + 1]
185+
actual_highlights.append((span_text, color.tag))
186+
self.assertEqual(actual_highlights, expected_highlights)

0 commit comments

Comments
 (0)