mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-20 11:25:34 +00:00
fileset: add recursive iterator over explicit paths
The primary use case is to warn unmatched paths. I originally thought paths in negated expressions shouldn't be checked, but doing that seems rather inconsistent than useful. For example, "~x" in "jj split '~x'" should match at least one file to split to non-empty revisions.
This commit is contained in:
parent
580a90b694
commit
33beb8d456
1 changed files with 76 additions and 2 deletions
|
@ -16,7 +16,7 @@
|
|||
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::slice;
|
||||
use std::{iter, slice};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use thiserror::Error;
|
||||
|
@ -30,7 +30,7 @@ use crate::matchers::{
|
|||
DifferenceMatcher, EverythingMatcher, FilesMatcher, IntersectionMatcher, Matcher,
|
||||
NothingMatcher, PrefixMatcher, UnionMatcher,
|
||||
};
|
||||
use crate::repo_path::{FsPathParseError, RelativePathParseError, RepoPathBuf};
|
||||
use crate::repo_path::{FsPathParseError, RelativePathParseError, RepoPath, RepoPathBuf};
|
||||
|
||||
/// Error occurred during file pattern parsing.
|
||||
#[derive(Debug, Error)]
|
||||
|
@ -129,6 +129,15 @@ impl FilePattern {
|
|||
let path = RepoPathBuf::from_relative_path(input)?;
|
||||
Ok(FilePattern::PrefixPath(path))
|
||||
}
|
||||
|
||||
/// Returns path if this pattern represents a literal path in a workspace.
|
||||
/// Returns `None` if this is a glob pattern for example.
|
||||
pub fn as_path(&self) -> Option<&RepoPath> {
|
||||
match self {
|
||||
FilePattern::FilePath(path) => Some(path),
|
||||
FilePattern::PrefixPath(path) => Some(path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// AST-level representation of the fileset expression.
|
||||
|
@ -217,6 +226,38 @@ impl FilesetExpression {
|
|||
}
|
||||
}
|
||||
|
||||
fn dfs_pre(&self) -> impl Iterator<Item = &Self> {
|
||||
let mut stack: Vec<&Self> = vec![self];
|
||||
iter::from_fn(move || {
|
||||
let expr = stack.pop()?;
|
||||
match expr {
|
||||
FilesetExpression::None
|
||||
| FilesetExpression::All
|
||||
| FilesetExpression::Pattern(_) => {}
|
||||
FilesetExpression::UnionAll(exprs) => stack.extend(exprs.iter().rev()),
|
||||
FilesetExpression::Intersection(expr1, expr2)
|
||||
| FilesetExpression::Difference(expr1, expr2) => {
|
||||
stack.push(expr2);
|
||||
stack.push(expr1);
|
||||
}
|
||||
}
|
||||
Some(expr)
|
||||
})
|
||||
}
|
||||
|
||||
/// Iterates literal paths recursively from this expression.
|
||||
///
|
||||
/// For example, `"a", "b", "c"` will be yielded in that order for
|
||||
/// expression `"a" | all() & "b" | ~"c"`.
|
||||
pub fn explicit_paths(&self) -> impl Iterator<Item = &RepoPath> {
|
||||
// pre/post-ordering doesn't matter so long as children are visited from
|
||||
// left to right.
|
||||
self.dfs_pre().flat_map(|expr| match expr {
|
||||
FilesetExpression::Pattern(pattern) => pattern.as_path(),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Transforms the expression tree to `Matcher` object.
|
||||
pub fn to_matcher(&self) -> Box<dyn Matcher> {
|
||||
build_union_matcher(self.as_union_all())
|
||||
|
@ -531,6 +572,39 @@ mod tests {
|
|||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_explicit_paths() {
|
||||
let collect = |expr: &FilesetExpression| -> Vec<RepoPathBuf> {
|
||||
expr.explicit_paths().map(|path| path.to_owned()).collect()
|
||||
};
|
||||
let file_expr = |path: &str| FilesetExpression::file_path(repo_path_buf(path));
|
||||
assert!(collect(&FilesetExpression::none()).is_empty());
|
||||
assert_eq!(collect(&file_expr("a")), ["a"].map(repo_path_buf));
|
||||
assert_eq!(
|
||||
collect(&FilesetExpression::union_all(vec![
|
||||
file_expr("a"),
|
||||
file_expr("b"),
|
||||
file_expr("c"),
|
||||
])),
|
||||
["a", "b", "c"].map(repo_path_buf)
|
||||
);
|
||||
assert_eq!(
|
||||
collect(&FilesetExpression::intersection(
|
||||
FilesetExpression::union_all(vec![
|
||||
file_expr("a"),
|
||||
FilesetExpression::none(),
|
||||
file_expr("b"),
|
||||
file_expr("c"),
|
||||
]),
|
||||
FilesetExpression::difference(
|
||||
file_expr("d"),
|
||||
FilesetExpression::union_all(vec![file_expr("e"), file_expr("f")])
|
||||
)
|
||||
)),
|
||||
["a", "b", "c", "d", "e", "f"].map(repo_path_buf)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_matcher_simple() {
|
||||
insta::assert_debug_snapshot!(FilesetExpression::none().to_matcher(), @"NothingMatcher");
|
||||
|
|
Loading…
Reference in a new issue