Commit Graph

351 Commits

Author SHA1 Message Date
Yuya Nishihara
2a17199870 templater: extend dot operator as a short for get(dict, key) 2017-09-18 23:07:17 +09:00
Yuya Nishihara
6215164ca2 templater: add dot operator to easily access a sub item
This and the next patch will allow us to access a deeply-nested item
by foo.bar.baz syntax.
2017-09-09 19:32:56 +09:00
Yuya Nishihara
366ad4616f templater: wrap get/min/max result so map operation can apply to element
See the test for usage example.

wraphybridvalue() takes a key/value pair because a hybrid dict passes a key
to its makemap() function. Since makemap() of showmanifest() doesn't need
a key, it's set to None.
2017-09-09 19:13:25 +09:00
Yuya Nishihara
a5c1e61fda templater: add experimental support for extdata
This is minimal and non-controversial implementation of extdata() template
function. Originally extdata sources were exposed to the keyword namespace,
but I've changed it to a plain function for simplicity.
2017-10-01 11:13:09 +01:00
Pulkit Goyal
64c3d74820 py3: use pycompat.strkwargs() before passing a dict as keyword argument
Differential Revision: https://phab.mercurial-scm.org/D854
2017-09-30 15:46:36 +05:30
Yuya Nishihara
7dafbbbbcf templatekw: add new-style template expansion to {manifest}
The goal is to allow us to easily access to nested data. The dot operator
will be introduced later so we can write '{p1.files}' instead of
'{revset("p1()") % "{files}"}' for example.

In the example above, 'p1' needs to carry a mapping dict along with its
string representation. If it were a list or a dict, it could be wrapped
semi-transparently with the _hybrid class, but for non-list/dict types,
it would be difficult to proxy all necessary functions to underlying value
type because several core operations may conflict with the ones of the
underlying value:

 - hash(value) should be different from hash(wrapped(value)), which means
   dict[wrapped(value)] would be invalid
 - 'value == wrapped(value)' would be false, breaks 'ifcontains'
 - len(wrapped(value)) may be either len(value) or len(iter(wrapped(value)))

So the wrapper has no proxy functions and its scope designed to be minimal.
It's unwrapped at eval*() functions so we don't have to care for a wrapped
object unless it's really needed:

  # most template functions just call evalfuncarg()
  unwrapped_value = evalfuncarg(context, mapping, args[n])
  # if wrapped value is needed, use evalrawexp()
  maybe_wrapped_value = evalrawexp(context, mapping, args[n])

Another idea was to wrap every template variable with a tagging class, but
which seemed uneasy without a static type checker.

This patch updates {manifest} to a mappable as an example.
2016-04-24 18:41:23 +09:00
Yuya Nishihara
c22459dc5d templater: adjust binding strength of '%' and '|' operators (BC)
This makes 'foo|bar%baz' parsed as '(foo|bar)%baz', not 'foo|(bar%baz)'.
Perhaps it was a mistake that '%' preceded '|'. Both '|' and '%' can be
considered a kind of function application, and '|' is more like a '.' operator
seen in OO languages. So IMHO '|' should have the same (or higher) binding as
'%'.

The BC breakage should be minimal since both '|' and '%' operators have
strict requirements for their operands and 'foo|bar%baz' was invalid:

 - right-hand side of '|' must be a symbol
 - left-hand side of '%' must be a dict or list
 - right-hand side of '%' must be a string or symbol
