revsets: allow the .. operator to be used as prefix or suffix (#46)

It makes sense to omit either of the arguments of the `..` operator,
even though `..x` is equivalent to `:x`. `x..`, with a implied right
argument of `heads()` is more useful.
This commit is contained in:
Martin von Zweigbergk 2021-12-15 21:57:55 -08:00
parent bc92b82ed4
commit 7d3d0fe83c
3 changed files with 27 additions and 6 deletions

View file

@ -68,6 +68,9 @@ only symbols.
* `x..y`: Ancestors of `y` that are not also ancestors of `x`, both inclusive.
Equivalent to `:y ~ :x`. This is what `git log` calls `x..y` (i.e. the same as
we call it).
* `..x`: Ancestors of `x`, including the commits in `x` itself. Equivalent to
`:x` and provided for consistency.
* `x..`: Revisions that are not ancestors of `x`.
You can use parentheses to control evaluation order, such as `(x & y) | z` or
`x & (y | z)`.
@ -113,7 +116,7 @@ jj log -r @-
Show commits not on any remote branch:
```
jj log -r 'remote_branches()..all()'
jj log -r 'remote_branches()..'
```
Show all ancestors of the working copy (almost like plain `git log`)

View file

@ -52,7 +52,9 @@ neighbors_expression = { primary ~ (parents_op | children_op)* }
range_expression = {
neighbors_expression ~ dag_range_op ~ neighbors_expression
| neighbors_expression ~ range_op ~ neighbors_expression
| neighbors_expression ~ range_op
| dag_range_op ~ neighbors_expression
| range_op ~ neighbors_expression
| neighbors_expression ~ dag_range_op
| neighbors_expression
}

View file

@ -406,7 +406,7 @@ fn parse_range_expression_rule(
) -> Result<Rc<RevsetExpression>, RevsetParseError> {
let first = pairs.next().unwrap();
match first.as_rule() {
Rule::dag_range_op => {
Rule::dag_range_op | Rule::range_op => {
return Ok(
parse_neighbors_expression_rule(pairs.next().unwrap().into_inner())?.ancestors(),
);
@ -431,9 +431,13 @@ fn parse_range_expression_rule(
}
}
Rule::range_op => {
let expression2 =
parse_neighbors_expression_rule(pairs.next().unwrap().into_inner())?;
expression = expression.range(&expression2);
if let Some(heads_pair) = pairs.next() {
let heads_expression =
parse_neighbors_expression_rule(heads_pair.into_inner())?;
expression = expression.range(&heads_expression);
} else {
expression = expression.range(&RevsetExpression::heads());
}
}
_ => {
panic!("unxpected revset range operator rule {:?}", next.as_rule());
@ -1334,6 +1338,13 @@ mod tests {
assert_eq!(parse("@:"), Ok(checkout_symbol.descendants()));
// Parse the "dag range" operator
assert_eq!(parse("foo:bar"), Ok(foo_symbol.dag_range_to(&bar_symbol)));
// Parse the "range" prefix operator
assert_eq!(parse("..@"), Ok(checkout_symbol.ancestors()));
assert_eq!(
parse("@.."),
Ok(checkout_symbol.range(&RevsetExpression::heads()))
);
assert_eq!(parse("foo..bar"), Ok(foo_symbol.range(&bar_symbol)));
// Parse the "intersection" operator
assert_eq!(parse("foo & bar"), Ok(foo_symbol.intersection(&bar_symbol)));
// Parse the "union" operator
@ -1371,13 +1382,18 @@ mod tests {
parse("foo+++"),
Ok(foo_symbol.children().children().children())
);
// Parse repeated "ancestors"/"descendants"/"dag range" operators
// Parse repeated "ancestors"/"descendants"/"dag range"/"range" operators
assert_matches!(parse(":foo:"), Err(RevsetParseError::SyntaxError(_)));
assert_matches!(parse("::foo"), Err(RevsetParseError::SyntaxError(_)));
assert_matches!(parse("foo::"), Err(RevsetParseError::SyntaxError(_)));
assert_matches!(parse("foo::bar"), Err(RevsetParseError::SyntaxError(_)));
assert_matches!(parse(":foo:bar"), Err(RevsetParseError::SyntaxError(_)));
assert_matches!(parse("foo:bar:"), Err(RevsetParseError::SyntaxError(_)));
assert_matches!(parse("....foo"), Err(RevsetParseError::SyntaxError(_)));
assert_matches!(parse("foo...."), Err(RevsetParseError::SyntaxError(_)));
assert_matches!(parse("foo.....bar"), Err(RevsetParseError::SyntaxError(_)));
assert_matches!(parse("..foo..bar"), Err(RevsetParseError::SyntaxError(_)));
assert_matches!(parse("foo..bar.."), Err(RevsetParseError::SyntaxError(_)));
// Parse combinations of "parents"/"children" operators and the range operators.
// The former bind more strongly.
assert_eq!(parse("foo-+"), Ok(foo_symbol.parents().children()));