##// END OF EJS Templates
color: add the ability to display configured style to 'debugcolor'...
Pierre-Yves David -
r30286:d44c407a default
parent child Browse files
Show More
@@ -1,700 +1,717 b''
1 # color.py color output for Mercurial commands
1 # color.py color output for Mercurial 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 software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''colorize output from some commands
8 '''colorize output from some commands
9
9
10 The color extension colorizes output from several Mercurial commands.
10 The color extension colorizes output from several Mercurial commands.
11 For example, the diff command shows additions in green and deletions
11 For example, the diff command shows additions in green and deletions
12 in red, while the status command shows modified files in magenta. Many
12 in red, while the status command shows modified files in magenta. Many
13 other commands have analogous colors. It is possible to customize
13 other commands have analogous colors. It is possible to customize
14 these colors.
14 these colors.
15
15
16 Effects
16 Effects
17 -------
17 -------
18
18
19 Other effects in addition to color, like bold and underlined text, are
19 Other effects in addition to color, like bold and underlined text, are
20 also available. By default, the terminfo database is used to find the
20 also available. By default, the terminfo database is used to find the
21 terminal codes used to change color and effect. If terminfo is not
21 terminal codes used to change color and effect. If terminfo is not
22 available, then effects are rendered with the ECMA-48 SGR control
22 available, then effects are rendered with the ECMA-48 SGR control
23 function (aka ANSI escape codes).
23 function (aka ANSI escape codes).
24
24
25 The available effects in terminfo mode are 'blink', 'bold', 'dim',
25 The available effects in terminfo mode are 'blink', 'bold', 'dim',
26 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
26 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
27 ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
27 ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
28 'underline'. How each is rendered depends on the terminal emulator.
28 'underline'. How each is rendered depends on the terminal emulator.
29 Some may not be available for a given terminal type, and will be
29 Some may not be available for a given terminal type, and will be
30 silently ignored.
30 silently ignored.
31
31
32 If the terminfo entry for your terminal is missing codes for an effect
32 If the terminfo entry for your terminal is missing codes for an effect
33 or has the wrong codes, you can add or override those codes in your
33 or has the wrong codes, you can add or override those codes in your
34 configuration::
34 configuration::
35
35
36 [color]
36 [color]
37 terminfo.dim = \E[2m
37 terminfo.dim = \E[2m
38
38
39 where '\E' is substituted with an escape character.
39 where '\E' is substituted with an escape character.
40
40
41 Labels
41 Labels
42 ------
42 ------
43
43
44 Text receives color effects depending on the labels that it has. Many
44 Text receives color effects depending on the labels that it has. Many
45 default Mercurial commands emit labelled text. You can also define
45 default Mercurial commands emit labelled text. You can also define
46 your own labels in templates using the label function, see :hg:`help
46 your own labels in templates using the label function, see :hg:`help
47 templates`. A single portion of text may have more than one label. In
47 templates`. A single portion of text may have more than one label. In
48 that case, effects given to the last label will override any other
48 that case, effects given to the last label will override any other
49 effects. This includes the special "none" effect, which nullifies
49 effects. This includes the special "none" effect, which nullifies
50 other effects.
50 other effects.
51
51
52 Labels are normally invisible. In order to see these labels and their
52 Labels are normally invisible. In order to see these labels and their
53 position in the text, use the global --color=debug option. The same
53 position in the text, use the global --color=debug option. The same
54 anchor text may be associated to multiple labels, e.g.
54 anchor text may be associated to multiple labels, e.g.
55
55
56 [log.changeset changeset.secret|changeset: 22611:6f0a53c8f587]
56 [log.changeset changeset.secret|changeset: 22611:6f0a53c8f587]
57
57
58 The following are the default effects for some default labels. Default
58 The following are the default effects for some default labels. Default
59 effects may be overridden from your configuration file::
59 effects may be overridden from your configuration file::
60
60
61 [color]
61 [color]
62 status.modified = blue bold underline red_background
62 status.modified = blue bold underline red_background
63 status.added = green bold
63 status.added = green bold
64 status.removed = red bold blue_background
64 status.removed = red bold blue_background
65 status.deleted = cyan bold underline
65 status.deleted = cyan bold underline
66 status.unknown = magenta bold underline
66 status.unknown = magenta bold underline
67 status.ignored = black bold
67 status.ignored = black bold
68
68
69 # 'none' turns off all effects
69 # 'none' turns off all effects
70 status.clean = none
70 status.clean = none
71 status.copied = none
71 status.copied = none
72
72
73 qseries.applied = blue bold underline
73 qseries.applied = blue bold underline
74 qseries.unapplied = black bold
74 qseries.unapplied = black bold
75 qseries.missing = red bold
75 qseries.missing = red bold
76
76
77 diff.diffline = bold
77 diff.diffline = bold
78 diff.extended = cyan bold
78 diff.extended = cyan bold
79 diff.file_a = red bold
79 diff.file_a = red bold
80 diff.file_b = green bold
80 diff.file_b = green bold
81 diff.hunk = magenta
81 diff.hunk = magenta
82 diff.deleted = red
82 diff.deleted = red
83 diff.inserted = green
83 diff.inserted = green
84 diff.changed = white
84 diff.changed = white
85 diff.tab =
85 diff.tab =
86 diff.trailingwhitespace = bold red_background
86 diff.trailingwhitespace = bold red_background
87
87
88 # Blank so it inherits the style of the surrounding label
88 # Blank so it inherits the style of the surrounding label
89 changeset.public =
89 changeset.public =
90 changeset.draft =
90 changeset.draft =
91 changeset.secret =
91 changeset.secret =
92
92
93 resolve.unresolved = red bold
93 resolve.unresolved = red bold
94 resolve.resolved = green bold
94 resolve.resolved = green bold
95
95
96 bookmarks.active = green
96 bookmarks.active = green
97
97
98 branches.active = none
98 branches.active = none
99 branches.closed = black bold
99 branches.closed = black bold
100 branches.current = green
100 branches.current = green
101 branches.inactive = none
101 branches.inactive = none
102
102
103 tags.normal = green
103 tags.normal = green
104 tags.local = black bold
104 tags.local = black bold
105
105
106 rebase.rebased = blue
106 rebase.rebased = blue
107 rebase.remaining = red bold
107 rebase.remaining = red bold
108
108
109 shelve.age = cyan
109 shelve.age = cyan
110 shelve.newest = green bold
110 shelve.newest = green bold
111 shelve.name = blue bold
111 shelve.name = blue bold
112
112
113 histedit.remaining = red bold
113 histedit.remaining = red bold
114
114
115 Custom colors
115 Custom colors
116 -------------
116 -------------
117
117
118 Because there are only eight standard colors, this module allows you
118 Because there are only eight standard colors, this module allows you
119 to define color names for other color slots which might be available
119 to define color names for other color slots which might be available
120 for your terminal type, assuming terminfo mode. For instance::
120 for your terminal type, assuming terminfo mode. For instance::
121
121
122 color.brightblue = 12
122 color.brightblue = 12
123 color.pink = 207
123 color.pink = 207
124 color.orange = 202
124 color.orange = 202
125
125
126 to set 'brightblue' to color slot 12 (useful for 16 color terminals
126 to set 'brightblue' to color slot 12 (useful for 16 color terminals
127 that have brighter colors defined in the upper eight) and, 'pink' and
127 that have brighter colors defined in the upper eight) and, 'pink' and
128 'orange' to colors in 256-color xterm's default color cube. These
128 'orange' to colors in 256-color xterm's default color cube. These
129 defined colors may then be used as any of the pre-defined eight,
129 defined colors may then be used as any of the pre-defined eight,
130 including appending '_background' to set the background to that color.
130 including appending '_background' to set the background to that color.
131
131
132 Modes
132 Modes
133 -----
133 -----
134
134
135 By default, the color extension will use ANSI mode (or win32 mode on
135 By default, the color extension will use ANSI mode (or win32 mode on
136 Windows) if it detects a terminal. To override auto mode (to enable
136 Windows) if it detects a terminal. To override auto mode (to enable
137 terminfo mode, for example), set the following configuration option::
137 terminfo mode, for example), set the following configuration option::
138
138
139 [color]
139 [color]
140 mode = terminfo
140 mode = terminfo
141
141
142 Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will
142 Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will
143 disable color.
143 disable color.
144
144
145 Note that on some systems, terminfo mode may cause problems when using
145 Note that on some systems, terminfo mode may cause problems when using
146 color with the pager extension and less -R. less with the -R option
146 color with the pager extension and less -R. less with the -R option
147 will only display ECMA-48 color codes, and terminfo mode may sometimes
147 will only display ECMA-48 color codes, and terminfo mode may sometimes
148 emit codes that less doesn't understand. You can work around this by
148 emit codes that less doesn't understand. You can work around this by
149 either using ansi mode (or auto mode), or by using less -r (which will
149 either using ansi mode (or auto mode), or by using less -r (which will
150 pass through all terminal control codes, not just color control
150 pass through all terminal control codes, not just color control
151 codes).
151 codes).
152
152
153 On some systems (such as MSYS in Windows), the terminal may support
153 On some systems (such as MSYS in Windows), the terminal may support
154 a different color mode than the pager (activated via the "pager"
154 a different color mode than the pager (activated via the "pager"
155 extension). It is possible to define separate modes depending on whether
155 extension). It is possible to define separate modes depending on whether
156 the pager is active::
156 the pager is active::
157
157
158 [color]
158 [color]
159 mode = auto
159 mode = auto
160 pagermode = ansi
160 pagermode = ansi
161
161
162 If ``pagermode`` is not defined, the ``mode`` will be used.
162 If ``pagermode`` is not defined, the ``mode`` will be used.
163 '''
163 '''
164
164
165 from __future__ import absolute_import
165 from __future__ import absolute_import
166
166
167 import os
167 import os
168
168
169 from mercurial.i18n import _
169 from mercurial.i18n import _
170 from mercurial import (
170 from mercurial import (
171 cmdutil,
171 cmdutil,
172 commands,
172 commands,
173 dispatch,
173 dispatch,
174 extensions,
174 extensions,
175 subrepo,
175 subrepo,
176 ui as uimod,
176 ui as uimod,
177 util,
177 util,
178 )
178 )
179
179
180 cmdtable = {}
180 cmdtable = {}
181 command = cmdutil.command(cmdtable)
181 command = cmdutil.command(cmdtable)
182 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
182 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
183 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
183 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
184 # be specifying the version(s) of Mercurial they are tested with, or
184 # be specifying the version(s) of Mercurial they are tested with, or
185 # leave the attribute unspecified.
185 # leave the attribute unspecified.
186 testedwith = 'ships-with-hg-core'
186 testedwith = 'ships-with-hg-core'
187
187
188 # start and stop parameters for effects
188 # start and stop parameters for effects
189 _effects = {'none': 0, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33,
189 _effects = {'none': 0, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33,
190 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'bold': 1,
190 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'bold': 1,
191 'italic': 3, 'underline': 4, 'inverse': 7, 'dim': 2,
191 'italic': 3, 'underline': 4, 'inverse': 7, 'dim': 2,
192 'black_background': 40, 'red_background': 41,
192 'black_background': 40, 'red_background': 41,
193 'green_background': 42, 'yellow_background': 43,
193 'green_background': 42, 'yellow_background': 43,
194 'blue_background': 44, 'purple_background': 45,
194 'blue_background': 44, 'purple_background': 45,
195 'cyan_background': 46, 'white_background': 47}
195 'cyan_background': 46, 'white_background': 47}
196
196
197 def _terminfosetup(ui, mode):
197 def _terminfosetup(ui, mode):
198 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
198 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
199
199
200 global _terminfo_params
200 global _terminfo_params
201 # If we failed to load curses, we go ahead and return.
201 # If we failed to load curses, we go ahead and return.
202 if not _terminfo_params:
202 if not _terminfo_params:
203 return
203 return
204 # Otherwise, see what the config file says.
204 # Otherwise, see what the config file says.
205 if mode not in ('auto', 'terminfo'):
205 if mode not in ('auto', 'terminfo'):
206 return
206 return
207
207
208 _terminfo_params.update((key[6:], (False, int(val), ''))
208 _terminfo_params.update((key[6:], (False, int(val), ''))
209 for key, val in ui.configitems('color')
209 for key, val in ui.configitems('color')
210 if key.startswith('color.'))
210 if key.startswith('color.'))
211 _terminfo_params.update((key[9:], (True, '', val.replace('\\E', '\x1b')))
211 _terminfo_params.update((key[9:], (True, '', val.replace('\\E', '\x1b')))
212 for key, val in ui.configitems('color')
212 for key, val in ui.configitems('color')
213 if key.startswith('terminfo.'))
213 if key.startswith('terminfo.'))
214
214
215 try:
215 try:
216 curses.setupterm()
216 curses.setupterm()
217 except curses.error as e:
217 except curses.error as e:
218 _terminfo_params = {}
218 _terminfo_params = {}
219 return
219 return
220
220
221 for key, (b, e, c) in _terminfo_params.items():
221 for key, (b, e, c) in _terminfo_params.items():
222 if not b:
222 if not b:
223 continue
223 continue
224 if not c and not curses.tigetstr(e):
224 if not c and not curses.tigetstr(e):
225 # Most terminals don't support dim, invis, etc, so don't be
225 # Most terminals don't support dim, invis, etc, so don't be
226 # noisy and use ui.debug().
226 # noisy and use ui.debug().
227 ui.debug("no terminfo entry for %s\n" % e)
227 ui.debug("no terminfo entry for %s\n" % e)
228 del _terminfo_params[key]
228 del _terminfo_params[key]
229 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
229 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
230 # Only warn about missing terminfo entries if we explicitly asked for
230 # Only warn about missing terminfo entries if we explicitly asked for
231 # terminfo mode.
231 # terminfo mode.
232 if mode == "terminfo":
232 if mode == "terminfo":
233 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
233 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
234 "ECMA-48 color\n"))
234 "ECMA-48 color\n"))
235 _terminfo_params = {}
235 _terminfo_params = {}
236
236
237 def _modesetup(ui, coloropt):
237 def _modesetup(ui, coloropt):
238 global _terminfo_params
238 global _terminfo_params
239
239
240 if coloropt == 'debug':
240 if coloropt == 'debug':
241 return 'debug'
241 return 'debug'
242
242
243 auto = (coloropt == 'auto')
243 auto = (coloropt == 'auto')
244 always = not auto and util.parsebool(coloropt)
244 always = not auto and util.parsebool(coloropt)
245 if not always and not auto:
245 if not always and not auto:
246 return None
246 return None
247
247
248 formatted = always or (os.environ.get('TERM') != 'dumb' and ui.formatted())
248 formatted = always or (os.environ.get('TERM') != 'dumb' and ui.formatted())
249
249
250 mode = ui.config('color', 'mode', 'auto')
250 mode = ui.config('color', 'mode', 'auto')
251
251
252 # If pager is active, color.pagermode overrides color.mode.
252 # If pager is active, color.pagermode overrides color.mode.
253 if getattr(ui, 'pageractive', False):
253 if getattr(ui, 'pageractive', False):
254 mode = ui.config('color', 'pagermode', mode)
254 mode = ui.config('color', 'pagermode', mode)
255
255
256 realmode = mode
256 realmode = mode
257 if mode == 'auto':
257 if mode == 'auto':
258 if os.name == 'nt':
258 if os.name == 'nt':
259 term = os.environ.get('TERM')
259 term = os.environ.get('TERM')
260 # TERM won't be defined in a vanilla cmd.exe environment.
260 # TERM won't be defined in a vanilla cmd.exe environment.
261
261
262 # UNIX-like environments on Windows such as Cygwin and MSYS will
262 # UNIX-like environments on Windows such as Cygwin and MSYS will
263 # set TERM. They appear to make a best effort attempt at setting it
263 # set TERM. They appear to make a best effort attempt at setting it
264 # to something appropriate. However, not all environments with TERM
264 # to something appropriate. However, not all environments with TERM
265 # defined support ANSI. Since "ansi" could result in terminal
265 # defined support ANSI. Since "ansi" could result in terminal
266 # gibberish, we error on the side of selecting "win32". However, if
266 # gibberish, we error on the side of selecting "win32". However, if
267 # w32effects is not defined, we almost certainly don't support
267 # w32effects is not defined, we almost certainly don't support
268 # "win32", so don't even try.
268 # "win32", so don't even try.
269 if (term and 'xterm' in term) or not w32effects:
269 if (term and 'xterm' in term) or not w32effects:
270 realmode = 'ansi'
270 realmode = 'ansi'
271 else:
271 else:
272 realmode = 'win32'
272 realmode = 'win32'
273 else:
273 else:
274 realmode = 'ansi'
274 realmode = 'ansi'
275
275
276 def modewarn():
276 def modewarn():
277 # only warn if color.mode was explicitly set and we're in
277 # only warn if color.mode was explicitly set and we're in
278 # an interactive terminal
278 # an interactive terminal
279 if mode == realmode and ui.interactive():
279 if mode == realmode and ui.interactive():
280 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
280 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
281
281
282 if realmode == 'win32':
282 if realmode == 'win32':
283 _terminfo_params = {}
283 _terminfo_params = {}
284 if not w32effects:
284 if not w32effects:
285 modewarn()
285 modewarn()
286 return None
286 return None
287 _effects.update(w32effects)
287 _effects.update(w32effects)
288 elif realmode == 'ansi':
288 elif realmode == 'ansi':
289 _terminfo_params = {}
289 _terminfo_params = {}
290 elif realmode == 'terminfo':
290 elif realmode == 'terminfo':
291 _terminfosetup(ui, mode)
291 _terminfosetup(ui, mode)
292 if not _terminfo_params:
292 if not _terminfo_params:
293 ## FIXME Shouldn't we return None in this case too?
293 ## FIXME Shouldn't we return None in this case too?
294 modewarn()
294 modewarn()
295 realmode = 'ansi'
295 realmode = 'ansi'
296 else:
296 else:
297 return None
297 return None
298
298
299 if always or (auto and formatted):
299 if always or (auto and formatted):
300 return realmode
300 return realmode
301 return None
301 return None
302
302
303 try:
303 try:
304 import curses
304 import curses
305 # Mapping from effect name to terminfo attribute name (or raw code) or
305 # Mapping from effect name to terminfo attribute name (or raw code) or
306 # color number. This will also force-load the curses module.
306 # color number. This will also force-load the curses module.
307 _terminfo_params = {'none': (True, 'sgr0', ''),
307 _terminfo_params = {'none': (True, 'sgr0', ''),
308 'standout': (True, 'smso', ''),
308 'standout': (True, 'smso', ''),
309 'underline': (True, 'smul', ''),
309 'underline': (True, 'smul', ''),
310 'reverse': (True, 'rev', ''),
310 'reverse': (True, 'rev', ''),
311 'inverse': (True, 'rev', ''),
311 'inverse': (True, 'rev', ''),
312 'blink': (True, 'blink', ''),
312 'blink': (True, 'blink', ''),
313 'dim': (True, 'dim', ''),
313 'dim': (True, 'dim', ''),
314 'bold': (True, 'bold', ''),
314 'bold': (True, 'bold', ''),
315 'invisible': (True, 'invis', ''),
315 'invisible': (True, 'invis', ''),
316 'italic': (True, 'sitm', ''),
316 'italic': (True, 'sitm', ''),
317 'black': (False, curses.COLOR_BLACK, ''),
317 'black': (False, curses.COLOR_BLACK, ''),
318 'red': (False, curses.COLOR_RED, ''),
318 'red': (False, curses.COLOR_RED, ''),
319 'green': (False, curses.COLOR_GREEN, ''),
319 'green': (False, curses.COLOR_GREEN, ''),
320 'yellow': (False, curses.COLOR_YELLOW, ''),
320 'yellow': (False, curses.COLOR_YELLOW, ''),
321 'blue': (False, curses.COLOR_BLUE, ''),
321 'blue': (False, curses.COLOR_BLUE, ''),
322 'magenta': (False, curses.COLOR_MAGENTA, ''),
322 'magenta': (False, curses.COLOR_MAGENTA, ''),
323 'cyan': (False, curses.COLOR_CYAN, ''),
323 'cyan': (False, curses.COLOR_CYAN, ''),
324 'white': (False, curses.COLOR_WHITE, '')}
324 'white': (False, curses.COLOR_WHITE, '')}
325 except ImportError:
325 except ImportError:
326 _terminfo_params = {}
326 _terminfo_params = {}
327
327
328 _styles = {'grep.match': 'red bold',
328 _styles = {'grep.match': 'red bold',
329 'grep.linenumber': 'green',
329 'grep.linenumber': 'green',
330 'grep.rev': 'green',
330 'grep.rev': 'green',
331 'grep.change': 'green',
331 'grep.change': 'green',
332 'grep.sep': 'cyan',
332 'grep.sep': 'cyan',
333 'grep.filename': 'magenta',
333 'grep.filename': 'magenta',
334 'grep.user': 'magenta',
334 'grep.user': 'magenta',
335 'grep.date': 'magenta',
335 'grep.date': 'magenta',
336 'bookmarks.active': 'green',
336 'bookmarks.active': 'green',
337 'branches.active': 'none',
337 'branches.active': 'none',
338 'branches.closed': 'black bold',
338 'branches.closed': 'black bold',
339 'branches.current': 'green',
339 'branches.current': 'green',
340 'branches.inactive': 'none',
340 'branches.inactive': 'none',
341 'diff.changed': 'white',
341 'diff.changed': 'white',
342 'diff.deleted': 'red',
342 'diff.deleted': 'red',
343 'diff.diffline': 'bold',
343 'diff.diffline': 'bold',
344 'diff.extended': 'cyan bold',
344 'diff.extended': 'cyan bold',
345 'diff.file_a': 'red bold',
345 'diff.file_a': 'red bold',
346 'diff.file_b': 'green bold',
346 'diff.file_b': 'green bold',
347 'diff.hunk': 'magenta',
347 'diff.hunk': 'magenta',
348 'diff.inserted': 'green',
348 'diff.inserted': 'green',
349 'diff.tab': '',
349 'diff.tab': '',
350 'diff.trailingwhitespace': 'bold red_background',
350 'diff.trailingwhitespace': 'bold red_background',
351 'changeset.public' : '',
351 'changeset.public' : '',
352 'changeset.draft' : '',
352 'changeset.draft' : '',
353 'changeset.secret' : '',
353 'changeset.secret' : '',
354 'diffstat.deleted': 'red',
354 'diffstat.deleted': 'red',
355 'diffstat.inserted': 'green',
355 'diffstat.inserted': 'green',
356 'histedit.remaining': 'red bold',
356 'histedit.remaining': 'red bold',
357 'ui.prompt': 'yellow',
357 'ui.prompt': 'yellow',
358 'log.changeset': 'yellow',
358 'log.changeset': 'yellow',
359 'patchbomb.finalsummary': '',
359 'patchbomb.finalsummary': '',
360 'patchbomb.from': 'magenta',
360 'patchbomb.from': 'magenta',
361 'patchbomb.to': 'cyan',
361 'patchbomb.to': 'cyan',
362 'patchbomb.subject': 'green',
362 'patchbomb.subject': 'green',
363 'patchbomb.diffstats': '',
363 'patchbomb.diffstats': '',
364 'rebase.rebased': 'blue',
364 'rebase.rebased': 'blue',
365 'rebase.remaining': 'red bold',
365 'rebase.remaining': 'red bold',
366 'resolve.resolved': 'green bold',
366 'resolve.resolved': 'green bold',
367 'resolve.unresolved': 'red bold',
367 'resolve.unresolved': 'red bold',
368 'shelve.age': 'cyan',
368 'shelve.age': 'cyan',
369 'shelve.newest': 'green bold',
369 'shelve.newest': 'green bold',
370 'shelve.name': 'blue bold',
370 'shelve.name': 'blue bold',
371 'status.added': 'green bold',
371 'status.added': 'green bold',
372 'status.clean': 'none',
372 'status.clean': 'none',
373 'status.copied': 'none',
373 'status.copied': 'none',
374 'status.deleted': 'cyan bold underline',
374 'status.deleted': 'cyan bold underline',
375 'status.ignored': 'black bold',
375 'status.ignored': 'black bold',
376 'status.modified': 'blue bold',
376 'status.modified': 'blue bold',
377 'status.removed': 'red bold',
377 'status.removed': 'red bold',
378 'status.unknown': 'magenta bold underline',
378 'status.unknown': 'magenta bold underline',
379 'tags.normal': 'green',
379 'tags.normal': 'green',
380 'tags.local': 'black bold'}
380 'tags.local': 'black bold'}
381
381
382
382
383 def _effect_str(effect):
383 def _effect_str(effect):
384 '''Helper function for render_effects().'''
384 '''Helper function for render_effects().'''
385
385
386 bg = False
386 bg = False
387 if effect.endswith('_background'):
387 if effect.endswith('_background'):
388 bg = True
388 bg = True
389 effect = effect[:-11]
389 effect = effect[:-11]
390 try:
390 try:
391 attr, val, termcode = _terminfo_params[effect]
391 attr, val, termcode = _terminfo_params[effect]
392 except KeyError:
392 except KeyError:
393 return ''
393 return ''
394 if attr:
394 if attr:
395 if termcode:
395 if termcode:
396 return termcode
396 return termcode
397 else:
397 else:
398 return curses.tigetstr(val)
398 return curses.tigetstr(val)
399 elif bg:
399 elif bg:
400 return curses.tparm(curses.tigetstr('setab'), val)
400 return curses.tparm(curses.tigetstr('setab'), val)
401 else:
401 else:
402 return curses.tparm(curses.tigetstr('setaf'), val)
402 return curses.tparm(curses.tigetstr('setaf'), val)
403
403
404 def render_effects(text, effects):
404 def render_effects(text, effects):
405 'Wrap text in commands to turn on each effect.'
405 'Wrap text in commands to turn on each effect.'
406 if not text:
406 if not text:
407 return text
407 return text
408 if not _terminfo_params:
408 if not _terminfo_params:
409 start = [str(_effects[e]) for e in ['none'] + effects.split()]
409 start = [str(_effects[e]) for e in ['none'] + effects.split()]
410 start = '\033[' + ';'.join(start) + 'm'
410 start = '\033[' + ';'.join(start) + 'm'
411 stop = '\033[' + str(_effects['none']) + 'm'
411 stop = '\033[' + str(_effects['none']) + 'm'
412 else:
412 else:
413 start = ''.join(_effect_str(effect)
413 start = ''.join(_effect_str(effect)
414 for effect in ['none'] + effects.split())
414 for effect in ['none'] + effects.split())
415 stop = _effect_str('none')
415 stop = _effect_str('none')
416 return ''.join([start, text, stop])
416 return ''.join([start, text, stop])
417
417
418 def extstyles():
418 def extstyles():
419 for name, ext in extensions.extensions():
419 for name, ext in extensions.extensions():
420 _styles.update(getattr(ext, 'colortable', {}))
420 _styles.update(getattr(ext, 'colortable', {}))
421
421
422 def valideffect(effect):
422 def valideffect(effect):
423 'Determine if the effect is valid or not.'
423 'Determine if the effect is valid or not.'
424 good = False
424 good = False
425 if not _terminfo_params and effect in _effects:
425 if not _terminfo_params and effect in _effects:
426 good = True
426 good = True
427 elif effect in _terminfo_params or effect[:-11] in _terminfo_params:
427 elif effect in _terminfo_params or effect[:-11] in _terminfo_params:
428 good = True
428 good = True
429 return good
429 return good
430
430
431 def configstyles(ui):
431 def configstyles(ui):
432 for status, cfgeffects in ui.configitems('color'):
432 for status, cfgeffects in ui.configitems('color'):
433 if '.' not in status or status.startswith(('color.', 'terminfo.')):
433 if '.' not in status or status.startswith(('color.', 'terminfo.')):
434 continue
434 continue
435 cfgeffects = ui.configlist('color', status)
435 cfgeffects = ui.configlist('color', status)
436 if cfgeffects:
436 if cfgeffects:
437 good = []
437 good = []
438 for e in cfgeffects:
438 for e in cfgeffects:
439 if valideffect(e):
439 if valideffect(e):
440 good.append(e)
440 good.append(e)
441 else:
441 else:
442 ui.warn(_("ignoring unknown color/effect %r "
442 ui.warn(_("ignoring unknown color/effect %r "
443 "(configured in color.%s)\n")
443 "(configured in color.%s)\n")
444 % (e, status))
444 % (e, status))
445 _styles[status] = ' '.join(good)
445 _styles[status] = ' '.join(good)
446
446
447 class colorui(uimod.ui):
447 class colorui(uimod.ui):
448 _colormode = 'ansi'
448 _colormode = 'ansi'
449 def write(self, *args, **opts):
449 def write(self, *args, **opts):
450 if self._colormode is None:
450 if self._colormode is None:
451 return super(colorui, self).write(*args, **opts)
451 return super(colorui, self).write(*args, **opts)
452
452
453 label = opts.get('label', '')
453 label = opts.get('label', '')
454 if self._buffers and not opts.get('prompt', False):
454 if self._buffers and not opts.get('prompt', False):
455 if self._bufferapplylabels:
455 if self._bufferapplylabels:
456 self._buffers[-1].extend(self.label(a, label) for a in args)
456 self._buffers[-1].extend(self.label(a, label) for a in args)
457 else:
457 else:
458 self._buffers[-1].extend(args)
458 self._buffers[-1].extend(args)
459 elif self._colormode == 'win32':
459 elif self._colormode == 'win32':
460 for a in args:
460 for a in args:
461 win32print(a, super(colorui, self).write, **opts)
461 win32print(a, super(colorui, self).write, **opts)
462 else:
462 else:
463 return super(colorui, self).write(
463 return super(colorui, self).write(
464 *[self.label(a, label) for a in args], **opts)
464 *[self.label(a, label) for a in args], **opts)
465
465
466 def write_err(self, *args, **opts):
466 def write_err(self, *args, **opts):
467 if self._colormode is None:
467 if self._colormode is None:
468 return super(colorui, self).write_err(*args, **opts)
468 return super(colorui, self).write_err(*args, **opts)
469
469
470 label = opts.get('label', '')
470 label = opts.get('label', '')
471 if self._bufferstates and self._bufferstates[-1][0]:
471 if self._bufferstates and self._bufferstates[-1][0]:
472 return self.write(*args, **opts)
472 return self.write(*args, **opts)
473 if self._colormode == 'win32':
473 if self._colormode == 'win32':
474 for a in args:
474 for a in args:
475 win32print(a, super(colorui, self).write_err, **opts)
475 win32print(a, super(colorui, self).write_err, **opts)
476 else:
476 else:
477 return super(colorui, self).write_err(
477 return super(colorui, self).write_err(
478 *[self.label(a, label) for a in args], **opts)
478 *[self.label(a, label) for a in args], **opts)
479
479
480 def showlabel(self, msg, label):
480 def showlabel(self, msg, label):
481 if label and msg:
481 if label and msg:
482 if msg[-1] == '\n':
482 if msg[-1] == '\n':
483 return "[%s|%s]\n" % (label, msg[:-1])
483 return "[%s|%s]\n" % (label, msg[:-1])
484 else:
484 else:
485 return "[%s|%s]" % (label, msg)
485 return "[%s|%s]" % (label, msg)
486 else:
486 else:
487 return msg
487 return msg
488
488
489 def label(self, msg, label):
489 def label(self, msg, label):
490 if self._colormode is None:
490 if self._colormode is None:
491 return super(colorui, self).label(msg, label)
491 return super(colorui, self).label(msg, label)
492
492
493 if self._colormode == 'debug':
493 if self._colormode == 'debug':
494 return self.showlabel(msg, label)
494 return self.showlabel(msg, label)
495
495
496 effects = []
496 effects = []
497 for l in label.split():
497 for l in label.split():
498 s = _styles.get(l, '')
498 s = _styles.get(l, '')
499 if s:
499 if s:
500 effects.append(s)
500 effects.append(s)
501 elif valideffect(l):
501 elif valideffect(l):
502 effects.append(l)
502 effects.append(l)
503 effects = ' '.join(effects)
503 effects = ' '.join(effects)
504 if effects:
504 if effects:
505 return '\n'.join([render_effects(s, effects)
505 return '\n'.join([render_effects(s, effects)
506 for s in msg.split('\n')])
506 for s in msg.split('\n')])
507 return msg
507 return msg
508
508
509 def uisetup(ui):
509 def uisetup(ui):
510 if ui.plain():
510 if ui.plain():
511 return
511 return
512 if not isinstance(ui, colorui):
512 if not isinstance(ui, colorui):
513 colorui.__bases__ = (ui.__class__,)
513 colorui.__bases__ = (ui.__class__,)
514 ui.__class__ = colorui
514 ui.__class__ = colorui
515 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
515 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
516 mode = _modesetup(ui_, opts['color'])
516 mode = _modesetup(ui_, opts['color'])
517 colorui._colormode = mode
517 colorui._colormode = mode
518 if mode and mode != 'debug':
518 if mode and mode != 'debug':
519 extstyles()
519 extstyles()
520 configstyles(ui_)
520 configstyles(ui_)
521 return orig(ui_, opts, cmd, cmdfunc)
521 return orig(ui_, opts, cmd, cmdfunc)
522 def colorgit(orig, gitsub, commands, env=None, stream=False, cwd=None):
522 def colorgit(orig, gitsub, commands, env=None, stream=False, cwd=None):
523 if gitsub.ui._colormode and len(commands) and commands[0] == "diff":
523 if gitsub.ui._colormode and len(commands) and commands[0] == "diff":
524 # insert the argument in the front,
524 # insert the argument in the front,
525 # the end of git diff arguments is used for paths
525 # the end of git diff arguments is used for paths
526 commands.insert(1, '--color')
526 commands.insert(1, '--color')
527 return orig(gitsub, commands, env, stream, cwd)
527 return orig(gitsub, commands, env, stream, cwd)
528 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
528 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
529 extensions.wrapfunction(subrepo.gitsubrepo, '_gitnodir', colorgit)
529 extensions.wrapfunction(subrepo.gitsubrepo, '_gitnodir', colorgit)
530
530
531 def extsetup(ui):
531 def extsetup(ui):
532 commands.globalopts.append(
532 commands.globalopts.append(
533 ('', 'color', 'auto',
533 ('', 'color', 'auto',
534 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
534 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
535 # and should not be translated
535 # and should not be translated
536 _("when to colorize (boolean, always, auto, never, or debug)"),
536 _("when to colorize (boolean, always, auto, never, or debug)"),
537 _('TYPE')))
537 _('TYPE')))
538
538
539 @command('debugcolor', [], 'hg debugcolor')
539 @command('debugcolor',
540 [('', 'style', None, _('show all configured styles'))],
541 'hg debugcolor')
540 def debugcolor(ui, repo, **opts):
542 def debugcolor(ui, repo, **opts):
541 """show available colors and effects"""
543 """show available color, effects or style"""
542 ui.write(('color mode: %s\n') % ui._colormode)
544 ui.write(('color mode: %s\n') % ui._colormode)
543 return _debugdisplaycolor(ui)
545 if opts.get('style'):
546 return _debugdisplaystyle(ui)
547 else:
548 return _debugdisplaycolor(ui)
544
549
545 def _debugdisplaycolor(ui):
550 def _debugdisplaycolor(ui):
546 global _styles
551 global _styles
547 oldstyle = _styles
552 oldstyle = _styles
548 try:
553 try:
549 _styles = {}
554 _styles = {}
550 for effect in _effects.keys():
555 for effect in _effects.keys():
551 _styles[effect] = effect
556 _styles[effect] = effect
552 if _terminfo_params:
557 if _terminfo_params:
553 for k, v in ui.configitems('color'):
558 for k, v in ui.configitems('color'):
554 if k.startswith('color.'):
559 if k.startswith('color.'):
555 _styles[k] = k[6:]
560 _styles[k] = k[6:]
556 elif k.startswith('terminfo.'):
561 elif k.startswith('terminfo.'):
557 _styles[k] = k[9:]
562 _styles[k] = k[9:]
558 ui.write(_('available colors:\n'))
563 ui.write(_('available colors:\n'))
559 # sort label with a '_' after the other to group '_background' entry.
564 # sort label with a '_' after the other to group '_background' entry.
560 items = sorted(_styles.items(),
565 items = sorted(_styles.items(),
561 key=lambda i: ('_' in i[0], i[0], i[1]))
566 key=lambda i: ('_' in i[0], i[0], i[1]))
562 for colorname, label in items:
567 for colorname, label in items:
563 ui.write(('%s\n') % colorname, label=label)
568 ui.write(('%s\n') % colorname, label=label)
564 finally:
569 finally:
565 _styles = oldstyle
570 _styles = oldstyle
566
571
572 def _debugdisplaystyle(ui):
573 ui.write(_('available style:\n'))
574 width = max(len(s) for s in _styles)
575 for label, effects in sorted(_styles.items()):
576 ui.write('%s' % label, label=label)
577 if effects:
578 # 50
579 ui.write(': ')
580 ui.write(' ' * (max(0, width - len(label))))
581 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
582 ui.write('\n')
583
567 if os.name != 'nt':
584 if os.name != 'nt':
568 w32effects = None
585 w32effects = None
569 else:
586 else:
570 import ctypes
587 import ctypes
571 import re
588 import re
572
589
573 _kernel32 = ctypes.windll.kernel32
590 _kernel32 = ctypes.windll.kernel32
574
591
575 _WORD = ctypes.c_ushort
592 _WORD = ctypes.c_ushort
576
593
577 _INVALID_HANDLE_VALUE = -1
594 _INVALID_HANDLE_VALUE = -1
578
595
579 class _COORD(ctypes.Structure):
596 class _COORD(ctypes.Structure):
580 _fields_ = [('X', ctypes.c_short),
597 _fields_ = [('X', ctypes.c_short),
581 ('Y', ctypes.c_short)]
598 ('Y', ctypes.c_short)]
582
599
583 class _SMALL_RECT(ctypes.Structure):
600 class _SMALL_RECT(ctypes.Structure):
584 _fields_ = [('Left', ctypes.c_short),
601 _fields_ = [('Left', ctypes.c_short),
585 ('Top', ctypes.c_short),
602 ('Top', ctypes.c_short),
586 ('Right', ctypes.c_short),
603 ('Right', ctypes.c_short),
587 ('Bottom', ctypes.c_short)]
604 ('Bottom', ctypes.c_short)]
588
605
589 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
606 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
590 _fields_ = [('dwSize', _COORD),
607 _fields_ = [('dwSize', _COORD),
591 ('dwCursorPosition', _COORD),
608 ('dwCursorPosition', _COORD),
592 ('wAttributes', _WORD),
609 ('wAttributes', _WORD),
593 ('srWindow', _SMALL_RECT),
610 ('srWindow', _SMALL_RECT),
594 ('dwMaximumWindowSize', _COORD)]
611 ('dwMaximumWindowSize', _COORD)]
595
612
596 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
613 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
597 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12
614 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12
598
615
599 _FOREGROUND_BLUE = 0x0001
616 _FOREGROUND_BLUE = 0x0001
600 _FOREGROUND_GREEN = 0x0002
617 _FOREGROUND_GREEN = 0x0002
601 _FOREGROUND_RED = 0x0004
618 _FOREGROUND_RED = 0x0004
602 _FOREGROUND_INTENSITY = 0x0008
619 _FOREGROUND_INTENSITY = 0x0008
603
620
604 _BACKGROUND_BLUE = 0x0010
621 _BACKGROUND_BLUE = 0x0010
605 _BACKGROUND_GREEN = 0x0020
622 _BACKGROUND_GREEN = 0x0020
606 _BACKGROUND_RED = 0x0040
623 _BACKGROUND_RED = 0x0040
607 _BACKGROUND_INTENSITY = 0x0080
624 _BACKGROUND_INTENSITY = 0x0080
608
625
609 _COMMON_LVB_REVERSE_VIDEO = 0x4000
626 _COMMON_LVB_REVERSE_VIDEO = 0x4000
610 _COMMON_LVB_UNDERSCORE = 0x8000
627 _COMMON_LVB_UNDERSCORE = 0x8000
611
628
612 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
629 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
613 w32effects = {
630 w32effects = {
614 'none': -1,
631 'none': -1,
615 'black': 0,
632 'black': 0,
616 'red': _FOREGROUND_RED,
633 'red': _FOREGROUND_RED,
617 'green': _FOREGROUND_GREEN,
634 'green': _FOREGROUND_GREEN,
618 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
635 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
619 'blue': _FOREGROUND_BLUE,
636 'blue': _FOREGROUND_BLUE,
620 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
637 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
621 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
638 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
622 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
639 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
623 'bold': _FOREGROUND_INTENSITY,
640 'bold': _FOREGROUND_INTENSITY,
624 'black_background': 0x100, # unused value > 0x0f
641 'black_background': 0x100, # unused value > 0x0f
625 'red_background': _BACKGROUND_RED,
642 'red_background': _BACKGROUND_RED,
626 'green_background': _BACKGROUND_GREEN,
643 'green_background': _BACKGROUND_GREEN,
627 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
644 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
628 'blue_background': _BACKGROUND_BLUE,
645 'blue_background': _BACKGROUND_BLUE,
629 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
646 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
630 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
647 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
631 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
648 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
632 _BACKGROUND_BLUE),
649 _BACKGROUND_BLUE),
633 'bold_background': _BACKGROUND_INTENSITY,
650 'bold_background': _BACKGROUND_INTENSITY,
634 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
651 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
635 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
652 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
636 }
653 }
637
654
638 passthrough = set([_FOREGROUND_INTENSITY,
655 passthrough = set([_FOREGROUND_INTENSITY,
639 _BACKGROUND_INTENSITY,
656 _BACKGROUND_INTENSITY,
640 _COMMON_LVB_UNDERSCORE,
657 _COMMON_LVB_UNDERSCORE,
641 _COMMON_LVB_REVERSE_VIDEO])
658 _COMMON_LVB_REVERSE_VIDEO])
642
659
643 stdout = _kernel32.GetStdHandle(
660 stdout = _kernel32.GetStdHandle(
644 _STD_OUTPUT_HANDLE) # don't close the handle returned
661 _STD_OUTPUT_HANDLE) # don't close the handle returned
645 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
662 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
646 w32effects = None
663 w32effects = None
647 else:
664 else:
648 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
665 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
649 if not _kernel32.GetConsoleScreenBufferInfo(
666 if not _kernel32.GetConsoleScreenBufferInfo(
650 stdout, ctypes.byref(csbi)):
667 stdout, ctypes.byref(csbi)):
651 # stdout may not support GetConsoleScreenBufferInfo()
668 # stdout may not support GetConsoleScreenBufferInfo()
652 # when called from subprocess or redirected
669 # when called from subprocess or redirected
653 w32effects = None
670 w32effects = None
654 else:
671 else:
655 origattr = csbi.wAttributes
672 origattr = csbi.wAttributes
656 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
673 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
657 re.MULTILINE | re.DOTALL)
674 re.MULTILINE | re.DOTALL)
658
675
659 def win32print(text, orig, **opts):
676 def win32print(text, orig, **opts):
660 label = opts.get('label', '')
677 label = opts.get('label', '')
661 attr = origattr
678 attr = origattr
662
679
663 def mapcolor(val, attr):
680 def mapcolor(val, attr):
664 if val == -1:
681 if val == -1:
665 return origattr
682 return origattr
666 elif val in passthrough:
683 elif val in passthrough:
667 return attr | val
684 return attr | val
668 elif val > 0x0f:
685 elif val > 0x0f:
669 return (val & 0x70) | (attr & 0x8f)
686 return (val & 0x70) | (attr & 0x8f)
670 else:
687 else:
671 return (val & 0x07) | (attr & 0xf8)
688 return (val & 0x07) | (attr & 0xf8)
672
689
673 # determine console attributes based on labels
690 # determine console attributes based on labels
674 for l in label.split():
691 for l in label.split():
675 style = _styles.get(l, '')
692 style = _styles.get(l, '')
676 for effect in style.split():
693 for effect in style.split():
677 try:
694 try:
678 attr = mapcolor(w32effects[effect], attr)
695 attr = mapcolor(w32effects[effect], attr)
679 except KeyError:
696 except KeyError:
680 # w32effects could not have certain attributes so we skip
697 # w32effects could not have certain attributes so we skip
681 # them if not found
698 # them if not found
682 pass
699 pass
683 # hack to ensure regexp finds data
700 # hack to ensure regexp finds data
684 if not text.startswith('\033['):
701 if not text.startswith('\033['):
685 text = '\033[m' + text
702 text = '\033[m' + text
686
703
687 # Look for ANSI-like codes embedded in text
704 # Look for ANSI-like codes embedded in text
688 m = re.match(ansire, text)
705 m = re.match(ansire, text)
689
706
690 try:
707 try:
691 while m:
708 while m:
692 for sattr in m.group(1).split(';'):
709 for sattr in m.group(1).split(';'):
693 if sattr:
710 if sattr:
694 attr = mapcolor(int(sattr), attr)
711 attr = mapcolor(int(sattr), attr)
695 _kernel32.SetConsoleTextAttribute(stdout, attr)
712 _kernel32.SetConsoleTextAttribute(stdout, attr)
696 orig(m.group(2), **opts)
713 orig(m.group(2), **opts)
697 m = re.match(ansire, m.group(3))
714 m = re.match(ansire, m.group(3))
698 finally:
715 finally:
699 # Explicitly reset original attributes
716 # Explicitly reset original attributes
700 _kernel32.SetConsoleTextAttribute(stdout, origattr)
717 _kernel32.SetConsoleTextAttribute(stdout, origattr)
General Comments 0
You need to be logged in to leave comments. Login now