From 27c9af7de5f47c33217be02107e31061366f481f Mon Sep 17 00:00:00 2001 From: pommicket Date: Thu, 8 Dec 2022 11:20:54 -0500 Subject: fix response receiving --- json.c | 32 ++++++++++--------- lsp.c | 109 +++++++++++++++++++++++++++++++++++++++++++++-------------------- main.c | 9 ++++-- 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); -- cgit v1.2.3