##// END OF EJS Templates
color: Add support for bookmarks
David Soria Parra -
r10046:0c23b0b3 default
parent child Browse files
Show More
@@ -1,318 +1,343 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
60 bookmarks.current = green
59 61 '''
60 62
61 63 import os, sys
62 64
63 65 from mercurial import cmdutil, commands, extensions, error
64 66 from mercurial.i18n import _
65 67
66 68 # start and stop parameters for effects
67 69 _effect_params = {'none': 0,
68 70 'black': 30,
69 71 'red': 31,
70 72 'green': 32,
71 73 'yellow': 33,
72 74 'blue': 34,
73 75 'magenta': 35,
74 76 'cyan': 36,
75 77 'white': 37,
76 78 'bold': 1,
77 79 'italic': 3,
78 80 'underline': 4,
79 81 'inverse': 7,
80 82 'black_background': 40,
81 83 'red_background': 41,
82 84 'green_background': 42,
83 85 'yellow_background': 43,
84 86 'blue_background': 44,
85 87 'purple_background': 45,
86 88 'cyan_background': 46,
87 89 'white_background': 47}
88 90
89 91 def render_effects(text, effects):
90 92 'Wrap text in commands to turn on each effect.'
91 93 start = [str(_effect_params[e]) for e in ['none'] + effects]
92 94 start = '\033[' + ';'.join(start) + 'm'
93 95 stop = '\033[' + str(_effect_params['none']) + 'm'
94 96 return ''.join([start, text, stop])
95 97
96 98 def colorstatus(orig, ui, repo, *pats, **opts):
97 99 '''run the status command with colored output'''
98 100
99 101 delimiter = opts['print0'] and '\0' or '\n'
100 102
101 103 nostatus = opts.get('no_status')
102 104 opts['no_status'] = False
103 105 # run status and capture its output
104 106 ui.pushbuffer()
105 107 retval = orig(ui, repo, *pats, **opts)
106 108 # filter out empty strings
107 109 lines_with_status = [ line for line in ui.popbuffer().split(delimiter) if line ]
108 110
109 111 if nostatus:
110 112 lines = [l[2:] for l in lines_with_status]
111 113 else:
112 114 lines = lines_with_status
113 115
114 116 # apply color to output and display it
115 117 for i in xrange(len(lines)):
116 118 status = _status_abbreviations[lines_with_status[i][0]]
117 119 effects = _status_effects[status]
118 120 if effects:
119 121 lines[i] = render_effects(lines[i], effects)
120 122 ui.write(lines[i] + delimiter)
121 123 return retval
122 124
123 125 _status_abbreviations = { 'M': 'modified',
124 126 'A': 'added',
125 127 'R': 'removed',
126 128 '!': 'deleted',
127 129 '?': 'unknown',
128 130 'I': 'ignored',
129 131 'C': 'clean',
130 132 ' ': 'copied', }
131 133
132 134 _status_effects = { 'modified': ['blue', 'bold'],
133 135 'added': ['green', 'bold'],
134 136 'removed': ['red', 'bold'],
135 137 'deleted': ['cyan', 'bold', 'underline'],
136 138 'unknown': ['magenta', 'bold', 'underline'],
137 139 'ignored': ['black', 'bold'],
138 140 'clean': ['none'],
139 141 'copied': ['none'], }
140 142
143 _bookmark_effects = { 'current': ['green'] }
144
145 def colorbookmarks(orig, ui, repo, *pats, **opts):
146 def colorize(orig, s):
147 lines = s.split('\n')
148 for i, line in enumerate(lines):
149 if line.startswith(" *"):
150 lines[i] = render_effects(line, _bookmark_effects['current'])
151 orig('\n'.join(lines))
152 oldwrite = extensions.wrapfunction(ui, 'write', colorize)
153 try:
154 orig(ui, repo, *pats, **opts)
155 finally:
156 ui.write = oldwrite
157
141 158 def colorqseries(orig, ui, repo, *dummy, **opts):
142 159 '''run the qseries command with colored output'''
143 160 ui.pushbuffer()
144 161 retval = orig(ui, repo, **opts)
145 162 patchlines = ui.popbuffer().splitlines()
146 163 patchnames = repo.mq.series
147 164
148 165 for patch, patchname in zip(patchlines, patchnames):
149 166 if opts['missing']:
150 167 effects = _patch_effects['missing']
151 168 # Determine if patch is applied.
152 169 elif [ applied for applied in repo.mq.applied
153 170 if patchname == applied.name ]:
154 171 effects = _patch_effects['applied']
155 172 else:
156 173 effects = _patch_effects['unapplied']
157 174
158 175 patch = patch.replace(patchname, render_effects(patchname, effects), 1)
159 176 ui.write(patch + '\n')
160 177 return retval
161 178
162 179 _patch_effects = { 'applied': ['blue', 'bold', 'underline'],
163 180 'missing': ['red', 'bold'],
164 181 'unapplied': ['black', 'bold'], }
165 182 def colorwrap(orig, *args):
166 183 '''wrap ui.write for colored diff output'''
167 184 def _colorize(s):
168 185 lines = s.split('\n')
169 186 for i, line in enumerate(lines):
170 187 stripline = line
171 188 if line and line[0] in '+-':
172 189 # highlight trailing whitespace, but only in changed lines
173 190 stripline = line.rstrip()
174 191 for prefix, style in _diff_prefixes:
175 192 if stripline.startswith(prefix):
176 193 lines[i] = render_effects(stripline, _diff_effects[style])
177 194 break
178 195 if line != stripline:
179 196 lines[i] += render_effects(
180 197 line[len(stripline):], _diff_effects['trailingwhitespace'])
181 198 return '\n'.join(lines)
182 199 orig(*[_colorize(s) for s in args])
183 200
184 201 def colorshowpatch(orig, self, node):
185 202 '''wrap cmdutil.changeset_printer.showpatch with colored output'''
186 203 oldwrite = extensions.wrapfunction(self.ui, 'write', colorwrap)
187 204 try:
188 205 orig(self, node)
189 206 finally:
190 207 self.ui.write = oldwrite
191 208
192 209 def colordiffstat(orig, s):
193 210 lines = s.split('\n')
194 211 for i, line in enumerate(lines):
195 212 if line and line[-1] in '+-':
196 213 name, graph = line.rsplit(' ', 1)
197 214 graph = graph.replace('-',
198 215 render_effects('-', _diff_effects['deleted']))
199 216 graph = graph.replace('+',
200 217 render_effects('+', _diff_effects['inserted']))
201 218 lines[i] = ' '.join([name, graph])
202 219 orig('\n'.join(lines))
203 220
204 221 def colordiff(orig, ui, repo, *pats, **opts):
205 222 '''run the diff command with colored output'''
206 223 if opts.get('stat'):
207 224 wrapper = colordiffstat
208 225 else:
209 226 wrapper = colorwrap
210 227 oldwrite = extensions.wrapfunction(ui, 'write', wrapper)
211 228 try:
212 229 orig(ui, repo, *pats, **opts)
213 230 finally:
214 231 ui.write = oldwrite
215 232
216 233 def colorchurn(orig, ui, repo, *pats, **opts):
217 234 '''run the churn command with colored output'''
218 235 if not opts.get('diffstat'):
219 236 return orig(ui, repo, *pats, **opts)
220 237 oldwrite = extensions.wrapfunction(ui, 'write', colordiffstat)
221 238 try:
222 239 orig(ui, repo, *pats, **opts)
223 240 finally:
224 241 ui.write = oldwrite
225 242
226 243 _diff_prefixes = [('diff', 'diffline'),
227 244 ('copy', 'extended'),
228 245 ('rename', 'extended'),
229 246 ('old', 'extended'),
230 247 ('new', 'extended'),
231 248 ('deleted', 'extended'),
232 249 ('---', 'file_a'),
233 250 ('+++', 'file_b'),
234 251 ('@', 'hunk'),
235 252 ('-', 'deleted'),
236 253 ('+', 'inserted')]
237 254
238 255 _diff_effects = {'diffline': ['bold'],
239 256 'extended': ['cyan', 'bold'],
240 257 'file_a': ['red', 'bold'],
241 258 'file_b': ['green', 'bold'],
242 259 'hunk': ['magenta'],
243 260 'deleted': ['red'],
244 261 'inserted': ['green'],
245 262 'changed': ['white'],
246 263 'trailingwhitespace': ['bold', 'red_background']}
247 264
248 265 def extsetup(ui):
249 266 '''Initialize the extension.'''
250 267 _setupcmd(ui, 'diff', commands.table, colordiff, _diff_effects)
251 268 _setupcmd(ui, 'incoming', commands.table, None, _diff_effects)
252 269 _setupcmd(ui, 'log', commands.table, None, _diff_effects)
253 270 _setupcmd(ui, 'outgoing', commands.table, None, _diff_effects)
254 271 _setupcmd(ui, 'tip', commands.table, None, _diff_effects)
255 272 _setupcmd(ui, 'status', commands.table, colorstatus, _status_effects)
256 273
257 274 try:
258 275 mq = extensions.find('mq')
259 276 _setupcmd(ui, 'qdiff', mq.cmdtable, colordiff, _diff_effects)
260 277 _setupcmd(ui, 'qseries', mq.cmdtable, colorqseries, _patch_effects)
261 278 except KeyError:
262 279 mq = None
263 280
264 281 try:
265 282 rec = extensions.find('record')
266 283 _setupcmd(ui, 'record', rec.cmdtable, colordiff, _diff_effects)
267 284 except KeyError:
268 285 rec = None
269 286
270 287 if mq and rec:
271 288 _setupcmd(ui, 'qrecord', rec.cmdtable, colordiff, _diff_effects)
272 289 try:
273 290 churn = extensions.find('churn')
274 291 _setupcmd(ui, 'churn', churn.cmdtable, colorchurn, _diff_effects)
275 292 except KeyError:
276 293 churn = None
277 294
295 try:
296 bookmarks = extensions.find('bookmarks')
297 _setupcmd(ui, 'bookmarks', bookmarks.cmdtable, colorbookmarks,
298 _bookmark_effects)
299 except KeyError:
300 # The bookmarks extension is not enabled
301 pass
302
278 303 def _setupcmd(ui, cmd, table, func, effectsmap):
279 304 '''patch in command to command table and load effect map'''
280 305 def nocolor(orig, *args, **opts):
281 306
282 307 if (opts['no_color'] or opts['color'] == 'never' or
283 308 (opts['color'] == 'auto' and (os.environ.get('TERM') == 'dumb'
284 309 or not sys.__stdout__.isatty()))):
285 310 del opts['no_color']
286 311 del opts['color']
287 312 return orig(*args, **opts)
288 313
289 314 oldshowpatch = extensions.wrapfunction(cmdutil.changeset_printer,
290 315 'showpatch', colorshowpatch)
291 316 del opts['no_color']
292 317 del opts['color']
293 318 try:
294 319 if func is not None:
295 320 return func(orig, *args, **opts)
296 321 return orig(*args, **opts)
297 322 finally:
298 323 cmdutil.changeset_printer.showpatch = oldshowpatch
299 324
300 325 entry = extensions.wrapcommand(table, cmd, nocolor)
301 326 entry[1].extend([
302 327 ('', 'color', 'auto', _("when to colorize (always, auto, or never)")),
303 328 ('', 'no-color', None, _("don't colorize output (DEPRECATED)")),
304 329 ])
305 330
306 331 for status in effectsmap:
307 332 configkey = cmd + '.' + status
308 333 effects = ui.configlist('color', configkey)
309 334 if effects:
310 335 good = []
311 336 for e in effects:
312 337 if e in _effect_params:
313 338 good.append(e)
314 339 else:
315 340 ui.warn(_("ignoring unknown color/effect %r "
316 341 "(configured in color.%s)\n")
317 342 % (e, configkey))
318 343 effectsmap[status] = good
General Comments 0
You need to be logged in to leave comments. Login now