Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-vcs/src/lib.rs')
| -rw-r--r-- | helix-vcs/src/lib.rs | 99 |
1 files changed, 85 insertions, 14 deletions
diff --git a/helix-vcs/src/lib.rs b/helix-vcs/src/lib.rs index 851fd6e9..7225c38e 100644 --- a/helix-vcs/src/lib.rs +++ b/helix-vcs/src/lib.rs @@ -1,6 +1,9 @@ -use anyhow::{bail, Result}; +use anyhow::{anyhow, bail, Result}; use arc_swap::ArcSwap; -use std::{path::Path, sync::Arc}; +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; #[cfg(feature = "git")] pub use git::Git; @@ -14,18 +17,14 @@ mod diff; pub use diff::{DiffHandle, Hunk}; -pub trait DiffProvider { - /// Returns the data that a diff should be computed against - /// if this provider is used. - /// The data is returned as raw byte without any decoding or encoding performed - /// to ensure all file encodings are handled correctly. - fn get_diff_base(&self, file: &Path) -> Result<Vec<u8>>; - fn get_current_head_name(&self, file: &Path) -> Result<Arc<ArcSwap<Box<str>>>>; -} +mod status; + +pub use status::FileChange; #[doc(hidden)] +#[derive(Clone, Copy)] pub struct Dummy; -impl DiffProvider for Dummy { +impl Dummy { fn get_diff_base(&self, _file: &Path) -> Result<Vec<u8>> { bail!("helix was compiled without git support") } @@ -33,10 +32,25 @@ impl DiffProvider for Dummy { fn get_current_head_name(&self, _file: &Path) -> Result<Arc<ArcSwap<Box<str>>>> { bail!("helix was compiled without git support") } + + fn for_each_changed_file( + &self, + _cwd: &Path, + _f: impl Fn(Result<FileChange>) -> bool, + ) -> Result<()> { + bail!("helix was compiled without git support") + } } +impl From<Dummy> for DiffProvider { + fn from(value: Dummy) -> Self { + DiffProvider::Dummy(value) + } +} + +#[derive(Clone)] pub struct DiffProviderRegistry { - providers: Vec<Box<dyn DiffProvider>>, + providers: Vec<DiffProvider>, } impl DiffProviderRegistry { @@ -65,14 +79,71 @@ impl DiffProviderRegistry { } }) } + + /// Fire-and-forget changed file iteration. Runs everything in a background task. Keeps + /// iteration until `on_change` returns `false`. + pub fn for_each_changed_file( + self, + cwd: PathBuf, + f: impl Fn(Result<FileChange>) -> bool + Send + 'static, + ) { + tokio::task::spawn_blocking(move || { + if self + .providers + .iter() + .find_map(|provider| provider.for_each_changed_file(&cwd, &f).ok()) + .is_none() + { + f(Err(anyhow!("no diff provider returns success"))); + } + }); + } } impl Default for DiffProviderRegistry { fn default() -> Self { // currently only git is supported // TODO make this configurable when more providers are added - let git: Box<dyn DiffProvider> = Box::new(Git); - let providers = vec![git]; + let providers = vec![Git.into()]; DiffProviderRegistry { providers } } } + +/// A union type that includes all types that implement [DiffProvider]. We need this type to allow +/// cloning [DiffProviderRegistry] as `Clone` cannot be used in trait objects. +#[derive(Clone)] +pub enum DiffProvider { + Dummy(Dummy), + #[cfg(feature = "git")] + Git(Git), +} + +impl DiffProvider { + fn get_diff_base(&self, file: &Path) -> Result<Vec<u8>> { + match self { + Self::Dummy(inner) => inner.get_diff_base(file), + #[cfg(feature = "git")] + Self::Git(inner) => inner.get_diff_base(file), + } + } + + fn get_current_head_name(&self, file: &Path) -> Result<Arc<ArcSwap<Box<str>>>> { + match self { + Self::Dummy(inner) => inner.get_current_head_name(file), + #[cfg(feature = "git")] + Self::Git(inner) => inner.get_current_head_name(file), + } + } + + fn for_each_changed_file( + &self, + cwd: &Path, + f: impl Fn(Result<FileChange>) -> bool, + ) -> Result<()> { + match self { + Self::Dummy(inner) => inner.for_each_changed_file(cwd, f), + #[cfg(feature = "git")] + Self::Git(inner) => inner.for_each_changed_file(cwd, f), + } + } +} |