revset: add support for file(kind:pattern) syntax

There are no more callers of parse_function_argument_to_string(), so it's
removed. This function was a thin wrapper of literal parser, and can be
easily reintroduced if needed.
This commit is contained in:
Yuya Nishihara 2024-04-05 22:04:32 +09:00
parent 850887cf09
commit 8b32a8a916
4 changed files with 42 additions and 23 deletions

View file

@ -70,6 +70,6 @@ tags()+++::
merges() merges()
~merges() ~merges()
# These are unbearably slow, so only filter within small set # These are unbearably slow, so only filter within small set
file(Makefile) & v1.0.0..v1.2.0 file(root:"Makefile") & v1.0.0..v1.2.0
empty() & v1.0.0..v1.2.0 empty() & v1.0.0..v1.2.0
conflict() & v1.0.0..v1.2.0 conflict() & v1.0.0..v1.2.0

View file

@ -152,15 +152,28 @@ fn test_bad_function_call() {
= Function "file": Expected at least 1 argument = Function "file": Expected at least 1 argument
"###); "###);
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "file(a, not:a-string)"]); let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "file(a, not@a-string)"]);
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r###"
Error: Failed to parse revset: Function "file": Expected function argument of type string Error: Failed to parse revset: Function "file": Expected function argument of file pattern
Caused by: --> 1:9 Caused by: --> 1:9
| |
1 | file(a, not:a-string) 1 | file(a, not@a-string)
| ^----------^ | ^----------^
| |
= Function "file": Expected function argument of type string = Function "file": Expected function argument of file pattern
"###);
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", r#"file(foo:"bar")"#]);
insta::assert_snapshot!(stderr, @r###"
Error: Failed to parse revset: Function "file": Invalid file pattern
Caused by:
1: --> 1:6
|
1 | file(foo:"bar")
| ^-------^
|
= Function "file": Invalid file pattern
2: Invalid file pattern kind "foo:"
"###); "###);
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", r#"file(a, "../out")"#]); let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", r#"file(a, "../out")"#]);

View file

@ -156,9 +156,8 @@ given [string pattern](#string-patterns).
* `empty()`: Commits modifying no files. This also includes `merges()` without * `empty()`: Commits modifying no files. This also includes `merges()` without
user modifications and `root()`. user modifications and `root()`.
* `file(relativepath)` or `file("relativepath"[, "relativepath"]...)`: Commits * `file(pattern[, pattern]...)`: Commits modifying paths matching one of the
modifying one of the paths specified. Currently, string patterns are *not* given [file patterns](filesets.md#file-patterns).
supported in the path arguments.
Paths are relative to the directory `jj` was invoked from. A directory name Paths are relative to the directory `jj` was invoked from. A directory name
will match all files in that directory and its subdirectories. will match all files in that directory and its subdirectories.

View file

@ -33,13 +33,12 @@ use thiserror::Error;
use crate::backend::{BackendError, BackendResult, ChangeId, CommitId}; use crate::backend::{BackendError, BackendResult, ChangeId, CommitId};
use crate::commit::Commit; use crate::commit::Commit;
use crate::fileset::FilesetExpression; use crate::fileset::{FilePattern, FilesetExpression, FilesetParseContext};
use crate::git; use crate::git;
use crate::hex_util::to_forward_hex; use crate::hex_util::to_forward_hex;
use crate::object_id::{HexPrefix, PrefixResolution}; use crate::object_id::{HexPrefix, PrefixResolution};
use crate::op_store::WorkspaceId; use crate::op_store::WorkspaceId;
use crate::repo::Repo; use crate::repo::Repo;
use crate::repo_path::RepoPathBuf;
use crate::revset_graph::RevsetGraphEdge; use crate::revset_graph::RevsetGraphEdge;
use crate::store::Store; use crate::store::Store;
use crate::str_util::StringPattern; use crate::str_util::StringPattern;
@ -1339,18 +1338,14 @@ static BUILTIN_FUNCTION_MAP: Lazy<HashMap<&'static str, RevsetFunction>> = Lazy:
map.insert("file", |name, arguments_pair, state| { map.insert("file", |name, arguments_pair, state| {
let arguments_span = arguments_pair.as_span(); let arguments_span = arguments_pair.as_span();
if let Some(ctx) = state.workspace_ctx { if let Some(ctx) = state.workspace_ctx {
let ctx = FilesetParseContext {
cwd: ctx.cwd,
workspace_root: ctx.workspace_root,
};
let file_expressions: Vec<_> = arguments_pair let file_expressions: Vec<_> = arguments_pair
.into_inner() .into_inner()
.map(|arg| -> Result<_, RevsetParseError> { .map(|arg| parse_function_argument_to_file_pattern(name, arg, state, &ctx))
let span = arg.as_span(); .map_ok(FilesetExpression::pattern)
let needle = parse_function_argument_to_string(name, arg, state)?;
let path = RepoPathBuf::parse_fs_path(ctx.cwd, ctx.workspace_root, needle)
.map_err(|e| {
RevsetParseError::invalid_arguments(name, "Invalid file pattern", span)
.with_source(e)
})?;
Ok(FilesetExpression::prefix_path(path))
})
.try_collect()?; .try_collect()?;
if file_expressions.is_empty() { if file_expressions.is_empty() {
Err(RevsetParseError::invalid_arguments( Err(RevsetParseError::invalid_arguments(
@ -1497,12 +1492,17 @@ fn expect_named_arguments_vec<'i>(
Ok((required, optional)) Ok((required, optional))
} }
fn parse_function_argument_to_string( fn parse_function_argument_to_file_pattern(
name: &str, name: &str,
pair: Pair<Rule>, pair: Pair<Rule>,
state: ParseState, state: ParseState,
) -> Result<String, RevsetParseError> { ctx: &FilesetParseContext,
parse_function_argument_as_literal("string", name, pair, state) ) -> Result<FilePattern, RevsetParseError> {
let parse_pattern = |value: &str, kind: Option<&str>| match kind {
Some(kind) => FilePattern::from_str_kind(ctx, value, kind),
None => FilePattern::cwd_prefix_path(ctx, value),
};
parse_function_argument_as_pattern("file pattern", name, pair, state, parse_pattern)
} }
fn parse_function_argument_to_string_pattern( fn parse_function_argument_to_string_pattern(
@ -2587,6 +2587,7 @@ mod tests {
use assert_matches::assert_matches; use assert_matches::assert_matches;
use super::*; use super::*;
use crate::repo_path::RepoPathBuf;
fn parse(revset_str: &str) -> Result<Rc<RevsetExpression>, RevsetParseErrorKind> { fn parse(revset_str: &str) -> Result<Rc<RevsetExpression>, RevsetParseErrorKind> {
parse_with_aliases(revset_str, [] as [(&str, &str); 0]) parse_with_aliases(revset_str, [] as [(&str, &str); 0])
@ -3343,6 +3344,12 @@ mod tests {
FilesetExpression::prefix_path(RepoPathBuf::from_internal_string("foo")) FilesetExpression::prefix_path(RepoPathBuf::from_internal_string("foo"))
))) )))
); );
assert_eq!(
parse_with_workspace(r#"file(file:"foo")"#, &WorkspaceId::default()),
Ok(RevsetExpression::filter(RevsetFilterPredicate::File(
FilesetExpression::file_path(RepoPathBuf::from_internal_string("foo"))
)))
);
assert_eq!( assert_eq!(
parse_with_workspace("file(foo, bar, baz)", &WorkspaceId::default()), parse_with_workspace("file(foo, bar, baz)", &WorkspaceId::default()),
Ok(RevsetExpression::filter(RevsetFilterPredicate::File( Ok(RevsetExpression::filter(RevsetFilterPredicate::File(