##// END OF EJS Templates
formatter: add template support...
Matt Mackall -
r25513:0c6f9839 default
parent child Browse files
Show More
@@ -1,195 +1,204
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 import cPickle
8 import cPickle
9 from node import hex, short
9 from node import hex, short
10 from i18n import _
10 from i18n import _
11 import encoding, util
11 import encoding, util
12 import templater
12 import templater
13 import os
13 import os
14
14
15 class baseformatter(object):
15 class baseformatter(object):
16 def __init__(self, ui, topic, opts):
16 def __init__(self, ui, topic, opts):
17 self._ui = ui
17 self._ui = ui
18 self._topic = topic
18 self._topic = topic
19 self._style = opts.get("style")
19 self._style = opts.get("style")
20 self._template = opts.get("template")
20 self._template = opts.get("template")
21 self._item = None
21 self._item = None
22 # function to convert node to string suitable for this output
22 # function to convert node to string suitable for this output
23 self.hexfunc = hex
23 self.hexfunc = hex
24 def __nonzero__(self):
24 def __nonzero__(self):
25 '''return False if we're not doing real templating so we can
25 '''return False if we're not doing real templating so we can
26 skip extra work'''
26 skip extra work'''
27 return True
27 return True
28 def _showitem(self):
28 def _showitem(self):
29 '''show a formatted item once all data is collected'''
29 '''show a formatted item once all data is collected'''
30 pass
30 pass
31 def startitem(self):
31 def startitem(self):
32 '''begin an item in the format list'''
32 '''begin an item in the format list'''
33 if self._item is not None:
33 if self._item is not None:
34 self._showitem()
34 self._showitem()
35 self._item = {}
35 self._item = {}
36 def data(self, **data):
36 def data(self, **data):
37 '''insert data into item that's not shown in default output'''
37 '''insert data into item that's not shown in default output'''
38 self._item.update(data)
38 self._item.update(data)
39 def write(self, fields, deftext, *fielddata, **opts):
39 def write(self, fields, deftext, *fielddata, **opts):
40 '''do default text output while assigning data to item'''
40 '''do default text output while assigning data to item'''
41 for k, v in zip(fields.split(), fielddata):
41 for k, v in zip(fields.split(), fielddata):
42 self._item[k] = v
42 self._item[k] = v
43 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
43 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
44 '''do conditional write (primarily for plain formatter)'''
44 '''do conditional write (primarily for plain formatter)'''
45 for k, v in zip(fields.split(), fielddata):
45 for k, v in zip(fields.split(), fielddata):
46 self._item[k] = v
46 self._item[k] = v
47 def plain(self, text, **opts):
47 def plain(self, text, **opts):
48 '''show raw text for non-templated mode'''
48 '''show raw text for non-templated mode'''
49 pass
49 pass
50 def end(self):
50 def end(self):
51 '''end output for the formatter'''
51 '''end output for the formatter'''
52 if self._item is not None:
52 if self._item is not None:
53 self._showitem()
53 self._showitem()
54
54
55 class plainformatter(baseformatter):
55 class plainformatter(baseformatter):
56 '''the default text output scheme'''
56 '''the default text output scheme'''
57 def __init__(self, ui, topic, opts):
57 def __init__(self, ui, topic, opts):
58 baseformatter.__init__(self, ui, topic, opts)
58 baseformatter.__init__(self, ui, topic, opts)
59 if ui.debugflag:
59 if ui.debugflag:
60 self.hexfunc = hex
60 self.hexfunc = hex
61 else:
61 else:
62 self.hexfunc = short
62 self.hexfunc = short
63 def __nonzero__(self):
63 def __nonzero__(self):
64 return False
64 return False
65 def startitem(self):
65 def startitem(self):
66 pass
66 pass
67 def data(self, **data):
67 def data(self, **data):
68 pass
68 pass
69 def write(self, fields, deftext, *fielddata, **opts):
69 def write(self, fields, deftext, *fielddata, **opts):
70 self._ui.write(deftext % fielddata, **opts)
70 self._ui.write(deftext % fielddata, **opts)
71 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
71 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
72 '''do conditional write'''
72 '''do conditional write'''
73 if cond:
73 if cond:
74 self._ui.write(deftext % fielddata, **opts)
74 self._ui.write(deftext % fielddata, **opts)
75 def plain(self, text, **opts):
75 def plain(self, text, **opts):
76 self._ui.write(text, **opts)
76 self._ui.write(text, **opts)
77 def end(self):
77 def end(self):
78 pass
78 pass
79
79
80 class debugformatter(baseformatter):
80 class debugformatter(baseformatter):
81 def __init__(self, ui, topic, opts):
81 def __init__(self, ui, topic, opts):
82 baseformatter.__init__(self, ui, topic, opts)
82 baseformatter.__init__(self, ui, topic, opts)
83 self._ui.write("%s = [\n" % self._topic)
83 self._ui.write("%s = [\n" % self._topic)
84 def _showitem(self):
84 def _showitem(self):
85 self._ui.write(" " + repr(self._item) + ",\n")
85 self._ui.write(" " + repr(self._item) + ",\n")
86 def end(self):
86 def end(self):
87 baseformatter.end(self)
87 baseformatter.end(self)
88 self._ui.write("]\n")
88 self._ui.write("]\n")
89
89
90 class pickleformatter(baseformatter):
90 class pickleformatter(baseformatter):
91 def __init__(self, ui, topic, opts):
91 def __init__(self, ui, topic, opts):
92 baseformatter.__init__(self, ui, topic, opts)
92 baseformatter.__init__(self, ui, topic, opts)
93 self._data = []
93 self._data = []
94 def _showitem(self):
94 def _showitem(self):
95 self._data.append(self._item)
95 self._data.append(self._item)
96 def end(self):
96 def end(self):
97 baseformatter.end(self)
97 baseformatter.end(self)
98 self._ui.write(cPickle.dumps(self._data))
98 self._ui.write(cPickle.dumps(self._data))
99
99
100 def _jsonifyobj(v):
100 def _jsonifyobj(v):
101 if isinstance(v, tuple):
101 if isinstance(v, tuple):
102 return '[' + ', '.join(_jsonifyobj(e) for e in v) + ']'
102 return '[' + ', '.join(_jsonifyobj(e) for e in v) + ']'
103 elif v is None:
103 elif v is None:
104 return 'null'
104 return 'null'
105 elif v is True:
105 elif v is True:
106 return 'true'
106 return 'true'
107 elif v is False:
107 elif v is False:
108 return 'false'
108 return 'false'
109 elif isinstance(v, (int, float)):
109 elif isinstance(v, (int, float)):
110 return str(v)
110 return str(v)
111 else:
111 else:
112 return '"%s"' % encoding.jsonescape(v)
112 return '"%s"' % encoding.jsonescape(v)
113
113
114 class jsonformatter(baseformatter):
114 class jsonformatter(baseformatter):
115 def __init__(self, ui, topic, opts):
115 def __init__(self, ui, topic, opts):
116 baseformatter.__init__(self, ui, topic, opts)
116 baseformatter.__init__(self, ui, topic, opts)
117 self._ui.write("[")
117 self._ui.write("[")
118 self._ui._first = True
118 self._ui._first = True
119 def _showitem(self):
119 def _showitem(self):
120 if self._ui._first:
120 if self._ui._first:
121 self._ui._first = False
121 self._ui._first = False
122 else:
122 else:
123 self._ui.write(",")
123 self._ui.write(",")
124
124
125 self._ui.write("\n {\n")
125 self._ui.write("\n {\n")
126 first = True
126 first = True
127 for k, v in sorted(self._item.items()):
127 for k, v in sorted(self._item.items()):
128 if first:
128 if first:
129 first = False
129 first = False
130 else:
130 else:
131 self._ui.write(",\n")
131 self._ui.write(",\n")
132 self._ui.write(' "%s": %s' % (k, _jsonifyobj(v)))
132 self._ui.write(' "%s": %s' % (k, _jsonifyobj(v)))
133 self._ui.write("\n }")
133 self._ui.write("\n }")
134 def end(self):
134 def end(self):
135 baseformatter.end(self)
135 baseformatter.end(self)
136 self._ui.write("\n]\n")
136 self._ui.write("\n]\n")
137
137
138 class templateformatter(baseformatter):
139 def __init__(self, ui, topic, opts):
140 baseformatter.__init__(self, ui, topic, opts)
141 self._topic = topic
142 self._t = gettemplater(ui, topic, opts.get('template', ''))
143 def _showitem(self):
144 g = self._t(self._topic, **self._item)
145 self._ui.write(templater.stringify(g))
146
138 def lookuptemplate(ui, topic, tmpl):
147 def lookuptemplate(ui, topic, tmpl):
139 # looks like a literal template?
148 # looks like a literal template?
140 if '{' in tmpl:
149 if '{' in tmpl:
141 return tmpl, None
150 return tmpl, None
142
151
143 # perhaps a stock style?
152 # perhaps a stock style?
144 if not os.path.split(tmpl)[0]:
153 if not os.path.split(tmpl)[0]:
145 mapname = (templater.templatepath('map-cmdline.' + tmpl)
154 mapname = (templater.templatepath('map-cmdline.' + tmpl)
146 or templater.templatepath(tmpl))
155 or templater.templatepath(tmpl))
147 if mapname and os.path.isfile(mapname):
156 if mapname and os.path.isfile(mapname):
148 return None, mapname
157 return None, mapname
149
158
150 # perhaps it's a reference to [templates]
159 # perhaps it's a reference to [templates]
151 t = ui.config('templates', tmpl)
160 t = ui.config('templates', tmpl)
152 if t:
161 if t:
153 try:
162 try:
154 tmpl = templater.unquotestring(t)
163 tmpl = templater.unquotestring(t)
155 except SyntaxError:
164 except SyntaxError:
156 tmpl = t
165 tmpl = t
157 return tmpl, None
166 return tmpl, None
158
167
159 if tmpl == 'list':
168 if tmpl == 'list':
160 ui.write(_("available styles: %s\n") % templater.stylelist())
169 ui.write(_("available styles: %s\n") % templater.stylelist())
161 raise util.Abort(_("specify a template"))
170 raise util.Abort(_("specify a template"))
162
171
163 # perhaps it's a path to a map or a template
172 # perhaps it's a path to a map or a template
164 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
173 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
165 # is it a mapfile for a style?
174 # is it a mapfile for a style?
166 if os.path.basename(tmpl).startswith("map-"):
175 if os.path.basename(tmpl).startswith("map-"):
167 return None, os.path.realpath(tmpl)
176 return None, os.path.realpath(tmpl)
168 tmpl = open(tmpl).read()
177 tmpl = open(tmpl).read()
169 return tmpl, None
178 return tmpl, None
170
179
171 # constant string?
180 # constant string?
172 return tmpl, None
181 return tmpl, None
173
182
174 def gettemplater(ui, topic, spec):
183 def gettemplater(ui, topic, spec):
175 tmpl, mapfile = lookuptemplate(ui, topic, spec)
184 tmpl, mapfile = lookuptemplate(ui, topic, spec)
176 t = templater.templater(mapfile, {})
185 t = templater.templater(mapfile, {})
177 if tmpl:
186 if tmpl:
178 t.cache[topic] = tmpl
187 t.cache[topic] = tmpl
179 return t
188 return t
180
189
181 def formatter(ui, topic, opts):
190 def formatter(ui, topic, opts):
182 template = opts.get("template", "")
191 template = opts.get("template", "")
183 if template == "json":
192 if template == "json":
184 return jsonformatter(ui, topic, opts)
193 return jsonformatter(ui, topic, opts)
185 elif template == "pickle":
194 elif template == "pickle":
186 return pickleformatter(ui, topic, opts)
195 return pickleformatter(ui, topic, opts)
187 elif template == "debug":
196 elif template == "debug":
188 return debugformatter(ui, topic, opts)
197 return debugformatter(ui, topic, opts)
189 elif template != "":
198 elif template != "":
190 raise util.Abort(_("custom templates not yet supported"))
199 return templateformatter(ui, topic, opts)
191 elif ui.configbool('ui', 'formatdebug'):
200 elif ui.configbool('ui', 'formatdebug'):
192 return debugformatter(ui, topic, opts)
201 return debugformatter(ui, topic, opts)
193 elif ui.configbool('ui', 'formatjson'):
202 elif ui.configbool('ui', 'formatjson'):
194 return jsonformatter(ui, topic, opts)
203 return jsonformatter(ui, topic, opts)
195 return plainformatter(ui, topic, opts)
204 return plainformatter(ui, topic, opts)
General Comments 0
You need to be logged in to leave comments. Login now