From 65cbe00bc124bdc1bd83bcd42ad5fbed521412b2 Mon Sep 17 00:00:00 2001 From: pommicket Date: Fri, 30 Dec 2022 11:38:00 -0500 Subject: parse workspace/symbol response --- ds.c | 2 ++ ide-definitions.c | 46 +++++++++++++++++++++++++++++++++++++ lsp-parse.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++-------- lsp-write.c | 59 ++++++++++++++++++++++++++++++++++------------- lsp.c | 50 +++------------------------------------- lsp.h | 7 ++++++ main.c | 1 + 7 files changed, 161 insertions(+), 73 deletions(-) diff --git a/ds.c b/ds.c index 99ad4d3..b599f36 100644 --- a/ds.c +++ b/ds.c @@ -100,6 +100,8 @@ static void arr_reserve_(void **arr, size_t member_size, size_t n) { if (*arr) free(arr_hdr_(*arr)); *arr = NULL; } + + if (n == 0) return; if (!*arr) { // create a new array with capacity n+1 diff --git a/ide-definitions.c b/ide-definitions.c index a748173..e63e977 100644 --- a/ide-definitions.c +++ b/ide-definitions.c @@ -1,3 +1,49 @@ + +static SymbolKind symbol_kind_to_ted(LSPSymbolKind kind) { + switch (kind) { + case LSP_SYMBOL_OTHER: + case LSP_SYMBOL_FILE: + case LSP_SYMBOL_MODULE: + case LSB_SYMBOL_NAMESPACE: + case LSP_SYMBOL_PACKAGE: + return SYMBOL_OTHER; + + case LSP_SYMBOL_CLASS: + case LSP_SYMBOL_TYPEPARAMETER: + case LSP_SYMBOL_ENUM: + case LSP_SYMBOL_INTERFACE: + case LSP_SYMBOL_STRUCT: + case LSP_SYMBOL_EVENT: // i have no clue what this is. let's say it's a type. + return SYMBOL_TYPE; + + case LSP_SYMBOL_PROPERTY: + case LSP_SYMBOL_FIELD: + case LSP_SYMBOL_KEY: + return SYMBOL_FIELD; + + case LSP_SYMBOL_CONSTRUCTOR: + case LSP_SYMBOL_FUNCTION: + case LSP_SYMBOL_OPERATOR: + case LSP_SYMBOL_METHOD: + return SYMBOL_FUNCTION; + + case LSP_SYMBOL_VARIABLE: + return SYMBOL_VARIABLE; + + case LSP_SYMBOL_CONSTANT: + case LSP_SYMBOL_STRING: + case LSP_SYMBOL_NUMBER: + case LSP_SYMBOL_BOOLEAN: + case LSP_SYMBOL_ARRAY: + case LSP_SYMBOL_OBJECT: + case LSP_SYMBOL_ENUMMEMBER: + case LSP_SYMBOL_NULL: + return SYMBOL_CONSTANT; + } + + return SYMBOL_OTHER; +} + void definition_goto(Ted *ted, LSP *lsp, const char *name, LSPDocumentPosition position) { Definitions *defs = &ted->definitions; if (lsp) { diff --git a/lsp-parse.c b/lsp-parse.c index 42a3c7b..c98ae85 100644 --- a/lsp-parse.c +++ b/lsp-parse.c @@ -82,11 +82,8 @@ static bool parse_range(LSP *lsp, const JSON *json, JSONValue range_value, LSPRa } static bool parse_document_uri(LSP *lsp, const JSON *json, JSONValue value, LSPDocumentID *id) { - if (value.type != JSON_STRING) { - lsp_set_error(lsp, "Expected string for URI, got %s", - json_type_to_str(value.type)); + if (!lsp_expect_string(lsp, value, "URI")) return false; - } char *string = json_string_get_alloc(json, value.val.string); if (!str_has_prefix(string, "file://")) { lsp_set_error(lsp, "Can't process non-local URI %s", @@ -262,7 +259,7 @@ static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response) JSONArray tags = json_object_get_array(json, item_object, "tags"); for (u32 i = 0; i < tags.len; ++i) { double tag = json_array_get_number(json, tags, i); - if (tag == 1 /* deprecated */) { + if (tag == LSP_SYMBOL_TAG_DEPRECATED) { item->deprecated = true; } } @@ -495,11 +492,8 @@ static bool parse_hover(LSP *lsp, const JSON *json, LSPResponse *response) { // parse a Location: {uri: DocumentUri, range: Range} static bool parse_location(LSP *lsp, const JSON *json, JSONValue value, LSPLocation *location) { - if (value.type != JSON_OBJECT) { - lsp_set_error(lsp, "Expected object for location but got %s", - json_type_to_str(value.type)); + if (!lsp_expect_object(lsp, value, "Location")) return false; - } JSONObject object = value.val.object; JSONValue uri = json_object_get(json, object, "uri"); if (!parse_document_uri(lsp, json, uri, &location->document)) @@ -534,9 +528,61 @@ static bool parse_definition(LSP *lsp, const JSON *json, LSPResponse *response) } } +// parses SymbolInformation or WorkspaceSymbol +static bool parse_symbol_information(LSP *lsp, const JSON *json, JSONValue value, + LSPResponse *response, LSPSymbolInformation *info) { + if (!lsp_expect_object(lsp, value, "SymbolInformation")) + return false; + JSONObject object = value.val.object; + + // parse name + JSONValue name_value = json_object_get(json, object, "name"); + if (!lsp_expect_string(lsp, name_value, "SymbolInformation.name")) + return false; + JSONString name = name_value.val.string; + info->name = lsp_response_add_json_string(response, json, name); + + // parse kind + JSONValue kind_value = json_object_get(json, object, "kind"); + if (!lsp_expect_number(lsp, kind_value, "SymbolInformation.kind")) + return false; + double kind = kind_value.val.number; + if (isfinite(kind) && kind >= LSP_SYMBOL_KIND_MIN && kind <= LSP_SYMBOL_KIND_MAX) + info->kind = (LSPSymbolKind)kind; + + // check if deprecated + bool deprecated = json_object_get(json, object, "deprecated").type == JSON_TRUE; + JSONArray tags = json_object_get_array(json, object, "tags"); + for (size_t i = 0; i < tags.len; ++i) { + if (json_array_get_number(json, tags, i) == LSP_SYMBOL_TAG_DEPRECATED) + deprecated = true; + } + info->deprecated = deprecated; + + // parse location + JSONValue location = json_object_get(json, object, "location"); + if (!parse_location(lsp, json, location, &info->location)) + return false; + + return true; +} + +static bool parse_workspace_symbols(LSP *lsp, const JSON *json, LSPResponse *response) { + LSPResponseWorkspaceSymbols *syms = &response->data.workspace_symbols; + JSONArray result = json_force_array(json_root(json)); + arr_set_len(syms->symbols, result.len); + for (size_t i = 0; i < result.len; ++i) { + LSPSymbolInformation *info = &syms->symbols[i]; + JSONValue value = json_array_get(json, result, i); + if (!parse_symbol_information(lsp, json, value, response, info)) + return false; + } + 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) { +static WarnUnusedResult bool parse_id(const JSON *json, LSPRequest *request) { JSONValue id_value = json_get(json, "id"); switch (id_value.type) { case JSON_NUMBER: { @@ -677,6 +723,9 @@ static void process_message(LSP *lsp, JSON *json) { case LSP_REQUEST_DEFINITION: add_to_messages = parse_definition(lsp, json, &response); break; + case LSP_REQUEST_WORKSPACE_SYMBOLS: + add_to_messages = parse_workspace_symbols(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 4068479..58c5a8f 100644 --- a/lsp-write.c +++ b/lsp-write.c @@ -318,6 +318,41 @@ static void message_writer_write_and_free(LSP *lsp, JSONWriter *o) { str_builder_free(&builder); } +static void write_symbol_tag_support(JSONWriter *o) { + write_key_obj_start(o, "tagSupport"); + write_key_arr_start(o, "valueSet"); + for (int i = LSP_SYMBOL_TAG_MIN; i <= LSP_SYMBOL_TAG_MAX; ++i) + write_arr_elem_number(o, i); + write_arr_end(o); + write_obj_end(o); +} + + +static void write_completion_item_kind_support(JSONWriter *o) { + // "completion item kinds" supported by ted + // (these are the little icons displayed for function/variable/etc.) + write_key_obj_start(o, "completionItemKind"); + write_key_arr_start(o, "valueSet"); + for (int i = LSP_COMPLETION_KIND_MIN; + i <= LSP_COMPLETION_KIND_MAX; ++i) { + write_arr_elem_number(o, i); + } + write_arr_end(o); + write_obj_end(o); +} + +static void write_symbol_kind_support(JSONWriter *o) { + write_key_obj_start(o, "symbolKind"); + write_key_arr_start(o, "valueSet"); + for (int i = LSP_SYMBOL_KIND_MIN; + i <= LSP_SYMBOL_KIND_MAX; + ++i) { + write_arr_elem_number(o, i); + } + write_arr_end(o); + write_obj_end(o); +} + // NOTE: don't call lsp_request_free after calling this function. // I will do it for you. static void write_request(LSP *lsp, LSPRequest *request) { @@ -365,24 +400,10 @@ static void write_request(LSP *lsp, LSPRequest *request) { write_arr_end(o); write_key_bool(o, "deprecatedSupport", true); write_key_bool(o, "preselectSupport", false); - write_key_obj_start(o, "tagSupport"); - write_key_arr_start(o, "valueSet"); - // currently the only tag in the spec - write_arr_elem_number(o, 1); - write_arr_end(o); - write_obj_end(o); + write_symbol_tag_support(o); write_key_bool(o, "insertReplaceSupport", false); write_obj_end(o); - // "completion item kinds" supported by ted - // (these are the little icons displayed for function/variable/etc.) - write_key_obj_start(o, "completionItemKind"); - write_key_arr_start(o, "valueSet"); - for (int i = LSP_COMPLETION_KIND_MIN; - i <= LSP_COMPLETION_KIND_MAX; ++i) { - write_arr_elem_number(o, i); - } - write_arr_end(o); - write_obj_end(o); + write_completion_item_kind_support(o); write_key_bool(o, "contextSupport", true); write_obj_end(o); @@ -412,6 +433,12 @@ static void write_request(LSP *lsp, LSPRequest *request) { write_obj_end(o); write_key_obj_start(o, "workspace"); write_key_bool(o, "workspaceFolders", true); + + write_key_obj_start(o, "symbol"); + write_symbol_kind_support(o); + write_symbol_tag_support(o); + // resolve is kind of a pain to implement. i'm not doing it yet. + write_obj_end(o); write_obj_end(o); write_obj_end(o); SDL_LockMutex(lsp->workspace_folders_mutex); diff --git a/lsp.c b/lsp.c index 3efa830..c8de1a7 100644 --- a/lsp.c +++ b/lsp.c @@ -93,6 +93,9 @@ static void lsp_response_free(LSPResponse *r) { case LSP_REQUEST_DEFINITION: arr_free(r->data.definition.locations); break; + case LSP_REQUEST_WORKSPACE_SYMBOLS: + arr_free(r->data.workspace_symbols.symbols); + break; default: break; } @@ -537,53 +540,6 @@ void lsp_document_changed(LSP *lsp, const char *document, LSPDocumentChangeEvent lsp_send_request(lsp, &request); } -#if 0 -SymbolKind lsp_symbol_kind_to_ted(LSPSymbolKind kind) { - switch (kind) { - case LSP_SYMBOL_OTHER: - case LSP_SYMBOL_FILE: - case LSP_SYMBOL_MODULE: - case LSB_SYMBOL_NAMESPACE: - case LSP_SYMBOL_PACKAGE: - return SYMBOL_OTHER; - - case LSP_SYMBOL_CLASS: - case LSP_SYMBOL_TYPEPARAMETER: - case LSP_SYMBOL_ENUM: - case LSP_SYMBOL_INTERFACE: - case LSP_SYMBOL_STRUCT: - case LSP_SYMBOL_EVENT: // i have no clue what this is. let's say it's a type. - return SYMBOL_TYPE; - - case LSP_SYMBOL_PROPERTY: - case LSP_SYMBOL_FIELD: - case LSP_SYMBOL_KEY: - return SYMBOL_FIELD; - - case LSP_SYMBOL_CONSTRUCTOR: - case LSP_SYMBOL_FUNCTION: - case LSP_SYMBOL_OPERATOR: - case LSP_SYMBOL_METHOD: - return SYMBOL_FUNCTION; - - case LSP_SYMBOL_VARIABLE: - return SYMBOL_VARIABLE; - - case LSP_SYMBOL_CONSTANT: - case LSP_SYMBOL_STRING: - case LSP_SYMBOL_NUMBER: - case LSP_SYMBOL_BOOLEAN: - case LSP_SYMBOL_ARRAY: - case LSP_SYMBOL_OBJECT: - case LSP_SYMBOL_ENUMMEMBER: - case LSP_SYMBOL_NULL: - return SYMBOL_CONSTANT; - } - - return SYMBOL_OTHER; -} -#endif - bool lsp_position_eq(LSPPosition a, LSPPosition b) { return a.line == b.line && a.character == b.character; } diff --git a/lsp.h b/lsp.h index 3529174..b80fbb2 100644 --- a/lsp.h +++ b/lsp.h @@ -301,6 +301,12 @@ typedef struct { LSPLocation *locations; } LSPResponseDefinition; +typedef enum { + #define LSP_SYMBOL_TAG_MIN 1 + LSP_SYMBOL_TAG_DEPRECATED = 1 + #define LSP_SYMBOL_TAG_MAX 1 +} LSPSymbolTag; + // SymbolInformation in the LSP spec typedef struct { LSPString name; @@ -327,6 +333,7 @@ typedef struct { LSPResponseSignatureHelp signature_help; LSPResponseHover hover; LSPResponseDefinition definition; + LSPResponseWorkspaceSymbols workspace_symbols; } data; } LSPResponse; diff --git a/main.c b/main.c index 172a415..61b319d 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,6 @@ /* @TODO: +- cancelling requests e.g. in ide-definitions.c - more LSP stuff: - go to definition using LSP - find usages -- cgit v1.2.3