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
//! One CPU owner for expensive pure grammar resolution. No thread per key and
//! no join on the editor thread; a frozen rope is the complete read input.
use super::{ResolutionData, ResolutionEvent, ResolutionKey};
use std::sync::{
atomic::{AtomicBool, Ordering},
mpsc, Arc,
};
use strop_core::worker::{CancelReason, Completion, FailureKind, Outcome, Ticket};
pub(super) struct Work {
pub ticket: Ticket<ResolutionKey>,
pub rope: ropey::Rope,
pub cancel: Arc<AtomicBool>,
}
pub(super) struct ResolutionWorker {
sender: mpsc::Sender<Work>,
}
impl ResolutionWorker {
pub fn start(events: mpsc::Sender<ResolutionEvent>) -> std::io::Result<Self> {
let (sender, receiver) = mpsc::channel::<Work>();
std::thread::Builder::new()
.name("grammar-resolution".into())
.spawn(move || {
let mut geometry = super::super::analysis::layouts::LayoutCache::default();
while let Ok(work) = receiver.recv() {
let outcome = if work.cancel.load(Ordering::Acquire) {
Outcome::Cancelled(CancelReason::Superseded)
} else {
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let buffer = strop_core::Buffer::from_snapshot(work.rope);
let mut command = work.ticket.key.command.clone();
bind_cancellation(&mut command.target, &work.cancel);
let resolved = strop_grammar::resolve_many_cancellable(
&buffer,
&work.ticket.key.cursors,
&command,
&work.cancel,
)?;
let plan = if command.op.is_some() {
strop_grammar::ActionPlan::from_resolved(
&work.ticket.key.cursors,
&resolved,
)
} else {
None
};
let mut lines = std::collections::BTreeSet::new();
for cursor in &work.ticket.key.cursors {
lines.insert(strop_core::id::LineIndex::new(
buffer.line_of(*cursor),
));
}
for value in resolved.iter().flatten() {
for byte in [
value.range.start.get(),
value.range.end.get(),
value.motion_target.unwrap_or(value.range.start.get()),
] {
lines.insert(strop_core::id::LineIndex::new(
buffer.line_of(byte),
));
}
}
let layouts = geometry
.prepare(
&buffer,
&super::super::analysis::AnalysisTarget::Document(
work.ticket.key.document,
),
work.ticket.key.revision,
work.ticket.key.tab,
lines,
|| work.cancel.load(Ordering::Acquire),
)
.ok_or(strop_grammar::QueryError::Cancelled)?;
Ok::<_, strop_grammar::QueryError>(ResolutionData {
resolved,
plan,
layouts,
})
}));
match result {
_ if work.cancel.load(Ordering::Acquire) => {
Outcome::Cancelled(CancelReason::Superseded)
}
Ok(Ok(data)) => Outcome::Success(data),
Ok(Err(strop_grammar::QueryError::Cancelled)) => {
Outcome::Cancelled(CancelReason::Superseded)
}
Ok(Err(error)) => {
Outcome::failed(FailureKind::InvalidInput, error.to_string())
}
Err(_) => {
Outcome::failed(FailureKind::Panic, "grammar resolution failed")
}
}
};
if events
.send(ResolutionEvent::Completed(Box::new(Completion {
ticket: work.ticket,
outcome,
})))
.is_err()
{
return;
}
}
let _ = events.send(ResolutionEvent::Stopped);
})?;
Ok(Self { sender })
}
pub fn resolve(&self, work: Work) -> Result<(), Box<Work>> {
self.sender.send(work).map_err(|error| Box::new(error.0))
}
}
fn bind_cancellation(target: &mut strop_grammar::Target, flag: &Arc<AtomicBool>) {
use strop_grammar::{Motion, Target};
match target {
Target::Motion(Motion::Search(query) | Motion::SearchBackward(query)) => {
*query = query.cancellable(flag.clone());
}
Target::SurroundAdd { inner, .. } => bind_cancellation(inner, flag),
_ => {}
}
}