diff options
Diffstat (limited to 'src/ninep')
| -rw-r--r-- | src/ninep/cols.zig | 29 |
1 files changed, 28 insertions, 1 deletions
diff --git a/src/ninep/cols.zig b/src/ninep/cols.zig index b1ca954c..4db78584 100644 --- a/src/ninep/cols.zig +++ b/src/ninep/cols.zig @@ -205,7 +205,8 @@ pub fn writeExec(p: *Pardes, req: Req, serial: ?u32) Reply { if (paneWord(line, if (serial == null) pardes.config.topbar_str else tagline.column_tag_default)) return pardes.ctlfs.ctl.refuseTo(p, req, "not a session control message", line, "pane/<n>/ctl"); const col = if (serial) |s| layout.columnBySerial(p, s) orelse return Reply.fail(req.tag, E.NOENT) else null; - if (p.panes[p.active] == null) return Reply.fail(req.tag, E.NOENT); + // With no pane at all, a word runs at the stand-in, as a click in + // the empty window's tag does (runBuiltinFrom). p.exec_column = col; defer p.exec_column = null; p.exec_header = true; @@ -260,6 +261,8 @@ fn paneWord(line: []const u8, own: []const u8) bool { const word = cmd[0 .. std.mem.indexOfAny(u8, cmd, " \t+") orelse cmd.len]; const b = std.meta.stringToEnum(pardes.builtins.registry.Builtin(), word) orelse return false; if (pardes.builtins.registry.scope(b) == .session) return false; + // A new pane is every tag's to make, in the session's directory. + if (b == .New or b == .Tty) return false; var words = std.mem.tokenizeScalar(u8, own, ' '); while (words.next()) |w| if (std.mem.eql(u8, w, word)) return false; return true; @@ -280,6 +283,29 @@ test "a pane's word written to /tagexec or a column's exec is refused, and acts try testing.expectEqual(tree.Status.ok, th.wr(p, col_exec, "New\n").reply.status); } +test "New and Tty run at /tagexec and a column's exec, with no pane at all too" { + const p = try th.withFile(testing.allocator, "x\n"); + defer p.deinit(); + const tagexec = @intFromEnum(tree.TopFile.tagexec); + const col_exec = tree.Node.ofCol(layout.columnSerial(p, 0), .exec); + for ([_]u64{ tagexec, col_exec }) |node| for ([_][]const u8{ "New\n", "Tty\n" }) |line| { + const before = p.next_serial; + try testing.expectEqual(tree.Status.ok, th.wr(p, node, line).reply.status); + try testing.expectEqual(before + 1, p.next_serial); + }; + // Every pane gone: the workspace tag makes one all the same. + for ([_][]const u8{ "New\n", "Tty\n" }) |line| { + while (p.ncol > 0) { + const first = p.panes[p.col_panes[0][0]].?.serial; + _ = th.wr(p, tree.Node.of(first, .ctl), "delete\n"); + if (p.anyPane() == null) break; + } + try testing.expect(p.anyPane() == null); + try testing.expectEqual(tree.Status.ok, th.wr(p, tagexec, line).reply.status); + try testing.expect(p.anyPane() != null); + } +} + test "every pane word a tag no pane owns does not hold is refused there, Edit included, and the pane is untouched" { const p = try th.withFile(testing.allocator, "keep me\n"); defer p.deinit(); @@ -292,6 +318,7 @@ test "every pane word a tag no pane owns does not hold is refused there, Edit in var checked: usize = 0; for (std.enums.values(Builtin)) |b| { if (pardes.builtins.registry.scope(b) == .session) continue; + if (b == .New or b == .Tty) continue; // every tag's var owned = false; var words = std.mem.tokenizeScalar(u8, tag_file.own, ' '); while (words.next()) |w| owned = owned or std.mem.eql(u8, w, @tagName(b)); |
