Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 117 additions & 3 deletions crates/diffy-syntax/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ enum Message {
slot: u64,
priority: Priority,
},
/// A pending grammar resolved: rerun the jobs waiting for one.
/// A pending grammar resolved: rerun the jobs waiting for one, and
/// start over the long passes that colored windows without one.
Retry,
/// A handle was dropped: drop its jobs.
Forget(u64),
Expand Down Expand Up @@ -513,6 +514,19 @@ fn run(
// Slots rerun for a retry carry what each last published: a rerun
// that changes nothing is parked again without another result.
if retry {
// A long pass that yielded colored its first windows without
// the grammars it was waiting for, and only a pass from the
// start recolors them.
for held in queued.values_mut() {
if held.job.resume.0 != 0
&& held
.previous
.as_ref()
.is_some_and(|previous| !previous.unresolved.is_empty())
{
held.job.resume = (0, None);
}
}
for (key, waiting) in parked.drain() {
order.push(key);
queued.insert(
Expand Down Expand Up @@ -603,8 +617,13 @@ fn run(
}
}
if outcome.streamed {
// Languages still arriving over every turn of this pass.
let mut unresolved = previous.map_or_else(Vec::new, |p| p.unresolved);
// Languages still arriving over every turn of this pass. A turn
// from the start begins a new pass, which owes nothing to the
// languages the last one was missing.
let mut unresolved = match previous {
Some(previous) if job.resume.0 != 0 => previous.unresolved,
_ => Vec::new(),
};
for language in outcome.unresolved {
if !unresolved.contains(&language) {
unresolved.push(language);
Expand Down Expand Up @@ -821,6 +840,101 @@ mod tests {
);
}

// Catches a grammar that arrives while a long pass waits for its turn
// never reaching the windows that pass colored before it arrived: the
// pass starts over, and once nothing is missing it is not run again on
// a later arrival.
#[test]
fn grammar_arriving_mid_pass_restarts_the_pass_once() {
let arrived = Arc::new(AtomicBool::new(false));
let (started_tx, started) = channel();
let (resume_tx, resume) = channel::<()>();
let resume = Mutex::new(resume);
let seen = arrived.clone();
let worker = HighlightWorker::with_highlighter(Arc::new(
move |request: &HighlightRequest,
slice: &Slice,
cancelled: &_,
emit: &mut dyn FnMut(Part)| {
if &*request.source != "long" {
if matches!(&*request.source, "hold" | "other") {
started_tx.send(()).unwrap();
resume.lock().unwrap().recv().unwrap();
}
return panics_on_boom(request, slice, cancelled, emit);
}
let unresolved = if seen.load(Ordering::Relaxed) {
Vec::new()
} else {
vec![LanguageId::from_fence("html").unwrap()]
};
let mut handed = 0;
for k in slice.from as u32..3 {
emit(Part {
range: k..k + 1,
exact: true,
spans: Vec::new(),
unresolved: unresolved.clone(),
});
handed += 1;
if k + 1 < 3 && (slice.yield_after)(handed) {
return Outcome {
unresolved,
streamed: true,
resume: Some((k as usize + 1, None)),
..Outcome::default()
};
}
}
Outcome {
unresolved,
streamed: true,
..Outcome::default()
}
},
));
let next = || {
worker
.done
.recv_timeout(std::time::Duration::from_secs(10))
.ok()
.map(|r| (r.slot, r.part.map(|p| p.range.start)))
};
worker.request(9, 1, rust(), Arc::from("hold"));
started.recv().unwrap();
worker.request(1, 1, rust(), Arc::from("long"));
worker.request(2, 1, rust(), Arc::from("other"));
resume_tx.send(()).unwrap();
// The long pass took one turn with HTML missing and waits behind
// "other", which runs now.
started.recv().unwrap();
arrived.store(true, Ordering::Relaxed);
let _ = worker.thread.jobs.send(Message::Retry);
resume_tx.send(()).unwrap();
let seen: Vec<_> = (0..6).map(|_| next()).collect();
let _ = worker.thread.jobs.send(Message::Retry);
worker.request_with(
3,
1,
HighlightRequest::new(rust(), Arc::from("after")).priority(Priority::Background),
);

assert_eq!(
(seen, next()),
(
vec![
Some((9, None)),
Some((1, Some(0))),
Some((2, None)),
Some((1, Some(0))),
Some((1, Some(1))),
Some((1, Some(2))),
],
Some((3, None))
)
);
}

// Catches a panicking highlight killing the thread, after which every
// block would stay plain.
#[test]
Expand Down
Loading