diff options
author | pommicket <pommicket@gmail.com> | 2022-12-22 18:28:15 -0500 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2022-12-22 18:28:15 -0500 |
commit | 2667c71e71d77ecade1142c133ed7181ce38c664 (patch) | |
tree | ef65d210d12f9c80e5a6a6965d315810d33375f8 /lsp.c | |
parent | 6205e83793eff547d404fa5bde747076a1191893 (diff) |
reorganize lsp code
Diffstat (limited to 'lsp.c')
-rw-r--r-- | lsp.c | 345 |
1 files changed, 9 insertions, 336 deletions
@@ -1,5 +1,13 @@ static void lsp_request_free(LSPRequest *r); -#include "lsp-write-request.c" +static void lsp_response_free(LSPResponse *r); + +#define lsp_set_error(lsp, ...) do {\ + SDL_LockMutex(lsp->error_mutex);\ + strbuf_printf(lsp->error, __VA_ARGS__);\ + SDL_UnlockMutex(lsp->error_mutex);\ + } while (0) +#include "lsp-write.c" +#include "lsp-parse.c" bool lsp_get_error(LSP *lsp, char *error, size_t error_size, bool clear) { bool has_err = false; @@ -13,12 +21,6 @@ bool lsp_get_error(LSP *lsp, char *error, size_t error_size, bool clear) { 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 lsp_document_change_event_free(LSPDocumentChangeEvent *event) { free(event->text); @@ -75,32 +77,6 @@ void lsp_message_free(LSPMessage *message) { memset(message, 0, sizeof *message); } -static WarnUnusedResult bool lsp_expect_type(LSP *lsp, JSONValue value, JSONValueType type, const char *what) { - if (value.type != type) { - lsp_set_error(lsp, "Expected %s for %s, got %s", - json_type_to_str(type), - what, - json_type_to_str(value.type)); - return false; - } - return true; -} - -static WarnUnusedResult bool lsp_expect_object(LSP *lsp, JSONValue value, const char *what) { - return lsp_expect_type(lsp, value, JSON_OBJECT, what); -} - -static WarnUnusedResult bool lsp_expect_array(LSP *lsp, JSONValue value, const char *what) { - return lsp_expect_type(lsp, value, JSON_ARRAY, what); -} - -static WarnUnusedResult bool lsp_expect_string(LSP *lsp, JSONValue value, const char *what) { - return lsp_expect_type(lsp, value, JSON_STRING, what); -} - -static WarnUnusedResult bool lsp_expect_number(LSP *lsp, JSONValue value, const char *what) { - return lsp_expect_type(lsp, value, JSON_NUMBER, what); -} // figure out if data begins with a complete LSP response. static bool has_response(const char *data, size_t data_len, u64 *p_offset, u64 *p_size) { @@ -125,314 +101,11 @@ void lsp_send_request(LSP *lsp, const LSPRequest *request) { SDL_UnlockMutex(lsp->requests_mutex); } -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")) - return false; - - char method[64] = {0}; - json_string_get(json, method_value.val.string, method, sizeof method); - - if (streq(method, "window/showMessage")) { - request->type = LSP_REQUEST_SHOW_MESSAGE; - goto window_message; - } else if (streq(method, "window/logMessage")) { - request->type = LSP_REQUEST_LOG_MESSAGE; - window_message:; - JSONValue type = json_get(json, "params.type"); - JSONValue message = json_get(json, "params.message"); - if (!lsp_expect_number(lsp, type, "MessageType")) - return false; - if (!lsp_expect_string(lsp, message, "message string")) - return false; - - int mtype = (int)type.val.number; - if (mtype < 1 || mtype > 4) { - lsp_set_error(lsp, "Bad MessageType: %g", type.val.number); - return false; - } - - LSPRequestMessage *m = &request->data.message; - m->type = (LSPWindowMessageType)mtype; - m->message = json_string_get_alloc(json, message.val.string); - return true; - } else if (str_has_prefix(method, "$/")) { - // we can safely ignore this - } else { - lsp_set_error(lsp, "Unrecognized request method: %s", method); - } - return false; -} - const char *lsp_response_string(const LSPResponse *response, LSPString string) { assert(string.offset < arr_len(response->string_data)); return &response->string_data[string.offset]; } -static LSPString lsp_response_add_json_string(LSPResponse *response, const JSON *json, JSONString string) { - u32 offset = arr_len(response->string_data); - arr_set_len(response->string_data, offset + string.len + 1); - json_string_get(json, string, response->string_data + offset, string.len + 1); - return (LSPString){ - .offset = offset - }; -} - -static int completion_qsort_cmp(void *context, const void *av, const void *bv) { - const LSPResponse *response = context; - const LSPCompletionItem *a = av, *b = bv; - const char *a_sort_text = lsp_response_string(response, a->sort_text); - const char *b_sort_text = lsp_response_string(response, b->sort_text); - int sort_text_cmp = strcmp(a_sort_text, b_sort_text); - if (sort_text_cmp != 0) - return sort_text_cmp; - // for some reason, rust-analyzer outputs identical sortTexts - // i have no clue what that means. - // the LSP "specification" is not very specific. - // we'll sort by label in this case. - // this is what VSCode seems to do. - // i hate microsofot. - const char *a_label = lsp_response_string(response, a->label); - const char *b_label = lsp_response_string(response, b->label); - return strcmp(a_label, b_label); -} - -static bool parse_position(LSP *lsp, const JSON *json, JSONValue pos_value, LSPPosition *pos) { - if (!lsp_expect_object(lsp, pos_value, "document position")) - return false; - JSONObject pos_object = pos_value.val.object; - JSONValue line = json_object_get(json, pos_object, "line"); - JSONValue character = json_object_get(json, pos_object, "character"); - if (!lsp_expect_number(lsp, line, "document line number") - || !lsp_expect_number(lsp, character, "document column number")) - return false; - pos->line = (u32)line.val.number; - pos->character = (u32)line.val.number; - return true; -} - -static bool parse_range(LSP *lsp, const JSON *json, JSONValue range_value, LSPRange *range) { - if (!lsp_expect_object(lsp, range_value, "document range")) - return false; - JSONObject range_object = range_value.val.object; - JSONValue start = json_object_get(json, range_object, "start"); - JSONValue end = json_object_get(json, range_object, "end"); - return parse_position(lsp, json, start, &range->start) - && parse_position(lsp, json, end, &range->end); -} - -static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response) { - // deal with textDocument/completion response. - // result: CompletionItem[] | CompletionList | null - LSPResponseCompletion *completion = &response->data.completion; - - JSONValue result = json_get(json, "result"); - JSONValue items_value = {0}; - switch (result.type) { - case JSON_NULL: - // no completions - return true; - case JSON_ARRAY: - items_value = result; - break; - case JSON_OBJECT: - items_value = json_object_get(json, result.val.object, "items"); - break; - default: - lsp_set_error(lsp, "Weird result type for textDocument/completion response: %s.", json_type_to_str(result.type)); - break; - } - - if (!lsp_expect_array(lsp, items_value, "completion list")) - return false; - - JSONArray items = items_value.val.array; - - arr_set_len(completion->items, items.len); - - for (u32 i = 0; i < items.len; ++i) { - LSPCompletionItem *item = &completion->items[i]; - - JSONValue item_value = json_array_get(json, items, i); - if (!lsp_expect_object(lsp, item_value, "completion list")) - return false; - JSONObject item_object = item_value.val.object; - - JSONValue label_value = json_object_get(json, item_object, "label"); - if (!lsp_expect_string(lsp, label_value, "completion label")) - return false; - JSONString label = label_value.val.string; - item->label = lsp_response_add_json_string(response, json, label); - - // defaults - item->sort_text = item->label; - item->filter_text = item->label; - item->text_edit = (LSPTextEdit) { - .type = LSP_TEXT_EDIT_PLAIN, - .at_cursor = true, - .range = {0}, - .new_text = item->label - }; - - JSONValue sort_text_value = json_object_get(json, item_object, "sortText"); - if (sort_text_value.type == JSON_STRING) { - // LSP allows using a different string for sorting. - item->sort_text = lsp_response_add_json_string(response, - json, sort_text_value.val.string); - } - - JSONValue filter_text_value = json_object_get(json, item_object, "filterText"); - if (filter_text_value.type == JSON_STRING) { - // LSP allows using a different string for filtering. - item->filter_text = lsp_response_add_json_string(response, - json, filter_text_value.val.string); - } - - JSONValue text_type_value = json_object_get(json, item_object, "insertTextFormat"); - if (text_type_value.type == JSON_NUMBER) { - double type = text_type_value.val.number; - if (type != LSP_TEXT_EDIT_PLAIN && type != LSP_TEXT_EDIT_SNIPPET) { - lsp_set_error(lsp, "Bad InsertTextFormat: %g", type); - return false; - } - item->text_edit.type = (LSPTextEditType)type; - } - - // @TODO: detail - - // @TODO(eventually): additionalTextEdits - // (try to find a case where this comes up) - - // what should happen when this completion is selected? - JSONValue text_edit_value = json_object_get(json, item_object, "textEdit"); - if (text_edit_value.type == JSON_OBJECT) { - JSONObject text_edit = text_edit_value.val.object; - item->text_edit.at_cursor = false; - - JSONValue range = json_object_get(json, text_edit, "range"); - if (!parse_range(lsp, json, range, &item->text_edit.range)) - return false; - - JSONValue new_text_value = json_object_get(json, text_edit, "newText"); - if (!lsp_expect_string(lsp, new_text_value, "completion newText")) - return false; - item->text_edit.new_text = lsp_response_add_json_string(response, - json, new_text_value.val.string); - } else { - // not using textEdit. check insertText. - JSONValue insert_text_value = json_object_get(json, item_object, "insertText"); - if (insert_text_value.type == JSON_STRING) { - // string which will be inserted if this completion is selected - item->text_edit.new_text = lsp_response_add_json_string(response, - json, insert_text_value.val.string); - } - } - - } - - qsort_with_context(completion->items, items.len, sizeof *completion->items, - completion_qsort_cmp, response); - - return true; -} - - -static void process_message(LSP *lsp, JSON *json) { - - #if 0 - printf("\x1b[3m"); - 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);; - - 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); - // don't free - memset(&response_to, 0, sizeof response_to); - } - } else { - lsp_set_error(lsp, "%s", err); - } - goto ret; - } - - JSONValue result = json_get(json, "result"); - if (result.type != JSON_UNDEFINED) { - 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_REQUEST_INITIALIZED, - .data = {0}, - }; - write_request(lsp, &initialized); - // we can now send requests which have nothing to do with initialization - lsp->initialized = true; - } else { - LSPResponse response = {0}; - bool success = false; - response.request = response_to; - switch (response_to.type) { - case LSP_REQUEST_COMPLETION: - success = parse_completion(lsp, json, &response); - break; - default: - // it's some response we don't care about - break; - } - if (success) { - SDL_LockMutex(lsp->messages_mutex); - LSPMessage *message = arr_addp(lsp->messages); - message->type = LSP_RESPONSE; - message->u.response = response; - SDL_UnlockMutex(lsp->messages_mutex); - response_to.type = 0; // don't free - } else { - lsp_response_free(&response); - } - } - } else if (json_has(json, "method")) { - LSPRequest request = {0}; - if (parse_server2client_request(lsp, json, &request)) { - SDL_LockMutex(lsp->messages_mutex); - LSPMessage *message = arr_addp(lsp->messages); - message->type = LSP_REQUEST; - message->u.request = request; - SDL_UnlockMutex(lsp->messages_mutex); - } - } else { - lsp_set_error(lsp, "Bad message from server (no result, no method)."); - } - ret: - lsp_request_free(&response_to); - json_free(json); - -} - // receive responses/requests/notifications from LSP, up to max_size bytes. static void lsp_receive(LSP *lsp, size_t max_size) { |