Combine multiple buffer update count fields into one (#13449)

Buffers carry several pieces of state besides their text: syntax tree,
diagnostics, git diff, and file data. Previously, the buffer maintained
a separate integer version number for each of these four pieces of
state, incrementing it every time that piece of state is updated. This
is used by MultiBuffers to detect when they need to update excerpts.

Previously, for a given buffer, these four version numbers were stored
on the buffer itself, on every snapshot of the buffer, in any
multi-buffer that referenced that buffer, **and** on snapshots of that
multi-buffer. But the only use for the version numbers was reduced down
to a single boolean predicate: whether or not the buffer's state has
changed.

In this PR, I've combined those 4 version numbers into one. I've called
it `non_text_state_update_count` because it tracks all state updates
outside of the text itself. This removes a bunch of unnecessary code,
and reduces the size of buffer snapshots and multi-buffer snapshots.

Release Notes:

- N/A
This commit is contained in:
Max Brunsfeld 2024-06-23 22:20:10 -07:00 committed by GitHub
parent 78bc3a9a36
commit 9813297892
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 37 additions and 150 deletions

View File

@ -462,11 +462,8 @@ impl InlayMap {
if buffer_edits.is_empty() { if buffer_edits.is_empty() {
if snapshot.buffer.edit_count() != buffer_snapshot.edit_count() if snapshot.buffer.edit_count() != buffer_snapshot.edit_count()
|| snapshot.buffer.parse_count() != buffer_snapshot.parse_count() || snapshot.buffer.non_text_state_update_count()
|| snapshot.buffer.diagnostics_update_count() != buffer_snapshot.non_text_state_update_count()
!= buffer_snapshot.diagnostics_update_count()
|| snapshot.buffer.git_diff_update_count()
!= buffer_snapshot.git_diff_update_count()
|| snapshot.buffer.trailing_excerpt_update_count() || snapshot.buffer.trailing_excerpt_update_count()
!= buffer_snapshot.trailing_excerpt_update_count() != buffer_snapshot.trailing_excerpt_update_count()
{ {

View File

@ -103,14 +103,10 @@ pub struct Buffer {
sync_parse_timeout: Duration, sync_parse_timeout: Duration,
syntax_map: Mutex<SyntaxMap>, syntax_map: Mutex<SyntaxMap>,
parsing_in_background: bool, parsing_in_background: bool,
parse_count: usize, non_text_state_update_count: usize,
diagnostics: SmallVec<[(LanguageServerId, DiagnosticSet); 2]>, diagnostics: SmallVec<[(LanguageServerId, DiagnosticSet); 2]>,
remote_selections: TreeMap<ReplicaId, SelectionSet>, remote_selections: TreeMap<ReplicaId, SelectionSet>,
selections_update_count: usize,
diagnostics_update_count: usize,
diagnostics_timestamp: clock::Lamport, diagnostics_timestamp: clock::Lamport,
file_update_count: usize,
git_diff_update_count: usize,
completion_triggers: Vec<String>, completion_triggers: Vec<String>,
completion_triggers_timestamp: clock::Lamport, completion_triggers_timestamp: clock::Lamport,
deferred_ops: OperationQueue<Operation>, deferred_ops: OperationQueue<Operation>,
@ -127,13 +123,9 @@ pub struct BufferSnapshot {
pub(crate) syntax: SyntaxSnapshot, pub(crate) syntax: SyntaxSnapshot,
file: Option<Arc<dyn File>>, file: Option<Arc<dyn File>>,
diagnostics: SmallVec<[(LanguageServerId, DiagnosticSet); 2]>, diagnostics: SmallVec<[(LanguageServerId, DiagnosticSet); 2]>,
diagnostics_update_count: usize,
file_update_count: usize,
git_diff_update_count: usize,
remote_selections: TreeMap<ReplicaId, SelectionSet>, remote_selections: TreeMap<ReplicaId, SelectionSet>,
selections_update_count: usize,
language: Option<Arc<Language>>, language: Option<Arc<Language>>,
parse_count: usize, non_text_state_update_count: usize,
} }
/// The kind and amount of indentation in a particular line. For now, /// The kind and amount of indentation in a particular line. For now,
@ -711,18 +703,14 @@ impl Buffer {
capability, capability,
syntax_map: Mutex::new(SyntaxMap::new()), syntax_map: Mutex::new(SyntaxMap::new()),
parsing_in_background: false, parsing_in_background: false,
parse_count: 0, non_text_state_update_count: 0,
sync_parse_timeout: Duration::from_millis(1), sync_parse_timeout: Duration::from_millis(1),
autoindent_requests: Default::default(), autoindent_requests: Default::default(),
pending_autoindent: Default::default(), pending_autoindent: Default::default(),
language: None, language: None,
remote_selections: Default::default(), remote_selections: Default::default(),
selections_update_count: 0,
diagnostics: Default::default(), diagnostics: Default::default(),
diagnostics_update_count: 0,
diagnostics_timestamp: Default::default(), diagnostics_timestamp: Default::default(),
file_update_count: 0,
git_diff_update_count: 0,
completion_triggers: Default::default(), completion_triggers: Default::default(),
completion_triggers_timestamp: Default::default(), completion_triggers_timestamp: Default::default(),
deferred_ops: OperationQueue::new(), deferred_ops: OperationQueue::new(),
@ -745,12 +733,8 @@ impl Buffer {
file: self.file.clone(), file: self.file.clone(),
remote_selections: self.remote_selections.clone(), remote_selections: self.remote_selections.clone(),
diagnostics: self.diagnostics.clone(), diagnostics: self.diagnostics.clone(),
diagnostics_update_count: self.diagnostics_update_count,
file_update_count: self.file_update_count,
git_diff_update_count: self.git_diff_update_count,
language: self.language.clone(), language: self.language.clone(),
parse_count: self.parse_count, non_text_state_update_count: self.non_text_state_update_count,
selections_update_count: self.selections_update_count,
} }
} }
@ -782,7 +766,7 @@ impl Buffer {
/// Assign a language to the buffer. /// Assign a language to the buffer.
pub fn set_language(&mut self, language: Option<Arc<Language>>, cx: &mut ModelContext<Self>) { pub fn set_language(&mut self, language: Option<Arc<Language>>, cx: &mut ModelContext<Self>) {
self.parse_count += 1; self.non_text_state_update_count += 1;
self.syntax_map.lock().clear(); self.syntax_map.lock().clear();
self.language = language; self.language = language;
self.reparse(cx); self.reparse(cx);
@ -915,7 +899,7 @@ impl Buffer {
self.file = Some(new_file); self.file = Some(new_file);
if file_changed { if file_changed {
self.file_update_count += 1; self.non_text_state_update_count += 1;
cx.emit(Event::FileHandleChanged); cx.emit(Event::FileHandleChanged);
cx.notify(); cx.notify();
} }
@ -969,7 +953,7 @@ impl Buffer {
let buffer_diff = diff.await; let buffer_diff = diff.await;
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
this.git_diff = buffer_diff; this.git_diff = buffer_diff;
this.git_diff_update_count += 1; this.non_text_state_update_count += 1;
cx.emit(Event::DiffUpdated); cx.emit(Event::DiffUpdated);
}) })
.ok(); .ok();
@ -992,29 +976,10 @@ impl Buffer {
.or_else(|| self.language.clone()) .or_else(|| self.language.clone())
} }
/// The number of times the buffer was parsed. /// An integer version number that accounts for all updates besides
pub fn parse_count(&self) -> usize { /// the buffer's text itself (which is versioned via a version vector).
self.parse_count pub fn non_text_state_update_count(&self) -> usize {
} self.non_text_state_update_count
/// The number of times selections were updated.
pub fn selections_update_count(&self) -> usize {
self.selections_update_count
}
/// The number of times diagnostics were updated.
pub fn diagnostics_update_count(&self) -> usize {
self.diagnostics_update_count
}
/// The number of times the underlying file was updated.
pub fn file_update_count(&self) -> usize {
self.file_update_count
}
/// The number of times the git diff status was updated.
pub fn git_diff_update_count(&self) -> usize {
self.git_diff_update_count
} }
/// Whether the buffer is being parsed in the background. /// Whether the buffer is being parsed in the background.
@ -1124,7 +1089,7 @@ impl Buffer {
} }
fn did_finish_parsing(&mut self, syntax_snapshot: SyntaxSnapshot, cx: &mut ModelContext<Self>) { fn did_finish_parsing(&mut self, syntax_snapshot: SyntaxSnapshot, cx: &mut ModelContext<Self>) {
self.parse_count += 1; self.non_text_state_update_count += 1;
self.syntax_map.lock().did_parse(syntax_snapshot); self.syntax_map.lock().did_parse(syntax_snapshot);
self.request_autoindent(cx); self.request_autoindent(cx);
cx.emit(Event::Reparsed); cx.emit(Event::Reparsed);
@ -1701,7 +1666,7 @@ impl Buffer {
}, },
cx, cx,
); );
self.selections_update_count += 1; self.non_text_state_update_count += 1;
cx.notify(); cx.notify();
} }
@ -1973,7 +1938,7 @@ impl Buffer {
}, },
); );
self.text.lamport_clock.observe(lamport_timestamp); self.text.lamport_clock.observe(lamport_timestamp);
self.selections_update_count += 1; self.non_text_state_update_count += 1;
} }
Operation::UpdateCompletionTriggers { Operation::UpdateCompletionTriggers {
triggers, triggers,
@ -2005,7 +1970,7 @@ impl Buffer {
}; };
} }
self.diagnostics_timestamp = lamport_timestamp; self.diagnostics_timestamp = lamport_timestamp;
self.diagnostics_update_count += 1; self.non_text_state_update_count += 1;
self.text.lamport_clock.observe(lamport_timestamp); self.text.lamport_clock.observe(lamport_timestamp);
cx.notify(); cx.notify();
cx.emit(Event::DiagnosticsUpdated); cx.emit(Event::DiagnosticsUpdated);
@ -3523,19 +3488,10 @@ impl BufferSnapshot {
.flat_map(move |(_, set)| set.group(group_id, self)) .flat_map(move |(_, set)| set.group(group_id, self))
} }
/// The number of times diagnostics were updated. /// An integer version number that accounts for all updates besides
pub fn diagnostics_update_count(&self) -> usize { /// the buffer's text itself (which is versioned via a version vector).
self.diagnostics_update_count pub fn non_text_state_update_count(&self) -> usize {
} self.non_text_state_update_count
/// The number of times the buffer was parsed.
pub fn parse_count(&self) -> usize {
self.parse_count
}
/// The number of times selections were updated.
pub fn selections_update_count(&self) -> usize {
self.selections_update_count
} }
/// Returns a snapshot of underlying file. /// Returns a snapshot of underlying file.
@ -3555,16 +3511,6 @@ impl BufferSnapshot {
None None
} }
} }
/// The number of times the underlying file was updated.
pub fn file_update_count(&self) -> usize {
self.file_update_count
}
/// The number of times the git diff status was updated.
pub fn git_diff_update_count(&self) -> usize {
self.git_diff_update_count
}
} }
fn indent_size_for_line(text: &text::BufferSnapshot, row: u32) -> IndentSize { fn indent_size_for_line(text: &text::BufferSnapshot, row: u32) -> IndentSize {
@ -3596,12 +3542,8 @@ impl Clone for BufferSnapshot {
file: self.file.clone(), file: self.file.clone(),
remote_selections: self.remote_selections.clone(), remote_selections: self.remote_selections.clone(),
diagnostics: self.diagnostics.clone(), diagnostics: self.diagnostics.clone(),
selections_update_count: self.selections_update_count,
diagnostics_update_count: self.diagnostics_update_count,
file_update_count: self.file_update_count,
git_diff_update_count: self.git_diff_update_count,
language: self.language.clone(), language: self.language.clone(),
parse_count: self.parse_count, non_text_state_update_count: self.non_text_state_update_count,
} }
} }
} }

