From b45cbd3bdecd95c3f2165438f9ef508b8ed3d3b8 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Fri, 10 Apr 2026 15:49:47 -0400 Subject: [PATCH 45/48] FIXME: WIP on "textDocument/codeAction" --- gcc/diagnostics/changes.cc | 59 +++++ gcc/diagnostics/changes.h | 5 + gcc/diagnostics/sarif-sink.cc | 55 +---- gcc/lsp/gcc-impl/server.cc | 67 +++++- gcc/lsp/gcc-impl/server.h | 4 + gcc/lsp/gcc-impl/worker-diagnostics.cc | 85 ++++++- gcc/lsp/gcc-impl/worker.cc | 4 +- gcc/lsp/lsp-spec.cc | 294 +++++++++++++++++++++---- gcc/lsp/lsp-spec.h | 158 +++++++++++-- 9 files changed, 606 insertions(+), 125 deletions(-) diff --git a/gcc/diagnostics/changes.cc b/gcc/diagnostics/changes.cc index a416e4b4c59..3badfb45bba 100644 --- a/gcc/diagnostics/changes.cc +++ b/gcc/diagnostics/changes.cc @@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see #include "pretty-print.h" #include "diagnostics/color.h" #include "diagnostics/file-cache.h" +#include "intl.h" #include "selftest.h" namespace diagnostics { @@ -899,6 +900,64 @@ changed_line::ensure_terminated () m_content[m_len] = '\0'; } +bool +try_to_print_fixit_hint_description (pretty_printer *pp, + const fixit_hint &hint, + file_cache &fc) +{ + if (hint.insertion_p ()) + { + pp_printf (pp, G_("Insert %qs"), hint.get_string ()); + return true; + } + else + { + /* Try to get prior content. */ + expanded_location start = expand_location (hint.get_start_loc ()); + expanded_location next_loc = expand_location (hint.get_next_loc ()); + if (start.file != next_loc.file) + return false; + if (start.line != next_loc.line) + return false; + if (start.column == 0) + return false; + if (next_loc.column == 0) + return false; + + const int start_offset = start.column - 1; + const int next_offset = next_loc.column - 1; + if (next_offset <= start_offset) + return false; + + size_t victim_len = next_offset - start_offset; + + char_span existing_line = fc.get_source_line (start.file, start.line); + if (!existing_line) + return false; + + label_text existing_text + = label_text::take (existing_line.subspan (start_offset, + victim_len).xstrdup ()); + + if (hint.deletion_p ()) + { + // Removal + pp_printf (pp, G_("Delete %qs"), + existing_text.get ()); + return true; + } + else + { + // Replacement + gcc_assert (hint.replacement_p ()); + pp_printf (pp, G_("Replace %qs with %qs"), + existing_text.get (), + hint.get_string ()); + return true; + } + } +} + #if CHECKING_P /* Selftests of code-editing. */ diff --git a/gcc/diagnostics/changes.h b/gcc/diagnostics/changes.h index 9d5a1460275..50b5f40f665 100644 --- a/gcc/diagnostics/changes.h +++ b/gcc/diagnostics/changes.h @@ -72,6 +72,11 @@ class change_set typed_splay_tree m_files; }; +extern bool +try_to_print_fixit_hint_description (pretty_printer *pp, + const fixit_hint &hint, + file_cache &fc); + } // namespace diagnostics::changes } // namespace diagnostics diff --git a/gcc/diagnostics/sarif-sink.cc b/gcc/diagnostics/sarif-sink.cc index f601dbe7c5f..60fd31f4851 100644 --- a/gcc/diagnostics/sarif-sink.cc +++ b/gcc/diagnostics/sarif-sink.cc @@ -50,6 +50,7 @@ struct sockaddr_un { #include "diagnostics/buffering.h" #include "diagnostics/dumping.h" #include "diagnostics/logging.h" +#include "diagnostics/changes.h" #include "json.h" #include "cpplib.h" #include "diagnostics/logical-locations.h" @@ -70,7 +71,6 @@ struct sockaddr_un { #include "demangle.h" #include "backtrace.h" #include "xml.h" -#include "intl.h" namespace diagnostics { @@ -3810,55 +3810,12 @@ sarif_builder:: make_message_describing_fix_it_hint (const fixit_hint &hint) const { pretty_printer pp; - if (hint.insertion_p ()) - pp_printf (&pp, G_("Insert %qs"), hint.get_string ()); + if (changes::try_to_print_fixit_hint_description + (&pp, hint, + get_context ().get_file_cache ())) + return make_message_object (pp_formatted_text (&pp)); else - { - /* Try to get prior content. */ - expanded_location start = expand_location (hint.get_start_loc ()); - expanded_location next_loc = expand_location (hint.get_next_loc ()); - if (start.file != next_loc.file) - return nullptr; - if (start.line != next_loc.line) - return nullptr; - if (start.column == 0) - return nullptr; - if (next_loc.column == 0) - return nullptr; - - const int start_offset = start.column - 1; - const int next_offset = next_loc.column - 1; - if (next_offset <= start_offset) - return nullptr; - - size_t victim_len = next_offset - start_offset; - - char_span existing_line = get_context () - .get_file_cache () - .get_source_line (start.file, start.line); - if (!existing_line) - return nullptr; - - label_text existing_text - = label_text::take (existing_line.subspan (start_offset, - victim_len).xstrdup ()); - - if (hint.deletion_p ()) - { - // Removal - pp_printf (&pp, G_("Delete %qs"), - existing_text.get ()); - } - else - { - // Replacement - gcc_assert (hint.replacement_p ()); - pp_printf (&pp, G_("Replace %qs with %qs"), - existing_text.get (), - hint.get_string ()); - } - } - return make_message_object (pp_formatted_text (&pp)); + return nullptr; } /* Make a "fix" object (SARIF v2.1.0 section 3.55) for RICHLOC. */ diff --git a/gcc/lsp/gcc-impl/server.cc b/gcc/lsp/gcc-impl/server.cc index 9d70d04f5ca..b1b32ca51f6 100644 --- a/gcc/lsp/gcc-impl/server.cc +++ b/gcc/lsp/gcc-impl/server.cc @@ -448,6 +448,26 @@ consume_client_request (json_rpc::request &&req) gcc_unreachable (); // return dispatch_call_shutdown (std::move (req)); } + else if (0 == strcmp (method, "textDocument/codeAction")) + { + auto unmarshal_result + = spec::CodeActionParams::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_codeAction (req.take_id (), + unmarshal_result.take_value ()); + return; + } else if (0 == strcmp (method, "textDocument/declaration")) { delegate_textDocument_request_to_worker @@ -564,6 +584,33 @@ on_client_request_initialize (std::unique_ptr req_id, (json_rpc::response::success (std::move (req_id), result.to_json ())); } +void +lsp::gcc_impl::server::mediator:: +on_client_request_textDocument_codeAction (std::unique_ptr req_id, + spec::CodeActionParams &&p) +{ + LSP_LOG_SCOPE (get_logger ()); + + vector result; + + // assume we stashed the fix-it information in the "data" for each diagnostic + for (auto diag : p.context.diagnostics) + if (auto v = diag.data.get_optional_value ()) + { + lsp::unmarshaller u (req_id.get ()); + auto action_res = lsp::spec::CodeAction::from_json (u, *v->m_val); + if (action_res.get_error ()) + { + // FIXME + gcc_unreachable (); // FIXME + } + result.push_back (action_res.take_value ()); + } + + send_response_to_client + (json_rpc::response::success (std::move (req_id), result.to_json ())); +} + /* FIXME. */ template @@ -696,29 +743,27 @@ lsp::gcc_impl::server::mediator::get_ServerCapabilities () (lsp::spec::CompletionOptions ()); // FIXME #endif -#if 1 result.hoverProvider.set_optional_value - (lsp::spec::HoverOptions ()); // FIXME -#endif + (lsp::spec::HoverOptions ()); #if 1 result.signatureHelpProvider.set_optional_value (lsp::spec::SignatureHelpOptions ()); // FIXME #endif -#if 1 result.declarationProvider.set_optional_value - (lsp::spec::boolean (true)); // FIXME -#endif + (lsp::spec::boolean (true)); -#if 1 result.documentHighlightProvider.set_optional_value - (lsp::spec::boolean (true)); // FIXME -#endif + (lsp::spec::boolean (true)); -#if 1 result.documentSymbolProvider.set_optional_value - (lsp::spec::boolean (true)); // FIXME + (lsp::spec::boolean (true)); + +#if 1 + // FIXME: + result.codeActionProvider.set_optional_value + (lsp::spec::boolean (true)); #endif #if 0 diff --git a/gcc/lsp/gcc-impl/server.h b/gcc/lsp/gcc-impl/server.h index b15534ab345..ffe91369919 100644 --- a/gcc/lsp/gcc-impl/server.h +++ b/gcc/lsp/gcc-impl/server.h @@ -243,6 +243,10 @@ public: on_client_request_initialize (std::unique_ptr req_id, spec::InitializeParams &&p); + void + on_client_request_textDocument_codeAction (std::unique_ptr req_id, + spec::CodeActionParams &&p); + template void delegate_textDocument_request_to_worker (json_rpc::request &&req); diff --git a/gcc/lsp/gcc-impl/worker-diagnostics.cc b/gcc/lsp/gcc-impl/worker-diagnostics.cc index d6a0b327332..32ae9f0e698 100644 --- a/gcc/lsp/gcc-impl/worker-diagnostics.cc +++ b/gcc/lsp/gcc-impl/worker-diagnostics.cc @@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic.h" #include "diagnostics/sink.h" #include "diagnostics/dumping.h" +#include "diagnostics/changes.h" #include "uris.h" using top_level_diagnostic @@ -298,12 +299,12 @@ get_severity_for_kind (diagnostics::kind kind) { case diagnostics::kind::debug: case diagnostics::kind::note: - result.m_val = lsp::spec::DiagnosticSeverity::Information; + result.m_val = lsp::spec::DiagnosticSeverity::values::Information; break; case diagnostics::kind::anachronism: case diagnostics::kind::warning: - result.m_val = lsp::spec::DiagnosticSeverity::Warning; + result.m_val = lsp::spec::DiagnosticSeverity::values::Warning; break; case diagnostics::kind::error: @@ -311,7 +312,7 @@ get_severity_for_kind (diagnostics::kind kind) case diagnostics::kind::ice: case diagnostics::kind::ice_nobt: case diagnostics::kind::fatal: - result.m_val = lsp::spec::DiagnosticSeverity::Error; + result.m_val = lsp::spec::DiagnosticSeverity::values::Error; break; default: @@ -360,6 +361,75 @@ get_relatedInformation_from_rich_location (const rich_location &rich_loc) return result; } +static lsp::spec::TextEdit +get_TextEdit_from_fixit_hint (const fixit_hint &hint) +{ + lsp::spec::TextEdit result; + /* Both fixit_hint and Range are half-open. */ + result.range.start + = lsp::spec::Position::from_location_t (hint.get_start_loc ()); + result.range.end + = lsp::spec::Position::from_location_t (hint.get_next_loc ()); + result.newText = std::string (hint.get_string (), hint.get_length ()); + return result; +} + +static lsp::spec::WorkspaceEdit +get_WorkspaceEdit_from_from_rich_location (const rich_location &rich_loc) +{ + lsp::spec::map_DocumentUri_to_vector_TextEdit changes; + for (int i = 0; i < rich_loc.get_num_fixit_hints (); ++i) + { + const fixit_hint *hint = rich_loc.get_fixit_hint (i); + + auto text_edit = get_TextEdit_from_fixit_hint (*hint); + + uris::file_uri file_uri (LOCATION_FILE (hint->get_start_loc ())); + lsp::spec::DocumentUri uri (file_uri.to_string ()); + + auto iter = changes.find (uri); + if (iter == changes.end ()) + { + lsp::vector edits; + edits.push_back (std::move (text_edit)); + changes.insert ({uri, std::move (edits)}); + } + else + iter->second.push_back (std::move (text_edit)); + } + lsp::spec::WorkspaceEdit edit; + edit.changes = std::move (changes); + return edit; +} + +/* FIXME. */ + +static bool +maybe_get_CodeAction_from_from_rich_location (const rich_location &rich_loc, + diagnostics::file_cache &fc, + lsp::spec::CodeAction &out) +{ + if (!rich_loc.get_num_fixit_hints ()) + return false; + + out.title = "Fix"; + if (rich_loc.get_num_fixit_hints () == 1) + { + pretty_printer pp; + if (diagnostics::changes::try_to_print_fixit_hint_description + (&pp, + *rich_loc.get_fixit_hint (0), + fc)) + out.title = pp_formatted_text (&pp); + } + out.kind.set_optional_value ("quickfix"); + out.isPreferred.set_optional_value (true); + out.edit.set_optional_value + (get_WorkspaceEdit_from_from_rich_location (rich_loc)); + + return true; +} + top_level_diagnostic lsp::gcc_impl::worker::diagnostics::sink:: make_top_level_diagnostic (const ::diagnostics::diagnostic_info &diagnostic, @@ -411,6 +481,15 @@ make_top_level_diagnostic (const ::diagnostics::diagnostic_info &diagnostic, pp_clear_output_area (pp); } + // FIXME: stash fix-it hints in the "data" + lsp::spec::CodeAction code_action; + if (maybe_get_CodeAction_from_from_rich_location + (*diagnostic.m_richloc, + get_context ().get_file_cache (), + code_action)) + lsp_diag.data.set_optional_value + (lsp::spec::LSPAny (code_action.to_json ())); + return {uri, std::move (lsp_diag)}; } diff --git a/gcc/lsp/gcc-impl/worker.cc b/gcc/lsp/gcc-impl/worker.cc index 6dd5378a58c..97de2976966 100644 --- a/gcc/lsp/gcc-impl/worker.cc +++ b/gcc/lsp/gcc-impl/worker.cc @@ -432,7 +432,7 @@ on_request_textDocument_declaration (std::unique_ptr req_id, else { if (logger) - logger->log ("ast node %qs has no tree node", ast_node->m_kind); + logger->log ("ast node %s has no tree node", ast_node->m_kind); } } @@ -517,7 +517,7 @@ on_request_textDocument_documentHighlight (std::unique_ptr req_id, else { if (logger) - logger->log ("ast node %qs has no tree node", ast_node->m_kind); + logger->log ("ast node %s has no tree node", ast_node->m_kind); } } diff --git a/gcc/lsp/lsp-spec.cc b/gcc/lsp/lsp-spec.cc index 4eec37a0f71..842b947ef95 100644 --- a/gcc/lsp/lsp-spec.cc +++ b/gcc/lsp/lsp-spec.cc @@ -105,6 +105,17 @@ not_an_integer (const unmarshaller &ctxt, return ctxt.invalid_params (pp_formatted_text (&pp)); } +result +not_a_bool (const unmarshaller &ctxt, + const json::value &jv) +{ + pretty_printer pp; + pp_markup::quoted_json_pointer e_js_pointer (jv); + pp_printf (&pp, "expected boolean for %e; got %s", + &e_js_pointer, ctxt.describe_kind (jv)); + return ctxt.invalid_params (pp_formatted_text (&pp)); +} + template result missing_property (const unmarshaller &ctxt, @@ -150,6 +161,47 @@ unmarshal_optional_property (const unmarshaller &ctxt, return optional (from_json_result.take_value ()); } +/* template + class map : public std::map. */ + +template +result> +lsp::map::from_json (const unmarshaller &ctxt, + const json::value &jv) +{ + const json::object *js_obj = jv.dyn_cast_object (); + if (!js_obj) + { + gcc_unreachable (); // FIXME + //return not_an_object> (ctxt, jv); + } + map m; + for (auto iter : js_obj->get_map ()) + { + const char *k = iter.first; + json::value *v = iter.second; + auto v_result = MappedType::from_json (ctxt, *v); + if (v_result.get_error ()) + return v_result.take_error (); + m.insert ({KeyType (k), v_result.take_value ()}); + } + return m; +} + +template +std::unique_ptr +lsp::map::to_json () const +{ + auto json_obj = std::make_unique (); + for (auto iter : *this) + { + const KeyType &key (iter.first); + auto json_mapped_val = iter.second.to_json (); + json_obj->set (key.m_val.c_str (), std::move (json_mapped_val)); + } + return json_obj; +} + /* Macros for implementing demarshalling for a given type. */ /* FIXME. */ @@ -225,17 +277,65 @@ lsp::spec::TYPE_NAME::from_json (const unmarshaller &ctxt, \ return marshalled_obj; \ } +// FIXME: add generic code for this +// FIXME: check for known values? + +template +lsp::result +unmarshal_enum (const unmarshaller &ctxt, + const json::value &jv) +{ + const json::integer_number *js_int = jv.dyn_cast_integer_number (); + if (!js_int) + return not_an_integer (ctxt, jv); + + T unmarshalled_val; + unmarshalled_val.m_val = static_cast (js_int->get ()); + return unmarshalled_val; +} + +template +std::unique_ptr +marshal_enum (const T &t) +{ + return std::make_unique (static_cast (t.m_val)); +} // TODO: autogenerate the interface binding/marshalling/demarshalling code // from an interface description. +/* struct LSPAny. */ + +lsp::result +lsp::spec::LSPAny::from_json (const unmarshaller &ctxt, + const json::value &jv) +{ + LSPAny result; + result.m_val = jv.clone (); + return result; +} + +std::unique_ptr +lsp::spec::LSPAny::to_json () const +{ + return m_val->clone (); +} + /* struct boolean. */ lsp::result lsp::spec::boolean::from_json (const unmarshaller &ctxt, - const json::value &jv) + const json::value &jv) { - gcc_unreachable (); // FIXME + switch (jv.get_kind ()) + { + default: + return not_a_bool (ctxt, jv); + case json::JSON_TRUE: + return lsp::spec::boolean (true); + case json::JSON_FALSE: + return lsp::spec::boolean (false); + } } std::unique_ptr @@ -482,6 +582,7 @@ BEGIN_DEFINE_FROM_JSON(ServerCapabilities) UNMARSHAL_OPTIONAL_PROPERTY (boolean, declarationProvider); UNMARSHAL_OPTIONAL_PROPERTY (boolean, documentHighlightProvider); UNMARSHAL_OPTIONAL_PROPERTY (boolean, documentSymbolProvider); + UNMARSHAL_OPTIONAL_PROPERTY (boolean, codeActionProvider); UNMARSHAL_OPTIONAL_PROPERTY (SemanticTokensOptions, semanticTokensProvider); } END_DEFINE_FROM_JSON @@ -495,6 +596,7 @@ BEGIN_DEFINE_TO_JSON(ServerCapabilities) MARSHAL_OPTIONAL_PROPERTY (boolean, declarationProvider); MARSHAL_OPTIONAL_PROPERTY (boolean, documentHighlightProvider); MARSHAL_OPTIONAL_PROPERTY (boolean, documentSymbolProvider); + MARSHAL_OPTIONAL_PROPERTY (boolean, codeActionProvider); MARSHAL_OPTIONAL_PROPERTY (SemanticTokensOptions, semanticTokensProvider); } END_DEFINE_TO_JSON @@ -564,6 +666,13 @@ lsp::spec::Position::to_json () const return jobj; } +lsp::spec::Position +lsp::spec::Position::from_location_t (location_t loc) +{ + expanded_location exp_loc = expand_location (loc); + return from_expanded_location (exp_loc); +} + lsp::spec::Position lsp::spec::Position::from_expanded_location (const expanded_location &exp_loc) { @@ -872,10 +981,17 @@ lsp::spec::TextDocumentPositionParams::from_json (const unmarshaller &ctxt, // struct DiagnosticSeverity +lsp::result +lsp::spec::DiagnosticSeverity::from_json (const unmarshaller &ctxt, + const json::value &jv) +{ + return unmarshal_enum (ctxt, jv); +} + std::unique_ptr lsp::spec::DiagnosticSeverity::to_json () const { - return std::make_unique (m_val); + return marshal_enum (*this); } // struct DiagnosticRelatedInformation @@ -910,25 +1026,35 @@ END_DEFINE_TO_JSON // struct Diagnostic -std::unique_ptr -lsp::spec::Diagnostic::to_json () const +BEGIN_DEFINE_FROM_JSON(Diagnostic) { - auto jobj = std::make_unique (); - jobj->set ("range", range.to_json ()); - if (auto val = severity.get_optional_value ()) - jobj->set ("severity", val->to_json ()); - if (auto val = code.get_optional_value ()) - jobj->set ("code", val->to_json ()); - if (auto val = codeDescription.get_optional_value ()) - jobj->set ("codeDescription", val->to_json ()); + UNMARSHAL_REQUIRED_PROPERTY (Range, range); + UNMARSHAL_OPTIONAL_PROPERTY (DiagnosticSeverity, severity); + UNMARSHAL_OPTIONAL_PROPERTY (string, code); + UNMARSHAL_OPTIONAL_PROPERTY (CodeDescription, codeDescription); - jobj->set ("message", message.to_json ()); + UNMARSHAL_REQUIRED_PROPERTY (string, message); - if (auto val = relatedInformation.get_optional_value ()) - jobj->set ("relatedInformation", val->to_json ()); + UNMARSHAL_OPTIONAL_PROPERTY (vector, + relatedInformation); + UNMARSHAL_OPTIONAL_PROPERTY (LSPAny, data); +} +END_DEFINE_FROM_JSON - return jobj; +BEGIN_DEFINE_TO_JSON(Diagnostic) +{ + MARSHAL_REQUIRED_PROPERTY (Range, range); + MARSHAL_OPTIONAL_PROPERTY (DiagnosticSeverity, severity); + MARSHAL_OPTIONAL_PROPERTY (string, severity); + MARSHAL_OPTIONAL_PROPERTY (CodeDescription, codeDescription); + + MARSHAL_REQUIRED_PROPERTY (string, message); + + MARSHAL_OPTIONAL_PROPERTY (vector, + relatedInformation); + MARSHAL_OPTIONAL_PROPERTY (LSPAny, data); } +END_DEFINE_TO_JSON // struct PublishDiagnosticsParams @@ -1030,6 +1156,116 @@ BEGIN_DEFINE_TO_JSON(MarkupContent) } END_DEFINE_TO_JSON +// struct CodeActionTriggerKind + +lsp::result +lsp::spec::CodeActionTriggerKind::from_json (const unmarshaller &ctxt, + const json::value &jv) +{ + return unmarshal_enum (ctxt, jv); +} + +std::unique_ptr +lsp::spec::CodeActionTriggerKind::to_json () const +{ + return marshal_enum (*this); +} + +// struct CodeActionContext + +BEGIN_DEFINE_FROM_JSON(CodeActionContext) +{ + UNMARSHAL_REQUIRED_PROPERTY (vector, diagnostics); + UNMARSHAL_OPTIONAL_PROPERTY (vector, only); + UNMARSHAL_OPTIONAL_PROPERTY (CodeActionTriggerKind, triggerKind); +} +END_DEFINE_FROM_JSON + +BEGIN_DEFINE_TO_JSON(CodeActionContext) +{ + MARSHAL_REQUIRED_PROPERTY (vector, diagnostics); + MARSHAL_OPTIONAL_PROPERTY (vector, only); + MARSHAL_OPTIONAL_PROPERTY (CodeActionTriggerKind, triggerKind); +} +END_DEFINE_TO_JSON + + +// struct CodeActionParams + +BEGIN_DEFINE_FROM_JSON(CodeActionParams) +{ + UNMARSHAL_REQUIRED_PROPERTY (TextDocumentIdentifier, textDocument); + UNMARSHAL_REQUIRED_PROPERTY (Range, range); + UNMARSHAL_REQUIRED_PROPERTY (CodeActionContext, context); +} +END_DEFINE_FROM_JSON + +BEGIN_DEFINE_TO_JSON(CodeActionParams) +{ + MARSHAL_REQUIRED_PROPERTY (TextDocumentIdentifier, textDocument); + MARSHAL_REQUIRED_PROPERTY (Range, range); + MARSHAL_REQUIRED_PROPERTY (CodeActionContext, context); +} +END_DEFINE_TO_JSON + +// struct TextEdit + +BEGIN_DEFINE_FROM_JSON(TextEdit) +{ + UNMARSHAL_REQUIRED_PROPERTY (Range, range); + UNMARSHAL_REQUIRED_PROPERTY (string, newText); +} +END_DEFINE_FROM_JSON + +BEGIN_DEFINE_TO_JSON(TextEdit) +{ + MARSHAL_REQUIRED_PROPERTY (Range, range); + MARSHAL_REQUIRED_PROPERTY (string, newText); +} +END_DEFINE_TO_JSON + +// struct WorkspaceEdit + +BEGIN_DEFINE_FROM_JSON(WorkspaceEdit) +{ + UNMARSHAL_OPTIONAL_PROPERTY (map_DocumentUri_to_vector_TextEdit, changes); +} +END_DEFINE_FROM_JSON + +BEGIN_DEFINE_TO_JSON(WorkspaceEdit) +{ + MARSHAL_OPTIONAL_PROPERTY (map_DocumentUri_to_vector_TextEdit, changes); +} +END_DEFINE_TO_JSON + +// struct CodeAction + +BEGIN_DEFINE_FROM_JSON(CodeAction) +{ + UNMARSHAL_REQUIRED_PROPERTY (string, title); + UNMARSHAL_OPTIONAL_PROPERTY (CodeActionKind, kind); + UNMARSHAL_OPTIONAL_PROPERTY (vector, diagnostics); + UNMARSHAL_OPTIONAL_PROPERTY (boolean, isPreferred); + + UNMARSHAL_OPTIONAL_PROPERTY (WorkspaceEdit, edit); + + UNMARSHAL_OPTIONAL_PROPERTY (LSPAny, data); +} +END_DEFINE_FROM_JSON + +BEGIN_DEFINE_TO_JSON(CodeAction) +{ + MARSHAL_REQUIRED_PROPERTY (string, title); + MARSHAL_OPTIONAL_PROPERTY (CodeActionKind, kind); + MARSHAL_OPTIONAL_PROPERTY (vector, diagnostics); + MARSHAL_OPTIONAL_PROPERTY (boolean, isPreferred); + + MARSHAL_OPTIONAL_PROPERTY (WorkspaceEdit, edit); + + MARSHAL_OPTIONAL_PROPERTY (LSPAny, data); +} +END_DEFINE_TO_JSON + // struct HoverParams : public TextDocumentPositionParams BEGIN_DEFINE_FROM_JSON(HoverParams) @@ -1084,23 +1320,6 @@ END_DEFINE_TO_JSON // struct DocumentHighlightKind -// FIXME: add generic code for this -// FIXME: check for known values? - -template -lsp::result -unmarshal_enum (const unmarshaller &ctxt, - const json::value &jv) -{ - const json::integer_number *js_int = jv.dyn_cast_integer_number (); - if (!js_int) - return not_an_integer (ctxt, jv); - - T unmarshalled_val; - unmarshalled_val.m_val = static_cast (js_int->get ()); - return unmarshalled_val; -} - lsp::result lsp::spec::DocumentHighlightKind::from_json (const unmarshaller &ctxt, const json::value &jv) @@ -1108,13 +1327,6 @@ lsp::spec::DocumentHighlightKind::from_json (const unmarshaller &ctxt, return unmarshal_enum (ctxt, jv); } -template -std::unique_ptr -marshal_enum (const T &t) -{ - return std::make_unique (static_cast (t.m_val)); -} - std::unique_ptr lsp::spec::DocumentHighlightKind::to_json () const { diff --git a/gcc/lsp/lsp-spec.h b/gcc/lsp/lsp-spec.h index a1eea5b67a8..55cfdcb03c7 100644 --- a/gcc/lsp/lsp-spec.h +++ b/gcc/lsp/lsp-spec.h @@ -226,6 +226,18 @@ public: } }; +template +class map : public std::map +{ +public: + static result> + from_json (const unmarshaller &ctxt, + const json::value &jv); + + std::unique_ptr + to_json () const; +}; + namespace spec { #define DECLARE_JSON_METHODS(TYPENAME) \ @@ -239,6 +251,24 @@ namespace spec { /* Types from the LSP protocol specification. This uses camel case, and so we do here, within this namespace, at least. */ +struct LSPAny +{ + DECLARE_JSON_METHODS(LSPAny) + + LSPAny () = default; + LSPAny (const LSPAny &other) + { + if (other.m_val) + m_val = other.m_val->clone (); + } + LSPAny (std::unique_ptr val) + : m_val (std::move (val)) + { + } + + std::unique_ptr m_val; +}; + // TODO: autogenerate the interface binding/marshalling/demarshalling code // from an interface description. @@ -304,6 +334,7 @@ struct string string () = default; string (const char *s) : m_val (s) {} + string (std::string s) : m_val (std::move (s)) {} std::string m_val; }; @@ -312,6 +343,9 @@ struct DocumentUri : public string { DECLARE_JSON_METHODS(DocumentUri) + DocumentUri () = default; + DocumentUri (std::string s) : string (std::move (s)) {} + bool operator< (const DocumentUri &other) const { @@ -479,15 +513,9 @@ struct ServerCapabilities #endif optional documentHighlightProvider; optional documentSymbolProvider; + optional codeActionProvider; #if 0 - /** - * The server provides code actions. The `CodeActionOptions` return type is - * only valid if the client signals code action literal support via the - * property `textDocument.codeAction.codeActionLiteralSupport`. - */ - codeActionProvider?: boolean | CodeActionOptions; - /** * The server provides code lens. */ @@ -698,6 +726,9 @@ struct Position { DECLARE_JSON_METHODS(Position) + static Position + from_location_t (location_t); + static Position from_expanded_location (const expanded_location &); @@ -803,7 +834,7 @@ struct TextDocumentPositionParams struct DiagnosticSeverity { DECLARE_JSON_METHODS(DiagnosticSeverity) - enum { + enum class values { Error = 1, Warning = 2, Information = 3, @@ -853,17 +884,7 @@ struct Diagnostic #endif optional> relatedInformation; - -#if 0 - /** - * A data entry field that is preserved between a - * `textDocument/publishDiagnostics` notification and - * `textDocument/codeAction` request. - * - * @since 3.16.0 - */ - data?: LSPAny; -#endif + optional data; }; struct PublishDiagnosticsParams @@ -959,6 +980,105 @@ struct MarkupContent string value; }; +using CodeActionKind = string; + +struct CodeActionTriggerKind +{ + DECLARE_JSON_METHODS(CodeActionTriggerKind) + + enum class values { + Invoked = 1, + Automatic = 2 + } m_val; +}; + +struct CodeActionContext +{ + DECLARE_JSON_METHODS(CodeActionContext) + + vector diagnostics; + optional> only; + optional triggerKind; +}; + +struct CodeActionParams +{ + DECLARE_JSON_METHODS(CodeActionParams) + + TextDocumentIdentifier textDocument; + Range range; + CodeActionContext context; +}; + +struct TextEdit +{ + DECLARE_JSON_METHODS(TextEdit) + + Range range; + string newText; +}; + +using map_DocumentUri_to_vector_TextEdit = map>; + +struct WorkspaceEdit +{ + DECLARE_JSON_METHODS(WorkspaceEdit) + + optional changes; + + // FIXME +}; + +struct CodeAction +{ + DECLARE_JSON_METHODS(CodeAction) + + string title; + optional kind; + optional> diagnostics; + optional isPreferred; + +#if 0 + /** + * Marks that the code action cannot currently be applied. + * + * Clients should follow the following guidelines regarding disabled code + * actions: + * + * - Disabled code actions are not shown in automatic lightbulbs code + * action menus. + * + * - Disabled actions are shown as faded out in the code action menu when + * the user request a more specific type of code action, such as + * refactorings. + * + * - If the user has a keybinding that auto applies a code action and only + * a disabled code actions are returned, the client should show the user + * an error message with `reason` in the editor. + * + * @since 3.16.0 + */ + disabled?: { + + /** + * Human readable description of why the code action is currently + * disabled. + * + * This is displayed in the code actions UI. + */ + reason: string; + }; +#endif + + optional edit; + +#if 0 + optional command; +#endif + + optional data; +}; + struct HoverParams : public TextDocumentPositionParams { DECLARE_JSON_METHODS(HoverParams) -- 2.49.0