##// END OF EJS Templates
color: add support for terminfo-based attributes and color...
Danek Duvall -
r13987:e0f07847 default
parent child Browse files
Show More
@@ -25,7 +25,9 b' diff-related commands to highlight addit'
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. By default, the terminfo database is used to find the
29 terminal codes used to change color and effect. If terminfo is not
30 available, then effects are rendered with the ECMA-48 SGR control
29 function (aka ANSI escape codes).
31 function (aka ANSI escape codes).
30
32
31 Default effects may be overridden from your configuration file::
33 Default effects may be overridden from your configuration file::
@@ -66,13 +68,35 b' Default effects may be overridden from y'
66 branches.current = green
68 branches.current = green
67 branches.inactive = none
69 branches.inactive = none
68
70
69 The color extension will try to detect whether to use ANSI codes or
71 The available effects in terminfo mode are 'blink', 'bold', 'dim',
70 Win32 console APIs, unless it is made explicit::
72 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
73 ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
74 'underline'. How each is rendered depends on the terminal emulator.
75 Some may not be available for a given terminal type, and will be
76 silently ignored.
77
78 Because there are only eight standard colors, this module allows you
79 to define color names for other color slots which might be available
80 for your terminal type, assuming terminfo mode. For instance::
81
82 color.brightblue = 12
83 color.pink = 207
84 color.orange = 202
85
86 to set 'brightblue' to color slot 12 (useful for 16 color terminals
87 that have brighter colors defined in the upper eight) and, 'pink' and
88 'orange' to colors in 256-color xterm's default color cube. These
89 defined colors may then be used as any of the pre-defined eight,
90 including appending '_background' to set the background to that color.
91
92 The color extension will try to detect whether to use terminfo, ANSI
93 codes or Win32 console APIs, unless it is made explicit; e.g.::
71
94
72 [color]
95 [color]
73 mode = ansi
96 mode = ansi
74
97
75 Any value other than 'ansi', 'win32', or 'auto' will disable color.
98 Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will
99 disable color.
76
100
77 '''
101 '''
78
102
@@ -90,6 +114,68 b' from mercurial.i18n import _'
90 'blue_background': 44, 'purple_background': 45,
114 'blue_background': 44, 'purple_background': 45,
91 'cyan_background': 46, 'white_background': 47}
115 'cyan_background': 46, 'white_background': 47}
92
116
117 def _terminfosetup(ui):
118 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
119
120 global _terminfo_params
121 # If we failed to load curses, we go ahead and return.
122 if not _terminfo_params:
123 return
124 # Otherwise, see what the config file says.
125 mode = ui.config('color', 'mode', 'auto')
126 if mode not in ('auto', 'terminfo'):
127 return
128
129 _terminfo_params.update(dict((
130 (key[6:], (False, int(val)))
131 for key, val in ui.configitems('color')
132 if key.startswith('color.')
133 )))
134
135 try:
136 curses.setupterm()
137 except curses.error, e:
138 _terminfo_params = {}
139 return
140
141 for key, (b, e) in _terminfo_params.items():
142 if not b:
143 continue
144 if not curses.tigetstr(e):
145 # Most terminals don't support dim, invis, etc, so don't be
146 # noisy and use ui.debug().
147 ui.debug("no terminfo entry for %s\n" % e)
148 del _terminfo_params[key]
149 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
150 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
151 "ECMA-48 color\n"))
152 _terminfo_params = {}
153
154 try:
155 import curses
156 # Mapping from effect name to terminfo attribute name or color number.
157 # This will also force-load the curses module.
158 _terminfo_params = {'none': (True, 'sgr0'),
159 'standout': (True, 'smso'),
160 'underline': (True, 'smul'),
161 'reverse': (True, 'rev'),
162 'inverse': (True, 'rev'),
163 'blink': (True, 'blink'),
164 'dim': (True, 'dim'),
165 'bold': (True, 'bold'),
166 'invisible': (True, 'invis'),
167 'italic': (True, 'sitm'),
168 'black': (False, curses.COLOR_BLACK),
169 'red': (False, curses.COLOR_RED),
170 'green': (False, curses.COLOR_GREEN),
171 'yellow': (False, curses.COLOR_YELLOW),
172 'blue': (False, curses.COLOR_BLUE),
173 'magenta': (False, curses.COLOR_MAGENTA),
174 'cyan': (False, curses.COLOR_CYAN),
175 'white': (False, curses.COLOR_WHITE)}
176 except ImportError:
177 _terminfo_params = False
178
93 _styles = {'grep.match': 'red bold',
179 _styles = {'grep.match': 'red bold',
94 'bookmarks.current': 'green',
180 'bookmarks.current': 'green',
95 'branches.active': 'none',
181 'branches.active': 'none',
@@ -121,13 +207,33 b' from mercurial.i18n import _'
121 'status.unknown': 'magenta bold underline'}
207 'status.unknown': 'magenta bold underline'}
122
208
123
209
210 def _effect_str(effect):
211 '''Helper function for render_effects().'''
212
213 bg = False
214 if effect.endswith('_background'):
215 bg = True
216 effect = effect[:-11]
217 attr, val = _terminfo_params[effect]
218 if attr:
219 return curses.tigetstr(val)
220 elif bg:
221 return curses.tparm(curses.tigetstr('setab'), val)
222 else:
223 return curses.tparm(curses.tigetstr('setaf'), val)
224
124 def render_effects(text, effects):
225 def render_effects(text, effects):
125 'Wrap text in commands to turn on each effect.'
226 'Wrap text in commands to turn on each effect.'
126 if not text:
227 if not text:
127 return text
228 return text
128 start = [str(_effects[e]) for e in ['none'] + effects.split()]
229 if not _terminfo_params:
129 start = '\033[' + ';'.join(start) + 'm'
230 start = [str(_effects[e]) for e in ['none'] + effects.split()]
130 stop = '\033[' + str(_effects['none']) + 'm'
231 start = '\033[' + ';'.join(start) + 'm'
232 stop = '\033[' + str(_effects['none']) + 'm'
233 else:
234 start = ''.join(_effect_str(effect)
235 for effect in ['none'] + effects.split())
236 stop = _effect_str('none')
131 return ''.join([start, text, stop])
237 return ''.join([start, text, stop])
132
238
133 def extstyles():
239 def extstyles():
@@ -136,13 +242,15 b' def extstyles():'
136
242
137 def configstyles(ui):
243 def configstyles(ui):
138 for status, cfgeffects in ui.configitems('color'):
244 for status, cfgeffects in ui.configitems('color'):
139 if '.' not in status:
245 if '.' not in status or status.startswith('color.'):
140 continue
246 continue
141 cfgeffects = ui.configlist('color', status)
247 cfgeffects = ui.configlist('color', status)
142 if cfgeffects:
248 if cfgeffects:
143 good = []
249 good = []
144 for e in cfgeffects:
250 for e in cfgeffects:
145 if e in _effects:
251 if not _terminfo_params and e in _effects:
252 good.append(e)
253 elif e in _terminfo_params or e[:-11] in _terminfo_params:
146 good.append(e)
254 good.append(e)
147 else:
255 else:
148 ui.warn(_("ignoring unknown color/effect %r "
256 ui.warn(_("ignoring unknown color/effect %r "
@@ -192,6 +300,7 b' class colorui(uimod.ui):'
192
300
193
301
194 def uisetup(ui):
302 def uisetup(ui):
303 global _terminfo_params
195 if ui.plain():
304 if ui.plain():
196 return
305 return
197 mode = ui.config('color', 'mode', 'auto')
306 mode = ui.config('color', 'mode', 'auto')
@@ -200,14 +309,22 b' def uisetup(ui):'
200 # looks line a cmd.exe console, use win32 API or nothing
309 # looks line a cmd.exe console, use win32 API or nothing
201 mode = w32effects and 'win32' or 'none'
310 mode = w32effects and 'win32' or 'none'
202 else:
311 else:
203 mode = 'ansi'
312 _terminfosetup(ui)
313 if not _terminfo_params:
314 mode = 'ansi'
315 else:
316 mode = 'terminfo'
204 if mode == 'win32':
317 if mode == 'win32':
205 if w32effects is None:
318 if w32effects is None:
206 # only warn if color.mode is explicitly set to win32
319 # only warn if color.mode is explicitly set to win32
207 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
320 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
208 return
321 return
209 _effects.update(w32effects)
322 _effects.update(w32effects)
210 elif mode != 'ansi':
323 elif mode == 'ansi':
324 _terminfo_params = {}
325 elif mode == 'terminfo':
326 _terminfosetup(ui)
327 elif mode not in ('ansi', 'terminfo'):
211 return
328 return
212 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
329 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
213 coloropt = opts['color']
330 coloropt = opts['color']
@@ -350,6 +350,8 b' default branch colors:'
350
350
351 $ echo "[extensions]" >> $HGRCPATH
351 $ echo "[extensions]" >> $HGRCPATH
352 $ echo "color =" >> $HGRCPATH
352 $ echo "color =" >> $HGRCPATH
353 $ echo "[color]" >> $HGRCPATH
354 $ echo "mode = ansi" >> $HGRCPATH
353
355
354 $ hg up -C c
356 $ hg up -C c
355 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
357 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
@@ -1,5 +1,7 b''
1 Setup
1 Setup
2
2
3 $ echo "[color]" >> $HGRCPATH
4 $ echo "mode = ansi" >> $HGRCPATH
3 $ echo "[extensions]" >> $HGRCPATH
5 $ echo "[extensions]" >> $HGRCPATH
4 $ echo "color=" >> $HGRCPATH
6 $ echo "color=" >> $HGRCPATH
5 $ hg init repo
7 $ hg init repo
@@ -57,6 +57,8 b' test issue2039'
57 $ cd bar
57 $ cd bar
58 $ echo "[extensions]" >> $HGRCPATH
58 $ echo "[extensions]" >> $HGRCPATH
59 $ echo "color=" >> $HGRCPATH
59 $ echo "color=" >> $HGRCPATH
60 $ echo "[color]" >> $HGRCPATH
61 $ echo "mode = ansi" >> $HGRCPATH
60 $ A=`printf 'foo\nbar'`
62 $ A=`printf 'foo\nbar'`
61 $ B=`printf 'foo\nbar.baz'`
63 $ B=`printf 'foo\nbar.baz'`
62 $ touch "$A"
64 $ touch "$A"
@@ -309,7 +309,7 b' and d.patch as Unapplied'
309
309
310 qseries again, but with color
310 qseries again, but with color
311
311
312 $ hg --config extensions.color= qseries -v --color=always
312 $ hg --config extensions.color= --config color.mode=ansi qseries -v --color=always
313 0 G \x1b[0;30;1mnew.patch\x1b[0m (esc)
313 0 G \x1b[0;30;1mnew.patch\x1b[0m (esc)
314 1 G \x1b[0;30;1mb.patch\x1b[0m (esc)
314 1 G \x1b[0;30;1mb.patch\x1b[0m (esc)
315 2 A \x1b[0;34;1;4mc.patch\x1b[0m (esc)
315 2 A \x1b[0;34;1;4mc.patch\x1b[0m (esc)
@@ -432,5 +432,5 b' the guards file was not ignored in the p'
432
432
433 hg qseries -m with color
433 hg qseries -m with color
434
434
435 $ hg --config extensions.color= qseries -m --color=always
435 $ hg --config extensions.color= --config color.mode=ansi qseries -m --color=always
436 \x1b[0;31;1mb.patch\x1b[0m (esc)
436 \x1b[0;31;1mb.patch\x1b[0m (esc)
@@ -177,7 +177,7 b' add an untracked file'
177
177
178 status --mq with color (issue2096)
178 status --mq with color (issue2096)
179
179
180 $ hg status --mq --config extensions.color= --color=always
180 $ hg status --mq --config extensions.color= --config color.mode=ansi --color=always
181 \x1b[0;32;1mA .hgignore\x1b[0m (esc)
181 \x1b[0;32;1mA .hgignore\x1b[0m (esc)
182 \x1b[0;32;1mA A\x1b[0m (esc)
182 \x1b[0;32;1mA A\x1b[0m (esc)
183 \x1b[0;32;1mA B\x1b[0m (esc)
183 \x1b[0;32;1mA B\x1b[0m (esc)
@@ -163,6 +163,19 b' hg status -A:'
163 \x1b[0;0mC .hgignore\x1b[0m (esc)
163 \x1b[0;0mC .hgignore\x1b[0m (esc)
164 \x1b[0;0mC modified\x1b[0m (esc)
164 \x1b[0;0mC modified\x1b[0m (esc)
165
165
166 hg status -A (with terminfo color):
167
168 $ TERM=xterm hg status --config color.mode=terminfo --color=always -A
169 \x1b(B\x1b[m\x1b[32m\x1b[1mA added\x1b(B\x1b[m (esc)
170 \x1b(B\x1b[m\x1b[32m\x1b[1mA copied\x1b(B\x1b[m (esc)
171 \x1b(B\x1b[m\x1b(B\x1b[m modified\x1b(B\x1b[m (esc)
172 \x1b(B\x1b[m\x1b[31m\x1b[1mR removed\x1b(B\x1b[m (esc)
173 \x1b(B\x1b[m\x1b[36m\x1b[1m\x1b[4m! deleted\x1b(B\x1b[m (esc)
174 \x1b(B\x1b[m\x1b[35m\x1b[1m\x1b[4m? unknown\x1b(B\x1b[m (esc)
175 \x1b(B\x1b[m\x1b[30m\x1b[1mI ignored\x1b(B\x1b[m (esc)
176 \x1b(B\x1b[m\x1b(B\x1b[mC .hgignore\x1b(B\x1b[m (esc)
177 \x1b(B\x1b[m\x1b(B\x1b[mC modified\x1b(B\x1b[m (esc)
178
166
179
167 $ echo "^ignoreddir$" > .hgignore
180 $ echo "^ignoreddir$" > .hgignore
168 $ mkdir ignoreddir
181 $ mkdir ignoreddir
General Comments 0
You need to be logged in to leave comments. Login now