Start work on implementing ::file on remote WorktreeHandles

This commit is contained in:
Max Brunsfeld 2021-06-18 13:32:38 -07:00
parent f9d8e952f2
commit a2a6d67e1e
4 changed files with 76 additions and 70 deletions

View file

@ -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,

View file

@ -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 file = file.await?;
let history = cx.read(|cx| file.load_history(cx)); let history = cx.read(|cx| file.load_history(cx));
let history = cx.background_executor().spawn(history).await; let history = cx.background_executor().spawn(history).await?;
let buffer = cx.add_model(|cx| {
*tx.borrow_mut() = Some(match history {
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 result = async move {
let file = handle let file = handle
.update(&mut cx, |me, cx| me.file_for_path(&path, cx)) .update(&mut cx, |me, cx| me.file_for_path(&path, cx))
.await?;
cx.update(|cx| item.save(Some(file), cx)).await
}
.await; .await;
if let Err(error) = cx.update(|cx| item.save(Some(file), cx)).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)

View file

@ -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,19 +1411,15 @@ 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)
.unwrap_or_else(|| {
let handle_state = if let Some(entry) = tree.entry_for_path(&path) { let handle_state = if let Some(entry) = tree.entry_for_path(&path) {
FileHandleState { FileHandleState {
path: entry.path().clone(), path: entry.path().clone(),
@ -1441,7 +1437,7 @@ impl WorktreeHandle for ModelHandle<Worktree> {
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())

View file

@ -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,
} }