Jelajahi Sumber

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.
Sam Atkins 1 tahun lalu
induk
melakukan
85101c6626

+ 31 - 0
Userland/DevTools/HackStudio/DeclarationsModel.cpp

@@ -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();
+}
+
 }

+ 2 - 0
Userland/DevTools/HackStudio/DeclarationsModel.h

@@ -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;

+ 7 - 18
Userland/DevTools/HackStudio/Locator.cpp

@@ -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()));
-    });
-
-    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))));
+    m_model->set_filter_term(m_textbox->text());
 
-    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));

+ 2 - 0
Userland/DevTools/HackStudio/Locator.h

@@ -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;
 };
 
 }

+ 28 - 3
Userland/DevTools/HackStudio/ProjectDeclarations.cpp

@@ -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));
+}
+
+}

+ 8 - 1
Userland/DevTools/HackStudio/ProjectDeclarations.h

@@ -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>