summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buffer.c4
-rw-r--r--lsp.c120
-rw-r--r--lsp.h64
-rw-r--r--main.c10
4 files changed, 130 insertions, 68 deletions
diff --git a/buffer.c b/buffer.c
index afb3306..ad8363c 100644
--- a/buffer.c
+++ b/buffer.c
@@ -2132,8 +2132,8 @@ Status buffer_load_file(TextBuffer *buffer, char const *filename) {
LSP *lsp = buffer_lsp(buffer);
if (lsp) {
// send didOpen
- LSPRequest request = {.type = LSP_OPEN};
- LSPRequestOpen *open = &request.data.open;
+ LSPRequest request = {.type = LSP_REQUEST_DID_OPEN};
+ LSPRequestDidOpen *open = &request.data.open;
open->file_contents = (char *)file_contents;
open->path = str_dup(filename);
open->language = buffer_language(buffer);
diff --git a/lsp.c b/lsp.c
index c4955e7..94b0eca 100644
--- a/lsp.c
+++ b/lsp.c
@@ -53,34 +53,45 @@ static void lsp_position_free(LSPDocumentPosition *position) {
free(position->path);
}
+static void lsp_document_change_event_free(LSPDocumentChangeEvent *event) {
+ free(event->text);
+}
+
static void lsp_request_free(LSPRequest *r) {
switch (r->type) {
- case LSP_NONE:
- case LSP_INITIALIZE:
- case LSP_INITIALIZED:
- case LSP_SHUTDOWN:
- case LSP_EXIT:
+ case LSP_REQUEST_NONE:
+ case LSP_REQUEST_INITIALIZE:
+ case LSP_REQUEST_INITIALIZED:
+ case LSP_REQUEST_SHUTDOWN:
+ case LSP_REQUEST_EXIT:
break;
- case LSP_COMPLETION: {
+ case LSP_REQUEST_COMPLETION: {
LSPRequestCompletion *completion = &r->data.completion;
lsp_position_free(&completion->position);
} break;
- case LSP_OPEN: {
- LSPRequestOpen *open = &r->data.open;
+ case LSP_REQUEST_DID_OPEN: {
+ LSPRequestDidOpen *open = &r->data.open;
free(open->path);
free(open->file_contents);
} break;
- case LSP_SHOW_MESSAGE:
- case LSP_LOG_MESSAGE:
+ case LSP_REQUEST_SHOW_MESSAGE:
+ case LSP_REQUEST_LOG_MESSAGE:
free(r->data.message.message);
break;
+ case LSP_REQUEST_DID_CHANGE: {
+ LSPRequestDidChange *c = &r->data.change;
+ arr_foreach_ptr(c->changes, LSPDocumentChangeEvent, event)
+ lsp_document_change_event_free(event);
+ free(c->document);
+ arr_free(c->changes);
+ } break;
}
}
static void lsp_response_free(LSPResponse *r) {
arr_free(r->string_data);
switch (r->type) {
- case LSP_COMPLETION:
+ case LSP_REQUEST_COMPLETION:
arr_free(r->data.completion.items);
break;
default:
@@ -127,6 +138,13 @@ static WarnUnusedResult bool lsp_expect_number(LSP *lsp, JSONValue value, const
return lsp_expect_type(lsp, value, JSON_NUMBER, what);
}
+static void write_string(StrBuilder *builder, const char* string) {
+ abort();
+}
+
+static void write_range(StrBuilder *builder, LSPRange range) {
+}
+
// 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.
@@ -142,9 +160,9 @@ static void write_request(LSP *lsp, LSPRequest *request) {
str_builder_append(&builder, "{\"jsonrpc\":\"2.0\",");
- bool is_notification = request->type == LSP_INITIALIZED
- || request->type == LSP_EXIT
- || request->type == LSP_OPEN;
+ bool is_notification = request->type == LSP_REQUEST_INITIALIZED
+ || request->type == LSP_REQUEST_EXIT
+ || request->type == LSP_REQUEST_DID_OPEN;
if (!is_notification) {
u32 id = lsp->request_id++;
request->id = id;
@@ -152,13 +170,13 @@ static void write_request(LSP *lsp, LSPRequest *request) {
}
switch (request->type) {
- case LSP_NONE:
+ case LSP_REQUEST_NONE:
// these are server-to-client-only requests
- case LSP_SHOW_MESSAGE:
- case LSP_LOG_MESSAGE:
+ case LSP_REQUEST_SHOW_MESSAGE:
+ case LSP_REQUEST_LOG_MESSAGE:
assert(0);
break;
- case LSP_INITIALIZE: {
+ case LSP_REQUEST_INITIALIZE: {
str_builder_appendf(&builder,
"\"method\":\"initialize\",\"params\":{"
"\"processId\":%d,"
@@ -167,13 +185,12 @@ static void write_request(LSP *lsp, LSPRequest *request) {
"\"workspaceFolders\":null"
"}", process_get_id());
} break;
- case LSP_INITIALIZED:
+ case LSP_REQUEST_INITIALIZED:
str_builder_append(&builder, "\"method\":\"initialized\"");
break;
- case LSP_OPEN: {
- const LSPRequestOpen *open = &request->data.open;
+ case LSP_REQUEST_DID_OPEN: {
+ const LSPRequestDidOpen *open = &request->data.open;
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/didOpen\",\"params\":{"
@@ -181,14 +198,34 @@ static void write_request(LSP *lsp, LSPRequest *request) {
"\"uri\":\"file://%s\","
"\"languageId\":\"%s\","
"\"version\":1,"
- "\"text\":\"%s\"}}",
+ "\"text\":",
escaped_path,
- lsp_language_id(open->language),
- escaped_text);
- free(escaped_text);
+ lsp_language_id(open->language));
+ write_string(&builder, open->file_contents);
+ str_builder_appendf(&builder, "}}");
free(escaped_path);
} break;
- case LSP_COMPLETION: {
+ case LSP_REQUEST_DID_CHANGE: {
+ LSPRequestDidChange *change = &request->data.change;
+ static unsigned long long version_number = 0; // @TODO @TEMPORARY
+ ++version_number;
+ str_builder_appendf(&builder,
+ "\"method\":\"textDocument/didChange\",\"params\":{"
+ "\"textDocument\":{\"version\":%llu,\"uri\":\"file://%s\"},"
+ "\"contentChanges\":[",
+ version_number, change->document);
+ arr_foreach_ptr(change->changes, LSPDocumentChangeEvent, event) {
+ if (event != change->changes) str_builder_append(&builder, ",");
+ str_builder_appendf(&builder, "{\"range\":");
+ write_range(&builder, event->range);
+ str_builder_appendf(&builder, "\"text\":");
+ write_string(&builder, event->text);
+ str_builder_append(&builder, "}");
+ }
+ str_builder_appendf(&builder,
+ "]}");
+ } break;
+ case LSP_REQUEST_COMPLETION: {
const LSPRequestCompletion *completion = &request->data.completion;
char *escaped_path = json_escape(completion->position.path);
str_builder_appendf(&builder,"\"method\":\"textDocument/completion\",\"params\":{"
@@ -203,10 +240,10 @@ static void write_request(LSP *lsp, LSPRequest *request) {
(ulong)completion->position.character);
free(escaped_path);
} break;
- case LSP_SHUTDOWN:
+ case LSP_REQUEST_SHUTDOWN:
str_builder_append(&builder, "\"method\":\"shutdown\"");
break;
- case LSP_EXIT:
+ case LSP_REQUEST_EXIT:
str_builder_append(&builder, "\"method\":\"exit\"");
break;
}
@@ -276,10 +313,10 @@ static bool parse_server2client_request(LSP *lsp, JSON *json, LSPRequest *reques
json_string_get(json, method_value.val.string, method, sizeof method);
if (streq(method, "window/showMessage")) {
- request->type = LSP_SHOW_MESSAGE;
+ request->type = LSP_REQUEST_SHOW_MESSAGE;
goto window_message;
} else if (streq(method, "window/logMessage")) {
- request->type = LSP_LOG_MESSAGE;
+ request->type = LSP_REQUEST_LOG_MESSAGE;
window_message:;
JSONValue type = json_get(json, "params.type");
JSONValue message = json_get(json, "params.message");
@@ -366,7 +403,7 @@ static bool parse_range(LSP *lsp, const JSON *json, JSONValue range_value, LSPRa
static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response) {
// deal with textDocument/completion response.
// result: CompletionItem[] | CompletionList | null
- response->type = LSP_COMPLETION;
+ response->type = LSP_REQUEST_COMPLETION;
LSPResponseCompletion *completion = &response->data.completion;
JSONValue result = json_get(json, "result");
@@ -517,12 +554,12 @@ static void process_message(LSP *lsp, JSON *json) {
JSONValue result = json_get(json, "result");
if (result.type != JSON_UNDEFINED) {
- if (response_to.type == LSP_INITIALIZE) {
+ if (response_to.type == LSP_REQUEST_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.
LSPRequest initialized = {
- .type = LSP_INITIALIZED,
+ .type = LSP_REQUEST_INITIALIZED,
.data = {0},
};
write_request(lsp, &initialized);
@@ -532,7 +569,7 @@ static void process_message(LSP *lsp, JSON *json) {
LSPResponse response = {0};
bool success = false;
switch (response_to.type) {
- case LSP_COMPLETION:
+ case LSP_REQUEST_COMPLETION:
success = parse_completion(lsp, json, &response);
break;
default:
@@ -671,11 +708,11 @@ static int lsp_communication_thread(void *data) {
if (lsp->initialized) {
LSPRequest shutdown = {
- .type = LSP_SHUTDOWN,
+ .type = LSP_REQUEST_SHUTDOWN,
.data = {0}
};
LSPRequest exit = {
- .type = LSP_EXIT,
+ .type = LSP_REQUEST_EXIT,
.data = {0}
};
write_request(lsp, &shutdown);
@@ -712,7 +749,7 @@ bool lsp_create(LSP *lsp, const char *analyzer_command) {
};
process_run_ex(&lsp->process, analyzer_command, &settings);
LSPRequest initialize = {
- .type = LSP_INITIALIZE
+ .type = LSP_REQUEST_INITIALIZE
};
// immediately send the request rather than queueing it.
// this is a small request, so it shouldn't be a problem.
@@ -750,3 +787,10 @@ void lsp_free(LSP *lsp) {
}
arr_free(lsp->messages);
}
+
+void lsp_document_changed(LSP *lsp, const char *document, LSPDocumentChangeEvent change) {
+ // @TODO(optimization, eventually): batch changes (using the contentChanges array)
+ //LSPRequest request = {.type = LSP_REQUEST_DID_CHANGE};
+ //LSPRequestDidChange *change = &request.change;
+ abort(); // @TODO
+}
diff --git a/lsp.h b/lsp.h
index 0bc5f41..6a7ca24 100644
--- a/lsp.h
+++ b/lsp.h
@@ -1,4 +1,6 @@
// @TODO:
+// - make a real JSON output library
+// - use document IDs instead of strings (also lets us use real document version numbers)
// - document this and lsp.c.
// - maximum queue size for requests/responses just in case?
// - delete old sent requests
@@ -10,20 +12,35 @@ typedef enum {
LSP_RESPONSE
} LSPMessageType;
+typedef struct {
+ u32 offset;
+} LSPString;
+
+typedef struct {
+ u32 line;
+ u32 character;
+} LSPPosition;
+
+typedef struct {
+ LSPPosition start;
+ LSPPosition end;
+} LSPRange;
+
typedef enum {
- LSP_NONE,
+ LSP_REQUEST_NONE,
// client-to-server
- LSP_INITIALIZE,
- LSP_INITIALIZED,
- LSP_OPEN,
- LSP_COMPLETION,
- LSP_SHUTDOWN,
- LSP_EXIT,
+ LSP_REQUEST_INITIALIZE,
+ LSP_REQUEST_INITIALIZED,
+ LSP_REQUEST_DID_OPEN,
+ LSP_REQUEST_DID_CHANGE,
+ LSP_REQUEST_COMPLETION,
+ LSP_REQUEST_SHUTDOWN,
+ LSP_REQUEST_EXIT,
// server-to-client
- LSP_SHOW_MESSAGE,
- LSP_LOG_MESSAGE
+ LSP_REQUEST_SHOW_MESSAGE,
+ LSP_REQUEST_LOG_MESSAGE
} LSPRequestType;
typedef struct {
@@ -33,7 +50,18 @@ typedef struct {
char *path;
// freed by lsp_request_free
char *file_contents;
-} LSPRequestOpen;
+} LSPRequestDidOpen;
+
+// see TextDocumentContentChangeEvent in the LSP spec
+typedef struct {
+ LSPRange range;
+ char *text;
+} LSPDocumentChangeEvent;
+
+typedef struct {
+ char *document;
+ LSPDocumentChangeEvent *changes; // dynamic array
+} LSPRequestDidChange;
typedef enum {
ERROR = 1,
@@ -65,26 +93,15 @@ typedef struct {
u32 id;
LSPRequestType type;
union {
- LSPRequestOpen open;
+ LSPRequestDidOpen open;
+ LSPRequestDidChange change;
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 {
@@ -177,4 +194,5 @@ 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_document_changed(LSP *lsp, const char *document, LSPDocumentChangeEvent change);
void lsp_free(LSP *lsp);
diff --git a/main.c b/main.c
index 6e7259c..f8e3c98 100644
--- a/main.c
+++ b/main.c
@@ -1,4 +1,4 @@
-/*
+/* o
@TODO:
- rename buffer->filename to buffer->path
- rust-analyzer bug reports:
@@ -307,7 +307,7 @@ int main(int argc, char **argv) {
exit(1);
}
usleep(1000000);//if we don't do this we get "waiting for cargo metadata or cargo check"
- LSPRequest test_req = {.type = LSP_COMPLETION};
+ LSPRequest test_req = {.type = LSP_REQUEST_COMPLETION};
test_req.data.completion = (LSPRequestCompletion){
.position = {
.path = str_dup("/p/test-lsp/src/main.rs"),
@@ -322,7 +322,7 @@ int main(int argc, char **argv) {
if (message.type == LSP_RESPONSE) {
const LSPResponse *response = &message.u.response;
switch (response->type) {
- case LSP_COMPLETION: {
+ case LSP_REQUEST_COMPLETION: {
const LSPResponseCompletion *completion = &response->data.completion;
arr_foreach_ptr(completion->items, LSPCompletionItem, item) {
printf("(%d)%s => ",
@@ -338,12 +338,12 @@ int main(int argc, char **argv) {
} else if (message.type == LSP_REQUEST) {
const LSPRequest *request = &message.u.request;
switch (request->type) {
- case LSP_SHOW_MESSAGE: {
+ case LSP_REQUEST_SHOW_MESSAGE: {
const LSPRequestMessage *m = &request->data.message;
// @TODO actually show
printf("Show (%d): %s\n", m->type, m->message);
} break;
- case LSP_LOG_MESSAGE: {
+ case LSP_REQUEST_LOG_MESSAGE: {
const LSPRequestMessage *m = &request->data.message;
// @TODO actually log
printf("Log (%d): %s\n", m->type, m->message);