Update git gutter status after debounced delay

Julia and Max Brunsfeld created

Co-authored-by: Max Brunsfeld <max@zed.com>

Change summary

crates/editor/src/display_map/fold_map.rs |  1 
crates/editor/src/items.rs                |  7 +++-
crates/editor/src/multi_buffer.rs         | 26 +++++++++++++++++++
crates/language/src/buffer.rs             | 33 ++++++++++++++++++++----
crates/workspace/src/workspace.rs         |  2 
5 files changed, 60 insertions(+), 9 deletions(-)

Detailed changes

crates/editor/src/display_map/fold_map.rs 🔗

@@ -274,6 +274,7 @@ impl FoldMap {
             if buffer.edit_count() != new_buffer.edit_count()
                 || buffer.parse_count() != new_buffer.parse_count()
                 || buffer.diagnostics_update_count() != new_buffer.diagnostics_update_count()
+                || buffer.diff_update_count() != new_buffer.diff_update_count()
                 || buffer.trailing_excerpt_update_count()
                     != new_buffer.trailing_excerpt_update_count()
             {

crates/editor/src/items.rs 🔗

@@ -481,9 +481,12 @@ impl Item for Editor {
     fn update_git(
         &mut self,
         _project: ModelHandle<Project>,
-        _cx: &mut ViewContext<Self>,
+        cx: &mut ViewContext<Self>,
     ) -> Task<Result<()>> {
-        println!("Editor::update_git");
+        self.buffer().update(cx, |multibuffer, cx| {
+            multibuffer.update_git(cx);
+        });
+        cx.notify();
         Task::ready(Ok(()))
     }
 

crates/editor/src/multi_buffer.rs 🔗

@@ -91,6 +91,7 @@ struct BufferState {
     last_selections_update_count: usize,
     last_diagnostics_update_count: usize,
     last_file_update_count: usize,
+    last_diff_update_count: usize,
     excerpts: Vec<ExcerptId>,
     _subscriptions: [gpui::Subscription; 2],
 }
@@ -102,6 +103,7 @@ pub struct MultiBufferSnapshot {
     parse_count: usize,
     diagnostics_update_count: usize,
     trailing_excerpt_update_count: usize,
+    diff_update_count: usize,
     edit_count: usize,
     is_dirty: bool,
     has_conflict: bool,
@@ -203,6 +205,7 @@ impl MultiBuffer {
                     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_diff_update_count: buffer_state.last_diff_update_count,
                     excerpts: buffer_state.excerpts.clone(),
                     _subscriptions: [
                         new_cx.observe(&buffer_state.buffer, |_, _, cx| cx.notify()),
@@ -309,6 +312,15 @@ impl MultiBuffer {
         self.read(cx).symbols_containing(offset, theme)
     }
 
+    pub fn update_git(&mut self, cx: &mut ModelContext<Self>) {
+        let mut buffers = self.buffers.borrow_mut();
+        for buffer in buffers.values_mut() {
+            buffer.buffer.update(cx, |buffer, _| {
+                buffer.update_git();
+            })
+        }
+    }
+
     pub fn edit<I, S, T>(
         &mut self,
         edits: I,
@@ -828,6 +840,7 @@ impl MultiBuffer {
             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_diff_update_count: buffer_snapshot.diff_update_count(),
             excerpts: Default::default(),
             _subscriptions: [
                 cx.observe(&buffer, |_, _, cx| cx.notify()),
@@ -1250,6 +1263,7 @@ impl MultiBuffer {
         let mut excerpts_to_edit = Vec::new();
         let mut reparsed = false;
         let mut diagnostics_updated = false;
+        let mut diff_updated = false;
         let mut is_dirty = false;
         let mut has_conflict = false;
         let mut edited = false;
@@ -1261,6 +1275,7 @@ impl MultiBuffer {
             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 diff_update_count = buffer.diff_update_count();
 
             let buffer_edited = version.changed_since(&buffer_state.last_version);
             let buffer_reparsed = parse_count > buffer_state.last_parse_count;
@@ -1269,17 +1284,20 @@ impl MultiBuffer {
             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_diff_updated = diff_update_count > buffer_state.last_diff_update_count;
             if buffer_edited
                 || buffer_reparsed
                 || buffer_selections_updated
                 || buffer_diagnostics_updated
                 || buffer_file_updated
+                || buffer_diff_updated
             {
                 buffer_state.last_version = version;
                 buffer_state.last_parse_count = parse_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_diff_update_count = diff_update_count;
                 excerpts_to_edit.extend(
                     buffer_state
                         .excerpts
@@ -1291,6 +1309,7 @@ impl MultiBuffer {
             edited |= buffer_edited;
             reparsed |= buffer_reparsed;
             diagnostics_updated |= buffer_diagnostics_updated;
+            diff_updated |= buffer_diff_updated;
             is_dirty |= buffer.is_dirty();
             has_conflict |= buffer.has_conflict();
         }
@@ -1303,6 +1322,9 @@ impl MultiBuffer {
         if diagnostics_updated {
             snapshot.diagnostics_update_count += 1;
         }
+        if diff_updated {
+            snapshot.diff_update_count += 1;
+        }
         snapshot.is_dirty = is_dirty;
         snapshot.has_conflict = has_conflict;
 
@@ -2480,6 +2502,10 @@ impl MultiBufferSnapshot {
         self.diagnostics_update_count
     }
 
+    pub fn diff_update_count(&self) -> usize {
+        self.diff_update_count
+    }
+
     pub fn trailing_excerpt_update_count(&self) -> usize {
         self.trailing_excerpt_update_count
     }

crates/language/src/buffer.rs 🔗

@@ -49,7 +49,7 @@ pub use lsp::DiagnosticSeverity;
 pub struct Buffer {
     text: TextBuffer,
     head_text: Option<String>,
-    diff: BufferDiff,
+    git_diff: BufferDiff,
     file: Option<Arc<dyn File>>,
     saved_version: clock::Global,
     saved_version_fingerprint: String,
@@ -69,6 +69,7 @@ pub struct Buffer {
     diagnostics_update_count: usize,
     diagnostics_timestamp: clock::Lamport,
     file_update_count: usize,
+    diff_update_count: usize,
     completion_triggers: Vec<String>,
     completion_triggers_timestamp: clock::Lamport,
     deferred_ops: OperationQueue<Operation>,
@@ -76,12 +77,13 @@ pub struct Buffer {
 
 pub struct BufferSnapshot {
     text: text::BufferSnapshot,
-    git_hunks: Arc<[DiffHunk<Anchor>]>,
+    pub git_hunks: Arc<[DiffHunk<Anchor>]>,
     pub(crate) syntax: SyntaxSnapshot,
     file: Option<Arc<dyn File>>,
     diagnostics: DiagnosticSet,
     diagnostics_update_count: usize,
     file_update_count: usize,
+    diff_update_count: usize,
     remote_selections: TreeMap<ReplicaId, SelectionSet>,
     selections_update_count: usize,
     language: Option<Arc<Language>>,
@@ -419,9 +421,9 @@ impl Buffer {
             UNIX_EPOCH
         };
 
-        let mut diff = BufferDiff::new();
+        let mut git_diff = BufferDiff::new();
         if let Some(head_text) = &head_text {
-            diff.update(head_text, &buffer);
+            git_diff.update(head_text, &buffer);
         }
 
         Self {
@@ -432,7 +434,7 @@ impl Buffer {
             was_dirty_before_starting_transaction: None,
             text: buffer,
             head_text,
-            diff,
+            git_diff,
             file,
             syntax_map: Mutex::new(SyntaxMap::new()),
             parsing_in_background: false,
@@ -447,6 +449,7 @@ impl Buffer {
             diagnostics_update_count: 0,
             diagnostics_timestamp: Default::default(),
             file_update_count: 0,
+            diff_update_count: 0,
             completion_triggers: Default::default(),
             completion_triggers_timestamp: Default::default(),
             deferred_ops: OperationQueue::new(),
@@ -462,12 +465,13 @@ impl Buffer {
         BufferSnapshot {
             text,
             syntax,
-            git_hunks: self.diff.hunks(),
+            git_hunks: self.git_diff.hunks(),
             file: self.file.clone(),
             remote_selections: self.remote_selections.clone(),
             diagnostics: self.diagnostics.clone(),
             diagnostics_update_count: self.diagnostics_update_count,
             file_update_count: self.file_update_count,
+            diff_update_count: self.diff_update_count,
             language: self.language.clone(),
             parse_count: self.parse_count,
             selections_update_count: self.selections_update_count,
@@ -649,6 +653,14 @@ impl Buffer {
         task
     }
 
+    pub fn update_git(&mut self) {
+        if let Some(head_text) = &self.head_text {
+            let snapshot = self.snapshot();
+            self.git_diff.update(head_text, &snapshot);
+            self.diff_update_count += 1;
+        }
+    }
+
     pub fn close(&mut self, cx: &mut ModelContext<Self>) {
         cx.emit(Event::Closed);
     }
@@ -673,6 +685,10 @@ impl Buffer {
         self.file_update_count
     }
 
+    pub fn diff_update_count(&self) -> usize {
+        self.diff_update_count
+    }
+
     #[cfg(any(test, feature = "test-support"))]
     pub fn is_parsing(&self) -> bool {
         self.parsing_in_background
@@ -2226,6 +2242,10 @@ impl BufferSnapshot {
     pub fn file_update_count(&self) -> usize {
         self.file_update_count
     }
+
+    pub fn diff_update_count(&self) -> usize {
+        self.diff_update_count
+    }
 }
 
 pub fn indent_size_for_line(text: &text::BufferSnapshot, row: u32) -> IndentSize {
@@ -2260,6 +2280,7 @@ impl Clone for BufferSnapshot {
             selections_update_count: self.selections_update_count,
             diagnostics_update_count: self.diagnostics_update_count,
             file_update_count: self.file_update_count,
+            diff_update_count: self.diff_update_count,
             language: self.language.clone(),
             parse_count: self.parse_count,
         }

crates/workspace/src/workspace.rs 🔗

@@ -734,7 +734,7 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
                                     );
                                 }
 
-                                const GIT_DELAY: Duration = Duration::from_millis(800);
+                                const GIT_DELAY: Duration = Duration::from_millis(600);
                                 let item = item.clone();
                                 pending_git_update.fire_new(
                                     GIT_DELAY,