Speed up is_dirty and has_conflict (#11946)
I noticed that scrolling the assistant panel was very slow in debug mode, after running a completion. From profiling, I saw that it was due to the buffer's `is_dirty` and `has_conflict` checks, which use `edits_since` to check if there are any non-undone edits since the saved version. I optimized this in two ways: * I introduced a specialized `has_edits_since` method on text buffers, which allows us to more cheaply check if the buffer has been edited since a given version, without some of the overhead involved in computing what the edits actually are. * In the case of `has_conflict`, we don't even need to call that method in the case where the buffer doesn't have a file (is untitled, as is the case in the assistant panel). Buffers without files cannot be in conflict. Release Notes: - Improved performance of editing the assistant panel and untitled buffers with many edits.
This commit is contained in:
parent
23315d214c
commit
df3bd40c56
3 changed files with 32 additions and 11 deletions
|
@ -1527,14 +1527,10 @@ impl Buffer {
|
||||||
self.end_transaction(cx)
|
self.end_transaction(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn changed_since_saved_version(&self) -> bool {
|
|
||||||
self.edits_since::<usize>(&self.saved_version)
|
|
||||||
.next()
|
|
||||||
.is_some()
|
|
||||||
}
|
|
||||||
/// Checks if the buffer has unsaved changes.
|
/// Checks if the buffer has unsaved changes.
|
||||||
pub fn is_dirty(&self) -> bool {
|
pub fn is_dirty(&self) -> bool {
|
||||||
(self.has_conflict || self.changed_since_saved_version())
|
self.has_conflict
|
||||||
|
|| self.has_edits_since(&self.saved_version)
|
||||||
|| self
|
|| self
|
||||||
.file
|
.file
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -1544,11 +1540,10 @@ impl Buffer {
|
||||||
/// Checks if the buffer and its file have both changed since the buffer
|
/// Checks if the buffer and its file have both changed since the buffer
|
||||||
/// was last saved or reloaded.
|
/// was last saved or reloaded.
|
||||||
pub fn has_conflict(&self) -> bool {
|
pub fn has_conflict(&self) -> bool {
|
||||||
(self.has_conflict || self.changed_since_saved_version())
|
self.has_conflict
|
||||||
&& self
|
|| self.file.as_ref().map_or(false, |file| {
|
||||||
.file
|
file.mtime() > self.saved_mtime && self.has_edits_since(&self.saved_version)
|
||||||
.as_ref()
|
})
|
||||||
.map_or(false, |file| file.mtime() > self.saved_mtime)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a [`Subscription`] that tracks all of the changes to the buffer's text.
|
/// Gets a [`Subscription`] that tracks all of the changes to the buffer's text.
|
||||||
|
|
|
@ -139,6 +139,14 @@ fn test_random_edits(mut rng: StdRng) {
|
||||||
assert_eq!(old_text, new_text);
|
assert_eq!(old_text, new_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
buffer.has_edits_since(&old_buffer.version),
|
||||||
|
buffer
|
||||||
|
.edits_since::<usize>(&old_buffer.version)
|
||||||
|
.next()
|
||||||
|
.is_some(),
|
||||||
|
);
|
||||||
|
|
||||||
let subscription_edits = subscription.consume();
|
let subscription_edits = subscription.consume();
|
||||||
log::info!(
|
log::info!(
|
||||||
"applying subscription edits since version {:?} to old text: {:?}: {:?}",
|
"applying subscription edits since version {:?} to old text: {:?}: {:?}",
|
||||||
|
|
|
@ -2165,6 +2165,24 @@ impl BufferSnapshot {
|
||||||
buffer_id: self.remote_id,
|
buffer_id: self.remote_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_edits_since(&self, since: &clock::Global) -> bool {
|
||||||
|
if *since != self.version {
|
||||||
|
let mut cursor = self
|
||||||
|
.fragments
|
||||||
|
.filter::<_, usize>(move |summary| !since.observed_all(&summary.max_version));
|
||||||
|
cursor.next(&None);
|
||||||
|
while let Some(fragment) = cursor.item() {
|
||||||
|
let was_visible = fragment.was_visible(since, &self.undo_map);
|
||||||
|
let is_visible = fragment.visible;
|
||||||
|
if was_visible != is_visible {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
cursor.next(&None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RopeBuilder<'a> {
|
struct RopeBuilder<'a> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue