From df69fb0018ad2509d7fa2aec3b2d77ffa688e528 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Thu, 9 Apr 2026 11:36:43 -0400 Subject: [PATCH 43/48] FIXME: implement "textDocument/hover"; add delegate_textDocument_request_to_worker --- gcc/c/c-decl.cc | 65 +++++++++++++++++++++++++ gcc/lsp/frontend-interface.h | 4 +- gcc/lsp/gcc-impl/server.cc | 94 +++++++++++++----------------------- gcc/lsp/gcc-impl/server.h | 8 +-- gcc/lsp/gcc-impl/worker.cc | 89 +++++++++++++++++++++++++++++++--- gcc/lsp/gcc-impl/worker.h | 4 ++ gcc/lsp/json-rpc.cc | 21 ++++++-- gcc/lsp/lsp-spec.cc | 50 +++++++++++++++++++ gcc/lsp/lsp-spec.h | 24 +++++++++ 9 files changed, 281 insertions(+), 78 deletions(-) diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 8c7679c9895..16ad10322c9 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -14429,4 +14429,69 @@ c_lsp_frontend_impl::get_symbols () return reversed; } +// FIXME: is this the best file for this? + +bool +c_lsp_frontend_impl::get_hover_text (tree node, + lsp::spec::MarkupContent &out) +{ + gcc_assert (node); + + out.kind = "plaintext"; + // FIXME: see if markdown works + + switch (TREE_CODE (node)) + { + default: + return false; + case FUNCTION_DECL: + { + auto pp = global_dc->clone_printer (); + tree return_type = TREE_TYPE (TREE_TYPE (node)); + pp_printf (pp.get (), "function %qs\n\n", + lang_hooks.decl_printable_name (node, 1)); + pp_printf (pp.get (), "Returns: %T\n\n", return_type); + pp_printf (pp.get (), "Parameters\n"); + for (tree parm = DECL_ARGUMENTS (node); parm; parm = TREE_CHAIN (parm)) + pp_printf (pp.get (), "- %T %s\n", + TREE_TYPE (parm), + lang_hooks.decl_printable_name (parm, 0)); + pp_printf (pp.get (), "\n%T %s(", + return_type, + lang_hooks.decl_printable_name (node, 1)); + for (tree parm = DECL_ARGUMENTS (node); parm; parm = TREE_CHAIN (parm)) + { + if (parm != DECL_ARGUMENTS (node)) + pp_printf (pp.get (), ", "); + pp_printf (pp.get (), "%T %s", + TREE_TYPE (parm), + lang_hooks.decl_printable_name (parm, 0)); + } + pp_printf (pp.get (), ")\n"); + out.value = pp_formatted_text (pp.get ()); + return true; + } + case VAR_DECL: + { + auto pp = global_dc->clone_printer (); + pp_printf (pp.get (), "variable %qs\n\n", + lang_hooks.decl_printable_name (node, 1)); + pp_printf (pp.get (), "Type: %T\n\n", TREE_TYPE (node)); + out.value = pp_formatted_text (pp.get ()); + return true; + } + case PARM_DECL: + { + auto pp = global_dc->clone_printer (); + pp_printf (pp.get (), "parameter %qs of %qE\n\n", + lang_hooks.decl_printable_name (node, 1), + DECL_CONTEXT (node)); + pp_printf (pp.get (), "Type: %T\n\n", TREE_TYPE (node)); + out.value = pp_formatted_text (pp.get ()); + return true; + } + } +} + + #include "gt-c-c-decl.h" diff --git a/gcc/lsp/frontend-interface.h b/gcc/lsp/frontend-interface.h index 94b95c4167c..8292d173b40 100644 --- a/gcc/lsp/frontend-interface.h +++ b/gcc/lsp/frontend-interface.h @@ -39,7 +39,9 @@ public: virtual lsp::vector get_symbols () = 0; -private: + virtual bool + get_hover_text (tree node, + lsp::spec::MarkupContent &out) = 0; }; } // namespace lsp diff --git a/gcc/lsp/gcc-impl/server.cc b/gcc/lsp/gcc-impl/server.cc index aad79fcb747..9a19231a726 100644 --- a/gcc/lsp/gcc-impl/server.cc +++ b/gcc/lsp/gcc-impl/server.cc @@ -450,54 +450,26 @@ consume_client_request (json_rpc::request &&req) } else if (0 == strcmp (method, "textDocument/declaration")) { - auto unmarshal_result - = spec::DeclarationParams::from_json (req.get_id (), - *req.get_params ()); - if (unmarshal_result.get_error ()) - { - gcc_unreachable (); // FIXME -#if 0 - send_response_to_client - (json_rpc::response::invalid_params (req, - std::move (unmarshal_result))); -#endif - return; - } - - on_client_request_textDocument_declaration - (req.take_id (), - unmarshal_result.take_value ()); - /* We will expect a response from the worker, and forward - that to the client. */ + delegate_textDocument_request_to_worker + (std::move (req)); return; } else if (0 == strcmp (method, "textDocument/documentSymbol")) { - auto unmarshal_result - = spec::DocumentSymbolParams::from_json (req.get_id (), - *req.get_params ()); - if (unmarshal_result.get_error ()) - { - gcc_unreachable (); // FIXME -#if 0 - send_response_to_client - (json_rpc::response::invalid_params (req, - std::move (unmarshal_result))); -#endif - return; - } - - on_client_request_textDocument_documentSymbol - (req.take_id (), - unmarshal_result.take_value ()); - /* We will expect a response from the worker, and forward - that to the client. */ + delegate_textDocument_request_to_worker + (std::move (req)); return; } #if 0 else if (0 == strcmp (method, "textDocument/definition")) return dispatch_textDocument_definition (std::move (req)); #endif + else if (0 == strcmp (method, "textDocument/hover")) + { + delegate_textDocument_request_to_worker + (std::move (req)); + return; + } send_response_to_client (json_rpc::response::method_not_found (std::move (req))); @@ -586,40 +558,42 @@ on_client_request_initialize (std::unique_ptr req_id, (json_rpc::response::success (std::move (req_id), result.to_json ())); } +/* FIXME. */ + +template void lsp::gcc_impl::server::mediator:: -on_client_request_textDocument_declaration (std::unique_ptr req_id, - spec::DeclarationParams &&p) +delegate_textDocument_request_to_worker (json_rpc::request &&req) { LSP_LOG_SCOPE (get_logger ()); - auto iter = m_open_documents.find (p.textDocument.uri); - gcc_assert (iter != m_open_documents.end ()); - - auto doc_ver = iter->second->get_most_recent_version (); - gcc_assert (doc_ver); - doc_ver->get_worker_subprocess ()->send_request - (json_rpc::request ("textDocument/declaration", - p.to_json (), - std::move (req_id))); -} + auto unmarshal_result + = RequestParams::from_json (req.get_id (), + *req.get_params ()); + if (unmarshal_result.get_error ()) + { + gcc_unreachable (); // FIXME +#if 0 + send_response_to_client + (json_rpc::response::invalid_params (req, + std::move (unmarshal_result))); +#endif + return; + } -void -lsp::gcc_impl::server::mediator:: -on_client_request_textDocument_documentSymbol (std::unique_ptr req_id, - spec::DocumentSymbolParams &&p) -{ - LSP_LOG_SCOPE (get_logger ()); + auto params = unmarshal_result.take_value (); - auto iter = m_open_documents.find (p.textDocument.uri); + auto iter = m_open_documents.find (params.textDocument.uri); gcc_assert (iter != m_open_documents.end ()); auto doc_ver = iter->second->get_most_recent_version (); gcc_assert (doc_ver); doc_ver->get_worker_subprocess ()->send_request - (json_rpc::request ("textDocument/documentSymbol", - p.to_json (), - std::move (req_id))); + (json_rpc::request (req.get_method (), + params.to_json (), + req.take_id ())); + /* We will expect a response from the worker, and forward + that to the client. */ } // Handling of notifications from client diff --git a/gcc/lsp/gcc-impl/server.h b/gcc/lsp/gcc-impl/server.h index e9b4c1cea12..b15534ab345 100644 --- a/gcc/lsp/gcc-impl/server.h +++ b/gcc/lsp/gcc-impl/server.h @@ -243,13 +243,9 @@ public: on_client_request_initialize (std::unique_ptr req_id, spec::InitializeParams &&p); + template void - on_client_request_textDocument_declaration (std::unique_ptr req_id, - spec::DeclarationParams &&p); - - void - on_client_request_textDocument_documentSymbol (std::unique_ptr req_id, - spec::DocumentSymbolParams &&p); + delegate_textDocument_request_to_worker (json_rpc::request &&req); // Handling of notifications from client diff --git a/gcc/lsp/gcc-impl/worker.cc b/gcc/lsp/gcc-impl/worker.cc index 200694d00da..fdeefc83561 100644 --- a/gcc/lsp/gcc-impl/worker.cc +++ b/gcc/lsp/gcc-impl/worker.cc @@ -50,6 +50,13 @@ extern struct cpp_reader* parse_in; using ast_node = gcc::topics::ast_events::recorder::saved_node; +static lsp::spec::Range +range_from_ast_node (const ast_node &node) +{ + return lsp::spec::Range::from_location_t_pair (node.m_start_loc, + node.m_end_loc); +} + // class lsp::gcc_impl::worker::mediator : public lsp::log_user lsp::gcc_impl::worker::mediator::mediator (lsp::logger *logger) @@ -262,6 +269,27 @@ consume_json_rpc_request (json_rpc::request &&req) unmarshal_result.take_value ()); return; } + else if (0 == strcmp (method, "textDocument/hover")) + { + auto unmarshal_result + = spec::HoverParams::from_json (req.get_id (), + *req.get_params ()); + if (unmarshal_result.get_error ()) + { + gcc_unreachable (); // FIXME +#if 0 + send_response_to_client + (json_rpc::response::invalid_params (req, + std::move (unmarshal_result))); +#endif + return; + } + + on_request_textDocument_hover + (req.take_id (), + unmarshal_result.take_value ()); + return; + } #if 0 else if (0 == strcmp (method, "textDocument/definition")) return dispatch_textDocument_definition (std::move (req)); @@ -409,6 +437,58 @@ on_request_textDocument_documentSymbol (std::unique_ptr req_id, symbols.to_json ())); } +void +lsp::gcc_impl::worker::mediator:: +on_request_textDocument_hover (std::unique_ptr req_id, + spec::HoverParams &&p) +{ + auto logger = get_logger (); + LSP_LOG_SCOPE (logger); + + gcc_assert (m_frontend); + + if (auto ast_node = get_ast_node_at_position (p)) + { + // FIXME: + if (1) + { + fprintf (stderr, "\nnode at position: \n"); + visit_node (*ast_node); + } + if (ast_node->m_expr) + { + lsp::spec::Hover result; + if (m_frontend->get_hover_text (ast_node->m_expr, + result.contents)) + { + result.range.set_optional_value (range_from_ast_node (*ast_node)); + send_response_to_server + (json_rpc::response::success (std::move (req_id), + result.to_json ())); + return; + } + else + { + if (logger) + logger->log ("frontend returned false"); + } + } + else + { + if (logger) + logger->log ("ast node %s has no tree node", ast_node->m_kind); + } + } + else + { + if (logger) + logger->log ("ast node not found"); + } + send_response_to_server + (json_rpc::response::success (std::move (req_id), + json::make_null ())); +} + // Handling of notifications from server void @@ -486,9 +566,8 @@ visit_node (const ast_node &node) // FIXME: see notes for dump_ast auto_diagnostic_nesting_level sentinel; const char *kind = node.m_kind ? node.m_kind : "NULL"; - lsp::pp_element e - (lsp::spec::Range::from_location_t_pair (node.m_start_loc, - node.m_end_loc)); + auto r = range_from_ast_node (node); + lsp::pp_element e (r); if (node.m_expr) inform (node.m_start_loc, "start of %qs: %qE: %e", kind, node.m_expr, &e); else @@ -505,9 +584,7 @@ static bool node_contains_p (const ast_node &node, const lsp::spec::TextDocumentPositionParams &p) { - lsp::spec::Range range_of_node - = lsp::spec::Range::from_location_t_pair (node.m_start_loc, - node.m_end_loc); + lsp::spec::Range range_of_node = range_from_ast_node (node); return range_of_node.contains_p (p.position); } diff --git a/gcc/lsp/gcc-impl/worker.h b/gcc/lsp/gcc-impl/worker.h index 1109efb98a1..ce3f693dfd8 100644 --- a/gcc/lsp/gcc-impl/worker.h +++ b/gcc/lsp/gcc-impl/worker.h @@ -113,6 +113,10 @@ public: on_request_textDocument_documentSymbol (std::unique_ptr req_id, spec::DocumentSymbolParams &&p); + void + on_request_textDocument_hover (std::unique_ptr req_id, + spec::HoverParams &&p); + // Handling of notifications from server void diff --git a/gcc/lsp/json-rpc.cc b/gcc/lsp/json-rpc.cc index 7765054576d..9492fa58330 100644 --- a/gcc/lsp/json-rpc.cc +++ b/gcc/lsp/json-rpc.cc @@ -257,13 +257,24 @@ consume_json_message (json::value &&jv_msg) { if (req_id.get ()) { - /* "id" without "method": this is a JSON-RPC response. */ + /* "id" without "method": this is a JSON-RPC response or error. */ if (logger) logger->log ("JSON-RPC response(FIXME)"); - auto result = msg_obj.maybe_take ("result"); - auto resp = json_rpc::response::success (std::move (req_id), - std::move (result)); - consume_json_rpc_response (std::move (resp)); + if (auto result = msg_obj.maybe_take ("result")) + { + auto resp = json_rpc::response::success (std::move (req_id), + std::move (result)); + consume_json_rpc_response (std::move (resp)); + } + else if (auto error = msg_obj.maybe_take ("error")) + { + gcc_unreachable (); // FIXME +#if 0 + auto resp = json_rpc::error (std::move (req_id), + std::move (error)); + consume_json_rpc_response (std::move (resp)); +#endif + } } else { diff --git a/gcc/lsp/lsp-spec.cc b/gcc/lsp/lsp-spec.cc index 876e5769057..3cd630b99c7 100644 --- a/gcc/lsp/lsp-spec.cc +++ b/gcc/lsp/lsp-spec.cc @@ -1012,6 +1012,56 @@ BEGIN_DEFINE_TO_JSON(DocumentSymbol) } END_DEFINE_TO_JSON +// struct MarkupContent + +BEGIN_DEFINE_FROM_JSON(MarkupContent) +{ + UNMARSHAL_REQUIRED_PROPERTY (string, kind); + UNMARSHAL_REQUIRED_PROPERTY (string, value); +} +END_DEFINE_FROM_JSON + +BEGIN_DEFINE_TO_JSON(MarkupContent) +{ + MARSHAL_REQUIRED_PROPERTY (string, kind); + MARSHAL_REQUIRED_PROPERTY (string, value); +} +END_DEFINE_TO_JSON + +// struct HoverParams : public TextDocumentPositionParams + +BEGIN_DEFINE_FROM_JSON(HoverParams) +{ + // FIXME: how to share impl with base class? + UNMARSHAL_REQUIRED_PROPERTY (TextDocumentIdentifier, textDocument); + UNMARSHAL_REQUIRED_PROPERTY (Position, position); +} +END_DEFINE_FROM_JSON + +BEGIN_DEFINE_TO_JSON(HoverParams) +{ + // FIXME: how to share impl with base class? + MARSHAL_REQUIRED_PROPERTY (TextDocumentIdentifier, textDocument); + MARSHAL_REQUIRED_PROPERTY (Position, position); +} +END_DEFINE_TO_JSON + +// struct Hover + +BEGIN_DEFINE_FROM_JSON(Hover) +{ + UNMARSHAL_REQUIRED_PROPERTY (MarkupContent, contents); + UNMARSHAL_OPTIONAL_PROPERTY (Range, range); +} +END_DEFINE_FROM_JSON + +BEGIN_DEFINE_TO_JSON(Hover) +{ + MARSHAL_REQUIRED_PROPERTY (MarkupContent, contents); + MARSHAL_OPTIONAL_PROPERTY (Range, range); +} +END_DEFINE_TO_JSON + #if CHECKING_P namespace selftest { diff --git a/gcc/lsp/lsp-spec.h b/gcc/lsp/lsp-spec.h index 078a5391c9d..70dc217c998 100644 --- a/gcc/lsp/lsp-spec.h +++ b/gcc/lsp/lsp-spec.h @@ -924,6 +924,30 @@ struct DocumentSymbol optional> children; }; +struct MarkupContent +{ + DECLARE_JSON_METHODS(MarkupContent) + + // FIXME: + // MarkupKind kind; + string kind; + + string value; +}; + +struct HoverParams : public TextDocumentPositionParams +{ + DECLARE_JSON_METHODS(HoverParams) +}; + +struct Hover +{ + DECLARE_JSON_METHODS(Hover) + + MarkupContent contents; + optional range; +}; + } // namespace lsp::spec /* A pp_element subclass for printing lsp::spec types, by printing -- 2.49.0