Compile gitignore patterns

This commit is contained in:
Félix Saparelli 2021-10-13 01:49:38 +13:00
parent c9da2c133a
commit fd2edbf11c
No known key found for this signature in database
GPG Key ID: B948C4BAE44FC474
4 changed files with 83 additions and 10 deletions

19
Cargo.lock generated
View File

@ -892,6 +892,24 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "ignore"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d"
dependencies = [
"crossbeam-utils",
"globset",
"lazy_static",
"log",
"memchr",
"regex",
"same-file",
"thread_local",
"walkdir",
"winapi-util",
]
[[package]]
name = "indexmap"
version = "1.7.0"
@ -2651,6 +2669,7 @@ dependencies = [
"futures",
"git2",
"globset",
"ignore",
"miette",
"nom 7.0.0",
"notify",

View File

@ -23,6 +23,7 @@ dunce = "1.0.2"
futures = "0.3.16"
git2 = "0.13.22"
globset = "0.4.8"
ignore = "0.4.18"
miette = "3.2.0"
nom = "7.0.0"
notify = "5.0.0-pre.12"

View File

@ -3,6 +3,7 @@ use std::path::PathBuf;
use std::sync::Arc;
use dunce::canonicalize;
use ignore::gitignore::{Gitignore, GitignoreBuilder};
use tokio::fs::read_to_string;
use tracing::{debug, trace, warn};
use unicase::UniCase;
@ -35,6 +36,12 @@ pub struct TaggedFilterer {
/// All filters that are applied, in order, by matcher.
filters: swaplock::SwapLock<HashMap<Matcher, Vec<Filter>>>,
/// Compiled matcher for Glob filters.
glob_compiled: swaplock::SwapLock<Option<Gitignore>>,
/// Compiled matcher for NotGlob filters.
not_glob_compiled: swaplock::SwapLock<Option<Gitignore>>,
}
impl Filterer for TaggedFilterer {
@ -108,6 +115,8 @@ impl TaggedFilterer {
origin: canonicalize(origin.into())?,
workdir: canonicalize(workdir.into())?,
filters: swaplock::SwapLock::new(HashMap::new()),
glob_compiled: swaplock::SwapLock::new(None),
not_glob_compiled: swaplock::SwapLock::new(None),
}))
}
@ -141,7 +150,12 @@ impl TaggedFilterer {
};
trace!(?resolved, "resolved path to match filter against");
filter.matches(resolved.to_string_lossy())
if matches!(filter.op, Op::Glob | Op::NotGlob) {
todo!("glob match using compiled ignores");
} else {
filter.matches(resolved.to_string_lossy())
}
}
(Tag::FileEventKind(kind), Matcher::FileEventKind) => {
filter.matches(format!("{:?}", kind))
@ -189,7 +203,7 @@ impl TaggedFilterer {
}
})
.await
.map_err(|err| error::TaggedFiltererError::FilterChange { action: "add", err })?;
.map_err(|err| TaggedFiltererError::FilterChange { action: "add", err })?;
if recompile_globs {
self.recompile_globs(Op::Glob).await?;
@ -202,13 +216,46 @@ impl TaggedFilterer {
Ok(())
}
async fn recompile_globs(&self, op_filter: Op) -> Result<(), error::TaggedFiltererError> {
todo!()
// TODO: globs for non-paths???
// globs:
// - use ignore's impl
// - after adding some filters, recompile by making a gitignorebuilder and storing the gitignore
// - use two gitignores: one for NotGlob (which is used for gitignores) and one for Glob (invert results from its matches)
async fn recompile_globs(&self, op_filter: Op) -> Result<(), TaggedFiltererError> {
let target = match op_filter {
Op::Glob => &self.glob_compiled,
Op::NotGlob => &self.not_glob_compiled,
_ => unreachable!("recompile_globs called with invalid op"),
};
let globs = {
let filters = self.filters.borrow();
if let Some(fs) = filters.get(&Matcher::Path) {
// we want to hold the lock as little as possible, so we clone the filters
fs.iter()
.cloned()
.filter(|f| f.op == op_filter)
.collect::<Vec<_>>()
} else {
return target
.replace(None)
.await
.map_err(TaggedFiltererError::GlobsetChange);
}
};
let mut builder = GitignoreBuilder::new(&self.origin);
for filter in globs {
if let Pattern::Glob(glob) = filter.pat {
builder
.add_line(filter.in_path, &glob)
.map_err(TaggedFiltererError::GlobParse)?;
}
}
let compiled = builder.build().map_err(TaggedFiltererError::GlobParse)?;
target
.replace(Some(compiled))
.await
.map_err(TaggedFiltererError::GlobsetChange)
}
pub async fn add_ignore_file(&self, file: &IgnoreFile) -> Result<(), TaggedFiltererError> {

View File

@ -2,6 +2,7 @@
use std::collections::HashMap;
use ignore::gitignore::Gitignore;
use miette::Diagnostic;
use thiserror::Error;
use tokio::sync::watch::error::SendError;
@ -31,7 +32,7 @@ pub enum TaggedFiltererError {
/// Error received when a filter cannot be added or removed from a tagged filter list.
#[error("cannot {action} filter: {err:?}")]
#[diagnostic(code(watchexec::filter::tagged::change))]
#[diagnostic(code(watchexec::filter::tagged::filter_change))]
FilterChange {
action: &'static str,
#[source]
@ -41,7 +42,12 @@ pub enum TaggedFiltererError {
/// Error received when a glob cannot be parsed.
#[error("cannot parse glob: {0}")]
#[diagnostic(code(watchexec::filter::tagged::glob_parse))]
GlobParse(#[from] globset::Error),
GlobParse(#[source] ignore::Error),
/// Error received when a compiled globset cannot be changed.
#[error("cannot change compiled globset: {0:?}")]
#[diagnostic(code(watchexec::filter::tagged::globset_change))]
GlobsetChange(#[source] SendError<Option<Gitignore>>),
}
impl From<TaggedFiltererError> for RuntimeError {