##// END OF EJS Templates
color: concatenate effects correctly and avoid printing empty effects
Brodie Rao -
r10831:441f5d66 default
parent child Browse files
Show More
@@ -1,180 +1,183 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 to their
21 This extension modifies the status and resolve commands to add color to their
22 output to reflect file status, the qseries command to add color to reflect
22 output to reflect file status, the qseries command to add color to reflect
23 patch status (applied, unapplied, missing), and to diff-related
23 patch status (applied, unapplied, missing), and to diff-related
24 commands to highlight additions, removals, diff headers, and trailing
24 commands to highlight additions, removals, diff headers, and trailing
25 whitespace.
25 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). This module also provides the
29 function (aka ANSI escape codes). This module also provides the
30 render_text function, which can be used to add effects to any text.
30 render_text function, which can be used to add effects to any text.
31
31
32 Default effects may be overridden from the .hgrc file::
32 Default effects may be overridden from the .hgrc file::
33
33
34 [color]
34 [color]
35 status.modified = blue bold underline red_background
35 status.modified = blue bold underline red_background
36 status.added = green bold
36 status.added = green bold
37 status.removed = red bold blue_background
37 status.removed = red bold blue_background
38 status.deleted = cyan bold underline
38 status.deleted = cyan bold underline
39 status.unknown = magenta bold underline
39 status.unknown = magenta bold underline
40 status.ignored = black bold
40 status.ignored = black bold
41
41
42 # 'none' turns off all effects
42 # 'none' turns off all effects
43 status.clean = none
43 status.clean = none
44 status.copied = none
44 status.copied = none
45
45
46 qseries.applied = blue bold underline
46 qseries.applied = blue bold underline
47 qseries.unapplied = black bold
47 qseries.unapplied = black bold
48 qseries.missing = red bold
48 qseries.missing = red bold
49
49
50 diff.diffline = bold
50 diff.diffline = bold
51 diff.extended = cyan bold
51 diff.extended = cyan bold
52 diff.file_a = red bold
52 diff.file_a = red bold
53 diff.file_b = green bold
53 diff.file_b = green bold
54 diff.hunk = magenta
54 diff.hunk = magenta
55 diff.deleted = red
55 diff.deleted = red
56 diff.inserted = green
56 diff.inserted = green
57 diff.changed = white
57 diff.changed = white
58 diff.trailingwhitespace = bold red_background
58 diff.trailingwhitespace = bold red_background
59
59
60 resolve.unresolved = red bold
60 resolve.unresolved = red bold
61 resolve.resolved = green bold
61 resolve.resolved = green bold
62
62
63 bookmarks.current = green
63 bookmarks.current = green
64 '''
64 '''
65
65
66 import os, sys
66 import os, sys
67
67
68 from mercurial import commands, dispatch, extensions
68 from mercurial import commands, dispatch, extensions
69 from mercurial.i18n import _
69 from mercurial.i18n import _
70 from mercurial.ui import ui as uicls
70 from mercurial.ui import ui as uicls
71
71
72 # start and stop parameters for effects
72 # start and stop parameters for effects
73 _effects = {'none': 0, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33,
73 _effects = {'none': 0, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33,
74 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'bold': 1,
74 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'bold': 1,
75 'italic': 3, 'underline': 4, 'inverse': 7,
75 'italic': 3, 'underline': 4, 'inverse': 7,
76 'black_background': 40, 'red_background': 41,
76 'black_background': 40, 'red_background': 41,
77 'green_background': 42, 'yellow_background': 43,
77 'green_background': 42, 'yellow_background': 43,
78 'blue_background': 44, 'purple_background': 45,
78 'blue_background': 44, 'purple_background': 45,
79 'cyan_background': 46, 'white_background': 47}
79 'cyan_background': 46, 'white_background': 47}
80
80
81 _styles = {'grep.match': 'red bold',
81 _styles = {'grep.match': 'red bold',
82 'diff.changed': 'white',
82 'diff.changed': 'white',
83 'diff.deleted': 'red',
83 'diff.deleted': 'red',
84 'diff.diffline': 'bold',
84 'diff.diffline': 'bold',
85 'diff.extended': 'cyan bold',
85 'diff.extended': 'cyan bold',
86 'diff.file_a': 'red bold',
86 'diff.file_a': 'red bold',
87 'diff.file_b': 'green bold',
87 'diff.file_b': 'green bold',
88 'diff.hunk': 'magenta',
88 'diff.hunk': 'magenta',
89 'diff.inserted': 'green',
89 'diff.inserted': 'green',
90 'diff.trailingwhitespace': 'bold red_background',
90 'diff.trailingwhitespace': 'bold red_background',
91 'diffstat.deleted': 'red',
91 'diffstat.deleted': 'red',
92 'diffstat.inserted': 'green',
92 'diffstat.inserted': 'green',
93 'log.changeset': 'yellow',
93 'log.changeset': 'yellow',
94 'resolve.resolved': 'green bold',
94 'resolve.resolved': 'green bold',
95 'resolve.unresolved': 'red bold',
95 'resolve.unresolved': 'red bold',
96 'status.added': 'green bold',
96 'status.added': 'green bold',
97 'status.clean': 'none',
97 'status.clean': 'none',
98 'status.copied': 'none',
98 'status.copied': 'none',
99 'status.deleted': 'cyan bold underline',
99 'status.deleted': 'cyan bold underline',
100 'status.ignored': 'black bold',
100 'status.ignored': 'black bold',
101 'status.modified': 'blue bold',
101 'status.modified': 'blue bold',
102 'status.removed': 'red bold',
102 'status.removed': 'red bold',
103 'status.unknown': 'magenta bold underline'}
103 'status.unknown': 'magenta bold underline'}
104
104
105
105
106 def render_effects(text, effects):
106 def render_effects(text, effects):
107 'Wrap text in commands to turn on each effect.'
107 'Wrap text in commands to turn on each effect.'
108 if not text:
108 if not text:
109 return text
109 return text
110 start = [str(_effects[e]) for e in ['none'] + effects.split()]
110 start = [str(_effects[e]) for e in ['none'] + effects.split()]
111 start = '\033[' + ';'.join(start) + 'm'
111 start = '\033[' + ';'.join(start) + 'm'
112 stop = '\033[' + str(_effects['none']) + 'm'
112 stop = '\033[' + str(_effects['none']) + 'm'
113 return ''.join([start, text, stop])
113 return ''.join([start, text, stop])
114
114
115 def extstyles():
115 def extstyles():
116 for name, ext in extensions.extensions():
116 for name, ext in extensions.extensions():
117 _styles.update(getattr(ext, 'colortable', {}))
117 _styles.update(getattr(ext, 'colortable', {}))
118
118
119 def configstyles(ui):
119 def configstyles(ui):
120 for status, cfgeffects in ui.configitems('color'):
120 for status, cfgeffects in ui.configitems('color'):
121 if '.' not in status:
121 if '.' not in status:
122 continue
122 continue
123 cfgeffects = ui.configlist('color', status)
123 cfgeffects = ui.configlist('color', status)
124 if cfgeffects:
124 if cfgeffects:
125 good = []
125 good = []
126 for e in cfgeffects:
126 for e in cfgeffects:
127 if e in _effects:
127 if e in _effects:
128 good.append(e)
128 good.append(e)
129 else:
129 else:
130 ui.warn(_("ignoring unknown color/effect %r "
130 ui.warn(_("ignoring unknown color/effect %r "
131 "(configured in color.%s)\n")
131 "(configured in color.%s)\n")
132 % (e, status))
132 % (e, status))
133 _styles[status] = ' '.join(good)
133 _styles[status] = ' '.join(good)
134
134
135 _buffers = None
135 _buffers = None
136 def style(msg, label):
136 def style(msg, label):
137 effects = ''
137 effects = []
138 for l in label.split():
138 for l in label.split():
139 effects += _styles.get(l, '')
139 s = _styles.get(l, '')
140 if s:
141 effects.append(s)
142 effects = ''.join(effects)
140 if effects:
143 if effects:
141 return '\n'.join([render_effects(s, effects)
144 return '\n'.join([render_effects(s, effects)
142 for s in msg.split('\n')])
145 for s in msg.split('\n')])
143 return msg
146 return msg
144
147
145 def popbuffer(orig, labeled=False):
148 def popbuffer(orig, labeled=False):
146 global _buffers
149 global _buffers
147 if labeled:
150 if labeled:
148 return ''.join(style(a, label) for a, label in _buffers.pop())
151 return ''.join(style(a, label) for a, label in _buffers.pop())
149 return ''.join(a for a, label in _buffers.pop())
152 return ''.join(a for a, label in _buffers.pop())
150
153
151 def write(orig, *args, **opts):
154 def write(orig, *args, **opts):
152 label = opts.get('label', '')
155 label = opts.get('label', '')
153 global _buffers
156 global _buffers
154 if _buffers:
157 if _buffers:
155 _buffers[-1].extend([(str(a), label) for a in args])
158 _buffers[-1].extend([(str(a), label) for a in args])
156 else:
159 else:
157 return orig(*[style(str(a), label) for a in args], **opts)
160 return orig(*[style(str(a), label) for a in args], **opts)
158
161
159 def write_err(orig, *args, **opts):
162 def write_err(orig, *args, **opts):
160 label = opts.get('label', '')
163 label = opts.get('label', '')
161 return orig(*[style(str(a), label) for a in args], **opts)
164 return orig(*[style(str(a), label) for a in args], **opts)
162
165
163 def uisetup(ui):
166 def uisetup(ui):
164 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
167 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
165 if (opts['color'] == 'always' or
168 if (opts['color'] == 'always' or
166 (opts['color'] == 'auto' and (os.environ.get('TERM') != 'dumb'
169 (opts['color'] == 'auto' and (os.environ.get('TERM') != 'dumb'
167 and sys.__stdout__.isatty()))):
170 and sys.__stdout__.isatty()))):
168 global _buffers
171 global _buffers
169 _buffers = ui_._buffers
172 _buffers = ui_._buffers
170 extensions.wrapfunction(ui_, 'popbuffer', popbuffer)
173 extensions.wrapfunction(ui_, 'popbuffer', popbuffer)
171 extensions.wrapfunction(ui_, 'write', write)
174 extensions.wrapfunction(ui_, 'write', write)
172 extensions.wrapfunction(ui_, 'write_err', write_err)
175 extensions.wrapfunction(ui_, 'write_err', write_err)
173 ui_.label = style
176 ui_.label = style
174 extstyles()
177 extstyles()
175 configstyles(ui)
178 configstyles(ui)
176 return orig(ui_, opts, cmd, cmdfunc)
179 return orig(ui_, opts, cmd, cmdfunc)
177 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
180 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
178
181
179 commands.globalopts.append(('', 'color', 'auto',
182 commands.globalopts.append(('', 'color', 'auto',
180 _("when to colorize (always, auto, or never)")))
183 _("when to colorize (always, auto, or never)")))
General Comments 0
You need to be logged in to leave comments. Login now