hgext/color.py
changeset 10870 a4944b430417
parent 10869 2d57be56c500
child 10871 3f30190781a3
equal deleted inserted replaced
10869:2d57be56c500 10870:a4944b430417
    59 
    59 
    60   resolve.unresolved = red bold
    60   resolve.unresolved = red bold
    61   resolve.resolved = green bold
    61   resolve.resolved = green bold
    62 
    62 
    63   bookmarks.current = green
    63   bookmarks.current = green
       
    64 
       
    65 The color extension will try to detect whether to use ANSI codes or
       
    66 Win32 console APIs, unless it is made explicit::
       
    67 
       
    68   [color]
       
    69   mode = ansi
       
    70 
       
    71 Any value other than 'ansi', 'win32', or 'auto' will disable color.
       
    72 
    64 '''
    73 '''
    65 
    74 
    66 import os, sys
    75 import os, sys
    67 
    76 
    68 from mercurial import commands, dispatch, extensions
    77 from mercurial import commands, dispatch, extensions
   148     global _buffers
   157     global _buffers
   149     if labeled:
   158     if labeled:
   150         return ''.join(style(a, label) for a, label in _buffers.pop())
   159         return ''.join(style(a, label) for a, label in _buffers.pop())
   151     return ''.join(a for a, label in _buffers.pop())
   160     return ''.join(a for a, label in _buffers.pop())
   152 
   161 
       
   162 mode = 'ansi'
   153 def write(orig, *args, **opts):
   163 def write(orig, *args, **opts):
   154     label = opts.get('label', '')
   164     label = opts.get('label', '')
   155     global _buffers
   165     global _buffers
   156     if _buffers:
   166     if _buffers:
   157         _buffers[-1].extend([(str(a), label) for a in args])
   167         _buffers[-1].extend([(str(a), label) for a in args])
       
   168     elif mode == 'win32':
       
   169         for a in args:
       
   170             win32print(a, orig, **opts)
   158     else:
   171     else:
   159         return orig(*[style(str(a), label) for a in args], **opts)
   172         return orig(*[style(str(a), label) for a in args], **opts)
   160 
   173 
   161 def write_err(orig, *args, **opts):
   174 def write_err(orig, *args, **opts):
   162     label = opts.get('label', '')
   175     label = opts.get('label', '')
   163     return orig(*[style(str(a), label) for a in args], **opts)
   176     if mode == 'win32':
       
   177         for a in args:
       
   178             win32print(a, orig, **opts)
       
   179     else:
       
   180         return orig(*[style(str(a), label) for a in args], **opts)
   164 
   181 
   165 def uisetup(ui):
   182 def uisetup(ui):
       
   183     global mode
       
   184     mode = ui.config('color', 'mode', 'auto')
       
   185     if mode == 'auto':
       
   186         if os.name == 'nt' and 'TERM' not in os.environ:
       
   187             # looks line a cmd.exe console, use win32 API or nothing
       
   188             mode = w32effects and 'win32' or 'none'
       
   189         else:
       
   190             mode = 'ansi'
       
   191     if mode == 'win32':
       
   192         if w32effects is None:
       
   193             # only warn if color.mode is explicitly set to win32
       
   194             ui.warn(_('win32console not found, please install pywin32\n'))
       
   195             return
       
   196         _effects.update(w32effects)
       
   197     elif mode != 'ansi':
       
   198         return
   166     def colorcmd(orig, ui_, opts, cmd, cmdfunc):
   199     def colorcmd(orig, ui_, opts, cmd, cmdfunc):
   167         if (opts['color'] == 'always' or
   200         if (opts['color'] == 'always' or
   168             (opts['color'] == 'auto' and (os.environ.get('TERM') != 'dumb'
   201             (opts['color'] == 'auto' and (os.environ.get('TERM') != 'dumb'
   169                                           and sys.__stdout__.isatty()))):
   202                                           and sys.__stdout__.isatty()))):
   170             global _buffers
   203             global _buffers
   178         return orig(ui_, opts, cmd, cmdfunc)
   211         return orig(ui_, opts, cmd, cmdfunc)
   179     extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
   212     extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
   180 
   213 
   181 commands.globalopts.append(('', 'color', 'auto',
   214 commands.globalopts.append(('', 'color', 'auto',
   182                             _("when to colorize (always, auto, or never)")))
   215                             _("when to colorize (always, auto, or never)")))
       
   216 
       
   217 try:
       
   218     import re
       
   219     from win32console import *
       
   220 
       
   221     # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
       
   222     w32effects = {
       
   223         'none': 0,
       
   224         'black': 0,
       
   225         'red': FOREGROUND_RED,
       
   226         'green': FOREGROUND_GREEN,
       
   227         'yellow': FOREGROUND_RED | FOREGROUND_GREEN,
       
   228         'blue': FOREGROUND_BLUE,
       
   229         'magenta': FOREGROUND_BLUE | FOREGROUND_RED,
       
   230         'cyan': FOREGROUND_BLUE | FOREGROUND_GREEN,
       
   231         'white': FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
       
   232         'bold': FOREGROUND_INTENSITY,
       
   233         'black_background': 0,
       
   234         'red_background': BACKGROUND_RED,
       
   235         'green_background': BACKGROUND_GREEN,
       
   236         'blue_background': BACKGROUND_BLUE,
       
   237         'cyan_background': BACKGROUND_BLUE | BACKGROUND_GREEN,
       
   238         'bold_background': FOREGROUND_INTENSITY,
       
   239         'underline': COMMON_LVB_UNDERSCORE,     # double-byte charsets only
       
   240         'inverse': COMMON_LVB_REVERSE_VIDEO,    # double-byte charsets only
       
   241     }
       
   242 
       
   243     stdout = GetStdHandle(STD_OUTPUT_HANDLE)
       
   244     origattr = stdout.GetConsoleScreenBufferInfo()['Attributes']
       
   245     ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)', re.MULTILINE | re.DOTALL)
       
   246 
       
   247     def win32print(text, orig, **opts):
       
   248         label = opts.get('label', '')
       
   249         attr = 0
       
   250 
       
   251         # determine console attributes based on labels
       
   252         for l in label.split():
       
   253             style = _styles.get(l, '')
       
   254             for effect in style.split():
       
   255                 attr |= w32effects[effect]
       
   256 
       
   257         # hack to ensure regexp finds data
       
   258         if not text.startswith('\033['):
       
   259             text = '\033[m' + text
       
   260 
       
   261         # Look for ANSI-like codes embedded in text
       
   262         m = re.match(ansire, text)
       
   263         while m:
       
   264             for sattr in m.group(1).split(';'):
       
   265                 if sattr:
       
   266                     val = int(sattr)
       
   267                     attr = val and attr|val or 0
       
   268             stdout.SetConsoleTextAttribute(attr or origattr)
       
   269             orig(m.group(2), **opts)
       
   270             m = re.match(ansire, m.group(3))
       
   271 
       
   272         # Explicity reset original attributes
       
   273         stdout.SetConsoleTextAttribute(origattr)
       
   274 
       
   275 except ImportError:
       
   276     w32effects = None