mirror of
https://github.com/watchexec/watchexec.git
synced 2024-11-13 07:41:11 +01:00
Compile gitignore patterns
This commit is contained in:
parent
c9da2c133a
commit
fd2edbf11c
4 changed files with 83 additions and 10 deletions
19
Cargo.lock
generated
19
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue