Block for up to 1ms when reparsing

This way, we'll avoid rendering two frames on edits that lead to fast reparses.

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld
2021-09-06 12:50:38 -07:00
co-authored by Nathan Sobo
parent 11b8577d1b
commit 8e5c709057
+56 -57
View File
@@ -120,7 +120,7 @@ pub struct Buffer {
file: Option<File>,
language: Option<Arc<Language>>,
syntax_tree: Mutex<Option<SyntaxTree>>,
is_parsing: bool,
parsing_in_background: bool,
parse_count: usize,
selections: HashMap<SelectionSetId, SelectionSet>,
deferred_ops: OperationQueue,
@@ -581,7 +581,7 @@ impl Buffer {
history,
file,
syntax_tree: Mutex::new(None),
is_parsing: false,
parsing_in_background: false,
parse_count: 0,
language,
saved_mtime,
@@ -610,7 +610,7 @@ impl Buffer {
fragments: self.fragments.clone(),
version: self.version.clone(),
tree: self.syntax_tree(),
is_parsing: self.is_parsing,
is_parsing: self.parsing_in_background,
language: self.language.clone(),
query_cursor: QueryCursorHandle::new(),
}
@@ -825,68 +825,67 @@ impl Buffer {
#[cfg(test)]
pub fn is_parsing(&self) -> bool {
self.is_parsing
self.parsing_in_background
}
fn should_reparse(&self) -> bool {
if let Some(syntax_tree) = self.syntax_tree.lock().as_ref() {
syntax_tree.dirty || syntax_tree.version != self.version
} else {
self.language.is_some()
}
}
fn reparse(&mut self, cx: &mut ModelContext<Self>) {
// Avoid spawning a new parsing task if the buffer is already being reparsed
// due to an earlier edit.
if self.is_parsing {
return;
fn reparse(&mut self, cx: &mut ModelContext<Self>) -> bool {
if self.parsing_in_background {
return false;
}
if let Some(language) = self.language.clone() {
self.is_parsing = true;
cx.spawn(|handle, mut cx| async move {
loop {
// The parse tree is out of date, so grab the syntax tree to synchronously
// splice all the edits that have happened since the last parse.
let new_tree = handle.update(&mut cx, |this, _| this.syntax_tree());
let (new_text, new_version) = handle
.read_with(&cx, |this, _| (this.visible_text.clone(), this.version()));
// The parse tree is out of date, so grab the syntax tree to synchronously
// splice all the edits that have happened since the last parse.
let old_tree = self.syntax_tree();
let parsed_text = self.visible_text.clone();
let parsed_version = self.version();
let parse_task = cx.background().spawn({
let language = language.clone();
async move { Self::parse_text(&parsed_text, old_tree, &language) }
});
// Parse the current text in a background thread.
let new_tree = cx
.background()
.spawn({
let language = language.clone();
async move { Self::parse_text(&new_text, new_tree, &language) }
})
.await;
let parse_again = handle.update(&mut cx, |this, cx| {
*this.syntax_tree.lock() = Some(SyntaxTree {
tree: new_tree,
dirty: false,
version: new_version,
});
this.parse_count += 1;
cx.emit(Event::Reparsed);
cx.notify();
if this.should_reparse() {
true
} else {
this.is_parsing = false;
false
}
match cx
.background()
.block_with_timeout(Duration::from_millis(1), parse_task)
{
Ok(new_tree) => {
*self.syntax_tree.lock() = Some(SyntaxTree {
tree: new_tree,
dirty: false,
version: parsed_version,
});
if !parse_again {
break;
}
self.parse_count += 1;
cx.emit(Event::Reparsed);
cx.notify();
return true;
}
})
.detach();
Err(parse_task) => {
self.parsing_in_background = true;
cx.spawn(move |this, mut cx| async move {
let new_tree = parse_task.await;
this.update(&mut cx, move |this, cx| {
let parse_again = this.version > parsed_version;
*this.syntax_tree.lock() = Some(SyntaxTree {
tree: new_tree,
dirty: false,
version: parsed_version,
});
this.parse_count += 1;
this.parsing_in_background = false;
if parse_again && this.reparse(cx) {
return;
}
cx.emit(Event::Reparsed);
cx.notify();
});
})
.detach();
}
}
}
false
}
fn parse_text(text: &Rope, old_tree: Option<Tree>, language: &Language) -> Tree {
@@ -1931,7 +1930,7 @@ impl Clone for Buffer {
file: self.file.clone(),
language: self.language.clone(),
syntax_tree: Mutex::new(self.syntax_tree.lock().clone()),
is_parsing: false,
parsing_in_background: false,
parse_count: self.parse_count,
deferred_replicas: self.deferred_replicas.clone(),
replica_id: self.replica_id,