##// END OF EJS Templates
color: Do not pass --color, --no-color parameters to wrapped function...
David Soria Parra -
r10045:d1f9640e default
parent child Browse files
Show More
@@ -1,314 +1,318
1 1 # color.py color output for the status and qseries commands
2 2 #
3 3 # Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.com>
4 4 #
5 5 # This program is free software; you can redistribute it and/or modify it
6 6 # under the terms of the GNU General Public License as published by the
7 7 # Free Software Foundation; either version 2 of the License, or (at your
8 8 # option) any later version.
9 9 #
10 10 # This program is distributed in the hope that it will be useful, but
11 11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13 13 # Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License along
16 16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 18
19 19 '''colorize output from some commands
20 20
21 21 This extension modifies the status command to add color to its output
22 22 to reflect file status, the qseries command to add color to reflect
23 23 patch status (applied, unapplied, missing), and to diff-related
24 24 commands to highlight additions, removals, diff headers, and trailing
25 25 whitespace.
26 26
27 27 Other effects in addition to color, like bold and underlined text, are
28 28 also available. Effects are rendered with the ECMA-48 SGR control
29 29 function (aka ANSI escape codes). This module also provides the
30 30 render_text function, which can be used to add effects to any text.
31 31
32 32 Default effects may be overridden from the .hgrc file::
33 33
34 34 [color]
35 35 status.modified = blue bold underline red_background
36 36 status.added = green bold
37 37 status.removed = red bold blue_background
38 38 status.deleted = cyan bold underline
39 39 status.unknown = magenta bold underline
40 40 status.ignored = black bold
41 41
42 42 # 'none' turns off all effects
43 43 status.clean = none
44 44 status.copied = none
45 45
46 46 qseries.applied = blue bold underline
47 47 qseries.unapplied = black bold
48 48 qseries.missing = red bold
49 49
50 50 diff.diffline = bold
51 51 diff.extended = cyan bold
52 52 diff.file_a = red bold
53 53 diff.file_b = green bold
54 54 diff.hunk = magenta
55 55 diff.deleted = red
56 56 diff.inserted = green
57 57 diff.changed = white
58 58 diff.trailingwhitespace = bold red_background
59 59 '''
60 60
61 61 import os, sys
62 62
63 63 from mercurial import cmdutil, commands, extensions, error
64 64 from mercurial.i18n import _
65 65
66 66 # start and stop parameters for effects
67 67 _effect_params = {'none': 0,
68 68 'black': 30,
69 69 'red': 31,
70 70 'green': 32,
71 71 'yellow': 33,
72 72 'blue': 34,
73 73 'magenta': 35,
74 74 'cyan': 36,
75 75 'white': 37,
76 76 'bold': 1,
77 77 'italic': 3,
78 78 'underline': 4,
79 79 'inverse': 7,
80 80 'black_background': 40,
81 81 'red_background': 41,
82 82 'green_background': 42,
83 83 'yellow_background': 43,
84 84 'blue_background': 44,
85 85 'purple_background': 45,
86 86 'cyan_background': 46,
87 87 'white_background': 47}
88 88
89 89 def render_effects(text, effects):
90 90 'Wrap text in commands to turn on each effect.'
91 91 start = [str(_effect_params[e]) for e in ['none'] + effects]
92 92 start = '\033[' + ';'.join(start) + 'm'
93 93 stop = '\033[' + str(_effect_params['none']) + 'm'
94 94 return ''.join([start, text, stop])
95 95
96 96 def colorstatus(orig, ui, repo, *pats, **opts):
97 97 '''run the status command with colored output'''
98 98
99 99 delimiter = opts['print0'] and '\0' or '\n'
100 100
101 101 nostatus = opts.get('no_status')
102 102 opts['no_status'] = False
103 103 # run status and capture its output
104 104 ui.pushbuffer()
105 105 retval = orig(ui, repo, *pats, **opts)
106 106 # filter out empty strings
107 107 lines_with_status = [ line for line in ui.popbuffer().split(delimiter) if line ]
108 108
109 109 if nostatus:
110 110 lines = [l[2:] for l in lines_with_status]
111 111 else:
112 112 lines = lines_with_status
113 113
114 114 # apply color to output and display it
115 115 for i in xrange(len(lines)):
116 116 status = _status_abbreviations[lines_with_status[i][0]]
117 117 effects = _status_effects[status]
118 118 if effects:
119 119 lines[i] = render_effects(lines[i], effects)
120 120 ui.write(lines[i] + delimiter)
121 121 return retval
122 122
123 123 _status_abbreviations = { 'M': 'modified',
124 124 'A': 'added',
125 125 'R': 'removed',
126 126 '!': 'deleted',
127 127 '?': 'unknown',
128 128 'I': 'ignored',
129 129 'C': 'clean',
130 130 ' ': 'copied', }
131 131
132 132 _status_effects = { 'modified': ['blue', 'bold'],
133 133 'added': ['green', 'bold'],
134 134 'removed': ['red', 'bold'],
135 135 'deleted': ['cyan', 'bold', 'underline'],
136 136 'unknown': ['magenta', 'bold', 'underline'],
137 137 'ignored': ['black', 'bold'],
138 138 'clean': ['none'],
139 139 'copied': ['none'], }
140 140
141 141 def colorqseries(orig, ui, repo, *dummy, **opts):
142 142 '''run the qseries command with colored output'''
143 143 ui.pushbuffer()
144 144 retval = orig(ui, repo, **opts)
145 145 patchlines = ui.popbuffer().splitlines()
146 146 patchnames = repo.mq.series
147 147
148 148 for patch, patchname in zip(patchlines, patchnames):
149 149 if opts['missing']:
150 150 effects = _patch_effects['missing']
151 151 # Determine if patch is applied.
152 152 elif [ applied for applied in repo.mq.applied
153 153 if patchname == applied.name ]:
154 154 effects = _patch_effects['applied']
155 155 else:
156 156 effects = _patch_effects['unapplied']
157 157
158 158 patch = patch.replace(patchname, render_effects(patchname, effects), 1)
159 159 ui.write(patch + '\n')
160 160 return retval
161 161
162 162 _patch_effects = { 'applied': ['blue', 'bold', 'underline'],
163 163 'missing': ['red', 'bold'],
164 164 'unapplied': ['black', 'bold'], }
165 165 def colorwrap(orig, *args):
166 166 '''wrap ui.write for colored diff output'''
167 167 def _colorize(s):
168 168 lines = s.split('\n')
169 169 for i, line in enumerate(lines):
170 170 stripline = line
171 171 if line and line[0] in '+-':
172 172 # highlight trailing whitespace, but only in changed lines
173 173 stripline = line.rstrip()
174 174 for prefix, style in _diff_prefixes:
175 175 if stripline.startswith(prefix):
176 176 lines[i] = render_effects(stripline, _diff_effects[style])
177 177 break
178 178 if line != stripline:
179 179 lines[i] += render_effects(
180 180 line[len(stripline):], _diff_effects['trailingwhitespace'])
181 181 return '\n'.join(lines)
182 182 orig(*[_colorize(s) for s in args])
183 183
184 184 def colorshowpatch(orig, self, node):
185 185 '''wrap cmdutil.changeset_printer.showpatch with colored output'''
186 186 oldwrite = extensions.wrapfunction(self.ui, 'write', colorwrap)
187 187 try:
188 188 orig(self, node)
189 189 finally:
190 190 self.ui.write = oldwrite
191 191
192 192 def colordiffstat(orig, s):
193 193 lines = s.split('\n')
194 194 for i, line in enumerate(lines):
195 195 if line and line[-1] in '+-':
196 196 name, graph = line.rsplit(' ', 1)
197 197 graph = graph.replace('-',
198 198 render_effects('-', _diff_effects['deleted']))
199 199 graph = graph.replace('+',
200 200 render_effects('+', _diff_effects['inserted']))
201 201 lines[i] = ' '.join([name, graph])
202 202 orig('\n'.join(lines))
203 203
204 204 def colordiff(orig, ui, repo, *pats, **opts):
205 205 '''run the diff command with colored output'''
206 206 if opts.get('stat'):
207 207 wrapper = colordiffstat
208 208 else:
209 209 wrapper = colorwrap
210 210 oldwrite = extensions.wrapfunction(ui, 'write', wrapper)
211 211 try:
212 212 orig(ui, repo, *pats, **opts)
213 213 finally:
214 214 ui.write = oldwrite
215 215
216 216 def colorchurn(orig, ui, repo, *pats, **opts):
217 217 '''run the churn command with colored output'''
218 218 if not opts.get('diffstat'):
219 219 return orig(ui, repo, *pats, **opts)
220 220 oldwrite = extensions.wrapfunction(ui, 'write', colordiffstat)
221 221 try:
222 222 orig(ui, repo, *pats, **opts)
223 223 finally:
224 224 ui.write = oldwrite
225 225
226 226 _diff_prefixes = [('diff', 'diffline'),
227 227 ('copy', 'extended'),
228 228 ('rename', 'extended'),
229 229 ('old', 'extended'),
230 230 ('new', 'extended'),
231 231 ('deleted', 'extended'),
232 232 ('---', 'file_a'),
233 233 ('+++', 'file_b'),
234 234 ('@', 'hunk'),
235 235 ('-', 'deleted'),
236 236 ('+', 'inserted')]
237 237
238 238 _diff_effects = {'diffline': ['bold'],
239 239 'extended': ['cyan', 'bold'],
240 240 'file_a': ['red', 'bold'],
241 241 'file_b': ['green', 'bold'],
242 242 'hunk': ['magenta'],
243 243 'deleted': ['red'],
244 244 'inserted': ['green'],
245 245 'changed': ['white'],
246 246 'trailingwhitespace': ['bold', 'red_background']}
247 247
248 248 def extsetup(ui):
249 249 '''Initialize the extension.'''
250 250 _setupcmd(ui, 'diff', commands.table, colordiff, _diff_effects)
251 251 _setupcmd(ui, 'incoming', commands.table, None, _diff_effects)
252 252 _setupcmd(ui, 'log', commands.table, None, _diff_effects)
253 253 _setupcmd(ui, 'outgoing', commands.table, None, _diff_effects)
254 254 _setupcmd(ui, 'tip', commands.table, None, _diff_effects)
255 255 _setupcmd(ui, 'status', commands.table, colorstatus, _status_effects)
256 256
257 257 try:
258 258 mq = extensions.find('mq')
259 259 _setupcmd(ui, 'qdiff', mq.cmdtable, colordiff, _diff_effects)
260 260 _setupcmd(ui, 'qseries', mq.cmdtable, colorqseries, _patch_effects)
261 261 except KeyError:
262 262 mq = None
263 263
264 264 try:
265 265 rec = extensions.find('record')
266 266 _setupcmd(ui, 'record', rec.cmdtable, colordiff, _diff_effects)
267 267 except KeyError:
268 268 rec = None
269 269
270 270 if mq and rec:
271 271 _setupcmd(ui, 'qrecord', rec.cmdtable, colordiff, _diff_effects)
272 272 try:
273 273 churn = extensions.find('churn')
274 274 _setupcmd(ui, 'churn', churn.cmdtable, colorchurn, _diff_effects)
275 275 except KeyError:
276 276 churn = None
277 277
278 278 def _setupcmd(ui, cmd, table, func, effectsmap):
279 279 '''patch in command to command table and load effect map'''
280 280 def nocolor(orig, *args, **opts):
281 281
282 282 if (opts['no_color'] or opts['color'] == 'never' or
283 283 (opts['color'] == 'auto' and (os.environ.get('TERM') == 'dumb'
284 284 or not sys.__stdout__.isatty()))):
285 del opts['no_color']
286 del opts['color']
285 287 return orig(*args, **opts)
286 288
287 289 oldshowpatch = extensions.wrapfunction(cmdutil.changeset_printer,
288 290 'showpatch', colorshowpatch)
291 del opts['no_color']
292 del opts['color']
289 293 try:
290 294 if func is not None:
291 295 return func(orig, *args, **opts)
292 296 return orig(*args, **opts)
293 297 finally:
294 298 cmdutil.changeset_printer.showpatch = oldshowpatch
295 299
296 300 entry = extensions.wrapcommand(table, cmd, nocolor)
297 301 entry[1].extend([
298 302 ('', 'color', 'auto', _("when to colorize (always, auto, or never)")),
299 303 ('', 'no-color', None, _("don't colorize output (DEPRECATED)")),
300 304 ])
301 305
302 306 for status in effectsmap:
303 307 configkey = cmd + '.' + status
304 308 effects = ui.configlist('color', configkey)
305 309 if effects:
306 310 good = []
307 311 for e in effects:
308 312 if e in _effect_params:
309 313 good.append(e)
310 314 else:
311 315 ui.warn(_("ignoring unknown color/effect %r "
312 316 "(configured in color.%s)\n")
313 317 % (e, configkey))
314 318 effectsmap[status] = good
General Comments 0
You need to be logged in to leave comments. Login now