Add the ability to follow the agent as it makes edits (#29839)
Nathan here: I also tacked on a bunch of UI refinement. Release Notes: - Introduced the ability to follow the agent around as it reads and edits files. --------- Co-authored-by: Nathan Sobo <nathan@zed.dev> Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
parent
425f32e068
commit
545ae27079
37 changed files with 1255 additions and 567 deletions
|
@ -13,6 +13,7 @@ use gpui::{BackgroundExecutor, Context, Entity, TestAppContext, Window};
|
|||
use rpc::{RECEIVE_TIMEOUT, proto::PeerId};
|
||||
use serde_json::json;
|
||||
use std::ops::Range;
|
||||
use workspace::CollaboratorId;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_core_channel_buffers(
|
||||
|
@ -300,13 +301,20 @@ fn assert_remote_selections(
|
|||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
let hub = editor.collaboration_hub().unwrap();
|
||||
let collaborators = hub.collaborators(cx);
|
||||
let range = Anchor::min()..Anchor::max();
|
||||
let remote_selections = snapshot
|
||||
.remote_selections_in_range(&range, editor.collaboration_hub().unwrap(), cx)
|
||||
.remote_selections_in_range(&range, hub, cx)
|
||||
.map(|s| {
|
||||
let CollaboratorId::PeerId(peer_id) = s.collaborator_id else {
|
||||
panic!("unexpected collaborator id");
|
||||
};
|
||||
let start = s.selection.start.to_offset(&snapshot.buffer_snapshot);
|
||||
let end = s.selection.end.to_offset(&snapshot.buffer_snapshot);
|
||||
(s.participant_index, start..end)
|
||||
let user_id = collaborators.get(&peer_id).unwrap().user_id;
|
||||
let participant_index = hub.user_participant_indices(cx).get(&user_id).copied();
|
||||
(participant_index, start..end)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
|
|
|
@ -18,7 +18,7 @@ use serde_json::json;
|
|||
use settings::SettingsStore;
|
||||
use text::{Point, ToPoint};
|
||||
use util::{path, test::sample_text};
|
||||
use workspace::{SplitDirection, Workspace, item::ItemHandle as _};
|
||||
use workspace::{CollaboratorId, SplitDirection, Workspace, item::ItemHandle as _};
|
||||
|
||||
use super::TestClient;
|
||||
|
||||
|
@ -425,7 +425,7 @@ async fn test_basic_following(
|
|||
executor.run_until_parked();
|
||||
assert_eq!(
|
||||
workspace_a.update(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
|
||||
Some(peer_id_b)
|
||||
Some(peer_id_b.into())
|
||||
);
|
||||
assert_eq!(
|
||||
workspace_a.update_in(cx_a, |workspace, _, cx| workspace
|
||||
|
@ -1267,7 +1267,7 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
|
|||
executor.run_until_parked();
|
||||
assert_eq!(
|
||||
workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
||||
Some(leader_id)
|
||||
Some(leader_id.into())
|
||||
);
|
||||
let editor_b2 = workspace_b.update(cx_b, |workspace, cx| {
|
||||
workspace
|
||||
|
@ -1292,7 +1292,7 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
|
|||
executor.run_until_parked();
|
||||
assert_eq!(
|
||||
workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
||||
Some(leader_id)
|
||||
Some(leader_id.into())
|
||||
);
|
||||
|
||||
// When client B edits, it automatically stops following client A.
|
||||
|
@ -1308,7 +1308,7 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
|
|||
executor.run_until_parked();
|
||||
assert_eq!(
|
||||
workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
||||
Some(leader_id)
|
||||
Some(leader_id.into())
|
||||
);
|
||||
|
||||
// When client B scrolls, it automatically stops following client A.
|
||||
|
@ -1326,7 +1326,7 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
|
|||
executor.run_until_parked();
|
||||
assert_eq!(
|
||||
workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
||||
Some(leader_id)
|
||||
Some(leader_id.into())
|
||||
);
|
||||
|
||||
// When client B activates a different pane, it continues following client A in the original pane.
|
||||
|
@ -1335,7 +1335,7 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
|
|||
});
|
||||
assert_eq!(
|
||||
workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
||||
Some(leader_id)
|
||||
Some(leader_id.into())
|
||||
);
|
||||
|
||||
workspace_b.update_in(cx_b, |workspace, window, cx| {
|
||||
|
@ -1343,7 +1343,7 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
|
|||
});
|
||||
assert_eq!(
|
||||
workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
||||
Some(leader_id)
|
||||
Some(leader_id.into())
|
||||
);
|
||||
|
||||
// When client B activates a different item in the original pane, it automatically stops following client A.
|
||||
|
@ -1406,13 +1406,13 @@ async fn test_peers_simultaneously_following_each_other(
|
|||
workspace_a.update(cx_a, |workspace, _| {
|
||||
assert_eq!(
|
||||
workspace.leader_for_pane(workspace.active_pane()),
|
||||
Some(client_b_id)
|
||||
Some(client_b_id.into())
|
||||
);
|
||||
});
|
||||
workspace_b.update(cx_b, |workspace, _| {
|
||||
assert_eq!(
|
||||
workspace.leader_for_pane(workspace.active_pane()),
|
||||
Some(client_a_id)
|
||||
Some(client_a_id.into())
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -1513,7 +1513,7 @@ async fn test_following_across_workspaces(cx_a: &mut TestAppContext, cx_b: &mut
|
|||
workspace_b_project_a.update(&mut cx_b2, |workspace, cx| {
|
||||
assert!(workspace.is_being_followed(client_a.peer_id().unwrap()));
|
||||
assert_eq!(
|
||||
client_a.peer_id(),
|
||||
client_a.peer_id().map(Into::into),
|
||||
workspace.leader_for_pane(workspace.active_pane())
|
||||
);
|
||||
let item = workspace.active_item(cx).unwrap();
|
||||
|
@ -1554,7 +1554,7 @@ async fn test_following_across_workspaces(cx_a: &mut TestAppContext, cx_b: &mut
|
|||
workspace_a.update(cx_a, |workspace, cx| {
|
||||
assert!(workspace.is_being_followed(client_b.peer_id().unwrap()));
|
||||
assert_eq!(
|
||||
client_b.peer_id(),
|
||||
client_b.peer_id().map(Into::into),
|
||||
workspace.leader_for_pane(workspace.active_pane())
|
||||
);
|
||||
let item = workspace.active_pane().read(cx).active_item().unwrap();
|
||||
|
@ -1615,7 +1615,7 @@ async fn test_following_across_workspaces(cx_a: &mut TestAppContext, cx_b: &mut
|
|||
assert_eq!(workspace.project().read(cx).remote_id(), Some(project_b_id));
|
||||
assert!(workspace.is_being_followed(client_b.peer_id().unwrap()));
|
||||
assert_eq!(
|
||||
client_b.peer_id(),
|
||||
client_b.peer_id().map(Into::into),
|
||||
workspace.leader_for_pane(workspace.active_pane())
|
||||
);
|
||||
let item = workspace.active_item(cx).unwrap();
|
||||
|
@ -1866,7 +1866,11 @@ fn pane_summaries(workspace: &Entity<Workspace>, cx: &mut VisualTestContext) ->
|
|||
.panes()
|
||||
.iter()
|
||||
.map(|pane| {
|
||||
let leader = workspace.leader_for_pane(pane);
|
||||
let leader = match workspace.leader_for_pane(pane) {
|
||||
Some(CollaboratorId::PeerId(peer_id)) => Some(peer_id),
|
||||
Some(CollaboratorId::Agent) => unimplemented!(),
|
||||
None => None,
|
||||
};
|
||||
let active = pane == active_pane;
|
||||
let pane = pane.read(cx);
|
||||
let active_ix = pane.active_item_index();
|
||||
|
@ -1985,7 +1989,7 @@ async fn test_following_to_channel_notes_without_a_shared_project(
|
|||
let channel_notes_1_b = workspace_b.update(cx_b, |workspace, cx| {
|
||||
assert_eq!(
|
||||
workspace.leader_for_pane(workspace.active_pane()),
|
||||
Some(client_a.peer_id().unwrap())
|
||||
Some(client_a.peer_id().unwrap().into())
|
||||
);
|
||||
workspace
|
||||
.active_item(cx)
|
||||
|
@ -2015,7 +2019,7 @@ async fn test_following_to_channel_notes_without_a_shared_project(
|
|||
let channel_notes_2_b = workspace_b.update(cx_b, |workspace, cx| {
|
||||
assert_eq!(
|
||||
workspace.leader_for_pane(workspace.active_pane()),
|
||||
Some(client_a.peer_id().unwrap())
|
||||
Some(client_a.peer_id().unwrap().into())
|
||||
);
|
||||
workspace
|
||||
.active_item(cx)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue