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