Line data Source code
1 : /* Tree diagrams.
2 : Copyright (C) 2024-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_VECTOR
23 : #include "system.h"
24 : #include "coretypes.h"
25 : #include "pretty-print.h"
26 : #include "selftest.h"
27 : #include "text-art/selftests.h"
28 : #include "text-art/tree-widget.h"
29 : #include "text-art/dump-widget-info.h"
30 :
31 : using namespace text_art;
32 :
33 : /* class text_art::tree_widget : public text_art::widget. */
34 :
35 : static const int margin_width = 3;
36 :
37 : std::unique_ptr<tree_widget>
38 120 : tree_widget::make (styled_string str, const theme &theme, style::id_t style_id)
39 : {
40 120 : return std::make_unique <tree_widget>
41 240 : (std::make_unique <text_widget> (std::move (str)),
42 : theme,
43 120 : style_id);
44 : }
45 :
46 : std::unique_ptr<tree_widget>
47 0 : tree_widget::make (const dump_widget_info &dwi, pretty_printer *pp)
48 : {
49 0 : return tree_widget::make (styled_string (dwi.m_sm, pp_formatted_text (pp)),
50 : dwi.m_theme,
51 0 : dwi.get_tree_style_id ());
52 : }
53 :
54 : std::unique_ptr<tree_widget>
55 8 : tree_widget::make (const dump_widget_info &dwi, const char *str)
56 : {
57 8 : return tree_widget::make (styled_string (dwi.m_sm, str),
58 : dwi.m_theme,
59 8 : dwi.get_tree_style_id ());
60 : }
61 :
62 : std::unique_ptr<tree_widget>
63 112 : tree_widget::from_fmt (const dump_widget_info &dwi,
64 : printer_fn format_decoder,
65 : const char *fmt, ...)
66 : {
67 112 : va_list ap;
68 112 : va_start (ap, fmt);
69 112 : styled_string styled_str
70 112 : (styled_string::from_fmt_va (dwi.m_sm, format_decoder, fmt, &ap));
71 112 : va_end (ap);
72 112 : return make (std::move (styled_str), dwi.m_theme, dwi.get_tree_style_id ());
73 112 : }
74 :
75 : const char *
76 0 : tree_widget::get_desc () const
77 : {
78 0 : return "tree_widget";
79 : }
80 :
81 : canvas::size_t
82 116 : tree_widget::calc_req_size ()
83 : {
84 116 : canvas::size_t result (0, 0);
85 116 : if (m_node)
86 : {
87 116 : canvas::size_t node_req_size = m_node->get_req_size ();
88 116 : result.h += node_req_size.h;
89 116 : result.w = std::max (result.w, node_req_size.w);
90 : }
91 220 : for (auto &child : m_children)
92 : {
93 104 : canvas::size_t child_req_size = child->get_req_size ();
94 104 : result.h += child_req_size.h;
95 104 : result.w = std::max (result.w, child_req_size.w + margin_width);
96 : }
97 116 : return result;
98 : }
99 :
100 : void
101 116 : tree_widget::update_child_alloc_rects ()
102 : {
103 116 : const int x = get_min_x ();
104 116 : int y = get_min_y ();
105 116 : if (m_node)
106 : {
107 116 : m_node->set_alloc_rect
108 116 : (canvas::rect_t (canvas::coord_t (x, y),
109 : canvas::size_t (get_alloc_w (),
110 116 : m_node->get_req_h ())));
111 116 : y += m_node->get_req_h ();
112 : }
113 220 : for (auto &child : m_children)
114 : {
115 104 : child->set_alloc_rect
116 208 : (canvas::rect_t (canvas::coord_t (x + margin_width, y),
117 104 : canvas::size_t (get_alloc_w () - margin_width,
118 104 : child->get_req_h ())));
119 104 : y += child->get_req_h ();
120 : }
121 116 : }
122 :
123 : void
124 116 : tree_widget::paint_to_canvas (canvas &canvas)
125 : {
126 116 : if (m_node)
127 116 : m_node->paint_to_canvas (canvas);
128 116 : const int min_x = get_min_x ();
129 116 : const canvas::cell_t cell_child_non_final
130 116 : (m_theme.get_cell (theme::cell_kind::TREE_CHILD_NON_FINAL, m_style_id));
131 116 : const canvas::cell_t cell_child_final
132 116 : (m_theme.get_cell (theme::cell_kind::TREE_CHILD_FINAL, m_style_id));
133 116 : const canvas::cell_t cell_x_connector
134 116 : (m_theme.get_cell (theme::cell_kind::TREE_X_CONNECTOR, m_style_id));
135 116 : const canvas::cell_t cell_y_connector
136 116 : (m_theme.get_cell (theme::cell_kind::TREE_Y_CONNECTOR, m_style_id));
137 116 : size_t idx = 0;
138 220 : for (auto &child : m_children)
139 : {
140 104 : child->paint_to_canvas (canvas);
141 :
142 104 : const bool last_child = (++idx == m_children.size ());
143 104 : canvas.paint (canvas::coord_t (min_x + 1, child->get_min_y ()),
144 : cell_x_connector);
145 208 : canvas.paint (canvas::coord_t (min_x, child->get_min_y ()),
146 : last_child ? cell_child_final : cell_child_non_final);
147 104 : if (!last_child)
148 112 : for (int y = child->get_min_y () + 1; y <= child ->get_max_y (); y++)
149 48 : canvas.paint (canvas::coord_t (min_x, y), cell_y_connector);
150 : }
151 116 : }
152 :
153 : #if CHECKING_P
154 :
155 : namespace selftest {
156 :
157 : static std::unique_ptr<tree_widget>
158 8 : make_test_tree_widget (const dump_widget_info &dwi)
159 : {
160 8 : std::unique_ptr<tree_widget> w
161 8 : (tree_widget::from_fmt (dwi, nullptr, "Root"));
162 32 : for (int i = 0; i < 3; i++)
163 : {
164 24 : std::unique_ptr<tree_widget> c
165 24 : (tree_widget::from_fmt (dwi, nullptr, "Child %i", i));
166 96 : for (int j = 0; j < 3; j++)
167 144 : c->add_child (tree_widget::from_fmt (dwi, nullptr,
168 : "Grandchild %i %i", i, j));
169 48 : w->add_child (std::move (c));
170 24 : }
171 8 : return w;
172 : }
173 :
174 : static void
175 4 : test_tree_widget ()
176 : {
177 4 : style_manager sm;
178 :
179 4 : style::id_t default_style_id (sm.get_or_create_id (style ()));
180 :
181 4 : {
182 4 : ascii_theme theme;
183 4 : dump_widget_info dwi (sm, theme, default_style_id);
184 4 : canvas c (make_test_tree_widget (dwi)->to_canvas (sm));
185 4 : ASSERT_CANVAS_STREQ
186 : (c, false,
187 : ("Root\n"
188 : "+- Child 0\n"
189 : "| +- Grandchild 0 0\n"
190 : "| +- Grandchild 0 1\n"
191 : "| `- Grandchild 0 2\n"
192 : "+- Child 1\n"
193 : "| +- Grandchild 1 0\n"
194 : "| +- Grandchild 1 1\n"
195 : "| `- Grandchild 1 2\n"
196 : "`- Child 2\n"
197 : " +- Grandchild 2 0\n"
198 : " +- Grandchild 2 1\n"
199 : " `- Grandchild 2 2\n"));
200 4 : }
201 :
202 4 : {
203 4 : unicode_theme theme;
204 4 : dump_widget_info dwi (sm, theme, default_style_id);
205 4 : canvas c (make_test_tree_widget (dwi)->to_canvas (sm));
206 4 : ASSERT_CANVAS_STREQ
207 : (c, false,
208 : ("Root\n"
209 : "├─ Child 0\n"
210 : "│ ├─ Grandchild 0 0\n"
211 : "│ ├─ Grandchild 0 1\n"
212 : "│ ╰─ Grandchild 0 2\n"
213 : "├─ Child 1\n"
214 : "│ ├─ Grandchild 1 0\n"
215 : "│ ├─ Grandchild 1 1\n"
216 : "│ ╰─ Grandchild 1 2\n"
217 : "╰─ Child 2\n"
218 : " ├─ Grandchild 2 0\n"
219 : " ├─ Grandchild 2 1\n"
220 : " ╰─ Grandchild 2 2\n"));
221 4 : }
222 4 : }
223 :
224 : /* Run all selftests in this file. */
225 :
226 : void
227 4 : text_art_tree_widget_cc_tests ()
228 : {
229 4 : test_tree_widget ();
230 4 : }
231 :
232 : } // namespace selftest
233 :
234 :
235 : #endif /* #if CHECKING_P */
|