diff options
Diffstat (limited to 'src/output_pane.zig')
| -rw-r--r-- | src/output_pane.zig | 54 |
1 files changed, 49 insertions, 5 deletions
diff --git a/src/output_pane.zig b/src/output_pane.zig index 8e08285f..a0583506 100644 --- a/src/output_pane.zig +++ b/src/output_pane.zig @@ -80,6 +80,19 @@ pub const Traits = struct { /// look path resolves, so the buffer IS helix's picker. Prose (a hover /// blurb, a rename diff) has nowhere to step to. steps: bool = false, + /// ...and what a step DOES with the row it lands on. Off, the row is a + /// LOCATION and its leading word is LOOKED. On, the row is a COMMAND LINE + /// and the whole of it is EXECUTED — so walking the list runs each row in + /// turn, which is what makes a picker over things that take effect + /// immediately (ThemeSel) a plain list of the words you would have typed. + /// Both go through the ordinary builtin (config.look_cmd / exec_cmd), so a + /// row does exactly what the matching mouse button on it would. + /// + /// Nothing about this is output-pane specific, which is why it is a column + /// here and not a branch in searchStep: `file_row` answers it too, so a + /// file pane whose lines happen to be commands is one word away from + /// behaving the same. + executes: bool = false, /// an answer of exactly ONE row jumps straight there instead of opening /// this buffer at all — helix: the gotos jump on a single location and /// show a picker on several, a symbol list is always a picker. @@ -114,6 +127,10 @@ pub fn traits(o: Origin) Traits { // the focus history, one location per row: not a search, but the // same kind of list, so n/N walk it and a row is a look target .Jumplist => .{ .name = config.jumps_buffer, .steps = true }, + // the theme ring, one `Theme <name>` per row. The only buffer whose + // rows are COMMANDS rather than locations, so n/N execute them: + // walking the list wears each theme, and stopping is picking one. + .ThemeSel => .{ .name = config.themes_buffer, .steps = true, .executes = true }, // Find (file names) and Grep (file contents) both list locations; // no other builtin opens a buffer, and the day one does it lands // here rather than in a call site. @@ -210,7 +227,6 @@ pub fn open(p: *Pardes, id: usize, dir: []const u8, from: Origin, arg: []const u /// content line for anything holding text, else the pane's directory: enough /// to recognise the place without opening it. pub fn openJumps(p: *Pardes, id: usize) void { - const pane = p.panes[id] orelse return; const arena = p.scratch.allocator(); var out: std.ArrayList(u8) = .empty; for (p.jumps[0..p.njumps]) |j| { @@ -241,19 +257,47 @@ pub fn openJumps(p: *Pardes, id: usize) void { std.fmt.allocPrint(arena, "{s}:{d}:{d} {s}\n", .{ loc, j.line, j.col, what[0..cut] }) catch return; out.appendSlice(arena, row) catch return; } - const content = p.gpa.dupe(u8, out.items) catch return; + openStepped(p, id, .{ .cmd = .Jumplist }, out.items); +} + +/// The ThemeSel builtin: the theme ring written out as one `Theme <name>` row +/// per theme — the ordinary builtin with its argument, exactly the line you +/// would type — into a buffer whose `executes` trait makes n/N RUN each row +/// rather than look it. So walking the list is trying the themes on, and +/// stopping on one is choosing it; there is no picker mode, no preview state +/// and nothing to commit or cancel, because every step already did the thing. +/// +/// The command's own name comes from the builtin rather than a literal: the +/// word is derived from the struct in exactly one place (builtins.word), and a +/// rename there must not leave rows here that name something that is gone. +pub fn openThemes(p: *Pardes, id: usize) void { + const arena = p.scratch.allocator(); + var out: std.ArrayList(u8) = .empty; + for (pardes.themes) |t| { + out.print(arena, comptime builtins.word(builtins.Theme) ++ " {s}\n", .{t.name}) catch return; + } + openStepped(p, id, .{ .cmd = .ThemeSel }, out.items); +} + +/// Open a buffer n/N will walk, and arm them on it: the shared tail of every +/// builtin that answers with a list. `text` is borrowed (the callers build it +/// in the scratch arena) and copied into a gpa buffer the pane adopts. +/// +/// Focus stays with the pane that ASKED, exactly as it does after a search: +/// n/N are read there, and they step the buffer they just armed. +fn openStepped(p: *Pardes, id: usize, from: Origin, text: []const u8) void { + const pane = p.panes[id] orelse return; + const content = p.gpa.dupe(u8, text) catch return; const dir = if (pane.file) |f| (std.fs.path.dirname(f.path) orelse "/") else pane.cwdSlice(); const free = p.freeSlot() orelse { p.gpa.free(content); return; }; - const np = open(p, free, dir, .{ .cmd = .Jumplist }, "", content) catch { + const np = open(p, free, dir, from, "", content) catch { p.gpa.free(content); return; }; p.placeDoc(id, free, np); - // focus stays with the pane that asked, exactly as it does after a search: - // n/N are read there, and they step the buffer they just armed. p.active = id; pane.search_pane = free; pane.search_row = null; |
