mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-23 18:02:05 -05:00
HackStudio: Support searching symbol declarations in the Locator
The Locator now keeps a cache of the declared symbol in a document. The language client updates that cache whenever it gets an update from the language server about declared symbols. This allows searching for symbol declarations in the Locator, in addition to file names. Closes #5478
This commit is contained in:
parent
a94b5376bc
commit
54bc9114b3
16 changed files with 140 additions and 30 deletions
BIN
Base/res/icons/hackstudio/Class.png
Normal file
BIN
Base/res/icons/hackstudio/Class.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 892 B |
BIN
Base/res/icons/hackstudio/Function.png
Normal file
BIN
Base/res/icons/hackstudio/Function.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 708 B |
BIN
Base/res/icons/hackstudio/Struct.png
Normal file
BIN
Base/res/icons/hackstudio/Struct.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 918 B |
BIN
Base/res/icons/hackstudio/Variable.png
Normal file
BIN
Base/res/icons/hackstudio/Variable.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 805 B |
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "CodeDocument.h"
|
||||
#include "Debugger/BreakpointCallback.h"
|
||||
#include "LanguageClient.h"
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibGUI/TextEditor.h>
|
||||
|
@ -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();
|
||||
|
|
|
@ -80,4 +80,6 @@ void EditorWrapper::set_editor_has_focus(Badge<Editor>, 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(); }
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "Debugger/BreakpointCallback.h"
|
||||
#include "LanguageClient.h"
|
||||
#include <AK/Function.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGUI/Widget.h>
|
||||
|
@ -48,6 +49,7 @@ public:
|
|||
const GUI::Label& filename_label() const { return *m_filename_label; }
|
||||
|
||||
void set_editor_has_focus(Badge<Editor>, bool);
|
||||
LanguageClient& language_client();
|
||||
|
||||
private:
|
||||
EditorWrapper();
|
||||
|
|
|
@ -37,9 +37,12 @@ namespace HackStudio {
|
|||
GUI::TextEditor& current_editor();
|
||||
void open_file(const String&);
|
||||
RefPtr<EditorWrapper> 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<EditorWrapper>);
|
||||
|
||||
class Locator;
|
||||
Locator& locator();
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
*/
|
||||
|
||||
#include "LanguageClient.h"
|
||||
#include "HackStudio.h"
|
||||
#include "Locator.h"
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <DevTools/HackStudio/LanguageServers/LanguageServerEndpoint.h>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "Locator.h"
|
||||
#include "HackStudio.h"
|
||||
#include "Project.h"
|
||||
#include <LibGUI/AutocompleteProvider.h>
|
||||
#include <LibGUI/BoxLayout.h>
|
||||
#include <LibGUI/FileIconProvider.h>
|
||||
#include <LibGUI/TableView.h>
|
||||
|
@ -37,7 +38,18 @@ namespace HackStudio {
|
|||
|
||||
class LocatorSuggestionModel final : public GUI::Model {
|
||||
public:
|
||||
explicit LocatorSuggestionModel(Vector<String>&& 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<String> as_filename;
|
||||
Optional<GUI::AutocompleteProvider::Declaration> as_symbol_declaration;
|
||||
};
|
||||
|
||||
explicit LocatorSuggestionModel(Vector<Suggestion>&& 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<Suggestion>& suggestions() const { return m_suggestions; }
|
||||
|
||||
private:
|
||||
Vector<String> m_suggestions;
|
||||
Vector<Suggestion> 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<GUI::VerticalBoxLayout>();
|
||||
|
@ -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<LocatorSuggestionModel&>(*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<String> suggestions;
|
||||
Vector<LocatorSuggestionModel::Suggestion> 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<GUI::AutocompleteProvider::Declaration>& declarations)
|
||||
{
|
||||
m_document_to_declarations.set(filename, declarations);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <LibGUI/AutocompleteProvider.h>
|
||||
#include <LibGUI/Widget.h>
|
||||
|
||||
namespace HackStudio {
|
||||
|
@ -37,6 +39,7 @@ public:
|
|||
|
||||
void open();
|
||||
void close();
|
||||
void set_declared_symbols(const String& filename, const Vector<GUI::AutocompleteProvider::Declaration>&);
|
||||
|
||||
private:
|
||||
void update_suggestions();
|
||||
|
@ -47,6 +50,7 @@ private:
|
|||
RefPtr<GUI::TextBox> m_textbox;
|
||||
RefPtr<GUI::Window> m_popup_window;
|
||||
RefPtr<GUI::TableView> m_suggestion_view;
|
||||
HashMap<String, Vector<GUI::AutocompleteProvider::Declaration>> m_document_to_declarations;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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<EditorWrapper> current_editor_wrapper()
|
||||
|
@ -180,4 +187,9 @@ void set_current_editor_wrapper(RefPtr<EditorWrapper> wrapper)
|
|||
s_hack_studio_widget->set_current_editor_wrapper(wrapper);
|
||||
}
|
||||
|
||||
Locator& locator()
|
||||
{
|
||||
return s_hack_studio_widget->locator();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -149,7 +149,6 @@ protected:
|
|||
: Statement(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class InvalidDeclaration : public Declaration {
|
||||
|
|
|
@ -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<Syntax::Highlighter>);
|
||||
|
|
Loading…
Add table
Reference in a new issue