Now template aliases are fully supported in log and formatter templates.
As I said before, aliases are not expanded in map files. This avoids possible
corruption of our stock styles and web templates. This behavior is undocumented
since no map file nor [templates] section are documented at all. Later on,
we might want to add [aliases] section to map files if it appears to be useful.
The debugtemplate command is updated to show expanded tree, but still the
template engine doesn't support alias expansion. That's why the test says
"parse error" for now.
This patch also adds loadfunction() to templater, because this
combination helps to figure out how they cooperate with each other.
Listing up loadfunction() in dispatch.extraloaders causes implicit
loading template function at loading (3rd party) extension.
This patch explicitly tests whether templatefunc decorator works as
expected, because there is no bundled extension, which defines
template function.
This change requires that "templatefunc" attribute of (3rd party)
extension is registrar.templatefunc or so.
Before this patch, the first and last characters were stripped from
ui.logtemplate and template.* if they were the same. It could lead to a
strange result as quotes are optional. See the test for example.
SyntaxError is the class representing syntax errors in Python code. We should
use a dedicated exception class for our needs. With this change, unnecessary
re-wrapping of SyntaxError can be eliminated.
Now compiled template fragments are packed into a generic type, (func, data),
a string can be a valid template. This change allows us to unwrap a trivial
string node. See the next patch for details.
Instead of the mapping hack introduced by d4686e0c15c9, this patch changes the
way how a label symbol is evaluated. This is still hackish, but should be more
predictable in that it doesn't depend on the known color effects.
This change is intended to eliminate the reference to color._effects so that
color.templatelabel() can be merged with templater.label().
Before this, "{noniterable % template}" raised an exception. This tries to
provide a better indication for the common case, where a left-hand-side
expression is a keyword.
A function argument may be an integer. In this case, it isn't necessary to
convert a value to string and back to integer.
Because an argument may be an arbitrary object (e.g. date tuple), TypeError
should be caught as well.
If a key is constructed from a template expression, it may be a generator.
In that case, a key have to be stringified.
A dictarg should never be a generator, but this patch also changes it to
call evalfuncarg() for consistency.
This is the same semantics as revset() introduced at eef3c19484ca. Before
this patch, {parents} provided nothing useful in new-style template. For
example, '{parents % "{parent}"}' generated cryptic string like
"rev12345node0123abcdef...".
This patch drops {parent} variable since it was useless. We can get a revision
number by '{parents % "{rev}"}'.
This is necessary to preserve filename encoding over JSON. Instead, this
patch inserts "|utf8" where non-ascii local-encoding texts can be passed
to "|json".
See also the commit that introduced "utf8" filter.
This will be applied prior to "|json" filter. This sounds like odd, but it
is necessary to handle local-encoding text as well as raw filename bytes.
Because filenames are bytes in Mercurial and Unix world, {filename|json} should
preserve the original byte sequence, which implies
{x|json} -> '"' toutf8b(x) '"'
On the other hand, most template strings are in local encoding. Because
"|json" filter have to be byte-transparent to filenames, we need something to
annotate an input as a local string, that's what "|utf8" will do.
{x|utf8|json} -> '"' toutf8b(fromlocal(x)) '"'
"|utf8" is an explicit call, so aborts if input bytes can't be converted to
UTF-8.
Because templater.revset() returns a list of strings, repo["-1"] was mapped to
the tipmost revision. Ideally, we should make revset() return a list of integer
revisions, but it turned out not simple. If revset() is a list of integers,
"{ifcontains(rev, revset(), ...)}" would fail because "ifcontains" casts "rev"
to a string.
So this patch just converts a string back to an integer revision.
Before this patch, test-command-template.t is timed out on Solaris,
because "rm" on permission denied file implies prompting "override
protection 0 (yes/no)?" and blocks execution of test script.
In this case, a template is parsed recursively with no thunk for lazy
evaluation. This patch prevents recursion by putting a dummy of the same name
into a cache that will be referenced while parsing if there's a recursion.
changeset = {files % changeset}\n
~~~~~~~~~
= [(_runrecursivesymbol, 'changeset')]
It would be nice if we could detect recursion at the parsing phase, but we
can't because a template can refer to a keyword of the same name. For example,
"rev = {rev}" is valid if rev is a keyword, and we don't know if rev is a
keyword or a template while parsing.
This provides a general-purpose interface to all custom namespaces.
The {namespaces} keyword honors the definition order of namespaces as they
are kept by sortdict.
As JSON string is known to be a unicode, we should try round-trip conversion
for localstr type. This patch tests localstr type explicitly because
encoding.fromlocal() may raise Abort for undecodable str, which is probably
not what we want. Maybe we can refactor json filter to use encoding module
more later.
Still "{desc|json}" can't round-trip because showdescription() modifies a
localstr object.
This allows the latest class of tag to be found, such as a release candidate or
final build, instead of just the absolute latest.
It doesn't appear that the existing keyword can be given an optional argument.
There is a keyword, function and filter for 'date', so it doesn't seem harmful
to introduce a new function with the same name as an existing keyword. Most
functions are pretty Mercurial agnostic, but there is {revset()} as precedent.
Even though templatekw.getlatesttags() returns a single tuple, one entry of
which is a list, it is simplest to present this as a list of tags instead of a
single item, with each tag having a distance and change count attribute. It is
also closer to how {latesttag} returns a list of tags, and how this function
works when not given a '%' operator.
Because revset() function generates a list of revisions, it seems sensible
to switch the ctx as well where a list expression will be evaluated. I think
"{revset(...) % "..."}" expression wasn't considered well when it was
introduced at 45e0e191755f.
The keyword extension uses "utcdate" for a different function, so we can't
add new "utcdate" filter or function. Instead, this patch extends "localdate"
to a general timezone converter.
GNU grep allows you to use "a\|b" in a regular expression to match either
"a" or "b", but at least Solaris grep does not; only egrep allows for that.
And egrep considers "a+" to be "a{1,}" instead of an "a" and a literal plus
sign, so escape that as well.
Instead of re-parsing quoted strings as templates, the tokenizer can delegate
the parsing of nested template strings to the parser. It has two benefits:
1. syntax errors can be reported with absolute positions
2. nested template can use quotes just like shell: "{"{rev}"}"
It doesn't sound nice that the tokenizer recurses into the parser. We could
instead make the tokenize itself recursive, but it would be much more
complicated because we would have to adjust binding strengths carefully and
put dummy infix operators to concatenate template fragments.
Now "string" token without r"" never appears. It will be removed by the next
patch.
Because we want to eliminate "if"s in the default template, it makes sense to
display wdirrev/wdirnode values for now. wdir() is still experimental, so the
output of "log -r'wdir()'" may change in future.
wdirrev/wdirnode identifiers are still experimental, but {node} is mapped to
wdirnode. So {rev} should do the same for consistency.
I'm not sure if templatekw can import scmutil. If not, we should move intrev()
to node module.
This will prevent crash by "hg log -r 'wdir()' -Tdefault". We could use the
pseudo ff... hash introduced by 187c3ec3d83f, but it isn't proven idea yet.
For now, I want to make "hg log" just works in order to test 'wdir()' revset.
Note that unlike its name, "{manifest}" is not a list of files in that
revision, but a pair of (manifestrev, manifestnode).
This patch backs out 297d563e92af which should no longer be needed.
The test for '{\"invalid\"}' is removed because the parser is permissive for
\"...\" literal.
As of Mercurial 3.4, there were several syntax rules to process nested
template strings. Unfortunately, they were inconsistent and conflicted
each other.
a. buildmap() rule
- template string is _parsed_ as string, and parsed as template
- <\"> is not allowed in nested template:
{xs % "{f(\"{x}\")}"} -> parse error
- template escaping <\{> is handled consistently:
{xs % "\{x}"} -> escaped
b. _evalifliteral() rule
- template string is _interpreted_ as string, and parsed as template
in crafted environment to avoid double processing of escape sequences
- <\"> is allowed in nested template:
{if(x, "{f(\"{x}\")}")}
- <\{> and escape sequences in string literal in nested template are not
handled well
c. pad() rule
- template string is first interpreted as string, and parsed as template,
which means escape sequences are processed twice
- <\"> is allowed in nested template:
{pad("{xs % \"{x}\"}', 10)}
Because of the issue of template escaping, issue4714, 56e0b66a4c27 (in stable)
unified the rule (b) to (a). Then, 41e044cfb1ef (in default) unified the rule
(c) to (b) = (a). But they disabled the following syntax that was somewhat
considered valid.
{if(rev, "{if(rev, \"{rev}\")}")}
{pad("{files % \"{file}\"}", 10)}
So, this patch introduces \"...\" literal to work around the escaped-quoted
nested template strings. Because this parsing rule exists only for the backward
compatibility, it is designed to copy the behavior of old _evalifliteral() as
possible.
Future patches will introduce a better parsing rule similar to a command
substitution of POSIX shells or a string interpolation of Ruby, where extra
escapes won't be necessary at all.
{pad("{files % "{file}"}", 10)}
~~~~~~~~~~~~~~~~~~
parsed as a template, not as a string
Because <\> character wasn't allowed in a template fragment, this patch won't
introduce more breakages. But the syntax of nested templates are interpreted
differently by people, there might be unknown issues. So if we want, we could
instead remove e926f2ef639a, 72be08a15d8d and 56e0b66a4c27 from the stable
branch as the bug fixed by these patches existed for longer periods.
554d6fcc3c8, "strip single backslash before quotation mark in quoted template",
should be superseded by this patch. I'll remove it later.