diff options
| author | Gabriel Schneider <[email protected]> | 2026-10-01 17:48:02 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 18:08:12 -0300 |
| commit | 8c74f9a8f199ea0c2d20cc524358d224ea192005 (patch) | |
| tree | d2a280eb296ad1dce02c9f8e3e8fad20a927edf8 /src/ninep | |
| parent | afec162464d360cbbf475d3fb7457cda83ce918f (diff) | |
| download | pardes-8c74f9a8f199ea0c2d20cc524358d224ea192005.tar.gz pardes-8c74f9a8f199ea0c2d20cc524358d224ea192005.zip | |
Get reads a pane's file again, refusing unsaved edits once as Del does, and Get file loads that file into the pane; in a directory pane it reads the directory again. Load is Restore, as Put is Save; Abort's refusal says it is acme's debugging word
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep')
| -rw-r--r-- | src/ninep/ctl.zig | 48 |
1 files changed, 41 insertions, 7 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 0f4e181c..269e9346 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -972,7 +972,7 @@ pub fn writePane(p: *Pardes, req: Req, pane: *Pane) Reply { if (std.mem.eql(u8, line, "get")) { if (!apply) continue; // Quoting the line, as a builtin's refusal (Save's) does. - if (getRefused(p, pane)) |said_in_ename| { + if (getRefused(p, pane, "get")) |said_in_ename| { // refuse writes ename, where the words are: copy them out. var said_buf: [320]u8 = undefined; const said = said_buf[0..said_in_ename.len]; @@ -1127,7 +1127,9 @@ const e_locked = tree.e_in_use ++ ": another open of this ctl holds the lock"; /// acme's get asks winclean first (exec.c:513): unsaved text is warned /// about once, and the same get again, nothing edited since, discards it. -fn getRefused(p: *Pardes, pane: *Pane) ?[]const u8 { +/// Asked once over unsaved edits, by `word` (ctl's `get`, the Get builtin): +/// the words of the refusal, in ename; null to go ahead. +pub fn getRefused(p: *Pardes, pane: *Pane, word: []const u8) ?[]const u8 { const f = pane_files.fileOf(pane) orelse return null; if (!panes.Output.fileTraits(f.output).saves or f.revision == f.saved_revision) return null; // Dirty by a rename alone is no text to lose: only edits are asked about. @@ -1140,15 +1142,16 @@ fn getRefused(p: *Pardes, pane: *Pane) ?[]const u8 { // write's err record logs it, not a msg too. // And logged `unsaved <serial> <name>`, as the other refusals are. tree.events.noteLog(p, .unsaved, pane); - const listed = std.fmt.allocPrint(p.gpa, "{s}: Modified\n\nget again to discard\n", .{f.path}) catch null; + const listed = std.fmt.allocPrint(p.gpa, "{s}: Modified\n\n{s} again to discard\n", .{ f.path, word }) catch null; if (listed) |content| panes.Output.openUnsaved(p, id, content); + var notice: [64]u8 = undefined; p.fs.unlogged = true; - p.setMessage(id, "1 unsaved pane \u{2014} get again to discard"); + p.setMessage(id, std.fmt.bufPrint(¬ice, "1 unsaved pane \u{2014} {s} again to discard", .{word}) catch "1 unsaved pane"); p.fs.unlogged = false; - return std.fmt.bufPrint(&p.fs.ename, "{s}: Modified (get again to discard)", .{f.path}) catch "Modified (get again to discard)"; + return std.fmt.bufPrint(&p.fs.ename, "{s}: Modified ({s} again to discard)", .{ f.path, word }) catch "Modified (get again to discard)"; } -fn get(p: *Pardes, pane: *Pane, failed: *anyerror) u16 { +pub fn get(p: *Pardes, pane: *Pane, failed: *anyerror) u16 { const f = pane_files.fileOf(pane) orelse return 0; // A directory pane: the directory read again (Dir.zig). if (f.listing != null) { @@ -1775,6 +1778,37 @@ test "get reloads the pane from disk and reports a missing file without losing d try testing.expect(pane_files.dirtyOf(pane)); } +test "Get reads the pane's file again, refusing unsaved edits once, and Get file loads that file into the pane" { + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try tmp.dir.writeFile(testing.io, .{ .sub_path = "note.txt", .data = "from disk\n" }); + try tmp.dir.writeFile(testing.io, .{ .sub_path = "other.txt", .data = "the other\n" }); + var directory_buf: [4096]u8 = undefined; + const directory = directory_buf[0..try tmp.dir.realPath(testing.io, &directory_buf)]; + const p = try withFile(testing.allocator, "in memory\n"); + defer p.deinit(); + const serial = serialOf(p); + const pane = p.panes[0].?; + var name: [std.fs.max_path_bytes + 8]u8 = undefined; + _ = wr(p, Node.of(serial, .name), try std.fmt.bufPrint(&name, "{s}/note.txt\n", .{directory})); + try testing.expect(p.executeBuiltinLine(0, "Get")); + try testing.expectEqualStrings("from disk\n", pane.file.?.content); + try testing.expect(!pane_files.dirtyOf(pane)); + // Edited: asked once, then read again. + _ = wr(p, Node.of(serial, .body), "unsaved\n"); + const failures = p.fs.failures; + try testing.expect(p.executeBuiltinLine(0, "Get")); + try testing.expect(p.fs.failures != failures); + try testing.expectEqualStrings("from disk\nunsaved\n", pane.file.?.content); + try testing.expect(p.executeBuiltinLine(0, "Get")); + try testing.expectEqualStrings("from disk\n", pane.file.?.content); + // Get file: that file, by a name from the pane's directory. + try testing.expect(p.executeBuiltinLine(0, "Get other.txt")); + try testing.expectEqualStrings("the other\n", pane.file.?.content); + try testing.expect(std.mem.endsWith(u8, pane.file.?.path, "/other.txt")); + try testing.expect(!pane_files.dirtyOf(pane)); +} + test "a body write to a terminal is typed input, not a paste, even to a program that asked for bracketed paste" { const p = try Pardes.init(testing.allocator, .{ .cols = 90, .rows = 30 }); defer p.deinit(); @@ -3157,7 +3191,7 @@ test "acme's words run as pardes's (Put is Save, Delete a Del that does not ask) const p = try withFile(testing.allocator, "x\n"); defer p.deinit(); const serial = serialOf(p); - for ([_][]const u8{ "Snarf\n", "Putall\n", "Zerox\n", "Get\n", "Tab\n", "Indent\n", "Local\n", "Incl\n", "Abort\n" }) |line| { + for ([_][]const u8{ "Snarf\n", "Putall\n", "Zerox\n", "Tab\n", "Indent\n", "Local\n", "Incl\n", "Abort\n" }) |line| { for ([_]u64{ Node.of(serial, .ctl), Node.of(serial, .exec) }) |node| { const r = wr(p, node, line); try testing.expectEqual(E.INVAL, r.errno()); |
