Commit Graph

74 Commits

Author SHA1 Message Date
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
Augie Fackler
3ae868f45f formatter: remove superfluous pass statements 2017-09-30 07:44:08 -04:00
Yuya Nishihara
407d4549be py3: convert system strings to bytes in doctest of formatter.py 2017-09-17 12:39:53 +09:00
Yuya Nishihara
2e451db94f py3: rewrite stdout hack of doctest by using ui.pushbuffer()
We can't use pycompat.stdout here because the doctest runner replaces
sys.stdout with a string buffer.
2017-09-17 12:39:14 +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
fad172bfb7 formatter: proxy fm.context() through converter
Otherwise nested template formatter would not see the context objects.

It's just a boolean flag now. We might want to change it to 'ctxs -> items'
function so changectx attributes are populated automatically in JSON, but
I'm not sure.
2017-06-26 09:33:01 +09:00
Yuya Nishihara
6b288f6b94 formatter: add support for parts map of [templates] section
Unlike a mapfile whose template is looked up by spec -> mapfile -> topic,
[templates] section is global. We use :sub-section syntax to define parts
per template.
2017-04-22 21:29:00 +09:00
Yuya Nishihara
3d57413212 formatter: add support for separator template
This seems useful for writing JSON template.
2017-04-22 21:50:51 +09:00
Yuya Nishihara
a93412a128 formatter: add support for docheader and docfooter templates
templatepartsmap() is a minimal copy of changeset_templater.__init__(). I
tried to factor out a common function, but it was unnecessarily complicated.
2017-04-22 21:46:14 +09:00
Yuya Nishihara
aeb6f6fba6 formatter: extract helper function to render template 2017-04-22 21:38:08 +09:00
Pulkit Goyal
43464e00e4 py3: convert keys of kwargs back to bytes using pycompat.byteskwargs() 2017-06-17 15:29:26 +05:30
Pulkit Goyal
591b6e9fb9 py3: use pycompat.strkwargs() to convert kwargs keys to str before passing 2017-06-17 15:05:11 +05:30
Yuya Nishihara
de79d3261a formatter: always store a literal template unnamed
Now spec.ref should be '' if spec.tmpl is specified. Since spec.ref is the
option to select the initial template to be rendered, it doesn't make sense
to store the given literal template as spec.ref.
2017-05-06 17:03:59 +09:00
Yuya Nishihara
75bcdb63fb formatter: load templates section like a map file
Since a map file has another level to select a template (spec -> mapfile
-> topic), this isn't exactly the same as how a map file works. But I believe
most users would expect the new behavior.

A literal template is stored as an unnamed template so that it will never
conflict with the templates defined in [templates] section.
2017-04-22 20:14:55 +09:00
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
2aef40eb43 formatter: render template specified by templatespec tuple 2017-04-22 19:29:41 +09:00
Yuya Nishihara
4fc90a661f formatter: put topic in templatespec tuple
This will allow us to change the initial template reference depending on how
the template is looked up. For example,

  -Tdefault => (ref='changeset', tmpl=None, mapfile='map-cmdline.default')
  -T'{rev}' => (ref='', tmpl='{rev}', mapfile=None)

A literal template given by -T option will be stored as an unnamed template,
which will free up the template namespace so that we can load named templates
from [templates] section of user config.
2017-04-22 19:07:00 +09:00
Yuya Nishihara
7242ce7327 formatter: wrap (tmpl, mapfile) by named tuple
I'm going to add more options to the templatespec tuple.

cmdutil.logtemplatespec() is just an alias now, but it will be changed to
a factory function later.
2017-04-22 18:48:38 +09:00
Yuya Nishihara
a0674d4810 formatter: document lookuptemplate() 2017-05-06 16:24:21 +09:00
Yuya Nishihara
a958cf025b formatter: inline gettemplater()
Since it's highly use-case dependent how template should be looked up,
gettemplater() function isn't useful. Keeping it would introduce another
bug I've made and fixed earlier in this series.
2017-04-22 15:11:53 +09:00
Yuya Nishihara
cb89cb49a0 formatter: factor out function to create templater from literal or map file
(tmpl, mapfile) will be packed into a named tuple later.
2017-04-22 15:06:06 +09:00
Yuya Nishihara
849d320dfa formatter: open raw template file in posix semantics
This should have no effect, but seems good for code consistency.
2017-05-06 14:48:47 +09:00
Yuya Nishihara
fbd006547a formatter: open raw template file in binary mode (BC)
I believe it was a mistake to open file in text mode. Now '\r' is preserved
on Windows, but it should be okay to print '\r\n' as long as users live in
such platform.
2017-05-06 14:47:31 +09:00
Yuya Nishihara
3853d4b59a formatter: close raw template file explicitly 2017-05-06 14:40:22 +09:00
Yuya Nishihara
d310821175 formatter: add nullformatter
This can be used as a placeholder variable.
2017-05-27 17:44:26 +09:00
Yuya Nishihara
cccb0536a1 formatter: add helper to create a formatter optionally backed by file
To make things simple, openformatter() and maybereopen() have no support
for a plain object API. Callers must use the "with" statement.

Unlike cmdutil.makefileobj(), append mode ('ab') isn't supported by these
functions. This is because JSON output can't be simply concatenated. Perhaps
cmdutil.export() will have to build a {filename: [revs...]} map first and
write revs per file.
2017-05-27 17:40:18 +09:00
Yuya Nishihara
7c796bc384 formatter: add option to redirect output to file object
Commands like 'export' have --output=OUTFILESPEC option, so we need a way
to write formatter output optionally to a file.
2015-01-18 18:04:44 +09:00
Pulkit Goyal
9b8ba742a1 py3: use pycompat.byteskwargs to converts kwargs to bytes
baseformatter._item must contain both keys and values in bytes. So to make
sure that, we convert the opts back to bytes.
2017-05-04 01:12:14 +05:30
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
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
d6502e3a12 formatter: reorder code that builds template mapping
This makes the future patch slightly simpler.
2017-04-02 22:43:18 +09:00
Yuya Nishihara
ce44b9ffb0 formatter: use templatefilters.json()
Now _jsonifyobj() is identical to templatefilters.json(paranoid=False).
2017-04-02 12:02:17 +09:00
Gregory Szorc
dc53c9ca1b formatter: support json formatting of long type
By luck, we appear to not pass any long instances into
the JSON formatter. I suspect this will change with all the
Python 3 porting work. Plus I have another series that will
convert some ints to longs that triggers this.
2017-03-13 18:31:29 -07:00
Martin von Zweigbergk
09677e4708 formatter: set _first on formatter, not ui
The _first field is used for tracking when to emit a separator between
items. It seems like it's clearly formatter state, not ui state, so
let's move it there.
2017-03-09 15:38:00 -08:00
Yuya Nishihara
bc9edb1202 formatter: add argument to change output file of non-plain formatter
This allows us to build data not written to the console. That would be
doable by ui.pushbuffer()/popbuffer(), but changing the file object seems
cleaner.
2015-01-18 18:04:44 +09:00
Yuya Nishihara
36d684e2c3 formatter: add support for changeset templating
Some formatter-based commands provide fields that are identical to the ones
defined in templatekw, but we had to specify them manually to support all
changeset-based template keywords.

This patch adds fm.context() that populates all templatekw. These keywords
are available only in template output, so we still need to set important
keywords via fm.data() if they should be available in e.g. JSON output.

Currently fm.context() takes only 'ctx' argument. It will eventually be
extended to take 'fctx' to support file-based keywords (e.g. {path}) seen
in hgweb.
2017-02-25 17:00:07 +09:00
Yuya Nishihara
d9d27f710f formatter: drop filters argument from maketemplater()
It's unused now. I want to keep the high-level API simple.
2017-02-25 22:04:30 +09:00
Yuya Nishihara
0a5e04d63d formatter: add overview of API and example as doctest 2016-10-22 15:02:11 +09:00
Mathias De Maré
ba1cc0ec1c formatter: introduce isplain() to replace (the inverse of) __nonzero__() (API)
V2: also remove and replace __nonzero__
2016-08-29 17:19:09 +02:00
Yuya Nishihara
e42d0fb100 formatter: add context manager interface for convenience
And port "hg files" to test it.

