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