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