summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--json.c32
-rw-r--r--lsp.c109
-rw-r--r--main.c9
3 files changed, 100 insertions, 50 deletions
diff --git a/json.c b/json.c
index e63bc64..4835eb9 100644
--- a/json.c
+++ b/json.c
@@ -1,6 +1,7 @@
// JSON parser for LSP
// provides FAST(ish) parsing but SLOW lookup
// this is especially fast for small objects
+// this actually supports "extended json", where objects can have arbitrary values as keys.
typedef struct {
u32 pos;
@@ -325,6 +326,8 @@ static bool json_parse_value(JSON *json, u32 *p_index, JSONValue *val) {
return false;
break;
case ANY_DIGIT:
+ case '-':
+ case '+':
val->type = JSON_NUMBER;
if (!json_parse_number(json, &index, &val->val.number))
return false;
@@ -355,8 +358,17 @@ static bool json_parse_value(JSON *json, u32 *p_index, JSONValue *val) {
return true;
}
+void json_free(JSON *json) {
+ arr_free(json->values);
+ // important we don't zero json here because we want to preserve json->error.
+ if (json->is_text_copied) {
+ free((void*)json->text);
+ }
+ json->text = NULL;
+}
+
// NOTE: text must live as long as json!!!
-static bool json_parse(JSON *json, const char *text) {
+bool json_parse(JSON *json, const char *text) {
memset(json, 0, sizeof *json);
json->text = text;
arr_reserve(json->values, strlen(text) / 8);
@@ -364,12 +376,12 @@ static bool json_parse(JSON *json, const char *text) {
JSONValue val = {0};
u32 index = 0;
if (!json_parse_value(json, &index, &val)) {
- arr_free(json->values);
+ json_free(json);
return false;
}
SKIP_WHITESPACE;
if (text[index]) {
- arr_free(json->values);
+ json_free(json);
strbuf_printf(json->error, "extra text after end of root object");
return false;
}
@@ -378,7 +390,7 @@ static bool json_parse(JSON *json, const char *text) {
}
// like json_parse, but a copy of text is made, so you can free/overwrite it immediately.
-static bool json_parse_copy(JSON *json, const char *text) {
+bool json_parse_copy(JSON *json, const char *text) {
bool success = json_parse(json, str_dup(text));
if (success) {
json->is_text_copied = true;
@@ -390,14 +402,6 @@ static bool json_parse_copy(JSON *json, const char *text) {
}
}
-static void json_free(JSON *json) {
- arr_free(json->values);
- memset(json, 0, sizeof *json);
- if (json->is_text_copied) {
- free((void*)json->text);
- }
-}
-
static bool json_streq(const JSON *json, const JSONString *string, const char *name) {
const char *p = &json->text[string->pos];
const char *end = p + string->len;
@@ -409,7 +413,7 @@ static bool json_streq(const JSON *json, const JSONString *string, const char *n
}
// returns undefined if the property `name` does not exist.
-static JSONValue json_object_get(const JSON *json, const JSONObject *object, const char *name) {
+JSONValue json_object_get(const JSON *json, const JSONObject *object, const char *name) {
const JSONValue *items = &json->values[object->items];
for (u32 i = 0; i < object->len; ++i) {
const JSONValue *this_name = items++;
@@ -422,7 +426,7 @@ static JSONValue json_object_get(const JSON *json, const JSONObject *object, con
// e.g. if json is { "a" : { "b": 3 }}, then json_get(json, "a.b") = 3.
// returns undefined if there is no such property
-static JSONValue json_get(const JSON *json, const char *path) {
+JSONValue json_get(const JSON *json, const char *path) {
char segment[128];
const char *p = path;
if (!json->values) {
diff --git a/lsp.c b/lsp.c
index 980a2b2..8b62853 100644
--- a/lsp.c
+++ b/lsp.c
@@ -36,9 +36,31 @@ typedef struct {
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);
+ has_err = *lsp->error != '\0';
+ if (error_size)
+ str_cpy(error, error_size, lsp->error);
+ if (clear)
+ *lsp->error = '\0';
+ SDL_UnlockMutex(lsp->error_mutex);
+ return has_err;
+}
+
+#define lsp_set_error(lsp, ...) do {\
+ SDL_LockMutex(lsp->error_mutex);\
+ strbuf_printf(lsp->error, __VA_ARGS__);\
+ SDL_UnlockMutex(lsp->error_mutex);\
+ } while (0)
static void write_request_content(LSP *lsp, const char *content) {
char header[128];
@@ -136,7 +158,7 @@ static u64 write_request(LSP *lsp, const LSPRequest *request) {
char content[1024];
// no params needed
strbuf_printf(content,
- "{\"jsonrpc\":\"2.0\",\"id\":%llu,\"method\":\"initialize\",\"params\":{"
+ "{\"jsonrpc\":\"2.0\",\"id\":%llu,\"method\":\"textDocument/completion\",\"params\":{"
"}}", id);
write_request_content(lsp, content);
} break;
@@ -177,6 +199,50 @@ void lsp_send_request(LSP *lsp, const LSPRequest *request) {
SDL_UnlockMutex(lsp->requests_mutex);
}
+static void process_response(LSP *lsp, const JSON *json) {
+
+ #if 1
+ printf("\x1b[3m");
+ json_debug_print(json);
+ printf("\x1b[0m\n");
+ #endif
+
+
+ JSONValue result = json_get(json, "result");
+ JSONValue id = json_get(json, "id");
+
+ if (result.type == JSON_UNDEFINED || id.type != JSON_NUMBER) {
+ // uh oh
+ 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);
+ } else {
+ lsp_set_error(lsp, "Server error (no message)");
+ }
+ } else if (id.val.number == 0) {
+ // 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,
+ .data = {0},
+ };
+ u64 initialized_id = write_request(lsp, &initialized);
+ // this should be the second request.
+ (void)initialized_id;
+ assert(initialized_id == 1);
+ // we can now send requests which have nothing to do with initialization
+ lsp->initialized = true;
+ } else {
+ SDL_LockMutex(lsp->responses_mutex);
+ arr_add(lsp->responses, *json);
+ SDL_UnlockMutex(lsp->responses_mutex);
+ }
+
+}
+
// receive responses from LSP, up to max_size bytes.
static void lsp_receive(LSP *lsp, size_t max_size) {
@@ -214,41 +280,15 @@ static void lsp_receive(LSP *lsp, size_t max_size) {
while (has_response(lsp->received_data, received_so_far, &response_offset, &response_size)) {
char *copy = strn_dup(lsp->received_data + response_offset, response_size);
JSON json = {0};
- json_parse(&json, copy);
- assert(json.text == copy);
- json.is_text_copied = true;
-
- JSONValue result = json_get(&json, "result");
- JSONValue id = json_get(&json, "id");
-
- if (result.type == JSON_UNDEFINED || id.type != JSON_NUMBER) {
- // uh oh
- JSONValue error = json_get(&json, "error.message");
- if (error.type == JSON_STRING) {
- json_string_get(&json, &error.val.string, lsp->error, sizeof lsp->error);
- } else {
- strbuf_printf(lsp->error, "Server error (no message)");
- }
- } else if (id.val.number == 0) {
- // 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,
- .data = {0},
- };
- u64 initialized_id = write_request(lsp, &initialized);
- // this should be the second request.
- (void)initialized_id;
- assert(initialized_id == 1);
- // we can now send requests which have nothing to do with initialization
- lsp->initialized = true;
+ if (json_parse(&json, copy)) {
+ assert(json.text == copy);
+ json.is_text_copied = true;
+ process_response(lsp, &json);
} else {
- SDL_LockMutex(lsp->responses_mutex);
- arr_add(lsp->responses, json);
- SDL_UnlockMutex(lsp->responses_mutex);
+
+ lsp_set_error(lsp, "couldn't parse response JSON: %s", json.error);
+ json_free(&json);
}
-
size_t leftover_data_len = arr_len(lsp->received_data) - (response_offset + response_size);
memmove(lsp->received_data, lsp->received_data + response_offset + response_size,
leftover_data_len);
@@ -289,6 +329,7 @@ static bool lsp_send(LSP *lsp) {
size_t n_requests = arr_len(lsp->requests);
requests = calloc(n_requests, sizeof *requests);
memcpy(requests, lsp->requests, n_requests * sizeof *requests);
+ arr_clear(lsp->requests);
SDL_UnlockMutex(lsp->requests_mutex);
bool quit = false;
diff --git a/main.c b/main.c
index 23b6119..82a0ef1 100644
--- a/main.c
+++ b/main.c
@@ -292,8 +292,9 @@ int main(int argc, char **argv) {
printf("lsp_create: %s\n",lsp.error);
exit(1);
}
-// LSPRequest test_req = {LSP_COMPLETION};
-// lsp_send_request(&lsp, &test_req);
+ usleep(500000);//if we don't do this we get "waiting for cargo metadata or cargo check"
+ LSPRequest test_req = {.type = LSP_COMPLETION};
+ lsp_send_request(&lsp, &test_req);
while (1) {
JSON response = {0};
if (lsp_next_response(&lsp, &response)) {
@@ -301,6 +302,10 @@ int main(int argc, char **argv) {
printf("\n");
break;
}
+ char error[256];
+ if (lsp_get_error(&lsp, error, sizeof error, true)) {
+ printf("lsp error: %s\n", error);
+ }
usleep(10000);
}
lsp_free(&lsp);