summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorItamar <itamar8910@gmail.com>2021-07-03 11:55:54 +0300
committerAndreas Kling <kling@serenityos.org>2021-07-04 17:50:33 +0200
commit32be65a8b4bf1a0b47551ee334a09befd0de2c96 (patch)
tree71882d3ad86415d9cd7c9ed56ce20bd9df80789a /Userland
parent232013c05bd024efc62f23cf99bafe4f0bef1333 (diff)
downloadserenity-32be65a8b4bf1a0b47551ee334a09befd0de2c96.zip
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.
Diffstat (limited to 'Userland')
-rw-r--r--Userland/DevTools/HackStudio/LanguageClient.cpp28
-rw-r--r--Userland/DevTools/HackStudio/LanguageClient.h5
-rw-r--r--Userland/DevTools/HackStudio/LanguageServers/ClientConnection.cpp25
-rw-r--r--Userland/DevTools/HackStudio/LanguageServers/ClientConnection.h1
-rw-r--r--Userland/DevTools/HackStudio/LanguageServers/CodeComprehensionEngine.h8
-rw-r--r--Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.cpp111
-rw-r--r--Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.h3
-rw-r--r--Userland/DevTools/HackStudio/LanguageServers/LanguageClient.ipc1
-rw-r--r--Userland/DevTools/HackStudio/LanguageServers/LanguageServer.ipc1
9 files changed, 181 insertions, 2 deletions
diff --git a/Userland/DevTools/HackStudio/LanguageClient.cpp b/Userland/DevTools/HackStudio/LanguageClient.cpp
index 6e47813609..745950170a 100644
--- a/Userland/DevTools/HackStudio/LanguageClient.cpp
+++ b/Userland/DevTools/HackStudio/LanguageClient.cpp
@@ -33,6 +33,17 @@ void ServerConnection::declaration_location(const GUI::AutocompleteProvider::Pro
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()
{
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 });
}
+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
{
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);
}
+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)
{
s_instance_for_language.set(language_name, move(connection_wrapper));
diff --git a/Userland/DevTools/HackStudio/LanguageClient.h b/Userland/DevTools/HackStudio/LanguageClient.h
index 004bbd2b5f..b99c16d64e 100644
--- a/Userland/DevTools/HackStudio/LanguageClient.h
+++ b/Userland/DevTools/HackStudio/LanguageClient.h
@@ -48,6 +48,7 @@ protected:
virtual void declaration_location(GUI::AutocompleteProvider::ProjectLocation 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 parameters_hint_result(Vector<String> const&, int index) override;
void set_wrapper(ServerConnectionWrapper& wrapper) { m_wrapper = &wrapper; }
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 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 get_parameters_hint(const String& path, size_t line, size_t column);
void provide_autocomplete_suggestions(const Vector<GUI::AutocompleteProvider::Entry>&) 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(const String&, size_t, size_t)> on_declaration_found;
+ Function<void(Vector<String> const&, size_t)> on_function_parameters_hint_result;
private:
ServerConnectionWrapper& m_connection_wrapper;
diff --git a/Userland/DevTools/HackStudio/LanguageServers/ClientConnection.cpp b/Userland/DevTools/HackStudio/LanguageServers/ClientConnection.cpp
index 5c4d579f70..567268a994 100644
--- a/Userland/DevTools/HackStudio/LanguageServers/ClientConnection.cpp
+++ b/Userland/DevTools/HackStudio/LanguageServers/ClientConnection.cpp
@@ -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);
+}
+
}
diff --git a/Userland/DevTools/HackStudio/LanguageServers/ClientConnection.h b/Userland/DevTools/HackStudio/LanguageServers/ClientConnection.h
index b227923dd3..1901743463 100644
--- a/Userland/DevTools/HackStudio/LanguageServers/ClientConnection.h
+++ b/Userland/DevTools/HackStudio/LanguageServers/ClientConnection.h
@@ -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;
diff --git a/Userland/DevTools/HackStudio/LanguageServers/CodeComprehensionEngine.h b/Userland/DevTools/HackStudio/LanguageServers/CodeComprehensionEngine.h
index e7a6aabaf6..700bba4603 100644
--- a/Userland/DevTools/HackStudio/LanguageServers/CodeComprehensionEngine.h
+++ b/Userland/DevTools/HackStudio/LanguageServers/CodeComprehensionEngine.h
@@ -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;
diff --git a/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.cpp b/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.cpp
index 7eec6b927a..ec29ee1ae5 100644
--- a/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.cpp
+++ b/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.cpp
@@ -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;
+}
+
}
diff --git a/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.h b/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.h
index e77e276f5d..742688219f 100644
--- a/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.h
+++ b/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.h
@@ -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 {
diff --git a/Userland/DevTools/HackStudio/LanguageServers/LanguageClient.ipc b/Userland/DevTools/HackStudio/LanguageServers/LanguageClient.ipc
index 6400dd7b29..6760c13c12 100644
--- a/Userland/DevTools/HackStudio/LanguageServers/LanguageClient.ipc
+++ b/Userland/DevTools/HackStudio/LanguageServers/LanguageClient.ipc
@@ -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) =|
}
diff --git a/Userland/DevTools/HackStudio/LanguageServers/LanguageServer.ipc b/Userland/DevTools/HackStudio/LanguageServers/LanguageServer.ipc
index 668ad0a6e9..ba6cac7196 100644
--- a/Userland/DevTools/HackStudio/LanguageServers/LanguageServer.ipc
+++ b/Userland/DevTools/HackStudio/LanguageServers/LanguageServer.ipc
@@ -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) =|
}