Add worktree to workspace when joining a remote worktree

This commit is contained in:
Antonio Scandurra
2021-06-18 14:52:38 +02:00
parent 79cac1340e
commit 89d25458ac
5 changed files with 103 additions and 29 deletions
+10 -1
View File
@@ -53,7 +53,16 @@ message OpenBufferResponse {
} }
message Worktree { message Worktree {
repeated string paths = 1; string root_name = 1;
repeated Entry entries = 2;
}
message Entry {
bool is_dir = 1;
string path = 2;
uint64 inode = 3;
bool is_symlink = 4;
bool is_ignored = 5;
} }
message Buffer { message Buffer {
+13 -17
View File
@@ -341,23 +341,19 @@ mod tests {
smol::spawn(server.handle_messages(server_conn_id2)).detach(); smol::spawn(server.handle_messages(server_conn_id2)).detach();
// define the expected requests and responses // define the expected requests and responses
let request1 = proto::OpenWorktree { let request1 = proto::Auth {
worktree_id: 101, user_id: 1,
access_token: "first-worktree-access-token".to_string(), access_token: "token-1".to_string(),
}; };
let response1 = proto::OpenWorktreeResponse { let response1 = proto::AuthResponse {
worktree: Some(proto::Worktree { credentials_valid: true,
paths: vec!["path/one".to_string()],
}),
}; };
let request2 = proto::OpenWorktree { let request2 = proto::Auth {
worktree_id: 102, user_id: 2,
access_token: "second-worktree-access-token".to_string(), access_token: "token-2".to_string(),
}; };
let response2 = proto::OpenWorktreeResponse { let response2 = proto::AuthResponse {
worktree: Some(proto::Worktree { credentials_valid: false,
paths: vec!["path/two".to_string(), "path/three".to_string()],
}),
}; };
let request3 = proto::OpenBuffer { let request3 = proto::OpenBuffer {
worktree_id: 102, worktree_id: 102,
@@ -386,7 +382,7 @@ mod tests {
// on the server, respond to two requests for each client // on the server, respond to two requests for each client
let mut open_buffer_rx = server.add_message_handler::<proto::OpenBuffer>().await; let mut open_buffer_rx = server.add_message_handler::<proto::OpenBuffer>().await;
let mut open_worktree_rx = server.add_message_handler::<proto::OpenWorktree>().await; let mut auth_rx = server.add_message_handler::<proto::Auth>().await;
let (mut server_done_tx, mut server_done_rx) = oneshot::channel::<()>(); let (mut server_done_tx, mut server_done_rx) = oneshot::channel::<()>();
smol::spawn({ smol::spawn({
let request1 = request1.clone(); let request1 = request1.clone();
@@ -398,11 +394,11 @@ mod tests {
let response3 = response3.clone(); let response3 = response3.clone();
let response4 = response4.clone(); let response4 = response4.clone();
async move { async move {
let msg = open_worktree_rx.recv().await.unwrap(); let msg = auth_rx.recv().await.unwrap();
assert_eq!(msg.payload, request1); assert_eq!(msg.payload, request1);
server.respond(msg, response1.clone()).await.unwrap(); server.respond(msg, response1.clone()).await.unwrap();
let msg = open_worktree_rx.recv().await.unwrap(); let msg = auth_rx.recv().await.unwrap();
assert_eq!(msg.payload, request2.clone()); assert_eq!(msg.payload, request2.clone());
server.respond(msg, response2.clone()).await.unwrap(); server.respond(msg, response2.clone()).await.unwrap();
+3 -4
View File
@@ -132,10 +132,9 @@ mod tests {
} }
.into_envelope(3, None); .into_envelope(3, None);
let message2 = ShareWorktree { let message2 = OpenBuffer {
worktree: Some(Worktree { worktree_id: 1,
paths: vec!["ok".to_string()], path: "path".to_string(),
}),
} }
.into_envelope(5, None); .into_envelope(5, None);
+10 -2
View File
@@ -705,7 +705,7 @@ impl Workspace {
let rpc = self.rpc.clone(); let rpc = self.rpc.clone();
let executor = cx.background_executor().clone(); let executor = cx.background_executor().clone();
let task = cx.spawn(|_, cx| async move { let task = cx.spawn(|this, mut cx| async move {
let connection_id = rpc.connect_to_server(&cx, &executor).await?; let connection_id = rpc.connect_to_server(&cx, &executor).await?;
let worktree_url = cx let worktree_url = cx
@@ -725,7 +725,15 @@ impl Workspace {
}, },
) )
.await?; .await?;
log::info!("joined worktree: {:?}", open_worktree_response); let worktree = open_worktree_response
.worktree
.ok_or_else(|| anyhow!("empty worktree"))?;
this.update(&mut cx, |workspace, cx| {
let worktree = cx.add_model(|cx| Worktree::remote(worktree, cx));
cx.observe_model(&worktree, |_, _, cx| cx.notify());
workspace.worktrees.insert(worktree);
cx.notify();
});
surf::Result::Ok(()) surf::Result::Ok(())
}); });
+67 -5
View File
@@ -48,6 +48,7 @@ enum ScanState {
pub enum Worktree { pub enum Worktree {
Local(LocalWorktree), Local(LocalWorktree),
Remote(RemoteWorktree),
} }
impl Entity for Worktree { impl Entity for Worktree {
@@ -59,6 +60,10 @@ impl Worktree {
Worktree::Local(LocalWorktree::new(path, cx)) Worktree::Local(LocalWorktree::new(path, cx))
} }
pub fn remote(worktree: proto::Worktree, cx: &mut ModelContext<Worktree>) -> Self {
Worktree::Remote(RemoteWorktree::new(worktree, cx))
}
pub fn as_local(&self) -> Option<&LocalWorktree> { pub fn as_local(&self) -> Option<&LocalWorktree> {
if let Worktree::Local(worktree) = self { if let Worktree::Local(worktree) = self {
Some(worktree) Some(worktree)
@@ -78,6 +83,7 @@ impl Worktree {
pub fn snapshot(&self) -> Snapshot { pub fn snapshot(&self) -> Snapshot {
match self { match self {
Worktree::Local(worktree) => worktree.snapshot(), Worktree::Local(worktree) => worktree.snapshot(),
Worktree::Remote(worktree) => worktree.snapshot.clone(),
} }
} }
@@ -88,6 +94,7 @@ impl Worktree {
) -> impl Future<Output = Result<History>> { ) -> impl Future<Output = Result<History>> {
match self { match self {
Worktree::Local(worktree) => worktree.load_history(path, cx), Worktree::Local(worktree) => worktree.load_history(path, cx),
Worktree::Remote(worktree) => todo!(),
} }
} }
@@ -99,6 +106,7 @@ impl Worktree {
) -> impl Future<Output = Result<()>> { ) -> impl Future<Output = Result<()>> {
match self { match self {
Worktree::Local(worktree) => worktree.save(path, content, cx), Worktree::Local(worktree) => worktree.save(path, content, cx),
Worktree::Remote(worktree) => todo!(),
} }
} }
} }
@@ -109,6 +117,7 @@ impl Deref for Worktree {
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
match self { match self {
Worktree::Local(worktree) => &worktree.snapshot, Worktree::Local(worktree) => &worktree.snapshot,
Worktree::Remote(worktree) => &worktree.snapshot,
} }
} }
} }
@@ -137,7 +146,7 @@ struct FileHandleState {
} }
impl LocalWorktree { impl LocalWorktree {
pub fn new(path: impl Into<Arc<Path>>, cx: &mut ModelContext<Worktree>) -> Self { fn new(path: impl Into<Arc<Path>>, cx: &mut ModelContext<Worktree>) -> Self {
let abs_path = path.into(); let abs_path = path.into();
let (scan_state_tx, scan_state_rx) = smol::channel::unbounded(); let (scan_state_tx, scan_state_rx) = smol::channel::unbounded();
let id = cx.model_id(); let id = cx.model_id();
@@ -309,14 +318,22 @@ impl LocalWorktree {
cx: &mut ModelContext<Worktree>, cx: &mut ModelContext<Worktree>,
) -> Task<anyhow::Result<(u64, String)>> { ) -> Task<anyhow::Result<(u64, String)>> {
self.rpc = Some(client.clone()); self.rpc = Some(client.clone());
let root_name = self.root_name.clone();
let snapshot = self.snapshot(); let snapshot = self.snapshot();
cx.spawn(|_this, cx| async move { cx.spawn(|_this, cx| async move {
let paths = cx let entries = cx
.background_executor() .background_executor()
.spawn(async move { .spawn(async move {
snapshot snapshot
.paths() .entries
.map(|path| path.to_string_lossy().to_string()) .cursor::<(), ()>()
.map(|entry| proto::Entry {
is_dir: entry.is_dir(),
path: entry.path.to_string_lossy().to_string(),
inode: entry.inode,
is_symlink: entry.is_symlink,
is_ignored: entry.is_ignored,
})
.collect() .collect()
}) })
.await; .await;
@@ -325,7 +342,7 @@ impl LocalWorktree {
.request( .request(
connection_id, connection_id,
proto::ShareWorktree { proto::ShareWorktree {
worktree: Some(proto::Worktree { paths }), worktree: Some(proto::Worktree { root_name, entries }),
}, },
) )
.await?; .await?;
@@ -350,6 +367,50 @@ impl fmt::Debug for LocalWorktree {
} }
} }
pub struct RemoteWorktree {
snapshot: Snapshot,
}
impl RemoteWorktree {
fn new(worktree: proto::Worktree, cx: &mut ModelContext<Worktree>) -> Self {
let id = cx.model_id();
let root_char_bag: CharBag = worktree
.root_name
.chars()
.map(|c| c.to_ascii_lowercase())
.collect();
let mut entries = SumTree::new();
entries.extend(
worktree.entries.into_iter().map(|entry| {
let kind = if entry.is_dir {
EntryKind::Dir
} else {
let mut char_bag = root_char_bag.clone();
char_bag.extend(entry.path.chars().map(|c| c.to_ascii_lowercase()));
EntryKind::File(char_bag)
};
Entry {
kind,
path: Path::new(&entry.path).into(),
inode: entry.inode,
is_symlink: entry.is_symlink,
is_ignored: entry.is_ignored,
}
}),
&(),
);
let snapshot = Snapshot {
id,
scan_id: 0,
abs_path: Path::new("").into(),
root_name: worktree.root_name,
ignores: Default::default(),
entries,
};
Self { snapshot }
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct Snapshot { pub struct Snapshot {
id: usize, id: usize,
@@ -1367,6 +1428,7 @@ impl WorktreeHandle for ModelHandle<Worktree> {
} }
}) })
} }
Worktree::Remote(tree) => todo!(),
} }
} }