##// END OF EJS Templates
color: move configstyles into the core module...
Pierre-Yves David -
r30971:bb638588 default
parent child Browse files
Show More
@@ -1,620 +1,604 b''
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 300 def _effect_str(effect):
301 301 '''Helper function for render_effects().'''
302 302
303 303 bg = False
304 304 if effect.endswith('_background'):
305 305 bg = True
306 306 effect = effect[:-11]
307 307 try:
308 308 attr, val, termcode = color._terminfo_params[effect]
309 309 except KeyError:
310 310 return ''
311 311 if attr:
312 312 if termcode:
313 313 return termcode
314 314 else:
315 315 return curses.tigetstr(val)
316 316 elif bg:
317 317 return curses.tparm(curses.tigetstr('setab'), val)
318 318 else:
319 319 return curses.tparm(curses.tigetstr('setaf'), val)
320 320
321 321 def render_effects(text, effects):
322 322 'Wrap text in commands to turn on each effect.'
323 323 if not text:
324 324 return text
325 325 if not color._terminfo_params:
326 326 start = [str(color._effects[e]) for e in ['none'] + effects.split()]
327 327 start = '\033[' + ';'.join(start) + 'm'
328 328 stop = '\033[' + str(color._effects['none']) + 'm'
329 329 else:
330 330 start = ''.join(_effect_str(effect)
331 331 for effect in ['none'] + effects.split())
332 332 stop = _effect_str('none')
333 333 return ''.join([start, text, stop])
334 334
335 def configstyles(ui):
336 for status, cfgeffects in ui.configitems('color'):
337 if '.' not in status or status.startswith(('color.', 'terminfo.')):
338 continue
339 cfgeffects = ui.configlist('color', status)
340 if cfgeffects:
341 good = []
342 for e in cfgeffects:
343 if color.valideffect(e):
344 good.append(e)
345 else:
346 ui.warn(_("ignoring unknown color/effect %r "
347 "(configured in color.%s)\n")
348 % (e, status))
349 color._styles[status] = ' '.join(good)
350
351 335 class colorui(uimod.ui):
352 336 _colormode = 'ansi'
353 337 def write(self, *args, **opts):
354 338 if self._colormode is None:
355 339 return super(colorui, self).write(*args, **opts)
356 340
357 341 label = opts.get('label', '')
358 342 if self._buffers and not opts.get('prompt', False):
359 343 if self._bufferapplylabels:
360 344 self._buffers[-1].extend(self.label(a, label) for a in args)
361 345 else:
362 346 self._buffers[-1].extend(args)
363 347 elif self._colormode == 'win32':
364 348 for a in args:
365 349 win32print(a, super(colorui, self).write, **opts)
366 350 else:
367 351 return super(colorui, self).write(
368 352 *[self.label(a, label) for a in args], **opts)
369 353
370 354 def write_err(self, *args, **opts):
371 355 if self._colormode is None:
372 356 return super(colorui, self).write_err(*args, **opts)
373 357
374 358 label = opts.get('label', '')
375 359 if self._bufferstates and self._bufferstates[-1][0]:
376 360 return self.write(*args, **opts)
377 361 if self._colormode == 'win32':
378 362 for a in args:
379 363 win32print(a, super(colorui, self).write_err, **opts)
380 364 else:
381 365 return super(colorui, self).write_err(
382 366 *[self.label(a, label) for a in args], **opts)
383 367
384 368 def showlabel(self, msg, label):
385 369 if label and msg:
386 370 if msg[-1] == '\n':
387 371 return "[%s|%s]\n" % (label, msg[:-1])
388 372 else:
389 373 return "[%s|%s]" % (label, msg)
390 374 else:
391 375 return msg
392 376
393 377 def label(self, msg, label):
394 378 if self._colormode is None:
395 379 return super(colorui, self).label(msg, label)
396 380
397 381 if self._colormode == 'debug':
398 382 return self.showlabel(msg, label)
399 383
400 384 effects = []
401 385 for l in label.split():
402 386 s = color._styles.get(l, '')
403 387 if s:
404 388 effects.append(s)
405 389 elif color.valideffect(l):
406 390 effects.append(l)
407 391 effects = ' '.join(effects)
408 392 if effects:
409 393 return '\n'.join([render_effects(line, effects)
410 394 for line in msg.split('\n')])
411 395 return msg
412 396
413 397 def uisetup(ui):
414 398 if ui.plain():
415 399 return
416 400 if not isinstance(ui, colorui):
417 401 colorui.__bases__ = (ui.__class__,)
418 402 ui.__class__ = colorui
419 403 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
420 404 mode = _modesetup(ui_, opts['color'])
421 405 colorui._colormode = mode
422 406 if mode and mode != 'debug':
423 configstyles(ui_)
407 color.configstyles(ui_)
424 408 return orig(ui_, opts, cmd, cmdfunc)
425 409 def colorgit(orig, gitsub, commands, env=None, stream=False, cwd=None):
426 410 if gitsub.ui._colormode and len(commands) and commands[0] == "diff":
427 411 # insert the argument in the front,
428 412 # the end of git diff arguments is used for paths
429 413 commands.insert(1, '--color')
430 414 return orig(gitsub, commands, env, stream, cwd)
431 415 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
432 416 extensions.wrapfunction(subrepo.gitsubrepo, '_gitnodir', colorgit)
433 417
434 418 def extsetup(ui):
435 419 commands.globalopts.append(
436 420 ('', 'color', 'auto',
437 421 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
438 422 # and should not be translated
439 423 _("when to colorize (boolean, always, auto, never, or debug)"),
440 424 _('TYPE')))
441 425
442 426 @command('debugcolor',
443 427 [('', 'style', None, _('show all configured styles'))],
444 428 'hg debugcolor')
445 429 def debugcolor(ui, repo, **opts):
446 430 """show available color, effects or style"""
447 431 ui.write(('color mode: %s\n') % ui._colormode)
448 432 if opts.get('style'):
449 433 return _debugdisplaystyle(ui)
450 434 else:
451 435 return _debugdisplaycolor(ui)
452 436
453 437 def _debugdisplaycolor(ui):
454 438 oldstyle = color._styles.copy()
455 439 try:
456 440 color._styles.clear()
457 441 for effect in color._effects.keys():
458 442 color._styles[effect] = effect
459 443 if color._terminfo_params:
460 444 for k, v in ui.configitems('color'):
461 445 if k.startswith('color.'):
462 446 color._styles[k] = k[6:]
463 447 elif k.startswith('terminfo.'):
464 448 color._styles[k] = k[9:]
465 449 ui.write(_('available colors:\n'))
466 450 # sort label with a '_' after the other to group '_background' entry.
467 451 items = sorted(color._styles.items(),
468 452 key=lambda i: ('_' in i[0], i[0], i[1]))
469 453 for colorname, label in items:
470 454 ui.write(('%s\n') % colorname, label=label)
471 455 finally:
472 456 color._styles.clear()
473 457 color._styles.update(oldstyle)
474 458
475 459 def _debugdisplaystyle(ui):
476 460 ui.write(_('available style:\n'))
477 461 width = max(len(s) for s in color._styles)
478 462 for label, effects in sorted(color._styles.items()):
479 463 ui.write('%s' % label, label=label)
480 464 if effects:
481 465 # 50
482 466 ui.write(': ')
483 467 ui.write(' ' * (max(0, width - len(label))))
484 468 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
485 469 ui.write('\n')
486 470
487 471 if pycompat.osname != 'nt':
488 472 w32effects = None
489 473 else:
490 474 import ctypes
491 475 import re
492 476
493 477 _kernel32 = ctypes.windll.kernel32
494 478
495 479 _WORD = ctypes.c_ushort
496 480
497 481 _INVALID_HANDLE_VALUE = -1
498 482
499 483 class _COORD(ctypes.Structure):
500 484 _fields_ = [('X', ctypes.c_short),
501 485 ('Y', ctypes.c_short)]
502 486
503 487 class _SMALL_RECT(ctypes.Structure):
504 488 _fields_ = [('Left', ctypes.c_short),
505 489 ('Top', ctypes.c_short),
506 490 ('Right', ctypes.c_short),
507 491 ('Bottom', ctypes.c_short)]
508 492
509 493 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
510 494 _fields_ = [('dwSize', _COORD),
511 495 ('dwCursorPosition', _COORD),
512 496 ('wAttributes', _WORD),
513 497 ('srWindow', _SMALL_RECT),
514 498 ('dwMaximumWindowSize', _COORD)]
515 499
516 500 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
517 501 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12
518 502
519 503 _FOREGROUND_BLUE = 0x0001
520 504 _FOREGROUND_GREEN = 0x0002
521 505 _FOREGROUND_RED = 0x0004
522 506 _FOREGROUND_INTENSITY = 0x0008
523 507
524 508 _BACKGROUND_BLUE = 0x0010
525 509 _BACKGROUND_GREEN = 0x0020
526 510 _BACKGROUND_RED = 0x0040
527 511 _BACKGROUND_INTENSITY = 0x0080
528 512
529 513 _COMMON_LVB_REVERSE_VIDEO = 0x4000
530 514 _COMMON_LVB_UNDERSCORE = 0x8000
531 515
532 516 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
533 517 w32effects = {
534 518 'none': -1,
535 519 'black': 0,
536 520 'red': _FOREGROUND_RED,
537 521 'green': _FOREGROUND_GREEN,
538 522 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
539 523 'blue': _FOREGROUND_BLUE,
540 524 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
541 525 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
542 526 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
543 527 'bold': _FOREGROUND_INTENSITY,
544 528 'black_background': 0x100, # unused value > 0x0f
545 529 'red_background': _BACKGROUND_RED,
546 530 'green_background': _BACKGROUND_GREEN,
547 531 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
548 532 'blue_background': _BACKGROUND_BLUE,
549 533 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
550 534 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
551 535 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
552 536 _BACKGROUND_BLUE),
553 537 'bold_background': _BACKGROUND_INTENSITY,
554 538 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
555 539 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
556 540 }
557 541
558 542 passthrough = set([_FOREGROUND_INTENSITY,
559 543 _BACKGROUND_INTENSITY,
560 544 _COMMON_LVB_UNDERSCORE,
561 545 _COMMON_LVB_REVERSE_VIDEO])
562 546
563 547 stdout = _kernel32.GetStdHandle(
564 548 _STD_OUTPUT_HANDLE) # don't close the handle returned
565 549 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
566 550 w32effects = None
567 551 else:
568 552 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
569 553 if not _kernel32.GetConsoleScreenBufferInfo(
570 554 stdout, ctypes.byref(csbi)):
571 555 # stdout may not support GetConsoleScreenBufferInfo()
572 556 # when called from subprocess or redirected
573 557 w32effects = None
574 558 else:
575 559 origattr = csbi.wAttributes
576 560 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
577 561 re.MULTILINE | re.DOTALL)
578 562
579 563 def win32print(text, orig, **opts):
580 564 label = opts.get('label', '')
581 565 attr = origattr
582 566
583 567 def mapcolor(val, attr):
584 568 if val == -1:
585 569 return origattr
586 570 elif val in passthrough:
587 571 return attr | val
588 572 elif val > 0x0f:
589 573 return (val & 0x70) | (attr & 0x8f)
590 574 else:
591 575 return (val & 0x07) | (attr & 0xf8)
592 576
593 577 # determine console attributes based on labels
594 578 for l in label.split():
595 579 style = color._styles.get(l, '')
596 580 for effect in style.split():
597 581 try:
598 582 attr = mapcolor(w32effects[effect], attr)
599 583 except KeyError:
600 584 # w32effects could not have certain attributes so we skip
601 585 # them if not found
602 586 pass
603 587 # hack to ensure regexp finds data
604 588 if not text.startswith('\033['):
605 589 text = '\033[m' + text
606 590
607 591 # Look for ANSI-like codes embedded in text
608 592 m = re.match(ansire, text)
609 593
610 594 try:
611 595 while m:
612 596 for sattr in m.group(1).split(';'):
613 597 if sattr:
614 598 attr = mapcolor(int(sattr), attr)
615 599 _kernel32.SetConsoleTextAttribute(stdout, attr)
616 600 orig(m.group(2), **opts)
617 601 m = re.match(ansire, m.group(3))
618 602 finally:
619 603 # Explicitly reset original attributes
620 604 _kernel32.SetConsoleTextAttribute(stdout, origattr)
@@ -1,121 +1,139 b''
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 from .i18n import _
11
10 12 try:
11 13 import curses
12 14 # Mapping from effect name to terminfo attribute name (or raw code) or
13 15 # color number. This will also force-load the curses module.
14 16 _terminfo_params = {'none': (True, 'sgr0', ''),
15 17 'standout': (True, 'smso', ''),
16 18 'underline': (True, 'smul', ''),
17 19 'reverse': (True, 'rev', ''),
18 20 'inverse': (True, 'rev', ''),
19 21 'blink': (True, 'blink', ''),
20 22 'dim': (True, 'dim', ''),
21 23 'bold': (True, 'bold', ''),
22 24 'invisible': (True, 'invis', ''),
23 25 'italic': (True, 'sitm', ''),
24 26 'black': (False, curses.COLOR_BLACK, ''),
25 27 'red': (False, curses.COLOR_RED, ''),
26 28 'green': (False, curses.COLOR_GREEN, ''),
27 29 'yellow': (False, curses.COLOR_YELLOW, ''),
28 30 'blue': (False, curses.COLOR_BLUE, ''),
29 31 'magenta': (False, curses.COLOR_MAGENTA, ''),
30 32 'cyan': (False, curses.COLOR_CYAN, ''),
31 33 'white': (False, curses.COLOR_WHITE, '')}
32 34 except ImportError:
33 35 curses = None
34 36 _terminfo_params = {}
35 37
36 38 # start and stop parameters for effects
37 39 _effects = {'none': 0,
38 40 'black': 30,
39 41 'red': 31,
40 42 'green': 32,
41 43 'yellow': 33,
42 44 'blue': 34,
43 45 'magenta': 35,
44 46 'cyan': 36,
45 47 'white': 37,
46 48 'bold': 1,
47 49 'italic': 3,
48 50 'underline': 4,
49 51 'inverse': 7,
50 52 'dim': 2,
51 53 'black_background': 40,
52 54 'red_background': 41,
53 55 'green_background': 42,
54 56 'yellow_background': 43,
55 57 'blue_background': 44,
56 58 'purple_background': 45,
57 59 'cyan_background': 46,
58 60 'white_background': 47}
59 61
60 62 _styles = {'grep.match': 'red bold',
61 63 'grep.linenumber': 'green',
62 64 'grep.rev': 'green',
63 65 'grep.change': 'green',
64 66 'grep.sep': 'cyan',
65 67 'grep.filename': 'magenta',
66 68 'grep.user': 'magenta',
67 69 'grep.date': 'magenta',
68 70 'bookmarks.active': 'green',
69 71 'branches.active': 'none',
70 72 'branches.closed': 'black bold',
71 73 'branches.current': 'green',
72 74 'branches.inactive': 'none',
73 75 'diff.changed': 'white',
74 76 'diff.deleted': 'red',
75 77 'diff.diffline': 'bold',
76 78 'diff.extended': 'cyan bold',
77 79 'diff.file_a': 'red bold',
78 80 'diff.file_b': 'green bold',
79 81 'diff.hunk': 'magenta',
80 82 'diff.inserted': 'green',
81 83 'diff.tab': '',
82 84 'diff.trailingwhitespace': 'bold red_background',
83 85 'changeset.public' : '',
84 86 'changeset.draft' : '',
85 87 'changeset.secret' : '',
86 88 'diffstat.deleted': 'red',
87 89 'diffstat.inserted': 'green',
88 90 'histedit.remaining': 'red bold',
89 91 'ui.prompt': 'yellow',
90 92 'log.changeset': 'yellow',
91 93 'patchbomb.finalsummary': '',
92 94 'patchbomb.from': 'magenta',
93 95 'patchbomb.to': 'cyan',
94 96 'patchbomb.subject': 'green',
95 97 'patchbomb.diffstats': '',
96 98 'rebase.rebased': 'blue',
97 99 'rebase.remaining': 'red bold',
98 100 'resolve.resolved': 'green bold',
99 101 'resolve.unresolved': 'red bold',
100 102 'shelve.age': 'cyan',
101 103 'shelve.newest': 'green bold',
102 104 'shelve.name': 'blue bold',
103 105 'status.added': 'green bold',
104 106 'status.clean': 'none',
105 107 'status.copied': 'none',
106 108 'status.deleted': 'cyan bold underline',
107 109 'status.ignored': 'black bold',
108 110 'status.modified': 'blue bold',
109 111 'status.removed': 'red bold',
110 112 'status.unknown': 'magenta bold underline',
111 113 'tags.normal': 'green',
112 114 'tags.local': 'black bold'}
113 115
114 116 def loadcolortable(ui, extname, colortable):
115 117 _styles.update(colortable)
116 118
119 def configstyles(ui):
120 for status, cfgeffects in ui.configitems('color'):
121 if '.' not in status or status.startswith(('color.', 'terminfo.')):
122 continue
123 cfgeffects = ui.configlist('color', status)
124 if cfgeffects:
125 good = []
126 for e in cfgeffects:
127 if valideffect(e):
128 good.append(e)
129 else:
130 ui.warn(_("ignoring unknown color/effect %r "
131 "(configured in color.%s)\n")
132 % (e, status))
133 _styles[status] = ' '.join(good)
134
117 135 def valideffect(effect):
118 136 'Determine if the effect is valid or not.'
119 137 return ((not _terminfo_params and effect in _effects)
120 138 or (effect in _terminfo_params
121 139 or effect[:-11] in _terminfo_params))
General Comments 0
You need to be logged in to leave comments. Login now