##// END OF EJS Templates
color: minor reversal of two conditional clause for clarity...
Pierre-Yves David -
r31071:350d737e default
parent child Browse files
Show More
@@ -1,310 +1,310 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 if not _terminfo_params:
168 if _terminfo_params:
169 start = ''.join(_effect_str(effect)
170 for effect in ['none'] + effects.split())
171 stop = _effect_str('none')
172 else:
169 173 start = [str(_effects[e]) for e in ['none'] + effects.split()]
170 174 start = '\033[' + ';'.join(start) + 'm'
171 175 stop = '\033[' + str(_effects['none']) + 'm'
172 else:
173 start = ''.join(_effect_str(effect)
174 for effect in ['none'] + effects.split())
175 stop = _effect_str('none')
176 176 return ''.join([start, text, stop])
177 177
178 178 w32effects = None
179 179 if pycompat.osname == 'nt':
180 180 import ctypes
181 181 import re
182 182
183 183 _kernel32 = ctypes.windll.kernel32
184 184
185 185 _WORD = ctypes.c_ushort
186 186
187 187 _INVALID_HANDLE_VALUE = -1
188 188
189 189 class _COORD(ctypes.Structure):
190 190 _fields_ = [('X', ctypes.c_short),
191 191 ('Y', ctypes.c_short)]
192 192
193 193 class _SMALL_RECT(ctypes.Structure):
194 194 _fields_ = [('Left', ctypes.c_short),
195 195 ('Top', ctypes.c_short),
196 196 ('Right', ctypes.c_short),
197 197 ('Bottom', ctypes.c_short)]
198 198
199 199 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
200 200 _fields_ = [('dwSize', _COORD),
201 201 ('dwCursorPosition', _COORD),
202 202 ('wAttributes', _WORD),
203 203 ('srWindow', _SMALL_RECT),
204 204 ('dwMaximumWindowSize', _COORD)]
205 205
206 206 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
207 207 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12
208 208
209 209 _FOREGROUND_BLUE = 0x0001
210 210 _FOREGROUND_GREEN = 0x0002
211 211 _FOREGROUND_RED = 0x0004
212 212 _FOREGROUND_INTENSITY = 0x0008
213 213
214 214 _BACKGROUND_BLUE = 0x0010
215 215 _BACKGROUND_GREEN = 0x0020
216 216 _BACKGROUND_RED = 0x0040
217 217 _BACKGROUND_INTENSITY = 0x0080
218 218
219 219 _COMMON_LVB_REVERSE_VIDEO = 0x4000
220 220 _COMMON_LVB_UNDERSCORE = 0x8000
221 221
222 222 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
223 223 w32effects = {
224 224 'none': -1,
225 225 'black': 0,
226 226 'red': _FOREGROUND_RED,
227 227 'green': _FOREGROUND_GREEN,
228 228 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
229 229 'blue': _FOREGROUND_BLUE,
230 230 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
231 231 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
232 232 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
233 233 'bold': _FOREGROUND_INTENSITY,
234 234 'black_background': 0x100, # unused value > 0x0f
235 235 'red_background': _BACKGROUND_RED,
236 236 'green_background': _BACKGROUND_GREEN,
237 237 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
238 238 'blue_background': _BACKGROUND_BLUE,
239 239 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
240 240 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
241 241 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
242 242 _BACKGROUND_BLUE),
243 243 'bold_background': _BACKGROUND_INTENSITY,
244 244 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
245 245 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
246 246 }
247 247
248 248 passthrough = set([_FOREGROUND_INTENSITY,
249 249 _BACKGROUND_INTENSITY,
250 250 _COMMON_LVB_UNDERSCORE,
251 251 _COMMON_LVB_REVERSE_VIDEO])
252 252
253 253 stdout = _kernel32.GetStdHandle(
254 254 _STD_OUTPUT_HANDLE) # don't close the handle returned
255 255 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
256 256 w32effects = None
257 257 else:
258 258 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
259 259 if not _kernel32.GetConsoleScreenBufferInfo(
260 260 stdout, ctypes.byref(csbi)):
261 261 # stdout may not support GetConsoleScreenBufferInfo()
262 262 # when called from subprocess or redirected
263 263 w32effects = None
264 264 else:
265 265 origattr = csbi.wAttributes
266 266 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
267 267 re.MULTILINE | re.DOTALL)
268 268
269 269 def win32print(text, orig, **opts):
270 270 label = opts.get('label', '')
271 271 attr = origattr
272 272
273 273 def mapcolor(val, attr):
274 274 if val == -1:
275 275 return origattr
276 276 elif val in passthrough:
277 277 return attr | val
278 278 elif val > 0x0f:
279 279 return (val & 0x70) | (attr & 0x8f)
280 280 else:
281 281 return (val & 0x07) | (attr & 0xf8)
282 282
283 283 # determine console attributes based on labels
284 284 for l in label.split():
285 285 style = _styles.get(l, '')
286 286 for effect in style.split():
287 287 try:
288 288 attr = mapcolor(w32effects[effect], attr)
289 289 except KeyError:
290 290 # w32effects could not have certain attributes so we skip
291 291 # them if not found
292 292 pass
293 293 # hack to ensure regexp finds data
294 294 if not text.startswith('\033['):
295 295 text = '\033[m' + text
296 296
297 297 # Look for ANSI-like codes embedded in text
298 298 m = re.match(ansire, text)
299 299
300 300 try:
301 301 while m:
302 302 for sattr in m.group(1).split(';'):
303 303 if sattr:
304 304 attr = mapcolor(int(sattr), attr)
305 305 _kernel32.SetConsoleTextAttribute(stdout, attr)
306 306 orig(m.group(2), **opts)
307 307 m = re.match(ansire, m.group(3))
308 308 finally:
309 309 # Explicitly reset original attributes
310 310 _kernel32.SetConsoleTextAttribute(stdout, origattr)
General Comments 0
You need to be logged in to leave comments. Login now