Commit Graph

160 Commits

Author SHA1 Message Date
Boris Feld
4ed4f0d702 templatekw: introduce obsfate keyword
Introduce an obsfate printer that uses all helpers functions defined in
obsutil to get all the obsfate-related data and format a string according to
the current format in test-obsmarker-template.t.

Then, introduce an obsfate templatekw that uses the obsfateprinter to return a
list of strings.

The goal is not to replace existing obsfate template functions but to propose
a default, good-enough and easily usable obsfate definition for end-users that
don't want to customize it. Such output would ultimately get included in the
default log output.

Here are some output examples for a commit amended:

rewritten using amend as 5:a9b1f8652753 by test (at 1970-01-01 00:00 +0000)

Next patches will make the output dependent on the verbosity.

Exemple of use-cases:

For having the obsfate on a single-line between brackets:

  {if(obsfate, " [{join(obsfate, "; ")}]")}

For having the obsfate in several lines:

  {if(obsfate, "{obsfate % "  Obsfate: {fate}\n"}")}
2017-10-05 17:42:56 +02:00
Yuya Nishihara
b3bcc5233c help: hide template keywords of obsolescence as they are still experimental 2017-10-12 21:48:02 +09:00
Yuya Nishihara
c9014c6361 help: fix formatting of template keywords
Our minirst formatter can't render multi-paragraph definition lists well.
Also added periods where appropriate.
2017-10-12 21:42:42 +09:00
Yuya Nishihara
6cc1150344 templater: store revisions as ints so min/max won't compare them as strings
Because a template value has no explicit type (like ancient PHP), ifcontains()
has to coerce the type of the needle. Before, it was always converted to a
string, which meant any container type should be a list/dict of strings.
This no longer works since we've introduced min/max functions.

In order to work around the untyped nature of templater, this patch adds
a type specifier to hybrid dict/list. It isn't named as "valuetype" since
the _hybrid class can also wrap a dict.
2017-09-19 23:13:46 +09:00
Yuya Nishihara
ef440af2e7 templatekw: allow accessing to nested namespace item by its template name
Since we have the dot operator, it makes more sense to write

  {namespaces.tags % "{tag}"}

instead of

  {namespaces.tags % "{name}"}
2017-09-30 08:57:50 +01:00
Yuya Nishihara
ccb359526e templatekw: get rid of temporary dicts from shownamespaces() 2017-09-30 08:50:24 +01:00
Yuya Nishihara
296d077704 templatekw: rename peerpaths to peerurls per naming convention (BC)
Since each element is called as "url", the template keyword should be named
as "<whatever>urls".

{peerurls} is now stabilized.
2017-09-18 23:53:05 +09:00
Yuya Nishihara
db07c1fcd9 templatekw: make experimental {peerpaths} return a single-level dict (BC)
This was planned as in 2bcc691583ec, "{peerpaths.default.pushurl} will be
translated to peerpaths['default'].makemap()['pushurl'], which means
{peerpaths} should be a single-level dict and sub-options should be
makemap()-ed."
2017-09-18 23:31:01 +09:00
Yuya Nishihara
3d25496222 templatekw: fix scope of peerpath url bound to generator
I had to explicitly bind 'd' to the generator. Otherwise, the last 'd' would
be used.
2017-09-18 23:49:05 +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
b12606055b formatter: fix default list/dict generator to be evaluated more than once
Before, _hybrid.gen must be a generator which could be consumed only once.
It was okay in templatekw.py since template keywords are functions which
create temporary hybrid objects, but the formatter doesn't work in that way.

To work around the issue, this patch makes _hybrid.gen optionally be a
function returning a generator.

Thanks to Pulkit for finding this issue.
2017-10-01 08:37:04 +01:00
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
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
b009eae820 scmutil: extract helper functions that returns human-readable change id
We do "'%d:%s' % (ctx...)" at several places, so let's formalize it. A low-
level function, formatrevnode(ui, rev, node), is extracted so we can pass
a manifest rev/node pair.

Note that hex() for manifest output can be replaced with hexfunc() because
it is printed only when debugflag is set.

i18n/de.po is updated so test-log.t passes with no error.
2017-09-24 12:43:57 +09:00
Boris Feld
e5f84ad7bf template: add minimal obsfate template function
The goal of this series is to have templates capable of displaying the
evolution of a changeset in a clean and human-readable way.

