summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buffer.c47
-rw-r--r--lsp-write-request.c51
-rw-r--r--lsp.c2
-rw-r--r--lsp.h4
-rw-r--r--main.c2
5 files changed, 78 insertions, 28 deletions
diff --git a/buffer.c b/buffer.c
index 619842b..6ee76d9 100644
--- a/buffer.c
+++ b/buffer.c
@@ -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);
diff --git a/lsp.c b/lsp.c
index b84abe2..e9654cb 100644
--- a/lsp.c
+++ b/lsp.c
@@ -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) {
diff --git a/lsp.h b/lsp.h
index 20fdf2d..d8604b4 100644
--- a/lsp.h
+++ b/lsp.h
@@ -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;
diff --git a/main.c b/main.c
index 2c0a18d..ae4b688 100644
--- a/main.c
+++ b/main.c
@@ -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,