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