mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
gh-131507: Add support for syntax highlighting in PyREPL (GH-133247)
Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
This commit is contained in:
parent
bfcbb28223
commit
fac41f56d4
21 changed files with 654 additions and 99 deletions
|
@ -1,14 +1,21 @@
|
|||
import itertools
|
||||
import functools
|
||||
import rlcompleter
|
||||
from textwrap import dedent
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from .support import handle_all_events, handle_events_narrow_console
|
||||
from .support import ScreenEqualMixin, code_to_events
|
||||
from .support import prepare_reader, prepare_console
|
||||
from .support import prepare_console, reader_force_colors
|
||||
from .support import reader_no_colors as prepare_reader
|
||||
from _pyrepl.console import Event
|
||||
from _pyrepl.reader import Reader
|
||||
from _colorize import theme
|
||||
|
||||
|
||||
overrides = {"RESET": "z", "SOFT_KEYWORD": "K"}
|
||||
colors = {overrides.get(k, k[0].lower()): v for k, v in theme.items()}
|
||||
|
||||
|
||||
class TestReader(ScreenEqualMixin, TestCase):
|
||||
|
@ -123,8 +130,9 @@ class TestReader(ScreenEqualMixin, TestCase):
|
|||
def test_control_characters(self):
|
||||
code = 'flag = "🏳️🌈"'
|
||||
events = code_to_events(code)
|
||||
reader, _ = handle_all_events(events)
|
||||
reader, _ = handle_all_events(events, prepare_reader=reader_force_colors)
|
||||
self.assert_screen_equal(reader, 'flag = "🏳️\\u200d🌈"', clean=True)
|
||||
self.assert_screen_equal(reader, 'flag {o}={z} {s}"🏳️\\u200d🌈"{z}'.format(**colors))
|
||||
|
||||
def test_setpos_from_xy_multiple_lines(self):
|
||||
# fmt: off
|
||||
|
@ -355,3 +363,140 @@ class TestReader(ScreenEqualMixin, TestCase):
|
|||
reader, _ = handle_all_events(events)
|
||||
reader.setpos_from_xy(8, 0)
|
||||
self.assertEqual(reader.pos, 7)
|
||||
|
||||
def test_syntax_highlighting_basic(self):
|
||||
code = dedent(
|
||||
"""\
|
||||
import re, sys
|
||||
def funct(case: str = sys.platform) -> None:
|
||||
match = re.search(
|
||||
"(me)",
|
||||
'''
|
||||
Come on
|
||||
Come on now
|
||||
You know that it's time to emerge
|
||||
''',
|
||||
)
|
||||
match case:
|
||||
case "emscripten": print("on the web")
|
||||
case "ios" | "android": print("on the phone")
|
||||
case _: print('arms around', match.group(1))
|
||||
"""
|
||||
)
|
||||
expected = dedent(
|
||||
"""\
|
||||
{k}import{z} re{o},{z} sys
|
||||
{a}{k}def{z} {d}funct{z}{o}({z}case{o}:{z} {b}str{z} {o}={z} sys{o}.{z}platform{o}){z} {o}->{z} {k}None{z}{o}:{z}
|
||||
match {o}={z} re{o}.{z}search{o}({z}
|
||||
{s}"(me)"{z}{o},{z}
|
||||
{s}'''{z}
|
||||
{s} Come on{z}
|
||||
{s} Come on now{z}
|
||||
{s} You know that it's time to emerge{z}
|
||||
{s} '''{z}{o},{z}
|
||||
{o}){z}
|
||||
{K}match{z} case{o}:{z}
|
||||
{K}case{z} {s}"emscripten"{z}{o}:{z} {b}print{z}{o}({z}{s}"on the web"{z}{o}){z}
|
||||
{K}case{z} {s}"ios"{z} {o}|{z} {s}"android"{z}{o}:{z} {b}print{z}{o}({z}{s}"on the phone"{z}{o}){z}
|
||||
{K}case{z} {K}_{z}{o}:{z} {b}print{z}{o}({z}{s}'arms around'{z}{o},{z} match{o}.{z}group{o}({z}{n}1{z}{o}){z}{o}){z}
|
||||
"""
|
||||
)
|
||||
expected_sync = expected.format(a="", **colors)
|
||||
events = code_to_events(code)
|
||||
reader, _ = handle_all_events(events, prepare_reader=reader_force_colors)
|
||||
self.assert_screen_equal(reader, code, clean=True)
|
||||
self.assert_screen_equal(reader, expected_sync)
|
||||
self.assertEqual(reader.pos, 2**7 + 2**8)
|
||||
self.assertEqual(reader.cxy, (0, 14))
|
||||
|
||||
async_msg = "{k}async{z} ".format(**colors)
|
||||
expected_async = expected.format(a=async_msg, **colors)
|
||||
more_events = itertools.chain(
|
||||
code_to_events(code),
|
||||
[Event(evt="key", data="up", raw=bytearray(b"\x1bOA"))] * 13,
|
||||
code_to_events("async "),
|
||||
)
|
||||
reader, _ = handle_all_events(more_events, prepare_reader=reader_force_colors)
|
||||
self.assert_screen_equal(reader, expected_async)
|
||||
self.assertEqual(reader.pos, 21)
|
||||
self.assertEqual(reader.cxy, (6, 1))
|
||||
|
||||
def test_syntax_highlighting_incomplete_string_first_line(self):
|
||||
code = dedent(
|
||||
"""\
|
||||
def unfinished_function(arg: str = "still typing
|
||||
"""
|
||||
)
|
||||
expected = dedent(
|
||||
"""\
|
||||
{k}def{z} {d}unfinished_function{z}{o}({z}arg{o}:{z} {b}str{z} {o}={z} {s}"still typing{z}
|
||||
"""
|
||||
).format(**colors)
|
||||
events = code_to_events(code)
|
||||
reader, _ = handle_all_events(events, prepare_reader=reader_force_colors)
|
||||
self.assert_screen_equal(reader, code, clean=True)
|
||||
self.assert_screen_equal(reader, expected)
|
||||
|
||||
def test_syntax_highlighting_incomplete_string_another_line(self):
|
||||
code = dedent(
|
||||
"""\
|
||||
def unfinished_function(
|
||||
arg: str = "still typing
|
||||
"""
|
||||
)
|
||||
expected = dedent(
|
||||
"""\
|
||||
{k}def{z} {d}unfinished_function{z}{o}({z}
|
||||
arg{o}:{z} {b}str{z} {o}={z} {s}"still typing{z}
|
||||
"""
|
||||
).format(**colors)
|
||||
events = code_to_events(code)
|
||||
reader, _ = handle_all_events(events, prepare_reader=reader_force_colors)
|
||||
self.assert_screen_equal(reader, code, clean=True)
|
||||
self.assert_screen_equal(reader, expected)
|
||||
|
||||
def test_syntax_highlighting_incomplete_multiline_string(self):
|
||||
code = dedent(
|
||||
"""\
|
||||
def unfinished_function():
|
||||
'''Still writing
|
||||
the docstring
|
||||
"""
|
||||
)
|
||||
expected = dedent(
|
||||
"""\
|
||||
{k}def{z} {d}unfinished_function{z}{o}({z}{o}){z}{o}:{z}
|
||||
{s}'''Still writing{z}
|
||||
{s} the docstring{z}
|
||||
"""
|
||||
).format(**colors)
|
||||
events = code_to_events(code)
|
||||
reader, _ = handle_all_events(events, prepare_reader=reader_force_colors)
|
||||
self.assert_screen_equal(reader, code, clean=True)
|
||||
self.assert_screen_equal(reader, expected)
|
||||
|
||||
def test_syntax_highlighting_incomplete_fstring(self):
|
||||
code = dedent(
|
||||
"""\
|
||||
def unfinished_function():
|
||||
var = f"Single-quote but {
|
||||
1
|
||||
+
|
||||
1
|
||||
} multi-line!
|
||||
"""
|
||||
)
|
||||
expected = dedent(
|
||||
"""\
|
||||
{k}def{z} {d}unfinished_function{z}{o}({z}{o}){z}{o}:{z}
|
||||
var {o}={z} {s}f"{z}{s}Single-quote but {z}{o}{OB}{z}
|
||||
{n}1{z}
|
||||
{o}+{z}
|
||||
{n}1{z}
|
||||
{o}{CB}{z}{s} multi-line!{z}
|
||||
"""
|
||||
).format(OB="{", CB="}", **colors)
|
||||
events = code_to_events(code)
|
||||
reader, _ = handle_all_events(events, prepare_reader=reader_force_colors)
|
||||
self.assert_screen_equal(reader, code, clean=True)
|
||||
self.assert_screen_equal(reader, expected)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue