summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2022-12-10 12:31:03 -0500
committerpommicket <pommicket@gmail.com>2022-12-10 12:31:03 -0500
commitb7cbc84f296024d3a89e4604c1b114e79b4e752e (patch)
tree6e093847a80a30ee93e929c793c7076062454692
parenta604ad77ff7ef6a75cccdeb86d1a5608caf7c2a6 (diff)
more lsp
-rw-r--r--buffer.c144
-rw-r--r--lsp.c277
-rw-r--r--lsp.h180
-rw-r--r--main.c18
-rw-r--r--ted.c5
-rw-r--r--ted.h3
-rw-r--r--unicode.h2
7 files changed, 338 insertions, 291 deletions
diff --git a/buffer.c b/buffer.c
index 6e7ba7e..afb3306 100644
--- a/buffer.c
+++ b/buffer.c
@@ -2040,6 +2040,9 @@ 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) {
@@ -2060,87 +2063,92 @@ Status buffer_load_file(TextBuffer *buffer, char const *filename) {
success = false;
} else {
u8 *file_contents = buffer_calloc(buffer, 1, file_size);
- if (file_contents) {
- lines_capacity = 4;
- lines = buffer_calloc(buffer, lines_capacity, sizeof *buffer->lines); // initial lines
- if (lines) {
- nlines = 1;
- size_t bytes_read = fread(file_contents, 1, file_size, fp);
- if (bytes_read == file_size) {
- char32_t c = 0;
- for (u8 *p = file_contents, *end = p + file_size; p != end; ) {
- if (*p == '\r' && p != end-1 && p[1] == '\n') {
- // CRLF line endings
- p += 2;
- c = '\n';
- } else {
- size_t n = unicode_utf8_to_utf32(&c, (char *)p, (size_t)(end - p));
- if (n == 0) {
- // null character
- c = 0;
- ++p;
- } else if (n >= (size_t)(-2)) {
- // invalid UTF-8
- success = false;
- buffer_seterr(buffer, "Invalid UTF-8 (position: %td).", p - file_contents);
- break;
- } else {
- p += n;
- }
- }
- if (c == '\n') {
- if (buffer_lines_set_min_capacity(buffer, &lines, &lines_capacity, nlines + 1))
- ++nlines;
- } else {
- u32 line_idx = nlines - 1;
- Line *line = &lines[line_idx];
- buffer_line_append_char(buffer, line, c);
- }
- }
+ lines_capacity = 4;
+ lines = buffer_calloc(buffer, lines_capacity, sizeof *buffer->lines); // initial lines
+ nlines = 1;
+ size_t bytes_read = fread(file_contents, 1, file_size, fp);
+ if (bytes_read == file_size) {
+ char32_t c = 0;
+ for (u8 *p = file_contents, *end = p + file_size; p != end; ) {
+ if (*p == '\r' && p != end-1 && p[1] == '\n') {
+ // CRLF line endings
+ p += 2;
+ c = '\n';
} else {
- success = false;
+ size_t n = unicode_utf8_to_utf32(&c, (char *)p, (size_t)(end - p));
+ if (n == 0) {
+ // null character
+ c = 0;
+ ++p;
+ } else if (n >= (size_t)(-2)) {
+ // invalid UTF-8
+ success = false;
+ buffer_seterr(buffer, "Invalid UTF-8 (position: %td).", p - file_contents);
+ break;
+ } else {
+ p += n;
+ }
}
- if (!success) {
- // something went wrong; we need to free all the memory we used
- for (u32 i = 0; i < nlines; ++i)
- buffer_line_free(&lines[i]);
- free(lines);
+ if (c == '\n') {
+ if (buffer_lines_set_min_capacity(buffer, &lines, &lines_capacity, nlines + 1))
+ ++nlines;
+ } else {
+ u32 line_idx = nlines - 1;
+ Line *line = &lines[line_idx];
+ buffer_line_append_char(buffer, line, c);
}
}
- free(file_contents);
}
- if (ferror(fp)) {
+ if (ferror(fp)) {
buffer_seterr(buffer, "Error reading from file.");
success = false;
}
+ if (!success) {
+ // something went wrong; we need to free all the memory we used
+ for (u32 i = 0; i < nlines; ++i)
+ buffer_line_free(&lines[i]);
+ free(lines);
+ }
+
+ if (success) {
+ char *filename_copy = buffer_strdup(buffer, filename);
+ if (!filename_copy) success = false;
+ if (success) {
+ // everything is good
+ buffer_clear(buffer);
+ buffer->lines = lines;
+ buffer->nlines = nlines;
+ buffer->frame_earliest_line_modified = 0;
+ buffer->frame_latest_line_modified = nlines - 1;
+ buffer->lines_capacity = lines_capacity;
+ buffer->filename = filename_copy;
+ buffer->last_write_time = time_last_modified(buffer->filename);
+ if (!(fs_path_permission(filename) & FS_PERMISSION_WRITE)) {
+ // can't write to this file; make the buffer view only.
+ buffer->view_only = true;
+ }
+ }
+
+ LSP *lsp = buffer_lsp(buffer);
+ if (lsp) {
+ // send didOpen
+ LSPRequest request = {.type = LSP_OPEN};
+ LSPRequestOpen *open = &request.data.open;
+ open->file_contents = (char *)file_contents;
+ open->path = str_dup(filename);
+ open->language = buffer_language(buffer);
+ lsp_send_request(lsp, &request);
+ file_contents = NULL; // don't free
+ }
+ }
+
+ free(file_contents);
}
- if (fclose(fp) != 0) {
- buffer_seterr(buffer, "Error closing file.");
- success = false;
- }
+ fclose(fp);
} else {
buffer_seterr(buffer, "Couldn't open file %s: %s.", filename, strerror(errno));
success = false;
}
- if (success) {
- char *filename_copy = buffer_strdup(buffer, filename);
- if (!filename_copy) success = false;
- if (success) {
- // everything is good
- buffer_clear(buffer);
- buffer->lines = lines;
- buffer->nlines = nlines;
- buffer->frame_earliest_line_modified = 0;
- buffer->frame_latest_line_modified = nlines - 1;
- buffer->lines_capacity = lines_capacity;
- buffer->filename = filename_copy;
- buffer->last_write_time = time_last_modified(buffer->filename);
- if (!(fs_path_permission(filename) & FS_PERMISSION_WRITE)) {
- // can't write to this file; make the buffer view only.
- buffer->view_only = true;
- }
- }
- }
return success;
}
diff --git a/lsp.c b/lsp.c
index 156ee00..22d27e8 100644
--- a/lsp.c
+++ b/lsp.c
@@ -1,174 +1,3 @@
-// @TODO:
-// - document this file. maybe make lsp.h
-// - maximum queue size for requests/responses just in case?
-// - delete old LSPRequestTrackedInfos
-// (if the server never sends a response)
-// - TESTING: make rust-analyzer-slow (waits 10s before sending response)
-
-typedef enum {
- LSP_REQUEST,
- LSP_RESPONSE
-} LSPMessageType;
-
-typedef enum {
- LSP_NONE,
-
- // client-to-server
- LSP_INITIALIZE,
- LSP_INITIALIZED,
- LSP_OPEN,
- LSP_COMPLETION,
- LSP_SHUTDOWN,
- LSP_EXIT,
-
- // server-to-client
- LSP_SHOW_MESSAGE,
- LSP_LOG_MESSAGE
-} LSPRequestType;
-
-typedef struct {
- // buffer language
- Language language;
- // freed by lsp_request_free
- char *filename;
- // freed by lsp_request_free
- char *file_contents;
-} LSPRequestOpen;
-
-typedef enum {
- ERROR = 1,
- WARNING = 2,
- INFO = 3,
- LOG = 4
-} LSPWindowMessageType;
-
-typedef struct {
- LSPWindowMessageType type;
- // freed by lsp_request_free
- char *message;
-} LSPRequestMessage;
-
-typedef struct {
- // freed by lsp_request_free
- char *path;
- u32 line;
- // the **UTF-16** "character" offset within the line
- u32 character;
-} LSPDocumentPosition;
-
-typedef struct {
- LSPDocumentPosition position;
-} LSPRequestCompletion;
-
-typedef struct {
- LSPRequestType type;
- union {
- LSPRequestOpen open;
- LSPRequestCompletion completion;
- // for LSP_SHOW_MESSAGE and LSP_LOG_MESSAGE
- LSPRequestMessage message;
- } data;
-} LSPRequest;
-
-// info we want to keep track of about a request,
-// so we can deal with the response appropriately.
-typedef struct {
- u64 id;
- LSPRequestType type;
-} LSPRequestTrackedInfo;
-
-typedef struct {
- u32 offset;
-} LSPString;
-
-typedef struct {
- u32 line;
- u32 character;
-} LSPPosition;
-
-typedef struct {
- LSPPosition start;
- LSPPosition end;
-} LSPRange;
-
-// see InsertTextFormat in the LSP spec.
-typedef enum {
- // plain text
- LSP_TEXT_EDIT_PLAIN = 1,
- // snippet e.g. "some_method($1, $2)$0"
- LSP_TEXT_EDIT_SNIPPET = 2
-} LSPTextEditType;
-
-typedef struct {
- LSPTextEditType type;
-
- // if set to true, `range` should be ignored
- // -- this is a completion which uses insertText.
- // how to handle this:
- // "VS Code when code complete is requested in this example
- // `con<cursor position>` and a completion item with an `insertText` of
- // `console` is provided it will only insert `sole`"
- bool at_cursor;
-
- LSPRange range;
- LSPString new_text;
-} LSPTextEdit;
-
-typedef struct {
- LSPString label;
- LSPTextEdit text_edit;
- // note: the items are sorted here in this file,
- // so you probably don't need to access this.
- LSPString sort_text;
-} LSPCompletionItem;
-
-typedef struct {
- // dynamic array
- LSPCompletionItem *items;
-} LSPResponseCompletion;
-
-typedef LSPRequestType LSPResponseType;
-typedef struct {
- LSPResponseType type;
- // 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;
- union {
- LSPResponseCompletion completion;
- } data;
-} LSPResponse;
-
-typedef struct {
- LSPMessageType type;
- union {
- LSPRequest request;
- LSPResponse response;
- } u;
-} LSPMessage;
-
-typedef struct {
- Process process;
- u64 request_id;
- LSPMessage *messages;
- SDL_mutex *messages_mutex;
- LSPRequest *requests_client2server;
- LSPRequest *requests_server2client;
- // only applicable for client-to-server requests
- LSPRequestTrackedInfo *requests_tracked_info;
- SDL_mutex *requests_mutex;
- bool initialized; // has the response to the initialize request been sent?
- SDL_Thread *communication_thread;
- SDL_sem *quit_sem;
- char *received_data; // dynamic array
- SDL_mutex *error_mutex;
- char error[256];
-} LSP;
-
-// returns true if there's an error.
-// returns false and sets error to "" if there's no error.
-// if clear = true, the error will be cleared.
-// you may set error = NULL, error_size = 0, clear = true to just clear the error
bool lsp_get_error(LSP *lsp, char *error, size_t error_size, bool clear) {
bool has_err = false;
SDL_LockMutex(lsp->error_mutex);
@@ -227,8 +56,6 @@ static void lsp_position_free(LSPDocumentPosition *position) {
static void lsp_request_free(LSPRequest *r) {
switch (r->type) {
case LSP_NONE:
- assert(0);
- break;
case LSP_INITIALIZE:
case LSP_INITIALIZED:
case LSP_SHUTDOWN:
@@ -240,7 +67,7 @@ static void lsp_request_free(LSPRequest *r) {
} break;
case LSP_OPEN: {
LSPRequestOpen *open = &r->data.open;
- free(open->filename);
+ free(open->path);
free(open->file_contents);
} break;
case LSP_SHOW_MESSAGE:
@@ -303,8 +130,9 @@ static WarnUnusedResult bool lsp_expect_number(LSP *lsp, JSONValue value, const
// 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 void write_request(LSP *lsp, const LSPRequest *request) {
+static void write_request(LSP *lsp, LSPRequest *request) {
StrBuilder builder = str_builder_new();
@@ -317,15 +145,9 @@ static void write_request(LSP *lsp, const LSPRequest *request) {
bool is_notification = request->type == LSP_INITIALIZED
|| request->type == LSP_EXIT;
if (!is_notification) {
- unsigned long long id = lsp->request_id++;
- str_builder_appendf(&builder, "\"id\":%llu,", id);
- LSPRequestTrackedInfo info = {
- .id = id,
- .type = request->type
- };
- SDL_LockMutex(lsp->requests_mutex);
- arr_add(lsp->requests_tracked_info, info);
- SDL_UnlockMutex(lsp->requests_mutex);
+ u32 id = lsp->request_id++;
+ request->id = id;
+ str_builder_appendf(&builder, "\"id\":%lu,", (unsigned long)id);
}
switch (request->type) {
@@ -339,7 +161,9 @@ static void write_request(LSP *lsp, const LSPRequest *request) {
str_builder_appendf(&builder,
"\"method\":\"initialize\",\"params\":{"
"\"processId\":%d,"
- "\"capabilities\":{}"
+ "\"capabilities\":{},"
+ "\"rootUri\":null,"
+ "\"workspaceFolders\":null"
"}", process_get_id());
} break;
case LSP_INITIALIZED:
@@ -347,21 +171,22 @@ static void write_request(LSP *lsp, const LSPRequest *request) {
break;
case LSP_OPEN: {
const LSPRequestOpen *open = &request->data.open;
- char *escaped_filename = json_escape(open->filename);
+ printf("text=%p\n",open->file_contents);
+ char *escaped_path = json_escape(open->path);
char *escaped_text = json_escape(open->file_contents);
-
+ // @TODO: escape directly into builder
str_builder_appendf(&builder,
- "\"method\":\"textDocument/open\",\"params\":{"
- "textDocument:{"
- "uri:\"file://%s\","
- "languageId:\"%s\","
- "version:1,"
- "text:\"%s\"}}",
- escaped_filename,
+ "\"method\":\"textDocument/didOpen\",\"params\":{"
+ "\"textDocument\":{"
+ "\"uri\":\"file://%s\","
+ "\"languageId\":\"%s\","
+ "\"version\":1,"
+ "\"text\":\"%s\"}}",
+ escaped_path,
lsp_language_id(open->language),
escaped_text);
free(escaped_text);
- free(escaped_filename);
+ free(escaped_path);
} break;
case LSP_COMPLETION: {
const LSPRequestCompletion *completion = &request->data.completion;
@@ -409,6 +234,14 @@ static void write_request(LSP *lsp, const LSPRequest *request) {
process_write(&lsp->process, content, strlen(content));
str_builder_free(&builder);
+
+ if (is_notification) {
+ lsp_request_free(request);
+ } else {
+ SDL_LockMutex(lsp->requests_mutex);
+ arr_add(lsp->requests_sent, *request);
+ SDL_UnlockMutex(lsp->requests_mutex);
+ }
}
// figure out if data begins with a complete LSP response.
@@ -647,25 +480,41 @@ static void process_message(LSP *lsp, JSON *json) {
json_debug_print(json);
printf("\x1b[0m\n");
#endif
-
+ JSONValue id_value = json_get(json, "id");
+
+ // get the request associated with this (if any)
+ LSPRequest response_to = {0};
+ if (id_value.type == JSON_NUMBER) {
+ u64 id = (u64)id_value.val.number;
+ arr_foreach_ptr(lsp->requests_sent, LSPRequest, req) {
+ if (req->id == id) {
+ response_to = *req;
+ arr_remove(lsp->requests_sent, (u32)(req - lsp->requests_sent));
+ break;
+ }
+ }
+ }
+
JSONValue error = json_get(json, "error.message");
if (error.type == JSON_STRING) {
char err[256] = {0};
json_string_get(json, error.val.string, err, sizeof err);;
- lsp_set_error(lsp, "%s", err);
+
+ if (streq(err, "waiting for cargo metadata or cargo check")) {
+ // fine. be that way. i'll resend the goddamn request.
+ // i'll keep bombarding you with requests.
+ // maybe next time you should abide by the standard and only send an initialize response when youre actually ready to handle my requests. fuck you.
+ if (response_to.type)
+ lsp_send_request(lsp, &response_to);
+ } else {
+ lsp_set_error(lsp, "%s", err);
+ }
goto ret;
}
JSONValue result = json_get(json, "result");
if (result.type != JSON_UNDEFINED) {
- JSONValue id = json_get(json, "id");
-
- if (id.type != JSON_NUMBER) {
- // what
- lsp_set_error(lsp, "Response with no ID.");
- goto ret;
- }
- if (id.val.number == 0) {
+ if (response_to.type == LSP_INITIALIZE) {
// it's the response to our initialize request!
// let's send back an "initialized" request (notification) because apparently
// that's something we need to do.
@@ -677,22 +526,9 @@ static void process_message(LSP *lsp, JSON *json) {
// we can now send requests which have nothing to do with initialization
lsp->initialized = true;
} else {
- u64 id_no = (u64)id.val.number;
- LSPRequestTrackedInfo tracked_info = {0};
- SDL_LockMutex(lsp->requests_mutex);
- arr_foreach_ptr(lsp->requests_tracked_info, LSPRequestTrackedInfo, info) {
- if (info->id == id_no) {
- // hey its the thing
- tracked_info = *info;
- arr_remove(lsp->requests_tracked_info, (u32)(info - lsp->requests_tracked_info));
- break;
- }
- }
- SDL_UnlockMutex(lsp->requests_mutex);
-
LSPResponse response = {0};
bool success = false;
- switch (tracked_info.type) {
+ switch (response_to.type) {
case LSP_COMPLETION:
success = parse_completion(lsp, json, &response);
break;
@@ -723,6 +559,7 @@ static void process_message(LSP *lsp, JSON *json) {
lsp_set_error(lsp, "Bad message from server (no result, no method).");
}
ret:
+ lsp_request_free(&response_to);
json_free(json);
}
@@ -804,7 +641,6 @@ static bool lsp_send(LSP *lsp) {
// whatever.
write_request(lsp, r);
}
- lsp_request_free(r);
if (SDL_SemTryWait(lsp->quit_sem) == 0) {
quit = true;
@@ -902,6 +738,7 @@ void lsp_free(LSP *lsp) {
SDL_SemPost(lsp->quit_sem);
SDL_WaitThread(lsp->communication_thread, NULL);
SDL_DestroyMutex(lsp->messages_mutex);
+ SDL_DestroyMutex(lsp->requests_mutex);
SDL_DestroySemaphore(lsp->quit_sem);
process_kill(&lsp->process);
arr_free(lsp->received_data);
diff --git a/lsp.h b/lsp.h
new file mode 100644
index 0000000..0bc5f41
--- /dev/null
+++ b/lsp.h
@@ -0,0 +1,180 @@
+// @TODO:
+// - document this and lsp.c.
+// - maximum queue size for requests/responses just in case?
+// - delete old sent requests
+// (if the server never sends a response)
+// - TESTING: make rust-analyzer-slow (waits 10s before sending response)
+
+typedef enum {
+ LSP_REQUEST,
+ LSP_RESPONSE
+} LSPMessageType;
+
+typedef enum {
+ LSP_NONE,
+
+ // client-to-server
+ LSP_INITIALIZE,
+ LSP_INITIALIZED,
+ LSP_OPEN,
+ LSP_COMPLETION,
+ LSP_SHUTDOWN,
+ LSP_EXIT,
+
+ // server-to-client
+ LSP_SHOW_MESSAGE,
+ LSP_LOG_MESSAGE
+} LSPRequestType;
+
+typedef struct {
+ // buffer language
+ Language language;
+ // freed by lsp_request_free
+ char *path;
+ // freed by lsp_request_free
+ char *file_contents;
+} LSPRequestOpen;
+
+typedef enum {
+ ERROR = 1,
+ WARNING = 2,
+ INFO = 3,
+ LOG = 4
+} LSPWindowMessageType;
+
+typedef struct {
+ LSPWindowMessageType type;
+ // freed by lsp_request_free
+ char *message;
+} LSPRequestMessage;
+
+typedef struct {
+ // freed by lsp_request_free
+ char *path;
+ u32 line;
+ // the **UTF-16** "character" offset within the line
+ u32 character;
+} LSPDocumentPosition;
+
+typedef struct {
+ LSPDocumentPosition position;
+} LSPRequestCompletion;
+
+typedef struct {
+ // id is set by lsp.c; you shouldn't set it.
+ u32 id;
+ LSPRequestType type;
+ union {
+ LSPRequestOpen open;
+ LSPRequestCompletion completion;
+ // for LSP_SHOW_MESSAGE and LSP_LOG_MESSAGE
+ LSPRequestMessage message;
+ } data;
+} LSPRequest;
+
+typedef struct {
+ u32 offset;
+} LSPString;
+
+typedef struct {
+ u32 line;
+ u32 character;
+} LSPPosition;
+
+typedef struct {
+ LSPPosition start;
+ LSPPosition end;
+} LSPRange;
+
+// see InsertTextFormat in the LSP spec.
+typedef enum {
+ // plain text
+ LSP_TEXT_EDIT_PLAIN = 1,
+ // snippet e.g. "some_method($1, $2)$0"
+ LSP_TEXT_EDIT_SNIPPET = 2
+} LSPTextEditType;
+
+typedef struct {
+ LSPTextEditType type;
+
+ // if set to true, `range` should be ignored
+ // -- this is a completion which uses insertText.
+ // how to handle this:
+ // "VS Code when code complete is requested in this example
+ // `con<cursor position>` and a completion item with an `insertText` of
+ // `console` is provided it will only insert `sole`"
+ bool at_cursor;
+
+ LSPRange range;
+ LSPString new_text;
+} LSPTextEdit;
+
+typedef struct {
+ LSPString label;
+ LSPTextEdit text_edit;
+ // note: the items are sorted here in this file,
+ // so you probably don't need to access this.
+ LSPString sort_text;
+} LSPCompletionItem;
+
+typedef struct {
+ // dynamic array
+ LSPCompletionItem *items;
+} LSPResponseCompletion;
+
+typedef LSPRequestType LSPResponseType;
+typedef struct {
+ LSPResponseType type;
+ // 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;
+ union {
+ LSPResponseCompletion completion;
+ } data;
+} LSPResponse;
+
+typedef struct {
+ LSPMessageType type;
+ union {
+ LSPRequest request;
+ LSPResponse response;
+ } u;
+} LSPMessage;
+
+typedef struct LSP {
+ Process process;
+ u32 request_id;
+ LSPMessage *messages;
+ SDL_mutex *messages_mutex;
+ LSPRequest *requests_client2server;
+ LSPRequest *requests_server2client;
+ // we keep track of client-to-server requests
+ // so that we can process responses.
+ // also fucking rust-analyzer gives "waiting for cargo metadata or cargo check"
+ // WHY NOT JUST WAIT UNTIL YOUVE DONE THAT BEFORE SENDING THE INITIALIZE RESPONSE. YOU HAVE NOT FINISHED INITIALIZATION. YOU ARE LYING.
+ // YOU GIVE A -32801 ERROR CODE WHICH IS "ContentModified" -- WHAT THE FUCK? THATS JUST COMPLETLY WRONG
+ // so we need to re-send requests in that case.
+ LSPRequest *requests_sent;
+ SDL_mutex *requests_mutex;
+ bool initialized; // has the response to the initialize request been sent?
+ SDL_Thread *communication_thread;
+ SDL_sem *quit_sem;
+ char *received_data; // dynamic array
+ SDL_mutex *error_mutex;
+ char error[256];
+} LSP;
+
+// @TODO: function declarations
+
+// returns true if there's an error.
+// returns false and sets error to "" if there's no error.
+// if clear = true, the error will be cleared.
+// you can set error = NULL, error_size = 0, clear = true to just clear the error
+bool lsp_get_error(LSP *lsp, char *error, size_t error_size, bool clear);
+void lsp_message_free(LSPMessage *message);
+void lsp_send_request(LSP *lsp, const LSPRequest *request);
+const char *lsp_response_string(const LSPResponse *response, LSPString string);
+bool lsp_create(LSP *lsp, const char *analyzer_command);
+bool lsp_next_message(LSP *lsp, LSPMessage *message);
+void lsp_free(LSP *lsp);
diff --git a/main.c b/main.c
index 567ac7a..69af788 100644
--- a/main.c
+++ b/main.c
@@ -1,4 +1,8 @@
/*
+@TODO:
+- rust-analyzer bug reports:
+ - bad json can give "Unexpected error: client exited without proper shutdown sequence"
+ - rust-analyzer should wait until cargo metadata/check is done before sending initialize response
FUTURE FEATURES:
- configurable max buffer size
- better undo chaining (dechain on backspace?)
@@ -92,6 +96,7 @@ static void die(char const *fmt, ...) {
#include "ted.h"
#include "gl.c"
#include "text.c"
+#include "lsp.h"
#include "string32.c"
#include "syntax.c"
@@ -288,10 +293,11 @@ int main(int argc, char **argv) {
PROFILE_TIME(init_start)
PROFILE_TIME(basic_init_start)
+
+ if (0) {
// @TODO TEMPORARY
- {
LSP lsp={0};
- chdir("/p/test-lsp");
+// chdir("/p/test-lsp");
if (!lsp_create(&lsp, "rust-analyzer")) {
printf("lsp_create: %s\n",lsp.error);
exit(1);
@@ -408,6 +414,14 @@ int main(int argc, char **argv) {
}
ted->last_save_time = -1e50;
+
+ // @TODO TEMPORARY
+ ted->test_lsp = calloc(1, sizeof(LSP));
+ if (!lsp_create(ted->test_lsp, "rust-analyzer")) {
+ printf("lsp_create: %s\n",ted->test_lsp->error);
+ exit(1);
+ }
+
// make sure signal handler has access to ted.
error_signal_handler_ted = ted;
diff --git a/ted.c b/ted.c
index 5d82198..ed5cb00 100644
--- a/ted.c
+++ b/ted.c
@@ -66,6 +66,11 @@ Settings *ted_active_settings(Ted *ted) {
return settings;
}
+LSP *ted_get_lsp(Ted *ted, Language lang) {
+ // @TODO
+ return ted->test_lsp;
+}
+
u32 ted_color(Ted *ted, ColorSetting color) {
return ted_active_settings(ted)->colors[color];
}
diff --git a/ted.h b/ted.h
index e204b17..3d2bbc6 100644
--- a/ted.h
+++ b/ted.h
@@ -353,6 +353,8 @@ typedef struct {
} BuildError;
typedef struct Ted {
+ struct LSP *test_lsp; // @TODO: something better
+
SDL_Window *window;
Font *font_bold;
Font *font;
@@ -471,6 +473,7 @@ 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
Settings *ted_active_settings(Ted *ted);
void ted_load_configs(Ted *ted, bool reloading);
+struct LSP *ted_get_lsp(Ted *ted, Language lang);
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
diff --git a/unicode.h b/unicode.h
index 211efd6..fb9810a 100644
--- a/unicode.h
+++ b/unicode.h
@@ -13,7 +13,7 @@ static bool unicode_is_start_of_code_point(u8 byte) {
// *c will be filled with the next UTF-8 code point in `str`. `bytes` refers to the maximum
// number of bytes that can be read from `str`.
// Returns:
-// 0 - if a NULL character was encountered
+// 0 - if a null character was encountered
// (size_t)-1 - on invalid UTF-8
// (size_t)-2 - on incomplete code point (str should be longer)
// other - the number of bytes read from `str`.