diff options
| author | Gabriel Schneider <[email protected]> | 2026-10-01 18:07:32 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 19:57:27 -0300 |
| commit | 6ee4c3caa6faf42b9825dca39e9130ac83f425a8 (patch) | |
| tree | 65b92aa4faa3161ee507a66c6d755186681541a0 /src | |
| parent | 7f748c657121c801f219b28680373729fefce82a (diff) | |
| download | pardes-6ee4c3caa6faf42b9825dca39e9130ac83f425a8.tar.gz pardes-6ee4c3caa6faf42b9825dca39e9130ac83f425a8.zip | |
Enter, o and O give the new line the old one's indent as it is, tabs and all, as acme's autoindent does; a level more after an opening delimiter is a tab where the indent is tabs
The indent was rebuilt as spaces, partial levels dropped, so a tab-indented
line went on with spaces (or with one space, after Enter).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src')
| -rw-r--r-- | src/edit.zig | 34 | ||||
| -rw-r--r-- | src/modal.zig | 8 |
2 files changed, 35 insertions, 7 deletions
diff --git a/src/edit.zig b/src/edit.zig index 3e3ba341..bf65935e 100644 --- a/src/edit.zig +++ b/src/edit.zig @@ -899,11 +899,18 @@ fn insertEdit(p: *Pardes, t: *Text, key: Key) void { t.show(); return; } - const indent = modal.newlineIndentWidth(line, c.col); + // The line's own indent, as it is (tabs stay tabs), and a + // level more after an opening delimiter: a tab where the + // indent is tabs, else spaces. + const lead = modal.indentText(line); + const more = modal.newlineIndentWidth(line, c.col) - lead.len; + const tabbed = std.mem.indexOfScalar(u8, lead, '\t') != null; + const indent = lead.len + (if (more == 0) 0 else if (tabbed) 1 else more); const arena = p.scratch.allocator(); const block = arena.alloc(u8, 1 + indent) catch return; block[0] = '\n'; - @memset(block[1..], ' '); + @memcpy(block[1..][0..lead.len], lead); + @memset(block[1 + lead.len ..], if (tabbed) '\t' else ' '); // a tag keeps its blanks: they part its words, and the last // may be its read-only prefix's const kept = if (t.what == .body) std.mem.trimEnd(u8, before, " \t").len else c.col; @@ -2136,6 +2143,29 @@ test "insert newline adds one indent level after a closing call" { try std.testing.expectEqual(@as(i32, 8), pane.body.cur_col); } +test "Enter in insert mode keeps a tab-indented line's tabs, as o and O do" { + const gpa = std.testing.allocator; + const p = try Pardes.init(gpa, .{ .tty_only = true }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = try p.setTestFile("\t\tvalue = 1;\n\tif (x) {\n"); + pane.body.mode = .insert; + pane.body.cur_col = 12; + p.update(.{ .key = .{ .cp = Key.enter } }); + try std.testing.expectEqualStrings("\t\tvalue = 1;\n\t\t\n\tif (x) {\n", pane.file.?.content); + try std.testing.expectEqual(@as(i32, 2), pane.body.cur_col); + // After an opening brace, a level more: a tab, as the indent is tabs. + pane.body.cur_row = 2; + pane.body.cur_col = 9; + p.update(.{ .key = .{ .cp = Key.enter } }); + try std.testing.expectEqualStrings("\t\tvalue = 1;\n\t\t\n\tif (x) {\n\t\t\n", pane.file.?.content); + // o opens a line with the indent as it is. + p.update(.{ .key = .{ .cp = Key.escape } }); + pane.body.cur_row = 0; + p.update(.{ .key = .{ .cp = 'o' } }); + try std.testing.expect(std.mem.startsWith(u8, pane.file.?.content, "\t\tvalue = 1;\n\t\t\n")); +} + test "insert and normal modes edit complete Unicode graphemes" { const gpa = std.testing.allocator; const p = try Pardes.init(gpa, .{ .cols = 60, .rows = 12 }); diff --git a/src/modal.zig b/src/modal.zig index 11eda7b9..316a66fa 100644 --- a/src/modal.zig +++ b/src/modal.zig @@ -1773,12 +1773,10 @@ pub fn indentWidth(line: []const u8) usize { return w; } -/// full indent LEVELS of a line as spaces (helix indent_level_for_line: -/// partial levels round down) — what o/O/insert-newline copy. +/// A line's indent as it is, tabs and spaces both, as acme's autoindent +/// copies it: what o/O/insert-newline give the new line. pub fn indentText(line: []const u8) []const u8 { - const level = indentWidth(line) / INDENT_W; - const max = " "; // 8 levels is plenty (ponytail) - return max[0..@min(level * INDENT_W, max.len)]; + return line[0 .. std.mem.indexOfNone(u8, line, " \t") orelse line.len]; } /// Copy full indent levels and add one after (, [, {, or ), without parsing. |
