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.rs127
1 files changed, 23 insertions, 104 deletions
diff --git a/helix-vcs/src/lib.rs b/helix-vcs/src/lib.rs
index 4c5f2036..97320d32 100644
--- a/helix-vcs/src/lib.rs
+++ b/helix-vcs/src/lib.rs
@@ -1,13 +1,9 @@
-//! `helix_vcs` provides types for working with diffs from a Version Control System (VCS).
-//! Currently `git` is the only supported provider for diffs, but this architecture allows
-//! for other providers to be added in the future.
+use std::path::Path;
-use anyhow::{anyhow, bail, Result};
-use arc_swap::ArcSwap;
-use std::{
- path::{Path, PathBuf},
- sync::Arc,
-};
+#[cfg(feature = "git")]
+pub use git::Git;
+#[cfg(not(feature = "git"))]
+pub use Dummy as Git;
#[cfg(feature = "git")]
mod git;
@@ -16,64 +12,31 @@ mod diff;
pub use diff::{DiffHandle, Hunk};
-mod status;
+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) -> Option<Vec<u8>>;
+}
-pub use status::FileChange;
+#[doc(hidden)]
+pub struct Dummy;
+impl DiffProvider for Dummy {
+ fn get_diff_base(&self, _file: &Path) -> Option<Vec<u8>> {
+ None
+ }
+}
-/// Contains all active diff providers. Diff providers are compiled in via features. Currently
-/// only `git` is supported.
-#[derive(Clone)]
pub struct DiffProviderRegistry {
- providers: Vec<DiffProvider>,
+ providers: Vec<Box<dyn DiffProvider>>,
}
impl DiffProviderRegistry {
- /// Get the given file from the VCS. This provides the unedited document as a "base"
- /// for a diff to be created.
pub fn get_diff_base(&self, file: &Path) -> Option<Vec<u8>> {
self.providers
.iter()
- .find_map(|provider| match provider.get_diff_base(file) {
- Ok(res) => Some(res),
- Err(err) => {
- log::debug!("{err:#?}");
- log::debug!("failed to open diff base for {}", file.display());
- None
- }
- })
- }
-
- /// Get the current name of the current [HEAD](https://stackoverflow.com/questions/2304087/what-is-head-in-git).
- pub fn get_current_head_name(&self, file: &Path) -> Option<Arc<ArcSwap<Box<str>>>> {
- self.providers
- .iter()
- .find_map(|provider| match provider.get_current_head_name(file) {
- Ok(res) => Some(res),
- Err(err) => {
- log::debug!("{err:#?}");
- log::debug!("failed to obtain current head name for {}", file.display());
- None
- }
- })
- }
-
- /// 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")));
- }
- });
+ .find_map(|provider| provider.get_diff_base(file))
}
}
@@ -81,52 +44,8 @@ impl Default for DiffProviderRegistry {
fn default() -> Self {
// currently only git is supported
// TODO make this configurable when more providers are added
- let providers = vec![
- #[cfg(feature = "git")]
- DiffProvider::Git,
- DiffProvider::None,
- ];
+ let git: Box<dyn DiffProvider> = Box::new(Git);
+ let providers = vec![git];
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.
-///
-/// `Copy` is simply to ensure the `clone()` call is the simplest it can be.
-#[derive(Copy, Clone)]
-enum DiffProvider {
- #[cfg(feature = "git")]
- Git,
- None,
-}
-
-impl DiffProvider {
- fn get_diff_base(&self, file: &Path) -> Result<Vec<u8>> {
- match self {
- #[cfg(feature = "git")]
- Self::Git => git::get_diff_base(file),
- Self::None => bail!("No diff support compiled in"),
- }
- }
-
- fn get_current_head_name(&self, file: &Path) -> Result<Arc<ArcSwap<Box<str>>>> {
- match self {
- #[cfg(feature = "git")]
- Self::Git => git::get_current_head_name(file),
- Self::None => bail!("No diff support compiled in"),
- }
- }
-
- fn for_each_changed_file(
- &self,
- cwd: &Path,
- f: impl Fn(Result<FileChange>) -> bool,
- ) -> Result<()> {
- match self {
- #[cfg(feature = "git")]
- Self::Git => git::for_each_changed_file(cwd, f),
- Self::None => bail!("No diff support compiled in"),
- }
- }
-}