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.rs99
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),
+ }
+ }
+}