Update FoldMap snapshot versions when only the parse tree changes

even if the buffer's text has not changed

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld 2021-09-06 11:57:55 -07:00
parent 94959d18c4
commit 11b8577d1b
2 changed files with 30 additions and 13 deletions

View file

@ -121,6 +121,7 @@ pub struct Buffer {
language: Option<Arc<Language>>, language: Option<Arc<Language>>,
syntax_tree: Mutex<Option<SyntaxTree>>, syntax_tree: Mutex<Option<SyntaxTree>>,
is_parsing: bool, is_parsing: bool,
parse_count: usize,
selections: HashMap<SelectionSetId, SelectionSet>, selections: HashMap<SelectionSetId, SelectionSet>,
deferred_ops: OperationQueue, deferred_ops: OperationQueue,
deferred_replicas: HashSet<ReplicaId>, deferred_replicas: HashSet<ReplicaId>,
@ -141,7 +142,7 @@ pub struct SelectionSet {
#[derive(Clone)] #[derive(Clone)]
struct SyntaxTree { struct SyntaxTree {
tree: Tree, tree: Tree,
parsed: bool, dirty: bool,
version: time::Global, version: time::Global,
} }
@ -581,6 +582,7 @@ impl Buffer {
file, file,
syntax_tree: Mutex::new(None), syntax_tree: Mutex::new(None),
is_parsing: false, is_parsing: false,
parse_count: 0,
language, language,
saved_mtime, saved_mtime,
selections: HashMap::default(), selections: HashMap::default(),
@ -790,9 +792,12 @@ impl Buffer {
cx.emit(Event::FileHandleChanged); cx.emit(Event::FileHandleChanged);
} }
pub fn parse_count(&self) -> usize {
self.parse_count
}
pub fn syntax_tree(&self) -> Option<Tree> { pub fn syntax_tree(&self) -> Option<Tree> {
if let Some(syntax_tree) = self.syntax_tree.lock().as_mut() { if let Some(syntax_tree) = self.syntax_tree.lock().as_mut() {
let mut edited = false;
let mut delta = 0_isize; let mut delta = 0_isize;
for edit in self.edits_since(syntax_tree.version.clone()) { for edit in self.edits_since(syntax_tree.version.clone()) {
let start_offset = (edit.old_bytes.start as isize + delta) as usize; let start_offset = (edit.old_bytes.start as isize + delta) as usize;
@ -809,9 +814,8 @@ impl Buffer {
.into(), .into(),
}); });
delta += edit.inserted_bytes() as isize - edit.deleted_bytes() as isize; delta += edit.inserted_bytes() as isize - edit.deleted_bytes() as isize;
edited = true; syntax_tree.dirty = true;
} }
syntax_tree.parsed &= !edited;
syntax_tree.version = self.version(); syntax_tree.version = self.version();
Some(syntax_tree.tree.clone()) Some(syntax_tree.tree.clone())
} else { } else {
@ -819,13 +823,14 @@ impl Buffer {
} }
} }
#[cfg(test)]
pub fn is_parsing(&self) -> bool { pub fn is_parsing(&self) -> bool {
self.is_parsing self.is_parsing
} }
fn should_reparse(&self) -> bool { fn should_reparse(&self) -> bool {
if let Some(syntax_tree) = self.syntax_tree.lock().as_ref() { if let Some(syntax_tree) = self.syntax_tree.lock().as_ref() {
!syntax_tree.parsed || syntax_tree.version != self.version syntax_tree.dirty || syntax_tree.version != self.version
} else { } else {
self.language.is_some() self.language.is_some()
} }
@ -841,7 +846,7 @@ impl Buffer {
if let Some(language) = self.language.clone() { if let Some(language) = self.language.clone() {
self.is_parsing = true; self.is_parsing = true;
cx.spawn(|handle, mut cx| async move { cx.spawn(|handle, mut cx| async move {
while handle.read_with(&cx, |this, _| this.should_reparse()) { loop {
// The parse tree is out of date, so grab the syntax tree to synchronously // 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. // splice all the edits that have happened since the last parse.
let new_tree = handle.update(&mut cx, |this, _| this.syntax_tree()); let new_tree = handle.update(&mut cx, |this, _| this.syntax_tree());
@ -857,17 +862,28 @@ impl Buffer {
}) })
.await; .await;
handle.update(&mut cx, |this, cx| { let parse_again = handle.update(&mut cx, |this, cx| {
*this.syntax_tree.lock() = Some(SyntaxTree { *this.syntax_tree.lock() = Some(SyntaxTree {
tree: new_tree, tree: new_tree,
parsed: true, dirty: false,
version: new_version, version: new_version,
}); });
this.parse_count += 1;
cx.emit(Event::Reparsed); cx.emit(Event::Reparsed);
cx.notify(); cx.notify();
if this.should_reparse() {
true
} else {
this.is_parsing = false;
false
}
}); });
if !parse_again {
break;
}
} }
handle.update(&mut cx, |this, _| this.is_parsing = false);
}) })
.detach(); .detach();
} }
@ -1916,6 +1932,7 @@ impl Clone for Buffer {
language: self.language.clone(), language: self.language.clone(),
syntax_tree: Mutex::new(self.syntax_tree.lock().clone()), syntax_tree: Mutex::new(self.syntax_tree.lock().clone()),
is_parsing: false, is_parsing: false,
parse_count: self.parse_count,
deferred_replicas: self.deferred_replicas.clone(), deferred_replicas: self.deferred_replicas.clone(),
replica_id: self.replica_id, replica_id: self.replica_id,
remote_id: self.remote_id.clone(), remote_id: self.remote_id.clone(),

View file

@ -201,7 +201,7 @@ pub struct FoldMap {
#[derive(Clone)] #[derive(Clone)]
struct SyncState { struct SyncState {
version: time::Global, version: time::Global,
is_parsing: bool, parse_count: usize,
} }
impl FoldMap { impl FoldMap {
@ -222,7 +222,7 @@ impl FoldMap {
)), )),
last_sync: Mutex::new(SyncState { last_sync: Mutex::new(SyncState {
version: buffer.version(), version: buffer.version(),
is_parsing: buffer.is_parsing(), parse_count: buffer.parse_count(),
}), }),
version: AtomicUsize::new(0), version: AtomicUsize::new(0),
}; };
@ -253,7 +253,7 @@ impl FoldMap {
&mut *self.last_sync.lock(), &mut *self.last_sync.lock(),
SyncState { SyncState {
version: buffer.version(), version: buffer.version(),
is_parsing: buffer.is_parsing(), parse_count: buffer.parse_count(),
}, },
); );
let edits = buffer let edits = buffer
@ -261,7 +261,7 @@ impl FoldMap {
.map(Into::into) .map(Into::into)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if edits.is_empty() { if edits.is_empty() {
if last_sync.is_parsing != buffer.is_parsing() { if last_sync.parse_count != buffer.parse_count() {
self.version.fetch_add(1, SeqCst); self.version.fetch_add(1, SeqCst);
} }
Vec::new() Vec::new()