diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index d8985ca13..66d057fa7 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -21,6 +21,7 @@ The following people have contributed to the development of Rich: - [Oleksis Fraga](https://github.com/oleksis) - [Andy Gimblett](https://github.com/gimbo) - [Kai Giokas](https://github.com/kaisforza) +- [Khojiev Jasur](https://github.com/jasur-py) - [Tom Gooding](https://github.com/TomJGooding) - [Michał Górny](https://github.com/mgorny) - [Nok Lam Chan](https://github.com/noklam) diff --git a/tests/test_markdown.py b/tests/test_markdown.py index 803c1ae97..1a504bc89 100644 --- a/tests/test_markdown.py +++ b/tests/test_markdown.py @@ -74,6 +74,7 @@ from rich.console import Console, RenderableType from rich.markdown import Markdown +from .test_utils import normalize_ansi_whitespace re_link_ids = re.compile(r"id=[\d\.\-]*?;.*?\x1b") @@ -110,10 +111,8 @@ def test_inline_code(): inline_code_theme="emacs", ) result = render(markdown) - expected = "inline \x1b[1;38;2;170;34;255;48;2;248;248;248mimport\x1b[0m\x1b[38;2;0;0;0;48;2;248;248;248m \x1b[0m\x1b[1;38;2;0;0;255;48;2;248;248;248mthis\x1b[0m code \n" - print(result) - print(repr(result)) - assert result == expected + expected = "inline \x1b[1;38;2;170;34;255;48;2;248;248;248mimport\x1b[0m\x1b[38;2;0;0;0;48;2;248;248;248m \x1b[0m\x1b[1;38;2;0;0;255;48;2;248;248;248mthis\x1b[0m code" + assert normalize_ansi_whitespace(result) == normalize_ansi_whitespace(expected) def test_markdown_table(): diff --git a/tests/test_syntax.py b/tests/test_syntax.py index bbd4c7a5c..b9c7baa8d 100644 --- a/tests/test_syntax.py +++ b/tests/test_syntax.py @@ -19,6 +19,7 @@ ) from .render import render +from .test_utils import normalize_ansi_whitespace if sys.version_info >= (3, 8): from importlib.metadata import Distribution @@ -50,11 +51,8 @@ def test_blank_lines(): code, lexer="python", theme="ascii_light", code_width=30, line_numbers=True ) result = render(syntax) - print(repr(result)) - assert ( - result - == "\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m1 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m2 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m3 \x1b[0m\x1b[1;38;2;0;128;0;48;2;248;248;248mimport\x1b[0m\x1b[38;2;0;0;0;48;2;248;248;248m \x1b[0m\x1b[1;38;2;0;0;255;48;2;248;248;248mthis\x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m4 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m5 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n" - ) + expected = "\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m1 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m2 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m3 \x1b[0m\x1b[1;38;2;0;128;0;48;2;248;248;248mimport\x1b[0m\x1b[38;2;0;0;0;48;2;248;248;248m \x1b[0m\x1b[1;38;2;0;0;255;48;2;248;248;248mthis\x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m4 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m5 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n" + assert normalize_ansi_whitespace(result) == normalize_ansi_whitespace(expected) def test_python_render(): @@ -118,9 +116,8 @@ def test_python_render_simple_indent_guides(): indent_guides=True, ) rendered_syntax = render(syntax) - print(repr(rendered_syntax)) expected = '\x1b[34mdef\x1b[0m \x1b[32mloop_first_last\x1b[0m(values: Iterable[T]) -> Iterable[Tuple[\x1b[36mb\x1b[0m\n\x1b[2;37m│ \x1b[0m\x1b[33m"""Iterate and generate a tuple with a flag for first an\x1b[0m\n\x1b[2m│ \x1b[0miter_values = \x1b[36miter\x1b[0m(values)\n\x1b[2m│ \x1b[0m\x1b[34mtry\x1b[0m:\n\x1b[2m│ │ \x1b[0mprevious_value = \x1b[36mnext\x1b[0m(iter_values)\n\x1b[2m│ \x1b[0m\x1b[34mexcept\x1b[0m \x1b[36mStopIteration\x1b[0m:\n\x1b[2m│ │ \x1b[0m\x1b[34mreturn\x1b[0m\n\x1b[2m│ \x1b[0mfirst = \x1b[34mTrue\x1b[0m\n\x1b[2m│ \x1b[0m\x1b[34mfor\x1b[0m value \x1b[35min\x1b[0m iter_values:\n\x1b[2m│ │ \x1b[0m\x1b[34myield\x1b[0m first, \x1b[34mFalse\x1b[0m, previous_value\n\x1b[2m│ │ \x1b[0mfirst = \x1b[34mFalse\x1b[0m\n\x1b[2m│ │ \x1b[0mprevious_value = value\n\x1b[2m│ \x1b[0m\x1b[34myield\x1b[0m first, \x1b[34mTrue\x1b[0m, previous_value\n' - assert rendered_syntax == expected + assert normalize_ansi_whitespace(rendered_syntax) == normalize_ansi_whitespace(expected) @pytest.mark.skipif(OLD_PYGMENTS, reason="Pygments changed their tokenizer") diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 000000000..db94ede7a --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,19 @@ +import re + + +def normalize_ansi_whitespace(text: str) -> str: + """Normalise whitespace in ANSI-coloured text while preserving colour codes.""" + ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') + parts = [] + current = '' + + for part in ansi_escape.split(text): + normalized = ' '.join(part.split()) + current += normalized + ansi_match = ansi_escape.search(text, len(''.join(parts)) + len(current)) + if ansi_match: + current += ansi_match.group() + parts.append(current) + current = '' + + return ''.join(parts) \ No newline at end of file