Show worktree thread status in sidebar (#51879)
Before you mark this PR as ready for review, make sure that you have: - [X] Added a solid test coverage and/or screenshots from doing manual testing - [X] Done a self-review taking into account security and performance aspects - [X] Aligned any UI changes with the [UI checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) Release Notes: - N/A Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
1 parent
14885feca5
commit
35059adb27
1 file changed
+139
-9
@@ -723,14 +723,7 @@ impl Sidebar {
|
||||
let is_collapsed = self.collapsed_groups.contains(&path_list);
|
||||
let should_load_threads = !is_collapsed || !query.is_empty();
|
||||
|
||||
let live_infos = Self::all_thread_infos_for_workspace(workspace, cx);
|
||||
let has_running_threads = live_infos
|
||||
.iter()
|
||||
.any(|info| info.status == AgentThreadStatus::Running);
|
||||
let waiting_thread_count = live_infos
|
||||
.iter()
|
||||
.filter(|info| info.status == AgentThreadStatus::WaitingForConfirmation)
|
||||
.count();
|
||||
let mut live_infos = Self::all_thread_infos_for_workspace(workspace, cx);
|
||||
|
||||
let mut threads: Vec<ThreadEntry> = Vec::new();
|
||||
|
||||
@@ -807,7 +800,13 @@ impl Sidebar {
|
||||
{
|
||||
let target_workspace =
|
||||
match absorbed_workspace_by_path.get(worktree_path.as_ref()) {
|
||||
Some(&idx) => ThreadEntryWorkspace::Open(workspaces[idx].clone()),
|
||||
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()),
|
||||
};
|
||||
|
||||
@@ -915,6 +914,16 @@ impl Sidebar {
|
||||
});
|
||||
}
|
||||
|
||||
// Compute running/waiting counts after live_infos has been
|
||||
// extended with any absorbed worktree workspaces.
|
||||
let has_running_threads = live_infos
|
||||
.iter()
|
||||
.any(|info| info.status == AgentThreadStatus::Running);
|
||||
let waiting_thread_count = live_infos
|
||||
.iter()
|
||||
.filter(|info| info.status == AgentThreadStatus::WaitingForConfirmation)
|
||||
.count();
|
||||
|
||||
if !query.is_empty() {
|
||||
let workspace_highlight_positions =
|
||||
fuzzy_match_positions(&query, &label).unwrap_or_default();
|
||||
@@ -5241,6 +5250,127 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_absorbed_worktree_running_thread_shows_live_status(cx: &mut TestAppContext) {
|
||||
// When a worktree workspace is absorbed under the main repo, a
|
||||
// running thread in the worktree's agent panel should still show
|
||||
// live status (spinner + "(running)") in the sidebar.
|
||||
agent_ui::test_support::init_test(cx);
|
||||
cx.update(|cx| {
|
||||
cx.update_flags(false, vec!["agent-v2".into()]);
|
||||
ThreadStore::init_global(cx);
|
||||
ThreadMetadataStore::init_global(cx);
|
||||
language_model::LanguageModelRegistry::test(cx);
|
||||
prompt_store::init(cx);
|
||||
});
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
// Main repo with a linked worktree.
|
||||
fs.insert_tree(
|
||||
"/project",
|
||||
serde_json::json!({
|
||||
".git": {
|
||||
"worktrees": {
|
||||
"feature-a": {
|
||||
"commondir": "../../",
|
||||
"HEAD": "ref: refs/heads/feature-a",
|
||||
},
|
||||
},
|
||||
},
|
||||
"src": {},
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Worktree checkout pointing back to the main repo.
|
||||
fs.insert_tree(
|
||||
"/wt-feature-a",
|
||||
serde_json::json!({
|
||||
".git": "gitdir: /project/.git/worktrees/feature-a",
|
||||
"src": {},
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
fs.with_git_state(std::path::Path::new("/project/.git"), false, |state| {
|
||||
state.worktrees.push(git::repository::Worktree {
|
||||
path: std::path::PathBuf::from("/wt-feature-a"),
|
||||
ref_name: "refs/heads/feature-a".into(),
|
||||
sha: "aaa".into(),
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
cx.update(|cx| <dyn fs::Fs>::set_global(fs.clone(), cx));
|
||||
|
||||
let main_project = project::Project::test(fs.clone(), ["/project".as_ref()], cx).await;
|
||||
let worktree_project =
|
||||
project::Project::test(fs.clone(), ["/wt-feature-a".as_ref()], cx).await;
|
||||
|
||||
main_project
|
||||
.update(cx, |p, cx| p.git_scans_complete(cx))
|
||||
.await;
|
||||
worktree_project
|
||||
.update(cx, |p, cx| p.git_scans_complete(cx))
|
||||
.await;
|
||||
|
||||
// Create the MultiWorkspace with both projects.
|
||||
let (multi_workspace, cx) = cx.add_window_view(|window, cx| {
|
||||
MultiWorkspace::test_new(main_project.clone(), window, cx)
|
||||
});
|
||||
|
||||
let worktree_workspace = multi_workspace.update_in(cx, |mw, window, cx| {
|
||||
mw.test_add_workspace(worktree_project.clone(), window, cx)
|
||||
});
|
||||
|
||||
// Add an agent panel to the worktree workspace so we can run a
|
||||
// thread inside it.
|
||||
let worktree_panel = add_agent_panel(&worktree_workspace, &worktree_project, cx);
|
||||
|
||||
// Switch back to the main workspace before setting up the sidebar.
|
||||
multi_workspace.update_in(cx, |mw, window, cx| {
|
||||
mw.activate_index(0, window, cx);
|
||||
});
|
||||
|
||||
let sidebar = setup_sidebar(&multi_workspace, cx);
|
||||
|
||||
// Start a thread in the worktree workspace's panel and keep it
|
||||
// generating (don't resolve it).
|
||||
let connection = StubAgentConnection::new();
|
||||
open_thread_with_connection(&worktree_panel, connection.clone(), cx);
|
||||
send_message(&worktree_panel, cx);
|
||||
|
||||
let session_id = active_session_id(&worktree_panel, cx);
|
||||
|
||||
// Save metadata so the sidebar knows about this thread.
|
||||
let wt_paths = PathList::new(&[std::path::PathBuf::from("/wt-feature-a")]);
|
||||
save_test_thread_metadata(&session_id, wt_paths, cx).await;
|
||||
|
||||
// Keep the thread generating by sending a chunk without ending
|
||||
// the turn.
|
||||
cx.update(|_, cx| {
|
||||
connection.send_update(
|
||||
session_id.clone(),
|
||||
acp::SessionUpdate::AgentMessageChunk(acp::ContentChunk::new("working...".into())),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
// The worktree thread should be absorbed under the main project
|
||||
// and show live running status.
|
||||
let entries = visible_entries_as_strings(&sidebar, cx);
|
||||
assert_eq!(
|
||||
entries,
|
||||
vec![
|
||||
"v [project]",
|
||||
" [+ New Thread]",
|
||||
" Hello {wt-feature-a} * (running)",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_clicking_worktree_thread_opens_workspace_when_none_exists(
|
||||
cx: &mut TestAppContext,
|
||||
|
||||
Reference in new issue
Block a user