/* * Copyright (c) 2021, Itamar S. * * SPDX-License-Identifier: BSD-2-Clause */ #include "ClassViewWidget.h" #include "HackStudio.h" #include "ProjectDeclarations.h" #include #include #include namespace HackStudio { ClassViewWidget::ClassViewWidget() { set_layout(); m_class_tree = add(); m_class_tree->on_selection_change = [this] { const auto& index = m_class_tree->selection().first(); if (!index.is_valid()) return; auto* node = static_cast(index.internal_data()); if (!node->declaration) return; open_file(node->declaration->position.file, node->declaration->position.line, node->declaration->position.column); }; } RefPtr 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(index.internal_data()); return node->children.size(); } GUI::Variant ClassViewModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const { auto* node = static_cast(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(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(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& children, NonnullOwnPtr&& 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(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(scope), parent, nullptr); } } NonnullOwnPtrVector* 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(decl.name), parent, &decl); } } void ClassViewWidget::refresh() { m_class_tree->set_model(ClassViewModel::create()); } }