diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-28 09:16:36 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:14 -0300 |
| commit | a9de2b51ab76a43250a4d6c41a0ddd54e6970049 (patch) | |
| tree | a2138ead27f434026c224fd8b926f66b7fd7fe0c /src/edit.zig | |
| parent | 0d04bc9214cccce42d74862b31376d49bdda3956 (diff) | |
| download | pardes-a9de2b51ab76a43250a4d6c41a0ddd54e6970049.tar.gz pardes-a9de2b51ab76a43250a4d6c41a0ddd54e6970049.zip | |
Let the keyboard reach a tag's path, read-only
The tag's computed path, dirty marker and page were out of the keyboard's
reach: its cursor lived in the text after them. It now lives in the tag as
shown, as in acme where the name is ordinary text at the start of the tag, so
`0` goes to the line's start and motions select and yank the path. The path
stays read-only: an edit that would change it is refused and leaves the
cursor where it was, and typing into a file's path drafts a new name as a
click on it does. Sync moves the tag's positions when the prefix changes
length, so a rename or the dirty marker still never moves the cursor off the
text after it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/edit.zig')
| -rw-r--r-- | src/edit.zig | 30 |
1 files changed, 27 insertions, 3 deletions
diff --git a/src/edit.zig b/src/edit.zig index e21352e1..b2d71ed2 100644 --- a/src/edit.zig +++ b/src/edit.zig @@ -112,8 +112,12 @@ pub fn applyPaste(p: *Pardes, bytes: []const u8) void { /// into, after the cursor's cell in normal mode, as a body's paste is. fn pasteInto(p: *Pardes, t: *Text, bytes: []const u8) void { if (bytes.len == 0) return; - if (t.mode == .insert) return insertKey(p, t, .{ .cp = bytes[0], .text = bytes }); - pasteText(p, t, bytes, false, 1); + const typed: pardes.Key = .{ .cp = bytes[0], .text = bytes }; + if (t.what == .tag and t.mode == .insert) if (t.pane()) |pane| if (tagline.typedIntoName(p, pane, typed)) return; + const kept = .{ t.cur_row, t.cur_col, t.vsel, t.msel, t.sels, t.nsel }; + t.refused = false; + if (t.mode == .insert) insertKey(p, t, typed) else pasteText(p, t, bytes, false, 1); + if (t.refused) t.cur_row, t.cur_col, t.vsel, t.msel, t.sels, t.nsel = kept; } pub fn clipYank(p: *Pardes, pane: *Pane, main_only: bool) void { @@ -358,7 +362,8 @@ pub fn pinPaneCursor(p: *Pardes, t: *Text) void { pub fn flatSurface(p: *Pardes, t: *Text) ![]const u8 { switch (t.what) { .body => {}, - .tag => return if (t.pane()) |pane| tagline.curTail(pane) else "", + // the tag as it is shown: the keyboard reaches its prefix too + .tag => return if (t.pane()) |pane| try tagline.tagText(p, p.scratch.allocator(), pane) else "", .input => return t.own orelse "", .column, .workspace => return t.own orelse tagline.headerDefault(p, t.what), } @@ -384,6 +389,10 @@ pub fn paneWrapWidth(p: *const Pardes, t: *Text) usize { fn editText(p: *Pardes, t: *Text, lo: i32, hi: i32, col: i32) ?panes.EditText { // A tag or header nobody has changed shows its default; the first edit // makes that default its own text. + if (t.what == .tag) { + const pane = t.pane() orelse return null; + return .{ .text = tagline.tagText(p, p.scratch.allocator(), pane) catch return null, .row0 = 0 }; + } if (t.what != .body and t.what != .input and t.own == null) t.own = p.gpa.dupe(u8, flatSurface(p, t) catch return null) catch return null; if (t.what != .body) return .{ .text = t.own orelse &.{}, .row0 = 0 }; @@ -403,6 +412,21 @@ fn editTextEol(p: *Pardes, t: *Text, b: Bounds) ?panes.EditText { /// install a rewritten editable text (frees the old one) 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); + const pane = t.pane() orelse return; + const prefix = tagline.pathPrefix(p.scratch.allocator(), pane) catch return; + if (!std.mem.startsWith(u8, new, prefix)) { + t.refused = true; + return; + } + const tail = p.gpa.dupe(u8, new[prefix.len..]) catch return; + if (t.own) |old| p.gpa.free(old); + t.own = tail; + return; + } if (t.what != .body) { if (t.own) |old| p.gpa.free(old); t.own = new; |
