mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-05-30 23:12:56 +00:00
CppLanguageServer: Add "get_parameters_hint" capability
Given a call site, the C++ language server can now return the declared parameters of the called function, as well as the index of the parameter that the cursor is currently at.
This commit is contained in:
parent
232013c05b
commit
32be65a8b4
Notes:
sideshowbarker
2024-07-18 10:30:05 +09:00
Author: https://github.com/itamar8910
Commit: 32be65a8b4
Pull-request: https://github.com/SerenityOS/serenity/pull/8403
Reviewed-by: https://github.com/awesomekling
9 changed files with 181 additions and 2 deletions
|
@ -33,6 +33,17 @@ void ServerConnection::declaration_location(const GUI::AutocompleteProvider::Pro
|
||||||
m_current_language_client->declaration_found(location.file, location.line, location.column);
|
m_current_language_client->declaration_found(location.file, location.line, location.column);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServerConnection::parameters_hint_result(Vector<String> const& params, int argument_index)
|
||||||
|
{
|
||||||
|
if (!m_current_language_client) {
|
||||||
|
dbgln("Language Server connection has no attached language client");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VERIFY(argument_index >= 0);
|
||||||
|
m_current_language_client->parameters_hint_result(params, static_cast<size_t>(argument_index));
|
||||||
|
}
|
||||||
|
|
||||||
void ServerConnection::die()
|
void ServerConnection::die()
|
||||||
{
|
{
|
||||||
VERIFY(m_wrapper);
|
VERIFY(m_wrapper);
|
||||||
|
@ -112,6 +123,14 @@ void LanguageClient::search_declaration(const String& path, size_t line, size_t
|
||||||
m_connection_wrapper.connection()->async_find_declaration(GUI::AutocompleteProvider::ProjectLocation { path, line, column });
|
m_connection_wrapper.connection()->async_find_declaration(GUI::AutocompleteProvider::ProjectLocation { path, line, column });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LanguageClient::get_parameters_hint(const String& path, size_t line, size_t column)
|
||||||
|
{
|
||||||
|
if (!m_connection_wrapper.connection())
|
||||||
|
return;
|
||||||
|
set_active_client();
|
||||||
|
m_connection_wrapper.connection()->async_get_parameters_hint(GUI::AutocompleteProvider::ProjectLocation { path, line, column });
|
||||||
|
}
|
||||||
|
|
||||||
void LanguageClient::declaration_found(const String& file, size_t line, size_t column) const
|
void LanguageClient::declaration_found(const String& file, size_t line, size_t column) const
|
||||||
{
|
{
|
||||||
if (!on_declaration_found) {
|
if (!on_declaration_found) {
|
||||||
|
@ -121,6 +140,15 @@ void LanguageClient::declaration_found(const String& file, size_t line, size_t c
|
||||||
on_declaration_found(file, line, column);
|
on_declaration_found(file, line, column);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LanguageClient::parameters_hint_result(Vector<String> const& params, size_t argument_index) const
|
||||||
|
{
|
||||||
|
if (!on_function_parameters_hint_result) {
|
||||||
|
dbgln("on_function_parameters_hint_result callback is not set");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
on_function_parameters_hint_result(params, argument_index);
|
||||||
|
}
|
||||||
|
|
||||||
void ServerConnectionInstances::set_instance_for_language(const String& language_name, NonnullOwnPtr<ServerConnectionWrapper>&& connection_wrapper)
|
void ServerConnectionInstances::set_instance_for_language(const String& language_name, NonnullOwnPtr<ServerConnectionWrapper>&& connection_wrapper)
|
||||||
{
|
{
|
||||||
s_instance_for_language.set(language_name, move(connection_wrapper));
|
s_instance_for_language.set(language_name, move(connection_wrapper));
|
||||||
|
|
|
@ -48,6 +48,7 @@ protected:
|
||||||
virtual void declaration_location(GUI::AutocompleteProvider::ProjectLocation const&) override;
|
virtual void declaration_location(GUI::AutocompleteProvider::ProjectLocation const&) override;
|
||||||
virtual void declarations_in_document(String const&, Vector<GUI::AutocompleteProvider::Declaration> const&) override;
|
virtual void declarations_in_document(String const&, Vector<GUI::AutocompleteProvider::Declaration> const&) override;
|
||||||
virtual void todo_entries_in_document(String const&, Vector<Cpp::Parser::TodoEntry> const&) override;
|
virtual void todo_entries_in_document(String const&, Vector<Cpp::Parser::TodoEntry> const&) override;
|
||||||
|
virtual void parameters_hint_result(Vector<String> const&, int index) override;
|
||||||
void set_wrapper(ServerConnectionWrapper& wrapper) { m_wrapper = &wrapper; }
|
void set_wrapper(ServerConnectionWrapper& wrapper) { m_wrapper = &wrapper; }
|
||||||
|
|
||||||
String m_project_path;
|
String m_project_path;
|
||||||
|
@ -129,12 +130,16 @@ 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 search_declaration(const String& path, size_t line, size_t column);
|
virtual void search_declaration(const String& path, size_t line, size_t column);
|
||||||
|
virtual void get_parameters_hint(const String& path, size_t line, size_t column);
|
||||||
|
|
||||||
void provide_autocomplete_suggestions(const Vector<GUI::AutocompleteProvider::Entry>&) const;
|
void provide_autocomplete_suggestions(const Vector<GUI::AutocompleteProvider::Entry>&) const;
|
||||||
void declaration_found(const String& file, size_t line, size_t column) const;
|
void declaration_found(const String& file, size_t line, size_t column) const;
|
||||||
|
void parameters_hint_result(Vector<String> const& params, size_t argument_index) const;
|
||||||
|
|
||||||
|
// Callbacks that get called when the result of a language server query is ready
|
||||||
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;
|
Function<void(const String&, size_t, size_t)> on_declaration_found;
|
||||||
|
Function<void(Vector<String> const&, size_t)> on_function_parameters_hint_result;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ServerConnectionWrapper& m_connection_wrapper;
|
ServerConnectionWrapper& m_connection_wrapper;
|
||||||
|
|
|
@ -118,4 +118,29 @@ void ClientConnection::find_declaration(GUI::AutocompleteProvider::ProjectLocati
|
||||||
async_declaration_location(GUI::AutocompleteProvider::ProjectLocation { decl_location.value().file, decl_location.value().line, decl_location.value().column });
|
async_declaration_location(GUI::AutocompleteProvider::ProjectLocation { decl_location.value().file, decl_location.value().line, decl_location.value().column });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientConnection::get_parameters_hint(GUI::AutocompleteProvider::ProjectLocation const& location)
|
||||||
|
{
|
||||||
|
dbgln_if(LANGUAGE_SERVER_DEBUG, "GetFunctionParams: {} {}:{}", location.file, location.line, location.column);
|
||||||
|
auto document = m_filedb.get(location.file);
|
||||||
|
if (!document) {
|
||||||
|
dbgln("file {} has not been opened", location.file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI::TextPosition identifier_position = { (size_t)location.line, (size_t)location.column };
|
||||||
|
auto params = m_autocomplete_engine->get_function_params_hint(location.file, identifier_position);
|
||||||
|
if (!params.has_value()) {
|
||||||
|
dbgln("could not get parameters hint");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbgln_if(LANGUAGE_SERVER_DEBUG, "parameters hint:");
|
||||||
|
for (auto& param : params->params) {
|
||||||
|
dbgln_if(LANGUAGE_SERVER_DEBUG, "{}", param);
|
||||||
|
}
|
||||||
|
dbgln_if(LANGUAGE_SERVER_DEBUG, "Parameter index: {}", params->current_index);
|
||||||
|
|
||||||
|
async_parameters_hint_result(params->params, params->current_index);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ protected:
|
||||||
virtual void set_file_content(String const&, String const&) override;
|
virtual void set_file_content(String const&, String const&) override;
|
||||||
virtual void auto_complete_suggestions(GUI::AutocompleteProvider::ProjectLocation const&) override;
|
virtual void auto_complete_suggestions(GUI::AutocompleteProvider::ProjectLocation const&) override;
|
||||||
virtual void find_declaration(GUI::AutocompleteProvider::ProjectLocation const&) override;
|
virtual void find_declaration(GUI::AutocompleteProvider::ProjectLocation const&) override;
|
||||||
|
virtual void get_parameters_hint(GUI::AutocompleteProvider::ProjectLocation const&) override;
|
||||||
|
|
||||||
FileDB m_filedb;
|
FileDB m_filedb;
|
||||||
OwnPtr<CodeComprehensionEngine> m_autocomplete_engine;
|
OwnPtr<CodeComprehensionEngine> m_autocomplete_engine;
|
||||||
|
|
|
@ -26,7 +26,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) {};
|
||||||
|
|
||||||
virtual Optional<GUI::AutocompleteProvider::ProjectLocation> find_declaration_of(const String&, const GUI::TextPosition&) { return {}; };
|
virtual Optional<GUI::AutocompleteProvider::ProjectLocation> find_declaration_of(const String&, const GUI::TextPosition&) { return {}; }
|
||||||
|
|
||||||
|
struct FunctionParamsHint {
|
||||||
|
Vector<String> params;
|
||||||
|
size_t current_index { 0 };
|
||||||
|
};
|
||||||
|
virtual Optional<FunctionParamsHint> get_function_params_hint(const String&, const GUI::TextPosition&) { return {}; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Function<void(const String&, Vector<GUI::AutocompleteProvider::Declaration>&&)> set_declarations_of_document_callback;
|
Function<void(const String&, Vector<GUI::AutocompleteProvider::Declaration>&&)> set_declarations_of_document_callback;
|
||||||
|
|
|
@ -177,6 +177,7 @@ Vector<StringView> CppComprehensionEngine::scope_of_reference_to_symbol(const AS
|
||||||
{
|
{
|
||||||
const Name* name = nullptr;
|
const Name* name = nullptr;
|
||||||
if (node.is_name()) {
|
if (node.is_name()) {
|
||||||
|
// FIXME It looks like this code path is never taken
|
||||||
name = reinterpret_cast<const Name*>(&node);
|
name = reinterpret_cast<const Name*>(&node);
|
||||||
} else if (node.is_identifier()) {
|
} else if (node.is_identifier()) {
|
||||||
auto* parent = node.parent();
|
auto* parent = node.parent();
|
||||||
|
@ -454,6 +455,7 @@ RefPtr<Declaration> CppComprehensionEngine::find_declaration_of(const DocumentDa
|
||||||
dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "find_declaration_of: {} ({})", document_data.parser().text_of_node(node), node.class_name());
|
dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "find_declaration_of: {} ({})", document_data.parser().text_of_node(node), node.class_name());
|
||||||
if (!node.is_identifier()) {
|
if (!node.is_identifier()) {
|
||||||
dbgln("node is not an identifier, can't find declaration");
|
dbgln("node is not an identifier, can't find declaration");
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto target_decl = get_target_declaration(node);
|
auto target_decl = get_target_declaration(node);
|
||||||
|
@ -726,4 +728,113 @@ bool CppComprehensionEngine::is_symbol_available(const Symbol& symbol, const Vec
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<CodeComprehensionEngine::FunctionParamsHint> CppComprehensionEngine::get_function_params_hint(const String& filename, const GUI::TextPosition& identifier_position)
|
||||||
|
{
|
||||||
|
const auto* document_ptr = get_or_create_document_data(filename);
|
||||||
|
if (!document_ptr)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const auto& document = *document_ptr;
|
||||||
|
Cpp::Position cpp_position { identifier_position.line(), identifier_position.column() };
|
||||||
|
auto node = document.parser().node_at(cpp_position);
|
||||||
|
if (!node) {
|
||||||
|
dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "no node at position {}:{}", identifier_position.line(), identifier_position.column());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "node type: {}", node->class_name());
|
||||||
|
|
||||||
|
FunctionCall* call_node { nullptr };
|
||||||
|
|
||||||
|
if (node->is_function_call()) {
|
||||||
|
call_node = ((FunctionCall*)node.ptr());
|
||||||
|
|
||||||
|
auto token = document.parser().token_at(cpp_position);
|
||||||
|
|
||||||
|
// If we're in a function call with 0 arguments
|
||||||
|
if (token.has_value() && (token->type() == Token::Type::LeftParen || token->type() == Token::Type::RightParen)) {
|
||||||
|
return get_function_params_hint(document, *call_node, call_node->m_arguments.is_empty() ? 0 : call_node->m_arguments.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk upwards in the AST to find a FunctionCall node
|
||||||
|
while (!call_node && node) {
|
||||||
|
auto parent_is_call = node->parent() && node->parent()->is_function_call();
|
||||||
|
if (parent_is_call) {
|
||||||
|
call_node = (FunctionCall*)node->parent();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
node = node->parent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!call_node) {
|
||||||
|
dbgln("did not find function call");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<size_t> invoked_arg_index;
|
||||||
|
for (size_t arg_index = 0; arg_index < call_node->m_arguments.size(); ++arg_index) {
|
||||||
|
if (&call_node->m_arguments[arg_index] == node.ptr()) {
|
||||||
|
invoked_arg_index = arg_index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!invoked_arg_index.has_value()) {
|
||||||
|
dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "could not find argument index, defaulting to the last argument");
|
||||||
|
invoked_arg_index = call_node->m_arguments.is_empty() ? 0 : call_node->m_arguments.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "arg index: {}", invoked_arg_index.value());
|
||||||
|
return get_function_params_hint(document, *call_node, invoked_arg_index.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<CppComprehensionEngine::FunctionParamsHint> CppComprehensionEngine::get_function_params_hint(
|
||||||
|
DocumentData const& document,
|
||||||
|
FunctionCall& call_node,
|
||||||
|
size_t argument_index)
|
||||||
|
{
|
||||||
|
Identifier* callee = nullptr;
|
||||||
|
if (call_node.m_callee->is_identifier()) {
|
||||||
|
callee = (Identifier*)call_node.m_callee.ptr();
|
||||||
|
} else if (call_node.m_callee->is_name()) {
|
||||||
|
callee = ((Name&)*call_node.m_callee).m_name.ptr();
|
||||||
|
} else if (call_node.m_callee->is_member_expression()) {
|
||||||
|
auto& member_exp = ((MemberExpression&)*call_node.m_callee);
|
||||||
|
if (member_exp.m_property->is_identifier()) {
|
||||||
|
callee = (Identifier*)member_exp.m_property.ptr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!callee) {
|
||||||
|
dbgln("unexpected node type for function call: {}", call_node.m_callee->class_name());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
VERIFY(callee);
|
||||||
|
|
||||||
|
auto decl = find_declaration_of(document, *callee);
|
||||||
|
if (!decl) {
|
||||||
|
dbgln("func decl not found");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (!decl->is_function()) {
|
||||||
|
dbgln("declaration is not a function");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& func_decl = (FunctionDeclaration&)*decl;
|
||||||
|
auto document_of_declaration = get_document_data(func_decl.filename());
|
||||||
|
|
||||||
|
FunctionParamsHint hint {};
|
||||||
|
hint.current_index = argument_index;
|
||||||
|
for (auto& arg : func_decl.m_parameters) {
|
||||||
|
Vector<StringView> tokens_text;
|
||||||
|
for (auto token : document_of_declaration->parser().tokens_in_range(arg.start(), arg.end())) {
|
||||||
|
tokens_text.append(token.text());
|
||||||
|
}
|
||||||
|
hint.params.append(String::join(" ", tokens_text));
|
||||||
|
}
|
||||||
|
|
||||||
|
return hint;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ public:
|
||||||
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<GUI::AutocompleteProvider::ProjectLocation> find_declaration_of(const String& filename, const GUI::TextPosition& identifier_position) override;
|
virtual Optional<GUI::AutocompleteProvider::ProjectLocation> find_declaration_of(const String& filename, const GUI::TextPosition& identifier_position) override;
|
||||||
|
virtual Optional<FunctionParamsHint> get_function_params_hint(const String&, const GUI::TextPosition&) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct SymbolName {
|
struct SymbolName {
|
||||||
|
@ -130,6 +131,7 @@ private:
|
||||||
Optional<Vector<GUI::AutocompleteProvider::Entry>> try_autocomplete_name(const DocumentData&, const ASTNode&, Optional<Token> containing_token) const;
|
Optional<Vector<GUI::AutocompleteProvider::Entry>> try_autocomplete_name(const DocumentData&, const ASTNode&, Optional<Token> containing_token) const;
|
||||||
Optional<Vector<GUI::AutocompleteProvider::Entry>> try_autocomplete_include(const DocumentData&, Token include_path_token);
|
Optional<Vector<GUI::AutocompleteProvider::Entry>> try_autocomplete_include(const DocumentData&, Token include_path_token);
|
||||||
static bool is_symbol_available(const Symbol&, const Vector<StringView>& current_scope, const Vector<StringView>& reference_scope);
|
static bool is_symbol_available(const Symbol&, const Vector<StringView>& current_scope, const Vector<StringView>& reference_scope);
|
||||||
|
Optional<FunctionParamsHint> get_function_params_hint(DocumentData const&, FunctionCall&, size_t argument_index);
|
||||||
|
|
||||||
template<typename Func>
|
template<typename Func>
|
||||||
void for_each_available_symbol(const DocumentData&, Func) const;
|
void for_each_available_symbol(const DocumentData&, Func) const;
|
||||||
|
@ -171,7 +173,6 @@ void CppComprehensionEngine::for_each_included_document_recursive(const Document
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace AK {
|
namespace AK {
|
||||||
|
|
|
@ -4,4 +4,5 @@ endpoint LanguageClient
|
||||||
declaration_location(GUI::AutocompleteProvider::ProjectLocation location) =|
|
declaration_location(GUI::AutocompleteProvider::ProjectLocation location) =|
|
||||||
declarations_in_document(String filename, Vector<GUI::AutocompleteProvider::Declaration> declarations) =|
|
declarations_in_document(String filename, Vector<GUI::AutocompleteProvider::Declaration> declarations) =|
|
||||||
todo_entries_in_document(String filename, Vector<Cpp::Parser::TodoEntry> todo_entries) =|
|
todo_entries_in_document(String filename, Vector<Cpp::Parser::TodoEntry> todo_entries) =|
|
||||||
|
parameters_hint_result(Vector<String> params, int current_index) =|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,4 +9,5 @@ endpoint LanguageServer
|
||||||
|
|
||||||
auto_complete_suggestions(GUI::AutocompleteProvider::ProjectLocation location) =|
|
auto_complete_suggestions(GUI::AutocompleteProvider::ProjectLocation location) =|
|
||||||
find_declaration(GUI::AutocompleteProvider::ProjectLocation location) =|
|
find_declaration(GUI::AutocompleteProvider::ProjectLocation location) =|
|
||||||
|
get_parameters_hint(GUI::AutocompleteProvider::ProjectLocation location) =|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue