diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ninep/cols.zig | 38 |
1 files changed, 34 insertions, 4 deletions
diff --git a/src/ninep/cols.zig b/src/ninep/cols.zig index 6fdcda07..3eeadfdc 100644 --- a/src/ninep/cols.zig +++ b/src/ninep/cols.zig @@ -208,14 +208,15 @@ test "New written to a column's ctl reads back the new pane alone, as its exec d try testing.expectEqualStrings(try std.fmt.bufPrint(&want, "{d}\n", .{p.panes[p.active].?.serial}), th.rd(p, root_exec, 0, 64).bytes); } -/// Whether `line` names a builtin that acts on a pane and is not one of -/// `own`, the words its tag holds by default. Edit, which the root's ctl -/// takes at the active pane, is taken here too. +/// Whether `line` names a builtin that acts on a pane (every one of pane +/// scope, Edit too, which the root's ctl takes at the active pane) and is +/// not one of `own`, the words its tag holds by default: those a tag no +/// pane owns refuses, never doing them at the pane with the keyboard. fn paneWord(line: []const u8, own: []const u8) bool { const cmd = pardes.exec.commandText(line); 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 (b == .Edit or pardes.builtins.registry.scope(b) == .session) return false; + if (pardes.builtins.registry.scope(b) == .session) return false; var words = std.mem.tokenizeScalar(u8, own, ' '); while (words.next()) |w| if (std.mem.eql(u8, w, word)) return false; return true; @@ -236,6 +237,35 @@ 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 "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(); + const Builtin = pardes.builtins.registry.Builtin(); + const col_exec = tree.Node.ofCol(layout.columnSerial(p, 0), .exec); + for ([_]struct { node: u64, own: []const u8 }{ + .{ .node = @intFromEnum(tree.TopFile.tagexec), .own = pardes.config.topbar_str }, + .{ .node = col_exec, .own = tagline.column_tag_default }, + }) |tag_file| { + var checked: usize = 0; + for (std.enums.values(Builtin)) |b| { + if (pardes.builtins.registry.scope(b) == .session) continue; + 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)); + if (owned) continue; + var line: [64]u8 = undefined; + const text = try std.fmt.bufPrint(&line, "{s}{s}\n", .{ @tagName(b), if (b == .Edit) " ,d" else if (pardes.builtins.registry.requiresArg(b)) " x" else "" }); + const refused = th.wr(p, tag_file.node, text); + try testing.expectEqual(E.INVAL, refused.errno()); + try testing.expectStringStartsWith(refused.reply.ename, "not a session control message"); + checked += 1; + } + try testing.expect(checked > 20); + } + // `Edit ,d` wiped the pane with the keyboard: nothing touched it. + try testing.expectEqualStrings("keep me\n", p.panes[0].?.file.?.content); +} + /// rmdir of a column closes it when it is empty; one with panes is /// refused, saying so (close them, or Delcol on its ctl, which asks first). pub fn remove(p: *Pardes, req: Req, serial: u32, file: tree.ColFile) Reply { |
