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