2017-04-24 21:37:11 +09:00
Yuya Nishihara
d5b31456c5 templatekw: just pass underlying value (or key) to joinfmt() function
Before, iter(hybrid) was proxied to hybrid.gen, which generated formatted
strings. That's why we had to apply joinfmt() to the dicts generated by
hybrid.itermaps(). Since this weird API was fixed at 1906bcc0f923, we can
get rid of the makemap() calls from join().
2017-09-24 15:22:46 +09:00
Yuya Nishihara
d370de84cb templater: extract helper to just evaluate template expression
A named function can be easily grepped and is probably good for code
readability.
2017-09-02 23:13:54 +09:00
Yuya Nishihara
2fd6f8e0a3 templater: do not destructure operands in buildmap()
This makes the next patch slightly simpler.
2017-09-02 23:09:34 +09:00
Yuya Nishihara
fae6e2d4b0 templater: use helper function to get name of non-iterable keyword 2017-09-09 19:01:18 +09:00
Martin von Zweigbergk
2881701c97 templates: introduce a obsfateoperation() function
Differential Revision: https://phab.mercurial-scm.org/D723
2017-09-15 10:43:22 -07:00
Martin von Zweigbergk
cb781eb5fa templater: extract shortest() logic from template function
It can be useful for extensions to be able to produce the shortest
unambiguous hash (including the in-tree "show" extension). That logic
is currently inside the shortest() template function. Let's move it
out of the templater. I've put it on revlog since it's closely related
to revlog._partialmatch. We may also want a convenience method on
context, but I'll leave that for a later patch.

Differential Revision: https://phab.mercurial-scm.org/D724
2017-09-15 00:01:57 -07:00
Yuya Nishihara
69001a6da3 doctest: coerce dict.keys() to list
Otherwise it would be printed as odict_keys([...]) on Python 3.
2017-09-03 17:33:10 +09:00
Yuya Nishihara
dcc07e5503 doctest: use print_function and convert bytes to unicode where needed 2017-09-03 14:56:31 +09:00
Yuya Nishihara
a71f259bd2 doctest: bulk-replace string literals with b'' for Python 3
Our code transformer can't rewrite string literals in docstrings, and I
don't want to make the transformer more complex.
2017-09-03 14:32:11 +09:00
Yuya Nishihara
f0fc1531a4 parser: stabilize output of prettyformat() by using byte-safe repr()
The format of leaf nodes is slightly changed so they look more similar to
internal nodes.
2017-09-03 21:17:25 +09:00
Yuya Nishihara
84faf498bc py3: use bytes[n:n + 1] to get bytes in templater._parsetemplate() 2017-09-03 17:37:17 +09:00
Boris Feld
f0beef3fad template: compute dates in obsfatedate
Extract the dates from obsmarkers. Compute the min and max date from the
obsmarker range list.
2017-07-03 15:34:10 +02:00
Boris Feld
6c65874220 template: compute user in obsfateusers
Extract, deduplicate users informations from obs markers in order to display
them.

Print all users for the moment, we might want to display users only in verbose
mode later.
2017-07-03 15:34:00 +02:00
Boris Feld
26b4084486 template: compute verb in obsfateverb
Add a template function obsfateverb which use the markers information to
compute a better obsfate verb.

The current logic behind the obsfate verb is simple for the moment:

- If the successorsets is empty, the changeset has been pruned, for example:

    Obsfate: pruned

- If the successorsets length is 1, the changeset has been rewritten without
  divergence, for example:

    Obsfate: rewritten as 2:337fec4d2edc, 3:f257fde29c7a

- If the successorsets length is more than 1, the changeset has diverged, for
  example:

    Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a

As the divergence might occurs on a subset of successors, we might see some
successors twice:

    Obsfate: split as 9:0b997eb7ceee, 5:dd800401bd8c, 10:eceed8f98ffc; split
    as 8:b18bc8331526, 5:dd800401bd8c, 10:eceed8f98ffc
2017-07-03 15:33:27 +02:00
Gregory Szorc
64adaa7b62 revset: pass repo when passing ui
The repo instance is currently only used to provide a changeset
lookup function as part of parsing revsets. I /think/ this allows
node fragments to resolve. I'm not sure why we wouldn't want this
to always "just work" if parsing a revset string.

Plus, an upcoming commit will introduce a new consumer that needs a
handle on the repo. So passing it more often will make that code
work more.

