ladybird/Userland/DevTools/HackStudio/ClassViewWidget.cpp

193 lines
5.9 KiB
C++

/*
* Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "ClassViewWidget.h"
#include "HackStudio.h"
#include "ProjectDeclarations.h"
#include <AK/StdLibExtras.h>
#include <LibGUI/BoxLayout.h>
#include <string.h>
namespace HackStudio {
ClassViewWidget::ClassViewWidget()
{
set_layout<GUI::VerticalBoxLayout>();
m_class_tree = add<GUI::TreeView>();
m_class_tree->on_selection_change = [this] {
const auto& index = m_class_tree->selection().first();
if (!index.is_valid())
return;
auto* node = static_cast<const ClassViewNode*>(index.internal_data());
if (!node->declaration)
return;
open_file(node->declaration->position.file, node->declaration->position.line, node->declaration->position.column);
};
}
RefPtr<ClassViewModel> ClassViewModel::create()
{
return adopt_ref(*new ClassViewModel());
}
int ClassViewModel::row_count(const GUI::ModelIndex& index) const
{
if (!index.is_valid())
return m_root_scope.size();
auto* node = static_cast<ClassViewNode*>(index.internal_data());
return node->children.size();
}
GUI::Variant ClassViewModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
{
auto* node = static_cast<const ClassViewNode*>(index.internal_data());
switch (role) {
case GUI::ModelRole::Display: {
return node->name;
}
case GUI::ModelRole::Icon: {
if (!node->declaration)
return {};
auto icon = ProjectDeclarations::get_icon_for(node->declaration->type);
if (icon.has_value())
return icon.value();
return {};
}
default:
return {};
}
}
GUI::ModelIndex ClassViewModel::parent_index(const GUI::ModelIndex& index) const
{
if (!index.is_valid())
return {};
auto* child = static_cast<const ClassViewNode*>(index.internal_data());
auto* parent = child->parent;
if (parent == nullptr)
return {};
if (parent->parent == nullptr) {
for (size_t row = 0; row < m_root_scope.size(); row++) {
if (m_root_scope.ptr_at(row).ptr() == parent)
return create_index(row, 0, parent);
}
VERIFY_NOT_REACHED();
}
for (size_t row = 0; row < parent->parent->children.size(); row++) {
ClassViewNode* child_at_row = parent->parent->children.ptr_at(row).ptr();
if (child_at_row == parent)
return create_index(row, 0, parent);
}
VERIFY_NOT_REACHED();
}
GUI::ModelIndex ClassViewModel::index(int row, int column, const GUI::ModelIndex& parent_index) const
{
if (!parent_index.is_valid())
return create_index(row, column, &m_root_scope[row]);
auto* parent = static_cast<const ClassViewNode*>(parent_index.internal_data());
auto* child = &parent->children[row];
return create_index(row, column, child);
}
ClassViewModel::ClassViewModel()
{
m_root_scope.clear();
ProjectDeclarations::the().for_each_declared_symbol([this](auto& decl) {
if (decl.type == GUI::AutocompleteProvider::DeclarationType::Class
|| decl.type == GUI::AutocompleteProvider::DeclarationType::Struct
|| decl.type == GUI::AutocompleteProvider::DeclarationType::Member
|| decl.type == GUI::AutocompleteProvider::DeclarationType::Namespace) {
add_declaration(decl);
}
});
}
static ClassViewNode& add_child_node(NonnullOwnPtrVector<ClassViewNode>& children, NonnullOwnPtr<ClassViewNode>&& node_ptr, ClassViewNode* parent, const GUI::AutocompleteProvider::Declaration* declaration)
{
node_ptr->parent = parent;
node_ptr->declaration = declaration;
size_t inserted_index = 0;
ClassViewNode& node = *node_ptr;
// Insert into parent's children list, sorted lexicographically by name.
children.insert_before_matching(
move(node_ptr), [&node](auto& other_node) {
return strncmp(node.name.characters_without_null_termination(), other_node->name.characters_without_null_termination(), min(node.name.length(), other_node->name.length())) < 0;
},
0, &inserted_index);
return children.at(inserted_index);
}
void ClassViewModel::add_declaration(const GUI::AutocompleteProvider::Declaration& decl)
{
ClassViewNode* parent = nullptr;
auto scope_parts = decl.scope.view().split_view("::");
if (!scope_parts.is_empty()) {
// Traverse declarations tree to the parent of 'decl'
for (auto& node : m_root_scope) {
if (node.name == scope_parts.first())
parent = &node;
}
if (parent == nullptr) {
m_root_scope.append(make<ClassViewNode>(scope_parts.first()));
parent = &m_root_scope.last();
}
for (size_t i = 1; i < scope_parts.size(); ++i) {
auto& scope = scope_parts[i];
ClassViewNode* next { nullptr };
for (auto& child : parent->children) {
if (child.name == scope) {
next = &child;
break;
}
}
if (next) {
parent = next;
continue;
}
parent = &add_child_node(parent->children, make<ClassViewNode>(scope), parent, nullptr);
}
}
NonnullOwnPtrVector<ClassViewNode>* children_of_parent = nullptr;
if (parent) {
children_of_parent = &parent->children;
} else {
children_of_parent = &m_root_scope;
}
bool already_exists = false;
for (auto& child : *children_of_parent) {
if (child.name == decl.name) {
already_exists = true;
if (!child.declaration) {
child.declaration = &decl;
}
break;
}
}
if (!already_exists) {
add_child_node(*children_of_parent, make<ClassViewNode>(decl.name), parent, &decl);
}
}
void ClassViewWidget::refresh()
{
m_class_tree->set_model(ClassViewModel::create());
}
}