diff --git a/Base/res/icons/hackstudio/Class.png b/Base/res/icons/hackstudio/Class.png new file mode 100644 index 0000000000..082bdd16f5 Binary files /dev/null and b/Base/res/icons/hackstudio/Class.png differ diff --git a/Base/res/icons/hackstudio/Function.png b/Base/res/icons/hackstudio/Function.png new file mode 100644 index 0000000000..c776832b37 Binary files /dev/null and b/Base/res/icons/hackstudio/Function.png differ diff --git a/Base/res/icons/hackstudio/Struct.png b/Base/res/icons/hackstudio/Struct.png new file mode 100644 index 0000000000..763d984654 Binary files /dev/null and b/Base/res/icons/hackstudio/Struct.png differ diff --git a/Base/res/icons/hackstudio/Variable.png b/Base/res/icons/hackstudio/Variable.png new file mode 100644 index 0000000000..ecafe2e39a Binary files /dev/null and b/Base/res/icons/hackstudio/Variable.png differ diff --git a/Userland/DevTools/HackStudio/Editor.cpp b/Userland/DevTools/HackStudio/Editor.cpp index f865ffef2c..a7754a79b3 100644 --- a/Userland/DevTools/HackStudio/Editor.cpp +++ b/Userland/DevTools/HackStudio/Editor.cpp @@ -522,19 +522,16 @@ void Editor::on_navigatable_link_click(const GUI::TextDocumentSpan& span) navigate_to_include_if_available(header_path); } -void Editor::open_and_set_cursor(const String& file, size_t line, size_t column) -{ - if (code_document().file_path() != file) - on_open(file); - set_cursor(GUI::TextPosition { line, column }); -} - void Editor::on_identifier_click(const GUI::TextDocumentSpan& span) { m_language_client->on_declaration_found = [this](const String& file, size_t line, size_t column) { - open_and_set_cursor(file, line, column); + HackStudio::open_file(file, line, column); }; m_language_client->search_declaration(code_document().file_path(), span.range.start().line(), span.range.start().column()); } +void Editor::set_cursor(const GUI::TextPosition& a_position) +{ + TextEditor::set_cursor(a_position); +} } diff --git a/Userland/DevTools/HackStudio/Editor.h b/Userland/DevTools/HackStudio/Editor.h index 87ef310a44..588965bfc9 100644 --- a/Userland/DevTools/HackStudio/Editor.h +++ b/Userland/DevTools/HackStudio/Editor.h @@ -30,6 +30,7 @@ #include "CodeDocument.h" #include "Debugger/BreakpointCallback.h" #include "LanguageClient.h" +#include #include #include #include @@ -66,6 +67,13 @@ public: virtual void undo() override; virtual void redo() override; + LanguageClient& language_client() + { + VERIFY(m_language_client); + return *m_language_client; + } + virtual void set_cursor(const GUI::TextPosition& a_position) override; + private: virtual void focusin_event(GUI::FocusEvent&) override; virtual void focusout_event(GUI::FocusEvent&) override; @@ -79,7 +87,6 @@ private: void navigate_to_include_if_available(String); void on_navigatable_link_click(const GUI::TextDocumentSpan&); void on_identifier_click(const GUI::TextDocumentSpan&); - void open_and_set_cursor(const String& file, size_t line, size_t column); Gfx::IntRect breakpoint_icon_rect(size_t line_number) const; static const Gfx::Bitmap& breakpoint_icon_bitmap(); diff --git a/Userland/DevTools/HackStudio/EditorWrapper.cpp b/Userland/DevTools/HackStudio/EditorWrapper.cpp index 40771b4d61..b96c4d645f 100644 --- a/Userland/DevTools/HackStudio/EditorWrapper.cpp +++ b/Userland/DevTools/HackStudio/EditorWrapper.cpp @@ -80,4 +80,6 @@ void EditorWrapper::set_editor_has_focus(Badge, bool focus) m_filename_label->set_font(focus ? Gfx::FontDatabase::default_bold_font() : Gfx::FontDatabase::default_font()); } +LanguageClient& EditorWrapper::language_client() { return m_editor->language_client(); } + } diff --git a/Userland/DevTools/HackStudio/EditorWrapper.h b/Userland/DevTools/HackStudio/EditorWrapper.h index 0cf67032a2..6dd19012c0 100644 --- a/Userland/DevTools/HackStudio/EditorWrapper.h +++ b/Userland/DevTools/HackStudio/EditorWrapper.h @@ -27,6 +27,7 @@ #pragma once #include "Debugger/BreakpointCallback.h" +#include "LanguageClient.h" #include #include #include @@ -48,6 +49,7 @@ public: const GUI::Label& filename_label() const { return *m_filename_label; } void set_editor_has_focus(Badge, bool); + LanguageClient& language_client(); private: EditorWrapper(); diff --git a/Userland/DevTools/HackStudio/HackStudio.h b/Userland/DevTools/HackStudio/HackStudio.h index 85247453d8..cf57b78a80 100644 --- a/Userland/DevTools/HackStudio/HackStudio.h +++ b/Userland/DevTools/HackStudio/HackStudio.h @@ -37,9 +37,12 @@ namespace HackStudio { GUI::TextEditor& current_editor(); void open_file(const String&); RefPtr current_editor_wrapper(); -void open_file(const String&); +void open_file(const String&, size_t line, size_t column); Project& project(); String currently_open_file(); void set_current_editor_wrapper(RefPtr); +class Locator; +Locator& locator(); + } diff --git a/Userland/DevTools/HackStudio/HackStudioWidget.h b/Userland/DevTools/HackStudio/HackStudioWidget.h index ef43639357..d7ce22f331 100644 --- a/Userland/DevTools/HackStudio/HackStudioWidget.h +++ b/Userland/DevTools/HackStudio/HackStudioWidget.h @@ -65,6 +65,12 @@ public: String currently_open_file() const { return m_currently_open_file; } void initialize_menubar(GUI::MenuBar&); + Locator& locator() + { + VERIFY(m_locator); + return *m_locator; + } + private: static String get_full_path_of_serenity_source(const String& file); diff --git a/Userland/DevTools/HackStudio/LanguageClient.cpp b/Userland/DevTools/HackStudio/LanguageClient.cpp index 7d701f46d2..fecd848cb8 100644 --- a/Userland/DevTools/HackStudio/LanguageClient.cpp +++ b/Userland/DevTools/HackStudio/LanguageClient.cpp @@ -25,6 +25,8 @@ */ #include "LanguageClient.h" +#include "HackStudio.h" +#include "Locator.h" #include #include #include @@ -50,11 +52,6 @@ void ServerConnection::handle(const Messages::LanguageClient::DeclarationLocatio m_language_client->declaration_found(message.location().file, message.location().line, message.location().column); } -void ServerConnection::handle(const Messages::LanguageClient::DeclarationList& message) -{ - (void)message; -} - void ServerConnection::die() { dbgln("ServerConnection::die()"); @@ -158,6 +155,10 @@ void ServerConnection::remove_instance_for_project(const String& project_path) auto key = LexicalPath { project_path }.string(); s_instances_for_projects.remove(key); } +void ServerConnection::handle(const Messages::LanguageClient::DeclarationsInDocument& message) +{ + locator().set_declared_symbols(message.filename(), message.declarations()); +} void LanguageClient::search_declaration(const String& path, size_t line, size_t column) { @@ -173,7 +174,6 @@ void LanguageClient::declaration_found(const String& file, size_t line, size_t c dbgln("on_declaration_found callback is not set"); return; } - dbgln("calling on_declaration_found"); on_declaration_found(file, line, column); } diff --git a/Userland/DevTools/HackStudio/Locator.cpp b/Userland/DevTools/HackStudio/Locator.cpp index 50c16fde1e..994600eab2 100644 --- a/Userland/DevTools/HackStudio/Locator.cpp +++ b/Userland/DevTools/HackStudio/Locator.cpp @@ -27,6 +27,7 @@ #include "Locator.h" #include "HackStudio.h" #include "Project.h" +#include #include #include #include @@ -37,7 +38,18 @@ namespace HackStudio { class LocatorSuggestionModel final : public GUI::Model { public: - explicit LocatorSuggestionModel(Vector&& suggestions) + struct Suggestion { + static Suggestion create_filename(const String& filename); + static Suggestion create_symbol_declaration(const GUI::AutocompleteProvider::Declaration&); + + bool is_filename() const { return as_filename.has_value(); } + bool is_symbol_declaration() const { return as_symbol_declaration.has_value(); } + + Optional as_filename; + Optional as_symbol_declaration; + }; + + explicit LocatorSuggestionModel(Vector&& suggestions) : m_suggestions(move(suggestions)) { } @@ -45,6 +57,7 @@ public: enum Column { Icon, Name, + Filename, __Column_Count, }; virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return m_suggestions.size(); } @@ -52,20 +65,63 @@ public: virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role) const override { auto& suggestion = m_suggestions.at(index.row()); - if (role == GUI::ModelRole::Display) { + if (role != GUI::ModelRole::Display) + return {}; + + if (suggestion.is_filename()) { if (index.column() == Column::Name) - return suggestion; + return suggestion.as_filename.value(); + if (index.column() == Column::Filename) + return ""; if (index.column() == Column::Icon) - return GUI::FileIconProvider::icon_for_path(suggestion); + return GUI::FileIconProvider::icon_for_path(suggestion.as_filename.value()); + } + if (suggestion.is_symbol_declaration()) { + if (index.column() == Column::Name) + return suggestion.as_symbol_declaration.value().name; + if (index.column() == Column::Filename) + return suggestion.as_symbol_declaration.value().position.file; + if (index.column() == Column::Icon) { + static GUI::Icon struct_icon(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/Struct.png")); + static GUI::Icon class_icon(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/Class.png")); + static GUI::Icon function_icon(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/Function.png")); + static GUI::Icon variable_icon(Gfx::Bitmap::load_from_file("/res/icons/hackstudio/Variable.png")); + switch (suggestion.as_symbol_declaration.value().type) { + case GUI::AutocompleteProvider::DeclarationType::Struct: + return struct_icon; + case GUI::AutocompleteProvider::DeclarationType::Class: + return class_icon; + case GUI::AutocompleteProvider::DeclarationType::Function: + return function_icon; + case GUI::AutocompleteProvider::DeclarationType::Variable: + return variable_icon; + } + return {}; + } } return {}; } virtual void update() override {}; + const Vector& suggestions() const { return m_suggestions; } + private: - Vector m_suggestions; + Vector m_suggestions; }; +LocatorSuggestionModel::Suggestion LocatorSuggestionModel::Suggestion::create_filename(const String& filename) +{ + LocatorSuggestionModel::Suggestion s; + s.as_filename = filename; + return s; +} +LocatorSuggestionModel::Suggestion LocatorSuggestionModel::Suggestion::create_symbol_declaration(const GUI::AutocompleteProvider::Declaration& decl) +{ + LocatorSuggestionModel::Suggestion s; + s.as_symbol_declaration = decl; + return s; +} + Locator::Locator() { set_layout(); @@ -128,9 +184,16 @@ Locator::~Locator() void Locator::open_suggestion(const GUI::ModelIndex& index) { - auto filename_index = m_suggestion_view->model()->index(index.row(), LocatorSuggestionModel::Column::Name); - auto filename = filename_index.data().to_string(); - open_file(filename); + auto& model = reinterpret_cast(*m_suggestion_view->model()); + auto suggestion = model.suggestions()[index.row()]; + if (suggestion.is_filename()) { + auto filename = suggestion.as_filename.value(); + open_file(filename); + } + if (suggestion.is_symbol_declaration()) { + auto position = suggestion.as_symbol_declaration.value().position; + open_file(position.file, position.line, position.column); + } close(); } @@ -151,14 +214,25 @@ void Locator::close() void Locator::update_suggestions() { auto typed_text = m_textbox->text(); - Vector suggestions; + Vector suggestions; project().for_each_text_file([&](auto& file) { if (file.name().contains(typed_text, CaseSensitivity::CaseInsensitive)) - suggestions.append(file.name()); + suggestions.append(LocatorSuggestionModel::Suggestion::create_filename(file.name())); }); + + for (auto& item : m_document_to_declarations) { + for (auto& decl : item.value) { + if (decl.name.contains(typed_text, CaseSensitivity::CaseInsensitive)) + suggestions.append((LocatorSuggestionModel::Suggestion::create_symbol_declaration(decl))); + } + } + dbgln("I have {} suggestion(s):", suggestions.size()); for (auto& s : suggestions) { - dbgln(" {}", s); + if (s.is_filename()) + dbgln(" {}", s.as_filename.value()); + if (s.is_symbol_declaration()) + dbgln(" {} ({})", s.as_symbol_declaration.value().name, s.as_symbol_declaration.value().position.file); } bool has_suggestions = !suggestions.is_empty(); @@ -174,5 +248,9 @@ void Locator::update_suggestions() dbgln("Popup rect: {}", m_popup_window->rect()); m_popup_window->show(); } +void Locator::set_declared_symbols(const String& filename, const Vector& declarations) +{ + m_document_to_declarations.set(filename, declarations); +} } diff --git a/Userland/DevTools/HackStudio/Locator.h b/Userland/DevTools/HackStudio/Locator.h index e52eb02447..9dfe28aca0 100644 --- a/Userland/DevTools/HackStudio/Locator.h +++ b/Userland/DevTools/HackStudio/Locator.h @@ -26,6 +26,8 @@ #pragma once +#include +#include #include namespace HackStudio { @@ -37,6 +39,7 @@ public: void open(); void close(); + void set_declared_symbols(const String& filename, const Vector&); private: void update_suggestions(); @@ -47,6 +50,7 @@ private: RefPtr m_textbox; RefPtr m_popup_window; RefPtr m_suggestion_view; + HashMap> m_document_to_declarations; }; } diff --git a/Userland/DevTools/HackStudio/main.cpp b/Userland/DevTools/HackStudio/main.cpp index 2384dce1d0..bc2636e6e9 100644 --- a/Userland/DevTools/HackStudio/main.cpp +++ b/Userland/DevTools/HackStudio/main.cpp @@ -24,6 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "Editor.h" #include "HackStudio.h" #include "HackStudioWidget.h" #include "Project.h" @@ -153,7 +154,13 @@ GUI::TextEditor& current_editor() void open_file(const String& file_name) { - return s_hack_studio_widget->open_file(file_name); + s_hack_studio_widget->open_file(file_name); +} + +void open_file(const String& file_name, size_t line, size_t column) +{ + s_hack_studio_widget->open_file(file_name); + s_hack_studio_widget->current_editor_wrapper().editor().set_cursor({ line, column }); } RefPtr current_editor_wrapper() @@ -180,4 +187,9 @@ void set_current_editor_wrapper(RefPtr wrapper) s_hack_studio_widget->set_current_editor_wrapper(wrapper); } +Locator& locator() +{ + return s_hack_studio_widget->locator(); +} + } diff --git a/Userland/Libraries/LibCpp/AST.h b/Userland/Libraries/LibCpp/AST.h index 26bcba7bd8..ce797609ba 100644 --- a/Userland/Libraries/LibCpp/AST.h +++ b/Userland/Libraries/LibCpp/AST.h @@ -149,7 +149,6 @@ protected: : Statement(parent, start, end, filename) { } - }; class InvalidDeclaration : public Declaration { diff --git a/Userland/Libraries/LibGUI/TextEditor.h b/Userland/Libraries/LibGUI/TextEditor.h index 052e6cb024..30083da163 100644 --- a/Userland/Libraries/LibGUI/TextEditor.h +++ b/Userland/Libraries/LibGUI/TextEditor.h @@ -174,7 +174,7 @@ public: void set_cursor_and_focus_line(size_t line, size_t column); void set_cursor(size_t line, size_t column); - void set_cursor(const TextPosition&); + virtual void set_cursor(const TextPosition&); const Syntax::Highlighter* syntax_highlighter() const; void set_syntax_highlighter(OwnPtr);