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

@ -56,7 +56,7 @@ use anyhow::{Context as _, Result, anyhow};
use blink_manager::BlinkManager;
use buffer_diff::DiffHunkStatus;
use client::{Collaborator, ParticipantIndex};
use clock::ReplicaId;
use clock::{AGENT_REPLICA_ID, ReplicaId};
use collections::{BTreeMap, HashMap, HashSet, VecDeque};
use convert_case::{Case, Casing};
use display_map::*;
@ -201,7 +201,7 @@ use ui::{
};
use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
use workspace::{
Item as WorkspaceItem, ItemId, ItemNavHistory, OpenInTerminal, OpenTerminal,
CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, OpenInTerminal, OpenTerminal,
RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection, TabBarSettings, Toast,
ViewId, Workspace, WorkspaceId, WorkspaceSettings,
item::{ItemHandle, PreviewTabsSettings},
@ -914,7 +914,7 @@ pub struct Editor {
input_enabled: bool,
use_modal_editing: bool,
read_only: bool,
leader_peer_id: Option<PeerId>,
leader_id: Option<CollaboratorId>,
remote_id: Option<ViewId>,
pub hover_state: HoverState,
pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
@ -1059,10 +1059,10 @@ pub struct RemoteSelection {
pub replica_id: ReplicaId,
pub selection: Selection<Anchor>,
pub cursor_shape: CursorShape,
pub peer_id: PeerId,
pub collaborator_id: CollaboratorId,
pub line_mode: bool,
pub participant_index: Option<ParticipantIndex>,
pub user_name: Option<SharedString>,
pub color: PlayerColor,
}
#[derive(Clone, Debug)]
@ -1723,7 +1723,7 @@ impl Editor {
use_auto_surround: true,
auto_replace_emoji_shortcode: false,
jsx_tag_auto_close_enabled_in_any_buffer: false,
leader_peer_id: None,
leader_id: None,
remote_id: None,
hover_state: Default::default(),
pending_mouse_down: None,
@ -2175,8 +2175,8 @@ impl Editor {
});
}
pub fn leader_peer_id(&self) -> Option<PeerId> {
self.leader_peer_id
pub fn leader_id(&self) -> Option<CollaboratorId> {
self.leader_id
}
pub fn buffer(&self) -> &Entity<MultiBuffer> {
@ -2517,7 +2517,7 @@ impl Editor {
}
}
if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
self.buffer.update(cx, |buffer, cx| {
buffer.set_active_selections(
&self.selections.disjoint_anchors(),
@ -18490,7 +18490,7 @@ impl Editor {
self.show_cursor_names(window, cx);
self.buffer.update(cx, |buffer, cx| {
buffer.finalize_last_transaction(cx);
if self.leader_peer_id.is_none() {
if self.leader_id.is_none() {
buffer.set_active_selections(
&self.selections.disjoint_anchors(),
self.selections.line_mode,
@ -19928,18 +19928,34 @@ impl EditorSnapshot {
self.buffer_snapshot
.selections_in_range(range, false)
.filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
let collaborator = collaborators_by_replica_id.get(&replica_id)?;
let participant_index = participant_indices.get(&collaborator.user_id).copied();
let user_name = participant_names.get(&collaborator.user_id).cloned();
Some(RemoteSelection {
replica_id,
selection,
cursor_shape,
line_mode,
participant_index,
peer_id: collaborator.peer_id,
user_name,
})
if replica_id == AGENT_REPLICA_ID {
Some(RemoteSelection {
replica_id,
selection,
cursor_shape,
line_mode,
collaborator_id: CollaboratorId::Agent,
user_name: Some("Agent".into()),
color: cx.theme().players().agent(),
})
} else {
let collaborator = collaborators_by_replica_id.get(&replica_id)?;
let participant_index = participant_indices.get(&collaborator.user_id).copied();
let user_name = participant_names.get(&collaborator.user_id).cloned();
Some(RemoteSelection {
replica_id,
selection,
cursor_shape,
line_mode,
collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
user_name,
color: if let Some(index) = participant_index {
cx.theme().players().color_for_participant(index.0)
} else {
cx.theme().players().absent()
},
})
}
})
}