summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buffer.c103
-rw-r--r--lsp.c6
-rw-r--r--lsp.h2
-rw-r--r--main.c5
-rw-r--r--ted.c10
-rw-r--r--ted.h7
6 files changed, 91 insertions, 42 deletions
diff --git a/buffer.c b/buffer.c
index 298e8b6..49d63a4 100644
--- a/buffer.c
+++ b/buffer.c
@@ -249,7 +249,7 @@ static inline Font *buffer_font(TextBuffer *buffer) {
// what programming language is this?
Language buffer_language(TextBuffer *buffer) {
- // @TODO: cache this?
+ // @TODO(optimization): 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);
@@ -286,10 +286,43 @@ Language buffer_language(TextBuffer *buffer) {
return match;
}
+// set filename = NULL to default to buffer->filename
+static void buffer_send_lsp_did_close(TextBuffer *buffer, LSP *lsp, const char *filename) {
+ LSPRequest did_close = {.type = LSP_REQUEST_DID_CLOSE};
+ did_close.data.close = (LSPRequestDidClose){
+ .document = lsp_document_id(lsp, filename ? filename : buffer->filename)
+ };
+ lsp_send_request(lsp, &did_close);
+ 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);
+ LSPRequest request = {.type = LSP_REQUEST_DID_OPEN};
+ LSPRequestDidOpen *open = &request.data.open;
+ open->file_contents = buffer_contents;
+ open->document = lsp_document_id(lsp, buffer->filename);
+ open->language = buffer_language(buffer);
+ lsp_send_request(lsp, &request);
+ buffer->lsp_opened_in = lsp->id;
+}
+
LSP *buffer_lsp(TextBuffer *buffer) {
if (!buffer_is_named_file(buffer))
return NULL;
- return ted_get_lsp(buffer->ted, buffer->filename, buffer_language(buffer));
+ LSP *true_lsp = ted_get_lsp(buffer->ted, buffer->filename, buffer_language(buffer));
+ LSP *curr_lsp = ted_get_lsp_by_id(buffer->ted, buffer->lsp_opened_in);
+ if (true_lsp != curr_lsp) {
+ if (curr_lsp)
+ buffer_send_lsp_did_close(buffer, curr_lsp, NULL);
+ if (true_lsp)
+ buffer_send_lsp_did_open(buffer, true_lsp, NULL);
+ }
+ return true_lsp;
}
@@ -410,11 +443,11 @@ char *buffer_get_utf8_text_at_pos(TextBuffer *buffer, BufferPos pos, size_t ncha
}
// Puts a UTF-8 string containing the contents of the buffer into out.
-// Returns the number of bytes.
+// Returns the number of bytes, including a null terminator.
// To use, first pass NULL for out to get the number of bytes you need to allocate.
size_t buffer_contents_utf8(TextBuffer *buffer, char *out) {
- size_t size = 0;
char *p = out, x[4];
+ size_t size = 0;
for (Line *line = buffer->lines, *end = line + buffer->nlines; line != end; ++line) {
char32_t *str = line->str;
for (u32 i = 0, len = line->len; i < len; ++i) {
@@ -428,9 +461,21 @@ size_t buffer_contents_utf8(TextBuffer *buffer, char *out) {
size += 1;
}
}
+ if (p) *p = '\0';
+ size += 1;
return size;
}
+// Returns a UTF-8 string containing the contents of `buffer`.
+// free the return value.
+char *buffer_contents_utf8_alloc(TextBuffer *buffer) {
+ size_t size = buffer_contents_utf8(buffer, NULL);
+ char *s = calloc(1, size);
+ buffer_contents_utf8(buffer, s);
+ return s;
+}
+
+
static BufferPos buffer_pos_advance(TextBuffer *buffer, BufferPos pos, size_t nchars) {
buffer_pos_validate(buffer, &pos);
size_t chars_left = nchars;
@@ -739,11 +784,7 @@ void buffer_free(TextBuffer *buffer) {
LSP *lsp = buffer_lsp(buffer);
if (lsp) {
- LSPRequest did_close = {.type = LSP_REQUEST_DID_CLOSE};
- did_close.data.close = (LSPRequestDidClose){
- .document = lsp_document_id(lsp, buffer->filename)
- };
- lsp_send_request(lsp, &did_close);
+ buffer_send_lsp_did_close(buffer, lsp, NULL);
}
}
@@ -1395,7 +1436,7 @@ LSPPosition buffer_pos_to_lsp(TextBuffer *buffer, BufferPos pos) {
return lsp_pos;
}
-static void buffer_send_lsp_did_change_request(LSP *lsp, TextBuffer *buffer, BufferPos pos,
+static void buffer_send_lsp_did_change(LSP *lsp, TextBuffer *buffer, BufferPos pos,
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.
@@ -1460,7 +1501,7 @@ BufferPos buffer_insert_text_at_pos(TextBuffer *buffer, BufferPos pos, String32
LSP *lsp = buffer_lsp(buffer);
if (lsp)
- buffer_send_lsp_did_change_request(lsp, buffer, pos, 0, str);
+ buffer_send_lsp_did_change(lsp, buffer, pos, 0, str);
if (buffer->store_undo_events) {
BufferEdit *last_edit = arr_lastp(buffer->undo_history);
@@ -1732,7 +1773,7 @@ void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, i64 nchars_)
LSP *lsp = buffer_lsp(buffer);
if (lsp)
- buffer_send_lsp_did_change_request(lsp, buffer, pos, nchars, (String32){0});
+ buffer_send_lsp_did_change(lsp, buffer, pos, nchars, (String32){0});
if (buffer->store_undo_events) {
// we need to make sure the undo history keeps track of the edit.
@@ -2213,17 +2254,8 @@ Status buffer_load_file(TextBuffer *buffer, char const *filename) {
buffer->view_only = true;
}
- LSP *lsp = buffer_lsp(buffer);
- if (lsp) {
- // send didOpen
- LSPRequest request = {.type = LSP_REQUEST_DID_OPEN};
- LSPRequestDidOpen *open = &request.data.open;
- open->file_contents = (char *)file_contents;
- open->document = lsp_document_id(lsp, filename);
- open->language = buffer_language(buffer);
- lsp_send_request(lsp, &request);
- file_contents = NULL; // don't free
- }
+ // this will send a didOpen request if needed
+ buffer_lsp(buffer);
}
}
@@ -2344,24 +2376,24 @@ bool buffer_save(TextBuffer *buffer) {
// save, but with a different file name
bool buffer_save_as(TextBuffer *buffer, char const *new_filename) {
+ LSP *lsp = buffer_lsp(buffer);
char *prev_filename = buffer->filename;
- if ((buffer->filename = buffer_strdup(buffer, new_filename))) {
+ buffer->filename = buffer_strdup(buffer, new_filename);
+
+ if (buffer->filename && buffer_save(buffer)) {
buffer->view_only = false;
-
// ensure whole file is syntax highlighted when saving with a different
// file extension
buffer->frame_earliest_line_modified = 0;
buffer->frame_latest_line_modified = buffer->nlines - 1;
-
- if (buffer_save(buffer)) {
- free(prev_filename);
- return true;
- } else {
- free(buffer->filename);
- buffer->filename = prev_filename;
- return false;
- }
+ if (lsp)
+ buffer_send_lsp_did_close(buffer, lsp, prev_filename);
+ // we'll send a didOpen the next time buffer_lsp is called.
+ free(prev_filename);
+ return true;
} else {
+ free(buffer->filename);
+ buffer->filename = prev_filename;
return false;
}
}
@@ -2430,6 +2462,9 @@ bool buffer_handle_click(Ted *ted, TextBuffer *buffer, v2 click, u8 times) {
// Render the text buffer in the given rectangle
void buffer_render(TextBuffer *buffer, Rect r) {
+
+ buffer_lsp(buffer); // this will send didOpen/didClose if the buffer's LSP changed
+
if (r.size.x < 1 || r.size.y < 1) {
// rectangle less than 1 pixel
// set x1,y1,x2,y2 to an size 0 rectangle
diff --git a/lsp.c b/lsp.c
index a3e07e7..025cc10 100644
--- a/lsp.c
+++ b/lsp.c
@@ -1,8 +1,7 @@
// print server-to-client communication
#define LSP_SHOW_S2C 0
// print client-to-server communication
-#define LSP_SHOW_C2S 1
-
+#define LSP_SHOW_C2S 0
#define write_bool lsp_write_bool
@@ -325,6 +324,9 @@ LSP *lsp_create(const char *root_dir, Language language, const char *analyzer_co
LSP *lsp = calloc(1, sizeof *lsp);
if (!lsp) return NULL;
+ static _Atomic LSPID curr_id = 1;
+ lsp->id = curr_id++;
+
#if DEBUG
printf("Starting up LSP %p `%s` for language %s in %s\n",
(void *)lsp, analyzer_command, language_to_str(language), root_dir);
diff --git a/lsp.h b/lsp.h
index e0595e2..c3770bb 100644
--- a/lsp.h
+++ b/lsp.h
@@ -1,4 +1,5 @@
typedef u32 LSPDocumentID;
+typedef u32 LSPID;
typedef enum {
LSP_REQUEST,
@@ -270,6 +271,7 @@ typedef struct {
} LSPCapabilities;
typedef struct LSP {
+ LSPID id;
Process process;
u32 request_id;
// for our purposes, folders are "documents"
diff --git a/main.c b/main.c
index e82d539..350e717 100644
--- a/main.c
+++ b/main.c
@@ -1,8 +1,5 @@
/*
@TODO:
-- LSP IDs, and make buffer send (didClose +) didOpen if its ID isn't maching up
- (this should fix current "unexpected didChange" multi-root rust-analyzer problem and
- also fix "save as")
- lsp_document_id / lsp_document_path thread-safety
- double check thread safety of other things
- ignore telemetry/event
@@ -19,7 +16,7 @@
- go to definition using LSP
- find usages
- that thing where it shows you the current function argument
-- workspaceFolders support (so we don't need to start up multiple instances of rust-analyzer)
+- do something with lsp->error
- document lsp.h and lsp.c.
- maximum queue size for requests/responses just in case?
- idea: configurable timeout
diff --git a/ted.c b/ted.c
index 825ab36..75a2246 100644
--- a/ted.c
+++ b/ted.c
@@ -95,6 +95,14 @@ Settings *ted_get_settings(Ted *ted, const char *path, Language language) {
return settings;
}
+LSP *ted_get_lsp_by_id(Ted *ted, LSPID id) {
+ for (int i = 0; ted->lsps[i]; ++i) {
+ if (ted->lsps[i]->id == id)
+ return ted->lsps[i];
+ }
+ return NULL;
+}
+
// IMPORTANT NOTE ABOUT CACHING LSPs:
// - be careful if you want to cache LSPs, e.g. adding a LSP *lsp member to `buffer`.
// - right now we pretend that the server has workspace folder support until the initialize response is sent.
@@ -134,7 +142,7 @@ LSP *ted_get_lsp(Ted *ted, const char *path, Language language) {
char *root_dir = settings_get_root_dir(settings, path);
ted->lsps[i] = lsp_create(root_dir, language, settings->lsp);
free(root_dir);
- return ted->lsps[i];
+ // don't actually return it yet, since it's still initializing (see above)
}
return NULL;
diff --git a/ted.h b/ted.h
index 13e3331..a33a1eb 100644
--- a/ted.h
+++ b/ted.h
@@ -274,6 +274,9 @@ typedef struct {
float x1, y1, x2, y2;
u32 nlines;
u32 lines_capacity;
+
+ // which LSP this document is open in (this is a LSPID)
+ u32 lsp_opened_in;
u32 undo_history_write_pos; // where in the undo history was the last write? used by buffer_unsaved_changes
u32 first_line_on_screen, last_line_on_screen; // which lines are on screen? updated when buffer_render is called.
@@ -412,7 +415,7 @@ typedef struct {
typedef struct Ted {
- struct LSP *lsps[TED_LSP_MAX];
+ struct LSP *lsps[TED_LSP_MAX + 1];
// current time, as of the start of this frame
struct timespec frame_time;
@@ -527,6 +530,7 @@ typedef struct Ted {
} Ted;
void autocomplete_close(Ted *ted);
+char *buffer_contents_utf8_alloc(TextBuffer *buffer);
void command_execute(Ted *ted, Command c, i64 argument);
void ted_switch_to_buffer(Ted *ted, TextBuffer *buffer);
// the settings of the active buffer, or the default settings if there is no active buffer
@@ -534,6 +538,7 @@ Settings *ted_active_settings(Ted *ted);
Settings *ted_get_settings(Ted *ted, const char *path, Language lang);
void ted_load_configs(Ted *ted, bool reloading);
struct LSP *ted_get_lsp(Ted *ted, const char *path, Language lang);
+struct LSP *ted_get_lsp_by_id(Ted *ted, u32 id);
static TextBuffer *find_search_buffer(Ted *ted);
// first, we read all config files, then we parse them.
// this is because we want less specific settings (e.g. settings applied