Fix project search holding a reference to closed unsaved files (#61677)
# Objective Fixes #28398 ## Solution This issue was occurring because `ProjectSearch` stored `Entity<MultiBuffer>` with has reference counted pointers to underlying buffers The function `remove_deleted_buffers` is renamed to `remove_stale_buffers` and it now also checks for buffers which do not have a file associated with them and are closed. Then, it removes them from `Entity<MultiBuffer>` stored in ProjectSearch. This function is also called in again when workspace is upgraded. ## Testing Bug being fixed was manually verified. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed project search retaining results from closed untitled buffers. --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
0a10d97e6a
commit
34c48d6579
5 files changed
+331
-55
No files matched your search
Generated
+1
@@ -16059,6 +16059,7 @@ dependencies = [
|
||||
"picker_preview",
|
||||
"pretty_assertions",
|
||||
"project",
|
||||
"proto",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"settings",
|
||||
|
||||
@@ -1666,6 +1666,18 @@ impl BufferStore {
|
||||
self.shared_buffers.remove(peer_id);
|
||||
}
|
||||
|
||||
pub fn is_shared(&self, buffer_id: BufferId, cx: &App) -> bool {
|
||||
self.shared_buffers
|
||||
.values()
|
||||
.any(|buffers| buffers.contains_key(&buffer_id))
|
||||
|| self.as_remote().is_some_and(|remote| {
|
||||
remote
|
||||
.shared_with_me
|
||||
.iter()
|
||||
.any(|buffer| buffer.read(cx).remote_id() == buffer_id)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update_peer_id(&mut self, old_peer_id: &proto::PeerId, new_peer_id: proto::PeerId) {
|
||||
if let Some(buffers) = self.shared_buffers.remove(old_peer_id) {
|
||||
self.shared_buffers.insert(new_peer_id, buffers);
|
||||
|
||||
@@ -60,5 +60,6 @@ gpui = { workspace = true, features = ["test-support"] }
|
||||
language = { workspace = true, features = ["test-support"] }
|
||||
lsp.workspace = true
|
||||
pretty_assertions.workspace = true
|
||||
proto.workspace = true
|
||||
unindent.workspace = true
|
||||
workspace = { workspace = true, features = ["test-support"] }
|
||||
@@ -58,7 +58,7 @@ use util::{ResultExt as _, paths::PathMatcher};
|
||||
use workspace::{
|
||||
DeploySearch, ItemNavHistory, NewSearch, ToolbarItemEvent, ToolbarItemLocation,
|
||||
ToolbarItemView, Workspace, WorkspaceId,
|
||||
item::{Item, ItemEvent, ItemHandle, SaveOptions},
|
||||
item::{Item, ItemBufferKind, ItemEvent, ItemHandle, SaveOptions},
|
||||
searchable::{Direction, SearchEvent, SearchToken, SearchableItem, SearchableItemHandle},
|
||||
};
|
||||
|
||||
@@ -253,6 +253,7 @@ const SEARCH_ON_TYPE_DEBOUNCE: Duration = Duration::from_millis(250);
|
||||
|
||||
pub struct ProjectSearch {
|
||||
pub(crate) project: Entity<Project>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
pub excerpts: Entity<MultiBuffer>,
|
||||
pub pending_search: Option<Task<Option<SearchResults<SearchResult>>>>,
|
||||
pub match_ranges: Vec<Range<Anchor>>,
|
||||
@@ -268,6 +269,7 @@ pub struct ProjectSearch {
|
||||
search_excluded_history_cursor: SearchHistoryCursor,
|
||||
pub project_search_turning_into_text_finder: Arc<AtomicBool>,
|
||||
_excerpts_subscription: Subscription,
|
||||
_workspace_subscription: Option<Subscription>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
@@ -393,13 +395,19 @@ pub struct ProjectSearchBar {
|
||||
}
|
||||
|
||||
impl ProjectSearch {
|
||||
pub fn new(project: Entity<Project>, cx: &mut Context<Self>) -> Self {
|
||||
pub fn new(
|
||||
project: Entity<Project>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let capability = project.read(cx).capability();
|
||||
let excerpts = cx.new(|_| MultiBuffer::new(capability));
|
||||
let subscription = Self::subscribe_to_excerpts(&excerpts, cx);
|
||||
let excerpts_subscription = Self::subscribe_to_excerpts(&excerpts, cx);
|
||||
let workspace_subscription = Self::subscribe_to_workspace(&workspace, cx);
|
||||
|
||||
Self {
|
||||
project,
|
||||
workspace,
|
||||
excerpts,
|
||||
pending_search: Default::default(),
|
||||
match_ranges: Default::default(),
|
||||
@@ -414,7 +422,8 @@ impl ProjectSearch {
|
||||
search_included_history_cursor: Default::default(),
|
||||
search_excluded_history_cursor: Default::default(),
|
||||
project_search_turning_into_text_finder: Arc::new(AtomicBool::new(false)),
|
||||
_excerpts_subscription: subscription,
|
||||
_excerpts_subscription: excerpts_subscription,
|
||||
_workspace_subscription: workspace_subscription,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,10 +432,12 @@ impl ProjectSearch {
|
||||
let excerpts = self
|
||||
.excerpts
|
||||
.update(cx, |excerpts, cx| cx.new(|cx| excerpts.clone(cx)));
|
||||
let subscription = Self::subscribe_to_excerpts(&excerpts, cx);
|
||||
let excerpts_subscription = Self::subscribe_to_excerpts(&excerpts, cx);
|
||||
let workspace_subscription = Self::subscribe_to_workspace(&self.workspace, cx);
|
||||
|
||||
Self {
|
||||
project: self.project.clone(),
|
||||
workspace: self.workspace.clone(),
|
||||
excerpts,
|
||||
pending_search: Default::default(),
|
||||
match_ranges: self.match_ranges.clone(),
|
||||
@@ -449,7 +460,8 @@ impl ProjectSearch {
|
||||
search_included_history_cursor: self.search_included_history_cursor.clone(),
|
||||
search_excluded_history_cursor: self.search_excluded_history_cursor.clone(),
|
||||
project_search_turning_into_text_finder: Arc::new(AtomicBool::new(false)),
|
||||
_excerpts_subscription: subscription,
|
||||
_excerpts_subscription: excerpts_subscription,
|
||||
_workspace_subscription: workspace_subscription,
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -464,26 +476,46 @@ impl ProjectSearch {
|
||||
})
|
||||
}
|
||||
|
||||
fn subscribe_to_workspace(
|
||||
workspace: &WeakEntity<Workspace>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<Subscription> {
|
||||
workspace.upgrade().map(|workspace| {
|
||||
cx.subscribe(&workspace, |this, _, event, cx| {
|
||||
if matches!(event, workspace::Event::ItemRemoved { .. }) {
|
||||
this.remove_closed_untitled_buffers(cx);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn remove_deleted_buffers(&mut self, cx: &mut Context<Self>) {
|
||||
let deleted_buffer_ids = self
|
||||
self.remove_stale_buffers(None, cx);
|
||||
}
|
||||
|
||||
fn remove_closed_untitled_buffers(&mut self, cx: &mut Context<Self>) {
|
||||
self.remove_stale_buffers(self.workspace.upgrade().as_ref(), cx);
|
||||
}
|
||||
|
||||
fn remove_stale_buffers(
|
||||
&mut self,
|
||||
workspace: Option<&Entity<Workspace>>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let stale_buffer_ids = self
|
||||
.excerpts
|
||||
.read(cx)
|
||||
.all_buffers_iter()
|
||||
.filter(|buffer| {
|
||||
buffer
|
||||
.read(cx)
|
||||
.file()
|
||||
.is_some_and(|file| file.disk_state().is_deleted())
|
||||
})
|
||||
.filter(|buffer| is_buffer_stale(&self.project, workspace, buffer, cx))
|
||||
.map(|buffer| buffer.read(cx).remote_id())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if deleted_buffer_ids.is_empty() {
|
||||
if stale_buffer_ids.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let snapshot = self.excerpts.update(cx, |excerpts, cx| {
|
||||
for buffer_id in deleted_buffer_ids {
|
||||
for buffer_id in stale_buffer_ids {
|
||||
excerpts.remove_excerpts_for_buffer(buffer_id, cx);
|
||||
}
|
||||
excerpts.snapshot(cx)
|
||||
@@ -691,25 +723,39 @@ async fn consume_search_stream(
|
||||
buffers_with_ranges
|
||||
.into_iter()
|
||||
.map(|(buffer, ranges)| {
|
||||
excerpts.set_anchored_excerpts_for_path(
|
||||
let new_ranges = excerpts.set_anchored_excerpts_for_path(
|
||||
PathKey::for_buffer(&buffer, cx),
|
||||
buffer,
|
||||
buffer.clone(),
|
||||
ranges,
|
||||
multibuffer_context_lines(cx),
|
||||
cx,
|
||||
)
|
||||
);
|
||||
async move { (buffer, new_ranges.await) }
|
||||
})
|
||||
.collect::<FuturesOrdered<_>>()
|
||||
})
|
||||
})
|
||||
.ok()?;
|
||||
while let Some(new_ranges) = new_ranges.next().await {
|
||||
while let Some((buffer, new_ranges)) = new_ranges.next().await {
|
||||
// `new_ranges.next().await` likely never gets hit while still pending so `async_task`
|
||||
// will not reschedule, starving other front end tasks, insert a yield point for that here
|
||||
smol::future::yield_now().await;
|
||||
project_search
|
||||
.update(cx, |project_search, cx| {
|
||||
project_search.match_ranges.extend(new_ranges);
|
||||
let workspace = project_search.workspace.upgrade();
|
||||
if !is_buffer_stale(
|
||||
&project_search.project,
|
||||
workspace.as_ref(),
|
||||
&buffer,
|
||||
cx,
|
||||
) {
|
||||
project_search.match_ranges.extend(new_ranges);
|
||||
} else {
|
||||
let buffer_id = buffer.read(cx).remote_id();
|
||||
project_search.excerpts.update(cx, |excerpts, cx| {
|
||||
excerpts.remove_excerpts_for_buffer(buffer_id, cx)
|
||||
});
|
||||
}
|
||||
cx.notify();
|
||||
})
|
||||
.ok()?;
|
||||
@@ -881,6 +927,7 @@ async fn apply_reused_chunk(
|
||||
.collect::<Vec<_>>();
|
||||
let batch_changed = project_search
|
||||
.update(cx, |project_search, cx| {
|
||||
let workspace = project_search.workspace.upgrade();
|
||||
let batch_changed = project_search.excerpts.update(cx, |excerpts, cx| {
|
||||
let mut batch_changed = false;
|
||||
let mut applied = Vec::with_capacity(batch.len());
|
||||
@@ -908,6 +955,12 @@ async fn apply_reused_chunk(
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if is_buffer_stale(&project_search.project, workspace.as_ref(), &buffer, cx)
|
||||
{
|
||||
excerpts.remove_excerpts_for_paths(vec![path_key], cx);
|
||||
batch_changed |= previous_span.is_some();
|
||||
continue;
|
||||
}
|
||||
excerpts.set_excerpt_ranges_for_path(
|
||||
path_key,
|
||||
buffer,
|
||||
@@ -1624,7 +1677,8 @@ impl ProjectSearchView {
|
||||
) {
|
||||
let weak_workspace = cx.entity().downgrade();
|
||||
|
||||
let entity = cx.new(|cx| ProjectSearch::new(workspace.project().clone(), cx));
|
||||
let entity = cx
|
||||
.new(|cx| ProjectSearch::new(workspace.project().clone(), weak_workspace.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearchView::new(weak_workspace, entity, window, cx, None));
|
||||
workspace.add_item_to_active_pane(Box::new(search.clone()), None, true, window, cx);
|
||||
search.update(cx, |search, cx| {
|
||||
@@ -1682,12 +1736,13 @@ impl ProjectSearchView {
|
||||
new_query
|
||||
});
|
||||
if let Some(new_query) = new_query {
|
||||
let weak_workspace = cx.entity().downgrade();
|
||||
let entity = cx.new(|cx| {
|
||||
let mut entity = ProjectSearch::new(workspace.project().clone(), cx);
|
||||
let mut entity =
|
||||
ProjectSearch::new(workspace.project().clone(), weak_workspace.clone(), cx);
|
||||
entity.search(new_query, SearchMode::Manual, false, cx);
|
||||
entity
|
||||
});
|
||||
let weak_workspace = cx.entity().downgrade();
|
||||
workspace.add_item_to_active_pane(
|
||||
Box::new(cx.new(|cx| {
|
||||
ProjectSearchView::new(weak_workspace, entity, window, cx, None)
|
||||
@@ -1756,7 +1811,9 @@ impl ProjectSearchView {
|
||||
|
||||
let weak_workspace = cx.entity().downgrade();
|
||||
|
||||
let project_search = cx.new(|cx| ProjectSearch::new(workspace.project().clone(), cx));
|
||||
let project_search = cx.new(|cx| {
|
||||
ProjectSearch::new(workspace.project().clone(), weak_workspace.clone(), cx)
|
||||
});
|
||||
let project_search_view = cx.new(|cx| {
|
||||
ProjectSearchView::new(weak_workspace, project_search, window, cx, settings)
|
||||
});
|
||||
@@ -3380,6 +3437,30 @@ fn register_workspace_action_for_present_search<A: Action>(
|
||||
});
|
||||
}
|
||||
|
||||
fn is_buffer_stale(
|
||||
project: &Entity<Project>,
|
||||
workspace: Option<&Entity<Workspace>>,
|
||||
buffer: &Entity<Buffer>,
|
||||
cx: &App,
|
||||
) -> bool {
|
||||
let buffer_entity_id = buffer.entity_id();
|
||||
let buffer = buffer.read(cx);
|
||||
if let Some(file) = buffer.file() {
|
||||
file.disk_state().is_deleted()
|
||||
} else if let Some(workspace) = workspace {
|
||||
!workspace.read(cx).items(cx).any(|item| {
|
||||
item.buffer_kind(cx) == ItemBufferKind::Singleton
|
||||
&& item.project_item_model_ids(cx).contains(&buffer_entity_id)
|
||||
}) && !project
|
||||
.read(cx)
|
||||
.buffer_store()
|
||||
.read(cx)
|
||||
.is_shared(buffer.remote_id(), cx)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn perform_project_search(
|
||||
search_view: &Entity<ProjectSearchView>,
|
||||
@@ -3422,7 +3503,7 @@ pub mod tests {
|
||||
};
|
||||
use util::{path, paths::PathStyle, rel_path::rel_path};
|
||||
use util_macros::perf;
|
||||
use workspace::{DeploySearch, MultiWorkspace};
|
||||
use workspace::{DeploySearch, MultiWorkspace, SaveIntent};
|
||||
|
||||
#[test]
|
||||
fn test_split_glob_patterns() {
|
||||
@@ -3474,7 +3555,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search, window, cx, None)
|
||||
});
|
||||
@@ -3544,7 +3625,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search, window, cx, None)
|
||||
});
|
||||
@@ -3585,7 +3666,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search, window, cx, None)
|
||||
});
|
||||
@@ -3735,7 +3816,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -3887,6 +3968,184 @@ pub mod tests {
|
||||
assert!(!results_collapsed);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_search_results_do_not_read_closed_untitled_buffer(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background_executor.clone());
|
||||
fs.insert_tree(
|
||||
path!("/dir"),
|
||||
json!({
|
||||
"one.rs": "const ONE: usize = 1;",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
|
||||
let window =
|
||||
cx.add_window(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx));
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
|
||||
let untitled_buffer = project.update(cx, |project, cx| {
|
||||
project.create_local_buffer("const TWO: usize = one::ONE;\n", None, true, cx)
|
||||
});
|
||||
let editor = window
|
||||
.update(cx, |_, window, cx| {
|
||||
let multibuffer = MultiBuffer::build_from_buffer(untitled_buffer.clone(), cx);
|
||||
let editor = cx.new(|cx| {
|
||||
Editor::new(
|
||||
editor::EditorMode::full(),
|
||||
multibuffer,
|
||||
Some(project.clone()),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.add_item_to_center(Box::new(editor.clone()), window, cx);
|
||||
});
|
||||
editor
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
|
||||
perform_search(search_view, "const", cx);
|
||||
|
||||
search_view
|
||||
.update(cx, |search_view, _window, cx| {
|
||||
let results_text = search_view
|
||||
.results_editor
|
||||
.update(cx, |editor, cx| editor.display_text(cx));
|
||||
|
||||
assert_eq!(
|
||||
"\n\nconst TWO: usize = one::ONE;\n\n\n\nconst ONE: usize = 1;",
|
||||
results_text
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
|
||||
let close_task = window
|
||||
.update(cx, |_, window, cx| {
|
||||
pane.update(cx, |pane, cx| {
|
||||
pane.close_item_by_id(editor.entity_id(), SaveIntent::Skip, window, cx)
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
close_task.await.unwrap();
|
||||
cx.run_until_parked();
|
||||
|
||||
search_view
|
||||
.update(cx, |search_view, _window, cx| {
|
||||
let results_text = search_view
|
||||
.results_editor
|
||||
.update(cx, |editor, cx| editor.display_text(cx));
|
||||
assert_eq!("\n\nconst ONE: usize = 1;", results_text);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// Re-run the search and verify the closed untitled buffer stays gone
|
||||
perform_search(search_view, "const", cx);
|
||||
|
||||
search_view
|
||||
.update(cx, |search_view, _window, cx| {
|
||||
let results_text = search_view
|
||||
.results_editor
|
||||
.update(cx, |editor, cx| editor.display_text(cx));
|
||||
assert_eq!("\n\nconst ONE: usize = 1;", results_text);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_search_results_keep_peer_shared_untitled_buffers(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background_executor.clone());
|
||||
fs.insert_tree(
|
||||
path!("/dir"),
|
||||
json!({
|
||||
"one.rs": "const ONE: usize = 1;",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
|
||||
let window =
|
||||
cx.add_window(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx));
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
|
||||
let untitled_buffer = project.update(cx, |project, cx| {
|
||||
project.create_local_buffer("const TWO: usize = one::ONE;\n", None, true, cx)
|
||||
});
|
||||
project.update(cx, |project, cx| {
|
||||
project.buffer_store().update(cx, |buffer_store, cx| {
|
||||
buffer_store
|
||||
.create_buffer_for_peer(
|
||||
&untitled_buffer,
|
||||
proto::PeerId { owner_id: 0, id: 1 },
|
||||
cx,
|
||||
)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
});
|
||||
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
|
||||
perform_search(search_view, "const", cx);
|
||||
search_view
|
||||
.update(cx, |search_view, _window, cx| {
|
||||
let results_text = search_view
|
||||
.results_editor
|
||||
.update(cx, |editor, cx| editor.display_text(cx));
|
||||
assert_eq!(
|
||||
"\n\nconst TWO: usize = one::ONE;\n\n\n\nconst ONE: usize = 1;",
|
||||
results_text
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
search.update(cx, |search, cx| search.remove_closed_untitled_buffers(cx));
|
||||
cx.run_until_parked();
|
||||
|
||||
search_view
|
||||
.update(cx, |search_view, _window, cx| {
|
||||
let results_text = search_view
|
||||
.results_editor
|
||||
.update(cx, |editor, cx| editor.display_text(cx));
|
||||
assert_eq!(
|
||||
"\n\nconst TWO: usize = one::ONE;\n\n\n\nconst ONE: usize = 1;",
|
||||
results_text
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
perform_search(search_view, "const", cx);
|
||||
|
||||
search_view
|
||||
.update(cx, |search_view, _window, cx| {
|
||||
let results_text = search_view
|
||||
.results_editor
|
||||
.update(cx, |editor, cx| editor.display_text(cx));
|
||||
assert_eq!(
|
||||
"\n\nconst TWO: usize = one::ONE;\n\n\n\nconst ONE: usize = 1;",
|
||||
results_text
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[perf]
|
||||
#[gpui::test]
|
||||
async fn test_collapse_state_syncs_after_manual_buffer_fold(cx: &mut TestAppContext) {
|
||||
@@ -3908,7 +4167,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -5632,7 +5891,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -5986,7 +6245,7 @@ pub mod tests {
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let cx = &mut VisualTestContext::from_window(window.into(), cx);
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -6156,7 +6415,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -6420,7 +6679,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -6627,7 +6886,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -6699,7 +6958,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -6906,7 +7165,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -7151,7 +7410,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -7213,7 +7472,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -7286,7 +7545,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -7378,7 +7637,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -7458,7 +7717,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -7838,7 +8097,8 @@ pub mod tests {
|
||||
let buffer_untitled = project.update(cx, |project, cx| {
|
||||
project.create_local_buffer("fn needle_untitled() {}", None, true, cx)
|
||||
});
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search =
|
||||
cx.new(|cx| ProjectSearch::new(project.clone(), WeakEntity::new_invalid(), cx));
|
||||
let build_query = || {
|
||||
SearchOptions::NONE
|
||||
.build_query(
|
||||
@@ -7913,7 +8173,8 @@ pub mod tests {
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search =
|
||||
cx.new(|cx| ProjectSearch::new(project.clone(), WeakEntity::new_invalid(), cx));
|
||||
|
||||
let query = SearchOptions::NONE
|
||||
.build_query(
|
||||
@@ -7964,7 +8225,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -8025,7 +8286,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -8493,7 +8754,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -8577,7 +8838,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -8659,7 +8920,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -8728,7 +8989,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -8769,7 +9030,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -8835,7 +9096,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -8918,7 +9179,7 @@ pub mod tests {
|
||||
let workspace = window
|
||||
.read_with(cx, |mw, _| mw.workspace().clone())
|
||||
.unwrap();
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), workspace.downgrade(), cx));
|
||||
let search_view = cx.add_window(|window, cx| {
|
||||
ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
|
||||
});
|
||||
@@ -8985,7 +9246,8 @@ pub mod tests {
|
||||
)
|
||||
.await;
|
||||
let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
|
||||
let search = cx.new(|cx| ProjectSearch::new(project.clone(), cx));
|
||||
let search =
|
||||
cx.new(|cx| ProjectSearch::new(project.clone(), WeakEntity::new_invalid(), cx));
|
||||
let query = SearchOptions::REGEX
|
||||
.build_query(
|
||||
"needle_[a-z]+",
|
||||
|
||||
@@ -350,7 +350,7 @@ impl Delegate {
|
||||
.get(&project.downgrade())
|
||||
.cloned();
|
||||
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, cx));
|
||||
let search = cx.new(|cx| ProjectSearch::new(project, weak_workspace.clone(), cx));
|
||||
let project_search =
|
||||
cx.new(|cx| ProjectSearchView::new(weak_workspace, search, window, cx, settings));
|
||||
cx.spawn(async move |_, cx| Self::new_from_project_search(project_search, cx).await)
|
||||
|
||||
Reference in new issue
Block a user