mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-26 10:40:54 +00:00
Serialize initial follow state in leader and reflect it in follower
This commit is contained in:
parent
0fdaa1d715
commit
f0b7bd6e17
10 changed files with 246 additions and 63 deletions
|
@ -494,14 +494,13 @@ impl Client {
|
||||||
message_type_id,
|
message_type_id,
|
||||||
Arc::new(move |handle, envelope, cx| {
|
Arc::new(move |handle, envelope, cx| {
|
||||||
if let Some(client) = client.upgrade() {
|
if let Some(client) = client.upgrade() {
|
||||||
let model = handle.downcast::<E>().unwrap();
|
|
||||||
let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
|
|
||||||
let handle = if let AnyEntityHandle::Model(handle) = handle {
|
let handle = if let AnyEntityHandle::Model(handle) = handle {
|
||||||
handle
|
handle
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
|
let model = handle.downcast::<E>().unwrap();
|
||||||
|
let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
|
||||||
handler(model, *envelope, client.clone(), cx).boxed_local()
|
handler(model, *envelope, client.clone(), cx).boxed_local()
|
||||||
} else {
|
} else {
|
||||||
async move { Ok(()) }.boxed_local()
|
async move { Ok(()) }.boxed_local()
|
||||||
|
@ -513,7 +512,7 @@ impl Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_entity_request_handler<M, E, H, F>(self: &Arc<Self>, handler: H)
|
pub fn add_model_request_handler<M, E, H, F>(self: &Arc<Self>, handler: H)
|
||||||
where
|
where
|
||||||
M: EntityMessage + RequestMessage,
|
M: EntityMessage + RequestMessage,
|
||||||
E: Entity,
|
E: Entity,
|
||||||
|
@ -546,6 +545,39 @@ impl Client {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_view_request_handler<M, E, H, F>(self: &Arc<Self>, handler: H)
|
||||||
|
where
|
||||||
|
M: EntityMessage + RequestMessage,
|
||||||
|
E: View,
|
||||||
|
H: 'static
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ Fn(ViewHandle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
|
||||||
|
F: 'static + Future<Output = Result<M::Response>>,
|
||||||
|
{
|
||||||
|
self.add_view_message_handler(move |view, envelope, client, cx| {
|
||||||
|
let receipt = envelope.receipt();
|
||||||
|
let response = handler(view, envelope, client.clone(), cx);
|
||||||
|
async move {
|
||||||
|
match response.await {
|
||||||
|
Ok(response) => {
|
||||||
|
client.respond(receipt, response)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
client.respond_with_error(
|
||||||
|
receipt,
|
||||||
|
proto::Error {
|
||||||
|
message: error.to_string(),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
Err(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn has_keychain_credentials(&self, cx: &AsyncAppContext) -> bool {
|
pub fn has_keychain_credentials(&self, cx: &AsyncAppContext) -> bool {
|
||||||
read_credentials_from_keychain(cx).is_some()
|
read_credentials_from_keychain(cx).is_some()
|
||||||
}
|
}
|
||||||
|
|
|
@ -341,6 +341,7 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||||
cx.add_async_action(Editor::find_all_references);
|
cx.add_async_action(Editor::find_all_references);
|
||||||
|
|
||||||
workspace::register_project_item::<Editor>(cx);
|
workspace::register_project_item::<Editor>(cx);
|
||||||
|
workspace::register_followed_item::<Editor>(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
trait SelectionExt {
|
trait SelectionExt {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::{Autoscroll, Editor, Event, NavigationData, ToOffset, ToPoint as _};
|
use crate::{Autoscroll, Editor, Event, NavigationData, ToOffset, ToPoint as _};
|
||||||
use anyhow::Result;
|
use anyhow::{anyhow, Result};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::*, AppContext, Entity, ModelHandle, RenderContext, Subscription, Task, View,
|
elements::*, AppContext, Entity, ModelHandle, MutableAppContext, RenderContext, Subscription,
|
||||||
ViewContext, ViewHandle,
|
Task, View, ViewContext, ViewHandle,
|
||||||
};
|
};
|
||||||
use language::{Bias, Buffer, Diagnostic, File as _};
|
use language::{Bias, Buffer, Diagnostic, File as _};
|
||||||
use project::{File, Project, ProjectEntryId, ProjectPath};
|
use project::{File, Project, ProjectEntryId, ProjectPath};
|
||||||
|
@ -19,13 +19,58 @@ impl FollowedItem for Editor {
|
||||||
pane: ViewHandle<workspace::Pane>,
|
pane: ViewHandle<workspace::Pane>,
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
state: &mut Option<proto::view::Variant>,
|
state: &mut Option<proto::view::Variant>,
|
||||||
cx: &mut gpui::MutableAppContext,
|
cx: &mut MutableAppContext,
|
||||||
) -> Option<Task<Result<Box<dyn ItemHandle>>>> {
|
) -> Option<Task<Result<Box<dyn ItemHandle>>>> {
|
||||||
todo!()
|
let state = if matches!(state, Some(proto::view::Variant::Editor(_))) {
|
||||||
|
if let Some(proto::view::Variant::Editor(state)) = state.take() {
|
||||||
|
state
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let buffer = project.update(cx, |project, cx| {
|
||||||
|
project.open_buffer_by_id(state.buffer_id, cx)
|
||||||
|
});
|
||||||
|
Some(cx.spawn(|mut cx| async move {
|
||||||
|
let buffer = buffer.await?;
|
||||||
|
let editor = pane
|
||||||
|
.read_with(&cx, |pane, cx| {
|
||||||
|
pane.items_of_type::<Self>().find(|editor| {
|
||||||
|
editor.read(cx).buffer.read(cx).as_singleton().as_ref() == Some(&buffer)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
cx.add_view(pane.window_id(), |cx| {
|
||||||
|
Editor::for_buffer(buffer, Some(project), cx)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
Ok(Box::new(editor) as Box<_>)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_state_message(&self, cx: &mut gpui::MutableAppContext) -> proto::view::Variant {
|
fn to_state_message(&self, cx: &AppContext) -> proto::view::Variant {
|
||||||
todo!()
|
let buffer_id = self
|
||||||
|
.buffer
|
||||||
|
.read(cx)
|
||||||
|
.as_singleton()
|
||||||
|
.unwrap()
|
||||||
|
.read(cx)
|
||||||
|
.remote_id();
|
||||||
|
let selection = self.newest_anchor_selection();
|
||||||
|
let selection = Selection {
|
||||||
|
id: selection.id,
|
||||||
|
start: selection.start.text_anchor.clone(),
|
||||||
|
end: selection.end.text_anchor.clone(),
|
||||||
|
reversed: selection.reversed,
|
||||||
|
goal: Default::default(),
|
||||||
|
};
|
||||||
|
proto::view::Variant::Editor(proto::view::Editor {
|
||||||
|
buffer_id,
|
||||||
|
newest_selection: Some(language::proto::serialize_selection(&selection)),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,15 +100,16 @@ pub fn serialize_undo_map_entry(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serialize_selections(selections: &Arc<[Selection<Anchor>]>) -> Vec<proto::Selection> {
|
pub fn serialize_selections(selections: &Arc<[Selection<Anchor>]>) -> Vec<proto::Selection> {
|
||||||
selections
|
selections.iter().map(serialize_selection).collect()
|
||||||
.iter()
|
}
|
||||||
.map(|selection| proto::Selection {
|
|
||||||
id: selection.id as u64,
|
pub fn serialize_selection(selection: &Selection<Anchor>) -> proto::Selection {
|
||||||
start: Some(serialize_anchor(&selection.start)),
|
proto::Selection {
|
||||||
end: Some(serialize_anchor(&selection.end)),
|
id: selection.id as u64,
|
||||||
reversed: selection.reversed,
|
start: Some(serialize_anchor(&selection.start)),
|
||||||
})
|
end: Some(serialize_anchor(&selection.end)),
|
||||||
.collect()
|
reversed: selection.reversed,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serialize_diagnostics<'a>(
|
pub fn serialize_diagnostics<'a>(
|
||||||
|
|
|
@ -267,21 +267,22 @@ impl Project {
|
||||||
client.add_model_message_handler(Self::handle_update_buffer);
|
client.add_model_message_handler(Self::handle_update_buffer);
|
||||||
client.add_model_message_handler(Self::handle_update_diagnostic_summary);
|
client.add_model_message_handler(Self::handle_update_diagnostic_summary);
|
||||||
client.add_model_message_handler(Self::handle_update_worktree);
|
client.add_model_message_handler(Self::handle_update_worktree);
|
||||||
client.add_entity_request_handler(Self::handle_apply_additional_edits_for_completion);
|
client.add_model_request_handler(Self::handle_apply_additional_edits_for_completion);
|
||||||
client.add_entity_request_handler(Self::handle_apply_code_action);
|
client.add_model_request_handler(Self::handle_apply_code_action);
|
||||||
client.add_entity_request_handler(Self::handle_format_buffers);
|
client.add_model_request_handler(Self::handle_format_buffers);
|
||||||
client.add_entity_request_handler(Self::handle_get_code_actions);
|
client.add_model_request_handler(Self::handle_get_code_actions);
|
||||||
client.add_entity_request_handler(Self::handle_get_completions);
|
client.add_model_request_handler(Self::handle_get_completions);
|
||||||
client.add_entity_request_handler(Self::handle_lsp_command::<GetDefinition>);
|
client.add_model_request_handler(Self::handle_lsp_command::<GetDefinition>);
|
||||||
client.add_entity_request_handler(Self::handle_lsp_command::<GetDocumentHighlights>);
|
client.add_model_request_handler(Self::handle_lsp_command::<GetDocumentHighlights>);
|
||||||
client.add_entity_request_handler(Self::handle_lsp_command::<GetReferences>);
|
client.add_model_request_handler(Self::handle_lsp_command::<GetReferences>);
|
||||||
client.add_entity_request_handler(Self::handle_lsp_command::<PrepareRename>);
|
client.add_model_request_handler(Self::handle_lsp_command::<PrepareRename>);
|
||||||
client.add_entity_request_handler(Self::handle_lsp_command::<PerformRename>);
|
client.add_model_request_handler(Self::handle_lsp_command::<PerformRename>);
|
||||||
client.add_entity_request_handler(Self::handle_search_project);
|
client.add_model_request_handler(Self::handle_search_project);
|
||||||
client.add_entity_request_handler(Self::handle_get_project_symbols);
|
client.add_model_request_handler(Self::handle_get_project_symbols);
|
||||||
client.add_entity_request_handler(Self::handle_open_buffer_for_symbol);
|
client.add_model_request_handler(Self::handle_open_buffer_for_symbol);
|
||||||
client.add_entity_request_handler(Self::handle_open_buffer_by_path);
|
client.add_model_request_handler(Self::handle_open_buffer_by_id);
|
||||||
client.add_entity_request_handler(Self::handle_save_buffer);
|
client.add_model_request_handler(Self::handle_open_buffer_by_path);
|
||||||
|
client.add_model_request_handler(Self::handle_save_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local(
|
pub fn local(
|
||||||
|
@ -488,7 +489,6 @@ impl Project {
|
||||||
cx.update(|cx| Project::local(client, user_store, languages, fs, cx))
|
cx.update(|cx| Project::local(client, user_store, languages, fs, cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
|
||||||
pub fn buffer_for_id(&self, remote_id: u64, cx: &AppContext) -> Option<ModelHandle<Buffer>> {
|
pub fn buffer_for_id(&self, remote_id: u64, cx: &AppContext) -> Option<ModelHandle<Buffer>> {
|
||||||
self.opened_buffers
|
self.opened_buffers
|
||||||
.get(&remote_id)
|
.get(&remote_id)
|
||||||
|
@ -981,6 +981,32 @@ impl Project {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn open_buffer_by_id(
|
||||||
|
&mut self,
|
||||||
|
id: u64,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) -> Task<Result<ModelHandle<Buffer>>> {
|
||||||
|
if let Some(buffer) = self.buffer_for_id(id, cx) {
|
||||||
|
Task::ready(Ok(buffer))
|
||||||
|
} else if self.is_local() {
|
||||||
|
Task::ready(Err(anyhow!("buffer {} does not exist", id)))
|
||||||
|
} else if let Some(project_id) = self.remote_id() {
|
||||||
|
let request = self
|
||||||
|
.client
|
||||||
|
.request(proto::OpenBufferById { project_id, id });
|
||||||
|
cx.spawn(|this, mut cx| async move {
|
||||||
|
let buffer = request
|
||||||
|
.await?
|
||||||
|
.buffer
|
||||||
|
.ok_or_else(|| anyhow!("invalid buffer"))?;
|
||||||
|
this.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Task::ready(Err(anyhow!("cannot open buffer while disconnected")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn save_buffer_as(
|
pub fn save_buffer_as(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: ModelHandle<Buffer>,
|
buffer: ModelHandle<Buffer>,
|
||||||
|
@ -3889,6 +3915,25 @@ impl Project {
|
||||||
hasher.finalize().as_slice().try_into().unwrap()
|
hasher.finalize().as_slice().try_into().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_open_buffer_by_id(
|
||||||
|
this: ModelHandle<Self>,
|
||||||
|
envelope: TypedEnvelope<proto::OpenBufferById>,
|
||||||
|
_: Arc<Client>,
|
||||||
|
mut cx: AsyncAppContext,
|
||||||
|
) -> Result<proto::OpenBufferResponse> {
|
||||||
|
let peer_id = envelope.original_sender_id()?;
|
||||||
|
let buffer = this
|
||||||
|
.update(&mut cx, |this, cx| {
|
||||||
|
this.open_buffer_by_id(envelope.payload.id, cx)
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
this.update(&mut cx, |this, cx| {
|
||||||
|
Ok(proto::OpenBufferResponse {
|
||||||
|
buffer: Some(this.serialize_buffer_for_peer(&buffer, peer_id, cx)),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_open_buffer_by_path(
|
async fn handle_open_buffer_by_path(
|
||||||
this: ModelHandle<Self>,
|
this: ModelHandle<Self>,
|
||||||
envelope: TypedEnvelope<proto::OpenBufferByPath>,
|
envelope: TypedEnvelope<proto::OpenBufferByPath>,
|
||||||
|
|
|
@ -544,7 +544,7 @@ message Follow {
|
||||||
}
|
}
|
||||||
|
|
||||||
message FollowResponse {
|
message FollowResponse {
|
||||||
uint64 current_view_id = 1;
|
optional uint64 current_view_id = 1;
|
||||||
repeated View views = 2;
|
repeated View views = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,7 @@ impl Server {
|
||||||
.add_request_handler(Server::forward_project_request::<proto::GetDocumentHighlights>)
|
.add_request_handler(Server::forward_project_request::<proto::GetDocumentHighlights>)
|
||||||
.add_request_handler(Server::forward_project_request::<proto::GetProjectSymbols>)
|
.add_request_handler(Server::forward_project_request::<proto::GetProjectSymbols>)
|
||||||
.add_request_handler(Server::forward_project_request::<proto::OpenBufferForSymbol>)
|
.add_request_handler(Server::forward_project_request::<proto::OpenBufferForSymbol>)
|
||||||
|
.add_request_handler(Server::forward_project_request::<proto::OpenBufferById>)
|
||||||
.add_request_handler(Server::forward_project_request::<proto::OpenBufferByPath>)
|
.add_request_handler(Server::forward_project_request::<proto::OpenBufferByPath>)
|
||||||
.add_request_handler(Server::forward_project_request::<proto::GetCompletions>)
|
.add_request_handler(Server::forward_project_request::<proto::GetCompletions>)
|
||||||
.add_request_handler(
|
.add_request_handler(
|
||||||
|
@ -4240,6 +4241,13 @@ mod tests {
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
workspace_b.read_with(cx_b, |workspace, cx| workspace
|
||||||
|
.active_item(cx)
|
||||||
|
.unwrap()
|
||||||
|
.project_path(cx)),
|
||||||
|
Some((worktree_id, "2.txt").into())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test(iterations = 100)]
|
#[gpui::test(iterations = 100)]
|
||||||
|
|
|
@ -18,6 +18,12 @@ pub struct Selection<T> {
|
||||||
pub goal: SelectionGoal,
|
pub goal: SelectionGoal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for SelectionGoal {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Clone> Selection<T> {
|
impl<T: Clone> Selection<T> {
|
||||||
pub fn head(&self) -> T {
|
pub fn head(&self) -> T {
|
||||||
if self.reversed {
|
if self.reversed {
|
||||||
|
|
|
@ -109,7 +109,7 @@ pub struct Pane {
|
||||||
|
|
||||||
pub(crate) struct FollowerState {
|
pub(crate) struct FollowerState {
|
||||||
pub(crate) leader_id: PeerId,
|
pub(crate) leader_id: PeerId,
|
||||||
pub(crate) current_view_id: usize,
|
pub(crate) current_view_id: Option<usize>,
|
||||||
pub(crate) items_by_leader_view_id: HashMap<usize, Box<dyn ItemHandle>>,
|
pub(crate) items_by_leader_view_id: HashMap<usize, Box<dyn ItemHandle>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,6 +308,11 @@ impl Pane {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_item(&mut self, mut item: Box<dyn ItemHandle>, cx: &mut ViewContext<Self>) {
|
pub(crate) fn add_item(&mut self, mut item: Box<dyn ItemHandle>, cx: &mut ViewContext<Self>) {
|
||||||
|
// Prevent adding the same item to the pane more than once.
|
||||||
|
if self.items.iter().any(|i| i.id() == item.id()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
item.set_nav_history(self.nav_history.clone(), cx);
|
item.set_nav_history(self.nav_history.clone(), cx);
|
||||||
item.added_to_pane(cx);
|
item.added_to_pane(cx);
|
||||||
let item_idx = cmp::min(self.active_item_index + 1, self.items.len());
|
let item_idx = cmp::min(self.active_item_index + 1, self.items.len());
|
||||||
|
@ -321,13 +326,14 @@ impl Pane {
|
||||||
follower_state: FollowerState,
|
follower_state: FollowerState,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let current_view_id = follower_state.current_view_id as usize;
|
if let Some(current_view_id) = follower_state.current_view_id {
|
||||||
let item = follower_state
|
let item = follower_state
|
||||||
.items_by_leader_view_id
|
.items_by_leader_view_id
|
||||||
.get(¤t_view_id)
|
.get(¤t_view_id)
|
||||||
.ok_or_else(|| anyhow!("invalid current view id"))?
|
.ok_or_else(|| anyhow!("invalid current view id"))?
|
||||||
.clone();
|
.clone();
|
||||||
self.add_item(item, cx);
|
self.add_item(item, cx);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,6 +341,12 @@ impl Pane {
|
||||||
self.items.iter()
|
self.items.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn items_of_type<'a, T: View>(&'a self) -> impl 'a + Iterator<Item = ViewHandle<T>> {
|
||||||
|
self.items
|
||||||
|
.iter()
|
||||||
|
.filter_map(|item| item.to_any().downcast())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn active_item(&self) -> Option<Box<dyn ItemHandle>> {
|
pub fn active_item(&self) -> Option<Box<dyn ItemHandle>> {
|
||||||
self.items.get(self.active_item_index).cloned()
|
self.items.get(self.active_item_index).cloned()
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,8 +111,8 @@ pub fn init(client: &Arc<Client>, cx: &mut MutableAppContext) {
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
client.add_entity_request_handler(Workspace::handle_follow);
|
client.add_view_request_handler(Workspace::handle_follow);
|
||||||
client.add_model_message_handler(Workspace::handle_unfollow);
|
client.add_view_message_handler(Workspace::handle_unfollow);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_project_item<I: ProjectItem>(cx: &mut MutableAppContext) {
|
pub fn register_project_item<I: ProjectItem>(cx: &mut MutableAppContext) {
|
||||||
|
@ -235,7 +235,7 @@ pub trait FollowedItem {
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
|
||||||
fn to_state_message(&self, cx: &mut MutableAppContext) -> proto::view::Variant;
|
fn to_state_message(&self, cx: &AppContext) -> proto::view::Variant;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ItemHandle: 'static {
|
pub trait ItemHandle: 'static {
|
||||||
|
@ -262,6 +262,8 @@ pub trait ItemHandle: 'static {
|
||||||
cx: &mut MutableAppContext,
|
cx: &mut MutableAppContext,
|
||||||
) -> Task<Result<()>>;
|
) -> Task<Result<()>>;
|
||||||
fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option<AnyViewHandle>;
|
fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option<AnyViewHandle>;
|
||||||
|
fn can_be_followed(&self, cx: &AppContext) -> bool;
|
||||||
|
fn to_state_message(&self, cx: &AppContext) -> Option<proto::view::Variant>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait WeakItemHandle {
|
pub trait WeakItemHandle {
|
||||||
|
@ -297,11 +299,7 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
|
||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone_on_split(
|
fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option<Box<dyn ItemHandle>> {
|
||||||
&self,
|
|
||||||
// nav_history: Rc<RefCell<NavHistory>>,
|
|
||||||
cx: &mut MutableAppContext,
|
|
||||||
) -> Option<Box<dyn ItemHandle>> {
|
|
||||||
self.update(cx, |item, cx| {
|
self.update(cx, |item, cx| {
|
||||||
cx.add_option_view(|cx| item.clone_on_split(cx))
|
cx.add_option_view(|cx| item.clone_on_split(cx))
|
||||||
})
|
})
|
||||||
|
@ -381,6 +379,16 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
|
||||||
fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option<AnyViewHandle> {
|
fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option<AnyViewHandle> {
|
||||||
self.read(cx).act_as_type(type_id, self, cx)
|
self.read(cx).act_as_type(type_id, self, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn can_be_followed(&self, cx: &AppContext) -> bool {
|
||||||
|
self.read(cx).as_followed().is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_state_message(&self, cx: &AppContext) -> Option<proto::view::Variant> {
|
||||||
|
self.read(cx)
|
||||||
|
.as_followed()
|
||||||
|
.map(|item| item.to_state_message(cx))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<AnyViewHandle> for Box<dyn ItemHandle> {
|
impl Into<AnyViewHandle> for Box<dyn ItemHandle> {
|
||||||
|
@ -709,6 +717,13 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn items<'a>(
|
||||||
|
&'a self,
|
||||||
|
cx: &'a AppContext,
|
||||||
|
) -> impl 'a + Iterator<Item = &Box<dyn ItemHandle>> {
|
||||||
|
self.panes.iter().flat_map(|pane| pane.read(cx).items())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn item_of_type<T: Item>(&self, cx: &AppContext) -> Option<ViewHandle<T>> {
|
pub fn item_of_type<T: Item>(&self, cx: &AppContext) -> Option<ViewHandle<T>> {
|
||||||
self.items_of_type(cx).max_by_key(|item| item.id())
|
self.items_of_type(cx).max_by_key(|item| item.id())
|
||||||
}
|
}
|
||||||
|
@ -717,11 +732,9 @@ impl Workspace {
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a AppContext,
|
cx: &'a AppContext,
|
||||||
) -> impl 'a + Iterator<Item = ViewHandle<T>> {
|
) -> impl 'a + Iterator<Item = ViewHandle<T>> {
|
||||||
self.panes.iter().flat_map(|pane| {
|
self.panes
|
||||||
pane.read(cx)
|
.iter()
|
||||||
.items()
|
.flat_map(|pane| pane.read(cx).items_of_type())
|
||||||
.filter_map(|item| item.to_any().downcast())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn active_item(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
|
pub fn active_item(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
|
||||||
|
@ -1085,7 +1098,7 @@ impl Workspace {
|
||||||
pane.set_follow_state(
|
pane.set_follow_state(
|
||||||
FollowerState {
|
FollowerState {
|
||||||
leader_id,
|
leader_id,
|
||||||
current_view_id: response.current_view_id as usize,
|
current_view_id: response.current_view_id.map(|id| id as usize),
|
||||||
items_by_leader_view_id,
|
items_by_leader_view_id,
|
||||||
},
|
},
|
||||||
cx,
|
cx,
|
||||||
|
@ -1310,13 +1323,33 @@ impl Workspace {
|
||||||
|
|
||||||
async fn handle_follow(
|
async fn handle_follow(
|
||||||
this: ViewHandle<Self>,
|
this: ViewHandle<Self>,
|
||||||
envelope: TypedEnvelope<proto::Follow>,
|
_: TypedEnvelope<proto::Follow>,
|
||||||
_: Arc<Client>,
|
_: Arc<Client>,
|
||||||
cx: AsyncAppContext,
|
cx: AsyncAppContext,
|
||||||
) -> Result<proto::FollowResponse> {
|
) -> Result<proto::FollowResponse> {
|
||||||
Ok(proto::FollowResponse {
|
this.read_with(&cx, |this, cx| {
|
||||||
current_view_id: 0,
|
let current_view_id = if let Some(active_item) = this.active_item(cx) {
|
||||||
views: Default::default(),
|
if active_item.can_be_followed(cx) {
|
||||||
|
Some(active_item.id() as u64)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Ok(proto::FollowResponse {
|
||||||
|
current_view_id,
|
||||||
|
views: this
|
||||||
|
.items(cx)
|
||||||
|
.filter_map(|item| {
|
||||||
|
let variant = item.to_state_message(cx)?;
|
||||||
|
Some(proto::View {
|
||||||
|
id: item.id() as u64,
|
||||||
|
variant: Some(variant),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue