Sidebar and git cleanups (#52431)
A few minor cleanups in sidebar.rs and some related areas. Release notes: - N/A
This commit is contained in:
1 parent
d434ac4446
commit
edc8bafa3c
2 files changed
+114
-87
No files matched your search
@@ -3706,6 +3706,23 @@ impl RepositorySnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
/// The main worktree is the original checkout that other worktrees were
|
||||
/// created from.
|
||||
///
|
||||
/// For example, if you had both `~/code/zed` and `~/code/worktrees/zed-2`,
|
||||
/// then `~/code/zed` is the main worktree and `~/code/worktrees/zed-2` is a linked worktree.
|
||||
pub fn is_main_worktree(&self) -> bool {
|
||||
self.work_directory_abs_path == self.original_repo_abs_path
|
||||
}
|
||||
|
||||
/// Returns true if this repository is a linked worktree, that is, one that
|
||||
/// was created from another worktree.
|
||||
///
|
||||
/// This is by definition the opposite of [`Self::is_main_worktree`].
|
||||
pub fn is_linked_worktree(&self) -> bool {
|
||||
!self.is_main_worktree()
|
||||
}
|
||||
|
||||
pub fn linked_worktrees(&self) -> &[GitWorktree] {
|
||||
&self.linked_worktrees
|
||||
}
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
Reference in new issue
Block a user