project: Be explicit about project-searchability for buffers (#37773)

Lukas Wirth created

Closes https://github.com/zed-industries/zed/issues/28830

Release Notes:

- Fixed builtin buffers and log views showing up in project search

Change summary

crates/activity_indicator/src/activity_indicator.rs |  3 
crates/agent_ui/src/acp/thread_view.rs              |  2 
crates/agent_ui/src/active_thread.rs                |  2 
crates/auto_update_ui/src/auto_update_ui.rs         |  5 -
crates/collab/src/tests/following_tests.rs          |  2 
crates/collab/src/tests/integration_tests.rs        |  2 
crates/editor/src/editor.rs                         |  4 
crates/editor/src/editor_tests.rs                   |  6 +-
crates/editor/src/items.rs                          |  4 
crates/editor/src/rust_analyzer_ext.rs              |  2 
crates/git_ui/src/git_panel.rs                      |  3 -
crates/language_tools/src/lsp_button.rs             |  2 
crates/project/src/buffer_store.rs                  | 44 +++++++++++---
crates/project/src/git_store.rs                     |  2 
crates/project/src/project.rs                       | 22 +++---
crates/project/src/project_tests.rs                 | 10 +-
crates/remote_server/src/headless_project.rs        |  2 
crates/zed/src/zed.rs                               |  7 +-
18 files changed, 71 insertions(+), 53 deletions(-)

Detailed changes

crates/activity_indicator/src/activity_indicator.rs 🔗

@@ -212,7 +212,8 @@ impl ActivityIndicator {
                 server_name,
                 status,
             } => {
-                let create_buffer = project.update(cx, |project, cx| project.create_buffer(cx));
+                let create_buffer =
+                    project.update(cx, |project, cx| project.create_buffer(false, cx));
                 let status = status.clone();
                 let server_name = server_name.clone();
                 cx.spawn_in(window, async move |workspace, cx| {

crates/agent_ui/src/acp/thread_view.rs 🔗

@@ -4255,7 +4255,7 @@ impl AcpThreadView {
                 }
 
                 let buffer = project.update(cx, |project, cx| {
-                    project.create_local_buffer(&markdown, Some(markdown_language), cx)
+                    project.create_local_buffer(&markdown, Some(markdown_language), true, cx)
                 });
                 let buffer = cx.new(|cx| {
                     MultiBuffer::singleton(buffer, cx).with_title(thread_summary.clone())

crates/agent_ui/src/active_thread.rs 🔗

@@ -3585,7 +3585,7 @@ pub(crate) fn open_active_thread_as_markdown(
             }
 
             let buffer = project.update(cx, |project, cx| {
-                project.create_local_buffer(&markdown, Some(markdown_language), cx)
+                project.create_local_buffer(&markdown, Some(markdown_language), true, cx)
             });
             let buffer =
                 cx.new(|cx| MultiBuffer::singleton(buffer, cx).with_title(thread_summary.clone()));

crates/auto_update_ui/src/auto_update_ui.rs 🔗

@@ -88,10 +88,7 @@ fn view_release_notes_locally(
                         .update_in(cx, |workspace, window, cx| {
                             let project = workspace.project().clone();
                             let buffer = project.update(cx, |project, cx| {
-                                let buffer = project.create_local_buffer("", markdown, cx);
-                                project
-                                    .mark_buffer_as_non_searchable(buffer.read(cx).remote_id(), cx);
-                                buffer
+                                project.create_local_buffer("", markdown, false, cx)
                             });
                             buffer.update(cx, |buffer, cx| {
                                 buffer.edit([(0..0, body.release_notes)], None, cx)

crates/collab/src/tests/following_tests.rs 🔗

@@ -2098,7 +2098,7 @@ async fn test_following_after_replacement(cx_a: &mut TestAppContext, cx_b: &mut
     share_workspace(&workspace, cx_a).await.unwrap();
     let buffer = workspace.update(cx_a, |workspace, cx| {
         workspace.project().update(cx, |project, cx| {
-            project.create_local_buffer(&sample_text(26, 5, 'a'), None, cx)
+            project.create_local_buffer(&sample_text(26, 5, 'a'), None, false, cx)
         })
     });
     let multibuffer = cx_a.new(|cx| {

crates/collab/src/tests/integration_tests.rs 🔗

@@ -2506,7 +2506,7 @@ async fn test_propagate_saves_and_fs_changes(
     });
 
     let new_buffer_a = project_a
-        .update(cx_a, |p, cx| p.create_buffer(cx))
+        .update(cx_a, |p, cx| p.create_buffer(false, cx))
         .await
         .unwrap();
 

crates/editor/src/editor.rs 🔗

@@ -2619,7 +2619,7 @@ impl Editor {
         cx: &mut Context<Workspace>,
     ) -> Task<Result<Entity<Editor>>> {
         let project = workspace.project().clone();
-        let create = project.update(cx, |project, cx| project.create_buffer(cx));
+        let create = project.update(cx, |project, cx| project.create_buffer(true, cx));
 
         cx.spawn_in(window, async move |workspace, cx| {
             let buffer = create.await?;
@@ -2657,7 +2657,7 @@ impl Editor {
         cx: &mut Context<Workspace>,
     ) {
         let project = workspace.project().clone();
-        let create = project.update(cx, |project, cx| project.create_buffer(cx));
+        let create = project.update(cx, |project, cx| project.create_buffer(true, cx));
 
         cx.spawn_in(window, async move |workspace, cx| {
             let buffer = create.await?;

crates/editor/src/editor_tests.rs 🔗

@@ -15913,7 +15913,7 @@ async fn test_following(cx: &mut TestAppContext) {
     let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
 
     let buffer = project.update(cx, |project, cx| {
-        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
+        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, false, cx);
         cx.new(|cx| MultiBuffer::singleton(buffer, cx))
     });
     let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
@@ -16165,8 +16165,8 @@ async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
 
     let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
         (
-            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
-            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
+            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, false, cx),
+            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, false, cx),
         )
     });
 

crates/editor/src/items.rs 🔗

@@ -1130,7 +1130,7 @@ impl SerializableItem for Editor {
 
                     // First create the empty buffer
                     let buffer = project
-                        .update(cx, |project, cx| project.create_buffer(cx))?
+                        .update(cx, |project, cx| project.create_buffer(true, cx))?
                         .await?;
 
                     // Then set the text so that the dirty bit is set correctly
@@ -1238,7 +1238,7 @@ impl SerializableItem for Editor {
                 ..
             } => window.spawn(cx, async move |cx| {
                 let buffer = project
-                    .update(cx, |project, cx| project.create_buffer(cx))?
+                    .update(cx, |project, cx| project.create_buffer(true, cx))?
                     .await?;
 
                 cx.update(|window, cx| {

crates/editor/src/rust_analyzer_ext.rs 🔗

@@ -200,7 +200,7 @@ pub fn expand_macro_recursively(
         }
 
         let buffer = project
-            .update(cx, |project, cx| project.create_buffer(cx))?
+            .update(cx, |project, cx| project.create_buffer(false, cx))?
             .await?;
         workspace.update_in(cx, |workspace, window, cx| {
             buffer.update(cx, |buffer, cx| {

crates/git_ui/src/git_panel.rs 🔗

@@ -388,9 +388,6 @@ pub(crate) fn commit_message_editor(
     window: &mut Window,
     cx: &mut Context<Editor>,
 ) -> Editor {
-    project.update(cx, |this, cx| {
-        this.mark_buffer_as_non_searchable(commit_message_buffer.read(cx).remote_id(), cx);
-    });
     let buffer = cx.new(|cx| MultiBuffer::singleton(commit_message_buffer, cx));
     let max_lines = if in_panel { MAX_PANEL_EDITOR_LINES } else { 18 };
     let mut commit_editor = Editor::new(

crates/language_tools/src/lsp_button.rs 🔗

@@ -296,7 +296,7 @@ impl LanguageServerState {
                                 .update(cx, |workspace, cx| {
                                     workspace
                                         .project()
-                                        .update(cx, |project, cx| project.create_buffer(cx))
+                                        .update(cx, |project, cx| project.create_buffer(false, cx))
                                 })
                                 .ok()
                             else {

crates/project/src/buffer_store.rs 🔗

@@ -319,7 +319,11 @@ impl RemoteBufferStore {
         })
     }
 
-    fn create_buffer(&self, cx: &mut Context<BufferStore>) -> Task<Result<Entity<Buffer>>> {
+    fn create_buffer(
+        &self,
+        project_searchable: bool,
+        cx: &mut Context<BufferStore>,
+    ) -> Task<Result<Entity<Buffer>>> {
         let create = self.upstream_client.request(proto::OpenNewBuffer {
             project_id: self.project_id,
         });
@@ -327,8 +331,13 @@ impl RemoteBufferStore {
             let response = create.await?;
             let buffer_id = BufferId::new(response.buffer_id)?;
 
-            this.update(cx, |this, cx| this.wait_for_remote_buffer(buffer_id, cx))?
-                .await
+            this.update(cx, |this, cx| {
+                if !project_searchable {
+                    this.non_searchable_buffers.insert(buffer_id);
+                }
+                this.wait_for_remote_buffer(buffer_id, cx)
+            })?
+            .await
         })
     }
 
@@ -670,12 +679,21 @@ impl LocalBufferStore {
         })
     }
 
-    fn create_buffer(&self, cx: &mut Context<BufferStore>) -> Task<Result<Entity<Buffer>>> {
+    fn create_buffer(
+        &self,
+        project_searchable: bool,
+        cx: &mut Context<BufferStore>,
+    ) -> Task<Result<Entity<Buffer>>> {
         cx.spawn(async move |buffer_store, cx| {
             let buffer =
                 cx.new(|cx| Buffer::local("", cx).with_language(language::PLAIN_TEXT.clone(), cx))?;
             buffer_store.update(cx, |buffer_store, cx| {
                 buffer_store.add_buffer(buffer.clone(), cx).log_err();
+                if !project_searchable {
+                    buffer_store
+                        .non_searchable_buffers
+                        .insert(buffer.read(cx).remote_id());
+                }
             })?;
             Ok(buffer)
         })
@@ -848,10 +866,14 @@ impl BufferStore {
         })
     }
 
-    pub fn create_buffer(&mut self, cx: &mut Context<Self>) -> Task<Result<Entity<Buffer>>> {
+    pub fn create_buffer(
+        &mut self,
+        project_searchable: bool,
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Buffer>>> {
         match &self.state {
-            BufferStoreState::Local(this) => this.create_buffer(cx),
-            BufferStoreState::Remote(this) => this.create_buffer(cx),
+            BufferStoreState::Local(this) => this.create_buffer(project_searchable, cx),
+            BufferStoreState::Remote(this) => this.create_buffer(project_searchable, cx),
         }
     }
 
@@ -1610,6 +1632,7 @@ impl BufferStore {
         &mut self,
         text: &str,
         language: Option<Arc<Language>>,
+        project_searchable: bool,
         cx: &mut Context<Self>,
     ) -> Entity<Buffer> {
         let buffer = cx.new(|cx| {
@@ -1619,6 +1642,9 @@ impl BufferStore {
 
         self.add_buffer(buffer.clone(), cx).log_err();
         let buffer_id = buffer.read(cx).remote_id();
+        if !project_searchable {
+            self.non_searchable_buffers.insert(buffer_id);
+        }
 
         if let Some(file) = File::from_dyn(buffer.read(cx).file()) {
             self.path_to_buffer_id.insert(
@@ -1688,10 +1714,6 @@ impl BufferStore {
         }
         serialized_transaction
     }
-
-    pub(crate) fn mark_buffer_as_non_searchable(&mut self, buffer_id: BufferId) {
-        self.non_searchable_buffers.insert(buffer_id);
-    }
 }
 
 impl OpenBuffer {

crates/project/src/git_store.rs 🔗

@@ -3317,7 +3317,7 @@ impl Repository {
     ) -> Task<Result<Entity<Buffer>>> {
         cx.spawn(async move |repository, cx| {
             let buffer = buffer_store
-                .update(cx, |buffer_store, cx| buffer_store.create_buffer(cx))?
+                .update(cx, |buffer_store, cx| buffer_store.create_buffer(false, cx))?
                 .await?;
 
             if let Some(language_registry) = language_registry {

crates/project/src/project.rs 🔗

@@ -2540,22 +2540,28 @@ impl Project {
         }
     }
 
-    pub fn create_buffer(&mut self, cx: &mut Context<Self>) -> Task<Result<Entity<Buffer>>> {
-        self.buffer_store
-            .update(cx, |buffer_store, cx| buffer_store.create_buffer(cx))
+    pub fn create_buffer(
+        &mut self,
+        searchable: bool,
+        cx: &mut Context<Self>,
+    ) -> Task<Result<Entity<Buffer>>> {
+        self.buffer_store.update(cx, |buffer_store, cx| {
+            buffer_store.create_buffer(searchable, cx)
+        })
     }
 
     pub fn create_local_buffer(
         &mut self,
         text: &str,
         language: Option<Arc<Language>>,
+        project_searchable: bool,
         cx: &mut Context<Self>,
     ) -> Entity<Buffer> {
         if self.is_via_collab() || self.is_via_remote_server() {
             panic!("called create_local_buffer on a remote project")
         }
         self.buffer_store.update(cx, |buffer_store, cx| {
-            buffer_store.create_local_buffer(text, language, cx)
+            buffer_store.create_local_buffer(text, language, project_searchable, cx)
         })
     }
 
@@ -4913,7 +4919,7 @@ impl Project {
         mut cx: AsyncApp,
     ) -> Result<proto::OpenBufferResponse> {
         let buffer = this
-            .update(&mut cx, |this, cx| this.create_buffer(cx))?
+            .update(&mut cx, |this, cx| this.create_buffer(true, cx))?
             .await?;
         let peer_id = envelope.original_sender_id()?;
 
@@ -5244,12 +5250,6 @@ impl Project {
     pub fn agent_location(&self) -> Option<AgentLocation> {
         self.agent_location.clone()
     }
-
-    pub fn mark_buffer_as_non_searchable(&self, buffer_id: BufferId, cx: &mut Context<Project>) {
-        self.buffer_store.update(cx, |buffer_store, _| {
-            buffer_store.mark_buffer_as_non_searchable(buffer_id)
-        });
-    }
 }
 
 pub struct PathMatchCandidateSet {

crates/project/src/project_tests.rs 🔗

@@ -3876,7 +3876,7 @@ async fn test_save_file_spawns_language_server(cx: &mut gpui::TestAppContext) {
     );
 
     let buffer = project
-        .update(cx, |this, cx| this.create_buffer(cx))
+        .update(cx, |this, cx| this.create_buffer(false, cx))
         .unwrap()
         .await;
     project.update(cx, |this, cx| {
@@ -4088,7 +4088,9 @@ async fn test_save_as(cx: &mut gpui::TestAppContext) {
     let languages = project.update(cx, |project, _| project.languages().clone());
     languages.add(rust_lang());
 
-    let buffer = project.update(cx, |project, cx| project.create_local_buffer("", None, cx));
+    let buffer = project.update(cx, |project, cx| {
+        project.create_local_buffer("", None, false, cx)
+    });
     buffer.update(cx, |buffer, cx| {
         buffer.edit([(0..0, "abc")], None, cx);
         assert!(buffer.is_dirty());
@@ -5585,9 +5587,7 @@ async fn test_search_with_buffer_exclusions(cx: &mut gpui::TestAppContext) {
 
     let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
     let _buffer = project.update(cx, |project, cx| {
-        let buffer = project.create_local_buffer("file", None, cx);
-        project.mark_buffer_as_non_searchable(buffer.read(cx).remote_id(), cx);
-        buffer
+        project.create_local_buffer("file", None, false, cx)
     });
 
     assert_eq!(

crates/remote_server/src/headless_project.rs 🔗

@@ -517,7 +517,7 @@ impl HeadlessProject {
             let buffer_store = this.buffer_store.clone();
             let buffer = this
                 .buffer_store
-                .update(cx, |buffer_store, cx| buffer_store.create_buffer(cx));
+                .update(cx, |buffer_store, cx| buffer_store.create_buffer(true, cx));
             anyhow::Ok((buffer_store, buffer))
         })??;
 

crates/zed/src/zed.rs 🔗

@@ -1167,7 +1167,7 @@ fn open_log_file(workspace: &mut Workspace, window: &mut Window, cx: &mut Contex
                         };
                         let project = workspace.project().clone();
                         let buffer = project.update(cx, |project, cx| {
-                            project.create_local_buffer(&log, None, cx)
+                            project.create_local_buffer(&log, None, false, cx)
                         });
 
                         let buffer = cx
@@ -1733,7 +1733,7 @@ fn open_telemetry_log_file(
 
             workspace.update_in( cx, |workspace, window, cx| {
                 let project = workspace.project().clone();
-                let buffer = project.update(cx, |project, cx| project.create_local_buffer(&content, json, cx));
+                let buffer = project.update(cx, |project, cx| project.create_local_buffer(&content, json,false, cx));
                 let buffer = cx.new(|cx| {
                     MultiBuffer::singleton(buffer, cx).with_title("Telemetry Log".into())
                 });
@@ -1772,7 +1772,8 @@ fn open_bundled_file(
                 workspace.with_local_workspace(window, cx, |workspace, window, cx| {
                     let project = workspace.project();
                     let buffer = project.update(cx, move |project, cx| {
-                        let buffer = project.create_local_buffer(text.as_ref(), language, cx);
+                        let buffer =
+                            project.create_local_buffer(text.as_ref(), language, false, cx);
                         buffer.update(cx, |buffer, cx| {
                             buffer.set_capability(Capability::ReadOnly, cx);
                         });