Set up logic for starting following
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
This commit is contained in:
parent
2b4738d82d
commit
9716ff7964
4 changed files with 142 additions and 28 deletions
|
@ -83,7 +83,8 @@ message Envelope {
|
||||||
|
|
||||||
Follow follow = 72;
|
Follow follow = 72;
|
||||||
FollowResponse follow_response = 73;
|
FollowResponse follow_response = 73;
|
||||||
UpdateFollower update_follower = 74;
|
UpdateFollowers update_followers = 74;
|
||||||
|
Unfollow unfollow = 75;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,16 +538,27 @@ message UpdateDiagnostics {
|
||||||
repeated Diagnostic diagnostics = 3;
|
repeated Diagnostic diagnostics = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Follow {}
|
message Follow {
|
||||||
|
uint64 project_id = 1;
|
||||||
|
uint32 leader_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message FollowResponse {
|
message FollowResponse {
|
||||||
uint64 current_view_id = 1;
|
uint64 current_view_id = 1;
|
||||||
repeated View views = 2;
|
repeated View views = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UpdateFollower {
|
message UpdateFollowers {
|
||||||
uint64 current_view_id = 1;
|
uint64 project_id = 1;
|
||||||
repeated ViewUpdate view_updates = 2;
|
uint64 current_view_id = 2;
|
||||||
|
repeated View created_views = 3;
|
||||||
|
repeated ViewUpdate updated_views = 4;
|
||||||
|
repeated uint32 follower_ids = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Unfollow {
|
||||||
|
uint64 project_id = 1;
|
||||||
|
uint32 leader_id = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entities
|
// Entities
|
||||||
|
|
|
@ -147,6 +147,8 @@ messages!(
|
||||||
(BufferSaved, Foreground),
|
(BufferSaved, Foreground),
|
||||||
(ChannelMessageSent, Foreground),
|
(ChannelMessageSent, Foreground),
|
||||||
(Error, Foreground),
|
(Error, Foreground),
|
||||||
|
(Follow, Foreground),
|
||||||
|
(FollowResponse, Foreground),
|
||||||
(FormatBuffers, Foreground),
|
(FormatBuffers, Foreground),
|
||||||
(FormatBuffersResponse, Foreground),
|
(FormatBuffersResponse, Foreground),
|
||||||
(GetChannelMessages, Foreground),
|
(GetChannelMessages, Foreground),
|
||||||
|
@ -196,6 +198,7 @@ messages!(
|
||||||
(SendChannelMessageResponse, Foreground),
|
(SendChannelMessageResponse, Foreground),
|
||||||
(ShareProject, Foreground),
|
(ShareProject, Foreground),
|
||||||
(Test, Foreground),
|
(Test, Foreground),
|
||||||
|
(Unfollow, Foreground),
|
||||||
(UnregisterProject, Foreground),
|
(UnregisterProject, Foreground),
|
||||||
(UnregisterWorktree, Foreground),
|
(UnregisterWorktree, Foreground),
|
||||||
(UnshareProject, Foreground),
|
(UnshareProject, Foreground),
|
||||||
|
@ -203,6 +206,7 @@ messages!(
|
||||||
(UpdateBufferFile, Foreground),
|
(UpdateBufferFile, Foreground),
|
||||||
(UpdateContacts, Foreground),
|
(UpdateContacts, Foreground),
|
||||||
(UpdateDiagnosticSummary, Foreground),
|
(UpdateDiagnosticSummary, Foreground),
|
||||||
|
(UpdateFollowers, Foreground),
|
||||||
(UpdateWorktree, Foreground),
|
(UpdateWorktree, Foreground),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -212,6 +216,7 @@ request_messages!(
|
||||||
ApplyCompletionAdditionalEdits,
|
ApplyCompletionAdditionalEdits,
|
||||||
ApplyCompletionAdditionalEditsResponse
|
ApplyCompletionAdditionalEditsResponse
|
||||||
),
|
),
|
||||||
|
(Follow, FollowResponse),
|
||||||
(FormatBuffers, FormatBuffersResponse),
|
(FormatBuffers, FormatBuffersResponse),
|
||||||
(GetChannelMessages, GetChannelMessagesResponse),
|
(GetChannelMessages, GetChannelMessagesResponse),
|
||||||
(GetChannels, GetChannelsResponse),
|
(GetChannels, GetChannelsResponse),
|
||||||
|
@ -248,6 +253,7 @@ entity_messages!(
|
||||||
ApplyCompletionAdditionalEdits,
|
ApplyCompletionAdditionalEdits,
|
||||||
BufferReloaded,
|
BufferReloaded,
|
||||||
BufferSaved,
|
BufferSaved,
|
||||||
|
Follow,
|
||||||
FormatBuffers,
|
FormatBuffers,
|
||||||
GetCodeActions,
|
GetCodeActions,
|
||||||
GetCompletions,
|
GetCompletions,
|
||||||
|
@ -266,11 +272,13 @@ entity_messages!(
|
||||||
SaveBuffer,
|
SaveBuffer,
|
||||||
SearchProject,
|
SearchProject,
|
||||||
StartLanguageServer,
|
StartLanguageServer,
|
||||||
|
Unfollow,
|
||||||
UnregisterWorktree,
|
UnregisterWorktree,
|
||||||
UnshareProject,
|
UnshareProject,
|
||||||
UpdateBuffer,
|
UpdateBuffer,
|
||||||
UpdateBufferFile,
|
UpdateBufferFile,
|
||||||
UpdateDiagnosticSummary,
|
UpdateDiagnosticSummary,
|
||||||
|
UpdateFollowers,
|
||||||
UpdateLanguageServer,
|
UpdateLanguageServer,
|
||||||
RegisterWorktree,
|
RegisterWorktree,
|
||||||
UpdateWorktree,
|
UpdateWorktree,
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use super::{ItemHandle, SplitDirection};
|
use super::{ItemHandle, SplitDirection};
|
||||||
use crate::{Item, Settings, WeakItemHandle, Workspace};
|
use crate::{Item, Settings, WeakItemHandle, Workspace};
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use client::PeerId;
|
||||||
use collections::{HashMap, VecDeque};
|
use collections::{HashMap, VecDeque};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
action,
|
action,
|
||||||
|
@ -105,6 +107,13 @@ pub struct Pane {
|
||||||
active_toolbar_visible: bool,
|
active_toolbar_visible: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct FollowerState {
|
||||||
|
pub(crate) leader_id: PeerId,
|
||||||
|
pub(crate) current_view_id: usize,
|
||||||
|
pub(crate) items_by_leader_view_id:
|
||||||
|
HashMap<usize, (Option<ProjectEntryId>, Box<dyn ItemHandle>)>,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Toolbar: View {
|
pub trait Toolbar: View {
|
||||||
fn active_item_changed(
|
fn active_item_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -313,6 +322,21 @@ impl Pane {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_follow_state(
|
||||||
|
&mut self,
|
||||||
|
follower_state: FollowerState,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let current_view_id = follower_state.current_view_id as usize;
|
||||||
|
let (project_entry_id, item) = follower_state
|
||||||
|
.items_by_leader_view_id
|
||||||
|
.get(¤t_view_id)
|
||||||
|
.ok_or_else(|| anyhow!("invalid current view id"))?
|
||||||
|
.clone();
|
||||||
|
self.add_item(project_entry_id, item, cx);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn items(&self) -> impl Iterator<Item = &Box<dyn ItemHandle>> {
|
pub fn items(&self) -> impl Iterator<Item = &Box<dyn ItemHandle>> {
|
||||||
self.items.iter().map(|(_, view)| view)
|
self.items.iter().map(|(_, view)| view)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ pub mod sidebar;
|
||||||
mod status_bar;
|
mod status_bar;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use client::{Authenticate, ChannelList, Client, User, UserStore};
|
use client::{proto, Authenticate, ChannelList, Client, PeerId, User, UserStore};
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
@ -42,16 +42,18 @@ use std::{
|
||||||
};
|
};
|
||||||
use theme::{Theme, ThemeRegistry};
|
use theme::{Theme, ThemeRegistry};
|
||||||
|
|
||||||
type ItemBuilders = HashMap<
|
type ProjectItemBuilders = HashMap<
|
||||||
TypeId,
|
TypeId,
|
||||||
Arc<
|
fn(usize, ModelHandle<Project>, AnyModelHandle, &mut MutableAppContext) -> Box<dyn ItemHandle>,
|
||||||
dyn Fn(
|
>;
|
||||||
usize,
|
|
||||||
ModelHandle<Project>,
|
type FollowedItemBuilders = Vec<
|
||||||
AnyModelHandle,
|
fn(
|
||||||
&mut MutableAppContext,
|
ViewHandle<Pane>,
|
||||||
) -> Box<dyn ItemHandle>,
|
ModelHandle<Project>,
|
||||||
>,
|
&mut Option<proto::view::Variant>,
|
||||||
|
&mut MutableAppContext,
|
||||||
|
) -> Option<Task<Result<(Option<ProjectEntryId>, Box<dyn ItemHandle>)>>>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
action!(Open, Arc<AppState>);
|
action!(Open, Arc<AppState>);
|
||||||
|
@ -108,18 +110,18 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_project_item<V>(cx: &mut MutableAppContext)
|
pub fn register_project_item<I: ProjectItem>(cx: &mut MutableAppContext) {
|
||||||
where
|
cx.update_default_global(|builders: &mut ProjectItemBuilders, _| {
|
||||||
V: ProjectItem,
|
builders.insert(TypeId::of::<I::Item>(), |window_id, project, model, cx| {
|
||||||
{
|
let item = model.downcast::<I::Item>().unwrap();
|
||||||
cx.update_default_global(|builders: &mut ItemBuilders, _| {
|
Box::new(cx.add_view(window_id, |cx| I::for_project_item(project, item, cx)))
|
||||||
builders.insert(
|
});
|
||||||
TypeId::of::<V::Item>(),
|
});
|
||||||
Arc::new(move |window_id, project, model, cx| {
|
}
|
||||||
let item = model.downcast::<V::Item>().unwrap();
|
|
||||||
Box::new(cx.add_view(window_id, |cx| V::for_project_item(project, item, cx)))
|
pub fn register_followed_item<I: FollowedItem>(cx: &mut MutableAppContext) {
|
||||||
}),
|
cx.update_default_global(|builders: &mut FollowedItemBuilders, _| {
|
||||||
);
|
builders.push(I::for_state_message)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,6 +216,17 @@ pub trait ProjectItem: Item {
|
||||||
) -> Self;
|
) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait FollowedItem: Item {
|
||||||
|
type UpdateMessage;
|
||||||
|
|
||||||
|
fn for_state_message(
|
||||||
|
pane: ViewHandle<Pane>,
|
||||||
|
project: ModelHandle<Project>,
|
||||||
|
state: &mut Option<proto::view::Variant>,
|
||||||
|
cx: &mut MutableAppContext,
|
||||||
|
) -> Option<Task<Result<(Option<ProjectEntryId>, Box<dyn ItemHandle>)>>>;
|
||||||
|
}
|
||||||
|
|
||||||
pub trait ItemHandle: 'static {
|
pub trait ItemHandle: 'static {
|
||||||
fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox;
|
fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox;
|
||||||
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
|
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
|
||||||
|
@ -840,7 +853,7 @@ impl Workspace {
|
||||||
cx.as_mut().spawn(|mut cx| async move {
|
cx.as_mut().spawn(|mut cx| async move {
|
||||||
let (project_entry_id, project_item) = project_item.await?;
|
let (project_entry_id, project_item) = project_item.await?;
|
||||||
let build_item = cx.update(|cx| {
|
let build_item = cx.update(|cx| {
|
||||||
cx.default_global::<ItemBuilders>()
|
cx.default_global::<ProjectItemBuilders>()
|
||||||
.get(&project_item.model_type())
|
.get(&project_item.model_type())
|
||||||
.ok_or_else(|| anyhow!("no item builder for project item"))
|
.ok_or_else(|| anyhow!("no item builder for project item"))
|
||||||
.cloned()
|
.cloned()
|
||||||
|
@ -990,6 +1003,63 @@ impl Workspace {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn follow(&mut self, leader_id: PeerId, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
|
||||||
|
if let Some(project_id) = self.project.read(cx).remote_id() {
|
||||||
|
let request = self.client.request(proto::Follow {
|
||||||
|
project_id,
|
||||||
|
leader_id: leader_id.0,
|
||||||
|
});
|
||||||
|
cx.spawn_weak(|this, mut cx| async move {
|
||||||
|
let mut response = request.await?;
|
||||||
|
if let Some(this) = this.upgrade(&cx) {
|
||||||
|
let mut item_tasks = Vec::new();
|
||||||
|
let (project, pane) = this.read_with(&cx, |this, _| {
|
||||||
|
(this.project.clone(), this.active_pane().clone())
|
||||||
|
});
|
||||||
|
for view in &mut response.views {
|
||||||
|
let variant = view
|
||||||
|
.variant
|
||||||
|
.take()
|
||||||
|
.ok_or_else(|| anyhow!("missing variant"))?;
|
||||||
|
cx.update(|cx| {
|
||||||
|
let mut variant = Some(variant);
|
||||||
|
for build_item in cx.default_global::<FollowedItemBuilders>().clone() {
|
||||||
|
if let Some(task) =
|
||||||
|
build_item(pane.clone(), project.clone(), &mut variant, cx)
|
||||||
|
{
|
||||||
|
item_tasks.push(task);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
assert!(variant.is_some());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = futures::future::try_join_all(item_tasks).await?;
|
||||||
|
let mut items_by_leader_view_id = HashMap::default();
|
||||||
|
for (view, item) in response.views.into_iter().zip(items) {
|
||||||
|
items_by_leader_view_id.insert(view.id as usize, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
pane.update(&mut cx, |pane, cx| {
|
||||||
|
pane.set_follow_state(
|
||||||
|
FollowerState {
|
||||||
|
leader_id,
|
||||||
|
current_view_id: response.current_view_id as usize,
|
||||||
|
items_by_leader_view_id,
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Task::ready(Err(anyhow!("project is not remote")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_connection_status(&self, cx: &mut RenderContext<Self>) -> Option<ElementBox> {
|
fn render_connection_status(&self, cx: &mut RenderContext<Self>) -> Option<ElementBox> {
|
||||||
let theme = &cx.global::<Settings>().theme;
|
let theme = &cx.global::<Settings>().theme;
|
||||||
match &*self.client.status().borrow() {
|
match &*self.client.status().borrow() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue