diff options
-rw-r--r-- | buffer.c | 47 | ||||
-rw-r--r-- | lsp-write-request.c | 51 | ||||
-rw-r--r-- | lsp.c | 2 | ||||
-rw-r--r-- | lsp.h | 4 | ||||
-rw-r--r-- | main.c | 2 |
5 files changed, 78 insertions, 28 deletions
@@ -67,6 +67,10 @@ bool buffer_is_untitled(TextBuffer *buffer) { return false; } +bool buffer_is_named_file(TextBuffer *buffer) { + return buffer->filename && !buffer_is_untitled(buffer); +} + // add this edit to the undo history static void buffer_append_edit(TextBuffer *buffer, BufferEdit const *edit) { // whenever an edit is made, clear the redo history @@ -245,6 +249,8 @@ static inline Font *buffer_font(TextBuffer *buffer) { // what programming language is this? Language buffer_language(TextBuffer *buffer) { + // @TODO: cache this? + // (we're calling buffer_lsp on every edit and that calls this) if (buffer->manual_language >= 1 && buffer->manual_language <= LANG_COUNT) return (Language)(buffer->manual_language - 1); Settings const *settings = buffer->ted->default_settings; // important we don't use buffer_settings here since that would cause a loop! @@ -280,6 +286,11 @@ Language buffer_language(TextBuffer *buffer) { return match; } +LSP *buffer_lsp(TextBuffer *buffer) { + return ted_get_lsp(buffer->ted, buffer_language(buffer)); +} + + // score is higher if context is closer match. static long context_score(const char *path, Language lang, const SettingsContext *context) { long score = 0; @@ -545,6 +556,10 @@ void buffer_check_valid(TextBuffer *buffer) { } } #else +static void buffer_pos_check_valid(TextBuffer *buffer, BufferPos p) { + (void)buffer; (void)p; +} + void buffer_check_valid(TextBuffer *buffer) { (void)buffer; } @@ -1370,10 +1385,34 @@ static Status buffer_insert_lines(TextBuffer *buffer, u32 where, u32 number) { return false; } +// LSP uses UTF-16 indices because Microsoft fucking loves UTF-16 and won't let it die +static LSPPosition buffer_pos_to_lsp(TextBuffer *buffer, BufferPos pos) { + LSPPosition lsp_pos = { + .line = pos.line + }; + buffer_pos_validate(buffer, &pos); + const Line *line = &buffer->lines[pos.line]; + const char32_t *str = line->str; + for (uint32_t i = 0; i < pos.index; ++i) { + if (str[i] < 0x10000) + lsp_pos.character += 1; // this codepoint needs 1 UTF-16 word + else + lsp_pos.character += 2; // this codepoint needs 2 UTF-16 words + } + return lsp_pos; +} + static void buffer_send_lsp_did_change_request(LSP *lsp, TextBuffer *buffer, BufferPos pos, u32 nchars_deleted, String32 new_text) { - // @TODO - abort(); + 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(buffer, pos); + BufferPos pos_end = buffer_pos_advance(buffer, pos, nchars_deleted); + event.range.end = buffer_pos_to_lsp(buffer, pos_end); + lsp_document_changed(lsp, buffer->filename, event); } // inserts the given text, returning the position of the end of the text @@ -2054,10 +2093,6 @@ void buffer_paste(TextBuffer *buffer) { } } -LSP *buffer_lsp(TextBuffer *buffer) { - return ted_get_lsp(buffer->ted, buffer_language(buffer)); -} - // if an error occurs, buffer is left untouched (except for the error field) and the function returns false. Status buffer_load_file(TextBuffer *buffer, char const *filename) { FILE *fp = fopen(filename, "rb"); diff --git a/lsp-write-request.c b/lsp-write-request.c index b7ce148..49db0e4 100644 --- a/lsp-write-request.c +++ b/lsp-write-request.c @@ -66,8 +66,9 @@ static void write_arr_end(JSONWriter *o) { static void write_arr_elem(JSONWriter *o) { if (o->is_first) { - str_builder_append(&o->builder, ","); o->is_first = false; + } else { + str_builder_append(&o->builder, ","); } } @@ -178,11 +179,25 @@ static const char *lsp_request_method(LSPRequest *request) { return "$/ignore"; } -// technically there are "requests" and "notifications" -// notifications are different in that they don't have IDs and don't return responses. -// this function handles both. -// NOTE: do not call lsp_request_free on request. freeing the request will be handled. -// returns the ID of the request +static bool request_type_is_notification(LSPRequestType type) { + switch (type) { + case LSP_REQUEST_NONE: break; + case LSP_REQUEST_INITIALIZED: + case LSP_REQUEST_EXIT: + case LSP_REQUEST_DID_OPEN: + case LSP_REQUEST_DID_CHANGE: + return true; + case LSP_REQUEST_INITIALIZE: + case LSP_REQUEST_SHUTDOWN: + case LSP_REQUEST_SHOW_MESSAGE: + case LSP_REQUEST_LOG_MESSAGE: + case LSP_REQUEST_COMPLETION: + return false; + } + assert(0); + return false; +} + static void write_request(LSP *lsp, LSPRequest *request) { JSONWriter writer = json_writer_new(); JSONWriter *o = &writer; @@ -194,9 +209,7 @@ static void write_request(LSP *lsp, LSPRequest *request) { write_obj_start(o); write_key_string(o, "jsonrpc", "2.0"); - bool is_notification = request->type == LSP_REQUEST_INITIALIZED - || request->type == LSP_REQUEST_EXIT - || request->type == LSP_REQUEST_DID_OPEN; + bool is_notification = request_type_is_notification(request->type); if (!is_notification) { u32 id = lsp->request_id++; request->id = id; @@ -244,23 +257,23 @@ static void write_request(LSP *lsp, LSPRequest *request) { write_key_obj_start(o, "textDocument"); write_key_number(o, "version", (double)version_number); write_key_file_uri(o, "uri", change->document); - write_key_arr_start(o, "contentChanges"); - arr_foreach_ptr(change->changes, LSPDocumentChangeEvent, event) { - write_arr_elem(o); - write_obj_start(o); - write_key_range(o, "range", event->range); - write_key_string(o, "text", event->text); - write_obj_end(o); - } - write_arr_end(o); write_obj_end(o); + write_key_arr_start(o, "contentChanges"); + arr_foreach_ptr(change->changes, LSPDocumentChangeEvent, event) { + 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_obj_end(o); + } + write_arr_end(o); write_obj_end(o); } break; 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.path); + write_key_file_uri(o, "uri", completion->position.document); write_obj_end(o); write_key_position(o, "position", completion->position.pos); write_obj_end(o); @@ -21,7 +21,7 @@ bool lsp_get_error(LSP *lsp, char *error, size_t error_size, bool clear) { static void lsp_document_position_free(LSPDocumentPosition *position) { - free(position->path); + free(position->document); } static void lsp_document_change_event_free(LSPDocumentChangeEvent *event) { @@ -1,6 +1,7 @@ // @TODO: // - use document IDs instead of strings (also lets us use real document version numbers) // - document this and lsp.c. +// - deal with "Save as" (generate didOpen) // - maximum queue size for requests/responses just in case? // - delete old sent requests // (if the server never sends a response) @@ -55,6 +56,7 @@ 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; } LSPDocumentChangeEvent; @@ -78,7 +80,7 @@ typedef struct { typedef struct { // freed by lsp_request_free - char *path; + char *document; LSPPosition pos; } LSPDocumentPosition; @@ -310,7 +310,7 @@ int main(int argc, char **argv) { LSPRequest test_req = {.type = LSP_REQUEST_COMPLETION}; test_req.data.completion = (LSPRequestCompletion){ .position = { - .path = str_dup("/p/test-lsp/src/main.rs"), + .document = str_dup("/p/test-lsp/src/main.rs"), .pos = { .line = 2, .character = 2, |