##// END OF EJS Templates
diff: restore original color scheme for worddiff...
Yuya Nishihara -
r37817:1770d8b3 stable
parent child Browse files
Show More
@@ -1,534 +1,534 b''
1 # utility for color output for Mercurial commands
1 # utility for color output for Mercurial commands
2 #
2 #
3 # Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.com> and other
3 # Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.com> and other
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import re
10 import re
11
11
12 from .i18n import _
12 from .i18n import _
13
13
14 from . import (
14 from . import (
15 encoding,
15 encoding,
16 pycompat,
16 pycompat,
17 )
17 )
18
18
19 from .utils import (
19 from .utils import (
20 stringutil,
20 stringutil,
21 )
21 )
22
22
23 try:
23 try:
24 import curses
24 import curses
25 # Mapping from effect name to terminfo attribute name (or raw code) or
25 # Mapping from effect name to terminfo attribute name (or raw code) or
26 # color number. This will also force-load the curses module.
26 # color number. This will also force-load the curses module.
27 _baseterminfoparams = {
27 _baseterminfoparams = {
28 'none': (True, 'sgr0', ''),
28 'none': (True, 'sgr0', ''),
29 'standout': (True, 'smso', ''),
29 'standout': (True, 'smso', ''),
30 'underline': (True, 'smul', ''),
30 'underline': (True, 'smul', ''),
31 'reverse': (True, 'rev', ''),
31 'reverse': (True, 'rev', ''),
32 'inverse': (True, 'rev', ''),
32 'inverse': (True, 'rev', ''),
33 'blink': (True, 'blink', ''),
33 'blink': (True, 'blink', ''),
34 'dim': (True, 'dim', ''),
34 'dim': (True, 'dim', ''),
35 'bold': (True, 'bold', ''),
35 'bold': (True, 'bold', ''),
36 'invisible': (True, 'invis', ''),
36 'invisible': (True, 'invis', ''),
37 'italic': (True, 'sitm', ''),
37 'italic': (True, 'sitm', ''),
38 'black': (False, curses.COLOR_BLACK, ''),
38 'black': (False, curses.COLOR_BLACK, ''),
39 'red': (False, curses.COLOR_RED, ''),
39 'red': (False, curses.COLOR_RED, ''),
40 'green': (False, curses.COLOR_GREEN, ''),
40 'green': (False, curses.COLOR_GREEN, ''),
41 'yellow': (False, curses.COLOR_YELLOW, ''),
41 'yellow': (False, curses.COLOR_YELLOW, ''),
42 'blue': (False, curses.COLOR_BLUE, ''),
42 'blue': (False, curses.COLOR_BLUE, ''),
43 'magenta': (False, curses.COLOR_MAGENTA, ''),
43 'magenta': (False, curses.COLOR_MAGENTA, ''),
44 'cyan': (False, curses.COLOR_CYAN, ''),
44 'cyan': (False, curses.COLOR_CYAN, ''),
45 'white': (False, curses.COLOR_WHITE, ''),
45 'white': (False, curses.COLOR_WHITE, ''),
46 }
46 }
47 except ImportError:
47 except ImportError:
48 curses = None
48 curses = None
49 _baseterminfoparams = {}
49 _baseterminfoparams = {}
50
50
51 # start and stop parameters for effects
51 # start and stop parameters for effects
52 _effects = {
52 _effects = {
53 'none': 0,
53 'none': 0,
54 'black': 30,
54 'black': 30,
55 'red': 31,
55 'red': 31,
56 'green': 32,
56 'green': 32,
57 'yellow': 33,
57 'yellow': 33,
58 'blue': 34,
58 'blue': 34,
59 'magenta': 35,
59 'magenta': 35,
60 'cyan': 36,
60 'cyan': 36,
61 'white': 37,
61 'white': 37,
62 'bold': 1,
62 'bold': 1,
63 'italic': 3,
63 'italic': 3,
64 'underline': 4,
64 'underline': 4,
65 'inverse': 7,
65 'inverse': 7,
66 'dim': 2,
66 'dim': 2,
67 'black_background': 40,
67 'black_background': 40,
68 'red_background': 41,
68 'red_background': 41,
69 'green_background': 42,
69 'green_background': 42,
70 'yellow_background': 43,
70 'yellow_background': 43,
71 'blue_background': 44,
71 'blue_background': 44,
72 'purple_background': 45,
72 'purple_background': 45,
73 'cyan_background': 46,
73 'cyan_background': 46,
74 'white_background': 47,
74 'white_background': 47,
75 }
75 }
76
76
77 _defaultstyles = {
77 _defaultstyles = {
78 'grep.match': 'red bold',
78 'grep.match': 'red bold',
79 'grep.linenumber': 'green',
79 'grep.linenumber': 'green',
80 'grep.rev': 'green',
80 'grep.rev': 'green',
81 'grep.change': 'green',
81 'grep.change': 'green',
82 'grep.sep': 'cyan',
82 'grep.sep': 'cyan',
83 'grep.filename': 'magenta',
83 'grep.filename': 'magenta',
84 'grep.user': 'magenta',
84 'grep.user': 'magenta',
85 'grep.date': 'magenta',
85 'grep.date': 'magenta',
86 'bookmarks.active': 'green',
86 'bookmarks.active': 'green',
87 'branches.active': 'none',
87 'branches.active': 'none',
88 'branches.closed': 'black bold',
88 'branches.closed': 'black bold',
89 'branches.current': 'green',
89 'branches.current': 'green',
90 'branches.inactive': 'none',
90 'branches.inactive': 'none',
91 'diff.changed': 'white',
91 'diff.changed': 'white',
92 'diff.deleted': 'red',
92 'diff.deleted': 'red',
93 'diff.deleted.changed': 'red',
93 'diff.deleted.changed': 'red bold underline',
94 'diff.deleted.unchanged': 'red dim',
94 'diff.deleted.unchanged': 'red',
95 'diff.diffline': 'bold',
95 'diff.diffline': 'bold',
96 'diff.extended': 'cyan bold',
96 'diff.extended': 'cyan bold',
97 'diff.file_a': 'red bold',
97 'diff.file_a': 'red bold',
98 'diff.file_b': 'green bold',
98 'diff.file_b': 'green bold',
99 'diff.hunk': 'magenta',
99 'diff.hunk': 'magenta',
100 'diff.inserted': 'green',
100 'diff.inserted': 'green',
101 'diff.inserted.changed': 'green',
101 'diff.inserted.changed': 'green bold underline',
102 'diff.inserted.unchanged': 'green dim',
102 'diff.inserted.unchanged': 'green',
103 'diff.tab': '',
103 'diff.tab': '',
104 'diff.trailingwhitespace': 'bold red_background',
104 'diff.trailingwhitespace': 'bold red_background',
105 'changeset.public': '',
105 'changeset.public': '',
106 'changeset.draft': '',
106 'changeset.draft': '',
107 'changeset.secret': '',
107 'changeset.secret': '',
108 'diffstat.deleted': 'red',
108 'diffstat.deleted': 'red',
109 'diffstat.inserted': 'green',
109 'diffstat.inserted': 'green',
110 'formatvariant.name.mismatchconfig': 'red',
110 'formatvariant.name.mismatchconfig': 'red',
111 'formatvariant.name.mismatchdefault': 'yellow',
111 'formatvariant.name.mismatchdefault': 'yellow',
112 'formatvariant.name.uptodate': 'green',
112 'formatvariant.name.uptodate': 'green',
113 'formatvariant.repo.mismatchconfig': 'red',
113 'formatvariant.repo.mismatchconfig': 'red',
114 'formatvariant.repo.mismatchdefault': 'yellow',
114 'formatvariant.repo.mismatchdefault': 'yellow',
115 'formatvariant.repo.uptodate': 'green',
115 'formatvariant.repo.uptodate': 'green',
116 'formatvariant.config.special': 'yellow',
116 'formatvariant.config.special': 'yellow',
117 'formatvariant.config.default': 'green',
117 'formatvariant.config.default': 'green',
118 'formatvariant.default': '',
118 'formatvariant.default': '',
119 'histedit.remaining': 'red bold',
119 'histedit.remaining': 'red bold',
120 'ui.prompt': 'yellow',
120 'ui.prompt': 'yellow',
121 'log.changeset': 'yellow',
121 'log.changeset': 'yellow',
122 'patchbomb.finalsummary': '',
122 'patchbomb.finalsummary': '',
123 'patchbomb.from': 'magenta',
123 'patchbomb.from': 'magenta',
124 'patchbomb.to': 'cyan',
124 'patchbomb.to': 'cyan',
125 'patchbomb.subject': 'green',
125 'patchbomb.subject': 'green',
126 'patchbomb.diffstats': '',
126 'patchbomb.diffstats': '',
127 'rebase.rebased': 'blue',
127 'rebase.rebased': 'blue',
128 'rebase.remaining': 'red bold',
128 'rebase.remaining': 'red bold',
129 'resolve.resolved': 'green bold',
129 'resolve.resolved': 'green bold',
130 'resolve.unresolved': 'red bold',
130 'resolve.unresolved': 'red bold',
131 'shelve.age': 'cyan',
131 'shelve.age': 'cyan',
132 'shelve.newest': 'green bold',
132 'shelve.newest': 'green bold',
133 'shelve.name': 'blue bold',
133 'shelve.name': 'blue bold',
134 'status.added': 'green bold',
134 'status.added': 'green bold',
135 'status.clean': 'none',
135 'status.clean': 'none',
136 'status.copied': 'none',
136 'status.copied': 'none',
137 'status.deleted': 'cyan bold underline',
137 'status.deleted': 'cyan bold underline',
138 'status.ignored': 'black bold',
138 'status.ignored': 'black bold',
139 'status.modified': 'blue bold',
139 'status.modified': 'blue bold',
140 'status.removed': 'red bold',
140 'status.removed': 'red bold',
141 'status.unknown': 'magenta bold underline',
141 'status.unknown': 'magenta bold underline',
142 'tags.normal': 'green',
142 'tags.normal': 'green',
143 'tags.local': 'black bold',
143 'tags.local': 'black bold',
144 }
144 }
145
145
146 def loadcolortable(ui, extname, colortable):
146 def loadcolortable(ui, extname, colortable):
147 _defaultstyles.update(colortable)
147 _defaultstyles.update(colortable)
148
148
149 def _terminfosetup(ui, mode, formatted):
149 def _terminfosetup(ui, mode, formatted):
150 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
150 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
151
151
152 # If we failed to load curses, we go ahead and return.
152 # If we failed to load curses, we go ahead and return.
153 if curses is None:
153 if curses is None:
154 return
154 return
155 # Otherwise, see what the config file says.
155 # Otherwise, see what the config file says.
156 if mode not in ('auto', 'terminfo'):
156 if mode not in ('auto', 'terminfo'):
157 return
157 return
158 ui._terminfoparams.update(_baseterminfoparams)
158 ui._terminfoparams.update(_baseterminfoparams)
159
159
160 for key, val in ui.configitems('color'):
160 for key, val in ui.configitems('color'):
161 if key.startswith('color.'):
161 if key.startswith('color.'):
162 newval = (False, int(val), '')
162 newval = (False, int(val), '')
163 ui._terminfoparams[key[6:]] = newval
163 ui._terminfoparams[key[6:]] = newval
164 elif key.startswith('terminfo.'):
164 elif key.startswith('terminfo.'):
165 newval = (True, '', val.replace('\\E', '\x1b'))
165 newval = (True, '', val.replace('\\E', '\x1b'))
166 ui._terminfoparams[key[9:]] = newval
166 ui._terminfoparams[key[9:]] = newval
167 try:
167 try:
168 curses.setupterm()
168 curses.setupterm()
169 except curses.error as e:
169 except curses.error as e:
170 ui._terminfoparams.clear()
170 ui._terminfoparams.clear()
171 return
171 return
172
172
173 for key, (b, e, c) in ui._terminfoparams.copy().items():
173 for key, (b, e, c) in ui._terminfoparams.copy().items():
174 if not b:
174 if not b:
175 continue
175 continue
176 if not c and not curses.tigetstr(pycompat.sysstr(e)):
176 if not c and not curses.tigetstr(pycompat.sysstr(e)):
177 # 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
178 # noisy and use ui.debug().
178 # noisy and use ui.debug().
179 ui.debug("no terminfo entry for %s\n" % e)
179 ui.debug("no terminfo entry for %s\n" % e)
180 del ui._terminfoparams[key]
180 del ui._terminfoparams[key]
181 if not curses.tigetstr(r'setaf') or not curses.tigetstr(r'setab'):
181 if not curses.tigetstr(r'setaf') or not curses.tigetstr(r'setab'):
182 # Only warn about missing terminfo entries if we explicitly asked for
182 # Only warn about missing terminfo entries if we explicitly asked for
183 # terminfo mode and we're in a formatted terminal.
183 # terminfo mode and we're in a formatted terminal.
184 if mode == "terminfo" and formatted:
184 if mode == "terminfo" and formatted:
185 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
185 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
186 "ECMA-48 color\n"))
186 "ECMA-48 color\n"))
187 ui._terminfoparams.clear()
187 ui._terminfoparams.clear()
188
188
189 def setup(ui):
189 def setup(ui):
190 """configure color on a ui
190 """configure color on a ui
191
191
192 That function both set the colormode for the ui object and read
192 That function both set the colormode for the ui object and read
193 the configuration looking for custom colors and effect definitions."""
193 the configuration looking for custom colors and effect definitions."""
194 mode = _modesetup(ui)
194 mode = _modesetup(ui)
195 ui._colormode = mode
195 ui._colormode = mode
196 if mode and mode != 'debug':
196 if mode and mode != 'debug':
197 configstyles(ui)
197 configstyles(ui)
198
198
199 def _modesetup(ui):
199 def _modesetup(ui):
200 if ui.plain('color'):
200 if ui.plain('color'):
201 return None
201 return None
202 config = ui.config('ui', 'color')
202 config = ui.config('ui', 'color')
203 if config == 'debug':
203 if config == 'debug':
204 return 'debug'
204 return 'debug'
205
205
206 auto = (config == 'auto')
206 auto = (config == 'auto')
207 always = False
207 always = False
208 if not auto and stringutil.parsebool(config):
208 if not auto and stringutil.parsebool(config):
209 # We want the config to behave like a boolean, "on" is actually auto,
209 # We want the config to behave like a boolean, "on" is actually auto,
210 # but "always" value is treated as a special case to reduce confusion.
210 # but "always" value is treated as a special case to reduce confusion.
211 if ui.configsource('ui', 'color') == '--color' or config == 'always':
211 if ui.configsource('ui', 'color') == '--color' or config == 'always':
212 always = True
212 always = True
213 else:
213 else:
214 auto = True
214 auto = True
215
215
216 if not always and not auto:
216 if not always and not auto:
217 return None
217 return None
218
218
219 formatted = (always or (encoding.environ.get('TERM') != 'dumb'
219 formatted = (always or (encoding.environ.get('TERM') != 'dumb'
220 and ui.formatted()))
220 and ui.formatted()))
221
221
222 mode = ui.config('color', 'mode')
222 mode = ui.config('color', 'mode')
223
223
224 # If pager is active, color.pagermode overrides color.mode.
224 # If pager is active, color.pagermode overrides color.mode.
225 if getattr(ui, 'pageractive', False):
225 if getattr(ui, 'pageractive', False):
226 mode = ui.config('color', 'pagermode', mode)
226 mode = ui.config('color', 'pagermode', mode)
227
227
228 realmode = mode
228 realmode = mode
229 if pycompat.iswindows:
229 if pycompat.iswindows:
230 from . import win32
230 from . import win32
231
231
232 term = encoding.environ.get('TERM')
232 term = encoding.environ.get('TERM')
233 # TERM won't be defined in a vanilla cmd.exe environment.
233 # TERM won't be defined in a vanilla cmd.exe environment.
234
234
235 # UNIX-like environments on Windows such as Cygwin and MSYS will
235 # UNIX-like environments on Windows such as Cygwin and MSYS will
236 # set TERM. They appear to make a best effort attempt at setting it
236 # set TERM. They appear to make a best effort attempt at setting it
237 # to something appropriate. However, not all environments with TERM
237 # to something appropriate. However, not all environments with TERM
238 # defined support ANSI.
238 # defined support ANSI.
239 ansienviron = term and 'xterm' in term
239 ansienviron = term and 'xterm' in term
240
240
241 if mode == 'auto':
241 if mode == 'auto':
242 # Since "ansi" could result in terminal gibberish, we error on the
242 # Since "ansi" could result in terminal gibberish, we error on the
243 # side of selecting "win32". However, if w32effects is not defined,
243 # side of selecting "win32". However, if w32effects is not defined,
244 # we almost certainly don't support "win32", so don't even try.
244 # we almost certainly don't support "win32", so don't even try.
245 # w32ffects is not populated when stdout is redirected, so checking
245 # w32ffects is not populated when stdout is redirected, so checking
246 # it first avoids win32 calls in a state known to error out.
246 # it first avoids win32 calls in a state known to error out.
247 if ansienviron or not w32effects or win32.enablevtmode():
247 if ansienviron or not w32effects or win32.enablevtmode():
248 realmode = 'ansi'
248 realmode = 'ansi'
249 else:
249 else:
250 realmode = 'win32'
250 realmode = 'win32'
251 # An empty w32effects is a clue that stdout is redirected, and thus
251 # An empty w32effects is a clue that stdout is redirected, and thus
252 # cannot enable VT mode.
252 # cannot enable VT mode.
253 elif mode == 'ansi' and w32effects and not ansienviron:
253 elif mode == 'ansi' and w32effects and not ansienviron:
254 win32.enablevtmode()
254 win32.enablevtmode()
255 elif mode == 'auto':
255 elif mode == 'auto':
256 realmode = 'ansi'
256 realmode = 'ansi'
257
257
258 def modewarn():
258 def modewarn():
259 # only warn if color.mode was explicitly set and we're in
259 # only warn if color.mode was explicitly set and we're in
260 # a formatted terminal
260 # a formatted terminal
261 if mode == realmode and formatted:
261 if mode == realmode and formatted:
262 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
262 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
263
263
264 if realmode == 'win32':
264 if realmode == 'win32':
265 ui._terminfoparams.clear()
265 ui._terminfoparams.clear()
266 if not w32effects:
266 if not w32effects:
267 modewarn()
267 modewarn()
268 return None
268 return None
269 elif realmode == 'ansi':
269 elif realmode == 'ansi':
270 ui._terminfoparams.clear()
270 ui._terminfoparams.clear()
271 elif realmode == 'terminfo':
271 elif realmode == 'terminfo':
272 _terminfosetup(ui, mode, formatted)
272 _terminfosetup(ui, mode, formatted)
273 if not ui._terminfoparams:
273 if not ui._terminfoparams:
274 ## FIXME Shouldn't we return None in this case too?
274 ## FIXME Shouldn't we return None in this case too?
275 modewarn()
275 modewarn()
276 realmode = 'ansi'
276 realmode = 'ansi'
277 else:
277 else:
278 return None
278 return None
279
279
280 if always or (auto and formatted):
280 if always or (auto and formatted):
281 return realmode
281 return realmode
282 return None
282 return None
283
283
284 def configstyles(ui):
284 def configstyles(ui):
285 ui._styles.update(_defaultstyles)
285 ui._styles.update(_defaultstyles)
286 for status, cfgeffects in ui.configitems('color'):
286 for status, cfgeffects in ui.configitems('color'):
287 if '.' not in status or status.startswith(('color.', 'terminfo.')):
287 if '.' not in status or status.startswith(('color.', 'terminfo.')):
288 continue
288 continue
289 cfgeffects = ui.configlist('color', status)
289 cfgeffects = ui.configlist('color', status)
290 if cfgeffects:
290 if cfgeffects:
291 good = []
291 good = []
292 for e in cfgeffects:
292 for e in cfgeffects:
293 if valideffect(ui, e):
293 if valideffect(ui, e):
294 good.append(e)
294 good.append(e)
295 else:
295 else:
296 ui.warn(_("ignoring unknown color/effect %r "
296 ui.warn(_("ignoring unknown color/effect %r "
297 "(configured in color.%s)\n")
297 "(configured in color.%s)\n")
298 % (e, status))
298 % (e, status))
299 ui._styles[status] = ' '.join(good)
299 ui._styles[status] = ' '.join(good)
300
300
301 def _activeeffects(ui):
301 def _activeeffects(ui):
302 '''Return the effects map for the color mode set on the ui.'''
302 '''Return the effects map for the color mode set on the ui.'''
303 if ui._colormode == 'win32':
303 if ui._colormode == 'win32':
304 return w32effects
304 return w32effects
305 elif ui._colormode is not None:
305 elif ui._colormode is not None:
306 return _effects
306 return _effects
307 return {}
307 return {}
308
308
309 def valideffect(ui, effect):
309 def valideffect(ui, effect):
310 'Determine if the effect is valid or not.'
310 'Determine if the effect is valid or not.'
311 return ((not ui._terminfoparams and effect in _activeeffects(ui))
311 return ((not ui._terminfoparams and effect in _activeeffects(ui))
312 or (effect in ui._terminfoparams
312 or (effect in ui._terminfoparams
313 or effect[:-11] in ui._terminfoparams))
313 or effect[:-11] in ui._terminfoparams))
314
314
315 def _effect_str(ui, effect):
315 def _effect_str(ui, effect):
316 '''Helper function for render_effects().'''
316 '''Helper function for render_effects().'''
317
317
318 bg = False
318 bg = False
319 if effect.endswith('_background'):
319 if effect.endswith('_background'):
320 bg = True
320 bg = True
321 effect = effect[:-11]
321 effect = effect[:-11]
322 try:
322 try:
323 attr, val, termcode = ui._terminfoparams[effect]
323 attr, val, termcode = ui._terminfoparams[effect]
324 except KeyError:
324 except KeyError:
325 return ''
325 return ''
326 if attr:
326 if attr:
327 if termcode:
327 if termcode:
328 return termcode
328 return termcode
329 else:
329 else:
330 return curses.tigetstr(pycompat.sysstr(val))
330 return curses.tigetstr(pycompat.sysstr(val))
331 elif bg:
331 elif bg:
332 return curses.tparm(curses.tigetstr(r'setab'), val)
332 return curses.tparm(curses.tigetstr(r'setab'), val)
333 else:
333 else:
334 return curses.tparm(curses.tigetstr(r'setaf'), val)
334 return curses.tparm(curses.tigetstr(r'setaf'), val)
335
335
336 def _mergeeffects(text, start, stop):
336 def _mergeeffects(text, start, stop):
337 """Insert start sequence at every occurrence of stop sequence
337 """Insert start sequence at every occurrence of stop sequence
338
338
339 >>> s = _mergeeffects(b'cyan', b'[C]', b'|')
339 >>> s = _mergeeffects(b'cyan', b'[C]', b'|')
340 >>> s = _mergeeffects(s + b'yellow', b'[Y]', b'|')
340 >>> s = _mergeeffects(s + b'yellow', b'[Y]', b'|')
341 >>> s = _mergeeffects(b'ma' + s + b'genta', b'[M]', b'|')
341 >>> s = _mergeeffects(b'ma' + s + b'genta', b'[M]', b'|')
342 >>> s = _mergeeffects(b'red' + s, b'[R]', b'|')
342 >>> s = _mergeeffects(b'red' + s, b'[R]', b'|')
343 >>> s
343 >>> s
344 '[R]red[M]ma[Y][C]cyan|[R][M][Y]yellow|[R][M]genta|'
344 '[R]red[M]ma[Y][C]cyan|[R][M][Y]yellow|[R][M]genta|'
345 """
345 """
346 parts = []
346 parts = []
347 for t in text.split(stop):
347 for t in text.split(stop):
348 if not t:
348 if not t:
349 continue
349 continue
350 parts.extend([start, t, stop])
350 parts.extend([start, t, stop])
351 return ''.join(parts)
351 return ''.join(parts)
352
352
353 def _render_effects(ui, text, effects):
353 def _render_effects(ui, text, effects):
354 'Wrap text in commands to turn on each effect.'
354 'Wrap text in commands to turn on each effect.'
355 if not text:
355 if not text:
356 return text
356 return text
357 if ui._terminfoparams:
357 if ui._terminfoparams:
358 start = ''.join(_effect_str(ui, effect)
358 start = ''.join(_effect_str(ui, effect)
359 for effect in ['none'] + effects.split())
359 for effect in ['none'] + effects.split())
360 stop = _effect_str(ui, 'none')
360 stop = _effect_str(ui, 'none')
361 else:
361 else:
362 activeeffects = _activeeffects(ui)
362 activeeffects = _activeeffects(ui)
363 start = [pycompat.bytestr(activeeffects[e])
363 start = [pycompat.bytestr(activeeffects[e])
364 for e in ['none'] + effects.split()]
364 for e in ['none'] + effects.split()]
365 start = '\033[' + ';'.join(start) + 'm'
365 start = '\033[' + ';'.join(start) + 'm'
366 stop = '\033[' + pycompat.bytestr(activeeffects['none']) + 'm'
366 stop = '\033[' + pycompat.bytestr(activeeffects['none']) + 'm'
367 return _mergeeffects(text, start, stop)
367 return _mergeeffects(text, start, stop)
368
368
369 _ansieffectre = re.compile(br'\x1b\[[0-9;]*m')
369 _ansieffectre = re.compile(br'\x1b\[[0-9;]*m')
370
370
371 def stripeffects(text):
371 def stripeffects(text):
372 """Strip ANSI control codes which could be inserted by colorlabel()"""
372 """Strip ANSI control codes which could be inserted by colorlabel()"""
373 return _ansieffectre.sub('', text)
373 return _ansieffectre.sub('', text)
374
374
375 def colorlabel(ui, msg, label):
375 def colorlabel(ui, msg, label):
376 """add color control code according to the mode"""
376 """add color control code according to the mode"""
377 if ui._colormode == 'debug':
377 if ui._colormode == 'debug':
378 if label and msg:
378 if label and msg:
379 if msg.endswith('\n'):
379 if msg.endswith('\n'):
380 msg = "[%s|%s]\n" % (label, msg[:-1])
380 msg = "[%s|%s]\n" % (label, msg[:-1])
381 else:
381 else:
382 msg = "[%s|%s]" % (label, msg)
382 msg = "[%s|%s]" % (label, msg)
383 elif ui._colormode is not None:
383 elif ui._colormode is not None:
384 effects = []
384 effects = []
385 for l in label.split():
385 for l in label.split():
386 s = ui._styles.get(l, '')
386 s = ui._styles.get(l, '')
387 if s:
387 if s:
388 effects.append(s)
388 effects.append(s)
389 elif valideffect(ui, l):
389 elif valideffect(ui, l):
390 effects.append(l)
390 effects.append(l)
391 effects = ' '.join(effects)
391 effects = ' '.join(effects)
392 if effects:
392 if effects:
393 msg = '\n'.join([_render_effects(ui, line, effects)
393 msg = '\n'.join([_render_effects(ui, line, effects)
394 for line in msg.split('\n')])
394 for line in msg.split('\n')])
395 return msg
395 return msg
396
396
397 w32effects = None
397 w32effects = None
398 if pycompat.iswindows:
398 if pycompat.iswindows:
399 import ctypes
399 import ctypes
400
400
401 _kernel32 = ctypes.windll.kernel32
401 _kernel32 = ctypes.windll.kernel32
402
402
403 _WORD = ctypes.c_ushort
403 _WORD = ctypes.c_ushort
404
404
405 _INVALID_HANDLE_VALUE = -1
405 _INVALID_HANDLE_VALUE = -1
406
406
407 class _COORD(ctypes.Structure):
407 class _COORD(ctypes.Structure):
408 _fields_ = [('X', ctypes.c_short),
408 _fields_ = [('X', ctypes.c_short),
409 ('Y', ctypes.c_short)]
409 ('Y', ctypes.c_short)]
410
410
411 class _SMALL_RECT(ctypes.Structure):
411 class _SMALL_RECT(ctypes.Structure):
412 _fields_ = [('Left', ctypes.c_short),
412 _fields_ = [('Left', ctypes.c_short),
413 ('Top', ctypes.c_short),
413 ('Top', ctypes.c_short),
414 ('Right', ctypes.c_short),
414 ('Right', ctypes.c_short),
415 ('Bottom', ctypes.c_short)]
415 ('Bottom', ctypes.c_short)]
416
416
417 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
417 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
418 _fields_ = [('dwSize', _COORD),
418 _fields_ = [('dwSize', _COORD),
419 ('dwCursorPosition', _COORD),
419 ('dwCursorPosition', _COORD),
420 ('wAttributes', _WORD),
420 ('wAttributes', _WORD),
421 ('srWindow', _SMALL_RECT),
421 ('srWindow', _SMALL_RECT),
422 ('dwMaximumWindowSize', _COORD)]
422 ('dwMaximumWindowSize', _COORD)]
423
423
424 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
424 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
425 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12
425 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12
426
426
427 _FOREGROUND_BLUE = 0x0001
427 _FOREGROUND_BLUE = 0x0001
428 _FOREGROUND_GREEN = 0x0002
428 _FOREGROUND_GREEN = 0x0002
429 _FOREGROUND_RED = 0x0004
429 _FOREGROUND_RED = 0x0004
430 _FOREGROUND_INTENSITY = 0x0008
430 _FOREGROUND_INTENSITY = 0x0008
431
431
432 _BACKGROUND_BLUE = 0x0010
432 _BACKGROUND_BLUE = 0x0010
433 _BACKGROUND_GREEN = 0x0020
433 _BACKGROUND_GREEN = 0x0020
434 _BACKGROUND_RED = 0x0040
434 _BACKGROUND_RED = 0x0040
435 _BACKGROUND_INTENSITY = 0x0080
435 _BACKGROUND_INTENSITY = 0x0080
436
436
437 _COMMON_LVB_REVERSE_VIDEO = 0x4000
437 _COMMON_LVB_REVERSE_VIDEO = 0x4000
438 _COMMON_LVB_UNDERSCORE = 0x8000
438 _COMMON_LVB_UNDERSCORE = 0x8000
439
439
440 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
440 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
441 w32effects = {
441 w32effects = {
442 'none': -1,
442 'none': -1,
443 'black': 0,
443 'black': 0,
444 'red': _FOREGROUND_RED,
444 'red': _FOREGROUND_RED,
445 'green': _FOREGROUND_GREEN,
445 'green': _FOREGROUND_GREEN,
446 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
446 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
447 'blue': _FOREGROUND_BLUE,
447 'blue': _FOREGROUND_BLUE,
448 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
448 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
449 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
449 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
450 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
450 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
451 'bold': _FOREGROUND_INTENSITY,
451 'bold': _FOREGROUND_INTENSITY,
452 'black_background': 0x100, # unused value > 0x0f
452 'black_background': 0x100, # unused value > 0x0f
453 'red_background': _BACKGROUND_RED,
453 'red_background': _BACKGROUND_RED,
454 'green_background': _BACKGROUND_GREEN,
454 'green_background': _BACKGROUND_GREEN,
455 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
455 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
456 'blue_background': _BACKGROUND_BLUE,
456 'blue_background': _BACKGROUND_BLUE,
457 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
457 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
458 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
458 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
459 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
459 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
460 _BACKGROUND_BLUE),
460 _BACKGROUND_BLUE),
461 'bold_background': _BACKGROUND_INTENSITY,
461 'bold_background': _BACKGROUND_INTENSITY,
462 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
462 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
463 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
463 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
464 }
464 }
465
465
466 passthrough = {_FOREGROUND_INTENSITY,
466 passthrough = {_FOREGROUND_INTENSITY,
467 _BACKGROUND_INTENSITY,
467 _BACKGROUND_INTENSITY,
468 _COMMON_LVB_UNDERSCORE,
468 _COMMON_LVB_UNDERSCORE,
469 _COMMON_LVB_REVERSE_VIDEO}
469 _COMMON_LVB_REVERSE_VIDEO}
470
470
471 stdout = _kernel32.GetStdHandle(
471 stdout = _kernel32.GetStdHandle(
472 _STD_OUTPUT_HANDLE) # don't close the handle returned
472 _STD_OUTPUT_HANDLE) # don't close the handle returned
473 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
473 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
474 w32effects = None
474 w32effects = None
475 else:
475 else:
476 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
476 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
477 if not _kernel32.GetConsoleScreenBufferInfo(
477 if not _kernel32.GetConsoleScreenBufferInfo(
478 stdout, ctypes.byref(csbi)):
478 stdout, ctypes.byref(csbi)):
479 # stdout may not support GetConsoleScreenBufferInfo()
479 # stdout may not support GetConsoleScreenBufferInfo()
480 # when called from subprocess or redirected
480 # when called from subprocess or redirected
481 w32effects = None
481 w32effects = None
482 else:
482 else:
483 origattr = csbi.wAttributes
483 origattr = csbi.wAttributes
484 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
484 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
485 re.MULTILINE | re.DOTALL)
485 re.MULTILINE | re.DOTALL)
486
486
487 def win32print(ui, writefunc, *msgs, **opts):
487 def win32print(ui, writefunc, *msgs, **opts):
488 for text in msgs:
488 for text in msgs:
489 _win32print(ui, text, writefunc, **opts)
489 _win32print(ui, text, writefunc, **opts)
490
490
491 def _win32print(ui, text, writefunc, **opts):
491 def _win32print(ui, text, writefunc, **opts):
492 label = opts.get(r'label', '')
492 label = opts.get(r'label', '')
493 attr = origattr
493 attr = origattr
494
494
495 def mapcolor(val, attr):
495 def mapcolor(val, attr):
496 if val == -1:
496 if val == -1:
497 return origattr
497 return origattr
498 elif val in passthrough:
498 elif val in passthrough:
499 return attr | val
499 return attr | val
500 elif val > 0x0f:
500 elif val > 0x0f:
501 return (val & 0x70) | (attr & 0x8f)
501 return (val & 0x70) | (attr & 0x8f)
502 else:
502 else:
503 return (val & 0x07) | (attr & 0xf8)
503 return (val & 0x07) | (attr & 0xf8)
504
504
505 # determine console attributes based on labels
505 # determine console attributes based on labels
506 for l in label.split():
506 for l in label.split():
507 style = ui._styles.get(l, '')
507 style = ui._styles.get(l, '')
508 for effect in style.split():
508 for effect in style.split():
509 try:
509 try:
510 attr = mapcolor(w32effects[effect], attr)
510 attr = mapcolor(w32effects[effect], attr)
511 except KeyError:
511 except KeyError:
512 # w32effects could not have certain attributes so we skip
512 # w32effects could not have certain attributes so we skip
513 # them if not found
513 # them if not found
514 pass
514 pass
515 # hack to ensure regexp finds data
515 # hack to ensure regexp finds data
516 if not text.startswith('\033['):
516 if not text.startswith('\033['):
517 text = '\033[m' + text
517 text = '\033[m' + text
518
518
519 # Look for ANSI-like codes embedded in text
519 # Look for ANSI-like codes embedded in text
520 m = re.match(ansire, text)
520 m = re.match(ansire, text)
521
521
522 try:
522 try:
523 while m:
523 while m:
524 for sattr in m.group(1).split(';'):
524 for sattr in m.group(1).split(';'):
525 if sattr:
525 if sattr:
526 attr = mapcolor(int(sattr), attr)
526 attr = mapcolor(int(sattr), attr)
527 ui.flush()
527 ui.flush()
528 _kernel32.SetConsoleTextAttribute(stdout, attr)
528 _kernel32.SetConsoleTextAttribute(stdout, attr)
529 writefunc(m.group(2), **opts)
529 writefunc(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 ui.flush()
533 ui.flush()
534 _kernel32.SetConsoleTextAttribute(stdout, origattr)
534 _kernel32.SetConsoleTextAttribute(stdout, origattr)
General Comments 0
You need to be logged in to leave comments. Login now