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 |
|
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 |
st |
|
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 |
|
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 |
$ |
|
180 | $ echo "^ignoreddir$" > .hgignore | |
168 |
$ |
|
181 | $ mkdir ignoreddir |
General Comments 0
You need to be logged in to leave comments.
Login now