diff --git a/src/tui/editor.zig b/src/tui/editor.zig index ce6b9b1..51309f5 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -4231,8 +4231,11 @@ pub const Editor = struct { } pub const rename_symbol_meta = .{ .description = "Language: Rename symbol at cursor" }; - pub fn add_rename_symbol_cursor(self: *Self, sel_: Selection, first: bool) !void { - if (first) self.cancel_all_selections() else try self.push_cursor(); + pub fn add_cursor_from_selection(self: *Self, sel_: Selection, op: enum { cancel, push }) !void { + switch (op) { + .cancel => self.cancel_all_selections(), + .push => try self.push_cursor(), + } const root = self.buf_root() catch return; const sel: Selection = .{ .begin = .{ @@ -4250,6 +4253,73 @@ pub const Editor = struct { self.need_render(); } + pub fn add_cursors_from_content_diff(self: *Self, new_content: []const u8) !void { + const frame = tracy.initZone(@src(), .{ .name = "editor diff syntax" }); + defer frame.deinit(); + + var content_ = std.ArrayList(u8).init(self.allocator); + defer content_.deinit(); + const root = self.buf_root() catch return; + const eol_mode = self.buf_eol_mode() catch return; + try root.store(content_.writer(), eol_mode); + const content = content_.items; + var last_begin_row: usize = 0; + var last_begin_col_pos: usize = 0; + var last_end_row: usize = 0; + var last_end_col_pos: usize = 0; + + const diffs = try @import("diff").diff(self.allocator, new_content, content); + defer self.allocator.free(diffs); + var first = true; + for (diffs) |diff| { + switch (diff.kind) { + .delete => { + var begin_row, var begin_col_pos = count_lines(content[0..diff.start]); + const end_row, const end_col_pos = count_lines(content[0 .. diff.start + diff.bytes.len]); + if (begin_row == last_end_row and begin_col_pos == last_end_col_pos) { + begin_row = last_begin_row; + begin_col_pos = last_begin_col_pos; + } else { + if (first) { + self.cancel_all_selections(); + } else { + try self.push_cursor(); + } + first = false; + } + const begin_col = try root.pos_to_width(begin_row, begin_col_pos, self.metrics); + const end_col = try root.pos_to_width(end_row, end_col_pos, self.metrics); + + last_begin_row = begin_row; + last_begin_col_pos = begin_col_pos; + last_end_row = end_row; + last_end_col_pos = end_col_pos; + + const sel: Selection = .{ + .begin = .{ .row = begin_row, .col = begin_col }, + .end = .{ .row = end_row, .col = end_col }, + }; + const primary = self.get_primary(); + primary.selection = sel; + primary.cursor = sel.end; + self.need_render(); + }, + else => {}, + } + } + } + + fn count_lines(content: []const u8) struct { usize, usize } { + var pos = content; + var offset = content.len; + var lines: usize = 0; + while (pos.len > 0) : (pos = pos[1..]) if (pos[0] == '\n') { + offset = pos.len - 1; + lines += 1; + }; + return .{ lines, offset }; + } + pub fn hover(self: *Self, _: Context) Result { const primary = self.get_primary(); return self.hover_at(primary.cursor.row, primary.cursor.col); diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 4852169..4eb251e 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -562,8 +562,7 @@ const cmds = struct { var iter = ctx.args.buf; var len = try cbor.decodeArrayHeader(&iter); var first = true; - while (len != 0) { - len -= 1; + while (len != 0) : (len -= 1) { if (try cbor.decodeArrayHeader(&iter) != 7) return error.InvalidRenameSymbolItemArgument; var file_path: []const u8 = undefined; if (!try cbor.matchString(&iter, &file_path)) return error.MissingArgument; @@ -579,7 +578,9 @@ const cmds = struct { file_path = project_manager.normalize_file_path(file_path); if (std.mem.eql(u8, file_path, editor.file_path orelse "")) { - try editor.add_rename_symbol_cursor(sel, first); + if (len == 1 and sel.begin.row == 0 and sel.begin.col == 0 and sel.end.row > 0) //probably a full file edit + return editor.add_cursors_from_content_diff(new_text); + try editor.add_cursor_from_selection(sel, if (first) .cancel else .push); first = false; } else { try self.add_find_in_files_result(