##// END OF EJS Templates
color: clarify name of an argument of 'win32print'...
Pierre-Yves David -
r31088:75c4aafe default
parent child Browse files
Show More
@@ -1,332 +1,332 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 10 from .i18n import _
11 11
12 12 from . import pycompat
13 13
14 14 try:
15 15 import curses
16 16 # Mapping from effect name to terminfo attribute name (or raw code) or
17 17 # color number. This will also force-load the curses module.
18 18 _terminfo_params = {'none': (True, 'sgr0', ''),
19 19 'standout': (True, 'smso', ''),
20 20 'underline': (True, 'smul', ''),
21 21 'reverse': (True, 'rev', ''),
22 22 'inverse': (True, 'rev', ''),
23 23 'blink': (True, 'blink', ''),
24 24 'dim': (True, 'dim', ''),
25 25 'bold': (True, 'bold', ''),
26 26 'invisible': (True, 'invis', ''),
27 27 'italic': (True, 'sitm', ''),
28 28 'black': (False, curses.COLOR_BLACK, ''),
29 29 'red': (False, curses.COLOR_RED, ''),
30 30 'green': (False, curses.COLOR_GREEN, ''),
31 31 'yellow': (False, curses.COLOR_YELLOW, ''),
32 32 'blue': (False, curses.COLOR_BLUE, ''),
33 33 'magenta': (False, curses.COLOR_MAGENTA, ''),
34 34 'cyan': (False, curses.COLOR_CYAN, ''),
35 35 'white': (False, curses.COLOR_WHITE, '')}
36 36 except ImportError:
37 37 curses = None
38 38 _terminfo_params = {}
39 39
40 40 # start and stop parameters for effects
41 41 _effects = {'none': 0,
42 42 'black': 30,
43 43 'red': 31,
44 44 'green': 32,
45 45 'yellow': 33,
46 46 'blue': 34,
47 47 'magenta': 35,
48 48 'cyan': 36,
49 49 'white': 37,
50 50 'bold': 1,
51 51 'italic': 3,
52 52 'underline': 4,
53 53 'inverse': 7,
54 54 'dim': 2,
55 55 'black_background': 40,
56 56 'red_background': 41,
57 57 'green_background': 42,
58 58 'yellow_background': 43,
59 59 'blue_background': 44,
60 60 'purple_background': 45,
61 61 'cyan_background': 46,
62 62 'white_background': 47}
63 63
64 64 _styles = {'grep.match': 'red bold',
65 65 'grep.linenumber': 'green',
66 66 'grep.rev': 'green',
67 67 'grep.change': 'green',
68 68 'grep.sep': 'cyan',
69 69 'grep.filename': 'magenta',
70 70 'grep.user': 'magenta',
71 71 'grep.date': 'magenta',
72 72 'bookmarks.active': 'green',
73 73 'branches.active': 'none',
74 74 'branches.closed': 'black bold',
75 75 'branches.current': 'green',
76 76 'branches.inactive': 'none',
77 77 'diff.changed': 'white',
78 78 'diff.deleted': 'red',
79 79 'diff.diffline': 'bold',
80 80 'diff.extended': 'cyan bold',
81 81 'diff.file_a': 'red bold',
82 82 'diff.file_b': 'green bold',
83 83 'diff.hunk': 'magenta',
84 84 'diff.inserted': 'green',
85 85 'diff.tab': '',
86 86 'diff.trailingwhitespace': 'bold red_background',
87 87 'changeset.public' : '',
88 88 'changeset.draft' : '',
89 89 'changeset.secret' : '',
90 90 'diffstat.deleted': 'red',
91 91 'diffstat.inserted': 'green',
92 92 'histedit.remaining': 'red bold',
93 93 'ui.prompt': 'yellow',
94 94 'log.changeset': 'yellow',
95 95 'patchbomb.finalsummary': '',
96 96 'patchbomb.from': 'magenta',
97 97 'patchbomb.to': 'cyan',
98 98 'patchbomb.subject': 'green',
99 99 'patchbomb.diffstats': '',
100 100 'rebase.rebased': 'blue',
101 101 'rebase.remaining': 'red bold',
102 102 'resolve.resolved': 'green bold',
103 103 'resolve.unresolved': 'red bold',
104 104 'shelve.age': 'cyan',
105 105 'shelve.newest': 'green bold',
106 106 'shelve.name': 'blue bold',
107 107 'status.added': 'green bold',
108 108 'status.clean': 'none',
109 109 'status.copied': 'none',
110 110 'status.deleted': 'cyan bold underline',
111 111 'status.ignored': 'black bold',
112 112 'status.modified': 'blue bold',
113 113 'status.removed': 'red bold',
114 114 'status.unknown': 'magenta bold underline',
115 115 'tags.normal': 'green',
116 116 'tags.local': 'black bold'}
117 117
118 118 def loadcolortable(ui, extname, colortable):
119 119 _styles.update(colortable)
120 120
121 121 def configstyles(ui):
122 122 for status, cfgeffects in ui.configitems('color'):
123 123 if '.' not in status or status.startswith(('color.', 'terminfo.')):
124 124 continue
125 125 cfgeffects = ui.configlist('color', status)
126 126 if cfgeffects:
127 127 good = []
128 128 for e in cfgeffects:
129 129 if valideffect(e):
130 130 good.append(e)
131 131 else:
132 132 ui.warn(_("ignoring unknown color/effect %r "
133 133 "(configured in color.%s)\n")
134 134 % (e, status))
135 135 _styles[status] = ' '.join(good)
136 136
137 137 def valideffect(effect):
138 138 'Determine if the effect is valid or not.'
139 139 return ((not _terminfo_params and effect in _effects)
140 140 or (effect in _terminfo_params
141 141 or effect[:-11] in _terminfo_params))
142 142
143 143 def _effect_str(effect):
144 144 '''Helper function for render_effects().'''
145 145
146 146 bg = False
147 147 if effect.endswith('_background'):
148 148 bg = True
149 149 effect = effect[:-11]
150 150 try:
151 151 attr, val, termcode = _terminfo_params[effect]
152 152 except KeyError:
153 153 return ''
154 154 if attr:
155 155 if termcode:
156 156 return termcode
157 157 else:
158 158 return curses.tigetstr(val)
159 159 elif bg:
160 160 return curses.tparm(curses.tigetstr('setab'), val)
161 161 else:
162 162 return curses.tparm(curses.tigetstr('setaf'), val)
163 163
164 164 def _render_effects(text, effects):
165 165 'Wrap text in commands to turn on each effect.'
166 166 if not text:
167 167 return text
168 168 if _terminfo_params:
169 169 start = ''.join(_effect_str(effect)
170 170 for effect in ['none'] + effects.split())
171 171 stop = _effect_str('none')
172 172 else:
173 173 start = [str(_effects[e]) for e in ['none'] + effects.split()]
174 174 start = '\033[' + ';'.join(start) + 'm'
175 175 stop = '\033[' + str(_effects['none']) + 'm'
176 176 return ''.join([start, text, stop])
177 177
178 178 def colorlabel(ui, msg, label):
179 179 """add color control code according to the mode"""
180 180 if ui._colormode == 'debug':
181 181 if label and msg:
182 182 if msg[-1] == '\n':
183 183 msg = "[%s|%s]\n" % (label, msg[:-1])
184 184 else:
185 185 msg = "[%s|%s]" % (label, msg)
186 186 elif ui._colormode is not None:
187 187 effects = []
188 188 for l in label.split():
189 189 s = _styles.get(l, '')
190 190 if s:
191 191 effects.append(s)
192 192 elif valideffect(l):
193 193 effects.append(l)
194 194 effects = ' '.join(effects)
195 195 if effects:
196 196 msg = '\n'.join([_render_effects(line, effects)
197 197 for line in msg.split('\n')])
198 198 return msg
199 199
200 200 w32effects = None
201 201 if pycompat.osname == 'nt':
202 202 import ctypes
203 203 import re
204 204
205 205 _kernel32 = ctypes.windll.kernel32
206 206
207 207 _WORD = ctypes.c_ushort
208 208
209 209 _INVALID_HANDLE_VALUE = -1
210 210
211 211 class _COORD(ctypes.Structure):
212 212 _fields_ = [('X', ctypes.c_short),
213 213 ('Y', ctypes.c_short)]
214 214
215 215 class _SMALL_RECT(ctypes.Structure):
216 216 _fields_ = [('Left', ctypes.c_short),
217 217 ('Top', ctypes.c_short),
218 218 ('Right', ctypes.c_short),
219 219 ('Bottom', ctypes.c_short)]
220 220
221 221 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
222 222 _fields_ = [('dwSize', _COORD),
223 223 ('dwCursorPosition', _COORD),
224 224 ('wAttributes', _WORD),
225 225 ('srWindow', _SMALL_RECT),
226 226 ('dwMaximumWindowSize', _COORD)]
227 227
228 228 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
229 229 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12
230 230
231 231 _FOREGROUND_BLUE = 0x0001
232 232 _FOREGROUND_GREEN = 0x0002
233 233 _FOREGROUND_RED = 0x0004
234 234 _FOREGROUND_INTENSITY = 0x0008
235 235
236 236 _BACKGROUND_BLUE = 0x0010
237 237 _BACKGROUND_GREEN = 0x0020
238 238 _BACKGROUND_RED = 0x0040
239 239 _BACKGROUND_INTENSITY = 0x0080
240 240
241 241 _COMMON_LVB_REVERSE_VIDEO = 0x4000
242 242 _COMMON_LVB_UNDERSCORE = 0x8000
243 243
244 244 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
245 245 w32effects = {
246 246 'none': -1,
247 247 'black': 0,
248 248 'red': _FOREGROUND_RED,
249 249 'green': _FOREGROUND_GREEN,
250 250 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
251 251 'blue': _FOREGROUND_BLUE,
252 252 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
253 253 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
254 254 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
255 255 'bold': _FOREGROUND_INTENSITY,
256 256 'black_background': 0x100, # unused value > 0x0f
257 257 'red_background': _BACKGROUND_RED,
258 258 'green_background': _BACKGROUND_GREEN,
259 259 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
260 260 'blue_background': _BACKGROUND_BLUE,
261 261 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
262 262 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
263 263 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
264 264 _BACKGROUND_BLUE),
265 265 'bold_background': _BACKGROUND_INTENSITY,
266 266 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
267 267 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
268 268 }
269 269
270 270 passthrough = set([_FOREGROUND_INTENSITY,
271 271 _BACKGROUND_INTENSITY,
272 272 _COMMON_LVB_UNDERSCORE,
273 273 _COMMON_LVB_REVERSE_VIDEO])
274 274
275 275 stdout = _kernel32.GetStdHandle(
276 276 _STD_OUTPUT_HANDLE) # don't close the handle returned
277 277 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
278 278 w32effects = None
279 279 else:
280 280 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
281 281 if not _kernel32.GetConsoleScreenBufferInfo(
282 282 stdout, ctypes.byref(csbi)):
283 283 # stdout may not support GetConsoleScreenBufferInfo()
284 284 # when called from subprocess or redirected
285 285 w32effects = None
286 286 else:
287 287 origattr = csbi.wAttributes
288 288 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
289 289 re.MULTILINE | re.DOTALL)
290 290
291 def win32print(text, orig, **opts):
291 def win32print(text, writefunc, **opts):
292 292 label = opts.get('label', '')
293 293 attr = origattr
294 294
295 295 def mapcolor(val, attr):
296 296 if val == -1:
297 297 return origattr
298 298 elif val in passthrough:
299 299 return attr | val
300 300 elif val > 0x0f:
301 301 return (val & 0x70) | (attr & 0x8f)
302 302 else:
303 303 return (val & 0x07) | (attr & 0xf8)
304 304
305 305 # determine console attributes based on labels
306 306 for l in label.split():
307 307 style = _styles.get(l, '')
308 308 for effect in style.split():
309 309 try:
310 310 attr = mapcolor(w32effects[effect], attr)
311 311 except KeyError:
312 312 # w32effects could not have certain attributes so we skip
313 313 # them if not found
314 314 pass
315 315 # hack to ensure regexp finds data
316 316 if not text.startswith('\033['):
317 317 text = '\033[m' + text
318 318
319 319 # Look for ANSI-like codes embedded in text
320 320 m = re.match(ansire, text)
321 321
322 322 try:
323 323 while m:
324 324 for sattr in m.group(1).split(';'):
325 325 if sattr:
326 326 attr = mapcolor(int(sattr), attr)
327 327 _kernel32.SetConsoleTextAttribute(stdout, attr)
328 orig(m.group(2), **opts)
328 writefunc(m.group(2), **opts)
329 329 m = re.match(ansire, m.group(3))
330 330 finally:
331 331 # Explicitly reset original attributes
332 332 _kernel32.SetConsoleTextAttribute(stdout, origattr)
General Comments 0
You need to be logged in to leave comments. Login now