/* Tree diagrams. Copyright (C) 2024-2025 Free Software Foundation, Inc. Contributed by David Malcolm . 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 . */ #include "config.h" #define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "pretty-print.h" #include "selftest.h" #include "make-unique.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::make (styled_string str, const theme &theme, style::id_t style_id) { return ::make_unique (::make_unique (std::move (str)), theme, style_id); } std::unique_ptr 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::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::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 make_test_tree_widget (const dump_widget_info &dwi) { std::unique_ptr w (tree_widget::from_fmt (dwi, nullptr, "Root")); for (int i = 0; i < 3; i++) { std::unique_ptr 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 */