##// END OF EJS Templates
color: add missing import of error...
Bryan O'Sullivan -
r18921:ac31f586 default
parent child Browse files
Show More
@@ -1,533 +1,533
1 # color.py color output for the status and qseries commands
1 # color.py color output for the status and qseries commands
2 #
2 #
3 # Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.com>
3 # Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.com>
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 '''colorize output from some commands
8 '''colorize output from some commands
9
9
10 This extension modifies the status and resolve commands to add color
10 This extension modifies the status and resolve commands to add color
11 to their output to reflect file status, the qseries command to add
11 to their output to reflect file status, the qseries command to add
12 color to reflect patch status (applied, unapplied, missing), and to
12 color to reflect patch status (applied, unapplied, missing), and to
13 diff-related commands to highlight additions, removals, diff headers,
13 diff-related commands to highlight additions, removals, diff headers,
14 and trailing whitespace.
14 and trailing whitespace.
15
15
16 Other effects in addition to color, like bold and underlined text, are
16 Other effects in addition to color, like bold and underlined text, are
17 also available. By default, the terminfo database is used to find the
17 also available. By default, the terminfo database is used to find the
18 terminal codes used to change color and effect. If terminfo is not
18 terminal codes used to change color and effect. If terminfo is not
19 available, then effects are rendered with the ECMA-48 SGR control
19 available, then effects are rendered with the ECMA-48 SGR control
20 function (aka ANSI escape codes).
20 function (aka ANSI escape codes).
21
21
22 Default effects may be overridden from your configuration file::
22 Default effects may be overridden from your configuration file::
23
23
24 [color]
24 [color]
25 status.modified = blue bold underline red_background
25 status.modified = blue bold underline red_background
26 status.added = green bold
26 status.added = green bold
27 status.removed = red bold blue_background
27 status.removed = red bold blue_background
28 status.deleted = cyan bold underline
28 status.deleted = cyan bold underline
29 status.unknown = magenta bold underline
29 status.unknown = magenta bold underline
30 status.ignored = black bold
30 status.ignored = black bold
31
31
32 # 'none' turns off all effects
32 # 'none' turns off all effects
33 status.clean = none
33 status.clean = none
34 status.copied = none
34 status.copied = none
35
35
36 qseries.applied = blue bold underline
36 qseries.applied = blue bold underline
37 qseries.unapplied = black bold
37 qseries.unapplied = black bold
38 qseries.missing = red bold
38 qseries.missing = red bold
39
39
40 diff.diffline = bold
40 diff.diffline = bold
41 diff.extended = cyan bold
41 diff.extended = cyan bold
42 diff.file_a = red bold
42 diff.file_a = red bold
43 diff.file_b = green bold
43 diff.file_b = green bold
44 diff.hunk = magenta
44 diff.hunk = magenta
45 diff.deleted = red
45 diff.deleted = red
46 diff.inserted = green
46 diff.inserted = green
47 diff.changed = white
47 diff.changed = white
48 diff.trailingwhitespace = bold red_background
48 diff.trailingwhitespace = bold red_background
49
49
50 resolve.unresolved = red bold
50 resolve.unresolved = red bold
51 resolve.resolved = green bold
51 resolve.resolved = green bold
52
52
53 bookmarks.current = green
53 bookmarks.current = green
54
54
55 branches.active = none
55 branches.active = none
56 branches.closed = black bold
56 branches.closed = black bold
57 branches.current = green
57 branches.current = green
58 branches.inactive = none
58 branches.inactive = none
59
59
60 tags.normal = green
60 tags.normal = green
61 tags.local = black bold
61 tags.local = black bold
62
62
63 The available effects in terminfo mode are 'blink', 'bold', 'dim',
63 The available effects in terminfo mode are 'blink', 'bold', 'dim',
64 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
64 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
65 ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
65 ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
66 'underline'. How each is rendered depends on the terminal emulator.
66 'underline'. How each is rendered depends on the terminal emulator.
67 Some may not be available for a given terminal type, and will be
67 Some may not be available for a given terminal type, and will be
68 silently ignored.
68 silently ignored.
69
69
70 Note that on some systems, terminfo mode may cause problems when using
70 Note that on some systems, terminfo mode may cause problems when using
71 color with the pager extension and less -R. less with the -R option
71 color with the pager extension and less -R. less with the -R option
72 will only display ECMA-48 color codes, and terminfo mode may sometimes
72 will only display ECMA-48 color codes, and terminfo mode may sometimes
73 emit codes that less doesn't understand. You can work around this by
73 emit codes that less doesn't understand. You can work around this by
74 either using ansi mode (or auto mode), or by using less -r (which will
74 either using ansi mode (or auto mode), or by using less -r (which will
75 pass through all terminal control codes, not just color control
75 pass through all terminal control codes, not just color control
76 codes).
76 codes).
77
77
78 Because there are only eight standard colors, this module allows you
78 Because there are only eight standard colors, this module allows you
79 to define color names for other color slots which might be available
79 to define color names for other color slots which might be available
80 for your terminal type, assuming terminfo mode. For instance::
80 for your terminal type, assuming terminfo mode. For instance::
81
81
82 color.brightblue = 12
82 color.brightblue = 12
83 color.pink = 207
83 color.pink = 207
84 color.orange = 202
84 color.orange = 202
85
85
86 to set 'brightblue' to color slot 12 (useful for 16 color terminals
86 to set 'brightblue' to color slot 12 (useful for 16 color terminals
87 that have brighter colors defined in the upper eight) and, 'pink' and
87 that have brighter colors defined in the upper eight) and, 'pink' and
88 'orange' to colors in 256-color xterm's default color cube. These
88 'orange' to colors in 256-color xterm's default color cube. These
89 defined colors may then be used as any of the pre-defined eight,
89 defined colors may then be used as any of the pre-defined eight,
90 including appending '_background' to set the background to that color.
90 including appending '_background' to set the background to that color.
91
91
92 By default, the color extension will use ANSI mode (or win32 mode on
92 By default, the color extension will use ANSI mode (or win32 mode on
93 Windows) if it detects a terminal. To override auto mode (to enable
93 Windows) if it detects a terminal. To override auto mode (to enable
94 terminfo mode, for example), set the following configuration option::
94 terminfo mode, for example), set the following configuration option::
95
95
96 [color]
96 [color]
97 mode = terminfo
97 mode = terminfo
98
98
99 Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will
99 Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will
100 disable color.
100 disable color.
101 '''
101 '''
102
102
103 import os
103 import os
104
104
105 from mercurial import commands, dispatch, extensions, ui as uimod, util
105 from mercurial import commands, dispatch, extensions, ui as uimod, util
106 from mercurial import templater
106 from mercurial import templater, error
107 from mercurial.i18n import _
107 from mercurial.i18n import _
108
108
109 testedwith = 'internal'
109 testedwith = 'internal'
110
110
111 # start and stop parameters for effects
111 # start and stop parameters for effects
112 _effects = {'none': 0, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33,
112 _effects = {'none': 0, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33,
113 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'bold': 1,
113 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'bold': 1,
114 'italic': 3, 'underline': 4, 'inverse': 7,
114 'italic': 3, 'underline': 4, 'inverse': 7,
115 'black_background': 40, 'red_background': 41,
115 'black_background': 40, 'red_background': 41,
116 'green_background': 42, 'yellow_background': 43,
116 'green_background': 42, 'yellow_background': 43,
117 'blue_background': 44, 'purple_background': 45,
117 'blue_background': 44, 'purple_background': 45,
118 'cyan_background': 46, 'white_background': 47}
118 'cyan_background': 46, 'white_background': 47}
119
119
120 def _terminfosetup(ui, mode):
120 def _terminfosetup(ui, mode):
121 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
121 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
122
122
123 global _terminfo_params
123 global _terminfo_params
124 # If we failed to load curses, we go ahead and return.
124 # If we failed to load curses, we go ahead and return.
125 if not _terminfo_params:
125 if not _terminfo_params:
126 return
126 return
127 # Otherwise, see what the config file says.
127 # Otherwise, see what the config file says.
128 if mode not in ('auto', 'terminfo'):
128 if mode not in ('auto', 'terminfo'):
129 return
129 return
130
130
131 _terminfo_params.update((key[6:], (False, int(val)))
131 _terminfo_params.update((key[6:], (False, int(val)))
132 for key, val in ui.configitems('color')
132 for key, val in ui.configitems('color')
133 if key.startswith('color.'))
133 if key.startswith('color.'))
134
134
135 try:
135 try:
136 curses.setupterm()
136 curses.setupterm()
137 except curses.error, e:
137 except curses.error, e:
138 _terminfo_params = {}
138 _terminfo_params = {}
139 return
139 return
140
140
141 for key, (b, e) in _terminfo_params.items():
141 for key, (b, e) in _terminfo_params.items():
142 if not b:
142 if not b:
143 continue
143 continue
144 if not curses.tigetstr(e):
144 if not curses.tigetstr(e):
145 # Most terminals don't support dim, invis, etc, so don't be
145 # Most terminals don't support dim, invis, etc, so don't be
146 # noisy and use ui.debug().
146 # noisy and use ui.debug().
147 ui.debug("no terminfo entry for %s\n" % e)
147 ui.debug("no terminfo entry for %s\n" % e)
148 del _terminfo_params[key]
148 del _terminfo_params[key]
149 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
149 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
150 # Only warn about missing terminfo entries if we explicitly asked for
150 # Only warn about missing terminfo entries if we explicitly asked for
151 # terminfo mode.
151 # terminfo mode.
152 if mode == "terminfo":
152 if mode == "terminfo":
153 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
153 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
154 "ECMA-48 color\n"))
154 "ECMA-48 color\n"))
155 _terminfo_params = {}
155 _terminfo_params = {}
156
156
157 def _modesetup(ui, opts):
157 def _modesetup(ui, opts):
158 global _terminfo_params
158 global _terminfo_params
159
159
160 coloropt = opts['color']
160 coloropt = opts['color']
161 auto = coloropt == 'auto'
161 auto = coloropt == 'auto'
162 always = not auto and util.parsebool(coloropt)
162 always = not auto and util.parsebool(coloropt)
163 if not always and not auto:
163 if not always and not auto:
164 return None
164 return None
165
165
166 formatted = always or (os.environ.get('TERM') != 'dumb' and ui.formatted())
166 formatted = always or (os.environ.get('TERM') != 'dumb' and ui.formatted())
167
167
168 mode = ui.config('color', 'mode', 'auto')
168 mode = ui.config('color', 'mode', 'auto')
169 realmode = mode
169 realmode = mode
170 if mode == 'auto':
170 if mode == 'auto':
171 if os.name == 'nt' and 'TERM' not in os.environ:
171 if os.name == 'nt' and 'TERM' not in os.environ:
172 # looks line a cmd.exe console, use win32 API or nothing
172 # looks line a cmd.exe console, use win32 API or nothing
173 realmode = 'win32'
173 realmode = 'win32'
174 else:
174 else:
175 realmode = 'ansi'
175 realmode = 'ansi'
176
176
177 if realmode == 'win32':
177 if realmode == 'win32':
178 _terminfo_params = {}
178 _terminfo_params = {}
179 if not w32effects:
179 if not w32effects:
180 if mode == 'win32':
180 if mode == 'win32':
181 # only warn if color.mode is explicitly set to win32
181 # only warn if color.mode is explicitly set to win32
182 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
182 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
183 return None
183 return None
184 _effects.update(w32effects)
184 _effects.update(w32effects)
185 elif realmode == 'ansi':
185 elif realmode == 'ansi':
186 _terminfo_params = {}
186 _terminfo_params = {}
187 elif realmode == 'terminfo':
187 elif realmode == 'terminfo':
188 _terminfosetup(ui, mode)
188 _terminfosetup(ui, mode)
189 if not _terminfo_params:
189 if not _terminfo_params:
190 if mode == 'terminfo':
190 if mode == 'terminfo':
191 ## FIXME Shouldn't we return None in this case too?
191 ## FIXME Shouldn't we return None in this case too?
192 # only warn if color.mode is explicitly set to win32
192 # only warn if color.mode is explicitly set to win32
193 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
193 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
194 realmode = 'ansi'
194 realmode = 'ansi'
195 else:
195 else:
196 return None
196 return None
197
197
198 if always or (auto and formatted):
198 if always or (auto and formatted):
199 return realmode
199 return realmode
200 return None
200 return None
201
201
202 try:
202 try:
203 import curses
203 import curses
204 # Mapping from effect name to terminfo attribute name or color number.
204 # Mapping from effect name to terminfo attribute name or color number.
205 # This will also force-load the curses module.
205 # This will also force-load the curses module.
206 _terminfo_params = {'none': (True, 'sgr0'),
206 _terminfo_params = {'none': (True, 'sgr0'),
207 'standout': (True, 'smso'),
207 'standout': (True, 'smso'),
208 'underline': (True, 'smul'),
208 'underline': (True, 'smul'),
209 'reverse': (True, 'rev'),
209 'reverse': (True, 'rev'),
210 'inverse': (True, 'rev'),
210 'inverse': (True, 'rev'),
211 'blink': (True, 'blink'),
211 'blink': (True, 'blink'),
212 'dim': (True, 'dim'),
212 'dim': (True, 'dim'),
213 'bold': (True, 'bold'),
213 'bold': (True, 'bold'),
214 'invisible': (True, 'invis'),
214 'invisible': (True, 'invis'),
215 'italic': (True, 'sitm'),
215 'italic': (True, 'sitm'),
216 'black': (False, curses.COLOR_BLACK),
216 'black': (False, curses.COLOR_BLACK),
217 'red': (False, curses.COLOR_RED),
217 'red': (False, curses.COLOR_RED),
218 'green': (False, curses.COLOR_GREEN),
218 'green': (False, curses.COLOR_GREEN),
219 'yellow': (False, curses.COLOR_YELLOW),
219 'yellow': (False, curses.COLOR_YELLOW),
220 'blue': (False, curses.COLOR_BLUE),
220 'blue': (False, curses.COLOR_BLUE),
221 'magenta': (False, curses.COLOR_MAGENTA),
221 'magenta': (False, curses.COLOR_MAGENTA),
222 'cyan': (False, curses.COLOR_CYAN),
222 'cyan': (False, curses.COLOR_CYAN),
223 'white': (False, curses.COLOR_WHITE)}
223 'white': (False, curses.COLOR_WHITE)}
224 except ImportError:
224 except ImportError:
225 _terminfo_params = False
225 _terminfo_params = False
226
226
227 _styles = {'grep.match': 'red bold',
227 _styles = {'grep.match': 'red bold',
228 'grep.linenumber': 'green',
228 'grep.linenumber': 'green',
229 'grep.rev': 'green',
229 'grep.rev': 'green',
230 'grep.change': 'green',
230 'grep.change': 'green',
231 'grep.sep': 'cyan',
231 'grep.sep': 'cyan',
232 'grep.filename': 'magenta',
232 'grep.filename': 'magenta',
233 'grep.user': 'magenta',
233 'grep.user': 'magenta',
234 'grep.date': 'magenta',
234 'grep.date': 'magenta',
235 'bookmarks.current': 'green',
235 'bookmarks.current': 'green',
236 'branches.active': 'none',
236 'branches.active': 'none',
237 'branches.closed': 'black bold',
237 'branches.closed': 'black bold',
238 'branches.current': 'green',
238 'branches.current': 'green',
239 'branches.inactive': 'none',
239 'branches.inactive': 'none',
240 'diff.changed': 'white',
240 'diff.changed': 'white',
241 'diff.deleted': 'red',
241 'diff.deleted': 'red',
242 'diff.diffline': 'bold',
242 'diff.diffline': 'bold',
243 'diff.extended': 'cyan bold',
243 'diff.extended': 'cyan bold',
244 'diff.file_a': 'red bold',
244 'diff.file_a': 'red bold',
245 'diff.file_b': 'green bold',
245 'diff.file_b': 'green bold',
246 'diff.hunk': 'magenta',
246 'diff.hunk': 'magenta',
247 'diff.inserted': 'green',
247 'diff.inserted': 'green',
248 'diff.trailingwhitespace': 'bold red_background',
248 'diff.trailingwhitespace': 'bold red_background',
249 'diffstat.deleted': 'red',
249 'diffstat.deleted': 'red',
250 'diffstat.inserted': 'green',
250 'diffstat.inserted': 'green',
251 'ui.prompt': 'yellow',
251 'ui.prompt': 'yellow',
252 'log.changeset': 'yellow',
252 'log.changeset': 'yellow',
253 'resolve.resolved': 'green bold',
253 'resolve.resolved': 'green bold',
254 'resolve.unresolved': 'red bold',
254 'resolve.unresolved': 'red bold',
255 'status.added': 'green bold',
255 'status.added': 'green bold',
256 'status.clean': 'none',
256 'status.clean': 'none',
257 'status.copied': 'none',
257 'status.copied': 'none',
258 'status.deleted': 'cyan bold underline',
258 'status.deleted': 'cyan bold underline',
259 'status.ignored': 'black bold',
259 'status.ignored': 'black bold',
260 'status.modified': 'blue bold',
260 'status.modified': 'blue bold',
261 'status.removed': 'red bold',
261 'status.removed': 'red bold',
262 'status.unknown': 'magenta bold underline',
262 'status.unknown': 'magenta bold underline',
263 'tags.normal': 'green',
263 'tags.normal': 'green',
264 'tags.local': 'black bold'}
264 'tags.local': 'black bold'}
265
265
266
266
267 def _effect_str(effect):
267 def _effect_str(effect):
268 '''Helper function for render_effects().'''
268 '''Helper function for render_effects().'''
269
269
270 bg = False
270 bg = False
271 if effect.endswith('_background'):
271 if effect.endswith('_background'):
272 bg = True
272 bg = True
273 effect = effect[:-11]
273 effect = effect[:-11]
274 attr, val = _terminfo_params[effect]
274 attr, val = _terminfo_params[effect]
275 if attr:
275 if attr:
276 return curses.tigetstr(val)
276 return curses.tigetstr(val)
277 elif bg:
277 elif bg:
278 return curses.tparm(curses.tigetstr('setab'), val)
278 return curses.tparm(curses.tigetstr('setab'), val)
279 else:
279 else:
280 return curses.tparm(curses.tigetstr('setaf'), val)
280 return curses.tparm(curses.tigetstr('setaf'), val)
281
281
282 def render_effects(text, effects):
282 def render_effects(text, effects):
283 'Wrap text in commands to turn on each effect.'
283 'Wrap text in commands to turn on each effect.'
284 if not text:
284 if not text:
285 return text
285 return text
286 if not _terminfo_params:
286 if not _terminfo_params:
287 start = [str(_effects[e]) for e in ['none'] + effects.split()]
287 start = [str(_effects[e]) for e in ['none'] + effects.split()]
288 start = '\033[' + ';'.join(start) + 'm'
288 start = '\033[' + ';'.join(start) + 'm'
289 stop = '\033[' + str(_effects['none']) + 'm'
289 stop = '\033[' + str(_effects['none']) + 'm'
290 else:
290 else:
291 start = ''.join(_effect_str(effect)
291 start = ''.join(_effect_str(effect)
292 for effect in ['none'] + effects.split())
292 for effect in ['none'] + effects.split())
293 stop = _effect_str('none')
293 stop = _effect_str('none')
294 return ''.join([start, text, stop])
294 return ''.join([start, text, stop])
295
295
296 def extstyles():
296 def extstyles():
297 for name, ext in extensions.extensions():
297 for name, ext in extensions.extensions():
298 _styles.update(getattr(ext, 'colortable', {}))
298 _styles.update(getattr(ext, 'colortable', {}))
299
299
300 def configstyles(ui):
300 def configstyles(ui):
301 for status, cfgeffects in ui.configitems('color'):
301 for status, cfgeffects in ui.configitems('color'):
302 if '.' not in status or status.startswith('color.'):
302 if '.' not in status or status.startswith('color.'):
303 continue
303 continue
304 cfgeffects = ui.configlist('color', status)
304 cfgeffects = ui.configlist('color', status)
305 if cfgeffects:
305 if cfgeffects:
306 good = []
306 good = []
307 for e in cfgeffects:
307 for e in cfgeffects:
308 if not _terminfo_params and e in _effects:
308 if not _terminfo_params and e in _effects:
309 good.append(e)
309 good.append(e)
310 elif e in _terminfo_params or e[:-11] in _terminfo_params:
310 elif e in _terminfo_params or e[:-11] in _terminfo_params:
311 good.append(e)
311 good.append(e)
312 else:
312 else:
313 ui.warn(_("ignoring unknown color/effect %r "
313 ui.warn(_("ignoring unknown color/effect %r "
314 "(configured in color.%s)\n")
314 "(configured in color.%s)\n")
315 % (e, status))
315 % (e, status))
316 _styles[status] = ' '.join(good)
316 _styles[status] = ' '.join(good)
317
317
318 class colorui(uimod.ui):
318 class colorui(uimod.ui):
319 def popbuffer(self, labeled=False):
319 def popbuffer(self, labeled=False):
320 if labeled:
320 if labeled:
321 return ''.join(self.label(a, label) for a, label
321 return ''.join(self.label(a, label) for a, label
322 in self._buffers.pop())
322 in self._buffers.pop())
323 return ''.join(a for a, label in self._buffers.pop())
323 return ''.join(a for a, label in self._buffers.pop())
324
324
325 _colormode = 'ansi'
325 _colormode = 'ansi'
326 def write(self, *args, **opts):
326 def write(self, *args, **opts):
327 label = opts.get('label', '')
327 label = opts.get('label', '')
328 if self._buffers:
328 if self._buffers:
329 self._buffers[-1].extend([(str(a), label) for a in args])
329 self._buffers[-1].extend([(str(a), label) for a in args])
330 elif self._colormode == 'win32':
330 elif self._colormode == 'win32':
331 for a in args:
331 for a in args:
332 win32print(a, super(colorui, self).write, **opts)
332 win32print(a, super(colorui, self).write, **opts)
333 else:
333 else:
334 return super(colorui, self).write(
334 return super(colorui, self).write(
335 *[self.label(str(a), label) for a in args], **opts)
335 *[self.label(str(a), label) for a in args], **opts)
336
336
337 def write_err(self, *args, **opts):
337 def write_err(self, *args, **opts):
338 label = opts.get('label', '')
338 label = opts.get('label', '')
339 if self._colormode == 'win32':
339 if self._colormode == 'win32':
340 for a in args:
340 for a in args:
341 win32print(a, super(colorui, self).write_err, **opts)
341 win32print(a, super(colorui, self).write_err, **opts)
342 else:
342 else:
343 return super(colorui, self).write_err(
343 return super(colorui, self).write_err(
344 *[self.label(str(a), label) for a in args], **opts)
344 *[self.label(str(a), label) for a in args], **opts)
345
345
346 def label(self, msg, label):
346 def label(self, msg, label):
347 effects = []
347 effects = []
348 for l in label.split():
348 for l in label.split():
349 s = _styles.get(l, '')
349 s = _styles.get(l, '')
350 if s:
350 if s:
351 effects.append(s)
351 effects.append(s)
352 effects = ' '.join(effects)
352 effects = ' '.join(effects)
353 if effects:
353 if effects:
354 return '\n'.join([render_effects(s, effects)
354 return '\n'.join([render_effects(s, effects)
355 for s in msg.split('\n')])
355 for s in msg.split('\n')])
356 return msg
356 return msg
357
357
358 def templatelabel(context, mapping, args):
358 def templatelabel(context, mapping, args):
359 if len(args) != 2:
359 if len(args) != 2:
360 # i18n: "label" is a keyword
360 # i18n: "label" is a keyword
361 raise error.ParseError(_("label expects two arguments"))
361 raise error.ParseError(_("label expects two arguments"))
362
362
363 thing = templater.stringify(args[1][0](context, mapping, args[1][1]))
363 thing = templater.stringify(args[1][0](context, mapping, args[1][1]))
364 thing = templater.runtemplate(context, mapping,
364 thing = templater.runtemplate(context, mapping,
365 templater.compiletemplate(thing, context))
365 templater.compiletemplate(thing, context))
366
366
367 # apparently, repo could be a string that is the favicon?
367 # apparently, repo could be a string that is the favicon?
368 repo = mapping.get('repo', '')
368 repo = mapping.get('repo', '')
369 if isinstance(repo, str):
369 if isinstance(repo, str):
370 return thing
370 return thing
371
371
372 label = templater.stringify(args[0][0](context, mapping, args[0][1]))
372 label = templater.stringify(args[0][0](context, mapping, args[0][1]))
373 label = templater.runtemplate(context, mapping,
373 label = templater.runtemplate(context, mapping,
374 templater.compiletemplate(label, context))
374 templater.compiletemplate(label, context))
375
375
376 thing = templater.stringify(thing)
376 thing = templater.stringify(thing)
377 label = templater.stringify(label)
377 label = templater.stringify(label)
378
378
379 return repo.ui.label(thing, label)
379 return repo.ui.label(thing, label)
380
380
381 def uisetup(ui):
381 def uisetup(ui):
382 if ui.plain():
382 if ui.plain():
383 return
383 return
384 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
384 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
385 mode = _modesetup(ui_, opts)
385 mode = _modesetup(ui_, opts)
386 if mode:
386 if mode:
387 colorui._colormode = mode
387 colorui._colormode = mode
388 if not issubclass(ui_.__class__, colorui):
388 if not issubclass(ui_.__class__, colorui):
389 colorui.__bases__ = (ui_.__class__,)
389 colorui.__bases__ = (ui_.__class__,)
390 ui_.__class__ = colorui
390 ui_.__class__ = colorui
391 extstyles()
391 extstyles()
392 configstyles(ui_)
392 configstyles(ui_)
393 return orig(ui_, opts, cmd, cmdfunc)
393 return orig(ui_, opts, cmd, cmdfunc)
394 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
394 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
395 templater.funcs['label'] = templatelabel
395 templater.funcs['label'] = templatelabel
396
396
397 def extsetup(ui):
397 def extsetup(ui):
398 commands.globalopts.append(
398 commands.globalopts.append(
399 ('', 'color', 'auto',
399 ('', 'color', 'auto',
400 # i18n: 'always', 'auto', and 'never' are keywords and should
400 # i18n: 'always', 'auto', and 'never' are keywords and should
401 # not be translated
401 # not be translated
402 _("when to colorize (boolean, always, auto, or never)"),
402 _("when to colorize (boolean, always, auto, or never)"),
403 _('TYPE')))
403 _('TYPE')))
404
404
405 if os.name != 'nt':
405 if os.name != 'nt':
406 w32effects = None
406 w32effects = None
407 else:
407 else:
408 import re, ctypes
408 import re, ctypes
409
409
410 _kernel32 = ctypes.windll.kernel32
410 _kernel32 = ctypes.windll.kernel32
411
411
412 _WORD = ctypes.c_ushort
412 _WORD = ctypes.c_ushort
413
413
414 _INVALID_HANDLE_VALUE = -1
414 _INVALID_HANDLE_VALUE = -1
415
415
416 class _COORD(ctypes.Structure):
416 class _COORD(ctypes.Structure):
417 _fields_ = [('X', ctypes.c_short),
417 _fields_ = [('X', ctypes.c_short),
418 ('Y', ctypes.c_short)]
418 ('Y', ctypes.c_short)]
419
419
420 class _SMALL_RECT(ctypes.Structure):
420 class _SMALL_RECT(ctypes.Structure):
421 _fields_ = [('Left', ctypes.c_short),
421 _fields_ = [('Left', ctypes.c_short),
422 ('Top', ctypes.c_short),
422 ('Top', ctypes.c_short),
423 ('Right', ctypes.c_short),
423 ('Right', ctypes.c_short),
424 ('Bottom', ctypes.c_short)]
424 ('Bottom', ctypes.c_short)]
425
425
426 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
426 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
427 _fields_ = [('dwSize', _COORD),
427 _fields_ = [('dwSize', _COORD),
428 ('dwCursorPosition', _COORD),
428 ('dwCursorPosition', _COORD),
429 ('wAttributes', _WORD),
429 ('wAttributes', _WORD),
430 ('srWindow', _SMALL_RECT),
430 ('srWindow', _SMALL_RECT),
431 ('dwMaximumWindowSize', _COORD)]
431 ('dwMaximumWindowSize', _COORD)]
432
432
433 _STD_OUTPUT_HANDLE = 0xfffffff5L # (DWORD)-11
433 _STD_OUTPUT_HANDLE = 0xfffffff5L # (DWORD)-11
434 _STD_ERROR_HANDLE = 0xfffffff4L # (DWORD)-12
434 _STD_ERROR_HANDLE = 0xfffffff4L # (DWORD)-12
435
435
436 _FOREGROUND_BLUE = 0x0001
436 _FOREGROUND_BLUE = 0x0001
437 _FOREGROUND_GREEN = 0x0002
437 _FOREGROUND_GREEN = 0x0002
438 _FOREGROUND_RED = 0x0004
438 _FOREGROUND_RED = 0x0004
439 _FOREGROUND_INTENSITY = 0x0008
439 _FOREGROUND_INTENSITY = 0x0008
440
440
441 _BACKGROUND_BLUE = 0x0010
441 _BACKGROUND_BLUE = 0x0010
442 _BACKGROUND_GREEN = 0x0020
442 _BACKGROUND_GREEN = 0x0020
443 _BACKGROUND_RED = 0x0040
443 _BACKGROUND_RED = 0x0040
444 _BACKGROUND_INTENSITY = 0x0080
444 _BACKGROUND_INTENSITY = 0x0080
445
445
446 _COMMON_LVB_REVERSE_VIDEO = 0x4000
446 _COMMON_LVB_REVERSE_VIDEO = 0x4000
447 _COMMON_LVB_UNDERSCORE = 0x8000
447 _COMMON_LVB_UNDERSCORE = 0x8000
448
448
449 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
449 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
450 w32effects = {
450 w32effects = {
451 'none': -1,
451 'none': -1,
452 'black': 0,
452 'black': 0,
453 'red': _FOREGROUND_RED,
453 'red': _FOREGROUND_RED,
454 'green': _FOREGROUND_GREEN,
454 'green': _FOREGROUND_GREEN,
455 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
455 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
456 'blue': _FOREGROUND_BLUE,
456 'blue': _FOREGROUND_BLUE,
457 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
457 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
458 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
458 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
459 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
459 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
460 'bold': _FOREGROUND_INTENSITY,
460 'bold': _FOREGROUND_INTENSITY,
461 'black_background': 0x100, # unused value > 0x0f
461 'black_background': 0x100, # unused value > 0x0f
462 'red_background': _BACKGROUND_RED,
462 'red_background': _BACKGROUND_RED,
463 'green_background': _BACKGROUND_GREEN,
463 'green_background': _BACKGROUND_GREEN,
464 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
464 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
465 'blue_background': _BACKGROUND_BLUE,
465 'blue_background': _BACKGROUND_BLUE,
466 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
466 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
467 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
467 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
468 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
468 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
469 _BACKGROUND_BLUE),
469 _BACKGROUND_BLUE),
470 'bold_background': _BACKGROUND_INTENSITY,
470 'bold_background': _BACKGROUND_INTENSITY,
471 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
471 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
472 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
472 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
473 }
473 }
474
474
475 passthrough = set([_FOREGROUND_INTENSITY,
475 passthrough = set([_FOREGROUND_INTENSITY,
476 _BACKGROUND_INTENSITY,
476 _BACKGROUND_INTENSITY,
477 _COMMON_LVB_UNDERSCORE,
477 _COMMON_LVB_UNDERSCORE,
478 _COMMON_LVB_REVERSE_VIDEO])
478 _COMMON_LVB_REVERSE_VIDEO])
479
479
480 stdout = _kernel32.GetStdHandle(
480 stdout = _kernel32.GetStdHandle(
481 _STD_OUTPUT_HANDLE) # don't close the handle returned
481 _STD_OUTPUT_HANDLE) # don't close the handle returned
482 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
482 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
483 w32effects = None
483 w32effects = None
484 else:
484 else:
485 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
485 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
486 if not _kernel32.GetConsoleScreenBufferInfo(
486 if not _kernel32.GetConsoleScreenBufferInfo(
487 stdout, ctypes.byref(csbi)):
487 stdout, ctypes.byref(csbi)):
488 # stdout may not support GetConsoleScreenBufferInfo()
488 # stdout may not support GetConsoleScreenBufferInfo()
489 # when called from subprocess or redirected
489 # when called from subprocess or redirected
490 w32effects = None
490 w32effects = None
491 else:
491 else:
492 origattr = csbi.wAttributes
492 origattr = csbi.wAttributes
493 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
493 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
494 re.MULTILINE | re.DOTALL)
494 re.MULTILINE | re.DOTALL)
495
495
496 def win32print(text, orig, **opts):
496 def win32print(text, orig, **opts):
497 label = opts.get('label', '')
497 label = opts.get('label', '')
498 attr = origattr
498 attr = origattr
499
499
500 def mapcolor(val, attr):
500 def mapcolor(val, attr):
501 if val == -1:
501 if val == -1:
502 return origattr
502 return origattr
503 elif val in passthrough:
503 elif val in passthrough:
504 return attr | val
504 return attr | val
505 elif val > 0x0f:
505 elif val > 0x0f:
506 return (val & 0x70) | (attr & 0x8f)
506 return (val & 0x70) | (attr & 0x8f)
507 else:
507 else:
508 return (val & 0x07) | (attr & 0xf8)
508 return (val & 0x07) | (attr & 0xf8)
509
509
510 # determine console attributes based on labels
510 # determine console attributes based on labels
511 for l in label.split():
511 for l in label.split():
512 style = _styles.get(l, '')
512 style = _styles.get(l, '')
513 for effect in style.split():
513 for effect in style.split():
514 attr = mapcolor(w32effects[effect], attr)
514 attr = mapcolor(w32effects[effect], attr)
515
515
516 # hack to ensure regexp finds data
516 # hack to ensure regexp finds data
517 if not text.startswith('\033['):
517 if not text.startswith('\033['):
518 text = '\033[m' + text
518 text = '\033[m' + text
519
519
520 # Look for ANSI-like codes embedded in text
520 # Look for ANSI-like codes embedded in text
521 m = re.match(ansire, text)
521 m = re.match(ansire, text)
522
522
523 try:
523 try:
524 while m:
524 while m:
525 for sattr in m.group(1).split(';'):
525 for sattr in m.group(1).split(';'):
526 if sattr:
526 if sattr:
527 attr = mapcolor(int(sattr), attr)
527 attr = mapcolor(int(sattr), attr)
528 _kernel32.SetConsoleTextAttribute(stdout, attr)
528 _kernel32.SetConsoleTextAttribute(stdout, attr)
529 orig(m.group(2), **opts)
529 orig(m.group(2), **opts)
530 m = re.match(ansire, m.group(3))
530 m = re.match(ansire, m.group(3))
531 finally:
531 finally:
532 # Explicitly reset original attributes
532 # Explicitly reset original attributes
533 _kernel32.SetConsoleTextAttribute(stdout, origattr)
533 _kernel32.SetConsoleTextAttribute(stdout, origattr)
General Comments 0
You need to be logged in to leave comments. Login now