##// END OF EJS Templates
color: allow multiple args to ui.write()
Kevin Bullock -
r9520:bcc27ee3 default
parent child Browse files
Show More
@@ -1,287 +1,286 b''
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 patches = ui.popbuffer().splitlines()
146 146 for patch in patches:
147 147 patchname = patch
148 148 if opts['summary']:
149 149 patchname = patchname.split(': ')[0]
150 150 if ui.verbose:
151 151 patchname = patchname.split(' ', 2)[-1]
152 152
153 153 if opts['missing']:
154 154 effects = _patch_effects['missing']
155 155 # Determine if patch is applied.
156 156 elif [ applied for applied in repo.mq.applied
157 157 if patchname == applied.name ]:
158 158 effects = _patch_effects['applied']
159 159 else:
160 160 effects = _patch_effects['unapplied']
161 161 ui.write(render_effects(patch, effects) + '\n')
162 162 return retval
163 163
164 164 _patch_effects = { 'applied': ['blue', 'bold', 'underline'],
165 165 'missing': ['red', 'bold'],
166 166 'unapplied': ['black', 'bold'], }
167
168 167 def colorwrap(orig, s):
169 168 '''wrap ui.write for colored diff output'''
170 169 lines = s.split('\n')
171 170 for i, line in enumerate(lines):
172 171 stripline = line
173 172 if line and line[0] in '+-':
174 173 # highlight trailing whitespace, but only in changed lines
175 174 stripline = line.rstrip()
176 175 for prefix, style in _diff_prefixes:
177 176 if stripline.startswith(prefix):
178 177 lines[i] = render_effects(stripline, _diff_effects[style])
179 178 break
180 179 if line != stripline:
181 180 lines[i] += render_effects(
182 181 line[len(stripline):], _diff_effects['trailingwhitespace'])
183 182 orig('\n'.join(lines))
184 183
185 184 def colorshowpatch(orig, self, node):
186 185 '''wrap cmdutil.changeset_printer.showpatch with colored output'''
187 186 oldwrite = extensions.wrapfunction(self.ui, 'write', colorwrap)
188 187 try:
189 188 orig(self, node)
190 189 finally:
191 190 self.ui.write = oldwrite
192 191
193 192 def colordiff(orig, ui, repo, *pats, **opts):
194 193 '''run the diff command with colored output'''
195 194 oldwrite = extensions.wrapfunction(ui, 'write', colorwrap)
196 195 try:
197 196 orig(ui, repo, *pats, **opts)
198 197 finally:
199 198 ui.write = oldwrite
200 199
201 200 _diff_prefixes = [('diff', 'diffline'),
202 201 ('copy', 'extended'),
203 202 ('rename', 'extended'),
204 203 ('old', 'extended'),
205 204 ('new', 'extended'),
206 205 ('deleted', 'extended'),
207 206 ('---', 'file_a'),
208 207 ('+++', 'file_b'),
209 208 ('@', 'hunk'),
210 209 ('-', 'deleted'),
211 210 ('+', 'inserted')]
212 211
213 212 _diff_effects = {'diffline': ['bold'],
214 213 'extended': ['cyan', 'bold'],
215 214 'file_a': ['red', 'bold'],
216 215 'file_b': ['green', 'bold'],
217 216 'hunk': ['magenta'],
218 217 'deleted': ['red'],
219 218 'inserted': ['green'],
220 219 'changed': ['white'],
221 220 'trailingwhitespace': ['bold', 'red_background']}
222 221
223 222 _ui = None
224 223
225 224 def uisetup(ui):
226 225 '''Initialize the extension.'''
227 226 global _ui
228 227 _ui = ui
229 228 _setupcmd(ui, 'diff', commands.table, colordiff, _diff_effects)
230 229 _setupcmd(ui, 'incoming', commands.table, None, _diff_effects)
231 230 _setupcmd(ui, 'log', commands.table, None, _diff_effects)
232 231 _setupcmd(ui, 'outgoing', commands.table, None, _diff_effects)
233 232 _setupcmd(ui, 'tip', commands.table, None, _diff_effects)
234 233 _setupcmd(ui, 'status', commands.table, colorstatus, _status_effects)
235 234
236 235 def extsetup():
237 236 try:
238 237 mq = extensions.find('mq')
239 238 try:
240 239 # If we are loaded after mq, we must wrap commands.table
241 240 _setupcmd(_ui, 'qdiff', commands.table, colordiff, _diff_effects)
242 241 _setupcmd(_ui, 'qseries', commands.table, colorqseries, _patch_effects)
243 242 except error.UnknownCommand:
244 243 # Otherwise we wrap mq.cmdtable
245 244 _setupcmd(_ui, 'qdiff', mq.cmdtable, colordiff, _diff_effects)
246 245 _setupcmd(_ui, 'qseries', mq.cmdtable, colorqseries, _patch_effects)
247 246 except KeyError:
248 247 # The mq extension is not enabled
249 248 pass
250 249
251 250 def _setupcmd(ui, cmd, table, func, effectsmap):
252 251 '''patch in command to command table and load effect map'''
253 252 def nocolor(orig, *args, **opts):
254 253
255 254 if (opts['no_color'] or opts['color'] == 'never' or
256 255 (opts['color'] == 'auto' and (os.environ.get('TERM') == 'dumb'
257 256 or not sys.__stdout__.isatty()))):
258 257 return orig(*args, **opts)
259 258
260 259 oldshowpatch = extensions.wrapfunction(cmdutil.changeset_printer,
261 260 'showpatch', colorshowpatch)
262 261 try:
263 262 if func is not None:
264 263 return func(orig, *args, **opts)
265 264 return orig(*args, **opts)
266 265 finally:
267 266 cmdutil.changeset_printer.showpatch = oldshowpatch
268 267
269 268 entry = extensions.wrapcommand(table, cmd, nocolor)
270 269 entry[1].extend([
271 270 ('', 'color', 'auto', _("when to colorize (always, auto, or never)")),
272 271 ('', 'no-color', None, _("don't colorize output")),
273 272 ])
274 273
275 274 for status in effectsmap:
276 275 configkey = cmd + '.' + status
277 276 effects = ui.configlist('color', configkey)
278 277 if effects:
279 278 good = []
280 279 for e in effects:
281 280 if e in _effect_params:
282 281 good.append(e)
283 282 else:
284 283 ui.warn(_("ignoring unknown color/effect %r "
285 284 "(configured in color.%s)\n")
286 285 % (e, configkey))
287 286 effectsmap[status] = good
General Comments 0
You need to be logged in to leave comments. Login now