diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 6b3938e..e432ab8 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -372,7 +372,7 @@ pub const Editor = struct { const Result = command.Result; pub fn write_state(self: *const Self, writer: Buffer.MetaWriter) !void { - try cbor.writeArrayHeader(writer, 11); + try cbor.writeArrayHeader(writer, 12); try cbor.writeValue(writer, self.file_path orelse ""); try cbor.writeValue(writer, self.clipboard orelse ""); try cbor.writeValue(writer, self.last_find_query orelse ""); @@ -381,6 +381,7 @@ pub const Editor = struct { try cbor.writeValue(writer, self.indent_size); try cbor.writeValue(writer, self.tab_width); try cbor.writeValue(writer, self.indent_mode); + try cbor.writeValue(writer, self.syntax_no_render); if (self.find_history) |history| { try cbor.writeArrayHeader(writer, history.items.len); for (history.items) |item| @@ -419,6 +420,7 @@ pub const Editor = struct { tp.extract(&self.indent_size), tp.extract(&self.tab_width), tp.extract(&self.indent_mode), + tp.extract(&self.syntax_no_render), tp.extract_cbor(&find_history), tp.extract_cbor(&view_cbor), tp.extract_cbor(&cursels_cbor), @@ -866,6 +868,33 @@ pub const Editor = struct { } pub const resume_undo_history_meta: Meta = .{ .description = "Resume undo history" }; + fn collapse_trailing_ws_line(self: *Self, root: Buffer.Root, row: usize, allocator: Allocator) Buffer.Root { + const last = find_last_non_ws(root, row, self.metrics); + var cursel: CurSel = .{ .cursor = .{ .row = row, .col = last } }; + with_selection_const(root, move_cursor_end, &cursel, self.metrics) catch return root; + return self.delete_selection(root, &cursel, allocator) catch root; + } + + fn find_last_non_ws(root: Buffer.Root, row: usize, metrics: Buffer.Metrics) usize { + const Ctx = struct { + col: usize = 0, + last_non_ws: usize = 0, + fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize, _: Buffer.Metrics) Buffer.Walker { + const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_))); + ctx.col += wcwidth; + switch (egc[0]) { + ' ', '\t' => {}, + '\n' => return Buffer.Walker.stop, + else => ctx.last_non_ws = ctx.col, + } + return Buffer.Walker.keep_walking; + } + }; + var ctx: Ctx = .{}; + root.walk_egc_forward(row, Ctx.walker, &ctx, metrics) catch return 0; + return ctx.last_non_ws; + } + fn find_first_non_ws(root: Buffer.Root, row: usize, metrics: Buffer.Metrics) usize { const Ctx = struct { col: usize = 0, @@ -4430,7 +4459,8 @@ pub const Editor = struct { } fn cursel_smart_insert_line(self: *Self, root: Buffer.Root, cursel: *CurSel, b_allocator: std.mem.Allocator) !Buffer.Root { - const leading_ws = @min(find_first_non_ws(root, cursel.cursor.row, self.metrics), cursel.cursor.col); + const row = cursel.cursor.row; + const leading_ws = @min(find_first_non_ws(root, row, self.metrics), cursel.cursor.col); var sfa = std.heap.stackFallback(512, self.allocator); const allocator = sfa.get(); var stream = std.ArrayListUnmanaged(u8).empty; @@ -4438,7 +4468,18 @@ pub const Editor = struct { var writer = stream.writer(allocator); _ = try writer.write("\n"); try self.generate_leading_ws(&writer, leading_ws); - return self.insert(root, cursel, stream.items, b_allocator); + var root_ = try self.insert(root, cursel, stream.items, b_allocator); + root_ = self.collapse_trailing_ws_line(root_, row, b_allocator); + const leading_ws_ = find_first_non_ws(root_, cursel.cursor.row, self.metrics); + if (leading_ws_ > leading_ws and leading_ws_ > cursel.cursor.col) { + const sel = try cursel.enable_selection(root_, self.metrics); + sel.* = .{ + .begin = .{ .row = cursel.cursor.row, .col = cursel.cursor.col }, + .end = .{ .row = cursel.cursor.row, .col = leading_ws_ }, + }; + root_ = self.delete_selection(root_, cursel, b_allocator) catch root_; + } + return root_; } pub fn smart_insert_line(self: *Self, _: Context) Result { @@ -4494,6 +4535,7 @@ pub const Editor = struct { const leading_ws = @min(find_first_non_ws(root, cursel.cursor.row, self.metrics), cursel.cursor.col); try move_cursor_begin(root, &cursel.cursor, self.metrics); root = try self.insert(root, cursel, "\n", b.allocator); + const row = cursel.cursor.row; try move_cursor_left(root, &cursel.cursor, self.metrics); var sfa = std.heap.stackFallback(512, self.allocator); const allocator = sfa.get(); @@ -4503,6 +4545,7 @@ pub const Editor = struct { try self.generate_leading_ws(&writer, leading_ws); if (stream.items.len > 0) root = try self.insert(root, cursel, stream.items, b.allocator); + root = self.collapse_trailing_ws_line(root, row, b.allocator); }; try self.update_buf(root); self.clamp(); @@ -4526,6 +4569,7 @@ pub const Editor = struct { var root = b.root; for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { const leading_ws = @min(find_first_non_ws(root, cursel.cursor.row, self.metrics), cursel.cursor.col); + const row = cursel.cursor.row; try move_cursor_end(root, &cursel.cursor, self.metrics); var sfa = std.heap.stackFallback(512, self.allocator); const allocator = sfa.get(); @@ -4536,6 +4580,7 @@ pub const Editor = struct { try self.generate_leading_ws(&writer, leading_ws); if (stream.items.len > 0) root = try self.insert(root, cursel, stream.items, b.allocator); + root = self.collapse_trailing_ws_line(root, row, b.allocator); }; try self.update_buf(root); self.clamp(); diff --git a/src/tui/mode/overlay/command_palette.zig b/src/tui/mode/overlay/command_palette.zig index 1f0ba2b..9e0be4b 100644 --- a/src/tui/mode/overlay/command_palette.zig +++ b/src/tui/mode/overlay/command_palette.zig @@ -21,11 +21,13 @@ pub const Entry = struct { pub fn load_entries(palette: *Type) !usize { const hints = if (tui.input_mode()) |m| m.keybind_hints else @panic("no keybind hints"); - var longest_hint: usize = 0; + var longest_description: usize = 0; + var longest_total: usize = 0; for (command.commands.items) |cmd_| if (cmd_) |p| { if (p.meta.description.len > 0) { const hint = hints.get(p.name) orelse ""; - longest_hint = @max(longest_hint, hint.len); + longest_description = @max(longest_description, p.meta.description.len); + longest_total = @max(longest_total, p.meta.description.len + hint.len + 1); (try palette.entries.addOne()).* = .{ .label = if (p.meta.description.len > 0) p.meta.description else p.name, .name = p.name, @@ -35,7 +37,7 @@ pub fn load_entries(palette: *Type) !usize { }; } }; - return longest_hint; + return longest_total - longest_description; } pub fn add_menu_entry(palette: *Type, entry: *Entry, matches: ?[]const usize) !void {