LCOV - code coverage report
Current view: top level - gcc/text-art - ruler.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 95.5 % 331 316
Test Date: 2026-02-28 14:20:25 Functions: 95.7 % 23 22
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Classes for printing labelled rulers.
       2              :    Copyright (C) 2023-2026 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_VECTOR
      24              : #include "system.h"
      25              : #include "coretypes.h"
      26              : #include "pretty-print.h"
      27              : #include "selftest.h"
      28              : #include "text-art/selftests.h"
      29              : #include "text-art/ruler.h"
      30              : #include "text-art/theme.h"
      31              : 
      32              : using namespace text_art;
      33              : 
      34              : void
      35          410 : x_ruler::add_label (const canvas::range_t &r,
      36              :                     styled_string text,
      37              :                     style::id_t style_id,
      38              :                     label_kind kind)
      39              : {
      40          410 :   m_labels.push_back (label (r, std::move (text), style_id, kind));
      41          410 :   m_has_layout = false;
      42          410 : }
      43              : 
      44              : int
      45         1391 : x_ruler::get_canvas_y (int rel_y) const
      46              : {
      47         1391 :   gcc_assert (rel_y >= 0);
      48         1391 :   gcc_assert (rel_y < m_size.h);
      49         1391 :   switch (m_label_dir)
      50              :     {
      51            0 :     default:
      52            0 :       gcc_unreachable ();
      53          184 :     case label_dir::ABOVE:
      54          184 :       return m_size.h - (rel_y + 1);
      55              :     case label_dir::BELOW:
      56              :       return rel_y;
      57              :     }
      58              : }
      59              : 
      60              : void
      61          148 : x_ruler::paint_to_canvas (canvas &canvas,
      62              :                           canvas::coord_t offset,
      63              :                           const theme &theme)
      64              : {
      65          148 :   ensure_layout ();
      66              : 
      67          148 :   if (0)
      68              :     canvas.fill (canvas::rect_t (offset, m_size),
      69              :                  canvas::cell_t ('*'));
      70              : 
      71          453 :   for (size_t idx = 0; idx < m_labels.size (); idx++)
      72              :     {
      73          305 :       const label &iter_label = m_labels[idx];
      74              : 
      75              :       /* Paint the ruler itself.  */
      76          305 :       const int ruler_rel_y = get_canvas_y (0);
      77          305 :       for (int rel_x = iter_label.m_range.start;
      78         6547 :            rel_x < iter_label.m_range.next;
      79              :            rel_x++)
      80              :         {
      81         6242 :           enum theme::cell_kind kind = theme::cell_kind::X_RULER_MIDDLE;
      82              : 
      83         6242 :           if (rel_x == iter_label.m_range.start)
      84              :             {
      85          305 :               kind = theme::cell_kind::X_RULER_LEFT_EDGE;
      86          305 :               if (idx > 0)
      87              :                 {
      88          157 :                   const label &prev_label = m_labels[idx - 1];
      89          157 :                   if (prev_label.m_range.get_max () == iter_label.m_range.start)
      90         6242 :                     kind = theme::cell_kind::X_RULER_INTERNAL_EDGE;
      91              :                 }
      92              :             }
      93         5937 :           else if (rel_x == iter_label.m_range.get_max ())
      94              :             kind = theme::cell_kind::X_RULER_RIGHT_EDGE;
      95         5632 :           else if (rel_x == iter_label.m_connector_x)
      96              :             {
      97          305 :               switch (m_label_dir)
      98              :                 {
      99            0 :                 default:
     100            0 :                   gcc_unreachable ();
     101              :                 case label_dir::ABOVE:
     102              :                   kind = theme::cell_kind::X_RULER_CONNECTOR_TO_LABEL_ABOVE;
     103              :                   break;
     104          257 :                 case label_dir::BELOW:
     105          257 :                   kind = theme::cell_kind::X_RULER_CONNECTOR_TO_LABEL_BELOW;
     106          257 :                   break;
     107              :                 }
     108              :             }
     109         6242 :           canvas.paint (canvas::coord_t (rel_x, ruler_rel_y) + offset,
     110        12484 :                         theme.get_cell (kind, iter_label.m_style_id));
     111              :         }
     112              : 
     113              :       /* Paint the connector to the text.  */
     114          403 :       for (int connector_rel_y = 1;
     115          708 :            connector_rel_y < iter_label.m_text_rect.get_min_y ();
     116              :            connector_rel_y++)
     117              :         {
     118          403 :           canvas.paint
     119          403 :             ((canvas::coord_t (iter_label.m_connector_x,
     120          403 :                                get_canvas_y (connector_rel_y))
     121              :               + offset),
     122          403 :              theme.get_cell (theme::cell_kind::X_RULER_VERTICAL_CONNECTOR,
     123          403 :                              iter_label.m_style_id));
     124              :         }
     125              : 
     126              :       /* Paint the text.  */
     127          305 :       switch (iter_label.m_kind)
     128              :         {
     129            0 :         default:
     130            0 :           gcc_unreachable ();
     131          116 :         case x_ruler::label_kind::TEXT:
     132          116 :           canvas.paint_text
     133          116 :             ((canvas::coord_t (iter_label.m_text_rect.get_min_x (),
     134          116 :                                get_canvas_y (iter_label.m_text_rect.get_min_y ()))
     135              :               + offset),
     136          116 :              iter_label.m_text);
     137          116 :           break;
     138              : 
     139          189 :         case x_ruler::label_kind::TEXT_WITH_BORDER:
     140          189 :           {
     141          189 :             const canvas::range_t rel_x_range
     142          189 :               (iter_label.m_text_rect.get_x_range ());
     143              : 
     144          189 :             enum theme::cell_kind inner_left_kind;
     145          189 :             enum theme::cell_kind inner_connector_kind;
     146          189 :             enum theme::cell_kind inner_right_kind;
     147          189 :             enum theme::cell_kind outer_left_kind;
     148          189 :             enum theme::cell_kind outer_right_kind;
     149              : 
     150          189 :               switch (m_label_dir)
     151              :                 {
     152            0 :                 default:
     153            0 :                   gcc_unreachable ();
     154              :                 case label_dir::ABOVE:
     155              :                   outer_left_kind = theme::cell_kind::TEXT_BORDER_TOP_LEFT;
     156              :                   outer_right_kind = theme::cell_kind::TEXT_BORDER_TOP_RIGHT;
     157              :                   inner_left_kind = theme::cell_kind::TEXT_BORDER_BOTTOM_LEFT;
     158              :                   inner_connector_kind = theme::cell_kind::X_RULER_CONNECTOR_TO_LABEL_BELOW;
     159              :                   inner_right_kind = theme::cell_kind::TEXT_BORDER_BOTTOM_RIGHT;
     160              :                   break;
     161          173 :                 case label_dir::BELOW:
     162          173 :                   inner_left_kind = theme::cell_kind::TEXT_BORDER_TOP_LEFT;
     163          173 :                   inner_connector_kind = theme::cell_kind::X_RULER_CONNECTOR_TO_LABEL_ABOVE;
     164          173 :                   inner_right_kind = theme::cell_kind::TEXT_BORDER_TOP_RIGHT;
     165          173 :                   outer_left_kind = theme::cell_kind::TEXT_BORDER_BOTTOM_LEFT;
     166          173 :                   outer_right_kind = theme::cell_kind::TEXT_BORDER_BOTTOM_RIGHT;
     167          173 :                   break;
     168              :                 }
     169              :             /* Inner border.  */
     170          189 :             {
     171          189 :               const int rel_canvas_y
     172          189 :                 = get_canvas_y (iter_label.m_text_rect.get_min_y ());
     173              :               /* Left corner.  */
     174          189 :               canvas.paint ((canvas::coord_t (rel_x_range.get_min (),
     175          189 :                                               rel_canvas_y)
     176              :                              + offset),
     177          189 :                             theme.get_cell (inner_left_kind,
     178          189 :                                             iter_label.m_style_id));
     179              :               /* Edge.  */
     180          189 :               const canvas::cell_t edge_border_cell
     181              :                 = theme.get_cell (theme::cell_kind::TEXT_BORDER_HORIZONTAL,
     182          189 :                                   iter_label.m_style_id);
     183          189 :               const canvas::cell_t connector_border_cell
     184              :                 = theme.get_cell (inner_connector_kind,
     185          189 :                                   iter_label.m_style_id);
     186         3522 :               for (int rel_x = rel_x_range.get_min () + 1;
     187         3522 :                    rel_x < rel_x_range.get_max ();
     188              :                    rel_x++)
     189         3333 :                 if (rel_x == iter_label.m_connector_x)
     190          189 :                   canvas.paint ((canvas::coord_t (rel_x, rel_canvas_y)
     191              :                                  + offset),
     192              :                                 connector_border_cell);
     193              :                 else
     194         3144 :                   canvas.paint ((canvas::coord_t (rel_x, rel_canvas_y)
     195              :                                  + offset),
     196              :                                 edge_border_cell);
     197              : 
     198              :               /* Right corner.  */
     199          189 :               canvas.paint ((canvas::coord_t (rel_x_range.get_max (),
     200          189 :                                               rel_canvas_y)
     201              :                              + offset),
     202          378 :                             theme.get_cell (inner_right_kind,
     203          189 :                                             iter_label.m_style_id));
     204          189 :             }
     205              : 
     206          189 :             {
     207          189 :               const int rel_canvas_y
     208          189 :                 = get_canvas_y (iter_label.m_text_rect.get_min_y () + 1);
     209          189 :               const canvas::cell_t border_cell
     210              :                 = theme.get_cell (theme::cell_kind::TEXT_BORDER_VERTICAL,
     211          189 :                                   iter_label.m_style_id);
     212              : 
     213              :               /* Left border.  */
     214          378 :               canvas.paint ((canvas::coord_t (rel_x_range.get_min (),
     215          189 :                                               rel_canvas_y)
     216              :                              + offset),
     217              :                             border_cell);
     218              :               /* Text.  */
     219          189 :               canvas.paint_text ((canvas::coord_t (rel_x_range.get_min () + 1,
     220          189 :                                                    rel_canvas_y)
     221              :                                   + offset),
     222          189 :                                  iter_label.m_text);
     223              :               /* Right border.  */
     224          378 :               canvas.paint ((canvas::coord_t (rel_x_range.get_max (),
     225          189 :                                               rel_canvas_y)
     226              :                              + offset),
     227              :                             border_cell);
     228          189 :             }
     229              : 
     230              :             /* Outer border.  */
     231          189 :             {
     232          189 :               const int rel_canvas_y
     233          189 :                 = get_canvas_y (iter_label.m_text_rect.get_max_y ());
     234              :               /* Left corner.  */
     235          189 :               canvas.paint ((canvas::coord_t (rel_x_range.get_min (),
     236          189 :                                               rel_canvas_y)
     237              :                              + offset),
     238          189 :                             theme.get_cell (outer_left_kind,
     239          189 :                                             iter_label.m_style_id));
     240              :               /* Edge.  */
     241          189 :               const canvas::cell_t border_cell
     242              :                 = theme.get_cell (theme::cell_kind::TEXT_BORDER_HORIZONTAL,
     243          189 :                                   iter_label.m_style_id);
     244          189 :               for (int rel_x = rel_x_range.get_min () + 1;
     245         3522 :                    rel_x < rel_x_range.get_max ();
     246              :                    rel_x++)
     247         3333 :                 canvas.paint ((canvas::coord_t (rel_x, rel_canvas_y)
     248              :                                + offset),
     249              :                               border_cell);
     250              : 
     251              :               /* Right corner.  */
     252          189 :               canvas.paint ((canvas::coord_t (rel_x_range.get_max (),
     253          189 :                                               rel_canvas_y)
     254              :                              + offset),
     255          378 :                             theme.get_cell (outer_right_kind,
     256          189 :                                             iter_label.m_style_id));
     257          189 :             }
     258              :           }
     259          189 :           break;
     260              :         }
     261              :     }
     262          148 : }
     263              : 
     264              : DEBUG_FUNCTION void
     265            0 : x_ruler::debug (const style_manager &sm)
     266              : {
     267            0 :   canvas c (get_size (), sm);
     268            0 :   paint_to_canvas (c, canvas::coord_t (0, 0), unicode_theme ());
     269            0 :   c.debug (true);
     270            0 : }
     271              : 
     272          410 : x_ruler::label::label (const canvas::range_t &range,
     273              :                        styled_string text,
     274              :                        style::id_t style_id,
     275          410 :                        label_kind kind)
     276          410 : : m_range (range),
     277          410 :   m_text (std::move (text)),
     278          410 :   m_style_id (style_id),
     279          410 :   m_kind (kind),
     280          410 :   m_text_rect (canvas::coord_t (0, 0),
     281          410 :                canvas::size_t (m_text.calc_canvas_width (), 1)),
     282          410 :   m_connector_x ((m_range.get_min () + m_range.get_max ()) / 2)
     283              : {
     284          410 :   if (kind == label_kind::TEXT_WITH_BORDER)
     285              :     {
     286          326 :       m_text_rect.m_size.w += 2;
     287          326 :       m_text_rect.m_size.h += 2;
     288              :     }
     289          410 : }
     290              : 
     291              : bool
     292          436 : x_ruler::label::operator< (const label &other) const
     293              : {
     294          436 :   int cmp = m_range.start - other.m_range.start;
     295          436 :   if (cmp)
     296          436 :     return cmp < 0;
     297            0 :   return m_range.next < other.m_range.next;
     298              : }
     299              : 
     300              : void
     301          296 : x_ruler::ensure_layout ()
     302              : {
     303          296 :   if (m_has_layout)
     304              :     return;
     305          192 :   update_layout ();
     306          192 :   m_has_layout = true;
     307              : }
     308              : 
     309              : void
     310          192 : x_ruler::update_layout ()
     311              : {
     312          192 :   if (m_labels.empty ())
     313            0 :     return;
     314              : 
     315          192 :   std::sort (m_labels.begin (), m_labels.end ());
     316              : 
     317              :   /* Place labels.  */
     318          192 :   int ruler_width = m_labels.back ().m_range.get_next ();
     319          192 :   int width_with_labels = ruler_width;
     320              : 
     321              :   /* Get x coordinates of text parts of each label
     322              :      (m_text_rect.m_top_left.x for each label).  */
     323          602 :   for (size_t idx = 0; idx < m_labels.size (); idx++)
     324              :     {
     325          410 :       label &iter_label = m_labels[idx];
     326              :       /* Attempt to center the text label.  */
     327          410 :       int min_x;
     328          410 :       if (idx > 0)
     329              :         {
     330              :           /* ...but don't overlap with the connector to the left.  */
     331          218 :           int left_neighbor_connector_x = m_labels[idx - 1].m_connector_x;
     332          218 :           min_x = left_neighbor_connector_x + 1;
     333              :         }
     334              :       else
     335              :         {
     336              :           /* ...or go beyond the leftmost column.  */
     337          192 :           min_x = 0;
     338              :         }
     339          410 :       int connector_x = iter_label.m_connector_x;
     340          410 :       int centered_x
     341          410 :         = connector_x - ((int)iter_label.m_text_rect.get_width () / 2);
     342          410 :       int text_x = std::max (min_x, centered_x);
     343          410 :       iter_label.m_text_rect.m_top_left.x = text_x;
     344              :     }
     345              : 
     346              :   /* Now walk backwards trying to place them vertically,
     347              :      setting m_text_rect.m_top_left.y for each label,
     348              :      consolidating the rows where possible.
     349              :      The y cooordinates are stored with respect to label_dir::BELOW.  */
     350          192 :   int label_y = 2;
     351          602 :   for (int idx = m_labels.size () - 1; idx >= 0; idx--)
     352              :     {
     353          410 :       label &iter_label = m_labels[idx];
     354              :       /* Does it fit on the same row as the text label to the right?  */
     355          410 :       size_t text_len = iter_label.m_text_rect.get_width ();
     356              :       /* Get the x-coord of immediately beyond iter_label's text.  */
     357          410 :       int next_x = iter_label.m_text_rect.get_min_x () + text_len;
     358          410 :       if (idx < (int)m_labels.size () - 1)
     359              :         {
     360          218 :           if (next_x >= m_labels[idx + 1].m_text_rect.get_min_x ())
     361              :             {
     362              :               /* If not, start a new row.  */
     363           52 :               label_y += m_labels[idx + 1].m_text_rect.get_height ();
     364              :             }
     365              :         }
     366          410 :       iter_label.m_text_rect.m_top_left.y = label_y;
     367          410 :       width_with_labels = std::max (width_with_labels, next_x);
     368              :     }
     369              : 
     370          192 :   m_size = canvas::size_t (width_with_labels,
     371          192 :                            label_y + m_labels[0].m_text_rect.get_height ());
     372              : }
     373              : 
     374              : #if CHECKING_P
     375              : 
     376              : namespace selftest {
     377              : 
     378              : static void
     379           80 : assert_x_ruler_streq (const location &loc,
     380              :                       x_ruler &ruler,
     381              :                       const theme &theme,
     382              :                       const style_manager &sm,
     383              :                       bool styled,
     384              :                       const char *expected_str)
     385              : {
     386           80 :   canvas c (ruler.get_size (), sm);
     387           80 :   ruler.paint_to_canvas (c, canvas::coord_t (0, 0), theme);
     388           80 :   if (0)
     389              :     c.debug (styled);
     390           80 :   assert_canvas_streq (loc, c, styled, expected_str);
     391           80 : }
     392              : 
     393              : #define ASSERT_X_RULER_STREQ(RULER, THEME, SM, STYLED, EXPECTED_STR)    \
     394              :   SELFTEST_BEGIN_STMT                                                   \
     395              :     assert_x_ruler_streq ((SELFTEST_LOCATION),                          \
     396              :                           (RULER),                                      \
     397              :                           (THEME),                                      \
     398              :                           (SM),                                         \
     399              :                           (STYLED),                                     \
     400              :                           (EXPECTED_STR));                              \
     401              :   SELFTEST_END_STMT
     402              : 
     403              : static void
     404            4 : test_single ()
     405              : {
     406            4 :   style_manager sm;
     407            4 :   x_ruler r (x_ruler::label_dir::BELOW);
     408            4 :   r.add_label (canvas::range_t (0, 11), styled_string (sm, "foo"),
     409              :                style::id_plain, x_ruler::label_kind::TEXT);
     410            4 :   ASSERT_X_RULER_STREQ
     411              :     (r, ascii_theme (), sm, true,
     412              :      ("|~~~~+~~~~|\n"
     413              :       "     |\n"
     414              :       "    foo\n"));
     415            4 :   ASSERT_X_RULER_STREQ
     416              :     (r, unicode_theme (), sm, true,
     417              :      ("├────┬────┤\n"
     418              :       "     │\n"
     419              :       "    foo\n"));
     420            4 : }
     421              : 
     422              : static void
     423            4 : test_single_above ()
     424              : {
     425            4 :   style_manager sm;
     426            4 :   x_ruler r (x_ruler::label_dir::ABOVE);
     427            4 :   r.add_label (canvas::range_t (0, 11), styled_string (sm, "hello world"),
     428              :                style::id_plain);
     429            4 :   ASSERT_X_RULER_STREQ
     430              :     (r, ascii_theme (), sm, true,
     431              :      ("hello world\n"
     432              :       "     |\n"
     433              :       "|~~~~+~~~~|\n"));
     434            4 :   ASSERT_X_RULER_STREQ
     435              :     (r, unicode_theme (), sm, true,
     436              :      ("hello world\n"
     437              :       "     │\n"
     438              :       "├────┴────┤\n"));
     439            4 : }
     440              : 
     441              : static void
     442            4 : test_multiple_contiguous ()
     443              : {
     444            4 :   style_manager sm;
     445            4 :   x_ruler r (x_ruler::label_dir::BELOW);
     446            4 :   r.add_label (canvas::range_t (0, 11), styled_string (sm, "foo"),
     447              :                style::id_plain);
     448            4 :   r.add_label (canvas::range_t (10, 16), styled_string (sm, "bar"),
     449              :                style::id_plain);
     450            4 :   ASSERT_X_RULER_STREQ
     451              :     (r, ascii_theme (), sm, true,
     452              :      ("|~~~~+~~~~|~+~~|\n"
     453              :       "     |      |\n"
     454              :       "    foo    bar\n"));
     455            4 :   ASSERT_X_RULER_STREQ
     456              :     (r, unicode_theme (), sm, true,
     457              :      ("├────┬────┼─┬──┤\n"
     458              :       "     │      │\n"
     459              :       "    foo    bar\n"));
     460            4 : }
     461              : 
     462              : static void
     463            4 : test_multiple_contiguous_above ()
     464              : {
     465            4 :   style_manager sm;
     466            4 :   x_ruler r (x_ruler::label_dir::ABOVE);
     467            4 :   r.add_label (canvas::range_t (0, 11), styled_string (sm, "foo"),
     468              :                style::id_plain);
     469            4 :   r.add_label (canvas::range_t (10, 16), styled_string (sm, "bar"),
     470              :                style::id_plain);
     471            4 :   ASSERT_X_RULER_STREQ
     472              :     (r, ascii_theme (), sm, true,
     473              :      ("    foo    bar\n"
     474              :       "     |      |\n"
     475              :       "|~~~~+~~~~|~+~~|\n"));
     476            4 :   ASSERT_X_RULER_STREQ
     477              :     (r, unicode_theme (), sm, true,
     478              :      ("    foo    bar\n"
     479              :       "     │      │\n"
     480              :       "├────┴────┼─┴──┤\n"));
     481            4 : }
     482              : 
     483              : static void
     484            4 : test_multiple_contiguous_abutting_labels ()
     485              : {
     486            4 :   style_manager sm;
     487            4 :   x_ruler r (x_ruler::label_dir::BELOW);
     488            4 :   r.add_label (canvas::range_t (0, 11), styled_string (sm, "12345678"),
     489              :                style::id_plain);
     490            4 :   r.add_label (canvas::range_t (10, 16), styled_string (sm, "1234678"),
     491              :                style::id_plain);
     492            4 :   ASSERT_X_RULER_STREQ
     493              :     (r, unicode_theme (), sm, true,
     494              :      ("├────┬────┼─┬──┤\n"
     495              :       "     │      │\n"
     496              :       "     │   1234678\n"
     497              :       " 12345678\n"));
     498            4 : }
     499              : 
     500              : static void
     501            4 : test_multiple_contiguous_overlapping_labels ()
     502              : {
     503            4 :   style_manager sm;
     504            4 :   x_ruler r (x_ruler::label_dir::BELOW);
     505            4 :   r.add_label (canvas::range_t (0, 11), styled_string (sm, "123456789"),
     506              :                style::id_plain);
     507            4 :   r.add_label (canvas::range_t (10, 16), styled_string (sm, "12346789"),
     508              :                style::id_plain);
     509            4 :   ASSERT_X_RULER_STREQ
     510              :     (r, unicode_theme (), sm, true,
     511              :      ("├────┬────┼─┬──┤\n"
     512              :       "     │      │\n"
     513              :       "     │  12346789\n"
     514              :       " 123456789\n"));
     515            4 : }
     516              : static void
     517            4 : test_abutting_left_border ()
     518              : {
     519            4 :   style_manager sm;
     520            4 :   x_ruler r (x_ruler::label_dir::BELOW);
     521            4 :   r.add_label (canvas::range_t (0, 6),
     522            4 :                styled_string (sm, "this is a long label"),
     523              :                style::id_plain);
     524            4 :   ASSERT_X_RULER_STREQ
     525              :     (r, unicode_theme (), sm, true,
     526              :      ("├─┬──┤\n"
     527              :       "  │\n"
     528              :       "this is a long label\n"));
     529            4 : }
     530              : 
     531              : static void
     532            4 : test_too_long_to_consolidate_vertically ()
     533              : {
     534            4 :   style_manager sm;
     535            4 :   x_ruler r (x_ruler::label_dir::BELOW);
     536            4 :   r.add_label (canvas::range_t (0, 11),
     537            4 :                styled_string (sm, "long string A"),
     538              :                style::id_plain);
     539            4 :   r.add_label (canvas::range_t (10, 16),
     540            4 :                styled_string (sm, "long string B"),
     541              :                style::id_plain);
     542            4 :   ASSERT_X_RULER_STREQ
     543              :     (r, unicode_theme (), sm, true,
     544              :      ("├────┬────┼─┬──┤\n"
     545              :       "     │      │\n"
     546              :       "     │long string B\n"
     547              :       "long string A\n"));
     548            4 : }
     549              : 
     550              : static void
     551            4 : test_abutting_neighbor ()
     552              : {
     553            4 :   style_manager sm;
     554            4 :   x_ruler r (x_ruler::label_dir::BELOW);
     555            4 :   r.add_label (canvas::range_t (0, 11),
     556            4 :                styled_string (sm, "very long string A"),
     557              :                style::id_plain);
     558            4 :   r.add_label (canvas::range_t (10, 16),
     559            4 :                styled_string (sm, "very long string B"),
     560              :                style::id_plain);
     561            4 :   ASSERT_X_RULER_STREQ
     562              :     (r, unicode_theme (), sm, true,
     563              :      ("├────┬────┼─┬──┤\n"
     564              :       "     │      │\n"
     565              :       "     │very long string B\n"
     566              :       "very long string A\n"));
     567            4 : }
     568              : 
     569              : static void
     570            4 : test_gaps ()
     571              : {
     572            4 :   style_manager sm;
     573            4 :   x_ruler r (x_ruler::label_dir::BELOW);
     574            4 :   r.add_label (canvas::range_t (0, 5),
     575            4 :                styled_string (sm, "foo"),
     576              :                style::id_plain);
     577            4 :   r.add_label (canvas::range_t (10, 15),
     578            4 :                styled_string (sm, "bar"),
     579              :                style::id_plain);
     580            4 :   ASSERT_X_RULER_STREQ
     581              :     (r, ascii_theme (), sm, true,
     582              :      ("|~+~|     |~+~|\n"
     583              :       "  |         |\n"
     584              :       " foo       bar\n"));
     585            4 : }
     586              : 
     587              : static void
     588            4 : test_styled ()
     589              : {
     590            4 :   style_manager sm;
     591            4 :   style s1, s2;
     592            4 :   s1.m_bold = true;
     593            4 :   s1.m_fg_color = style::named_color::YELLOW;
     594            4 :   s2.m_bold = true;
     595            4 :   s2.m_fg_color = style::named_color::BLUE;
     596            4 :   style::id_t sid1 = sm.get_or_create_id (s1);
     597            4 :   style::id_t sid2 = sm.get_or_create_id (s2);
     598              : 
     599            4 :   x_ruler r (x_ruler::label_dir::BELOW);
     600            4 :   r.add_label (canvas::range_t (0, 5), styled_string (sm, "foo"), sid1);
     601            4 :   r.add_label (canvas::range_t (10, 15), styled_string (sm, "bar"), sid2);
     602            4 :   ASSERT_X_RULER_STREQ
     603              :     (r, ascii_theme (), sm, true,
     604              :      ("|~+~|     |~+~|\n"
     605              :       "  |         |\n"
     606              :       " foo       bar\n"));
     607            4 : }
     608              : 
     609              : static void
     610            4 : test_borders ()
     611              : {
     612            4 :   style_manager sm;
     613            4 :   {
     614            4 :     x_ruler r (x_ruler::label_dir::BELOW);
     615            4 :     r.add_label (canvas::range_t (0, 5),
     616            4 :                  styled_string (sm, "label 1"),
     617              :                  style::id_plain,
     618              :                  x_ruler::label_kind::TEXT_WITH_BORDER);
     619            4 :     r.add_label (canvas::range_t (10, 15),
     620            4 :                  styled_string (sm, "label 2"),
     621              :                  style::id_plain);
     622            4 :     r.add_label (canvas::range_t (20, 25),
     623            4 :                  styled_string (sm, "label 3"),
     624              :                  style::id_plain,
     625              :                  x_ruler::label_kind::TEXT_WITH_BORDER);
     626            4 :     ASSERT_X_RULER_STREQ
     627              :       (r, ascii_theme (), sm, true,
     628              :        "|~+~|     |~+~|     |~+~|\n"
     629              :        "  |         |         |\n"
     630              :        "  |      label 2  +---+---+\n"
     631              :        "+-+-----+         |label 3|\n"
     632              :        "|label 1|         +-------+\n"
     633              :        "+-------+\n");
     634            4 :     ASSERT_X_RULER_STREQ
     635              :       (r, unicode_theme (), sm, true,
     636              :        "├─┬─┤     ├─┬─┤     ├─┬─┤\n"
     637              :        "  │         │         │\n"
     638              :        "  │      label 2  ╭───┴───╮\n"
     639              :        "╭─┴─────╮         │label 3│\n"
     640              :        "│label 1│         ╰───────╯\n"
     641              :        "╰───────╯\n");
     642            4 :   }
     643            4 :   {
     644            4 :     x_ruler r (x_ruler::label_dir::ABOVE);
     645            4 :     r.add_label (canvas::range_t (0, 5),
     646            4 :                  styled_string (sm, "label 1"),
     647              :                  style::id_plain,
     648              :                  x_ruler::label_kind::TEXT_WITH_BORDER);
     649            4 :     r.add_label (canvas::range_t (10, 15),
     650            4 :                  styled_string (sm, "label 2"),
     651              :                  style::id_plain);
     652            4 :     r.add_label (canvas::range_t (20, 25),
     653            4 :                  styled_string (sm, "label 3"),
     654              :                  style::id_plain,
     655              :                  x_ruler::label_kind::TEXT_WITH_BORDER);
     656            4 :     ASSERT_X_RULER_STREQ
     657              :       (r, ascii_theme (), sm, true,
     658              :        "+-------+\n"
     659              :        "|label 1|         +-------+\n"
     660              :        "+-+-----+         |label 3|\n"
     661              :        "  |      label 2  +---+---+\n"
     662              :        "  |         |         |\n"
     663              :        "|~+~|     |~+~|     |~+~|\n");
     664            4 :     ASSERT_X_RULER_STREQ
     665              :       (r, unicode_theme (), sm, true,
     666              :        "╭───────╮\n"
     667              :        "│label 1│         ╭───────╮\n"
     668              :        "╰─┬─────╯         │label 3│\n"
     669              :        "  │      label 2  ╰───┬───╯\n"
     670              :        "  │         │         │\n"
     671              :        "├─┴─┤     ├─┴─┤     ├─┴─┤\n");
     672            4 :   }
     673            4 : }
     674              : 
     675              : static void
     676            4 : test_emoji ()
     677              : {
     678            4 :   style_manager sm;
     679              : 
     680            4 :   styled_string s;
     681            4 :   s.append (styled_string (0x26A0, /* U+26A0 WARNING SIGN.  */
     682            4 :                            true));
     683            4 :   s.append (styled_string (sm, "  "));
     684            4 :   s.append (styled_string (sm, "this is a warning"));
     685              : 
     686            4 :   x_ruler r (x_ruler::label_dir::BELOW);
     687            4 :   r.add_label (canvas::range_t (0, 5),
     688              :                std::move (s),
     689              :                style::id_plain,
     690              :                x_ruler::label_kind::TEXT_WITH_BORDER);
     691              : 
     692            4 :   ASSERT_X_RULER_STREQ
     693              :     (r, ascii_theme (), sm, true,
     694              :      "|~+~|\n"
     695              :      "  |\n"
     696              :      "+-+------------------+\n"
     697              :      "|⚠️  this is a warning|\n"
     698              :      "+--------------------+\n");
     699            4 : }
     700              : 
     701              : /* Run all selftests in this file.  */
     702              : 
     703              : void
     704            4 : text_art_ruler_cc_tests ()
     705              : {
     706            4 :   test_single ();
     707            4 :   test_single_above ();
     708            4 :   test_multiple_contiguous ();
     709            4 :   test_multiple_contiguous_above ();
     710            4 :   test_multiple_contiguous_abutting_labels ();
     711            4 :   test_multiple_contiguous_overlapping_labels ();
     712            4 :   test_abutting_left_border ();
     713            4 :   test_too_long_to_consolidate_vertically ();
     714            4 :   test_abutting_neighbor ();
     715            4 :   test_gaps ();
     716            4 :   test_styled ();
     717            4 :   test_borders ();
     718            4 :   test_emoji ();
     719            4 : }
     720              : 
     721              : } // namespace selftest
     722              : 
     723              : 
     724              : #endif /* #if CHECKING_P */
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.