##// END OF EJS Templates
color: wrap docstrings at 70 characters
Martin Geisler -
r9255:acfbb88c default
parent child Browse files
Show More
@@ -1,288 +1,289 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 This extension modifies the status command to add color to its output to
22 reflect file status, the qseries command to add color to reflect patch status
23 (applied, unapplied, missing), and to diff-related commands to highlight
24 additions, removals, diff headers, and trailing whitespace.
21 This extension modifies the status command to add color to its output
22 to reflect file status, the qseries command to add color to reflect
23 patch status (applied, unapplied, missing), and to diff-related
24 commands to highlight additions, removals, diff headers, and trailing
25 whitespace.
25 26
26 Other effects in addition to color, like bold and underlined text, are also
27 available. Effects are rendered with the ECMA-48 SGR control function (aka
28 ANSI escape codes). This module also provides the render_text function, which
29 can be used to add effects to any text.
27 Other effects in addition to color, like bold and underlined text, are
28 also available. Effects are rendered with the ECMA-48 SGR control
29 function (aka ANSI escape codes). This module also provides the
30 render_text function, which can be used to add effects to any text.
30 31
31 32 Default effects may be overridden from the .hgrc file::
32 33
33 34 [color]
34 35 status.modified = blue bold underline red_background
35 36 status.added = green bold
36 37 status.removed = red bold blue_background
37 38 status.deleted = cyan bold underline
38 39 status.unknown = magenta bold underline
39 40 status.ignored = black bold
40 41
41 42 # 'none' turns off all effects
42 43 status.clean = none
43 44 status.copied = none
44 45
45 46 qseries.applied = blue bold underline
46 47 qseries.unapplied = black bold
47 48 qseries.missing = red bold
48 49
49 50 diff.diffline = bold
50 51 diff.extended = cyan bold
51 52 diff.file_a = red bold
52 53 diff.file_b = green bold
53 54 diff.hunk = magenta
54 55 diff.deleted = red
55 56 diff.inserted = green
56 57 diff.changed = white
57 58 diff.trailingwhitespace = bold red_background
58 59 '''
59 60
60 61 import os, sys
61 62
62 63 from mercurial import cmdutil, commands, extensions, error
63 64 from mercurial.i18n import _
64 65
65 66 # start and stop parameters for effects
66 67 _effect_params = {'none': 0,
67 68 'black': 30,
68 69 'red': 31,
69 70 'green': 32,
70 71 'yellow': 33,
71 72 'blue': 34,
72 73 'magenta': 35,
73 74 'cyan': 36,
74 75 'white': 37,
75 76 'bold': 1,
76 77 'italic': 3,
77 78 'underline': 4,
78 79 'inverse': 7,
79 80 'black_background': 40,
80 81 'red_background': 41,
81 82 'green_background': 42,
82 83 'yellow_background': 43,
83 84 'blue_background': 44,
84 85 'purple_background': 45,
85 86 'cyan_background': 46,
86 87 'white_background': 47}
87 88
88 89 def render_effects(text, effects):
89 90 'Wrap text in commands to turn on each effect.'
90 91 start = [str(_effect_params[e]) for e in ['none'] + effects]
91 92 start = '\033[' + ';'.join(start) + 'm'
92 93 stop = '\033[' + str(_effect_params['none']) + 'm'
93 94 return ''.join([start, text, stop])
94 95
95 96 def colorstatus(orig, ui, repo, *pats, **opts):
96 97 '''run the status command with colored output'''
97 98
98 99 delimiter = opts['print0'] and '\0' or '\n'
99 100
100 101 nostatus = opts.get('no_status')
101 102 opts['no_status'] = False
102 103 # run status and capture its output
103 104 ui.pushbuffer()
104 105 retval = orig(ui, repo, *pats, **opts)
105 106 # filter out empty strings
106 107 lines_with_status = [ line for line in ui.popbuffer().split(delimiter) if line ]
107 108
108 109 if nostatus:
109 110 lines = [l[2:] for l in lines_with_status]
110 111 else:
111 112 lines = lines_with_status
112 113
113 114 # apply color to output and display it
114 115 for i in xrange(len(lines)):
115 116 status = _status_abbreviations[lines_with_status[i][0]]
116 117 effects = _status_effects[status]
117 118 if effects:
118 119 lines[i] = render_effects(lines[i], effects)
119 120 ui.write(lines[i] + delimiter)
120 121 return retval
121 122
122 123 _status_abbreviations = { 'M': 'modified',
123 124 'A': 'added',
124 125 'R': 'removed',
125 126 '!': 'deleted',
126 127 '?': 'unknown',
127 128 'I': 'ignored',
128 129 'C': 'clean',
129 130 ' ': 'copied', }
130 131
131 132 _status_effects = { 'modified': ['blue', 'bold'],
132 133 'added': ['green', 'bold'],
133 134 'removed': ['red', 'bold'],
134 135 'deleted': ['cyan', 'bold', 'underline'],
135 136 'unknown': ['magenta', 'bold', 'underline'],
136 137 'ignored': ['black', 'bold'],
137 138 'clean': ['none'],
138 139 'copied': ['none'], }
139 140
140 141 def colorqseries(orig, ui, repo, *dummy, **opts):
141 142 '''run the qseries command with colored output'''
142 143 ui.pushbuffer()
143 144 retval = orig(ui, repo, **opts)
144 145 patches = ui.popbuffer().splitlines()
145 146 for patch in patches:
146 147 patchname = patch
147 148 if opts['summary']:
148 149 patchname = patchname.split(': ', 1)[0]
149 150 if ui.verbose:
150 151 patchname = patchname.lstrip().split(' ', 2)[-1]
151 152
152 153 if opts['missing']:
153 154 effects = _patch_effects['missing']
154 155 # Determine if patch is applied.
155 156 elif [ applied for applied in repo.mq.applied
156 157 if patchname == applied.name ]:
157 158 effects = _patch_effects['applied']
158 159 else:
159 160 effects = _patch_effects['unapplied']
160 161
161 162 patch = patch.replace(patchname, render_effects(patchname, effects), 1)
162 163 ui.write(patch + '\n')
163 164 return retval
164 165
165 166 _patch_effects = { 'applied': ['blue', 'bold', 'underline'],
166 167 'missing': ['red', 'bold'],
167 168 'unapplied': ['black', 'bold'], }
168 169
169 170 def colorwrap(orig, s):
170 171 '''wrap ui.write for colored diff output'''
171 172 lines = s.split('\n')
172 173 for i, line in enumerate(lines):
173 174 stripline = line
174 175 if line and line[0] in '+-':
175 176 # highlight trailing whitespace, but only in changed lines
176 177 stripline = line.rstrip()
177 178 for prefix, style in _diff_prefixes:
178 179 if stripline.startswith(prefix):
179 180 lines[i] = render_effects(stripline, _diff_effects[style])
180 181 break
181 182 if line != stripline:
182 183 lines[i] += render_effects(
183 184 line[len(stripline):], _diff_effects['trailingwhitespace'])
184 185 orig('\n'.join(lines))
185 186
186 187 def colorshowpatch(orig, self, node):
187 188 '''wrap cmdutil.changeset_printer.showpatch with colored output'''
188 189 oldwrite = extensions.wrapfunction(self.ui, 'write', colorwrap)
189 190 try:
190 191 orig(self, node)
191 192 finally:
192 193 self.ui.write = oldwrite
193 194
194 195 def colordiff(orig, ui, repo, *pats, **opts):
195 196 '''run the diff command with colored output'''
196 197 oldwrite = extensions.wrapfunction(ui, 'write', colorwrap)
197 198 try:
198 199 orig(ui, repo, *pats, **opts)
199 200 finally:
200 201 ui.write = oldwrite
201 202
202 203 _diff_prefixes = [('diff', 'diffline'),
203 204 ('copy', 'extended'),
204 205 ('rename', 'extended'),
205 206 ('old', 'extended'),
206 207 ('new', 'extended'),
207 208 ('deleted', 'extended'),
208 209 ('---', 'file_a'),
209 210 ('+++', 'file_b'),
210 211 ('@', 'hunk'),
211 212 ('-', 'deleted'),
212 213 ('+', 'inserted')]
213 214
214 215 _diff_effects = {'diffline': ['bold'],
215 216 'extended': ['cyan', 'bold'],
216 217 'file_a': ['red', 'bold'],
217 218 'file_b': ['green', 'bold'],
218 219 'hunk': ['magenta'],
219 220 'deleted': ['red'],
220 221 'inserted': ['green'],
221 222 'changed': ['white'],
222 223 'trailingwhitespace': ['bold', 'red_background']}
223 224
224 225 _ui = None
225 226
226 227 def uisetup(ui):
227 228 '''Initialize the extension.'''
228 229 global _ui
229 230 _ui = ui
230 231 _setupcmd(ui, 'diff', commands.table, colordiff, _diff_effects)
231 232 _setupcmd(ui, 'incoming', commands.table, None, _diff_effects)
232 233 _setupcmd(ui, 'log', commands.table, None, _diff_effects)
233 234 _setupcmd(ui, 'outgoing', commands.table, None, _diff_effects)
234 235 _setupcmd(ui, 'tip', commands.table, None, _diff_effects)
235 236 _setupcmd(ui, 'status', commands.table, colorstatus, _status_effects)
236 237
237 238 def extsetup():
238 239 try:
239 240 mq = extensions.find('mq')
240 241 try:
241 242 # If we are loaded after mq, we must wrap commands.table
242 243 _setupcmd(_ui, 'qdiff', commands.table, colordiff, _diff_effects)
243 244 _setupcmd(_ui, 'qseries', commands.table, colorqseries, _patch_effects)
244 245 except error.UnknownCommand:
245 246 # Otherwise we wrap mq.cmdtable
246 247 _setupcmd(_ui, 'qdiff', mq.cmdtable, colordiff, _diff_effects)
247 248 _setupcmd(_ui, 'qseries', mq.cmdtable, colorqseries, _patch_effects)
248 249 except KeyError:
249 250 # The mq extension is not enabled
250 251 pass
251 252
252 253 def _setupcmd(ui, cmd, table, func, effectsmap):
253 254 '''patch in command to command table and load effect map'''
254 255 def nocolor(orig, *args, **opts):
255 256
256 257 if (opts['no_color'] or opts['color'] == 'never' or
257 258 (opts['color'] == 'auto' and (os.environ.get('TERM') == 'dumb'
258 259 or not sys.__stdout__.isatty()))):
259 260 return orig(*args, **opts)
260 261
261 262 oldshowpatch = extensions.wrapfunction(cmdutil.changeset_printer,
262 263 'showpatch', colorshowpatch)
263 264 try:
264 265 if func is not None:
265 266 return func(orig, *args, **opts)
266 267 return orig(*args, **opts)
267 268 finally:
268 269 cmdutil.changeset_printer.showpatch = oldshowpatch
269 270
270 271 entry = extensions.wrapcommand(table, cmd, nocolor)
271 272 entry[1].extend([
272 273 ('', 'color', 'auto', _("when to colorize (always, auto, or never)")),
273 274 ('', 'no-color', None, _("don't colorize output")),
274 275 ])
275 276
276 277 for status in effectsmap:
277 278 configkey = cmd + '.' + status
278 279 effects = ui.configlist('color', configkey)
279 280 if effects:
280 281 good = []
281 282 for e in effects:
282 283 if e in _effect_params:
283 284 good.append(e)
284 285 else:
285 286 ui.warn(_("ignoring unknown color/effect %r "
286 287 "(configured in color.%s)\n")
287 288 % (e, configkey))
288 289 effectsmap[status] = good
General Comments 0
You need to be logged in to leave comments. Login now