summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buffer.c23
-rw-r--r--ide-definitions.c5
-rw-r--r--ide-rename-symbol.c8
-rw-r--r--lsp-parse.c47
-rw-r--r--lsp-write.c19
-rw-r--r--lsp.c132
-rw-r--r--lsp.h66
-rw-r--r--main.c35
-rw-r--r--util.c33
-rw-r--r--util.h12
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);