mirror of
https://gcc.gnu.org/git/gcc.git
synced 2026-02-21 19:35:28 -05:00
Existing text output in GCC has to be implemented by writing sequentially to a pretty_printer instance. This makes it hard to implement some kinds of diagnostic output (see e.g. diagnostic-show-locus.cc). This patch adds more flexible ways of creating text output: - a canvas class, which can be "painted" to via random-access (rather that sequentially) - a table class for 2D grid layout, supporting items that span multiple rows/columns - a widget class for organizing diagrams hierarchically. The patch also expands GCC's diagnostics subsystem so that diagnostics can have "text art" diagrams - think ASCII art, but potentially including some Unicode characters, such as box-drawing chars. The new code is in a new "gcc/text-art" subdirectory and "text_art" namespace. The patch adds a new "-fdiagnostics-text-art-charset=VAL" option, with values: - "none": don't emit diagrams (added to -fdiagnostics-plain-output) - "ascii": use pure ASCII in diagrams - "unicode": allow for conservative use of unicode drawing characters (such as box-drawing characters). - "emoji" (the default): as "unicode", but potentially allow for conservative use of emoji in the output (such as U+26A0 WARNING SIGN). I made it possible to disable emoji separately from unicode as I believe there's a generation gap in acceptance of these characters (some older programmers have a visceral reaction against them, whereas younger programmers may have no problem with them). Diagrams are emitted to stderr by default. With SARIF output they are captured as a location in "relatedLocations", with the diagram as a code block in Markdown within a "markdown" property of a message. This patch doesn't add any such diagram usage to GCC, saving that for followups, apart from adding a plugin to the test suite to exercise the functionality. contrib/ChangeLog: * unicode/gen-box-drawing-chars.py: New file. * unicode/gen-combining-chars.py: New file. * unicode/gen-printable-chars.py: New file. gcc/ChangeLog: * Makefile.in (OBJS-libcommon): Add text-art/box-drawing.o, text-art/canvas.o, text-art/ruler.o, text-art/selftests.o, text-art/style.o, text-art/styled-string.o, text-art/table.o, text-art/theme.o, and text-art/widget.o. * color-macros.h (COLOR_FG_BRIGHT_BLACK): New. (COLOR_FG_BRIGHT_RED): New. (COLOR_FG_BRIGHT_GREEN): New. (COLOR_FG_BRIGHT_YELLOW): New. (COLOR_FG_BRIGHT_BLUE): New. (COLOR_FG_BRIGHT_MAGENTA): New. (COLOR_FG_BRIGHT_CYAN): New. (COLOR_FG_BRIGHT_WHITE): New. (COLOR_BG_BRIGHT_BLACK): New. (COLOR_BG_BRIGHT_RED): New. (COLOR_BG_BRIGHT_GREEN): New. (COLOR_BG_BRIGHT_YELLOW): New. (COLOR_BG_BRIGHT_BLUE): New. (COLOR_BG_BRIGHT_MAGENTA): New. (COLOR_BG_BRIGHT_CYAN): New. (COLOR_BG_BRIGHT_WHITE): New. * common.opt (fdiagnostics-text-art-charset=): New option. (diagnostic-text-art.h): New SourceInclude. (diagnostic_text_art_charset) New Enum and EnumValues. * configure: Regenerate. * configure.ac (gccdepdir): Add text-art to loop. * diagnostic-diagram.h: New file. * diagnostic-format-json.cc (json_emit_diagram): New. (diagnostic_output_format_init_json): Wire it up to context->m_diagrams.m_emission_cb. * diagnostic-format-sarif.cc: Include "diagnostic-diagram.h" and "text-art/canvas.h". (sarif_result::on_nested_diagnostic): Move code to... (sarif_result::add_related_location): ...this new function. (sarif_result::on_diagram): New. (sarif_builder::emit_diagram): New. (sarif_builder::make_message_object_for_diagram): New. (sarif_emit_diagram): New. (diagnostic_output_format_init_sarif): Set context->m_diagrams.m_emission_cb to sarif_emit_diagram. * diagnostic-text-art.h: New file. * diagnostic.cc: Include "diagnostic-text-art.h", "diagnostic-diagram.h", and "text-art/theme.h". (diagnostic_initialize): Initialize context->m_diagrams and call diagnostics_text_art_charset_init. (diagnostic_finish): Clean up context->m_diagrams.m_theme. (diagnostic_emit_diagram): New. (diagnostics_text_art_charset_init): New. * diagnostic.h (text_art::theme): New forward decl. (class diagnostic_diagram): Likewise. (diagnostic_context::m_diagrams): New field. (diagnostic_emit_diagram): New decl. * doc/invoke.texi (Diagnostic Message Formatting Options): Add -fdiagnostics-text-art-charset=. (-fdiagnostics-plain-output): Add -fdiagnostics-text-art-charset=none. * gcc.cc: Include "diagnostic-text-art.h". (driver_handle_option): Handle OPT_fdiagnostics_text_art_charset_. * opts-common.cc (decode_cmdline_options_to_array): Add "-fdiagnostics-text-art-charset=none" to expanded_args for -fdiagnostics-plain-output. * opts.cc: Include "diagnostic-text-art.h". (common_handle_option): Handle OPT_fdiagnostics_text_art_charset_. * pretty-print.cc (pp_unicode_character): New. * pretty-print.h (pp_unicode_character): New decl. * selftest-run-tests.cc: Include "text-art/selftests.h". (selftest::run_tests): Call text_art_tests. * text-art/box-drawing-chars.inc: New file, generated by contrib/unicode/gen-box-drawing-chars.py. * text-art/box-drawing.cc: New file. * text-art/box-drawing.h: New file. * text-art/canvas.cc: New file. * text-art/canvas.h: New file. * text-art/ruler.cc: New file. * text-art/ruler.h: New file. * text-art/selftests.cc: New file. * text-art/selftests.h: New file. * text-art/style.cc: New file. * text-art/styled-string.cc: New file. * text-art/table.cc: New file. * text-art/table.h: New file. * text-art/theme.cc: New file. * text-art/theme.h: New file. * text-art/types.h: New file. * text-art/widget.cc: New file. * text-art/widget.h: New file. gcc/testsuite/ChangeLog: * gcc.dg/plugin/diagnostic-test-text-art-ascii-bw.c: New test. * gcc.dg/plugin/diagnostic-test-text-art-ascii-color.c: New test. * gcc.dg/plugin/diagnostic-test-text-art-none.c: New test. * gcc.dg/plugin/diagnostic-test-text-art-unicode-bw.c: New test. * gcc.dg/plugin/diagnostic-test-text-art-unicode-color.c: New test. * gcc.dg/plugin/diagnostic_plugin_test_text_art.c: New test plugin. * gcc.dg/plugin/plugin.exp (plugin_test_list): Add them. libcpp/ChangeLog: * charset.cc (get_cppchar_property): New function template, based on... (cpp_wcwidth): ...this function. Rework to use the above. Include "combining-chars.inc". (cpp_is_combining_char): New function Include "printable-chars.inc". (cpp_is_printable_char): New function * combining-chars.inc: New file, generated by contrib/unicode/gen-combining-chars.py. * include/cpplib.h (cpp_is_combining_char): New function decl. (cpp_is_printable_char): New function decl. * printable-chars.inc: New file, generated by contrib/unicode/gen-printable-chars.py. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
184 lines
5.6 KiB
C++
184 lines
5.6 KiB
C++
/* Classes for abstracting ascii vs unicode output.
|
|
Copyright (C) 2023 Free Software Foundation, Inc.
|
|
Contributed by David Malcolm <dmalcolm@redhat.com>.
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free
|
|
Software Foundation; either version 3, or (at your option) any later
|
|
version.
|
|
|
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "pretty-print.h"
|
|
#include "selftest.h"
|
|
#include "text-art/selftests.h"
|
|
#include "text-art/ruler.h"
|
|
#include "text-art/theme.h"
|
|
|
|
using namespace text_art;
|
|
|
|
/* class theme. */
|
|
|
|
void
|
|
theme::paint_y_arrow (canvas &canvas,
|
|
int canvas_x,
|
|
canvas::range_t y_range,
|
|
y_arrow_dir dir,
|
|
style::id_t style_id) const
|
|
{
|
|
int canvas_y;
|
|
int delta_y;
|
|
const canvas::cell_t head (get_cppchar (dir == y_arrow_dir::UP
|
|
? cell_kind::Y_ARROW_UP_HEAD
|
|
: cell_kind::Y_ARROW_DOWN_HEAD),
|
|
false, style_id);
|
|
const canvas::cell_t tail (get_cppchar (dir == y_arrow_dir::UP
|
|
? cell_kind::Y_ARROW_UP_TAIL
|
|
: cell_kind::Y_ARROW_DOWN_TAIL),
|
|
false, style_id);
|
|
if (dir == y_arrow_dir::UP)
|
|
{
|
|
canvas_y = y_range.get_max ();
|
|
delta_y = -1;
|
|
}
|
|
else
|
|
{
|
|
canvas_y = y_range.get_min ();
|
|
delta_y = 1;
|
|
}
|
|
for (int len = y_range.get_size (); len; len--)
|
|
{
|
|
const canvas::cell_t cell = (len > 1) ? tail : head;
|
|
canvas.paint (canvas::coord_t (canvas_x, canvas_y), cell);
|
|
canvas_y += delta_y;
|
|
}
|
|
}
|
|
|
|
/* class ascii_theme : public theme. */
|
|
|
|
canvas::cell_t
|
|
ascii_theme::get_line_art (directions line_dirs) const
|
|
{
|
|
if (line_dirs.m_up
|
|
&& line_dirs.m_down
|
|
&& !(line_dirs.m_left || line_dirs.m_right))
|
|
return canvas::cell_t ('|');
|
|
if (line_dirs.m_left
|
|
&& line_dirs.m_right
|
|
&& !(line_dirs.m_up || line_dirs.m_down))
|
|
return canvas::cell_t ('-');
|
|
if (line_dirs.m_up
|
|
|| line_dirs.m_down
|
|
|| line_dirs.m_left
|
|
|| line_dirs.m_right)
|
|
return canvas::cell_t ('+');
|
|
return canvas::cell_t (' ');
|
|
}
|
|
|
|
cppchar_t
|
|
ascii_theme::get_cppchar (enum cell_kind kind) const
|
|
{
|
|
switch (kind)
|
|
{
|
|
default:
|
|
gcc_unreachable ();
|
|
case cell_kind::X_RULER_LEFT_EDGE:
|
|
return '|';
|
|
case cell_kind::X_RULER_MIDDLE:
|
|
return '~';
|
|
case cell_kind::X_RULER_INTERNAL_EDGE:
|
|
return '|';
|
|
case cell_kind::X_RULER_CONNECTOR_TO_LABEL_BELOW:
|
|
case cell_kind::X_RULER_CONNECTOR_TO_LABEL_ABOVE:
|
|
return '+';
|
|
case cell_kind::X_RULER_RIGHT_EDGE:
|
|
return '|';
|
|
case cell_kind::X_RULER_VERTICAL_CONNECTOR:
|
|
return '|';
|
|
|
|
case cell_kind::TEXT_BORDER_HORIZONTAL:
|
|
return '-';
|
|
case cell_kind::TEXT_BORDER_VERTICAL:
|
|
return '|';
|
|
case cell_kind::TEXT_BORDER_TOP_LEFT:
|
|
case cell_kind::TEXT_BORDER_TOP_RIGHT:
|
|
case cell_kind::TEXT_BORDER_BOTTOM_LEFT:
|
|
case cell_kind::TEXT_BORDER_BOTTOM_RIGHT:
|
|
return '+';
|
|
|
|
case cell_kind::Y_ARROW_UP_HEAD: return '^';
|
|
case cell_kind::Y_ARROW_DOWN_HEAD: return 'v';
|
|
|
|
case cell_kind::Y_ARROW_UP_TAIL:
|
|
case cell_kind::Y_ARROW_DOWN_TAIL:
|
|
return '|';
|
|
}
|
|
}
|
|
|
|
/* class unicode_theme : public theme. */
|
|
|
|
canvas::cell_t
|
|
unicode_theme::get_line_art (directions line_dirs) const
|
|
{
|
|
return canvas::cell_t (get_box_drawing_char (line_dirs));
|
|
}
|
|
|
|
cppchar_t
|
|
unicode_theme::get_cppchar (enum cell_kind kind) const
|
|
{
|
|
switch (kind)
|
|
{
|
|
default:
|
|
gcc_unreachable ();
|
|
case cell_kind::X_RULER_LEFT_EDGE:
|
|
return 0x251C; /* "├": U+251C: BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
|
|
case cell_kind::X_RULER_MIDDLE:
|
|
return 0x2500; /* "─": U+2500: BOX DRAWINGS LIGHT HORIZONTAL */
|
|
case cell_kind::X_RULER_INTERNAL_EDGE:
|
|
return 0x253C; /* "┼": U+253C: BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
|
|
case cell_kind::X_RULER_CONNECTOR_TO_LABEL_BELOW:
|
|
return 0x252C; /* "┬": U+252C: BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
|
|
case cell_kind::X_RULER_CONNECTOR_TO_LABEL_ABOVE:
|
|
return 0x2534; /* "┴": U+2534: BOX DRAWINGS LIGHT UP AND HORIZONTAL */
|
|
case cell_kind::X_RULER_RIGHT_EDGE:
|
|
return 0x2524; /* "┤": U+2524: BOX DRAWINGS LIGHT VERTICAL AND LEFT */
|
|
case cell_kind::X_RULER_VERTICAL_CONNECTOR:
|
|
return 0x2502; /* "│": U+2502: BOX DRAWINGS LIGHT VERTICAL */
|
|
|
|
case cell_kind::TEXT_BORDER_HORIZONTAL:
|
|
return 0x2500; /* "─": U+2500: BOX DRAWINGS LIGHT HORIZONTAL */
|
|
case cell_kind::TEXT_BORDER_VERTICAL:
|
|
return 0x2502; /* "│": U+2502: BOX DRAWINGS LIGHT VERTICAL */
|
|
|
|
/* Round corners. */
|
|
case cell_kind::TEXT_BORDER_TOP_LEFT:
|
|
return 0x256D; /* "╭": U+256D BOX DRAWINGS LIGHT ARC DOWN AND RIGHT. */
|
|
case cell_kind::TEXT_BORDER_TOP_RIGHT:
|
|
return 0x256E; /* "╮": U+256E BOX DRAWINGS LIGHT ARC DOWN AND LEFT. */
|
|
case cell_kind::TEXT_BORDER_BOTTOM_LEFT:
|
|
return 0x2570; /* "╰": U+2570 BOX DRAWINGS LIGHT ARC UP AND RIGHT. */
|
|
case cell_kind::TEXT_BORDER_BOTTOM_RIGHT:
|
|
return 0x256F; /* "╯": U+256F BOX DRAWINGS LIGHT ARC UP AND LEFT. */
|
|
|
|
case cell_kind::Y_ARROW_UP_HEAD:
|
|
return '^';
|
|
case cell_kind::Y_ARROW_DOWN_HEAD:
|
|
return 'v';
|
|
case cell_kind::Y_ARROW_UP_TAIL:
|
|
case cell_kind::Y_ARROW_DOWN_TAIL:
|
|
return 0x2502; /* "│": U+2502: BOX DRAWINGS LIGHT VERTICAL */
|
|
}
|
|
}
|