##// END OF EJS Templates
color: move the '_render_effects' function to the core module
Pierre-Yves David -
r30973:e5363cb9 default
parent child Browse files
Show More
@@ -1,583 +1,569
1 # color.py color output for Mercurial commands
1 # color.py color output for Mercurial 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 The color extension colorizes output from several Mercurial commands.
10 The color extension colorizes output from several Mercurial commands.
11 For example, the diff command shows additions in green and deletions
11 For example, the diff command shows additions in green and deletions
12 in red, while the status command shows modified files in magenta. Many
12 in red, while the status command shows modified files in magenta. Many
13 other commands have analogous colors. It is possible to customize
13 other commands have analogous colors. It is possible to customize
14 these colors.
14 these colors.
15
15
16 Effects
16 Effects
17 -------
17 -------
18
18
19 Other effects in addition to color, like bold and underlined text, are
19 Other effects in addition to color, like bold and underlined text, are
20 also available. By default, the terminfo database is used to find the
20 also available. By default, the terminfo database is used to find the
21 terminal codes used to change color and effect. If terminfo is not
21 terminal codes used to change color and effect. If terminfo is not
22 available, then effects are rendered with the ECMA-48 SGR control
22 available, then effects are rendered with the ECMA-48 SGR control
23 function (aka ANSI escape codes).
23 function (aka ANSI escape codes).
24
24
25 The available effects in terminfo mode are 'blink', 'bold', 'dim',
25 The available effects in terminfo mode are 'blink', 'bold', 'dim',
26 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
26 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
27 ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
27 ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
28 'underline'. How each is rendered depends on the terminal emulator.
28 'underline'. How each is rendered depends on the terminal emulator.
29 Some may not be available for a given terminal type, and will be
29 Some may not be available for a given terminal type, and will be
30 silently ignored.
30 silently ignored.
31
31
32 If the terminfo entry for your terminal is missing codes for an effect
32 If the terminfo entry for your terminal is missing codes for an effect
33 or has the wrong codes, you can add or override those codes in your
33 or has the wrong codes, you can add or override those codes in your
34 configuration::
34 configuration::
35
35
36 [color]
36 [color]
37 terminfo.dim = \E[2m
37 terminfo.dim = \E[2m
38
38
39 where '\E' is substituted with an escape character.
39 where '\E' is substituted with an escape character.
40
40
41 Labels
41 Labels
42 ------
42 ------
43
43
44 Text receives color effects depending on the labels that it has. Many
44 Text receives color effects depending on the labels that it has. Many
45 default Mercurial commands emit labelled text. You can also define
45 default Mercurial commands emit labelled text. You can also define
46 your own labels in templates using the label function, see :hg:`help
46 your own labels in templates using the label function, see :hg:`help
47 templates`. A single portion of text may have more than one label. In
47 templates`. A single portion of text may have more than one label. In
48 that case, effects given to the last label will override any other
48 that case, effects given to the last label will override any other
49 effects. This includes the special "none" effect, which nullifies
49 effects. This includes the special "none" effect, which nullifies
50 other effects.
50 other effects.
51
51
52 Labels are normally invisible. In order to see these labels and their
52 Labels are normally invisible. In order to see these labels and their
53 position in the text, use the global --color=debug option. The same
53 position in the text, use the global --color=debug option. The same
54 anchor text may be associated to multiple labels, e.g.
54 anchor text may be associated to multiple labels, e.g.
55
55
56 [log.changeset changeset.secret|changeset: 22611:6f0a53c8f587]
56 [log.changeset changeset.secret|changeset: 22611:6f0a53c8f587]
57
57
58 The following are the default effects for some default labels. Default
58 The following are the default effects for some default labels. Default
59 effects may be overridden from your configuration file::
59 effects may be overridden from your configuration file::
60
60
61 [color]
61 [color]
62 status.modified = blue bold underline red_background
62 status.modified = blue bold underline red_background
63 status.added = green bold
63 status.added = green bold
64 status.removed = red bold blue_background
64 status.removed = red bold blue_background
65 status.deleted = cyan bold underline
65 status.deleted = cyan bold underline
66 status.unknown = magenta bold underline
66 status.unknown = magenta bold underline
67 status.ignored = black bold
67 status.ignored = black bold
68
68
69 # 'none' turns off all effects
69 # 'none' turns off all effects
70 status.clean = none
70 status.clean = none
71 status.copied = none
71 status.copied = none
72
72
73 qseries.applied = blue bold underline
73 qseries.applied = blue bold underline
74 qseries.unapplied = black bold
74 qseries.unapplied = black bold
75 qseries.missing = red bold
75 qseries.missing = red bold
76
76
77 diff.diffline = bold
77 diff.diffline = bold
78 diff.extended = cyan bold
78 diff.extended = cyan bold
79 diff.file_a = red bold
79 diff.file_a = red bold
80 diff.file_b = green bold
80 diff.file_b = green bold
81 diff.hunk = magenta
81 diff.hunk = magenta
82 diff.deleted = red
82 diff.deleted = red
83 diff.inserted = green
83 diff.inserted = green
84 diff.changed = white
84 diff.changed = white
85 diff.tab =
85 diff.tab =
86 diff.trailingwhitespace = bold red_background
86 diff.trailingwhitespace = bold red_background
87
87
88 # Blank so it inherits the style of the surrounding label
88 # Blank so it inherits the style of the surrounding label
89 changeset.public =
89 changeset.public =
90 changeset.draft =
90 changeset.draft =
91 changeset.secret =
91 changeset.secret =
92
92
93 resolve.unresolved = red bold
93 resolve.unresolved = red bold
94 resolve.resolved = green bold
94 resolve.resolved = green bold
95
95
96 bookmarks.active = green
96 bookmarks.active = green
97
97
98 branches.active = none
98 branches.active = none
99 branches.closed = black bold
99 branches.closed = black bold
100 branches.current = green
100 branches.current = green
101 branches.inactive = none
101 branches.inactive = none
102
102
103 tags.normal = green
103 tags.normal = green
104 tags.local = black bold
104 tags.local = black bold
105
105
106 rebase.rebased = blue
106 rebase.rebased = blue
107 rebase.remaining = red bold
107 rebase.remaining = red bold
108
108
109 shelve.age = cyan
109 shelve.age = cyan
110 shelve.newest = green bold
110 shelve.newest = green bold
111 shelve.name = blue bold
111 shelve.name = blue bold
112
112
113 histedit.remaining = red bold
113 histedit.remaining = red bold
114
114
115 Custom colors
115 Custom colors
116 -------------
116 -------------
117
117
118 Because there are only eight standard colors, this module allows you
118 Because there are only eight standard colors, this module allows you
119 to define color names for other color slots which might be available
119 to define color names for other color slots which might be available
120 for your terminal type, assuming terminfo mode. For instance::
120 for your terminal type, assuming terminfo mode. For instance::
121
121
122 color.brightblue = 12
122 color.brightblue = 12
123 color.pink = 207
123 color.pink = 207
124 color.orange = 202
124 color.orange = 202
125
125
126 to set 'brightblue' to color slot 12 (useful for 16 color terminals
126 to set 'brightblue' to color slot 12 (useful for 16 color terminals
127 that have brighter colors defined in the upper eight) and, 'pink' and
127 that have brighter colors defined in the upper eight) and, 'pink' and
128 'orange' to colors in 256-color xterm's default color cube. These
128 'orange' to colors in 256-color xterm's default color cube. These
129 defined colors may then be used as any of the pre-defined eight,
129 defined colors may then be used as any of the pre-defined eight,
130 including appending '_background' to set the background to that color.
130 including appending '_background' to set the background to that color.
131
131
132 Modes
132 Modes
133 -----
133 -----
134
134
135 By default, the color extension will use ANSI mode (or win32 mode on
135 By default, the color extension will use ANSI mode (or win32 mode on
136 Windows) if it detects a terminal. To override auto mode (to enable
136 Windows) if it detects a terminal. To override auto mode (to enable
137 terminfo mode, for example), set the following configuration option::
137 terminfo mode, for example), set the following configuration option::
138
138
139 [color]
139 [color]
140 mode = terminfo
140 mode = terminfo
141
141
142 Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will
142 Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will
143 disable color.
143 disable color.
144
144
145 Note that on some systems, terminfo mode may cause problems when using
145 Note that on some systems, terminfo mode may cause problems when using
146 color with the pager extension and less -R. less with the -R option
146 color with the pager extension and less -R. less with the -R option
147 will only display ECMA-48 color codes, and terminfo mode may sometimes
147 will only display ECMA-48 color codes, and terminfo mode may sometimes
148 emit codes that less doesn't understand. You can work around this by
148 emit codes that less doesn't understand. You can work around this by
149 either using ansi mode (or auto mode), or by using less -r (which will
149 either using ansi mode (or auto mode), or by using less -r (which will
150 pass through all terminal control codes, not just color control
150 pass through all terminal control codes, not just color control
151 codes).
151 codes).
152
152
153 On some systems (such as MSYS in Windows), the terminal may support
153 On some systems (such as MSYS in Windows), the terminal may support
154 a different color mode than the pager (activated via the "pager"
154 a different color mode than the pager (activated via the "pager"
155 extension). It is possible to define separate modes depending on whether
155 extension). It is possible to define separate modes depending on whether
156 the pager is active::
156 the pager is active::
157
157
158 [color]
158 [color]
159 mode = auto
159 mode = auto
160 pagermode = ansi
160 pagermode = ansi
161
161
162 If ``pagermode`` is not defined, the ``mode`` will be used.
162 If ``pagermode`` is not defined, the ``mode`` will be used.
163 '''
163 '''
164
164
165 from __future__ import absolute_import
165 from __future__ import absolute_import
166
166
167 try:
167 try:
168 import curses
168 import curses
169 curses.COLOR_BLACK # force import
169 curses.COLOR_BLACK # force import
170 except ImportError:
170 except ImportError:
171 curses = None
171 curses = None
172
172
173 from mercurial.i18n import _
173 from mercurial.i18n import _
174 from mercurial import (
174 from mercurial import (
175 cmdutil,
175 cmdutil,
176 color,
176 color,
177 commands,
177 commands,
178 dispatch,
178 dispatch,
179 encoding,
179 encoding,
180 extensions,
180 extensions,
181 pycompat,
181 pycompat,
182 subrepo,
182 subrepo,
183 ui as uimod,
183 ui as uimod,
184 util,
184 util,
185 )
185 )
186
186
187 cmdtable = {}
187 cmdtable = {}
188 command = cmdutil.command(cmdtable)
188 command = cmdutil.command(cmdtable)
189 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
189 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
190 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
190 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
191 # be specifying the version(s) of Mercurial they are tested with, or
191 # be specifying the version(s) of Mercurial they are tested with, or
192 # leave the attribute unspecified.
192 # leave the attribute unspecified.
193 testedwith = 'ships-with-hg-core'
193 testedwith = 'ships-with-hg-core'
194
194
195 def _terminfosetup(ui, mode):
195 def _terminfosetup(ui, mode):
196 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
196 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
197
197
198 # If we failed to load curses, we go ahead and return.
198 # If we failed to load curses, we go ahead and return.
199 if not color._terminfo_params:
199 if not color._terminfo_params:
200 return
200 return
201 # Otherwise, see what the config file says.
201 # Otherwise, see what the config file says.
202 if mode not in ('auto', 'terminfo'):
202 if mode not in ('auto', 'terminfo'):
203 return
203 return
204
204
205 color._terminfo_params.update((key[6:], (False, int(val), ''))
205 color._terminfo_params.update((key[6:], (False, int(val), ''))
206 for key, val in ui.configitems('color')
206 for key, val in ui.configitems('color')
207 if key.startswith('color.'))
207 if key.startswith('color.'))
208 color._terminfo_params.update((key[9:],
208 color._terminfo_params.update((key[9:],
209 (True, '', val.replace('\\E', '\x1b')))
209 (True, '', val.replace('\\E', '\x1b')))
210 for key, val in ui.configitems('color')
210 for key, val in ui.configitems('color')
211 if key.startswith('terminfo.'))
211 if key.startswith('terminfo.'))
212
212
213 try:
213 try:
214 curses.setupterm()
214 curses.setupterm()
215 except curses.error as e:
215 except curses.error as e:
216 color._terminfo_params.clear()
216 color._terminfo_params.clear()
217 return
217 return
218
218
219 for key, (b, e, c) in color._terminfo_params.items():
219 for key, (b, e, c) in color._terminfo_params.items():
220 if not b:
220 if not b:
221 continue
221 continue
222 if not c and not curses.tigetstr(e):
222 if not c and not curses.tigetstr(e):
223 # Most terminals don't support dim, invis, etc, so don't be
223 # Most terminals don't support dim, invis, etc, so don't be
224 # noisy and use ui.debug().
224 # noisy and use ui.debug().
225 ui.debug("no terminfo entry for %s\n" % e)
225 ui.debug("no terminfo entry for %s\n" % e)
226 del color._terminfo_params[key]
226 del color._terminfo_params[key]
227 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
227 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
228 # Only warn about missing terminfo entries if we explicitly asked for
228 # Only warn about missing terminfo entries if we explicitly asked for
229 # terminfo mode.
229 # terminfo mode.
230 if mode == "terminfo":
230 if mode == "terminfo":
231 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
231 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
232 "ECMA-48 color\n"))
232 "ECMA-48 color\n"))
233 color._terminfo_params.clear()
233 color._terminfo_params.clear()
234
234
235 def _modesetup(ui, coloropt):
235 def _modesetup(ui, coloropt):
236 if coloropt == 'debug':
236 if coloropt == 'debug':
237 return 'debug'
237 return 'debug'
238
238
239 auto = (coloropt == 'auto')
239 auto = (coloropt == 'auto')
240 always = not auto and util.parsebool(coloropt)
240 always = not auto and util.parsebool(coloropt)
241 if not always and not auto:
241 if not always and not auto:
242 return None
242 return None
243
243
244 formatted = (always or (encoding.environ.get('TERM') != 'dumb'
244 formatted = (always or (encoding.environ.get('TERM') != 'dumb'
245 and ui.formatted()))
245 and ui.formatted()))
246
246
247 mode = ui.config('color', 'mode', 'auto')
247 mode = ui.config('color', 'mode', 'auto')
248
248
249 # If pager is active, color.pagermode overrides color.mode.
249 # If pager is active, color.pagermode overrides color.mode.
250 if getattr(ui, 'pageractive', False):
250 if getattr(ui, 'pageractive', False):
251 mode = ui.config('color', 'pagermode', mode)
251 mode = ui.config('color', 'pagermode', mode)
252
252
253 realmode = mode
253 realmode = mode
254 if mode == 'auto':
254 if mode == 'auto':
255 if pycompat.osname == 'nt':
255 if pycompat.osname == 'nt':
256 term = encoding.environ.get('TERM')
256 term = encoding.environ.get('TERM')
257 # TERM won't be defined in a vanilla cmd.exe environment.
257 # TERM won't be defined in a vanilla cmd.exe environment.
258
258
259 # UNIX-like environments on Windows such as Cygwin and MSYS will
259 # UNIX-like environments on Windows such as Cygwin and MSYS will
260 # set TERM. They appear to make a best effort attempt at setting it
260 # set TERM. They appear to make a best effort attempt at setting it
261 # to something appropriate. However, not all environments with TERM
261 # to something appropriate. However, not all environments with TERM
262 # defined support ANSI. Since "ansi" could result in terminal
262 # defined support ANSI. Since "ansi" could result in terminal
263 # gibberish, we error on the side of selecting "win32". However, if
263 # gibberish, we error on the side of selecting "win32". However, if
264 # w32effects is not defined, we almost certainly don't support
264 # w32effects is not defined, we almost certainly don't support
265 # "win32", so don't even try.
265 # "win32", so don't even try.
266 if (term and 'xterm' in term) or not w32effects:
266 if (term and 'xterm' in term) or not w32effects:
267 realmode = 'ansi'
267 realmode = 'ansi'
268 else:
268 else:
269 realmode = 'win32'
269 realmode = 'win32'
270 else:
270 else:
271 realmode = 'ansi'
271 realmode = 'ansi'
272
272
273 def modewarn():
273 def modewarn():
274 # only warn if color.mode was explicitly set and we're in
274 # only warn if color.mode was explicitly set and we're in
275 # a formatted terminal
275 # a formatted terminal
276 if mode == realmode and ui.formatted():
276 if mode == realmode and ui.formatted():
277 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
277 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
278
278
279 if realmode == 'win32':
279 if realmode == 'win32':
280 color._terminfo_params.clear()
280 color._terminfo_params.clear()
281 if not w32effects:
281 if not w32effects:
282 modewarn()
282 modewarn()
283 return None
283 return None
284 color._effects.update(w32effects)
284 color._effects.update(w32effects)
285 elif realmode == 'ansi':
285 elif realmode == 'ansi':
286 color._terminfo_params.clear()
286 color._terminfo_params.clear()
287 elif realmode == 'terminfo':
287 elif realmode == 'terminfo':
288 _terminfosetup(ui, mode)
288 _terminfosetup(ui, mode)
289 if not color._terminfo_params:
289 if not color._terminfo_params:
290 ## FIXME Shouldn't we return None in this case too?
290 ## FIXME Shouldn't we return None in this case too?
291 modewarn()
291 modewarn()
292 realmode = 'ansi'
292 realmode = 'ansi'
293 else:
293 else:
294 return None
294 return None
295
295
296 if always or (auto and formatted):
296 if always or (auto and formatted):
297 return realmode
297 return realmode
298 return None
298 return None
299
299
300 def render_effects(text, effects):
301 'Wrap text in commands to turn on each effect.'
302 if not text:
303 return text
304 if not color._terminfo_params:
305 start = [str(color._effects[e]) for e in ['none'] + effects.split()]
306 start = '\033[' + ';'.join(start) + 'm'
307 stop = '\033[' + str(color._effects['none']) + 'm'
308 else:
309 start = ''.join(color._effect_str(effect)
310 for effect in ['none'] + effects.split())
311 stop = color._effect_str('none')
312 return ''.join([start, text, stop])
313
314 class colorui(uimod.ui):
300 class colorui(uimod.ui):
315 _colormode = 'ansi'
301 _colormode = 'ansi'
316 def write(self, *args, **opts):
302 def write(self, *args, **opts):
317 if self._colormode is None:
303 if self._colormode is None:
318 return super(colorui, self).write(*args, **opts)
304 return super(colorui, self).write(*args, **opts)
319
305
320 label = opts.get('label', '')
306 label = opts.get('label', '')
321 if self._buffers and not opts.get('prompt', False):
307 if self._buffers and not opts.get('prompt', False):
322 if self._bufferapplylabels:
308 if self._bufferapplylabels:
323 self._buffers[-1].extend(self.label(a, label) for a in args)
309 self._buffers[-1].extend(self.label(a, label) for a in args)
324 else:
310 else:
325 self._buffers[-1].extend(args)
311 self._buffers[-1].extend(args)
326 elif self._colormode == 'win32':
312 elif self._colormode == 'win32':
327 for a in args:
313 for a in args:
328 win32print(a, super(colorui, self).write, **opts)
314 win32print(a, super(colorui, self).write, **opts)
329 else:
315 else:
330 return super(colorui, self).write(
316 return super(colorui, self).write(
331 *[self.label(a, label) for a in args], **opts)
317 *[self.label(a, label) for a in args], **opts)
332
318
333 def write_err(self, *args, **opts):
319 def write_err(self, *args, **opts):
334 if self._colormode is None:
320 if self._colormode is None:
335 return super(colorui, self).write_err(*args, **opts)
321 return super(colorui, self).write_err(*args, **opts)
336
322
337 label = opts.get('label', '')
323 label = opts.get('label', '')
338 if self._bufferstates and self._bufferstates[-1][0]:
324 if self._bufferstates and self._bufferstates[-1][0]:
339 return self.write(*args, **opts)
325 return self.write(*args, **opts)
340 if self._colormode == 'win32':
326 if self._colormode == 'win32':
341 for a in args:
327 for a in args:
342 win32print(a, super(colorui, self).write_err, **opts)
328 win32print(a, super(colorui, self).write_err, **opts)
343 else:
329 else:
344 return super(colorui, self).write_err(
330 return super(colorui, self).write_err(
345 *[self.label(a, label) for a in args], **opts)
331 *[self.label(a, label) for a in args], **opts)
346
332
347 def showlabel(self, msg, label):
333 def showlabel(self, msg, label):
348 if label and msg:
334 if label and msg:
349 if msg[-1] == '\n':
335 if msg[-1] == '\n':
350 return "[%s|%s]\n" % (label, msg[:-1])
336 return "[%s|%s]\n" % (label, msg[:-1])
351 else:
337 else:
352 return "[%s|%s]" % (label, msg)
338 return "[%s|%s]" % (label, msg)
353 else:
339 else:
354 return msg
340 return msg
355
341
356 def label(self, msg, label):
342 def label(self, msg, label):
357 if self._colormode is None:
343 if self._colormode is None:
358 return super(colorui, self).label(msg, label)
344 return super(colorui, self).label(msg, label)
359
345
360 if self._colormode == 'debug':
346 if self._colormode == 'debug':
361 return self.showlabel(msg, label)
347 return self.showlabel(msg, label)
362
348
363 effects = []
349 effects = []
364 for l in label.split():
350 for l in label.split():
365 s = color._styles.get(l, '')
351 s = color._styles.get(l, '')
366 if s:
352 if s:
367 effects.append(s)
353 effects.append(s)
368 elif color.valideffect(l):
354 elif color.valideffect(l):
369 effects.append(l)
355 effects.append(l)
370 effects = ' '.join(effects)
356 effects = ' '.join(effects)
371 if effects:
357 if effects:
372 return '\n'.join([render_effects(line, effects)
358 return '\n'.join([color._render_effects(line, effects)
373 for line in msg.split('\n')])
359 for line in msg.split('\n')])
374 return msg
360 return msg
375
361
376 def uisetup(ui):
362 def uisetup(ui):
377 if ui.plain():
363 if ui.plain():
378 return
364 return
379 if not isinstance(ui, colorui):
365 if not isinstance(ui, colorui):
380 colorui.__bases__ = (ui.__class__,)
366 colorui.__bases__ = (ui.__class__,)
381 ui.__class__ = colorui
367 ui.__class__ = colorui
382 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
368 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
383 mode = _modesetup(ui_, opts['color'])
369 mode = _modesetup(ui_, opts['color'])
384 colorui._colormode = mode
370 colorui._colormode = mode
385 if mode and mode != 'debug':
371 if mode and mode != 'debug':
386 color.configstyles(ui_)
372 color.configstyles(ui_)
387 return orig(ui_, opts, cmd, cmdfunc)
373 return orig(ui_, opts, cmd, cmdfunc)
388 def colorgit(orig, gitsub, commands, env=None, stream=False, cwd=None):
374 def colorgit(orig, gitsub, commands, env=None, stream=False, cwd=None):
389 if gitsub.ui._colormode and len(commands) and commands[0] == "diff":
375 if gitsub.ui._colormode and len(commands) and commands[0] == "diff":
390 # insert the argument in the front,
376 # insert the argument in the front,
391 # the end of git diff arguments is used for paths
377 # the end of git diff arguments is used for paths
392 commands.insert(1, '--color')
378 commands.insert(1, '--color')
393 return orig(gitsub, commands, env, stream, cwd)
379 return orig(gitsub, commands, env, stream, cwd)
394 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
380 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
395 extensions.wrapfunction(subrepo.gitsubrepo, '_gitnodir', colorgit)
381 extensions.wrapfunction(subrepo.gitsubrepo, '_gitnodir', colorgit)
396
382
397 def extsetup(ui):
383 def extsetup(ui):
398 commands.globalopts.append(
384 commands.globalopts.append(
399 ('', 'color', 'auto',
385 ('', 'color', 'auto',
400 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
386 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
401 # and should not be translated
387 # and should not be translated
402 _("when to colorize (boolean, always, auto, never, or debug)"),
388 _("when to colorize (boolean, always, auto, never, or debug)"),
403 _('TYPE')))
389 _('TYPE')))
404
390
405 @command('debugcolor',
391 @command('debugcolor',
406 [('', 'style', None, _('show all configured styles'))],
392 [('', 'style', None, _('show all configured styles'))],
407 'hg debugcolor')
393 'hg debugcolor')
408 def debugcolor(ui, repo, **opts):
394 def debugcolor(ui, repo, **opts):
409 """show available color, effects or style"""
395 """show available color, effects or style"""
410 ui.write(('color mode: %s\n') % ui._colormode)
396 ui.write(('color mode: %s\n') % ui._colormode)
411 if opts.get('style'):
397 if opts.get('style'):
412 return _debugdisplaystyle(ui)
398 return _debugdisplaystyle(ui)
413 else:
399 else:
414 return _debugdisplaycolor(ui)
400 return _debugdisplaycolor(ui)
415
401
416 def _debugdisplaycolor(ui):
402 def _debugdisplaycolor(ui):
417 oldstyle = color._styles.copy()
403 oldstyle = color._styles.copy()
418 try:
404 try:
419 color._styles.clear()
405 color._styles.clear()
420 for effect in color._effects.keys():
406 for effect in color._effects.keys():
421 color._styles[effect] = effect
407 color._styles[effect] = effect
422 if color._terminfo_params:
408 if color._terminfo_params:
423 for k, v in ui.configitems('color'):
409 for k, v in ui.configitems('color'):
424 if k.startswith('color.'):
410 if k.startswith('color.'):
425 color._styles[k] = k[6:]
411 color._styles[k] = k[6:]
426 elif k.startswith('terminfo.'):
412 elif k.startswith('terminfo.'):
427 color._styles[k] = k[9:]
413 color._styles[k] = k[9:]
428 ui.write(_('available colors:\n'))
414 ui.write(_('available colors:\n'))
429 # sort label with a '_' after the other to group '_background' entry.
415 # sort label with a '_' after the other to group '_background' entry.
430 items = sorted(color._styles.items(),
416 items = sorted(color._styles.items(),
431 key=lambda i: ('_' in i[0], i[0], i[1]))
417 key=lambda i: ('_' in i[0], i[0], i[1]))
432 for colorname, label in items:
418 for colorname, label in items:
433 ui.write(('%s\n') % colorname, label=label)
419 ui.write(('%s\n') % colorname, label=label)
434 finally:
420 finally:
435 color._styles.clear()
421 color._styles.clear()
436 color._styles.update(oldstyle)
422 color._styles.update(oldstyle)
437
423
438 def _debugdisplaystyle(ui):
424 def _debugdisplaystyle(ui):
439 ui.write(_('available style:\n'))
425 ui.write(_('available style:\n'))
440 width = max(len(s) for s in color._styles)
426 width = max(len(s) for s in color._styles)
441 for label, effects in sorted(color._styles.items()):
427 for label, effects in sorted(color._styles.items()):
442 ui.write('%s' % label, label=label)
428 ui.write('%s' % label, label=label)
443 if effects:
429 if effects:
444 # 50
430 # 50
445 ui.write(': ')
431 ui.write(': ')
446 ui.write(' ' * (max(0, width - len(label))))
432 ui.write(' ' * (max(0, width - len(label))))
447 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
433 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
448 ui.write('\n')
434 ui.write('\n')
449
435
450 if pycompat.osname != 'nt':
436 if pycompat.osname != 'nt':
451 w32effects = None
437 w32effects = None
452 else:
438 else:
453 import ctypes
439 import ctypes
454 import re
440 import re
455
441
456 _kernel32 = ctypes.windll.kernel32
442 _kernel32 = ctypes.windll.kernel32
457
443
458 _WORD = ctypes.c_ushort
444 _WORD = ctypes.c_ushort
459
445
460 _INVALID_HANDLE_VALUE = -1
446 _INVALID_HANDLE_VALUE = -1
461
447
462 class _COORD(ctypes.Structure):
448 class _COORD(ctypes.Structure):
463 _fields_ = [('X', ctypes.c_short),
449 _fields_ = [('X', ctypes.c_short),
464 ('Y', ctypes.c_short)]
450 ('Y', ctypes.c_short)]
465
451
466 class _SMALL_RECT(ctypes.Structure):
452 class _SMALL_RECT(ctypes.Structure):
467 _fields_ = [('Left', ctypes.c_short),
453 _fields_ = [('Left', ctypes.c_short),
468 ('Top', ctypes.c_short),
454 ('Top', ctypes.c_short),
469 ('Right', ctypes.c_short),
455 ('Right', ctypes.c_short),
470 ('Bottom', ctypes.c_short)]
456 ('Bottom', ctypes.c_short)]
471
457
472 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
458 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
473 _fields_ = [('dwSize', _COORD),
459 _fields_ = [('dwSize', _COORD),
474 ('dwCursorPosition', _COORD),
460 ('dwCursorPosition', _COORD),
475 ('wAttributes', _WORD),
461 ('wAttributes', _WORD),
476 ('srWindow', _SMALL_RECT),
462 ('srWindow', _SMALL_RECT),
477 ('dwMaximumWindowSize', _COORD)]
463 ('dwMaximumWindowSize', _COORD)]
478
464
479 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
465 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
480 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12
466 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12
481
467
482 _FOREGROUND_BLUE = 0x0001
468 _FOREGROUND_BLUE = 0x0001
483 _FOREGROUND_GREEN = 0x0002
469 _FOREGROUND_GREEN = 0x0002
484 _FOREGROUND_RED = 0x0004
470 _FOREGROUND_RED = 0x0004
485 _FOREGROUND_INTENSITY = 0x0008
471 _FOREGROUND_INTENSITY = 0x0008
486
472
487 _BACKGROUND_BLUE = 0x0010
473 _BACKGROUND_BLUE = 0x0010
488 _BACKGROUND_GREEN = 0x0020
474 _BACKGROUND_GREEN = 0x0020
489 _BACKGROUND_RED = 0x0040
475 _BACKGROUND_RED = 0x0040
490 _BACKGROUND_INTENSITY = 0x0080
476 _BACKGROUND_INTENSITY = 0x0080
491
477
492 _COMMON_LVB_REVERSE_VIDEO = 0x4000
478 _COMMON_LVB_REVERSE_VIDEO = 0x4000
493 _COMMON_LVB_UNDERSCORE = 0x8000
479 _COMMON_LVB_UNDERSCORE = 0x8000
494
480
495 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
481 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
496 w32effects = {
482 w32effects = {
497 'none': -1,
483 'none': -1,
498 'black': 0,
484 'black': 0,
499 'red': _FOREGROUND_RED,
485 'red': _FOREGROUND_RED,
500 'green': _FOREGROUND_GREEN,
486 'green': _FOREGROUND_GREEN,
501 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
487 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
502 'blue': _FOREGROUND_BLUE,
488 'blue': _FOREGROUND_BLUE,
503 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
489 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
504 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
490 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
505 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
491 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
506 'bold': _FOREGROUND_INTENSITY,
492 'bold': _FOREGROUND_INTENSITY,
507 'black_background': 0x100, # unused value > 0x0f
493 'black_background': 0x100, # unused value > 0x0f
508 'red_background': _BACKGROUND_RED,
494 'red_background': _BACKGROUND_RED,
509 'green_background': _BACKGROUND_GREEN,
495 'green_background': _BACKGROUND_GREEN,
510 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
496 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
511 'blue_background': _BACKGROUND_BLUE,
497 'blue_background': _BACKGROUND_BLUE,
512 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
498 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
513 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
499 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
514 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
500 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
515 _BACKGROUND_BLUE),
501 _BACKGROUND_BLUE),
516 'bold_background': _BACKGROUND_INTENSITY,
502 'bold_background': _BACKGROUND_INTENSITY,
517 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
503 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
518 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
504 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
519 }
505 }
520
506
521 passthrough = set([_FOREGROUND_INTENSITY,
507 passthrough = set([_FOREGROUND_INTENSITY,
522 _BACKGROUND_INTENSITY,
508 _BACKGROUND_INTENSITY,
523 _COMMON_LVB_UNDERSCORE,
509 _COMMON_LVB_UNDERSCORE,
524 _COMMON_LVB_REVERSE_VIDEO])
510 _COMMON_LVB_REVERSE_VIDEO])
525
511
526 stdout = _kernel32.GetStdHandle(
512 stdout = _kernel32.GetStdHandle(
527 _STD_OUTPUT_HANDLE) # don't close the handle returned
513 _STD_OUTPUT_HANDLE) # don't close the handle returned
528 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
514 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
529 w32effects = None
515 w32effects = None
530 else:
516 else:
531 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
517 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
532 if not _kernel32.GetConsoleScreenBufferInfo(
518 if not _kernel32.GetConsoleScreenBufferInfo(
533 stdout, ctypes.byref(csbi)):
519 stdout, ctypes.byref(csbi)):
534 # stdout may not support GetConsoleScreenBufferInfo()
520 # stdout may not support GetConsoleScreenBufferInfo()
535 # when called from subprocess or redirected
521 # when called from subprocess or redirected
536 w32effects = None
522 w32effects = None
537 else:
523 else:
538 origattr = csbi.wAttributes
524 origattr = csbi.wAttributes
539 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
525 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
540 re.MULTILINE | re.DOTALL)
526 re.MULTILINE | re.DOTALL)
541
527
542 def win32print(text, orig, **opts):
528 def win32print(text, orig, **opts):
543 label = opts.get('label', '')
529 label = opts.get('label', '')
544 attr = origattr
530 attr = origattr
545
531
546 def mapcolor(val, attr):
532 def mapcolor(val, attr):
547 if val == -1:
533 if val == -1:
548 return origattr
534 return origattr
549 elif val in passthrough:
535 elif val in passthrough:
550 return attr | val
536 return attr | val
551 elif val > 0x0f:
537 elif val > 0x0f:
552 return (val & 0x70) | (attr & 0x8f)
538 return (val & 0x70) | (attr & 0x8f)
553 else:
539 else:
554 return (val & 0x07) | (attr & 0xf8)
540 return (val & 0x07) | (attr & 0xf8)
555
541
556 # determine console attributes based on labels
542 # determine console attributes based on labels
557 for l in label.split():
543 for l in label.split():
558 style = color._styles.get(l, '')
544 style = color._styles.get(l, '')
559 for effect in style.split():
545 for effect in style.split():
560 try:
546 try:
561 attr = mapcolor(w32effects[effect], attr)
547 attr = mapcolor(w32effects[effect], attr)
562 except KeyError:
548 except KeyError:
563 # w32effects could not have certain attributes so we skip
549 # w32effects could not have certain attributes so we skip
564 # them if not found
550 # them if not found
565 pass
551 pass
566 # hack to ensure regexp finds data
552 # hack to ensure regexp finds data
567 if not text.startswith('\033['):
553 if not text.startswith('\033['):
568 text = '\033[m' + text
554 text = '\033[m' + text
569
555
570 # Look for ANSI-like codes embedded in text
556 # Look for ANSI-like codes embedded in text
571 m = re.match(ansire, text)
557 m = re.match(ansire, text)
572
558
573 try:
559 try:
574 while m:
560 while m:
575 for sattr in m.group(1).split(';'):
561 for sattr in m.group(1).split(';'):
576 if sattr:
562 if sattr:
577 attr = mapcolor(int(sattr), attr)
563 attr = mapcolor(int(sattr), attr)
578 _kernel32.SetConsoleTextAttribute(stdout, attr)
564 _kernel32.SetConsoleTextAttribute(stdout, attr)
579 orig(m.group(2), **opts)
565 orig(m.group(2), **opts)
580 m = re.match(ansire, m.group(3))
566 m = re.match(ansire, m.group(3))
581 finally:
567 finally:
582 # Explicitly reset original attributes
568 # Explicitly reset original attributes
583 _kernel32.SetConsoleTextAttribute(stdout, origattr)
569 _kernel32.SetConsoleTextAttribute(stdout, origattr)
@@ -1,160 +1,174
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 try:
12 try:
13 import curses
13 import curses
14 # Mapping from effect name to terminfo attribute name (or raw code) or
14 # Mapping from effect name to terminfo attribute name (or raw code) or
15 # color number. This will also force-load the curses module.
15 # color number. This will also force-load the curses module.
16 _terminfo_params = {'none': (True, 'sgr0', ''),
16 _terminfo_params = {'none': (True, 'sgr0', ''),
17 'standout': (True, 'smso', ''),
17 'standout': (True, 'smso', ''),
18 'underline': (True, 'smul', ''),
18 'underline': (True, 'smul', ''),
19 'reverse': (True, 'rev', ''),
19 'reverse': (True, 'rev', ''),
20 'inverse': (True, 'rev', ''),
20 'inverse': (True, 'rev', ''),
21 'blink': (True, 'blink', ''),
21 'blink': (True, 'blink', ''),
22 'dim': (True, 'dim', ''),
22 'dim': (True, 'dim', ''),
23 'bold': (True, 'bold', ''),
23 'bold': (True, 'bold', ''),
24 'invisible': (True, 'invis', ''),
24 'invisible': (True, 'invis', ''),
25 'italic': (True, 'sitm', ''),
25 'italic': (True, 'sitm', ''),
26 'black': (False, curses.COLOR_BLACK, ''),
26 'black': (False, curses.COLOR_BLACK, ''),
27 'red': (False, curses.COLOR_RED, ''),
27 'red': (False, curses.COLOR_RED, ''),
28 'green': (False, curses.COLOR_GREEN, ''),
28 'green': (False, curses.COLOR_GREEN, ''),
29 'yellow': (False, curses.COLOR_YELLOW, ''),
29 'yellow': (False, curses.COLOR_YELLOW, ''),
30 'blue': (False, curses.COLOR_BLUE, ''),
30 'blue': (False, curses.COLOR_BLUE, ''),
31 'magenta': (False, curses.COLOR_MAGENTA, ''),
31 'magenta': (False, curses.COLOR_MAGENTA, ''),
32 'cyan': (False, curses.COLOR_CYAN, ''),
32 'cyan': (False, curses.COLOR_CYAN, ''),
33 'white': (False, curses.COLOR_WHITE, '')}
33 'white': (False, curses.COLOR_WHITE, '')}
34 except ImportError:
34 except ImportError:
35 curses = None
35 curses = None
36 _terminfo_params = {}
36 _terminfo_params = {}
37
37
38 # start and stop parameters for effects
38 # start and stop parameters for effects
39 _effects = {'none': 0,
39 _effects = {'none': 0,
40 'black': 30,
40 'black': 30,
41 'red': 31,
41 'red': 31,
42 'green': 32,
42 'green': 32,
43 'yellow': 33,
43 'yellow': 33,
44 'blue': 34,
44 'blue': 34,
45 'magenta': 35,
45 'magenta': 35,
46 'cyan': 36,
46 'cyan': 36,
47 'white': 37,
47 'white': 37,
48 'bold': 1,
48 'bold': 1,
49 'italic': 3,
49 'italic': 3,
50 'underline': 4,
50 'underline': 4,
51 'inverse': 7,
51 'inverse': 7,
52 'dim': 2,
52 'dim': 2,
53 'black_background': 40,
53 'black_background': 40,
54 'red_background': 41,
54 'red_background': 41,
55 'green_background': 42,
55 'green_background': 42,
56 'yellow_background': 43,
56 'yellow_background': 43,
57 'blue_background': 44,
57 'blue_background': 44,
58 'purple_background': 45,
58 'purple_background': 45,
59 'cyan_background': 46,
59 'cyan_background': 46,
60 'white_background': 47}
60 'white_background': 47}
61
61
62 _styles = {'grep.match': 'red bold',
62 _styles = {'grep.match': 'red bold',
63 'grep.linenumber': 'green',
63 'grep.linenumber': 'green',
64 'grep.rev': 'green',
64 'grep.rev': 'green',
65 'grep.change': 'green',
65 'grep.change': 'green',
66 'grep.sep': 'cyan',
66 'grep.sep': 'cyan',
67 'grep.filename': 'magenta',
67 'grep.filename': 'magenta',
68 'grep.user': 'magenta',
68 'grep.user': 'magenta',
69 'grep.date': 'magenta',
69 'grep.date': 'magenta',
70 'bookmarks.active': 'green',
70 'bookmarks.active': 'green',
71 'branches.active': 'none',
71 'branches.active': 'none',
72 'branches.closed': 'black bold',
72 'branches.closed': 'black bold',
73 'branches.current': 'green',
73 'branches.current': 'green',
74 'branches.inactive': 'none',
74 'branches.inactive': 'none',
75 'diff.changed': 'white',
75 'diff.changed': 'white',
76 'diff.deleted': 'red',
76 'diff.deleted': 'red',
77 'diff.diffline': 'bold',
77 'diff.diffline': 'bold',
78 'diff.extended': 'cyan bold',
78 'diff.extended': 'cyan bold',
79 'diff.file_a': 'red bold',
79 'diff.file_a': 'red bold',
80 'diff.file_b': 'green bold',
80 'diff.file_b': 'green bold',
81 'diff.hunk': 'magenta',
81 'diff.hunk': 'magenta',
82 'diff.inserted': 'green',
82 'diff.inserted': 'green',
83 'diff.tab': '',
83 'diff.tab': '',
84 'diff.trailingwhitespace': 'bold red_background',
84 'diff.trailingwhitespace': 'bold red_background',
85 'changeset.public' : '',
85 'changeset.public' : '',
86 'changeset.draft' : '',
86 'changeset.draft' : '',
87 'changeset.secret' : '',
87 'changeset.secret' : '',
88 'diffstat.deleted': 'red',
88 'diffstat.deleted': 'red',
89 'diffstat.inserted': 'green',
89 'diffstat.inserted': 'green',
90 'histedit.remaining': 'red bold',
90 'histedit.remaining': 'red bold',
91 'ui.prompt': 'yellow',
91 'ui.prompt': 'yellow',
92 'log.changeset': 'yellow',
92 'log.changeset': 'yellow',
93 'patchbomb.finalsummary': '',
93 'patchbomb.finalsummary': '',
94 'patchbomb.from': 'magenta',
94 'patchbomb.from': 'magenta',
95 'patchbomb.to': 'cyan',
95 'patchbomb.to': 'cyan',
96 'patchbomb.subject': 'green',
96 'patchbomb.subject': 'green',
97 'patchbomb.diffstats': '',
97 'patchbomb.diffstats': '',
98 'rebase.rebased': 'blue',
98 'rebase.rebased': 'blue',
99 'rebase.remaining': 'red bold',
99 'rebase.remaining': 'red bold',
100 'resolve.resolved': 'green bold',
100 'resolve.resolved': 'green bold',
101 'resolve.unresolved': 'red bold',
101 'resolve.unresolved': 'red bold',
102 'shelve.age': 'cyan',
102 'shelve.age': 'cyan',
103 'shelve.newest': 'green bold',
103 'shelve.newest': 'green bold',
104 'shelve.name': 'blue bold',
104 'shelve.name': 'blue bold',
105 'status.added': 'green bold',
105 'status.added': 'green bold',
106 'status.clean': 'none',
106 'status.clean': 'none',
107 'status.copied': 'none',
107 'status.copied': 'none',
108 'status.deleted': 'cyan bold underline',
108 'status.deleted': 'cyan bold underline',
109 'status.ignored': 'black bold',
109 'status.ignored': 'black bold',
110 'status.modified': 'blue bold',
110 'status.modified': 'blue bold',
111 'status.removed': 'red bold',
111 'status.removed': 'red bold',
112 'status.unknown': 'magenta bold underline',
112 'status.unknown': 'magenta bold underline',
113 'tags.normal': 'green',
113 'tags.normal': 'green',
114 'tags.local': 'black bold'}
114 'tags.local': 'black bold'}
115
115
116 def loadcolortable(ui, extname, colortable):
116 def loadcolortable(ui, extname, colortable):
117 _styles.update(colortable)
117 _styles.update(colortable)
118
118
119 def configstyles(ui):
119 def configstyles(ui):
120 for status, cfgeffects in ui.configitems('color'):
120 for status, cfgeffects in ui.configitems('color'):
121 if '.' not in status or status.startswith(('color.', 'terminfo.')):
121 if '.' not in status or status.startswith(('color.', 'terminfo.')):
122 continue
122 continue
123 cfgeffects = ui.configlist('color', status)
123 cfgeffects = ui.configlist('color', status)
124 if cfgeffects:
124 if cfgeffects:
125 good = []
125 good = []
126 for e in cfgeffects:
126 for e in cfgeffects:
127 if valideffect(e):
127 if valideffect(e):
128 good.append(e)
128 good.append(e)
129 else:
129 else:
130 ui.warn(_("ignoring unknown color/effect %r "
130 ui.warn(_("ignoring unknown color/effect %r "
131 "(configured in color.%s)\n")
131 "(configured in color.%s)\n")
132 % (e, status))
132 % (e, status))
133 _styles[status] = ' '.join(good)
133 _styles[status] = ' '.join(good)
134
134
135 def valideffect(effect):
135 def valideffect(effect):
136 'Determine if the effect is valid or not.'
136 'Determine if the effect is valid or not.'
137 return ((not _terminfo_params and effect in _effects)
137 return ((not _terminfo_params and effect in _effects)
138 or (effect in _terminfo_params
138 or (effect in _terminfo_params
139 or effect[:-11] in _terminfo_params))
139 or effect[:-11] in _terminfo_params))
140
140
141 def _effect_str(effect):
141 def _effect_str(effect):
142 '''Helper function for render_effects().'''
142 '''Helper function for render_effects().'''
143
143
144 bg = False
144 bg = False
145 if effect.endswith('_background'):
145 if effect.endswith('_background'):
146 bg = True
146 bg = True
147 effect = effect[:-11]
147 effect = effect[:-11]
148 try:
148 try:
149 attr, val, termcode = _terminfo_params[effect]
149 attr, val, termcode = _terminfo_params[effect]
150 except KeyError:
150 except KeyError:
151 return ''
151 return ''
152 if attr:
152 if attr:
153 if termcode:
153 if termcode:
154 return termcode
154 return termcode
155 else:
155 else:
156 return curses.tigetstr(val)
156 return curses.tigetstr(val)
157 elif bg:
157 elif bg:
158 return curses.tparm(curses.tigetstr('setab'), val)
158 return curses.tparm(curses.tigetstr('setab'), val)
159 else:
159 else:
160 return curses.tparm(curses.tigetstr('setaf'), val)
160 return curses.tparm(curses.tigetstr('setaf'), val)
161
162 def _render_effects(text, effects):
163 'Wrap text in commands to turn on each effect.'
164 if not text:
165 return text
166 if not _terminfo_params:
167 start = [str(_effects[e]) for e in ['none'] + effects.split()]
168 start = '\033[' + ';'.join(start) + 'm'
169 stop = '\033[' + str(_effects['none']) + 'm'
170 else:
171 start = ''.join(_effect_str(effect)
172 for effect in ['none'] + effects.split())
173 stop = _effect_str('none')
174 return ''.join([start, text, stop])
General Comments 0
You need to be logged in to leave comments. Login now