View File

@ -152,11 +152,7 @@ pub trait ToPointUtf16: 'static + fmt::Debug {
struct BufferState { struct BufferState {
buffer: Model<Buffer>, buffer: Model<Buffer>,
last_version: clock::Global, last_version: clock::Global,
last_parse_count: usize, last_non_text_state_update_count: usize,
last_selections_update_count: usize,
last_diagnostics_update_count: usize,
last_file_update_count: usize,
last_git_diff_update_count: usize,
excerpts: Vec<Locator>, excerpts: Vec<Locator>,
_subscriptions: [gpui::Subscription; 2], _subscriptions: [gpui::Subscription; 2],
} }
@ -167,10 +163,8 @@ pub struct MultiBufferSnapshot {
singleton: bool, singleton: bool,
excerpts: SumTree<Excerpt>, excerpts: SumTree<Excerpt>,
excerpt_ids: SumTree<ExcerptIdMapping>, excerpt_ids: SumTree<ExcerptIdMapping>,
parse_count: usize,
diagnostics_update_count: usize,
trailing_excerpt_update_count: usize, trailing_excerpt_update_count: usize,
git_diff_update_count: usize, non_text_state_update_count: usize,
edit_count: usize, edit_count: usize,
is_dirty: bool, is_dirty: bool,
has_conflict: bool, has_conflict: bool,
@ -396,11 +390,7 @@ impl MultiBuffer {
BufferState { BufferState {
buffer: buffer_state.buffer.clone(), buffer: buffer_state.buffer.clone(),
last_version: buffer_state.last_version.clone(), last_version: buffer_state.last_version.clone(),
last_parse_count: buffer_state.last_parse_count, last_non_text_state_update_count: buffer_state.last_non_text_state_update_count,
last_selections_update_count: buffer_state.last_selections_update_count,
last_diagnostics_update_count: buffer_state.last_diagnostics_update_count,
last_file_update_count: buffer_state.last_file_update_count,
last_git_diff_update_count: buffer_state.last_git_diff_update_count,
excerpts: buffer_state.excerpts.clone(), excerpts: buffer_state.excerpts.clone(),
_subscriptions: [ _subscriptions: [
new_cx.observe(&buffer_state.buffer, |_, _, cx| cx.notify()), new_cx.observe(&buffer_state.buffer, |_, _, cx| cx.notify()),
@ -1244,11 +1234,7 @@ impl MultiBuffer {
let mut buffers = self.buffers.borrow_mut(); let mut buffers = self.buffers.borrow_mut();
let buffer_state = buffers.entry(buffer_id).or_insert_with(|| BufferState { let buffer_state = buffers.entry(buffer_id).or_insert_with(|| BufferState {
last_version: buffer_snapshot.version().clone(), last_version: buffer_snapshot.version().clone(),
last_parse_count: buffer_snapshot.parse_count(), last_non_text_state_update_count: buffer_snapshot.non_text_state_update_count(),
last_selections_update_count: buffer_snapshot.selections_update_count(),
last_diagnostics_update_count: buffer_snapshot.diagnostics_update_count(),
last_file_update_count: buffer_snapshot.file_update_count(),
last_git_diff_update_count: buffer_snapshot.git_diff_update_count(),
excerpts: Default::default(), excerpts: Default::default(),
_subscriptions: [ _subscriptions: [
cx.observe(&buffer, |_, _, cx| cx.notify()), cx.observe(&buffer, |_, _, cx| cx.notify()),
@ -1823,9 +1809,7 @@ impl MultiBuffer {
fn sync(&self, cx: &AppContext) { fn sync(&self, cx: &AppContext) {
let mut snapshot = self.snapshot.borrow_mut(); let mut snapshot = self.snapshot.borrow_mut();
let mut excerpts_to_edit = Vec::new(); let mut excerpts_to_edit = Vec::new();
let mut reparsed = false; let mut non_text_state_updated = false;
let mut diagnostics_updated = false;
let mut git_diff_updated = false;
let mut is_dirty = false; let mut is_dirty = false;
let mut has_conflict = false; let mut has_conflict = false;
let mut edited = false; let mut edited = false;
@ -1833,34 +1817,14 @@ impl MultiBuffer {
for buffer_state in buffers.values_mut() { for buffer_state in buffers.values_mut() {
let buffer = buffer_state.buffer.read(cx); let buffer = buffer_state.buffer.read(cx);
let version = buffer.version(); let version = buffer.version();
let parse_count = buffer.parse_count(); let non_text_state_update_count = buffer.non_text_state_update_count();
let selections_update_count = buffer.selections_update_count();
let diagnostics_update_count = buffer.diagnostics_update_count();
let file_update_count = buffer.file_update_count();
let git_diff_update_count = buffer.git_diff_update_count();
let buffer_edited = version.changed_since(&buffer_state.last_version); let buffer_edited = version.changed_since(&buffer_state.last_version);
let buffer_reparsed = parse_count > buffer_state.last_parse_count; let buffer_non_text_state_updated =
let buffer_selections_updated = non_text_state_update_count > buffer_state.last_non_text_state_update_count;
selections_update_count > buffer_state.last_selections_update_count; if buffer_edited || buffer_non_text_state_updated {
let buffer_diagnostics_updated =
diagnostics_update_count > buffer_state.last_diagnostics_update_count;
let buffer_file_updated = file_update_count > buffer_state.last_file_update_count;
let buffer_git_diff_updated =
git_diff_update_count > buffer_state.last_git_diff_update_count;
if buffer_edited
|| buffer_reparsed
|| buffer_selections_updated
|| buffer_diagnostics_updated
|| buffer_file_updated
|| buffer_git_diff_updated
{
buffer_state.last_version = version; buffer_state.last_version = version;
buffer_state.last_parse_count = parse_count; buffer_state.last_non_text_state_update_count = non_text_state_update_count;
buffer_state.last_selections_update_count = selections_update_count;
buffer_state.last_diagnostics_update_count = diagnostics_update_count;
buffer_state.last_file_update_count = file_update_count;
buffer_state.last_git_diff_update_count = git_diff_update_count;
excerpts_to_edit.extend( excerpts_to_edit.extend(
buffer_state buffer_state
.excerpts .excerpts
@ -1870,23 +1834,15 @@ impl MultiBuffer {
} }
edited |= buffer_edited; edited |= buffer_edited;
reparsed |= buffer_reparsed; non_text_state_updated |= buffer_non_text_state_updated;
diagnostics_updated |= buffer_diagnostics_updated;
git_diff_updated |= buffer_git_diff_updated;
is_dirty |= buffer.is_dirty(); is_dirty |= buffer.is_dirty();
has_conflict |= buffer.has_conflict(); has_conflict |= buffer.has_conflict();
} }
if edited { if edited {
snapshot.edit_count += 1; snapshot.edit_count += 1;
} }
if reparsed { if non_text_state_updated {
snapshot.parse_count += 1; snapshot.non_text_state_update_count += 1;
}
if diagnostics_updated {
snapshot.diagnostics_update_count += 1;
}
if git_diff_updated {
snapshot.git_diff_update_count += 1;
} }
snapshot.is_dirty = is_dirty; snapshot.is_dirty = is_dirty;
snapshot.has_conflict = has_conflict; snapshot.has_conflict = has_conflict;
@ -3198,8 +3154,8 @@ impl MultiBufferSnapshot {
self.edit_count self.edit_count
} }
pub fn parse_count(&self) -> usize { pub fn non_text_state_update_count(&self) -> usize {
self.parse_count self.non_text_state_update_count
} }
/// Returns the smallest enclosing bracket ranges containing the given range or /// Returns the smallest enclosing bracket ranges containing the given range or
@ -3412,14 +3368,6 @@ impl MultiBufferSnapshot {
.collect() .collect()
} }
pub fn diagnostics_update_count(&self) -> usize {
self.diagnostics_update_count
}
pub fn git_diff_update_count(&self) -> usize {
self.git_diff_update_count
}
pub fn trailing_excerpt_update_count(&self) -> usize { pub fn trailing_excerpt_update_count(&self) -> usize {
self.trailing_excerpt_update_count self.trailing_excerpt_update_count
} }