diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Output.zig | 35 | ||||
| -rw-r--r-- | src/ninep/ctl.zig | 25 | ||||
| -rw-r--r-- | src/pardes.zig | 1 |
3 files changed, 61 insertions, 0 deletions
diff --git a/src/Output.zig b/src/Output.zig index f2ab1bda..b8dd5e34 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -609,6 +609,41 @@ pub fn openUnsaved(p: *Pardes, id: usize, content: []u8) void { p.active = id; } +/// Pane `gone` closed: its row goes from +Unsaved, which lists only panes +/// still there to lose. +pub fn dropUnsaved(p: *Pardes, gone: *const Pane) void { + const gf = gone.file orelse return; + if (gf.output != null and !(gf.output.?.from == .cmd and gf.output.?.from.cmd == .New)) return; + for (p.panes) |slot| { + const rp = slot orelse continue; + if (rp == gone) continue; + const rf = if (rp.file) |*f| f else continue; + const o = rf.output orelse continue; + if (o.from != .unsaved) continue; + var scratch_name: [limits.host_path_cap + 24]u8 = undefined; + const as_scratch = std.fmt.bufPrint(&scratch_name, "{s} (pane {d}): Modified", .{ gf.path, gone.serial }) catch ""; + var out: std.ArrayList(u8) = .empty; + var lines = std.mem.splitScalar(u8, rf.content, '\n'); + var dropped = false; + var first = true; + while (lines.next()) |line| { + const is_it = std.mem.startsWith(u8, line, gf.path) and std.mem.eql(u8, line[gf.path.len..], ": Modified"); + if (is_it or (as_scratch.len > 0 and std.mem.eql(u8, line, as_scratch))) { + dropped = true; + continue; + } + if (!first) out.append(p.gpa, '\n') catch return out.deinit(p.gpa); + out.appendSlice(p.gpa, line) catch return out.deinit(p.gpa); + first = false; + } + if (!dropped) return out.deinit(p.gpa); + const content = out.toOwnedSlice(p.gpa) catch return out.deinit(p.gpa); + File.setContent(p, rf, content); + resetBody(p, rp); + return; + } +} + /// Recent: the one +Recent, filled again when it is there (as Find and /// Grep's +Search is), else opened below the asking pane. pub fn openRecent(p: *Pardes, id: usize) !void { diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 0d3f084d..3d7ee91a 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -2176,6 +2176,31 @@ test "Grep, Symbols and Diagnostics share their directory's one +Search" { try testing.expectEqual(@as(usize, 1), searches); } +test "+Unsaved drops a pane once it has closed" { + const p = try withFile(testing.allocator, "x\n"); + defer p.deinit(); + const a = try th.newPane(p); + _ = wr(p, Node.of(a, .name), "/tmp/pardes-unsaved-a.txt\n"); + _ = wr(p, Node.of(a, .body), "edited\n"); + const b = try th.newPane(p); + _ = wr(p, Node.of(b, .name), "/tmp/pardes-unsaved-b.txt\n"); + _ = wr(p, Node.of(b, .body), "edited\n"); + const root_ctl = @intFromEnum(tree.TopFile.ctl); + try testing.expectEqual(Status.err, wr(p, root_ctl, "Exit\n").reply.status); + var listed: ?*pardes.Pane = null; + for (p.panes) |slot| if (slot) |q| if (q.file) |f| if (f.output) |o| if (o.from == .unsaved) { + listed = q; + }; + const list = listed.?; + try testing.expect(std.mem.indexOf(u8, list.file.?.content, "/tmp/pardes-unsaved-a.txt: Modified") != null); + // a closes (discarded): its row goes; b's stays. + _ = wr(p, Node.of(a, .ctl), "Del\n"); + _ = wr(p, Node.of(a, .ctl), "Del\n"); + p.sync(); + try testing.expect(p.paneBySerial(a) == null); + try testing.expect(std.mem.indexOf(u8, list.file.?.content, "/tmp/pardes-unsaved-a.txt: Modified") == null); +} + test "every EINVAL a write gets says why, in its err record too; DEL is a control character in a line" { const p = try withFile(testing.allocator, "x\n"); defer p.deinit(); diff --git a/src/pardes.zig b/src/pardes.zig index c405c3d9..2649014a 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -4898,6 +4898,7 @@ pub const Pardes = struct { // Closed, a file is remembered with its dot: Recent and a jump back // to it reopen it there. p.noteClosed(pane); + panes.Output.dropUnsaved(p, pane); const watched = (if (pane.file) |f| f.output == null else false) or pane.hasPdf(); for (p.panes, 0..) |slot, id| if (slot == pane) { if (watched) p.emit(.{ .watch = .{ .pane = @intCast(id), .on = false } }); |
