From 6c4baeebd88352399059a8df8d38ebe3511588ea Mon Sep 17 00:00:00 2001 From: pommicket Date: Mon, 29 Sep 2025 13:29:56 -0400 Subject: Start code actions --- CMakeLists.txt | 2 +- command.c | 4 ++++ command.h | 2 ++ ide-code-action.c | 18 ++++++++++++++++++ lsp-parse.c | 5 +++++ lsp-write.c | 28 ++++++++++++++++++++++++++++ lsp.c | 4 ++++ lsp.h | 12 ++++++++++-- main.c | 1 + ted-internal.h | 3 +++ 10 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 ide-code-action.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 99c539e..1896751 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.5...3.25) project(ted) if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(SOURCES buffer.c build.c colors.c command.c config.c find.c gl.c ide-autocomplete.c - ide-document-link.c ide-definitions.c ide-format.c ide-highlights.c ide-hover.c + ide-document-link.c ide-definitions.c ide-format.c ide-highlights.c ide-hover.c ide-code-action.c ide-signature-help.c ide-usages.c ide-rename-symbol.c lsp.c lsp-json.c lsp-parse.c lsp-write.c main.c menu.c node.c os.c session.c stb_truetype.c syntax.c tags.c ted.c text.c ui.c util.c macro.c) diff --git a/command.c b/command.c index 3b647f4..66ae4bb 100644 --- a/command.c +++ b/command.c @@ -111,6 +111,7 @@ static CommandName command_names[] = { {"indent-with-tabs", CMD_INDENT_WITH_TABS}, {"set-tab-width", CMD_SET_TAB_WIDTH}, {"debug-print-undo-history", CMD_DEBUG_PRINT_UNDO_HISTORY}, + {"code-action", CMD_CODE_ACTION}, }; static_assert_if_possible(arr_count(command_names) == CMD_COUNT) @@ -740,5 +741,8 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen case CMD_DEBUG_PRINT_UNDO_HISTORY: buffer_print_undo_history(buffer); break; + case CMD_CODE_ACTION: + code_action_start(ted); + break; } } diff --git a/command.h b/command.h index 400dab1..2f60a7a 100644 --- a/command.h +++ b/command.h @@ -107,6 +107,8 @@ typedef enum { CMD_AUTOCOMPLETE_BACK, CMD_FIND_USAGES, CMD_RENAME_SYMBOL, + CMD_CODE_ACTION, + /// "go to definition of..." menu CMD_GOTO_DEFINITION, CMD_GOTO_DEFINITION_AT_CURSOR, diff --git a/ide-code-action.c b/ide-code-action.c new file mode 100644 index 0000000..678c046 --- /dev/null +++ b/ide-code-action.c @@ -0,0 +1,18 @@ +#include "ted-internal.h" + +void code_action_start(Ted *ted) { + TextBuffer *buffer = ted_active_buffer(ted); + LSP *lsp = buffer_lsp(buffer); + LSPRange range = {0}; + if (buffer_has_selection(buffer)) + range = buffer_selection_as_lsp_range(buffer); + else + 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; + lsp_send_request(lsp, &req); +} diff --git a/lsp-parse.c b/lsp-parse.c index 63d5605..3f81a2f 100644 --- a/lsp-parse.c +++ b/lsp-parse.c @@ -280,6 +280,11 @@ static void parse_capabilities(LSP *lsp, const JSON *json, JSONObject capabiliti cap->range_formatting_support = true; } + JSONValue code_action_value = json_object_get(json, capabilities, "codeActionProvider"); + if (code_action_value.type == JSON_OBJECT || code_action_value.type == JSON_TRUE) { + cap->code_action_support = true; + } + JSONObject workspace = json_object_get_object(json, capabilities, "workspace"); // check WorkspaceFoldersServerCapabilities JSONObject workspace_folders = json_object_get_object(json, workspace, "workspaceFolders"); diff --git a/lsp-write.c b/lsp-write.c index c1c846a..e4caf4f 100644 --- a/lsp-write.c +++ b/lsp-write.c @@ -311,6 +311,8 @@ static const char *lsp_request_method(LSPRequest *request) { return "textDocument/rangeFormatting"; case LSP_REQUEST_FORMATTING: return "textDocument/formatting"; + case LSP_REQUEST_CODE_ACTION: + return "textDocument/codeAction"; } assert(0); return "$/ignore"; @@ -468,6 +470,9 @@ void write_request(LSP *lsp, LSPRequest *request, StrBuilder *builder) { write_key_obj_start(o, "publishDiagnostics"); write_key_bool(o, "codeDescriptionSupport", true); write_obj_end(o); + + write_key_obj_start(o, "codeAction"); + write_obj_end(o); write_obj_end(o); write_key_obj_start(o, "workspace"); write_key_bool(o, "workspaceFolders", true); @@ -670,6 +675,29 @@ void write_request(LSP *lsp, LSPRequest *request, StrBuilder *builder) { } write_obj_end(o); } break; + case LSP_REQUEST_CODE_ACTION: { + LSPRequestCodeAction *code_action = &request->data.code_action; + write_key_obj_start(o, "params"); + write_key_obj_start(o, "textDocument"); + write_key_file_uri(o, "uri", code_action->document); + write_obj_end(o); + 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"); + write_arr_end(o); + write_obj_end(o); + write_obj_end(o); + } break; } write_obj_end(o); diff --git a/lsp.c b/lsp.c index 2004bb7..4337099 100644 --- a/lsp.c +++ b/lsp.c @@ -125,6 +125,7 @@ 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; @@ -237,6 +238,8 @@ static bool lsp_supports_request(LSP *lsp, const LSPRequest *request) { return cap->open_close_support; case LSP_REQUEST_DID_CHANGE: return cap->sync_support; + case LSP_REQUEST_CODE_ACTION: + return cap->code_action_support; case LSP_REQUEST_INITIALIZE: case LSP_REQUEST_INITIALIZED: case LSP_REQUEST_CANCEL: @@ -320,6 +323,7 @@ static bool request_type_is_notification(LSPRequestType type) { case LSP_REQUEST_DOCUMENT_LINK: case LSP_REQUEST_FORMATTING: case LSP_REQUEST_RANGE_FORMATTING: + case LSP_REQUEST_CODE_ACTION: return false; } assert(0); diff --git a/lsp.h b/lsp.h index c267283..21b2dda 100644 --- a/lsp.h +++ b/lsp.h @@ -97,6 +97,7 @@ typedef enum { LSP_REQUEST_LOG_MESSAGE, //< window/logMessage LSP_REQUEST_WORKSPACE_FOLDERS, //< workspace/workspaceFolders - NOTE: this is handled directly in lsp-parse.c (because it only needs information from the LSP struct) LSP_REQUEST_PUBLISH_DIAGNOSTICS, //< textDocument/publishDiagnostics + LSP_REQUEST_CODE_ACTION, //< textDocument/codeAction } LSPRequestType; typedef enum { @@ -258,6 +259,11 @@ typedef struct { LSPRange range; } LSPRequestFormatting; +typedef struct { + LSPDocumentID document; + LSPRange range; +} LSPRequestCodeAction; + typedef struct { LSPMessageType type; /// LSP requests/responses tend to have a lot of strings. @@ -296,6 +302,7 @@ typedef struct { LSPRequestPublishDiagnostics publish_diagnostics; // LSP_REQUEST_FORMATTING and LSP_REQUEST_RANGE_FORMATTING LSPRequestFormatting formatting; + LSPRequestCodeAction code_action; } data; } LSPRequest; @@ -629,6 +636,7 @@ typedef struct { bool document_link_support; bool formatting_support; bool range_formatting_support; + bool code_action_support; } LSPCapabilities; typedef struct LSP LSP; @@ -939,8 +947,8 @@ LSPString lsp_request_add_json_string(LSPRequest *request, const JSON *json, JSO void lsp_write_quit(void); /// print server-to-client communication -#define LSP_SHOW_S2C 0 +#define LSP_SHOW_S2C 1 /// print client-to-server communication -#define LSP_SHOW_C2S 0 +#define LSP_SHOW_C2S 1 #endif // LSP_INTERNAL diff --git a/main.c b/main.c index 0d0732c..425984b 100644 --- a/main.c +++ b/main.c @@ -95,6 +95,7 @@ the first character can be interpreted specially if it is one of the following: #include "menu.c" #include "ide-autocomplete.c" #include "ide-signature-help.c" +#include "ide-code-action.c" #include "ide-rename-symbol.c" #include "ide-hover.c" #include "ide-definitions.c" diff --git a/ted-internal.h b/ted-internal.h index a9de122..3e8da9f 100644 --- a/ted-internal.h +++ b/ted-internal.h @@ -634,6 +634,9 @@ void autocomplete_quit(Ted *ted); void autocomplete_frame(Ted *ted); void autocomplete_process_lsp_response(Ted *ted, const LSPResponse *response); +// === ide-code-action.c === +void code_action_start(Ted *ted); + // === ide-definitions.c === void definitions_init(Ted *ted); /// go to the definition of `name`. -- cgit v1.2.3