Add the succsandmarkers template return successors and markers so it can be
used separately like this:

> {succsandmarkers % "{get(succsandmarkers, "markers")|json};"}

The following patches will add template functions that takes successors and
markers as inputs and compute various obsfate fields from them.
2017-08-17 18:26:11 +02:00
Sean Farley
e18a90ab08 merge with stable 2017-08-21 21:35:06 -07:00
Martin von Zweigbergk
7cd70adbc1 templatekw: choose {latesttag} by len(changes), not date (issue5659)
As Augie reported in the bug, the current heuristic of choosing the
best tag of a merge commit by taking the one with newest tag (in terms
of tagging date) currently fails in the Mercurial repo itself. Copying
the example from Yuya:

  $ hg glog -T '{node|short} {latesttag}+{latesttagdistance}\n' \
    -r '4.2.3: & (merge() + parents(merge()) + tag())'
  o    cc59efae4cc0 4.2.3+5
  |\
  | o    06f60e88fc3a 4.2.3+4
  | |\
  | | o  c191a9eb0b10 4.3-rc+109
  | | |
  | | ~
  o |  49ada93fdc10 4.3.1+2
  : |
  o |  229937197835 4.3.1+0
  |/
  o    6a83ad94c0f2 4.2.3+3
  |\
  | ~
  o  8e9dcdd1de74 4.2.3+2
  :
  o  525f2b18248f 4.2.3+0
  |
  ~

It seems to me like the best choice is the tag with the smallest
number of changes since it (across all paths, not the longest single
path). So that's what this patch does, even though it's
costly. Best-of-5 timings for Yuya's command above shows a slowdown
from 1.293s to 1.610s. We can optimize it later.

Differential Revision: https://phab.mercurial-scm.org/D447
2017-08-15 23:23:55 -07:00
Danny Hooper
54e3286e1e log: add a "graphwidth" template variable
Wrapping text in templates for 'hg log --graph' can't be done very well,
because the template doesn't know how wide the graph drawing is. The edge
drawing function needs to know the number of lines in the template output, so
we need to also determine how wide that drawing would be before we call the
edgefn or evaluate the template.

This patch makes edgefn compute the graph width and pass it into the template
so that we can do something like this:

COLUMNS=10 hg log --graph --template "{fill(desc, termwidth - graphwidth)}"
@  a a a a
|  a a a a
|  a a a a
o    a a a
|\   a a a
| |  a a a
| |  a a a

Using extensions to do this would be relatively complicated due to a lack of
hooks in this area of the code.

In the future it may make sense to have a more generic "textwidth" that tells
you how many columns you can expect to fill without causing the terminal to
wrap your output. I'm not sure there are other situations to motivate this yet,
or if it is entirely feasible.

Differential Revision: https://phab.mercurial-scm.org/D360
2017-08-15 10:15:31 -07:00
Yuya Nishihara
38282452e0 templatekw: specify plural form of instability
Follows up ebfef9a04f8d.
2017-08-13 14:12:28 +09:00
Yuya Nishihara
79af97cbbf templatekw: rename termwidth() per convention 2017-08-16 13:57:19 +09:00
Boris Feld
d833c9f0c7 label: rename trouble.X into instability.X
The renaming is done according to
https://www.mercurial-scm.org/wiki/CEDVocabulary.

Differential Revision: https://phab.mercurial-scm.org/D258
2017-08-03 15:30:41 +02:00
Boris Feld
b03347ff01 context: rename troubles into instabilities
Rename troubles context method into instabilities.

Copy the old troubles method and add a deprecation warning. This way
extensions calling troubles will see the deprecation warning but will not
break due to new return values.

The renaming is done according to
https://www.mercurial-scm.org/wiki/CEDVocabulary.

Differential Revision: https://phab.mercurial-scm.org/D238
2017-08-02 18:34:39 +02:00
Boris Feld
52010996a6 template: rename troubles templatekw into instabilities
Rename troubles template keyword into instabilities and add a deprecation
warning on templatekw.

Update default mapfile and test files to use the new template keyword.

The renaming is done according to
https://www.mercurial-scm.org/wiki/CEDVocabulary.

Differential Revision: https://phab.mercurial-scm.org/D237
2017-08-02 11:32:25 +02:00
Yuya Nishihara
46a6e6a290 templatekw: hide {peerpaths} keyword for 4.3
Thinking a bit further about list/dict subscript operation (proposed by
issue 5534), I noticed the current data structure, a dict of dicts, might
not be ideal.

