diff options
-rw-r--r-- | buffer.c | 15 | ||||
-rw-r--r-- | ide-code-action.c | 33 | ||||
-rw-r--r-- | lsp-parse.c | 6 | ||||
-rw-r--r-- | lsp-write.c | 76 | ||||
-rw-r--r-- | lsp.c | 5 | ||||
-rw-r--r-- | lsp.h | 5 | ||||
-rw-r--r-- | ted-internal.h | 14 |
7 files changed, 132 insertions, 22 deletions
@@ -39,14 +39,6 @@ struct BufferEdit { double time; // time at start of edit (i.e. the time just before the edit), in seconds since epoch }; -typedef struct { - MessageType severity; - BufferPos pos; - char *message; - // may be NULL - char *url; -} Diagnostic; - struct TextBuffer { /// NULL if this buffer is untitled or doesn't correspond to a file (e.g. line buffers) char *path; @@ -1004,6 +996,7 @@ static void buffer_line_free(Line *line) { static void diagnostic_free(Diagnostic *diagnostic) { free(diagnostic->message); free(diagnostic->url); + free(diagnostic->raw); memset(diagnostic, 0, sizeof *diagnostic); } @@ -4245,7 +4238,9 @@ void buffer_publish_diagnostics(TextBuffer *buffer, const LSPRequest *request, L arr_foreach_ptr(diagnostics, const LSPDiagnostic, diagnostic) { Diagnostic *d = arr_addp(buffer->diagnostics); d->pos = buffer_pos_from_lsp(buffer, diagnostic->range.start); + d->end = buffer_pos_from_lsp(buffer, diagnostic->range.end); d->severity = diagnostic_severity(diagnostic); + d->raw = str_dup(lsp_request_string(request, diagnostic->raw)); char message[280]; const char *code = lsp_request_string(request, diagnostic->code); if (*code) { @@ -4287,3 +4282,7 @@ void buffer_set_inotify_modified(TextBuffer *buffer) { buffer->inotify_modified = true; } #endif + +const Diagnostic *buffer_diagnostics(TextBuffer *buffer) { + return buffer->diagnostics; +} diff --git a/ide-code-action.c b/ide-code-action.c index 678c046..13a1b36 100644 --- a/ide-code-action.c +++ b/ide-code-action.c @@ -1,18 +1,47 @@ #include "ted-internal.h" +static bool ranges_touch(BufferPos p1, BufferPos p2, BufferPos q1, BufferPos q2) { + int cmp21 = buffer_pos_cmp(p2, q1); + if (cmp21 < 0) { + // p is entirely before q + return false; + } + int cmp12 = buffer_pos_cmp(p1, q2); + if (cmp12 > 0) { + // p is entirely after q + return false; + } + return true; +} + void code_action_start(Ted *ted) { TextBuffer *buffer = ted_active_buffer(ted); LSP *lsp = buffer_lsp(buffer); + BufferPos range_start = {0}, range_end = {0}; LSPRange range = {0}; - if (buffer_has_selection(buffer)) + if (buffer_selection_pos(buffer, &range_start)) { range = buffer_selection_as_lsp_range(buffer); - else + range_end = buffer_cursor_pos(buffer); + if (buffer_pos_cmp(range_start, range_end) > 0) { + BufferPos tmp = range_start; + range_start = range_end; + range_end = tmp; + } + } else { + range_start = range_end = buffer_cursor_pos(buffer); range.start = range.end = buffer_cursor_pos_as_lsp_position(buffer); + } LSPRequest req = { .type = LSP_REQUEST_CODE_ACTION, }; LSPRequestCodeAction *code_action_req = &req.data.code_action; code_action_req->document = buffer_lsp_document_id(buffer); code_action_req->range = range; + arr_foreach_ptr(buffer_diagnostics(buffer), const Diagnostic, d) { + if (ranges_touch(range_start, range_end, d->pos, d->end)) { + LSPString raw = lsp_request_add_string(&req, d->raw); + arr_add(code_action_req->raw_diagnostics, raw); + } + } lsp_send_request(lsp, &req); } diff --git a/lsp-parse.c b/lsp-parse.c index 3f81a2f..4046f6f 100644 --- a/lsp-parse.c +++ b/lsp-parse.c @@ -800,10 +800,14 @@ static bool parse_publish_diagnostics(LSP *lsp, const JSON *json, LSPRequest *re return false; JSONArray diagnostics = json_object_get_array(json, params, "diagnostics"); for (u32 i = 0; i < diagnostics.len; ++i) { - JSONObject diagnostic_in = json_array_get_object(json, diagnostics, i); + JSONValue diagnostic_val = json_array_get(json, diagnostics, i); + JSONObject diagnostic_in = json_force_object(diagnostic_val); LSPDiagnostic *diagnostic_out = arr_addp(pub->diagnostics); if (!parse_diagnostic(lsp, request, json, diagnostic_in, diagnostic_out)) return false; + char *raw = json_reserialize(json, diagnostic_val); + diagnostic_out->raw = lsp_request_add_string(request, raw); + free(raw); } return true; } diff --git a/lsp-write.c b/lsp-write.c index 23bb14e..c81c574 100644 --- a/lsp-write.c +++ b/lsp-write.c @@ -250,6 +250,68 @@ static void write_document_position(JSONWriter *o, LSPDocumentPosition pos) { write_key_position(o, "position", pos.pos); } + +static void json_reserialize_to(const JSON *json, JSONWriter *o, JSONValue val) { + switch (val.type) { + case JSON_UNDEFINED: + assert(false); + break; + case JSON_TRUE: + write_bool(o, true); + break; + case JSON_FALSE: + write_bool(o, false); + break; + case JSON_NULL: + write_null(o); + break; + case JSON_NUMBER: + write_number(o, val.val.number); + break; + case JSON_STRING: { + char *s = json_string_get_alloc(json, val.val.string); + write_string(o, s); + free(s); + } break; + case JSON_ARRAY: { + JSONArray array = val.val.array; + write_arr_start(o); + for (u32 i = 0; i < array.len; i++) { + JSONValue elem = json_array_get(json, array, i); + write_arr_elem(o); + json_reserialize_to(json, o, elem); + } + write_arr_end(o); + } break; + case JSON_OBJECT: { + JSONObject object = val.val.object; + write_obj_start(o); + JSONValue *keys = &json->values[object.items]; + JSONValue *values = keys + object.len; + for (u32 i = 0; i < object.len; i++) { + JSONValue key_val = keys[i]; + assert(key_val.type == JSON_STRING); + JSONString key = key_val.val.string; + JSONValue value = values[i]; + char *key_str = json_string_get_alloc(json, key); + write_key(o, key_str); + free(key_str); + json_reserialize_to(json, o, value); + } + write_obj_end(o); + } break; + } +} + +char *json_reserialize(const JSON *json, JSONValue value) { + StrBuilder builder = str_builder_new(); + JSONWriter writer = json_writer_new(NULL, &builder); + json_reserialize_to(json, &writer, value); + char *str = str_dup(builder.str); + str_builder_free(&builder); + return str; +} + static const char *lsp_request_method(LSPRequest *request) { switch (request->type) { case LSP_REQUEST_NONE: break; @@ -698,16 +760,10 @@ void write_request(LSP *lsp, LSPRequest *request, StrBuilder *builder) { write_key_range(o, "range", code_action->range); write_key_obj_start(o, "context"); write_key_arr_start(o, "diagnostics"); - write_arr_end(o); - write_key_arr_start(o, "only"); - write_arr_elem_string(o, "quickfix"); - write_arr_elem_string(o, "refactor"); - write_arr_elem_string(o, "refactor.extract"); - write_arr_elem_string(o, "refactor.inline"); - write_arr_elem_string(o, "refactor.rewrite"); - write_arr_elem_string(o, "source"); - write_arr_elem_string(o, "source.organizeImports"); - write_arr_elem_string(o, "source.fixAll"); + arr_foreach_ptr(code_action->raw_diagnostics, const LSPString, raw_diagnostic) { + write_arr_elem(o); + str_builder_append(o->builder, lsp_request_string(request, *raw_diagnostic)); + } write_arr_end(o); write_obj_end(o); write_obj_end(o); @@ -125,7 +125,6 @@ void lsp_request_free(LSPRequest *r) { case LSP_REQUEST_DID_OPEN: case LSP_REQUEST_FORMATTING: case LSP_REQUEST_RANGE_FORMATTING: - case LSP_REQUEST_CODE_ACTION: break; case LSP_REQUEST_PUBLISH_DIAGNOSTICS: { LSPRequestPublishDiagnostics *pub = &r->data.publish_diagnostics; @@ -137,6 +136,10 @@ void lsp_request_free(LSPRequest *r) { case LSP_REQUEST_PREPARE_RENAME: case LSP_REQUEST_WORKSPACE_SYMBOLS: break; + case LSP_REQUEST_CODE_ACTION: { + LSPRequestCodeAction *c = &r->data.code_action; + arr_free(c->raw_diagnostics); + } break; case LSP_REQUEST_DID_CHANGE: { LSPRequestDidChange *c = &r->data.change; arr_free(c->changes); @@ -171,6 +171,8 @@ typedef struct { /// URI to description of code /// e.g. for Rust's E0621, this would be https://doc.rust-lang.org/error_codes/E0621.html LSPString code_description_uri; + // raw diagnostic object, to be used with textDocument/codeAction + LSPString raw; } LSPDiagnostic; typedef struct { @@ -262,6 +264,7 @@ typedef struct { typedef struct { LSPDocumentID document; LSPRange range; + LSPString *raw_diagnostics; } LSPRequestCodeAction; typedef struct { @@ -945,6 +948,8 @@ LSPString lsp_response_add_json_string(LSPResponse *response, const JSON *json, LSPString lsp_request_add_json_string(LSPRequest *request, const JSON *json, JSONString string); /// free resources used by lsp-write.c void lsp_write_quit(void); +// convert JSON value back into string +char *json_reserialize(const JSON *json, JSONValue value); /// print server-to-client communication #define LSP_SHOW_S2C 1 diff --git a/ted-internal.h b/ted-internal.h index 3e8da9f..a4b5574 100644 --- a/ted-internal.h +++ b/ted-internal.h @@ -450,6 +450,19 @@ struct Ted { EditNotifyInfo *edit_notifys; }; +typedef struct { + MessageType severity; + // start position + BufferPos pos; + // end position + BufferPos end; + char *message; + // may be NULL + char *url; + // raw JSON object (for textDocument/codeAction) + char *raw; +} Diagnostic; + // === buffer.c === /// create a new empty buffer with no file name TextBuffer *buffer_new(Ted *ted); @@ -508,6 +521,7 @@ int buffer_inotify_watch(TextBuffer *buffer); /// inform buffer that its watch on the Ted's inotify was modified void buffer_set_inotify_modified(TextBuffer *buffer); #endif +const Diagnostic *buffer_diagnostics(TextBuffer *buffer); // === build.c === void build_frame(Ted *ted, float x1, float y1, float x2, float y2); |