| /* Tree diagrams. |
| Copyright (C) 2024-2025 Free Software Foundation, Inc. |
| Contributed by David Malcolm <dmalcolm@redhat.com>. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #define INCLUDE_VECTOR |
| #include "system.h" |
| #include "coretypes.h" |
| #include "pretty-print.h" |
| #include "selftest.h" |
| #include "text-art/selftests.h" |
| #include "text-art/tree-widget.h" |
| #include "text-art/dump-widget-info.h" |
| |
| using namespace text_art; |
| |
| /* class text_art::tree_widget : public text_art::widget. */ |
| |
| static const int margin_width = 3; |
| |
| std::unique_ptr<tree_widget> |
| tree_widget::make (styled_string str, const theme &theme, style::id_t style_id) |
| { |
| return std::make_unique <tree_widget> |
| (std::make_unique <text_widget> (std::move (str)), |
| theme, |
| style_id); |
| } |
| |
| std::unique_ptr<tree_widget> |
| tree_widget::make (const dump_widget_info &dwi, pretty_printer *pp) |
| { |
| return tree_widget::make (styled_string (dwi.m_sm, pp_formatted_text (pp)), |
| dwi.m_theme, |
| dwi.get_tree_style_id ()); |
| } |
| |
| std::unique_ptr<tree_widget> |
| tree_widget::make (const dump_widget_info &dwi, const char *str) |
| { |
| return tree_widget::make (styled_string (dwi.m_sm, str), |
| dwi.m_theme, |
| dwi.get_tree_style_id ()); |
| } |
| |
| std::unique_ptr<tree_widget> |
| tree_widget::from_fmt (const dump_widget_info &dwi, |
| printer_fn format_decoder, |
| const char *fmt, ...) |
| { |
| va_list ap; |
| va_start (ap, fmt); |
| styled_string styled_str |
| (styled_string::from_fmt_va (dwi.m_sm, format_decoder, fmt, &ap)); |
| va_end (ap); |
| return make (std::move (styled_str), dwi.m_theme, dwi.get_tree_style_id ()); |
| } |
| |
| const char * |
| tree_widget::get_desc () const |
| { |
| return "tree_widget"; |
| } |
| |
| canvas::size_t |
| tree_widget::calc_req_size () |
| { |
| canvas::size_t result (0, 0); |
| if (m_node) |
| { |
| canvas::size_t node_req_size = m_node->get_req_size (); |
| result.h += node_req_size.h; |
| result.w = std::max (result.w, node_req_size.w); |
| } |
| for (auto &child : m_children) |
| { |
| canvas::size_t child_req_size = child->get_req_size (); |
| result.h += child_req_size.h; |
| result.w = std::max (result.w, child_req_size.w + margin_width); |
| } |
| return result; |
| } |
| |
| void |
| tree_widget::update_child_alloc_rects () |
| { |
| const int x = get_min_x (); |
| int y = get_min_y (); |
| if (m_node) |
| { |
| m_node->set_alloc_rect |
| (canvas::rect_t (canvas::coord_t (x, y), |
| canvas::size_t (get_alloc_w (), |
| m_node->get_req_h ()))); |
| y += m_node->get_req_h (); |
| } |
| for (auto &child : m_children) |
| { |
| child->set_alloc_rect |
| (canvas::rect_t (canvas::coord_t (x + margin_width, y), |
| canvas::size_t (get_alloc_w () - margin_width, |
| child->get_req_h ()))); |
| y += child->get_req_h (); |
| } |
| } |
| |
| void |
| tree_widget::paint_to_canvas (canvas &canvas) |
| { |
| if (m_node) |
| m_node->paint_to_canvas (canvas); |
| const int min_x = get_min_x (); |
| const canvas::cell_t cell_child_non_final |
| (m_theme.get_cell (theme::cell_kind::TREE_CHILD_NON_FINAL, m_style_id)); |
| const canvas::cell_t cell_child_final |
| (m_theme.get_cell (theme::cell_kind::TREE_CHILD_FINAL, m_style_id)); |
| const canvas::cell_t cell_x_connector |
| (m_theme.get_cell (theme::cell_kind::TREE_X_CONNECTOR, m_style_id)); |
| const canvas::cell_t cell_y_connector |
| (m_theme.get_cell (theme::cell_kind::TREE_Y_CONNECTOR, m_style_id)); |
| size_t idx = 0; |
| for (auto &child : m_children) |
| { |
| child->paint_to_canvas (canvas); |
| |
| const bool last_child = (++idx == m_children.size ()); |
| canvas.paint (canvas::coord_t (min_x + 1, child->get_min_y ()), |
| cell_x_connector); |
| canvas.paint (canvas::coord_t (min_x, child->get_min_y ()), |
| last_child ? cell_child_final : cell_child_non_final); |
| if (!last_child) |
| for (int y = child->get_min_y () + 1; y <= child ->get_max_y (); y++) |
| canvas.paint (canvas::coord_t (min_x, y), cell_y_connector); |
| } |
| } |
| |
| #if CHECKING_P |
| |
| namespace selftest { |
| |
| static std::unique_ptr<tree_widget> |
| make_test_tree_widget (const dump_widget_info &dwi) |
| { |
| std::unique_ptr<tree_widget> w |
| (tree_widget::from_fmt (dwi, nullptr, "Root")); |
| for (int i = 0; i < 3; i++) |
| { |
| std::unique_ptr<tree_widget> c |
| (tree_widget::from_fmt (dwi, nullptr, "Child %i", i)); |
| for (int j = 0; j < 3; j++) |
| c->add_child (tree_widget::from_fmt (dwi, nullptr, |
| "Grandchild %i %i", i, j)); |
| w->add_child (std::move (c)); |
| } |
| return w; |
| } |
| |
| static void |
| test_tree_widget () |
| { |
| style_manager sm; |
| |
| style::id_t default_style_id (sm.get_or_create_id (style ())); |
| |
| { |
| ascii_theme theme; |
| dump_widget_info dwi (sm, theme, default_style_id); |
| canvas c (make_test_tree_widget (dwi)->to_canvas (sm)); |
| ASSERT_CANVAS_STREQ |
| (c, false, |
| ("Root\n" |
| "+- Child 0\n" |
| "| +- Grandchild 0 0\n" |
| "| +- Grandchild 0 1\n" |
| "| `- Grandchild 0 2\n" |
| "+- Child 1\n" |
| "| +- Grandchild 1 0\n" |
| "| +- Grandchild 1 1\n" |
| "| `- Grandchild 1 2\n" |
| "`- Child 2\n" |
| " +- Grandchild 2 0\n" |
| " +- Grandchild 2 1\n" |
| " `- Grandchild 2 2\n")); |
| } |
| |
| { |
| unicode_theme theme; |
| dump_widget_info dwi (sm, theme, default_style_id); |
| canvas c (make_test_tree_widget (dwi)->to_canvas (sm)); |
| ASSERT_CANVAS_STREQ |
| (c, false, |
| ("Root\n" |
| "├─ Child 0\n" |
| "│ ├─ Grandchild 0 0\n" |
| "│ ├─ Grandchild 0 1\n" |
| "│ ╰─ Grandchild 0 2\n" |
| "├─ Child 1\n" |
| "│ ├─ Grandchild 1 0\n" |
| "│ ├─ Grandchild 1 1\n" |
| "│ ╰─ Grandchild 1 2\n" |
| "╰─ Child 2\n" |
| " ├─ Grandchild 2 0\n" |
| " ├─ Grandchild 2 1\n" |
| " ╰─ Grandchild 2 2\n")); |
| } |
| } |
| |
| /* Run all selftests in this file. */ |
| |
| void |
| text_art_tree_widget_cc_tests () |
| { |
| test_tree_widget (); |
| } |
| |
| } // namespace selftest |
| |
| |
| #endif /* #if CHECKING_P */ |