Only keep one blame up-to-date (cherry-pick #11274) (#11277)

gcp-cherry-pick-bot[bot] and Conrad Irwin created

Cherry-picked Only keep one blame up-to-date (#11274)

I was experiencing hang, and we blamed it on spawning a few hundred git
processes
simultaneously.

cc @MrNugget

Release Notes:

- Fixed slowness with hundreds of buffers open doing git blame.

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>

Change summary

crates/editor/src/editor.rs    | 16 +++++++++++++++-
crates/editor/src/git/blame.rs | 30 ++++++++++++++++++++++++++----
2 files changed, 41 insertions(+), 5 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -8986,8 +8986,14 @@ impl Editor {
                 return;
             };
 
+            if buffer.read(cx).file().is_none() {
+                return;
+            }
+
+            let focused = self.focus_handle(cx).contains_focused(cx);
             let project = project.clone();
-            let blame = cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, cx));
+            let blame =
+                cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
             self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
             self.blame = Some(blame);
         }
@@ -9975,6 +9981,10 @@ impl Editor {
             let rename_editor_focus_handle = rename.editor.read(cx).focus_handle.clone();
             cx.focus(&rename_editor_focus_handle);
         } else {
+            if let Some(blame) = self.blame.as_ref() {
+                blame.update(cx, GitBlame::focus)
+            }
+
             self.blink_manager.update(cx, BlinkManager::enable);
             self.show_cursor_names(cx);
             self.buffer.update(cx, |buffer, cx| {
@@ -9995,6 +10005,10 @@ impl Editor {
         self.blink_manager.update(cx, BlinkManager::disable);
         self.buffer
             .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
+
+        if let Some(blame) = self.blame.as_ref() {
+            blame.update(cx, GitBlame::blur)
+        }
         self.hide_context_menu(cx);
         hide_hover(self, cx);
         cx.emit(EditorEvent::Blurred);

crates/editor/src/git/blame.rs 🔗

@@ -88,7 +88,9 @@ pub struct GitBlame {
     buffer_snapshot: BufferSnapshot,
     buffer_edits: text::Subscription,
     task: Task<Result<()>>,
+    focused: bool,
     generated: bool,
+    changed_while_blurred: bool,
     user_triggered: bool,
     regenerate_on_edit_task: Task<Result<()>>,
     _regenerate_subscriptions: Vec<Subscription>,
@@ -99,6 +101,7 @@ impl GitBlame {
         buffer: Model<Buffer>,
         project: Model<Project>,
         user_triggered: bool,
+        focused: bool,
         cx: &mut ModelContext<Self>,
     ) -> Self {
         let entries = SumTree::from_item(
@@ -153,6 +156,8 @@ impl GitBlame {
             entries,
             buffer_edits,
             user_triggered,
+            focused,
+            changed_while_blurred: false,
             commit_details: HashMap::default(),
             task: Task::ready(Ok(())),
             generated: false,
@@ -186,6 +191,18 @@ impl GitBlame {
         })
     }
 
+    pub fn blur(&mut self, _: &mut ModelContext<Self>) {
+        self.focused = false;
+    }
+
+    pub fn focus(&mut self, cx: &mut ModelContext<Self>) {
+        self.focused = true;
+        if self.changed_while_blurred {
+            self.changed_while_blurred = false;
+            self.generate(cx);
+        }
+    }
+
     fn sync(&mut self, cx: &mut ModelContext<Self>) {
         let edits = self.buffer_edits.consume();
         let new_snapshot = self.buffer.read(cx).snapshot();
@@ -298,6 +315,10 @@ impl GitBlame {
     }
 
     fn generate(&mut self, cx: &mut ModelContext<Self>) {
+        if !self.focused {
+            self.changed_while_blurred = true;
+            return;
+        }
         let buffer_edits = self.buffer.update(cx, |buffer, _| buffer.subscribe());
         let snapshot = self.buffer.read(cx).snapshot();
         let blame = self.project.read(cx).blame_buffer(&self.buffer, None, cx);
@@ -544,7 +565,8 @@ mod tests {
             .await
             .unwrap();
 
-        let blame = cx.new_model(|cx| GitBlame::new(buffer.clone(), project.clone(), true, cx));
+        let blame =
+            cx.new_model(|cx| GitBlame::new(buffer.clone(), project.clone(), true, true, cx));
 
         let event = project.next_event(cx).await;
         assert_eq!(
@@ -613,7 +635,7 @@ mod tests {
             .await
             .unwrap();
 
-        let git_blame = cx.new_model(|cx| GitBlame::new(buffer.clone(), project, false, cx));
+        let git_blame = cx.new_model(|cx| GitBlame::new(buffer.clone(), project, false, true, cx));
 
         cx.executor().run_until_parked();
 
@@ -693,7 +715,7 @@ mod tests {
             .await
             .unwrap();
 
-        let git_blame = cx.new_model(|cx| GitBlame::new(buffer.clone(), project, false, cx));
+        let git_blame = cx.new_model(|cx| GitBlame::new(buffer.clone(), project, false, true, cx));
 
         cx.executor().run_until_parked();
 
@@ -842,7 +864,7 @@ mod tests {
             .await
             .unwrap();
 
-        let git_blame = cx.new_model(|cx| GitBlame::new(buffer.clone(), project, false, cx));
+        let git_blame = cx.new_model(|cx| GitBlame::new(buffer.clone(), project, false, true, cx));
         cx.executor().run_until_parked();
         git_blame.update(cx, |blame, cx| blame.check_invariants(cx));