HackStudio: Keep the DeclarationsModel around, and use a filtering proxy

Rather than construct a new DeclarationsModel each time the user types
something in the Locator, keep a single one around permanently in the
ProjectDeclarations, and then use a FilteringProxyModel over it for the
suggestions.
This commit is contained in:
Sam Atkins 2024-01-25 14:19:15 +00:00 committed by Sam Atkins
parent e72b14ef1d
commit 85101c6626
6 changed files with 78 additions and 22 deletions

View file

@ -57,4 +57,35 @@ GUI::Variant DeclarationsModel::data(GUI::ModelIndex const& index, GUI::ModelRol
return {};
}
GUI::Model::MatchResult DeclarationsModel::data_matches(GUI::ModelIndex const& index, GUI::Variant const& term) const
{
if (index.row() < 0 || (size_t)index.row() >= m_declarations.size())
return { TriState::False };
auto needle = term.as_string();
if (needle.is_empty())
return { TriState::True };
auto& declaration = m_declarations[index.row()];
if (declaration.is_filename()) {
if (declaration.as_filename->contains(needle, CaseSensitivity::CaseInsensitive))
return { TriState::True };
return { TriState::False };
}
if (declaration.is_symbol_declaration()) {
if (declaration.as_symbol_declaration->name.contains(needle, CaseSensitivity::CaseInsensitive)
|| declaration.as_symbol_declaration->scope.contains(needle, CaseSensitivity::CaseInsensitive))
return { TriState::True };
return { TriState::False };
}
return { TriState::False };
}
void DeclarationsModel::set_declarations(Vector<HackStudio::Declaration>&& declarations)
{
m_declarations = move(declarations);
did_update();
}
}

View file

@ -48,8 +48,10 @@ public:
virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return Column::__Column_Count; }
virtual GUI::Variant data(GUI::ModelIndex const& index, GUI::ModelRole role) const override;
virtual MatchResult data_matches(GUI::ModelIndex const&, GUI::Variant const&) const override;
Vector<Declaration> const& declarations() const { return m_declarations; }
void set_declarations(Vector<Declaration>&&);
private:
Vector<Declaration> m_declarations;

View file

@ -77,12 +77,15 @@ Locator::Locator(Core::EventReceiver* parent)
m_suggestion_view->on_activation = [this](auto& index) {
open_suggestion(index);
};
m_model = GUI::FilteringProxyModel::create(ProjectDeclarations::the().declarations_model()).release_value_but_fixme_should_propagate_errors();
m_suggestion_view->set_model(m_model);
}
void Locator::open_suggestion(const GUI::ModelIndex& index)
{
auto& model = reinterpret_cast<DeclarationsModel&>(*m_suggestion_view->model());
auto suggestion = model.declarations()[index.row()];
auto original_index = m_model->map(index);
auto suggestion = ProjectDeclarations::the().declarations_model().declarations()[original_index.row()];
if (suggestion.is_filename()) {
auto filename = suggestion.as_filename.value();
open_file(filename);
@ -111,23 +114,9 @@ void Locator::close()
void Locator::update_suggestions()
{
auto typed_text = m_textbox->text();
Vector<Declaration> suggestions;
project().for_each_text_file([&](auto& file) {
if (file.name().contains(typed_text, CaseSensitivity::CaseInsensitive))
suggestions.append(Declaration::create_filename(file.name()));
});
m_model->set_filter_term(m_textbox->text());
ProjectDeclarations::the().for_each_declared_symbol([&suggestions, &typed_text](auto& decl) {
if (decl.name.contains(typed_text, CaseSensitivity::CaseInsensitive) || decl.scope.contains(typed_text, CaseSensitivity::CaseInsensitive))
suggestions.append((Declaration::create_symbol_declaration(decl)));
});
bool has_suggestions = !suggestions.is_empty();
m_suggestion_view->set_model(adopt_ref(*new DeclarationsModel(move(suggestions))));
if (!has_suggestions)
if (m_model->row_count() == 0)
m_suggestion_view->selection().clear();
else
m_suggestion_view->selection().set(m_suggestion_view->model()->index(0));

View file

@ -8,6 +8,7 @@
#pragma once
#include <AK/HashMap.h>
#include <LibGUI/FilteringProxyModel.h>
#include <LibGUI/Widget.h>
namespace HackStudio {
@ -29,6 +30,7 @@ private:
RefPtr<GUI::TextBox> m_textbox;
RefPtr<GUI::Window> m_popup_window;
RefPtr<GUI::TableView> m_suggestion_view;
RefPtr<GUI::FilteringProxyModel> m_model;
};
}

View file

@ -1,24 +1,35 @@
/*
* Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
* Copyright (c) 2024, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "ProjectDeclarations.h"
#include "HackStudio.h"
HackStudio::ProjectDeclarations& HackStudio::ProjectDeclarations::the()
namespace HackStudio {
ProjectDeclarations::ProjectDeclarations()
: m_declarations_model(adopt_ref(*new DeclarationsModel({})))
{
}
ProjectDeclarations& ProjectDeclarations::the()
{
static ProjectDeclarations s_instance;
return s_instance;
}
void HackStudio::ProjectDeclarations::set_declared_symbols(ByteString const& filename, Vector<CodeComprehension::Declaration> const& declarations)
void ProjectDeclarations::set_declared_symbols(ByteString const& filename, Vector<CodeComprehension::Declaration> const& declarations)
{
m_document_to_declarations.set(filename, declarations);
// FIXME: Partially invalidate the model instead of fully rebuilding it.
update_declarations_model();
if (on_update)
on_update();
}
Optional<GUI::Icon> HackStudio::ProjectDeclarations::get_icon_for(CodeComprehension::DeclarationType type)
Optional<GUI::Icon> ProjectDeclarations::get_icon_for(CodeComprehension::DeclarationType type)
{
static GUI::Icon struct_icon(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/Struct.png"sv).release_value_but_fixme_should_propagate_errors());
static GUI::Icon class_icon(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/Class.png"sv).release_value_but_fixme_should_propagate_errors());
@ -46,3 +57,17 @@ Optional<GUI::Icon> HackStudio::ProjectDeclarations::get_icon_for(CodeComprehens
return {};
}
}
void ProjectDeclarations::update_declarations_model()
{
Vector<Declaration> declarations;
project().for_each_text_file([&](auto& file) {
declarations.append(Declaration::create_filename(file.name()));
});
for_each_declared_symbol([&declarations](auto& decl) {
declarations.append((Declaration::create_symbol_declaration(decl)));
});
m_declarations_model->set_declarations(move(declarations));
}
}

View file

@ -1,11 +1,13 @@
/*
* Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
* Copyright (c) 2024, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "DeclarationsModel.h"
#include <AK/ByteString.h>
#include <AK/Function.h>
#include <AK/HashMap.h>
@ -25,13 +27,18 @@ public:
void set_declared_symbols(ByteString const& filename, Vector<CodeComprehension::Declaration> const&);
DeclarationsModel& declarations_model() { return m_declarations_model; }
void update_declarations_model();
static Optional<GUI::Icon> get_icon_for(CodeComprehension::DeclarationType);
Function<void()> on_update = nullptr;
private:
ProjectDeclarations() = default;
ProjectDeclarations();
HashMap<ByteString, Vector<CodeComprehension::Declaration>> m_document_to_declarations;
NonnullRefPtr<DeclarationsModel> m_declarations_model;
};
template<typename Func>