For example, if there were "'[' index ']'" and "'.' key" operators,
"{parents[0]}" would return "{p1rev}:{p1node}", and we would probably want to
write "{parents[0].desc}" to get the first element of "{parents % "{desc}"}".
This will basically execute parents[0].makemap()['desc'] in Python.

Given the rule above, "{peerpaths.default.pushurl}" will be translated to
peerpaths['default'].makemap()['pushurl'], which means {peerpaths} should
be a single-level dict and sub-options should be makemap()-ed.

  "{peerpaths % "{name} = {url}, {pushurl}, ..."}"

(Well, it could be peerpaths['default']['pushurl'], but in which case,
peerpaths['default'] should be a plain dict, not a hybrid object.)

So, let's mark the current implementation experimental and revisit it later.
2017-07-15 00:38:57 +09:00
Yuya Nishihara
963c78d353 templatekw: export ui.paths as {peerpaths}
It's sometimes useful to show hyperlinks in log output.

  "{get(peerpaths, "default")}/rev/{node}"

Since each path may have sub options, "{peerpaths}" is structured as a dict
of dicts, but the inner dict is rendered as if it were a string URL. The
implementation is ad-hoc, so there are some weird behaviors described in
the test. We might need to introduce a proper way of handling a hybrid
scalar object.

This patch adds _hybrid.__getitem__() so d['path']['url'] works.

The keyword is named as "peerpaths" since "paths" seemed too generic in
log context.
2017-07-13 00:35:54 +09:00
Boris Feld
37426f06b5 template: add successors template
Add a 'successorssets' template that returns the list of all closest known
sucessorssets for a changectx. The elements of the list are changesets.

The "closest successors" are the first locally known revisions encountered
while, walking successors markers. It uses successorsets previously modified
to support the closest argument.

This logic respect repository filtering. So hidden revision will be skipped by
this logic unless --hidden is specified. Since we only display the visible
predecessors, this template will not display anything in most case. It makes a
good candidate for inclusion in the default log output.

I updated the test-obsmarker-template.t test file introduced with the
predecessors template to test successorssets template.
2017-07-03 11:22:00 +02:00
Gregory Szorc
6c60659b4c namespaces: record and expose whether namespace is built-in
Currently, the templating layer tends to treat each namespace
as a one-off, with explicit usage of {bookmarks}, {tags}, {branch},
etc instead of using {namespaces}. It would be really useful if
we could iterate over namespaces and operate on them generically.
However, some consumers may wish to differentiate namespaces by
whether they are built-in to core Mercurial or provided by extensions.
Expected use cases include ignoring non-built-in namespaces or
emitting a generic label for non-built-in namespaces.

This commit introduces an attribute on namespace instances
that says whether the namespace is "built-in" and then exposes
this to the templating layer.

As part of this, we implement a reusable extension for defining
custom names on each changeset for testing. A second consumer
will be introduced in a subsequent commit.
2017-06-24 14:52:15 -07:00
Gregory Szorc
da11a0810d templatekw: expose color name in {namespaces} entries
Templates make use of a "log.<namespace>" label. The <namespace> value
here differs from the actual namespace name in that the namespace
itself is plural but the label/color value is singular.

Expose the color name to the templating layer so log.* labels
can be emitted for {namespaces}.

As part of this, we refactored the logic to eliminate a gnarly
comprehension. We store color names in their own dict because the
lookup can occur in tight loops and we shouldn't have to go to
repo.names[ns] multiple times for every changeset.
2017-06-24 13:39:20 -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
dc55608174 py3: use "%d" % val for int rather than pycompat.bytestr
Earlier I used pycompat.bytestr() to convert integers to bytes, but we can do
b"%d" % val to convert that int to bytes. b'' is already added by the
transformer.

