@@ -190,21 +190,17 @@ fn fuzzy_match_positions(query: &str, candidate: &str) -> Option<Vec<usize>> {
fn root_repository_snapshots(
workspace: &Entity<Workspace>,
cx: &App,
-) -> Vec<project::git_store::RepositorySnapshot> {
+) -> impl Iterator<Item = project::git_store::RepositorySnapshot> {
let path_list = workspace_path_list(workspace, cx);
let project = workspace.read(cx).project().read(cx);
- project
- .repositories(cx)
- .values()
- .filter_map(|repo| {
- let snapshot = repo.read(cx).snapshot();
- let is_root = path_list
- .paths()
- .iter()
- .any(|p| p.as_path() == snapshot.work_directory_abs_path.as_ref());
- is_root.then_some(snapshot)
- })
- .collect()
+ project.repositories(cx).values().filter_map(move |repo| {
+ let snapshot = repo.read(cx).snapshot();
+ let is_root = path_list
+ .paths()
+ .iter()
+ .any(|p| p.as_path() == snapshot.work_directory_abs_path.as_ref());
+ is_root.then_some(snapshot)
+ })
}
fn workspace_path_list(workspace: &Entity<Workspace>, cx: &App) -> PathList {
@@ -544,59 +540,6 @@ impl Sidebar {
result
}
- fn all_thread_infos_for_workspace(
- workspace: &Entity<Workspace>,
- cx: &App,
- ) -> Vec<ActiveThreadInfo> {
- let Some(agent_panel) = workspace.read(cx).panel::<AgentPanel>(cx) else {
- return Vec::new();
- };
- let agent_panel_ref = agent_panel.read(cx);
-
- agent_panel_ref
- .parent_threads(cx)
- .into_iter()
- .map(|thread_view| {
- let thread_view_ref = thread_view.read(cx);
- let thread = thread_view_ref.thread.read(cx);
-
- let icon = thread_view_ref.agent_icon;
- let icon_from_external_svg = thread_view_ref.agent_icon_from_external_svg.clone();
- let title = thread
- .title()
- .unwrap_or_else(|| DEFAULT_THREAD_TITLE.into());
- let is_native = thread_view_ref.as_native_thread(cx).is_some();
- let is_title_generating = is_native && thread.has_provisional_title();
- let session_id = thread.session_id().clone();
- let is_background = agent_panel_ref.is_background_thread(&session_id);
-
- let status = if thread.is_waiting_for_confirmation() {
- AgentThreadStatus::WaitingForConfirmation
- } else if thread.had_error() {
- AgentThreadStatus::Error
- } else {
- match thread.status() {
- ThreadStatus::Generating => AgentThreadStatus::Running,
- ThreadStatus::Idle => AgentThreadStatus::Completed,
- }
- };
-
- let diff_stats = thread.action_log().read(cx).diff_stats(cx);
-
- ActiveThreadInfo {
- session_id,
- title,
- status,
- icon,
- icon_from_external_svg,
- is_background,
- is_title_generating,
- diff_stats,
- }
- })
- .collect()
- }
-
/// When modifying this thread, aim for a single forward pass over workspaces
/// and threads plus an O(T log T) sort. Avoid adding extra scans over the data.
fn rebuild_contents(&mut self, cx: &App) {
@@ -686,7 +629,7 @@ impl Sidebar {
for (i, workspace) in workspaces.iter().enumerate() {
for snapshot in root_repository_snapshots(workspace, cx) {
- if snapshot.work_directory_abs_path == snapshot.original_repo_abs_path {
+ if snapshot.is_main_worktree() {
main_repo_workspace
.entry(snapshot.work_directory_abs_path.clone())
.or_insert(i);
@@ -773,7 +716,7 @@ impl Sidebar {
.is_some_and(|(main_idx, _)| *main_idx == ws_index)
});
- let mut live_infos = Self::all_thread_infos_for_workspace(workspace, cx);
+ let mut live_infos: Vec<_> = all_thread_infos_for_workspace(workspace, cx).collect();
let mut threads: Vec<ThreadEntry> = Vec::new();
let mut has_running_threads = false;
@@ -831,7 +774,7 @@ impl Sidebar {
let mut linked_worktree_queries: Vec<(PathList, SharedString, Arc<Path>)> =
Vec::new();
for snapshot in root_repository_snapshots(workspace, cx) {
- if snapshot.work_directory_abs_path != snapshot.original_repo_abs_path {
+ if snapshot.is_linked_worktree() {
continue;
}
@@ -852,17 +795,16 @@ impl Sidebar {
for (worktree_path_list, worktree_name, worktree_path) in
&linked_worktree_queries
{
- let target_workspace =
- match absorbed_workspace_by_path.get(worktree_path.as_ref()) {
- Some(&idx) => {
- live_infos.extend(Self::all_thread_infos_for_workspace(
- &workspaces[idx],
- cx,
- ));
- ThreadEntryWorkspace::Open(workspaces[idx].clone())
- }
- None => ThreadEntryWorkspace::Closed(worktree_path_list.clone()),
- };
+ let target_workspace = match absorbed_workspace_by_path
+ .get(worktree_path.as_ref())
+ {
+ Some(&idx) => {
+ live_infos
+ .extend(all_thread_infos_for_workspace(&workspaces[idx], cx));
+ ThreadEntryWorkspace::Open(workspaces[idx].clone())
+ }
+ None => ThreadEntryWorkspace::Closed(worktree_path_list.clone()),
+ };
let worktree_rows: Vec<_> = thread_store
.read(cx)
@@ -1721,7 +1663,7 @@ impl Sidebar {
let mut known_worktree_paths: HashSet<std::path::PathBuf> = HashSet::new();
for workspace in &workspaces {
for snapshot in root_repository_snapshots(workspace, cx) {
- if snapshot.work_directory_abs_path != snapshot.original_repo_abs_path {
+ if snapshot.is_linked_worktree() {
continue;
}
for git_worktree in snapshot.linked_worktrees() {
@@ -1740,12 +1682,10 @@ impl Sidebar {
if path_list.paths().len() != 1 {
continue;
}
- let should_prune = root_repository_snapshots(workspace, cx)
- .iter()
- .any(|snapshot| {
- snapshot.work_directory_abs_path != snapshot.original_repo_abs_path
- && !known_worktree_paths.contains(snapshot.work_directory_abs_path.as_ref())
- });
+ let should_prune = root_repository_snapshots(workspace, cx).any(|snapshot| {
+ snapshot.is_linked_worktree()
+ && !known_worktree_paths.contains(snapshot.work_directory_abs_path.as_ref())
+ });
if should_prune {
to_remove.push(workspace.clone());
}
@@ -3217,6 +3157,76 @@ impl Render for Sidebar {
}
}
+fn all_thread_infos_for_workspace(
+ workspace: &Entity<Workspace>,
+ cx: &App,
+) -> impl Iterator<Item = ActiveThreadInfo> {
+ enum ThreadInfoIterator<T: Iterator<Item = ActiveThreadInfo>> {
+ Empty,
+ Threads(T),
+ }
+
+ impl<T: Iterator<Item = ActiveThreadInfo>> Iterator for ThreadInfoIterator<T> {
+ type Item = ActiveThreadInfo;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self {
+ ThreadInfoIterator::Empty => None,
+ ThreadInfoIterator::Threads(threads) => threads.next(),
+ }
+ }
+ }
+
+ let Some(agent_panel) = workspace.read(cx).panel::<AgentPanel>(cx) else {
+ return ThreadInfoIterator::Empty;
+ };
+ let agent_panel = agent_panel.read(cx);
+
+ let threads = agent_panel
+ .parent_threads(cx)
+ .into_iter()
+ .map(|thread_view| {
+ let thread_view_ref = thread_view.read(cx);
+ let thread = thread_view_ref.thread.read(cx);
+
+ let icon = thread_view_ref.agent_icon;
+ let icon_from_external_svg = thread_view_ref.agent_icon_from_external_svg.clone();
+ let title = thread
+ .title()
+ .unwrap_or_else(|| DEFAULT_THREAD_TITLE.into());
+ let is_native = thread_view_ref.as_native_thread(cx).is_some();
+ let is_title_generating = is_native && thread.has_provisional_title();
+ let session_id = thread.session_id().clone();
+ let is_background = agent_panel.is_background_thread(&session_id);
+
+ let status = if thread.is_waiting_for_confirmation() {
+ AgentThreadStatus::WaitingForConfirmation
+ } else if thread.had_error() {
+ AgentThreadStatus::Error
+ } else {
+ match thread.status() {
+ ThreadStatus::Generating => AgentThreadStatus::Running,
+ ThreadStatus::Idle => AgentThreadStatus::Completed,
+ }
+ };
+
+ let diff_stats = thread.action_log().read(cx).diff_stats(cx);
+
+ ActiveThreadInfo {
+ session_id,
+ title,
+ status,
+ icon,
+ icon_from_external_svg,
+ is_background,
+ is_title_generating,
+ diff_stats,
+ }
+ });
+
+ ThreadInfoIterator::Threads(threads)
+}
+
#[cfg(test)]
mod tests {
use super::*;