summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/builtins.zig15
-rw-r--r--src/config.zig1
-rw-r--r--src/exec.zig1
-rw-r--r--src/ninep/ctl.zig21
4 files changed, 36 insertions, 2 deletions
diff --git a/src/builtins.zig b/src/builtins.zig
index 1d524514..8fcc3c01 100644
--- a/src/builtins.zig
+++ b/src/builtins.zig
@@ -1144,6 +1144,21 @@ pub const Get = struct {
}
};
+/// acme's Putall: every pane holding unsaved edits to a file is saved, each
+/// as its own Save; one that is refused (changed on disk since, a write
+/// that fails) says so and the rest are saved all the same.
+pub const Putall = struct {
+ pub const scope: Scope = .session;
+ pub fn run(c: Ctx) void {
+ for (c.p.panes, 0..) |slot, id| {
+ const pane = slot orelse continue;
+ const f = pane.file orelse continue;
+ if (f.output != null or f.listing != null or f.revision == f.saved_revision) continue;
+ exec.saveFile(c.p, id);
+ }
+ }
+};
+
/// Write the pane's text to its file, or to the file its argument names.
pub const Save = struct {
pub const takes_arg = true;
diff --git a/src/config.zig b/src/config.zig
index 673e91ed..640f3cd0 100644
--- a/src/config.zig
+++ b/src/config.zig
@@ -59,6 +59,7 @@ pub const leader_path = paths: {
.Kill = null,
.Exit = "q",
.Get = null,
+ .Putall = null,
.ClipYank = "y",
.ClipYankMain = "Y",
.ClipPaste = "p",
diff --git a/src/exec.zig b/src/exec.zig
index 9d1b81d5..c465bda8 100644
--- a/src/exec.zig
+++ b/src/exec.zig
@@ -945,7 +945,6 @@ pub fn namesBuiltin(txt: []const u8) bool {
pub const acme_words = [_]struct { acme: []const u8, pardes: ?[]const u8, hint: []const u8 = "" }{
.{ .acme = "Put", .pardes = "Save" },
.{ .acme = "Delete", .pardes = "Del" },
- .{ .acme = "Putall", .pardes = null, .hint = ": Save each pane" },
.{ .acme = "Snarf", .pardes = null, .hint = ": y yanks the selection" },
.{ .acme = "Cut", .pardes = null, .hint = ": d deletes the selection into a register" },
.{ .acme = "Paste", .pardes = null, .hint = ": p puts a register" },
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 269e9346..028ddf55 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -1809,6 +1809,25 @@ test "Get reads the pane's file again, refusing unsaved edits once, and Get file
try testing.expect(!pane_files.dirtyOf(pane));
}
+test "Putall saves every pane with unsaved edits to a file, one each, and leaves the rest" {
+ const p = try withFile(testing.allocator, "x\n");
+ defer p.deinit();
+ const first = serialOf(p);
+ _ = wr(p, Node.of(first, .name), "/tmp/pardes-putall-a.txt\n");
+ _ = wr(p, Node.of(first, .body), "edited\n");
+ const second = p.freeSlot().?;
+ _ = try p.newDocPane(second);
+ p.panes[second].?.file = .{ .path = try p.gpa.dupe(u8, "/tmp/pardes-putall-b.txt"), .content = try p.gpa.dupe(u8, "clean\n"), .history = try panes.File.History.create(p.gpa) };
+ while (p.nextEffect()) |_| {}
+ try testing.expect(p.executeBuiltinLine(0, "Putall"));
+ var saves: usize = 0;
+ while (p.nextEffect()) |e| if (e == .save_file) {
+ saves += 1;
+ try testing.expectEqual(@as(u16, 0), e.save_file.pane);
+ };
+ try testing.expectEqual(@as(usize, 1), saves);
+}
+
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();
@@ -3191,7 +3210,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", "Tab\n", "Indent\n", "Local\n", "Incl\n", "Abort\n" }) |line| {
+ for ([_][]const u8{ "Snarf\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());