##// END OF EJS Templates
Add a reset before and after colorized output...
Kevin Christen -
r6856:c6890cfc default
parent child Browse files
Show More
@@ -1,224 +1,225
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 '''add color output to the status and qseries commands
19 '''add color output to the status and qseries 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, and the qseries command to add color to reflect patch
22 reflect file status, and the qseries command to add color to reflect patch
23 status (applied, unapplied, missing). Other effects in addition to color,
23 status (applied, unapplied, missing). Other effects in addition to color,
24 like bold and underlined text, are also available. Effects are rendered
24 like bold and underlined text, are also available. Effects are rendered
25 with the ECMA-48 SGR control function (aka ANSI escape codes). This module
25 with the ECMA-48 SGR control function (aka ANSI escape codes). This module
26 also provides the render_text function, which can be used to add effects to
26 also provides the render_text function, which can be used to add effects to
27 any text.
27 any text.
28
28
29 To enable this extension, add this to your .hgrc file:
29 To enable this extension, add this to your .hgrc file:
30 [extensions]
30 [extensions]
31 color =
31 color =
32
32
33 Default effects my be overriden from the .hgrc file:
33 Default effects my be overriden from the .hgrc file:
34
34
35 [color]
35 [color]
36 status.modified = blue bold underline red_background
36 status.modified = blue bold underline red_background
37 status.added = green bold
37 status.added = green bold
38 status.removed = red bold blue_background
38 status.removed = red bold blue_background
39 status.deleted = cyan bold underline
39 status.deleted = cyan bold underline
40 status.unknown = magenta bold underline
40 status.unknown = magenta bold underline
41 status.ignored = black bold
41 status.ignored = black bold
42
42
43 # 'none' turns off all effects
43 # 'none' turns off all effects
44 status.clean = none
44 status.clean = none
45 status.copied = none
45 status.copied = none
46
46
47 qseries.applied = blue bold underline
47 qseries.applied = blue bold underline
48 qseries.unapplied = black bold
48 qseries.unapplied = black bold
49 qseries.missing = red bold
49 qseries.missing = red bold
50 '''
50 '''
51
51
52 import re, sys
52 import re, sys
53
53
54 from mercurial import commands, cmdutil
54 from mercurial import commands, cmdutil
55 from mercurial.i18n import _
55 from mercurial.i18n import _
56
56
57 # start and stop parameters for effects
57 # start and stop parameters for effects
58 _effect_params = { 'none': (0, 0),
58 _effect_params = { 'none': (0, 0),
59 'black': (30, 39),
59 'black': (30, 39),
60 'red': (31, 39),
60 'red': (31, 39),
61 'green': (32, 39),
61 'green': (32, 39),
62 'yellow': (33, 39),
62 'yellow': (33, 39),
63 'blue': (34, 39),
63 'blue': (34, 39),
64 'magenta': (35, 39),
64 'magenta': (35, 39),
65 'cyan': (36, 39),
65 'cyan': (36, 39),
66 'white': (37, 39),
66 'white': (37, 39),
67 'bold': (1, 22),
67 'bold': (1, 22),
68 'italic': (3, 23),
68 'italic': (3, 23),
69 'underline': (4, 24),
69 'underline': (4, 24),
70 'inverse': (7, 27),
70 'inverse': (7, 27),
71 'black_background': (40, 49),
71 'black_background': (40, 49),
72 'red_background': (41, 49),
72 'red_background': (41, 49),
73 'green_background': (42, 49),
73 'green_background': (42, 49),
74 'yellow_background': (43, 49),
74 'yellow_background': (43, 49),
75 'blue_background': (44, 49),
75 'blue_background': (44, 49),
76 'purple_background': (45, 49),
76 'purple_background': (45, 49),
77 'cyan_background': (46, 49),
77 'cyan_background': (46, 49),
78 'white_background': (47, 49), }
78 'white_background': (47, 49), }
79
79
80 def render_effects(text, *effects):
80 def render_effects(text, *effects):
81 'Wrap text in commands to turn on each effect.'
81 'Wrap text in commands to turn on each effect.'
82 start = []
82 start = [ str(_effect_params['none'][0]) ]
83 stop = []
83 stop = []
84 for effect in effects:
84 for effect in effects:
85 start.append(str(_effect_params[effect][0]))
85 start.append(str(_effect_params[effect][0]))
86 stop.append(str(_effect_params[effect][1]))
86 stop.append(str(_effect_params[effect][1]))
87 stop.append(str(_effect_params['none'][1]))
87 start = '\033[' + ';'.join(start) + 'm'
88 start = '\033[' + ';'.join(start) + 'm'
88 stop = '\033[' + ';'.join(stop) + 'm'
89 stop = '\033[' + ';'.join(stop) + 'm'
89 return start + text + stop
90 return start + text + stop
90
91
91 def colorstatus(statusfunc, ui, repo, *pats, **opts):
92 def colorstatus(statusfunc, ui, repo, *pats, **opts):
92 '''run the status command with colored output'''
93 '''run the status command with colored output'''
93
94
94 delimiter = opts['print0'] and '\0' or '\n'
95 delimiter = opts['print0'] and '\0' or '\n'
95
96
96 # run status and capture it's output
97 # run status and capture it's output
97 ui.pushbuffer()
98 ui.pushbuffer()
98 retval = statusfunc(ui, repo, *pats, **opts)
99 retval = statusfunc(ui, repo, *pats, **opts)
99 # filter out empty strings
100 # filter out empty strings
100 lines = [ line for line in ui.popbuffer().split(delimiter) if line ]
101 lines = [ line for line in ui.popbuffer().split(delimiter) if line ]
101
102
102 if opts['no_status']:
103 if opts['no_status']:
103 # if --no-status, run the command again without that option to get
104 # if --no-status, run the command again without that option to get
104 # output with status abbreviations
105 # output with status abbreviations
105 opts['no_status'] = False
106 opts['no_status'] = False
106 ui.pushbuffer()
107 ui.pushbuffer()
107 statusfunc(ui, repo, *pats, **opts)
108 statusfunc(ui, repo, *pats, **opts)
108 # filter out empty strings
109 # filter out empty strings
109 lines_with_status = [ line for
110 lines_with_status = [ line for
110 line in ui.popbuffer().split(delimiter) if line ]
111 line in ui.popbuffer().split(delimiter) if line ]
111 else:
112 else:
112 lines_with_status = lines
113 lines_with_status = lines
113
114
114 # apply color to output and display it
115 # apply color to output and display it
115 for i in xrange(0, len(lines)):
116 for i in xrange(0, len(lines)):
116 status = _status_abbreviations[lines_with_status[i][0]]
117 status = _status_abbreviations[lines_with_status[i][0]]
117 effects = _status_effects[status]
118 effects = _status_effects[status]
118 if effects:
119 if effects:
119 lines[i] = render_effects(lines[i], *effects)
120 lines[i] = render_effects(lines[i], *effects)
120 sys.stdout.write(lines[i] + delimiter)
121 sys.stdout.write(lines[i] + delimiter)
121 return retval
122 return retval
122
123
123 _status_abbreviations = { 'M': 'modified',
124 _status_abbreviations = { 'M': 'modified',
124 'A': 'added',
125 'A': 'added',
125 'R': 'removed',
126 'R': 'removed',
126 '!': 'deleted',
127 '!': 'deleted',
127 '?': 'unknown',
128 '?': 'unknown',
128 'I': 'ignored',
129 'I': 'ignored',
129 'C': 'clean',
130 'C': 'clean',
130 ' ': 'copied', }
131 ' ': 'copied', }
131
132
132 _status_effects = { 'modified': ('blue', 'bold'),
133 _status_effects = { 'modified': ('blue', 'bold'),
133 'added': ('green', 'bold'),
134 'added': ('green', 'bold'),
134 'removed': ('red', 'bold'),
135 'removed': ('red', 'bold'),
135 'deleted': ('cyan', 'bold', 'underline'),
136 'deleted': ('cyan', 'bold', 'underline'),
136 'unknown': ('magenta', 'bold', 'underline'),
137 'unknown': ('magenta', 'bold', 'underline'),
137 'ignored': ('black', 'bold'),
138 'ignored': ('black', 'bold'),
138 'clean': ('none', ),
139 'clean': ('none', ),
139 'copied': ('none', ), }
140 'copied': ('none', ), }
140
141
141 def colorqseries(qseriesfunc, ui, repo, *dummy, **opts):
142 def colorqseries(qseriesfunc, ui, repo, *dummy, **opts):
142 '''run the qseries command with colored output'''
143 '''run the qseries command with colored output'''
143 ui.pushbuffer()
144 ui.pushbuffer()
144 retval = qseriesfunc(ui, repo, **opts)
145 retval = qseriesfunc(ui, repo, **opts)
145 patches = ui.popbuffer().splitlines()
146 patches = ui.popbuffer().splitlines()
146 for patch in patches:
147 for patch in patches:
147 patchname = patch
148 patchname = patch
148 if opts['summary']:
149 if opts['summary']:
149 patchname = patchname.split(': ')[0]
150 patchname = patchname.split(': ')[0]
150 if ui.verbose:
151 if ui.verbose:
151 patchname = patchname.split(' ', 2)[-1]
152 patchname = patchname.split(' ', 2)[-1]
152
153
153 if opts['missing']:
154 if opts['missing']:
154 effects = _patch_effects['missing']
155 effects = _patch_effects['missing']
155 # Determine if patch is applied.
156 # Determine if patch is applied.
156 elif [ applied for applied in repo.mq.applied
157 elif [ applied for applied in repo.mq.applied
157 if patchname == applied.name ]:
158 if patchname == applied.name ]:
158 effects = _patch_effects['applied']
159 effects = _patch_effects['applied']
159 else:
160 else:
160 effects = _patch_effects['unapplied']
161 effects = _patch_effects['unapplied']
161 sys.stdout.write(render_effects(patch, *effects) + '\n')
162 sys.stdout.write(render_effects(patch, *effects) + '\n')
162 return retval
163 return retval
163
164
164 _patch_effects = { 'applied': ('blue', 'bold', 'underline'),
165 _patch_effects = { 'applied': ('blue', 'bold', 'underline'),
165 'missing': ('red', 'bold'),
166 'missing': ('red', 'bold'),
166 'unapplied': ('black', 'bold'), }
167 'unapplied': ('black', 'bold'), }
167
168
168 def uisetup(ui):
169 def uisetup(ui):
169 '''Initialize the extension.'''
170 '''Initialize the extension.'''
170 nocoloropt = ('', 'no-color', None, _("don't colorize output"))
171 nocoloropt = ('', 'no-color', None, _("don't colorize output"))
171 _decoratecmd(ui, 'status', commands.table, colorstatus, nocoloropt)
172 _decoratecmd(ui, 'status', commands.table, colorstatus, nocoloropt)
172 _configcmdeffects(ui, 'status', _status_effects);
173 _configcmdeffects(ui, 'status', _status_effects);
173 if ui.config('extensions', 'hgext.mq') is not None or \
174 if ui.config('extensions', 'hgext.mq') is not None or \
174 ui.config('extensions', 'mq') is not None:
175 ui.config('extensions', 'mq') is not None:
175 from hgext import mq
176 from hgext import mq
176 _decoratecmd(ui, 'qseries', mq.cmdtable, colorqseries, nocoloropt)
177 _decoratecmd(ui, 'qseries', mq.cmdtable, colorqseries, nocoloropt)
177 _configcmdeffects(ui, 'qseries', _patch_effects);
178 _configcmdeffects(ui, 'qseries', _patch_effects);
178
179
179 def _decoratecmd(ui, cmd, table, delegate, *delegateoptions):
180 def _decoratecmd(ui, cmd, table, delegate, *delegateoptions):
180 '''Replace the function that implements cmd in table with a decorator.
181 '''Replace the function that implements cmd in table with a decorator.
181
182
182 The decorator that becomes the new implementation of cmd calls
183 The decorator that becomes the new implementation of cmd calls
183 delegate. The delegate's first argument is the replaced function,
184 delegate. The delegate's first argument is the replaced function,
184 followed by the normal Mercurial command arguments (ui, repo, ...). If
185 followed by the normal Mercurial command arguments (ui, repo, ...). If
185 the delegate adds command options, supply them as delegateoptions.
186 the delegate adds command options, supply them as delegateoptions.
186 '''
187 '''
187 cmdkey, cmdentry = _cmdtableitem(ui, cmd, table)
188 cmdkey, cmdentry = _cmdtableitem(ui, cmd, table)
188 decorator = lambda ui, repo, *args, **opts: \
189 decorator = lambda ui, repo, *args, **opts: \
189 _colordecorator(delegate, cmdentry[0],
190 _colordecorator(delegate, cmdentry[0],
190 ui, repo, *args, **opts)
191 ui, repo, *args, **opts)
191 # make sure 'hg help cmd' still works
192 # make sure 'hg help cmd' still works
192 decorator.__doc__ = cmdentry[0].__doc__
193 decorator.__doc__ = cmdentry[0].__doc__
193 decoratorentry = (decorator,) + cmdentry[1:]
194 decoratorentry = (decorator,) + cmdentry[1:]
194 for option in delegateoptions:
195 for option in delegateoptions:
195 decoratorentry[1].append(option)
196 decoratorentry[1].append(option)
196 table[cmdkey] = decoratorentry
197 table[cmdkey] = decoratorentry
197
198
198 def _cmdtableitem(ui, cmd, table):
199 def _cmdtableitem(ui, cmd, table):
199 '''Return key, value from table for cmd, or None if not found.'''
200 '''Return key, value from table for cmd, or None if not found.'''
200 aliases, entry = cmdutil.findcmd(ui, cmd, table)
201 aliases, entry = cmdutil.findcmd(ui, cmd, table)
201 for candidatekey, candidateentry in table.iteritems():
202 for candidatekey, candidateentry in table.iteritems():
202 if candidateentry is entry:
203 if candidateentry is entry:
203 return candidatekey, entry
204 return candidatekey, entry
204
205
205 def _colordecorator(colorfunc, nocolorfunc, ui, repo, *args, **opts):
206 def _colordecorator(colorfunc, nocolorfunc, ui, repo, *args, **opts):
206 '''Delegate to colorfunc or nocolorfunc, depending on conditions.
207 '''Delegate to colorfunc or nocolorfunc, depending on conditions.
207
208
208 Delegate to colorfunc unless --no-color option is set or output is not
209 Delegate to colorfunc unless --no-color option is set or output is not
209 to a tty.
210 to a tty.
210 '''
211 '''
211 if opts['no_color'] or not sys.stdout.isatty():
212 if opts['no_color'] or not sys.stdout.isatty():
212 return nocolorfunc(ui, repo, *args, **opts)
213 return nocolorfunc(ui, repo, *args, **opts)
213 return colorfunc(nocolorfunc, ui, repo, *args, **opts)
214 return colorfunc(nocolorfunc, ui, repo, *args, **opts)
214
215
215 def _configcmdeffects(ui, cmdname, effectsmap):
216 def _configcmdeffects(ui, cmdname, effectsmap):
216 '''Override default effects for cmdname with those from .hgrc file.
217 '''Override default effects for cmdname with those from .hgrc file.
217
218
218 Entries in the .hgrc file are in the [color] section, and look like
219 Entries in the .hgrc file are in the [color] section, and look like
219 'cmdname'.'status' (for instance, 'status.modified = blue bold inverse').
220 'cmdname'.'status' (for instance, 'status.modified = blue bold inverse').
220 '''
221 '''
221 for status in effectsmap:
222 for status in effectsmap:
222 effects = ui.config('color', cmdname + '.' + status)
223 effects = ui.config('color', cmdname + '.' + status)
223 if effects:
224 if effects:
224 effectsmap[status] = re.split('\W+', effects)
225 effectsmap[status] = re.split('\W+', effects)
General Comments 0
You need to be logged in to leave comments. Login now