As you can see, extra indent is necessary to port to this API. I don't think
we should switch every fm.formatter() call to "with" statement.
2016-08-29 00:00:05 +09:00
Yuya Nishihara
a0741d6e1d formatter: add fm.nested(field) to either write or build sub items
We sometimes need to build nested items by formatter, but there was no
convenient way other than building and putting them manually by fm.data():

  exts = []
  for n, v in extensions:
      fm.plain('%s %s\n' % (n, v))
      exts.append({'name': n, 'ver': v})
  fm.data(extensions=exts)

This should work for simple cases, but doing this would make it harder to
change the underlying data type for better templating support.

So this patch provides fm.nested(field), which returns new nested formatter
(or self if items aren't structured and just written to ui.) A nested formatter
stores items which will later be rendered by the parent formatter.

  fn = fm.nested('extensions')
  for n, v in extensions:
      fn.startitem()
      fn.write('name ver', '%s %s\n', n, v)
  fn.end()

Nested items are directly exported to a template for now:

  {extensions % "{name} {ver}\n"}

There's no {extensions} nor {join(extensions, sep)} yet. I have a plan for
them by extending fm.nested() API, but I want to revisit it after trying
out this API in the real world.
2016-03-13 19:59:39 +09:00
Yuya Nishihara
2c363af9f2 formatter: factor out format*() functions to separate classes
New converter classes will be reused by a nested formatter. See the next
patch for details.

This change is also good in that the default values are defined uniquely
by the baseformatter.
2016-08-15 13:51:14 +09:00
Yuya Nishihara
d586657c81 formatter: add function to convert dict to appropriate format
This will be used to process key-value pairs by formatter. The default
field names and format are derived from the {extras} template keyword.

Tests will be added later.
2016-08-15 12:58:33 +09:00
Yuya Nishihara
bcdd7c502f formatter: add function to convert date tuple to appropriate format 2016-07-31 17:07:29 +09:00
Yuya Nishihara
f449ec5977 formatter: add function to convert list to appropriate format (issue5217)
Before, it wasn't possible for formatter to handle array structure other
than date tuple. We've discussed that at the last sprint, which ended we
would probably want to allow only templatable data structure, i.e. a list
of dicts:

  data(tags=[{'tag': a}, {'tag': b}, ...])

Unfortunately, it turned out not working well with template functions:

  "{ifcontains(a, tags, ...)}"
    ^^^^^^^^^^^^^^^^^^
    "a in tags", where tags should be a plain list/set of tags

So the formatter must at least know if the type [{}] was constructed from
a plain list or was actually a list of dicts.

This patch introduces new explicit interface to convert an array structure
to an appropriate data type for the current formatter, which can be used
as follows:

  fm.write('tags', _('tags: %s\n'), fm.formatlist(tags, name='tag'))

No separate fm.data() call should be necessary.
2016-07-10 21:03:06 +09:00
Pulkit Goyal
5f52c722cf py3: conditionalize cPickle import by adding in util
The cPickle is renamed to _pickle in python3 and this C extension is available
 in pickle which was not included in earlier versions. So imports are conditionalized
 to import cPickle in py2 and pickle in py3. Moreover the use of pickle in py2 is
 switched to cPickle as the C extension is faster. The hack is added in util.py and
the modules import util.pickle
2016-06-04 14:38:00 +05:30
Yuya Nishihara
ec53346d72 templater: load and expand aliases by template engine (API) (issue4842)
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.
2016-03-27 20:59:36 +09:00
Yuya Nishihara
84a8ba9511 templater: factor out function that creates templater from string template
This function will host loading of template aliases. It is not defined at
templater, but at formatter, since formatter is the module handling ui stuff
in front of templater.
2016-04-10 17:23:09 +09:00
Yuya Nishihara
3f981af86b templater: separate function to create templater from map file (API)
New frommapfile() function will make it clear when template aliases will be
loaded. They should be applied to command arguments and templates in hgrc,
but not to map files. Otherwise, our stock styles and web templates
(i.e map-file templates) could be modified unintentionally.

Future patches will add "aliases" argument to __init__(), but not to
frommapfile().
2016-04-03 23:26:48 +09:00