https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116792
--- Comment #9 from GCC Commits <cvs-commit at gcc dot gnu.org> --- The master branch has been updated by David Malcolm <dmalc...@gcc.gnu.org>: https://gcc.gnu.org/g:04871e748862fc48fac739f0ce9769e566ca7cc3 commit r16-909-g04871e748862fc48fac739f0ce9769e566ca7cc3 Author: David Malcolm <dmalc...@redhat.com> Date: Tue May 27 12:22:26 2025 -0400 diagnostics: rework experimental-html output [PR116792] This patch reworks the HTML output from the the option -fdiagnostics-add-output=experimental-html so that for source quoting and path printing, rather than simply adding the textual output inside <pre> elements, it breaks up the output into HTML tags reflecting the structure of the output, using CSS, SVG and a little javascript to help the user navigate the diagnostics and the events within any paths. This uses ideas from the patch I posted in: https://gcc.gnu.org/pipermail/gcc-patches/2020-November/558603.html but reworks the above patch so that: * rather than printing source to a pretty_printer, the HTML is created by building a DOM tree in memory, using a new xml::printer class. This should be less error-prone than the pretty_printer approach, since it ought to solve escaping issues. Instead of a text vs html boolean, the code is generalized via templates with to_text vs to_html sinks. This templatization applies both to path-printing and diagnostic-show-locus.cc. * the HTML output can have multiple diagnostics and multiple paths rather than just a single path. The javascript keyboard controls now cycle through all diagnostics and all events within them An example of the output can be seen at: https://dmalcolm.fedorapeople.org/gcc/2025-05-27/malloc-1.c.html where the keys "j" and "k" cycle through diagnostics and events within them. gcc/ChangeLog: PR other/116792 * diagnostic-format-html.cc: Define INCLUDE_STRING. Include "xml.h", "xml-printer.h", and "json.h". (html_generation_options::html_generation_options): New. (namespace xml): Move decls to xml.h and convert from using label_text to std::string. (xml::text::write_as_xml): Reimplement indentation so it is done by this node, rather than the parent. (xml::node_with_children::add_text): Convert from label_text to std::string. Consolidate runs of text into a single node. (xml::document::write_as_xml): Reimplement indentation. (xml::element::write_as_xml): Reimplement indentation so it is done by this node, rather than the parent. Convert from label_text to std::string. Update attribute-printing to new representation to preserve insertion order. (xml::element::set_attr): Convert from label_text to std::string. Record insertion order. (xml::raw::write_as_xml): New. (xml::printer::printer): New. (xml::printer::push_tag): New. (xml::printer::push_tag_with_class): New. (xml::printer::pop_tag): New. (xml::printer::set_attr): New. (xml::printer::add_text): New. (xml::printer::add_raw): New. (xml::printer::push_element): New. (xml::printer::append): New. (xml::printer::get_insertion_point): New. (html_builder::add_focus_id): New. (html_builder::m_html_gen_opts): New field. (html_builder::m_head_element): New field. (html_builder::m_next_diag_id): New field. (html_builder::m_ui_focus_ids): New field. (make_div): Convert from label_text to std::string. (make_span): Likewise. (HTML_STYLE): New. (HTML_SCRIPT): New. (html_builder::html_builder): Fix indentation. Add "html_gen_opts" param. Initialize new fields. Reimplement using xml::printer. Optionally add style and script tags. (class html_path_label_writer): New. (html_builder::make_element_for_diagnostic): Convert from label_text to std::string. Set "id" on "gcc-diagnostic" and "gcc-message" <div> elements; add the latter to the focus ids. Use diagnostic_context::maybe_show_locus_as_html rather than html_builder::make_element_for_source. Use print_path_as_html rather than html_builder::make_element_for_path. (html_builder::make_element_for_source): Drop. (html_builder::make_element_for_path): Drop. (html_builder::make_element_for_patch): Convert from label_text to std::string. (html_builder::make_metadata_element): Likewise. Use xml::printer. (html_builder::make_element_for_metadata): Convert from label_text to std::string. (html_builder::emit_diagram): Expand comment. (html_builder::flush_to_file): Write out initializer for "focus_ids" into javascript. (html_output_format::html_output_format): Add param "html_gen_opts" and use it to initialize m_builder. (html_file_output_format::html_file_output_format): Likewise, to initialize base class. (make_html_sink): Likewise, to pass to ctor. (selftest::test_html_diagnostic_context::test_html_diagnostic_context): Set up html_generation_options. (selftest::html_buffered_output_format::html_buffered_output_format): Add html_gen_opts param. (selftest::test_simple_log): Add id attributes to expected text for "gcc-diagnostic" and "gcc-message" elements. Update whitespace for indentation fixes. (selftest::test_metadata): Update whitespace for indentation fixes. (selftest::test_printer): New selftest. (selftest::test_attribute_ordering): New selftest. (selftest::diagnostic_format_html_cc_tests): Call the new selftests. * diagnostic-format-html.h (struct html_generation_options): New. (make_html_sink): Add "html_gen_opts" param. (print_path_as_html): New decl. * diagnostic-path-output.cc: Define INCLUDE_MAP. Add includes of "diagnostic-format-html.h", "xml.h", and "xml-printer.h". (path_print_policy::path_print_policy): Add ctor. (path_print_policy::get_diagram_theme): Fix whitespace. (struct stack_frame): New. (begin_html_stack_frame): New function. (end_html_stack_frame): New function. (emit_svg_arrow): New function. (event_range::print): Rename to... (event_range::print_as_text): ...this. Update call to diagnostic_start_span. (event_range::print_as_html): New, based on the above, but ported from pretty_printer to xml::printer. (thread_event_printer::print_swimlane_for_event_range): Rename to... (thread_event_printer::print_swimlane_for_event_range_as_text): ...this. Update for renaming of event_range::print to event_range::print_as_text. (thread_event_printer::print_swimlane_for_event_range_as_html): New. (print_path_summary_as_text): Update for "_as_text" renaming. (print_path_summary_as_html): New. (print_path_as_html): New. * diagnostic-show-locus.cc: Add defines of INCLUDE_MAP and INCLUDE_STRING. Add includes of "xml.h" and "xml-printer.h". (struct char_display_policy): Replace "m_print_cb" with "m_print_text_cb" and "m_print_html_cb". (struct to_text): New. (struct to_html): New. (get_printer): New. (default_diagnostic_start_span_fn<to_text>): New. (default_diagnostic_start_span_fn<to_html>): New. (class layout): Update "friend class layout_printer;" for template. (enum class margin_kind): New. (class layout_printer): Convert into a template. (layout_printer::m_pp): Replace field with... (layout_printer::m_sink): ...this. (layout_printer::m_colorizer): Drop field in favor of a pointer in the "to_text" sink. (default_print_decoded_ch): Convert into a template. (escape_as_bytes_print): Likewise. (escape_as_unicode_print): Likewise. (make_char_policy): Update to use both text and html callbacks. (layout_printer::print_gap_in_line_numbering): Replace with... (layout_printer<to_text>::print_gap_in_line_numbering): ...this (layout_printer<to_html>::print_gap_in_line_numbering): ...and this. (layout_printer::print_source_line): Convert to template, using m_sink. (layout_printer::print_leftmost_column): Likewise. (layout_printer::start_annotation_line): Likewise. (layout_printer<to_text>::end_line): New. (layout_printer<to_html>::end_line): New. (layout_printer::print_annotation_line): Convert to template, using m_sink. (class line_label): Add field m_original_range_idx. (layout_printer<to_text>::begin_label): New. (layout_printer<to_html>::begin_label): New. (layout_printer<to_text>::end_label): New. (layout_printer<to_html>::end_label): New. (layout_printer::print_any_labels): Convert to template, using m_sink. (layout_printer::print_leading_fixits): Likewise. (layout_printer::print_trailing_fixits): Likewise. (layout_printer::print_newline): Drop. (layout_printer::move_to_column): Convert to template, using m_sink. (layout_printer::show_ruler): Likewise. (layout_printer::print_line): Likewise. (layout_printer::print_any_right_to_left_edge_lines): Likewise. (layout_printer::layout_printer): Likewise. (diagnostic_context::maybe_show_locus_as_html): New. (diagnostic_source_print_policy::diagnostic_source_print_policy): Update for split of start_span_cb into text vs html variants. (diagnostic_source_print_policy::print): Update for use of templates; use to_text. (diagnostic_source_print_policy::print_as_html): New. (layout_printer::print): Convert to template, using m_sink. (selftest::make_element_for_locus): New. (selftest::make_raw_html_for_locus): New. (selftest::test_layout_x_offset_display_utf8): Update for use of templates. (selftest::test_layout_x_offset_display_tab): Likewise. (selftest::test_one_liner_caret_and_range): Add test coverage of HTML output. (selftest::test_one_liner_labels): Likewise. * diagnostic.cc (diagnostic_context::initialize): Update for split of start_span_cb into text vs html variants. (default_diagnostic_start_span_fn): Move to diagnostic-show-locus.cc, converting to template. * diagnostic.h (class xml::printer): New forward decl. (diagnostic_start_span_fn): Replace typedef with "using", converting to a template. (struct to_text): New forward decl. (struct to_html): New forward decl. (get_printer): New decl. (diagnostic_location_print_policy::print_text_span_start): New decl. (diagnostic_location_print_policy::print_html_span_start): New decl. (class html_label_writer): New. (diagnostic_source_print_policy::print_as_html): New decl. (diagnostic_source_print_policy::get_start_span_fn): Replace with... (diagnostic_source_print_policy::get_text_start_span_fn): ...this (diagnostic_source_print_policy::get_html_start_span_fn): ...and this (diagnostic_source_print_policy::m_start_span_cb): Replace with... (diagnostic_source_print_policy::m_text_start_span_cb): ...this (diagnostic_source_print_policy::m_html_start_span_cb): ...and this. (diagnostic_context::maybe_show_locus_as_html): New decl. (diagnostic_context::m_text_callbacks::m_start_span): Replace with... (diagnostic_context::m_text_callbacks::m_text_start_span): ...this (diagnostic_context::m_text_callbacks::m_html_start_span): ...and this. (diagnostic_start_span): Update for template change. (diagnostic_show_locus_as_html): New inline function. (default_diagnostic_start_span_fn): Convert to template. * doc/invoke.texi (experimental-html): Add "css" and "javascript" keys. * opts-diagnostic.cc (html_scheme_handler::make_sink): Likewise. * selftest-diagnostic.cc (selftest::test_diagnostic_context::start_span_cb): Update for template changes. * selftest-diagnostic.h (selftest::test_diagnostic_context::start_span_cb): Likewise. * xml-printer.h: New file. * xml.h: New file, based on material in diagnostic-format-html.cc, but using std::string rather than label_text. (xml::element::m_key_insertion_order): New field. (struct xml::raw): New. gcc/fortran/ChangeLog PR other/116792 * error.cc (gfc_diagnostic_start_span): Update for diagnostic.h changes. gcc/testsuite/ChangeLog: PR other/116792 * gcc.dg/html-output/missing-semicolon.c: Add ":javascript=no" to html output. * gcc.dg/html-output/missing-semicolon.py: Move repeated definitions into lib/htmltest.py. * gcc.dg/plugin/diagnostic_group_plugin.cc: Update for template changes. * gcc.dg/plugin/diagnostic-test-metadata-html.c: Add ":javascript=no" to html output. Add "-fdiagnostics-show-line-numbers". * gcc.dg/plugin/diagnostic-test-metadata-html.py: Move repeated definitions into lib/htmltest.py. Add checks of annotated source. * gcc.dg/plugin/diagnostic-test-paths-2.c: Add ":javascript=no" to html output. * gcc.dg/plugin/diagnostic-test-paths-2.py: Move repeated definitions into lib/htmltest.py. Add checks of execution path. * gcc.dg/plugin/diagnostic-test-paths-4.c: Add -fdiagnostics-add-output=experimental-html:javascript=no. Add invocation ot diagnostic-test-paths-4.py. * gcc.dg/plugin/diagnostic-test-paths-4.py: New test script. * gcc.dg/plugin/diagnostic-test-show-locus-bw-line-numbers.c: Add -fdiagnostics-add-output=experimental-html:javascript=no. Add invocation of diagnostic-test-show-locus.py. * gcc.dg/plugin/diagnostic-test-show-locus.py: New test script. * lib/htmltest.py: New test support script. Signed-off-by: David Malcolm <dmalc...@redhat.com>