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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! Bund hook dispatch.
//!
//! Every `Store` mutation that has a corresponding well-known hook
//! name (`hook.on_save`, `hook.on_rename`, …) calls [`fire`] after
//! the mutation succeeds. If the user has defined a Bund lambda
//! with that name (via the bootstrap script in `inkhaven.hjson`),
//! we push the supplied args onto the workbench and run it.
//!
//! ## Failure model
//!
//! Hooks must not break the editor. Every failure path is logged
//! at WARN and swallowed — a misbehaving hook never aborts a save,
//! a rename, or a snapshot. The user sees the log line and the
//! store mutation still completes.
//!
//! ## Recursion guard
//!
//! A hook lambda can in principle invoke an `ink.*` write word in
//! a future phase (P5+), which would re-enter the store mutation
//! that fired the hook — and recurse. We track depth in a
//! thread-local counter and refuse to dispatch past [`MAX_DEPTH`].
//! Today's read-only `ink.*` words can't trigger this, but the
//! guard is forward-looking.
//!
//! ## Single-threaded by design
//!
//! Hooks fire synchronously on the calling thread, which holds
//! Adam's write lock for the duration. That means a slow hook is
//! a slow save — felt by the writer. Backgrounding via an
//! ephemeral worker pool is P6.
use Cell;
use Value;
/// Recursion cap. Picked to be large enough that legitimate
/// nested workflows (a save hook that calls a rename hook that
/// calls a snapshot hook) keep working, small enough that a
/// runaway recursion is bounded in cost.
const MAX_DEPTH: u32 = 4;
thread_local!
/// Fire `name` against Adam if a lambda with that name is defined.
/// Silent no-op when:
///
/// - Adam hasn't been initialised yet (called before TUI startup).
/// - No lambda named `name` is registered.
/// - The recursion depth cap has been reached.
///
/// `args` are pushed onto the workbench stack **in order** before
/// the lambda runs, so the lambda body sees the first arg at the
/// bottom of its workbench and the last on top. Hooks that need
/// no args pass an empty `Vec`.