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