From bb4b7774aabc2686f936935b02899b1aa0bf1f2b Mon Sep 17 00:00:00 2001 From: pommicket Date: Thu, 7 Sep 2023 14:06:16 -0400 Subject: use LSPString in requests too for consistency --- buffer.c | 23 ++++----- ide-definitions.c | 5 +- ide-rename-symbol.c | 8 +--- lsp-parse.c | 47 +++++++------------ lsp-write.c | 19 ++++---- lsp.c | 132 +++++++++++++++++++++++++++++++++++++++------------- lsp.h | 66 ++++++++++++++++---------- main.c | 35 +++++++------- util.c | 33 +++++++------ util.h | 12 +++++ 10 files changed, 228 insertions(+), 152 deletions(-) diff --git a/buffer.c b/buffer.c index 94cebff..1d8effd 100644 --- a/buffer.c +++ b/buffer.c @@ -497,15 +497,12 @@ static void buffer_send_lsp_did_close(TextBuffer *buffer, LSP *lsp, const char * buffer->lsp_opened_in = 0; } -// buffer_contents must either be NULL or allocated with malloc or similar -// - don't free it after calling this function. -// if buffer_contents = NULL, fetches the current buffer contents. -static void buffer_send_lsp_did_open(TextBuffer *buffer, LSP *lsp, char *buffer_contents) { - if (!buffer_contents) - buffer_contents = buffer_contents_utf8_alloc(buffer); +static void buffer_send_lsp_did_open(TextBuffer *buffer, LSP *lsp) { + size_t buffer_contents_len = buffer_contents_utf8(buffer, NULL); LSPRequest request = {.type = LSP_REQUEST_DID_OPEN}; LSPRequestDidOpen *open = &request.data.open; - open->file_contents = buffer_contents; + char *contents = lsp_message_alloc_string(&request.base, buffer_contents_len, &open->file_contents); + buffer_contents_utf8(buffer, contents); open->document = lsp_document_id(lsp, buffer->path); open->language = buffer_language(buffer); lsp_send_request(lsp, &request); @@ -529,7 +526,7 @@ LSP *buffer_lsp(TextBuffer *buffer) { if (curr_lsp) buffer_send_lsp_did_close(buffer, curr_lsp, NULL); if (true_lsp) - buffer_send_lsp_did_open(buffer, true_lsp, NULL); + buffer_send_lsp_did_open(buffer, true_lsp); } buffer->last_lsp_check = buffer->ted->frame_time; return true_lsp; @@ -1798,13 +1795,11 @@ static void buffer_send_lsp_did_change(LSP *lsp, TextBuffer *buffer, BufferPos p u32 nchars_deleted, String32 new_text) { if (!buffer_is_named_file(buffer)) return; // this isn't a named buffer so we can't send a didChange request. - LSPDocumentChangeEvent event = {0}; - if (new_text.len > 0) - event.text = str32_to_utf8_cstr(new_text); - event.range.start = buffer_pos_to_lsp_position(buffer, pos); + LSPRange range = {0}; + range.start = buffer_pos_to_lsp_position(buffer, pos); BufferPos pos_end = buffer_pos_advance(buffer, pos, nchars_deleted); - event.range.end = buffer_pos_to_lsp_position(buffer, pos_end); - lsp_document_changed(lsp, buffer->path, event); + range.end = buffer_pos_to_lsp_position(buffer, pos_end); + lsp_document_changed(lsp, buffer->path, range, new_text); } BufferPos buffer_insert_text_at_pos(TextBuffer *buffer, BufferPos pos, String32 str) { diff --git a/ide-definitions.c b/ide-definitions.c index aab145a..79677e8 100644 --- a/ide-definitions.c +++ b/ide-definitions.c @@ -240,9 +240,8 @@ void definitions_send_request_if_needed(Ted *ted) { } LSPRequest request = {.type = LSP_REQUEST_WORKSPACE_SYMBOLS}; LSPRequestWorkspaceSymbols *syms = &request.data.workspace_symbols; - syms->query = str_dup(query); - // cancel old request - definition_cancel_lookup(ted); + syms->query = lsp_request_add_string(&request, query); + definition_cancel_lookup(ted); // cancel old request defs->last_request = lsp_send_request(lsp, &request); defs->last_request_time = ted->frame_time; free(defs->last_request_query); diff --git a/ide-rename-symbol.c b/ide-rename-symbol.c index ed7483c..b5208cc 100644 --- a/ide-rename-symbol.c +++ b/ide-rename-symbol.c @@ -26,7 +26,7 @@ void rename_symbol_at_cursor(Ted *ted, TextBuffer *buffer, const char *new_name) LSPRequest request = {.type = LSP_REQUEST_RENAME}; LSPRequestRename *data = &request.data.rename; data->position = buffer_cursor_pos_as_lsp_document_position(buffer); - data->new_name = str_dup(new_name); + data->new_name = lsp_request_add_string(&request, new_name); rs->request_id = lsp_send_request(lsp, &request); } @@ -119,12 +119,6 @@ void rename_symbol_process_lsp_response(Ted *ted, const LSPResponse *response) { return; } - if (response->error) { - ted_error(ted, "%s", response->error); - goto cleanup; - - } - const LSPResponseRename *data = &response->data.rename; LSP *lsp = ted_get_lsp_by_id(ted, rs->request_id.lsp); if (!lsp) { diff --git a/lsp-parse.c b/lsp-parse.c index c07679a..ace4881 100644 --- a/lsp-parse.c +++ b/lsp-parse.c @@ -32,19 +32,6 @@ static WarnUnusedResult bool lsp_expect_number(LSP *lsp, JSONValue value, const return lsp_expect_type(lsp, value, JSON_NUMBER, what); } - -static LSPString lsp_response_add_json_string(LSPResponse *response, const JSON *json, JSONString string) { - if (string.len == 0) { - return (LSPString){0}; - } - u32 offset = arr_len(response->string_data); - arr_set_len(response->string_data, offset + string.len + 1); - json_string_get(json, string, response->string_data + offset, string.len + 1); - return (LSPString){ - .offset = offset - }; -} - static int completion_qsort_cmp(void *context, const void *av, const void *bv) { const LSPResponse *response = context; const LSPCompletionItem *a = av, *b = bv; @@ -692,7 +679,7 @@ static WarnUnusedResult bool parse_id(const JSON *json, LSPRequest *request) { } } break; case JSON_STRING: - request->id_string = json_string_get_alloc(json, id_value.val.string); + request->id_string = lsp_request_add_json_string(request, json, id_value.val.string); return true; default: break; } @@ -700,7 +687,8 @@ static WarnUnusedResult bool parse_id(const JSON *json, LSPRequest *request) { } // handles window/showMessage, window/logMessage, and window/showMessageRequest parameters -static bool parse_window_message(LSP *lsp, const JSON *json, LSPRequestMessage *m) { +static bool parse_window_message(LSP *lsp, const JSON *json, LSPRequest *request) { + LSPRequestMessage *m = &request->data.message; JSONObject params = json_force_object(json_get(json, "params")); JSONValue type = json_object_get(json, params, "type"); JSONValue message = json_object_get(json, params, "message"); @@ -716,7 +704,7 @@ static bool parse_window_message(LSP *lsp, const JSON *json, LSPRequestMessage * } m->type = (LSPWindowMessageType)mtype; - m->message = json_string_get_alloc(json, message.val.string); + m->message = lsp_request_add_json_string(request, json, message.val.string); return true; } @@ -731,7 +719,7 @@ static bool parse_server2client_request(LSP *lsp, JSON *json, LSPRequest *reques if (streq(method, "window/showMessage")) { request->type = LSP_REQUEST_SHOW_MESSAGE; - return parse_window_message(lsp, json, &request->data.message); + return parse_window_message(lsp, json, request); } else if (streq(method, "window/showMessageRequest")) { // we'll deal with the repsonse right here LSPResponse response = {0}; @@ -744,10 +732,10 @@ static bool parse_server2client_request(LSP *lsp, JSON *json, LSPRequest *reques lsp_send_response(lsp, &response); request->type = LSP_REQUEST_SHOW_MESSAGE; - return parse_window_message(lsp, json, &request->data.message); + return parse_window_message(lsp, json, request); } else if (streq(method, "window/logMessage")) { request->type = LSP_REQUEST_LOG_MESSAGE; - return parse_window_message(lsp, json, &request->data.message); + return parse_window_message(lsp, json, request); } else if (streq(method, "workspace/workspaceFolders")) { // we can deal with this request right here LSPResponse response = {0}; @@ -988,14 +976,11 @@ void process_message(LSP *lsp, JSON *json) { // now response_to is response's responsibility memset(&response_to, 0, sizeof response_to); - // make sure (LSPString){0} gets treated as an empty string - arr_add(response.string_data, '\0'); - if (error.type == JSON_STRING) { - response.error = json_string_get_alloc(json, error.val.string); + response.error = lsp_response_add_json_string(&response, json, error.val.string); } - if (response.error) { + if (!lsp_string_is_empty(response.error)) { if (error_code != LSP_ERROR_REQUEST_CANCELLED) add_to_messages = true; } else switch (response.request.type) { @@ -1049,7 +1034,11 @@ void process_message(LSP *lsp, JSON *json) { LSPRequest configuration = { .type = LSP_REQUEST_CONFIGURATION }; - configuration.data.configuration.settings = lsp->configuration_to_send; + configuration.data.configuration.settings = lsp_request_add_string( + &configuration, + lsp->configuration_to_send + ); + free(lsp->configuration_to_send); lsp->configuration_to_send = NULL; lsp_send_request(lsp, &configuration); } @@ -1063,8 +1052,8 @@ void process_message(LSP *lsp, JSON *json) { if (add_to_messages) { SDL_LockMutex(lsp->messages_mutex); LSPMessage *message = arr_addp(lsp->messages_server2client); - message->type = LSP_RESPONSE; - message->u.response = response; + response.base.type = LSP_RESPONSE; + message->response = response; SDL_UnlockMutex(lsp->messages_mutex); } else { lsp_response_free(&response); @@ -1076,8 +1065,8 @@ void process_message(LSP *lsp, JSON *json) { if (parse_server2client_request(lsp, json, &request)) { SDL_LockMutex(lsp->messages_mutex); LSPMessage *message = arr_addp(lsp->messages_server2client); - message->type = LSP_REQUEST; - message->u.request = request; + request.base.type = LSP_REQUEST; + message->request = request; SDL_UnlockMutex(lsp->messages_mutex); } else { lsp_request_free(&request); diff --git a/lsp-write.c b/lsp-write.c index 85c5781..99a4e82 100644 --- a/lsp-write.c +++ b/lsp-write.c @@ -495,7 +495,7 @@ void write_request(LSP *lsp, LSPRequest *request) { write_key_file_uri(o, "uri", open->document); write_key_string(o, "languageId", lsp_language_id(open->language)); write_key_number(o, "version", 0); - write_key_string(o, "text", open->file_contents); + write_key_string(o, "text", lsp_request_string(request, open->file_contents)); write_obj_end(o); write_obj_end(o); } break; @@ -525,7 +525,7 @@ void write_request(LSP *lsp, LSPRequest *request) { write_arr_elem(o); write_obj_start(o); write_key_range(o, "range", event->range); - write_key_string(o, "text", event->text ? event->text : ""); + write_key_string(o, "text", lsp_request_string(request, event->text)); write_obj_end(o); } write_arr_end(o); @@ -598,13 +598,13 @@ void write_request(LSP *lsp, LSPRequest *request) { const LSPRequestRename *rename = &request->data.rename; write_key_obj_start(o, "params"); write_document_position(o, rename->position); - write_key_string(o, "newName", rename->new_name); + write_key_string(o, "newName", lsp_request_string(request, rename->new_name)); write_obj_end(o); } break; case LSP_REQUEST_WORKSPACE_SYMBOLS: { const LSPRequestWorkspaceSymbols *syms = &request->data.workspace_symbols; write_key_obj_start(o, "params"); - write_key_string(o, "query", syms->query); + write_key_string(o, "query", lsp_request_string(request, syms->query)); write_obj_end(o); } break; case LSP_REQUEST_DID_CHANGE_WORKSPACE_FOLDERS: { @@ -630,7 +630,7 @@ void write_request(LSP *lsp, LSPRequest *request) { const LSPRequestConfiguration *config = &request->data.configuration; write_key_obj_start(o, "params"); write_key(o, "settings"); - str_builder_append(&o->builder, config->settings); + str_builder_append(&o->builder, lsp_request_string(request, config->settings)); write_obj_end(o); } break; } @@ -657,8 +657,9 @@ static void write_response(LSP *lsp, LSPResponse *response) { LSPRequest *request = &response->request; write_obj_start(o); - if (request->id_string) - write_key_string(o, "id", request->id_string); + const char *id_string = lsp_request_string(request, request->id_string); + if (*id_string) + write_key_string(o, "id", id_string); else write_key_number(o, "id", request->id); write_key_string(o, "jsonrpc", "2.0"); @@ -686,10 +687,10 @@ static void write_response(LSP *lsp, LSPResponse *response) { void write_message(LSP *lsp, LSPMessage *message) { switch (message->type) { case LSP_REQUEST: - write_request(lsp, &message->u.request); + write_request(lsp, &message->request); break; case LSP_RESPONSE: - write_response(lsp, &message->u.response); + write_response(lsp, &message->response); break; } } diff --git a/lsp.c b/lsp.c index 77e1454..5944c29 100644 --- a/lsp.c +++ b/lsp.c @@ -29,13 +29,75 @@ bool lsp_get_error(LSP *lsp, char *error, size_t error_size, bool clear) { return has_err; } +bool lsp_string_is_empty(LSPString string) { + return string.offset == 0; +} + +char *lsp_message_alloc_string(LSPMessageBase *message, size_t len, LSPString *string) { + u32 offset = arr_len(message->string_data); + if (len == 0) { + offset = 0; + } else if (offset == 0) { + // reserve offset 0 for empty string + arr_add(message->string_data, 0); + offset = 1; + } + arr_set_len(message->string_data, offset + len + 1); + string->offset = offset; + return message->string_data + offset; +} + +static LSPString lsp_message_add_string(LSPMessageBase *message, const char *string) { + LSPString ret = {0}; + size_t len = strlen(string); + if (len == 0) { + return ret; + } + char *dest = lsp_message_alloc_string(message, len, &ret); + memcpy(dest, string, len); + return ret; +} +static LSPString lsp_message_add_json_string(LSPMessageBase *message, const JSON *json, JSONString string) { + LSPString ret = {0}; + size_t len = string.len; + if (len == 0) { + return ret; + } + char *dest = lsp_message_alloc_string(message, len, &ret); + json_string_get(json, string, dest, len + 1); + return ret; +} +static LSPString lsp_message_add_string32(LSPMessageBase *message, String32 string) { + LSPString ret = {0}; + size_t len32 = string.len; + if (len32 == 0) { + return ret; + } + char *dest = lsp_message_alloc_string(message, len32 * 4 + 1, &ret); + str32_to_utf8_cstr_in_place(string, dest); + return ret; +} + +LSPString lsp_request_add_string(LSPRequest *request, const char *string) { + return lsp_message_add_string(&request->base, string); +} +LSPString lsp_response_add_string(LSPResponse *response, const char *string) { + return lsp_message_add_string(&response->base, string); +} +LSPString lsp_response_add_json_string(LSPResponse *response, const JSON *json, JSONString string) { + return lsp_message_add_json_string(&response->base, json, string); +} +LSPString lsp_request_add_json_string(LSPRequest *request, const JSON *json, JSONString string) { + return lsp_message_add_json_string(&request->base, json, string); +} + -static void lsp_document_change_event_free(LSPDocumentChangeEvent *event) { - free(event->text); +static void lsp_message_base_free(LSPMessageBase *base) { + arr_free(base->string_data); } void lsp_request_free(LSPRequest *r) { - free(r->id_string); + lsp_message_base_free(&r->base); switch (r->type) { case LSP_REQUEST_NONE: case LSP_REQUEST_INITIALIZE: @@ -55,23 +117,15 @@ void lsp_request_free(LSPRequest *r) { case LSP_REQUEST_DID_CLOSE: case LSP_REQUEST_WORKSPACE_FOLDERS: case LSP_REQUEST_DOCUMENT_LINK: - break; - case LSP_REQUEST_CONFIGURATION: { - LSPRequestConfiguration *config = &r->data.configuration; - free(config->settings); - } break; - case LSP_REQUEST_DID_OPEN: { - LSPRequestDidOpen *open = &r->data.open; - free(open->file_contents); - } break; + case LSP_REQUEST_CONFIGURATION: + case LSP_REQUEST_DID_OPEN: case LSP_REQUEST_SHOW_MESSAGE: case LSP_REQUEST_LOG_MESSAGE: - free(r->data.message.message); + case LSP_REQUEST_RENAME: + case LSP_REQUEST_WORKSPACE_SYMBOLS: break; case LSP_REQUEST_DID_CHANGE: { LSPRequestDidChange *c = &r->data.change; - arr_foreach_ptr(c->changes, LSPDocumentChangeEvent, event) - lsp_document_change_event_free(event); arr_free(c->changes); } break; case LSP_REQUEST_DID_CHANGE_WORKSPACE_FOLDERS: { @@ -79,18 +133,12 @@ void lsp_request_free(LSPRequest *r) { arr_free(w->added); arr_free(w->removed); } break; - case LSP_REQUEST_RENAME: - free(r->data.rename.new_name); - break; - case LSP_REQUEST_WORKSPACE_SYMBOLS: - free(r->data.workspace_symbols.query); - break; } memset(r, 0, sizeof *r); } void lsp_response_free(LSPResponse *r) { - arr_free(r->string_data); + lsp_message_base_free(&r->base); switch (r->request.type) { case LSP_REQUEST_COMPLETION: arr_free(r->data.completion.items); @@ -120,17 +168,16 @@ void lsp_response_free(LSPResponse *r) { break; } lsp_request_free(&r->request); - free(r->error); memset(r, 0, sizeof *r); } void lsp_message_free(LSPMessage *message) { switch (message->type) { case LSP_REQUEST: - lsp_request_free(&message->u.request); + lsp_request_free(&message->request); break; case LSP_RESPONSE: - lsp_response_free(&message->u.response); + lsp_response_free(&message->response); break; } memset(message, 0, sizeof *message); @@ -254,8 +301,9 @@ LSPServerRequestID lsp_send_request(LSP *lsp, LSPRequest *request) { bool is_notification = request_type_is_notification(request->type); if (!is_notification) request->id = get_request_id(); - LSPMessage message = {.type = LSP_REQUEST}; - message.u.request = *request; + LSPMessage message = {0}; + request->base.type = LSP_REQUEST; + message.request = *request; lsp_send_message(lsp, &message); return (LSPServerRequestID) { .lsp = lsp->id, @@ -264,14 +312,28 @@ LSPServerRequestID lsp_send_request(LSP *lsp, LSPRequest *request) { } void lsp_send_response(LSP *lsp, LSPResponse *response) { - LSPMessage message = {.type = LSP_RESPONSE}; - message.u.response = *response; + LSPMessage message = {0}; + response->base.type = LSP_RESPONSE; + message.response = *response; lsp_send_message(lsp, &message); } +static const char *lsp_message_string(const LSPMessageBase *message, LSPString string) { + // important that we have this check here, since + // it's possible that message->string_data is NULL (i.e. if no strings were ever added) + if (string.offset == 0) { + return ""; + } + assert(string.offset < arr_len(message->string_data)); + return &message->string_data[string.offset]; +} + const char *lsp_response_string(const LSPResponse *response, LSPString string) { - assert(string.offset < arr_len(response->string_data)); - return &response->string_data[string.offset]; + return lsp_message_string(&response->base, string); +} + +const char *lsp_request_string(const LSPRequest *request, LSPString string) { + return lsp_message_string(&request->base, string); } // receive responses/requests/notifications from LSP, up to max_size bytes. @@ -652,9 +714,13 @@ void lsp_free(LSP *lsp) { free(lsp); } -void lsp_document_changed(LSP *lsp, const char *document, LSPDocumentChangeEvent change) { +void lsp_document_changed(LSP *lsp, const char *document, LSPRange range, String32 new_text) { // @TODO(optimization, eventually): batch changes (using the contentChanges array) LSPRequest request = {.type = LSP_REQUEST_DID_CHANGE}; + LSPDocumentChangeEvent change = { + .range = range, + .text = lsp_message_add_string32(&request.base, new_text), + }; LSPRequestDidChange *c = &request.data.change; c->document = lsp_document_id(lsp, document); arr_add(c->changes, change); @@ -734,7 +800,7 @@ void lsp_cancel_request(LSP *lsp, LSPRequestID id) { for (u32 i = 0; i < arr_len(lsp->messages_client2server); ++i) { LSPMessage *message = &lsp->messages_client2server[i]; - if (message->type == LSP_REQUEST && message->u.request.id == id) { + if (message->type == LSP_REQUEST && message->request.id == id) { // we haven't sent this request yet arr_remove(lsp->messages_client2server, i); break; diff --git a/lsp.h b/lsp.h index ab095f5..a03256d 100644 --- a/lsp.h +++ b/lsp.h @@ -9,6 +9,7 @@ #include "base.h" #include "ds.h" #include "os.h" +#include "util.h" /// an ID specific to a path. a document's ID is never 0 (thanks to lsp_create). typedef u32 LSPDocumentID; @@ -116,8 +117,7 @@ typedef struct { typedef struct { u64 language; LSPDocumentID document; - // freed by \ref lsp_request_free - char *file_contents; + LSPString file_contents; } LSPRequestDidOpen; typedef struct { @@ -127,8 +127,8 @@ typedef struct { // see TextDocumentContentChangeEvent in the LSP spec typedef struct { LSPRange range; - /// new text. will be freed. you can use NULL for the empty string. - char *text; + /// new text. + LSPString text; } LSPDocumentChangeEvent; typedef struct { @@ -145,8 +145,7 @@ typedef enum { typedef struct { LSPWindowMessageType type; - // freed by \ref lsp_request_free - char *message; + LSPString message; } LSPRequestMessage; @@ -197,12 +196,12 @@ typedef struct { typedef struct { // string to filter the symbols by - char *query; + LSPString query; } LSPRequestWorkspaceSymbols; typedef struct { LSPDocumentPosition position; - char *new_name; + LSPString new_name; } LSPRequestRename; typedef struct { @@ -211,14 +210,23 @@ typedef struct { } LSPRequestDidChangeWorkspaceFolders; typedef struct { - // this string should be valid JSON. will be freed. - char *settings; + // this string should be valid JSON. + LSPString settings; } LSPRequestConfiguration; typedef struct { + LSPMessageType type; + /// LSP requests/responses tend to have a lot of strings. + /// to avoid doing a ton of allocations+frees, + /// they're all stored here. + char *string_data; +} LSPMessageBase; + +typedef struct { + LSPMessageBase base; LSPRequestID id; LSPRequestType type; - char *id_string; // if not NULL, this is the ID (only for server-to-client messages; we always use integer IDs) + LSPString id_string; // if non-empty, this is the ID (only for server-to-client messages; we always use integer IDs) // one member of this union is set depending on `type`. union { LSPRequestCancel cancel; @@ -491,14 +499,11 @@ typedef struct { } LSPResponseDocumentLink; typedef struct { + LSPMessageBase base; /// the request which this is a response to LSPRequest request; /// if not NULL, the data field will just be zeroed - char *error; - /// LSP responses tend to have a lot of strings. - /// to avoid doing a ton of allocations+frees, - /// they're all stored here. - char *string_data; + LSPString error; /// one of these is filled based on request.type union { LSPResponseCompletion completion; @@ -514,12 +519,13 @@ typedef struct { } data; } LSPResponse; -typedef struct { +// *technically* using unions this way is UB, +// but Xlib/SDL/others do it so compilers've gotta deal with it. +typedef union { LSPMessageType type; - union { - LSPRequest request; - LSPResponse response; - } u; + LSPMessageBase base; + LSPRequest request; + LSPResponse response; } LSPMessage; typedef struct { @@ -629,8 +635,18 @@ void lsp_cancel_request(LSP *lsp, LSPRequestID id); // don't free the contents of this response! let me handle it! void lsp_send_response(LSP *lsp, LSPResponse *response); const char *lsp_response_string(const LSPResponse *response, LSPString string); -// Start up an LSP server. -// configuration and log can be NULL. +const char *lsp_request_string(const LSPRequest *request, LSPString string); +/// low-level API for allocating message strings. +/// +/// sets `*string` to the LSPString, and returns a pointer which you can write the string to. +/// the returned pointer will be zeroed up to and including [len]. +char *lsp_message_alloc_string(LSPMessageBase *message, size_t len, LSPString *string); +LSPString lsp_request_add_string(LSPRequest *request, const char *string); +LSPString lsp_response_add_string(LSPResponse *response, const char *string); +bool lsp_string_is_empty(LSPString string); +/// Start up an LSP server. +/// +/// configuration and log can be NULL. LSP *lsp_create(const char *root_dir, const char *command, const char *configuration, FILE *log); // try to add a new "workspace folder" to the lsp. // IMPORTANT: only call this if lsp->initialized is true @@ -640,7 +656,7 @@ LSP *lsp_create(const char *root_dir, const char *command, const char *configura // with root directory `new_root_dir`. bool lsp_try_add_root_dir(LSP *lsp, const char *new_root_dir); // report that this document has changed -void lsp_document_changed(LSP *lsp, const char *document, LSPDocumentChangeEvent change); +void lsp_document_changed(LSP *lsp, const char *document, LSPRange range, String32 new_text); // is this path in the LSP's workspace folders? bool lsp_covers_path(LSP *lsp, const char *path); // get next message from server @@ -775,6 +791,8 @@ char *json_string_get_alloc(const JSON *json, JSONString string); void json_debug_print(const JSON *json); size_t json_escape_to(char *out, size_t out_sz, const char *in); char *json_escape(const char *str); +LSPString lsp_response_add_json_string(LSPResponse *response, const JSON *json, JSONString string); +LSPString lsp_request_add_json_string(LSPRequest *request, const JSON *json, JSONString string); /// free resources used by lsp-write.c void lsp_write_quit(void); diff --git a/main.c b/main.c index e6bd506..1bb1163 100644 --- a/main.c +++ b/main.c @@ -874,37 +874,36 @@ int main(int argc, char **argv) { while (lsp_next_message(lsp, &message)) { switch (message.type) { case LSP_REQUEST: { - LSPRequest *r = &message.u.request; + LSPRequest *r = &message.request; switch (r->type) { case LSP_REQUEST_SHOW_MESSAGE: { LSPRequestMessage *m = &r->data.message; MessageType type = ted_message_type_from_lsp(m->type); - ted_set_message(ted, type, "%s", m->message); + ted_set_message(ted, type, "%s", lsp_request_string(r, m->message)); } break; case LSP_REQUEST_LOG_MESSAGE: { LSPRequestMessage *m = &r->data.message; - ted_log(ted, "%s\n", m->message); + ted_log(ted, "%s\n", lsp_request_string(r, m->message)); } break; default: break; } } break; case LSP_RESPONSE: { - LSPResponse *r = &message.u.response; - if (r->error) { - // not displaying this right now - // idk it might be spammy - //ted_error(ted, "%s", r->error); + LSPResponse *r = &message.response; + if (!lsp_string_is_empty(r->error)) { + ted_error(ted, "LSP error: %s", lsp_response_string(r, r->error)); + } else { + // it's important that we send error responses here too. + // we don't want to be waiting around for a response that's never coming. + autocomplete_process_lsp_response(ted, r); + signature_help_process_lsp_response(ted, r); + hover_process_lsp_response(ted, r); + definitions_process_lsp_response(ted, lsp, r); + highlights_process_lsp_response(ted, r); + usages_process_lsp_response(ted, r); + document_link_process_lsp_response(ted, r); + rename_symbol_process_lsp_response(ted, r); } - // it's important that we send error responses here too. - // we don't want to be waiting around for a response that's never coming. - autocomplete_process_lsp_response(ted, r); - signature_help_process_lsp_response(ted, r); - hover_process_lsp_response(ted, r); - definitions_process_lsp_response(ted, lsp, r); - highlights_process_lsp_response(ted, r); - usages_process_lsp_response(ted, r); - document_link_process_lsp_response(ted, r); - rename_symbol_process_lsp_response(ted, r); } break; } lsp_message_free(&message); diff --git a/util.c b/util.c index b146c82..1b9b9f3 100644 --- a/util.c +++ b/util.c @@ -1018,24 +1018,27 @@ String32 str32_from_utf8(const char *utf8) { return string; } -// returns a null-terminated UTF-8 string -// the string returned should be free'd -// this will return NULL on failure + +bool str32_to_utf8_cstr_in_place(String32 s, char *out) { + char *p = out; + for (size_t i = 0; i < s.len; ++i) { + size_t bytes = unicode_utf32_to_utf8(p, s.str[i]); + if (bytes == (size_t)-1) { + // invalid UTF-32 code point + *p = '\0'; + return false; + } else { + p += bytes; + } + } + *p = '\0'; + return true; +} + char *str32_to_utf8_cstr(String32 s) { char *utf8 = calloc(4 * s.len + 1, 1); // each codepoint takes up at most 4 bytes in UTF-8, + we need a terminating null byte if (utf8) { - char *p = utf8; - for (size_t i = 0; i < s.len; ++i) { - size_t bytes = unicode_utf32_to_utf8(p, s.str[i]); - if (bytes == (size_t)-1) { - // invalid UTF-32 code point - free(utf8); - return NULL; - } else { - p += bytes; - } - } - *p = '\0'; + str32_to_utf8_cstr_in_place(s, utf8); } return utf8; } diff --git a/util.h b/util.h index b3d4fba..25ff72b 100644 --- a/util.h +++ b/util.h @@ -259,6 +259,18 @@ String32 str32(char32_t *str, size_t len); String32 str32_substr(String32 s, size_t from, size_t len); void str32_free(String32 *s); String32 str32_from_utf8(const char *utf8); +/// convert UTF-32 to UTF-8. +/// +/// returns false on invalid UTF-32 (and `out` will be truncated to just +/// the prefix of `s` that is valid UTF-32) +/// +/// out buffer must be long enough to fit the UTF-8 representation of `s`! +/// (`s.len * 4 + 1` is a safe buffer size) +bool str32_to_utf8_cstr_in_place(String32 s, char *out); +/// returns a null-terminated UTF-8 string +/// +/// the string returned should be free'd +/// this will return NULL on failure char *str32_to_utf8_cstr(String32 s); int str32_cmp_ascii(String32 s, const char *ascii); bool str32_has_ascii_prefix(String32 s, const char *ascii); -- cgit v1.2.3