mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 18:57:42 +00:00
LanguageServers/Cpp: Add 'FindDeclaration' capability
The C++ LanguageServer can now find the matching declaration for variable names, function calls, struct/class types and properties. When clicking on one of the above with Ctrl pressed, HackStudio will ask the language server to find a matching declaration, and navigate to the result in the Editor. :^)
This commit is contained in:
parent
d3ff82ba80
commit
5bc82c0185
12 changed files with 150 additions and 38 deletions
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
||||||
|
* 2018-2021, the SerenityOS developers
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -153,9 +154,7 @@ void Editor::show_documentation_tooltip_if_available(const String& hovered_token
|
||||||
{
|
{
|
||||||
auto it = man_paths().find(hovered_token);
|
auto it = man_paths().find(hovered_token);
|
||||||
if (it == man_paths().end()) {
|
if (it == man_paths().end()) {
|
||||||
#if EDITOR_DEBUG
|
dbgln_if(EDITOR_DEBUG, "no man path for {}", hovered_token);
|
||||||
dbgln("no man path for {}", hovered_token);
|
|
||||||
#endif
|
|
||||||
m_documentation_tooltip_window->hide();
|
m_documentation_tooltip_window->hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -164,9 +163,7 @@ void Editor::show_documentation_tooltip_if_available(const String& hovered_token
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if EDITOR_DEBUG
|
dbgln(EDITOR_DEBUG, "opening {}", it->value);
|
||||||
dbgln("opening {}", it->value);
|
|
||||||
#endif
|
|
||||||
auto file = Core::File::construct(it->value);
|
auto file = Core::File::construct(it->value);
|
||||||
if (!file->open(Core::File::ReadOnly)) {
|
if (!file->open(Core::File::ReadOnly)) {
|
||||||
dbgln("failed to open {}, {}", it->value, file->error_string());
|
dbgln("failed to open {}, {}", it->value, file->error_string());
|
||||||
|
@ -236,9 +233,7 @@ void Editor::mousemove_event(GUI::MouseEvent& event)
|
||||||
auto end_line_length = document().line(span.range.end().line()).length();
|
auto end_line_length = document().line(span.range.end().line()).length();
|
||||||
adjusted_range.end().set_column(min(end_line_length, adjusted_range.end().column() + 1));
|
adjusted_range.end().set_column(min(end_line_length, adjusted_range.end().column() + 1));
|
||||||
auto hovered_span_text = document().text_in_range(adjusted_range);
|
auto hovered_span_text = document().text_in_range(adjusted_range);
|
||||||
#if EDITOR_DEBUG
|
dbgln_if(EDITOR_DEBUG, "Hovering: {} \"{}\"", adjusted_range, hovered_span_text);
|
||||||
dbgln("Hovering: {} \"{}\"", adjusted_range, hovered_span_text);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (is_clickable) {
|
if (is_clickable) {
|
||||||
is_over_clickable = true;
|
is_over_clickable = true;
|
||||||
|
@ -298,6 +293,10 @@ void Editor::mousedown_event(GUI::MouseEvent& event)
|
||||||
on_navigatable_link_click(*span);
|
on_navigatable_link_click(*span);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (highlighter->is_identifier(span->data)) {
|
||||||
|
on_identifier_click(*span);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GUI::TextEditor::mousedown_event(event);
|
GUI::TextEditor::mousedown_event(event);
|
||||||
|
@ -325,9 +324,7 @@ static HashMap<String, String>& include_paths()
|
||||||
auto path = it.next_full_path();
|
auto path = it.next_full_path();
|
||||||
if (!Core::File::is_directory(path)) {
|
if (!Core::File::is_directory(path)) {
|
||||||
auto key = path.substring(base.length() + 1, path.length() - base.length() - 1);
|
auto key = path.substring(base.length() + 1, path.length() - base.length() - 1);
|
||||||
#if EDITOR_DEBUG
|
dbgln_if(EDITOR_DEBUG, "Adding header \"{}\" in path \"{}\"", key, path);
|
||||||
dbgln("Adding header \"{}\" in path \"{}\"", key, path);
|
|
||||||
#endif
|
|
||||||
paths.set(key, path);
|
paths.set(key, path);
|
||||||
} else {
|
} else {
|
||||||
handle_directory(base, path, handle_directory);
|
handle_directory(base, path, handle_directory);
|
||||||
|
@ -349,9 +346,7 @@ void Editor::navigate_to_include_if_available(String path)
|
||||||
{
|
{
|
||||||
auto it = include_paths().find(path);
|
auto it = include_paths().find(path);
|
||||||
if (it == include_paths().end()) {
|
if (it == include_paths().end()) {
|
||||||
#if EDITOR_DEBUG
|
dbgln_if(EDITOR_DEBUG, "no header {} found.", path);
|
||||||
dbgln("no header {} found.", path);
|
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -523,9 +518,23 @@ void Editor::on_navigatable_link_click(const GUI::TextDocumentSpan& span)
|
||||||
adjusted_range.end().set_column(adjusted_range.end().column() + 1);
|
adjusted_range.end().set_column(adjusted_range.end().column() + 1);
|
||||||
auto span_text = document().text_in_range(adjusted_range);
|
auto span_text = document().text_in_range(adjusted_range);
|
||||||
auto header_path = span_text.substring(1, span_text.length() - 2);
|
auto header_path = span_text.substring(1, span_text.length() - 2);
|
||||||
#if EDITOR_DEBUG
|
dbgln_if(EDITOR_DEBUG, "Ctrl+click: {} \"{}\"", adjusted_range, header_path);
|
||||||
dbgln("Ctrl+click: {} \"{}\"", adjusted_range, header_path);
|
|
||||||
#endif
|
|
||||||
navigate_to_include_if_available(header_path);
|
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);
|
||||||
|
};
|
||||||
|
m_language_client->search_declaration(code_document().file_path(), span.range.start().line(), span.range.start().column());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
||||||
|
* 2018-2021, the SerenityOS developers
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -77,6 +78,8 @@ private:
|
||||||
void show_documentation_tooltip_if_available(const String&, const Gfx::IntPoint& screen_location);
|
void show_documentation_tooltip_if_available(const String&, const Gfx::IntPoint& screen_location);
|
||||||
void navigate_to_include_if_available(String);
|
void navigate_to_include_if_available(String);
|
||||||
void on_navigatable_link_click(const GUI::TextDocumentSpan&);
|
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;
|
Gfx::IntRect breakpoint_icon_rect(size_t line_number) const;
|
||||||
static const Gfx::Bitmap& breakpoint_icon_bitmap();
|
static const Gfx::Bitmap& breakpoint_icon_bitmap();
|
||||||
|
|
|
@ -40,6 +40,14 @@ void ServerConnection::handle(const Messages::LanguageClient::AutoCompleteSugges
|
||||||
}
|
}
|
||||||
m_language_client->provide_autocomplete_suggestions(message.suggestions());
|
m_language_client->provide_autocomplete_suggestions(message.suggestions());
|
||||||
}
|
}
|
||||||
|
void ServerConnection::handle(const Messages::LanguageClient::DeclarationLocation& message)
|
||||||
|
{
|
||||||
|
if (!m_language_client) {
|
||||||
|
dbgln("Language Server connection has no attached language client");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_language_client->declaration_found(message.file_name(), message.line(), message.column());
|
||||||
|
}
|
||||||
|
|
||||||
void ServerConnection::die()
|
void ServerConnection::die()
|
||||||
{
|
{
|
||||||
|
@ -87,8 +95,6 @@ void LanguageClient::request_autocomplete(const String& path, size_t cursor_line
|
||||||
|
|
||||||
void LanguageClient::provide_autocomplete_suggestions(const Vector<GUI::AutocompleteProvider::Entry>& suggestions)
|
void LanguageClient::provide_autocomplete_suggestions(const Vector<GUI::AutocompleteProvider::Entry>& suggestions)
|
||||||
{
|
{
|
||||||
if (!m_server_connection)
|
|
||||||
return;
|
|
||||||
if (on_autocomplete_suggestions)
|
if (on_autocomplete_suggestions)
|
||||||
on_autocomplete_suggestions(suggestions);
|
on_autocomplete_suggestions(suggestions);
|
||||||
|
|
||||||
|
@ -147,4 +153,22 @@ void ServerConnection::remove_instance_for_project(const String& project_path)
|
||||||
s_instances_for_projects.remove(key);
|
s_instances_for_projects.remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LanguageClient::search_declaration(const String& path, size_t line, size_t column)
|
||||||
|
{
|
||||||
|
if (!m_server_connection)
|
||||||
|
return;
|
||||||
|
set_active_client();
|
||||||
|
m_server_connection->post_message(Messages::LanguageServer::FindDeclaration(path, line, column));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LanguageClient::declaration_found(const String& file, size_t line, size_t column)
|
||||||
|
{
|
||||||
|
if (!on_declaration_found) {
|
||||||
|
dbgln("on_declaration_found callback is not set");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dbgln("calling on_declaration_found");
|
||||||
|
on_declaration_found(file, line, column);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,6 +90,7 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void handle(const Messages::LanguageClient::AutoCompleteSuggestions&) override;
|
virtual void handle(const Messages::LanguageClient::AutoCompleteSuggestions&) override;
|
||||||
|
virtual void handle(const Messages::LanguageClient::DeclarationLocation&) override;
|
||||||
|
|
||||||
String m_project_path;
|
String m_project_path;
|
||||||
WeakPtr<LanguageClient> m_language_client;
|
WeakPtr<LanguageClient> m_language_client;
|
||||||
|
@ -123,11 +124,14 @@ public:
|
||||||
virtual void remove_text(const String& path, size_t from_line, size_t from_column, size_t to_line, size_t to_column);
|
virtual void remove_text(const String& path, size_t from_line, size_t from_column, size_t to_line, size_t to_column);
|
||||||
virtual void request_autocomplete(const String& path, size_t cursor_line, size_t cursor_column);
|
virtual void request_autocomplete(const String& path, size_t cursor_line, size_t cursor_column);
|
||||||
virtual void set_autocomplete_mode(const String& mode);
|
virtual void set_autocomplete_mode(const String& mode);
|
||||||
|
virtual void search_declaration(const String& path, size_t line, size_t column);
|
||||||
|
|
||||||
void provide_autocomplete_suggestions(const Vector<GUI::AutocompleteProvider::Entry>&);
|
void provide_autocomplete_suggestions(const Vector<GUI::AutocompleteProvider::Entry>&);
|
||||||
|
void declaration_found(const String& file, size_t line, size_t column);
|
||||||
void on_server_crash();
|
void on_server_crash();
|
||||||
|
|
||||||
Function<void(Vector<GUI::AutocompleteProvider::Entry>)> on_autocomplete_suggestions;
|
Function<void(Vector<GUI::AutocompleteProvider::Entry>)> on_autocomplete_suggestions;
|
||||||
|
Function<void(const String&, size_t, size_t)> on_declaration_found;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WeakPtr<ServerConnection> m_server_connection;
|
WeakPtr<ServerConnection> m_server_connection;
|
||||||
|
|
|
@ -41,6 +41,13 @@ public:
|
||||||
virtual void on_edit([[maybe_unused]] const String& file) {};
|
virtual void on_edit([[maybe_unused]] const String& file) {};
|
||||||
virtual void file_opened([[maybe_unused]] const String& file) {};
|
virtual void file_opened([[maybe_unused]] const String& file) {};
|
||||||
|
|
||||||
|
struct ProjectPosition {
|
||||||
|
String file;
|
||||||
|
size_t line;
|
||||||
|
size_t column;
|
||||||
|
};
|
||||||
|
virtual Optional<ProjectPosition> find_declaration_of(const String&, const GUI::TextPosition&) { return {}; };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const FileDB& filedb() const { return m_filedb; }
|
const FileDB& filedb() const { return m_filedb; }
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ void ClientConnection::handle(const Messages::LanguageServer::SetFileContent& me
|
||||||
|
|
||||||
void ClientConnection::handle(const Messages::LanguageServer::SetAutoCompleteMode& message)
|
void ClientConnection::handle(const Messages::LanguageServer::SetAutoCompleteMode& message)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_CPP_LANGUAGE_SERVER
|
#ifdef CPP_LANGUAGE_SERVER_DEBUG
|
||||||
dbgln("SetAutoCompleteMode: {}", message.mode());
|
dbgln("SetAutoCompleteMode: {}", message.mode());
|
||||||
#endif
|
#endif
|
||||||
if (message.mode() == "Parser")
|
if (message.mode() == "Parser")
|
||||||
|
@ -137,4 +137,23 @@ void ClientConnection::handle(const Messages::LanguageServer::SetAutoCompleteMod
|
||||||
m_autocomplete_engine = make<LexerAutoComplete>(m_filedb);
|
m_autocomplete_engine = make<LexerAutoComplete>(m_filedb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientConnection::handle(const Messages::LanguageServer::FindDeclaration& message)
|
||||||
|
{
|
||||||
|
dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "FindDeclaration: {} {}:{}", message.file_name(), message.line(), message.column());
|
||||||
|
auto document = m_filedb.get(message.file_name());
|
||||||
|
if (!document) {
|
||||||
|
dbgln("file {} has not been opened", message.file_name());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI::TextPosition identifier_position = { (size_t)message.line(), (size_t)message.column() };
|
||||||
|
auto location = m_autocomplete_engine->find_declaration_of(message.file_name(), identifier_position);
|
||||||
|
if (!location.has_value()) {
|
||||||
|
dbgln("could not find declaration");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "declaration location: {} {}:{}", location.value().file, location.value().line, location.value().column);
|
||||||
|
post_message(Messages::LanguageClient::DeclarationLocation(location.value().file, location.value().line, location.value().column));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ private:
|
||||||
virtual void handle(const Messages::LanguageServer::SetFileContent&) override;
|
virtual void handle(const Messages::LanguageServer::SetFileContent&) override;
|
||||||
virtual void handle(const Messages::LanguageServer::AutoCompleteSuggestions&) override;
|
virtual void handle(const Messages::LanguageServer::AutoCompleteSuggestions&) override;
|
||||||
virtual void handle(const Messages::LanguageServer::SetAutoCompleteMode&) override;
|
virtual void handle(const Messages::LanguageServer::SetAutoCompleteMode&) override;
|
||||||
|
virtual void handle(const Messages::LanguageServer::FindDeclaration&) override;
|
||||||
|
|
||||||
FileDB m_filedb;
|
FileDB m_filedb;
|
||||||
OwnPtr<AutoCompleteEngine> m_autocomplete_engine;
|
OwnPtr<AutoCompleteEngine> m_autocomplete_engine;
|
||||||
|
|
|
@ -33,16 +33,6 @@
|
||||||
#include <LibCpp/Preprocessor.h>
|
#include <LibCpp/Preprocessor.h>
|
||||||
#include <LibRegex/Regex.h>
|
#include <LibRegex/Regex.h>
|
||||||
|
|
||||||
// #define AUTOCOMPLETE_VERBOSE
|
|
||||||
|
|
||||||
#ifdef AUTOCOMPLETE_VERBOSE
|
|
||||||
# define VERBOSE(fmt, ...) dbgln(fmt, ##__VA_ARGS__)
|
|
||||||
#else
|
|
||||||
# define VERBOSE(fmt, ...) \
|
|
||||||
do { \
|
|
||||||
} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace LanguageServers::Cpp {
|
namespace LanguageServers::Cpp {
|
||||||
|
|
||||||
ParserAutoComplete::ParserAutoComplete(const FileDB& filedb)
|
ParserAutoComplete::ParserAutoComplete(const FileDB& filedb)
|
||||||
|
@ -77,7 +67,7 @@ OwnPtr<ParserAutoComplete::DocumentData> ParserAutoComplete::create_document_dat
|
||||||
for (auto& path : document_data->preprocessor.included_paths()) {
|
for (auto& path : document_data->preprocessor.included_paths()) {
|
||||||
get_or_create_document_data(document_path_from_include_path(path));
|
get_or_create_document_data(document_path_from_include_path(path));
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_AUTOCOMPLETE
|
#ifdef CPP_LANGUAGE_SERVER_DEBUG
|
||||||
root->dump(0);
|
root->dump(0);
|
||||||
#endif
|
#endif
|
||||||
return move(document_data);
|
return move(document_data);
|
||||||
|
@ -99,12 +89,12 @@ Vector<GUI::AutocompleteProvider::Entry> ParserAutoComplete::get_suggestions(con
|
||||||
{
|
{
|
||||||
Cpp::Position position { autocomplete_position.line(), autocomplete_position.column() > 0 ? autocomplete_position.column() - 1 : 0 };
|
Cpp::Position position { autocomplete_position.line(), autocomplete_position.column() > 0 ? autocomplete_position.column() - 1 : 0 };
|
||||||
|
|
||||||
VERBOSE("ParserAutoComplete position {}:{}", position.line, position.column);
|
dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "ParserAutoComplete position {}:{}", position.line, position.column);
|
||||||
|
|
||||||
const auto& document = get_or_create_document_data(file);
|
const auto& document = get_or_create_document_data(file);
|
||||||
auto node = document.parser.node_at(position);
|
auto node = document.parser.node_at(position);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
VERBOSE("no node at position {}:{}", position.line, position.column);
|
dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "no node at position {}:{}", position.line, position.column);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,17 +120,22 @@ Vector<GUI::AutocompleteProvider::Entry> ParserAutoComplete::get_suggestions(con
|
||||||
return autocomplete_name(document, *node, partial_text.view());
|
return autocomplete_name(document, *node, partial_text.view());
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<GUI::AutocompleteProvider::Entry> ParserAutoComplete::autocomplete_name(const DocumentData& document, const ASTNode& node, const StringView& partial_text) const
|
NonnullRefPtrVector<Declaration> ParserAutoComplete::get_available_declarations(const DocumentData& document, const ASTNode& node) const
|
||||||
{
|
{
|
||||||
const Cpp::ASTNode* current = &node;
|
const Cpp::ASTNode* current = &node;
|
||||||
NonnullRefPtrVector<Cpp::Declaration> available_declarations;
|
NonnullRefPtrVector<Declaration> available_declarations;
|
||||||
while (current) {
|
while (current) {
|
||||||
available_declarations.append(current->declarations());
|
available_declarations.append(current->declarations());
|
||||||
current = current->parent();
|
current = current->parent();
|
||||||
}
|
}
|
||||||
|
|
||||||
available_declarations.append(get_declarations_in_outer_scope_including_headers(document));
|
available_declarations.append(get_declarations_in_outer_scope_including_headers(document));
|
||||||
|
return available_declarations;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<GUI::AutocompleteProvider::Entry> ParserAutoComplete::autocomplete_name(const DocumentData& document, const ASTNode& node, const StringView& partial_text) const
|
||||||
|
{
|
||||||
|
auto available_declarations = get_available_declarations(document, node);
|
||||||
Vector<StringView> available_names;
|
Vector<StringView> available_names;
|
||||||
auto add_name = [&available_names](auto& name) {
|
auto add_name = [&available_names](auto& name) {
|
||||||
if (name.is_null() || name.is_empty())
|
if (name.is_null() || name.is_empty())
|
||||||
|
@ -173,7 +168,7 @@ Vector<GUI::AutocompleteProvider::Entry> ParserAutoComplete::autocomplete_proper
|
||||||
{
|
{
|
||||||
auto type = type_of(document, *parent.m_object);
|
auto type = type_of(document, *parent.m_object);
|
||||||
if (type.is_null()) {
|
if (type.is_null()) {
|
||||||
VERBOSE("Could not infer type of object");
|
dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "Could not infer type of object");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,4 +316,47 @@ void ParserAutoComplete::file_opened([[maybe_unused]] const String& file)
|
||||||
set_document_data(file, create_document_data_for(file));
|
set_document_data(file, create_document_data_for(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<AutoCompleteEngine::ProjectPosition> ParserAutoComplete::find_declaration_of(const String& file_name, const GUI::TextPosition& identifier_position)
|
||||||
|
{
|
||||||
|
const auto& document = get_or_create_document_data(file_name);
|
||||||
|
auto node = document.parser.node_at(Cpp::Position { identifier_position.line(), identifier_position.column() });
|
||||||
|
if (!node) {
|
||||||
|
dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "no node at position {}:{}", identifier_position.line(), identifier_position.column());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto decl = find_declaration_of(document, *node);
|
||||||
|
if (!decl)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return ProjectPosition { decl->filename(), decl->start().line, decl->start().column };
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<Declaration> ParserAutoComplete::find_declaration_of(const DocumentData& document_data, const ASTNode& node) const
|
||||||
|
{
|
||||||
|
dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "find_declaration_of: {}", document_data.parser.text_of_node(node));
|
||||||
|
auto declarations = get_available_declarations(document_data, node);
|
||||||
|
for (auto& decl : declarations) {
|
||||||
|
if (node.is_identifier() && decl.is_variable_or_parameter_declaration()) {
|
||||||
|
if (((Cpp::VariableOrParameterDeclaration&)decl).m_name == static_cast<const Identifier&>(node).m_name)
|
||||||
|
return decl;
|
||||||
|
}
|
||||||
|
if (node.is_type() && decl.is_struct_or_class()) {
|
||||||
|
if (((Cpp::StructOrClassDeclaration&)decl).m_name == static_cast<const Type&>(node).m_name)
|
||||||
|
return decl;
|
||||||
|
}
|
||||||
|
if (node.is_function_call() && decl.is_function()) {
|
||||||
|
if (((Cpp::FunctionDeclaration&)decl).m_name == static_cast<const FunctionCall&>(node).m_name)
|
||||||
|
return decl;
|
||||||
|
}
|
||||||
|
if (is_property(node) && decl.is_struct_or_class()) {
|
||||||
|
for (auto& member : ((Cpp::StructOrClassDeclaration&)decl).m_members) {
|
||||||
|
ASSERT(node.is_identifier());
|
||||||
|
if (member.m_name == ((const Identifier&)node).m_name)
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,10 +47,11 @@ public:
|
||||||
virtual Vector<GUI::AutocompleteProvider::Entry> get_suggestions(const String& file, const GUI::TextPosition& autocomplete_position) override;
|
virtual Vector<GUI::AutocompleteProvider::Entry> get_suggestions(const String& file, const GUI::TextPosition& autocomplete_position) override;
|
||||||
virtual void on_edit(const String& file) override;
|
virtual void on_edit(const String& file) override;
|
||||||
virtual void file_opened([[maybe_unused]] const String& file) override;
|
virtual void file_opened([[maybe_unused]] const String& file) override;
|
||||||
|
virtual Optional<ProjectPosition> find_declaration_of(const String& file_name, const GUI::TextPosition& identifier_position) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct DocumentData {
|
struct DocumentData {
|
||||||
DocumentData(String&& text);
|
DocumentData(String&& text, const String& filename);
|
||||||
String text;
|
String text;
|
||||||
Preprocessor preprocessor;
|
Preprocessor preprocessor;
|
||||||
Parser parser;
|
Parser parser;
|
||||||
|
@ -63,6 +64,8 @@ private:
|
||||||
String type_of_variable(const Identifier&) const;
|
String type_of_variable(const Identifier&) const;
|
||||||
bool is_property(const ASTNode&) const;
|
bool is_property(const ASTNode&) const;
|
||||||
bool is_empty_property(const DocumentData&, const ASTNode&, const Position& autocomplete_position) const;
|
bool is_empty_property(const DocumentData&, const ASTNode&, const Position& autocomplete_position) const;
|
||||||
|
RefPtr<Declaration> find_declaration_of(const DocumentData&, const ASTNode&) const;
|
||||||
|
NonnullRefPtrVector<Declaration> get_available_declarations(const DocumentData&, const ASTNode&) const;
|
||||||
|
|
||||||
struct PropertyInfo {
|
struct PropertyInfo {
|
||||||
StringView name;
|
StringView name;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
endpoint LanguageClient = 8002
|
endpoint LanguageClient = 8002
|
||||||
{
|
{
|
||||||
AutoCompleteSuggestions(Vector<GUI::AutocompleteProvider::Entry> suggestions) =|
|
AutoCompleteSuggestions(Vector<GUI::AutocompleteProvider::Entry> suggestions) =|
|
||||||
|
DeclarationLocation(String file_name, i32 line, i32 column) =|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,4 +9,6 @@ endpoint LanguageServer = 8001
|
||||||
|
|
||||||
AutoCompleteSuggestions(String file_name, i32 cursor_line, i32 cursor_column) =|
|
AutoCompleteSuggestions(String file_name, i32 cursor_line, i32 cursor_column) =|
|
||||||
SetAutoCompleteMode(String mode) =|
|
SetAutoCompleteMode(String mode) =|
|
||||||
|
FindDeclaration(String file_name, i32 line, i32 column) =|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ private:
|
||||||
virtual void handle(const Messages::LanguageServer::SetFileContent&) override;
|
virtual void handle(const Messages::LanguageServer::SetFileContent&) override;
|
||||||
virtual void handle(const Messages::LanguageServer::AutoCompleteSuggestions&) override;
|
virtual void handle(const Messages::LanguageServer::AutoCompleteSuggestions&) override;
|
||||||
virtual void handle(const Messages::LanguageServer::SetAutoCompleteMode&) override { }
|
virtual void handle(const Messages::LanguageServer::SetAutoCompleteMode&) override { }
|
||||||
|
virtual void handle(const Messages::LanguageServer::FindDeclaration&) override {};
|
||||||
|
|
||||||
RefPtr<GUI::TextDocument> document_for(const String& file_name);
|
RefPtr<GUI::TextDocument> document_for(const String& file_name);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue