feat(lsp): initial support for textDocument/rename

flow keybinds: changes f2 from toggle_input_mode to rename_symbol and
moves toggle_input_mode command to ctrl+shift+f2 (since ctrl+f2 is
already bound to insert_command_name)

the replacement text is hard coded for now. i've checked that replace
works with zls and pylsp which send WorkspaceEdit response messages in
different shapes - zls sends shape `{"changes": {}}` while pylsp sends
`{"documentChanges": []}`.

currently the 'rename_symbol_item' commands are sent one at a time.
however they should be buffered and be performed between one
buf_for_update, update_buf pair.  this will be addressed in a follow up.
This commit is contained in:
Travis Staloch 2025-01-12 14:00:48 -08:00 committed by CJ van den Berg
parent 7558a63819
commit 1fd4455adb
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
5 changed files with 172 additions and 1 deletions

View file

@ -554,6 +554,32 @@ const cmds = struct {
}
pub const add_diagnostic_meta = .{ .arguments = &.{ .string, .string, .string, .string, .integer, .integer, .integer, .integer, .integer } };
pub fn rename_symbol_item(self: *Self, ctx: Ctx) Result {
var file_uri: []const u8 = undefined;
var sel: ed.Selection = .{};
var new_text: []const u8 = undefined;
if (!try ctx.args.match(.{
tp.extract(&file_uri),
tp.extract(&sel.begin.row),
tp.extract(&sel.begin.col),
tp.extract(&sel.end.row),
tp.extract(&sel.end.col),
tp.extract(&new_text),
})) return error.InvalidRenameSymbolArgument;
file_uri = project_manager.normalize_file_path(file_uri);
if (self.get_active_editor()) |editor| {
// TODO match correctly. endsWith() isn't correct because path is a
// short, relative path while file_uri is an absolute path starting with 'file://'
const match = if (editor.file_path) |path| std.mem.endsWith(u8, file_uri, path) else false;
if (match) {
try editor.rename_symbol_item(sel, new_text);
} else {
// TODO perform renames in other files
}
}
}
pub const rename_symbol_item_meta = .{ .arguments = &.{ .string, .integer, .integer, .integer, .integer, .string } };
pub fn clear_diagnostics(self: *Self, ctx: Ctx) Result {
var file_path: []const u8 = undefined;
if (!try ctx.args.match(.{tp.extract(&file_path)})) return error.InvalidClearDiagnosticsArgument;