Passing a repo instance in all callers of revset.match* results in
a bunch of test changes. Notably, branch and tags caches get
populated as part of evaluating revsets. I'm not sure if this is
desirable. So this patch takes the conservative approach and only
passes the repo if we're passing a ui instance.

Differential Revision: https://phab.mercurial-scm.org/D97
2017-07-15 15:51:57 -07:00
Pulkit Goyal
3e9e1184d1 py3: convert kwargs' keys' to str using pycompat.strkwargs()
On Python 3, we must have keys of keyword arguments as str.
2017-06-22 03:16:16 +05:30
Pulkit Goyal
6b29e8fbdd py3: convert kwargs keys' back to bytes using pycompat.byteskwargs() 2017-06-22 03:10:24 +05:30
Pulkit Goyal
b5c1ee7c59 py3: use pycompat.bytestr() in place of str() 2017-06-21 02:20:34 +05:30
Pulkit Goyal
d05173a0a0 py3: replace str with bytes in isinstance()
We were using str because on Python 2, str were bytes but now we have to use
bytes. Otherwise the if conditions fails and we have weird results from commands
on Python 3.
2017-06-20 23:46:18 +05:30
Yuya Nishihara
41654e97a9 templater: add simple interface for unnamed template (API)
This provides a simpler API for callers which don't need full templating
stack. Instead of storing the given template as the name specified by topic,
use '' as the default template to be rendered.
2017-04-22 19:56:47 +09:00
Yuya Nishihara
685172007c revlog: add support for partial matching of wdir node id
The idea is simple. If the given node id prefix is 'ff...f', add +1 to the
number of matches (e.g. ambiguous if partial + maybewdir > 1).

This patch also fixes id() revset and shortest() template since _partialmatch()
can raise WdirUnsupported exception.
2016-08-19 18:26:04 +09:00
Pulkit Goyal
485ee65947 py3: use pycompat.bytestr instead of bytes 2017-05-05 01:26:49 +05:30
Yuya Nishihara
e83ec90299 templatekw: have showlist() take mapping dict with no **kwargs expansion (API)
See the previous commit for why.

splitlines() does not pass a mapping dict, which would probably mean the
legacy template didn't work from the beginning.
2017-04-05 21:47:34 +09:00
Yuya Nishihara
35d42be491 templater: add shorthand for building a dict like {"key": key}
Like field init shorthand of Rust. This is convenient for building a JSON
object from selected keywords.

This means dict() won't support Python-like dict(iterable) syntax because
it's ambiguous. Perhaps it could be implemented as 'mapdict(xs % (k, v))'.
2017-04-03 23:13:49 +09:00
Yuya Nishihara
d86057a7bc templater: find keyword name more thoroughly on filtering error
Before, it could spill an internal representation of compiled template such
as [(<function runsymbol at 0x....>, 'extras'), ...]. Show less cryptic
message if no symbol found.

New findsymbolicname() function will be also used by dict() constructor.
2017-04-08 23:33:32 +09:00
Yuya Nishihara
ada544b9a5 templater: add dict() constructor
It's troublesome to build JSON by template, so let's add programmatic way.
2017-04-03 22:54:06 +09:00
Yuya Nishihara
e70ac1c73a parser: preserve order of keyword arguments
This helps building dict(key1=value1, ...) in deterministic way.
2017-04-09 11:58:27 +09:00
Yuya Nishihara
33d96b70bc parser: extend buildargsdict() to support arbitrary number of **kwargs
Prepares for adding dict(key1=value1, ...) template function. More tests
will be added later.
2017-04-03 22:07:09 +09:00
Yuya Nishihara
073239ae67 templater: port pad() to take keyword arguments
This is another example where keyword arguments can be actually useful.
2017-04-03 22:23:52 +09:00
Yuya Nishihara
85fe439717 templater: add support for keyword arguments
Unlike revset, function arguments are pre-processed in templater. That's why
we need to define argspec per function. An argspec field looks somewhat
redundant in @templatefunc definition as a name field contains human-readable
list of arguments. I'll make function doc be built from argspec later.

