diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-28 16:12:29 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:15 -0300 |
| commit | 07a3f53993a27c886ccb3ed665acb9c8b34a2873 (patch) | |
| tree | 4a4cb5c3c21c7da4989c65a5db0ed01cbb0aa652 /src/edit.zig | |
| parent | cc66593d1432813c9af029ce3779ccba9ded8883 (diff) | |
| download | pardes-07a3f53993a27c886ccb3ed665acb9c8b34a2873.tar.gz pardes-07a3f53993a27c886ccb3ed665acb9c8b34a2873.zip | |
A tag's edited text stays alive until the next edit, so ms and md in a tag read it whole
setEditText freed the text a tag edit was handed once it had copied the
tag's own part out of it, but every caller reads that text back to place the
cursor, and surround goes on to edit it again: ms and md in a tag read freed
memory. The same held for a terminal without an edit buffer and a text with
no pane. The text is now kept until the next such edit (edit.retireEditText),
which covers every caller at once; the callers the audit found reading it
back are surround's two, the paste and replace paths, the number adjust, and
the insert-mode Enter.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/edit.zig')
| -rw-r--r-- | src/edit.zig | 13 |
1 files changed, 11 insertions, 2 deletions
diff --git a/src/edit.zig b/src/edit.zig index 8f6f09c6..3e12f169 100644 --- a/src/edit.zig +++ b/src/edit.zig @@ -463,7 +463,9 @@ pub fn setEditText(p: *Pardes, t: *Text, new: []u8) void { if (t.what == .tag) { // A tag is edited as it is shown, but only what follows its // prefix is its own: an edit that changed the prefix is refused. - defer p.gpa.free(new); + // The shown text stays alive until the next such edit, since every + // caller reads the new text back, as a body's is kept. + defer retireEditText(p, new); const pane = t.pane() orelse return; const prefix = tagline.pathPrefix(p.scratch.allocator(), pane) catch return; if (!std.mem.startsWith(u8, new, prefix)) { @@ -480,11 +482,18 @@ pub fn setEditText(p: *Pardes, t: *Text, new: []u8) void { t.own = new; return; } - const pane = t.pane() orelse return p.gpa.free(new); + const pane = t.pane() orelse return retireEditText(p, new); if (pane.file) |*f| return panes.File.setContent(p, f, new); panes.Terminal.setEditText(p, pane, new); } +/// Take a text an edit made that nothing keeps, holding it until the next +/// one: the edit's caller still reads it to place the cursor. +pub fn retireEditText(p: *Pardes, new: []u8) void { + if (p.retired_edit) |old| p.gpa.free(old); + p.retired_edit = new; +} + pub const InsertAt = enum { at, append, line_start, line_end, open_below, open_above }; /// Files insert only inside existing rows and at grapheme boundaries. |
