//! Output panes: acme's +Errors, a file pane with no file behind it, holding //! text the core produced itself (+Search results, +Help, the LSP answer //! buffers). The ONLY thing that makes one different from a file pane is that //! it cannot be saved — every mode, motion, chord and look works because it IS //! a file pane, and that reuse is the point. //! //! Thin on purpose, for now. The `f.output` tests still scattered through //! pardes.zig each ask a one-line question inside kind-agnostic code (does //! this pane claim a column? can it be saved? is it a doc?) and pulling them //! here would trade one line for a call. What belongs here is what an output //! pane KNOWS about itself, and today that is just how one is opened. const std = @import("std"); const pardes = @import("pardes.zig"); const Pardes = pardes.Pardes; const Pane = pardes.Pane; const file_pane = @import("file_pane.zig"); const config = @import("config.zig"); /// `content` is gpa-owned and adopted. The name carries `dir` so looks inside /// the buffer resolve like anywhere else. pub fn open(p: *Pardes, id: usize, dir: []const u8, name: []const u8, content: []u8) !*Pane { const path = try std.fmt.allocPrint(p.gpa, "{s}/{s}", .{ std.mem.trimEnd(u8, dir, "/"), name }); errdefer p.gpa.free(path); const pane = try p.newDocPane(id); pane.file = .{ .path = path, .content = content, .output = true }; pane.cur_pinned = true; return pane; } /// The Help builtin: every builtin reachable from `prefix` (empty = all /// of them) with the keys that run it, written into an output buffer /// (acme's +Errors) — ordinary text, so the names in it are LIVE: middle- /// click `Tutor` there and the tutor opens. Reuses the open +Help buffer /// instead of piling panes up, and focus follows: you asked to read it. pub fn openHelp(p: *Pardes, id: usize, prefix: []const u8) void { const pane = p.panes[id] orelse return; const arena = p.scratch.allocator(); var out: std.ArrayList(u8) = .empty; if (prefix.len == 0) { out.appendSlice(arena, "pardes builtins - SPC then the keys,\nor execute the name anywhere\n\n") catch return; } else { out.appendSlice(arena, "pardes builtins under SPC") catch return; for (prefix) |c| out.appendSlice(arena, &[_]u8{ ' ', c }) catch return; out.appendSlice(arena, "\n\n") catch return; } for (pardes.leader_rows) |row| { if (!std.mem.startsWith(u8, row.path, prefix)) continue; out.appendSlice(arena, row.line) catch return; out.append(arena, '\n') catch return; } const content = p.gpa.dupe(u8, out.items) catch return; for (p.panes, 0..) |slot, i| { const hp = slot orelse continue; const hf = if (hp.file) |*f| f else continue; if (!hf.output or !std.mem.endsWith(u8, hf.path, "/" ++ config.help_buffer)) continue; file_pane.setContent(p, hf, content); hf.scroll = 0; hp.cur_row = 0; hp.msel.active = false; p.active = i; 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, config.help_buffer, content) catch { p.gpa.free(content); return; }; p.placeDoc(id, free, np); }