From 66fce5ec8e129e574f658db19a1791d1781ec020 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 22 Jan 2022 15:52:14 -0700 Subject: [PATCH] Introduce LocalFile trait If you want to call `abs_path` or `load`, the file needs to be local. You call `as_local` which returns `Option` with those local-only methods. I think this makes it more explicit what works only locally vs everywhere. --- crates/language/src/buffer.rs | 36 ++++++++++++++++------------ crates/project/src/project.rs | 6 ++--- crates/project/src/worktree.rs | 44 +++++++++++++++++++++------------- 3 files changed, 51 insertions(+), 35 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 3d0ef512d4a998a20b886f4fc93ace7ea4fb9c68..9ff432ae8675b11c1050e3b3d1326e4af5d8e13c 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -156,14 +156,13 @@ pub enum Event { } pub trait File { + fn as_local(&self) -> Option<&dyn LocalFile>; + fn mtime(&self) -> SystemTime; /// Returns the path of this file relative to the worktree's root directory. fn path(&self) -> &Arc; - /// Returns the absolute path of this file. - fn abs_path(&self, cx: &AppContext) -> Option; - /// Returns the path of this file relative to the worktree's parent directory (this means it /// includes the name of the worktree's root folder). fn full_path(&self, cx: &AppContext) -> PathBuf; @@ -182,8 +181,6 @@ pub trait File { cx: &mut MutableAppContext, ) -> Task>; - fn load_local(&self, cx: &AppContext) -> Option>>; - fn format_remote(&self, buffer_id: u64, cx: &mut MutableAppContext) -> Option>>; @@ -194,6 +191,13 @@ pub trait File { fn as_any(&self) -> &dyn Any; } +pub trait LocalFile: File { + /// Returns the absolute path of this file. + fn abs_path(&self, cx: &AppContext) -> PathBuf; + + fn load(&self, cx: &AppContext) -> Task>; +} + pub(crate) struct QueryCursorHandle(Option); #[derive(Clone)] @@ -457,7 +461,7 @@ impl Buffer { if let Some(LanguageServerState { server, .. }) = self.language_server.as_ref() { let server = server.clone(); - let abs_path = file.abs_path(cx).unwrap(); + let abs_path = file.as_local().unwrap().abs_path(cx); let version = self.version(); cx.spawn(|this, mut cx| async move { let edits = server @@ -634,7 +638,11 @@ impl Buffer { if let Some(new_file) = new_file { self.file = Some(new_file); } - if let Some(state) = &self.language_server { + if let Some((state, local_file)) = &self + .language_server + .as_ref() + .zip(self.file.as_ref().and_then(|f| f.as_local())) + { cx.background() .spawn( state @@ -642,10 +650,7 @@ impl Buffer { .notify::( lsp::DidSaveTextDocumentParams { text_document: lsp::TextDocumentIdentifier { - uri: lsp::Url::from_file_path( - self.file.as_ref().unwrap().abs_path(cx).unwrap(), - ) - .unwrap(), + uri: lsp::Url::from_file_path(local_file.abs_path(cx)).unwrap(), }, text: None, }, @@ -685,7 +690,9 @@ impl Buffer { task = Some(cx.spawn(|this, mut cx| { async move { let new_text = this.read_with(&cx, |this, cx| { - this.file.as_ref().and_then(|file| file.load_local(cx)) + this.file + .as_ref() + .and_then(|file| file.as_local().map(|f| f.load(cx))) }); if let Some(new_text) = new_text { let new_text = new_text.await?; @@ -1235,9 +1242,8 @@ impl Buffer { let abs_path = self .file .as_ref() - .map_or(Path::new("/").to_path_buf(), |file| { - file.abs_path(cx).unwrap() - }); + .and_then(|f| f.as_local()) + .map_or(Path::new("/").to_path_buf(), |file| file.abs_path(cx)); let version = post_inc(&mut language_server.next_version); let snapshot = LanguageServerSnapshot { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 56db25365edff9f926892af6cc0a9a033972cb6b..b78f44f9d52288962623727eab97d1c2ab3af344 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -938,7 +938,7 @@ impl Project { let buffer_abs_path; if let Some(file) = File::from_dyn(buffer.file()) { worktree = file.worktree.clone(); - buffer_abs_path = file.abs_path(cx); + buffer_abs_path = file.as_local().map(|f| f.abs_path(cx)); } else { return Task::ready(Err(anyhow!("buffer does not belong to any worktree"))); }; @@ -2181,8 +2181,8 @@ mod tests { cx.update(|cx| { let target_buffer = definition.target_buffer.read(cx); assert_eq!( - target_buffer.file().unwrap().abs_path(cx), - Some(dir.path().join("a.rs")) + target_buffer.file().unwrap().as_local().unwrap().abs_path(cx), + dir.path().join("a.rs") ); assert_eq!(definition.target_range.to_offset(target_buffer), 9..10); assert_eq!( diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 32e72a5f177f3793505bf92680664690c5de071b..4fb359ba1bc6c7e52da7ddadd11ddffafe30660c 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -1366,6 +1366,14 @@ pub struct File { } impl language::File for File { + fn as_local(&self) -> Option<&dyn language::LocalFile> { + if self.is_local { + Some(self) + } else { + None + } + } + fn mtime(&self) -> SystemTime { self.mtime } @@ -1374,13 +1382,6 @@ impl language::File for File { &self.path } - fn abs_path(&self, cx: &AppContext) -> Option { - self.worktree - .read(cx) - .as_local() - .map(|local_worktree| local_worktree.abs_path.join(&self.path)) - } - fn full_path(&self, cx: &AppContext) -> PathBuf { let mut full_path = PathBuf::new(); full_path.push(self.worktree.read(cx).root_name()); @@ -1448,16 +1449,6 @@ impl language::File for File { }) } - fn load_local(&self, cx: &AppContext) -> Option>> { - let worktree = self.worktree.read(cx).as_local()?; - let abs_path = worktree.absolutize(&self.path); - let fs = worktree.fs.clone(); - Some( - cx.background() - .spawn(async move { fs.load(&abs_path).await }), - ) - } - fn format_remote( &self, buffer_id: u64, @@ -1510,6 +1501,25 @@ impl language::File for File { } } +impl language::LocalFile for File { + fn abs_path(&self, cx: &AppContext) -> PathBuf { + self.worktree + .read(cx) + .as_local() + .unwrap() + .abs_path + .join(&self.path) + } + + fn load(&self, cx: &AppContext) -> Task> { + let worktree = self.worktree.read(cx).as_local().unwrap(); + let abs_path = worktree.absolutize(&self.path); + let fs = worktree.fs.clone(); + cx.background() + .spawn(async move { fs.load(&abs_path).await }) + } +} + impl File { pub fn from_dyn(file: Option<&dyn language::File>) -> Option<&Self> { file.and_then(|f| f.as_any().downcast_ref())