summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2025-03-05 20:31:30 -0500
committerpommicket <pommicket@gmail.com>2025-03-05 20:48:59 -0500
commit8de9b9568caf088e9f75d880fae7105661d9e0dc (patch)
tree696b5447b6da7b5f3a816f3bed5f5cae2ad5bcd3
parent4d7533828738401b76bab5be2e22f0d4a69e30b1 (diff)
add support for textDocument/prepareRename
-rw-r--r--command.c5
-rw-r--r--ide-rename-symbol.c56
-rw-r--r--lsp-parse.c38
-rw-r--r--lsp-write.c8
-rw-r--r--lsp.c8
-rw-r--r--lsp.h17
-rw-r--r--main.c1
-rw-r--r--ted.h2
8 files changed, 125 insertions, 10 deletions
diff --git a/command.c b/command.c
index c1e9e3a..4f8e7ea 100644
--- a/command.c
+++ b/command.c
@@ -705,8 +705,9 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen
macro_execute(ted, (u32)argument);
break;
case CMD_RENAME_SYMBOL:
- if (buffer && buffer_lsp(buffer))
- menu_open(ted, MENU_RENAME_SYMBOL);
+ if (buffer && buffer_lsp(buffer)) {
+ rename_symbol_start(ted);
+ }
break;
case CMD_FORMAT_FILE:
format_file(ted);
diff --git a/ide-rename-symbol.c b/ide-rename-symbol.c
index ff96311..dbbed0f 100644
--- a/ide-rename-symbol.c
+++ b/ide-rename-symbol.c
@@ -2,11 +2,17 @@
struct RenameSymbol {
LSPServerRequestID request_id;
+ bool sent_rename;
+ bool have_range;
+ BufferPos range_start;
+ BufferPos range_end;
};
static void rename_symbol_clear(Ted *ted) {
RenameSymbol *rs = ted->rename_symbol;
ted_cancel_lsp_request(ted, &rs->request_id);
+ rs->sent_rename = false;
+ rs->have_range = false;
}
void rename_symbol_quit(Ted *ted) {
@@ -15,21 +21,37 @@ void rename_symbol_quit(Ted *ted) {
ted->rename_symbol = NULL;
}
+void rename_symbol_start(Ted *ted) {
+ RenameSymbol *rs = ted->rename_symbol;
+ TextBuffer *buffer = ted_active_buffer(ted);
+ LSP *lsp = ted_active_lsp(ted);
+ if (!buffer || !lsp) {
+ ted_flash_error_cursor(ted);
+ return;
+ }
+ if (lsp_has_prepare_rename(lsp)) {
+ LSPRequest request = {.type = LSP_REQUEST_PREPARE_RENAME};
+ LSPRequestPrepareRename *data = &request.data.prepare_rename;
+ data->position = buffer_cursor_pos_as_lsp_document_position(buffer);
+ rs->request_id = lsp_send_request(lsp, &request);
+ }
+ menu_open(ted, MENU_RENAME_SYMBOL);
+}
+
void rename_symbol_at_cursor(Ted *ted, TextBuffer *buffer, const char *new_name) {
if (!buffer) return;
RenameSymbol *rs = ted->rename_symbol;
LSP *lsp = buffer_lsp(buffer);
if (!lsp) return;
-
- if (!rs->request_id.id) {
+ if (!rs->sent_rename) {
// send the request
LSPRequest request = {.type = LSP_REQUEST_RENAME};
LSPRequestRename *data = &request.data.rename;
data->position = buffer_cursor_pos_as_lsp_document_position(buffer);
data->new_name = lsp_request_add_string(&request, new_name);
rs->request_id = lsp_send_request(lsp, &request);
+ rs->sent_rename = true;
}
-
}
void rename_symbol_frame(Ted *ted) {
@@ -61,7 +83,7 @@ static void rename_symbol_menu_render(Ted *ted) {
menu_close(ted);
return;
}
- if (rs->request_id.id) {
+ if (rs->sent_rename) {
// already entered a new name
return;
}
@@ -80,6 +102,10 @@ static void rename_symbol_menu_render(Ted *ted) {
.line = cursor_pos.line,
.index = sym_end
};
+ if (rs->have_range) {
+ bpos0 = rs->range_start;
+ bpos1 = rs->range_end;
+ }
// symbol should span from pos0 to pos1
vec2 p0 = buffer_pos_to_pixels(buffer, bpos0);
vec2 p1 = buffer_pos_to_pixels(buffer, bpos1);
@@ -114,10 +140,28 @@ static bool rename_symbol_menu_close(Ted *ted) {
void rename_symbol_process_lsp_response(Ted *ted, const LSPResponse *response) {
RenameSymbol *rs = ted->rename_symbol;
- if (response->request.type != LSP_REQUEST_RENAME
- || response->request.id != rs->request_id.id) {
+ if (response->request.id != rs->request_id.id) {
return;
}
+ if (response->request.type == LSP_REQUEST_PREPARE_RENAME) {
+ if (lsp_response_is_error(response)) {
+ // prepareRename returned an error
+ menu_close(ted);
+ ted_flash_error_cursor(ted);
+ return;
+ }
+ const LSPResponsePrepareRename *prep = &response->data.prepare_rename;
+ if (prep->use_range) {
+ TextBuffer *buffer = ted_active_buffer_behind_menu(ted);
+ if (!buffer) return;
+ rs->range_start = buffer_pos_from_lsp(buffer, prep->range.start);
+ rs->range_end = buffer_pos_from_lsp(buffer, prep->range.end);
+ rs->have_range = true;
+ }
+ return;
+ }
+ if (response->request.type != LSP_REQUEST_RENAME)
+ return;
LSP *lsp = ted_get_lsp_by_id(ted, rs->request_id.lsp);
if (menu_is_open(ted, MENU_RENAME_SYMBOL))
diff --git a/lsp-parse.c b/lsp-parse.c
index 72c6b88..5a38453 100644
--- a/lsp-parse.c
+++ b/lsp-parse.c
@@ -249,9 +249,15 @@ static void parse_capabilities(LSP *lsp, const JSON *json, JSONObject capabiliti
// check for textDocument/rename support
JSONValue rename_value = json_object_get(json, capabilities, "renameProvider");
if (rename_value.type != JSON_UNDEFINED && rename_value.type != JSON_FALSE) {
+ if (rename_value.type == JSON_OBJECT) {
+ JSONValue rename_prepare_value = json_object_get(json, rename_value.val.object, "prepareProvider");
+ if (rename_prepare_value.type != JSON_UNDEFINED && rename_prepare_value.type != JSON_FALSE) {
+ cap->prepare_rename_support = true;
+ }
+ }
cap->rename_support = true;
}
-
+
// check for textDocument/documentLink support
JSONValue document_link_value = json_object_get(json, capabilities, "documentLinkProvider");
if (document_link_value.type == JSON_OBJECT) {
@@ -938,6 +944,33 @@ static bool parse_rename_response(LSP *lsp, const JSON *json, LSPResponse *respo
return parse_workspace_edit(lsp, response, json, result, &response->data.rename);
}
+static bool parse_prepare_rename_response(LSP *lsp, const JSON *json, LSPResponse *response) {
+ LSPResponsePrepareRename *prep = &response->data.prepare_rename;
+ JSONValue result_value = json_get(json, "result");
+ if (result_value.type == JSON_NULL)
+ return false; // invalid rename
+ if (result_value.type != JSON_OBJECT) {
+ lsp_set_error(lsp, "Bad result type for textDocument/prepareRename response.");
+ return false;
+ }
+ JSONObject result = result_value.val.object;
+ JSONValue range_value = json_object_get(json, result, "range");
+ if (json_object_get(json, result, "start").type != JSON_UNDEFINED) {
+ // result is a Range
+ prep->use_range = true;
+ return parse_range(lsp, json, result_value, &prep->range);
+ } else if (range_value.type != JSON_UNDEFINED) {
+ prep->use_range = true;
+ return parse_range(lsp, json, range_value, &prep->range);
+ } else if (json_object_get(json, result, "defaultBehavior").type == JSON_TRUE) {
+ prep->use_range = false;
+ return true;
+ } else {
+ lsp_set_error(lsp, "Unrecognizable response for textDocument/prepareRename.");
+ return false;
+ }
+}
+
static bool parse_highlight_response(LSP *lsp, const JSON *json, LSPResponse *response) {
LSPResponseHighlight *hl = &response->data.highlight;
JSONArray result = json_force_array(json_get(json, "result"));
@@ -1121,6 +1154,9 @@ void process_message(LSP *lsp, JSON *json) {
case LSP_REQUEST_RENAME:
add_to_messages = parse_rename_response(lsp, json, &response);
break;
+ case LSP_REQUEST_PREPARE_RENAME:
+ add_to_messages = parse_prepare_rename_response(lsp, json, &response);
+ break;
case LSP_REQUEST_FORMATTING:
case LSP_REQUEST_RANGE_FORMATTING:
add_to_messages = parse_formatting_response(lsp, json, &response);
diff --git a/lsp-write.c b/lsp-write.c
index 9788347..506369e 100644
--- a/lsp-write.c
+++ b/lsp-write.c
@@ -297,6 +297,8 @@ static const char *lsp_request_method(LSPRequest *request) {
return "textDocument/documentLink";
case LSP_REQUEST_RENAME:
return "textDocument/rename";
+ case LSP_REQUEST_PREPARE_RENAME:
+ return "textDocument/prepareRename";
case LSP_REQUEST_WORKSPACE_FOLDERS:
return "workspace/workspaceFolders";
case LSP_REQUEST_DID_CHANGE_WORKSPACE_FOLDERS:
@@ -614,6 +616,12 @@ void write_request(LSP *lsp, LSPRequest *request, StrBuilder *builder) {
write_key_string(o, "newName", lsp_request_string(request, rename->new_name));
write_obj_end(o);
} break;
+ case LSP_REQUEST_PREPARE_RENAME: {
+ const LSPRequestPrepareRename *prep = &request->data.prepare_rename;
+ write_key_obj_start(o, "params");
+ write_document_position(o, prep->position);
+ write_obj_end(o);
+ } break;
case LSP_REQUEST_WORKSPACE_SYMBOLS: {
const LSPRequestWorkspaceSymbols *syms = &request->data.workspace_symbols;
write_key_obj_start(o, "params");
diff --git a/lsp.c b/lsp.c
index 7de51c3..b1fe8e0 100644
--- a/lsp.c
+++ b/lsp.c
@@ -133,6 +133,7 @@ void lsp_request_free(LSPRequest *r) {
case LSP_REQUEST_SHOW_MESSAGE:
case LSP_REQUEST_LOG_MESSAGE:
case LSP_REQUEST_RENAME:
+ case LSP_REQUEST_PREPARE_RENAME:
case LSP_REQUEST_WORKSPACE_SYMBOLS:
break;
case LSP_REQUEST_DID_CHANGE: {
@@ -263,6 +264,8 @@ static bool lsp_supports_request(LSP *lsp, const LSPRequest *request) {
return cap->workspace_symbols_support;
case LSP_REQUEST_RENAME:
return cap->rename_support;
+ case LSP_REQUEST_PREPARE_RENAME:
+ return cap->prepare_rename_support;
case LSP_REQUEST_HIGHLIGHT:
return cap->highlight_support;
case LSP_REQUEST_REFERENCES:
@@ -311,6 +314,7 @@ static bool request_type_is_notification(LSPRequestType type) {
case LSP_REQUEST_IMPLEMENTATION:
case LSP_REQUEST_REFERENCES:
case LSP_REQUEST_RENAME:
+ case LSP_REQUEST_PREPARE_RENAME:
case LSP_REQUEST_WORKSPACE_SYMBOLS:
case LSP_REQUEST_WORKSPACE_FOLDERS:
case LSP_REQUEST_DOCUMENT_LINK:
@@ -946,6 +950,10 @@ bool lsp_has_incremental_sync_support(LSP *lsp) {
return lsp->capabilities.incremental_sync_support;
}
+bool lsp_has_prepare_rename(LSP *lsp) {
+ return lsp->capabilities.prepare_rename_support;
+}
+
const char *lsp_get_command(LSP *lsp) {
return lsp->command;
}
diff --git a/lsp.h b/lsp.h
index 37370bc..66f24de 100644
--- a/lsp.h
+++ b/lsp.h
@@ -86,6 +86,7 @@ typedef enum {
LSP_REQUEST_HIGHLIGHT, //< textDocument/documentHighlight
LSP_REQUEST_REFERENCES, //< textDocument/references
LSP_REQUEST_RENAME, //< textDocument/rename
+ LSP_REQUEST_PREPARE_RENAME, //< textDocument/prepareRename
LSP_REQUEST_DOCUMENT_LINK, //< textDocument/documentLink
LSP_REQUEST_FORMATTING, //< textDocument/formatting
LSP_REQUEST_RANGE_FORMATTING, //< textDocument/rangeFormatting
@@ -233,6 +234,10 @@ typedef struct {
} LSPRequestRename;
typedef struct {
+ LSPDocumentPosition position;
+} LSPRequestPrepareRename;
+
+typedef struct {
LSPDocumentID *removed; // dynamic array
LSPDocumentID *added; // dynamic array
} LSPRequestDidChangeWorkspaceFolders;
@@ -286,6 +291,7 @@ typedef struct {
LSPRequestMessage message;
LSPRequestDidChangeWorkspaceFolders change_workspace_folders;
LSPRequestRename rename;
+ LSPRequestPrepareRename prepare_rename;
LSPRequestDocumentLink document_link;
LSPRequestPublishDiagnostics publish_diagnostics;
// LSP_REQUEST_FORMATTING and LSP_REQUEST_RANGE_FORMATTING
@@ -539,6 +545,13 @@ typedef struct {
typedef LSPWorkspaceEdit LSPResponseRename;
typedef struct {
+ // if false, the rename is valid but no range should be highlighted
+ bool use_range;
+ // range to highlight
+ LSPRange range;
+} LSPResponsePrepareRename;
+
+typedef struct {
LSPRange range;
LSPString target;
LSPString tooltip;
@@ -567,6 +580,7 @@ typedef struct {
LSPResponseDefinition definition;
LSPResponseWorkspaceSymbols workspace_symbols;
LSPResponseRename rename;
+ LSPResponsePrepareRename prepare_rename;
LSPResponseHighlight highlight;
LSPResponseReferences references;
LSPResponseDocumentLink document_link;
@@ -610,6 +624,7 @@ typedef struct {
// (but jdtls and gopls do)
bool workspace_folders_support;
bool rename_support;
+ bool prepare_rename_support;
bool references_support;
bool document_link_support;
bool formatting_support;
@@ -702,6 +717,8 @@ bool lsp_document_position_eq(LSPDocumentPosition a, LSPDocumentPosition b);
/// see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_synchronization
/// for more info.
bool lsp_has_incremental_sync_support(LSP *lsp);
+/// does this server support textDocument/prepareRename requests?
+bool lsp_has_prepare_rename(LSP *lsp);
/// get dynamic array of completion trigger characters.
const uint32_t *lsp_completion_trigger_chars(LSP *lsp);
/// get dynamic array of signature help trigger characters.
diff --git a/main.c b/main.c
index 1839f56..3ca8029 100644
--- a/main.c
+++ b/main.c
@@ -2,7 +2,6 @@
FUTURE FEATURES:
- path-specific extensions
- more tests
-- prepare rename support
- config variables
- bind key to series of commands
- convert macro to command list
diff --git a/ted.h b/ted.h
index da4144e..44527ea 100644
--- a/ted.h
+++ b/ted.h
@@ -945,6 +945,8 @@ void format_file(Ted *ted);
void hover_reset_timer(Ted *ted);
// === ide-rename-symbol.c ===
+/// Start renaming process (open rename menu when LSP is ready)
+void rename_symbol_start(Ted *ted);
/// rename symbol at cursor of `buffer` to `new_name`
void rename_symbol_at_cursor(Ted *ted, TextBuffer *buffer, const char *new_name);