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:
Antonio Scandurra 2025-05-04 10:28:39 +02:00 committed by GitHub
parent 425f32e068
commit 545ae27079
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 1255 additions and 567 deletions

View file

@ -1,16 +1,13 @@
use crate::{
DelayedDebouncedEditAction, FollowableViewRegistry, ItemNavHistory, SerializableItemRegistry,
ToolbarItemLocation, ViewId, Workspace, WorkspaceId,
CollaboratorId, DelayedDebouncedEditAction, FollowableViewRegistry, ItemNavHistory,
SerializableItemRegistry, ToolbarItemLocation, ViewId, Workspace, WorkspaceId,
pane::{self, Pane},
persistence::model::ItemId,
searchable::SearchableItemHandle,
workspace_settings::{AutosaveSetting, WorkspaceSettings},
};
use anyhow::Result;
use client::{
Client,
proto::{self, PeerId},
};
use client::{Client, proto};
use futures::{StreamExt, channel::mpsc};
use gpui::{
Action, AnyElement, AnyView, App, Context, Entity, EntityId, EventEmitter, FocusHandle,
@ -770,7 +767,7 @@ impl<T: Item> ItemHandle for Entity<T> {
proto::UpdateView {
id: item
.remote_id(workspace.client(), window, cx)
.map(|id| id.to_proto()),
.and_then(|id| id.to_proto()),
variant: pending_update.borrow_mut().take(),
leader_id,
},
@ -810,13 +807,27 @@ impl<T: Item> ItemHandle for Entity<T> {
}
if item.item_focus_handle(cx).contains_focused(window, cx) {
item.add_event_to_update_proto(
event,
&mut pending_update.borrow_mut(),
window,
cx,
);
pending_update_tx.unbounded_send(leader_id).ok();
match leader_id {
Some(CollaboratorId::Agent) => {}
Some(CollaboratorId::PeerId(leader_peer_id)) => {
item.add_event_to_update_proto(
event,
&mut pending_update.borrow_mut(),
window,
cx,
);
pending_update_tx.unbounded_send(Some(leader_peer_id)).ok();
}
None => {
item.add_event_to_update_proto(
event,
&mut pending_update.borrow_mut(),
window,
cx,
);
pending_update_tx.unbounded_send(None).ok();
}
}
}
}
@ -1081,7 +1092,7 @@ pub trait ProjectItem: Item {
fn for_project_item(
project: Entity<Project>,
pane: &Pane,
pane: Option<&Pane>,
item: Entity<Self::Item>,
window: &mut Window,
cx: &mut Context<Self>,
@ -1126,19 +1137,31 @@ pub trait FollowableItem: Item {
cx: &mut Context<Self>,
) -> Task<Result<()>>;
fn is_project_item(&self, window: &Window, cx: &App) -> bool;
fn set_leader_peer_id(
fn set_leader_id(
&mut self,
leader_peer_id: Option<PeerId>,
leader_peer_id: Option<CollaboratorId>,
window: &mut Window,
cx: &mut Context<Self>,
);
fn dedup(&self, existing: &Self, window: &Window, cx: &App) -> Option<Dedup>;
fn update_agent_location(
&mut self,
_location: language::Anchor,
_window: &mut Window,
_cx: &mut Context<Self>,
) {
}
}
pub trait FollowableItemHandle: ItemHandle {
fn remote_id(&self, client: &Arc<Client>, window: &mut Window, cx: &mut App) -> Option<ViewId>;
fn downgrade(&self) -> Box<dyn WeakFollowableItemHandle>;
fn set_leader_peer_id(&self, leader_peer_id: Option<PeerId>, window: &mut Window, cx: &mut App);
fn set_leader_id(
&self,
leader_peer_id: Option<CollaboratorId>,
window: &mut Window,
cx: &mut App,
);
fn to_state_proto(&self, window: &mut Window, cx: &mut App) -> Option<proto::view::Variant>;
fn add_event_to_update_proto(
&self,
@ -1162,13 +1185,14 @@ pub trait FollowableItemHandle: ItemHandle {
window: &mut Window,
cx: &mut App,
) -> Option<Dedup>;
fn update_agent_location(&self, location: language::Anchor, window: &mut Window, cx: &mut App);
}
impl<T: FollowableItem> FollowableItemHandle for Entity<T> {
fn remote_id(&self, client: &Arc<Client>, _: &mut Window, cx: &mut App) -> Option<ViewId> {
self.read(cx).remote_id().or_else(|| {
client.peer_id().map(|creator| ViewId {
creator,
creator: CollaboratorId::PeerId(creator),
id: self.item_id().as_u64(),
})
})
@ -1178,15 +1202,8 @@ impl<T: FollowableItem> FollowableItemHandle for Entity<T> {
Box::new(self.downgrade())
}
fn set_leader_peer_id(
&self,
leader_peer_id: Option<PeerId>,
window: &mut Window,
cx: &mut App,
) {
self.update(cx, |this, cx| {
this.set_leader_peer_id(leader_peer_id, window, cx)
})
fn set_leader_id(&self, leader_id: Option<CollaboratorId>, window: &mut Window, cx: &mut App) {
self.update(cx, |this, cx| this.set_leader_id(leader_id, window, cx))
}
fn to_state_proto(&self, window: &mut Window, cx: &mut App) -> Option<proto::view::Variant> {
@ -1237,6 +1254,12 @@ impl<T: FollowableItem> FollowableItemHandle for Entity<T> {
let existing = existing.to_any().downcast::<T>().ok()?;
self.read(cx).dedup(existing.read(cx), window, cx)
}
fn update_agent_location(&self, location: language::Anchor, window: &mut Window, cx: &mut App) {
self.update(cx, |this, cx| {
this.update_agent_location(location, window, cx)
})
}
}
pub trait WeakFollowableItemHandle: Send + Sync {