A lambda expression will be allowed only in .map() operation. The syntax is
borrowed from Rust closure.
In Mercurial, a map operation is implemented by context substitution. For
example, 'parents % "{node}"' prints parents[i].node for each. There are two
major problems: 1. the top-level context cannot be referred from the inner map
expression. 2. context of different types inserts arbitrarily-named keywords
(e.g. a dict type inserts "{key}" and "{value}", but how we could know.)
These issues should be avoided by using explicitly named parameters.
parents.map(|parent| parent.commit_id ++ " " ++ commit_id)
^^^^^^^^^ global keyword
A downside is that we can't reuse template fragment in map expression. Suppose
we have -T commit_summary, -T 'parents.map(commit_summary)' doesn't work.
# only usable as a top-level template
'commit_summary' = 'commit_id.short() ++ " " ++ description.first_line()'
Another problem is that a lambda expression might be confused with an alias
function.
# .map(f) doesn't work, but .map(g) does
'f(x)' = 'x'
'g' = '|x| x'
It's getting confusing since we now have a list property type.
expand/normalize_list() functions aren't renamed since they are also applied
to a list of function arguments.
This eliminates ambiguous parsing between "func()" and "expr ()".
I chose "++" as template concatenation operator in case we want to add
bit-wise negate operator. It's also easier to find/replace than "~".
This is basically a copy of revset::RevsetAliasesMap. We could extract a
common table struct, but that wouldn't be worth the effort since the core
alias substitution logic can't be easily abstracted.
This will be used as a parameter of id.shortest*() methods. For now, only
decimal literal is supported, and there's no unary negate operator.
"0"-prefix is disallowed because it looks like an octal number.
I don't think we would want multiple integer types in the template language,
so I chose i64 as the integer type of reasonable width.
Before, "f()" was parsed as a function call with one empty argument. In
practice, this change means "if(divergent,,false_template)" is no longer
valid.
Suppose "template" is a sequence of "term"s, it makes more sense to handle
an empty sequence there. It might be even better to disallow empty template
other than the top-level one.
A "list" is a sequence of more than one "term" nodes, so it shouldn't contain
a single parenthesized node.
Also, a parenthesized "term" rule wasn't handled.
A method call is typically parsed as (obj.meth)(), not as obj.(meth()),
but the latter is good enough for our needs. It's unlikely we'll add a
first-class function support.
.into_inner().next().unwrap() mess will be cleaned up by the next commit.
Let's acknowledge everyone's contributions by replacing "Google LLC"
in the copyright header by "The Jujutsu Authors". If I understand
correctly, it won't have any legal effect, but maybe it still helps
reduce concerns from contributors (though I haven't heard any
concerns).
Google employees can read about Google's policy at
go/releasing/contributions#copyright.