Serenity Operating System
1/*
2 * Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include "ClassViewWidget.h"
8#include "HackStudio.h"
9#include "ProjectDeclarations.h"
10#include <AK/StdLibExtras.h>
11#include <LibGUI/BoxLayout.h>
12#include <string.h>
13
14namespace HackStudio {
15
16ClassViewWidget::ClassViewWidget()
17{
18 set_layout<GUI::VerticalBoxLayout>();
19 m_class_tree = add<GUI::TreeView>();
20
21 m_class_tree->on_selection_change = [this] {
22 const auto& index = m_class_tree->selection().first();
23 if (!index.is_valid())
24 return;
25
26 auto* node = static_cast<const ClassViewNode*>(index.internal_data());
27 if (!node->declaration)
28 return;
29
30 open_file(node->declaration->position.file, node->declaration->position.line, node->declaration->position.column);
31 };
32}
33
34RefPtr<ClassViewModel> ClassViewModel::create()
35{
36 return adopt_ref(*new ClassViewModel());
37}
38
39int ClassViewModel::row_count(const GUI::ModelIndex& index) const
40{
41 if (!index.is_valid())
42 return m_root_scope.size();
43 auto* node = static_cast<ClassViewNode*>(index.internal_data());
44 return node->children.size();
45}
46
47GUI::Variant ClassViewModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
48{
49 auto* node = static_cast<ClassViewNode const*>(index.internal_data());
50 switch (role) {
51 case GUI::ModelRole::Display: {
52 return node->name;
53 }
54 case GUI::ModelRole::Icon: {
55 if (!node->declaration)
56 return {};
57 auto icon = ProjectDeclarations::get_icon_for(node->declaration->type);
58 if (icon.has_value())
59 return icon.value();
60 return {};
61 }
62 default:
63 return {};
64 }
65}
66
67GUI::ModelIndex ClassViewModel::parent_index(const GUI::ModelIndex& index) const
68{
69 if (!index.is_valid())
70 return {};
71 auto* child = static_cast<ClassViewNode const*>(index.internal_data());
72 auto* parent = child->parent;
73 if (parent == nullptr)
74 return {};
75
76 if (parent->parent == nullptr) {
77 for (size_t row = 0; row < m_root_scope.size(); row++) {
78 if (m_root_scope[row].ptr() == parent)
79 return create_index(row, 0, parent);
80 }
81 VERIFY_NOT_REACHED();
82 }
83 for (size_t row = 0; row < parent->parent->children.size(); row++) {
84 ClassViewNode* child_at_row = parent->parent->children[row].ptr();
85 if (child_at_row == parent)
86 return create_index(row, 0, parent);
87 }
88 VERIFY_NOT_REACHED();
89}
90
91GUI::ModelIndex ClassViewModel::index(int row, int column, const GUI::ModelIndex& parent_index) const
92{
93 if (!parent_index.is_valid())
94 return create_index(row, column, m_root_scope[row].ptr());
95 auto* parent = static_cast<ClassViewNode const*>(parent_index.internal_data());
96 auto* child = parent->children[row].ptr();
97 return create_index(row, column, child);
98}
99
100ClassViewModel::ClassViewModel()
101{
102 m_root_scope.clear();
103 ProjectDeclarations::the().for_each_declared_symbol([this](auto& decl) {
104 if (decl.type == CodeComprehension::DeclarationType::Class
105 || decl.type == CodeComprehension::DeclarationType::Struct
106 || decl.type == CodeComprehension::DeclarationType::Member
107 || decl.type == CodeComprehension::DeclarationType::Namespace) {
108 add_declaration(decl);
109 }
110 });
111}
112
113static ClassViewNode& add_child_node(Vector<NonnullOwnPtr<ClassViewNode>>& children, NonnullOwnPtr<ClassViewNode>&& node_ptr, ClassViewNode* parent, CodeComprehension::Declaration const* declaration)
114{
115 node_ptr->parent = parent;
116 node_ptr->declaration = declaration;
117
118 size_t inserted_index = 0;
119 ClassViewNode& node = *node_ptr;
120 // Insert into parent's children list, sorted lexicographically by name.
121 children.insert_before_matching(
122 move(node_ptr), [&node](auto& other_node) {
123 return strncmp(node.name.characters_without_null_termination(), other_node->name.characters_without_null_termination(), min(node.name.length(), other_node->name.length())) < 0;
124 },
125 0, &inserted_index);
126
127 return *children.at(inserted_index);
128}
129
130void ClassViewModel::add_declaration(CodeComprehension::Declaration const& decl)
131{
132 ClassViewNode* parent = nullptr;
133 auto scope_parts = decl.scope.view().split_view("::"sv);
134
135 if (!scope_parts.is_empty()) {
136 // Traverse declarations tree to the parent of 'decl'
137 for (auto& node : m_root_scope) {
138 if (node->name == scope_parts.first())
139 parent = node;
140 }
141
142 if (parent == nullptr) {
143 m_root_scope.append(make<ClassViewNode>(scope_parts.first()));
144 parent = m_root_scope.last();
145 }
146
147 for (size_t i = 1; i < scope_parts.size(); ++i) {
148 auto& scope = scope_parts[i];
149 ClassViewNode* next { nullptr };
150 for (auto& child : parent->children) {
151 if (child->name == scope) {
152 next = child;
153 break;
154 }
155 }
156
157 if (next) {
158 parent = next;
159 continue;
160 }
161
162 parent = &add_child_node(parent->children, make<ClassViewNode>(scope), parent, nullptr);
163 }
164 }
165
166 Vector<NonnullOwnPtr<ClassViewNode>>* children_of_parent = nullptr;
167 if (parent) {
168 children_of_parent = &parent->children;
169 } else {
170 children_of_parent = &m_root_scope;
171 }
172
173 bool already_exists = false;
174 for (auto& child : *children_of_parent) {
175 if (child->name == decl.name) {
176 already_exists = true;
177 if (!child->declaration) {
178 child->declaration = &decl;
179 }
180 break;
181 }
182 }
183 if (!already_exists) {
184 add_child_node(*children_of_parent, make<ClassViewNode>(decl.name), parent, &decl);
185 }
186}
187
188void ClassViewWidget::refresh()
189{
190 m_class_tree->set_model(ClassViewModel::create());
191}
192
193}