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