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