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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
//! The admitted frontend boundary (0056 AR02): every cross-crate read
//! of editor state is a readonly borrowed query here, every frontend
//! write an admitted method — no public field reaches documents,
//! service maps, leases, config or recovery state. `fixture_*` hooks
//! exist only under test-support so frontend tests plant state through
//! named seams instead of mutating engine internals.
use super::*;
impl Editor {
/// Focus identity for frame-preparation stamps (AR01).
pub fn focus_epoch(&self) -> u64 {
self.focus_epoch
}
/// Readonly user config (0056 AR02): frontends never mutate it
/// field-by-field; startup/reload replaces it whole.
pub fn config(&self) -> &crate::config::Config {
&self.config
}
/// Replace the user config (startup load, settings reload).
pub fn set_config(&mut self, config: crate::config::Config) {
self.config = config;
}
/// The shared state root for trust/session/recovery, when enabled.
pub fn state_dir(&self) -> Option<&std::path::Path> {
self.state_dir.as_deref()
}
pub fn set_state_dir(&mut self, state_dir: Option<PathBuf>) {
self.state_dir = state_dir;
}
pub fn set_session_policy(&mut self, policy: crate::session::SessionPolicy) {
self.session_policy = policy;
}
/// The interaction mode (0056 AR02 readonly query): paint reads it;
/// input through [`Editor::feed`]/[`Editor::handle_frontend_input`]
/// is the only way it changes.
pub fn mode(&self) -> Mode {
self.mode
}
/// The forensic tape (0056 AR02 readonly borrow): the frontend
/// samples ticks, seeds and seals recordings through [`Tape`]'s own
/// API; a recorder error surfaces through
/// [`Editor::note_tape_divergence`], never a direct message write.
pub fn tape(&self) -> &strop_trace::replay::Tape {
&self.tape
}
/// The open picker, when one is up (readonly; AR02).
pub fn picker(&self) -> Option<&PickerGlue> {
self.picker.as_ref()
}
/// The picker preview file cache (readonly; AR02).
pub fn previews(&self) -> &Previews {
&self.previews
}
/// The working-tree hunk set (readonly; AR02).
pub fn hunks(&self) -> &git_memory::HunkSet {
&self.hunks
}
/// The staged (HEAD↔index) hunk set (readonly; AR02).
pub fn staged_hunks(&self) -> &git_memory::HunkSet {
&self.staged_hunks
}
/// Whether untracked files exist beside the hunk sets (readonly; AR02).
pub fn hunks_untracked(&self) -> bool {
self.hunks_untracked
}
/// The blame card under the cursor, when one is up (readonly; AR02).
pub fn blame_card(&self) -> Option<&strop_git::memory::BlameCard> {
self.blame_card.as_ref()
}
/// The git working-surface context, when discovered (readonly; AR02).
pub fn git(&self) -> Option<&strop_git::GitContext> {
self.git.as_ref()
}
/// The LSP hover card text, when one is up (readonly; AR02).
pub fn hover_card(&self) -> Option<&str> {
self.hover_card.as_deref()
}
/// The statusline message (readonly; AR02).
pub fn message(&self) -> &str {
&self.message
}
/// The admitted message write (0056 AR02): startup/load failures and
/// host-effect errors surface here; engine handlers keep setting the
/// field directly.
pub fn set_message(&mut self, message: impl Into<String>) {
self.message = message.into();
}
/// Tape divergence at the draw boundary (0056 AR02): a failed
/// `Frame` record or observation check is the one message paint may
/// raise, and only through here.
pub fn note_tape_divergence(&mut self, error: impl std::fmt::Display) {
self.message = error.to_string();
}
/// Whether the editor is quitting (readonly; AR02): quit itself is
/// an admitted action (`close_buffer`, `ctrl_c_quit`,
/// [`Editor::request_quit`]).
pub fn should_quit(&self) -> bool {
self.should_quit
}
/// The frontend's quit request (0056 AR02): the input/event stream
/// ended underneath the editor (terminal disconnect, scripted EOF).
pub fn request_quit(&mut self) {
self.should_quit = true;
}
/// Take a pending full-repaint request (ctrl-l desync recovery):
/// the draw loop consumes it exactly once.
pub fn take_repaint_request(&mut self) -> bool {
std::mem::take(&mut self.needs_repaint)
}
/// Drain the queued host-effect payloads (OSC52 clipboard writes):
/// the terminal loop owns writing them to the tty.
pub fn take_terminal_output(&mut self) -> Vec<String> {
std::mem::take(&mut self.terminal_output)
}
/// The process-wide project directory (readonly; AR02).
pub fn cwd(&self) -> &std::path::Path {
&self.cwd
}
/// Name-resolution state (readonly; AR02): the driver polls
/// `pending()` on it while waiting out goto-definition turns.
pub fn resolution(&self) -> &ResolutionState {
&self.resolution
}
/// The free-text pending line (readonly; AR02): `:`/`/` input in
/// flight, drawn by the command card and statusline.
pub fn pending(&self) -> &pending::PendingInput {
&self.pending
}
/// The input walker (readonly; AR02): typed count/register/operator
/// prefix state for the which-key and statusline hints.
pub fn walker(&self) -> &input::Walker {
&self.walker
}
/// MRU document order, most recent first (readonly; AR02).
pub fn mru(&self) -> &[strop_core::id::DocumentId] {
&self.mru
}
/// Install the injected frame renderer (0046): the binary's
/// TestBackend draw used by headless mode and trace replay.
pub fn set_frame_draw(&mut self, frame_draw: Option<FrameDraw>) {
self.frame_draw = frame_draw;
}
/// Fixture hook (tests only): plant a git context without discovery.
#[cfg(any(test, feature = "test-support"))]
pub fn fixture_set_git(&mut self, git: Option<strop_git::GitContext>) {
self.git = git;
}
/// Fixture hook (tests only): plant the untracked-files flag.
#[cfg(any(test, feature = "test-support"))]
pub fn fixture_set_hunks_untracked(&mut self, untracked: bool) {
self.hunks_untracked = untracked;
}
/// Fixture hook (tests only): plant an LSP hover card.
#[cfg(any(test, feature = "test-support"))]
pub fn fixture_set_hover_card(&mut self, card: Option<String>) {
self.hover_card = card;
}
/// The armed search, when `n`/`N` have something to replay
/// (readonly; AR02).
pub fn last_search(&self) -> Option<&LastSearch> {
self.last_search.as_ref()
}
/// The cursor fade-in's start tick, while fading (readonly; AR02) —
/// progress itself is [`Editor::cursor_fade_progress`].
pub fn cursor_fade(&self) -> Option<strop_trace::replay::Tick> {
self.cursor_fade
}
/// Fixture hook (tests only): plant diagnostics without an LSP
/// server round-trip.
#[cfg(any(test, feature = "test-support"))]
pub fn fixture_insert_diagnostics(
&mut self,
document: strop_core::id::DocumentId,
diagnostics: DocumentDiagnostics,
) {
self.diags.insert(document, diagnostics);
}
/// Fixture hook (tests only): plant the working-tree hunk set
/// without a git worker round-trip.
#[cfg(any(test, feature = "test-support"))]
pub fn fixture_set_hunks(&mut self, hunks: git_memory::HunkSet) {
self.hunks = hunks;
}
/// Fixture hook (tests only): plant the cursor fade-in clock.
#[cfg(any(test, feature = "test-support"))]
pub fn fixture_set_cursor_fade(&mut self, fade: Option<strop_trace::replay::Tick>) {
self.cursor_fade = fade;
}
}