1mod worker;
5use super::{Editor, Key};
6use std::collections::VecDeque;
7use std::sync::{
8 atomic::{AtomicBool, Ordering},
9 mpsc, Arc,
10};
11use strop_core::{
12 id::{BufferRevision, DocumentId},
13 worker::{Completion, FailureKind, Outcome, Ticket},
14};
15use strop_grammar::{ActionPlan, Command, Resolved};
16
17#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
18pub struct ResolutionKey {
19 document: DocumentId,
20 revision: BufferRevision,
21 pane: usize,
22 command: Command,
23 cursors: Vec<usize>,
24 tab: usize,
25}
26#[derive(Clone, serde::Serialize, serde::Deserialize)]
27pub struct ResolutionData {
28 resolved: Vec<Option<Resolved>>,
29 plan: Option<ActionPlan>,
30 layouts: Vec<strop_core::layout::PreparedLineLayout>,
31}
32#[derive(serde::Serialize, serde::Deserialize)]
33pub enum ResolutionEvent {
34 Completed(Box<Completion<ResolutionKey, ResolutionData>>),
35 Stopped,
36}
37#[derive(Clone, Copy, PartialEq, Eq)]
38pub(crate) enum ResolutionPurpose {
39 Motion,
40 Execute,
41 IncSearch,
42 Preview,
43 VisualObject,
44 RepeatSearch(bool),
45 DotRepeat,
46}
47impl ResolutionPurpose {
48 fn blocks_input(self) -> bool {
49 matches!(
50 self,
51 Self::Motion
52 | Self::Execute
53 | Self::VisualObject
54 | Self::RepeatSearch(_)
55 | Self::DotRepeat
56 )
57 }
58}
59struct Pending {
60 ticket: Ticket<ResolutionKey>,
61 purpose: ResolutionPurpose,
62 cancel: Arc<AtomicBool>,
63 macro_depth: usize,
64}
65pub enum DeferredInput {
66 Key(Key),
67 GeneratedKey {
68 key: Key,
69 depth: usize,
70 },
71 Paste(String),
72 Leaf {
73 handler: fn(&mut Editor, char),
74 key: char,
75 remaining: usize,
76 depth: usize,
77 },
78 Macro {
79 keys: Arc<[Key]>,
80 position: usize,
81 repetitions: usize,
82 depth: usize,
83 },
84}
85pub struct ResolutionState {
86 pub tx: mpsc::Sender<ResolutionEvent>,
87 pub rx: Option<mpsc::Receiver<ResolutionEvent>>,
88 worker: Option<worker::ResolutionWorker>,
89 pending: Option<Pending>,
90 ready: Option<(ResolutionKey, Result<ResolutionData, String>)>,
91 pub queue: VecDeque<DeferredInput>,
92 pub staged: VecDeque<DeferredInput>,
93 pub in_action: bool,
94 resume_scheduled: bool,
95 started: bool,
96 stopping: bool,
97 pub enabled: bool,
98}
99impl Default for ResolutionState {
100 fn default() -> Self {
101 let (tx, rx) = mpsc::channel();
102 Self {
103 tx,
104 rx: Some(rx),
105 worker: None,
106 pending: None,
107 ready: None,
108 queue: VecDeque::new(),
109 staged: VecDeque::new(),
110 in_action: false,
111 resume_scheduled: false,
112 started: false,
113 stopping: false,
114 enabled: false,
115 }
116 }
117}
118impl ResolutionState {
119 pub fn blocked(&self) -> bool {
120 self.pending
121 .as_ref()
122 .is_some_and(|pending| pending.purpose.blocks_input())
123 }
124 pub fn pending(&self) -> bool {
125 self.pending.is_some() || self.stopping || !self.queue.is_empty()
126 }
127 pub fn cancel(&mut self) {
128 if let Some(pending) = self.pending.take() {
129 pending.cancel.store(true, Ordering::Release);
130 }
131 }
132 pub fn cancel_preview(&mut self) {
133 if self
134 .pending
135 .as_ref()
136 .is_some_and(|pending| !pending.purpose.blocks_input())
137 {
138 self.cancel();
139 }
140 }
141 pub fn stop(&mut self) {
142 self.cancel();
143 self.queue.clear();
144 self.staged.clear();
145 self.stopping = self.started;
146 self.worker = None;
147 }
148}
149
150impl Editor {
151 pub(crate) fn resolution_is_large(&self, command: &Command) -> bool {
154 const INLINE_BYTES: usize = 4096;
155 self.resolution.enabled
156 && (self.buf().len_bytes() > INLINE_BYTES || command.count.unwrap_or(1) > INLINE_BYTES)
157 }
158 fn resolution_matches(
159 &self,
160 key: &ResolutionKey,
161 command: &Command,
162 cursors: &[usize],
163 ) -> bool {
164 !self.docs.is_empty()
165 && key.document == self.current()
166 && key.revision == self.buf().revision()
167 && key.pane == self.active_pane
168 && key.tab == self.cur_indent().width.max(1)
169 && &key.command == command
170 && key.cursors == cursors
171 }
172 fn prepared_resolution(
173 &self,
174 command: &Command,
175 cursors: &[usize],
176 ) -> Option<Result<&ResolutionData, &str>> {
177 let (key, ready) = self.resolution.ready.as_ref()?;
178 self.resolution_matches(key, command, cursors)
179 .then(|| ready.as_ref().map_err(String::as_str))
180 }
181 pub(crate) fn resolved_many(
182 &self,
183 command: &Command,
184 cursors: &[usize],
185 ) -> Result<Vec<Option<Resolved>>, String> {
186 if let Some(ready) = self.prepared_resolution(command, cursors) {
187 return ready
188 .map(|data| data.resolved.clone())
189 .map_err(str::to_owned);
190 }
191 strop_grammar::resolve_many(self.buf(), cursors, command).map_err(|error| error.to_string())
192 }
193 pub(crate) fn resolved_plan(
194 &self,
195 command: &Command,
196 cursors: &[usize],
197 ) -> Result<Option<ActionPlan>, String> {
198 if let Some(ready) = self.prepared_resolution(command, cursors) {
199 return ready.map(|data| data.plan.clone()).map_err(str::to_owned);
200 }
201 strop_grammar::plan(self.buf(), cursors, command).map_err(|error| error.to_string())
202 }
203 pub(crate) fn resolution_ready(&self, command: &Command, cursors: &[usize]) -> bool {
204 self.prepared_resolution(command, cursors).is_some()
205 }
206
207 pub(crate) fn defer_resolution(
208 &mut self,
209 command: &Command,
210 cursors: Vec<usize>,
211 purpose: ResolutionPurpose,
212 ) -> bool {
213 if !self.resolution_is_large(command) || self.resolution_ready(command, &cursors) {
214 return false;
215 }
216 if let Some(pending) = self.resolution.pending.as_ref() {
217 if self.resolution_matches(&pending.ticket.key, command, &cursors) {
218 if purpose.blocks_input() {
219 if let Some(pending) = self.resolution.pending.as_mut() {
220 pending.purpose = purpose;
221 }
222 }
223 return true;
224 }
225 }
226 self.resolution.cancel();
227 if !self.resolution.started {
228 let started: Result<(), String> = match self.tape.call("grammar.start", &(), || {
229 worker::ResolutionWorker::start(self.resolution.tx.clone())
230 .map(|worker| self.resolution.worker = Some(worker))
231 .map_err(|error| error.to_string())
232 }) {
233 Ok(result) => result,
234 Err(error) => Err(error.to_string()),
235 };
236 if let Err(error) = started {
237 self.message = format!("grammar: {error}");
238 return true;
239 }
240 self.resolution.started = true;
241 }
242 let request = match self.worker_ids.allocate() {
243 Ok(request) => request,
244 Err(error) => {
245 self.message = error.message;
246 return true;
247 }
248 };
249 let key = ResolutionKey {
250 document: self.current(),
251 revision: self.buf().revision(),
252 pane: self.active_pane,
253 command: command.clone(),
254 cursors,
255 tab: self.cur_indent().width.max(1),
256 };
257 let ticket = Ticket { request, key };
258 let cancel = Arc::new(AtomicBool::new(false));
259 self.resolution.pending = Some(Pending {
260 ticket: ticket.clone(),
261 purpose,
262 cancel: cancel.clone(),
263 macro_depth: self.macro_depth,
264 });
265 match self.tape.request("grammar.resolve", &ticket) {
266 Ok(false) => return true,
267 Ok(true) => {}
268 Err(error) => {
269 self.handle_resolution(ResolutionEvent::Completed(Box::new(Completion {
270 ticket,
271 outcome: Outcome::failed(FailureKind::Protocol, error.to_string()),
272 })));
273 return true;
274 }
275 }
276 let work = worker::Work {
277 ticket,
278 rope: self.buf().snapshot(),
279 cancel,
280 };
281 let failed = match &self.resolution.worker {
282 Some(worker) => worker.resolve(work).err(),
283 None => Some(Box::new(work)),
284 };
285 if let Some(work) = failed {
286 self.handle_resolution(ResolutionEvent::Completed(Box::new(Completion {
287 ticket: work.ticket,
288 outcome: Outcome::failed(FailureKind::Disconnected, "grammar worker stopped"),
289 })));
290 }
291 true
292 }
293
294 pub(crate) fn handle_resolution(&mut self, event: ResolutionEvent) {
295 let ResolutionEvent::Completed(completion) = event else {
296 self.resolution.started = false;
297 self.resolution.stopping = false;
298 return;
299 };
300 if !self
301 .resolution
302 .pending
303 .as_ref()
304 .is_some_and(|pending| pending.ticket == completion.ticket)
305 {
306 return;
307 }
308 let Some(pending) = self.resolution.pending.take() else {
309 return;
310 };
311 let key = completion.ticket.key;
312 let current = !self.finishing
313 && !self.docs.is_empty()
314 && self.current() == key.document
315 && self.active_pane == key.pane
316 && self.buf().revision() == key.revision
317 && (!pending.purpose.blocks_input() || self.all_cursors() == key.cursors);
318 if !current {
319 self.resolution.queue.clear();
320 if !self.finishing {
321 self.message = "resolution cancelled: document or cursor changed".into();
322 }
323 return;
324 }
325 let ready = match completion.outcome {
326 Outcome::Success(data) => {
327 if !self.docs.get_mut(key.document).is_some_and(|document| {
328 document
329 .buf
330 .install_line_layouts(key.revision, &data.layouts)
331 }) {
332 self.message = "invalid layout publication".into();
333 self.resolution.queue.clear();
334 return;
335 }
336 Ok(data)
337 }
338 Outcome::Failed { failure, .. } => Err(failure.message),
339 Outcome::Cancelled(_) => {
340 self.resume_resolution_input();
341 return;
342 }
343 };
344 let succeeded = ready.is_ok();
345 if let Err(error) = &ready {
346 self.message = error.clone();
347 }
348 self.resolution.ready = Some((key.clone(), ready));
349 if succeeded {
350 let saved_depth = self.macro_depth;
351 self.macro_depth = pending.macro_depth;
352 self.run_input_action(|editor| match pending.purpose {
353 ResolutionPurpose::Motion => editor.move_cursor(&key.command),
354 ResolutionPurpose::Execute => editor.dispatch_grammar(&key.command),
355 ResolutionPurpose::IncSearch => editor.incsearch_jump(),
356 ResolutionPurpose::Preview => {}
357 ResolutionPurpose::VisualObject => editor.select_visual_object(&key.command),
358 ResolutionPurpose::RepeatSearch(invert) => editor.repeat_search(invert),
359 ResolutionPurpose::DotRepeat => editor.dot_repeat_pub(),
360 });
361 self.macro_depth = saved_depth;
362 }
363 self.resume_resolution_input();
364 }
365
366 pub(crate) fn resume_resolution_input(&mut self) {
367 self.resolution.resume_scheduled = false;
368 if !self.resolution.blocked() && !self.should_quit {
371 if let Some(input) = self.resolution.queue.pop_front() {
372 self.run_input_action(|editor| {
373 match input {
374 DeferredInput::Key(key) => editor.feed_inner(key),
375 DeferredInput::GeneratedKey { key, depth } => {
376 let saved = editor.macro_depth;
377 editor.macro_depth = depth;
378 editor.feed_inner(key);
379 editor.macro_depth = saved;
380 }
381 DeferredInput::Paste(text) => editor.paste_bracketed(&text),
382 DeferredInput::Leaf {
383 handler,
384 key,
385 remaining,
386 depth,
387 } => {
388 let saved = editor.macro_depth;
389 editor.macro_depth = depth;
390 editor.run_counted_leaf(handler, key, remaining);
391 editor.macro_depth = saved;
392 }
393 DeferredInput::Macro {
394 keys,
395 position,
396 repetitions,
397 depth,
398 } => {
399 editor.run_macro_step(keys, position, repetitions, depth);
400 }
401 }
402 editor.prepare_resolution_preview();
403 });
404 }
405 }
406 self.schedule_resolution_input();
407 }
408}
409
410impl Editor {
411 pub(crate) fn run_input_action(&mut self, action: impl FnOnce(&mut Editor)) {
412 let nested = self.resolution.in_action;
413 self.resolution.in_action = true;
414 action(self);
415 self.resolution.in_action = nested;
416 if !nested {
417 while let Some(input) = self.resolution.staged.pop_back() {
418 self.resolution.queue.push_front(input);
419 }
420 self.schedule_resolution_input();
421 if self.mode == super::Mode::Normal {
424 self.maybe_sync_collection();
425 }
426 }
427 }
428 pub(crate) fn run_counted_leaf(
429 &mut self,
430 handler: fn(&mut Editor, char),
431 key: char,
432 count: usize,
433 ) {
434 if !self.resolution.enabled {
435 for _ in 0..count {
436 handler(self, key);
437 }
438 return;
439 }
440 if count == 0 {
441 return;
442 }
443 handler(self, key);
444 if count > 1 {
445 self.resolution.queue.push_front(DeferredInput::Leaf {
446 handler,
447 key,
448 remaining: count - 1,
449 depth: self.macro_depth,
450 });
451 }
452 self.schedule_resolution_input();
453 }
454 fn schedule_resolution_input(&mut self) {
455 if !self.resolution.blocked()
456 && !self.resolution.queue.is_empty()
457 && !self.resolution.resume_scheduled
458 {
459 self.resolution.resume_scheduled = true;
460 if let Some(sender) = &self.app_tx {
461 if sender.send(super::events::AppEvent::ResumeInput).is_err() {
462 self.message = "deferred input channel closed".into();
463 }
464 }
465 }
466 }
467 pub(crate) fn queue_macro(&mut self, keys: Vec<Key>, repetitions: usize, depth: usize) {
468 if keys.is_empty() || repetitions == 0 {
469 return;
470 }
471 self.resolution.queue.push_front(DeferredInput::Macro {
472 keys: keys.into(),
473 position: 0,
474 repetitions,
475 depth,
476 });
477 self.schedule_resolution_input();
478 }
479 fn run_macro_step(
480 &mut self,
481 keys: Arc<[Key]>,
482 position: usize,
483 repetitions: usize,
484 depth: usize,
485 ) {
486 let key = keys[position];
487 let next = position + 1;
488 if next < keys.len() {
489 self.resolution.queue.push_front(DeferredInput::Macro {
490 keys,
491 position: next,
492 repetitions,
493 depth,
494 });
495 } else if repetitions > 1 {
496 self.resolution.queue.push_front(DeferredInput::Macro {
497 keys,
498 position: 0,
499 repetitions: repetitions - 1,
500 depth,
501 });
502 }
503 let saved = self.macro_depth;
504 self.macro_depth = depth;
505 self.feed_inner(key);
506 self.macro_depth = saved;
507 }
508}