Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JSON text renderer #1086

Merged
merged 2 commits into from
Dec 31, 2022
Merged

Conversation

patriksvensson
Copy link
Contributor

@patriksvensson patriksvensson commented Nov 22, 2022

This PR adds a new widget JsonText which can be used to render syntax highlighted JSON.

image

Resolves #1051

@patriksvensson patriksvensson marked this pull request as ready for review November 23, 2022 16:58
@patriksvensson patriksvensson force-pushed the feature/json branch 2 times, most recently from a1f3814 to 20810b9 Compare November 23, 2022 17:10
@patriksvensson patriksvensson requested a review from a team November 23, 2022 17:17
@thoemmi
Copy link
Contributor

thoemmi commented Nov 27, 2022

Glad to see JSON rendering is finally supported, after my proposal was rejected 😉

@patriksvensson
Copy link
Contributor Author

@thoemmi It wasn't rejected. We didn't want to add a dependency to a JSON library 😊

@thoemmi
Copy link
Contributor

thoemmi commented Nov 27, 2022

Yeah, I was too lazy to write my own parser 😉 So kudos to you for doing all the work 👍

@0xced
Copy link
Contributor

0xced commented Dec 18, 2022

I couldn't resist running this new JSON parser through the best JSON test suite (created by Nicolas Seriot).

Without further ado, here are the results.

TL;DR The problematic ones are the 37 🟠 and the 2 🔴.

