language: Memoize value of has_edits_since for a given buffer version (#13656)

As a drive-by of https://github.com/zed-industries/zed/pull/13654, I've
noticed that the editor felt sluggish after I've undone the changes made
by the replacement. It turns out that we are repeatedly checking whether
there are any edits to estabilish dirty/conflict state of a buffer, even
though this operation is pure; this PR stores away the result of a
computation and refers to it before rerunning it.

Release Notes:

- Improve editor's performance with large undo histories
This commit is contained in:
Piotr Osiewicz 2024-06-28 20:23:59 +02:00 committed by GitHub
parent 0761383752
commit 218629cdd4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -31,6 +31,7 @@ use smallvec::SmallVec;
use smol::future::yield_now;
use std::{
any::Any,
cell::Cell,
cmp::{self, Ordering},
collections::BTreeMap,
ffi::OsStr,
@ -113,6 +114,9 @@ pub struct Buffer {
capability: Capability,
has_conflict: bool,
diff_base_version: usize,
/// Memoize calls to has_changes_since(saved_version).
/// The contents of a cell are (self.version, has_changes) at the time of a last call.
has_unsaved_edits: Cell<(clock::Global, bool)>,
}
/// An immutable, cheaply cloneable representation of a fixed
@ -690,6 +694,7 @@ impl Buffer {
reload_task: None,
transaction_depth: 0,
was_dirty_before_starting_transaction: None,
has_unsaved_edits: Cell::new((buffer.version(), false)),
text: buffer,
diff_base: diff_base
.map(|mut raw_diff_base| {
@ -799,6 +804,8 @@ impl Buffer {
cx: &mut ModelContext<Self>,
) {
self.saved_version = version;
self.has_unsaved_edits
.set((self.saved_version().clone(), false));
self.has_conflict = false;
self.saved_mtime = mtime;
cx.emit(Event::Saved);
@ -860,6 +867,8 @@ impl Buffer {
cx: &mut ModelContext<Self>,
) {
self.saved_version = version;
self.has_unsaved_edits
.set((self.saved_version.clone(), false));
self.text.set_line_ending(line_ending);
self.saved_mtime = mtime;
cx.emit(Event::Reloaded);
@ -1516,10 +1525,25 @@ impl Buffer {
self.end_transaction(cx)
}
fn has_unsaved_edits(&self) -> bool {
let (last_version, has_unsaved_edits) = self.has_unsaved_edits.take();
if last_version == self.version {
self.has_unsaved_edits
.set((last_version, has_unsaved_edits));
return has_unsaved_edits;
}
let has_edits = self.has_edits_since(&self.saved_version);
self.has_unsaved_edits
.set((self.version.clone(), has_edits));
has_edits
}
/// Checks if the buffer has unsaved changes.
pub fn is_dirty(&self) -> bool {
self.has_conflict
|| self.has_edits_since(&self.saved_version)
|| self.has_unsaved_edits()
|| self
.file
.as_ref()
@ -1531,7 +1555,7 @@ impl Buffer {
pub fn has_conflict(&self) -> bool {
self.has_conflict
|| self.file.as_ref().map_or(false, |file| {
file.mtime() > self.saved_mtime && self.has_edits_since(&self.saved_version)
file.mtime() > self.saved_mtime && self.has_unsaved_edits()
})
}