Add worktree to workspace when joining a remote worktree
This commit is contained in:
+10
-1
@@ -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
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user