diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/CHANGELOG.md | 10 | ||||
| -rw-r--r-- | src/File.zig | 4 | ||||
| -rw-r--r-- | src/Messages.zig | 2 | ||||
| -rw-r--r-- | src/Output.zig | 2 | ||||
| -rw-r--r-- | src/builtins.zig | 32 | ||||
| -rw-r--r-- | src/config.zig | 11 | ||||
| -rw-r--r-- | src/crash.zig | 2 | ||||
| -rw-r--r-- | src/fs.zig | 19 | ||||
| -rw-r--r-- | src/ninep/ctl.zig | 6 | ||||
| -rw-r--r-- | src/pardes.zig | 10 | ||||
| -rw-r--r-- | src/tutor.txt | 4 |
11 files changed, 83 insertions, 19 deletions
diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md index 50149551..ca37856e 100644 --- a/src/CHANGELOG.md +++ b/src/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## Unreleased + +- `Config` (`SPC f c`) opens the startup file, `init`, in a pane, or goes + to its pane when it is open. With no file yet, it opens an empty pane of + that name, and Save writes it, making the directory first. The settings + report `Config` used to open is `DumpConfig`, in a `+DumpConfig` pane. +- The workspace tag reads `… Dump Themes Config Debug Exit`: `Themes` in + place of `NextColor`, which stays a builtin, and `Config` beside it. A + workspace tag you edited, and one a dump kept, stays as it is. + ## 0.0.5 - Over a slow link the terminal build keeps at most one frame in flight: it diff --git a/src/File.zig b/src/File.zig index 8babb27d..e8a9e2f8 100644 --- a/src/File.zig +++ b/src/File.zig @@ -58,6 +58,10 @@ pub const State = struct { /// as an undo to the save point is in acme. saved_hash: ?u64 = null, watch_after_save: bool = false, + /// Save makes the file's directory, and any above it, first: `Config`'s + /// pane for an init file that is not there yet, whose directory may not + /// be either. + make_dirs: bool = false, /// The file changed on disk under unsaved edits, which were kept (see /// `changed`): its hash, so the same change is said once, until a Save /// or get settles it. A Save warns once before overwriting it, as acme's diff --git a/src/Messages.zig b/src/Messages.zig index 7dcb22ed..d1478ac7 100644 --- a/src/Messages.zig +++ b/src/Messages.zig @@ -782,7 +782,7 @@ test "a message falls in, lingers past the input that dismisses it, and dissolve try std.testing.expect(p.executeBuiltinLine(p.active, "MessageDissolve 0")); try std.testing.expectEqual(@as(u16, 3), messageFrames(p.settings.message_fall_ms)); try std.testing.expectEqual(@as(u16, 1), messageFrames(p.settings.message_dissolve_ms)); - // The Config report reads back as configuration. + // The DumpConfig report reads back as configuration. try std.testing.expect(p.executeBuiltinLine(p.active, "MessageAnimation")); try std.testing.expect(!p.settings.message_animation); try std.testing.expect(p.executeBuiltinLine(p.active, "Msg plain")); diff --git a/src/Output.zig b/src/Output.zig index fb8fe7a3..69e9644c 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -835,7 +835,7 @@ pub fn openConfig(p: *Pardes, id: usize) !void { .state = &p.settings, }); const content = try out.toOwnedSlice(); - return openRead(p, id, .{ .cmd = .Config }, "", content); + return openRead(p, id, .{ .cmd = .DumpConfig }, "", content); } /// The message-row log, oldest first — the lines that were said in passing and diff --git a/src/builtins.zig b/src/builtins.zig index c549b17f..c40b2e00 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -1283,10 +1283,38 @@ pub const Help = struct { } }; -/// Open the settings in effect, each a word to click. +/// Open the startup file, `init`, in a pane: its pane when it is open, else +/// the file, else a pane of that name for Save to make it and its directory. pub const Config = struct { pub const scope: Scope = .session; - pub const output: OutputTraits = .{ .name = config.config_buffer }; + pub fn run(c: Ctx) void { + const path = c.p.opts.startup_config_path orelse + return c.p.reportFailure(c.id, "Config: no startup file here: no configuration directory"); + var real_buf: [4096]u8 = undefined; + const real = if (pardes.filesystem.resolveOs(path, &real_buf)) |r| r.path else null; + for (c.p.panes, 0..) |slot, id| { + const pane = slot orelse continue; + const f = pane.file orelse continue; + if (f.output != null) continue; + if (!std.mem.eql(u8, f.path, path) and !(if (real) |r| std.mem.eql(u8, f.path, r) else false)) continue; + return look.focusPaneLine(c.p, id, .{}, .keep); + } + if (real != null) return look.lookAt(c.p, c.id, path); + // Not there yet: a pane named for it, which Save makes. + const free = c.p.freeSlot() orelse return c.p.reportError(c.id, "Config", error.NoPaneSlots); + c.p.newScratchFor(c.id, .scratch); + const pane = c.p.panes[free] orelse return; + @import("ninep/pane.zig").nameBuffer(c.p, free, path, false) catch |err| return c.p.reportError(c.id, "Config", err); + pane.file.?.make_dirs = true; + c.p.active = free; + } +}; + +/// Open the settings in effect, each a word to click, and the startup +/// file's path. +pub const DumpConfig = struct { + pub const scope: Scope = .session; + pub const output: OutputTraits = .{ .name = config.dump_config_buffer }; pub fn run(c: Ctx) void { panes.Output.openConfig(c.p, c.id) catch |err| c.p.reportError(c.id, "config", err); } diff --git a/src/config.zig b/src/config.zig index ea7a877a..bb033823 100644 --- a/src/config.zig +++ b/src/config.zig @@ -68,6 +68,7 @@ pub const leader_path = paths: { .Recent = "fr", .Grep = "fg", .Config = "fc", + .DumpConfig = null, .LocationsConfig = null, .Tutor = "ht", .Changelog = "hc", @@ -266,7 +267,7 @@ pub const child_term = "xterm-256color"; pub const child_colorterm = "truecolor"; pub const child_term_program = "pardes"; -pub const topbar_str = "Newcol Joincol Find Grep Help Changelog Tutor Dump NextColor Debug Exit"; +pub const topbar_str = "Newcol Joincol Find Grep Help Changelog Tutor Dump Themes Config Debug Exit"; pub const pane_builtins_str = "Tty Collapse Del"; pub const pdf_pane_builtins_str = "Tty PdfSections PdfTint Collapse Del"; @@ -452,7 +453,7 @@ pub const pipe_marker = " |"; pub const search_buffer = "+Search"; pub const help_buffer = "+Help"; -pub const config_buffer = "+Config"; +pub const dump_config_buffer = "+DumpConfig"; pub const effect_code_buffer = "+EffectCode"; pub const jumps_buffer = "+Jumps"; pub const recent_buffer = "+Recent"; @@ -968,7 +969,7 @@ pub const Runtime = struct { } }; - // The builtin registry and Config report share this command table. + // The builtin registry and DumpConfig report share this command table. pub const settings = [_]Setting{ .{ .word = "Colors", .action = .{ .toggle = .colors }, .doc = "Syntax colours in text panes, on or off." }, .{ .word = "Wrap", .action = .{ .toggle = .wrap }, .doc = "Long lines wrap onto the next row, on or off." }, @@ -1292,7 +1293,7 @@ pub const Runtime = struct { /// Each line reads as the command that would set it: the word, then the /// value you would type after it. No colons, no units — `WindowOpacity 70` - /// is both what Config prints and what you write back. + /// is both what DumpConfig prints and what you write back. pub fn writeReport(out: *std.Io.Writer, context: ReportContext) !void { const state = context.state; var wrote_transition = false; @@ -1653,7 +1654,7 @@ pub const Runtime = struct { try std.testing.expect(FontSpec.parse(bad) == null); } - test "Config report observes every simple setting and all live context" { + test "DumpConfig report observes every simple setting and all live context" { var state: Runtime = .{}; // Every setting, every context line: the report outgrows 4K. var storage: [8192]u8 = undefined; diff --git a/src/crash.zig b/src/crash.zig index 10d38591..99822aad 100644 --- a/src/crash.zig +++ b/src/crash.zig @@ -23,7 +23,7 @@ const std = @import("std"); const builtin = @import("builtin"); const pardes = @import("pardes.zig"); -/// Beside `init`, so `Config` opens the directory that holds both. +/// The crash log's name, beside `init` in the configuration directory. pub const name = "crashes"; /// The config directory `config.User.load` resolved, COPIED rather than @@ -637,6 +637,25 @@ test "a path with a part over 255 bytes is NameTooLong, never a panic" { try std.testing.expectError(error.NameTooLong, statPath(std.testing.io, long, .{})); } +/// Make `dir` and each directory above it that is missing, as `mkdir -p` +/// does: one already there is no failure. A failure shows at the open +/// that follows. +pub fn makeDirs(dir: []const u8) void { + if (comptime !platform_has_fs) return; + var z: [4096:0]u8 = undefined; + if (dir.len == 0 or dir.len >= z.len) return; + @memcpy(z[0..dir.len], dir); + z[dir.len] = 0; + pardes.turn.yield(); + defer pardes.turn.back(); + for (z[1..dir.len], 1..) |c, i| if (c == '/') { + z[i] = 0; + _ = libc.mkdir(z[0..i :0], 0o755); + z[i] = '/'; + }; + _ = libc.mkdir(z[0..dir.len :0], 0o755); +} + pub fn localPath(path: []const u8) ?[]const u8 { if (std.mem.eql(u8, path, "/n/os")) return "/"; if (std.mem.startsWith(u8, path, "/n/os/")) return path[5..]; diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 060e0d6c..5ed03c58 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -485,7 +485,7 @@ const e_prompt = "control message needs its argument"; /// Every setting, one a line, in the words a write of it takes, so that the /// file read back is the configuration, as webfs's ctl reads (webfs(4)). /// What is not a setting -- platform, themes, where the config came from -- -/// is /status's or the Config window's. +/// is /status's or the DumpConfig window's. fn writeSettings(p: *Pardes, w: *std.Io.Writer) !void { try writeSettingsOf(p, &p.settings, w); try w.writeAll("LocationsConfig "); @@ -523,7 +523,7 @@ pub fn writeSettingsOf(p: *Pardes, s: *const config.Runtime, w: *std.Io.Writer) inline else => |field| try w.print("{s} {s}\n", .{ word, @tagName(@field(s, @tagName(field))) }), }, .message_ms => |which| try w.print("{s} {d}\n", .{ word, @field(s, "message_" ++ @tagName(which) ++ "_ms") }), - // The directory in effect, as Config reports it; bare only when + // The directory in effect, as DumpConfig reports it; bare only when // there is none (no home to put the default in). .dump_dir => { var buf: [900]u8 = undefined; @@ -1165,7 +1165,7 @@ test "the root ctl reads the settings as a write takes them, and takes the sessi try testing.expectEqual(@as(u64, settings.len), call(p, .{ .tag = 1, .op = .getattr, .node = root_ctl }).reply.attr.size); try testing.expect(std.mem.indexOf(u8, settings, "Verbose on\n") != null); try testing.expect(std.mem.indexOf(u8, settings, "LocationsConfig ") != null); - // Settings only: nothing /status or the Config window says. + // Settings only: nothing /status or the DumpConfig window says. try testing.expect(std.mem.indexOf(u8, settings, "Platform") == null); try testing.expect(std.mem.indexOf(u8, settings, "requested") == null); diff --git a/src/pardes.zig b/src/pardes.zig index 18794975..e3db97d9 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -1329,7 +1329,7 @@ test "acme placement: New and 9P's new take the bottom half of the last pane" { try std.testing.expect(p.rects[made].y > was.y + BOX_H); } -test "Placement pardes brings back the old rules, and says so in Config" { +test "Placement pardes brings back the old rules, and says so in DumpConfig" { const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 100, .rows = 40 }); defer p.deinit(); try std.testing.expect(p.executeBuiltinLine(p.active, "Placement pardes")); @@ -3493,7 +3493,7 @@ test "s in a header selects in the header's own text" { var ranges: [Text.max_selections]modal.Selection = undefined; const text = try edit.flatSurface(p, &p.global_tag); const got = p.global_tag.ranges(text, 0, &ranges); - try std.testing.expectEqual(@as(usize, 3), got.n); // Newcol, Joincol, NextColor + try std.testing.expectEqual(@as(usize, 2), got.n); // Newcol, Joincol try std.testing.expectEqual(@as(u16, 0), pane.body.nsel); } @@ -5261,7 +5261,7 @@ pub const Pardes = struct { } /// Resolve the request already handed to the GUI host. Requested and - /// effective tuples deliberately remain separate so Config can show both. + /// effective tuples deliberately remain separate so DumpConfig can show both. pub fn acknowledgeFont( p: *Pardes, effective_name: []const u8, @@ -5596,6 +5596,8 @@ pub const Pardes = struct { const was = f.saved_revision; const failures = p.fs.failures; f.saved_revision = f.revision; + if (f.make_dirs) if (filesystem.localPath(f.path)) |local| + filesystem.makeDirs(std.fs.path.dirname(local) orelse "/"); p.hostWriteFile(sf.pane, f.path, f.content); const saved_pane = p.panes[sf.pane] orelse return; if (saved_pane.serial != serial) return; @@ -6721,7 +6723,7 @@ pub const Pardes = struct { } /// A native post pass proved unavailable: the chain is emptied, which - /// Config then reports. + /// DumpConfig then reports. pub fn disableSceneEffects(p: *Pardes) void { p.settings.post.clear(); } diff --git a/src/tutor.txt b/src/tutor.txt index c59deeae..f8aa7aef 100644 --- a/src/tutor.txt +++ b/src/tutor.txt @@ -65,8 +65,8 @@ Every pane has Collapse: hide its body, then execute again to expand it. The top bar is: - Newcol Joincol Find Grep Help Changelog Tutor Dump NextColor - Debug Exit + Newcol Joincol Find Grep Help Changelog Tutor Dump Themes + Config Debug Exit Reach it from the keyboard with `k` off the topmost tagline; `j` comes back down. |
