From 47adb1651d35dcc545850916c4a16b747901dba5 Mon Sep 17 00:00:00 2001 From: pommicket Date: Tue, 27 Dec 2022 23:15:27 -0500 Subject: more signature help --- lsp-parse.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------- lsp-write.c | 20 ++++++++++++----- lsp.c | 8 ++++++- lsp.h | 21 +++++++++++++++++ main.c | 1 + 5 files changed, 109 insertions(+), 16 deletions(-) diff --git a/lsp-parse.c b/lsp-parse.c index 339c490..c340d33 100644 --- a/lsp-parse.c +++ b/lsp-parse.c @@ -132,6 +132,18 @@ static void parse_capabilities(LSP *lsp, const JSON *json, JSONObject capabiliti } } +static JSONString get_markup_content(const JSON *json, JSONValue markup_value) { + // some fields are of type string | MarkupContent (e.g. completion documentation) + // this converts either one to a string. + if (markup_value.type == JSON_STRING) { + return markup_value.val.string; + } else if (markup_value.type == JSON_OBJECT) { + return json_object_get_string(json, markup_value.val.object, "value"); + } else { + return (JSONString){0}; + } +} + static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response) { // deal with textDocument/completion response. // result: CompletionItem[] | CompletionList | null @@ -229,16 +241,8 @@ static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response) item->text_edit.type = (LSPTextEditType)edit_type; } - JSONString documentation = {0}; JSONValue documentation_value = json_object_get(json, item_object, "documentation"); - // the "documentation" field is either just a string or an object containing - // a type ("markdown" or "plaintext") and a string. - if (documentation_value.type == JSON_STRING) { - documentation = documentation_value.val.string; - } else if (documentation_value.type == JSON_OBJECT) { - documentation = json_object_get_string(json, documentation_value.val.object, - "value"); - } + JSONString documentation = get_markup_content(json, documentation_value); if (documentation.len) { if (documentation.len > 1000) { // rust has some docs which are *20,000* bytes long @@ -292,6 +296,56 @@ static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response) return true; } +static bool parse_signature_help(LSP *lsp, const JSON *json, LSPResponse *response) { + JSONObject result = json_force_object(json_get(json, "result")); + LSPResponseSignatureHelp *help = &response->data.signature_help; + + u32 active_signature = 0; + double active_signature_dbl = json_object_get_number(json, result, "activeSignature"); + if (isnormal(active_signature_dbl)) + active_signature = (u32)active_signature_dbl; + double active_signature_active_parameter = json_object_get_number(json, result, "activeParameter"); + + JSONArray signatures = json_object_get_array(json, result, "signatures"); + if (active_signature >= signatures.len) + active_signature = 0; + for (u32 s = 0; s < signatures.len; ++s) { + // parse SignatureInformation + LSPSignatureInformation *signature_out = arr_addp(help->signatures); + JSONObject signature_in = json_array_get_object(json, signatures, s); + JSONString label = json_object_get_string(json, signature_in, "label"); + signature_out->label = lsp_response_add_json_string(response, json, label); + JSONString documentation = get_markup_content(json, + json_object_get(json, signature_in, "documentation")); + if (documentation.len) + signature_out->documentation = lsp_response_add_json_string(response, json, documentation); + + JSONArray parameters = json_object_get_array(json, signature_in, "parameters"); + u32 active_parameter = U32_MAX; + double active_parameter_dbl = json_object_get_number(json, signature_in, "activeParameter"); + if (isnormal(active_parameter_dbl)) { + active_parameter = (u32)active_parameter_dbl; + } + if (s == active_signature && active_parameter == U32_MAX && + isnormal(active_signature_active_parameter)) { + active_parameter = (u32)active_parameter_dbl; + } + if (active_parameter < parameters.len) { + JSONObject parameter_info = json_array_get_object(json, parameters, active_parameter); + @TODO + } + } + + if (active_signature != 0) { + //make sure active signature is #0 + LSPSignatureInformation active = help->signatures[active_signature]; + arr_remove(help->signatures, active_signature); + arr_insert(help->signatures, 0, active); + } + + return true; +} + // fills request->id/id_string appropriately given the request's json // returns true on success static WarnUnusedResult bool parse_id(JSON *json, LSPRequest *request) { @@ -425,6 +479,9 @@ static void process_message(LSP *lsp, JSON *json) { case LSP_REQUEST_COMPLETION: add_to_messages = parse_completion(lsp, json, &response); break; + case LSP_REQUEST_SIGNATURE_HELP: + add_to_messages = parse_signature_help(lsp, json, &response); + break; case LSP_REQUEST_INITIALIZE: { // it's the response to our initialize request! if (result.type == JSON_OBJECT) { diff --git a/lsp-write.c b/lsp-write.c index 835cfce..910eb1b 100644 --- a/lsp-write.c +++ b/lsp-write.c @@ -1,5 +1,3 @@ - - static const char *lsp_language_id(Language lang) { switch (lang) { case LANG_CONFIG: @@ -228,6 +226,13 @@ static void write_workspace_folders(JSONWriter *o, LSPDocumentID *workspace_fold write_arr_end(o); } +static void write_document_position(JSONWriter *o, LSPDocumentPosition pos) { + write_key_obj_start(o, "textDocument"); + write_key_file_uri(o, "uri", pos.document); + write_obj_end(o); + write_key_position(o, "position", pos.pos); +} + static const char *lsp_request_method(LSPRequest *request) { switch (request->type) { case LSP_REQUEST_NONE: break; @@ -454,10 +459,7 @@ static void write_request(LSP *lsp, LSPRequest *request) { case LSP_REQUEST_COMPLETION: { const LSPRequestCompletion *completion = &request->data.completion; write_key_obj_start(o, "params"); - write_key_obj_start(o, "textDocument"); - write_key_file_uri(o, "uri", completion->position.document); - write_obj_end(o); - write_key_position(o, "position", completion->position.pos); + write_document_position(o, completion->position); const LSPCompletionContext *context = &completion->context; LSPCompletionTriggerKind trigger_kind = context->trigger_kind; if (trigger_kind != LSP_TRIGGER_NONE) { @@ -469,6 +471,12 @@ static void write_request(LSP *lsp, LSPRequest *request) { } write_obj_end(o); } break; + case LSP_REQUEST_SIGNATURE_HELP: { + const LSPRequestSignatureHelp *help = &request->data.signature_help; + write_key_obj_start(o, "params"); + write_document_position(o, help->position); + write_obj_end(o); + } break; case LSP_REQUEST_DID_CHANGE_WORKSPACE_FOLDERS: { const LSPRequestDidChangeWorkspaceFolders *w = &request->data.change_workspace_folders; write_key_obj_start(o, "params"); diff --git a/lsp.c b/lsp.c index 9ec7ad7..bd6348a 100644 --- a/lsp.c +++ b/lsp.c @@ -1,5 +1,5 @@ // print server-to-client communication -#define LSP_SHOW_S2C 0 +#define LSP_SHOW_S2C 1 // print client-to-server communication #define LSP_SHOW_C2S 0 @@ -42,6 +42,7 @@ static void lsp_request_free(LSPRequest *r) { case LSP_REQUEST_SHUTDOWN: case LSP_REQUEST_EXIT: case LSP_REQUEST_COMPLETION: + case LSP_REQUEST_SIGNATURE_HELP: case LSP_REQUEST_DID_CLOSE: case LSP_REQUEST_WORKSPACE_FOLDERS: break; @@ -74,6 +75,9 @@ static void lsp_response_free(LSPResponse *r) { case LSP_REQUEST_COMPLETION: arr_free(r->data.completion.items); break; + case LSP_REQUEST_SIGNATURE_HELP: + arr_free(r->data.signature_help.signatures); + break; default: break; } @@ -130,6 +134,8 @@ static bool lsp_supports_request(LSP *lsp, const LSPRequest *request) { return true; case LSP_REQUEST_COMPLETION: return cap->completion_support; + case LSP_REQUEST_SIGNATURE_HELP: + return cap->signature_help_support; case LSP_REQUEST_DID_CHANGE_WORKSPACE_FOLDERS: return cap->workspace_folders_support; } diff --git a/lsp.h b/lsp.h index 1c75861..4491cd2 100644 --- a/lsp.h +++ b/lsp.h @@ -245,6 +245,26 @@ typedef struct { LSPCompletionItem *items; } LSPResponseCompletion; +typedef struct { + LSPString label; + LSPString documentation; + // NOTE: LSP gives us parameter information for *all* + // parameters, but we only really need it for the active parameter. + + // (UTF-16) indices into `label` indicating which + // part of it should be highlighted for the active parameter + u16 active_start; + u16 active_end; + // documentation for the active parameter + LSPString active_documentation; +} LSPSignatureInformation; + +typedef struct { + // NOTE: the "active" signature will be the first one + // in this array. + LSPSignatureInformation *signatures; +} LSPResponseSignatureHelp; + typedef LSPRequestType LSPResponseType; typedef struct { @@ -255,6 +275,7 @@ typedef struct { char *string_data; union { LSPResponseCompletion completion; + LSPResponseSignatureHelp signature_help; } data; } LSPResponse; diff --git a/main.c b/main.c index 3f028f6..3468577 100644 --- a/main.c +++ b/main.c @@ -6,6 +6,7 @@ - go to definition using LSP - find usages - go through signature help capabilities +- JSON syntax highlighting - separate signature-help setting (dont use trigger-characters) - check if there are any other non-optional/nice-to-have-support-for server-to-client requests - do something with lsp->error -- cgit v1.2.3