Branch data Line data Source code
1 : : /* Classes for styling text cells (color, URLs).
2 : : Copyright (C) 2023-2024 Free Software Foundation, Inc.
3 : : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 : :
5 : : This file is part of GCC.
6 : :
7 : : GCC is free software; you can redistribute it and/or modify it under
8 : : the terms of the GNU General Public License as published by the Free
9 : : Software Foundation; either version 3, or (at your option) any later
10 : : version.
11 : :
12 : : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : : WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : : for more details.
16 : :
17 : : You should have received a copy of the GNU General Public License
18 : : along with GCC; see the file COPYING3. If not see
19 : : <http://www.gnu.org/licenses/>. */
20 : :
21 : : #include "config.h"
22 : : #define INCLUDE_ALGORITHM
23 : : #define INCLUDE_MEMORY
24 : : #define INCLUDE_VECTOR
25 : : #include "system.h"
26 : : #include "coretypes.h"
27 : : #include "make-unique.h"
28 : : #include "pretty-print.h"
29 : : #include "intl.h"
30 : : #include "selftest.h"
31 : : #include "text-art/selftests.h"
32 : : #include "text-art/types.h"
33 : : #include "color-macros.h"
34 : : #include "diagnostic-color.h"
35 : :
36 : : using namespace text_art;
37 : :
38 : : /* class text_art::style. */
39 : :
40 : : style &
41 : 24 : style::set_style_url (const char *url)
42 : : {
43 : 24 : m_url.clear ();
44 : 696 : while (*url)
45 : 648 : m_url.push_back (*(url++));
46 : 24 : return *this;
47 : : }
48 : :
49 : : /* class text_art::style::color. */
50 : :
51 : : bool
52 : 10392 : style::color::operator== (const style::color &other) const
53 : : {
54 : 10392 : if (m_kind != other.m_kind)
55 : : return false;
56 : 9424 : switch (m_kind)
57 : : {
58 : 0 : default:
59 : 0 : gcc_unreachable ();
60 : 8074 : case kind::NAMED:
61 : 8074 : return (u.m_named.m_name == other.u.m_named.m_name
62 : 8074 : && u.m_named.m_bright == other.u.m_named.m_bright);
63 : 8 : case kind::BITS_8:
64 : 8 : return u.m_8bit == other.u.m_8bit;
65 : 1342 : case kind::BITS_24:
66 : 1342 : return (u.m_24bit.r == other.u.m_24bit.r
67 : : && u.m_24bit.g == other.u.m_24bit.g
68 : 1342 : && u.m_24bit.b == other.u.m_24bit.b);
69 : : }
70 : : }
71 : :
72 : : static void
73 : 516 : ensure_separator (pretty_printer *pp, bool &need_separator)
74 : : {
75 : 0 : if (need_separator)
76 : 112 : pp_string (pp, COLOR_SEPARATOR);
77 : 516 : need_separator = true;
78 : 0 : }
79 : :
80 : : void
81 : 1056 : style::color::print_sgr (pretty_printer *pp,
82 : : bool fg,
83 : : bool &need_separator) const
84 : : {
85 : 1056 : switch (m_kind)
86 : : {
87 : 0 : default:
88 : 0 : gcc_unreachable ();
89 : 808 : case kind::NAMED:
90 : 808 : {
91 : 808 : static const char * const fg_normal[] = {"", // reset, for DEFAULT
92 : : COLOR_FG_BLACK,
93 : : COLOR_FG_RED,
94 : : COLOR_FG_GREEN,
95 : : COLOR_FG_YELLOW,
96 : : COLOR_FG_BLUE,
97 : : COLOR_FG_MAGENTA,
98 : : COLOR_FG_CYAN,
99 : : COLOR_FG_WHITE};
100 : 808 : static const char * const fg_bright[] = {"", // reset, for DEFAULT
101 : : COLOR_FG_BRIGHT_BLACK,
102 : : COLOR_FG_BRIGHT_RED,
103 : : COLOR_FG_BRIGHT_GREEN,
104 : : COLOR_FG_BRIGHT_YELLOW,
105 : : COLOR_FG_BRIGHT_BLUE,
106 : : COLOR_FG_BRIGHT_MAGENTA,
107 : : COLOR_FG_BRIGHT_CYAN,
108 : : COLOR_FG_BRIGHT_WHITE};
109 : 808 : static const char * const bg_normal[] = {"", // reset, for DEFAULT
110 : : COLOR_BG_BLACK,
111 : : COLOR_BG_RED,
112 : : COLOR_BG_GREEN,
113 : : COLOR_BG_YELLOW,
114 : : COLOR_BG_BLUE,
115 : : COLOR_BG_MAGENTA,
116 : : COLOR_BG_CYAN,
117 : : COLOR_BG_WHITE};
118 : 808 : static const char * const bg_bright[] = {"", // reset, for DEFAULT
119 : : COLOR_BG_BRIGHT_BLACK,
120 : : COLOR_BG_BRIGHT_RED,
121 : : COLOR_BG_BRIGHT_GREEN,
122 : : COLOR_BG_BRIGHT_YELLOW,
123 : : COLOR_BG_BRIGHT_BLUE,
124 : : COLOR_BG_BRIGHT_MAGENTA,
125 : : COLOR_BG_BRIGHT_CYAN,
126 : : COLOR_BG_BRIGHT_WHITE};
127 : 808 : STATIC_ASSERT (ARRAY_SIZE (fg_normal) == ARRAY_SIZE (fg_bright));
128 : 808 : STATIC_ASSERT (ARRAY_SIZE (fg_normal) == ARRAY_SIZE (bg_normal));
129 : 808 : STATIC_ASSERT (ARRAY_SIZE (fg_normal) == ARRAY_SIZE (bg_bright));
130 : 808 : gcc_assert ((size_t)u.m_named.m_name < ARRAY_SIZE (fg_normal));
131 : 808 : const char *const *arr;
132 : 808 : if (fg)
133 : 436 : arr = u.m_named.m_bright ? fg_bright : fg_normal;
134 : : else
135 : 372 : arr = u.m_named.m_bright ? bg_bright : bg_normal;
136 : 808 : const char *str = arr[(size_t)u.m_named.m_name];
137 : 808 : if (strlen (str) > 0)
138 : : {
139 : 236 : ensure_separator (pp, need_separator);
140 : 236 : pp_string (pp, str);
141 : : }
142 : : }
143 : : break;
144 : 48 : case kind::BITS_8:
145 : 48 : {
146 : 48 : ensure_separator (pp, need_separator);
147 : 48 : if (fg)
148 : 24 : pp_string (pp, "38");
149 : : else
150 : 24 : pp_string (pp, "48");
151 : 48 : pp_printf (pp, ";5;%i", (int)u.m_8bit);
152 : : }
153 : 48 : break;
154 : 200 : case kind::BITS_24:
155 : 200 : {
156 : 200 : ensure_separator (pp, need_separator);
157 : 200 : if (fg)
158 : 68 : pp_string (pp, "38");
159 : : else
160 : 132 : pp_string (pp, "48");
161 : 200 : pp_printf (pp, ";2;%i;%i;%i",
162 : 200 : (int)u.m_24bit.r,
163 : 200 : (int)u.m_24bit.g,
164 : 200 : (int)u.m_24bit.b);
165 : : }
166 : 200 : break;
167 : : }
168 : 1056 : }
169 : :
170 : : /* class text_art::style. */
171 : :
172 : : /* See https://www.ecma-international.org/wp-content/uploads/ECMA-48_5th_edition_june_1991.pdf
173 : : GRCM - GRAPHIC RENDITION COMBINATION MODE can be "REPLACING" or
174 : : "CUMULATIVE", which affects whether we need to respecify all attributes
175 : : at each SGR, or can accumulate them. Looks like we can't rely on the value
176 : : of this, so we have to emit a single SGR for all changes, with a "0" reset
177 : : at the front, forcing it to be effectively replacing. */
178 : :
179 : : void
180 : 5252 : style::print_changes (pretty_printer *pp,
181 : : const style &old_style,
182 : : const style &new_style)
183 : : {
184 : 5252 : if (pp_show_color (pp))
185 : : {
186 : 568 : bool needs_sgr = ((old_style.m_bold != new_style.m_bold)
187 : : || (old_style.m_underscore != new_style.m_underscore)
188 : 568 : || (old_style.m_blink != new_style.m_blink)
189 : 504 : || (old_style.m_fg_color != new_style.m_fg_color)
190 : 832 : || (old_style.m_bg_color != new_style.m_bg_color));
191 : 528 : if (needs_sgr)
192 : : {
193 : 1056 : bool emit_reset = (old_style.m_bold
194 : 504 : || new_style.m_bold
195 : 480 : || old_style.m_underscore
196 : 476 : || new_style.m_underscore
197 : 472 : || old_style.m_blink
198 : 996 : || new_style.m_blink);
199 : 528 : bool need_separator = false;
200 : :
201 : 528 : pp_string (pp, SGR_START);
202 : 528 : if (emit_reset)
203 : : {
204 : 64 : pp_string (pp, COLOR_NONE);
205 : 64 : need_separator = true;
206 : : }
207 : 528 : if (new_style.m_bold)
208 : : {
209 : 24 : gcc_assert (emit_reset);
210 : 24 : ensure_separator (pp, need_separator);
211 : 24 : pp_string (pp, COLOR_BOLD);
212 : : }
213 : 528 : if (new_style.m_underscore)
214 : : {
215 : 4 : gcc_assert (emit_reset);
216 : 4 : ensure_separator (pp, need_separator);
217 : 4 : pp_string (pp, COLOR_UNDERSCORE);
218 : : }
219 : 528 : if (new_style.m_blink)
220 : : {
221 : 4 : gcc_assert (emit_reset);
222 : 4 : ensure_separator (pp, need_separator);
223 : 4 : pp_string (pp, COLOR_BLINK);
224 : : }
225 : 528 : new_style.m_fg_color.print_sgr (pp, true, need_separator);
226 : 528 : new_style.m_bg_color.print_sgr (pp, false, need_separator);
227 : 528 : pp_string (pp, SGR_END);
228 : : }
229 : : }
230 : :
231 : 5252 : if (old_style.m_url != new_style.m_url)
232 : : {
233 : 48 : if (!old_style.m_url.empty ())
234 : 24 : pp_end_url (pp);
235 : 48 : if (pp->url_format != URL_FORMAT_NONE
236 : 48 : && !new_style.m_url.empty ())
237 : : {
238 : : /* Adapted from pp_begin_url, but encoding the
239 : : chars to UTF-8 on the fly, rather than converting
240 : : to a buffer. */
241 : 16 : pp_string (pp, "\33]8;;");
242 : 448 : for (auto ch : new_style.m_url)
243 : 432 : pp_unicode_character (pp, ch);
244 : 16 : switch (pp->url_format)
245 : : {
246 : 0 : default:
247 : 0 : case URL_FORMAT_NONE:
248 : 0 : gcc_unreachable ();
249 : 8 : case URL_FORMAT_ST:
250 : 8 : pp_string (pp, "\33\\");
251 : 8 : break;
252 : 8 : case URL_FORMAT_BEL:
253 : 8 : pp_string (pp, "\a");
254 : 8 : break;
255 : : }
256 : : }
257 : : }
258 : 5252 : }
259 : :
260 : : /* Look up the current SGR codes for a color capability NAME
261 : : (from GCC_COLORS or the defaults), and convert them to
262 : : a text_art::style. */
263 : :
264 : : style
265 : 146 : text_art::get_style_from_color_cap_name (const char *name)
266 : : {
267 : 146 : const char *sgr_codes = colorize_start (true, name);
268 : 146 : gcc_assert (sgr_codes);
269 : :
270 : : /* Parse the sgr codes. We expect the resulting styled_string to be
271 : : empty; we're interested in the final style created during parsing. */
272 : 146 : style_manager sm;
273 : 146 : styled_string styled_str (sm, sgr_codes);
274 : 146 : return sm.get_style (sm.get_num_styles () - 1);
275 : 146 : }
276 : :
277 : : /* class text_art::style_manager. */
278 : :
279 : 503 : style_manager::style_manager ()
280 : : {
281 : : // index 0 will be the default style
282 : 503 : m_styles.push_back (style ());
283 : 503 : }
284 : :
285 : : style::id_t
286 : 2221 : style_manager::get_or_create_id (const style &s)
287 : : {
288 : : // For now, linear search
289 : 2221 : std::vector<style>::iterator existing
290 : 2221 : (std::find (m_styles.begin (), m_styles.end (), s));
291 : :
292 : : /* If found, return index of slot. */
293 : 2221 : if (existing != m_styles.end ())
294 : 1436 : return std::distance (m_styles.begin (), existing);
295 : :
296 : : /* Not found. */
297 : :
298 : : /* styled_str uses 7 bits for style information, so we can only support
299 : : up to 128 different style combinations.
300 : : Gracefully fail by turning off styling when this limit is reached. */
301 : 785 : if (m_styles.size () >= 127)
302 : : return 0;
303 : :
304 : 785 : m_styles.push_back (s);
305 : 785 : return m_styles.size () - 1;
306 : : }
307 : :
308 : : void
309 : 7131 : style_manager::print_any_style_changes (pretty_printer *pp,
310 : : style::id_t old_id,
311 : : style::id_t new_id) const
312 : : {
313 : 7131 : gcc_assert (pp);
314 : 7131 : if (old_id == new_id)
315 : : return;
316 : :
317 : 5028 : const style &old_style = m_styles[old_id];
318 : 5028 : const style &new_style = m_styles[new_id];
319 : 5028 : gcc_assert (!(old_style == new_style));
320 : 5028 : style::print_changes (pp, old_style, new_style);
321 : : }
322 : :
323 : : #if CHECKING_P
324 : :
325 : : namespace selftest {
326 : :
327 : : void
328 : 224 : assert_style_change_streq (const location &loc,
329 : : const style &old_style,
330 : : const style &new_style,
331 : : const char *expected_str)
332 : : {
333 : 224 : pretty_printer pp;
334 : 224 : pp_show_color (&pp) = true;
335 : 224 : style::print_changes (&pp, old_style, new_style);
336 : 224 : ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected_str);
337 : 224 : }
338 : :
339 : : #define ASSERT_STYLE_CHANGE_STREQ(OLD_STYLE, NEW_STYLE, EXPECTED_STR) \
340 : : SELFTEST_BEGIN_STMT \
341 : : assert_style_change_streq ((SELFTEST_LOCATION), \
342 : : (OLD_STYLE), \
343 : : (NEW_STYLE), \
344 : : (EXPECTED_STR)); \
345 : : SELFTEST_END_STMT
346 : :
347 : : static void
348 : 4 : test_bold ()
349 : : {
350 : 4 : style_manager sm;
351 : 4 : ASSERT_EQ (sm.get_num_styles (), 1);
352 : :
353 : 4 : style plain;
354 : 4 : ASSERT_EQ (sm.get_or_create_id (plain), 0);
355 : 4 : ASSERT_EQ (sm.get_num_styles (), 1);
356 : :
357 : 4 : style bold;
358 : 4 : bold.m_bold = true;
359 : :
360 : 4 : ASSERT_EQ (sm.get_or_create_id (bold), 1);
361 : 4 : ASSERT_EQ (sm.get_num_styles (), 2);
362 : 4 : ASSERT_EQ (sm.get_or_create_id (bold), 1);
363 : 4 : ASSERT_EQ (sm.get_num_styles (), 2);
364 : :
365 : 4 : ASSERT_STYLE_CHANGE_STREQ (plain, bold, "\33[00;01m\33[K");
366 : 4 : ASSERT_STYLE_CHANGE_STREQ (bold, plain, "\33[00m\33[K");
367 : 4 : }
368 : :
369 : : static void
370 : 4 : test_underscore ()
371 : : {
372 : 4 : style_manager sm;
373 : 4 : ASSERT_EQ (sm.get_num_styles (), 1);
374 : :
375 : 4 : style plain;
376 : 4 : ASSERT_EQ (sm.get_or_create_id (plain), 0);
377 : 4 : ASSERT_EQ (sm.get_num_styles (), 1);
378 : :
379 : 4 : style underscore;
380 : 4 : underscore.m_underscore = true;
381 : :
382 : 4 : ASSERT_EQ (sm.get_or_create_id (underscore), 1);
383 : 4 : ASSERT_EQ (sm.get_num_styles (), 2);
384 : 4 : ASSERT_EQ (sm.get_or_create_id (underscore), 1);
385 : 4 : ASSERT_EQ (sm.get_num_styles (), 2);
386 : :
387 : 4 : ASSERT_STYLE_CHANGE_STREQ (plain, underscore, "\33[00;04m\33[K");
388 : 4 : ASSERT_STYLE_CHANGE_STREQ (underscore, plain, "\33[00m\33[K");
389 : 4 : }
390 : :
391 : : static void
392 : 4 : test_blink ()
393 : : {
394 : 4 : style_manager sm;
395 : 4 : ASSERT_EQ (sm.get_num_styles (), 1);
396 : :
397 : 4 : style plain;
398 : 4 : ASSERT_EQ (sm.get_or_create_id (plain), 0);
399 : 4 : ASSERT_EQ (sm.get_num_styles (), 1);
400 : :
401 : 4 : style blink;
402 : 4 : blink.m_blink = true;
403 : :
404 : 4 : ASSERT_EQ (sm.get_or_create_id (blink), 1);
405 : 4 : ASSERT_EQ (sm.get_num_styles (), 2);
406 : 4 : ASSERT_EQ (sm.get_or_create_id (blink), 1);
407 : 4 : ASSERT_EQ (sm.get_num_styles (), 2);
408 : :
409 : 4 : ASSERT_STYLE_CHANGE_STREQ (plain, blink, "\33[00;05m\33[K");
410 : 4 : ASSERT_STYLE_CHANGE_STREQ (blink, plain, "\33[00m\33[K");
411 : 4 : }
412 : :
413 : : #define ASSERT_NAMED_COL_STREQ(NAMED_COLOR, FG, BRIGHT, EXPECTED_STR) \
414 : : SELFTEST_BEGIN_STMT \
415 : : { \
416 : : style plain; \
417 : : style s; \
418 : : if (FG) \
419 : : s.m_fg_color = style::color ((NAMED_COLOR), (BRIGHT)); \
420 : : else \
421 : : s.m_bg_color = style::color ((NAMED_COLOR), (BRIGHT)); \
422 : : assert_style_change_streq ((SELFTEST_LOCATION), \
423 : : plain, \
424 : : s, \
425 : : (EXPECTED_STR)); \
426 : : } \
427 : : SELFTEST_END_STMT
428 : :
429 : : static void
430 : 4 : test_named_colors ()
431 : : {
432 : : /* Foreground colors. */
433 : 4 : {
434 : 4 : const bool fg = true;
435 : 4 : {
436 : 4 : const bool bright = false;
437 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::DEFAULT, fg, bright, "");
438 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::BLACK, fg, bright,
439 : : "[30m[K");
440 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::RED, fg, bright,
441 : : "[31m[K");
442 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::GREEN, fg, bright,
443 : : "[32m[K");
444 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::YELLOW, fg, bright,
445 : : "[33m[K");
446 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::BLUE, fg, bright,
447 : : "[34m[K");
448 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::MAGENTA, fg, bright,
449 : : "[35m[K");
450 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::CYAN, fg, bright,
451 : : "[36m[K");
452 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::WHITE, fg, bright,
453 : : "[37m[K");
454 : : }
455 : 4 : {
456 : 4 : const bool bright = true;
457 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::DEFAULT, fg, bright,
458 : : "[m[K");
459 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::BLACK, fg, bright,
460 : : "[90m[K");
461 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::RED, fg, bright,
462 : : "[91m[K");
463 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::GREEN, fg, bright,
464 : : "[92m[K");
465 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::YELLOW, fg, bright,
466 : : "[93m[K");
467 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::BLUE, fg, bright,
468 : : "[94m[K");
469 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::MAGENTA, fg, bright,
470 : : "[95m[K");
471 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::CYAN, fg, bright,
472 : : "[96m[K");
473 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::WHITE, fg, bright,
474 : : "[97m[K");
475 : : }
476 : : }
477 : :
478 : : /* Background colors. */
479 : 4 : {
480 : 4 : const bool fg = false;
481 : 4 : {
482 : 4 : const bool bright = false;
483 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::DEFAULT, fg, bright, "");
484 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::BLACK, fg, bright,
485 : : "[40m[K");
486 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::RED, fg, bright,
487 : : "[41m[K");
488 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::GREEN, fg, bright,
489 : : "[42m[K");
490 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::YELLOW, fg, bright,
491 : : "[43m[K");
492 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::BLUE, fg, bright,
493 : : "[44m[K");
494 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::MAGENTA, fg, bright,
495 : : "[45m[K");
496 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::CYAN, fg, bright,
497 : : "[46m[K");
498 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::WHITE, fg, bright,
499 : : "[47m[K");
500 : : }
501 : 4 : {
502 : 4 : const bool bright = true;
503 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::DEFAULT, fg, bright,
504 : : "[m[K");
505 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::BLACK, fg, bright,
506 : : "[100m[K");
507 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::RED, fg, bright,
508 : : "[101m[K");
509 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::GREEN, fg, bright,
510 : : "[102m[K");
511 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::YELLOW, fg, bright,
512 : : "[103m[K");
513 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::BLUE, fg, bright,
514 : : "[104m[K");
515 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::MAGENTA, fg, bright,
516 : : "[105m[K");
517 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::CYAN, fg, bright,
518 : : "[106m[K");
519 : 4 : ASSERT_NAMED_COL_STREQ (style::named_color::WHITE, fg, bright,
520 : : "[107m[K");
521 : : }
522 : : }
523 : 4 : }
524 : :
525 : : #define ASSERT_8_BIT_COL_STREQ(COL_VAL, FG, EXPECTED_STR) \
526 : : SELFTEST_BEGIN_STMT \
527 : : { \
528 : : style plain; \
529 : : style s; \
530 : : if (FG) \
531 : : s.m_fg_color = style::color (COL_VAL); \
532 : : else \
533 : : s.m_bg_color = style::color (COL_VAL); \
534 : : assert_style_change_streq ((SELFTEST_LOCATION), \
535 : : plain, \
536 : : s, \
537 : : (EXPECTED_STR)); \
538 : : } \
539 : : SELFTEST_END_STMT
540 : :
541 : : static void
542 : 4 : test_8_bit_colors ()
543 : : {
544 : : /* Foreground colors. */
545 : 4 : {
546 : 4 : const bool fg = true;
547 : : /* 0-15: standard and high-intensity standard colors. */
548 : 4 : ASSERT_8_BIT_COL_STREQ (0, fg, "[38;5;0m[K");
549 : 4 : ASSERT_8_BIT_COL_STREQ (15, fg, "[38;5;15m[K");
550 : : /* 16-231: 6x6x6 color cube. */
551 : 4 : ASSERT_8_BIT_COL_STREQ (16, fg, "[38;5;16m[K");
552 : 4 : ASSERT_8_BIT_COL_STREQ (231, fg, "[38;5;231m[K");
553 : : /* 232-255: grayscale. */
554 : 4 : ASSERT_8_BIT_COL_STREQ (232, fg, "[38;5;232m[K");
555 : 4 : ASSERT_8_BIT_COL_STREQ (255, fg, "[38;5;255m[K");
556 : : }
557 : : /* Background colors. */
558 : 4 : {
559 : 4 : const bool fg = false;
560 : : /* 0-15: standard and high-intensity standard colors. */
561 : 4 : ASSERT_8_BIT_COL_STREQ (0, fg, "[48;5;0m[K");
562 : 4 : ASSERT_8_BIT_COL_STREQ (15, fg, "[48;5;15m[K");
563 : : /* 16-231: 6x6x6 color cube. */
564 : 4 : ASSERT_8_BIT_COL_STREQ (16, fg, "[48;5;16m[K");
565 : 4 : ASSERT_8_BIT_COL_STREQ (231, fg, "[48;5;231m[K");
566 : : /* 232-255: grayscale. */
567 : 4 : ASSERT_8_BIT_COL_STREQ (232, fg, "[48;5;232m[K");
568 : 4 : ASSERT_8_BIT_COL_STREQ (255, fg, "[48;5;255m[K");
569 : : }
570 : 4 : }
571 : :
572 : : #define ASSERT_24_BIT_COL_STREQ(R, G, B, FG, EXPECTED_STR) \
573 : : SELFTEST_BEGIN_STMT \
574 : : { \
575 : : style plain; \
576 : : style s; \
577 : : if (FG) \
578 : : s.m_fg_color = style::color ((R), (G), (B)); \
579 : : else \
580 : : s.m_bg_color = style::color ((R), (G), (B)); \
581 : : assert_style_change_streq ((SELFTEST_LOCATION), \
582 : : plain, \
583 : : s, \
584 : : (EXPECTED_STR)); \
585 : : } \
586 : : SELFTEST_END_STMT
587 : :
588 : : static void
589 : 4 : test_24_bit_colors ()
590 : : {
591 : : /* Foreground colors. */
592 : 4 : {
593 : 4 : const bool fg = true;
594 : : // #F3FAF2:
595 : 4 : ASSERT_24_BIT_COL_STREQ (0xf3, 0xfa, 0xf2, fg,
596 : : "[38;2;243;250;242m[K");
597 : : }
598 : : /* Background colors. */
599 : 4 : {
600 : 4 : const bool fg = false;
601 : : // #FDF7E7
602 : 4 : ASSERT_24_BIT_COL_STREQ (0xfd, 0xf7, 0xe7, fg,
603 : : "[48;2;253;247;231m[K");
604 : : }
605 : 4 : }
606 : :
607 : : static void
608 : 4 : test_style_combinations ()
609 : : {
610 : 4 : style_manager sm;
611 : 4 : ASSERT_EQ (sm.get_num_styles (), 1);
612 : :
613 : 4 : style plain;
614 : 4 : ASSERT_EQ (sm.get_or_create_id (plain), 0);
615 : 4 : ASSERT_EQ (sm.get_num_styles (), 1);
616 : :
617 : 4 : style bold;
618 : 4 : bold.m_bold = true;
619 : :
620 : 4 : ASSERT_EQ (sm.get_or_create_id (bold), 1);
621 : 4 : ASSERT_EQ (sm.get_num_styles (), 2);
622 : 4 : ASSERT_EQ (sm.get_or_create_id (bold), 1);
623 : 4 : ASSERT_EQ (sm.get_num_styles (), 2);
624 : :
625 : 4 : style magenta_on_blue;
626 : 4 : magenta_on_blue.m_fg_color = style::named_color::MAGENTA;
627 : 4 : magenta_on_blue.m_bg_color = style::named_color::BLUE;
628 : 4 : ASSERT_EQ (sm.get_or_create_id (magenta_on_blue), 2);
629 : 4 : ASSERT_EQ (sm.get_num_styles (), 3);
630 : 4 : ASSERT_EQ (sm.get_or_create_id (magenta_on_blue), 2);
631 : 4 : ASSERT_EQ (sm.get_num_styles (), 3);
632 : 4 : }
633 : :
634 : : /* Run all selftests in this file. */
635 : :
636 : : void
637 : 4 : text_art_style_cc_tests ()
638 : : {
639 : 4 : test_bold ();
640 : 4 : test_underscore ();
641 : 4 : test_blink ();
642 : 4 : test_named_colors ();
643 : 4 : test_8_bit_colors ();
644 : 4 : test_24_bit_colors ();
645 : 4 : test_style_combinations ();
646 : 4 : }
647 : :
648 : : } // namespace selftest
649 : :
650 : :
651 : : #endif /* #if CHECKING_P */
|