mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 04:47:34 +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
9 changed files with 181 additions and 2 deletions
|
@ -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 });
|
||||
}
|
||||
|
||||
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 auto_complete_suggestions(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;
|
||||
OwnPtr<CodeComprehensionEngine> m_autocomplete_engine;
|
||||
|
|
|
@ -26,7 +26,13 @@ public:
|
|||
virtual void on_edit([[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:
|
||||
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;
|
||||
if (node.is_name()) {
|
||||
// FIXME It looks like this code path is never taken
|
||||
name = reinterpret_cast<const Name*>(&node);
|
||||
} else if (node.is_identifier()) {
|
||||
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());
|
||||
if (!node.is_identifier()) {
|
||||
dbgln("node is not an identifier, can't find declaration");
|
||||
return {};
|
||||
}
|
||||
|
||||
auto target_decl = get_target_declaration(node);
|
||||
|
@ -726,4 +728,113 @@ bool CppComprehensionEngine::is_symbol_available(const Symbol& symbol, const Vec
|
|||
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 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<FunctionParamsHint> get_function_params_hint(const String&, const GUI::TextPosition&) override;
|
||||
|
||||
private:
|
||||
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_include(const DocumentData&, Token include_path_token);
|
||||
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>
|
||||
void for_each_available_symbol(const DocumentData&, Func) const;
|
||||
|
@ -171,7 +173,6 @@ void CppComprehensionEngine::for_each_included_document_recursive(const Document
|
|||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace AK {
|
||||
|
|
|
@ -4,4 +4,5 @@ endpoint LanguageClient
|
|||
declaration_location(GUI::AutocompleteProvider::ProjectLocation location) =|
|
||||
declarations_in_document(String filename, Vector<GUI::AutocompleteProvider::Declaration> declarations) =|
|
||||
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) =|
|
||||
find_declaration(GUI::AutocompleteProvider::ProjectLocation location) =|
|
||||
get_parameters_hint(GUI::AutocompleteProvider::ProjectLocation location) =|
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue