Start work on implementing ::file on remote WorktreeHandles
This commit is contained in:
parent
f9d8e952f2
commit
a2a6d67e1e
4 changed files with 76 additions and 70 deletions
|
@ -2687,7 +2687,7 @@ mod tests {
|
||||||
cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
|
cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let file1 = cx.update(|cx| tree.file("file1", cx)).await;
|
let file1 = cx.update(|cx| tree.file("file1", cx)).await.unwrap();
|
||||||
let buffer1 = cx.add_model(|cx| {
|
let buffer1 = cx.add_model(|cx| {
|
||||||
Buffer::from_history(0, History::new("abc".into()), Some(file1), None, cx)
|
Buffer::from_history(0, History::new("abc".into()), Some(file1), None, cx)
|
||||||
});
|
});
|
||||||
|
@ -2747,7 +2747,7 @@ mod tests {
|
||||||
|
|
||||||
// When a file is deleted, the buffer is considered dirty.
|
// When a file is deleted, the buffer is considered dirty.
|
||||||
let events = Rc::new(RefCell::new(Vec::new()));
|
let events = Rc::new(RefCell::new(Vec::new()));
|
||||||
let file2 = cx.update(|cx| tree.file("file2", cx)).await;
|
let file2 = cx.update(|cx| tree.file("file2", cx)).await.unwrap();
|
||||||
let buffer2 = cx.add_model(|cx: &mut ModelContext<Buffer>| {
|
let buffer2 = cx.add_model(|cx: &mut ModelContext<Buffer>| {
|
||||||
cx.subscribe(&cx.handle(), {
|
cx.subscribe(&cx.handle(), {
|
||||||
let events = events.clone();
|
let events = events.clone();
|
||||||
|
@ -2766,7 +2766,7 @@ mod tests {
|
||||||
|
|
||||||
// When a file is already dirty when deleted, we don't emit a Dirtied event.
|
// When a file is already dirty when deleted, we don't emit a Dirtied event.
|
||||||
let events = Rc::new(RefCell::new(Vec::new()));
|
let events = Rc::new(RefCell::new(Vec::new()));
|
||||||
let file3 = cx.update(|cx| tree.file("file3", cx)).await;
|
let file3 = cx.update(|cx| tree.file("file3", cx)).await.unwrap();
|
||||||
let buffer3 = cx.add_model(|cx: &mut ModelContext<Buffer>| {
|
let buffer3 = cx.add_model(|cx: &mut ModelContext<Buffer>| {
|
||||||
cx.subscribe(&cx.handle(), {
|
cx.subscribe(&cx.handle(), {
|
||||||
let events = events.clone();
|
let events = events.clone();
|
||||||
|
@ -2799,7 +2799,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let abs_path = dir.path().join("the-file");
|
let abs_path = dir.path().join("the-file");
|
||||||
let file = cx.update(|cx| tree.file("the-file", cx)).await;
|
let file = cx.update(|cx| tree.file("the-file", cx)).await.unwrap();
|
||||||
let buffer = cx.add_model(|cx| {
|
let buffer = cx.add_model(|cx| {
|
||||||
Buffer::from_history(
|
Buffer::from_history(
|
||||||
0,
|
0,
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
worktree::{FileHandle, Worktree, WorktreeHandle},
|
worktree::{FileHandle, Worktree, WorktreeHandle},
|
||||||
AppState,
|
AppState,
|
||||||
};
|
};
|
||||||
use anyhow::anyhow;
|
use anyhow::{anyhow, Result};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, AppContext,
|
color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, AppContext,
|
||||||
AsyncAppContext, ClipboardItem, Entity, ModelHandle, MutableAppContext, PathPromptOptions,
|
AsyncAppContext, ClipboardItem, Entity, ModelHandle, MutableAppContext, PathPromptOptions,
|
||||||
|
@ -23,6 +23,7 @@ use postage::watch;
|
||||||
use smol::prelude::*;
|
use smol::prelude::*;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{hash_map::Entry, HashMap, HashSet},
|
collections::{hash_map::Entry, HashMap, HashSet},
|
||||||
|
convert::TryInto,
|
||||||
future::Future,
|
future::Future,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
@ -404,15 +405,13 @@ impl Workspace {
|
||||||
.map(|(abs_path, file)| {
|
.map(|(abs_path, file)| {
|
||||||
let is_file = bg.spawn(async move { abs_path.is_file() });
|
let is_file = bg.spawn(async move { abs_path.is_file() });
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
let file = file.await;
|
if let Ok(file) = file.await {
|
||||||
let is_file = is_file.await;
|
if is_file.await {
|
||||||
this.update(&mut cx, |this, cx| {
|
return this
|
||||||
if is_file {
|
.update(&mut cx, |this, cx| this.open_entry(file.entry_id(), cx));
|
||||||
this.open_entry(file.entry_id(), cx)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
None
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -425,7 +424,11 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file_for_path(&mut self, abs_path: &Path, cx: &mut ViewContext<Self>) -> Task<FileHandle> {
|
fn file_for_path(
|
||||||
|
&mut self,
|
||||||
|
abs_path: &Path,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Task<Result<FileHandle>> {
|
||||||
for tree in self.worktrees.iter() {
|
for tree in self.worktrees.iter() {
|
||||||
if let Some(relative_path) = tree
|
if let Some(relative_path) = tree
|
||||||
.read(cx)
|
.read(cx)
|
||||||
|
@ -546,12 +549,11 @@ impl Workspace {
|
||||||
|
|
||||||
cx.as_mut()
|
cx.as_mut()
|
||||||
.spawn(|mut cx| async move {
|
.spawn(|mut cx| async move {
|
||||||
let file = file.await;
|
let buffer = async move {
|
||||||
let history = cx.read(|cx| file.load_history(cx));
|
let file = file.await?;
|
||||||
let history = cx.background_executor().spawn(history).await;
|
let history = cx.read(|cx| file.load_history(cx));
|
||||||
|
let history = cx.background_executor().spawn(history).await?;
|
||||||
*tx.borrow_mut() = Some(match history {
|
let buffer = cx.add_model(|cx| {
|
||||||
Ok(history) => Ok(Box::new(cx.add_model(|cx| {
|
|
||||||
let language = language_registry.select_language(path);
|
let language = language_registry.select_language(path);
|
||||||
Buffer::from_history(
|
Buffer::from_history(
|
||||||
replica_id,
|
replica_id,
|
||||||
|
@ -560,9 +562,11 @@ impl Workspace {
|
||||||
language.cloned(),
|
language.cloned(),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
}))),
|
});
|
||||||
Err(error) => Err(Arc::new(error)),
|
Ok(Box::new(buffer) as Box<dyn ItemHandle>)
|
||||||
})
|
}
|
||||||
|
.await;
|
||||||
|
*tx.borrow_mut() = Some(buffer.map_err(Arc::new));
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
@ -612,10 +616,14 @@ impl Workspace {
|
||||||
cx.prompt_for_new_path(&start_path, move |path, cx| {
|
cx.prompt_for_new_path(&start_path, move |path, cx| {
|
||||||
if let Some(path) = path {
|
if let Some(path) = path {
|
||||||
cx.spawn(|mut cx| async move {
|
cx.spawn(|mut cx| async move {
|
||||||
let file = handle
|
let result = async move {
|
||||||
.update(&mut cx, |me, cx| me.file_for_path(&path, cx))
|
let file = handle
|
||||||
.await;
|
.update(&mut cx, |me, cx| me.file_for_path(&path, cx))
|
||||||
if let Err(error) = cx.update(|cx| item.save(Some(file), cx)).await {
|
.await?;
|
||||||
|
cx.update(|cx| item.save(Some(file), cx)).await
|
||||||
|
}
|
||||||
|
.await;
|
||||||
|
if let Err(error) = result {
|
||||||
error!("failed to save item: {:?}, ", error);
|
error!("failed to save item: {:?}, ", error);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -728,6 +736,8 @@ impl Workspace {
|
||||||
let worktree = open_worktree_response
|
let worktree = open_worktree_response
|
||||||
.worktree
|
.worktree
|
||||||
.ok_or_else(|| anyhow!("empty worktree"))?;
|
.ok_or_else(|| anyhow!("empty worktree"))?;
|
||||||
|
|
||||||
|
let worktree_id = worktree_id.try_into().unwrap();
|
||||||
this.update(&mut cx, |workspace, cx| {
|
this.update(&mut cx, |workspace, cx| {
|
||||||
let worktree = cx.add_model(|cx| {
|
let worktree = cx.add_model(|cx| {
|
||||||
Worktree::remote(worktree_id, worktree, rpc, connection_id, cx)
|
Worktree::remote(worktree_id, worktree, rpc, connection_id, cx)
|
||||||
|
|
|
@ -30,7 +30,7 @@ use std::{
|
||||||
os::unix::{ffi::OsStrExt, fs::MetadataExt},
|
os::unix::{ffi::OsStrExt, fs::MetadataExt},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::{Arc, Weak},
|
sync::{Arc, Weak},
|
||||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
time::{Duration, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::{char_bag::CharBag, ignore::IgnoreStack};
|
use self::{char_bag::CharBag, ignore::IgnoreStack};
|
||||||
|
@ -61,7 +61,7 @@ impl Worktree {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remote(
|
pub fn remote(
|
||||||
id: u64,
|
id: usize,
|
||||||
worktree: proto::Worktree,
|
worktree: proto::Worktree,
|
||||||
rpc: rpc::Client,
|
rpc: rpc::Client,
|
||||||
connection_id: ConnectionId,
|
connection_id: ConnectionId,
|
||||||
|
@ -155,7 +155,7 @@ impl LocalWorktree {
|
||||||
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() as u64;
|
let id = cx.model_id();
|
||||||
let snapshot = Snapshot {
|
let snapshot = Snapshot {
|
||||||
id,
|
id,
|
||||||
scan_id: 0,
|
scan_id: 0,
|
||||||
|
@ -374,7 +374,7 @@ impl fmt::Debug for LocalWorktree {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RemoteWorktree {
|
pub struct RemoteWorktree {
|
||||||
id: u64,
|
id: usize,
|
||||||
snapshot: Snapshot,
|
snapshot: Snapshot,
|
||||||
handles: Arc<Mutex<HashMap<Arc<Path>, Weak<Mutex<FileHandleState>>>>>,
|
handles: Arc<Mutex<HashMap<Arc<Path>, Weak<Mutex<FileHandleState>>>>>,
|
||||||
rpc: rpc::Client,
|
rpc: rpc::Client,
|
||||||
|
@ -383,7 +383,7 @@ pub struct RemoteWorktree {
|
||||||
|
|
||||||
impl RemoteWorktree {
|
impl RemoteWorktree {
|
||||||
fn new(
|
fn new(
|
||||||
id: u64,
|
id: usize,
|
||||||
worktree: proto::Worktree,
|
worktree: proto::Worktree,
|
||||||
rpc: rpc::Client,
|
rpc: rpc::Client,
|
||||||
connection_id: ConnectionId,
|
connection_id: ConnectionId,
|
||||||
|
@ -434,7 +434,7 @@ impl RemoteWorktree {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Snapshot {
|
pub struct Snapshot {
|
||||||
id: u64,
|
id: usize,
|
||||||
scan_id: usize,
|
scan_id: usize,
|
||||||
abs_path: Arc<Path>,
|
abs_path: Arc<Path>,
|
||||||
root_name: String,
|
root_name: String,
|
||||||
|
@ -871,7 +871,7 @@ impl BackgroundScanner {
|
||||||
snapshot: Arc<Mutex<Snapshot>>,
|
snapshot: Arc<Mutex<Snapshot>>,
|
||||||
handles: Arc<Mutex<HashMap<Arc<Path>, Weak<Mutex<FileHandleState>>>>>,
|
handles: Arc<Mutex<HashMap<Arc<Path>, Weak<Mutex<FileHandleState>>>>>,
|
||||||
notify: Sender<ScanState>,
|
notify: Sender<ScanState>,
|
||||||
worktree_id: u64,
|
worktree_id: usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut scanner = Self {
|
let mut scanner = Self {
|
||||||
root_char_bag: Default::default(),
|
root_char_bag: Default::default(),
|
||||||
|
@ -1411,37 +1411,33 @@ impl WorktreeHandle for ModelHandle<Worktree> {
|
||||||
cx.spawn(|cx| async move {
|
cx.spawn(|cx| async move {
|
||||||
let mtime = cx
|
let mtime = cx
|
||||||
.background_executor()
|
.background_executor()
|
||||||
.spawn(async move {
|
.spawn(async move { fs::metadata(&abs_path) })
|
||||||
if let Ok(metadata) = fs::metadata(&abs_path) {
|
.await?
|
||||||
metadata.modified().unwrap()
|
.modified()?;
|
||||||
} else {
|
|
||||||
UNIX_EPOCH
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
let state = handle.read_with(&cx, |tree, _| {
|
let state = handle.read_with(&cx, |tree, _| {
|
||||||
let mut handles = tree.as_local().unwrap().handles.lock();
|
let mut handles = tree.as_local().unwrap().handles.lock();
|
||||||
if let Some(state) = handles.get(&path).and_then(Weak::upgrade) {
|
handles
|
||||||
state
|
.get(&path)
|
||||||
} else {
|
.and_then(Weak::upgrade)
|
||||||
let handle_state = if let Some(entry) = tree.entry_for_path(&path) {
|
.unwrap_or_else(|| {
|
||||||
FileHandleState {
|
let handle_state = if let Some(entry) = tree.entry_for_path(&path) {
|
||||||
path: entry.path().clone(),
|
FileHandleState {
|
||||||
is_deleted: false,
|
path: entry.path().clone(),
|
||||||
mtime,
|
is_deleted: false,
|
||||||
}
|
mtime,
|
||||||
} else {
|
}
|
||||||
FileHandleState {
|
} else {
|
||||||
path: path.clone(),
|
FileHandleState {
|
||||||
is_deleted: !tree.path_is_pending(path),
|
path: path.clone(),
|
||||||
mtime,
|
is_deleted: !tree.path_is_pending(path),
|
||||||
}
|
mtime,
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let state = Arc::new(Mutex::new(handle_state.clone()));
|
let state = Arc::new(Mutex::new(handle_state.clone()));
|
||||||
handles.insert(handle_state.path, Arc::downgrade(&state));
|
handles.insert(handle_state.path, Arc::downgrade(&state));
|
||||||
state
|
state
|
||||||
}
|
})
|
||||||
});
|
});
|
||||||
Ok(FileHandle {
|
Ok(FileHandle {
|
||||||
worktree: handle.clone(),
|
worktree: handle.clone(),
|
||||||
|
@ -1458,7 +1454,7 @@ impl WorktreeHandle for ModelHandle<Worktree> {
|
||||||
.request(
|
.request(
|
||||||
connection_id,
|
connection_id,
|
||||||
proto::OpenFile {
|
proto::OpenFile {
|
||||||
worktree_id,
|
worktree_id: worktree_id as u64,
|
||||||
path: path.to_string_lossy().to_string(),
|
path: path.to_string_lossy().to_string(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -1724,7 +1720,7 @@ mod tests {
|
||||||
|
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(1, "a line of text.\n".repeat(10 * 1024), cx));
|
let buffer = cx.add_model(|cx| Buffer::new(1, "a line of text.\n".repeat(10 * 1024), cx));
|
||||||
|
|
||||||
let file = cx.update(|cx| tree.file("", cx)).await;
|
let file = cx.update(|cx| tree.file("", cx)).await.unwrap();
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
assert_eq!(file.path().file_name(), None);
|
assert_eq!(file.path().file_name(), None);
|
||||||
smol::block_on(file.save(buffer.read(cx).snapshot().text(), cx.as_ref())).unwrap();
|
smol::block_on(file.save(buffer.read(cx).snapshot().text(), cx.as_ref())).unwrap();
|
||||||
|
@ -1751,11 +1747,11 @@ mod tests {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let tree = cx.add_model(|cx| Worktree::local(dir.path(), cx));
|
let tree = cx.add_model(|cx| Worktree::local(dir.path(), cx));
|
||||||
let file2 = cx.update(|cx| tree.file("a/file2", cx)).await;
|
let file2 = cx.update(|cx| tree.file("a/file2", cx)).await.unwrap();
|
||||||
let file3 = cx.update(|cx| tree.file("a/file3", cx)).await;
|
let file3 = cx.update(|cx| tree.file("a/file3", cx)).await.unwrap();
|
||||||
let file4 = cx.update(|cx| tree.file("b/c/file4", cx)).await;
|
let file4 = cx.update(|cx| tree.file("b/c/file4", cx)).await.unwrap();
|
||||||
let file5 = cx.update(|cx| tree.file("b/c/file5", cx)).await;
|
let file5 = cx.update(|cx| tree.file("b/c/file5", cx)).await.unwrap();
|
||||||
let non_existent_file = cx.update(|cx| tree.file("a/file_x", cx)).await;
|
let non_existent_file = cx.update(|cx| tree.file("a/file_x", cx)).await.unwrap();
|
||||||
|
|
||||||
// After scanning, the worktree knows which files exist and which don't.
|
// After scanning, the worktree knows which files exist and which don't.
|
||||||
cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
|
cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub struct MatchCandidate<'a> {
|
||||||
pub struct PathMatch {
|
pub struct PathMatch {
|
||||||
pub score: f64,
|
pub score: f64,
|
||||||
pub positions: Vec<usize>,
|
pub positions: Vec<usize>,
|
||||||
pub tree_id: u64,
|
pub tree_id: usize,
|
||||||
pub path: Arc<Path>,
|
pub path: Arc<Path>,
|
||||||
pub include_root_name: bool,
|
pub include_root_name: bool,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue