comparison mercurial/color.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents a91615b71679
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
14 from . import ( 14 from . import (
15 encoding, 15 encoding,
16 pycompat, 16 pycompat,
17 ) 17 )
18 18
19 from .utils import ( 19 from .utils import stringutil
20 stringutil,
21 )
22 20
23 try: 21 try:
24 import curses 22 import curses
23
25 # Mapping from effect name to terminfo attribute name (or raw code) or 24 # Mapping from effect name to terminfo attribute name (or raw code) or
26 # color number. This will also force-load the curses module. 25 # color number. This will also force-load the curses module.
27 _baseterminfoparams = { 26 _baseterminfoparams = {
28 'none': (True, 'sgr0', ''), 27 'none': (True, 'sgr0', ''),
29 'standout': (True, 'smso', ''), 28 'standout': (True, 'smso', ''),
70 'yellow_background': 43, 69 'yellow_background': 43,
71 'blue_background': 44, 70 'blue_background': 44,
72 'purple_background': 45, 71 'purple_background': 45,
73 'cyan_background': 46, 72 'cyan_background': 46,
74 'white_background': 47, 73 'white_background': 47,
75 } 74 }
76 75
77 _defaultstyles = { 76 _defaultstyles = {
78 'grep.match': 'red bold', 77 'grep.match': 'red bold',
79 'grep.linenumber': 'green', 78 'grep.linenumber': 'green',
80 'grep.rev': 'blue', 79 'grep.rev': 'blue',
145 'status.unknown': 'magenta bold underline', 144 'status.unknown': 'magenta bold underline',
146 'tags.normal': 'green', 145 'tags.normal': 'green',
147 'tags.local': 'black bold', 146 'tags.local': 'black bold',
148 } 147 }
149 148
149
150 def loadcolortable(ui, extname, colortable): 150 def loadcolortable(ui, extname, colortable):
151 _defaultstyles.update(colortable) 151 _defaultstyles.update(colortable)
152
152 153
153 def _terminfosetup(ui, mode, formatted): 154 def _terminfosetup(ui, mode, formatted):
154 '''Initialize terminfo data and the terminal if we're in terminfo mode.''' 155 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
155 156
156 # If we failed to load curses, we go ahead and return. 157 # If we failed to load curses, we go ahead and return.
184 del ui._terminfoparams[key] 185 del ui._terminfoparams[key]
185 if not curses.tigetstr(r'setaf') or not curses.tigetstr(r'setab'): 186 if not curses.tigetstr(r'setaf') or not curses.tigetstr(r'setab'):
186 # Only warn about missing terminfo entries if we explicitly asked for 187 # Only warn about missing terminfo entries if we explicitly asked for
187 # terminfo mode and we're in a formatted terminal. 188 # terminfo mode and we're in a formatted terminal.
188 if mode == "terminfo" and formatted: 189 if mode == "terminfo" and formatted:
189 ui.warn(_("no terminfo entry for setab/setaf: reverting to " 190 ui.warn(
190 "ECMA-48 color\n")) 191 _(
192 "no terminfo entry for setab/setaf: reverting to "
193 "ECMA-48 color\n"
194 )
195 )
191 ui._terminfoparams.clear() 196 ui._terminfoparams.clear()
197
192 198
193 def setup(ui): 199 def setup(ui):
194 """configure color on a ui 200 """configure color on a ui
195 201
196 That function both set the colormode for the ui object and read 202 That function both set the colormode for the ui object and read
198 mode = _modesetup(ui) 204 mode = _modesetup(ui)
199 ui._colormode = mode 205 ui._colormode = mode
200 if mode and mode != 'debug': 206 if mode and mode != 'debug':
201 configstyles(ui) 207 configstyles(ui)
202 208
209
203 def _modesetup(ui): 210 def _modesetup(ui):
204 if ui.plain('color'): 211 if ui.plain('color'):
205 return None 212 return None
206 config = ui.config('ui', 'color') 213 config = ui.config('ui', 'color')
207 if config == 'debug': 214 if config == 'debug':
208 return 'debug' 215 return 'debug'
209 216
210 auto = (config == 'auto') 217 auto = config == 'auto'
211 always = False 218 always = False
212 if not auto and stringutil.parsebool(config): 219 if not auto and stringutil.parsebool(config):
213 # We want the config to behave like a boolean, "on" is actually auto, 220 # We want the config to behave like a boolean, "on" is actually auto,
214 # but "always" value is treated as a special case to reduce confusion. 221 # but "always" value is treated as a special case to reduce confusion.
215 if ui.configsource('ui', 'color') == '--color' or config == 'always': 222 if ui.configsource('ui', 'color') == '--color' or config == 'always':
218 auto = True 225 auto = True
219 226
220 if not always and not auto: 227 if not always and not auto:
221 return None 228 return None
222 229
223 formatted = (always or (encoding.environ.get('TERM') != 'dumb' 230 formatted = always or (
224 and ui.formatted())) 231 encoding.environ.get('TERM') != 'dumb' and ui.formatted()
232 )
225 233
226 mode = ui.config('color', 'mode') 234 mode = ui.config('color', 'mode')
227 235
228 # If pager is active, color.pagermode overrides color.mode. 236 # If pager is active, color.pagermode overrides color.mode.
229 if getattr(ui, 'pageractive', False): 237 if getattr(ui, 'pageractive', False):
283 291
284 if always or (auto and formatted): 292 if always or (auto and formatted):
285 return realmode 293 return realmode
286 return None 294 return None
287 295
296
288 def configstyles(ui): 297 def configstyles(ui):
289 ui._styles.update(_defaultstyles) 298 ui._styles.update(_defaultstyles)
290 for status, cfgeffects in ui.configitems('color'): 299 for status, cfgeffects in ui.configitems('color'):
291 if '.' not in status or status.startswith(('color.', 'terminfo.')): 300 if '.' not in status or status.startswith(('color.', 'terminfo.')):
292 continue 301 continue
295 good = [] 304 good = []
296 for e in cfgeffects: 305 for e in cfgeffects:
297 if valideffect(ui, e): 306 if valideffect(ui, e):
298 good.append(e) 307 good.append(e)
299 else: 308 else:
300 ui.warn(_("ignoring unknown color/effect %s " 309 ui.warn(
301 "(configured in color.%s)\n") 310 _(
302 % (stringutil.pprint(e), status)) 311 "ignoring unknown color/effect %s "
312 "(configured in color.%s)\n"
313 )
314 % (stringutil.pprint(e), status)
315 )
303 ui._styles[status] = ' '.join(good) 316 ui._styles[status] = ' '.join(good)
317
304 318
305 def _activeeffects(ui): 319 def _activeeffects(ui):
306 '''Return the effects map for the color mode set on the ui.''' 320 '''Return the effects map for the color mode set on the ui.'''
307 if ui._colormode == 'win32': 321 if ui._colormode == 'win32':
308 return w32effects 322 return w32effects
309 elif ui._colormode is not None: 323 elif ui._colormode is not None:
310 return _effects 324 return _effects
311 return {} 325 return {}
312 326
327
313 def valideffect(ui, effect): 328 def valideffect(ui, effect):
314 'Determine if the effect is valid or not.' 329 'Determine if the effect is valid or not.'
315 return ((not ui._terminfoparams and effect in _activeeffects(ui)) 330 return (not ui._terminfoparams and effect in _activeeffects(ui)) or (
316 or (effect in ui._terminfoparams 331 effect in ui._terminfoparams or effect[:-11] in ui._terminfoparams
317 or effect[:-11] in ui._terminfoparams)) 332 )
333
318 334
319 def _effect_str(ui, effect): 335 def _effect_str(ui, effect):
320 '''Helper function for render_effects().''' 336 '''Helper function for render_effects().'''
321 337
322 bg = False 338 bg = False
335 elif bg: 351 elif bg:
336 return curses.tparm(curses.tigetstr(r'setab'), val) 352 return curses.tparm(curses.tigetstr(r'setab'), val)
337 else: 353 else:
338 return curses.tparm(curses.tigetstr(r'setaf'), val) 354 return curses.tparm(curses.tigetstr(r'setaf'), val)
339 355
356
340 def _mergeeffects(text, start, stop): 357 def _mergeeffects(text, start, stop):
341 """Insert start sequence at every occurrence of stop sequence 358 """Insert start sequence at every occurrence of stop sequence
342 359
343 >>> s = _mergeeffects(b'cyan', b'[C]', b'|') 360 >>> s = _mergeeffects(b'cyan', b'[C]', b'|')
344 >>> s = _mergeeffects(s + b'yellow', b'[Y]', b'|') 361 >>> s = _mergeeffects(s + b'yellow', b'[Y]', b'|')
352 if not t: 369 if not t:
353 continue 370 continue
354 parts.extend([start, t, stop]) 371 parts.extend([start, t, stop])
355 return ''.join(parts) 372 return ''.join(parts)
356 373
374
357 def _render_effects(ui, text, effects): 375 def _render_effects(ui, text, effects):
358 'Wrap text in commands to turn on each effect.' 376 'Wrap text in commands to turn on each effect.'
359 if not text: 377 if not text:
360 return text 378 return text
361 if ui._terminfoparams: 379 if ui._terminfoparams:
362 start = ''.join(_effect_str(ui, effect) 380 start = ''.join(
363 for effect in ['none'] + effects.split()) 381 _effect_str(ui, effect) for effect in ['none'] + effects.split()
382 )
364 stop = _effect_str(ui, 'none') 383 stop = _effect_str(ui, 'none')
365 else: 384 else:
366 activeeffects = _activeeffects(ui) 385 activeeffects = _activeeffects(ui)
367 start = [pycompat.bytestr(activeeffects[e]) 386 start = [
368 for e in ['none'] + effects.split()] 387 pycompat.bytestr(activeeffects[e])
388 for e in ['none'] + effects.split()
389 ]
369 start = '\033[' + ';'.join(start) + 'm' 390 start = '\033[' + ';'.join(start) + 'm'
370 stop = '\033[' + pycompat.bytestr(activeeffects['none']) + 'm' 391 stop = '\033[' + pycompat.bytestr(activeeffects['none']) + 'm'
371 return _mergeeffects(text, start, stop) 392 return _mergeeffects(text, start, stop)
372 393
394
373 _ansieffectre = re.compile(br'\x1b\[[0-9;]*m') 395 _ansieffectre = re.compile(br'\x1b\[[0-9;]*m')
396
374 397
375 def stripeffects(text): 398 def stripeffects(text):
376 """Strip ANSI control codes which could be inserted by colorlabel()""" 399 """Strip ANSI control codes which could be inserted by colorlabel()"""
377 return _ansieffectre.sub('', text) 400 return _ansieffectre.sub('', text)
401
378 402
379 def colorlabel(ui, msg, label): 403 def colorlabel(ui, msg, label):
380 """add color control code according to the mode""" 404 """add color control code according to the mode"""
381 if ui._colormode == 'debug': 405 if ui._colormode == 'debug':
382 if label and msg: 406 if label and msg:
392 effects.append(s) 416 effects.append(s)
393 elif valideffect(ui, l): 417 elif valideffect(ui, l):
394 effects.append(l) 418 effects.append(l)
395 effects = ' '.join(effects) 419 effects = ' '.join(effects)
396 if effects: 420 if effects:
397 msg = '\n'.join([_render_effects(ui, line, effects) 421 msg = '\n'.join(
398 for line in msg.split('\n')]) 422 [_render_effects(ui, line, effects) for line in msg.split('\n')]
423 )
399 return msg 424 return msg
425
400 426
401 w32effects = None 427 w32effects = None
402 if pycompat.iswindows: 428 if pycompat.iswindows:
403 import ctypes 429 import ctypes
404 430
407 _WORD = ctypes.c_ushort 433 _WORD = ctypes.c_ushort
408 434
409 _INVALID_HANDLE_VALUE = -1 435 _INVALID_HANDLE_VALUE = -1
410 436
411 class _COORD(ctypes.Structure): 437 class _COORD(ctypes.Structure):
412 _fields_ = [(r'X', ctypes.c_short), 438 _fields_ = [(r'X', ctypes.c_short), (r'Y', ctypes.c_short)]
413 (r'Y', ctypes.c_short)]
414 439
415 class _SMALL_RECT(ctypes.Structure): 440 class _SMALL_RECT(ctypes.Structure):
416 _fields_ = [(r'Left', ctypes.c_short), 441 _fields_ = [
417 (r'Top', ctypes.c_short), 442 (r'Left', ctypes.c_short),
418 (r'Right', ctypes.c_short), 443 (r'Top', ctypes.c_short),
419 (r'Bottom', ctypes.c_short)] 444 (r'Right', ctypes.c_short),
445 (r'Bottom', ctypes.c_short),
446 ]
420 447
421 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure): 448 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
422 _fields_ = [(r'dwSize', _COORD), 449 _fields_ = [
423 (r'dwCursorPosition', _COORD), 450 (r'dwSize', _COORD),
424 (r'wAttributes', _WORD), 451 (r'dwCursorPosition', _COORD),
425 (r'srWindow', _SMALL_RECT), 452 (r'wAttributes', _WORD),
426 (r'dwMaximumWindowSize', _COORD)] 453 (r'srWindow', _SMALL_RECT),
427 454 (r'dwMaximumWindowSize', _COORD),
428 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11 455 ]
429 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12 456
457 _STD_OUTPUT_HANDLE = 0xFFFFFFF5 # (DWORD)-11
458 _STD_ERROR_HANDLE = 0xFFFFFFF4 # (DWORD)-12
430 459
431 _FOREGROUND_BLUE = 0x0001 460 _FOREGROUND_BLUE = 0x0001
432 _FOREGROUND_GREEN = 0x0002 461 _FOREGROUND_GREEN = 0x0002
433 _FOREGROUND_RED = 0x0004 462 _FOREGROUND_RED = 0x0004
434 _FOREGROUND_INTENSITY = 0x0008 463 _FOREGROUND_INTENSITY = 0x0008
451 'blue': _FOREGROUND_BLUE, 480 'blue': _FOREGROUND_BLUE,
452 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED, 481 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
453 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN, 482 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
454 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE, 483 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
455 'bold': _FOREGROUND_INTENSITY, 484 'bold': _FOREGROUND_INTENSITY,
456 'black_background': 0x100, # unused value > 0x0f 485 'black_background': 0x100, # unused value > 0x0f
457 'red_background': _BACKGROUND_RED, 486 'red_background': _BACKGROUND_RED,
458 'green_background': _BACKGROUND_GREEN, 487 'green_background': _BACKGROUND_GREEN,
459 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN, 488 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
460 'blue_background': _BACKGROUND_BLUE, 489 'blue_background': _BACKGROUND_BLUE,
461 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED, 490 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
462 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN, 491 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
463 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN | 492 'white_background': (
464 _BACKGROUND_BLUE), 493 _BACKGROUND_RED | _BACKGROUND_GREEN | _BACKGROUND_BLUE
494 ),
465 'bold_background': _BACKGROUND_INTENSITY, 495 'bold_background': _BACKGROUND_INTENSITY,
466 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only 496 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
467 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only 497 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
468 } 498 }
469 499
470 passthrough = {_FOREGROUND_INTENSITY, 500 passthrough = {
471 _BACKGROUND_INTENSITY, 501 _FOREGROUND_INTENSITY,
472 _COMMON_LVB_UNDERSCORE, 502 _BACKGROUND_INTENSITY,
473 _COMMON_LVB_REVERSE_VIDEO} 503 _COMMON_LVB_UNDERSCORE,
504 _COMMON_LVB_REVERSE_VIDEO,
505 }
474 506
475 stdout = _kernel32.GetStdHandle( 507 stdout = _kernel32.GetStdHandle(
476 _STD_OUTPUT_HANDLE) # don't close the handle returned 508 _STD_OUTPUT_HANDLE
509 ) # don't close the handle returned
477 if stdout is None or stdout == _INVALID_HANDLE_VALUE: 510 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
478 w32effects = None 511 w32effects = None
479 else: 512 else:
480 csbi = _CONSOLE_SCREEN_BUFFER_INFO() 513 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
481 if not _kernel32.GetConsoleScreenBufferInfo( 514 if not _kernel32.GetConsoleScreenBufferInfo(stdout, ctypes.byref(csbi)):
482 stdout, ctypes.byref(csbi)):
483 # stdout may not support GetConsoleScreenBufferInfo() 515 # stdout may not support GetConsoleScreenBufferInfo()
484 # when called from subprocess or redirected 516 # when called from subprocess or redirected
485 w32effects = None 517 w32effects = None
486 else: 518 else:
487 origattr = csbi.wAttributes 519 origattr = csbi.wAttributes
488 ansire = re.compile(br'\033\[([^m]*)m([^\033]*)(.*)', 520 ansire = re.compile(
489 re.MULTILINE | re.DOTALL) 521 br'\033\[([^m]*)m([^\033]*)(.*)', re.MULTILINE | re.DOTALL
522 )
490 523
491 def win32print(ui, writefunc, text, **opts): 524 def win32print(ui, writefunc, text, **opts):
492 label = opts.get(r'label', '') 525 label = opts.get(r'label', '')
493 attr = origattr 526 attr = origattr
494 527
495 def mapcolor(val, attr): 528 def mapcolor(val, attr):
496 if val == -1: 529 if val == -1:
497 return origattr 530 return origattr
498 elif val in passthrough: 531 elif val in passthrough:
499 return attr | val 532 return attr | val
500 elif val > 0x0f: 533 elif val > 0x0F:
501 return (val & 0x70) | (attr & 0x8f) 534 return (val & 0x70) | (attr & 0x8F)
502 else: 535 else:
503 return (val & 0x07) | (attr & 0xf8) 536 return (val & 0x07) | (attr & 0xF8)
504 537
505 # determine console attributes based on labels 538 # determine console attributes based on labels
506 for l in label.split(): 539 for l in label.split():
507 style = ui._styles.get(l, '') 540 style = ui._styles.get(l, '')
508 for effect in style.split(): 541 for effect in style.split():