blob: 33c918d0023e58059ecbb4e93b61a73f80b1bd1c [file] [log] [blame]
/* 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 */