summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lsp-parse.c33
-rw-r--r--lsp-write.c120
-rw-r--r--lsp.c9
-rw-r--r--lsp.h6
-rw-r--r--main.c2
-rw-r--r--ted.c7
6 files changed, 140 insertions, 37 deletions
diff --git a/lsp-parse.c b/lsp-parse.c
index b5a05d0..de63401 100644
--- a/lsp-parse.c
+++ b/lsp-parse.c
@@ -266,6 +266,27 @@ static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response)
return true;
}
+// fills request->id/id_string appropriately given the request's json
+// returns true on success
+static WarnUnusedResult bool parse_id(JSON *json, LSPRequest *request) {
+ JSONValue id_value = json_get(json, "id");
+ switch (id_value.type) {
+ case JSON_NUMBER: {
+ double id = id_value.val.number;
+ if (id == (u32)id) {
+ request->id = (u32)id;
+ return true;
+ }
+ } break;
+ case JSON_STRING:
+ request->id_string = json_string_get_alloc(json, id_value.val.string);
+ return true;
+ default: break;
+ }
+ return false;
+}
+
+// returns true if `request` was actually filled with a request.
static bool parse_server2client_request(LSP *lsp, JSON *json, LSPRequest *request) {
JSONValue method_value = json_get(json, "method");
if (!lsp_expect_string(lsp, method_value, "request method"))
@@ -297,6 +318,18 @@ static bool parse_server2client_request(LSP *lsp, JSON *json, LSPRequest *reques
m->type = (LSPWindowMessageType)mtype;
m->message = json_string_get_alloc(json, message.val.string);
return true;
+ } else if (streq(method, "workspace/workspaceFolders")) {
+ // we can deal with this request right here
+ LSPResponse response = {0};
+ request = &response.request;
+ request->type = LSP_REQUEST_WORKSPACE_FOLDERS;
+ if (!parse_id(json, request)) {
+ // we can't even send an error response since we have no ID.
+ debug_println("Bad ID in workspace/workspaceFolders request. This shouldn't happen.");
+ return false;
+ }
+ lsp_send_response(lsp, &response);
+ return false;
} else if (str_has_prefix(method, "$/")) {
// we can safely ignore this
} else {
diff --git a/lsp-write.c b/lsp-write.c
index b66def9..ef60b91 100644
--- a/lsp-write.c
+++ b/lsp-write.c
@@ -217,6 +217,17 @@ static void write_key_range(JSONWriter *o, const char *key, LSPRange range) {
write_range(o, range);
}
+static void write_workspace_folders(JSONWriter *o, char **workspace_folders) {
+ write_arr_start(o);
+ arr_foreach_ptr(workspace_folders, char *, folder) {
+ write_arr_elem_obj_start(o);
+ write_key_file_uri_direct(o, "uri", *folder);
+ write_key_string(o, "name", *folder);
+ write_obj_end(o);
+ }
+ write_arr_end(o);
+}
+
static const char *lsp_request_method(LSPRequest *request) {
switch (request->type) {
case LSP_REQUEST_NONE: break;
@@ -240,6 +251,8 @@ static const char *lsp_request_method(LSPRequest *request) {
return "shutdown";
case LSP_REQUEST_EXIT:
return "exit";
+ case LSP_REQUEST_WORKSPACE_FOLDERS:
+ return "workspace/workspaceFolders";
}
assert(0);
return "$/ignore";
@@ -259,22 +272,54 @@ static bool request_type_is_notification(LSPRequestType type) {
case LSP_REQUEST_SHOW_MESSAGE:
case LSP_REQUEST_LOG_MESSAGE:
case LSP_REQUEST_COMPLETION:
+ case LSP_REQUEST_WORKSPACE_FOLDERS:
return false;
}
assert(0);
return false;
}
+
+static const size_t max_header_size = 64;
+static JSONWriter message_writer_new(LSP *lsp) {
+ JSONWriter writer = json_writer_new(lsp);
+ // this is where our header will go
+ str_builder_append_null(&writer.builder, max_header_size);
+ return writer;
+}
+
+static void message_writer_write_and_free(LSP *lsp, JSONWriter *o) {
+ StrBuilder builder = o->builder;
+
+ // this is kind of hacky but it lets us send the whole request with one write call.
+ // probably not *actually* needed. i thought it would help fix an error but it didn't.
+ size_t content_length = str_builder_len(&builder) - max_header_size;
+ char content_length_str[32];
+ sprintf(content_length_str, "%zu", content_length);
+ size_t header_size = strlen("Content-Length: \r\n\r\n") + strlen(content_length_str);
+ char *header = &builder.str[max_header_size - header_size];
+ strcpy(header, "Content-Length: ");
+ strcat(header, content_length_str);
+ // we specifically DON'T want a null byte
+ memcpy(header + strlen(header), "\r\n\r\n", 4);
+
+ char *content = header;
+ #if LSP_SHOW_C2S
+ printf("%s%s%s\n",term_bold(stdout),content,term_clear(stdout));
+ #endif
+
+ // @TODO: does write always write the full amount? probably not. this should be fixed.
+ process_write(&lsp->process, content, strlen(content));
+
+ str_builder_free(&builder);
+}
+
// NOTE: don't call lsp_request_free after calling this function.
// I will do it for you.
static void write_request(LSP *lsp, LSPRequest *request) {
- JSONWriter writer = json_writer_new(lsp);
+ JSONWriter writer = message_writer_new(lsp);
JSONWriter *o = &writer;
- u32 max_header_size = 64;
- // this is where our header will go
- str_builder_append_null(&o->builder, max_header_size);
-
write_obj_start(o);
write_key_string(o, "jsonrpc", "2.0");
@@ -291,6 +336,7 @@ static void write_request(LSP *lsp, LSPRequest *request) {
// these are server-to-client-only requests
case LSP_REQUEST_SHOW_MESSAGE:
case LSP_REQUEST_LOG_MESSAGE:
+ case LSP_REQUEST_WORKSPACE_FOLDERS:
assert(0);
break;
case LSP_REQUEST_INITIALIZED:
@@ -335,11 +381,13 @@ static void write_request(LSP *lsp, LSPRequest *request) {
write_key_bool(o, "contextSupport", true);
write_obj_end(o);
write_obj_end(o);
+ write_key_obj_start(o, "workspace");
+ write_key_bool(o, "workspaceFolders", true);
+ write_obj_end(o);
write_obj_end(o);
- write_key_file_uri_direct(o, "rootUri", lsp->root_dir);
-// write_key_arr_start(o, "workspaceFolders");
-// write_arr_elem_obj_start(o);
-// write_arr_end(o);
+ write_key_file_uri_direct(o, "rootUri", lsp->workspace_folders[0]);
+ write_key(o, "workspaceFolders");
+ write_workspace_folders(o, lsp->workspace_folders);
write_key_obj_start(o, "clientInfo");
write_key_string(o, "name", "ted");
write_obj_end(o);
@@ -410,29 +458,7 @@ static void write_request(LSP *lsp, LSPRequest *request) {
write_obj_end(o);
- StrBuilder builder = o->builder;
-
- // this is kind of hacky but it lets us send the whole request with one write call.
- // probably not *actually* needed. i thought it would help fix an error but it didn't.
- size_t content_length = str_builder_len(&builder) - max_header_size;
- char content_length_str[32];
- sprintf(content_length_str, "%zu", content_length);
- size_t header_size = strlen("Content-Length: \r\n\r\n") + strlen(content_length_str);
- char *header = &builder.str[max_header_size - header_size];
- strcpy(header, "Content-Length: ");
- strcat(header, content_length_str);
- // we specifically DON'T want a null byte
- memcpy(header + strlen(header), "\r\n\r\n", 4);
-
- char *content = header;
- #if LSP_SHOW_C2S
- printf("%s%s%s\n",term_bold(stdout),content,term_clear(stdout));
- #endif
-
- // @TODO: does write always write the full amount? probably not. this should be fixed.
- process_write(&lsp->process, content, strlen(content));
-
- str_builder_free(&builder);
+ message_writer_write_and_free(lsp, o);
if (is_notification) {
lsp_request_free(request);
@@ -442,3 +468,33 @@ static void write_request(LSP *lsp, LSPRequest *request) {
SDL_UnlockMutex(lsp->requests_mutex);
}
}
+
+// NOTE: don't call lsp_response_free after calling this function.
+// I will do it for you.
+static void write_response(LSP *lsp, LSPResponse *response) {
+
+ JSONWriter writer = message_writer_new(lsp);
+ JSONWriter *o = &writer;
+ LSPRequest *request = &response->request;
+
+ if (request->id_string)
+ write_key_string(o, "id", request->id_string);
+ else
+ write_key_number(o, "id", request->id);
+ write_key_obj_start(o, "result");
+
+ switch (response->request.type) {
+ case LSP_REQUEST_WORKSPACE_FOLDERS:
+ write_workspace_folders(o, lsp->workspace_folders);
+ break;
+ default:
+ // this is not a valid client-to-server response.
+ assert(0);
+ break;
+ }
+ write_obj_end(o);
+
+ message_writer_write_and_free(lsp, o);
+
+ lsp_response_free(response);
+}
diff --git a/lsp.c b/lsp.c
index f2c3cf9..4267fd3 100644
--- a/lsp.c
+++ b/lsp.c
@@ -35,6 +35,7 @@ static void lsp_document_change_event_free(LSPDocumentChangeEvent *event) {
}
static void lsp_request_free(LSPRequest *r) {
+ free(r->id_string);
switch (r->type) {
case LSP_REQUEST_NONE:
case LSP_REQUEST_INITIALIZE:
@@ -43,6 +44,7 @@ static void lsp_request_free(LSPRequest *r) {
case LSP_REQUEST_EXIT:
case LSP_REQUEST_COMPLETION:
case LSP_REQUEST_DID_CLOSE:
+ case LSP_REQUEST_WORKSPACE_FOLDERS:
break;
case LSP_REQUEST_DID_OPEN: {
LSPRequestDidOpen *open = &r->data.open;
@@ -108,8 +110,10 @@ static bool has_response(const char *data, size_t data_len, u64 *p_offset, u64 *
static bool lsp_supports_request(LSP *lsp, const LSPRequest *request) {
switch (request->type) {
case LSP_REQUEST_NONE:
+ // return false for server-to-client requests since we should never send them
case LSP_REQUEST_SHOW_MESSAGE:
case LSP_REQUEST_LOG_MESSAGE:
+ case LSP_REQUEST_WORKSPACE_FOLDERS:
return false;
case LSP_REQUEST_INITIALIZE:
case LSP_REQUEST_INITIALIZED:
@@ -307,7 +311,7 @@ LSP *lsp_create(const char *root_dir, Language language, const char *analyzer_co
#endif
str_hash_table_create(&lsp->document_ids, sizeof(u32));
- strbuf_cpy(lsp->root_dir, root_dir);
+ arr_add(lsp->workspace_folders, str_dup(root_dir));
lsp->language = language;
lsp->quit_sem = SDL_CreateSemaphore(0);
lsp->messages_mutex = SDL_CreateMutex();
@@ -358,6 +362,9 @@ void lsp_free(LSP *lsp) {
arr_foreach_ptr(lsp->messages, LSPMessage, message) {
lsp_message_free(message);
}
+ arr_foreach_ptr(lsp->workspace_folders, char *, folder)
+ free(*folder);
+ arr_free(lsp->workspace_folders);
arr_free(lsp->messages);
arr_free(lsp->trigger_chars);
memset(lsp, 0, sizeof *lsp);
diff --git a/lsp.h b/lsp.h
index 4301636..666ff1b 100644
--- a/lsp.h
+++ b/lsp.h
@@ -36,6 +36,7 @@ typedef enum {
// server-to-client
LSP_REQUEST_SHOW_MESSAGE,
LSP_REQUEST_LOG_MESSAGE,
+ LSP_REQUEST_WORKSPACE_FOLDERS, // NOTE: this is handled directly in lsp-parse.c (because it only needs information from the LSP struct)
} LSPRequestType;
typedef struct {
@@ -100,6 +101,7 @@ typedef struct {
// id is set by lsp.c; you shouldn't set it.
u32 id;
LSPRequestType type;
+ char *id_string; // if not NULL, this is the ID (only for server-to-client messages; we always use integer IDs)
union {
LSPRequestDidOpen open;
LSPRequestDidClose close;
@@ -274,7 +276,7 @@ typedef struct LSP {
char32_t *trigger_chars; // dynamic array of "trigger characters"
SDL_mutex *error_mutex;
Language language;
- char root_dir[TED_PATH_MAX];
+ char **workspace_folders; // dynamic array of root directories of LSP "workspaces" (meaningless garbage)
char error[256];
} LSP;
@@ -289,6 +291,8 @@ void lsp_message_free(LSPMessage *message);
u32 lsp_document_id(LSP *lsp, const char *path);
// don't free the contents of this request! let me handle it!
void lsp_send_request(LSP *lsp, LSPRequest *request);
+// 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);
LSP *lsp_create(const char *root_dir, Language language, const char *analyzer_command);
bool lsp_next_message(LSP *lsp, LSPMessage *message);
diff --git a/main.c b/main.c
index 6bec271..7fdb6de 100644
--- a/main.c
+++ b/main.c
@@ -1,6 +1,8 @@
/*
@TODO:
- what's wrong with gopls?
+- ignore telemetry/event
+- handle window/showMessageRequest
- make sure "save as" works
- more LSP stuff:
- hover
diff --git a/ted.c b/ted.c
index 637a592..34d67a2 100644
--- a/ted.c
+++ b/ted.c
@@ -103,9 +103,10 @@ LSP *ted_get_lsp(Ted *ted, const char *path, Language language) {
if (lsp->language != language) continue;
// check if root matches up
- const char *lsp_root = lsp->root_dir;
- if (str_has_path_prefix(path, lsp_root))
- return lsp;
+ arr_foreach_ptr(lsp->workspace_folders, char *, lsp_root) {
+ if (str_has_path_prefix(path, *lsp_root))
+ return lsp;
+ }
}
if (i == TED_LSP_MAX)
return NULL; // why are there so many LSP open???