From c938561c1612704d06548da69ebecff136875e5d Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Mon, 7 Mar 2022 17:01:02 +0000 Subject: [PATCH 1/7] Fix printing JSON highlighting when indent=None --- rich/console.py | 2 ++ rich/highlighter.py | 2 +- tests/test_console.py | 11 ++++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/rich/console.py b/rich/console.py index e2d7a6d52..1eb2e3eeb 100644 --- a/rich/console.py +++ b/rich/console.py @@ -2209,3 +2209,5 @@ def save_html( } ) console.log("foo") + + console.print_json(data={"name": "apple", "count": 1}, indent=None) diff --git a/rich/highlighter.py b/rich/highlighter.py index 69d326021..7bd1613f8 100644 --- a/rich/highlighter.py +++ b/rich/highlighter.py @@ -112,7 +112,7 @@ class JSONHighlighter(RegexHighlighter): r"(?P(?b?\".*?(?b?\".*?(?b?\"[^\"]*?(? Date: Tue, 8 Mar 2022 15:21:50 +0000 Subject: [PATCH 2/7] Fix JSON key highlighting when no indentation --- rich/highlighter.py | 32 ++++++++++++++++++++++++++++--- tests/test_highlighter.py | 40 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/rich/highlighter.py b/rich/highlighter.py index 7bd1613f8..6a7ad1170 100644 --- a/rich/highlighter.py +++ b/rich/highlighter.py @@ -1,7 +1,10 @@ +import json +import re +import string from abc import ABC, abstractmethod from typing import List, Union -from .text import Text +from .text import Span, Text def _combine_regex(*regexes: str) -> str: @@ -104,17 +107,38 @@ class ReprHighlighter(RegexHighlighter): class JSONHighlighter(RegexHighlighter): """Highlights JSON""" + # Captures the start and end of JSON strings, handling escaped quotes + JSON_STR = r"(?b?\".*?(?[\{\[\(\)\]\}])", r"\b(?Ptrue)\b|\b(?Pfalse)\b|\b(?Pnull)\b", r"(?P(?b?\".*?(?b?\"[^\"]*?(? None: + super().highlight(text) + + # Additional work to handle highlighting JSON keys + plain = text.plain + append = text.spans.append + for match in re.finditer(self.JSON_STR, plain): + start, end = match.span() + cursor = end + while cursor < len(plain): + char = plain[cursor] + cursor += 1 + if char in string.whitespace: + continue + elif char == ":": + append(Span(start, end, "json.key")) + else: + break + if __name__ == "__main__": # pragma: no cover from .console import Console @@ -145,3 +169,5 @@ class JSONHighlighter(RegexHighlighter): console.print( "127.0.1.1 bar 192.168.1.4 2001:0db8:85a3:0000:0000:8a2e:0370:7334 foo" ) + + console.print_json(json.dumps(obj={"name": "apple", "count": 1}), indent=None) diff --git a/tests/test_highlighter.py b/tests/test_highlighter.py index c5303b5c0..a3fe2b056 100644 --- a/tests/test_highlighter.py +++ b/tests/test_highlighter.py @@ -1,8 +1,10 @@ """Tests for the highlighter classes.""" -import pytest +import json from typing import List -from rich.highlighter import NullHighlighter, ReprHighlighter +import pytest + +from rich.highlighter import JSONHighlighter, NullHighlighter, ReprHighlighter from rich.text import Span, Text @@ -92,3 +94,37 @@ def test_highlight_regex(test: str, spans: List[Span]): highlighter.highlight(text) print(text.spans) assert text.spans == spans + + +def test_highlight_json_with_indent(): + json_string = json.dumps({"name": "apple", "count": 1}, indent=4) + text = Text(json_string) + highlighter = JSONHighlighter() + highlighter.highlight(text) + assert text.spans == [ + Span(0, 1, "json.brace"), + Span(6, 12, "json.str"), + Span(14, 21, "json.str"), + Span(27, 34, "json.str"), + Span(36, 37, "json.number"), + Span(38, 39, "json.brace"), + Span(6, 12, "json.key"), + Span(27, 34, "json.key"), + ] + + +def test_highlight_json_no_indent(): + json_string = json.dumps({"name": "apple", "count": 1}, indent=None) + text = Text(json_string) + highlighter = JSONHighlighter() + highlighter.highlight(text) + assert text.spans == [ + Span(0, 1, "json.brace"), + Span(1, 7, "json.str"), + Span(9, 16, "json.str"), + Span(18, 25, "json.str"), + Span(27, 28, "json.number"), + Span(28, 29, "json.brace"), + Span(1, 7, "json.key"), + Span(18, 25, "json.key"), + ] From 7ce9bcabcd6b3ac1a16645bdc836c3ff401e269b Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Tue, 8 Mar 2022 15:30:51 +0000 Subject: [PATCH 3/7] Remove unused json import in highlighter.py --- rich/highlighter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rich/highlighter.py b/rich/highlighter.py index 6a7ad1170..1881f0db6 100644 --- a/rich/highlighter.py +++ b/rich/highlighter.py @@ -1,4 +1,3 @@ -import json import re import string from abc import ABC, abstractmethod @@ -169,5 +168,6 @@ def highlight(self, text: Text) -> None: console.print( "127.0.1.1 bar 192.168.1.4 2001:0db8:85a3:0000:0000:8a2e:0370:7334 foo" ) + import json console.print_json(json.dumps(obj={"name": "apple", "count": 1}), indent=None) From 93e584c47cf4cd873fb1610ff8044cde0065422c Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Tue, 8 Mar 2022 15:43:35 +0000 Subject: [PATCH 4/7] Add extra JSON highlighting tests --- CHANGELOG.md | 1 + tests/test_highlighter.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79da255bf..b91cb3a4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Collapsed definitions for single-character spinners, to save memory and reduce import time. - Fix print_json indent type in __init__.py - Fix error when inspecting object defined in REPL https://github.com/Textualize/rich/pull/2037 +- Fix incorrect highlighting of non-indented JSON https://github.com/Textualize/rich/pull/2038 ### Changed diff --git a/tests/test_highlighter.py b/tests/test_highlighter.py index a3fe2b056..a34334e3d 100644 --- a/tests/test_highlighter.py +++ b/tests/test_highlighter.py @@ -113,6 +113,22 @@ def test_highlight_json_with_indent(): ] +def test_highlight_json_string_only(): + json_string = '"abc"' + text = Text(json_string) + highlighter = JSONHighlighter() + highlighter.highlight(text) + assert text.spans == [Span(0, 5, "json.str")] + + +def test_highlight_json_empty_string_only(): + json_string = '""' + text = Text(json_string) + highlighter = JSONHighlighter() + highlighter.highlight(text) + assert text.spans == [Span(0, 2, "json.str")] + + def test_highlight_json_no_indent(): json_string = json.dumps({"name": "apple", "count": 1}, indent=None) text = Text(json_string) From 0dd591c07d53ac1dbe8c3d9c7e3331a0693055ec Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Tue, 8 Mar 2022 15:44:30 +0000 Subject: [PATCH 5/7] Update CHANGELOG.md r.e. fix for incorrect non-indented JSON highlighting --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b91cb3a4e..44e65eae6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix print_json indent type in __init__.py - Fix error when inspecting object defined in REPL https://github.com/Textualize/rich/pull/2037 - Fix incorrect highlighting of non-indented JSON https://github.com/Textualize/rich/pull/2038 - ### Changed - Improved support for enum.Flag in ReprHighlighter https://github.com/Textualize/rich/pull/1920 From 9e3ae26b789105f19b1ea29fec10a8128a9b6d28 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Tue, 8 Mar 2022 15:44:36 +0000 Subject: [PATCH 6/7] Update CHANGELOG.md r.e. fix for incorrect non-indented JSON highlighting --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44e65eae6..b91cb3a4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix print_json indent type in __init__.py - Fix error when inspecting object defined in REPL https://github.com/Textualize/rich/pull/2037 - Fix incorrect highlighting of non-indented JSON https://github.com/Textualize/rich/pull/2038 + ### Changed - Improved support for enum.Flag in ReprHighlighter https://github.com/Textualize/rich/pull/1920 From 94a10747c1e8e8e49791e862d3fe5c77e35871d3 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Tue, 8 Mar 2022 16:06:54 +0000 Subject: [PATCH 7/7] Some improvements on JSON highlighting --- rich/highlighter.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rich/highlighter.py b/rich/highlighter.py index 1881f0db6..6e10d56d7 100644 --- a/rich/highlighter.py +++ b/rich/highlighter.py @@ -108,6 +108,7 @@ class JSONHighlighter(RegexHighlighter): # Captures the start and end of JSON strings, handling escaped quotes JSON_STR = r"(?b?\".*?(? None: # Additional work to handle highlighting JSON keys plain = text.plain append = text.spans.append + whitespace = self.JSON_WHITESPACE for match in re.finditer(self.JSON_STR, plain): start, end = match.span() cursor = end while cursor < len(plain): char = plain[cursor] cursor += 1 - if char in string.whitespace: - continue - elif char == ":": + if char == ":": append(Span(start, end, "json.key")) - else: - break + elif char in whitespace: + continue + break if __name__ == "__main__": # pragma: no cover