##// END OF EJS Templates
color: reset win32 console color in a finally block
Idan Kamara -
r13919:67f20625 default
parent child Browse files
Show More
@@ -1,361 +1,363 b''
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 program is free software; you can redistribute it and/or modify it
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the
6 # under the terms of the GNU General Public License as published by the
7 # Free Software Foundation; either version 2 of the License, or (at your
7 # Free Software Foundation; either version 2 of the License, or (at your
8 # option) any later version.
8 # option) any later version.
9 #
9 #
10 # This program is distributed in the hope that it will be useful, but
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13 # Public License for more details.
13 # Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License along
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc.,
16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
18
19 '''colorize output from some commands
19 '''colorize output from some commands
20
20
21 This extension modifies the status and resolve commands to add color
21 This extension modifies the status and resolve commands to add color
22 to their output to reflect file status, the qseries command to add
22 to their output to reflect file status, the qseries command to add
23 color to reflect patch status (applied, unapplied, missing), and to
23 color to reflect patch status (applied, unapplied, missing), and to
24 diff-related commands to highlight additions, removals, diff headers,
24 diff-related commands to highlight additions, removals, diff headers,
25 and trailing whitespace.
25 and trailing whitespace.
26
26
27 Other effects in addition to color, like bold and underlined text, are
27 Other effects in addition to color, like bold and underlined text, are
28 also available. Effects are rendered with the ECMA-48 SGR control
28 also available. Effects are rendered with the ECMA-48 SGR control
29 function (aka ANSI escape codes).
29 function (aka ANSI escape codes).
30
30
31 Default effects may be overridden from your configuration file::
31 Default effects may be overridden from your configuration file::
32
32
33 [color]
33 [color]
34 status.modified = blue bold underline red_background
34 status.modified = blue bold underline red_background
35 status.added = green bold
35 status.added = green bold
36 status.removed = red bold blue_background
36 status.removed = red bold blue_background
37 status.deleted = cyan bold underline
37 status.deleted = cyan bold underline
38 status.unknown = magenta bold underline
38 status.unknown = magenta bold underline
39 status.ignored = black bold
39 status.ignored = black bold
40
40
41 # 'none' turns off all effects
41 # 'none' turns off all effects
42 status.clean = none
42 status.clean = none
43 status.copied = none
43 status.copied = none
44
44
45 qseries.applied = blue bold underline
45 qseries.applied = blue bold underline
46 qseries.unapplied = black bold
46 qseries.unapplied = black bold
47 qseries.missing = red bold
47 qseries.missing = red bold
48
48
49 diff.diffline = bold
49 diff.diffline = bold
50 diff.extended = cyan bold
50 diff.extended = cyan bold
51 diff.file_a = red bold
51 diff.file_a = red bold
52 diff.file_b = green bold
52 diff.file_b = green bold
53 diff.hunk = magenta
53 diff.hunk = magenta
54 diff.deleted = red
54 diff.deleted = red
55 diff.inserted = green
55 diff.inserted = green
56 diff.changed = white
56 diff.changed = white
57 diff.trailingwhitespace = bold red_background
57 diff.trailingwhitespace = bold red_background
58
58
59 resolve.unresolved = red bold
59 resolve.unresolved = red bold
60 resolve.resolved = green bold
60 resolve.resolved = green bold
61
61
62 bookmarks.current = green
62 bookmarks.current = green
63
63
64 branches.active = none
64 branches.active = none
65 branches.closed = black bold
65 branches.closed = black bold
66 branches.current = green
66 branches.current = green
67 branches.inactive = none
67 branches.inactive = none
68
68
69 The color extension will try to detect whether to use ANSI codes or
69 The color extension will try to detect whether to use ANSI codes or
70 Win32 console APIs, unless it is made explicit::
70 Win32 console APIs, unless it is made explicit::
71
71
72 [color]
72 [color]
73 mode = ansi
73 mode = ansi
74
74
75 Any value other than 'ansi', 'win32', or 'auto' will disable color.
75 Any value other than 'ansi', 'win32', or 'auto' will disable color.
76
76
77 '''
77 '''
78
78
79 import os
79 import os
80
80
81 from mercurial import commands, dispatch, extensions, ui as uimod, util
81 from mercurial import commands, dispatch, extensions, ui as uimod, util
82 from mercurial.i18n import _
82 from mercurial.i18n import _
83
83
84 # start and stop parameters for effects
84 # start and stop parameters for effects
85 _effects = {'none': 0, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33,
85 _effects = {'none': 0, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33,
86 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'bold': 1,
86 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'bold': 1,
87 'italic': 3, 'underline': 4, 'inverse': 7,
87 'italic': 3, 'underline': 4, 'inverse': 7,
88 'black_background': 40, 'red_background': 41,
88 'black_background': 40, 'red_background': 41,
89 'green_background': 42, 'yellow_background': 43,
89 'green_background': 42, 'yellow_background': 43,
90 'blue_background': 44, 'purple_background': 45,
90 'blue_background': 44, 'purple_background': 45,
91 'cyan_background': 46, 'white_background': 47}
91 'cyan_background': 46, 'white_background': 47}
92
92
93 _styles = {'grep.match': 'red bold',
93 _styles = {'grep.match': 'red bold',
94 'bookmarks.current': 'green',
94 'bookmarks.current': 'green',
95 'branches.active': 'none',
95 'branches.active': 'none',
96 'branches.closed': 'black bold',
96 'branches.closed': 'black bold',
97 'branches.current': 'green',
97 'branches.current': 'green',
98 'branches.inactive': 'none',
98 'branches.inactive': 'none',
99 'diff.changed': 'white',
99 'diff.changed': 'white',
100 'diff.deleted': 'red',
100 'diff.deleted': 'red',
101 'diff.diffline': 'bold',
101 'diff.diffline': 'bold',
102 'diff.extended': 'cyan bold',
102 'diff.extended': 'cyan bold',
103 'diff.file_a': 'red bold',
103 'diff.file_a': 'red bold',
104 'diff.file_b': 'green bold',
104 'diff.file_b': 'green bold',
105 'diff.hunk': 'magenta',
105 'diff.hunk': 'magenta',
106 'diff.inserted': 'green',
106 'diff.inserted': 'green',
107 'diff.trailingwhitespace': 'bold red_background',
107 'diff.trailingwhitespace': 'bold red_background',
108 'diffstat.deleted': 'red',
108 'diffstat.deleted': 'red',
109 'diffstat.inserted': 'green',
109 'diffstat.inserted': 'green',
110 'ui.prompt': 'yellow',
110 'ui.prompt': 'yellow',
111 'log.changeset': 'yellow',
111 'log.changeset': 'yellow',
112 'resolve.resolved': 'green bold',
112 'resolve.resolved': 'green bold',
113 'resolve.unresolved': 'red bold',
113 'resolve.unresolved': 'red bold',
114 'status.added': 'green bold',
114 'status.added': 'green bold',
115 'status.clean': 'none',
115 'status.clean': 'none',
116 'status.copied': 'none',
116 'status.copied': 'none',
117 'status.deleted': 'cyan bold underline',
117 'status.deleted': 'cyan bold underline',
118 'status.ignored': 'black bold',
118 'status.ignored': 'black bold',
119 'status.modified': 'blue bold',
119 'status.modified': 'blue bold',
120 'status.removed': 'red bold',
120 'status.removed': 'red bold',
121 'status.unknown': 'magenta bold underline'}
121 'status.unknown': 'magenta bold underline'}
122
122
123
123
124 def render_effects(text, effects):
124 def render_effects(text, effects):
125 'Wrap text in commands to turn on each effect.'
125 'Wrap text in commands to turn on each effect.'
126 if not text:
126 if not text:
127 return text
127 return text
128 start = [str(_effects[e]) for e in ['none'] + effects.split()]
128 start = [str(_effects[e]) for e in ['none'] + effects.split()]
129 start = '\033[' + ';'.join(start) + 'm'
129 start = '\033[' + ';'.join(start) + 'm'
130 stop = '\033[' + str(_effects['none']) + 'm'
130 stop = '\033[' + str(_effects['none']) + 'm'
131 return ''.join([start, text, stop])
131 return ''.join([start, text, stop])
132
132
133 def extstyles():
133 def extstyles():
134 for name, ext in extensions.extensions():
134 for name, ext in extensions.extensions():
135 _styles.update(getattr(ext, 'colortable', {}))
135 _styles.update(getattr(ext, 'colortable', {}))
136
136
137 def configstyles(ui):
137 def configstyles(ui):
138 for status, cfgeffects in ui.configitems('color'):
138 for status, cfgeffects in ui.configitems('color'):
139 if '.' not in status:
139 if '.' not in status:
140 continue
140 continue
141 cfgeffects = ui.configlist('color', status)
141 cfgeffects = ui.configlist('color', status)
142 if cfgeffects:
142 if cfgeffects:
143 good = []
143 good = []
144 for e in cfgeffects:
144 for e in cfgeffects:
145 if e in _effects:
145 if e in _effects:
146 good.append(e)
146 good.append(e)
147 else:
147 else:
148 ui.warn(_("ignoring unknown color/effect %r "
148 ui.warn(_("ignoring unknown color/effect %r "
149 "(configured in color.%s)\n")
149 "(configured in color.%s)\n")
150 % (e, status))
150 % (e, status))
151 _styles[status] = ' '.join(good)
151 _styles[status] = ' '.join(good)
152
152
153 class colorui(uimod.ui):
153 class colorui(uimod.ui):
154 def popbuffer(self, labeled=False):
154 def popbuffer(self, labeled=False):
155 if labeled:
155 if labeled:
156 return ''.join(self.label(a, label) for a, label
156 return ''.join(self.label(a, label) for a, label
157 in self._buffers.pop())
157 in self._buffers.pop())
158 return ''.join(a for a, label in self._buffers.pop())
158 return ''.join(a for a, label in self._buffers.pop())
159
159
160 _colormode = 'ansi'
160 _colormode = 'ansi'
161 def write(self, *args, **opts):
161 def write(self, *args, **opts):
162 label = opts.get('label', '')
162 label = opts.get('label', '')
163 if self._buffers:
163 if self._buffers:
164 self._buffers[-1].extend([(str(a), label) for a in args])
164 self._buffers[-1].extend([(str(a), label) for a in args])
165 elif self._colormode == 'win32':
165 elif self._colormode == 'win32':
166 for a in args:
166 for a in args:
167 win32print(a, super(colorui, self).write, **opts)
167 win32print(a, super(colorui, self).write, **opts)
168 else:
168 else:
169 return super(colorui, self).write(
169 return super(colorui, self).write(
170 *[self.label(str(a), label) for a in args], **opts)
170 *[self.label(str(a), label) for a in args], **opts)
171
171
172 def write_err(self, *args, **opts):
172 def write_err(self, *args, **opts):
173 label = opts.get('label', '')
173 label = opts.get('label', '')
174 if self._colormode == 'win32':
174 if self._colormode == 'win32':
175 for a in args:
175 for a in args:
176 win32print(a, super(colorui, self).write_err, **opts)
176 win32print(a, super(colorui, self).write_err, **opts)
177 else:
177 else:
178 return super(colorui, self).write_err(
178 return super(colorui, self).write_err(
179 *[self.label(str(a), label) for a in args], **opts)
179 *[self.label(str(a), label) for a in args], **opts)
180
180
181 def label(self, msg, label):
181 def label(self, msg, label):
182 effects = []
182 effects = []
183 for l in label.split():
183 for l in label.split():
184 s = _styles.get(l, '')
184 s = _styles.get(l, '')
185 if s:
185 if s:
186 effects.append(s)
186 effects.append(s)
187 effects = ''.join(effects)
187 effects = ''.join(effects)
188 if effects:
188 if effects:
189 return '\n'.join([render_effects(s, effects)
189 return '\n'.join([render_effects(s, effects)
190 for s in msg.split('\n')])
190 for s in msg.split('\n')])
191 return msg
191 return msg
192
192
193
193
194 def uisetup(ui):
194 def uisetup(ui):
195 if ui.plain():
195 if ui.plain():
196 return
196 return
197 mode = ui.config('color', 'mode', 'auto')
197 mode = ui.config('color', 'mode', 'auto')
198 if mode == 'auto':
198 if mode == 'auto':
199 if os.name == 'nt' and 'TERM' not in os.environ:
199 if os.name == 'nt' and 'TERM' not in os.environ:
200 # looks line a cmd.exe console, use win32 API or nothing
200 # looks line a cmd.exe console, use win32 API or nothing
201 mode = w32effects and 'win32' or 'none'
201 mode = w32effects and 'win32' or 'none'
202 else:
202 else:
203 mode = 'ansi'
203 mode = 'ansi'
204 if mode == 'win32':
204 if mode == 'win32':
205 if w32effects is None:
205 if w32effects is None:
206 # only warn if color.mode is explicitly set to win32
206 # only warn if color.mode is explicitly set to win32
207 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
207 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
208 return
208 return
209 _effects.update(w32effects)
209 _effects.update(w32effects)
210 elif mode != 'ansi':
210 elif mode != 'ansi':
211 return
211 return
212 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
212 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
213 coloropt = opts['color']
213 coloropt = opts['color']
214 auto = coloropt == 'auto'
214 auto = coloropt == 'auto'
215 always = util.parsebool(coloropt)
215 always = util.parsebool(coloropt)
216 if (always or
216 if (always or
217 (always is None and
217 (always is None and
218 (auto and (os.environ.get('TERM') != 'dumb' and ui_.formatted())))):
218 (auto and (os.environ.get('TERM') != 'dumb' and ui_.formatted())))):
219 colorui._colormode = mode
219 colorui._colormode = mode
220 colorui.__bases__ = (ui_.__class__,)
220 colorui.__bases__ = (ui_.__class__,)
221 ui_.__class__ = colorui
221 ui_.__class__ = colorui
222 extstyles()
222 extstyles()
223 configstyles(ui_)
223 configstyles(ui_)
224 return orig(ui_, opts, cmd, cmdfunc)
224 return orig(ui_, opts, cmd, cmdfunc)
225 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
225 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
226
226
227 def extsetup(ui):
227 def extsetup(ui):
228 commands.globalopts.append(
228 commands.globalopts.append(
229 ('', 'color', 'auto',
229 ('', 'color', 'auto',
230 # i18n: 'always', 'auto', and 'never' are keywords and should
230 # i18n: 'always', 'auto', and 'never' are keywords and should
231 # not be translated
231 # not be translated
232 _("when to colorize (boolean, always, auto, or never)"),
232 _("when to colorize (boolean, always, auto, or never)"),
233 _('TYPE')))
233 _('TYPE')))
234
234
235 if os.name != 'nt':
235 if os.name != 'nt':
236 w32effects = None
236 w32effects = None
237 else:
237 else:
238 import re, ctypes
238 import re, ctypes
239
239
240 _kernel32 = ctypes.windll.kernel32
240 _kernel32 = ctypes.windll.kernel32
241
241
242 _WORD = ctypes.c_ushort
242 _WORD = ctypes.c_ushort
243
243
244 _INVALID_HANDLE_VALUE = -1
244 _INVALID_HANDLE_VALUE = -1
245
245
246 class _COORD(ctypes.Structure):
246 class _COORD(ctypes.Structure):
247 _fields_ = [('X', ctypes.c_short),
247 _fields_ = [('X', ctypes.c_short),
248 ('Y', ctypes.c_short)]
248 ('Y', ctypes.c_short)]
249
249
250 class _SMALL_RECT(ctypes.Structure):
250 class _SMALL_RECT(ctypes.Structure):
251 _fields_ = [('Left', ctypes.c_short),
251 _fields_ = [('Left', ctypes.c_short),
252 ('Top', ctypes.c_short),
252 ('Top', ctypes.c_short),
253 ('Right', ctypes.c_short),
253 ('Right', ctypes.c_short),
254 ('Bottom', ctypes.c_short)]
254 ('Bottom', ctypes.c_short)]
255
255
256 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
256 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
257 _fields_ = [('dwSize', _COORD),
257 _fields_ = [('dwSize', _COORD),
258 ('dwCursorPosition', _COORD),
258 ('dwCursorPosition', _COORD),
259 ('wAttributes', _WORD),
259 ('wAttributes', _WORD),
260 ('srWindow', _SMALL_RECT),
260 ('srWindow', _SMALL_RECT),
261 ('dwMaximumWindowSize', _COORD)]
261 ('dwMaximumWindowSize', _COORD)]
262
262
263 _STD_OUTPUT_HANDLE = 0xfffffff5L # (DWORD)-11
263 _STD_OUTPUT_HANDLE = 0xfffffff5L # (DWORD)-11
264 _STD_ERROR_HANDLE = 0xfffffff4L # (DWORD)-12
264 _STD_ERROR_HANDLE = 0xfffffff4L # (DWORD)-12
265
265
266 _FOREGROUND_BLUE = 0x0001
266 _FOREGROUND_BLUE = 0x0001
267 _FOREGROUND_GREEN = 0x0002
267 _FOREGROUND_GREEN = 0x0002
268 _FOREGROUND_RED = 0x0004
268 _FOREGROUND_RED = 0x0004
269 _FOREGROUND_INTENSITY = 0x0008
269 _FOREGROUND_INTENSITY = 0x0008
270
270
271 _BACKGROUND_BLUE = 0x0010
271 _BACKGROUND_BLUE = 0x0010
272 _BACKGROUND_GREEN = 0x0020
272 _BACKGROUND_GREEN = 0x0020
273 _BACKGROUND_RED = 0x0040
273 _BACKGROUND_RED = 0x0040
274 _BACKGROUND_INTENSITY = 0x0080
274 _BACKGROUND_INTENSITY = 0x0080
275
275
276 _COMMON_LVB_REVERSE_VIDEO = 0x4000
276 _COMMON_LVB_REVERSE_VIDEO = 0x4000
277 _COMMON_LVB_UNDERSCORE = 0x8000
277 _COMMON_LVB_UNDERSCORE = 0x8000
278
278
279 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
279 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
280 w32effects = {
280 w32effects = {
281 'none': -1,
281 'none': -1,
282 'black': 0,
282 'black': 0,
283 'red': _FOREGROUND_RED,
283 'red': _FOREGROUND_RED,
284 'green': _FOREGROUND_GREEN,
284 'green': _FOREGROUND_GREEN,
285 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
285 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
286 'blue': _FOREGROUND_BLUE,
286 'blue': _FOREGROUND_BLUE,
287 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
287 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
288 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
288 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
289 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
289 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
290 'bold': _FOREGROUND_INTENSITY,
290 'bold': _FOREGROUND_INTENSITY,
291 'black_background': 0x100, # unused value > 0x0f
291 'black_background': 0x100, # unused value > 0x0f
292 'red_background': _BACKGROUND_RED,
292 'red_background': _BACKGROUND_RED,
293 'green_background': _BACKGROUND_GREEN,
293 'green_background': _BACKGROUND_GREEN,
294 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
294 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
295 'blue_background': _BACKGROUND_BLUE,
295 'blue_background': _BACKGROUND_BLUE,
296 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
296 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
297 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
297 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
298 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
298 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
299 _BACKGROUND_BLUE),
299 _BACKGROUND_BLUE),
300 'bold_background': _BACKGROUND_INTENSITY,
300 'bold_background': _BACKGROUND_INTENSITY,
301 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
301 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
302 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
302 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
303 }
303 }
304
304
305 passthrough = set([_FOREGROUND_INTENSITY,
305 passthrough = set([_FOREGROUND_INTENSITY,
306 _BACKGROUND_INTENSITY,
306 _BACKGROUND_INTENSITY,
307 _COMMON_LVB_UNDERSCORE,
307 _COMMON_LVB_UNDERSCORE,
308 _COMMON_LVB_REVERSE_VIDEO])
308 _COMMON_LVB_REVERSE_VIDEO])
309
309
310 stdout = _kernel32.GetStdHandle(
310 stdout = _kernel32.GetStdHandle(
311 _STD_OUTPUT_HANDLE) # don't close the handle returned
311 _STD_OUTPUT_HANDLE) # don't close the handle returned
312 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
312 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
313 w32effects = None
313 w32effects = None
314 else:
314 else:
315 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
315 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
316 if not _kernel32.GetConsoleScreenBufferInfo(
316 if not _kernel32.GetConsoleScreenBufferInfo(
317 stdout, ctypes.byref(csbi)):
317 stdout, ctypes.byref(csbi)):
318 # stdout may not support GetConsoleScreenBufferInfo()
318 # stdout may not support GetConsoleScreenBufferInfo()
319 # when called from subprocess or redirected
319 # when called from subprocess or redirected
320 w32effects = None
320 w32effects = None
321 else:
321 else:
322 origattr = csbi.wAttributes
322 origattr = csbi.wAttributes
323 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
323 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
324 re.MULTILINE | re.DOTALL)
324 re.MULTILINE | re.DOTALL)
325
325
326 def win32print(text, orig, **opts):
326 def win32print(text, orig, **opts):
327 label = opts.get('label', '')
327 label = opts.get('label', '')
328 attr = origattr
328 attr = origattr
329
329
330 def mapcolor(val, attr):
330 def mapcolor(val, attr):
331 if val == -1:
331 if val == -1:
332 return origattr
332 return origattr
333 elif val in passthrough:
333 elif val in passthrough:
334 return attr | val
334 return attr | val
335 elif val > 0x0f:
335 elif val > 0x0f:
336 return (val & 0x70) | (attr & 0x8f)
336 return (val & 0x70) | (attr & 0x8f)
337 else:
337 else:
338 return (val & 0x07) | (attr & 0xf8)
338 return (val & 0x07) | (attr & 0xf8)
339
339
340 # determine console attributes based on labels
340 # determine console attributes based on labels
341 for l in label.split():
341 for l in label.split():
342 style = _styles.get(l, '')
342 style = _styles.get(l, '')
343 for effect in style.split():
343 for effect in style.split():
344 attr = mapcolor(w32effects[effect], attr)
344 attr = mapcolor(w32effects[effect], attr)
345
345
346 # hack to ensure regexp finds data
346 # hack to ensure regexp finds data
347 if not text.startswith('\033['):
347 if not text.startswith('\033['):
348 text = '\033[m' + text
348 text = '\033[m' + text
349
349
350 # Look for ANSI-like codes embedded in text
350 # Look for ANSI-like codes embedded in text
351 m = re.match(ansire, text)
351 m = re.match(ansire, text)
352
353 try:
352 while m:
354 while m:
353 for sattr in m.group(1).split(';'):
355 for sattr in m.group(1).split(';'):
354 if sattr:
356 if sattr:
355 attr = mapcolor(int(sattr), attr)
357 attr = mapcolor(int(sattr), attr)
356 _kernel32.SetConsoleTextAttribute(stdout, attr)
358 _kernel32.SetConsoleTextAttribute(stdout, attr)
357 orig(m.group(2), **opts)
359 orig(m.group(2), **opts)
358 m = re.match(ansire, m.group(3))
360 m = re.match(ansire, m.group(3))
359
361 finally:
360 # Explicity reset original attributes
362 # Explicity reset original attributes
361 _kernel32.SetConsoleTextAttribute(stdout, origattr)
363 _kernel32.SetConsoleTextAttribute(stdout, origattr)
General Comments 0
You need to be logged in to leave comments. Login now