1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
//! 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);
}
|