diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index eceaa8815967bb13e35006c840abdcd721c64c78..3f8bb2eab3412719f2efee6da08ee5f946b32d10 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -36,7 +36,7 @@ impl FollowableItem for Editor { }); Some(cx.spawn(|mut cx| async move { let buffer = buffer.await?; - Ok(pane + let editor = pane .read_with(&cx, |pane, cx| { pane.items_of_type::().find(|editor| { editor.read(cx).buffer.read(cx).as_singleton().as_ref() == Some(&buffer) @@ -44,25 +44,48 @@ impl FollowableItem for Editor { }) .unwrap_or_else(|| { cx.add_view(pane.window_id(), |cx| { - let mut editor = Editor::for_buffer(buffer, Some(project), cx); - let selections = { - let buffer = editor.buffer.read(cx); - let buffer = buffer.read(cx); - let (excerpt_id, buffer_id, _) = buffer.as_singleton().unwrap(); - state - .selections - .into_iter() - .filter_map(|selection| { - deserialize_selection(&excerpt_id, buffer_id, selection) - }) - .collect::>() - }; - if !selections.is_empty() { - editor.set_selections(selections.into(), None, false, cx); - } - editor + Editor::for_buffer(buffer, Some(project), cx) }) - })) + }); + editor.update(&mut cx, |editor, cx| { + let excerpt_id; + let buffer_id; + { + let buffer = editor.buffer.read(cx).read(cx); + let singleton = buffer.as_singleton().unwrap(); + excerpt_id = singleton.0.clone(); + buffer_id = singleton.1; + } + let selections = state + .selections + .into_iter() + .map(|selection| { + deserialize_selection(&excerpt_id, buffer_id, selection) + .ok_or_else(|| anyhow!("invalid selection")) + }) + .collect::>>()?; + if !selections.is_empty() { + editor.set_selections(selections.into(), None, false, cx); + } + + if let Some(anchor) = state.scroll_top { + editor.set_scroll_top_anchor( + Some(Anchor { + buffer_id: Some(state.buffer_id as usize), + excerpt_id: excerpt_id.clone(), + text_anchor: language::proto::deserialize_anchor(anchor) + .ok_or_else(|| anyhow!("invalid scroll top"))?, + }), + false, + cx, + ); + } else { + editor.set_scroll_top_anchor(None, false, cx); + } + + Ok::<_, anyhow::Error>(()) + })?; + Ok(editor) })) } diff --git a/crates/server/src/rpc.rs b/crates/server/src/rpc.rs index 047912c0ffd951890bfba11a7e37ed88525aead8..3f149390d35e91b751cda9eeab5c706e2ed11109 100644 --- a/crates/server/src/rpc.rs +++ b/crates/server/src/rpc.rs @@ -1116,7 +1116,7 @@ mod tests { }, time::Duration, }; - use workspace::{Settings, SplitDirection, Workspace, WorkspaceParams}; + use workspace::{Item, Settings, SplitDirection, Workspace, WorkspaceParams}; #[cfg(test)] #[ctor::ctor] @@ -4287,7 +4287,9 @@ mod tests { .downcast::() .unwrap(); - // Client B starts following client A. + // When client B starts following client A, all visible view states are replicated to client B. + editor_a1.update(cx_a, |editor, cx| editor.select_ranges([0..1], None, cx)); + editor_a2.update(cx_a, |editor, cx| editor.select_ranges([2..3], None, cx)); workspace_b .update(cx_b, |workspace, cx| { let leader_id = project_b @@ -4301,13 +4303,25 @@ mod tests { }) .await .unwrap(); - assert_eq!( - workspace_b.read_with(cx_b, |workspace, cx| workspace + let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| { + workspace .active_item(cx) .unwrap() - .project_path(cx)), + .downcast::() + .unwrap() + }); + assert_eq!( + editor_b2.read_with(cx_b, |editor, cx| editor.project_path(cx)), Some((worktree_id, "2.txt").into()) ); + assert_eq!( + editor_b2.read_with(cx_b, |editor, cx| editor.selected_ranges(cx)), + vec![2..3] + ); + assert_eq!( + editor_b1.read_with(cx_b, |editor, cx| editor.selected_ranges(cx)), + vec![0..1] + ); // When client A activates a different editor, client B does so as well. workspace_a.update(cx_a, |workspace, cx| {