Color scheme
🟢 expected result
🟤 parsing should have succeeded but failed
🟠 parsing should have failed but succeeded
🔵 result undefined, parsing succeeded
🟣 result undefined, parsing failed
🔴 parser crashed
⚫️ timeout
Result File Contents
🔵 i_number_double_huge_neg_exp.json [123.456e-789]
🔵 i_number_huge_exp.json [0.4e0066999999999999999999999999999(...)
🔵 i_number_neg_int_huge_exp.json [-1e+9999]
🔵 i_number_pos_double_huge_exp.json [1.5e+9999]
🔵 i_number_real_neg_overflow.json [-123123e100000]
🔵 i_number_real_pos_overflow.json [123123e100000]
🔵 i_number_real_underflow.json [123e-10000000]
🔵 i_number_too_big_neg_int.json [-123123123123123123123123123123]
🔵 i_number_too_big_pos_int.json [100000000000000000000]
🔵 i_number_very_big_negative_int.json [-2374623746732768942798327498324234(...)
🔵 i_object_key_lone_2nd_surrogate.json {"\uDFAA":0}
🔵 i_string_1st_surrogate_but_2nd_missing.json ["\uDADA"]
🔵 i_string_1st_valid_surrogate_2nd_invalid.json ["\uD888\u1234"]
🔵 i_string_UTF-16LE_with_BOM.json FFFE[00"00E900"00]00 ⇔ [""]
🔵 i_string_UTF-8_invalid_sequence.json ["E697A5D188FA"] ⇔ ["日ш"]
🔵 i_string_UTF8_surrogate_U+D800.json ["EDA080"] ⇔ [""]
🔵 i_string_incomplete_surrogate_and_escape_valid.json ["\uD800\n"]
🔵 i_string_incomplete_surrogate_pair.json ["\uDd1ea"]
🔵 i_string_incomplete_surrogates_escape_valid.json ["\uD800\uD800\n"]
🔵 i_string_invalid_lonely_surrogate.json ["\ud800"]
🔵 i_string_invalid_surrogate.json ["\ud800abc"]
🔵 i_string_invalid_utf-8.json ["FF"] ⇔ [""]
🔵 i_string_inverted_surrogates_U+1D11E.json ["\uDd1e\uD834"]
🔵 i_string_iso_latin_1.json ["E9"] ⇔ [""]
🔵 i_string_lone_second_surrogate.json ["\uDFAA"]
🔵 i_string_lone_utf8_continuation_byte.json ["81"] ⇔ [""]
🔵 i_string_not_in_unicode_range.json ["F4BFBFBF"] ⇔ [""]
🔵 i_string_overlong_sequence_2_bytes.json ["C0AF"] ⇔ [""]
🔵 i_string_overlong_sequence_6_bytes.json ["FC83BFBFBFBF"] ⇔ [""]
🔵 i_string_overlong_sequence_6_bytes_null.json ["FC8080808080"] ⇔ [""]
🔵 i_string_truncated-utf-8.json ["E0FF"] ⇔ [""]
🟣 i_string_utf16BE_no_BOM.json 00[00"00E900"00] ⇔ [""]
🟣 i_string_utf16LE_no_BOM.json [00"00E900"00]00 ⇔ [""]
🔵 i_structure_500_nested_arrays.json [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[(...)
🔵 i_structure_UTF-8_BOM_empty_object.json EFBBBF{} ⇔ {}
🟢 n_array_1_true_without_comma.json [1 true]
🟢 n_array_a_invalid_utf8.json [aE5] ⇔ [a]
🟢 n_array_colon_instead_of_comma.json ["": 1]
🟠 n_array_comma_after_close.json [""],
🟢 n_array_comma_and_number.json [,1]
🟢 n_array_double_comma.json [1,,2]
🟢 n_array_double_extra_comma.json ["x",,]
🟠 n_array_extra_close.json ["x"]]
🟢 n_array_extra_comma.json ["",]
🟢 n_array_incomplete.json ["x"
🟢 n_array_incomplete_invalid_value.json [x
🟢 n_array_inner_array_no_comma.json [3[4]]
🟢 n_array_invalid_utf8.json [FF] ⇔ []
🟢 n_array_items_separated_by_semicolon.json [1:2]
🟢 n_array_just_comma.json [,]
🟢 n_array_just_minus.json [-]
🟢 n_array_missing_value.json [ , ""]
🟢 n_array_newlines_unclosed.json ["a",0A40A,1, ⇔ ["a",
4
,1,
🟢 n_array_number_and_comma.json [1,]
🟢 n_array_number_and_several_commas.json [1,,]
🟢 n_array_spaces_vertical_tab_formfeed.json ["0Ba"\f] ⇔ ["�a"\f]
🟢 n_array_star_inside.json [*]
🟢 n_array_unclosed.json [""
🟢 n_array_unclosed_trailing_comma.json [1,
🟢 n_array_unclosed_with_new_lines.json [1,0A10A,1 ⇔ [1,
1
,1
🟢 n_array_unclosed_with_object_inside.json [{}
🟢 n_incomplete_false.json [fals]
🟢 n_incomplete_null.json [nul]
🟢 n_incomplete_true.json [tru]
🟢 n_multidigit_number_then_00.json 12300 ⇔ 123
🟢 n_number_++.json [++1234]
🟢 n_number_+1.json [+1]
🟢 n_number_+Inf.json [+Inf]
🟠 n_number_-01.json [-01]
🟢 n_number_-1.0..json [-1.0.]
🟠 n_number_-2..json [-2.]
🟢 n_number_-NaN.json [-NaN]
🟢 n_number_.-1.json [.-1]
🟢 n_number_.2e-3.json [.2e-3]
🟢 n_number_0.1.2.json [0.1.2]
🟠 n_number_0.3e+.json [0.3e+]
🟠 n_number_0.3e.json [0.3e]
🟠 n_number_0.e1.json [0.e1]
🟠 n_number_0_capital_E+.json [0E+]
🟠 n_number_0_capital_E.json [0E]
🟠 n_number_0e+.json [0e+]
🟠 n_number_0e.json [0e]
🟠 n_number_1.0e+.json [1.0e+]
🟠 n_number_1.0e-.json [1.0e-]
🟠 n_number_1.0e.json [1.0e]
🟢 n_number_1_000.json [1 000.0]
🟢 n_number_1eE2.json [1eE2]
🟠 n_number_2.e+3.json [2.e+3]
🟠 n_number_2.e-3.json [2.e-3]
🟠 n_number_2.e3.json [2.e3]
🟠 n_number_9.e+.json [9.e+]
🟢 n_number_Inf.json [Inf]
🟢 n_number_NaN.json [NaN]
🟠 n_number_U+FF11_fullwidth_digit_one.json [EFBC91] ⇔ [1]
🟢 n_number_expression.json [1+2]
🟢 n_number_hex_1_digit.json [0x1]
🟢 n_number_hex_2_digits.json [0x42]
🟢 n_number_infinity.json [Infinity]
🟢 n_number_invalid+-.json [0e+-1]
🟢 n_number_invalid-negative-real.json [-123.123foo]
🟢 n_number_invalid-utf-8-in-bigger-int.json [123E5] ⇔ [123]
🟢 n_number_invalid-utf-8-in-exponent.json [1e1E5] ⇔ [1e1]
🟢 n_number_invalid-utf-8-in-int.json [0E5]0A ⇔ [0]
🟢 n_number_minus_infinity.json [-Infinity]
🟢 n_number_minus_sign_with_trailing_garbage.json [-foo]
🟢 n_number_minus_space_1.json [- 1]
🟠 n_number_neg_int_starting_with_zero.json [-012]
🟢 n_number_neg_real_without_int_part.json [-.123]
🟢 n_number_neg_with_garbage_at_end.json [-1x]
🟢 n_number_real_garbage_after_e.json [1ea]
🟢 n_number_real_with_invalid_utf8_after_e.json [1eE5] ⇔ [1e]
🟠 n_number_real_without_fractional_part.json [1.]
🟢 n_number_starting_with_dot.json [.123]
🟢 n_number_with_alpha.json [1.2a-3]
🟢 n_number_with_alpha_char.json [1.8011670033376514H-308]
🟠 n_number_with_leading_zero.json [012]
🟢 n_object_bad_value.json ["x", truth]
🟢 n_object_bracket_key.json {[: "x"}0A ⇔ {[: "x"}
🟢 n_object_comma_instead_of_colon.json {"x", null}
🟢 n_object_double_colon.json {"x"::"b"}
🟢 n_object_emoji.json {F09F87A8F09F87AD} ⇔ {🇨🇭}
🟢 n_object_garbage_at_end.json {"a":"a" 123}
🟢 n_object_key_with_single_quotes.json {key: 'value'}
🟢 n_object_lone_continuation_byte_in_key_and_trailing_comma.json {"B9":"0",} ⇔ {"":"0",}
🟢 n_object_missing_colon.json {"a" b}
🟢 n_object_missing_key.json {:"b"}
🟢 n_object_missing_semicolon.json {"a" "b"}
🟢 n_object_missing_value.json {"a":
🟢 n_object_no-colon.json {"a"
🟢 n_object_non_string_key.json {1:1}
🟢 n_object_non_string_key_but_huge_number_instead.json {9999E9999:1}
🟢 n_object_repeated_null_null.json {null:null,null:null}
🟢 n_object_several_trailing_commas.json {"id":0,,,,,}
🟢 n_object_single_quote.json {'a':0}
🟢 n_object_trailing_comma.json {"id":0,}
🟢 n_object_trailing_comment.json {"a":"b"}/**/
🟢 n_object_trailing_comment_open.json {"a":"b"}/**//
🟢 n_object_trailing_comment_slash_open.json {"a":"b"}//
🟢 n_object_trailing_comment_slash_open_incomplete.json {"a":"b"}/
🟢 n_object_two_commas_in_a_row.json {"a":"b",,"c":"d"}
🟢 n_object_unquoted_key.json {a: "b"}
🟢 n_object_unterminated-value.json {"a":"a
🟢 n_object_with_single_string.json { "foo" : "bar", "a" }
🟢 n_object_with_trailing_garbage.json {"a":"b"}#
🟢 n_single_space.json
🟢 n_string_1_surrogate_then_escape.json ["\uD800\"]
🟠 n_string_1_surrogate_then_escape_u.json ["\uD800\u"]
🟠 n_string_1_surrogate_then_escape_u1.json ["\uD800\u1"]
🟠 n_string_1_surrogate_then_escape_u1x.json ["\uD800\u1x"]
🟢 n_string_accentuated_char_no_quotes.json [C3A9] ⇔ [é]
🟢 n_string_backslash_00.json ["\00"] ⇔ [""]
🟢 n_string_escape_x.json ["\x00"]
🟢 n_string_escaped_backslash_bad.json ["\\\"]
🟢 n_string_escaped_ctrl_char_tab.json ["\09"] ⇔ ["\ "]
🟢 n_string_escaped_emoji.json ["\F09F8C80"] ⇔ ["\🌀"]
🟢 n_string_incomplete_escape.json ["\"]
🟠 n_string_incomplete_escaped_character.json ["\u00A"]
🟠 n_string_incomplete_surrogate.json ["\uD834\uDd"]
🟢 n_string_incomplete_surrogate_escape_invalid.json ["\uD800\uD800\x"]
🟠 n_string_invalid-utf-8-in-escape.json ["\uE5"] ⇔ ["\u"]
🟢 n_string_invalid_backslash_esc.json ["\a"]
🟠 n_string_invalid_unicode_escape.json ["\uqqqq"]
🟢 n_string_invalid_utf8_after_escape.json ["\E5"] ⇔ [""]
🟢 n_string_leading_uescaped_thinspace.json [\u0020"asd"]
🟢 n_string_no_quotes_with_bad_escape.json [\n]
🟢 n_string_single_doublequote.json "
🟢 n_string_single_quote.json ['single quote']
🟢 n_string_single_string_no_double_quotes.json abc
🟢 n_string_start_escape_unclosed.json ["\
🟠 n_string_unescaped_ctrl_char.json ["a00a"] ⇔ ["aa"]
🟠 n_string_unescaped_newline.json ["new0Aline"] ⇔ ["new
line"]
🟠 n_string_unescaped_tab.json ["09"] ⇔ [" "]
🟢 n_string_unicode_CapitalU.json "\UA66D"
🟢 n_string_with_trailing_garbage.json ""x
🔴 n_structure_100000_opening_arrays.json [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[(...)
🟢 n_structure_U+2060_word_joined.json [E281A0] ⇔ [⁠]
🟢 n_structure_UTF8_BOM_no_data.json EFBBBF ⇔ 
🟢 n_structure_angle_bracket_..json <.>
🟢 n_structure_angle_bracket_null.json []
🟢 n_structure_array_trailing_garbage.json [1]x
🟠 n_structure_array_with_extra_array_close.json [1]]
🟢 n_structure_array_with_unclosed_string.json ["asd]
🟢 n_structure_ascii-unicode-identifier.json aC3A5 ⇔ aå
🟢 n_structure_capitalized_True.json [True]
🟠 n_structure_close_unopened_array.json 1]
🟢 n_structure_comma_instead_of_closing_brace.json {"x": true,
🟠 n_structure_double_array.json [][]
🟢 n_structure_end_array.json ]
🟢 n_structure_incomplete_UTF8_BOM.json EFBB{} ⇔ {}
🟢 n_structure_lone-invalid-utf-8.json E5
🟢 n_structure_lone-open-bracket.json [
🟢 n_structure_no_data.json
🟢 n_structure_null-byte-outside-string.json [00] ⇔ []
🟢 n_structure_number_with_trailing_garbage.json 2@
🟠 n_structure_object_followed_by_closing_object.json {}}
🟢 n_structure_object_unclosed_no_value.json {"":
🟢 n_structure_object_with_comment.json {"a":/comment/"b"}
🟠 n_structure_object_with_trailing_garbage.json {"a": true} "x"
🟢 n_structure_open_array_apostrophe.json ['
🟢 n_structure_open_array_comma.json [,
🔴 n_structure_open_array_object.json [{"":[{"":[{"":[{"":[{"":[{"":[{"":[(...)
🟢 n_structure_open_array_open_object.json [{
🟢 n_structure_open_array_open_string.json ["a
🟢 n_structure_open_array_string.json ["a"
🟢 n_structure_open_object.json {
🟢 n_structure_open_object_close_array.json {]
🟢 n_structure_open_object_comma.json {,
🟢 n_structure_open_object_open_array.json {[
🟢 n_structure_open_object_open_string.json {"a
🟢 n_structure_open_object_string_with_apostrophes.json {'a'
🟢 n_structure_open_open.json ["\{["\{["\{["\{
🟢 n_structure_single_eacute.json E9
🟢 n_structure_single_star.json *
🟢 n_structure_trailing_#.json {"a":"b"}#{}
🟢 n_structure_uescaped_LF_before_string.json [\u000A""]
🟢 n_structure_unclosed_array.json [1
🟢 n_structure_unclosed_array_partial_null.json [ false, nul
🟢 n_structure_unclosed_array_unfinished_false.json [ true, fals
🟢 n_structure_unclosed_array_unfinished_true.json [ false, tru
🟢 n_structure_unclosed_object.json {"asd":"asd"
🟢 n_structure_unicode-identifier.json C3A5 ⇔ å
🟢 n_structure_whitespace_U+2060_word_joiner.json [E281A0] ⇔ [⁠]
🟢 n_structure_whitespace_formfeed.json [0C] ⇔ [ ]
🟢 y_array_arraysWithSpaces.json [[] ]
🟢 y_array_empty-string.json [""]
🟢 y_array_empty.json []
🟢 y_array_ending_with_newline.json ["a"]
🟢 y_array_false.json [false]
🟢 y_array_heterogeneous.json [null, 1, "1", {}]
🟢 y_array_null.json [null]
🟢 y_array_with_1_and_newline.json [10A] ⇔ [1
]
🟢 y_array_with_leading_space.json [1]
🟢 y_array_with_several_null.json [1,null,null,null,2]
🟢 y_array_with_trailing_space.json [2]
🟢 y_number.json [123e65]
🟢 y_number_0e+1.json [0e+1]
🟢 y_number_0e1.json [0e1]
🟢 y_number_after_space.json [ 4]
🟢 y_number_double_close_to_zero.json [-0.00000000000000000000000000000000(...)
🟢 y_number_int_with_exp.json [20e1]
🟢 y_number_minus_zero.json [-0]
🟢 y_number_negative_int.json [-123]
🟢 y_number_negative_one.json [-1]
🟢 y_number_negative_zero.json [-0]
🟢 y_number_real_capital_e.json [1E22]
🟢 y_number_real_capital_e_neg_exp.json [1E-2]
🟢 y_number_real_capital_e_pos_exp.json [1E+2]
🟢 y_number_real_exponent.json [123e45]
🟢 y_number_real_fraction_exponent.json [123.456e78]
🟢 y_number_real_neg_exp.json [1e-2]
🟢 y_number_real_pos_exponent.json [1e+2]
🟢 y_number_simple_int.json [123]
🟢 y_number_simple_real.json [123.456789]
🟢 y_object.json {"asd":"sdf", "dfg":"fgh"}
🟢 y_object_basic.json {"asd":"sdf"}
🟢 y_object_duplicated_key.json {"a":"b","a":"c"}
🟢 y_object_duplicated_key_and_value.json {"a":"b","a":"b"}
🟢 y_object_empty.json {}
🟢 y_object_empty_key.json {"":0}
🟢 y_object_escaped_null_in_key.json {"foo\u0000bar": 42}
🟢 y_object_extreme_numbers.json { "min": -1.0e+28, "max": 1.0e+28 }
🟢 y_object_long_strings.json {"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxx(...)
🟢 y_object_simple.json {"a":[]}
🟢 y_object_string_unicode.json {"title":"\u041f\u043e\u043b\u04(...)
🟢 y_object_with_newlines.json {0A"a": "b"0A} ⇔ {
"a": "b"
}
🟢 y_string_1_2_3_bytes_UTF-8_sequences.json ["\u0060\u012a\u12AB"]
🟢 y_string_accepted_surrogate_pair.json ["\uD801\udc37"]
🟢 y_string_accepted_surrogate_pairs.json ["\ud83d\ude39\ud83d\udc8d"]
🟢 y_string_allowed_escapes.json ["\"\\\/\b\f\n\r\t"]
🟢 y_string_backslash_and_u_escaped_zero.json ["\\u0000"]
🟢 y_string_backslash_doublequotes.json ["\""]
🟢 y_string_comments.json ["a/b/c/*d//e"]
🟢 y_string_double_escape_a.json ["\\a"]
🟢 y_string_double_escape_n.json ["\\n"]
🟢 y_string_escaped_control_character.json ["\u0012"]
🟢 y_string_escaped_noncharacter.json ["\uFFFF"]
🟢 y_string_in_array.json ["asd"]
🟢 y_string_in_array_with_leading_space.json [ "asd"]
🟢 y_string_last_surrogates_1_and_2.json ["\uDBFF\uDFFF"]
🟢 y_string_nbsp_uescaped.json ["new\u00A0line"]
🟢 y_string_nonCharacterInUTF-8_U+10FFFF.json ["F48FBFBF"] ⇔ ["�"]
🟢 y_string_nonCharacterInUTF-8_U+FFFF.json ["EFBFBF"] ⇔ ["�"]
🟢 y_string_null_escape.json ["\u0000"]
🟢 y_string_one-byte-utf-8.json ["\u002c"]
🟢 y_string_pi.json ["CF80"] ⇔ ["π"]
🟢 y_string_reservedCharacterInUTF-8_U+1BFFF.json ["F09BBFBF"] ⇔ ["𛿿"]
🟢 y_string_simple_ascii.json ["asd "]
🟢 y_string_space.json " "
🟢 y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json ["\uD834\uDd1e"]
🟢 y_string_three-byte-utf-8.json ["\u0821"]
🟢 y_string_two-byte-utf-8.json ["\u0123"]
🟢 y_string_u+2028_line_sep.json ["E280A8"] ⇔ ["
"]
🟢 y_string_u+2029_par_sep.json ["E280A9"] ⇔ ["
"]
🟢 y_string_uEscape.json ["\u0061\u30af\u30EA\u30b9"]
🟢 y_string_uescaped_newline.json ["new\u000Aline"]
🟢 y_string_unescaped_char_delete.json ["7F"] ⇔ ["�"]
🟢 y_string_unicode.json ["\uA66D"]
🟢 y_string_unicodeEscapedBackslash.json ["\u005C"]
🟢 y_string_unicode_2.json ["E28D82E388B4E28D82"] ⇔ ["⍂㈴⍂"]
🟢 y_string_unicode_U+10FFFE_nonchar.json ["\uDBFF\uDFFE"]
🟢 y_string_unicode_U+1FFFE_nonchar.json ["\uD83F\uDFFE"]
🟢 y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json ["\u200B"]
🟢 y_string_unicode_U+2064_invisible_plus.json ["\u2064"]
🟢 y_string_unicode_U+FDD0_nonchar.json ["\uFDD0"]
🟢 y_string_unicode_U+FFFE_nonchar.json ["\uFFFE"]
🟢 y_string_unicode_escaped_double_quote.json ["\u0022"]
🟢 y_string_utf8.json ["E282ACF09D849E"] ⇔ ["€𝄞"]
🟢 y_string_with_del_character.json ["a7Fa"] ⇔ ["a�a"]
🟢 y_structure_lonely_false.json false
🟢 y_structure_lonely_int.json 42
🟢 y_structure_lonely_negative_real.json -0.1
🟢 y_structure_lonely_null.json null
🟢 y_structure_lonely_string.json "asd"
🟢 y_structure_lonely_true.json true
🟢 y_structure_string_empty.json ""
🟢 y_structure_trailing_newline.json ["a"]0A ⇔ ["a"]
🟢 y_structure_true_in_array.json [true]
🟢 y_structure_whitespace_array.json []

@phil-scott-78
Copy link
Contributor

I think @0xced test case makes a case for a pluggable system. I think shipping with the current json parser that could be swapped out by someone using something like System.Text.Json or newtonsoft if they need something to handle all the edge cases.

Might be worth checking out the two errors in the current parser though

@patriksvensson
Copy link
Contributor Author

@0xced Awesome idea! I'll add those tests to our tests as well.

@phil-scott-78 I'll fix the parsing errors, add an abstraction for it, and move it to a plugin in the repo. I still think it's worth having a JSON renderer without dependencies. It has turned out to be a very useful feature in Rich.

@patriksvensson
Copy link
Contributor Author

A side note; we really want to be as tolerant as possible since we're only renderering the JSON, so only the red and purple tests should be fixed.

@patriksvensson patriksvensson marked this pull request as draft December 18, 2022 10:39
@patriksvensson
Copy link
Contributor Author

@spectreconsole/maintainers This one is ready for review now.

@0xced
Copy link
Contributor

0xced commented Dec 18, 2022

The two red tests are really here to test for stack overflow, which is exactly what happened. The blue and purple results are not really relevant, they are implementation defined. The orange results are the most interesting: they are all tests of invalid JSON where the parser did not throw any exception. They could definitively be used to make sure the JSON is rendered in another shape/color to indicate that the JSON is invalid. But that could turn out to be even harder than writing a conforming JSON parser that throws on invalid input…

And as Patrick said, since the only purpose of this parser is to render the JSON, being very tolerant is a good thing and I don't think it would be worthwhile to design a pluggable parser system.

@FrankRay78
Copy link
Contributor

Hi @patriksvensson, based on my review comments above, I've done some minor reworking of the JsonText class, see: https:/FrankRay78/spectre.console/tree/json (this commit: 6c1efc1)

I feel it's cleaner removing both statics and relocating the number of spaces to indent each time (unless of course you have reasons for doing this which I'm not aware of). Happy to discuss further or defer to your judgement here though.

@patriksvensson
Copy link
Contributor Author

@FrankRay78 I'm afraid I don't agree on removing the static shared instance. The builder has no state, so newing up a new instance of the builder really gives no benefit. I also don't think that indenation spaces should be part of styles, which contains Styles for different parts. If we add IndentationSpaces, then I think we should rename this class to something like JsonSettings or similar.

@FrankRay78
Copy link
Contributor

I've come to hate all static classes because of how they hinder unit testing and obscure dependencies. However, that's personal preference and in this case, JsonText is a self contained module with a covering integration test and not intended for public extension.

Regarding IndentationSpaces, I did look at the possibility of having it located on the BuilderContext class, which would allow the styles class to remain unchanged, preserving the current name.

Anyhow, these were my PR review thoughts - take (or leave) what you feel is most helpful. Happy to mark the review comments as resolved.

@FrankRay78
Copy link
Contributor

Ok, probably the final review comment from me... Given the json parser test results above provided by @0xced, I did wonder whether individual test cases to be fixed could/should be included in the VS solution, accompanied with (at least) failing tests written for each, to keep track of known issues to fix - either now or at some point later. eg. for both red tests, that would involve including the following files:

https:/nst/JSONTestSuite/blob/master/test_parsing/n_structure_open_array_object.json
https:/nst/JSONTestSuite/blob/master/test_parsing/n_structure_100000_opening_arrays.json

However, when I opened up each of these json files, it became clear these are very verbose and almost certainly edge case parsing conditions. Also, we'd really need to include all tests in the VS solution, and as such, seems to be a sledgehammer cracking a nut approach.

Then I noticed that actual parsers had been uploaded into the JSONTestSuite repo and integrated into the test harness there, used to produce the following html report (located here: https:/nst/JSONTestSuite/tree/master/results) eg:

image

I also noticed that @0xced had previously uploaded both dotnet parsers into the JSONTestSuite repo, and so I wondered, whether for good measure and in a similar way, actually if the Spectre.Console.Json parser should be uploaded alongside the other parsers, and the html report regenerated - as a way to fully document the exact behaviour of the spectre Json parser against all others.

We could link out to that report somewhere in the Spectre.Console documentation should end users be interested in this. And over time should the Json parser be updated, then new versions could be uploaded into the JSONTestSuite repo and the html report regenerated.

(possibly completely over the top, but I would be remiss not to mention this thought as an outcome of my PR review)

Copy link
Contributor

@FrankRay78 FrankRay78 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All my comments have been addressed in Patrik's comment here - #1086 (comment)

src/Spectre.Console.Json/JsonBuilder.cs Show resolved Hide resolved
src/Spectre.Console.Json/JsonParser.cs Show resolved Hide resolved
src/Spectre.Console.Json/JsonBuilder.cs Show resolved Hide resolved
@FrankRay78 FrankRay78 merged commit 54be64e into spectreconsole:main Dec 31, 2022
@patriksvensson patriksvensson deleted the feature/json branch December 31, 2022 18:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

JSON Syntax Highlighting Support In Console
5 participants