Thanks to Yuya for suggesting this.
2017-06-22 01:29:07 +05:30
Pulkit Goyal
b5c1ee7c59 py3: use pycompat.bytestr() in place of str() 2017-06-21 02:20:34 +05:30
Pulkit Goyal
42ca4394c3 py3: use r'' to access values from kwargs where keys are str
These are the cases where either args is again passed as keyword argument or 1
or 2 elements are accessed. So it's better to add an r'' to prevent it
converting to bytes rather than doing the conversion of args.
2017-06-21 02:13:34 +05:30
Pulkit Goyal
bdff49ff8e py3: convert keys of kwargs in template keywords functions to bytes
This patch converts the args argument keys' to bytes wherever necessary as there
are some places where either args is not used or using r'' is better or args is
again passed as keyword arguments.
2017-06-21 02:10:25 +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
2db9374d13 templatekw: use common "rev:node" format as the default of predecessors
I'm not sure if this is better. If we're planning to add a template keyword
that returns obsoleted nodes unavailable in the repo (i.e. they have no valid
revision numbers), we might want to use the current "node"-only format
everywhere.
2017-06-17 13:34:18 +09:00
Yuya Nishihara
c3de10454c templatekw: populate all keywords depending on predecessor in map operation
This is what showparents() does. repo[precnode] should never fail since its
validity is tested by closestpredecessors().
2017-06-17 13:23:55 +09:00
Yuya Nishihara
5e5cff46c2 templatekw: reference predecessor node id as {node} in map operation
More predecessor-depending values will be populated by the next patch.
2017-06-17 13:18:03 +09:00
Boris Feld
6b02ddc020 template: add predecessors template
Add a 'predecessors' template that returns the list of all closest known
predecessors for a changectx. The elements of the list are row changectx node id
formatted by default as short nodes.

The "closest predecessors" are the first locally known revisions encountered
while, walking predecessors markers. For example:

  1) If a (A, (B)) markers exists and both A and B are locally known A is a
  closest predecessors of B.

  2) If a (A, (B)) and (B, (C)) markers exists and only A and C are known
  locally, A will be the closest precursors of C.

This logic respect repository filtering. So hidden revision will be skipped by
this logic unless --hidden is specified. Since we only display the visible
predecessors, this template will not display anything in most case. It makes a
good candidate for inclusion in the default log output.

I added a new test-file for testing the precursors in various scenarios. This
test file will also be used for the successors template.

A new "obsutil" module has been added to start gathering utility function
outside of the large obsolete.py module.
2017-06-15 13:02:58 +02:00
Yuya Nishihara
32b5726eef scmutil: introduce binnode(ctx) as paired function with intrev(ctx)
It seemed silly to convert ctx.hex() back to binary to use node.hex/short(),
or to use [:12] instead of node.short() because ctx.node() could be None.

Eventually I want to change wctx.rev() and wctx.node() to return wdirrev and
wdirid respectively, but that's quite big API breakage and can't be achieved
without some compatibility wrappers.
2017-06-03 19:12:01 +09:00
Yuya Nishihara
ca7d54c6e1 scmutil: pass ctx object to intrev()
This makes it slightly easier to sort basectx objects by key=scmutil.intrev.
We're most likely to have ctx objects where changectx/workingctx abstraction
is necessary, so this won't increase the abstraction overhead.
2017-06-03 18:57:28 +09:00
Yuya Nishihara
3a01d624e0 templatekw: factor out showdict() helper
Make it less cryptic for common cases.
2017-04-05 21:57:05 +09:00
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
14174e42d0 templatekw: change _showlist() to take mapping dict with no **kwargs expansion
There was a risk that a template keyword could conflict with an argument
name (e.g. 'name', 'values', 'plural', etc.) Let's make it less magical.
2017-04-05 21:40:38 +09:00
Yuya Nishihara
e7f0402cb5 templatekw: rename 'args' to 'mapping' in showlist()
The name 'args' provides no information. Call it 'mapping' as in templater.py.
2017-04-05 21:32:32 +09:00
Yuya Nishihara
ae7c681de5 templatekw: eliminate unnecessary temporary variable 'names' from _showlist()
Replace 'names' with the optional argument 'plural'.
2017-04-05 21:27:44 +09:00
Yuya Nishihara
2274942817 templatekw: add public function to wrap a dict by _hybrid object 2017-04-05 22:28:09 +09:00
Yuya Nishihara
f8dcd91891 templatekw: add public function to wrap a list by _hybrid object 2017-04-05 22:25:36 +09:00
Yuya Nishihara
17d1580914 templatekw: add default implementation of _hybrid.gen
This is convenient for new template keyword, which doesn't need to support
the legacy list hack (provided by _showlist()), but still wants to have
a string representation.
2017-04-12 21:10:47 +09:00
Yuya Nishihara
0aa51ecaec templater: make _hybrid provide more list/dict-like methods
So the JSON filter works.
2017-04-04 22:31:59 +09:00