Ported separate() function as an example.
2017-04-03 21:22:39 +09:00
Yuya Nishihara
1d5bb45321 templater: add parsing rule for key-value pair
Based on the revset implementation, ef14ee493cf7. This patch also adjusts
the test as '=' is now a valid token.
2017-04-03 20:55:55 +09:00
Yuya Nishihara
fe158d1bad templater: adjust binding strengths to make room for key-value operator
Changed as follows:

 - template ops (%, |): +10
 - arithmetic ops: +1 (but "negate" should be greater than "%")
2017-04-03 20:44:05 +09:00
Yuya Nishihara
ef29c2e54c templater: sort token table by binding strength
Just for readability.
2017-04-03 20:37:25 +09:00
Yuya Nishihara
e6ea93a8d4 templater: remove __iter__() from _hybrid, resolve it explicitly
The goal is to fix "{hybrid_obj|json}" output.

A _hybrid object must act as a list or a dict as well as a generator of
legacy template strings. Before, _hybrid.__iter__() was assigned for legacy
template, which conflicted with list.__iter__() API.

This patch drops _hybrid.__iter__() and makes stringify/flatten functions
unwrap a generator instead.
2017-04-04 22:19:02 +09:00
Matt Harbison
6376502430 templatekw: clarify the result of {latesttag} when no tag exists
My initial expectation was that the list would be empty, and therefore
detectable with {if()}.  The map for {latesttag()} is populated with real values
in this case (except {tag}), so it probably doesn't make any sense to change
this to an empty list.
2017-04-09 00:10:54 -04:00
Yuya Nishihara
caee220313 templater: provide loop counter as "index" keyword
This was originally written for JSON templating where we would have to be
careful to not add extra comma, but seems generally useful.

Inner loop started by % operator has its own counter.
2016-04-22 21:46:33 +09:00
Yuya Nishihara
717a34e2df templater: rename variable "i" to "v" in runmap()
I want to reuse "i" for index.
2016-04-22 21:45:06 +09:00
Yuya Nishihara
32e72957d2 templater: make pad() strip color codes before computing width (issue5416)
Tested in ANSI mode. We might have to extend _ansieffectre to support
terminfo mode.
2017-03-18 21:02:20 +09:00
Yuya Nishihara
b5783c4070 templater: make pad() compute actual width
str.ljust() and .rjust() are based on byte length, which are valid only for
ASCII characters.
2017-03-18 20:50:15 +09:00
Yuya Nishihara
469d1e92a3 templater: reject bad fillchar argument passed to pad()
Otherwise TypeError would be raised.
2017-03-18 20:38:44 +09:00
Yuya Nishihara
e5c5114662 templater: port formatnode filter from changeset_templater
This slightly reduces the difference between changeset_templater and formatter,
and helps extending formatter to support changeset templating.

New formatnode() is not a template filter, but a function since a filter
cannot access to ui. And it's marked as DEPRECATED since I think it exists
only for compatibility reasons.
2017-02-25 16:26:58 +09:00
Yuya Nishihara
b2229f5117 revset: split language services to revsetlang module (API)
New revsetlang module hosts parser, tokenizer, and miscellaneous functions
working on parsed tree. It does not include functions for evaluation such as
getset() and match().

  2288 mercurial/revset.py
   684 mercurial/revsetlang.py
  2972 total

get*() functions are aliased since they are common in revset.py.
2017-02-19 18:19:33 +09:00
Pulkit Goyal
c109648881 py3: replace os.altsep with pycompat.altsep
All the occurences of os.altsep are replaced with pycompat.altsep which
returns bytes.
2016-12-18 01:17:12 +05:30