mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-11-26 21:33:59 +08:00
diagnostics: add support for "text art" diagrams
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>
This commit is contained in:
parent
985d6480fe
commit
4f01ae3761
94
contrib/unicode/gen-box-drawing-chars.py
Executable file
94
contrib/unicode/gen-box-drawing-chars.py
Executable file
@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Script to generate gcc/text-art/box-drawing-chars.inc
|
||||
#
|
||||
# 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/>. */
|
||||
|
||||
import unicodedata
|
||||
|
||||
def get_box_drawing_char_name(up: bool,
|
||||
down: bool,
|
||||
left: bool,
|
||||
right: bool) -> str:
|
||||
if 0:
|
||||
print(f'{locals()=}')
|
||||
if up and down:
|
||||
vertical = True
|
||||
up = False
|
||||
down = False
|
||||
else:
|
||||
vertical = False
|
||||
|
||||
if left and right:
|
||||
horizontal = True
|
||||
left = False
|
||||
right = False
|
||||
else:
|
||||
horizontal = False
|
||||
|
||||
weights = []
|
||||
heavy = []
|
||||
light = []
|
||||
dirs = []
|
||||
for dir_name in ('up', 'down', 'vertical', 'left', 'right', 'horizontal'):
|
||||
val = locals()[dir_name]
|
||||
if val:
|
||||
dirs.append(dir_name.upper())
|
||||
|
||||
if not dirs:
|
||||
return 'SPACE'
|
||||
|
||||
name = 'BOX DRAWINGS'
|
||||
#print(f'{light=} {heavy=}')
|
||||
|
||||
if 0:
|
||||
print(dirs)
|
||||
|
||||
def weights_frag(weight: str, dirs: list, prefix: bool):
|
||||
"""
|
||||
Generate a fragment where all directions share the same weight, e.g.:
|
||||
'HEAVY HORIZONTAL'
|
||||
'DOWN LIGHT'
|
||||
'LEFT DOWN HEAVY'
|
||||
'HEAVY DOWN AND RIGHT'
|
||||
"""
|
||||
assert len(dirs) >= 1
|
||||
assert len(dirs) <= 2
|
||||
if prefix:
|
||||
return f' {weight} ' + (' AND '.join(dirs))
|
||||
else:
|
||||
return ' ' + (' '.join(dirs)) + f' {weight}'
|
||||
|
||||
assert(len(dirs) >= 1 and len(dirs) <= 2)
|
||||
name += weights_frag('LIGHT', dirs, True)
|
||||
|
||||
return name
|
||||
|
||||
print('/* Generated by contrib/unicode/gen-box-drawing-chars.py. */')
|
||||
print()
|
||||
for i in range(16):
|
||||
up = (i & 8)
|
||||
down = (i & 4)
|
||||
left = (i & 2)
|
||||
right = (i & 1)
|
||||
name = get_box_drawing_char_name(up, down, left, right)
|
||||
if i < 15:
|
||||
trailing_comma = ','
|
||||
else:
|
||||
trailing_comma = ' '
|
||||
unichar = unicodedata.lookup(name)
|
||||
print(f'0x{ord(unichar):04X}{trailing_comma} /* "{unichar}": U+{ord(unichar):04X}: {name} */')
|
75
contrib/unicode/gen-combining-chars.py
Executable file
75
contrib/unicode/gen-combining-chars.py
Executable file
@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Script to generate libcpp/combining-chars.inc
|
||||
#
|
||||
# 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/>. */
|
||||
|
||||
from pprint import pprint
|
||||
import unicodedata
|
||||
|
||||
def is_combining_char(code_point) -> bool:
|
||||
return unicodedata.combining(chr(code_point)) != 0
|
||||
|
||||
class Range:
|
||||
def __init__(self, start, end, value):
|
||||
self.start = start
|
||||
self.end = end
|
||||
self.value = value
|
||||
|
||||
def __repr__(self):
|
||||
return f'Range({self.start:x}, {self.end:x}, {self.value})'
|
||||
|
||||
def make_ranges(value_callback):
|
||||
ranges = []
|
||||
for code_point in range(0x10FFFF):
|
||||
value = is_combining_char(code_point)
|
||||
if 0:
|
||||
print(f'{code_point=:x} {value=}')
|
||||
if ranges and ranges[-1].value == value:
|
||||
# Extend current range
|
||||
ranges[-1].end = code_point
|
||||
else:
|
||||
# Start a new range
|
||||
ranges.append(Range(code_point, code_point, value))
|
||||
return ranges
|
||||
|
||||
ranges = make_ranges(is_combining_char)
|
||||
if 0:
|
||||
pprint(ranges)
|
||||
|
||||
print(f"/* Generated by contrib/unicode/gen-combining-chars.py")
|
||||
print(f" using version {unicodedata.unidata_version}"
|
||||
" of the Unicode standard. */")
|
||||
print("\nstatic const cppchar_t combining_range_ends[] = {", end="")
|
||||
for i, r in enumerate(ranges):
|
||||
if i % 8:
|
||||
print(" ", end="")
|
||||
else:
|
||||
print("\n ", end="")
|
||||
print("0x%x," % r.end, end="")
|
||||
print("\n};\n")
|
||||
print("static const bool is_combining[] = {", end="")
|
||||
for i, r in enumerate(ranges):
|
||||
if i % 24:
|
||||
print(" ", end="")
|
||||
else:
|
||||
print("\n ", end="")
|
||||
if r.value:
|
||||
print("1,", end="")
|
||||
else:
|
||||
print("0,", end="")
|
||||
print("\n};")
|
77
contrib/unicode/gen-printable-chars.py
Executable file
77
contrib/unicode/gen-printable-chars.py
Executable file
@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Script to generate libcpp/printable-chars.inc
|
||||
#
|
||||
# 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/>. */
|
||||
|
||||
from pprint import pprint
|
||||
import unicodedata
|
||||
|
||||
def is_printable_char(code_point) -> bool:
|
||||
category = unicodedata.category(chr(code_point))
|
||||
# "Cc" is "control" and "Cf" is "format"
|
||||
return category[0] != 'C'
|
||||
|
||||
class Range:
|
||||
def __init__(self, start, end, value):
|
||||
self.start = start
|
||||
self.end = end
|
||||
self.value = value
|
||||
|
||||
def __repr__(self):
|
||||
return f'Range({self.start:x}, {self.end:x}, {self.value})'
|
||||
|
||||
def make_ranges(value_callback):
|
||||
ranges = []
|
||||
for code_point in range(0x10FFFF):
|
||||
value = is_printable_char(code_point)
|
||||
if 0:
|
||||
print(f'{code_point=:x} {value=}')
|
||||
if ranges and ranges[-1].value == value:
|
||||
# Extend current range
|
||||
ranges[-1].end = code_point
|
||||
else:
|
||||
# Start a new range
|
||||
ranges.append(Range(code_point, code_point, value))
|
||||
return ranges
|
||||
|
||||
ranges = make_ranges(is_printable_char)
|
||||
if 0:
|
||||
pprint(ranges)
|
||||
|
||||
print(f"/* Generated by contrib/unicode/gen-printable-chars.py")
|
||||
print(f" using version {unicodedata.unidata_version}"
|
||||
" of the Unicode standard. */")
|
||||
print("\nstatic const cppchar_t printable_range_ends[] = {", end="")
|
||||
for i, r in enumerate(ranges):
|
||||
if i % 8:
|
||||
print(" ", end="")
|
||||
else:
|
||||
print("\n ", end="")
|
||||
print("0x%x," % r.end, end="")
|
||||
print("\n};\n")
|
||||
print("static const bool is_printable[] = {", end="")
|
||||
for i, r in enumerate(ranges):
|
||||
if i % 24:
|
||||
print(" ", end="")
|
||||
else:
|
||||
print("\n ", end="")
|
||||
if r.value:
|
||||
print("1,", end="")
|
||||
else:
|
||||
print("0,", end="")
|
||||
print("\n};")
|
@ -1788,7 +1788,16 @@ OBJS-libcommon = diagnostic-spec.o diagnostic.o diagnostic-color.o \
|
||||
json.o \
|
||||
sbitmap.o \
|
||||
vec.o input.o hash-table.o ggc-none.o memory-block.o \
|
||||
selftest.o selftest-diagnostic.o sort.o
|
||||
selftest.o selftest-diagnostic.o sort.o \
|
||||
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 \
|
||||
text-art/widget.o
|
||||
|
||||
# Objects in libcommon-target.a, used by drivers and by the core
|
||||
# compiler and containing target-dependent code.
|
||||
|
@ -92,6 +92,14 @@ along with GCC; see the file COPYING3. If not see
|
||||
#define COLOR_FG_MAGENTA "35"
|
||||
#define COLOR_FG_CYAN "36"
|
||||
#define COLOR_FG_WHITE "37"
|
||||
#define COLOR_FG_BRIGHT_BLACK "90"
|
||||
#define COLOR_FG_BRIGHT_RED "91"
|
||||
#define COLOR_FG_BRIGHT_GREEN "92"
|
||||
#define COLOR_FG_BRIGHT_YELLOW "93"
|
||||
#define COLOR_FG_BRIGHT_BLUE "94"
|
||||
#define COLOR_FG_BRIGHT_MAGENTA "95"
|
||||
#define COLOR_FG_BRIGHT_CYAN "96"
|
||||
#define COLOR_FG_BRIGHT_WHITE "97"
|
||||
#define COLOR_BG_BLACK "40"
|
||||
#define COLOR_BG_RED "41"
|
||||
#define COLOR_BG_GREEN "42"
|
||||
@ -100,6 +108,14 @@ along with GCC; see the file COPYING3. If not see
|
||||
#define COLOR_BG_MAGENTA "45"
|
||||
#define COLOR_BG_CYAN "46"
|
||||
#define COLOR_BG_WHITE "47"
|
||||
#define COLOR_BG_BRIGHT_BLACK "100"
|
||||
#define COLOR_BG_BRIGHT_RED "101"
|
||||
#define COLOR_BG_BRIGHT_GREEN "102"
|
||||
#define COLOR_BG_BRIGHT_YELLOW "103"
|
||||
#define COLOR_BG_BRIGHT_BLUE "104"
|
||||
#define COLOR_BG_BRIGHT_MAGENTA "105"
|
||||
#define COLOR_BG_BRIGHT_CYAN "106"
|
||||
#define COLOR_BG_BRIGHT_WHITE "107"
|
||||
#define SGR_START "\33["
|
||||
#define SGR_END "m\33[K"
|
||||
#define SGR_SEQ(str) SGR_START str SGR_END
|
||||
|
@ -1502,6 +1502,29 @@ fdiagnostics-show-path-depths
|
||||
Common Var(flag_diagnostics_show_path_depths) Init(0)
|
||||
Show stack depths of events in paths.
|
||||
|
||||
fdiagnostics-text-art-charset=
|
||||
Driver Common Joined RejectNegative Var(flag_diagnostics_text_art_charset) Enum(diagnostic_text_art_charset) Init(DIAGNOSTICS_TEXT_ART_CHARSET_EMOJI)
|
||||
-fdiagnostics-text-art-charset=[none|ascii|unicode|emoji] Determine which characters to use in text arg diagrams.
|
||||
|
||||
; Required for these enum values.
|
||||
SourceInclude
|
||||
diagnostic-text-art.h
|
||||
|
||||
Enum
|
||||
Name(diagnostic_text_art_charset) Type(int)
|
||||
|
||||
EnumValue
|
||||
Enum(diagnostic_text_art_charset) String(none) Value(DIAGNOSTICS_TEXT_ART_CHARSET_NONE)
|
||||
|
||||
EnumValue
|
||||
Enum(diagnostic_text_art_charset) String(ascii) Value(DIAGNOSTICS_TEXT_ART_CHARSET_ASCII)
|
||||
|
||||
EnumValue
|
||||
Enum(diagnostic_text_art_charset) String(unicode) Value(DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE)
|
||||
|
||||
EnumValue
|
||||
Enum(diagnostic_text_art_charset) String(emoji) Value(DIAGNOSTICS_TEXT_ART_CHARSET_EMOJI)
|
||||
|
||||
fdiagnostics-minimum-margin-width=
|
||||
Common Joined UInteger Var(diagnostics_minimum_margin_width) Init(6)
|
||||
Set minimum width of left margin of source code when showing source.
|
||||
|
2
gcc/configure
vendored
2
gcc/configure
vendored
@ -34009,7 +34009,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;}
|
||||
"depdir":C) $SHELL $ac_aux_dir/mkinstalldirs $DEPDIR ;;
|
||||
"gccdepdir":C)
|
||||
${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs build/$DEPDIR
|
||||
for lang in $subdirs c-family common analyzer rtl-ssa
|
||||
for lang in $subdirs c-family common analyzer text-art rtl-ssa
|
||||
do
|
||||
${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs $lang/$DEPDIR
|
||||
done ;;
|
||||
|
@ -1382,7 +1382,7 @@ AC_CHECK_HEADERS(ext/hash_map)
|
||||
ZW_CREATE_DEPDIR
|
||||
AC_CONFIG_COMMANDS([gccdepdir],[
|
||||
${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs build/$DEPDIR
|
||||
for lang in $subdirs c-family common analyzer rtl-ssa
|
||||
for lang in $subdirs c-family common analyzer text-art rtl-ssa
|
||||
do
|
||||
${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs $lang/$DEPDIR
|
||||
done], [subdirs="$subdirs" ac_aux_dir=$ac_aux_dir DEPDIR=$DEPDIR])
|
||||
|
51
gcc/diagnostic-diagram.h
Normal file
51
gcc/diagnostic-diagram.h
Normal file
@ -0,0 +1,51 @@
|
||||
/* Support for diagrams within diagnostics.
|
||||
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/>. */
|
||||
|
||||
#ifndef GCC_DIAGNOSTIC_DIAGRAM_H
|
||||
#define GCC_DIAGNOSTIC_DIAGRAM_H
|
||||
|
||||
namespace text_art
|
||||
{
|
||||
class canvas;
|
||||
} // namespace text_art
|
||||
|
||||
/* A text art diagram, along with an "alternative text" string
|
||||
describing it. */
|
||||
|
||||
class diagnostic_diagram
|
||||
{
|
||||
public:
|
||||
diagnostic_diagram (const text_art::canvas &canvas,
|
||||
const char *alt_text)
|
||||
: m_canvas (canvas),
|
||||
m_alt_text (alt_text)
|
||||
{
|
||||
gcc_assert (alt_text);
|
||||
}
|
||||
|
||||
const text_art::canvas &get_canvas () const { return m_canvas; }
|
||||
const char *get_alt_text () const { return m_alt_text; }
|
||||
|
||||
private:
|
||||
const text_art::canvas &m_canvas;
|
||||
const char *const m_alt_text;
|
||||
};
|
||||
|
||||
#endif /* ! GCC_DIAGNOSTIC_DIAGRAM_H */
|
@ -324,6 +324,15 @@ json_file_final_cb (diagnostic_context *)
|
||||
free (filename);
|
||||
}
|
||||
|
||||
/* Callback for diagnostic_context::m_diagrams.m_emission_cb. */
|
||||
|
||||
static void
|
||||
json_emit_diagram (diagnostic_context *,
|
||||
const diagnostic_diagram &)
|
||||
{
|
||||
/* No-op. */
|
||||
}
|
||||
|
||||
/* Populate CONTEXT in preparation for JSON output (either to stderr, or
|
||||
to a file). */
|
||||
|
||||
@ -340,6 +349,7 @@ diagnostic_output_format_init_json (diagnostic_context *context)
|
||||
context->begin_group_cb = json_begin_group;
|
||||
context->end_group_cb = json_end_group;
|
||||
context->print_path = NULL; /* handled in json_end_diagnostic. */
|
||||
context->m_diagrams.m_emission_cb = json_emit_diagram;
|
||||
|
||||
/* The metadata is handled in JSON format, rather than as text. */
|
||||
context->show_cwe = false;
|
||||
|
@ -29,6 +29,8 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "cpplib.h"
|
||||
#include "logical-location.h"
|
||||
#include "diagnostic-client-data-hooks.h"
|
||||
#include "diagnostic-diagram.h"
|
||||
#include "text-art/canvas.h"
|
||||
|
||||
class sarif_builder;
|
||||
|
||||
@ -66,8 +68,13 @@ public:
|
||||
diagnostic_info *diagnostic,
|
||||
diagnostic_t orig_diag_kind,
|
||||
sarif_builder *builder);
|
||||
void on_diagram (diagnostic_context *context,
|
||||
const diagnostic_diagram &diagram,
|
||||
sarif_builder *builder);
|
||||
|
||||
private:
|
||||
void add_related_location (json::object *location_obj);
|
||||
|
||||
json::array *m_related_locations_arr;
|
||||
};
|
||||
|
||||
@ -135,7 +142,8 @@ public:
|
||||
|
||||
void end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
|
||||
diagnostic_t orig_diag_kind);
|
||||
|
||||
void emit_diagram (diagnostic_context *context,
|
||||
const diagnostic_diagram &diagram);
|
||||
void end_group ();
|
||||
|
||||
void flush_to_file (FILE *outf);
|
||||
@ -144,6 +152,9 @@ public:
|
||||
json::object *make_location_object (const rich_location &rich_loc,
|
||||
const logical_location *logical_loc);
|
||||
json::object *make_message_object (const char *msg) const;
|
||||
json::object *
|
||||
make_message_object_for_diagram (diagnostic_context *context,
|
||||
const diagnostic_diagram &diagram);
|
||||
|
||||
private:
|
||||
sarif_result *make_result_object (diagnostic_context *context,
|
||||
@ -261,12 +272,6 @@ sarif_result::on_nested_diagnostic (diagnostic_context *context,
|
||||
diagnostic_t /*orig_diag_kind*/,
|
||||
sarif_builder *builder)
|
||||
{
|
||||
if (!m_related_locations_arr)
|
||||
{
|
||||
m_related_locations_arr = new json::array ();
|
||||
set ("relatedLocations", m_related_locations_arr);
|
||||
}
|
||||
|
||||
/* We don't yet generate meaningful logical locations for notes;
|
||||
sometimes these will related to current_function_decl, but
|
||||
often they won't. */
|
||||
@ -277,6 +282,39 @@ sarif_result::on_nested_diagnostic (diagnostic_context *context,
|
||||
pp_clear_output_area (context->printer);
|
||||
location_obj->set ("message", message_obj);
|
||||
|
||||
add_related_location (location_obj);
|
||||
}
|
||||
|
||||
/* Handle diagrams that occur within a diagnostic group.
|
||||
The closest thing in SARIF seems to be to add a location to the
|
||||
"releatedLocations" property (SARIF v2.1.0 section 3.27.22),
|
||||
and to put the diagram into the "message" property of that location
|
||||
(SARIF v2.1.0 section 3.28.5). */
|
||||
|
||||
void
|
||||
sarif_result::on_diagram (diagnostic_context *context,
|
||||
const diagnostic_diagram &diagram,
|
||||
sarif_builder *builder)
|
||||
{
|
||||
json::object *location_obj = new json::object ();
|
||||
json::object *message_obj
|
||||
= builder->make_message_object_for_diagram (context, diagram);
|
||||
location_obj->set ("message", message_obj);
|
||||
|
||||
add_related_location (location_obj);
|
||||
}
|
||||
|
||||
/* Add LOCATION_OBJ to this result's "relatedLocations" array,
|
||||
creating it if it doesn't yet exist. */
|
||||
|
||||
void
|
||||
sarif_result::add_related_location (json::object *location_obj)
|
||||
{
|
||||
if (!m_related_locations_arr)
|
||||
{
|
||||
m_related_locations_arr = new json::array ();
|
||||
set ("relatedLocations", m_related_locations_arr);
|
||||
}
|
||||
m_related_locations_arr->append (location_obj);
|
||||
}
|
||||
|
||||
@ -348,6 +386,18 @@ sarif_builder::end_diagnostic (diagnostic_context *context,
|
||||
}
|
||||
}
|
||||
|
||||
/* Implementation of diagnostic_context::m_diagrams.m_emission_cb
|
||||
for SARIF output. */
|
||||
|
||||
void
|
||||
sarif_builder::emit_diagram (diagnostic_context *context,
|
||||
const diagnostic_diagram &diagram)
|
||||
{
|
||||
/* We must be within the emission of a top-level diagnostic. */
|
||||
gcc_assert (m_cur_group_result);
|
||||
m_cur_group_result->on_diagram (context, diagram, this);
|
||||
}
|
||||
|
||||
/* Implementation of "end_group_cb" for SARIF output. */
|
||||
|
||||
void
|
||||
@ -1115,6 +1165,37 @@ sarif_builder::make_message_object (const char *msg) const
|
||||
return message_obj;
|
||||
}
|
||||
|
||||
/* Make a message object (SARIF v2.1.0 section 3.11) for DIAGRAM.
|
||||
We emit the diagram as a code block within the Markdown part
|
||||
of the message. */
|
||||
|
||||
json::object *
|
||||
sarif_builder::make_message_object_for_diagram (diagnostic_context *context,
|
||||
const diagnostic_diagram &diagram)
|
||||
{
|
||||
json::object *message_obj = new json::object ();
|
||||
|
||||
/* "text" property (SARIF v2.1.0 section 3.11.8). */
|
||||
message_obj->set ("text", new json::string (diagram.get_alt_text ()));
|
||||
|
||||
char *saved_prefix = pp_take_prefix (context->printer);
|
||||
pp_set_prefix (context->printer, NULL);
|
||||
|
||||
/* "To produce a code block in Markdown, simply indent every line of
|
||||
the block by at least 4 spaces or 1 tab."
|
||||
Here we use 4 spaces. */
|
||||
diagram.get_canvas ().print_to_pp (context->printer, " ");
|
||||
pp_set_prefix (context->printer, saved_prefix);
|
||||
|
||||
/* "markdown" property (SARIF v2.1.0 section 3.11.9). */
|
||||
message_obj->set ("markdown",
|
||||
new json::string (pp_formatted_text (context->printer)));
|
||||
|
||||
pp_clear_output_area (context->printer);
|
||||
|
||||
return message_obj;
|
||||
}
|
||||
|
||||
/* Make a multiformatMessageString object (SARIF v2.1.0 section 3.12)
|
||||
for MSG. */
|
||||
|
||||
@ -1630,6 +1711,16 @@ sarif_ice_handler (diagnostic_context *context)
|
||||
fnotice (stderr, "Internal compiler error:\n");
|
||||
}
|
||||
|
||||
/* Callback for diagnostic_context::m_diagrams.m_emission_cb. */
|
||||
|
||||
static void
|
||||
sarif_emit_diagram (diagnostic_context *context,
|
||||
const diagnostic_diagram &diagram)
|
||||
{
|
||||
gcc_assert (the_builder);
|
||||
the_builder->emit_diagram (context, diagram);
|
||||
}
|
||||
|
||||
/* Populate CONTEXT in preparation for SARIF output (either to stderr, or
|
||||
to a file). */
|
||||
|
||||
@ -1645,6 +1736,7 @@ diagnostic_output_format_init_sarif (diagnostic_context *context)
|
||||
context->end_group_cb = sarif_end_group;
|
||||
context->print_path = NULL; /* handled in sarif_end_diagnostic. */
|
||||
context->ice_handler_cb = sarif_ice_handler;
|
||||
context->m_diagrams.m_emission_cb = sarif_emit_diagram;
|
||||
|
||||
/* The metadata is handled in SARIF format, rather than as text. */
|
||||
context->show_cwe = false;
|
||||
|
49
gcc/diagnostic-text-art.h
Normal file
49
gcc/diagnostic-text-art.h
Normal file
@ -0,0 +1,49 @@
|
||||
/* 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/>. */
|
||||
|
||||
#ifndef GCC_DIAGNOSTIC_TEXT_ART_H
|
||||
#define GCC_DIAGNOSTIC_TEXT_ART_H
|
||||
|
||||
/* Values for -fdiagnostics-text-art-charset=. */
|
||||
|
||||
enum diagnostic_text_art_charset
|
||||
{
|
||||
/* No text art diagrams shall be emitted. */
|
||||
DIAGNOSTICS_TEXT_ART_CHARSET_NONE,
|
||||
|
||||
/* Use pure ASCII for text art diagrams. */
|
||||
DIAGNOSTICS_TEXT_ART_CHARSET_ASCII,
|
||||
|
||||
/* Use ASCII + conservative use of other unicode characters
|
||||
in text art diagrams. */
|
||||
DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE,
|
||||
|
||||
/* Use Emoji. */
|
||||
DIAGNOSTICS_TEXT_ART_CHARSET_EMOJI
|
||||
};
|
||||
|
||||
const enum diagnostic_text_art_charset DIAGNOSTICS_TEXT_ART_CHARSET_DEFAULT
|
||||
= DIAGNOSTICS_TEXT_ART_CHARSET_EMOJI;
|
||||
|
||||
extern void
|
||||
diagnostics_text_art_charset_init (diagnostic_context *context,
|
||||
enum diagnostic_text_art_charset charset);
|
||||
|
||||
|
||||
#endif /* ! GCC_DIAGNOSTIC_TEXT_ART_H */
|
@ -35,11 +35,14 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "diagnostic-metadata.h"
|
||||
#include "diagnostic-path.h"
|
||||
#include "diagnostic-client-data-hooks.h"
|
||||
#include "diagnostic-text-art.h"
|
||||
#include "diagnostic-diagram.h"
|
||||
#include "edit-context.h"
|
||||
#include "selftest.h"
|
||||
#include "selftest-diagnostic.h"
|
||||
#include "opts.h"
|
||||
#include "cpplib.h"
|
||||
#include "text-art/theme.h"
|
||||
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
# include <termios.h>
|
||||
@ -244,6 +247,10 @@ diagnostic_initialize (diagnostic_context *context, int n_opts)
|
||||
context->ice_handler_cb = NULL;
|
||||
context->includes_seen = NULL;
|
||||
context->m_client_data_hooks = NULL;
|
||||
context->m_diagrams.m_theme = NULL;
|
||||
context->m_diagrams.m_emission_cb = NULL;
|
||||
diagnostics_text_art_charset_init (context,
|
||||
DIAGNOSTICS_TEXT_ART_CHARSET_DEFAULT);
|
||||
}
|
||||
|
||||
/* Maybe initialize the color support. We require clients to do this
|
||||
@ -320,6 +327,12 @@ diagnostic_finish (diagnostic_context *context)
|
||||
if (context->final_cb)
|
||||
context->final_cb (context);
|
||||
|
||||
if (context->m_diagrams.m_theme)
|
||||
{
|
||||
delete context->m_diagrams.m_theme;
|
||||
context->m_diagrams.m_theme = NULL;
|
||||
}
|
||||
|
||||
diagnostic_file_cache_fini ();
|
||||
|
||||
XDELETEVEC (context->classify_diagnostic);
|
||||
@ -2174,6 +2187,33 @@ internal_error_no_backtrace (const char *gmsgid, ...)
|
||||
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
/* Emit DIAGRAM to CONTEXT, respecting the output format. */
|
||||
|
||||
void
|
||||
diagnostic_emit_diagram (diagnostic_context *context,
|
||||
const diagnostic_diagram &diagram)
|
||||
{
|
||||
if (context->m_diagrams.m_theme == nullptr)
|
||||
return;
|
||||
|
||||
if (context->m_diagrams.m_emission_cb)
|
||||
{
|
||||
context->m_diagrams.m_emission_cb (context, diagram);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Default implementation. */
|
||||
char *saved_prefix = pp_take_prefix (context->printer);
|
||||
pp_set_prefix (context->printer, NULL);
|
||||
/* Use a newline before and after and a two-space indent
|
||||
to make the diagram stand out a little from the wall of text. */
|
||||
pp_newline (context->printer);
|
||||
diagram.get_canvas ().print_to_pp (context->printer, " ");
|
||||
pp_newline (context->printer);
|
||||
pp_set_prefix (context->printer, saved_prefix);
|
||||
pp_flush (context->printer);
|
||||
}
|
||||
|
||||
/* Special case error functions. Most are implemented in terms of the
|
||||
above, or should be. */
|
||||
@ -2316,6 +2356,38 @@ diagnostic_output_format_init (diagnostic_context *context,
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize CONTEXT->m_diagrams based on CHARSET.
|
||||
Specifically, make a text_art::theme object for m_diagrams.m_theme,
|
||||
(or NULL for "no diagrams"). */
|
||||
|
||||
void
|
||||
diagnostics_text_art_charset_init (diagnostic_context *context,
|
||||
enum diagnostic_text_art_charset charset)
|
||||
{
|
||||
delete context->m_diagrams.m_theme;
|
||||
switch (charset)
|
||||
{
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
|
||||
case DIAGNOSTICS_TEXT_ART_CHARSET_NONE:
|
||||
context->m_diagrams.m_theme = NULL;
|
||||
break;
|
||||
|
||||
case DIAGNOSTICS_TEXT_ART_CHARSET_ASCII:
|
||||
context->m_diagrams.m_theme = new text_art::ascii_theme ();
|
||||
break;
|
||||
|
||||
case DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE:
|
||||
context->m_diagrams.m_theme = new text_art::unicode_theme ();
|
||||
break;
|
||||
|
||||
case DIAGNOSTICS_TEXT_ART_CHARSET_EMOJI:
|
||||
context->m_diagrams.m_theme = new text_art::emoji_theme ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Implementation of diagnostic_path::num_events vfunc for
|
||||
simple_diagnostic_path: simply get the number of events in the vec. */
|
||||
|
||||
|
@ -24,6 +24,11 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "pretty-print.h"
|
||||
#include "diagnostic-core.h"
|
||||
|
||||
namespace text_art
|
||||
{
|
||||
class theme;
|
||||
} // namespace text_art
|
||||
|
||||
/* An enum for controlling what units to use for the column number
|
||||
when diagnostics are output, used by the -fdiagnostics-column-unit option.
|
||||
Tabs will be expanded or not according to the value of -ftabstop. The origin
|
||||
@ -170,6 +175,7 @@ class edit_context;
|
||||
namespace json { class value; }
|
||||
class diagnostic_client_data_hooks;
|
||||
class logical_location;
|
||||
class diagnostic_diagram;
|
||||
|
||||
/* This data structure bundles altogether any information relevant to
|
||||
the context of a diagnostic message. */
|
||||
@ -417,6 +423,18 @@ struct diagnostic_context
|
||||
Used by SARIF output to give metadata about the client that's
|
||||
producing diagnostics. */
|
||||
diagnostic_client_data_hooks *m_client_data_hooks;
|
||||
|
||||
/* Support for diagrams. */
|
||||
struct
|
||||
{
|
||||
/* Theme to use when generating diagrams.
|
||||
Can be NULL (if text art is disabled). */
|
||||
text_art::theme *m_theme;
|
||||
|
||||
/* Callback for emitting diagrams. */
|
||||
void (*m_emission_cb) (diagnostic_context *context,
|
||||
const diagnostic_diagram &diagram);
|
||||
} m_diagrams;
|
||||
};
|
||||
|
||||
inline void
|
||||
@ -619,4 +637,7 @@ extern bool warning_enabled_at (location_t, int);
|
||||
|
||||
extern char *get_cwe_url (int cwe);
|
||||
|
||||
extern void diagnostic_emit_diagram (diagnostic_context *context,
|
||||
const diagnostic_diagram &diagram);
|
||||
|
||||
#endif /* ! GCC_DIAGNOSTIC_H */
|
||||
|
@ -317,7 +317,8 @@ Objective-C and Objective-C++ Dialects}.
|
||||
-fno-show-column
|
||||
-fdiagnostics-column-unit=@r{[}display@r{|}byte@r{]}
|
||||
-fdiagnostics-column-origin=@var{origin}
|
||||
-fdiagnostics-escape-format=@r{[}unicode@r{|}bytes@r{]}}
|
||||
-fdiagnostics-escape-format=@r{[}unicode@r{|}bytes@r{]}
|
||||
-fdiagnostics-text-art-charset=@r{[}none@r{|}ascii@r{|}unicode@r{|}emoji@r{]}}
|
||||
|
||||
@item Warning Options
|
||||
@xref{Warning Options,,Options to Request or Suppress Warnings}.
|
||||
@ -5078,7 +5079,8 @@ options:
|
||||
-fno-diagnostics-show-line-numbers
|
||||
-fdiagnostics-color=never
|
||||
-fdiagnostics-urls=never
|
||||
-fdiagnostics-path-format=separate-events}
|
||||
-fdiagnostics-path-format=separate-events
|
||||
-fdiagnostics-text-art-charset=none}
|
||||
In the future, if GCC changes the default appearance of its diagnostics, the
|
||||
corresponding option to disable the new behavior will be added to this list.
|
||||
|
||||
@ -5604,6 +5606,25 @@ Unicode characters. For the example above, the following will be printed:
|
||||
before<CF><80><BF>after
|
||||
@end smallexample
|
||||
|
||||
@opindex fdiagnostics-text-art-charset
|
||||
@item -fdiagnostics-text-art-charset=@var{CHARSET}
|
||||
Some diagnostics can contain ``text art'' diagrams: visualizations created
|
||||
from text, intended to be viewed in a monospaced font.
|
||||
|
||||
This option selects which characters should be used for printing such
|
||||
diagrams, if any. @var{CHARSET} is @samp{none}, @samp{ascii}, @samp{unicode},
|
||||
or @samp{emoji}.
|
||||
|
||||
The @samp{none} value suppresses the printing of such diagrams.
|
||||
The @samp{ascii} value will ensure that such diagrams are pure ASCII
|
||||
(``ASCII art''). The @samp{unicode} value will allow for conservative use of
|
||||
unicode drawing characters (such as box-drawing characters). The @samp{emoji}
|
||||
value further adds the possibility of emoji in the output (such as emitting
|
||||
U+26A0 WARNING SIGN followed by U+FE0F VARIATION SELECTOR-16 to select the
|
||||
emoji variant of the character).
|
||||
|
||||
The default is @samp{emoji}.
|
||||
|
||||
@opindex fdiagnostics-format
|
||||
@item -fdiagnostics-format=@var{FORMAT}
|
||||
Select a different format for printing diagnostics.
|
||||
|
@ -46,6 +46,7 @@ compilation is specified by a string called a "spec". */
|
||||
#include "spellcheck.h"
|
||||
#include "opts-jobserver.h"
|
||||
#include "common/common-target.h"
|
||||
#include "diagnostic-text-art.h"
|
||||
|
||||
#ifndef MATH_LIBRARY
|
||||
#define MATH_LIBRARY "m"
|
||||
@ -4344,6 +4345,11 @@ driver_handle_option (struct gcc_options *opts,
|
||||
break;
|
||||
}
|
||||
|
||||
case OPT_fdiagnostics_text_art_charset_:
|
||||
diagnostics_text_art_charset_init (dc,
|
||||
(enum diagnostic_text_art_charset)value);
|
||||
break;
|
||||
|
||||
case OPT_Wa_:
|
||||
{
|
||||
int prev, j;
|
||||
|
@ -1068,6 +1068,7 @@ decode_cmdline_options_to_array (unsigned int argc, const char **argv,
|
||||
"-fdiagnostics-color=never",
|
||||
"-fdiagnostics-urls=never",
|
||||
"-fdiagnostics-path-format=separate-events",
|
||||
"-fdiagnostics-text-art-charset=none"
|
||||
};
|
||||
const int num_expanded = ARRAY_SIZE (expanded_args);
|
||||
opt_array_len += num_expanded - 1;
|
||||
|
@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "version.h"
|
||||
#include "selftest.h"
|
||||
#include "file-prefix-map.h"
|
||||
#include "diagnostic-text-art.h"
|
||||
|
||||
/* In this file all option sets are explicit. */
|
||||
#undef OPTION_SET_P
|
||||
@ -2887,6 +2888,11 @@ common_handle_option (struct gcc_options *opts,
|
||||
break;
|
||||
}
|
||||
|
||||
case OPT_fdiagnostics_text_art_charset_:
|
||||
diagnostics_text_art_charset_init (dc,
|
||||
(enum diagnostic_text_art_charset)value);
|
||||
break;
|
||||
|
||||
case OPT_fdiagnostics_parseable_fixits:
|
||||
dc->extra_output_kind = (value
|
||||
? EXTRA_DIAGNOSTIC_OUTPUT_fixits_v1
|
||||
|
@ -1828,6 +1828,35 @@ pp_string (pretty_printer *pp, const char *str)
|
||||
pp_maybe_wrap_text (pp, str, str + strlen (str));
|
||||
}
|
||||
|
||||
/* Append code point C to the output area of PRETTY-PRINTER, encoding it
|
||||
as UTF-8. */
|
||||
|
||||
void
|
||||
pp_unicode_character (pretty_printer *pp, unsigned c)
|
||||
{
|
||||
static const uchar masks[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
||||
static const uchar limits[6] = { 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
|
||||
size_t nbytes;
|
||||
uchar buf[6], *p = &buf[6];
|
||||
|
||||
nbytes = 1;
|
||||
if (c < 0x80)
|
||||
*--p = c;
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
*--p = ((c & 0x3F) | 0x80);
|
||||
c >>= 6;
|
||||
nbytes++;
|
||||
}
|
||||
while (c >= 0x3F || (c & limits[nbytes-1]));
|
||||
*--p = (c | masks[nbytes-1]);
|
||||
}
|
||||
|
||||
pp_append_r (pp, (const char *)p, nbytes);
|
||||
}
|
||||
|
||||
/* Append the leading N characters of STRING to the output area of
|
||||
PRETTY-PRINTER, quoting in hexadecimal non-printable characters.
|
||||
Setting N = -1 is as if N were set to strlen (STRING). The STRING
|
||||
|
@ -401,6 +401,7 @@ extern void pp_indent (pretty_printer *);
|
||||
extern void pp_newline (pretty_printer *);
|
||||
extern void pp_character (pretty_printer *, int);
|
||||
extern void pp_string (pretty_printer *, const char *);
|
||||
extern void pp_unicode_character (pretty_printer *, unsigned);
|
||||
|
||||
extern void pp_write_text_to_stream (pretty_printer *);
|
||||
extern void pp_write_text_as_dot_label_to_stream (pretty_printer *, bool);
|
||||
|
@ -28,6 +28,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "stringpool.h"
|
||||
#include "attribs.h"
|
||||
#include "analyzer/analyzer-selftests.h"
|
||||
#include "text-art/selftests.h"
|
||||
|
||||
/* This function needed to be split out from selftest.cc as it references
|
||||
tests from the whole source tree, and so is within
|
||||
@ -118,6 +119,8 @@ selftest::run_tests ()
|
||||
/* Run any lang-specific selftests. */
|
||||
lang_hooks.run_lang_selftests ();
|
||||
|
||||
text_art_tests ();
|
||||
|
||||
/* Run the analyzer selftests (if enabled). */
|
||||
ana::selftest::run_analyzer_selftests ();
|
||||
|
||||
|
@ -0,0 +1,57 @@
|
||||
/* { dg-additional-options "-fdiagnostics-text-art-charset=ascii -fdiagnostics-color=never" } */
|
||||
|
||||
int non_empty;
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
||||
A
|
||||
B
|
||||
C
|
||||
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
||||
♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜
|
||||
♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟
|
||||
|
||||
|
||||
|
||||
|
||||
♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙
|
||||
♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
|
||||
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
||||
+--+
|
||||
|🙂|
|
||||
+--+
|
||||
|
||||
{ dg-end-multiline-output "" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
||||
+-------+-----+---------------+---------------------+-----------------------+-----------------------+
|
||||
|Offsets|Octet| 0 | 1 | 2 | 3 |
|
||||
+-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
| Octet | Bit |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|
|
||||
+-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
| 0 | 0 |Version| IHL | DSCP | ECN | Total Length |
|
||||
+-------+-----+-------+-------+---------------+-----+--------+--------------------------------------+
|
||||
| 4 | 32 | Identification | Flags | Fragment Offset |
|
||||
+-------+-----+---------------+---------------------+--------+--------------------------------------+
|
||||
| 8 | 64 | Time To Live | Protocol | Header Checksum |
|
||||
+-------+-----+---------------+---------------------+-----------------------------------------------+
|
||||
| 12 | 96 | Source IP Address |
|
||||
+-------+-----+-------------------------------------------------------------------------------------+
|
||||
| 16 | 128 | Destination IP Address |
|
||||
+-------+-----+-------------------------------------------------------------------------------------+
|
||||
| 20 | 160 | |
|
||||
+-------+-----+ |
|
||||
| ... | ... | Options |
|
||||
+-------+-----+ |
|
||||
| 56 | 448 | |
|
||||
+-------+-----+-------------------------------------------------------------------------------------+
|
||||
|
||||
{ dg-end-multiline-output "" } */
|
@ -0,0 +1,58 @@
|
||||
/* { dg-additional-options "-fdiagnostics-text-art-charset=ascii -fdiagnostics-color=always" } */
|
||||
|
||||
int non_empty;
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
||||
A
|
||||
B
|
||||
C
|
||||
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
||||
[38;2;0;0;0;48;2;240;217;181m[K♜ [38;2;0;0;0;48;2;181;136;99m[K♞ [38;2;0;0;0;48;2;240;217;181m[K♝ [38;2;0;0;0;48;2;181;136;99m[K♛ [38;2;0;0;0;48;2;240;217;181m[K♚ [38;2;0;0;0;48;2;181;136;99m[K♝ [38;2;0;0;0;48;2;240;217;181m[K♞ [38;2;0;0;0;48;2;181;136;99m[K♜ [m[K
|
||||
[38;2;0;0;0;48;2;181;136;99m[K♟ [38;2;0;0;0;48;2;240;217;181m[K♟ [38;2;0;0;0;48;2;181;136;99m[K♟ [38;2;0;0;0;48;2;240;217;181m[K♟ [38;2;0;0;0;48;2;181;136;99m[K♟ [38;2;0;0;0;48;2;240;217;181m[K♟ [38;2;0;0;0;48;2;181;136;99m[K♟ [38;2;0;0;0;48;2;240;217;181m[K♟ [m[K
|
||||
[48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [m[K
|
||||
[48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [m[K
|
||||
[48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [m[K
|
||||
[48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [m[K
|
||||
[38;2;255;255;255;48;2;240;217;181m[K♙ [38;2;255;255;255;48;2;181;136;99m[K♙ [38;2;255;255;255;48;2;240;217;181m[K♙ [38;2;255;255;255;48;2;181;136;99m[K♙ [38;2;255;255;255;48;2;240;217;181m[K♙ [38;2;255;255;255;48;2;181;136;99m[K♙ [38;2;255;255;255;48;2;240;217;181m[K♙ [38;2;255;255;255;48;2;181;136;99m[K♙ [m[K
|
||||
[38;2;255;255;255;48;2;181;136;99m[K♖ [38;2;255;255;255;48;2;240;217;181m[K♘ [38;2;255;255;255;48;2;181;136;99m[K♗ [38;2;255;255;255;48;2;240;217;181m[K♕ [38;2;255;255;255;48;2;181;136;99m[K♔ [38;2;255;255;255;48;2;240;217;181m[K♗ [38;2;255;255;255;48;2;181;136;99m[K♘ [38;2;255;255;255;48;2;240;217;181m[K♖ [m[K
|
||||
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
||||
+--+
|
||||
|🙂|
|
||||
+--+
|
||||
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
||||
+-------+-----+---------------+---------------------+-----------------------+-----------------------+
|
||||
|Offsets|Octet| 0 | 1 | 2 | 3 |
|
||||
+-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
| Octet | Bit |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|
|
||||
+-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
| 0 | 0 |Version| IHL | DSCP | ECN | Total Length |
|
||||
+-------+-----+-------+-------+---------------+-----+--------+--------------------------------------+
|
||||
| 4 | 32 | Identification | Flags | Fragment Offset |
|
||||
+-------+-----+---------------+---------------------+--------+--------------------------------------+
|
||||
| 8 | 64 | Time To Live | Protocol | Header Checksum |
|
||||
+-------+-----+---------------+---------------------+-----------------------------------------------+
|
||||
| 12 | 96 | Source IP Address |
|
||||
+-------+-----+-------------------------------------------------------------------------------------+
|
||||
| 16 | 128 | Destination IP Address |
|
||||
+-------+-----+-------------------------------------------------------------------------------------+
|
||||
| 20 | 160 | |
|
||||
+-------+-----+ |
|
||||
| ... | ... | Options |
|
||||
+-------+-----+ |
|
||||
| 56 | 448 | |
|
||||
+-------+-----+-------------------------------------------------------------------------------------+
|
||||
|
||||
{ dg-end-multiline-output "" } */
|
@ -0,0 +1,5 @@
|
||||
/* { dg-additional-options "-fdiagnostics-text-art-charset=none" } */
|
||||
|
||||
int non_empty;
|
||||
|
||||
/* We expect no output. */
|
@ -0,0 +1,58 @@
|
||||
/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode -fdiagnostics-color=never" } */
|
||||
|
||||
int non_empty;
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
||||
A
|
||||
B
|
||||
C
|
||||
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
||||
♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜
|
||||
♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟
|
||||
|
||||
|
||||
|
||||
|
||||
♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙
|
||||
♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
|
||||
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
||||
┌──┐
|
||||
│🙂│
|
||||
└──┘
|
||||
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
||||
┌───────┬─────┬───────────────┬─────────────────────┬───────────────────────┬───────────────────────┐
|
||||
│Offsets│Octet│ 0 │ 1 │ 2 │ 3 │
|
||||
├───────┼─────┼─┬─┬─┬─┬─┬─┬─┬─┼─┬─┬──┬──┬──┬──┬──┬──┼──┬──┬──┬──┬──┬──┬──┬──┼──┬──┬──┬──┬──┬──┬──┬──┤
|
||||
│ Octet │ Bit │0│1│2│3│4│5│6│7│8│9│10│11│12│13│14│15│16│17│18│19│20│21│22│23│24│25│26│27│28│29│30│31│
|
||||
├───────┼─────┼─┴─┴─┴─┼─┴─┴─┴─┼─┴─┴──┴──┴──┴──┼──┴──┼──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┤
|
||||
│ 0 │ 0 │Version│ IHL │ DSCP │ ECN │ Total Length │
|
||||
├───────┼─────┼───────┴───────┴───────────────┴─────┼────────┬──────────────────────────────────────┤
|
||||
│ 4 │ 32 │ Identification │ Flags │ Fragment Offset │
|
||||
├───────┼─────┼───────────────┬─────────────────────┼────────┴──────────────────────────────────────┤
|
||||
│ 8 │ 64 │ Time To Live │ Protocol │ Header Checksum │
|
||||
├───────┼─────┼───────────────┴─────────────────────┴───────────────────────────────────────────────┤
|
||||
│ 12 │ 96 │ Source IP Address │
|
||||
├───────┼─────┼─────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ 16 │ 128 │ Destination IP Address │
|
||||
├───────┼─────┼─────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ 20 │ 160 │ │
|
||||
├───────┼─────┤ │
|
||||
│ ... │ ... │ Options │
|
||||
├───────┼─────┤ │
|
||||
│ 56 │ 448 │ │
|
||||
└───────┴─────┴─────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
{ dg-end-multiline-output "" } */
|
@ -0,0 +1,59 @@
|
||||
/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode -fdiagnostics-color=always" } */
|
||||
|
||||
int non_empty;
|
||||
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
||||
A
|
||||
B
|
||||
C
|
||||
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
||||
[38;2;0;0;0;48;2;240;217;181m[K♜ [38;2;0;0;0;48;2;181;136;99m[K♞ [38;2;0;0;0;48;2;240;217;181m[K♝ [38;2;0;0;0;48;2;181;136;99m[K♛ [38;2;0;0;0;48;2;240;217;181m[K♚ [38;2;0;0;0;48;2;181;136;99m[K♝ [38;2;0;0;0;48;2;240;217;181m[K♞ [38;2;0;0;0;48;2;181;136;99m[K♜ [m[K
|
||||
[38;2;0;0;0;48;2;181;136;99m[K♟ [38;2;0;0;0;48;2;240;217;181m[K♟ [38;2;0;0;0;48;2;181;136;99m[K♟ [38;2;0;0;0;48;2;240;217;181m[K♟ [38;2;0;0;0;48;2;181;136;99m[K♟ [38;2;0;0;0;48;2;240;217;181m[K♟ [38;2;0;0;0;48;2;181;136;99m[K♟ [38;2;0;0;0;48;2;240;217;181m[K♟ [m[K
|
||||
[48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [m[K
|
||||
[48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [m[K
|
||||
[48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [m[K
|
||||
[48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [48;2;181;136;99m[K [48;2;240;217;181m[K [m[K
|
||||
[38;2;255;255;255;48;2;240;217;181m[K♙ [38;2;255;255;255;48;2;181;136;99m[K♙ [38;2;255;255;255;48;2;240;217;181m[K♙ [38;2;255;255;255;48;2;181;136;99m[K♙ [38;2;255;255;255;48;2;240;217;181m[K♙ [38;2;255;255;255;48;2;181;136;99m[K♙ [38;2;255;255;255;48;2;240;217;181m[K♙ [38;2;255;255;255;48;2;181;136;99m[K♙ [m[K
|
||||
[38;2;255;255;255;48;2;181;136;99m[K♖ [38;2;255;255;255;48;2;240;217;181m[K♘ [38;2;255;255;255;48;2;181;136;99m[K♗ [38;2;255;255;255;48;2;240;217;181m[K♕ [38;2;255;255;255;48;2;181;136;99m[K♔ [38;2;255;255;255;48;2;240;217;181m[K♗ [38;2;255;255;255;48;2;181;136;99m[K♘ [38;2;255;255;255;48;2;240;217;181m[K♖ [m[K
|
||||
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
||||
┌──┐
|
||||
│🙂│
|
||||
└──┘
|
||||
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
|
||||
┌───────┬─────┬───────────────┬─────────────────────┬───────────────────────┬───────────────────────┐
|
||||
│Offsets│Octet│ 0 │ 1 │ 2 │ 3 │
|
||||
├───────┼─────┼─┬─┬─┬─┬─┬─┬─┬─┼─┬─┬──┬──┬──┬──┬──┬──┼──┬──┬──┬──┬──┬──┬──┬──┼──┬──┬──┬──┬──┬──┬──┬──┤
|
||||
│ Octet │ Bit │0│1│2│3│4│5│6│7│8│9│10│11│12│13│14│15│16│17│18│19│20│21│22│23│24│25│26│27│28│29│30│31│
|
||||
├───────┼─────┼─┴─┴─┴─┼─┴─┴─┴─┼─┴─┴──┴──┴──┴──┼──┴──┼──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┤
|
||||
│ 0 │ 0 │Version│ IHL │ DSCP │ ECN │ Total Length │
|
||||
├───────┼─────┼───────┴───────┴───────────────┴─────┼────────┬──────────────────────────────────────┤
|
||||
│ 4 │ 32 │ Identification │ Flags │ Fragment Offset │
|
||||
├───────┼─────┼───────────────┬─────────────────────┼────────┴──────────────────────────────────────┤
|
||||
│ 8 │ 64 │ Time To Live │ Protocol │ Header Checksum │
|
||||
├───────┼─────┼───────────────┴─────────────────────┴───────────────────────────────────────────────┤
|
||||
│ 12 │ 96 │ Source IP Address │
|
||||
├───────┼─────┼─────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ 16 │ 128 │ Destination IP Address │
|
||||
├───────┼─────┼─────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ 20 │ 160 │ │
|
||||
├───────┼─────┤ │
|
||||
│ ... │ ... │ Options │
|
||||
├───────┼─────┤ │
|
||||
│ 56 │ 448 │ │
|
||||
└───────┴─────┴─────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
{ dg-end-multiline-output "" } */
|
257
gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_text_art.c
Normal file
257
gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_text_art.c
Normal file
@ -0,0 +1,257 @@
|
||||
/* { dg-options "-O" } */
|
||||
|
||||
/* This plugin exercises the text_art code. */
|
||||
|
||||
#include "gcc-plugin.h"
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "plugin-version.h"
|
||||
#include "diagnostic.h"
|
||||
#include "diagnostic-diagram.h"
|
||||
#include "text-art/canvas.h"
|
||||
#include "text-art/table.h"
|
||||
|
||||
int plugin_is_GPL_compatible;
|
||||
|
||||
using namespace text_art;
|
||||
|
||||
/* Canvas tests. */
|
||||
|
||||
static void
|
||||
emit_canvas (const canvas &c, const char *alt_text)
|
||||
{
|
||||
diagnostic_diagram diagram (c, alt_text);
|
||||
diagnostic_emit_diagram (global_dc, diagram);
|
||||
}
|
||||
|
||||
static void
|
||||
test_abc ()
|
||||
{
|
||||
style_manager sm;
|
||||
canvas c (canvas::size_t (3, 3), sm);
|
||||
c.paint (canvas::coord_t (0, 0), styled_unichar ('A'));
|
||||
c.paint (canvas::coord_t (1, 1), styled_unichar ('B'));
|
||||
c.paint (canvas::coord_t (2, 2), styled_unichar ('C'));
|
||||
emit_canvas (c, "test_abc");
|
||||
}
|
||||
|
||||
/* Test of procedural art using 24-bit color: chess starting position. */
|
||||
|
||||
static void
|
||||
test_chessboard ()
|
||||
{
|
||||
/* With the exception of NONE, these are in order of the chess symbols
|
||||
in the Unicode Miscellaneous Symbols block. */
|
||||
enum class piece { KING, QUEEN, ROOK, BISHOP, KNIGHT, PAWN, NONE };
|
||||
enum class color { BLACK, WHITE, NONE };
|
||||
|
||||
style_manager sm;
|
||||
|
||||
/* We assume double-column chars for the pieces, so allow two canvas
|
||||
columns per square. */
|
||||
canvas canvas (canvas::size_t (16, 8), sm);
|
||||
|
||||
for (int x = 0; x < 8; x++)
|
||||
for (int y = 0; y < 8; y++)
|
||||
{
|
||||
enum piece piece_kind;
|
||||
enum color piece_color;
|
||||
switch (y)
|
||||
{
|
||||
case 0:
|
||||
case 7:
|
||||
switch (x)
|
||||
{
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
case 0:
|
||||
piece_kind = piece::ROOK;
|
||||
break;
|
||||
case 1:
|
||||
piece_kind = piece::KNIGHT;
|
||||
break;
|
||||
case 2:
|
||||
piece_kind = piece::BISHOP;
|
||||
break;
|
||||
case 3:
|
||||
piece_kind = piece::QUEEN;
|
||||
break;
|
||||
case 4:
|
||||
piece_kind = piece::KING;
|
||||
break;
|
||||
case 5:
|
||||
piece_kind = piece::BISHOP;
|
||||
break;
|
||||
case 6:
|
||||
piece_kind = piece::KNIGHT;
|
||||
break;
|
||||
case 7:
|
||||
piece_kind = piece::ROOK;
|
||||
break;
|
||||
}
|
||||
piece_color = (y == 0) ? color::BLACK : color::WHITE;
|
||||
break;
|
||||
case 1:
|
||||
case 6:
|
||||
piece_kind = piece::PAWN;
|
||||
piece_color = (y == 1) ? color::BLACK : color::WHITE;
|
||||
break;
|
||||
default:
|
||||
piece_kind = piece::NONE;
|
||||
piece_color = color::NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
style s;
|
||||
const bool white_square = (x + y) % 2 == 0;
|
||||
if (white_square)
|
||||
s.m_bg_color = style::color (0xf0, 0xd9, 0xb5);
|
||||
else
|
||||
s.m_bg_color = style::color (0xb5, 0x88, 0x63);
|
||||
switch (piece_color)
|
||||
{
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
case color::WHITE:
|
||||
s.m_fg_color = style::color (0xff, 0xff, 0xff);
|
||||
break;
|
||||
case color::BLACK:
|
||||
s.m_fg_color = style::color (0x00, 0x00, 0x00);
|
||||
break;
|
||||
case color::NONE:
|
||||
break;
|
||||
}
|
||||
style::id_t style_id = sm.get_or_create_id (s);
|
||||
|
||||
cppchar_t ch;
|
||||
if (piece_kind == piece::NONE)
|
||||
ch = ' ';
|
||||
else
|
||||
{
|
||||
const cppchar_t WHITE_KING = 0x2654;
|
||||
const cppchar_t BLACK_KING = 0x265A;
|
||||
cppchar_t base ((piece_color == color::WHITE)
|
||||
? WHITE_KING : BLACK_KING);
|
||||
ch = base + ((int)piece_kind - (int)piece::KING);
|
||||
}
|
||||
canvas.paint (canvas::coord_t (x * 2, y),
|
||||
canvas::cell_t (ch, false, style_id));
|
||||
canvas.paint (canvas::coord_t (x * 2 + 1, y),
|
||||
canvas::cell_t (' ', false, style_id));
|
||||
}
|
||||
emit_canvas (canvas, "test_chessboard");
|
||||
}
|
||||
|
||||
/* Table tests. */
|
||||
|
||||
static void
|
||||
emit_table (const table &table, const style_manager &sm, const char *alt_text)
|
||||
{
|
||||
const text_art::theme *theme = global_dc->m_diagrams.m_theme;
|
||||
if (!theme)
|
||||
return;
|
||||
canvas c (table.to_canvas (*theme, sm));
|
||||
emit_canvas (c, alt_text);
|
||||
}
|
||||
|
||||
static void
|
||||
test_double_width_chars ()
|
||||
{
|
||||
style_manager sm;
|
||||
table table (table::size_t (1, 1));
|
||||
table.set_cell (table::coord_t (0,0),
|
||||
styled_string ((cppchar_t)0x1f642));
|
||||
|
||||
emit_table (table, sm, "test_double_width_chars");
|
||||
}
|
||||
|
||||
static void
|
||||
test_ipv4_header ()
|
||||
{
|
||||
style_manager sm;
|
||||
table table (table::size_t (34, 10));
|
||||
table.set_cell (table::coord_t (0, 0), styled_string (sm, "Offsets"));
|
||||
table.set_cell (table::coord_t (1, 0), styled_string (sm, "Octet"));
|
||||
table.set_cell (table::coord_t (0, 1), styled_string (sm, "Octet"));
|
||||
for (int octet = 0; octet < 4; octet++)
|
||||
table.set_cell_span (table::rect_t (table::coord_t (2 + (octet * 8), 0),
|
||||
table::size_t (8, 1)),
|
||||
styled_string::from_fmt (sm, nullptr, "%i", octet));
|
||||
table.set_cell (table::coord_t (1, 1), styled_string (sm, "Bit"));
|
||||
for (int bit = 0; bit < 32; bit++)
|
||||
table.set_cell (table::coord_t (bit + 2, 1),
|
||||
styled_string::from_fmt (sm, nullptr, "%i", bit));
|
||||
for (int word = 0; word < 6; word++)
|
||||
{
|
||||
table.set_cell (table::coord_t (0, word + 2),
|
||||
styled_string::from_fmt (sm, nullptr, "%i", word * 4));
|
||||
table.set_cell (table::coord_t (1, word + 2),
|
||||
styled_string::from_fmt (sm, nullptr, "%i", word * 32));
|
||||
}
|
||||
|
||||
table.set_cell (table::coord_t (0, 8), styled_string (sm, "..."));
|
||||
table.set_cell (table::coord_t (1, 8), styled_string (sm, "..."));
|
||||
table.set_cell (table::coord_t (0, 9), styled_string (sm, "56"));
|
||||
table.set_cell (table::coord_t (1, 9), styled_string (sm, "448"));
|
||||
|
||||
#define SET_BITS(FIRST, LAST, NAME) \
|
||||
do { \
|
||||
const int first = (FIRST); \
|
||||
const int last = (LAST); \
|
||||
const char *name = (NAME); \
|
||||
const int row = first / 32; \
|
||||
gcc_assert (last / 32 == row); \
|
||||
table::rect_t rect (table::coord_t ((first % 32) + 2, row + 2), \
|
||||
table::size_t (last + 1 - first , 1)); \
|
||||
table.set_cell_span (rect, styled_string (sm, name)); \
|
||||
} while (0)
|
||||
|
||||
SET_BITS (0, 3, "Version");
|
||||
SET_BITS (4, 7, "IHL");
|
||||
SET_BITS (8, 13, "DSCP");
|
||||
SET_BITS (14, 15, "ECN");
|
||||
SET_BITS (16, 31, "Total Length");
|
||||
|
||||
SET_BITS (32 + 0, 32 + 15, "Identification");
|
||||
SET_BITS (32 + 16, 32 + 18, "Flags");
|
||||
SET_BITS (32 + 19, 32 + 31, "Fragment Offset");
|
||||
|
||||
SET_BITS (64 + 0, 64 + 7, "Time To Live");
|
||||
SET_BITS (64 + 8, 64 + 15, "Protocol");
|
||||
SET_BITS (64 + 16, 64 + 31, "Header Checksum");
|
||||
|
||||
SET_BITS (96 + 0, 96 + 31, "Source IP Address");
|
||||
SET_BITS (128 + 0, 128 + 31, "Destination IP Address");
|
||||
|
||||
table.set_cell_span(table::rect_t (table::coord_t (2, 7),
|
||||
table::size_t (32, 3)),
|
||||
styled_string (sm, "Options"));
|
||||
|
||||
emit_table (table, sm, "test_ipv4_header");
|
||||
}
|
||||
|
||||
static void
|
||||
show_diagrams ()
|
||||
{
|
||||
test_abc ();
|
||||
test_chessboard ();
|
||||
test_double_width_chars ();
|
||||
test_ipv4_header ();
|
||||
}
|
||||
|
||||
int
|
||||
plugin_init (struct plugin_name_args *plugin_info,
|
||||
struct plugin_gcc_version *version)
|
||||
{
|
||||
const char *plugin_name = plugin_info->base_name;
|
||||
int argc = plugin_info->argc;
|
||||
struct plugin_argument *argv = plugin_info->argv;
|
||||
|
||||
if (!plugin_default_version_check (version, &gcc_version))
|
||||
return 1;
|
||||
|
||||
show_diagrams ();
|
||||
|
||||
return 0;
|
||||
}
|
@ -114,6 +114,12 @@ set plugin_test_list [list \
|
||||
diagnostic-path-format-inline-events-1.c \
|
||||
diagnostic-path-format-inline-events-2.c \
|
||||
diagnostic-path-format-inline-events-3.c } \
|
||||
{ diagnostic_plugin_test_text_art.c \
|
||||
diagnostic-test-text-art-none.c \
|
||||
diagnostic-test-text-art-ascii-bw.c \
|
||||
diagnostic-test-text-art-ascii-color.c \
|
||||
diagnostic-test-text-art-unicode-bw.c \
|
||||
diagnostic-test-text-art-unicode-color.c } \
|
||||
{ location_overflow_plugin.c \
|
||||
location-overflow-test-1.c \
|
||||
location-overflow-test-2.c \
|
||||
|
18
gcc/text-art/box-drawing-chars.inc
Normal file
18
gcc/text-art/box-drawing-chars.inc
Normal file
@ -0,0 +1,18 @@
|
||||
/* Generated by contrib/unicode/gen-box-drawing-chars.py. */
|
||||
|
||||
0x0020, /* " ": U+0020: SPACE */
|
||||
0x2576, /* "╶": U+2576: BOX DRAWINGS LIGHT RIGHT */
|
||||
0x2574, /* "╴": U+2574: BOX DRAWINGS LIGHT LEFT */
|
||||
0x2500, /* "─": U+2500: BOX DRAWINGS LIGHT HORIZONTAL */
|
||||
0x2577, /* "╷": U+2577: BOX DRAWINGS LIGHT DOWN */
|
||||
0x250C, /* "┌": U+250C: BOX DRAWINGS LIGHT DOWN AND RIGHT */
|
||||
0x2510, /* "┐": U+2510: BOX DRAWINGS LIGHT DOWN AND LEFT */
|
||||
0x252C, /* "┬": U+252C: BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
|
||||
0x2575, /* "╵": U+2575: BOX DRAWINGS LIGHT UP */
|
||||
0x2514, /* "└": U+2514: BOX DRAWINGS LIGHT UP AND RIGHT */
|
||||
0x2518, /* "┘": U+2518: BOX DRAWINGS LIGHT UP AND LEFT */
|
||||
0x2534, /* "┴": U+2534: BOX DRAWINGS LIGHT UP AND HORIZONTAL */
|
||||
0x2502, /* "│": U+2502: BOX DRAWINGS LIGHT VERTICAL */
|
||||
0x251C, /* "├": U+251C: BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
|
||||
0x2524, /* "┤": U+2524: BOX DRAWINGS LIGHT VERTICAL AND LEFT */
|
||||
0x253C /* "┼": U+253C: BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
|
72
gcc/text-art/box-drawing.cc
Normal file
72
gcc/text-art/box-drawing.cc
Normal file
@ -0,0 +1,72 @@
|
||||
/* Procedural lookup of box drawing characters.
|
||||
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 "text-art/box-drawing.h"
|
||||
#include "selftest.h"
|
||||
#include "text-art/selftests.h"
|
||||
|
||||
|
||||
/* According to
|
||||
https://en.wikipedia.org/wiki/Box-drawing_character#Character_code
|
||||
"DOS line- and box-drawing characters are not ordered in any programmatic
|
||||
manner, so calculating a particular character shape needs to use a look-up
|
||||
table. "
|
||||
Hence this array. */
|
||||
static const cppchar_t box_drawing_chars[] = {
|
||||
#include "text-art/box-drawing-chars.inc"
|
||||
};
|
||||
|
||||
cppchar_t
|
||||
text_art::get_box_drawing_char (directions line_dirs)
|
||||
{
|
||||
const size_t idx = line_dirs.as_index ();
|
||||
gcc_assert (idx < 16);
|
||||
return box_drawing_chars[idx];
|
||||
}
|
||||
|
||||
#if CHECKING_P
|
||||
|
||||
namespace selftest {
|
||||
|
||||
/* Run all selftests in this file. */
|
||||
|
||||
void
|
||||
text_art_box_drawing_cc_tests ()
|
||||
{
|
||||
ASSERT_EQ (text_art::get_box_drawing_char
|
||||
(text_art::directions (false, false, false, false)),
|
||||
' ');
|
||||
ASSERT_EQ (text_art::get_box_drawing_char
|
||||
(text_art::directions (false, false, true, true)),
|
||||
0x2500); /* BOX DRAWINGS LIGHT HORIZONTAL */
|
||||
ASSERT_EQ (text_art::get_box_drawing_char
|
||||
(text_art::directions (true, true, false, false)),
|
||||
0x2502); /* BOX DRAWINGS LIGHT VERTICAL */
|
||||
ASSERT_EQ (text_art::get_box_drawing_char
|
||||
(text_art::directions (true, false, true, false)),
|
||||
0x2518); /* BOX DRAWINGS LIGHT UP AND LEFT */
|
||||
}
|
||||
|
||||
} // namespace selftest
|
||||
|
||||
#endif /* #if CHECKING_P */
|
32
gcc/text-art/box-drawing.h
Normal file
32
gcc/text-art/box-drawing.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* Procedural lookup of box drawing characters.
|
||||
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/>. */
|
||||
|
||||
#ifndef GCC_TEXT_ART_BOX_DRAWING_H
|
||||
#define GCC_TEXT_ART_BOX_DRAWING_H
|
||||
|
||||
#include "text-art/types.h"
|
||||
|
||||
namespace text_art {
|
||||
|
||||
extern cppchar_t get_box_drawing_char (directions line_dirs);
|
||||
|
||||
} // namespace text_art
|
||||
|
||||
#endif /* GCC_TEXT_ART_BOX_DRAWING_H */
|
437
gcc/text-art/canvas.cc
Normal file
437
gcc/text-art/canvas.cc
Normal file
@ -0,0 +1,437 @@
|
||||
/* Canvas for random-access procedural text art.
|
||||
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/canvas.h"
|
||||
|
||||
using namespace text_art;
|
||||
|
||||
canvas::canvas (size_t size, const style_manager &style_mgr)
|
||||
: m_cells (size_t (size.w, size.h)),
|
||||
m_style_mgr (style_mgr)
|
||||
{
|
||||
m_cells.fill (cell_t (' '));
|
||||
}
|
||||
|
||||
void
|
||||
canvas::paint (coord_t coord, styled_unichar ch)
|
||||
{
|
||||
m_cells.set (coord, std::move (ch));
|
||||
}
|
||||
|
||||
void
|
||||
canvas::paint_text (coord_t coord, const styled_string &text)
|
||||
{
|
||||
for (auto ch : text)
|
||||
{
|
||||
paint (coord, ch);
|
||||
if (ch.double_width_p ())
|
||||
coord.x += 2;
|
||||
else
|
||||
coord.x++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
canvas::fill (rect_t rect, cell_t c)
|
||||
{
|
||||
for (int y = rect.get_min_y (); y < rect.get_next_y (); y++)
|
||||
for (int x = rect.get_min_x (); x < rect.get_next_x (); x++)
|
||||
paint(coord_t (x, y), c);
|
||||
}
|
||||
|
||||
void
|
||||
canvas::debug_fill ()
|
||||
{
|
||||
fill (rect_t (coord_t (0, 0), get_size ()), cell_t ('*'));
|
||||
}
|
||||
|
||||
void
|
||||
canvas::print_to_pp (pretty_printer *pp,
|
||||
const char *per_line_prefix) const
|
||||
{
|
||||
for (int y = 0; y < m_cells.get_size ().h; y++)
|
||||
{
|
||||
style::id_t curr_style_id = 0;
|
||||
if (per_line_prefix)
|
||||
pp_string (pp, per_line_prefix);
|
||||
|
||||
pretty_printer line_pp;
|
||||
line_pp.show_color = pp->show_color;
|
||||
line_pp.url_format = pp->url_format;
|
||||
const int final_x_in_row = get_final_x_in_row (y);
|
||||
for (int x = 0; x <= final_x_in_row; x++)
|
||||
{
|
||||
if (x > 0)
|
||||
{
|
||||
const cell_t prev_cell = m_cells.get (coord_t (x - 1, y));
|
||||
if (prev_cell.double_width_p ())
|
||||
/* This cell is just a placeholder for the
|
||||
2nd column of a double width cell; skip it. */
|
||||
continue;
|
||||
}
|
||||
const cell_t cell = m_cells.get (coord_t (x, y));
|
||||
if (cell.get_style_id () != curr_style_id)
|
||||
{
|
||||
m_style_mgr.print_any_style_changes (&line_pp,
|
||||
curr_style_id,
|
||||
cell.get_style_id ());
|
||||
curr_style_id = cell.get_style_id ();
|
||||
}
|
||||
pp_unicode_character (&line_pp, cell.get_code ());
|
||||
if (cell.emoji_variant_p ())
|
||||
/* Append U+FE0F VARIATION SELECTOR-16 to select the emoji
|
||||
variation of the char. */
|
||||
pp_unicode_character (&line_pp, 0xFE0F);
|
||||
}
|
||||
/* Reset the style at the end of each line. */
|
||||
m_style_mgr.print_any_style_changes (&line_pp, curr_style_id, 0);
|
||||
|
||||
/* Print from line_pp to pp, stripping trailing whitespace from
|
||||
the line. */
|
||||
const char *line_buf = pp_formatted_text (&line_pp);
|
||||
::size_t len = strlen (line_buf);
|
||||
while (len > 0)
|
||||
{
|
||||
if (line_buf[len - 1] == ' ')
|
||||
len--;
|
||||
else
|
||||
break;
|
||||
}
|
||||
pp_append_text (pp, line_buf, line_buf + len);
|
||||
pp_newline (pp);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION void
|
||||
canvas::debug (bool styled) const
|
||||
{
|
||||
pretty_printer pp;
|
||||
if (styled)
|
||||
{
|
||||
pp_show_color (&pp) = true;
|
||||
pp.url_format = determine_url_format (DIAGNOSTICS_URL_AUTO);
|
||||
}
|
||||
print_to_pp (&pp);
|
||||
fprintf (stderr, "%s\n", pp_formatted_text (&pp));
|
||||
}
|
||||
|
||||
/* Find right-most non-default cell in this row,
|
||||
or -1 if all are default. */
|
||||
|
||||
int
|
||||
canvas::get_final_x_in_row (int y) const
|
||||
{
|
||||
for (int x = m_cells.get_size ().w - 1; x >= 0; x--)
|
||||
{
|
||||
cell_t cell = m_cells.get (coord_t (x, y));
|
||||
if (cell.get_code () != ' '
|
||||
|| cell.get_style_id () != style::id_plain)
|
||||
return x;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if CHECKING_P
|
||||
|
||||
namespace selftest {
|
||||
|
||||
static void
|
||||
test_blank ()
|
||||
{
|
||||
style_manager sm;
|
||||
canvas c (canvas::size_t (5, 5), sm);
|
||||
ASSERT_CANVAS_STREQ (c, false,
|
||||
("\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_abc ()
|
||||
{
|
||||
style_manager sm;
|
||||
canvas c (canvas::size_t (3, 3), sm);
|
||||
c.paint (canvas::coord_t (0, 0), styled_unichar ('A'));
|
||||
c.paint (canvas::coord_t (1, 1), styled_unichar ('B'));
|
||||
c.paint (canvas::coord_t (2, 2), styled_unichar ('C'));
|
||||
|
||||
ASSERT_CANVAS_STREQ (c, false,
|
||||
"A\n B\n C\n");
|
||||
}
|
||||
|
||||
static void
|
||||
test_debug_fill ()
|
||||
{
|
||||
style_manager sm;
|
||||
canvas c (canvas::size_t (5, 3), sm);
|
||||
c.debug_fill();
|
||||
ASSERT_CANVAS_STREQ (c, false,
|
||||
("*****\n"
|
||||
"*****\n"
|
||||
"*****\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_text ()
|
||||
{
|
||||
style_manager sm;
|
||||
canvas c (canvas::size_t (6, 1), sm);
|
||||
c.paint_text (canvas::coord_t (0, 0), styled_string (sm, "012345"));
|
||||
ASSERT_CANVAS_STREQ (c, false,
|
||||
("012345\n"));
|
||||
|
||||
/* Paint an emoji character that should occupy two canvas columns when
|
||||
printed. */
|
||||
c.paint_text (canvas::coord_t (2, 0), styled_string ((cppchar_t)0x1f642));
|
||||
ASSERT_CANVAS_STREQ (c, false,
|
||||
("01🙂45\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_circle ()
|
||||
{
|
||||
canvas::size_t sz (30, 30);
|
||||
style_manager sm;
|
||||
canvas canvas (sz, sm);
|
||||
canvas::coord_t center (sz.w / 2, sz.h / 2);
|
||||
const int radius = 12;
|
||||
const int radius_squared = radius * radius;
|
||||
for (int x = 0; x < sz.w; x++)
|
||||
for (int y = 0; y < sz.h; y++)
|
||||
{
|
||||
int dx = x - center.x;
|
||||
int dy = y - center.y;
|
||||
char ch = "AB"[(x + y) % 2];
|
||||
if (dx * dx + dy * dy < radius_squared)
|
||||
canvas.paint (canvas::coord_t (x, y), styled_unichar (ch));
|
||||
}
|
||||
ASSERT_CANVAS_STREQ
|
||||
(canvas, false,
|
||||
("\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"\n"
|
||||
" BABABABAB\n"
|
||||
" ABABABABABABA\n"
|
||||
" ABABABABABABABA\n"
|
||||
" ABABABABABABABABA\n"
|
||||
" ABABABABABABABABABA\n"
|
||||
" ABABABABABABABABABABA\n"
|
||||
" BABABABABABABABABABAB\n"
|
||||
" BABABABABABABABABABABAB\n"
|
||||
" ABABABABABABABABABABABA\n"
|
||||
" BABABABABABABABABABABAB\n"
|
||||
" ABABABABABABABABABABABA\n"
|
||||
" BABABABABABABABABABABAB\n"
|
||||
" ABABABABABABABABABABABA\n"
|
||||
" BABABABABABABABABABABAB\n"
|
||||
" ABABABABABABABABABABABA\n"
|
||||
" BABABABABABABABABABABAB\n"
|
||||
" BABABABABABABABABABAB\n"
|
||||
" ABABABABABABABABABABA\n"
|
||||
" ABABABABABABABABABA\n"
|
||||
" ABABABABABABABABA\n"
|
||||
" ABABABABABABABA\n"
|
||||
" ABABABABABABA\n"
|
||||
" BABABABAB\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_color_circle ()
|
||||
{
|
||||
const canvas::size_t sz (10, 10);
|
||||
const canvas::coord_t center (sz.w / 2, sz.h / 2);
|
||||
const int outer_r2 = 25;
|
||||
const int inner_r2 = 10;
|
||||
style_manager sm;
|
||||
canvas c (sz, sm);
|
||||
for (int x = 0; x < sz.w; x++)
|
||||
for (int y = 0; y < sz.h; y++)
|
||||
{
|
||||
const int dist_from_center_squared
|
||||
= ((x - center.x) * (x - center.x) + (y - center.y) * (y - center.y));
|
||||
if (dist_from_center_squared < outer_r2)
|
||||
{
|
||||
style s;
|
||||
if (dist_from_center_squared < inner_r2)
|
||||
s.m_fg_color = style::named_color::RED;
|
||||
else
|
||||
s.m_fg_color = style::named_color::GREEN;
|
||||
c.paint (canvas::coord_t (x, y),
|
||||
styled_unichar ('*', false, sm.get_or_create_id (s)));
|
||||
}
|
||||
}
|
||||
ASSERT_EQ (sm.get_num_styles (), 3);
|
||||
ASSERT_CANVAS_STREQ
|
||||
(c, false,
|
||||
("\n"
|
||||
" *****\n"
|
||||
" *******\n"
|
||||
" *********\n"
|
||||
" *********\n"
|
||||
" *********\n"
|
||||
" *********\n"
|
||||
" *********\n"
|
||||
" *******\n"
|
||||
" *****\n"));
|
||||
ASSERT_CANVAS_STREQ
|
||||
(c, true,
|
||||
("\n"
|
||||
" [32m[K*****[m[K\n"
|
||||
" [32m[K***[31m[K*[32m[K***[m[K\n"
|
||||
" [32m[K**[31m[K*****[32m[K**[m[K\n"
|
||||
" [32m[K**[31m[K*****[32m[K**[m[K\n"
|
||||
" [32m[K*[31m[K*******[32m[K*[m[K\n"
|
||||
" [32m[K**[31m[K*****[32m[K**[m[K\n"
|
||||
" [32m[K**[31m[K*****[32m[K**[m[K\n"
|
||||
" [32m[K***[31m[K*[32m[K***[m[K\n"
|
||||
" [32m[K*****[m[K\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_bold ()
|
||||
{
|
||||
auto_fix_quotes fix_quotes;
|
||||
style_manager sm;
|
||||
styled_string s (styled_string::from_fmt (sm, nullptr,
|
||||
"before %qs after", "foo"));
|
||||
canvas c (canvas::size_t (s.calc_canvas_width (), 1), sm);
|
||||
c.paint_text (canvas::coord_t (0, 0), s);
|
||||
ASSERT_CANVAS_STREQ (c, false,
|
||||
"before `foo' after\n");
|
||||
ASSERT_CANVAS_STREQ (c, true,
|
||||
"before `[00;01m[Kfoo[00m[K' after\n");
|
||||
}
|
||||
|
||||
static void
|
||||
test_emoji ()
|
||||
{
|
||||
style_manager sm;
|
||||
styled_string s (0x26A0, /* U+26A0 WARNING SIGN. */
|
||||
true);
|
||||
canvas c (canvas::size_t (s.calc_canvas_width (), 1), sm);
|
||||
c.paint_text (canvas::coord_t (0, 0), s);
|
||||
ASSERT_CANVAS_STREQ (c, false, "⚠️\n");
|
||||
ASSERT_CANVAS_STREQ (c, true, "⚠️\n");
|
||||
}
|
||||
|
||||
static void
|
||||
test_emoji_2 ()
|
||||
{
|
||||
style_manager sm;
|
||||
styled_string s;
|
||||
s.append (styled_string (0x26A0, /* U+26A0 WARNING SIGN. */
|
||||
true));
|
||||
s.append (styled_string (sm, "test"));
|
||||
ASSERT_EQ (s.size (), 5);
|
||||
ASSERT_EQ (s.calc_canvas_width (), 5);
|
||||
canvas c (canvas::size_t (s.calc_canvas_width (), 1), sm);
|
||||
c.paint_text (canvas::coord_t (0, 0), s);
|
||||
ASSERT_CANVAS_STREQ (c, false,
|
||||
/* U+26A0 WARNING SIGN, as UTF-8: 0xE2 0x9A 0xA0. */
|
||||
"\xE2\x9A\xA0"
|
||||
/* U+FE0F VARIATION SELECTOR-16, as UTF-8: 0xEF 0xB8 0x8F. */
|
||||
"\xEF\xB8\x8F"
|
||||
"test\n");
|
||||
}
|
||||
|
||||
static void
|
||||
test_canvas_urls ()
|
||||
{
|
||||
style_manager sm;
|
||||
canvas canvas (canvas::size_t (9, 3), sm);
|
||||
styled_string foo_ss (sm, "foo");
|
||||
foo_ss.set_url (sm, "https://www.example.com/foo");
|
||||
styled_string bar_ss (sm, "bar");
|
||||
bar_ss.set_url (sm, "https://www.example.com/bar");
|
||||
canvas.paint_text(canvas::coord_t (1, 1), foo_ss);
|
||||
canvas.paint_text(canvas::coord_t (5, 1), bar_ss);
|
||||
|
||||
ASSERT_CANVAS_STREQ (canvas, false,
|
||||
("\n"
|
||||
" foo bar\n"
|
||||
"\n"));
|
||||
{
|
||||
pretty_printer pp;
|
||||
pp_show_color (&pp) = true;
|
||||
pp.url_format = URL_FORMAT_ST;
|
||||
assert_canvas_streq (SELFTEST_LOCATION, canvas, &pp,
|
||||
(/* Line 1. */
|
||||
"\n"
|
||||
/* Line 2. */
|
||||
" "
|
||||
"\33]8;;https://www.example.com/foo\33\\foo\33]8;;\33\\"
|
||||
" "
|
||||
"\33]8;;https://www.example.com/bar\33\\bar\33]8;;\33\\"
|
||||
"\n"
|
||||
/* Line 3. */
|
||||
"\n"));
|
||||
}
|
||||
|
||||
{
|
||||
pretty_printer pp;
|
||||
pp_show_color (&pp) = true;
|
||||
pp.url_format = URL_FORMAT_BEL;
|
||||
assert_canvas_streq (SELFTEST_LOCATION, canvas, &pp,
|
||||
(/* Line 1. */
|
||||
"\n"
|
||||
/* Line 2. */
|
||||
" "
|
||||
"\33]8;;https://www.example.com/foo\afoo\33]8;;\a"
|
||||
" "
|
||||
"\33]8;;https://www.example.com/bar\abar\33]8;;\a"
|
||||
"\n"
|
||||
/* Line 3. */
|
||||
"\n"));
|
||||
}
|
||||
}
|
||||
|
||||
/* Run all selftests in this file. */
|
||||
|
||||
void
|
||||
text_art_canvas_cc_tests ()
|
||||
{
|
||||
test_blank ();
|
||||
test_abc ();
|
||||
test_debug_fill ();
|
||||
test_text ();
|
||||
test_circle ();
|
||||
test_color_circle ();
|
||||
test_bold ();
|
||||
test_emoji ();
|
||||
test_emoji_2 ();
|
||||
test_canvas_urls ();
|
||||
}
|
||||
|
||||
} // namespace selftest
|
||||
|
||||
|
||||
#endif /* #if CHECKING_P */
|
74
gcc/text-art/canvas.h
Normal file
74
gcc/text-art/canvas.h
Normal file
@ -0,0 +1,74 @@
|
||||
/* Canvas for random-access procedural text art.
|
||||
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/>. */
|
||||
|
||||
#ifndef GCC_TEXT_ART_CANVAS_H
|
||||
#define GCC_TEXT_ART_CANVAS_H
|
||||
|
||||
#include "text-art/types.h"
|
||||
|
||||
namespace text_art {
|
||||
|
||||
class canvas;
|
||||
|
||||
/* A 2 dimensional grid of text cells (a "canvas"), which
|
||||
can be written to ("painted") via random access, and then
|
||||
written out to a pretty_printer once the picture is complete.
|
||||
|
||||
Each text cell can be styled independently (colorization,
|
||||
URLs, etc). */
|
||||
|
||||
class canvas
|
||||
{
|
||||
public:
|
||||
typedef styled_unichar cell_t;
|
||||
typedef size<class canvas> size_t;
|
||||
typedef coord<class canvas> coord_t;
|
||||
typedef range<class canvas> range_t;
|
||||
typedef rect<class canvas> rect_t;
|
||||
|
||||
canvas (size_t size, const style_manager &style_mgr);
|
||||
|
||||
size_t get_size () const { return m_cells.get_size (); }
|
||||
|
||||
void paint (coord_t coord, cell_t c);
|
||||
void paint_text (coord_t coord, const styled_string &text);
|
||||
|
||||
void fill (rect_t rect, cell_t c);
|
||||
void debug_fill ();
|
||||
|
||||
void print_to_pp (pretty_printer *pp,
|
||||
const char *per_line_prefix = NULL) const;
|
||||
void debug (bool styled) const;
|
||||
|
||||
const cell_t &get (coord_t coord) const
|
||||
{
|
||||
return m_cells.get (coord);
|
||||
}
|
||||
|
||||
private:
|
||||
int get_final_x_in_row (int y) const;
|
||||
|
||||
array2<cell_t, size_t, coord_t> m_cells;
|
||||
const style_manager &m_style_mgr;
|
||||
};
|
||||
|
||||
} // namespace text_art
|
||||
|
||||
#endif /* GCC_TEXT_ART_CANVAS_H */
|
723
gcc/text-art/ruler.cc
Normal file
723
gcc/text-art/ruler.cc
Normal file
@ -0,0 +1,723 @@
|
||||
/* Classes for printing labelled rulers.
|
||||
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"
|
||||
#define INCLUDE_ALGORITHM
|
||||
#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;
|
||||
|
||||
void
|
||||
x_ruler::add_label (const canvas::range_t &r,
|
||||
styled_string text,
|
||||
style::id_t style_id,
|
||||
label_kind kind)
|
||||
{
|
||||
m_labels.push_back (label (r, std::move (text), style_id, kind));
|
||||
m_has_layout = false;
|
||||
}
|
||||
|
||||
int
|
||||
x_ruler::get_canvas_y (int rel_y) const
|
||||
{
|
||||
gcc_assert (rel_y >= 0);
|
||||
gcc_assert (rel_y < m_size.h);
|
||||
switch (m_label_dir)
|
||||
{
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
case label_dir::ABOVE:
|
||||
return m_size.h - (rel_y + 1);
|
||||
case label_dir::BELOW:
|
||||
return rel_y;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
x_ruler::paint_to_canvas (canvas &canvas,
|
||||
canvas::coord_t offset,
|
||||
const theme &theme)
|
||||
{
|
||||
ensure_layout ();
|
||||
|
||||
if (0)
|
||||
canvas.fill (canvas::rect_t (offset, m_size),
|
||||
canvas::cell_t ('*'));
|
||||
|
||||
for (size_t idx = 0; idx < m_labels.size (); idx++)
|
||||
{
|
||||
const label &iter_label = m_labels[idx];
|
||||
|
||||
/* Paint the ruler itself. */
|
||||
const int ruler_rel_y = get_canvas_y (0);
|
||||
for (int rel_x = iter_label.m_range.start;
|
||||
rel_x < iter_label.m_range.next;
|
||||
rel_x++)
|
||||
{
|
||||
enum theme::cell_kind kind = theme::cell_kind::X_RULER_MIDDLE;
|
||||
|
||||
if (rel_x == iter_label.m_range.start)
|
||||
{
|
||||
kind = theme::cell_kind::X_RULER_LEFT_EDGE;
|
||||
if (idx > 0)
|
||||
{
|
||||
const label &prev_label = m_labels[idx - 1];
|
||||
if (prev_label.m_range.get_max () == iter_label.m_range.start)
|
||||
kind = theme::cell_kind::X_RULER_INTERNAL_EDGE;
|
||||
}
|
||||
}
|
||||
else if (rel_x == iter_label.m_range.get_max ())
|
||||
kind = theme::cell_kind::X_RULER_RIGHT_EDGE;
|
||||
else if (rel_x == iter_label.m_connector_x)
|
||||
{
|
||||
switch (m_label_dir)
|
||||
{
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
case label_dir::ABOVE:
|
||||
kind = theme::cell_kind::X_RULER_CONNECTOR_TO_LABEL_ABOVE;
|
||||
break;
|
||||
case label_dir::BELOW:
|
||||
kind = theme::cell_kind::X_RULER_CONNECTOR_TO_LABEL_BELOW;
|
||||
break;
|
||||
}
|
||||
}
|
||||
canvas.paint (canvas::coord_t (rel_x, ruler_rel_y) + offset,
|
||||
theme.get_cell (kind, iter_label.m_style_id));
|
||||
}
|
||||
|
||||
/* Paint the connector to the text. */
|
||||
for (int connector_rel_y = 1;
|
||||
connector_rel_y < iter_label.m_text_rect.get_min_y ();
|
||||
connector_rel_y++)
|
||||
{
|
||||
canvas.paint
|
||||
((canvas::coord_t (iter_label.m_connector_x,
|
||||
get_canvas_y (connector_rel_y))
|
||||
+ offset),
|
||||
theme.get_cell (theme::cell_kind::X_RULER_VERTICAL_CONNECTOR,
|
||||
iter_label.m_style_id));
|
||||
}
|
||||
|
||||
/* Paint the text. */
|
||||
switch (iter_label.m_kind)
|
||||
{
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
case x_ruler::label_kind::TEXT:
|
||||
canvas.paint_text
|
||||
((canvas::coord_t (iter_label.m_text_rect.get_min_x (),
|
||||
get_canvas_y (iter_label.m_text_rect.get_min_y ()))
|
||||
+ offset),
|
||||
iter_label.m_text);
|
||||
break;
|
||||
|
||||
case x_ruler::label_kind::TEXT_WITH_BORDER:
|
||||
{
|
||||
const canvas::range_t rel_x_range
|
||||
(iter_label.m_text_rect.get_x_range ());
|
||||
|
||||
enum theme::cell_kind inner_left_kind;
|
||||
enum theme::cell_kind inner_connector_kind;
|
||||
enum theme::cell_kind inner_right_kind;
|
||||
enum theme::cell_kind outer_left_kind;
|
||||
enum theme::cell_kind outer_right_kind;
|
||||
|
||||
switch (m_label_dir)
|
||||
{
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
case label_dir::ABOVE:
|
||||
outer_left_kind = theme::cell_kind::TEXT_BORDER_TOP_LEFT;
|
||||
outer_right_kind = theme::cell_kind::TEXT_BORDER_TOP_RIGHT;
|
||||
inner_left_kind = theme::cell_kind::TEXT_BORDER_BOTTOM_LEFT;
|
||||
inner_connector_kind = theme::cell_kind::X_RULER_CONNECTOR_TO_LABEL_BELOW;
|
||||
inner_right_kind = theme::cell_kind::TEXT_BORDER_BOTTOM_RIGHT;
|
||||
break;
|
||||
case label_dir::BELOW:
|
||||
inner_left_kind = theme::cell_kind::TEXT_BORDER_TOP_LEFT;
|
||||
inner_connector_kind = theme::cell_kind::X_RULER_CONNECTOR_TO_LABEL_ABOVE;
|
||||
inner_right_kind = theme::cell_kind::TEXT_BORDER_TOP_RIGHT;
|
||||
outer_left_kind = theme::cell_kind::TEXT_BORDER_BOTTOM_LEFT;
|
||||
outer_right_kind = theme::cell_kind::TEXT_BORDER_BOTTOM_RIGHT;
|
||||
break;
|
||||
}
|
||||
/* Inner border. */
|
||||
{
|
||||
const int rel_canvas_y
|
||||
= get_canvas_y (iter_label.m_text_rect.get_min_y ());
|
||||
/* Left corner. */
|
||||
canvas.paint ((canvas::coord_t (rel_x_range.get_min (),
|
||||
rel_canvas_y)
|
||||
+ offset),
|
||||
theme.get_cell (inner_left_kind,
|
||||
iter_label.m_style_id));
|
||||
/* Edge. */
|
||||
const canvas::cell_t edge_border_cell
|
||||
= theme.get_cell (theme::cell_kind::TEXT_BORDER_HORIZONTAL,
|
||||
iter_label.m_style_id);
|
||||
const canvas::cell_t connector_border_cell
|
||||
= theme.get_cell (inner_connector_kind,
|
||||
iter_label.m_style_id);
|
||||
for (int rel_x = rel_x_range.get_min () + 1;
|
||||
rel_x < rel_x_range.get_max ();
|
||||
rel_x++)
|
||||
if (rel_x == iter_label.m_connector_x)
|
||||
canvas.paint ((canvas::coord_t (rel_x, rel_canvas_y)
|
||||
+ offset),
|
||||
connector_border_cell);
|
||||
else
|
||||
canvas.paint ((canvas::coord_t (rel_x, rel_canvas_y)
|
||||
+ offset),
|
||||
edge_border_cell);
|
||||
|
||||
/* Right corner. */
|
||||
canvas.paint ((canvas::coord_t (rel_x_range.get_max (),
|
||||
rel_canvas_y)
|
||||
+ offset),
|
||||
theme.get_cell (inner_right_kind,
|
||||
iter_label.m_style_id));
|
||||
}
|
||||
|
||||
{
|
||||
const int rel_canvas_y
|
||||
= get_canvas_y (iter_label.m_text_rect.get_min_y () + 1);
|
||||
const canvas::cell_t border_cell
|
||||
= theme.get_cell (theme::cell_kind::TEXT_BORDER_VERTICAL,
|
||||
iter_label.m_style_id);
|
||||
|
||||
/* Left border. */
|
||||
canvas.paint ((canvas::coord_t (rel_x_range.get_min (),
|
||||
rel_canvas_y)
|
||||
+ offset),
|
||||
border_cell);
|
||||
/* Text. */
|
||||
canvas.paint_text ((canvas::coord_t (rel_x_range.get_min () + 1,
|
||||
rel_canvas_y)
|
||||
+ offset),
|
||||
iter_label.m_text);
|
||||
/* Right border. */
|
||||
canvas.paint ((canvas::coord_t (rel_x_range.get_max (),
|
||||
rel_canvas_y)
|
||||
+ offset),
|
||||
border_cell);
|
||||
}
|
||||
|
||||
/* Outer border. */
|
||||
{
|
||||
const int rel_canvas_y
|
||||
= get_canvas_y (iter_label.m_text_rect.get_max_y ());
|
||||
/* Left corner. */
|
||||
canvas.paint ((canvas::coord_t (rel_x_range.get_min (),
|
||||
rel_canvas_y)
|
||||
+ offset),
|
||||
theme.get_cell (outer_left_kind,
|
||||
iter_label.m_style_id));
|
||||
/* Edge. */
|
||||
const canvas::cell_t border_cell
|
||||
= theme.get_cell (theme::cell_kind::TEXT_BORDER_HORIZONTAL,
|
||||
iter_label.m_style_id);
|
||||
for (int rel_x = rel_x_range.get_min () + 1;
|
||||
rel_x < rel_x_range.get_max ();
|
||||
rel_x++)
|
||||
canvas.paint ((canvas::coord_t (rel_x, rel_canvas_y)
|
||||
+ offset),
|
||||
border_cell);
|
||||
|
||||
/* Right corner. */
|
||||
canvas.paint ((canvas::coord_t (rel_x_range.get_max (),
|
||||
rel_canvas_y)
|
||||
+ offset),
|
||||
theme.get_cell (outer_right_kind,
|
||||
iter_label.m_style_id));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION void
|
||||
x_ruler::debug (const style_manager &sm)
|
||||
{
|
||||
canvas c (get_size (), sm);
|
||||
paint_to_canvas (c, canvas::coord_t (0, 0), unicode_theme ());
|
||||
c.debug (true);
|
||||
}
|
||||
|
||||
x_ruler::label::label (const canvas::range_t &range,
|
||||
styled_string text,
|
||||
style::id_t style_id,
|
||||
label_kind kind)
|
||||
: m_range (range),
|
||||
m_text (std::move (text)),
|
||||
m_style_id (style_id),
|
||||
m_kind (kind),
|
||||
m_text_rect (canvas::coord_t (0, 0),
|
||||
canvas::size_t (m_text.calc_canvas_width (), 1)),
|
||||
m_connector_x ((m_range.get_min () + m_range.get_max ()) / 2)
|
||||
{
|
||||
if (kind == label_kind::TEXT_WITH_BORDER)
|
||||
{
|
||||
m_text_rect.m_size.w += 2;
|
||||
m_text_rect.m_size.h += 2;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
x_ruler::label::operator< (const label &other) const
|
||||
{
|
||||
int cmp = m_range.start - other.m_range.start;
|
||||
if (cmp)
|
||||
return cmp < 0;
|
||||
return m_range.next < other.m_range.next;
|
||||
}
|
||||
|
||||
void
|
||||
x_ruler::ensure_layout ()
|
||||
{
|
||||
if (m_has_layout)
|
||||
return;
|
||||
update_layout ();
|
||||
m_has_layout = true;
|
||||
}
|
||||
|
||||
void
|
||||
x_ruler::update_layout ()
|
||||
{
|
||||
if (m_labels.empty ())
|
||||
return;
|
||||
|
||||
std::sort (m_labels.begin (), m_labels.end ());
|
||||
|
||||
/* Place labels. */
|
||||
int ruler_width = m_labels.back ().m_range.get_next ();
|
||||
int width_with_labels = ruler_width;
|
||||
|
||||
/* Get x coordinates of text parts of each label
|
||||
(m_text_rect.m_top_left.x for each label). */
|
||||
for (size_t idx = 0; idx < m_labels.size (); idx++)
|
||||
{
|
||||
label &iter_label = m_labels[idx];
|
||||
/* Attempt to center the text label. */
|
||||
int min_x;
|
||||
if (idx > 0)
|
||||
{
|
||||
/* ...but don't overlap with the connector to the left. */
|
||||
int left_neighbor_connector_x = m_labels[idx - 1].m_connector_x;
|
||||
min_x = left_neighbor_connector_x + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* ...or go beyond the leftmost column. */
|
||||
min_x = 0;
|
||||
}
|
||||
int connector_x = iter_label.m_connector_x;
|
||||
int centered_x
|
||||
= connector_x - ((int)iter_label.m_text_rect.get_width () / 2);
|
||||
int text_x = std::max (min_x, centered_x);
|
||||
iter_label.m_text_rect.m_top_left.x = text_x;
|
||||
}
|
||||
|
||||
/* Now walk backwards trying to place them vertically,
|
||||
setting m_text_rect.m_top_left.y for each label,
|
||||
consolidating the rows where possible.
|
||||
The y cooordinates are stored with respect to label_dir::BELOW. */
|
||||
int label_y = 2;
|
||||
for (int idx = m_labels.size () - 1; idx >= 0; idx--)
|
||||
{
|
||||
label &iter_label = m_labels[idx];
|
||||
/* Does it fit on the same row as the text label to the right? */
|
||||
size_t text_len = iter_label.m_text_rect.get_width ();
|
||||
/* Get the x-coord of immediately beyond iter_label's text. */
|
||||
int next_x = iter_label.m_text_rect.get_min_x () + text_len;
|
||||
if (idx < (int)m_labels.size () - 1)
|
||||
{
|
||||
if (next_x >= m_labels[idx + 1].m_text_rect.get_min_x ())
|
||||
{
|
||||
/* If not, start a new row. */
|
||||
label_y += m_labels[idx + 1].m_text_rect.get_height ();
|
||||
}
|
||||
}
|
||||
iter_label.m_text_rect.m_top_left.y = label_y;
|
||||
width_with_labels = std::max (width_with_labels, next_x);
|
||||
}
|
||||
|
||||
m_size = canvas::size_t (width_with_labels,
|
||||
label_y + m_labels[0].m_text_rect.get_height ());
|
||||
}
|
||||
|
||||
#if CHECKING_P
|
||||
|
||||
namespace selftest {
|
||||
|
||||
static void
|
||||
assert_x_ruler_streq (const location &loc,
|
||||
x_ruler &ruler,
|
||||
const theme &theme,
|
||||
const style_manager &sm,
|
||||
bool styled,
|
||||
const char *expected_str)
|
||||
{
|
||||
canvas c (ruler.get_size (), sm);
|
||||
ruler.paint_to_canvas (c, canvas::coord_t (0, 0), theme);
|
||||
if (0)
|
||||
c.debug (styled);
|
||||
assert_canvas_streq (loc, c, styled, expected_str);
|
||||
}
|
||||
|
||||
#define ASSERT_X_RULER_STREQ(RULER, THEME, SM, STYLED, EXPECTED_STR) \
|
||||
SELFTEST_BEGIN_STMT \
|
||||
assert_x_ruler_streq ((SELFTEST_LOCATION), \
|
||||
(RULER), \
|
||||
(THEME), \
|
||||
(SM), \
|
||||
(STYLED), \
|
||||
(EXPECTED_STR)); \
|
||||
SELFTEST_END_STMT
|
||||
|
||||
static void
|
||||
test_single ()
|
||||
{
|
||||
style_manager sm;
|
||||
x_ruler r (x_ruler::label_dir::BELOW);
|
||||
r.add_label (canvas::range_t (0, 11), styled_string (sm, "foo"),
|
||||
style::id_plain, x_ruler::label_kind::TEXT);
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, ascii_theme (), sm, true,
|
||||
("|~~~~+~~~~|\n"
|
||||
" |\n"
|
||||
" foo\n"));
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, unicode_theme (), sm, true,
|
||||
("├────┬────┤\n"
|
||||
" │\n"
|
||||
" foo\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_single_above ()
|
||||
{
|
||||
style_manager sm;
|
||||
x_ruler r (x_ruler::label_dir::ABOVE);
|
||||
r.add_label (canvas::range_t (0, 11), styled_string (sm, "hello world"),
|
||||
style::id_plain);
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, ascii_theme (), sm, true,
|
||||
("hello world\n"
|
||||
" |\n"
|
||||
"|~~~~+~~~~|\n"));
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, unicode_theme (), sm, true,
|
||||
("hello world\n"
|
||||
" │\n"
|
||||
"├────┴────┤\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_multiple_contiguous ()
|
||||
{
|
||||
style_manager sm;
|
||||
x_ruler r (x_ruler::label_dir::BELOW);
|
||||
r.add_label (canvas::range_t (0, 11), styled_string (sm, "foo"),
|
||||
style::id_plain);
|
||||
r.add_label (canvas::range_t (10, 16), styled_string (sm, "bar"),
|
||||
style::id_plain);
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, ascii_theme (), sm, true,
|
||||
("|~~~~+~~~~|~+~~|\n"
|
||||
" | |\n"
|
||||
" foo bar\n"));
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, unicode_theme (), sm, true,
|
||||
("├────┬────┼─┬──┤\n"
|
||||
" │ │\n"
|
||||
" foo bar\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_multiple_contiguous_above ()
|
||||
{
|
||||
style_manager sm;
|
||||
x_ruler r (x_ruler::label_dir::ABOVE);
|
||||
r.add_label (canvas::range_t (0, 11), styled_string (sm, "foo"),
|
||||
style::id_plain);
|
||||
r.add_label (canvas::range_t (10, 16), styled_string (sm, "bar"),
|
||||
style::id_plain);
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, ascii_theme (), sm, true,
|
||||
(" foo bar\n"
|
||||
" | |\n"
|
||||
"|~~~~+~~~~|~+~~|\n"));
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, unicode_theme (), sm, true,
|
||||
(" foo bar\n"
|
||||
" │ │\n"
|
||||
"├────┴────┼─┴──┤\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_multiple_contiguous_abutting_labels ()
|
||||
{
|
||||
style_manager sm;
|
||||
x_ruler r (x_ruler::label_dir::BELOW);
|
||||
r.add_label (canvas::range_t (0, 11), styled_string (sm, "12345678"),
|
||||
style::id_plain);
|
||||
r.add_label (canvas::range_t (10, 16), styled_string (sm, "1234678"),
|
||||
style::id_plain);
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, unicode_theme (), sm, true,
|
||||
("├────┬────┼─┬──┤\n"
|
||||
" │ │\n"
|
||||
" │ 1234678\n"
|
||||
" 12345678\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_multiple_contiguous_overlapping_labels ()
|
||||
{
|
||||
style_manager sm;
|
||||
x_ruler r (x_ruler::label_dir::BELOW);
|
||||
r.add_label (canvas::range_t (0, 11), styled_string (sm, "123456789"),
|
||||
style::id_plain);
|
||||
r.add_label (canvas::range_t (10, 16), styled_string (sm, "12346789"),
|
||||
style::id_plain);
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, unicode_theme (), sm, true,
|
||||
("├────┬────┼─┬──┤\n"
|
||||
" │ │\n"
|
||||
" │ 12346789\n"
|
||||
" 123456789\n"));
|
||||
}
|
||||
static void
|
||||
test_abutting_left_border ()
|
||||
{
|
||||
style_manager sm;
|
||||
x_ruler r (x_ruler::label_dir::BELOW);
|
||||
r.add_label (canvas::range_t (0, 6),
|
||||
styled_string (sm, "this is a long label"),
|
||||
style::id_plain);
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, unicode_theme (), sm, true,
|
||||
("├─┬──┤\n"
|
||||
" │\n"
|
||||
"this is a long label\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_too_long_to_consolidate_vertically ()
|
||||
{
|
||||
style_manager sm;
|
||||
x_ruler r (x_ruler::label_dir::BELOW);
|
||||
r.add_label (canvas::range_t (0, 11),
|
||||
styled_string (sm, "long string A"),
|
||||
style::id_plain);
|
||||
r.add_label (canvas::range_t (10, 16),
|
||||
styled_string (sm, "long string B"),
|
||||
style::id_plain);
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, unicode_theme (), sm, true,
|
||||
("├────┬────┼─┬──┤\n"
|
||||
" │ │\n"
|
||||
" │long string B\n"
|
||||
"long string A\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_abutting_neighbor ()
|
||||
{
|
||||
style_manager sm;
|
||||
x_ruler r (x_ruler::label_dir::BELOW);
|
||||
r.add_label (canvas::range_t (0, 11),
|
||||
styled_string (sm, "very long string A"),
|
||||
style::id_plain);
|
||||
r.add_label (canvas::range_t (10, 16),
|
||||
styled_string (sm, "very long string B"),
|
||||
style::id_plain);
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, unicode_theme (), sm, true,
|
||||
("├────┬────┼─┬──┤\n"
|
||||
" │ │\n"
|
||||
" │very long string B\n"
|
||||
"very long string A\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_gaps ()
|
||||
{
|
||||
style_manager sm;
|
||||
x_ruler r (x_ruler::label_dir::BELOW);
|
||||
r.add_label (canvas::range_t (0, 5),
|
||||
styled_string (sm, "foo"),
|
||||
style::id_plain);
|
||||
r.add_label (canvas::range_t (10, 15),
|
||||
styled_string (sm, "bar"),
|
||||
style::id_plain);
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, ascii_theme (), sm, true,
|
||||
("|~+~| |~+~|\n"
|
||||
" | |\n"
|
||||
" foo bar\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_styled ()
|
||||
{
|
||||
style_manager sm;
|
||||
style s1, s2;
|
||||
s1.m_bold = true;
|
||||
s1.m_fg_color = style::named_color::YELLOW;
|
||||
s2.m_bold = true;
|
||||
s2.m_fg_color = style::named_color::BLUE;
|
||||
style::id_t sid1 = sm.get_or_create_id (s1);
|
||||
style::id_t sid2 = sm.get_or_create_id (s2);
|
||||
|
||||
x_ruler r (x_ruler::label_dir::BELOW);
|
||||
r.add_label (canvas::range_t (0, 5), styled_string (sm, "foo"), sid1);
|
||||
r.add_label (canvas::range_t (10, 15), styled_string (sm, "bar"), sid2);
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, ascii_theme (), sm, true,
|
||||
("[00;01;33m[K|~+~|[00m[K [00;01;34m[K|~+~|[00m[K\n"
|
||||
" [00;01;33m[K|[00m[K [00;01;34m[K|[00m[K\n"
|
||||
" foo bar\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_borders ()
|
||||
{
|
||||
style_manager sm;
|
||||
{
|
||||
x_ruler r (x_ruler::label_dir::BELOW);
|
||||
r.add_label (canvas::range_t (0, 5),
|
||||
styled_string (sm, "label 1"),
|
||||
style::id_plain,
|
||||
x_ruler::label_kind::TEXT_WITH_BORDER);
|
||||
r.add_label (canvas::range_t (10, 15),
|
||||
styled_string (sm, "label 2"),
|
||||
style::id_plain);
|
||||
r.add_label (canvas::range_t (20, 25),
|
||||
styled_string (sm, "label 3"),
|
||||
style::id_plain,
|
||||
x_ruler::label_kind::TEXT_WITH_BORDER);
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, ascii_theme (), sm, true,
|
||||
"|~+~| |~+~| |~+~|\n"
|
||||
" | | |\n"
|
||||
" | label 2 +---+---+\n"
|
||||
"+-+-----+ |label 3|\n"
|
||||
"|label 1| +-------+\n"
|
||||
"+-------+\n");
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, unicode_theme (), sm, true,
|
||||
"├─┬─┤ ├─┬─┤ ├─┬─┤\n"
|
||||
" │ │ │\n"
|
||||
" │ label 2 ╭───┴───╮\n"
|
||||
"╭─┴─────╮ │label 3│\n"
|
||||
"│label 1│ ╰───────╯\n"
|
||||
"╰───────╯\n");
|
||||
}
|
||||
{
|
||||
x_ruler r (x_ruler::label_dir::ABOVE);
|
||||
r.add_label (canvas::range_t (0, 5),
|
||||
styled_string (sm, "label 1"),
|
||||
style::id_plain,
|
||||
x_ruler::label_kind::TEXT_WITH_BORDER);
|
||||
r.add_label (canvas::range_t (10, 15),
|
||||
styled_string (sm, "label 2"),
|
||||
style::id_plain);
|
||||
r.add_label (canvas::range_t (20, 25),
|
||||
styled_string (sm, "label 3"),
|
||||
style::id_plain,
|
||||
x_ruler::label_kind::TEXT_WITH_BORDER);
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, ascii_theme (), sm, true,
|
||||
"+-------+\n"
|
||||
"|label 1| +-------+\n"
|
||||
"+-+-----+ |label 3|\n"
|
||||
" | label 2 +---+---+\n"
|
||||
" | | |\n"
|
||||
"|~+~| |~+~| |~+~|\n");
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, unicode_theme (), sm, true,
|
||||
"╭───────╮\n"
|
||||
"│label 1│ ╭───────╮\n"
|
||||
"╰─┬─────╯ │label 3│\n"
|
||||
" │ label 2 ╰───┬───╯\n"
|
||||
" │ │ │\n"
|
||||
"├─┴─┤ ├─┴─┤ ├─┴─┤\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_emoji ()
|
||||
{
|
||||
style_manager sm;
|
||||
|
||||
styled_string s;
|
||||
s.append (styled_string (0x26A0, /* U+26A0 WARNING SIGN. */
|
||||
true));
|
||||
s.append (styled_string (sm, " "));
|
||||
s.append (styled_string (sm, "this is a warning"));
|
||||
|
||||
x_ruler r (x_ruler::label_dir::BELOW);
|
||||
r.add_label (canvas::range_t (0, 5),
|
||||
std::move (s),
|
||||
style::id_plain,
|
||||
x_ruler::label_kind::TEXT_WITH_BORDER);
|
||||
|
||||
ASSERT_X_RULER_STREQ
|
||||
(r, ascii_theme (), sm, true,
|
||||
"|~+~|\n"
|
||||
" |\n"
|
||||
"+-+------------------+\n"
|
||||
"|⚠️ this is a warning|\n"
|
||||
"+--------------------+\n");
|
||||
}
|
||||
|
||||
/* Run all selftests in this file. */
|
||||
|
||||
void
|
||||
text_art_ruler_cc_tests ()
|
||||
{
|
||||
test_single ();
|
||||
test_single_above ();
|
||||
test_multiple_contiguous ();
|
||||
test_multiple_contiguous_above ();
|
||||
test_multiple_contiguous_abutting_labels ();
|
||||
test_multiple_contiguous_overlapping_labels ();
|
||||
test_abutting_left_border ();
|
||||
test_too_long_to_consolidate_vertically ();
|
||||
test_abutting_neighbor ();
|
||||
test_gaps ();
|
||||
test_styled ();
|
||||
test_borders ();
|
||||
test_emoji ();
|
||||
}
|
||||
|
||||
} // namespace selftest
|
||||
|
||||
|
||||
#endif /* #if CHECKING_P */
|
125
gcc/text-art/ruler.h
Normal file
125
gcc/text-art/ruler.h
Normal file
@ -0,0 +1,125 @@
|
||||
/* Classes for printing labelled rulers.
|
||||
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/>. */
|
||||
|
||||
#ifndef GCC_TEXT_ART_RULER_H
|
||||
#define GCC_TEXT_ART_RULER_H
|
||||
|
||||
#include "text-art/canvas.h"
|
||||
|
||||
namespace text_art {
|
||||
|
||||
/* A way to annotate a series of ranges of canvas coordinates
|
||||
with text labels either above or, in this example, below:
|
||||
├───────┬───────┼───────┬───────┼───────┬───────┤
|
||||
│ │ │
|
||||
label A label B label C
|
||||
with logic to ensure that the text labels don't overlap
|
||||
when printed. */
|
||||
|
||||
class x_ruler
|
||||
{
|
||||
public:
|
||||
enum class label_dir { ABOVE, BELOW };
|
||||
enum class label_kind
|
||||
{
|
||||
TEXT,
|
||||
TEXT_WITH_BORDER
|
||||
};
|
||||
|
||||
x_ruler (label_dir dir)
|
||||
: m_label_dir (dir),
|
||||
m_size (canvas::size_t (0, 0)),
|
||||
m_has_layout (false)
|
||||
{}
|
||||
|
||||
void add_label (const canvas::range_t &r,
|
||||
styled_string text,
|
||||
style::id_t style_id,
|
||||
label_kind kind = label_kind::TEXT);
|
||||
|
||||
canvas::size_t get_size ()
|
||||
{
|
||||
ensure_layout ();
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void paint_to_canvas (canvas &canvas,
|
||||
canvas::coord_t offset,
|
||||
const theme &theme);
|
||||
|
||||
void debug (const style_manager &sm);
|
||||
|
||||
private:
|
||||
/* A particular label within an x_ruler.
|
||||
Consider e.g.:
|
||||
|
||||
# x: 01234567890123456789012345678901234567890123456789
|
||||
# y: 0: ├───────┬───────┼───────┬───────┼───────┬───────┤
|
||||
# 1: │ │ │
|
||||
# 2: label A label B label C
|
||||
#
|
||||
|
||||
Then "label A" is:
|
||||
|
||||
# m_connector_x == 8
|
||||
# V
|
||||
# x: 0123456789012
|
||||
# y: 0: ┬
|
||||
# 1: │
|
||||
# 2: label A
|
||||
# x: 0123456789012
|
||||
# ^
|
||||
# m_text_coord.x == 6
|
||||
|
||||
and m_text_coord is (2, 6).
|
||||
The y cooordinates are stored with respect to label_dir::BELOW;
|
||||
for label_dir::ABOVE we flip them when painting the ruler. */
|
||||
class label
|
||||
{
|
||||
friend class x_ruler;
|
||||
public:
|
||||
label (const canvas::range_t &range, styled_string text, style::id_t style_id,
|
||||
label_kind kind);
|
||||
|
||||
bool operator< (const label &other) const;
|
||||
|
||||
private:
|
||||
canvas::range_t m_range;
|
||||
styled_string m_text;
|
||||
style::id_t m_style_id;
|
||||
label_kind m_kind;
|
||||
canvas::rect_t m_text_rect; // includes any border
|
||||
int m_connector_x;
|
||||
};
|
||||
|
||||
void ensure_layout ();
|
||||
void update_layout ();
|
||||
int get_canvas_y (int rel_y) const;
|
||||
|
||||
label_dir m_label_dir;
|
||||
std::vector<label> m_labels;
|
||||
canvas::size_t m_size;
|
||||
bool m_has_layout = false;
|
||||
|
||||
};
|
||||
|
||||
} // namespace text_art
|
||||
|
||||
#endif /* GCC_TEXT_ART_RULER_H */
|
77
gcc/text-art/selftests.cc
Normal file
77
gcc/text-art/selftests.cc
Normal file
@ -0,0 +1,77 @@
|
||||
/* Selftests for text art.
|
||||
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 "selftest.h"
|
||||
#include "pretty-print.h"
|
||||
#include "text-art/selftests.h"
|
||||
#include "text-art/canvas.h"
|
||||
|
||||
#if CHECKING_P
|
||||
|
||||
/* Run all tests, aborting if any fail. */
|
||||
|
||||
void
|
||||
selftest::text_art_tests ()
|
||||
{
|
||||
text_art_style_cc_tests ();
|
||||
text_art_styled_string_cc_tests ();
|
||||
|
||||
text_art_box_drawing_cc_tests ();
|
||||
text_art_canvas_cc_tests ();
|
||||
text_art_ruler_cc_tests ();
|
||||
text_art_table_cc_tests ();
|
||||
text_art_widget_cc_tests ();
|
||||
}
|
||||
|
||||
/* Implementation detail of ASSERT_CANVAS_STREQ. */
|
||||
|
||||
void
|
||||
selftest::assert_canvas_streq (const location &loc,
|
||||
const text_art::canvas &canvas,
|
||||
pretty_printer *pp,
|
||||
const char *expected_str)
|
||||
{
|
||||
canvas.print_to_pp (pp);
|
||||
if (0)
|
||||
fprintf (stderr, "%s\n", pp_formatted_text (pp));
|
||||
ASSERT_STREQ_AT (loc, pp_formatted_text (pp), expected_str);
|
||||
}
|
||||
|
||||
/* Implementation detail of ASSERT_CANVAS_STREQ. */
|
||||
|
||||
void
|
||||
selftest::assert_canvas_streq (const location &loc,
|
||||
const text_art::canvas &canvas,
|
||||
bool styled,
|
||||
const char *expected_str)
|
||||
{
|
||||
pretty_printer pp;
|
||||
if (styled)
|
||||
{
|
||||
pp_show_color (&pp) = true;
|
||||
pp.url_format = URL_FORMAT_DEFAULT;
|
||||
}
|
||||
assert_canvas_streq (loc, canvas, &pp, expected_str);
|
||||
}
|
||||
|
||||
#endif /* #if CHECKING_P */
|
60
gcc/text-art/selftests.h
Normal file
60
gcc/text-art/selftests.h
Normal file
@ -0,0 +1,60 @@
|
||||
/* 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/>. */
|
||||
|
||||
#ifndef GCC_TEXT_ART_SELFTESTS_H
|
||||
#define GCC_TEXT_ART_SELFTESTS_H
|
||||
|
||||
#if CHECKING_P
|
||||
|
||||
#include "text-art/types.h"
|
||||
|
||||
namespace selftest {
|
||||
|
||||
extern void text_art_box_drawing_cc_tests ();
|
||||
extern void text_art_canvas_cc_tests ();
|
||||
extern void text_art_ruler_cc_tests ();
|
||||
extern void text_art_style_cc_tests ();
|
||||
extern void text_art_styled_string_cc_tests ();
|
||||
extern void text_art_table_cc_tests ();
|
||||
extern void text_art_widget_cc_tests ();
|
||||
|
||||
extern void text_art_tests ();
|
||||
|
||||
extern void assert_canvas_streq (const location &loc,
|
||||
const text_art::canvas &canvas,
|
||||
pretty_printer *pp,
|
||||
const char *expected_str);
|
||||
extern void assert_canvas_streq (const location &loc,
|
||||
const text_art::canvas &canvas,
|
||||
bool styled,
|
||||
const char *expected_str);
|
||||
|
||||
#define ASSERT_CANVAS_STREQ(CANVAS, STYLED, EXPECTED_STR) \
|
||||
SELFTEST_BEGIN_STMT \
|
||||
assert_canvas_streq ((SELFTEST_LOCATION), \
|
||||
(CANVAS), \
|
||||
(STYLED), \
|
||||
(EXPECTED_STR)); \
|
||||
SELFTEST_END_STMT
|
||||
|
||||
} /* end of namespace selftest. */
|
||||
|
||||
#endif /* #if CHECKING_P */
|
||||
|
||||
#endif /* GCC_TEXT_ART_SELFTESTS_H */
|
632
gcc/text-art/style.cc
Normal file
632
gcc/text-art/style.cc
Normal file
@ -0,0 +1,632 @@
|
||||
/* Classes for styling text cells (color, URLs).
|
||||
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"
|
||||
#define INCLUDE_ALGORITHM
|
||||
#define INCLUDE_MEMORY
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "make-unique.h"
|
||||
#include "pretty-print.h"
|
||||
#include "intl.h"
|
||||
#include "selftest.h"
|
||||
#include "text-art/selftests.h"
|
||||
#include "text-art/types.h"
|
||||
#include "color-macros.h"
|
||||
|
||||
using namespace text_art;
|
||||
|
||||
/* class text_art::style. */
|
||||
|
||||
style &
|
||||
style::set_style_url (const char *url)
|
||||
{
|
||||
m_url.clear ();
|
||||
while (*url)
|
||||
m_url.push_back (*(url++));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* class text_art::style::color. */
|
||||
|
||||
bool
|
||||
style::color::operator== (const style::color &other) const
|
||||
{
|
||||
if (m_kind != other.m_kind)
|
||||
return false;
|
||||
switch (m_kind)
|
||||
{
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
case kind::NAMED:
|
||||
return (u.m_named.m_name == other.u.m_named.m_name
|
||||
&& u.m_named.m_bright == other.u.m_named.m_bright);
|
||||
case kind::BITS_8:
|
||||
return u.m_8bit == other.u.m_8bit;
|
||||
case kind::BITS_24:
|
||||
return (u.m_24bit.r == other.u.m_24bit.r
|
||||
&& u.m_24bit.g == other.u.m_24bit.g
|
||||
&& u.m_24bit.b == other.u.m_24bit.b);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ensure_separator (pretty_printer *pp, bool &need_separator)
|
||||
{
|
||||
if (need_separator)
|
||||
pp_string (pp, COLOR_SEPARATOR);
|
||||
need_separator = true;
|
||||
}
|
||||
|
||||
void
|
||||
style::color::print_sgr (pretty_printer *pp,
|
||||
bool fg,
|
||||
bool &need_separator) const
|
||||
{
|
||||
switch (m_kind)
|
||||
{
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
case kind::NAMED:
|
||||
{
|
||||
static const char * const fg_normal[] = {"", // reset, for DEFAULT
|
||||
COLOR_FG_BLACK,
|
||||
COLOR_FG_RED,
|
||||
COLOR_FG_GREEN,
|
||||
COLOR_FG_YELLOW,
|
||||
COLOR_FG_BLUE,
|
||||
COLOR_FG_MAGENTA,
|
||||
COLOR_FG_CYAN,
|
||||
COLOR_FG_WHITE};
|
||||
static const char * const fg_bright[] = {"", // reset, for DEFAULT
|
||||
COLOR_FG_BRIGHT_BLACK,
|
||||
COLOR_FG_BRIGHT_RED,
|
||||
COLOR_FG_BRIGHT_GREEN,
|
||||
COLOR_FG_BRIGHT_YELLOW,
|
||||
COLOR_FG_BRIGHT_BLUE,
|
||||
COLOR_FG_BRIGHT_MAGENTA,
|
||||
COLOR_FG_BRIGHT_CYAN,
|
||||
COLOR_FG_BRIGHT_WHITE};
|
||||
static const char * const bg_normal[] = {"", // reset, for DEFAULT
|
||||
COLOR_BG_BLACK,
|
||||
COLOR_BG_RED,
|
||||
COLOR_BG_GREEN,
|
||||
COLOR_BG_YELLOW,
|
||||
COLOR_BG_BLUE,
|
||||
COLOR_BG_MAGENTA,
|
||||
COLOR_BG_CYAN,
|
||||
COLOR_BG_WHITE};
|
||||
static const char * const bg_bright[] = {"", // reset, for DEFAULT
|
||||
COLOR_BG_BRIGHT_BLACK,
|
||||
COLOR_BG_BRIGHT_RED,
|
||||
COLOR_BG_BRIGHT_GREEN,
|
||||
COLOR_BG_BRIGHT_YELLOW,
|
||||
COLOR_BG_BRIGHT_BLUE,
|
||||
COLOR_BG_BRIGHT_MAGENTA,
|
||||
COLOR_BG_BRIGHT_CYAN,
|
||||
COLOR_BG_BRIGHT_WHITE};
|
||||
STATIC_ASSERT (ARRAY_SIZE (fg_normal) == ARRAY_SIZE (fg_bright));
|
||||
STATIC_ASSERT (ARRAY_SIZE (fg_normal) == ARRAY_SIZE (bg_normal));
|
||||
STATIC_ASSERT (ARRAY_SIZE (fg_normal) == ARRAY_SIZE (bg_bright));
|
||||
gcc_assert ((size_t)u.m_named.m_name < ARRAY_SIZE (fg_normal));
|
||||
const char *const *arr;
|
||||
if (fg)
|
||||
arr = u.m_named.m_bright ? fg_bright : fg_normal;
|
||||
else
|
||||
arr = u.m_named.m_bright ? bg_bright : bg_normal;
|
||||
const char *str = arr[(size_t)u.m_named.m_name];
|
||||
if (strlen (str) > 0)
|
||||
{
|
||||
ensure_separator (pp, need_separator);
|
||||
pp_string (pp, str);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kind::BITS_8:
|
||||
{
|
||||
ensure_separator (pp, need_separator);
|
||||
if (fg)
|
||||
pp_string (pp, "38");
|
||||
else
|
||||
pp_string (pp, "48");
|
||||
pp_printf (pp, ";5;%i", (int)u.m_8bit);
|
||||
}
|
||||
break;
|
||||
case kind::BITS_24:
|
||||
{
|
||||
ensure_separator (pp, need_separator);
|
||||
if (fg)
|
||||
pp_string (pp, "38");
|
||||
else
|
||||
pp_string (pp, "48");
|
||||
pp_printf (pp, ";2;%i;%i;%i",
|
||||
(int)u.m_24bit.r,
|
||||
(int)u.m_24bit.g,
|
||||
(int)u.m_24bit.b);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* class text_art::style. */
|
||||
|
||||
/* See https://www.ecma-international.org/wp-content/uploads/ECMA-48_5th_edition_june_1991.pdf
|
||||
GRCM - GRAPHIC RENDITION COMBINATION MODE can be "REPLACING" or
|
||||
"CUMULATIVE", which affects whether we need to respecify all attributes
|
||||
at each SGR, or can accumulate them. Looks like we can't rely on the value
|
||||
of this, so we have to emit a single SGR for all changes, with a "0" reset
|
||||
at the front, forcing it to be effectively replacing. */
|
||||
|
||||
void
|
||||
style::print_changes (pretty_printer *pp,
|
||||
const style &old_style,
|
||||
const style &new_style)
|
||||
{
|
||||
if (pp_show_color (pp))
|
||||
{
|
||||
bool needs_sgr = ((old_style.m_bold != new_style.m_bold)
|
||||
|| (old_style.m_underscore != new_style.m_underscore)
|
||||
|| (old_style.m_blink != new_style.m_blink)
|
||||
|| (old_style.m_fg_color != new_style.m_fg_color)
|
||||
|| (old_style.m_bg_color != new_style.m_bg_color));
|
||||
if (needs_sgr)
|
||||
{
|
||||
bool emit_reset = (old_style.m_bold
|
||||
|| new_style.m_bold
|
||||
|| old_style.m_underscore
|
||||
|| new_style.m_underscore
|
||||
|| old_style.m_blink
|
||||
|| new_style.m_blink);
|
||||
bool need_separator = false;
|
||||
|
||||
pp_string (pp, SGR_START);
|
||||
if (emit_reset)
|
||||
{
|
||||
pp_string (pp, COLOR_NONE);
|
||||
need_separator = true;
|
||||
}
|
||||
if (new_style.m_bold)
|
||||
{
|
||||
gcc_assert (emit_reset);
|
||||
ensure_separator (pp, need_separator);
|
||||
pp_string (pp, COLOR_BOLD);
|
||||
}
|
||||
if (new_style.m_underscore)
|
||||
{
|
||||
gcc_assert (emit_reset);
|
||||
ensure_separator (pp, need_separator);
|
||||
pp_string (pp, COLOR_UNDERSCORE);
|
||||
}
|
||||
if (new_style.m_blink)
|
||||
{
|
||||
gcc_assert (emit_reset);
|
||||
ensure_separator (pp, need_separator);
|
||||
pp_string (pp, COLOR_BLINK);
|
||||
}
|
||||
new_style.m_fg_color.print_sgr (pp, true, need_separator);
|
||||
new_style.m_bg_color.print_sgr (pp, false, need_separator);
|
||||
pp_string (pp, SGR_END);
|
||||
}
|
||||
}
|
||||
|
||||
if (old_style.m_url != new_style.m_url)
|
||||
{
|
||||
if (!old_style.m_url.empty ())
|
||||
pp_end_url (pp);
|
||||
if (pp->url_format != URL_FORMAT_NONE
|
||||
&& !new_style.m_url.empty ())
|
||||
{
|
||||
/* Adapted from pp_begin_url, but encoding the
|
||||
chars to UTF-8 on the fly, rather than converting
|
||||
to a buffer. */
|
||||
pp_string (pp, "\33]8;;");
|
||||
for (auto ch : new_style.m_url)
|
||||
pp_unicode_character (pp, ch);
|
||||
switch (pp->url_format)
|
||||
{
|
||||
default:
|
||||
case URL_FORMAT_NONE:
|
||||
gcc_unreachable ();
|
||||
case URL_FORMAT_ST:
|
||||
pp_string (pp, "\33\\");
|
||||
break;
|
||||
case URL_FORMAT_BEL:
|
||||
pp_string (pp, "\a");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* class text_art::style_manager. */
|
||||
|
||||
style_manager::style_manager ()
|
||||
{
|
||||
// index 0 will be the default style
|
||||
m_styles.push_back (style ());
|
||||
}
|
||||
|
||||
style::id_t
|
||||
style_manager::get_or_create_id (const style &s)
|
||||
{
|
||||
// For now, linear search
|
||||
std::vector<style>::iterator existing
|
||||
(std::find (m_styles.begin (), m_styles.end (), s));
|
||||
|
||||
/* If found, return index of slot. */
|
||||
if (existing != m_styles.end ())
|
||||
return std::distance (m_styles.begin (), existing);
|
||||
|
||||
/* Not found. */
|
||||
|
||||
/* styled_str uses 7 bits for style information, so we can only support
|
||||
up to 128 different style combinations.
|
||||
Gracefully fail by turning off styling when this limit is reached. */
|
||||
if (m_styles.size () >= 127)
|
||||
return 0;
|
||||
|
||||
m_styles.push_back (s);
|
||||
return m_styles.size () - 1;
|
||||
}
|
||||
|
||||
void
|
||||
style_manager::print_any_style_changes (pretty_printer *pp,
|
||||
style::id_t old_id,
|
||||
style::id_t new_id) const
|
||||
{
|
||||
gcc_assert (pp);
|
||||
if (old_id == new_id)
|
||||
return;
|
||||
|
||||
const style &old_style = m_styles[old_id];
|
||||
const style &new_style = m_styles[new_id];
|
||||
gcc_assert (!(old_style == new_style));
|
||||
style::print_changes (pp, old_style, new_style);
|
||||
}
|
||||
|
||||
#if CHECKING_P
|
||||
|
||||
namespace selftest {
|
||||
|
||||
void
|
||||
assert_style_change_streq (const location &loc,
|
||||
const style &old_style,
|
||||
const style &new_style,
|
||||
const char *expected_str)
|
||||
{
|
||||
pretty_printer pp;
|
||||
pp_show_color (&pp) = true;
|
||||
style::print_changes (&pp, old_style, new_style);
|
||||
ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected_str);
|
||||
}
|
||||
|
||||
#define ASSERT_STYLE_CHANGE_STREQ(OLD_STYLE, NEW_STYLE, EXPECTED_STR) \
|
||||
SELFTEST_BEGIN_STMT \
|
||||
assert_style_change_streq ((SELFTEST_LOCATION), \
|
||||
(OLD_STYLE), \
|
||||
(NEW_STYLE), \
|
||||
(EXPECTED_STR)); \
|
||||
SELFTEST_END_STMT
|
||||
|
||||
static void
|
||||
test_bold ()
|
||||
{
|
||||
style_manager sm;
|
||||
ASSERT_EQ (sm.get_num_styles (), 1);
|
||||
|
||||
style plain;
|
||||
ASSERT_EQ (sm.get_or_create_id (plain), 0);
|
||||
ASSERT_EQ (sm.get_num_styles (), 1);
|
||||
|
||||
style bold;
|
||||
bold.m_bold = true;
|
||||
|
||||
ASSERT_EQ (sm.get_or_create_id (bold), 1);
|
||||
ASSERT_EQ (sm.get_num_styles (), 2);
|
||||
ASSERT_EQ (sm.get_or_create_id (bold), 1);
|
||||
ASSERT_EQ (sm.get_num_styles (), 2);
|
||||
|
||||
ASSERT_STYLE_CHANGE_STREQ (plain, bold, "\33[00;01m\33[K");
|
||||
ASSERT_STYLE_CHANGE_STREQ (bold, plain, "\33[00m\33[K");
|
||||
}
|
||||
|
||||
static void
|
||||
test_underscore ()
|
||||
{
|
||||
style_manager sm;
|
||||
ASSERT_EQ (sm.get_num_styles (), 1);
|
||||
|
||||
style plain;
|
||||
ASSERT_EQ (sm.get_or_create_id (plain), 0);
|
||||
ASSERT_EQ (sm.get_num_styles (), 1);
|
||||
|
||||
style underscore;
|
||||
underscore.m_underscore = true;
|
||||
|
||||
ASSERT_EQ (sm.get_or_create_id (underscore), 1);
|
||||
ASSERT_EQ (sm.get_num_styles (), 2);
|
||||
ASSERT_EQ (sm.get_or_create_id (underscore), 1);
|
||||
ASSERT_EQ (sm.get_num_styles (), 2);
|
||||
|
||||
ASSERT_STYLE_CHANGE_STREQ (plain, underscore, "\33[00;04m\33[K");
|
||||
ASSERT_STYLE_CHANGE_STREQ (underscore, plain, "\33[00m\33[K");
|
||||
}
|
||||
|
||||
static void
|
||||
test_blink ()
|
||||
{
|
||||
style_manager sm;
|
||||
ASSERT_EQ (sm.get_num_styles (), 1);
|
||||
|
||||
style plain;
|
||||
ASSERT_EQ (sm.get_or_create_id (plain), 0);
|
||||
ASSERT_EQ (sm.get_num_styles (), 1);
|
||||
|
||||
style blink;
|
||||
blink.m_blink = true;
|
||||
|
||||
ASSERT_EQ (sm.get_or_create_id (blink), 1);
|
||||
ASSERT_EQ (sm.get_num_styles (), 2);
|
||||
ASSERT_EQ (sm.get_or_create_id (blink), 1);
|
||||
ASSERT_EQ (sm.get_num_styles (), 2);
|
||||
|
||||
ASSERT_STYLE_CHANGE_STREQ (plain, blink, "\33[00;05m\33[K");
|
||||
ASSERT_STYLE_CHANGE_STREQ (blink, plain, "\33[00m\33[K");
|
||||
}
|
||||
|
||||
#define ASSERT_NAMED_COL_STREQ(NAMED_COLOR, FG, BRIGHT, EXPECTED_STR) \
|
||||
SELFTEST_BEGIN_STMT \
|
||||
{ \
|
||||
style plain; \
|
||||
style s; \
|
||||
if (FG) \
|
||||
s.m_fg_color = style::color ((NAMED_COLOR), (BRIGHT)); \
|
||||
else \
|
||||
s.m_bg_color = style::color ((NAMED_COLOR), (BRIGHT)); \
|
||||
assert_style_change_streq ((SELFTEST_LOCATION), \
|
||||
plain, \
|
||||
s, \
|
||||
(EXPECTED_STR)); \
|
||||
} \
|
||||
SELFTEST_END_STMT
|
||||
|
||||
static void
|
||||
test_named_colors ()
|
||||
{
|
||||
/* Foreground colors. */
|
||||
{
|
||||
const bool fg = true;
|
||||
{
|
||||
const bool bright = false;
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::DEFAULT, fg, bright, "");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::BLACK, fg, bright,
|
||||
"[30m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::RED, fg, bright,
|
||||
"[31m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::GREEN, fg, bright,
|
||||
"[32m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::YELLOW, fg, bright,
|
||||
"[33m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::BLUE, fg, bright,
|
||||
"[34m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::MAGENTA, fg, bright,
|
||||
"[35m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::CYAN, fg, bright,
|
||||
"[36m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::WHITE, fg, bright,
|
||||
"[37m[K");
|
||||
}
|
||||
{
|
||||
const bool bright = true;
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::DEFAULT, fg, bright,
|
||||
"[m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::BLACK, fg, bright,
|
||||
"[90m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::RED, fg, bright,
|
||||
"[91m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::GREEN, fg, bright,
|
||||
"[92m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::YELLOW, fg, bright,
|
||||
"[93m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::BLUE, fg, bright,
|
||||
"[94m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::MAGENTA, fg, bright,
|
||||
"[95m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::CYAN, fg, bright,
|
||||
"[96m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::WHITE, fg, bright,
|
||||
"[97m[K");
|
||||
}
|
||||
}
|
||||
|
||||
/* Background colors. */
|
||||
{
|
||||
const bool fg = false;
|
||||
{
|
||||
const bool bright = false;
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::DEFAULT, fg, bright, "");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::BLACK, fg, bright,
|
||||
"[40m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::RED, fg, bright,
|
||||
"[41m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::GREEN, fg, bright,
|
||||
"[42m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::YELLOW, fg, bright,
|
||||
"[43m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::BLUE, fg, bright,
|
||||
"[44m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::MAGENTA, fg, bright,
|
||||
"[45m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::CYAN, fg, bright,
|
||||
"[46m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::WHITE, fg, bright,
|
||||
"[47m[K");
|
||||
}
|
||||
{
|
||||
const bool bright = true;
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::DEFAULT, fg, bright,
|
||||
"[m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::BLACK, fg, bright,
|
||||
"[100m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::RED, fg, bright,
|
||||
"[101m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::GREEN, fg, bright,
|
||||
"[102m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::YELLOW, fg, bright,
|
||||
"[103m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::BLUE, fg, bright,
|
||||
"[104m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::MAGENTA, fg, bright,
|
||||
"[105m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::CYAN, fg, bright,
|
||||
"[106m[K");
|
||||
ASSERT_NAMED_COL_STREQ (style::named_color::WHITE, fg, bright,
|
||||
"[107m[K");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define ASSERT_8_BIT_COL_STREQ(COL_VAL, FG, EXPECTED_STR) \
|
||||
SELFTEST_BEGIN_STMT \
|
||||
{ \
|
||||
style plain; \
|
||||
style s; \
|
||||
if (FG) \
|
||||
s.m_fg_color = style::color (COL_VAL); \
|
||||
else \
|
||||
s.m_bg_color = style::color (COL_VAL); \
|
||||
assert_style_change_streq ((SELFTEST_LOCATION), \
|
||||
plain, \
|
||||
s, \
|
||||
(EXPECTED_STR)); \
|
||||
} \
|
||||
SELFTEST_END_STMT
|
||||
|
||||
static void
|
||||
test_8_bit_colors ()
|
||||
{
|
||||
/* Foreground colors. */
|
||||
{
|
||||
const bool fg = true;
|
||||
/* 0-15: standard and high-intensity standard colors. */
|
||||
ASSERT_8_BIT_COL_STREQ (0, fg, "[38;5;0m[K");
|
||||
ASSERT_8_BIT_COL_STREQ (15, fg, "[38;5;15m[K");
|
||||
/* 16-231: 6x6x6 color cube. */
|
||||
ASSERT_8_BIT_COL_STREQ (16, fg, "[38;5;16m[K");
|
||||
ASSERT_8_BIT_COL_STREQ (231, fg, "[38;5;231m[K");
|
||||
/* 232-255: grayscale. */
|
||||
ASSERT_8_BIT_COL_STREQ (232, fg, "[38;5;232m[K");
|
||||
ASSERT_8_BIT_COL_STREQ (255, fg, "[38;5;255m[K");
|
||||
}
|
||||
/* Background colors. */
|
||||
{
|
||||
const bool fg = false;
|
||||
/* 0-15: standard and high-intensity standard colors. */
|
||||
ASSERT_8_BIT_COL_STREQ (0, fg, "[48;5;0m[K");
|
||||
ASSERT_8_BIT_COL_STREQ (15, fg, "[48;5;15m[K");
|
||||
/* 16-231: 6x6x6 color cube. */
|
||||
ASSERT_8_BIT_COL_STREQ (16, fg, "[48;5;16m[K");
|
||||
ASSERT_8_BIT_COL_STREQ (231, fg, "[48;5;231m[K");
|
||||
/* 232-255: grayscale. */
|
||||
ASSERT_8_BIT_COL_STREQ (232, fg, "[48;5;232m[K");
|
||||
ASSERT_8_BIT_COL_STREQ (255, fg, "[48;5;255m[K");
|
||||
}
|
||||
}
|
||||
|
||||
#define ASSERT_24_BIT_COL_STREQ(R, G, B, FG, EXPECTED_STR) \
|
||||
SELFTEST_BEGIN_STMT \
|
||||
{ \
|
||||
style plain; \
|
||||
style s; \
|
||||
if (FG) \
|
||||
s.m_fg_color = style::color ((R), (G), (B)); \
|
||||
else \
|
||||
s.m_bg_color = style::color ((R), (G), (B)); \
|
||||
assert_style_change_streq ((SELFTEST_LOCATION), \
|
||||
plain, \
|
||||
s, \
|
||||
(EXPECTED_STR)); \
|
||||
} \
|
||||
SELFTEST_END_STMT
|
||||
|
||||
static void
|
||||
test_24_bit_colors ()
|
||||
{
|
||||
/* Foreground colors. */
|
||||
{
|
||||
const bool fg = true;
|
||||
// #F3FAF2:
|
||||
ASSERT_24_BIT_COL_STREQ (0xf3, 0xfa, 0xf2, fg,
|
||||
"[38;2;243;250;242m[K");
|
||||
}
|
||||
/* Background colors. */
|
||||
{
|
||||
const bool fg = false;
|
||||
// #FDF7E7
|
||||
ASSERT_24_BIT_COL_STREQ (0xfd, 0xf7, 0xe7, fg,
|
||||
"[48;2;253;247;231m[K");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_style_combinations ()
|
||||
{
|
||||
style_manager sm;
|
||||
ASSERT_EQ (sm.get_num_styles (), 1);
|
||||
|
||||
style plain;
|
||||
ASSERT_EQ (sm.get_or_create_id (plain), 0);
|
||||
ASSERT_EQ (sm.get_num_styles (), 1);
|
||||
|
||||
style bold;
|
||||
bold.m_bold = true;
|
||||
|
||||
ASSERT_EQ (sm.get_or_create_id (bold), 1);
|
||||
ASSERT_EQ (sm.get_num_styles (), 2);
|
||||
ASSERT_EQ (sm.get_or_create_id (bold), 1);
|
||||
ASSERT_EQ (sm.get_num_styles (), 2);
|
||||
|
||||
style magenta_on_blue;
|
||||
magenta_on_blue.m_fg_color = style::named_color::MAGENTA;
|
||||
magenta_on_blue.m_bg_color = style::named_color::BLUE;
|
||||
ASSERT_EQ (sm.get_or_create_id (magenta_on_blue), 2);
|
||||
ASSERT_EQ (sm.get_num_styles (), 3);
|
||||
ASSERT_EQ (sm.get_or_create_id (magenta_on_blue), 2);
|
||||
ASSERT_EQ (sm.get_num_styles (), 3);
|
||||
}
|
||||
|
||||
/* Run all selftests in this file. */
|
||||
|
||||
void
|
||||
text_art_style_cc_tests ()
|
||||
{
|
||||
test_bold ();
|
||||
test_underscore ();
|
||||
test_blink ();
|
||||
test_named_colors ();
|
||||
test_8_bit_colors ();
|
||||
test_24_bit_colors ();
|
||||
test_style_combinations ();
|
||||
}
|
||||
|
||||
} // namespace selftest
|
||||
|
||||
|
||||
#endif /* #if CHECKING_P */
|
1107
gcc/text-art/styled-string.cc
Normal file
1107
gcc/text-art/styled-string.cc
Normal file
File diff suppressed because it is too large
Load Diff
1272
gcc/text-art/table.cc
Normal file
1272
gcc/text-art/table.cc
Normal file
File diff suppressed because it is too large
Load Diff
262
gcc/text-art/table.h
Normal file
262
gcc/text-art/table.h
Normal file
@ -0,0 +1,262 @@
|
||||
/* Support for tabular/grid-based content.
|
||||
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/>. */
|
||||
|
||||
#ifndef GCC_TEXT_ART_TABLE_H
|
||||
#define GCC_TEXT_ART_TABLE_H
|
||||
|
||||
#include "text-art/canvas.h"
|
||||
#include "text-art/theme.h"
|
||||
#include <vector>
|
||||
|
||||
namespace text_art {
|
||||
|
||||
class table;
|
||||
class table_geometry;
|
||||
|
||||
/* A class representing the content of a particular table cell,
|
||||
or of a span of table cells. */
|
||||
|
||||
class table_cell_content
|
||||
{
|
||||
public:
|
||||
table_cell_content () : m_str (), m_size (0, 0) {}
|
||||
table_cell_content (styled_string &&s);
|
||||
|
||||
bool operator== (const table_cell_content &other) const
|
||||
{
|
||||
return m_str == other.m_str;
|
||||
}
|
||||
|
||||
canvas::size_t get_canvas_size () const { return m_size; }
|
||||
|
||||
void paint_to_canvas (canvas &canvas,
|
||||
canvas::coord_t top_left) const;
|
||||
|
||||
private:
|
||||
styled_string m_str;
|
||||
canvas::size_t m_size;
|
||||
};
|
||||
|
||||
/* A list of required sizes of table rows or columns
|
||||
in canvas units (row heights or column widths). */
|
||||
|
||||
struct table_dimension_sizes
|
||||
{
|
||||
table_dimension_sizes (unsigned num);
|
||||
|
||||
void require (unsigned idx, int amount)
|
||||
{
|
||||
m_requirements[idx] = std::max (m_requirements[idx], amount);
|
||||
}
|
||||
|
||||
std::vector<int> m_requirements;
|
||||
};
|
||||
|
||||
/* A 2D grid of cells. Instances of table_cell_content can be assigned
|
||||
to individual table cells, and to rectangular spans of cells. Such
|
||||
assignments do not have to fully cover the 2D grid, but they must not
|
||||
overlap. */
|
||||
|
||||
class table
|
||||
{
|
||||
public:
|
||||
typedef size<class table> size_t;
|
||||
typedef coord<class table> coord_t;
|
||||
typedef range<class table> range_t;
|
||||
typedef rect<class table> rect_t;
|
||||
|
||||
/* A record of how a table_cell_content was placed at a table::rect_t
|
||||
with a certain alignment. */
|
||||
class cell_placement
|
||||
{
|
||||
public:
|
||||
cell_placement (rect_t rect,
|
||||
table_cell_content &&content,
|
||||
x_align x_align,
|
||||
y_align y_align)
|
||||
: m_rect (rect),
|
||||
m_content (std::move (content)),
|
||||
m_x_align (x_align),
|
||||
m_y_align (y_align)
|
||||
{
|
||||
}
|
||||
|
||||
bool one_by_one_p () const
|
||||
{
|
||||
return m_rect.m_size.w == 1 && m_rect.m_size.h == 1;
|
||||
}
|
||||
|
||||
canvas::size_t get_min_canvas_size () const
|
||||
{
|
||||
// Doesn't include border
|
||||
return m_content.get_canvas_size ();
|
||||
}
|
||||
|
||||
void paint_cell_contents_to_canvas(canvas &canvas,
|
||||
canvas::coord_t offset,
|
||||
const table_geometry &tg) const;
|
||||
|
||||
const table_cell_content &get_content () const { return m_content; }
|
||||
|
||||
private:
|
||||
friend class table_cell_sizes;
|
||||
rect_t m_rect;
|
||||
table_cell_content m_content;
|
||||
x_align m_x_align;
|
||||
y_align m_y_align;
|
||||
};
|
||||
|
||||
table (size_t size);
|
||||
~table () = default;
|
||||
table (table &&) = default;
|
||||
table (const table &) = delete;
|
||||
table &operator= (const table &) = delete;
|
||||
|
||||
const size_t &get_size () const { return m_size; }
|
||||
|
||||
int add_row ()
|
||||
{
|
||||
m_size.h++;
|
||||
m_occupancy.add_row (-1);
|
||||
return m_size.h - 1; // return the table_y of the newly-added row
|
||||
}
|
||||
|
||||
void set_cell (coord_t coord,
|
||||
table_cell_content &&content,
|
||||
enum x_align x_align = x_align::CENTER,
|
||||
enum y_align y_align = y_align::CENTER);
|
||||
|
||||
void set_cell_span (rect_t span,
|
||||
table_cell_content &&content,
|
||||
enum x_align x_align = x_align::CENTER,
|
||||
enum y_align y_align = y_align::CENTER);
|
||||
|
||||
canvas to_canvas (const theme &theme, const style_manager &sm) const;
|
||||
|
||||
void paint_to_canvas(canvas &canvas,
|
||||
canvas::coord_t offset,
|
||||
const table_geometry &tg,
|
||||
const theme &theme) const;
|
||||
|
||||
void debug () const;
|
||||
|
||||
/* Self-test support. */
|
||||
const cell_placement *get_placement_at (coord_t coord) const;
|
||||
|
||||
private:
|
||||
int get_occupancy_safe (coord_t coord) const;
|
||||
directions get_connections (int table_x, int table_y) const;
|
||||
void paint_cell_borders_to_canvas(canvas &canvas,
|
||||
canvas::coord_t offset,
|
||||
const table_geometry &tg,
|
||||
const theme &theme) const;
|
||||
void paint_cell_contents_to_canvas(canvas &canvas,
|
||||
canvas::coord_t offset,
|
||||
const table_geometry &tg) const;
|
||||
|
||||
friend class table_cell_sizes;
|
||||
|
||||
size_t m_size;
|
||||
std::vector<cell_placement> m_placements;
|
||||
array2<int, size_t, coord_t> m_occupancy; /* indices into the m_placements vec. */
|
||||
};
|
||||
|
||||
/* A workspace for computing the row heights and column widths
|
||||
of a table (in canvas units).
|
||||
The col_widths and row_heights could be shared between multiple
|
||||
instances, for aligning multiple tables vertically or horizontally. */
|
||||
|
||||
class table_cell_sizes
|
||||
{
|
||||
public:
|
||||
table_cell_sizes (table_dimension_sizes &col_widths,
|
||||
table_dimension_sizes &row_heights)
|
||||
: m_col_widths (col_widths),
|
||||
m_row_heights (row_heights)
|
||||
{
|
||||
}
|
||||
|
||||
void pass_1 (const table &table);
|
||||
void pass_2 (const table &table);
|
||||
|
||||
canvas::size_t get_canvas_size (const table::rect_t &rect) const;
|
||||
|
||||
table_dimension_sizes &m_col_widths;
|
||||
table_dimension_sizes &m_row_heights;
|
||||
};
|
||||
|
||||
/* A class responsible for mapping from table cell coords
|
||||
to canvas coords, handling column widths.
|
||||
It's the result of solving "how big are all the table cells and where
|
||||
do they go?"
|
||||
The cell_sizes are passed in, for handling aligning multiple tables,
|
||||
sharing column widths or row heights. */
|
||||
|
||||
class table_geometry
|
||||
{
|
||||
public:
|
||||
table_geometry (const table &table, table_cell_sizes &cell_sizes);
|
||||
|
||||
void recalc_coords ();
|
||||
|
||||
const canvas::size_t get_canvas_size () const { return m_canvas_size; }
|
||||
|
||||
canvas::coord_t table_to_canvas (table::coord_t table_coord) const;
|
||||
int table_x_to_canvas_x (int table_x) const;
|
||||
int table_y_to_canvas_y (int table_y) const;
|
||||
|
||||
int get_col_width (int table_x) const
|
||||
{
|
||||
return m_cell_sizes.m_col_widths.m_requirements[table_x];
|
||||
}
|
||||
|
||||
canvas::size_t get_canvas_size (const table::rect_t &rect) const
|
||||
{
|
||||
return m_cell_sizes.get_canvas_size (rect);
|
||||
}
|
||||
|
||||
private:
|
||||
const table &m_table;
|
||||
table_cell_sizes &m_cell_sizes;
|
||||
canvas::size_t m_canvas_size;
|
||||
|
||||
/* Start canvas column of table cell, including leading border. */
|
||||
std::vector<int> m_col_start_x;
|
||||
|
||||
/* Start canvas row of table cell, including leading border. */
|
||||
std::vector<int> m_row_start_y;
|
||||
};
|
||||
|
||||
/* Helper class for handling the simple case of a single table
|
||||
that doesn't need to be aligned with respect to anything else. */
|
||||
|
||||
struct simple_table_geometry
|
||||
{
|
||||
simple_table_geometry (const table &table);
|
||||
|
||||
table_dimension_sizes m_col_widths;
|
||||
table_dimension_sizes m_row_heights;
|
||||
table_cell_sizes m_cell_sizes;
|
||||
table_geometry m_tg;
|
||||
};
|
||||
|
||||
} // namespace text_art
|
||||
|
||||
#endif /* GCC_TEXT_ART_TABLE_H */
|
183
gcc/text-art/theme.cc
Normal file
183
gcc/text-art/theme.cc
Normal file
@ -0,0 +1,183 @@
|
||||
/* 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 */
|
||||
}
|
||||
}
|
123
gcc/text-art/theme.h
Normal file
123
gcc/text-art/theme.h
Normal file
@ -0,0 +1,123 @@
|
||||
/* 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/>. */
|
||||
|
||||
#ifndef GCC_TEXT_ART_THEME_H
|
||||
#define GCC_TEXT_ART_THEME_H
|
||||
|
||||
#include "text-art/canvas.h"
|
||||
#include "text-art/box-drawing.h"
|
||||
|
||||
namespace text_art {
|
||||
|
||||
class theme
|
||||
{
|
||||
public:
|
||||
enum class cell_kind
|
||||
{
|
||||
/* A left-hand edge of a range e.g. "├". */
|
||||
X_RULER_LEFT_EDGE,
|
||||
|
||||
/* Within a range e.g. "─". */
|
||||
X_RULER_MIDDLE,
|
||||
|
||||
/* A border between two neighboring ranges e.g. "┼". */
|
||||
X_RULER_INTERNAL_EDGE,
|
||||
|
||||
/* The connector with the text label within a range e.g. "┬". */
|
||||
X_RULER_CONNECTOR_TO_LABEL_BELOW,
|
||||
|
||||
/* As above, but when the text label is above the ruler. */
|
||||
X_RULER_CONNECTOR_TO_LABEL_ABOVE,
|
||||
|
||||
/* The vertical connection to a text label. */
|
||||
X_RULER_VERTICAL_CONNECTOR,
|
||||
|
||||
/* A right-hand edge of a range e.g. "┤". */
|
||||
X_RULER_RIGHT_EDGE,
|
||||
|
||||
TEXT_BORDER_HORIZONTAL,
|
||||
TEXT_BORDER_VERTICAL,
|
||||
TEXT_BORDER_TOP_LEFT,
|
||||
TEXT_BORDER_TOP_RIGHT,
|
||||
TEXT_BORDER_BOTTOM_LEFT,
|
||||
TEXT_BORDER_BOTTOM_RIGHT,
|
||||
|
||||
Y_ARROW_UP_HEAD,
|
||||
Y_ARROW_UP_TAIL,
|
||||
Y_ARROW_DOWN_HEAD,
|
||||
Y_ARROW_DOWN_TAIL,
|
||||
};
|
||||
|
||||
virtual ~theme () = default;
|
||||
|
||||
virtual bool unicode_p () const = 0;
|
||||
virtual bool emojis_p () const = 0;
|
||||
|
||||
virtual canvas::cell_t
|
||||
get_line_art (directions line_dirs) const = 0;
|
||||
|
||||
canvas::cell_t get_cell (enum cell_kind kind, unsigned style_idx) const
|
||||
{
|
||||
return canvas::cell_t (get_cppchar (kind), false, style_idx);
|
||||
}
|
||||
|
||||
virtual cppchar_t get_cppchar (enum cell_kind kind) const = 0;
|
||||
|
||||
enum class y_arrow_dir { UP, DOWN };
|
||||
void paint_y_arrow (canvas &canvas,
|
||||
int x,
|
||||
canvas::range_t y_range,
|
||||
y_arrow_dir dir,
|
||||
style::id_t style_id) const;
|
||||
};
|
||||
|
||||
class ascii_theme : public theme
|
||||
{
|
||||
public:
|
||||
bool unicode_p () const final override { return false; }
|
||||
bool emojis_p () const final override { return false; }
|
||||
|
||||
canvas::cell_t
|
||||
get_line_art (directions line_dirs) const final override;
|
||||
|
||||
cppchar_t get_cppchar (enum cell_kind kind) const final override;
|
||||
};
|
||||
|
||||
class unicode_theme : public theme
|
||||
{
|
||||
public:
|
||||
bool unicode_p () const final override { return true; }
|
||||
bool emojis_p () const override { return false; }
|
||||
|
||||
canvas::cell_t
|
||||
get_line_art (directions line_dirs) const final override;
|
||||
|
||||
cppchar_t get_cppchar (enum cell_kind kind) const final override;
|
||||
};
|
||||
|
||||
class emoji_theme : public unicode_theme
|
||||
{
|
||||
public:
|
||||
bool emojis_p () const final override { return true; }
|
||||
};
|
||||
|
||||
} // namespace text_art
|
||||
|
||||
#endif /* GCC_TEXT_ART_THEME_H */
|
504
gcc/text-art/types.h
Normal file
504
gcc/text-art/types.h
Normal file
@ -0,0 +1,504 @@
|
||||
/* Types for drawing 2d "text art".
|
||||
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/>. */
|
||||
|
||||
#ifndef GCC_TEXT_ART_TYPES_H
|
||||
#define GCC_TEXT_ART_TYPES_H
|
||||
|
||||
#include "cpplib.h"
|
||||
#include "pretty-print.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace text_art {
|
||||
|
||||
/* Forward decls. */
|
||||
|
||||
class canvas;
|
||||
class table;
|
||||
class theme;
|
||||
|
||||
/* Classes for geometry.
|
||||
We use templates to avoid mixing up e.g. canvas coordinates
|
||||
with table coordinates. */
|
||||
|
||||
template <typename CoordinateSystem>
|
||||
struct size
|
||||
{
|
||||
size (int w_, int h_) : w (w_), h (h_) {}
|
||||
int w;
|
||||
int h;
|
||||
};
|
||||
|
||||
template <typename CoordinateSystem>
|
||||
struct coord
|
||||
{
|
||||
coord (int x_, int y_) : x (x_), y (y_) {}
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
template <typename CoordinateSystem>
|
||||
coord<CoordinateSystem> operator+ (coord<CoordinateSystem> a,
|
||||
coord<CoordinateSystem> b)
|
||||
{
|
||||
return coord<CoordinateSystem> (a.x + b.x, a.y + b.y);
|
||||
}
|
||||
|
||||
/* A half-open range [start, next) of int. */
|
||||
|
||||
template <typename CoordinateSystem>
|
||||
struct range
|
||||
{
|
||||
range (int start_, int next_)
|
||||
: start (start_), next (next_)
|
||||
{}
|
||||
|
||||
int get_min () const { return start; }
|
||||
int get_max () const { return next - 1; }
|
||||
int get_next () const { return next; }
|
||||
int get_size () const { return next - start; }
|
||||
|
||||
int get_midpoint () const { return get_min () + get_size () / 2; }
|
||||
|
||||
int start;
|
||||
int next;
|
||||
};
|
||||
|
||||
/* A rectangle area within CoordinateSystem. */
|
||||
|
||||
template <typename CoordinateSystem>
|
||||
struct rect
|
||||
{
|
||||
rect (coord<CoordinateSystem> top_left,
|
||||
size<CoordinateSystem> size)
|
||||
: m_top_left (top_left),
|
||||
m_size (size)
|
||||
{
|
||||
}
|
||||
|
||||
rect (range<CoordinateSystem> x_range,
|
||||
range<CoordinateSystem> y_range)
|
||||
: m_top_left (x_range.get_min (), y_range.get_min ()),
|
||||
m_size (x_range.get_size (), y_range.get_size ())
|
||||
{
|
||||
}
|
||||
|
||||
int get_min_x () const { return m_top_left.x; }
|
||||
int get_min_y () const { return m_top_left.y; }
|
||||
int get_max_x () const { return m_top_left.x + m_size.w - 1; }
|
||||
int get_max_y () const { return m_top_left.y + m_size.h - 1; }
|
||||
int get_next_x () const { return m_top_left.x + m_size.w; }
|
||||
int get_next_y () const { return m_top_left.y + m_size.h; }
|
||||
|
||||
range<CoordinateSystem> get_x_range () const
|
||||
{
|
||||
return range<CoordinateSystem> (get_min_x (), get_next_x ());
|
||||
}
|
||||
range<CoordinateSystem> get_y_range () const
|
||||
{
|
||||
return range<CoordinateSystem> (get_min_y (), get_next_y ());
|
||||
}
|
||||
|
||||
int get_width () const { return m_size.w; }
|
||||
int get_height () const { return m_size.h; }
|
||||
|
||||
coord<CoordinateSystem> m_top_left;
|
||||
size<CoordinateSystem> m_size;
|
||||
};
|
||||
|
||||
template <typename ElementType, typename SizeType, typename CoordType>
|
||||
class array2
|
||||
{
|
||||
public:
|
||||
typedef ElementType element_t;
|
||||
typedef SizeType size_t;
|
||||
typedef CoordType coord_t;
|
||||
|
||||
array2 (size_t sz)
|
||||
: m_size (sz),
|
||||
m_elements (sz.w * sz.h)
|
||||
{
|
||||
}
|
||||
array2 (array2 &&other)
|
||||
: m_size (other.m_size),
|
||||
m_elements (std::move (other.m_elements))
|
||||
{
|
||||
}
|
||||
|
||||
/* Move assignment not implemented or used. */
|
||||
array2 &operator== (array2 &&other) = delete;
|
||||
|
||||
/* No copy ctor or assignment op. */
|
||||
array2 (const array2 &other) = delete;
|
||||
array2 &operator= (const array2 &other) = delete;
|
||||
|
||||
|
||||
const size_t &get_size () const { return m_size; }
|
||||
|
||||
void add_row (const element_t &element)
|
||||
{
|
||||
m_size.h++;
|
||||
m_elements.insert (m_elements.end (), m_size.w, element);
|
||||
}
|
||||
|
||||
const element_t &get (const coord_t &coord) const
|
||||
{
|
||||
::size_t idx = get_idx (coord);
|
||||
return m_elements[idx];
|
||||
}
|
||||
|
||||
void set (const coord_t &coord, const element_t &element)
|
||||
{
|
||||
::size_t idx = get_idx (coord);
|
||||
m_elements[idx] = element;
|
||||
}
|
||||
|
||||
void fill (element_t element)
|
||||
{
|
||||
for (int y = 0; y < m_size.h; y++)
|
||||
for (int x = 0; x < m_size.w; x++)
|
||||
set (coord_t (x, y), element);
|
||||
}
|
||||
|
||||
private:
|
||||
::size_t get_idx (const coord_t &coord) const
|
||||
{
|
||||
gcc_assert (coord.x >= 0);
|
||||
gcc_assert (coord.x < m_size.w);
|
||||
gcc_assert (coord.y >= 0);
|
||||
gcc_assert (coord.y < m_size.h);
|
||||
return (coord.y * m_size.w) + coord.x;
|
||||
}
|
||||
|
||||
size_t m_size;
|
||||
std::vector<element_t> m_elements;
|
||||
};
|
||||
|
||||
/* A combination of attributes describing how to style a text cell.
|
||||
We only support those attributes mentioned in invoke.texi:
|
||||
- bold,
|
||||
- underscore,
|
||||
- blink,
|
||||
- inverse,
|
||||
- colors for foreground and background:
|
||||
- default color
|
||||
- named colors
|
||||
- 16-color mode colors (the "bright" variants)
|
||||
- 88-color mode
|
||||
- 256-color mode
|
||||
plus URLs. */
|
||||
|
||||
struct style
|
||||
{
|
||||
typedef unsigned char id_t;
|
||||
static const id_t id_plain = 0;
|
||||
|
||||
/* Colors. */
|
||||
enum class named_color
|
||||
{
|
||||
DEFAULT,
|
||||
// ANSI order
|
||||
BLACK,
|
||||
RED,
|
||||
GREEN,
|
||||
YELLOW,
|
||||
BLUE,
|
||||
MAGENTA,
|
||||
CYAN,
|
||||
WHITE
|
||||
};
|
||||
|
||||
|
||||
struct color
|
||||
{
|
||||
enum class kind
|
||||
{
|
||||
NAMED,
|
||||
BITS_8,
|
||||
BITS_24,
|
||||
} m_kind;
|
||||
|
||||
union
|
||||
{
|
||||
struct {
|
||||
enum named_color m_name;
|
||||
bool m_bright;
|
||||
} m_named;
|
||||
uint8_t m_8bit;
|
||||
struct {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
} m_24bit;
|
||||
} u;
|
||||
|
||||
/* Constructor for named colors. */
|
||||
color (enum named_color name = named_color::DEFAULT,
|
||||
bool bright = false)
|
||||
: m_kind (kind::NAMED)
|
||||
{
|
||||
u.m_named.m_name = name;
|
||||
u.m_named.m_bright = bright;
|
||||
}
|
||||
|
||||
/* Constructor for 8-bit colors. */
|
||||
color (uint8_t col_val)
|
||||
: m_kind (kind::BITS_8)
|
||||
{
|
||||
u.m_8bit = col_val;
|
||||
}
|
||||
|
||||
/* Constructor for 24-bit colors. */
|
||||
color (uint8_t r, uint8_t g, uint8_t b)
|
||||
: m_kind (kind::BITS_24)
|
||||
{
|
||||
u.m_24bit.r = r;
|
||||
u.m_24bit.g = g;
|
||||
u.m_24bit.b = b;
|
||||
}
|
||||
|
||||
bool operator== (const color &other) const;
|
||||
bool operator!= (const color &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void print_sgr (pretty_printer *pp, bool fg, bool &need_separator) const;
|
||||
};
|
||||
|
||||
style ()
|
||||
: m_bold (false),
|
||||
m_underscore (false),
|
||||
m_blink (false),
|
||||
m_reverse (false),
|
||||
m_fg_color (named_color::DEFAULT),
|
||||
m_bg_color (named_color::DEFAULT),
|
||||
m_url ()
|
||||
{}
|
||||
|
||||
bool operator== (const style &other) const
|
||||
{
|
||||
return (m_bold == other.m_bold
|
||||
&& m_underscore == other.m_underscore
|
||||
&& m_blink == other.m_blink
|
||||
&& m_reverse == other.m_reverse
|
||||
&& m_fg_color == other.m_fg_color
|
||||
&& m_bg_color == other.m_bg_color
|
||||
&& m_url == other.m_url);
|
||||
}
|
||||
|
||||
style &set_style_url (const char *url);
|
||||
|
||||
static void print_changes (pretty_printer *pp,
|
||||
const style &old_style,
|
||||
const style &new_style);
|
||||
|
||||
bool m_bold;
|
||||
bool m_underscore;
|
||||
bool m_blink;
|
||||
bool m_reverse;
|
||||
color m_fg_color;
|
||||
color m_bg_color;
|
||||
std::vector<cppchar_t> m_url; // empty = no URL
|
||||
};
|
||||
|
||||
/* A class to keep track of all the styles in use in a drawing, so that
|
||||
we can refer to them via the compact style::id_t type, rather than
|
||||
via e.g. pointers. */
|
||||
|
||||
class style_manager
|
||||
{
|
||||
public:
|
||||
style_manager ();
|
||||
style::id_t get_or_create_id (const style &style);
|
||||
const style &get_style (style::id_t id) const
|
||||
{
|
||||
return m_styles[id];
|
||||
}
|
||||
void print_any_style_changes (pretty_printer *pp,
|
||||
style::id_t old_id,
|
||||
style::id_t new_id) const;
|
||||
unsigned get_num_styles () const { return m_styles.size (); }
|
||||
|
||||
private:
|
||||
std::vector<style> m_styles;
|
||||
};
|
||||
|
||||
class styled_unichar
|
||||
{
|
||||
public:
|
||||
friend class styled_string;
|
||||
|
||||
explicit styled_unichar ()
|
||||
: m_code (0),
|
||||
m_style_id (style::id_plain)
|
||||
{}
|
||||
explicit styled_unichar (cppchar_t ch)
|
||||
: m_code (ch),
|
||||
m_emoji_variant_p (false),
|
||||
m_style_id (style::id_plain)
|
||||
{}
|
||||
explicit styled_unichar (cppchar_t ch, bool emoji, style::id_t style_id)
|
||||
: m_code (ch),
|
||||
m_emoji_variant_p (emoji),
|
||||
m_style_id (style_id)
|
||||
{
|
||||
gcc_assert (style_id <= 0x7f);
|
||||
}
|
||||
|
||||
cppchar_t get_code () const { return m_code; }
|
||||
bool emoji_variant_p () const { return m_emoji_variant_p; }
|
||||
style::id_t get_style_id () const { return m_style_id; }
|
||||
|
||||
bool double_width_p () const
|
||||
{
|
||||
int width = cpp_wcwidth (get_code ());
|
||||
gcc_assert (width == 1 || width == 2);
|
||||
return width == 2;
|
||||
}
|
||||
|
||||
bool operator== (const styled_unichar &other) const
|
||||
{
|
||||
return (m_code == other.m_code
|
||||
&& m_emoji_variant_p == other.m_emoji_variant_p
|
||||
&& m_style_id == other.m_style_id);
|
||||
}
|
||||
|
||||
void set_emoji_variant () { m_emoji_variant_p = true; }
|
||||
|
||||
int get_canvas_width () const
|
||||
{
|
||||
return cpp_wcwidth (m_code);
|
||||
}
|
||||
|
||||
void add_combining_char (cppchar_t ch)
|
||||
{
|
||||
m_combining_chars.push_back (ch);
|
||||
}
|
||||
|
||||
const std::vector<cppchar_t> get_combining_chars () const
|
||||
{
|
||||
return m_combining_chars;
|
||||
}
|
||||
|
||||
private:
|
||||
cppchar_t m_code : 24;
|
||||
bool m_emoji_variant_p : 1;
|
||||
style::id_t m_style_id : 7;
|
||||
std::vector<cppchar_t> m_combining_chars;
|
||||
};
|
||||
|
||||
class styled_string
|
||||
{
|
||||
public:
|
||||
explicit styled_string () = default;
|
||||
explicit styled_string (style_manager &sm, const char *str);
|
||||
explicit styled_string (cppchar_t cppchar, bool emoji = false);
|
||||
|
||||
styled_string (styled_string &&) = default;
|
||||
styled_string &operator= (styled_string &&) = default;
|
||||
|
||||
/* No copy ctor or assignment op. */
|
||||
styled_string (const styled_string &) = delete;
|
||||
styled_string &operator= (const styled_string &) = delete;
|
||||
|
||||
/* For the few cases where copying is required, spell it out explicitly. */
|
||||
styled_string copy () const
|
||||
{
|
||||
styled_string result;
|
||||
result.m_chars = m_chars;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool operator== (const styled_string &other) const
|
||||
{
|
||||
return m_chars == other.m_chars;
|
||||
}
|
||||
|
||||
static styled_string from_fmt (style_manager &sm,
|
||||
printer_fn format_decoder,
|
||||
const char *fmt, ...)
|
||||
ATTRIBUTE_GCC_PPDIAG(3, 4);
|
||||
static styled_string from_fmt_va (style_manager &sm,
|
||||
printer_fn format_decoder,
|
||||
const char *fmt,
|
||||
va_list *args)
|
||||
ATTRIBUTE_GCC_PPDIAG(3, 0);
|
||||
|
||||
size_t size () const { return m_chars.size (); }
|
||||
styled_unichar operator[] (size_t idx) const { return m_chars[idx]; }
|
||||
|
||||
std::vector<styled_unichar>::const_iterator begin () const
|
||||
{
|
||||
return m_chars.begin ();
|
||||
}
|
||||
std::vector<styled_unichar>::const_iterator end () const
|
||||
{
|
||||
return m_chars.end ();
|
||||
}
|
||||
|
||||
int calc_canvas_width () const;
|
||||
|
||||
void append (const styled_string &suffix);
|
||||
|
||||
void set_url (style_manager &sm, const char *url);
|
||||
|
||||
private:
|
||||
std::vector<styled_unichar> m_chars;
|
||||
};
|
||||
|
||||
enum class x_align
|
||||
{
|
||||
LEFT,
|
||||
CENTER,
|
||||
RIGHT
|
||||
};
|
||||
|
||||
enum class y_align
|
||||
{
|
||||
TOP,
|
||||
CENTER,
|
||||
BOTTOM
|
||||
};
|
||||
|
||||
/* A set of cardinal directions within a canvas or table. */
|
||||
|
||||
struct directions
|
||||
{
|
||||
public:
|
||||
directions (bool up, bool down, bool left, bool right)
|
||||
: m_up (up), m_down (down), m_left (left), m_right (right)
|
||||
{
|
||||
}
|
||||
|
||||
size_t as_index () const
|
||||
{
|
||||
return (m_up << 3) | (m_down << 2) | (m_left << 1) | m_right;
|
||||
}
|
||||
|
||||
bool m_up: 1;
|
||||
bool m_down: 1;
|
||||
bool m_left: 1;
|
||||
bool m_right: 1;
|
||||
};
|
||||
|
||||
} // namespace text_art
|
||||
|
||||
#endif /* GCC_TEXT_ART_TYPES_H */
|
275
gcc/text-art/widget.cc
Normal file
275
gcc/text-art/widget.cc
Normal file
@ -0,0 +1,275 @@
|
||||
/* Hierarchical diagram elements.
|
||||
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"
|
||||
#define INCLUDE_MEMORY
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "pretty-print.h"
|
||||
#include "selftest.h"
|
||||
#include "make-unique.h"
|
||||
#include "text-art/selftests.h"
|
||||
#include "text-art/widget.h"
|
||||
|
||||
using namespace text_art;
|
||||
|
||||
/* class text_art::widget. */
|
||||
|
||||
canvas
|
||||
widget::to_canvas (const style_manager &style_mgr)
|
||||
{
|
||||
const canvas::size_t req_size = get_req_size ();
|
||||
|
||||
/* For now we don't constrain the allocation; we give
|
||||
the widget the full size it requested, and widgets
|
||||
assume they got their full size request. */
|
||||
const canvas::size_t alloc_size = req_size;
|
||||
|
||||
set_alloc_rect (canvas::rect_t (canvas::coord_t (0, 0), alloc_size));
|
||||
canvas c (alloc_size, style_mgr);
|
||||
paint_to_canvas (c);
|
||||
return c;
|
||||
}
|
||||
|
||||
/* class text_art::vbox_widget : public text_art::container_widget. */
|
||||
|
||||
const char *
|
||||
vbox_widget::get_desc () const
|
||||
{
|
||||
return "vbox_widget";
|
||||
}
|
||||
|
||||
canvas::size_t
|
||||
vbox_widget::calc_req_size ()
|
||||
{
|
||||
canvas::size_t result (0, 0);
|
||||
for (auto &child : m_children)
|
||||
{
|
||||
canvas::size_t child_req_size = child->get_req_size();
|
||||
result.h += child_req_size.h;
|
||||
result.w = std::max (result.w, child_req_size.w);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
vbox_widget::update_child_alloc_rects ()
|
||||
{
|
||||
const int x = get_min_x ();
|
||||
int y = get_min_y ();
|
||||
for (auto &child : m_children)
|
||||
{
|
||||
child->set_alloc_rect
|
||||
(canvas::rect_t (canvas::coord_t (x, y),
|
||||
canvas::size_t (get_alloc_w (),
|
||||
child->get_req_h ())));
|
||||
y += child->get_req_h ();
|
||||
}
|
||||
}
|
||||
|
||||
/* class text_art::text_widget : public text_art::leaf_widget. */
|
||||
|
||||
const char *
|
||||
text_widget::get_desc () const
|
||||
{
|
||||
return "text_widget";
|
||||
}
|
||||
|
||||
canvas::size_t
|
||||
text_widget::calc_req_size ()
|
||||
{
|
||||
return canvas::size_t (m_str.size (), 1);
|
||||
}
|
||||
|
||||
void
|
||||
text_widget::paint_to_canvas (canvas &canvas)
|
||||
{
|
||||
canvas.paint_text (get_top_left (), m_str);
|
||||
}
|
||||
|
||||
/* class text_art::canvas_widget : public text_art::leaf_widget. */
|
||||
|
||||
const char *
|
||||
canvas_widget::get_desc () const
|
||||
{
|
||||
return "canvas_widget";
|
||||
}
|
||||
|
||||
canvas::size_t
|
||||
canvas_widget::calc_req_size ()
|
||||
{
|
||||
return m_canvas.get_size ();
|
||||
}
|
||||
|
||||
void
|
||||
canvas_widget::paint_to_canvas (canvas &canvas)
|
||||
{
|
||||
for (int y = 0; y < m_canvas.get_size ().h; y++)
|
||||
for (int x = 0; x < m_canvas.get_size ().w; x++)
|
||||
{
|
||||
canvas::coord_t rel_xy (x, y);
|
||||
canvas.paint (get_top_left () + rel_xy,
|
||||
m_canvas.get (rel_xy));
|
||||
}
|
||||
}
|
||||
|
||||
#if CHECKING_P
|
||||
|
||||
namespace selftest {
|
||||
|
||||
/* Concrete widget subclass for writing selftests.
|
||||
Requests a hard-coded size, and fills its allocated rectangle
|
||||
with a specific character. */
|
||||
|
||||
class test_widget : public leaf_widget
|
||||
{
|
||||
public:
|
||||
test_widget (canvas::size_t size, char ch)
|
||||
: m_test_size (size), m_ch (ch)
|
||||
{}
|
||||
|
||||
const char *get_desc () const final override
|
||||
{
|
||||
return "test_widget";
|
||||
}
|
||||
canvas::size_t calc_req_size () final override
|
||||
{
|
||||
return m_test_size;
|
||||
}
|
||||
void paint_to_canvas (canvas &canvas) final override
|
||||
{
|
||||
canvas.fill (get_alloc_rect (), canvas::cell_t (m_ch));
|
||||
}
|
||||
|
||||
private:
|
||||
canvas::size_t m_test_size;
|
||||
char m_ch;
|
||||
};
|
||||
|
||||
static void
|
||||
test_test_widget ()
|
||||
{
|
||||
style_manager sm;
|
||||
test_widget w (canvas::size_t (3, 3), 'A');
|
||||
canvas c (w.to_canvas (sm));
|
||||
ASSERT_CANVAS_STREQ
|
||||
(c, false,
|
||||
("AAA\n"
|
||||
"AAA\n"
|
||||
"AAA\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_text_widget ()
|
||||
{
|
||||
style_manager sm;
|
||||
text_widget w (styled_string (sm, "hello world"));
|
||||
canvas c (w.to_canvas (sm));
|
||||
ASSERT_CANVAS_STREQ
|
||||
(c, false,
|
||||
("hello world\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_wrapper_widget ()
|
||||
{
|
||||
style_manager sm;
|
||||
wrapper_widget w (::make_unique<test_widget> (canvas::size_t (3, 3), 'B'));
|
||||
canvas c (w.to_canvas (sm));
|
||||
ASSERT_CANVAS_STREQ
|
||||
(c, false,
|
||||
("BBB\n"
|
||||
"BBB\n"
|
||||
"BBB\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_vbox_1 ()
|
||||
{
|
||||
style_manager sm;
|
||||
vbox_widget w;
|
||||
for (int i = 0; i < 5; i++)
|
||||
w.add_child
|
||||
(::make_unique <text_widget>
|
||||
(styled_string::from_fmt (sm, nullptr,
|
||||
"this is line %i", i)));
|
||||
canvas c (w.to_canvas (sm));
|
||||
ASSERT_CANVAS_STREQ
|
||||
(c, false,
|
||||
("this is line 0\n"
|
||||
"this is line 1\n"
|
||||
"this is line 2\n"
|
||||
"this is line 3\n"
|
||||
"this is line 4\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_vbox_2 ()
|
||||
{
|
||||
style_manager sm;
|
||||
vbox_widget w;
|
||||
w.add_child (::make_unique<test_widget> (canvas::size_t (1, 3), 'A'));
|
||||
w.add_child (::make_unique<test_widget> (canvas::size_t (4, 1), 'B'));
|
||||
w.add_child (::make_unique<test_widget> (canvas::size_t (1, 2), 'C'));
|
||||
canvas c (w.to_canvas (sm));
|
||||
ASSERT_CANVAS_STREQ
|
||||
(c, false,
|
||||
("AAAA\n"
|
||||
"AAAA\n"
|
||||
"AAAA\n"
|
||||
"BBBB\n"
|
||||
"CCCC\n"
|
||||
"CCCC\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_canvas_widget ()
|
||||
{
|
||||
style_manager sm;
|
||||
canvas inner_canvas (canvas::size_t (5, 3), sm);
|
||||
inner_canvas.fill (canvas::rect_t (canvas::coord_t (0, 0),
|
||||
canvas::size_t (5, 3)),
|
||||
canvas::cell_t ('a'));
|
||||
canvas_widget cw (std::move (inner_canvas));
|
||||
canvas c (cw.to_canvas (sm));
|
||||
ASSERT_CANVAS_STREQ
|
||||
(c, false,
|
||||
("aaaaa\n"
|
||||
"aaaaa\n"
|
||||
"aaaaa\n"));
|
||||
}
|
||||
|
||||
/* Run all selftests in this file. */
|
||||
|
||||
void
|
||||
text_art_widget_cc_tests ()
|
||||
{
|
||||
test_test_widget ();
|
||||
test_text_widget ();
|
||||
test_wrapper_widget ();
|
||||
test_vbox_1 ();
|
||||
test_vbox_2 ();
|
||||
test_canvas_widget ();
|
||||
}
|
||||
|
||||
} // namespace selftest
|
||||
|
||||
|
||||
#endif /* #if CHECKING_P */
|
246
gcc/text-art/widget.h
Normal file
246
gcc/text-art/widget.h
Normal file
@ -0,0 +1,246 @@
|
||||
/* Hierarchical diagram elements.
|
||||
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/>. */
|
||||
|
||||
#ifndef GCC_TEXT_ART_WIDGET_H
|
||||
#define GCC_TEXT_ART_WIDGET_H
|
||||
|
||||
#include <vector>
|
||||
#include "text-art/canvas.h"
|
||||
#include "text-art/table.h"
|
||||
|
||||
namespace text_art {
|
||||
|
||||
/* Abstract base class: something that knows how to size itself and
|
||||
how to paint itself to a canvas, potentially with children, with
|
||||
support for hierarchical sizing and positioning.
|
||||
|
||||
Widgets have a two-phase sizing/positioning algorithm.
|
||||
|
||||
Step 1: size requests: the root widget is asked for its size request i.e
|
||||
how big it wants to be. This is handled by recursively asking child
|
||||
widgets for their requested sizes. Each widget subclass can implement
|
||||
their own logic for this in the "calc_req_size" vfunc, and the result
|
||||
is cached in m_req_size.
|
||||
|
||||
Step 2: rect allocation: the root widget is set a canvas::rect_t as
|
||||
its "allocated" rectangle. Each widget subclass can then place its
|
||||
children recursively using the "update_child_alloc_rects" vfunc.
|
||||
For simplicity, all coordinates in the hierarchy are within the same
|
||||
coordinate system (rather than attempting to store per-child offsets).
|
||||
|
||||
Widget subclasses are responsible for managing their own children. */
|
||||
|
||||
/* Subclasses in this header, with indentation indicating inheritance. */
|
||||
|
||||
class widget; /* Abstract base class. */
|
||||
class wrapper_widget; /* Concrete subclass: a widget with a single child. */
|
||||
class container_widget; /* Abstract subclass: widgets with an arbitrary
|
||||
number of children. */
|
||||
class vbox_widget; /* Concrete widget subclass: lay out children
|
||||
vertically. */
|
||||
class leaf_widget; /* Abstract subclass: a widget with no children. */
|
||||
class text_widget; /* Concrete subclass: a text string. */
|
||||
class canvas_widget; /* Concrete subclass: a pre-rendered canvas. */
|
||||
|
||||
class widget
|
||||
{
|
||||
public:
|
||||
/* This can be very useful for debugging when implementing new
|
||||
widget subclasses. */
|
||||
static const bool DEBUG_GEOMETRY = false;
|
||||
|
||||
virtual ~widget () {}
|
||||
|
||||
canvas to_canvas (const style_manager &style_mgr);
|
||||
|
||||
canvas::size_t get_req_size ()
|
||||
{
|
||||
m_req_size = calc_req_size();
|
||||
if (DEBUG_GEOMETRY)
|
||||
fprintf (stderr, "calc_req_size (%s) -> (w:%i, h:%i)\n",
|
||||
get_desc (),
|
||||
m_req_size.w, m_req_size.h);
|
||||
return m_req_size;
|
||||
}
|
||||
|
||||
void set_alloc_rect (const canvas::rect_t &rect)
|
||||
{
|
||||
if (DEBUG_GEOMETRY)
|
||||
fprintf (stderr, "set_alloc_rect (%s): ((x:%i, y:%i), (w:%i, h:%i))\n",
|
||||
get_desc (),
|
||||
rect.m_top_left.x, rect.m_top_left.y,
|
||||
rect.m_size.w, rect.m_size.h);
|
||||
m_alloc_rect = rect;
|
||||
update_child_alloc_rects ();
|
||||
}
|
||||
|
||||
virtual const char *get_desc () const = 0;
|
||||
virtual canvas::size_t calc_req_size () = 0;
|
||||
virtual void update_child_alloc_rects () = 0;
|
||||
virtual void paint_to_canvas (canvas &canvas) = 0;
|
||||
|
||||
/* Access to the cached size request of this widget. */
|
||||
const canvas::size_t get_req_size () const { return m_req_size; }
|
||||
int get_req_w () const { return m_req_size.w; }
|
||||
int get_req_h () const { return m_req_size.h; }
|
||||
|
||||
/* Access to the allocated canvas coordinates of this widget. */
|
||||
const canvas::rect_t &get_alloc_rect () const { return m_alloc_rect; }
|
||||
int get_alloc_w () const { return m_alloc_rect.get_width (); }
|
||||
int get_alloc_h () const { return m_alloc_rect.get_height (); }
|
||||
int get_min_x () const { return m_alloc_rect.get_min_x (); }
|
||||
int get_max_x () const { return m_alloc_rect.get_max_x (); }
|
||||
int get_next_x () const { return m_alloc_rect.get_next_x (); }
|
||||
int get_min_y () const { return m_alloc_rect.get_min_y (); }
|
||||
int get_max_y () const { return m_alloc_rect.get_max_y (); }
|
||||
int get_next_y () const { return m_alloc_rect.get_max_y (); }
|
||||
canvas::range_t get_x_range () const { return m_alloc_rect.get_x_range (); }
|
||||
canvas::range_t get_y_range () const { return m_alloc_rect.get_y_range (); }
|
||||
const canvas::coord_t &get_top_left () const
|
||||
{
|
||||
return m_alloc_rect.m_top_left;
|
||||
}
|
||||
|
||||
protected:
|
||||
widget ()
|
||||
: m_req_size (0, 0),
|
||||
m_alloc_rect (canvas::coord_t (0, 0),
|
||||
canvas::size_t (0, 0))
|
||||
{}
|
||||
|
||||
private:
|
||||
/* How much size this widget requested. */
|
||||
canvas::size_t m_req_size;
|
||||
/* Where (and how big) this widget was allocated. */
|
||||
canvas::rect_t m_alloc_rect;
|
||||
};
|
||||
|
||||
/* Concrete subclass for a widget with a single child. */
|
||||
|
||||
class wrapper_widget : public widget
|
||||
{
|
||||
public:
|
||||
wrapper_widget (std::unique_ptr<widget> child)
|
||||
: m_child (std::move (child))
|
||||
{}
|
||||
|
||||
const char *get_desc () const override
|
||||
{
|
||||
return "wrapper_widget";
|
||||
}
|
||||
canvas::size_t calc_req_size () override
|
||||
{
|
||||
return m_child->get_req_size ();
|
||||
}
|
||||
void update_child_alloc_rects ()
|
||||
{
|
||||
m_child->set_alloc_rect (get_alloc_rect ());
|
||||
}
|
||||
void paint_to_canvas (canvas &canvas) override
|
||||
{
|
||||
m_child->paint_to_canvas (canvas);
|
||||
}
|
||||
private:
|
||||
std::unique_ptr<widget> m_child;
|
||||
};
|
||||
|
||||
/* Abstract subclass for widgets with an arbitrary number of children. */
|
||||
|
||||
class container_widget : public widget
|
||||
{
|
||||
public:
|
||||
void add_child (std::unique_ptr<widget> child)
|
||||
{
|
||||
m_children.push_back (std::move (child));
|
||||
}
|
||||
|
||||
void paint_to_canvas (canvas &canvas) final override
|
||||
{
|
||||
for (auto &child : m_children)
|
||||
child->paint_to_canvas (canvas);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<std::unique_ptr<widget>> m_children;
|
||||
};
|
||||
|
||||
/* Concrete widget subclass: lay out children vertically. */
|
||||
|
||||
class vbox_widget : public container_widget
|
||||
{
|
||||
public:
|
||||
const char *get_desc () const override;
|
||||
canvas::size_t calc_req_size () override;
|
||||
void update_child_alloc_rects () final override;
|
||||
};
|
||||
|
||||
/* Abstract subclass for widgets with no children. */
|
||||
|
||||
class leaf_widget : public widget
|
||||
{
|
||||
public:
|
||||
void update_child_alloc_rects () final override
|
||||
{
|
||||
/* no-op. */
|
||||
}
|
||||
|
||||
protected:
|
||||
leaf_widget () : widget () {}
|
||||
};
|
||||
|
||||
/* Concrete widget subclass for a text string. */
|
||||
|
||||
class text_widget : public leaf_widget
|
||||
{
|
||||
public:
|
||||
text_widget (styled_string str)
|
||||
: leaf_widget (), m_str (std::move (str))
|
||||
{
|
||||
}
|
||||
|
||||
const char *get_desc () const override;
|
||||
canvas::size_t calc_req_size () final override;
|
||||
void paint_to_canvas (canvas &canvas) final override;
|
||||
|
||||
private:
|
||||
styled_string m_str;
|
||||
};
|
||||
|
||||
/* Concrete widget subclass for a pre-rendered canvas. */
|
||||
|
||||
class canvas_widget : public leaf_widget
|
||||
{
|
||||
public:
|
||||
canvas_widget (canvas &&c)
|
||||
: leaf_widget (), m_canvas (std::move (c))
|
||||
{
|
||||
}
|
||||
|
||||
const char *get_desc () const override;
|
||||
canvas::size_t calc_req_size () final override;
|
||||
void paint_to_canvas (canvas &canvas) final override;
|
||||
|
||||
private:
|
||||
canvas m_canvas;
|
||||
};
|
||||
|
||||
} // namespace text_art
|
||||
|
||||
#endif /* GCC_TEXT_ART_WIDGET_H */
|
@ -3154,6 +3154,40 @@ cpp_display_column_to_byte_column (const char *data, int data_length,
|
||||
return dw.bytes_processed () + MAX (0, display_col - avail_display);
|
||||
}
|
||||
|
||||
template <typename PropertyType>
|
||||
PropertyType
|
||||
get_cppchar_property (cppchar_t c,
|
||||
const cppchar_t *range_ends,
|
||||
const PropertyType *range_values,
|
||||
size_t num_ranges,
|
||||
PropertyType default_value)
|
||||
{
|
||||
if (__builtin_expect (c <= range_ends[0], true))
|
||||
return range_values[0];
|
||||
|
||||
/* Binary search the tables. */
|
||||
int begin = 1;
|
||||
static const int end = num_ranges;
|
||||
int len = end - begin;
|
||||
do
|
||||
{
|
||||
int half = len/2;
|
||||
int middle = begin + half;
|
||||
if (c > range_ends[middle])
|
||||
{
|
||||
begin = middle + 1;
|
||||
len -= half + 1;
|
||||
}
|
||||
else
|
||||
len = half;
|
||||
} while (len);
|
||||
|
||||
if (__builtin_expect (begin != end, true))
|
||||
return range_values[begin];
|
||||
|
||||
return default_value;
|
||||
}
|
||||
|
||||
/* Our own version of wcwidth(). We don't use the actual wcwidth() in glibc,
|
||||
because that will inspect the user's locale, and in particular in an ASCII
|
||||
locale, it will not return anything useful for extended characters. But GCC
|
||||
@ -3167,30 +3201,43 @@ cpp_display_column_to_byte_column (const char *data, int data_length,
|
||||
diagnostics, they are sufficient. */
|
||||
|
||||
#include "generated_cpp_wcwidth.h"
|
||||
int cpp_wcwidth (cppchar_t c)
|
||||
|
||||
int
|
||||
cpp_wcwidth (cppchar_t c)
|
||||
{
|
||||
if (__builtin_expect (c <= wcwidth_range_ends[0], true))
|
||||
return wcwidth_widths[0];
|
||||
|
||||
/* Binary search the tables. */
|
||||
int begin = 1;
|
||||
static const int end
|
||||
= sizeof wcwidth_range_ends / sizeof (*wcwidth_range_ends);
|
||||
int len = end - begin;
|
||||
do
|
||||
{
|
||||
int half = len/2;
|
||||
int middle = begin + half;
|
||||
if (c > wcwidth_range_ends[middle])
|
||||
{
|
||||
begin = middle + 1;
|
||||
len -= half + 1;
|
||||
}
|
||||
else
|
||||
len = half;
|
||||
} while (len);
|
||||
|
||||
if (__builtin_expect (begin != end, true))
|
||||
return wcwidth_widths[begin];
|
||||
return 1;
|
||||
const size_t num_ranges
|
||||
= sizeof wcwidth_range_ends / sizeof (*wcwidth_range_ends);
|
||||
return get_cppchar_property<unsigned char > (c,
|
||||
&wcwidth_range_ends[0],
|
||||
&wcwidth_widths[0],
|
||||
num_ranges,
|
||||
1);
|
||||
}
|
||||
|
||||
#include "combining-chars.inc"
|
||||
|
||||
bool
|
||||
cpp_is_combining_char (cppchar_t c)
|
||||
{
|
||||
const size_t num_ranges
|
||||
= sizeof combining_range_ends / sizeof (*combining_range_ends);
|
||||
return get_cppchar_property<bool> (c,
|
||||
&combining_range_ends[0],
|
||||
&is_combining[0],
|
||||
num_ranges,
|
||||
false);
|
||||
}
|
||||
|
||||
#include "printable-chars.inc"
|
||||
|
||||
bool
|
||||
cpp_is_printable_char (cppchar_t c)
|
||||
{
|
||||
const size_t num_ranges
|
||||
= sizeof printable_range_ends / sizeof (*printable_range_ends);
|
||||
return get_cppchar_property<bool> (c,
|
||||
&printable_range_ends[0],
|
||||
&is_printable[0],
|
||||
num_ranges,
|
||||
false);
|
||||
}
|
||||
|
68
libcpp/combining-chars.inc
Normal file
68
libcpp/combining-chars.inc
Normal file
@ -0,0 +1,68 @@
|
||||
/* Generated by contrib/unicode/gen-combining-chars.py
|
||||
using version 12.1.0 of the Unicode standard. */
|
||||
|
||||
static const cppchar_t combining_range_ends[] = {
|
||||
0x2ff, 0x34e, 0x34f, 0x36f, 0x482, 0x487, 0x590, 0x5bd,
|
||||
0x5be, 0x5bf, 0x5c0, 0x5c2, 0x5c3, 0x5c5, 0x5c6, 0x5c7,
|
||||
0x60f, 0x61a, 0x64a, 0x65f, 0x66f, 0x670, 0x6d5, 0x6dc,
|
||||
0x6de, 0x6e4, 0x6e6, 0x6e8, 0x6e9, 0x6ed, 0x710, 0x711,
|
||||
0x72f, 0x74a, 0x7ea, 0x7f3, 0x7fc, 0x7fd, 0x815, 0x819,
|
||||
0x81a, 0x823, 0x824, 0x827, 0x828, 0x82d, 0x858, 0x85b,
|
||||
0x8d2, 0x8e1, 0x8e2, 0x8ff, 0x93b, 0x93c, 0x94c, 0x94d,
|
||||
0x950, 0x954, 0x9bb, 0x9bc, 0x9cc, 0x9cd, 0x9fd, 0x9fe,
|
||||
0xa3b, 0xa3c, 0xa4c, 0xa4d, 0xabb, 0xabc, 0xacc, 0xacd,
|
||||
0xb3b, 0xb3c, 0xb4c, 0xb4d, 0xbcc, 0xbcd, 0xc4c, 0xc4d,
|
||||
0xc54, 0xc56, 0xcbb, 0xcbc, 0xccc, 0xccd, 0xd3a, 0xd3c,
|
||||
0xd4c, 0xd4d, 0xdc9, 0xdca, 0xe37, 0xe3a, 0xe47, 0xe4b,
|
||||
0xeb7, 0xeba, 0xec7, 0xecb, 0xf17, 0xf19, 0xf34, 0xf35,
|
||||
0xf36, 0xf37, 0xf38, 0xf39, 0xf70, 0xf72, 0xf73, 0xf74,
|
||||
0xf79, 0xf7d, 0xf7f, 0xf80, 0xf81, 0xf84, 0xf85, 0xf87,
|
||||
0xfc5, 0xfc6, 0x1036, 0x1037, 0x1038, 0x103a, 0x108c, 0x108d,
|
||||
0x135c, 0x135f, 0x1713, 0x1714, 0x1733, 0x1734, 0x17d1, 0x17d2,
|
||||
0x17dc, 0x17dd, 0x18a8, 0x18a9, 0x1938, 0x193b, 0x1a16, 0x1a18,
|
||||
0x1a5f, 0x1a60, 0x1a74, 0x1a7c, 0x1a7e, 0x1a7f, 0x1aaf, 0x1abd,
|
||||
0x1b33, 0x1b34, 0x1b43, 0x1b44, 0x1b6a, 0x1b73, 0x1ba9, 0x1bab,
|
||||
0x1be5, 0x1be6, 0x1bf1, 0x1bf3, 0x1c36, 0x1c37, 0x1ccf, 0x1cd2,
|
||||
0x1cd3, 0x1ce0, 0x1ce1, 0x1ce8, 0x1cec, 0x1ced, 0x1cf3, 0x1cf4,
|
||||
0x1cf7, 0x1cf9, 0x1dbf, 0x1df9, 0x1dfa, 0x1dff, 0x20cf, 0x20dc,
|
||||
0x20e0, 0x20e1, 0x20e4, 0x20f0, 0x2cee, 0x2cf1, 0x2d7e, 0x2d7f,
|
||||
0x2ddf, 0x2dff, 0x3029, 0x302f, 0x3098, 0x309a, 0xa66e, 0xa66f,
|
||||
0xa673, 0xa67d, 0xa69d, 0xa69f, 0xa6ef, 0xa6f1, 0xa805, 0xa806,
|
||||
0xa8c3, 0xa8c4, 0xa8df, 0xa8f1, 0xa92a, 0xa92d, 0xa952, 0xa953,
|
||||
0xa9b2, 0xa9b3, 0xa9bf, 0xa9c0, 0xaaaf, 0xaab0, 0xaab1, 0xaab4,
|
||||
0xaab6, 0xaab8, 0xaabd, 0xaabf, 0xaac0, 0xaac1, 0xaaf5, 0xaaf6,
|
||||
0xabec, 0xabed, 0xfb1d, 0xfb1e, 0xfe1f, 0xfe2f, 0x101fc, 0x101fd,
|
||||
0x102df, 0x102e0, 0x10375, 0x1037a, 0x10a0c, 0x10a0d, 0x10a0e, 0x10a0f,
|
||||
0x10a37, 0x10a3a, 0x10a3e, 0x10a3f, 0x10ae4, 0x10ae6, 0x10d23, 0x10d27,
|
||||
0x10f45, 0x10f50, 0x11045, 0x11046, 0x1107e, 0x1107f, 0x110b8, 0x110ba,
|
||||
0x110ff, 0x11102, 0x11132, 0x11134, 0x11172, 0x11173, 0x111bf, 0x111c0,
|
||||
0x111c9, 0x111ca, 0x11234, 0x11236, 0x112e8, 0x112ea, 0x1133a, 0x1133c,
|
||||
0x1134c, 0x1134d, 0x11365, 0x1136c, 0x1136f, 0x11374, 0x11441, 0x11442,
|
||||
0x11445, 0x11446, 0x1145d, 0x1145e, 0x114c1, 0x114c3, 0x115be, 0x115c0,
|
||||
0x1163e, 0x1163f, 0x116b5, 0x116b7, 0x1172a, 0x1172b, 0x11838, 0x1183a,
|
||||
0x119df, 0x119e0, 0x11a33, 0x11a34, 0x11a46, 0x11a47, 0x11a98, 0x11a99,
|
||||
0x11c3e, 0x11c3f, 0x11d41, 0x11d42, 0x11d43, 0x11d45, 0x11d96, 0x11d97,
|
||||
0x16aef, 0x16af4, 0x16b2f, 0x16b36, 0x1bc9d, 0x1bc9e, 0x1d164, 0x1d169,
|
||||
0x1d16c, 0x1d172, 0x1d17a, 0x1d182, 0x1d184, 0x1d18b, 0x1d1a9, 0x1d1ad,
|
||||
0x1d241, 0x1d244, 0x1dfff, 0x1e006, 0x1e007, 0x1e018, 0x1e01a, 0x1e021,
|
||||
0x1e022, 0x1e024, 0x1e025, 0x1e02a, 0x1e12f, 0x1e136, 0x1e2eb, 0x1e2ef,
|
||||
0x1e8cf, 0x1e8d6, 0x1e943, 0x1e94a, 0x10fffe,
|
||||
};
|
||||
|
||||
static const bool is_combining[] = {
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
};
|
@ -1602,4 +1602,7 @@ bool cpp_input_conversion_is_trivial (const char *input_charset);
|
||||
int cpp_check_utf8_bom (const char *data, size_t data_length);
|
||||
bool cpp_valid_utf8_p (const char *data, size_t num_bytes);
|
||||
|
||||
bool cpp_is_combining_char (cppchar_t c);
|
||||
bool cpp_is_printable_char (cppchar_t c);
|
||||
|
||||
#endif /* ! LIBCPP_CPPLIB_H */
|
||||
|
231
libcpp/printable-chars.inc
Normal file
231
libcpp/printable-chars.inc
Normal file
@ -0,0 +1,231 @@
|
||||
/* Generated by contrib/unicode/gen-printable-chars.py
|
||||
using version 12.1.0 of the Unicode standard. */
|
||||
|
||||
static const cppchar_t printable_range_ends[] = {
|
||||
0x1f, 0x7e, 0x9f, 0xac, 0xad, 0x377, 0x379, 0x37f,
|
||||
0x383, 0x38a, 0x38b, 0x38c, 0x38d, 0x3a1, 0x3a2, 0x52f,
|
||||
0x530, 0x556, 0x558, 0x58a, 0x58c, 0x58f, 0x590, 0x5c7,
|
||||
0x5cf, 0x5ea, 0x5ee, 0x5f4, 0x605, 0x61b, 0x61d, 0x6dc,
|
||||
0x6dd, 0x70d, 0x70f, 0x74a, 0x74c, 0x7b1, 0x7bf, 0x7fa,
|
||||
0x7fc, 0x82d, 0x82f, 0x83e, 0x83f, 0x85b, 0x85d, 0x85e,
|
||||
0x85f, 0x86a, 0x89f, 0x8b4, 0x8b5, 0x8bd, 0x8d2, 0x8e1,
|
||||
0x8e2, 0x983, 0x984, 0x98c, 0x98e, 0x990, 0x992, 0x9a8,
|
||||
0x9a9, 0x9b0, 0x9b1, 0x9b2, 0x9b5, 0x9b9, 0x9bb, 0x9c4,
|
||||
0x9c6, 0x9c8, 0x9ca, 0x9ce, 0x9d6, 0x9d7, 0x9db, 0x9dd,
|
||||
0x9de, 0x9e3, 0x9e5, 0x9fe, 0xa00, 0xa03, 0xa04, 0xa0a,
|
||||
0xa0e, 0xa10, 0xa12, 0xa28, 0xa29, 0xa30, 0xa31, 0xa33,
|
||||
0xa34, 0xa36, 0xa37, 0xa39, 0xa3b, 0xa3c, 0xa3d, 0xa42,
|
||||
0xa46, 0xa48, 0xa4a, 0xa4d, 0xa50, 0xa51, 0xa58, 0xa5c,
|
||||
0xa5d, 0xa5e, 0xa65, 0xa76, 0xa80, 0xa83, 0xa84, 0xa8d,
|
||||
0xa8e, 0xa91, 0xa92, 0xaa8, 0xaa9, 0xab0, 0xab1, 0xab3,
|
||||
0xab4, 0xab9, 0xabb, 0xac5, 0xac6, 0xac9, 0xaca, 0xacd,
|
||||
0xacf, 0xad0, 0xadf, 0xae3, 0xae5, 0xaf1, 0xaf8, 0xaff,
|
||||
0xb00, 0xb03, 0xb04, 0xb0c, 0xb0e, 0xb10, 0xb12, 0xb28,
|
||||
0xb29, 0xb30, 0xb31, 0xb33, 0xb34, 0xb39, 0xb3b, 0xb44,
|
||||
0xb46, 0xb48, 0xb4a, 0xb4d, 0xb55, 0xb57, 0xb5b, 0xb5d,
|
||||
0xb5e, 0xb63, 0xb65, 0xb77, 0xb81, 0xb83, 0xb84, 0xb8a,
|
||||
0xb8d, 0xb90, 0xb91, 0xb95, 0xb98, 0xb9a, 0xb9b, 0xb9c,
|
||||
0xb9d, 0xb9f, 0xba2, 0xba4, 0xba7, 0xbaa, 0xbad, 0xbb9,
|
||||
0xbbd, 0xbc2, 0xbc5, 0xbc8, 0xbc9, 0xbcd, 0xbcf, 0xbd0,
|
||||
0xbd6, 0xbd7, 0xbe5, 0xbfa, 0xbff, 0xc0c, 0xc0d, 0xc10,
|
||||
0xc11, 0xc28, 0xc29, 0xc39, 0xc3c, 0xc44, 0xc45, 0xc48,
|
||||
0xc49, 0xc4d, 0xc54, 0xc56, 0xc57, 0xc5a, 0xc5f, 0xc63,
|
||||
0xc65, 0xc6f, 0xc76, 0xc8c, 0xc8d, 0xc90, 0xc91, 0xca8,
|
||||
0xca9, 0xcb3, 0xcb4, 0xcb9, 0xcbb, 0xcc4, 0xcc5, 0xcc8,
|
||||
0xcc9, 0xccd, 0xcd4, 0xcd6, 0xcdd, 0xcde, 0xcdf, 0xce3,
|
||||
0xce5, 0xcef, 0xcf0, 0xcf2, 0xcff, 0xd03, 0xd04, 0xd0c,
|
||||
0xd0d, 0xd10, 0xd11, 0xd44, 0xd45, 0xd48, 0xd49, 0xd4f,
|
||||
0xd53, 0xd63, 0xd65, 0xd7f, 0xd81, 0xd83, 0xd84, 0xd96,
|
||||
0xd99, 0xdb1, 0xdb2, 0xdbb, 0xdbc, 0xdbd, 0xdbf, 0xdc6,
|
||||
0xdc9, 0xdca, 0xdce, 0xdd4, 0xdd5, 0xdd6, 0xdd7, 0xddf,
|
||||
0xde5, 0xdef, 0xdf1, 0xdf4, 0xe00, 0xe3a, 0xe3e, 0xe5b,
|
||||
0xe80, 0xe82, 0xe83, 0xe84, 0xe85, 0xe8a, 0xe8b, 0xea3,
|
||||
0xea4, 0xea5, 0xea6, 0xebd, 0xebf, 0xec4, 0xec5, 0xec6,
|
||||
0xec7, 0xecd, 0xecf, 0xed9, 0xedb, 0xedf, 0xeff, 0xf47,
|
||||
0xf48, 0xf6c, 0xf70, 0xf97, 0xf98, 0xfbc, 0xfbd, 0xfcc,
|
||||
0xfcd, 0xfda, 0xfff, 0x10c5, 0x10c6, 0x10c7, 0x10cc, 0x10cd,
|
||||
0x10cf, 0x1248, 0x1249, 0x124d, 0x124f, 0x1256, 0x1257, 0x1258,
|
||||
0x1259, 0x125d, 0x125f, 0x1288, 0x1289, 0x128d, 0x128f, 0x12b0,
|
||||
0x12b1, 0x12b5, 0x12b7, 0x12be, 0x12bf, 0x12c0, 0x12c1, 0x12c5,
|
||||
0x12c7, 0x12d6, 0x12d7, 0x1310, 0x1311, 0x1315, 0x1317, 0x135a,
|
||||
0x135c, 0x137c, 0x137f, 0x1399, 0x139f, 0x13f5, 0x13f7, 0x13fd,
|
||||
0x13ff, 0x169c, 0x169f, 0x16f8, 0x16ff, 0x170c, 0x170d, 0x1714,
|
||||
0x171f, 0x1736, 0x173f, 0x1753, 0x175f, 0x176c, 0x176d, 0x1770,
|
||||
0x1771, 0x1773, 0x177f, 0x17dd, 0x17df, 0x17e9, 0x17ef, 0x17f9,
|
||||
0x17ff, 0x180d, 0x180f, 0x1819, 0x181f, 0x1878, 0x187f, 0x18aa,
|
||||
0x18af, 0x18f5, 0x18ff, 0x191e, 0x191f, 0x192b, 0x192f, 0x193b,
|
||||
0x193f, 0x1940, 0x1943, 0x196d, 0x196f, 0x1974, 0x197f, 0x19ab,
|
||||
0x19af, 0x19c9, 0x19cf, 0x19da, 0x19dd, 0x1a1b, 0x1a1d, 0x1a5e,
|
||||
0x1a5f, 0x1a7c, 0x1a7e, 0x1a89, 0x1a8f, 0x1a99, 0x1a9f, 0x1aad,
|
||||
0x1aaf, 0x1abe, 0x1aff, 0x1b4b, 0x1b4f, 0x1b7c, 0x1b7f, 0x1bf3,
|
||||
0x1bfb, 0x1c37, 0x1c3a, 0x1c49, 0x1c4c, 0x1c88, 0x1c8f, 0x1cba,
|
||||
0x1cbc, 0x1cc7, 0x1ccf, 0x1cfa, 0x1cff, 0x1df9, 0x1dfa, 0x1f15,
|
||||
0x1f17, 0x1f1d, 0x1f1f, 0x1f45, 0x1f47, 0x1f4d, 0x1f4f, 0x1f57,
|
||||
0x1f58, 0x1f59, 0x1f5a, 0x1f5b, 0x1f5c, 0x1f5d, 0x1f5e, 0x1f7d,
|
||||
0x1f7f, 0x1fb4, 0x1fb5, 0x1fc4, 0x1fc5, 0x1fd3, 0x1fd5, 0x1fdb,
|
||||
0x1fdc, 0x1fef, 0x1ff1, 0x1ff4, 0x1ff5, 0x1ffe, 0x1fff, 0x200a,
|
||||
0x200f, 0x2029, 0x202e, 0x205f, 0x206f, 0x2071, 0x2073, 0x208e,
|
||||
0x208f, 0x209c, 0x209f, 0x20bf, 0x20cf, 0x20f0, 0x20ff, 0x218b,
|
||||
0x218f, 0x2426, 0x243f, 0x244a, 0x245f, 0x2b73, 0x2b75, 0x2b95,
|
||||
0x2b97, 0x2c2e, 0x2c2f, 0x2c5e, 0x2c5f, 0x2cf3, 0x2cf8, 0x2d25,
|
||||
0x2d26, 0x2d27, 0x2d2c, 0x2d2d, 0x2d2f, 0x2d67, 0x2d6e, 0x2d70,
|
||||
0x2d7e, 0x2d96, 0x2d9f, 0x2da6, 0x2da7, 0x2dae, 0x2daf, 0x2db6,
|
||||
0x2db7, 0x2dbe, 0x2dbf, 0x2dc6, 0x2dc7, 0x2dce, 0x2dcf, 0x2dd6,
|
||||
0x2dd7, 0x2dde, 0x2ddf, 0x2e4f, 0x2e7f, 0x2e99, 0x2e9a, 0x2ef3,
|
||||
0x2eff, 0x2fd5, 0x2fef, 0x2ffb, 0x2fff, 0x303f, 0x3040, 0x3096,
|
||||
0x3098, 0x30ff, 0x3104, 0x312f, 0x3130, 0x318e, 0x318f, 0x31ba,
|
||||
0x31bf, 0x31e3, 0x31ef, 0x321e, 0x321f, 0x4db5, 0x4dbf, 0x9fef,
|
||||
0x9fff, 0xa48c, 0xa48f, 0xa4c6, 0xa4cf, 0xa62b, 0xa63f, 0xa6f7,
|
||||
0xa6ff, 0xa7bf, 0xa7c1, 0xa7c6, 0xa7f6, 0xa82b, 0xa82f, 0xa839,
|
||||
0xa83f, 0xa877, 0xa87f, 0xa8c5, 0xa8cd, 0xa8d9, 0xa8df, 0xa953,
|
||||
0xa95e, 0xa97c, 0xa97f, 0xa9cd, 0xa9ce, 0xa9d9, 0xa9dd, 0xa9fe,
|
||||
0xa9ff, 0xaa36, 0xaa3f, 0xaa4d, 0xaa4f, 0xaa59, 0xaa5b, 0xaac2,
|
||||
0xaada, 0xaaf6, 0xab00, 0xab06, 0xab08, 0xab0e, 0xab10, 0xab16,
|
||||
0xab1f, 0xab26, 0xab27, 0xab2e, 0xab2f, 0xab67, 0xab6f, 0xabed,
|
||||
0xabef, 0xabf9, 0xabff, 0xd7a3, 0xd7af, 0xd7c6, 0xd7ca, 0xd7fb,
|
||||
0xf8ff, 0xfa6d, 0xfa6f, 0xfad9, 0xfaff, 0xfb06, 0xfb12, 0xfb17,
|
||||
0xfb1c, 0xfb36, 0xfb37, 0xfb3c, 0xfb3d, 0xfb3e, 0xfb3f, 0xfb41,
|
||||
0xfb42, 0xfb44, 0xfb45, 0xfbc1, 0xfbd2, 0xfd3f, 0xfd4f, 0xfd8f,
|
||||
0xfd91, 0xfdc7, 0xfdef, 0xfdfd, 0xfdff, 0xfe19, 0xfe1f, 0xfe52,
|
||||
0xfe53, 0xfe66, 0xfe67, 0xfe6b, 0xfe6f, 0xfe74, 0xfe75, 0xfefc,
|
||||
0xff00, 0xffbe, 0xffc1, 0xffc7, 0xffc9, 0xffcf, 0xffd1, 0xffd7,
|
||||
0xffd9, 0xffdc, 0xffdf, 0xffe6, 0xffe7, 0xffee, 0xfffb, 0xfffd,
|
||||
0xffff, 0x1000b, 0x1000c, 0x10026, 0x10027, 0x1003a, 0x1003b, 0x1003d,
|
||||
0x1003e, 0x1004d, 0x1004f, 0x1005d, 0x1007f, 0x100fa, 0x100ff, 0x10102,
|
||||
0x10106, 0x10133, 0x10136, 0x1018e, 0x1018f, 0x1019b, 0x1019f, 0x101a0,
|
||||
0x101cf, 0x101fd, 0x1027f, 0x1029c, 0x1029f, 0x102d0, 0x102df, 0x102fb,
|
||||
0x102ff, 0x10323, 0x1032c, 0x1034a, 0x1034f, 0x1037a, 0x1037f, 0x1039d,
|
||||
0x1039e, 0x103c3, 0x103c7, 0x103d5, 0x103ff, 0x1049d, 0x1049f, 0x104a9,
|
||||
0x104af, 0x104d3, 0x104d7, 0x104fb, 0x104ff, 0x10527, 0x1052f, 0x10563,
|
||||
0x1056e, 0x1056f, 0x105ff, 0x10736, 0x1073f, 0x10755, 0x1075f, 0x10767,
|
||||
0x107ff, 0x10805, 0x10807, 0x10808, 0x10809, 0x10835, 0x10836, 0x10838,
|
||||
0x1083b, 0x1083c, 0x1083e, 0x10855, 0x10856, 0x1089e, 0x108a6, 0x108af,
|
||||
0x108df, 0x108f2, 0x108f3, 0x108f5, 0x108fa, 0x1091b, 0x1091e, 0x10939,
|
||||
0x1093e, 0x1093f, 0x1097f, 0x109b7, 0x109bb, 0x109cf, 0x109d1, 0x10a03,
|
||||
0x10a04, 0x10a06, 0x10a0b, 0x10a13, 0x10a14, 0x10a17, 0x10a18, 0x10a35,
|
||||
0x10a37, 0x10a3a, 0x10a3e, 0x10a48, 0x10a4f, 0x10a58, 0x10a5f, 0x10a9f,
|
||||
0x10abf, 0x10ae6, 0x10aea, 0x10af6, 0x10aff, 0x10b35, 0x10b38, 0x10b55,
|
||||
0x10b57, 0x10b72, 0x10b77, 0x10b91, 0x10b98, 0x10b9c, 0x10ba8, 0x10baf,
|
||||
0x10bff, 0x10c48, 0x10c7f, 0x10cb2, 0x10cbf, 0x10cf2, 0x10cf9, 0x10d27,
|
||||
0x10d2f, 0x10d39, 0x10e5f, 0x10e7e, 0x10eff, 0x10f27, 0x10f2f, 0x10f59,
|
||||
0x10fdf, 0x10ff6, 0x10fff, 0x1104d, 0x11051, 0x1106f, 0x1107e, 0x110bc,
|
||||
0x110bd, 0x110c1, 0x110cf, 0x110e8, 0x110ef, 0x110f9, 0x110ff, 0x11134,
|
||||
0x11135, 0x11146, 0x1114f, 0x11176, 0x1117f, 0x111cd, 0x111cf, 0x111df,
|
||||
0x111e0, 0x111f4, 0x111ff, 0x11211, 0x11212, 0x1123e, 0x1127f, 0x11286,
|
||||
0x11287, 0x11288, 0x11289, 0x1128d, 0x1128e, 0x1129d, 0x1129e, 0x112a9,
|
||||
0x112af, 0x112ea, 0x112ef, 0x112f9, 0x112ff, 0x11303, 0x11304, 0x1130c,
|
||||
0x1130e, 0x11310, 0x11312, 0x11328, 0x11329, 0x11330, 0x11331, 0x11333,
|
||||
0x11334, 0x11339, 0x1133a, 0x11344, 0x11346, 0x11348, 0x1134a, 0x1134d,
|
||||
0x1134f, 0x11350, 0x11356, 0x11357, 0x1135c, 0x11363, 0x11365, 0x1136c,
|
||||
0x1136f, 0x11374, 0x113ff, 0x11459, 0x1145a, 0x1145b, 0x1145c, 0x1145f,
|
||||
0x1147f, 0x114c7, 0x114cf, 0x114d9, 0x1157f, 0x115b5, 0x115b7, 0x115dd,
|
||||
0x115ff, 0x11644, 0x1164f, 0x11659, 0x1165f, 0x1166c, 0x1167f, 0x116b8,
|
||||
0x116bf, 0x116c9, 0x116ff, 0x1171a, 0x1171c, 0x1172b, 0x1172f, 0x1173f,
|
||||
0x117ff, 0x1183b, 0x1189f, 0x118f2, 0x118fe, 0x118ff, 0x1199f, 0x119a7,
|
||||
0x119a9, 0x119d7, 0x119d9, 0x119e4, 0x119ff, 0x11a47, 0x11a4f, 0x11aa2,
|
||||
0x11abf, 0x11af8, 0x11bff, 0x11c08, 0x11c09, 0x11c36, 0x11c37, 0x11c45,
|
||||
0x11c4f, 0x11c6c, 0x11c6f, 0x11c8f, 0x11c91, 0x11ca7, 0x11ca8, 0x11cb6,
|
||||
0x11cff, 0x11d06, 0x11d07, 0x11d09, 0x11d0a, 0x11d36, 0x11d39, 0x11d3a,
|
||||
0x11d3b, 0x11d3d, 0x11d3e, 0x11d47, 0x11d4f, 0x11d59, 0x11d5f, 0x11d65,
|
||||
0x11d66, 0x11d68, 0x11d69, 0x11d8e, 0x11d8f, 0x11d91, 0x11d92, 0x11d98,
|
||||
0x11d9f, 0x11da9, 0x11edf, 0x11ef8, 0x11fbf, 0x11ff1, 0x11ffe, 0x12399,
|
||||
0x123ff, 0x1246e, 0x1246f, 0x12474, 0x1247f, 0x12543, 0x12fff, 0x1342e,
|
||||
0x143ff, 0x14646, 0x167ff, 0x16a38, 0x16a3f, 0x16a5e, 0x16a5f, 0x16a69,
|
||||
0x16a6d, 0x16a6f, 0x16acf, 0x16aed, 0x16aef, 0x16af5, 0x16aff, 0x16b45,
|
||||
0x16b4f, 0x16b59, 0x16b5a, 0x16b61, 0x16b62, 0x16b77, 0x16b7c, 0x16b8f,
|
||||
0x16e3f, 0x16e9a, 0x16eff, 0x16f4a, 0x16f4e, 0x16f87, 0x16f8e, 0x16f9f,
|
||||
0x16fdf, 0x16fe3, 0x16fff, 0x187f7, 0x187ff, 0x18af2, 0x1afff, 0x1b11e,
|
||||
0x1b14f, 0x1b152, 0x1b163, 0x1b167, 0x1b16f, 0x1b2fb, 0x1bbff, 0x1bc6a,
|
||||
0x1bc6f, 0x1bc7c, 0x1bc7f, 0x1bc88, 0x1bc8f, 0x1bc99, 0x1bc9b, 0x1bc9f,
|
||||
0x1cfff, 0x1d0f5, 0x1d0ff, 0x1d126, 0x1d128, 0x1d172, 0x1d17a, 0x1d1e8,
|
||||
0x1d1ff, 0x1d245, 0x1d2df, 0x1d2f3, 0x1d2ff, 0x1d356, 0x1d35f, 0x1d378,
|
||||
0x1d3ff, 0x1d454, 0x1d455, 0x1d49c, 0x1d49d, 0x1d49f, 0x1d4a1, 0x1d4a2,
|
||||
0x1d4a4, 0x1d4a6, 0x1d4a8, 0x1d4ac, 0x1d4ad, 0x1d4b9, 0x1d4ba, 0x1d4bb,
|
||||
0x1d4bc, 0x1d4c3, 0x1d4c4, 0x1d505, 0x1d506, 0x1d50a, 0x1d50c, 0x1d514,
|
||||
0x1d515, 0x1d51c, 0x1d51d, 0x1d539, 0x1d53a, 0x1d53e, 0x1d53f, 0x1d544,
|
||||
0x1d545, 0x1d546, 0x1d549, 0x1d550, 0x1d551, 0x1d6a5, 0x1d6a7, 0x1d7cb,
|
||||
0x1d7cd, 0x1da8b, 0x1da9a, 0x1da9f, 0x1daa0, 0x1daaf, 0x1dfff, 0x1e006,
|
||||
0x1e007, 0x1e018, 0x1e01a, 0x1e021, 0x1e022, 0x1e024, 0x1e025, 0x1e02a,
|
||||
0x1e0ff, 0x1e12c, 0x1e12f, 0x1e13d, 0x1e13f, 0x1e149, 0x1e14d, 0x1e14f,
|
||||
0x1e2bf, 0x1e2f9, 0x1e2fe, 0x1e2ff, 0x1e7ff, 0x1e8c4, 0x1e8c6, 0x1e8d6,
|
||||
0x1e8ff, 0x1e94b, 0x1e94f, 0x1e959, 0x1e95d, 0x1e95f, 0x1ec70, 0x1ecb4,
|
||||
0x1ed00, 0x1ed3d, 0x1edff, 0x1ee03, 0x1ee04, 0x1ee1f, 0x1ee20, 0x1ee22,
|
||||
0x1ee23, 0x1ee24, 0x1ee26, 0x1ee27, 0x1ee28, 0x1ee32, 0x1ee33, 0x1ee37,
|
||||
0x1ee38, 0x1ee39, 0x1ee3a, 0x1ee3b, 0x1ee41, 0x1ee42, 0x1ee46, 0x1ee47,
|
||||
0x1ee48, 0x1ee49, 0x1ee4a, 0x1ee4b, 0x1ee4c, 0x1ee4f, 0x1ee50, 0x1ee52,
|
||||
0x1ee53, 0x1ee54, 0x1ee56, 0x1ee57, 0x1ee58, 0x1ee59, 0x1ee5a, 0x1ee5b,
|
||||
0x1ee5c, 0x1ee5d, 0x1ee5e, 0x1ee5f, 0x1ee60, 0x1ee62, 0x1ee63, 0x1ee64,
|
||||
0x1ee66, 0x1ee6a, 0x1ee6b, 0x1ee72, 0x1ee73, 0x1ee77, 0x1ee78, 0x1ee7c,
|
||||
0x1ee7d, 0x1ee7e, 0x1ee7f, 0x1ee89, 0x1ee8a, 0x1ee9b, 0x1eea0, 0x1eea3,
|
||||
0x1eea4, 0x1eea9, 0x1eeaa, 0x1eebb, 0x1eeef, 0x1eef1, 0x1efff, 0x1f02b,
|
||||
0x1f02f, 0x1f093, 0x1f09f, 0x1f0ae, 0x1f0b0, 0x1f0bf, 0x1f0c0, 0x1f0cf,
|
||||
0x1f0d0, 0x1f0f5, 0x1f0ff, 0x1f10c, 0x1f10f, 0x1f16c, 0x1f16f, 0x1f1ac,
|
||||
0x1f1e5, 0x1f202, 0x1f20f, 0x1f23b, 0x1f23f, 0x1f248, 0x1f24f, 0x1f251,
|
||||
0x1f25f, 0x1f265, 0x1f2ff, 0x1f6d5, 0x1f6df, 0x1f6ec, 0x1f6ef, 0x1f6fa,
|
||||
0x1f6ff, 0x1f773, 0x1f77f, 0x1f7d8, 0x1f7df, 0x1f7eb, 0x1f7ff, 0x1f80b,
|
||||
0x1f80f, 0x1f847, 0x1f84f, 0x1f859, 0x1f85f, 0x1f887, 0x1f88f, 0x1f8ad,
|
||||
0x1f8ff, 0x1f90b, 0x1f90c, 0x1f971, 0x1f972, 0x1f976, 0x1f979, 0x1f9a2,
|
||||
0x1f9a4, 0x1f9aa, 0x1f9ad, 0x1f9ca, 0x1f9cc, 0x1fa53, 0x1fa5f, 0x1fa6d,
|
||||
0x1fa6f, 0x1fa73, 0x1fa77, 0x1fa7a, 0x1fa7f, 0x1fa82, 0x1fa8f, 0x1fa95,
|
||||
0x1ffff, 0x2a6d6, 0x2a6ff, 0x2b734, 0x2b73f, 0x2b81d, 0x2b81f, 0x2cea1,
|
||||
0x2ceaf, 0x2ebe0, 0x2f7ff, 0x2fa1d, 0xe00ff, 0xe01ef, 0x10fffe,
|
||||
};
|
||||
|
||||
static const bool is_printable[] = {
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
};
|
Loading…
Reference in New Issue
Block a user