##// END OF EJS Templates
formatter: add function to convert list to appropriate format (issue5217)...
Yuya Nishihara -
r29676:c3a9cd78 default
parent child Browse files
Show More
@@ -1,224 +1,243 b''
1 # formatter.py - generic output formatting for mercurial
1 # formatter.py - generic output formatting for mercurial
2 #
2 #
3 # Copyright 2012 Matt Mackall <mpm@selenic.com>
3 # Copyright 2012 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import os
10 import os
11
11
12 from .i18n import _
12 from .i18n import _
13 from .node import (
13 from .node import (
14 hex,
14 hex,
15 short,
15 short,
16 )
16 )
17
17
18 from . import (
18 from . import (
19 encoding,
19 encoding,
20 error,
20 error,
21 templatekw,
21 templater,
22 templater,
22 util,
23 util,
23 )
24 )
24
25
25 pickle = util.pickle
26 pickle = util.pickle
26
27
27 class baseformatter(object):
28 class baseformatter(object):
28 def __init__(self, ui, topic, opts):
29 def __init__(self, ui, topic, opts):
29 self._ui = ui
30 self._ui = ui
30 self._topic = topic
31 self._topic = topic
31 self._style = opts.get("style")
32 self._style = opts.get("style")
32 self._template = opts.get("template")
33 self._template = opts.get("template")
33 self._item = None
34 self._item = None
34 # function to convert node to string suitable for this output
35 # function to convert node to string suitable for this output
35 self.hexfunc = hex
36 self.hexfunc = hex
36 def __nonzero__(self):
37 def __nonzero__(self):
37 '''return False if we're not doing real templating so we can
38 '''return False if we're not doing real templating so we can
38 skip extra work'''
39 skip extra work'''
39 return True
40 return True
40 def _showitem(self):
41 def _showitem(self):
41 '''show a formatted item once all data is collected'''
42 '''show a formatted item once all data is collected'''
42 pass
43 pass
43 def startitem(self):
44 def startitem(self):
44 '''begin an item in the format list'''
45 '''begin an item in the format list'''
45 if self._item is not None:
46 if self._item is not None:
46 self._showitem()
47 self._showitem()
47 self._item = {}
48 self._item = {}
49 @staticmethod
50 def formatlist(data, name, fmt='%s', sep=' '):
51 '''convert iterable to appropriate list format'''
52 return list(data)
48 def data(self, **data):
53 def data(self, **data):
49 '''insert data into item that's not shown in default output'''
54 '''insert data into item that's not shown in default output'''
50 self._item.update(data)
55 self._item.update(data)
51 def write(self, fields, deftext, *fielddata, **opts):
56 def write(self, fields, deftext, *fielddata, **opts):
52 '''do default text output while assigning data to item'''
57 '''do default text output while assigning data to item'''
53 fieldkeys = fields.split()
58 fieldkeys = fields.split()
54 assert len(fieldkeys) == len(fielddata)
59 assert len(fieldkeys) == len(fielddata)
55 self._item.update(zip(fieldkeys, fielddata))
60 self._item.update(zip(fieldkeys, fielddata))
56 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
61 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
57 '''do conditional write (primarily for plain formatter)'''
62 '''do conditional write (primarily for plain formatter)'''
58 fieldkeys = fields.split()
63 fieldkeys = fields.split()
59 assert len(fieldkeys) == len(fielddata)
64 assert len(fieldkeys) == len(fielddata)
60 self._item.update(zip(fieldkeys, fielddata))
65 self._item.update(zip(fieldkeys, fielddata))
61 def plain(self, text, **opts):
66 def plain(self, text, **opts):
62 '''show raw text for non-templated mode'''
67 '''show raw text for non-templated mode'''
63 pass
68 pass
64 def end(self):
69 def end(self):
65 '''end output for the formatter'''
70 '''end output for the formatter'''
66 if self._item is not None:
71 if self._item is not None:
67 self._showitem()
72 self._showitem()
68
73
69 class plainformatter(baseformatter):
74 class plainformatter(baseformatter):
70 '''the default text output scheme'''
75 '''the default text output scheme'''
71 def __init__(self, ui, topic, opts):
76 def __init__(self, ui, topic, opts):
72 baseformatter.__init__(self, ui, topic, opts)
77 baseformatter.__init__(self, ui, topic, opts)
73 if ui.debugflag:
78 if ui.debugflag:
74 self.hexfunc = hex
79 self.hexfunc = hex
75 else:
80 else:
76 self.hexfunc = short
81 self.hexfunc = short
77 def __nonzero__(self):
82 def __nonzero__(self):
78 return False
83 return False
79 def startitem(self):
84 def startitem(self):
80 pass
85 pass
86 @staticmethod
87 def formatlist(data, name, fmt='%s', sep=' '):
88 '''stringify iterable separated by sep'''
89 return sep.join(fmt % e for e in data)
81 def data(self, **data):
90 def data(self, **data):
82 pass
91 pass
83 def write(self, fields, deftext, *fielddata, **opts):
92 def write(self, fields, deftext, *fielddata, **opts):
84 self._ui.write(deftext % fielddata, **opts)
93 self._ui.write(deftext % fielddata, **opts)
85 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
94 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
86 '''do conditional write'''
95 '''do conditional write'''
87 if cond:
96 if cond:
88 self._ui.write(deftext % fielddata, **opts)
97 self._ui.write(deftext % fielddata, **opts)
89 def plain(self, text, **opts):
98 def plain(self, text, **opts):
90 self._ui.write(text, **opts)
99 self._ui.write(text, **opts)
91 def end(self):
100 def end(self):
92 pass
101 pass
93
102
94 class debugformatter(baseformatter):
103 class debugformatter(baseformatter):
95 def __init__(self, ui, topic, opts):
104 def __init__(self, ui, topic, opts):
96 baseformatter.__init__(self, ui, topic, opts)
105 baseformatter.__init__(self, ui, topic, opts)
97 self._ui.write("%s = [\n" % self._topic)
106 self._ui.write("%s = [\n" % self._topic)
98 def _showitem(self):
107 def _showitem(self):
99 self._ui.write(" " + repr(self._item) + ",\n")
108 self._ui.write(" " + repr(self._item) + ",\n")
100 def end(self):
109 def end(self):
101 baseformatter.end(self)
110 baseformatter.end(self)
102 self._ui.write("]\n")
111 self._ui.write("]\n")
103
112
104 class pickleformatter(baseformatter):
113 class pickleformatter(baseformatter):
105 def __init__(self, ui, topic, opts):
114 def __init__(self, ui, topic, opts):
106 baseformatter.__init__(self, ui, topic, opts)
115 baseformatter.__init__(self, ui, topic, opts)
107 self._data = []
116 self._data = []
108 def _showitem(self):
117 def _showitem(self):
109 self._data.append(self._item)
118 self._data.append(self._item)
110 def end(self):
119 def end(self):
111 baseformatter.end(self)
120 baseformatter.end(self)
112 self._ui.write(pickle.dumps(self._data))
121 self._ui.write(pickle.dumps(self._data))
113
122
114 def _jsonifyobj(v):
123 def _jsonifyobj(v):
115 if isinstance(v, tuple):
124 if isinstance(v, (list, tuple)):
116 return '[' + ', '.join(_jsonifyobj(e) for e in v) + ']'
125 return '[' + ', '.join(_jsonifyobj(e) for e in v) + ']'
117 elif v is None:
126 elif v is None:
118 return 'null'
127 return 'null'
119 elif v is True:
128 elif v is True:
120 return 'true'
129 return 'true'
121 elif v is False:
130 elif v is False:
122 return 'false'
131 return 'false'
123 elif isinstance(v, (int, float)):
132 elif isinstance(v, (int, float)):
124 return str(v)
133 return str(v)
125 else:
134 else:
126 return '"%s"' % encoding.jsonescape(v)
135 return '"%s"' % encoding.jsonescape(v)
127
136
128 class jsonformatter(baseformatter):
137 class jsonformatter(baseformatter):
129 def __init__(self, ui, topic, opts):
138 def __init__(self, ui, topic, opts):
130 baseformatter.__init__(self, ui, topic, opts)
139 baseformatter.__init__(self, ui, topic, opts)
131 self._ui.write("[")
140 self._ui.write("[")
132 self._ui._first = True
141 self._ui._first = True
133 def _showitem(self):
142 def _showitem(self):
134 if self._ui._first:
143 if self._ui._first:
135 self._ui._first = False
144 self._ui._first = False
136 else:
145 else:
137 self._ui.write(",")
146 self._ui.write(",")
138
147
139 self._ui.write("\n {\n")
148 self._ui.write("\n {\n")
140 first = True
149 first = True
141 for k, v in sorted(self._item.items()):
150 for k, v in sorted(self._item.items()):
142 if first:
151 if first:
143 first = False
152 first = False
144 else:
153 else:
145 self._ui.write(",\n")
154 self._ui.write(",\n")
146 self._ui.write(' "%s": %s' % (k, _jsonifyobj(v)))
155 self._ui.write(' "%s": %s' % (k, _jsonifyobj(v)))
147 self._ui.write("\n }")
156 self._ui.write("\n }")
148 def end(self):
157 def end(self):
149 baseformatter.end(self)
158 baseformatter.end(self)
150 self._ui.write("\n]\n")
159 self._ui.write("\n]\n")
151
160
152 class templateformatter(baseformatter):
161 class templateformatter(baseformatter):
153 def __init__(self, ui, topic, opts):
162 def __init__(self, ui, topic, opts):
154 baseformatter.__init__(self, ui, topic, opts)
163 baseformatter.__init__(self, ui, topic, opts)
155 self._topic = topic
164 self._topic = topic
156 self._t = gettemplater(ui, topic, opts.get('template', ''))
165 self._t = gettemplater(ui, topic, opts.get('template', ''))
157 def _showitem(self):
166 def _showitem(self):
158 g = self._t(self._topic, ui=self._ui, **self._item)
167 g = self._t(self._topic, ui=self._ui, **self._item)
159 self._ui.write(templater.stringify(g))
168 self._ui.write(templater.stringify(g))
169 @staticmethod
170 def formatlist(data, name, fmt='%s', sep=' '):
171 '''build object that can be evaluated as either plain string or list'''
172 # name is mandatory argument for now, but it could be optional if
173 # we have default template keyword, e.g. {item}
174 data = list(data)
175 def f():
176 yield plainformatter.formatlist(data, name, fmt, sep)
177 return templatekw._hybrid(f(), data, lambda x: {name: x},
178 lambda d: fmt % d[name])
160
179
161 def lookuptemplate(ui, topic, tmpl):
180 def lookuptemplate(ui, topic, tmpl):
162 # looks like a literal template?
181 # looks like a literal template?
163 if '{' in tmpl:
182 if '{' in tmpl:
164 return tmpl, None
183 return tmpl, None
165
184
166 # perhaps a stock style?
185 # perhaps a stock style?
167 if not os.path.split(tmpl)[0]:
186 if not os.path.split(tmpl)[0]:
168 mapname = (templater.templatepath('map-cmdline.' + tmpl)
187 mapname = (templater.templatepath('map-cmdline.' + tmpl)
169 or templater.templatepath(tmpl))
188 or templater.templatepath(tmpl))
170 if mapname and os.path.isfile(mapname):
189 if mapname and os.path.isfile(mapname):
171 return None, mapname
190 return None, mapname
172
191
173 # perhaps it's a reference to [templates]
192 # perhaps it's a reference to [templates]
174 t = ui.config('templates', tmpl)
193 t = ui.config('templates', tmpl)
175 if t:
194 if t:
176 return templater.unquotestring(t), None
195 return templater.unquotestring(t), None
177
196
178 if tmpl == 'list':
197 if tmpl == 'list':
179 ui.write(_("available styles: %s\n") % templater.stylelist())
198 ui.write(_("available styles: %s\n") % templater.stylelist())
180 raise error.Abort(_("specify a template"))
199 raise error.Abort(_("specify a template"))
181
200
182 # perhaps it's a path to a map or a template
201 # perhaps it's a path to a map or a template
183 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
202 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
184 # is it a mapfile for a style?
203 # is it a mapfile for a style?
185 if os.path.basename(tmpl).startswith("map-"):
204 if os.path.basename(tmpl).startswith("map-"):
186 return None, os.path.realpath(tmpl)
205 return None, os.path.realpath(tmpl)
187 tmpl = open(tmpl).read()
206 tmpl = open(tmpl).read()
188 return tmpl, None
207 return tmpl, None
189
208
190 # constant string?
209 # constant string?
191 return tmpl, None
210 return tmpl, None
192
211
193 def gettemplater(ui, topic, spec):
212 def gettemplater(ui, topic, spec):
194 tmpl, mapfile = lookuptemplate(ui, topic, spec)
213 tmpl, mapfile = lookuptemplate(ui, topic, spec)
195 assert not (tmpl and mapfile)
214 assert not (tmpl and mapfile)
196 if mapfile:
215 if mapfile:
197 return templater.templater.frommapfile(mapfile)
216 return templater.templater.frommapfile(mapfile)
198 return maketemplater(ui, topic, tmpl)
217 return maketemplater(ui, topic, tmpl)
199
218
200 def maketemplater(ui, topic, tmpl, filters=None, cache=None):
219 def maketemplater(ui, topic, tmpl, filters=None, cache=None):
201 """Create a templater from a string template 'tmpl'"""
220 """Create a templater from a string template 'tmpl'"""
202 aliases = ui.configitems('templatealias')
221 aliases = ui.configitems('templatealias')
203 t = templater.templater(filters=filters, cache=cache, aliases=aliases)
222 t = templater.templater(filters=filters, cache=cache, aliases=aliases)
204 if tmpl:
223 if tmpl:
205 t.cache[topic] = tmpl
224 t.cache[topic] = tmpl
206 return t
225 return t
207
226
208 def formatter(ui, topic, opts):
227 def formatter(ui, topic, opts):
209 template = opts.get("template", "")
228 template = opts.get("template", "")
210 if template == "json":
229 if template == "json":
211 return jsonformatter(ui, topic, opts)
230 return jsonformatter(ui, topic, opts)
212 elif template == "pickle":
231 elif template == "pickle":
213 return pickleformatter(ui, topic, opts)
232 return pickleformatter(ui, topic, opts)
214 elif template == "debug":
233 elif template == "debug":
215 return debugformatter(ui, topic, opts)
234 return debugformatter(ui, topic, opts)
216 elif template != "":
235 elif template != "":
217 return templateformatter(ui, topic, opts)
236 return templateformatter(ui, topic, opts)
218 # developer config: ui.formatdebug
237 # developer config: ui.formatdebug
219 elif ui.configbool('ui', 'formatdebug'):
238 elif ui.configbool('ui', 'formatdebug'):
220 return debugformatter(ui, topic, opts)
239 return debugformatter(ui, topic, opts)
221 # deprecated config: ui.formatjson
240 # deprecated config: ui.formatjson
222 elif ui.configbool('ui', 'formatjson'):
241 elif ui.configbool('ui', 'formatjson'):
223 return jsonformatter(ui, topic, opts)
242 return jsonformatter(ui, topic, opts)
224 return plainformatter(ui, topic, opts)
243 return plainformatter(ui, topic, opts)
General Comments 0
You need to be logged in to leave comments. Login now