diff options
Diffstat (limited to 'gcc/diagnostic-show-locus.cc')
-rw-r--r-- | gcc/diagnostic-show-locus.cc | 1136 |
1 files changed, 934 insertions, 202 deletions
diff --git a/gcc/diagnostic-show-locus.cc b/gcc/diagnostic-show-locus.cc index 898efe7..da7637c 100644 --- a/gcc/diagnostic-show-locus.cc +++ b/gcc/diagnostic-show-locus.cc @@ -19,6 +19,8 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MAP +#define INCLUDE_STRING #define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" @@ -37,6 +39,8 @@ along with GCC; see the file COPYING3. If not see #include "text-art/types.h" #include "text-art/theme.h" #include "diagnostic-label-effects.h" +#include "xml.h" +#include "xml-printer.h" #ifdef HAVE_TERMIOS_H # include <termios.h> @@ -70,7 +74,8 @@ struct point_state bool draw_caret_p; }; -/* A class to inject colorization codes when printing the diagnostic locus. +/* A class to inject colorization codes when printing the diagnostic locus + as text. It has one kind of colorization for each of: - normal text @@ -143,6 +148,7 @@ class colorizer const char *m_fixit_insert; const char *m_fixit_delete; const char *m_stop_color; + std::string m_current_named_color; }; /* In order to handle multibyte sources properly, all of this logic needs to be @@ -369,17 +375,380 @@ struct char_display_policy : public cpp_char_column_policy public: char_display_policy (int tabstop, int (*width_cb) (cppchar_t c), - void (*print_cb) (pretty_printer *pp, - const cpp_decoded_char &cp)) + void (*print_text_cb) (to_text &sink, + const cpp_decoded_char &cp), + void (*print_html_cb) (to_html &sink, + const cpp_decoded_char &cp)) : cpp_char_column_policy (tabstop, width_cb), - m_print_cb (print_cb) + m_print_text_cb (print_text_cb), + m_print_html_cb (print_html_cb) { } - void (*m_print_cb) (pretty_printer *pp, - const cpp_decoded_char &cp); + void (*m_print_text_cb) (to_text &sink, + const cpp_decoded_char &cp); + void (*m_print_html_cb) (to_html &sink, + const cpp_decoded_char &cp); }; +template <typename Sink> class layout_printer; + +} // anonymous namespace + +/* This code is written generically to write either: + - text, to a pretty_printer, potentially with colorization codes, or + - html, to an xml::printer, with nested HTML tags. + + This is handled via a "Sink" template, which is either to_text + or to_html. */ + +/* Writing text output. */ + +struct to_text +{ + friend class layout_printer<to_text>; + + // This is a RAII class for HTML, but is a no-op for text. + struct auto_check_tag_nesting + { + auto_check_tag_nesting (to_text &) {} + }; + + to_text (pretty_printer &pp, + colorizer &colorizer) + : m_pp (pp), + m_colorizer (&colorizer) + { + m_saved_rule = pp_prefixing_rule (&m_pp); + pp_prefixing_rule (&m_pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE; + } + to_text (pretty_printer &pp, + colorizer *colorizer) + : m_pp (pp), + m_colorizer (colorizer) + { + m_saved_rule = pp_prefixing_rule (&m_pp); + pp_prefixing_rule (&m_pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE; + } + ~to_text () + { + + pp_prefixing_rule (&m_pp) = m_saved_rule; + } + + static bool is_text () { return true; } + static bool is_html () { return false; } + + void emit_text_prefix () + { + pp_emit_prefix (&m_pp); + } + + void push_html_tag (std::string, bool) + { + // no-op for text + } + void push_html_tag_with_class (std::string, std::string, bool) + { + // no-op for text + } + void pop_html_tag (const char *) + { + // no-op for text + } + + void add_html_tag_with_class (std::string, std::string, bool) + { + // no-op for text + } + + void add_space () + { + pp_space (&m_pp); + } + + void add_character (cppchar_t ch) + { + pp_unicode_character (&m_pp, ch); + } + + void add_utf8_byte (char b) + { + pp_character (&m_pp, b); + } + + void add_text (const char *text) + { + pp_string (&m_pp, text); + } + + void print_decoded_char (const char_display_policy &char_policy, + cpp_decoded_char cp) + { + char_policy.m_print_text_cb (*this, cp); + } + + void colorize_text_ensure_normal () + { + gcc_assert (m_colorizer); + m_colorizer->set_normal_text (); + } + + void colorize_text_for_range_idx (int range_idx) + { + gcc_assert (m_colorizer); + m_colorizer->set_range (range_idx); + } + + void colorize_text_for_cfg_edge () + { + gcc_assert (m_colorizer); + m_colorizer->set_cfg_edge (); + } + + void colorize_text_for_fixit_insert () + { + gcc_assert (m_colorizer); + m_colorizer->set_fixit_insert (); + } + + void colorize_text_for_fixit_delete () + { + gcc_assert (m_colorizer); + m_colorizer->set_fixit_delete (); + } + + void + invoke_start_span_fn (const diagnostic_source_print_policy &source_policy, + const diagnostic_location_print_policy &loc_policy, + const expanded_location &exploc) + { + source_policy.get_text_start_span_fn () (loc_policy, *this, exploc); + } + + // Text-specific functions + void add_newline () + { + pp_newline (&m_pp); + } + + pretty_printer &m_pp; +private: + colorizer *m_colorizer; + diagnostic_prefixing_rule_t m_saved_rule; +}; + +/* Writing HTML output. */ + +struct to_html +{ + friend class layout_printer<to_html>; + + // RAII class for ensuring that the tags nested correctly + struct auto_check_tag_nesting : public xml::auto_check_tag_nesting + { + public: + auto_check_tag_nesting (to_html &sink) + : xml::auto_check_tag_nesting (sink.m_xp) + { + } + }; + + to_html (xml::printer &xp, + const rich_location *richloc, + html_label_writer *html_label_writer) + : m_xp (xp), + m_richloc (richloc), + m_html_label_writer (html_label_writer) + {} + + static bool is_text () { return false; } + static bool is_html () { return true; } + + void emit_text_prefix () + { + // no-op for HTML + } + + void push_html_tag (std::string name, + bool preserve_whitespace) + { + m_xp.push_tag (std::move (name), preserve_whitespace); + } + + void push_html_tag_with_class (std::string name, + std::string class_, + bool preserve_whitespace) + { + m_xp.push_tag_with_class (std::move (name), + std::move (class_), + preserve_whitespace); + } + + void pop_html_tag (const char *expected_name) + { + m_xp.pop_tag (expected_name); + } + + void add_html_tag_with_class (std::string name, + std::string class_, + bool preserve_whitespace) + { + auto element = std::make_unique<xml::element> (std::move (name), + preserve_whitespace); + element->set_attr ("class", std::move (class_)); + m_xp.append (std::move (element)); + } + + void add_raw_html (const char *src) + { + m_xp.add_raw (src); + } + + void add_space () + { + m_xp.add_text (" "); + } + + void add_character (cppchar_t ch) + { + pp_clear_output_area (&m_scratch_pp); + pp_unicode_character (&m_scratch_pp, ch); + m_xp.add_text_from_pp (m_scratch_pp); + } + + void add_utf8_byte (char b) + { + m_xp.add_text (std::string (1, b)); + } + + void add_text (const char *text) + { + m_xp.add_text (text); + } + + void print_decoded_char (const char_display_policy &char_policy, + cpp_decoded_char cp) + { + char_policy.m_print_html_cb (*this, cp); + } + + void colorize_text_ensure_normal () + { + // no-op for HTML + } + + void colorize_text_for_cfg_edge () + { + // no-op for HTML + } + + void colorize_text_for_fixit_insert () + { + // no-op for HTML + } + + void colorize_text_for_fixit_delete () + { + // no-op for HTML + } + + void + invoke_start_span_fn (const diagnostic_source_print_policy &source_policy, + const diagnostic_location_print_policy &loc_policy, + const expanded_location &exploc) + { + source_policy.get_html_start_span_fn () (loc_policy, *this, exploc); + } + + const location_range * + get_location_range_by_idx (int range_idx) + { + if (!m_richloc) + return nullptr; + return m_richloc->get_range (range_idx); + } + + const char * + get_highlight_color_for_range_idx (int range_idx) + { + const location_range *const loc_range + = get_location_range_by_idx (range_idx); + if (!loc_range) + return nullptr; + return loc_range->m_highlight_color; + } + + xml::printer &m_xp; + const rich_location *m_richloc; +private: + html_label_writer *m_html_label_writer; + pretty_printer m_scratch_pp; +}; + +void +diagnostic_location_print_policy:: +print_text_span_start (const diagnostic_context &dc, + pretty_printer &pp, + const expanded_location &exploc) +{ + to_text sink (pp, nullptr); + diagnostic_source_print_policy source_policy (dc); + source_policy.get_text_start_span_fn () (*this, sink, exploc); +} + +void +diagnostic_location_print_policy:: +print_html_span_start (const diagnostic_context &dc, + xml::printer &xp, + const expanded_location &exploc) +{ + to_html sink (xp, nullptr, nullptr); + diagnostic_source_print_policy source_policy (dc); + source_policy.get_html_start_span_fn () (*this, sink, exploc); +} + +pretty_printer * +get_printer (to_text &sink) +{ + return &sink.m_pp; +} + +template<> +void +default_diagnostic_start_span_fn<to_text> (const diagnostic_location_print_policy &loc_policy, + to_text &sink, + expanded_location exploc) +{ + const diagnostic_column_policy &column_policy + = loc_policy.get_column_policy (); + label_text text + = column_policy.get_location_text (exploc, + loc_policy.show_column_p (), + pp_show_color (&sink.m_pp)); + pp_string (&sink.m_pp, text.get ()); + pp_newline (&sink.m_pp); +} + +template<> +void +default_diagnostic_start_span_fn<to_html> (const diagnostic_location_print_policy &loc_policy, + to_html &sink, + expanded_location exploc) +{ + const diagnostic_column_policy &column_policy + = loc_policy.get_column_policy (); + label_text text + = column_policy.get_location_text (exploc, + loc_policy.show_column_p (), + false); + sink.m_xp.push_tag_with_class ("span", "location", true); + sink.m_xp.add_text (text.get ()); + sink.m_xp.pop_tag ("span"); +} + +namespace { + /* A class to control the overall layout when printing a diagnostic. The layout is determined within the constructor. @@ -392,7 +761,8 @@ struct char_display_policy : public cpp_char_column_policy class layout { public: - friend class layout_printer; + friend class layout_printer<to_text>; + friend class layout_printer<to_html>; layout (const diagnostic_source_print_policy &source_policy, const rich_location &richloc, @@ -458,16 +828,24 @@ class layout bool m_escape_on_output; }; -/* A bundle of state for printing a particular layout - to a particular pretty_printer. */ +class line_label; + +enum class margin_kind +{ + normal, + insertion, + ruler +}; +/* A bundle of state for printing a particular layout + to a particular Sink (either to_text or to_html). */ +template <typename Sink> class layout_printer { public: - layout_printer (pretty_printer &pp, + layout_printer (Sink &sink, const layout &layout, - const rich_location &richloc, - diagnostic_t diagnostic_kind); + bool is_diagnostic_path); void print (const diagnostic_source_print_policy &source_policy); @@ -485,23 +863,30 @@ private: line_bounds print_source_line (linenum_type row, const char *line, int line_bytes); void print_leftmost_column (); - void start_annotation_line (char margin_char = ' '); + void start_annotation_line (enum margin_kind); + void end_line (); void print_annotation_line (linenum_type row, const line_bounds lbounds); void print_any_labels (linenum_type row); + void begin_label (int state_idx, bool is_label_text); + void end_label (int state_idx, bool is_label_text); void print_trailing_fixits (linenum_type row); - void print_newline (); void move_to_column (int *column, int dest_column, bool add_left_margin); void print_any_right_to_left_edge_lines (); + void set_in_range (int range_idx); + void set_outside_range (); + private: - pretty_printer &m_pp; + Sink &m_sink; const layout &m_layout; - colorizer m_colorizer; bool m_is_diagnostic_path; + bool m_was_in_range_p; + int m_last_range_idx; + /* Fields for handling links between labels (e.g. for showing CFG edges in execution paths). Note that the logic for printing such links makes various simplifying @@ -586,9 +971,13 @@ colorizer::~colorizer () void colorizer::set_named_color (const char *color) { + if (m_current_state == STATE_NAMED_COLOR + && color == m_current_named_color) + return; finish_state (m_current_state); m_current_state = STATE_NAMED_COLOR; pp_string (&m_pp, colorize_start (pp_show_color (&m_pp), color)); + m_current_named_color = color; } /* Update state, printing color codes if necessary if there's a state @@ -862,9 +1251,9 @@ make_range (file_cache &fc, int start_line, int start_col, int end_line, int end_col) { const expanded_location start_exploc - = {"", start_line, start_col, NULL, false}; + = {"", start_line, start_col, nullptr, false}; const expanded_location finish_exploc - = {"", end_line, end_col, NULL, false}; + = {"", end_line, end_col, nullptr, false}; return layout_range (exploc_with_display_col (fc, start_exploc, def_policy (), LOCATION_ASPECT_START), @@ -875,7 +1264,7 @@ make_range (file_cache &fc, exploc_with_display_col (fc, start_exploc, def_policy (), LOCATION_ASPECT_CARET), - 0, NULL); + 0, nullptr); } /* Selftests for layout_range::contains_point and @@ -1178,8 +1567,9 @@ fixit_cmp (const void *p_a, const void *p_b) /* Callback for char_display_policy::m_print_cb for printing source chars when not escaping the source. */ +template <class Sink> static void -default_print_decoded_ch (pretty_printer *pp, +default_print_decoded_ch (Sink &sink, const cpp_decoded_char &decoded_ch) { for (const char *ptr = decoded_ch.m_start_byte; @@ -1187,11 +1577,11 @@ default_print_decoded_ch (pretty_printer *pp, { if (*ptr == '\0' || *ptr == '\r') { - pp_space (pp); + sink.add_space (); continue; } - pp_character (pp, *ptr); + sink.add_utf8_byte (*ptr); } } @@ -1219,8 +1609,9 @@ escape_as_bytes_width (cppchar_t ch) /* Callback for char_display_policy::m_print_cb for printing source chars when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */ +template <typename Sink> static void -escape_as_bytes_print (pretty_printer *pp, +escape_as_bytes_print (Sink &sink, const cpp_decoded_char &decoded_ch) { if (!decoded_ch.m_valid_ch) @@ -1230,14 +1621,14 @@ escape_as_bytes_print (pretty_printer *pp, { char buf[16]; sprintf (buf, "<%02x>", (unsigned char)*iter); - pp_string (pp, buf); + sink.add_text (buf); } return; } cppchar_t ch = decoded_ch.m_ch; if (ch < 0x80 && ISPRINT (ch)) - pp_character (pp, ch); + sink.add_character (ch); else { for (const char *iter = decoded_ch.m_start_byte; @@ -1245,7 +1636,7 @@ escape_as_bytes_print (pretty_printer *pp, { char buf[16]; sprintf (buf, "<%02x>", (unsigned char)*iter); - pp_string (pp, buf); + sink.add_text (buf); } } } @@ -1275,24 +1666,25 @@ escape_as_unicode_width (cppchar_t ch) /* Callback for char_display_policy::m_print_cb for printing source chars when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */ +template <typename Sink> static void -escape_as_unicode_print (pretty_printer *pp, +escape_as_unicode_print (Sink &sink, const cpp_decoded_char &decoded_ch) { if (!decoded_ch.m_valid_ch) { - escape_as_bytes_print (pp, decoded_ch); + escape_as_bytes_print<Sink> (sink, decoded_ch); return; } cppchar_t ch = decoded_ch.m_ch; if (ch < 0x80 && ISPRINT (ch)) - pp_character (pp, ch); + sink.add_character (ch); else { char buf[16]; sprintf (buf, "<U+%04X>", ch); - pp_string (pp, buf); + sink.add_text (buf); } } @@ -1306,7 +1698,8 @@ make_char_policy (const diagnostic_source_print_policy &source_policy, char_display_policy result (source_policy.get_column_policy ().get_tabstop (), cpp_wcwidth, - default_print_decoded_ch); + default_print_decoded_ch<to_text>, + default_print_decoded_ch<to_html>); /* If the diagnostic suggests escaping non-ASCII bytes, then use policy from user-supplied options. */ @@ -1319,11 +1712,13 @@ make_char_policy (const diagnostic_source_print_policy &source_policy, gcc_unreachable (); case DIAGNOSTICS_ESCAPE_FORMAT_UNICODE: result.m_width_cb = escape_as_unicode_width; - result.m_print_cb = escape_as_unicode_print; + result.m_print_text_cb = escape_as_unicode_print<to_text>; + result.m_print_html_cb = escape_as_unicode_print<to_html>; break; case DIAGNOSTICS_ESCAPE_FORMAT_BYTES: result.m_width_cb = escape_as_bytes_width; - result.m_print_cb = escape_as_bytes_print; + result.m_print_text_cb = escape_as_bytes_print<to_text>; + result.m_print_html_cb = escape_as_bytes_print<to_html>; break; } } @@ -1530,17 +1925,32 @@ layout::will_show_line_p (linenum_type row) const /* Print a line showing a gap in the line numbers, for showing the boundary between two line spans. */ +template<> void -layout_printer::print_gap_in_line_numbering () +layout_printer<to_text>::print_gap_in_line_numbering () { gcc_assert (m_layout.m_options.show_line_numbers_p); - pp_emit_prefix (&m_pp); + m_sink.emit_text_prefix (); for (int i = 0; i < m_layout.get_linenum_width () + 1; i++) - pp_character (&m_pp, '.'); + m_sink.add_character ('.'); - pp_newline (&m_pp); + m_sink.add_newline (); +} + +template<> +void +layout_printer<to_html>::print_gap_in_line_numbering () +{ + gcc_assert (m_layout.m_options.show_line_numbers_p); + + m_sink.add_raw_html + ("<tbody class=\"line-span-jump\">\n" + "<tr class=\"line-span-jump-row\">" + "<td class=\"linenum-gap\">[...]</td>" + "<td class=\"source-gap\"/></tr>\n" + "</tbody>\n"); } /* Return true iff we should print a heading when starting the @@ -1854,21 +2264,32 @@ layout::calculate_x_offset_display () colorization and tab expansion, this function tracks the line position in both byte and display column units. */ +template<typename Sink> line_bounds -layout_printer::print_source_line (linenum_type row, const char *line, int line_bytes) +layout_printer<Sink>::print_source_line (linenum_type row, + const char *line, + int line_bytes) { - m_colorizer.set_normal_text (); - - pp_emit_prefix (&m_pp); + m_sink.colorize_text_ensure_normal (); + m_sink.push_html_tag ("tr", true); + m_sink.emit_text_prefix (); if (m_layout.m_options.show_line_numbers_p) { + m_sink.push_html_tag_with_class ("td", "linenum", true); int width = num_digits (row); for (int i = 0; i < m_layout.get_linenum_width () - width; i++) - pp_space (&m_pp); - pp_printf (&m_pp, "%i |", row); + m_sink.add_space (); + char buf[20]; + sprintf (buf, "%i", row); + m_sink.add_text (buf); + if (Sink::is_text ()) + m_sink.add_text (" |"); + m_sink.pop_html_tag ("td"); } + m_sink.push_html_tag_with_class ("td", "left-margin", true); print_leftmost_column (); + m_sink.pop_html_tag ("td"); /* We will stop printing the source line at any trailing whitespace. */ line_bytes = get_line_bytes_without_trailing_whitespace (line, @@ -1879,6 +2300,8 @@ layout_printer::print_source_line (linenum_type row, const char *line, int line_ tab expansion, and for implementing m_x_offset_display. */ cpp_display_width_computation dw (line, line_bytes, m_layout.m_char_policy); + m_sink.push_html_tag_with_class ("td", "source", true); + /* Skip the first m_x_offset_display display columns. In case the leading portion that will be skipped ends with a character with wcwidth > 1, then it is possible we skipped too much, so account for that by padding with @@ -1889,7 +2312,7 @@ layout_printer::print_source_line (linenum_type row, const char *line, int line_ for (int skipped_display_cols = dw.advance_display_cols (m_layout.m_x_offset_display); skipped_display_cols > m_layout.m_x_offset_display; --skipped_display_cols) - pp_space (&m_pp); + m_sink.add_space (); /* Print the line and compute the line_bounds. */ line_bounds lbounds; @@ -1917,9 +2340,9 @@ layout_printer::print_source_line (linenum_type row, const char *line, int line_ CU_BYTES, &state); if (in_range_p) - m_colorizer.set_range (state.range_idx); + set_in_range (state.range_idx); else - m_colorizer.set_normal_text (); + set_outside_range (); } /* Get the display width of the next character to be output, expanding @@ -1933,7 +2356,7 @@ layout_printer::print_source_line (linenum_type row, const char *line, int line_ /* The returned display width is the number of spaces into which the tab should be expanded. */ for (int i = 0; i != this_display_width; ++i) - pp_space (&m_pp); + m_sink.add_space (); continue; } @@ -1947,10 +2370,11 @@ layout_printer::print_source_line (linenum_type row, const char *line, int line_ } /* Output the character. */ - m_layout.m_char_policy.m_print_cb (&m_pp, cp); + m_sink.print_decoded_char (m_layout.m_char_policy, cp); c = dw.next_byte (); } - print_newline (); + set_outside_range (); + end_line (); return lbounds; } @@ -1975,8 +2399,9 @@ layout::should_print_annotation_line_p (linenum_type row) const /* Print the leftmost column after the margin, which is used for showing links between labels (e.g. for CFG edges in execution paths). */ +template<typename Sink> void -layout_printer::print_leftmost_column () +layout_printer<Sink>::print_leftmost_column () { if (!get_options ().show_event_links_p) gcc_assert (m_link_lhs_state == link_lhs_state::none); @@ -1986,76 +2411,159 @@ layout_printer::print_leftmost_column () default: gcc_unreachable (); case link_lhs_state::none: - pp_space (&m_pp); + m_sink.add_space (); break; case link_lhs_state::rewinding_to_lhs: { - m_colorizer.set_cfg_edge (); + m_sink.colorize_text_for_cfg_edge (); const cppchar_t ch = get_theme ().get_cppchar (text_art::theme::cell_kind::CFG_FROM_LEFT_TO_DOWN); - pp_unicode_character (&m_pp, ch); - m_colorizer.set_normal_text (); + m_sink.add_character (ch); + m_sink.colorize_text_ensure_normal (); } break; case link_lhs_state::at_lhs: { - m_colorizer.set_cfg_edge (); + m_sink.colorize_text_for_cfg_edge (); const cppchar_t ch = get_theme ().get_cppchar (text_art::theme::cell_kind::CFG_DOWN); - pp_unicode_character (&m_pp, ch); - m_colorizer.set_normal_text (); + m_sink.add_character (ch); + m_sink.colorize_text_ensure_normal (); } break; case link_lhs_state::indenting_to_dest: { - m_colorizer.set_cfg_edge (); + m_sink.colorize_text_for_cfg_edge (); const cppchar_t ch = get_theme ().get_cppchar (text_art::theme::cell_kind::CFG_FROM_DOWN_TO_RIGHT); - pp_unicode_character (&m_pp, ch); - m_colorizer.set_normal_text (); + m_sink.add_character (ch); + m_sink.colorize_text_ensure_normal (); } break; } } -/* Begin an annotation line. If m_show_line_numbers_p, print the left - margin, which is empty for annotation lines. +/* Begin an annotation line for either text or html output + + If m_show_line_numbers_p, print the left margin, which is empty + for annotation lines. After any left margin, print a leftmost column, which is used for - showing links between labels (e.g. for CFG edges in execution paths). */ + showing links between labels (e.g. for CFG edges in execution paths). + + For text sinks, this also first prints the text prefix. + For html sinks, this also pushes <tr> and <td> open tags, where the + <td> is for the coming annotations. */ +template<typename Sink> void -layout_printer::start_annotation_line (char margin_char) +layout_printer<Sink>::start_annotation_line (enum margin_kind margin) { - pp_emit_prefix (&m_pp); + m_sink.emit_text_prefix (); + m_sink.push_html_tag ("tr", true); + + char margin_char = (margin == margin_kind::insertion + ? '+' + : ' '); + if (get_options ().show_line_numbers_p) { /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3 of it, right-aligned, padded with spaces. */ + m_sink.push_html_tag_with_class ("td", "linenum", true); int i; for (i = 0; i < m_layout.m_linenum_width - 3; i++) - pp_space (&m_pp); + m_sink.add_space (); for (; i < m_layout.m_linenum_width; i++) - pp_character (&m_pp, margin_char); - pp_string (&m_pp, " |"); + m_sink.add_character (margin_char); + if (Sink::is_text ()) + m_sink.add_text (" |"); + m_sink.pop_html_tag ("td"); } - if (margin_char == ' ') + + m_sink.push_html_tag_with_class ("td", "left-margin", true); + if (margin == margin_kind::insertion) + m_sink.add_character (margin_char); + else print_leftmost_column (); + m_sink.pop_html_tag ("td"); + + m_sink.push_html_tag_with_class ("td", + (margin == margin_kind::ruler + ? "ruler" + : "annotation"), + true); +} + +/* End a source or annotation line: text implementation. + Reset any colorization and emit a newline. */ + +template<> +void +layout_printer<to_text>::end_line () +{ + m_sink.colorize_text_ensure_normal (); + m_sink.add_newline (); +} + +/* End a source or annotation line: HTML implementation. + Close the <td> and <tr> tags. */ + +template<> +void +layout_printer<to_html>::end_line () +{ + m_sink.pop_html_tag ("td"); + m_sink.pop_html_tag ("tr"); +} + +/* Handle the various transitions between being-in-range and + not-being-in-a-range, and between ranges. */ + +template<typename Sink> +void +layout_printer<Sink>::set_in_range (int range_idx) +{ + if (m_was_in_range_p) + { + if (m_last_range_idx != range_idx) + { + /* transition between ranges. */ + end_label (m_last_range_idx, false); + begin_label (range_idx, false); + } + } else - pp_character (&m_pp, margin_char); + { + /* transition from "not in a range" to "in a range". */ + begin_label (range_idx, false); + m_was_in_range_p = true; + } + m_last_range_idx = range_idx; +} + +template<typename Sink> +void +layout_printer<Sink>::set_outside_range () +{ + if (m_was_in_range_p) + /* transition from "in a range" to "not in a range". */ + end_label (m_last_range_idx, false); + m_was_in_range_p = false; } /* Print a line consisting of the caret/underlines for the given source line. */ +template<typename Sink> void -layout_printer::print_annotation_line (linenum_type row, - const line_bounds lbounds) +layout_printer<Sink>::print_annotation_line (linenum_type row, + const line_bounds lbounds) { int x_bound = m_layout.get_x_bound_for_row (row, m_layout.m_exploc.m_display_col, lbounds.m_last_non_ws_disp_col); - start_annotation_line (); + start_annotation_line (margin_kind::normal); for (int column = 1 + m_layout.m_x_offset_display; column < x_bound; column++) { @@ -2067,9 +2575,13 @@ layout_printer::print_annotation_line (linenum_type row, CU_DISPLAY_COLS, &state); if (in_range_p) + set_in_range (state.range_idx); + else + set_outside_range (); + + if (in_range_p) { /* Within a range. Draw either the caret or an underline. */ - m_colorizer.set_range (state.range_idx); if (state.draw_caret_p) { /* Draw the caret. */ @@ -2078,19 +2590,21 @@ layout_printer::print_annotation_line (linenum_type row, caret_char = get_options ().caret_chars[state.range_idx]; else caret_char = '^'; - pp_character (&m_pp, caret_char); + m_sink.add_character (caret_char); } else - pp_character (&m_pp, '~'); + m_sink.add_character ('~'); } else { /* Not in a range. */ - m_colorizer.set_normal_text (); - pp_character (&m_pp, ' '); + m_sink.add_character (' '); } } - print_newline (); + + set_outside_range (); + + end_line (); } /* A version of label_text that can live inside a vec. @@ -2099,7 +2613,7 @@ layout_printer::print_annotation_line (linenum_type row, struct pod_label_text { pod_label_text () - : m_buffer (NULL), m_caller_owned (false) + : m_buffer (nullptr), m_caller_owned (false) {} pod_label_text (label_text &&other) @@ -2126,11 +2640,13 @@ struct pod_label_text class line_label { public: - line_label (int state_idx, int column, + line_label (unsigned original_range_idx, + int state_idx, int column, label_text text, bool has_in_edge, bool has_out_edge) - : m_state_idx (state_idx), m_column (column), + : m_original_range_idx (original_range_idx), + m_state_idx (state_idx), m_column (column), m_text (std::move (text)), m_label_line (0), m_has_vbar (true), m_has_in_edge (has_in_edge), m_has_out_edge (has_out_edge) @@ -2158,6 +2674,7 @@ public: return -compare (ll1->m_state_idx, ll2->m_state_idx); } + unsigned m_original_range_idx; int m_state_idx; int m_column; pod_label_text m_text; @@ -2168,9 +2685,66 @@ public: bool m_has_out_edge; }; +/* Implementations of layout_printer::{begin,end}_label for + to_text and to_html. + + RANGE_IDX is the index of the range within the rich_location. + + IS_LABEL_TEXT is true for the text of the label, + false when quoting the source code, underlining the source + code, and for the vertical bars connecting the underlines + to the text of the label. */ + +template<> +void +layout_printer<to_text>::begin_label (int range_idx, + bool is_label_text) +{ + /* Colorize the text, unless it's for labels for events in a + diagnostic_path. */ + if (is_label_text && m_is_diagnostic_path) + return; + + gcc_assert (m_sink.m_colorizer); + m_sink.m_colorizer->set_range (range_idx); +} + +template<> +void +layout_printer<to_html>::begin_label (int range_idx, + bool is_label_text) +{ + if (is_label_text && m_sink.m_html_label_writer) + m_sink.m_html_label_writer->begin_label (); + + if (const char *highlight_color + = m_sink.get_highlight_color_for_range_idx (range_idx)) + m_sink.m_xp.push_tag_with_class ("span", highlight_color); +} + +template<> +void +layout_printer<to_text>::end_label (int, bool) +{ + m_sink.colorize_text_ensure_normal (); +} + +template<> +void +layout_printer<to_html>::end_label (int range_idx, + bool is_label_text) +{ + if (m_sink.get_highlight_color_for_range_idx (range_idx)) + m_sink.m_xp.pop_tag ("span"); + + if (is_label_text && m_sink.m_html_label_writer) + m_sink.m_html_label_writer->end_label (); +} + /* Print any labels in this row. */ +template <typename Sink> void -layout_printer::print_any_labels (linenum_type row) +layout_printer<Sink>::print_any_labels (linenum_type row) { int i; auto_vec<line_label> labels; @@ -2181,7 +2755,7 @@ layout_printer::print_any_labels (linenum_type row) FOR_EACH_VEC_ELT (m_layout.m_layout_ranges, i, range) { /* Most ranges don't have labels, so reject this first. */ - if (range->m_label == NULL) + if (range->m_label == nullptr) continue; /* The range's caret must be on this line. */ @@ -2197,13 +2771,14 @@ layout_printer::print_any_labels (linenum_type row) label_text text; text = range->m_label->get_text (range->m_original_idx); - /* Allow for labels that return NULL from their get_text + /* Allow for labels that return nullptr from their get_text implementation (so e.g. such labels can control their own visibility). */ - if (text.get () == NULL) + if (text.get () == nullptr) continue; - labels.safe_push (line_label (i, disp_col, std::move (text), + labels.safe_push (line_label (range->m_original_idx, + i, disp_col, std::move (text), range->has_in_edge (), range->has_out_edge ())); } @@ -2288,7 +2863,7 @@ layout_printer::print_any_labels (linenum_type row) gcc_assert (get_options ().show_event_links_p); m_link_lhs_state = link_lhs_state::indenting_to_dest; } - start_annotation_line (); + start_annotation_line (margin_kind::normal); int column = 1 + m_layout.m_x_offset_display; line_label *label; @@ -2310,32 +2885,31 @@ layout_printer::print_any_labels (linenum_type row) . ^~~~~~~~~~~~~ . this text. */ gcc_assert (get_options ().show_event_links_p); - m_colorizer.set_cfg_edge (); + m_sink.colorize_text_for_cfg_edge (); const cppchar_t right= get_theme ().get_cppchar (text_art::theme::cell_kind::CFG_RIGHT); while (column < label->m_column - 1) { - pp_unicode_character (&m_pp, right); + m_sink.add_character (right); column++; } if (column == label->m_column - 1) { - pp_character (&m_pp, '>'); + m_sink.add_character ('>'); column++; } - m_colorizer.set_normal_text (); + m_sink.colorize_text_ensure_normal (); m_link_lhs_state = link_lhs_state::none; label_line_with_in_edge = -1; } else move_to_column (&column, label->m_column, true); gcc_assert (column == label->m_column); - /* Colorize the text, unless it's for events in a - diagnostic_path. */ - if (!m_is_diagnostic_path) - m_colorizer.set_range (label->m_state_idx); - pp_string (&m_pp, label->m_text.m_buffer); - m_colorizer.set_normal_text (); + + begin_label (label->m_state_idx, true); + m_sink.add_text (label->m_text.m_buffer); + end_label (label->m_state_idx, true); + column += label->m_display_width; if (get_options ().show_event_links_p && label->m_has_out_edge) { @@ -2350,13 +2924,13 @@ layout_printer::print_any_labels (linenum_type row) (text_art::theme::cell_kind::CFG_RIGHT); const cppchar_t from_right_to_down= get_theme ().get_cppchar (text_art::theme::cell_kind::CFG_FROM_RIGHT_TO_DOWN); - m_colorizer.set_cfg_edge (); - pp_space (&m_pp); - pp_unicode_character (&m_pp, right); - pp_unicode_character (&m_pp, '>'); - pp_unicode_character (&m_pp, right); - pp_unicode_character (&m_pp, from_right_to_down); - m_colorizer.set_normal_text (); + m_sink.colorize_text_for_cfg_edge (); + m_sink.add_space (); + m_sink.add_character (right); + m_sink.add_character ('>'); + m_sink.add_character (right); + m_sink.add_character (from_right_to_down); + m_sink.colorize_text_ensure_normal (); column += 5; m_link_rhs_column = column - 1; } @@ -2365,9 +2939,9 @@ layout_printer::print_any_labels (linenum_type row) { gcc_assert (column <= label->m_column); move_to_column (&column, label->m_column, true); - m_colorizer.set_range (label->m_state_idx); - pp_character (&m_pp, '|'); - m_colorizer.set_normal_text (); + begin_label (label->m_state_idx, false); + m_sink.add_character ('|'); + end_label (label->m_state_idx, false); column++; } } @@ -2377,14 +2951,14 @@ layout_printer::print_any_labels (linenum_type row) if (m_link_rhs_column != -1 && column < m_link_rhs_column) { move_to_column (&column, m_link_rhs_column, true); - m_colorizer.set_cfg_edge (); + m_sink.colorize_text_for_cfg_edge (); const cppchar_t down= get_theme ().get_cppchar (text_art::theme::cell_kind::CFG_DOWN); - pp_unicode_character (&m_pp, down); - m_colorizer.set_normal_text (); + m_sink.add_character (down); + m_sink.colorize_text_ensure_normal (); } - print_newline (); + end_line (); } } @@ -2393,14 +2967,13 @@ layout_printer::print_any_labels (linenum_type row) if (m_link_rhs_column != -1) { int column = 1 + m_layout.m_x_offset_display; - start_annotation_line (); + start_annotation_line (margin_kind::normal); move_to_column (&column, m_link_rhs_column, true); - m_colorizer.set_cfg_edge (); + m_sink.colorize_text_for_cfg_edge (); const cppchar_t down= get_theme ().get_cppchar (text_art::theme::cell_kind::CFG_DOWN); - pp_unicode_character (&m_pp, down); - m_colorizer.set_normal_text (); - print_newline (); + m_sink.add_character (down); + end_line (); } /* Clean up. */ @@ -2417,8 +2990,9 @@ layout_printer::print_any_labels (linenum_type row) They are printed on lines of their own, before the source line itself, with a leading '+'. */ +template <typename Sink> void -layout_printer::print_leading_fixits (linenum_type row) +layout_printer<Sink>::print_leading_fixits (linenum_type row) { for (unsigned int i = 0; i < m_layout.m_fixit_hints.length (); i++) { @@ -2438,16 +3012,15 @@ layout_printer::print_leading_fixits (linenum_type row) and the inserted line with "insert" colorization helps them stand out from each other, and from the surrounding text. */ - m_colorizer.set_normal_text (); - start_annotation_line ('+'); - m_colorizer.set_fixit_insert (); + m_sink.colorize_text_ensure_normal (); + start_annotation_line (margin_kind::insertion); + m_sink.colorize_text_for_fixit_insert (); /* Print all but the trailing newline of the fix-it hint. We have to print the newline separately to avoid getting additional pp prefixes printed. */ for (size_t i = 0; i < hint->get_length () - 1; i++) - pp_character (&m_pp, hint->get_string ()[i]); - m_colorizer.set_normal_text (); - pp_newline (&m_pp); + m_sink.add_character (hint->get_string ()[i]); + end_line (); } } } @@ -2877,9 +3450,12 @@ line_corrections::add_hint (const fixit_hint *hint) Fix-it hints that insert new lines are handled separately, in layout::print_leading_fixits. */ +template<typename Sink> void -layout_printer::print_trailing_fixits (linenum_type row) +layout_printer<Sink>::print_trailing_fixits (linenum_type row) { + typename Sink::auto_check_tag_nesting sentinel (m_sink); + /* Build a list of correction instances for the line, potentially consolidating hints (for the sake of readability). */ line_corrections corrections (m_layout.m_file_cache, m_layout.m_char_policy, @@ -2904,7 +3480,7 @@ layout_printer::print_trailing_fixits (linenum_type row) int column = 1 + m_layout.m_x_offset_display; if (!corrections.m_corrections.is_empty ()) - start_annotation_line (); + start_annotation_line (margin_kind::normal); FOR_EACH_VEC_ELT (corrections.m_corrections, i, c) { @@ -2914,9 +3490,9 @@ layout_printer::print_trailing_fixits (linenum_type row) /* This assumes the insertion just affects one line. */ int start_column = c->m_printed_columns.start; move_to_column (&column, start_column, true); - m_colorizer.set_fixit_insert (); - pp_string (&m_pp, c->m_text); - m_colorizer.set_normal_text (); + m_sink.colorize_text_for_fixit_insert (); + m_sink.add_text (c->m_text); + m_sink.colorize_text_ensure_normal (); column += c->m_display_cols; } else @@ -2932,10 +3508,10 @@ layout_printer::print_trailing_fixits (linenum_type row) || c->m_byte_length == 0) { move_to_column (&column, start_column, true); - m_colorizer.set_fixit_delete (); + m_sink.colorize_text_for_fixit_delete (); for (; column <= finish_column; column++) - pp_character (&m_pp, '-'); - m_colorizer.set_normal_text (); + m_sink.add_character ('-'); + m_sink.colorize_text_ensure_normal (); } /* Print the replacement text. REPLACE also covers removals, so only do this extra work (potentially starting @@ -2943,9 +3519,9 @@ layout_printer::print_trailing_fixits (linenum_type row) if (c->m_byte_length > 0) { move_to_column (&column, start_column, true); - m_colorizer.set_fixit_insert (); - pp_string (&m_pp, c->m_text); - m_colorizer.set_normal_text (); + m_sink.colorize_text_for_fixit_insert (); + m_sink.add_text (c->m_text); + m_sink.colorize_text_ensure_normal (); column += c->m_display_cols; } } @@ -2955,15 +3531,6 @@ layout_printer::print_trailing_fixits (linenum_type row) move_to_column (&column, 1 + m_layout.m_x_offset_display, false); } -/* Disable any colorization and emit a newline. */ - -void -layout_printer::print_newline () -{ - m_colorizer.set_normal_text (); - pp_newline (&m_pp); -} - /* Return true if (ROW/COLUMN) is within a range of the layout. If it returns true, OUT_STATE is written to, with the range index, and whether we should draw the caret at @@ -3060,27 +3627,28 @@ layout::get_x_bound_for_row (linenum_type row, int caret_column, and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty) left margin after any newline. */ +template<typename Sink> void -layout_printer::move_to_column (int *column, - int dest_column, - bool add_left_margin) +layout_printer<Sink>::move_to_column (int *column, + int dest_column, + bool add_left_margin) { /* Start a new line if we need to. */ if (*column > dest_column) { - print_newline (); + end_line (); if (add_left_margin) - start_annotation_line (); + start_annotation_line (margin_kind::normal); *column = 1 + m_layout.m_x_offset_display; } while (*column < dest_column) { /* For debugging column issues, it can be helpful to replace this - pp_space call with - pp_character (&m_pp, '0' + (*column % 10)); + add_space call with + m_sink.add_character ('0' + (*column % 10)); to visualize the changing value of "*column". */ - pp_space (&m_pp); + m_sink.add_space (); (*column)++; } } @@ -3088,50 +3656,58 @@ layout_printer::move_to_column (int *column, /* For debugging layout issues, render a ruler giving column numbers (after the 1-column indent). */ +template<typename Sink> void -layout_printer::show_ruler (int max_column) +layout_printer<Sink>::show_ruler (int max_column) { + m_sink.push_html_tag_with_class("thead", "ruler", false); + /* Hundreds. */ if (max_column > 99) { - start_annotation_line (); + start_annotation_line (margin_kind::ruler); for (int column = 1 + m_layout.m_x_offset_display; column <= max_column; ++column) if (column % 10 == 0) - pp_character (&m_pp, '0' + (column / 100) % 10); + m_sink.add_character ('0' + (column / 100) % 10); else - pp_space (&m_pp); - pp_newline (&m_pp); + m_sink.add_space (); + end_line (); } /* Tens. */ - start_annotation_line (); + start_annotation_line (margin_kind::ruler); for (int column = 1 + m_layout.m_x_offset_display; column <= max_column; ++column) if (column % 10 == 0) - pp_character (&m_pp, '0' + (column / 10) % 10); + m_sink.add_character ('0' + (column / 10) % 10); else - pp_space (&m_pp); - pp_newline (&m_pp); + m_sink.add_space (); + end_line (); /* Units. */ - start_annotation_line (); + start_annotation_line (margin_kind::ruler); for (int column = 1 + m_layout.m_x_offset_display; column <= max_column; ++column) - pp_character (&m_pp, '0' + (column % 10)); - pp_newline (&m_pp); + m_sink.add_character ('0' + (column % 10)); + end_line (); + + m_sink.pop_html_tag("thead"); // thead } /* Print leading fix-its (for new lines inserted before the source line) then the source line, followed by an annotation line consisting of any caret/underlines, then any fixits. If the source line can't be read, print nothing. */ +template<typename Sink> void -layout_printer::print_line (linenum_type row) +layout_printer<Sink>::print_line (linenum_type row) { + typename Sink::auto_check_tag_nesting sentinel (m_sink); + char_span line = m_layout.m_file_cache.get_source_line (m_layout.m_exploc.file, row); if (!line) @@ -3154,8 +3730,9 @@ layout_printer::print_line (linenum_type row) showing the link entering at the top right and emerging at the bottom left. */ +template<typename Sink> void -layout_printer::print_any_right_to_left_edge_lines () +layout_printer<Sink>::print_any_right_to_left_edge_lines () { if (m_link_rhs_column == -1) /* Can also happen if the out-edge had UNKNOWN_LOCATION. */ @@ -3164,31 +3741,30 @@ layout_printer::print_any_right_to_left_edge_lines () gcc_assert (get_options ().show_event_links_p); /* Print the line with "|". */ - start_annotation_line (); + start_annotation_line (margin_kind::normal); + int column = 1 + m_layout.m_x_offset_display; move_to_column (&column, m_link_rhs_column, true); - m_colorizer.set_cfg_edge (); + m_sink.colorize_text_for_cfg_edge (); const cppchar_t down= get_theme ().get_cppchar (text_art::theme::cell_kind::CFG_DOWN); - pp_unicode_character (&m_pp, down); - m_colorizer.set_normal_text (); - pp_newline (&m_pp); + m_sink.add_character (down); + end_line (); /* Print the line with "┌──────────────────────────────────────────┘". */ m_link_lhs_state = link_lhs_state::rewinding_to_lhs; - start_annotation_line (); - m_colorizer.set_cfg_edge (); + start_annotation_line (margin_kind::normal); + m_sink.colorize_text_for_cfg_edge (); const cppchar_t left= get_theme ().get_cppchar (text_art::theme::cell_kind::CFG_LEFT); for (int column = 1 + m_layout.m_x_offset_display; column < m_link_rhs_column; ++column) - pp_unicode_character (&m_pp, left); + m_sink.add_character (left); const cppchar_t from_down_to_left = get_theme ().get_cppchar (text_art::theme::cell_kind::CFG_FROM_DOWN_TO_LEFT); - pp_unicode_character (&m_pp, from_down_to_left); - m_colorizer.set_normal_text (); - pp_newline (&m_pp); + m_sink.add_character (from_down_to_left); + end_line (); /* We now have a link line on the LHS, and no longer have one on the RHS. */ @@ -3196,14 +3772,15 @@ layout_printer::print_any_right_to_left_edge_lines () m_link_rhs_column = -1; } -layout_printer::layout_printer (pretty_printer &pp, - const layout &layout, - const rich_location &richloc, - diagnostic_t diagnostic_kind) -: m_pp (pp), +template<typename Sink> +layout_printer<Sink>::layout_printer (Sink &sink, + const layout &layout, + bool is_diagnostic_path) +: m_sink (sink), m_layout (layout), - m_colorizer (m_pp, richloc, diagnostic_kind), - m_is_diagnostic_path (diagnostic_kind == DK_DIAGNOSTIC_PATH), + m_is_diagnostic_path (is_diagnostic_path), + m_was_in_range_p (false), + m_last_range_idx (0), m_link_lhs_state (link_lhs_state::none), m_link_rhs_column (-1) { @@ -3284,7 +3861,7 @@ diagnostic_context::maybe_show_locus (const rich_location &richloc, if (loc == m_last_location && richloc.get_num_fixit_hints () == 0 && richloc.get_num_locations () == 1 - && richloc.get_range (0)->m_label == NULL) + && richloc.get_range (0)->m_label == nullptr) return; m_last_location = loc; @@ -3293,11 +3870,47 @@ diagnostic_context::maybe_show_locus (const rich_location &richloc, source_policy.print (pp, richloc, diagnostic_kind, effects); } +/* As above, but print in HTML form to XP. + If non-null, use LABEL_WRITER when writing labelled ranges. */ + +void +diagnostic_context::maybe_show_locus_as_html (const rich_location &richloc, + const diagnostic_source_printing_options &opts, + diagnostic_t diagnostic_kind, + xml::printer &xp, + diagnostic_source_effect_info *effects, + html_label_writer *label_writer) +{ + const location_t loc = richloc.get_loc (); + /* Do nothing if source-printing has been disabled. */ + if (!opts.enabled) + return; + + /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */ + if (loc <= BUILTINS_LOCATION) + return; + + /* Don't print the same source location twice in a row, unless we have + fix-it hints, or multiple locations, or a label. */ + if (loc == m_last_location + && richloc.get_num_fixit_hints () == 0 + && richloc.get_num_locations () == 1 + && richloc.get_range (0)->m_label == nullptr) + return; + + m_last_location = loc; + + diagnostic_source_print_policy source_policy (*this, opts); + source_policy.print_as_html (xp, richloc, diagnostic_kind, effects, + label_writer); +} + diagnostic_source_print_policy:: diagnostic_source_print_policy (const diagnostic_context &dc) : m_options (dc.m_source_printing), m_location_policy (dc), - m_start_span_cb (dc.m_text_callbacks.m_start_span), + m_text_start_span_cb (dc.m_text_callbacks.m_text_start_span), + m_html_start_span_cb (dc.m_text_callbacks.m_html_start_span), m_file_cache (dc.get_file_cache ()), m_diagram_theme (dc.get_diagram_theme ()), m_escape_format (dc.get_escape_format ()) @@ -3309,7 +3922,8 @@ diagnostic_source_print_policy (const diagnostic_context &dc, const diagnostic_source_printing_options &opts) : m_options (opts), m_location_policy (dc), - m_start_span_cb (dc.m_text_callbacks.m_start_span), + m_text_start_span_cb (dc.m_text_callbacks.m_text_start_span), + m_html_start_span_cb (dc.m_text_callbacks.m_html_start_span), m_file_cache (dc.get_file_cache ()), m_diagram_theme (dc.get_diagram_theme ()), m_escape_format (dc.get_escape_format ()) @@ -3329,15 +3943,39 @@ diagnostic_source_print_policy::print (pretty_printer &pp, const { layout layout (*this, richloc, effects); - layout_printer lp (pp, layout, richloc, diagnostic_kind); + colorizer col (pp, richloc, diagnostic_kind); + to_text sink (pp, col); + layout_printer<to_text> lp (sink, layout, + diagnostic_kind == DK_DIAGNOSTIC_PATH); lp.print (*this); } +/* As above, but print in HTML form to XP. + If non-null, use LABEL_WRITER when writing labelled ranges. */ + void -layout_printer::print (const diagnostic_source_print_policy &source_policy) +diagnostic_source_print_policy::print_as_html (xml::printer &xp, + const rich_location &richloc, + diagnostic_t diagnostic_kind, + diagnostic_source_effect_info *effects, + html_label_writer *label_writer) + const { - diagnostic_prefixing_rule_t saved_rule = pp_prefixing_rule (&m_pp); - pp_prefixing_rule (&m_pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE; + layout layout (*this, richloc, effects); + to_html sink (xp, &richloc, label_writer); + layout_printer<to_html> lp (sink, layout, + diagnostic_kind == DK_DIAGNOSTIC_PATH); + xml::auto_check_tag_nesting sentinel (xp); + lp.print (*this); +} + +template <typename Sink> +void +layout_printer<Sink>::print (const diagnostic_source_print_policy &source_policy) +{ + typename Sink::auto_check_tag_nesting sentinel (m_sink); + + m_sink.push_html_tag_with_class ("table", "locus", false); if (get_options ().show_ruler_p) show_ruler (m_layout.m_x_offset_display + get_options ().max_width); @@ -3362,27 +4000,65 @@ layout_printer::print (const diagnostic_source_print_policy &source_policy) = m_layout.get_expanded_location (line_span); const diagnostic_location_print_policy & loc_policy = source_policy.get_location_policy (); - source_policy.get_start_span_fn () (loc_policy, &m_pp, exploc); + m_sink.invoke_start_span_fn (source_policy, loc_policy, exploc); } } + + m_sink.push_html_tag_with_class ("tbody", "line-span", false); + /* Iterate over the lines within this span (using linenum_arith_t to avoid overflow with 0xffffffff causing an infinite loop). */ linenum_arith_t last_line = line_span->get_last_line (); for (linenum_arith_t row = line_span->get_first_line (); row <= last_line; row++) print_line (row); + + m_sink.pop_html_tag ("tbody"); } if (auto effect_info = m_layout.m_effect_info) effect_info->m_trailing_out_edge_column = m_link_rhs_column; - pp_prefixing_rule (&m_pp) = saved_rule; + m_sink.pop_html_tag ("table"); } #if CHECKING_P namespace selftest { +static std::unique_ptr<xml::node> +make_element_for_locus (const rich_location &rich_loc, + diagnostic_t kind, + diagnostic_context &dc) +{ + dc.m_last_location = UNKNOWN_LOCATION; + + xml::element wrapper ("wrapper", false); + xml::printer xp (wrapper); + dc.maybe_show_locus_as_html (rich_loc, + dc.m_source_printing, + kind, + xp, + nullptr, + nullptr); // label_writer + if (wrapper.m_children.size () > 0) + return std::move (wrapper.m_children[0]); + else + return nullptr; +} + +static label_text +make_raw_html_for_locus (const rich_location &rich_loc, + diagnostic_t kind, + diagnostic_context &dc) +{ + auto node = make_element_for_locus (rich_loc, kind, dc); + pretty_printer pp; + if (node) + node->write_as_xml (&pp, 0, true); + return label_text::take (xstrdup (pp_formatted_text (&pp))); +} + /* Selftests for diagnostic_show_locus. */ diagnostic_show_locus_fixture:: @@ -3600,7 +4276,10 @@ test_layout_x_offset_display_utf8 (const line_table_case &case_) linemap_position_for_column (line_table, emoji_col)); layout test_layout (policy, richloc, nullptr); - layout_printer lp (*dc.get_reference_printer (), test_layout, richloc, DK_ERROR); + colorizer col (*dc.get_reference_printer (), + richloc, DK_ERROR); + to_text sink (*dc.get_reference_printer (), col); + layout_printer<to_text> lp (sink, test_layout, false); lp.print (policy); ASSERT_STREQ (" | 1 \n" " | 1 \n" @@ -3627,7 +4306,10 @@ test_layout_x_offset_display_utf8 (const line_table_case &case_) linemap_position_for_column (line_table, emoji_col + 2)); layout test_layout (dc, richloc, nullptr); - layout_printer lp (*dc.get_reference_printer (), test_layout, richloc, DK_ERROR); + colorizer col (*dc.get_reference_printer (), + richloc, DK_ERROR); + to_text sink (*dc.get_reference_printer (), col); + layout_printer<to_text> lp (sink, test_layout, false); lp.print (policy); ASSERT_STREQ (" | 1 1 \n" " | 1 2 \n" @@ -3708,14 +4390,17 @@ test_layout_x_offset_display_tab (const line_table_case &case_) dc.m_tabstop = tabstop; diagnostic_source_print_policy policy (dc); layout test_layout (policy, richloc, nullptr); - layout_printer lp (*dc.get_reference_printer (), test_layout, richloc, DK_ERROR); + colorizer col (*dc.get_reference_printer (), + richloc, DK_ERROR); + to_text sink (*dc.get_reference_printer (), col); + layout_printer<to_text> lp (sink, test_layout, false); lp.print (policy); const char *out = pp_formatted_text (dc.get_reference_printer ()); - ASSERT_EQ (NULL, strchr (out, '\t')); + ASSERT_EQ (nullptr, strchr (out, '\t')); const char *left_quote = strchr (out, '`'); const char *right_quote = strchr (out, '\''); - ASSERT_NE (NULL, left_quote); - ASSERT_NE (NULL, right_quote); + ASSERT_NE (nullptr, left_quote); + ASSERT_NE (nullptr, right_quote); ASSERT_EQ (right_quote - left_quote, extra_width[tabstop] + 2); } @@ -3733,7 +4418,10 @@ test_layout_x_offset_display_tab (const line_table_case &case_) dc.m_source_printing.show_line_numbers_p = true; diagnostic_source_print_policy policy (dc); layout test_layout (policy, richloc, nullptr); - layout_printer lp (*dc.get_reference_printer (), test_layout, richloc, DK_ERROR); + colorizer col (*dc.get_reference_printer (), + richloc, DK_ERROR); + to_text sink (*dc.get_reference_printer (), col); + layout_printer<to_text> lp (sink, test_layout, false); lp.print (policy); /* We have arranged things so that two columns will be printed before @@ -3814,6 +4502,32 @@ test_one_liner_caret_and_range () ASSERT_STREQ (" foo = bar.field;\n" " ~~~^~~~~~\n", dc.test_show_locus (richloc)); + + { + test_diagnostic_context dc; + auto out = make_raw_html_for_locus (richloc, DK_ERROR, dc); + ASSERT_STREQ + ("<table class=\"locus\">\n" + " <tbody class=\"line-span\">\n" + " <tr><td class=\"left-margin\"> </td><td class=\"source\">foo = bar.field;</td></tr>\n" + " <tr><td class=\"left-margin\"> </td><td class=\"annotation\"> ~~~^~~~~~</td></tr>\n" + " </tbody>\n" + "</table>\n", + out.get ()); + } + { + test_diagnostic_context dc; + dc.m_source_printing.show_line_numbers_p = true; + auto out = make_raw_html_for_locus (richloc, DK_ERROR, dc); + ASSERT_STREQ + ("<table class=\"locus\">\n" + " <tbody class=\"line-span\">\n" + " <tr><td class=\"linenum\"> 1</td><td class=\"left-margin\"> </td><td class=\"source\">foo = bar.field;</td></tr>\n" + " <tr><td class=\"linenum\"> </td><td class=\"left-margin\"> </td><td class=\"annotation\"> ~~~^~~~~~</td></tr>\n" + " </tbody>\n" + "</table>\n", + out.get ()); + } } /* Multiple ranges and carets. */ @@ -4231,6 +4945,24 @@ test_one_liner_labels () " | label 1\n" " label 0\n", dc.test_show_locus (richloc)); + + { + test_diagnostic_context dc; + dc.m_source_printing.show_line_numbers_p = true; + auto out = make_raw_html_for_locus (richloc, DK_ERROR, dc); + ASSERT_STREQ + ("<table class=\"locus\">\n" + " <tbody class=\"line-span\">\n" + " <tr><td class=\"linenum\"> 1</td><td class=\"left-margin\"> </td><td class=\"source\">foo = bar.field;</td></tr>\n" + " <tr><td class=\"linenum\"> </td><td class=\"left-margin\"> </td><td class=\"annotation\">^~~ ~~~ ~~~~~</td></tr>\n" + " <tr><td class=\"linenum\"> </td><td class=\"left-margin\"> </td><td class=\"annotation\">| | |</td></tr>\n" + " <tr><td class=\"linenum\"> </td><td class=\"left-margin\"> </td><td class=\"annotation\">| | label 2</td></tr>\n" + " <tr><td class=\"linenum\"> </td><td class=\"left-margin\"> </td><td class=\"annotation\">| label 1</td></tr>\n" + " <tr><td class=\"linenum\"> </td><td class=\"left-margin\"> </td><td class=\"annotation\">label 0</td></tr>\n" + " </tbody>\n" + "</table>\n", + out.get ()); + } } /* Example of boundary conditions: label 0 and 1 have just enough clearance, @@ -4329,10 +5061,10 @@ test_one_liner_labels () dc.test_show_locus (richloc)); } - /* Verify that a NULL result from range_label::get_text is + /* Verify that a nullptr result from range_label::get_text is handled gracefully. */ { - text_range_label label (NULL); + text_range_label label (nullptr); gcc_rich_location richloc (bar, &label, nullptr); test_diagnostic_context dc; |