Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Lib/_colorize.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ class Syntax(ThemeSection):
keyword: str = ANSIColors.BOLD_BLUE
keyword_constant: str = ANSIColors.BOLD_BLUE
builtin: str = ANSIColors.CYAN
command: str = ANSIColors.BOLD_CYAN
comment: str = ANSIColors.RED
string: str = ANSIColors.GREEN
number: str = ANSIColors.YELLOW
Expand Down
1 change: 1 addition & 0 deletions Lib/_pyrepl/simple_interact.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def _clear_screen():
reader.scheduled_commands.append("clear_screen")


# Keep this in sync with _pyrepl.utils.COMMANDS
REPL_COMMANDS = {
"exit": _sitebuiltins.Quitter('exit', ''),
"quit": _sitebuiltins.Quitter('quit' ,''),
Expand Down
9 changes: 9 additions & 0 deletions Lib/_pyrepl/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
IDENTIFIERS_AFTER = frozenset({"def", "class"})
KEYWORD_CONSTANTS = frozenset({"True", "False", "None"})
BUILTINS = frozenset({str(name) for name in dir(builtins) if not name.startswith('_')})
# Keep this in sync with _pyrepl.simple_interact.REPL_COMMANDS
COMMANDS = frozenset({"exit", "quit", "copyright", "help", "clear"})
Comment thread
johnslavik marked this conversation as resolved.


def THEME(**kwargs):
Expand Down Expand Up @@ -235,6 +237,13 @@ def gen_colors_from_token_stream(
):
span = Span.from_token(token, line_lengths)
yield ColorSpan(span, "soft_keyword")
elif (
token.string in COMMANDS
and (not prev_token or prev_token.type == T.INDENT)
and (not next_token or next_token.type == T.NEWLINE)
Comment thread
johnslavik marked this conversation as resolved.
):
span = Span.from_token(token, line_lengths)
yield ColorSpan(span, "command")
elif (
token.string in BUILTINS
and not (prev_token and prev_token.exact_type == T.DOT)
Expand Down
26 changes: 26 additions & 0 deletions Lib/test/test_pyrepl/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,29 @@ def test_gen_colors_keyword_highlighting(self):
span_text = code[color.span.start:color.span.end + 1]
actual_highlights.append((span_text, color.tag))
self.assertEqual(actual_highlights, expected_highlights)

def test_gen_colors_command_highlighting(self):
cases = [
# highlights bare command names (after stripping whitespaces)
("exit", [("exit", "command")]),
("quit", [("quit", "command")]),
("copyright", [("copyright", "command")]),
("help", [("help", "command")]),
("clear", [("clear", "command")]),
(" clear ", [("clear", "command")]),
# no highlight when not the only token on the line
("x = exit", [("=", "op"), ("exit", "builtin")]),
("obj.exit", [(".", "op")]),
# falls through to builtin when called as function or used in expression
("exit()", [("exit", "builtin"), ("(", "op"), (")", "op")]),
("quit(0)", [("quit", "builtin"), ("(", "op"), ("0", "number"), (")", "op")]),
("print(exit)", [("print", "builtin"), ("(", "op"), ("exit", "builtin"), (")", "op")]),
]
for code, expected_highlights in cases:
with self.subTest(code=code):
colors = list(gen_colors(code))
actual_highlights = []
for color in colors:
span_text = code[color.span.start:color.span.end + 1]
actual_highlights.append((span_text, color.tag))
self.assertEqual(actual_highlights, expected_highlights)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Colorize ``exit``, ``quit``, ``copyright``, ``help``, and ``clear`` as commands in the :term:`REPL` when typed alone on a line. Patch by Bartosz Sławecki.
Loading