##// END OF EJS Templates
color: drop use of the 'global' keyword for _terminfo_params...
Pierre-Yves David -
r30650:ae37e395 default
parent child Browse files
Show More
@@ -1,718 +1,715 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 from mercurial.i18n import _
167 from mercurial.i18n import _
168 from mercurial import (
168 from mercurial import (
169 cmdutil,
169 cmdutil,
170 commands,
170 commands,
171 dispatch,
171 dispatch,
172 encoding,
172 encoding,
173 extensions,
173 extensions,
174 pycompat,
174 pycompat,
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
201 # If we failed to load curses, we go ahead and return.
200 # If we failed to load curses, we go ahead and return.
202 if not _terminfo_params:
201 if not _terminfo_params:
203 return
202 return
204 # Otherwise, see what the config file says.
203 # Otherwise, see what the config file says.
205 if mode not in ('auto', 'terminfo'):
204 if mode not in ('auto', 'terminfo'):
206 return
205 return
207
206
208 _terminfo_params.update((key[6:], (False, int(val), ''))
207 _terminfo_params.update((key[6:], (False, int(val), ''))
209 for key, val in ui.configitems('color')
208 for key, val in ui.configitems('color')
210 if key.startswith('color.'))
209 if key.startswith('color.'))
211 _terminfo_params.update((key[9:], (True, '', val.replace('\\E', '\x1b')))
210 _terminfo_params.update((key[9:], (True, '', val.replace('\\E', '\x1b')))
212 for key, val in ui.configitems('color')
211 for key, val in ui.configitems('color')
213 if key.startswith('terminfo.'))
212 if key.startswith('terminfo.'))
214
213
215 try:
214 try:
216 curses.setupterm()
215 curses.setupterm()
217 except curses.error as e:
216 except curses.error as e:
218 _terminfo_params = {}
217 _terminfo_params.clear()
219 return
218 return
220
219
221 for key, (b, e, c) in _terminfo_params.items():
220 for key, (b, e, c) in _terminfo_params.items():
222 if not b:
221 if not b:
223 continue
222 continue
224 if not c and not curses.tigetstr(e):
223 if not c and not curses.tigetstr(e):
225 # Most terminals don't support dim, invis, etc, so don't be
224 # Most terminals don't support dim, invis, etc, so don't be
226 # noisy and use ui.debug().
225 # noisy and use ui.debug().
227 ui.debug("no terminfo entry for %s\n" % e)
226 ui.debug("no terminfo entry for %s\n" % e)
228 del _terminfo_params[key]
227 del _terminfo_params[key]
229 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
228 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
230 # Only warn about missing terminfo entries if we explicitly asked for
229 # Only warn about missing terminfo entries if we explicitly asked for
231 # terminfo mode.
230 # terminfo mode.
232 if mode == "terminfo":
231 if mode == "terminfo":
233 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
232 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
234 "ECMA-48 color\n"))
233 "ECMA-48 color\n"))
235 _terminfo_params = {}
234 _terminfo_params.clear()
236
235
237 def _modesetup(ui, coloropt):
236 def _modesetup(ui, coloropt):
238 global _terminfo_params
239
240 if coloropt == 'debug':
237 if coloropt == 'debug':
241 return 'debug'
238 return 'debug'
242
239
243 auto = (coloropt == 'auto')
240 auto = (coloropt == 'auto')
244 always = not auto and util.parsebool(coloropt)
241 always = not auto and util.parsebool(coloropt)
245 if not always and not auto:
242 if not always and not auto:
246 return None
243 return None
247
244
248 formatted = (always or (encoding.environ.get('TERM') != 'dumb'
245 formatted = (always or (encoding.environ.get('TERM') != 'dumb'
249 and ui.formatted()))
246 and ui.formatted()))
250
247
251 mode = ui.config('color', 'mode', 'auto')
248 mode = ui.config('color', 'mode', 'auto')
252
249
253 # If pager is active, color.pagermode overrides color.mode.
250 # If pager is active, color.pagermode overrides color.mode.
254 if getattr(ui, 'pageractive', False):
251 if getattr(ui, 'pageractive', False):
255 mode = ui.config('color', 'pagermode', mode)
252 mode = ui.config('color', 'pagermode', mode)
256
253
257 realmode = mode
254 realmode = mode
258 if mode == 'auto':
255 if mode == 'auto':
259 if pycompat.osname == 'nt':
256 if pycompat.osname == 'nt':
260 term = encoding.environ.get('TERM')
257 term = encoding.environ.get('TERM')
261 # TERM won't be defined in a vanilla cmd.exe environment.
258 # TERM won't be defined in a vanilla cmd.exe environment.
262
259
263 # UNIX-like environments on Windows such as Cygwin and MSYS will
260 # UNIX-like environments on Windows such as Cygwin and MSYS will
264 # set TERM. They appear to make a best effort attempt at setting it
261 # set TERM. They appear to make a best effort attempt at setting it
265 # to something appropriate. However, not all environments with TERM
262 # to something appropriate. However, not all environments with TERM
266 # defined support ANSI. Since "ansi" could result in terminal
263 # defined support ANSI. Since "ansi" could result in terminal
267 # gibberish, we error on the side of selecting "win32". However, if
264 # gibberish, we error on the side of selecting "win32". However, if
268 # w32effects is not defined, we almost certainly don't support
265 # w32effects is not defined, we almost certainly don't support
269 # "win32", so don't even try.
266 # "win32", so don't even try.
270 if (term and 'xterm' in term) or not w32effects:
267 if (term and 'xterm' in term) or not w32effects:
271 realmode = 'ansi'
268 realmode = 'ansi'
272 else:
269 else:
273 realmode = 'win32'
270 realmode = 'win32'
274 else:
271 else:
275 realmode = 'ansi'
272 realmode = 'ansi'
276
273
277 def modewarn():
274 def modewarn():
278 # only warn if color.mode was explicitly set and we're in
275 # only warn if color.mode was explicitly set and we're in
279 # a formatted terminal
276 # a formatted terminal
280 if mode == realmode and ui.formatted():
277 if mode == realmode and ui.formatted():
281 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
278 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
282
279
283 if realmode == 'win32':
280 if realmode == 'win32':
284 _terminfo_params = {}
281 _terminfo_params.clear()
285 if not w32effects:
282 if not w32effects:
286 modewarn()
283 modewarn()
287 return None
284 return None
288 _effects.update(w32effects)
285 _effects.update(w32effects)
289 elif realmode == 'ansi':
286 elif realmode == 'ansi':
290 _terminfo_params = {}
287 _terminfo_params.clear()
291 elif realmode == 'terminfo':
288 elif realmode == 'terminfo':
292 _terminfosetup(ui, mode)
289 _terminfosetup(ui, mode)
293 if not _terminfo_params:
290 if not _terminfo_params:
294 ## FIXME Shouldn't we return None in this case too?
291 ## FIXME Shouldn't we return None in this case too?
295 modewarn()
292 modewarn()
296 realmode = 'ansi'
293 realmode = 'ansi'
297 else:
294 else:
298 return None
295 return None
299
296
300 if always or (auto and formatted):
297 if always or (auto and formatted):
301 return realmode
298 return realmode
302 return None
299 return None
303
300
304 try:
301 try:
305 import curses
302 import curses
306 # Mapping from effect name to terminfo attribute name (or raw code) or
303 # Mapping from effect name to terminfo attribute name (or raw code) or
307 # color number. This will also force-load the curses module.
304 # color number. This will also force-load the curses module.
308 _terminfo_params = {'none': (True, 'sgr0', ''),
305 _terminfo_params = {'none': (True, 'sgr0', ''),
309 'standout': (True, 'smso', ''),
306 'standout': (True, 'smso', ''),
310 'underline': (True, 'smul', ''),
307 'underline': (True, 'smul', ''),
311 'reverse': (True, 'rev', ''),
308 'reverse': (True, 'rev', ''),
312 'inverse': (True, 'rev', ''),
309 'inverse': (True, 'rev', ''),
313 'blink': (True, 'blink', ''),
310 'blink': (True, 'blink', ''),
314 'dim': (True, 'dim', ''),
311 'dim': (True, 'dim', ''),
315 'bold': (True, 'bold', ''),
312 'bold': (True, 'bold', ''),
316 'invisible': (True, 'invis', ''),
313 'invisible': (True, 'invis', ''),
317 'italic': (True, 'sitm', ''),
314 'italic': (True, 'sitm', ''),
318 'black': (False, curses.COLOR_BLACK, ''),
315 'black': (False, curses.COLOR_BLACK, ''),
319 'red': (False, curses.COLOR_RED, ''),
316 'red': (False, curses.COLOR_RED, ''),
320 'green': (False, curses.COLOR_GREEN, ''),
317 'green': (False, curses.COLOR_GREEN, ''),
321 'yellow': (False, curses.COLOR_YELLOW, ''),
318 'yellow': (False, curses.COLOR_YELLOW, ''),
322 'blue': (False, curses.COLOR_BLUE, ''),
319 'blue': (False, curses.COLOR_BLUE, ''),
323 'magenta': (False, curses.COLOR_MAGENTA, ''),
320 'magenta': (False, curses.COLOR_MAGENTA, ''),
324 'cyan': (False, curses.COLOR_CYAN, ''),
321 'cyan': (False, curses.COLOR_CYAN, ''),
325 'white': (False, curses.COLOR_WHITE, '')}
322 'white': (False, curses.COLOR_WHITE, '')}
326 except ImportError:
323 except ImportError:
327 _terminfo_params = {}
324 _terminfo_params = {}
328
325
329 _styles = {'grep.match': 'red bold',
326 _styles = {'grep.match': 'red bold',
330 'grep.linenumber': 'green',
327 'grep.linenumber': 'green',
331 'grep.rev': 'green',
328 'grep.rev': 'green',
332 'grep.change': 'green',
329 'grep.change': 'green',
333 'grep.sep': 'cyan',
330 'grep.sep': 'cyan',
334 'grep.filename': 'magenta',
331 'grep.filename': 'magenta',
335 'grep.user': 'magenta',
332 'grep.user': 'magenta',
336 'grep.date': 'magenta',
333 'grep.date': 'magenta',
337 'bookmarks.active': 'green',
334 'bookmarks.active': 'green',
338 'branches.active': 'none',
335 'branches.active': 'none',
339 'branches.closed': 'black bold',
336 'branches.closed': 'black bold',
340 'branches.current': 'green',
337 'branches.current': 'green',
341 'branches.inactive': 'none',
338 'branches.inactive': 'none',
342 'diff.changed': 'white',
339 'diff.changed': 'white',
343 'diff.deleted': 'red',
340 'diff.deleted': 'red',
344 'diff.diffline': 'bold',
341 'diff.diffline': 'bold',
345 'diff.extended': 'cyan bold',
342 'diff.extended': 'cyan bold',
346 'diff.file_a': 'red bold',
343 'diff.file_a': 'red bold',
347 'diff.file_b': 'green bold',
344 'diff.file_b': 'green bold',
348 'diff.hunk': 'magenta',
345 'diff.hunk': 'magenta',
349 'diff.inserted': 'green',
346 'diff.inserted': 'green',
350 'diff.tab': '',
347 'diff.tab': '',
351 'diff.trailingwhitespace': 'bold red_background',
348 'diff.trailingwhitespace': 'bold red_background',
352 'changeset.public' : '',
349 'changeset.public' : '',
353 'changeset.draft' : '',
350 'changeset.draft' : '',
354 'changeset.secret' : '',
351 'changeset.secret' : '',
355 'diffstat.deleted': 'red',
352 'diffstat.deleted': 'red',
356 'diffstat.inserted': 'green',
353 'diffstat.inserted': 'green',
357 'histedit.remaining': 'red bold',
354 'histedit.remaining': 'red bold',
358 'ui.prompt': 'yellow',
355 'ui.prompt': 'yellow',
359 'log.changeset': 'yellow',
356 'log.changeset': 'yellow',
360 'patchbomb.finalsummary': '',
357 'patchbomb.finalsummary': '',
361 'patchbomb.from': 'magenta',
358 'patchbomb.from': 'magenta',
362 'patchbomb.to': 'cyan',
359 'patchbomb.to': 'cyan',
363 'patchbomb.subject': 'green',
360 'patchbomb.subject': 'green',
364 'patchbomb.diffstats': '',
361 'patchbomb.diffstats': '',
365 'rebase.rebased': 'blue',
362 'rebase.rebased': 'blue',
366 'rebase.remaining': 'red bold',
363 'rebase.remaining': 'red bold',
367 'resolve.resolved': 'green bold',
364 'resolve.resolved': 'green bold',
368 'resolve.unresolved': 'red bold',
365 'resolve.unresolved': 'red bold',
369 'shelve.age': 'cyan',
366 'shelve.age': 'cyan',
370 'shelve.newest': 'green bold',
367 'shelve.newest': 'green bold',
371 'shelve.name': 'blue bold',
368 'shelve.name': 'blue bold',
372 'status.added': 'green bold',
369 'status.added': 'green bold',
373 'status.clean': 'none',
370 'status.clean': 'none',
374 'status.copied': 'none',
371 'status.copied': 'none',
375 'status.deleted': 'cyan bold underline',
372 'status.deleted': 'cyan bold underline',
376 'status.ignored': 'black bold',
373 'status.ignored': 'black bold',
377 'status.modified': 'blue bold',
374 'status.modified': 'blue bold',
378 'status.removed': 'red bold',
375 'status.removed': 'red bold',
379 'status.unknown': 'magenta bold underline',
376 'status.unknown': 'magenta bold underline',
380 'tags.normal': 'green',
377 'tags.normal': 'green',
381 'tags.local': 'black bold'}
378 'tags.local': 'black bold'}
382
379
383
380
384 def _effect_str(effect):
381 def _effect_str(effect):
385 '''Helper function for render_effects().'''
382 '''Helper function for render_effects().'''
386
383
387 bg = False
384 bg = False
388 if effect.endswith('_background'):
385 if effect.endswith('_background'):
389 bg = True
386 bg = True
390 effect = effect[:-11]
387 effect = effect[:-11]
391 try:
388 try:
392 attr, val, termcode = _terminfo_params[effect]
389 attr, val, termcode = _terminfo_params[effect]
393 except KeyError:
390 except KeyError:
394 return ''
391 return ''
395 if attr:
392 if attr:
396 if termcode:
393 if termcode:
397 return termcode
394 return termcode
398 else:
395 else:
399 return curses.tigetstr(val)
396 return curses.tigetstr(val)
400 elif bg:
397 elif bg:
401 return curses.tparm(curses.tigetstr('setab'), val)
398 return curses.tparm(curses.tigetstr('setab'), val)
402 else:
399 else:
403 return curses.tparm(curses.tigetstr('setaf'), val)
400 return curses.tparm(curses.tigetstr('setaf'), val)
404
401
405 def render_effects(text, effects):
402 def render_effects(text, effects):
406 'Wrap text in commands to turn on each effect.'
403 'Wrap text in commands to turn on each effect.'
407 if not text:
404 if not text:
408 return text
405 return text
409 if not _terminfo_params:
406 if not _terminfo_params:
410 start = [str(_effects[e]) for e in ['none'] + effects.split()]
407 start = [str(_effects[e]) for e in ['none'] + effects.split()]
411 start = '\033[' + ';'.join(start) + 'm'
408 start = '\033[' + ';'.join(start) + 'm'
412 stop = '\033[' + str(_effects['none']) + 'm'
409 stop = '\033[' + str(_effects['none']) + 'm'
413 else:
410 else:
414 start = ''.join(_effect_str(effect)
411 start = ''.join(_effect_str(effect)
415 for effect in ['none'] + effects.split())
412 for effect in ['none'] + effects.split())
416 stop = _effect_str('none')
413 stop = _effect_str('none')
417 return ''.join([start, text, stop])
414 return ''.join([start, text, stop])
418
415
419 def extstyles():
416 def extstyles():
420 for name, ext in extensions.extensions():
417 for name, ext in extensions.extensions():
421 _styles.update(getattr(ext, 'colortable', {}))
418 _styles.update(getattr(ext, 'colortable', {}))
422
419
423 def valideffect(effect):
420 def valideffect(effect):
424 'Determine if the effect is valid or not.'
421 'Determine if the effect is valid or not.'
425 good = False
422 good = False
426 if not _terminfo_params and effect in _effects:
423 if not _terminfo_params and effect in _effects:
427 good = True
424 good = True
428 elif effect in _terminfo_params or effect[:-11] in _terminfo_params:
425 elif effect in _terminfo_params or effect[:-11] in _terminfo_params:
429 good = True
426 good = True
430 return good
427 return good
431
428
432 def configstyles(ui):
429 def configstyles(ui):
433 for status, cfgeffects in ui.configitems('color'):
430 for status, cfgeffects in ui.configitems('color'):
434 if '.' not in status or status.startswith(('color.', 'terminfo.')):
431 if '.' not in status or status.startswith(('color.', 'terminfo.')):
435 continue
432 continue
436 cfgeffects = ui.configlist('color', status)
433 cfgeffects = ui.configlist('color', status)
437 if cfgeffects:
434 if cfgeffects:
438 good = []
435 good = []
439 for e in cfgeffects:
436 for e in cfgeffects:
440 if valideffect(e):
437 if valideffect(e):
441 good.append(e)
438 good.append(e)
442 else:
439 else:
443 ui.warn(_("ignoring unknown color/effect %r "
440 ui.warn(_("ignoring unknown color/effect %r "
444 "(configured in color.%s)\n")
441 "(configured in color.%s)\n")
445 % (e, status))
442 % (e, status))
446 _styles[status] = ' '.join(good)
443 _styles[status] = ' '.join(good)
447
444
448 class colorui(uimod.ui):
445 class colorui(uimod.ui):
449 _colormode = 'ansi'
446 _colormode = 'ansi'
450 def write(self, *args, **opts):
447 def write(self, *args, **opts):
451 if self._colormode is None:
448 if self._colormode is None:
452 return super(colorui, self).write(*args, **opts)
449 return super(colorui, self).write(*args, **opts)
453
450
454 label = opts.get('label', '')
451 label = opts.get('label', '')
455 if self._buffers and not opts.get('prompt', False):
452 if self._buffers and not opts.get('prompt', False):
456 if self._bufferapplylabels:
453 if self._bufferapplylabels:
457 self._buffers[-1].extend(self.label(a, label) for a in args)
454 self._buffers[-1].extend(self.label(a, label) for a in args)
458 else:
455 else:
459 self._buffers[-1].extend(args)
456 self._buffers[-1].extend(args)
460 elif self._colormode == 'win32':
457 elif self._colormode == 'win32':
461 for a in args:
458 for a in args:
462 win32print(a, super(colorui, self).write, **opts)
459 win32print(a, super(colorui, self).write, **opts)
463 else:
460 else:
464 return super(colorui, self).write(
461 return super(colorui, self).write(
465 *[self.label(a, label) for a in args], **opts)
462 *[self.label(a, label) for a in args], **opts)
466
463
467 def write_err(self, *args, **opts):
464 def write_err(self, *args, **opts):
468 if self._colormode is None:
465 if self._colormode is None:
469 return super(colorui, self).write_err(*args, **opts)
466 return super(colorui, self).write_err(*args, **opts)
470
467
471 label = opts.get('label', '')
468 label = opts.get('label', '')
472 if self._bufferstates and self._bufferstates[-1][0]:
469 if self._bufferstates and self._bufferstates[-1][0]:
473 return self.write(*args, **opts)
470 return self.write(*args, **opts)
474 if self._colormode == 'win32':
471 if self._colormode == 'win32':
475 for a in args:
472 for a in args:
476 win32print(a, super(colorui, self).write_err, **opts)
473 win32print(a, super(colorui, self).write_err, **opts)
477 else:
474 else:
478 return super(colorui, self).write_err(
475 return super(colorui, self).write_err(
479 *[self.label(a, label) for a in args], **opts)
476 *[self.label(a, label) for a in args], **opts)
480
477
481 def showlabel(self, msg, label):
478 def showlabel(self, msg, label):
482 if label and msg:
479 if label and msg:
483 if msg[-1] == '\n':
480 if msg[-1] == '\n':
484 return "[%s|%s]\n" % (label, msg[:-1])
481 return "[%s|%s]\n" % (label, msg[:-1])
485 else:
482 else:
486 return "[%s|%s]" % (label, msg)
483 return "[%s|%s]" % (label, msg)
487 else:
484 else:
488 return msg
485 return msg
489
486
490 def label(self, msg, label):
487 def label(self, msg, label):
491 if self._colormode is None:
488 if self._colormode is None:
492 return super(colorui, self).label(msg, label)
489 return super(colorui, self).label(msg, label)
493
490
494 if self._colormode == 'debug':
491 if self._colormode == 'debug':
495 return self.showlabel(msg, label)
492 return self.showlabel(msg, label)
496
493
497 effects = []
494 effects = []
498 for l in label.split():
495 for l in label.split():
499 s = _styles.get(l, '')
496 s = _styles.get(l, '')
500 if s:
497 if s:
501 effects.append(s)
498 effects.append(s)
502 elif valideffect(l):
499 elif valideffect(l):
503 effects.append(l)
500 effects.append(l)
504 effects = ' '.join(effects)
501 effects = ' '.join(effects)
505 if effects:
502 if effects:
506 return '\n'.join([render_effects(line, effects)
503 return '\n'.join([render_effects(line, effects)
507 for line in msg.split('\n')])
504 for line in msg.split('\n')])
508 return msg
505 return msg
509
506
510 def uisetup(ui):
507 def uisetup(ui):
511 if ui.plain():
508 if ui.plain():
512 return
509 return
513 if not isinstance(ui, colorui):
510 if not isinstance(ui, colorui):
514 colorui.__bases__ = (ui.__class__,)
511 colorui.__bases__ = (ui.__class__,)
515 ui.__class__ = colorui
512 ui.__class__ = colorui
516 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
513 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
517 mode = _modesetup(ui_, opts['color'])
514 mode = _modesetup(ui_, opts['color'])
518 colorui._colormode = mode
515 colorui._colormode = mode
519 if mode and mode != 'debug':
516 if mode and mode != 'debug':
520 extstyles()
517 extstyles()
521 configstyles(ui_)
518 configstyles(ui_)
522 return orig(ui_, opts, cmd, cmdfunc)
519 return orig(ui_, opts, cmd, cmdfunc)
523 def colorgit(orig, gitsub, commands, env=None, stream=False, cwd=None):
520 def colorgit(orig, gitsub, commands, env=None, stream=False, cwd=None):
524 if gitsub.ui._colormode and len(commands) and commands[0] == "diff":
521 if gitsub.ui._colormode and len(commands) and commands[0] == "diff":
525 # insert the argument in the front,
522 # insert the argument in the front,
526 # the end of git diff arguments is used for paths
523 # the end of git diff arguments is used for paths
527 commands.insert(1, '--color')
524 commands.insert(1, '--color')
528 return orig(gitsub, commands, env, stream, cwd)
525 return orig(gitsub, commands, env, stream, cwd)
529 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
526 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
530 extensions.wrapfunction(subrepo.gitsubrepo, '_gitnodir', colorgit)
527 extensions.wrapfunction(subrepo.gitsubrepo, '_gitnodir', colorgit)
531
528
532 def extsetup(ui):
529 def extsetup(ui):
533 commands.globalopts.append(
530 commands.globalopts.append(
534 ('', 'color', 'auto',
531 ('', 'color', 'auto',
535 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
532 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
536 # and should not be translated
533 # and should not be translated
537 _("when to colorize (boolean, always, auto, never, or debug)"),
534 _("when to colorize (boolean, always, auto, never, or debug)"),
538 _('TYPE')))
535 _('TYPE')))
539
536
540 @command('debugcolor',
537 @command('debugcolor',
541 [('', 'style', None, _('show all configured styles'))],
538 [('', 'style', None, _('show all configured styles'))],
542 'hg debugcolor')
539 'hg debugcolor')
543 def debugcolor(ui, repo, **opts):
540 def debugcolor(ui, repo, **opts):
544 """show available color, effects or style"""
541 """show available color, effects or style"""
545 ui.write(('color mode: %s\n') % ui._colormode)
542 ui.write(('color mode: %s\n') % ui._colormode)
546 if opts.get('style'):
543 if opts.get('style'):
547 return _debugdisplaystyle(ui)
544 return _debugdisplaystyle(ui)
548 else:
545 else:
549 return _debugdisplaycolor(ui)
546 return _debugdisplaycolor(ui)
550
547
551 def _debugdisplaycolor(ui):
548 def _debugdisplaycolor(ui):
552 global _styles
549 global _styles
553 oldstyle = _styles
550 oldstyle = _styles
554 try:
551 try:
555 _styles = {}
552 _styles = {}
556 for effect in _effects.keys():
553 for effect in _effects.keys():
557 _styles[effect] = effect
554 _styles[effect] = effect
558 if _terminfo_params:
555 if _terminfo_params:
559 for k, v in ui.configitems('color'):
556 for k, v in ui.configitems('color'):
560 if k.startswith('color.'):
557 if k.startswith('color.'):
561 _styles[k] = k[6:]
558 _styles[k] = k[6:]
562 elif k.startswith('terminfo.'):
559 elif k.startswith('terminfo.'):
563 _styles[k] = k[9:]
560 _styles[k] = k[9:]
564 ui.write(_('available colors:\n'))
561 ui.write(_('available colors:\n'))
565 # sort label with a '_' after the other to group '_background' entry.
562 # sort label with a '_' after the other to group '_background' entry.
566 items = sorted(_styles.items(),
563 items = sorted(_styles.items(),
567 key=lambda i: ('_' in i[0], i[0], i[1]))
564 key=lambda i: ('_' in i[0], i[0], i[1]))
568 for colorname, label in items:
565 for colorname, label in items:
569 ui.write(('%s\n') % colorname, label=label)
566 ui.write(('%s\n') % colorname, label=label)
570 finally:
567 finally:
571 _styles = oldstyle
568 _styles = oldstyle
572
569
573 def _debugdisplaystyle(ui):
570 def _debugdisplaystyle(ui):
574 ui.write(_('available style:\n'))
571 ui.write(_('available style:\n'))
575 width = max(len(s) for s in _styles)
572 width = max(len(s) for s in _styles)
576 for label, effects in sorted(_styles.items()):
573 for label, effects in sorted(_styles.items()):
577 ui.write('%s' % label, label=label)
574 ui.write('%s' % label, label=label)
578 if effects:
575 if effects:
579 # 50
576 # 50
580 ui.write(': ')
577 ui.write(': ')
581 ui.write(' ' * (max(0, width - len(label))))
578 ui.write(' ' * (max(0, width - len(label))))
582 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
579 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
583 ui.write('\n')
580 ui.write('\n')
584
581
585 if pycompat.osname != 'nt':
582 if pycompat.osname != 'nt':
586 w32effects = None
583 w32effects = None
587 else:
584 else:
588 import ctypes
585 import ctypes
589 import re
586 import re
590
587
591 _kernel32 = ctypes.windll.kernel32
588 _kernel32 = ctypes.windll.kernel32
592
589
593 _WORD = ctypes.c_ushort
590 _WORD = ctypes.c_ushort
594
591
595 _INVALID_HANDLE_VALUE = -1
592 _INVALID_HANDLE_VALUE = -1
596
593
597 class _COORD(ctypes.Structure):
594 class _COORD(ctypes.Structure):
598 _fields_ = [('X', ctypes.c_short),
595 _fields_ = [('X', ctypes.c_short),
599 ('Y', ctypes.c_short)]
596 ('Y', ctypes.c_short)]
600
597
601 class _SMALL_RECT(ctypes.Structure):
598 class _SMALL_RECT(ctypes.Structure):
602 _fields_ = [('Left', ctypes.c_short),
599 _fields_ = [('Left', ctypes.c_short),
603 ('Top', ctypes.c_short),
600 ('Top', ctypes.c_short),
604 ('Right', ctypes.c_short),
601 ('Right', ctypes.c_short),
605 ('Bottom', ctypes.c_short)]
602 ('Bottom', ctypes.c_short)]
606
603
607 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
604 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
608 _fields_ = [('dwSize', _COORD),
605 _fields_ = [('dwSize', _COORD),
609 ('dwCursorPosition', _COORD),
606 ('dwCursorPosition', _COORD),
610 ('wAttributes', _WORD),
607 ('wAttributes', _WORD),
611 ('srWindow', _SMALL_RECT),
608 ('srWindow', _SMALL_RECT),
612 ('dwMaximumWindowSize', _COORD)]
609 ('dwMaximumWindowSize', _COORD)]
613
610
614 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
611 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
615 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12
612 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12
616
613
617 _FOREGROUND_BLUE = 0x0001
614 _FOREGROUND_BLUE = 0x0001
618 _FOREGROUND_GREEN = 0x0002
615 _FOREGROUND_GREEN = 0x0002
619 _FOREGROUND_RED = 0x0004
616 _FOREGROUND_RED = 0x0004
620 _FOREGROUND_INTENSITY = 0x0008
617 _FOREGROUND_INTENSITY = 0x0008
621
618
622 _BACKGROUND_BLUE = 0x0010
619 _BACKGROUND_BLUE = 0x0010
623 _BACKGROUND_GREEN = 0x0020
620 _BACKGROUND_GREEN = 0x0020
624 _BACKGROUND_RED = 0x0040
621 _BACKGROUND_RED = 0x0040
625 _BACKGROUND_INTENSITY = 0x0080
622 _BACKGROUND_INTENSITY = 0x0080
626
623
627 _COMMON_LVB_REVERSE_VIDEO = 0x4000
624 _COMMON_LVB_REVERSE_VIDEO = 0x4000
628 _COMMON_LVB_UNDERSCORE = 0x8000
625 _COMMON_LVB_UNDERSCORE = 0x8000
629
626
630 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
627 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
631 w32effects = {
628 w32effects = {
632 'none': -1,
629 'none': -1,
633 'black': 0,
630 'black': 0,
634 'red': _FOREGROUND_RED,
631 'red': _FOREGROUND_RED,
635 'green': _FOREGROUND_GREEN,
632 'green': _FOREGROUND_GREEN,
636 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
633 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
637 'blue': _FOREGROUND_BLUE,
634 'blue': _FOREGROUND_BLUE,
638 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
635 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
639 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
636 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
640 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
637 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
641 'bold': _FOREGROUND_INTENSITY,
638 'bold': _FOREGROUND_INTENSITY,
642 'black_background': 0x100, # unused value > 0x0f
639 'black_background': 0x100, # unused value > 0x0f
643 'red_background': _BACKGROUND_RED,
640 'red_background': _BACKGROUND_RED,
644 'green_background': _BACKGROUND_GREEN,
641 'green_background': _BACKGROUND_GREEN,
645 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
642 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
646 'blue_background': _BACKGROUND_BLUE,
643 'blue_background': _BACKGROUND_BLUE,
647 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
644 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
648 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
645 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
649 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
646 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
650 _BACKGROUND_BLUE),
647 _BACKGROUND_BLUE),
651 'bold_background': _BACKGROUND_INTENSITY,
648 'bold_background': _BACKGROUND_INTENSITY,
652 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
649 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
653 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
650 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
654 }
651 }
655
652
656 passthrough = set([_FOREGROUND_INTENSITY,
653 passthrough = set([_FOREGROUND_INTENSITY,
657 _BACKGROUND_INTENSITY,
654 _BACKGROUND_INTENSITY,
658 _COMMON_LVB_UNDERSCORE,
655 _COMMON_LVB_UNDERSCORE,
659 _COMMON_LVB_REVERSE_VIDEO])
656 _COMMON_LVB_REVERSE_VIDEO])
660
657
661 stdout = _kernel32.GetStdHandle(
658 stdout = _kernel32.GetStdHandle(
662 _STD_OUTPUT_HANDLE) # don't close the handle returned
659 _STD_OUTPUT_HANDLE) # don't close the handle returned
663 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
660 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
664 w32effects = None
661 w32effects = None
665 else:
662 else:
666 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
663 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
667 if not _kernel32.GetConsoleScreenBufferInfo(
664 if not _kernel32.GetConsoleScreenBufferInfo(
668 stdout, ctypes.byref(csbi)):
665 stdout, ctypes.byref(csbi)):
669 # stdout may not support GetConsoleScreenBufferInfo()
666 # stdout may not support GetConsoleScreenBufferInfo()
670 # when called from subprocess or redirected
667 # when called from subprocess or redirected
671 w32effects = None
668 w32effects = None
672 else:
669 else:
673 origattr = csbi.wAttributes
670 origattr = csbi.wAttributes
674 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
671 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
675 re.MULTILINE | re.DOTALL)
672 re.MULTILINE | re.DOTALL)
676
673
677 def win32print(text, orig, **opts):
674 def win32print(text, orig, **opts):
678 label = opts.get('label', '')
675 label = opts.get('label', '')
679 attr = origattr
676 attr = origattr
680
677
681 def mapcolor(val, attr):
678 def mapcolor(val, attr):
682 if val == -1:
679 if val == -1:
683 return origattr
680 return origattr
684 elif val in passthrough:
681 elif val in passthrough:
685 return attr | val
682 return attr | val
686 elif val > 0x0f:
683 elif val > 0x0f:
687 return (val & 0x70) | (attr & 0x8f)
684 return (val & 0x70) | (attr & 0x8f)
688 else:
685 else:
689 return (val & 0x07) | (attr & 0xf8)
686 return (val & 0x07) | (attr & 0xf8)
690
687
691 # determine console attributes based on labels
688 # determine console attributes based on labels
692 for l in label.split():
689 for l in label.split():
693 style = _styles.get(l, '')
690 style = _styles.get(l, '')
694 for effect in style.split():
691 for effect in style.split():
695 try:
692 try:
696 attr = mapcolor(w32effects[effect], attr)
693 attr = mapcolor(w32effects[effect], attr)
697 except KeyError:
694 except KeyError:
698 # w32effects could not have certain attributes so we skip
695 # w32effects could not have certain attributes so we skip
699 # them if not found
696 # them if not found
700 pass
697 pass
701 # hack to ensure regexp finds data
698 # hack to ensure regexp finds data
702 if not text.startswith('\033['):
699 if not text.startswith('\033['):
703 text = '\033[m' + text
700 text = '\033[m' + text
704
701
705 # Look for ANSI-like codes embedded in text
702 # Look for ANSI-like codes embedded in text
706 m = re.match(ansire, text)
703 m = re.match(ansire, text)
707
704
708 try:
705 try:
709 while m:
706 while m:
710 for sattr in m.group(1).split(';'):
707 for sattr in m.group(1).split(';'):
711 if sattr:
708 if sattr:
712 attr = mapcolor(int(sattr), attr)
709 attr = mapcolor(int(sattr), attr)
713 _kernel32.SetConsoleTextAttribute(stdout, attr)
710 _kernel32.SetConsoleTextAttribute(stdout, attr)
714 orig(m.group(2), **opts)
711 orig(m.group(2), **opts)
715 m = re.match(ansire, m.group(3))
712 m = re.match(ansire, m.group(3))
716 finally:
713 finally:
717 # Explicitly reset original attributes
714 # Explicitly reset original attributes
718 _kernel32.SetConsoleTextAttribute(stdout, origattr)
715 _kernel32.SetConsoleTextAttribute(stdout, origattr)
General Comments 0
You need to be logged in to leave comments. Login now