diff options
Diffstat (limited to 'src/exec.zig')
| -rw-r--r-- | src/exec.zig | 63 |
1 files changed, 43 insertions, 20 deletions
diff --git a/src/exec.zig b/src/exec.zig index 1fc38b9c..cf4e01ab 100644 --- a/src/exec.zig +++ b/src/exec.zig @@ -395,6 +395,22 @@ pub fn isDirectory(path: []const u8) bool { return stat.kind == .directory; } +/// Whether `path` is a plain file on this host, the turn out while it asks. +pub fn isFile(path: []const u8) bool { + return kindOf(path) == .file; +} + +/// What `path` is on this host, null when it is not there; the turn out +/// while it asks. +pub fn kindOf(path: []const u8) ?std.Io.File.Kind { + var buf: [4096]u8 = undefined; + const z = std.fmt.bufPrintSentinel(&buf, "{s}", .{path}, 0) catch return null; + pardes.turn.yield(); + defer pardes.turn.back(); + const stat = std.Io.Dir.cwd().statFile(std.Io.Threaded.global_single_threaded.io(), z, .{}) catch return null; + return stat.kind; +} + /// With `BootShell replace`, a doc landing in `col` closes a lone pristine /// shell there, the boot's placeholder, and absorbVWeight hands its space to /// the doc. With `keep`, the default, the shell stays like any other pane. @@ -641,6 +657,14 @@ pub fn executeFrom(p: *Pardes, id: usize, txt: []const u8, from_body: bool) ?usi .sent => |to| return to, .asked => return null, }; + // A builtin or setting this build lacks (Fonts on a terminal) is said, + // never run as a shell command. + const word = cmd[0 .. std.mem.indexOfAny(u8, cmd, " \t") orelse cmd.len]; + if (builtins.absentWhy(word) orelse if (config.Runtime.find(word) != null) @as([]const u8, "GUI-only") else null) |why| { + var said: [96]u8 = undefined; + p.reportFailure(id, std.fmt.bufPrint(&said, "{s} is {s}, invalid here", .{ word, why }) catch "GUI-only, invalid here"); + return null; + } if (!takesCommandLine(p, id)) return runCommand(p, id, cmd); panes.Terminal.padOutputBelowEdits(p, id); panes.Terminal.noteCommand(pane, cmd); @@ -824,20 +848,29 @@ pub fn applyStartupConfig(p: *Pardes) void { } test "an init file's ThemeSel runs as Themes and says it was renamed" { - const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 80, .rows = 24 }); + const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 80, .rows = 24, .startup_config = "ThemeSel\n" }); defer p.deinit(); - p.opts.startup_config = "ThemeSel\n"; - applyStartupConfig(p); var listed = false; for (p.panes) |slot| if (slot) |pane| if (pane.file) |f| if (f.output) |o| { listed = listed or std.meta.eql(o.from, panes.Output.Origin{ .cmd = .Themes }); }; try std.testing.expect(listed); + // In the session's directory, not `/`. + for (p.panes) |slot| if (slot) |pane| if (pane.file) |f| if (f.output != null) + try std.testing.expectEqualStrings(p.sessionDir(), std.fs.path.dirname(f.path).?); var said = false; for (p.panes) |slot| if (slot) |pane| { said = said or std.mem.eql(u8, pane.msg[0..pane.msg_len], "ThemeSel: renamed to Themes"); }; try std.testing.expect(said); + // The panes there at boot are the log's first records, before what the + // init file said. + const th = @import("ninep/testing.zig"); + const log = @intFromEnum(pardes.ctlfs.TopFile.log); + const h = th.call(p, .{ .tag = 1, .op = .open, .node = log }).reply.handle; + defer _ = th.call(p, .{ .tag = 3, .op = .release, .node = log, .handle = h }); + const text = th.call(p, .{ .tag = 2, .op = .read, .node = log, .handle = h, .size = 1 << 16 }).bytes; + try std.testing.expectStringStartsWith(text, "new "); } test "an init file's retired ColumnTags line is said and ignored" { @@ -861,23 +894,16 @@ pub fn runBuiltinFrom(p: *Pardes, b: Builtin, id: usize, txt: []const u8, arg: ? const pane = p.panes[id] orelse return; // A builtin says its own name before it runs, so a tagline click is // legible even when what it did is invisible. Whatever the builtin - // then says for itself replaces the announcement. - // Its log record stands only if it does not fail (events.dropFailedAnnouncement). - var announced: ?u64 = null; + // then says for itself replaces the announcement. It is the message + // row's only, never /log's: a msg record is something said. if (p.announce and p.settings.verbose and builtins.registry.announces(b)) { + p.fs.unlogged = true; p.setMessage(id, @tagName(b)); + p.fs.unlogged = false; pane.msg_announcement = true; - announced = pardes.ctlfs.events.newestSaying(p, @tagName(b)); - p.fs.announced_seq = announced; - const word = @tagName(b)[0..@min(@tagName(b).len, p.fs.announced_word.len)]; - @memcpy(p.fs.announced_word[0..word.len], word); - p.fs.announced_word_len = @intCast(word.len); } - const failures = p.fs.failures; const c: builtins.Ctx = .{ .p = p, .pane = pane, .id = id, .txt = txt, .arg = arg, .from_body = from_body }; builtins.registry.dispatch(b, c); - if (announced != null and p.fs.failures != failures and p.fs.announced_seq == announced) - pardes.ctlfs.events.dropFailedAnnouncement(p, "", true); } pub fn applySettingBuiltin(p: *Pardes, setting: config.Runtime.Setting, arg: ?[]const u8) void { @@ -888,15 +914,12 @@ pub fn applySettingBuiltin(p: *Pardes, setting: config.Runtime.Setting, arg: ?[] switch (setting.action) { .theme => { const want = std.mem.trim(u8, arg orelse return, " \t\r\n"); - for (themes, 0..) |t, i| if (std.mem.eql(u8, t.name, want)) { - colors.setThemeIndex(p, i); - return; - }; + if (colors.themeIndex(want)) |i| return colors.setThemeIndex(p, i); // A config line is not answered on screen (applyStartupConfig // runs with announce off); a click or a ctl write is. if (!p.announce) return; - var near: [96]u8 = undefined; - var text: [200]u8 = undefined; + var near: [170]u8 = undefined; + var text: [256]u8 = undefined; return p.reportFailure(p.active, std.fmt.bufPrint(&text, "Theme: no theme \"{s}\"; like it: {s}; Themes lists all", .{ want[0..@min(want.len, 40)], colors.themesNear(&near, want) }) catch "Theme: no such theme"); }, .font => { |
