##// END OF EJS Templates
ui: track label expansion when creating buffers...
Gregory Szorc -
r27106:6ef17697 default
parent child Browse files
Show More
@@ -1,1131 +1,1148 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
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 errno
10 import errno
11 import getpass
11 import getpass
12 import inspect
12 import inspect
13 import os
13 import os
14 import socket
14 import socket
15 import sys
15 import sys
16 import tempfile
16 import tempfile
17 import traceback
17 import traceback
18
18
19 from .i18n import _
19 from .i18n import _
20 from .node import hex
20 from .node import hex
21
21
22 from . import (
22 from . import (
23 config,
23 config,
24 error,
24 error,
25 formatter,
25 formatter,
26 progress,
26 progress,
27 scmutil,
27 scmutil,
28 util,
28 util,
29 )
29 )
30
30
31 samplehgrcs = {
31 samplehgrcs = {
32 'user':
32 'user':
33 """# example user config (see "hg help config" for more info)
33 """# example user config (see "hg help config" for more info)
34 [ui]
34 [ui]
35 # name and email, e.g.
35 # name and email, e.g.
36 # username = Jane Doe <jdoe@example.com>
36 # username = Jane Doe <jdoe@example.com>
37 username =
37 username =
38
38
39 [extensions]
39 [extensions]
40 # uncomment these lines to enable some popular extensions
40 # uncomment these lines to enable some popular extensions
41 # (see "hg help extensions" for more info)
41 # (see "hg help extensions" for more info)
42 #
42 #
43 # pager =
43 # pager =
44 # progress =
44 # progress =
45 # color =""",
45 # color =""",
46
46
47 'cloned':
47 'cloned':
48 """# example repository config (see "hg help config" for more info)
48 """# example repository config (see "hg help config" for more info)
49 [paths]
49 [paths]
50 default = %s
50 default = %s
51
51
52 # path aliases to other clones of this repo in URLs or filesystem paths
52 # path aliases to other clones of this repo in URLs or filesystem paths
53 # (see "hg help config.paths" for more info)
53 # (see "hg help config.paths" for more info)
54 #
54 #
55 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
55 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
56 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
56 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
57 # my-clone = /home/jdoe/jdoes-clone
57 # my-clone = /home/jdoe/jdoes-clone
58
58
59 [ui]
59 [ui]
60 # name and email (local to this repository, optional), e.g.
60 # name and email (local to this repository, optional), e.g.
61 # username = Jane Doe <jdoe@example.com>
61 # username = Jane Doe <jdoe@example.com>
62 """,
62 """,
63
63
64 'local':
64 'local':
65 """# example repository config (see "hg help config" for more info)
65 """# example repository config (see "hg help config" for more info)
66 [paths]
66 [paths]
67 # path aliases to other clones of this repo in URLs or filesystem paths
67 # path aliases to other clones of this repo in URLs or filesystem paths
68 # (see "hg help config.paths" for more info)
68 # (see "hg help config.paths" for more info)
69 #
69 #
70 # default = http://example.com/hg/example-repo
70 # default = http://example.com/hg/example-repo
71 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
71 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
72 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
72 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
73 # my-clone = /home/jdoe/jdoes-clone
73 # my-clone = /home/jdoe/jdoes-clone
74
74
75 [ui]
75 [ui]
76 # name and email (local to this repository, optional), e.g.
76 # name and email (local to this repository, optional), e.g.
77 # username = Jane Doe <jdoe@example.com>
77 # username = Jane Doe <jdoe@example.com>
78 """,
78 """,
79
79
80 'global':
80 'global':
81 """# example system-wide hg config (see "hg help config" for more info)
81 """# example system-wide hg config (see "hg help config" for more info)
82
82
83 [extensions]
83 [extensions]
84 # uncomment these lines to enable some popular extensions
84 # uncomment these lines to enable some popular extensions
85 # (see "hg help extensions" for more info)
85 # (see "hg help extensions" for more info)
86 #
86 #
87 # blackbox =
87 # blackbox =
88 # progress =
88 # progress =
89 # color =
89 # color =
90 # pager =""",
90 # pager =""",
91 }
91 }
92
92
93 class ui(object):
93 class ui(object):
94 def __init__(self, src=None):
94 def __init__(self, src=None):
95 # _buffers: used for temporary capture of output
95 # _buffers: used for temporary capture of output
96 self._buffers = []
96 self._buffers = []
97 # _bufferstates:
97 # 3-tuple describing how each buffer in the stack behaves.
98 # should the temporary capture include stderr and subprocess output
98 # Values are (capture stderr, capture subprocesses, apply labels).
99 self._bufferstates = []
99 self._bufferstates = []
100 # When a buffer is active, defines whether we are expanding labels.
101 # This exists to prevent an extra list lookup.
102 self._bufferapplylabels = None
100 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
103 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
101 self._reportuntrusted = True
104 self._reportuntrusted = True
102 self._ocfg = config.config() # overlay
105 self._ocfg = config.config() # overlay
103 self._tcfg = config.config() # trusted
106 self._tcfg = config.config() # trusted
104 self._ucfg = config.config() # untrusted
107 self._ucfg = config.config() # untrusted
105 self._trustusers = set()
108 self._trustusers = set()
106 self._trustgroups = set()
109 self._trustgroups = set()
107 self.callhooks = True
110 self.callhooks = True
108
111
109 if src:
112 if src:
110 self.fout = src.fout
113 self.fout = src.fout
111 self.ferr = src.ferr
114 self.ferr = src.ferr
112 self.fin = src.fin
115 self.fin = src.fin
113
116
114 self._tcfg = src._tcfg.copy()
117 self._tcfg = src._tcfg.copy()
115 self._ucfg = src._ucfg.copy()
118 self._ucfg = src._ucfg.copy()
116 self._ocfg = src._ocfg.copy()
119 self._ocfg = src._ocfg.copy()
117 self._trustusers = src._trustusers.copy()
120 self._trustusers = src._trustusers.copy()
118 self._trustgroups = src._trustgroups.copy()
121 self._trustgroups = src._trustgroups.copy()
119 self.environ = src.environ
122 self.environ = src.environ
120 self.callhooks = src.callhooks
123 self.callhooks = src.callhooks
121 self.fixconfig()
124 self.fixconfig()
122 else:
125 else:
123 self.fout = sys.stdout
126 self.fout = sys.stdout
124 self.ferr = sys.stderr
127 self.ferr = sys.stderr
125 self.fin = sys.stdin
128 self.fin = sys.stdin
126
129
127 # shared read-only environment
130 # shared read-only environment
128 self.environ = os.environ
131 self.environ = os.environ
129 # we always trust global config files
132 # we always trust global config files
130 for f in scmutil.rcpath():
133 for f in scmutil.rcpath():
131 self.readconfig(f, trust=True)
134 self.readconfig(f, trust=True)
132
135
133 def copy(self):
136 def copy(self):
134 return self.__class__(self)
137 return self.__class__(self)
135
138
136 def formatter(self, topic, opts):
139 def formatter(self, topic, opts):
137 return formatter.formatter(self, topic, opts)
140 return formatter.formatter(self, topic, opts)
138
141
139 def _trusted(self, fp, f):
142 def _trusted(self, fp, f):
140 st = util.fstat(fp)
143 st = util.fstat(fp)
141 if util.isowner(st):
144 if util.isowner(st):
142 return True
145 return True
143
146
144 tusers, tgroups = self._trustusers, self._trustgroups
147 tusers, tgroups = self._trustusers, self._trustgroups
145 if '*' in tusers or '*' in tgroups:
148 if '*' in tusers or '*' in tgroups:
146 return True
149 return True
147
150
148 user = util.username(st.st_uid)
151 user = util.username(st.st_uid)
149 group = util.groupname(st.st_gid)
152 group = util.groupname(st.st_gid)
150 if user in tusers or group in tgroups or user == util.username():
153 if user in tusers or group in tgroups or user == util.username():
151 return True
154 return True
152
155
153 if self._reportuntrusted:
156 if self._reportuntrusted:
154 self.warn(_('not trusting file %s from untrusted '
157 self.warn(_('not trusting file %s from untrusted '
155 'user %s, group %s\n') % (f, user, group))
158 'user %s, group %s\n') % (f, user, group))
156 return False
159 return False
157
160
158 def readconfig(self, filename, root=None, trust=False,
161 def readconfig(self, filename, root=None, trust=False,
159 sections=None, remap=None):
162 sections=None, remap=None):
160 try:
163 try:
161 fp = open(filename)
164 fp = open(filename)
162 except IOError:
165 except IOError:
163 if not sections: # ignore unless we were looking for something
166 if not sections: # ignore unless we were looking for something
164 return
167 return
165 raise
168 raise
166
169
167 cfg = config.config()
170 cfg = config.config()
168 trusted = sections or trust or self._trusted(fp, filename)
171 trusted = sections or trust or self._trusted(fp, filename)
169
172
170 try:
173 try:
171 cfg.read(filename, fp, sections=sections, remap=remap)
174 cfg.read(filename, fp, sections=sections, remap=remap)
172 fp.close()
175 fp.close()
173 except error.ConfigError as inst:
176 except error.ConfigError as inst:
174 if trusted:
177 if trusted:
175 raise
178 raise
176 self.warn(_("ignored: %s\n") % str(inst))
179 self.warn(_("ignored: %s\n") % str(inst))
177
180
178 if self.plain():
181 if self.plain():
179 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
182 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
180 'logtemplate', 'statuscopies', 'style',
183 'logtemplate', 'statuscopies', 'style',
181 'traceback', 'verbose'):
184 'traceback', 'verbose'):
182 if k in cfg['ui']:
185 if k in cfg['ui']:
183 del cfg['ui'][k]
186 del cfg['ui'][k]
184 for k, v in cfg.items('defaults'):
187 for k, v in cfg.items('defaults'):
185 del cfg['defaults'][k]
188 del cfg['defaults'][k]
186 # Don't remove aliases from the configuration if in the exceptionlist
189 # Don't remove aliases from the configuration if in the exceptionlist
187 if self.plain('alias'):
190 if self.plain('alias'):
188 for k, v in cfg.items('alias'):
191 for k, v in cfg.items('alias'):
189 del cfg['alias'][k]
192 del cfg['alias'][k]
190 if self.plain('revsetalias'):
193 if self.plain('revsetalias'):
191 for k, v in cfg.items('revsetalias'):
194 for k, v in cfg.items('revsetalias'):
192 del cfg['revsetalias'][k]
195 del cfg['revsetalias'][k]
193
196
194 if trusted:
197 if trusted:
195 self._tcfg.update(cfg)
198 self._tcfg.update(cfg)
196 self._tcfg.update(self._ocfg)
199 self._tcfg.update(self._ocfg)
197 self._ucfg.update(cfg)
200 self._ucfg.update(cfg)
198 self._ucfg.update(self._ocfg)
201 self._ucfg.update(self._ocfg)
199
202
200 if root is None:
203 if root is None:
201 root = os.path.expanduser('~')
204 root = os.path.expanduser('~')
202 self.fixconfig(root=root)
205 self.fixconfig(root=root)
203
206
204 def fixconfig(self, root=None, section=None):
207 def fixconfig(self, root=None, section=None):
205 if section in (None, 'paths'):
208 if section in (None, 'paths'):
206 # expand vars and ~
209 # expand vars and ~
207 # translate paths relative to root (or home) into absolute paths
210 # translate paths relative to root (or home) into absolute paths
208 root = root or os.getcwd()
211 root = root or os.getcwd()
209 for c in self._tcfg, self._ucfg, self._ocfg:
212 for c in self._tcfg, self._ucfg, self._ocfg:
210 for n, p in c.items('paths'):
213 for n, p in c.items('paths'):
211 if not p:
214 if not p:
212 continue
215 continue
213 if '%%' in p:
216 if '%%' in p:
214 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
217 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
215 % (n, p, self.configsource('paths', n)))
218 % (n, p, self.configsource('paths', n)))
216 p = p.replace('%%', '%')
219 p = p.replace('%%', '%')
217 p = util.expandpath(p)
220 p = util.expandpath(p)
218 if not util.hasscheme(p) and not os.path.isabs(p):
221 if not util.hasscheme(p) and not os.path.isabs(p):
219 p = os.path.normpath(os.path.join(root, p))
222 p = os.path.normpath(os.path.join(root, p))
220 c.set("paths", n, p)
223 c.set("paths", n, p)
221
224
222 if section in (None, 'ui'):
225 if section in (None, 'ui'):
223 # update ui options
226 # update ui options
224 self.debugflag = self.configbool('ui', 'debug')
227 self.debugflag = self.configbool('ui', 'debug')
225 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
228 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
226 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
229 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
227 if self.verbose and self.quiet:
230 if self.verbose and self.quiet:
228 self.quiet = self.verbose = False
231 self.quiet = self.verbose = False
229 self._reportuntrusted = self.debugflag or self.configbool("ui",
232 self._reportuntrusted = self.debugflag or self.configbool("ui",
230 "report_untrusted", True)
233 "report_untrusted", True)
231 self.tracebackflag = self.configbool('ui', 'traceback', False)
234 self.tracebackflag = self.configbool('ui', 'traceback', False)
232
235
233 if section in (None, 'trusted'):
236 if section in (None, 'trusted'):
234 # update trust information
237 # update trust information
235 self._trustusers.update(self.configlist('trusted', 'users'))
238 self._trustusers.update(self.configlist('trusted', 'users'))
236 self._trustgroups.update(self.configlist('trusted', 'groups'))
239 self._trustgroups.update(self.configlist('trusted', 'groups'))
237
240
238 def backupconfig(self, section, item):
241 def backupconfig(self, section, item):
239 return (self._ocfg.backup(section, item),
242 return (self._ocfg.backup(section, item),
240 self._tcfg.backup(section, item),
243 self._tcfg.backup(section, item),
241 self._ucfg.backup(section, item),)
244 self._ucfg.backup(section, item),)
242 def restoreconfig(self, data):
245 def restoreconfig(self, data):
243 self._ocfg.restore(data[0])
246 self._ocfg.restore(data[0])
244 self._tcfg.restore(data[1])
247 self._tcfg.restore(data[1])
245 self._ucfg.restore(data[2])
248 self._ucfg.restore(data[2])
246
249
247 def setconfig(self, section, name, value, source=''):
250 def setconfig(self, section, name, value, source=''):
248 for cfg in (self._ocfg, self._tcfg, self._ucfg):
251 for cfg in (self._ocfg, self._tcfg, self._ucfg):
249 cfg.set(section, name, value, source)
252 cfg.set(section, name, value, source)
250 self.fixconfig(section=section)
253 self.fixconfig(section=section)
251
254
252 def _data(self, untrusted):
255 def _data(self, untrusted):
253 return untrusted and self._ucfg or self._tcfg
256 return untrusted and self._ucfg or self._tcfg
254
257
255 def configsource(self, section, name, untrusted=False):
258 def configsource(self, section, name, untrusted=False):
256 return self._data(untrusted).source(section, name) or 'none'
259 return self._data(untrusted).source(section, name) or 'none'
257
260
258 def config(self, section, name, default=None, untrusted=False):
261 def config(self, section, name, default=None, untrusted=False):
259 if isinstance(name, list):
262 if isinstance(name, list):
260 alternates = name
263 alternates = name
261 else:
264 else:
262 alternates = [name]
265 alternates = [name]
263
266
264 for n in alternates:
267 for n in alternates:
265 value = self._data(untrusted).get(section, n, None)
268 value = self._data(untrusted).get(section, n, None)
266 if value is not None:
269 if value is not None:
267 name = n
270 name = n
268 break
271 break
269 else:
272 else:
270 value = default
273 value = default
271
274
272 if self.debugflag and not untrusted and self._reportuntrusted:
275 if self.debugflag and not untrusted and self._reportuntrusted:
273 for n in alternates:
276 for n in alternates:
274 uvalue = self._ucfg.get(section, n)
277 uvalue = self._ucfg.get(section, n)
275 if uvalue is not None and uvalue != value:
278 if uvalue is not None and uvalue != value:
276 self.debug("ignoring untrusted configuration option "
279 self.debug("ignoring untrusted configuration option "
277 "%s.%s = %s\n" % (section, n, uvalue))
280 "%s.%s = %s\n" % (section, n, uvalue))
278 return value
281 return value
279
282
280 def configpath(self, section, name, default=None, untrusted=False):
283 def configpath(self, section, name, default=None, untrusted=False):
281 'get a path config item, expanded relative to repo root or config file'
284 'get a path config item, expanded relative to repo root or config file'
282 v = self.config(section, name, default, untrusted)
285 v = self.config(section, name, default, untrusted)
283 if v is None:
286 if v is None:
284 return None
287 return None
285 if not os.path.isabs(v) or "://" not in v:
288 if not os.path.isabs(v) or "://" not in v:
286 src = self.configsource(section, name, untrusted)
289 src = self.configsource(section, name, untrusted)
287 if ':' in src:
290 if ':' in src:
288 base = os.path.dirname(src.rsplit(':')[0])
291 base = os.path.dirname(src.rsplit(':')[0])
289 v = os.path.join(base, os.path.expanduser(v))
292 v = os.path.join(base, os.path.expanduser(v))
290 return v
293 return v
291
294
292 def configbool(self, section, name, default=False, untrusted=False):
295 def configbool(self, section, name, default=False, untrusted=False):
293 """parse a configuration element as a boolean
296 """parse a configuration element as a boolean
294
297
295 >>> u = ui(); s = 'foo'
298 >>> u = ui(); s = 'foo'
296 >>> u.setconfig(s, 'true', 'yes')
299 >>> u.setconfig(s, 'true', 'yes')
297 >>> u.configbool(s, 'true')
300 >>> u.configbool(s, 'true')
298 True
301 True
299 >>> u.setconfig(s, 'false', 'no')
302 >>> u.setconfig(s, 'false', 'no')
300 >>> u.configbool(s, 'false')
303 >>> u.configbool(s, 'false')
301 False
304 False
302 >>> u.configbool(s, 'unknown')
305 >>> u.configbool(s, 'unknown')
303 False
306 False
304 >>> u.configbool(s, 'unknown', True)
307 >>> u.configbool(s, 'unknown', True)
305 True
308 True
306 >>> u.setconfig(s, 'invalid', 'somevalue')
309 >>> u.setconfig(s, 'invalid', 'somevalue')
307 >>> u.configbool(s, 'invalid')
310 >>> u.configbool(s, 'invalid')
308 Traceback (most recent call last):
311 Traceback (most recent call last):
309 ...
312 ...
310 ConfigError: foo.invalid is not a boolean ('somevalue')
313 ConfigError: foo.invalid is not a boolean ('somevalue')
311 """
314 """
312
315
313 v = self.config(section, name, None, untrusted)
316 v = self.config(section, name, None, untrusted)
314 if v is None:
317 if v is None:
315 return default
318 return default
316 if isinstance(v, bool):
319 if isinstance(v, bool):
317 return v
320 return v
318 b = util.parsebool(v)
321 b = util.parsebool(v)
319 if b is None:
322 if b is None:
320 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
323 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
321 % (section, name, v))
324 % (section, name, v))
322 return b
325 return b
323
326
324 def configint(self, section, name, default=None, untrusted=False):
327 def configint(self, section, name, default=None, untrusted=False):
325 """parse a configuration element as an integer
328 """parse a configuration element as an integer
326
329
327 >>> u = ui(); s = 'foo'
330 >>> u = ui(); s = 'foo'
328 >>> u.setconfig(s, 'int1', '42')
331 >>> u.setconfig(s, 'int1', '42')
329 >>> u.configint(s, 'int1')
332 >>> u.configint(s, 'int1')
330 42
333 42
331 >>> u.setconfig(s, 'int2', '-42')
334 >>> u.setconfig(s, 'int2', '-42')
332 >>> u.configint(s, 'int2')
335 >>> u.configint(s, 'int2')
333 -42
336 -42
334 >>> u.configint(s, 'unknown', 7)
337 >>> u.configint(s, 'unknown', 7)
335 7
338 7
336 >>> u.setconfig(s, 'invalid', 'somevalue')
339 >>> u.setconfig(s, 'invalid', 'somevalue')
337 >>> u.configint(s, 'invalid')
340 >>> u.configint(s, 'invalid')
338 Traceback (most recent call last):
341 Traceback (most recent call last):
339 ...
342 ...
340 ConfigError: foo.invalid is not an integer ('somevalue')
343 ConfigError: foo.invalid is not an integer ('somevalue')
341 """
344 """
342
345
343 v = self.config(section, name, None, untrusted)
346 v = self.config(section, name, None, untrusted)
344 if v is None:
347 if v is None:
345 return default
348 return default
346 try:
349 try:
347 return int(v)
350 return int(v)
348 except ValueError:
351 except ValueError:
349 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
352 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
350 % (section, name, v))
353 % (section, name, v))
351
354
352 def configbytes(self, section, name, default=0, untrusted=False):
355 def configbytes(self, section, name, default=0, untrusted=False):
353 """parse a configuration element as a quantity in bytes
356 """parse a configuration element as a quantity in bytes
354
357
355 Units can be specified as b (bytes), k or kb (kilobytes), m or
358 Units can be specified as b (bytes), k or kb (kilobytes), m or
356 mb (megabytes), g or gb (gigabytes).
359 mb (megabytes), g or gb (gigabytes).
357
360
358 >>> u = ui(); s = 'foo'
361 >>> u = ui(); s = 'foo'
359 >>> u.setconfig(s, 'val1', '42')
362 >>> u.setconfig(s, 'val1', '42')
360 >>> u.configbytes(s, 'val1')
363 >>> u.configbytes(s, 'val1')
361 42
364 42
362 >>> u.setconfig(s, 'val2', '42.5 kb')
365 >>> u.setconfig(s, 'val2', '42.5 kb')
363 >>> u.configbytes(s, 'val2')
366 >>> u.configbytes(s, 'val2')
364 43520
367 43520
365 >>> u.configbytes(s, 'unknown', '7 MB')
368 >>> u.configbytes(s, 'unknown', '7 MB')
366 7340032
369 7340032
367 >>> u.setconfig(s, 'invalid', 'somevalue')
370 >>> u.setconfig(s, 'invalid', 'somevalue')
368 >>> u.configbytes(s, 'invalid')
371 >>> u.configbytes(s, 'invalid')
369 Traceback (most recent call last):
372 Traceback (most recent call last):
370 ...
373 ...
371 ConfigError: foo.invalid is not a byte quantity ('somevalue')
374 ConfigError: foo.invalid is not a byte quantity ('somevalue')
372 """
375 """
373
376
374 value = self.config(section, name)
377 value = self.config(section, name)
375 if value is None:
378 if value is None:
376 if not isinstance(default, str):
379 if not isinstance(default, str):
377 return default
380 return default
378 value = default
381 value = default
379 try:
382 try:
380 return util.sizetoint(value)
383 return util.sizetoint(value)
381 except error.ParseError:
384 except error.ParseError:
382 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
385 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
383 % (section, name, value))
386 % (section, name, value))
384
387
385 def configlist(self, section, name, default=None, untrusted=False):
388 def configlist(self, section, name, default=None, untrusted=False):
386 """parse a configuration element as a list of comma/space separated
389 """parse a configuration element as a list of comma/space separated
387 strings
390 strings
388
391
389 >>> u = ui(); s = 'foo'
392 >>> u = ui(); s = 'foo'
390 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
393 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
391 >>> u.configlist(s, 'list1')
394 >>> u.configlist(s, 'list1')
392 ['this', 'is', 'a small', 'test']
395 ['this', 'is', 'a small', 'test']
393 """
396 """
394
397
395 def _parse_plain(parts, s, offset):
398 def _parse_plain(parts, s, offset):
396 whitespace = False
399 whitespace = False
397 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
400 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
398 whitespace = True
401 whitespace = True
399 offset += 1
402 offset += 1
400 if offset >= len(s):
403 if offset >= len(s):
401 return None, parts, offset
404 return None, parts, offset
402 if whitespace:
405 if whitespace:
403 parts.append('')
406 parts.append('')
404 if s[offset] == '"' and not parts[-1]:
407 if s[offset] == '"' and not parts[-1]:
405 return _parse_quote, parts, offset + 1
408 return _parse_quote, parts, offset + 1
406 elif s[offset] == '"' and parts[-1][-1] == '\\':
409 elif s[offset] == '"' and parts[-1][-1] == '\\':
407 parts[-1] = parts[-1][:-1] + s[offset]
410 parts[-1] = parts[-1][:-1] + s[offset]
408 return _parse_plain, parts, offset + 1
411 return _parse_plain, parts, offset + 1
409 parts[-1] += s[offset]
412 parts[-1] += s[offset]
410 return _parse_plain, parts, offset + 1
413 return _parse_plain, parts, offset + 1
411
414
412 def _parse_quote(parts, s, offset):
415 def _parse_quote(parts, s, offset):
413 if offset < len(s) and s[offset] == '"': # ""
416 if offset < len(s) and s[offset] == '"': # ""
414 parts.append('')
417 parts.append('')
415 offset += 1
418 offset += 1
416 while offset < len(s) and (s[offset].isspace() or
419 while offset < len(s) and (s[offset].isspace() or
417 s[offset] == ','):
420 s[offset] == ','):
418 offset += 1
421 offset += 1
419 return _parse_plain, parts, offset
422 return _parse_plain, parts, offset
420
423
421 while offset < len(s) and s[offset] != '"':
424 while offset < len(s) and s[offset] != '"':
422 if (s[offset] == '\\' and offset + 1 < len(s)
425 if (s[offset] == '\\' and offset + 1 < len(s)
423 and s[offset + 1] == '"'):
426 and s[offset + 1] == '"'):
424 offset += 1
427 offset += 1
425 parts[-1] += '"'
428 parts[-1] += '"'
426 else:
429 else:
427 parts[-1] += s[offset]
430 parts[-1] += s[offset]
428 offset += 1
431 offset += 1
429
432
430 if offset >= len(s):
433 if offset >= len(s):
431 real_parts = _configlist(parts[-1])
434 real_parts = _configlist(parts[-1])
432 if not real_parts:
435 if not real_parts:
433 parts[-1] = '"'
436 parts[-1] = '"'
434 else:
437 else:
435 real_parts[0] = '"' + real_parts[0]
438 real_parts[0] = '"' + real_parts[0]
436 parts = parts[:-1]
439 parts = parts[:-1]
437 parts.extend(real_parts)
440 parts.extend(real_parts)
438 return None, parts, offset
441 return None, parts, offset
439
442
440 offset += 1
443 offset += 1
441 while offset < len(s) and s[offset] in [' ', ',']:
444 while offset < len(s) and s[offset] in [' ', ',']:
442 offset += 1
445 offset += 1
443
446
444 if offset < len(s):
447 if offset < len(s):
445 if offset + 1 == len(s) and s[offset] == '"':
448 if offset + 1 == len(s) and s[offset] == '"':
446 parts[-1] += '"'
449 parts[-1] += '"'
447 offset += 1
450 offset += 1
448 else:
451 else:
449 parts.append('')
452 parts.append('')
450 else:
453 else:
451 return None, parts, offset
454 return None, parts, offset
452
455
453 return _parse_plain, parts, offset
456 return _parse_plain, parts, offset
454
457
455 def _configlist(s):
458 def _configlist(s):
456 s = s.rstrip(' ,')
459 s = s.rstrip(' ,')
457 if not s:
460 if not s:
458 return []
461 return []
459 parser, parts, offset = _parse_plain, [''], 0
462 parser, parts, offset = _parse_plain, [''], 0
460 while parser:
463 while parser:
461 parser, parts, offset = parser(parts, s, offset)
464 parser, parts, offset = parser(parts, s, offset)
462 return parts
465 return parts
463
466
464 result = self.config(section, name, untrusted=untrusted)
467 result = self.config(section, name, untrusted=untrusted)
465 if result is None:
468 if result is None:
466 result = default or []
469 result = default or []
467 if isinstance(result, basestring):
470 if isinstance(result, basestring):
468 result = _configlist(result.lstrip(' ,\n'))
471 result = _configlist(result.lstrip(' ,\n'))
469 if result is None:
472 if result is None:
470 result = default or []
473 result = default or []
471 return result
474 return result
472
475
473 def has_section(self, section, untrusted=False):
476 def has_section(self, section, untrusted=False):
474 '''tell whether section exists in config.'''
477 '''tell whether section exists in config.'''
475 return section in self._data(untrusted)
478 return section in self._data(untrusted)
476
479
477 def configitems(self, section, untrusted=False):
480 def configitems(self, section, untrusted=False):
478 items = self._data(untrusted).items(section)
481 items = self._data(untrusted).items(section)
479 if self.debugflag and not untrusted and self._reportuntrusted:
482 if self.debugflag and not untrusted and self._reportuntrusted:
480 for k, v in self._ucfg.items(section):
483 for k, v in self._ucfg.items(section):
481 if self._tcfg.get(section, k) != v:
484 if self._tcfg.get(section, k) != v:
482 self.debug("ignoring untrusted configuration option "
485 self.debug("ignoring untrusted configuration option "
483 "%s.%s = %s\n" % (section, k, v))
486 "%s.%s = %s\n" % (section, k, v))
484 return items
487 return items
485
488
486 def walkconfig(self, untrusted=False):
489 def walkconfig(self, untrusted=False):
487 cfg = self._data(untrusted)
490 cfg = self._data(untrusted)
488 for section in cfg.sections():
491 for section in cfg.sections():
489 for name, value in self.configitems(section, untrusted):
492 for name, value in self.configitems(section, untrusted):
490 yield section, name, value
493 yield section, name, value
491
494
492 def plain(self, feature=None):
495 def plain(self, feature=None):
493 '''is plain mode active?
496 '''is plain mode active?
494
497
495 Plain mode means that all configuration variables which affect
498 Plain mode means that all configuration variables which affect
496 the behavior and output of Mercurial should be
499 the behavior and output of Mercurial should be
497 ignored. Additionally, the output should be stable,
500 ignored. Additionally, the output should be stable,
498 reproducible and suitable for use in scripts or applications.
501 reproducible and suitable for use in scripts or applications.
499
502
500 The only way to trigger plain mode is by setting either the
503 The only way to trigger plain mode is by setting either the
501 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
504 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
502
505
503 The return value can either be
506 The return value can either be
504 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
507 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
505 - True otherwise
508 - True otherwise
506 '''
509 '''
507 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
510 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
508 return False
511 return False
509 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
512 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
510 if feature and exceptions:
513 if feature and exceptions:
511 return feature not in exceptions
514 return feature not in exceptions
512 return True
515 return True
513
516
514 def username(self):
517 def username(self):
515 """Return default username to be used in commits.
518 """Return default username to be used in commits.
516
519
517 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
520 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
518 and stop searching if one of these is set.
521 and stop searching if one of these is set.
519 If not found and ui.askusername is True, ask the user, else use
522 If not found and ui.askusername is True, ask the user, else use
520 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
523 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
521 """
524 """
522 user = os.environ.get("HGUSER")
525 user = os.environ.get("HGUSER")
523 if user is None:
526 if user is None:
524 user = self.config("ui", ["username", "user"])
527 user = self.config("ui", ["username", "user"])
525 if user is not None:
528 if user is not None:
526 user = os.path.expandvars(user)
529 user = os.path.expandvars(user)
527 if user is None:
530 if user is None:
528 user = os.environ.get("EMAIL")
531 user = os.environ.get("EMAIL")
529 if user is None and self.configbool("ui", "askusername"):
532 if user is None and self.configbool("ui", "askusername"):
530 user = self.prompt(_("enter a commit username:"), default=None)
533 user = self.prompt(_("enter a commit username:"), default=None)
531 if user is None and not self.interactive():
534 if user is None and not self.interactive():
532 try:
535 try:
533 user = '%s@%s' % (util.getuser(), socket.getfqdn())
536 user = '%s@%s' % (util.getuser(), socket.getfqdn())
534 self.warn(_("no username found, using '%s' instead\n") % user)
537 self.warn(_("no username found, using '%s' instead\n") % user)
535 except KeyError:
538 except KeyError:
536 pass
539 pass
537 if not user:
540 if not user:
538 raise error.Abort(_('no username supplied'),
541 raise error.Abort(_('no username supplied'),
539 hint=_('use "hg config --edit" '
542 hint=_('use "hg config --edit" '
540 'to set your username'))
543 'to set your username'))
541 if "\n" in user:
544 if "\n" in user:
542 raise error.Abort(_("username %s contains a newline\n")
545 raise error.Abort(_("username %s contains a newline\n")
543 % repr(user))
546 % repr(user))
544 return user
547 return user
545
548
546 def shortuser(self, user):
549 def shortuser(self, user):
547 """Return a short representation of a user name or email address."""
550 """Return a short representation of a user name or email address."""
548 if not self.verbose:
551 if not self.verbose:
549 user = util.shortuser(user)
552 user = util.shortuser(user)
550 return user
553 return user
551
554
552 def expandpath(self, loc, default=None):
555 def expandpath(self, loc, default=None):
553 """Return repository location relative to cwd or from [paths]"""
556 """Return repository location relative to cwd or from [paths]"""
554 try:
557 try:
555 p = self.paths.getpath(loc)
558 p = self.paths.getpath(loc)
556 if p:
559 if p:
557 return p.rawloc
560 return p.rawloc
558 except error.RepoError:
561 except error.RepoError:
559 pass
562 pass
560
563
561 if default:
564 if default:
562 try:
565 try:
563 p = self.paths.getpath(default)
566 p = self.paths.getpath(default)
564 if p:
567 if p:
565 return p.rawloc
568 return p.rawloc
566 except error.RepoError:
569 except error.RepoError:
567 pass
570 pass
568
571
569 return loc
572 return loc
570
573
571 @util.propertycache
574 @util.propertycache
572 def paths(self):
575 def paths(self):
573 return paths(self)
576 return paths(self)
574
577
575 def pushbuffer(self, error=False, subproc=False):
578 def pushbuffer(self, error=False, subproc=False, labeled=False):
576 """install a buffer to capture standard output of the ui object
579 """install a buffer to capture standard output of the ui object
577
580
578 If error is True, the error output will be captured too.
581 If error is True, the error output will be captured too.
579
582
580 If subproc is True, output from subprocesses (typically hooks) will be
583 If subproc is True, output from subprocesses (typically hooks) will be
581 captured too."""
584 captured too.
585
586 If labeled is True, any labels associated with buffered
587 output will be handled. By default, this has no effect
588 on the output returned, but extensions and GUI tools may
589 handle this argument and returned styled output. If output
590 is being buffered so it can be captured and parsed or
591 processed, labeled should not be set to True.
592 """
582 self._buffers.append([])
593 self._buffers.append([])
583 self._bufferstates.append((error, subproc))
594 self._bufferstates.append((error, subproc, labeled))
595 self._bufferapplylabels = labeled
584
596
585 def popbuffer(self, labeled=False):
597 def popbuffer(self, labeled=False):
586 '''pop the last buffer and return the buffered output
598 '''pop the last buffer and return the buffered output
587
599
588 If labeled is True, any labels associated with buffered
600 If labeled is True, any labels associated with buffered
589 output will be handled. By default, this has no effect
601 output will be handled. By default, this has no effect
590 on the output returned, but extensions and GUI tools may
602 on the output returned, but extensions and GUI tools may
591 handle this argument and returned styled output. If output
603 handle this argument and returned styled output. If output
592 is being buffered so it can be captured and parsed or
604 is being buffered so it can be captured and parsed or
593 processed, labeled should not be set to True.
605 processed, labeled should not be set to True.
594 '''
606 '''
595 self._bufferstates.pop()
607 self._bufferstates.pop()
608 if self._bufferstates:
609 self._bufferapplylabels = self._bufferstates[-1][2]
610 else:
611 self._bufferapplylabels = None
612
596 return "".join(self._buffers.pop())
613 return "".join(self._buffers.pop())
597
614
598 def write(self, *args, **opts):
615 def write(self, *args, **opts):
599 '''write args to output
616 '''write args to output
600
617
601 By default, this method simply writes to the buffer or stdout,
618 By default, this method simply writes to the buffer or stdout,
602 but extensions or GUI tools may override this method,
619 but extensions or GUI tools may override this method,
603 write_err(), popbuffer(), and label() to style output from
620 write_err(), popbuffer(), and label() to style output from
604 various parts of hg.
621 various parts of hg.
605
622
606 An optional keyword argument, "label", can be passed in.
623 An optional keyword argument, "label", can be passed in.
607 This should be a string containing label names separated by
624 This should be a string containing label names separated by
608 space. Label names take the form of "topic.type". For example,
625 space. Label names take the form of "topic.type". For example,
609 ui.debug() issues a label of "ui.debug".
626 ui.debug() issues a label of "ui.debug".
610
627
611 When labeling output for a specific command, a label of
628 When labeling output for a specific command, a label of
612 "cmdname.type" is recommended. For example, status issues
629 "cmdname.type" is recommended. For example, status issues
613 a label of "status.modified" for modified files.
630 a label of "status.modified" for modified files.
614 '''
631 '''
615 if self._buffers:
632 if self._buffers:
616 self._buffers[-1].extend([str(a) for a in args])
633 self._buffers[-1].extend([str(a) for a in args])
617 else:
634 else:
618 self._progclear()
635 self._progclear()
619 for a in args:
636 for a in args:
620 self.fout.write(str(a))
637 self.fout.write(str(a))
621
638
622 def write_err(self, *args, **opts):
639 def write_err(self, *args, **opts):
623 self._progclear()
640 self._progclear()
624 try:
641 try:
625 if self._bufferstates and self._bufferstates[-1][0]:
642 if self._bufferstates and self._bufferstates[-1][0]:
626 return self.write(*args, **opts)
643 return self.write(*args, **opts)
627 if not getattr(self.fout, 'closed', False):
644 if not getattr(self.fout, 'closed', False):
628 self.fout.flush()
645 self.fout.flush()
629 for a in args:
646 for a in args:
630 self.ferr.write(str(a))
647 self.ferr.write(str(a))
631 # stderr may be buffered under win32 when redirected to files,
648 # stderr may be buffered under win32 when redirected to files,
632 # including stdout.
649 # including stdout.
633 if not getattr(self.ferr, 'closed', False):
650 if not getattr(self.ferr, 'closed', False):
634 self.ferr.flush()
651 self.ferr.flush()
635 except IOError as inst:
652 except IOError as inst:
636 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
653 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
637 raise
654 raise
638
655
639 def flush(self):
656 def flush(self):
640 try: self.fout.flush()
657 try: self.fout.flush()
641 except (IOError, ValueError): pass
658 except (IOError, ValueError): pass
642 try: self.ferr.flush()
659 try: self.ferr.flush()
643 except (IOError, ValueError): pass
660 except (IOError, ValueError): pass
644
661
645 def _isatty(self, fh):
662 def _isatty(self, fh):
646 if self.configbool('ui', 'nontty', False):
663 if self.configbool('ui', 'nontty', False):
647 return False
664 return False
648 return util.isatty(fh)
665 return util.isatty(fh)
649
666
650 def interactive(self):
667 def interactive(self):
651 '''is interactive input allowed?
668 '''is interactive input allowed?
652
669
653 An interactive session is a session where input can be reasonably read
670 An interactive session is a session where input can be reasonably read
654 from `sys.stdin'. If this function returns false, any attempt to read
671 from `sys.stdin'. If this function returns false, any attempt to read
655 from stdin should fail with an error, unless a sensible default has been
672 from stdin should fail with an error, unless a sensible default has been
656 specified.
673 specified.
657
674
658 Interactiveness is triggered by the value of the `ui.interactive'
675 Interactiveness is triggered by the value of the `ui.interactive'
659 configuration variable or - if it is unset - when `sys.stdin' points
676 configuration variable or - if it is unset - when `sys.stdin' points
660 to a terminal device.
677 to a terminal device.
661
678
662 This function refers to input only; for output, see `ui.formatted()'.
679 This function refers to input only; for output, see `ui.formatted()'.
663 '''
680 '''
664 i = self.configbool("ui", "interactive", None)
681 i = self.configbool("ui", "interactive", None)
665 if i is None:
682 if i is None:
666 # some environments replace stdin without implementing isatty
683 # some environments replace stdin without implementing isatty
667 # usually those are non-interactive
684 # usually those are non-interactive
668 return self._isatty(self.fin)
685 return self._isatty(self.fin)
669
686
670 return i
687 return i
671
688
672 def termwidth(self):
689 def termwidth(self):
673 '''how wide is the terminal in columns?
690 '''how wide is the terminal in columns?
674 '''
691 '''
675 if 'COLUMNS' in os.environ:
692 if 'COLUMNS' in os.environ:
676 try:
693 try:
677 return int(os.environ['COLUMNS'])
694 return int(os.environ['COLUMNS'])
678 except ValueError:
695 except ValueError:
679 pass
696 pass
680 return util.termwidth()
697 return util.termwidth()
681
698
682 def formatted(self):
699 def formatted(self):
683 '''should formatted output be used?
700 '''should formatted output be used?
684
701
685 It is often desirable to format the output to suite the output medium.
702 It is often desirable to format the output to suite the output medium.
686 Examples of this are truncating long lines or colorizing messages.
703 Examples of this are truncating long lines or colorizing messages.
687 However, this is not often not desirable when piping output into other
704 However, this is not often not desirable when piping output into other
688 utilities, e.g. `grep'.
705 utilities, e.g. `grep'.
689
706
690 Formatted output is triggered by the value of the `ui.formatted'
707 Formatted output is triggered by the value of the `ui.formatted'
691 configuration variable or - if it is unset - when `sys.stdout' points
708 configuration variable or - if it is unset - when `sys.stdout' points
692 to a terminal device. Please note that `ui.formatted' should be
709 to a terminal device. Please note that `ui.formatted' should be
693 considered an implementation detail; it is not intended for use outside
710 considered an implementation detail; it is not intended for use outside
694 Mercurial or its extensions.
711 Mercurial or its extensions.
695
712
696 This function refers to output only; for input, see `ui.interactive()'.
713 This function refers to output only; for input, see `ui.interactive()'.
697 This function always returns false when in plain mode, see `ui.plain()'.
714 This function always returns false when in plain mode, see `ui.plain()'.
698 '''
715 '''
699 if self.plain():
716 if self.plain():
700 return False
717 return False
701
718
702 i = self.configbool("ui", "formatted", None)
719 i = self.configbool("ui", "formatted", None)
703 if i is None:
720 if i is None:
704 # some environments replace stdout without implementing isatty
721 # some environments replace stdout without implementing isatty
705 # usually those are non-interactive
722 # usually those are non-interactive
706 return self._isatty(self.fout)
723 return self._isatty(self.fout)
707
724
708 return i
725 return i
709
726
710 def _readline(self, prompt=''):
727 def _readline(self, prompt=''):
711 if self._isatty(self.fin):
728 if self._isatty(self.fin):
712 try:
729 try:
713 # magically add command line editing support, where
730 # magically add command line editing support, where
714 # available
731 # available
715 import readline
732 import readline
716 # force demandimport to really load the module
733 # force demandimport to really load the module
717 readline.read_history_file
734 readline.read_history_file
718 # windows sometimes raises something other than ImportError
735 # windows sometimes raises something other than ImportError
719 except Exception:
736 except Exception:
720 pass
737 pass
721
738
722 # call write() so output goes through subclassed implementation
739 # call write() so output goes through subclassed implementation
723 # e.g. color extension on Windows
740 # e.g. color extension on Windows
724 self.write(prompt)
741 self.write(prompt)
725
742
726 # instead of trying to emulate raw_input, swap (self.fin,
743 # instead of trying to emulate raw_input, swap (self.fin,
727 # self.fout) with (sys.stdin, sys.stdout)
744 # self.fout) with (sys.stdin, sys.stdout)
728 oldin = sys.stdin
745 oldin = sys.stdin
729 oldout = sys.stdout
746 oldout = sys.stdout
730 sys.stdin = self.fin
747 sys.stdin = self.fin
731 sys.stdout = self.fout
748 sys.stdout = self.fout
732 # prompt ' ' must exist; otherwise readline may delete entire line
749 # prompt ' ' must exist; otherwise readline may delete entire line
733 # - http://bugs.python.org/issue12833
750 # - http://bugs.python.org/issue12833
734 line = raw_input(' ')
751 line = raw_input(' ')
735 sys.stdin = oldin
752 sys.stdin = oldin
736 sys.stdout = oldout
753 sys.stdout = oldout
737
754
738 # When stdin is in binary mode on Windows, it can cause
755 # When stdin is in binary mode on Windows, it can cause
739 # raw_input() to emit an extra trailing carriage return
756 # raw_input() to emit an extra trailing carriage return
740 if os.linesep == '\r\n' and line and line[-1] == '\r':
757 if os.linesep == '\r\n' and line and line[-1] == '\r':
741 line = line[:-1]
758 line = line[:-1]
742 return line
759 return line
743
760
744 def prompt(self, msg, default="y"):
761 def prompt(self, msg, default="y"):
745 """Prompt user with msg, read response.
762 """Prompt user with msg, read response.
746 If ui is not interactive, the default is returned.
763 If ui is not interactive, the default is returned.
747 """
764 """
748 if not self.interactive():
765 if not self.interactive():
749 self.write(msg, ' ', default, "\n")
766 self.write(msg, ' ', default, "\n")
750 return default
767 return default
751 try:
768 try:
752 r = self._readline(self.label(msg, 'ui.prompt'))
769 r = self._readline(self.label(msg, 'ui.prompt'))
753 if not r:
770 if not r:
754 r = default
771 r = default
755 if self.configbool('ui', 'promptecho'):
772 if self.configbool('ui', 'promptecho'):
756 self.write(r, "\n")
773 self.write(r, "\n")
757 return r
774 return r
758 except EOFError:
775 except EOFError:
759 raise error.ResponseExpected()
776 raise error.ResponseExpected()
760
777
761 @staticmethod
778 @staticmethod
762 def extractchoices(prompt):
779 def extractchoices(prompt):
763 """Extract prompt message and list of choices from specified prompt.
780 """Extract prompt message and list of choices from specified prompt.
764
781
765 This returns tuple "(message, choices)", and "choices" is the
782 This returns tuple "(message, choices)", and "choices" is the
766 list of tuple "(response character, text without &)".
783 list of tuple "(response character, text without &)".
767 """
784 """
768 parts = prompt.split('$$')
785 parts = prompt.split('$$')
769 msg = parts[0].rstrip(' ')
786 msg = parts[0].rstrip(' ')
770 choices = [p.strip(' ') for p in parts[1:]]
787 choices = [p.strip(' ') for p in parts[1:]]
771 return (msg,
788 return (msg,
772 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
789 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
773 for s in choices])
790 for s in choices])
774
791
775 def promptchoice(self, prompt, default=0):
792 def promptchoice(self, prompt, default=0):
776 """Prompt user with a message, read response, and ensure it matches
793 """Prompt user with a message, read response, and ensure it matches
777 one of the provided choices. The prompt is formatted as follows:
794 one of the provided choices. The prompt is formatted as follows:
778
795
779 "would you like fries with that (Yn)? $$ &Yes $$ &No"
796 "would you like fries with that (Yn)? $$ &Yes $$ &No"
780
797
781 The index of the choice is returned. Responses are case
798 The index of the choice is returned. Responses are case
782 insensitive. If ui is not interactive, the default is
799 insensitive. If ui is not interactive, the default is
783 returned.
800 returned.
784 """
801 """
785
802
786 msg, choices = self.extractchoices(prompt)
803 msg, choices = self.extractchoices(prompt)
787 resps = [r for r, t in choices]
804 resps = [r for r, t in choices]
788 while True:
805 while True:
789 r = self.prompt(msg, resps[default])
806 r = self.prompt(msg, resps[default])
790 if r.lower() in resps:
807 if r.lower() in resps:
791 return resps.index(r.lower())
808 return resps.index(r.lower())
792 self.write(_("unrecognized response\n"))
809 self.write(_("unrecognized response\n"))
793
810
794 def getpass(self, prompt=None, default=None):
811 def getpass(self, prompt=None, default=None):
795 if not self.interactive():
812 if not self.interactive():
796 return default
813 return default
797 try:
814 try:
798 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
815 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
799 # disable getpass() only if explicitly specified. it's still valid
816 # disable getpass() only if explicitly specified. it's still valid
800 # to interact with tty even if fin is not a tty.
817 # to interact with tty even if fin is not a tty.
801 if self.configbool('ui', 'nontty'):
818 if self.configbool('ui', 'nontty'):
802 return self.fin.readline().rstrip('\n')
819 return self.fin.readline().rstrip('\n')
803 else:
820 else:
804 return getpass.getpass('')
821 return getpass.getpass('')
805 except EOFError:
822 except EOFError:
806 raise error.ResponseExpected()
823 raise error.ResponseExpected()
807 def status(self, *msg, **opts):
824 def status(self, *msg, **opts):
808 '''write status message to output (if ui.quiet is False)
825 '''write status message to output (if ui.quiet is False)
809
826
810 This adds an output label of "ui.status".
827 This adds an output label of "ui.status".
811 '''
828 '''
812 if not self.quiet:
829 if not self.quiet:
813 opts['label'] = opts.get('label', '') + ' ui.status'
830 opts['label'] = opts.get('label', '') + ' ui.status'
814 self.write(*msg, **opts)
831 self.write(*msg, **opts)
815 def warn(self, *msg, **opts):
832 def warn(self, *msg, **opts):
816 '''write warning message to output (stderr)
833 '''write warning message to output (stderr)
817
834
818 This adds an output label of "ui.warning".
835 This adds an output label of "ui.warning".
819 '''
836 '''
820 opts['label'] = opts.get('label', '') + ' ui.warning'
837 opts['label'] = opts.get('label', '') + ' ui.warning'
821 self.write_err(*msg, **opts)
838 self.write_err(*msg, **opts)
822 def note(self, *msg, **opts):
839 def note(self, *msg, **opts):
823 '''write note to output (if ui.verbose is True)
840 '''write note to output (if ui.verbose is True)
824
841
825 This adds an output label of "ui.note".
842 This adds an output label of "ui.note".
826 '''
843 '''
827 if self.verbose:
844 if self.verbose:
828 opts['label'] = opts.get('label', '') + ' ui.note'
845 opts['label'] = opts.get('label', '') + ' ui.note'
829 self.write(*msg, **opts)
846 self.write(*msg, **opts)
830 def debug(self, *msg, **opts):
847 def debug(self, *msg, **opts):
831 '''write debug message to output (if ui.debugflag is True)
848 '''write debug message to output (if ui.debugflag is True)
832
849
833 This adds an output label of "ui.debug".
850 This adds an output label of "ui.debug".
834 '''
851 '''
835 if self.debugflag:
852 if self.debugflag:
836 opts['label'] = opts.get('label', '') + ' ui.debug'
853 opts['label'] = opts.get('label', '') + ' ui.debug'
837 self.write(*msg, **opts)
854 self.write(*msg, **opts)
838
855
839 def edit(self, text, user, extra=None, editform=None, pending=None):
856 def edit(self, text, user, extra=None, editform=None, pending=None):
840 if extra is None:
857 if extra is None:
841 extra = {}
858 extra = {}
842 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
859 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
843 text=True)
860 text=True)
844 try:
861 try:
845 f = os.fdopen(fd, "w")
862 f = os.fdopen(fd, "w")
846 f.write(text)
863 f.write(text)
847 f.close()
864 f.close()
848
865
849 environ = {'HGUSER': user}
866 environ = {'HGUSER': user}
850 if 'transplant_source' in extra:
867 if 'transplant_source' in extra:
851 environ.update({'HGREVISION': hex(extra['transplant_source'])})
868 environ.update({'HGREVISION': hex(extra['transplant_source'])})
852 for label in ('intermediate-source', 'source', 'rebase_source'):
869 for label in ('intermediate-source', 'source', 'rebase_source'):
853 if label in extra:
870 if label in extra:
854 environ.update({'HGREVISION': extra[label]})
871 environ.update({'HGREVISION': extra[label]})
855 break
872 break
856 if editform:
873 if editform:
857 environ.update({'HGEDITFORM': editform})
874 environ.update({'HGEDITFORM': editform})
858 if pending:
875 if pending:
859 environ.update({'HG_PENDING': pending})
876 environ.update({'HG_PENDING': pending})
860
877
861 editor = self.geteditor()
878 editor = self.geteditor()
862
879
863 self.system("%s \"%s\"" % (editor, name),
880 self.system("%s \"%s\"" % (editor, name),
864 environ=environ,
881 environ=environ,
865 onerr=error.Abort, errprefix=_("edit failed"))
882 onerr=error.Abort, errprefix=_("edit failed"))
866
883
867 f = open(name)
884 f = open(name)
868 t = f.read()
885 t = f.read()
869 f.close()
886 f.close()
870 finally:
887 finally:
871 os.unlink(name)
888 os.unlink(name)
872
889
873 return t
890 return t
874
891
875 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None):
892 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None):
876 '''execute shell command with appropriate output stream. command
893 '''execute shell command with appropriate output stream. command
877 output will be redirected if fout is not stdout.
894 output will be redirected if fout is not stdout.
878 '''
895 '''
879 out = self.fout
896 out = self.fout
880 if any(s[1] for s in self._bufferstates):
897 if any(s[1] for s in self._bufferstates):
881 out = self
898 out = self
882 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
899 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
883 errprefix=errprefix, out=out)
900 errprefix=errprefix, out=out)
884
901
885 def traceback(self, exc=None, force=False):
902 def traceback(self, exc=None, force=False):
886 '''print exception traceback if traceback printing enabled or forced.
903 '''print exception traceback if traceback printing enabled or forced.
887 only to call in exception handler. returns true if traceback
904 only to call in exception handler. returns true if traceback
888 printed.'''
905 printed.'''
889 if self.tracebackflag or force:
906 if self.tracebackflag or force:
890 if exc is None:
907 if exc is None:
891 exc = sys.exc_info()
908 exc = sys.exc_info()
892 cause = getattr(exc[1], 'cause', None)
909 cause = getattr(exc[1], 'cause', None)
893
910
894 if cause is not None:
911 if cause is not None:
895 causetb = traceback.format_tb(cause[2])
912 causetb = traceback.format_tb(cause[2])
896 exctb = traceback.format_tb(exc[2])
913 exctb = traceback.format_tb(exc[2])
897 exconly = traceback.format_exception_only(cause[0], cause[1])
914 exconly = traceback.format_exception_only(cause[0], cause[1])
898
915
899 # exclude frame where 'exc' was chained and rethrown from exctb
916 # exclude frame where 'exc' was chained and rethrown from exctb
900 self.write_err('Traceback (most recent call last):\n',
917 self.write_err('Traceback (most recent call last):\n',
901 ''.join(exctb[:-1]),
918 ''.join(exctb[:-1]),
902 ''.join(causetb),
919 ''.join(causetb),
903 ''.join(exconly))
920 ''.join(exconly))
904 else:
921 else:
905 output = traceback.format_exception(exc[0], exc[1], exc[2])
922 output = traceback.format_exception(exc[0], exc[1], exc[2])
906 self.write_err(''.join(output))
923 self.write_err(''.join(output))
907 return self.tracebackflag or force
924 return self.tracebackflag or force
908
925
909 def geteditor(self):
926 def geteditor(self):
910 '''return editor to use'''
927 '''return editor to use'''
911 if sys.platform == 'plan9':
928 if sys.platform == 'plan9':
912 # vi is the MIPS instruction simulator on Plan 9. We
929 # vi is the MIPS instruction simulator on Plan 9. We
913 # instead default to E to plumb commit messages to
930 # instead default to E to plumb commit messages to
914 # avoid confusion.
931 # avoid confusion.
915 editor = 'E'
932 editor = 'E'
916 else:
933 else:
917 editor = 'vi'
934 editor = 'vi'
918 return (os.environ.get("HGEDITOR") or
935 return (os.environ.get("HGEDITOR") or
919 self.config("ui", "editor") or
936 self.config("ui", "editor") or
920 os.environ.get("VISUAL") or
937 os.environ.get("VISUAL") or
921 os.environ.get("EDITOR", editor))
938 os.environ.get("EDITOR", editor))
922
939
923 @util.propertycache
940 @util.propertycache
924 def _progbar(self):
941 def _progbar(self):
925 """setup the progbar singleton to the ui object"""
942 """setup the progbar singleton to the ui object"""
926 if (self.quiet or self.debugflag
943 if (self.quiet or self.debugflag
927 or self.configbool('progress', 'disable', False)
944 or self.configbool('progress', 'disable', False)
928 or not progress.shouldprint(self)):
945 or not progress.shouldprint(self)):
929 return None
946 return None
930 return getprogbar(self)
947 return getprogbar(self)
931
948
932 def _progclear(self):
949 def _progclear(self):
933 """clear progress bar output if any. use it before any output"""
950 """clear progress bar output if any. use it before any output"""
934 if '_progbar' not in vars(self): # nothing loaded yet
951 if '_progbar' not in vars(self): # nothing loaded yet
935 return
952 return
936 if self._progbar is not None and self._progbar.printed:
953 if self._progbar is not None and self._progbar.printed:
937 self._progbar.clear()
954 self._progbar.clear()
938
955
939 def progress(self, topic, pos, item="", unit="", total=None):
956 def progress(self, topic, pos, item="", unit="", total=None):
940 '''show a progress message
957 '''show a progress message
941
958
942 With stock hg, this is simply a debug message that is hidden
959 With stock hg, this is simply a debug message that is hidden
943 by default, but with extensions or GUI tools it may be
960 by default, but with extensions or GUI tools it may be
944 visible. 'topic' is the current operation, 'item' is a
961 visible. 'topic' is the current operation, 'item' is a
945 non-numeric marker of the current position (i.e. the currently
962 non-numeric marker of the current position (i.e. the currently
946 in-process file), 'pos' is the current numeric position (i.e.
963 in-process file), 'pos' is the current numeric position (i.e.
947 revision, bytes, etc.), unit is a corresponding unit label,
964 revision, bytes, etc.), unit is a corresponding unit label,
948 and total is the highest expected pos.
965 and total is the highest expected pos.
949
966
950 Multiple nested topics may be active at a time.
967 Multiple nested topics may be active at a time.
951
968
952 All topics should be marked closed by setting pos to None at
969 All topics should be marked closed by setting pos to None at
953 termination.
970 termination.
954 '''
971 '''
955 if self._progbar is not None:
972 if self._progbar is not None:
956 self._progbar.progress(topic, pos, item=item, unit=unit,
973 self._progbar.progress(topic, pos, item=item, unit=unit,
957 total=total)
974 total=total)
958 if pos is None or not self.configbool('progress', 'debug'):
975 if pos is None or not self.configbool('progress', 'debug'):
959 return
976 return
960
977
961 if unit:
978 if unit:
962 unit = ' ' + unit
979 unit = ' ' + unit
963 if item:
980 if item:
964 item = ' ' + item
981 item = ' ' + item
965
982
966 if total:
983 if total:
967 pct = 100.0 * pos / total
984 pct = 100.0 * pos / total
968 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
985 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
969 % (topic, item, pos, total, unit, pct))
986 % (topic, item, pos, total, unit, pct))
970 else:
987 else:
971 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
988 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
972
989
973 def log(self, service, *msg, **opts):
990 def log(self, service, *msg, **opts):
974 '''hook for logging facility extensions
991 '''hook for logging facility extensions
975
992
976 service should be a readily-identifiable subsystem, which will
993 service should be a readily-identifiable subsystem, which will
977 allow filtering.
994 allow filtering.
978
995
979 *msg should be a newline-terminated format string to log, and
996 *msg should be a newline-terminated format string to log, and
980 then any values to %-format into that format string.
997 then any values to %-format into that format string.
981
998
982 **opts currently has no defined meanings.
999 **opts currently has no defined meanings.
983 '''
1000 '''
984
1001
985 def label(self, msg, label):
1002 def label(self, msg, label):
986 '''style msg based on supplied label
1003 '''style msg based on supplied label
987
1004
988 Like ui.write(), this just returns msg unchanged, but extensions
1005 Like ui.write(), this just returns msg unchanged, but extensions
989 and GUI tools can override it to allow styling output without
1006 and GUI tools can override it to allow styling output without
990 writing it.
1007 writing it.
991
1008
992 ui.write(s, 'label') is equivalent to
1009 ui.write(s, 'label') is equivalent to
993 ui.write(ui.label(s, 'label')).
1010 ui.write(ui.label(s, 'label')).
994 '''
1011 '''
995 return msg
1012 return msg
996
1013
997 def develwarn(self, msg):
1014 def develwarn(self, msg):
998 """issue a developer warning message"""
1015 """issue a developer warning message"""
999 msg = 'devel-warn: ' + msg
1016 msg = 'devel-warn: ' + msg
1000 if self.tracebackflag:
1017 if self.tracebackflag:
1001 util.debugstacktrace(msg, 2, self.ferr, self.fout)
1018 util.debugstacktrace(msg, 2, self.ferr, self.fout)
1002 else:
1019 else:
1003 curframe = inspect.currentframe()
1020 curframe = inspect.currentframe()
1004 calframe = inspect.getouterframes(curframe, 2)
1021 calframe = inspect.getouterframes(curframe, 2)
1005 self.write_err('%s at: %s:%s (%s)\n' % ((msg,) + calframe[2][1:4]))
1022 self.write_err('%s at: %s:%s (%s)\n' % ((msg,) + calframe[2][1:4]))
1006
1023
1007 class paths(dict):
1024 class paths(dict):
1008 """Represents a collection of paths and their configs.
1025 """Represents a collection of paths and their configs.
1009
1026
1010 Data is initially derived from ui instances and the config files they have
1027 Data is initially derived from ui instances and the config files they have
1011 loaded.
1028 loaded.
1012 """
1029 """
1013 def __init__(self, ui):
1030 def __init__(self, ui):
1014 dict.__init__(self)
1031 dict.__init__(self)
1015
1032
1016 for name, loc in ui.configitems('paths'):
1033 for name, loc in ui.configitems('paths'):
1017 # No location is the same as not existing.
1034 # No location is the same as not existing.
1018 if not loc:
1035 if not loc:
1019 continue
1036 continue
1020
1037
1021 # TODO ignore default-push once all consumers stop referencing it
1038 # TODO ignore default-push once all consumers stop referencing it
1022 # since it is handled specifically below.
1039 # since it is handled specifically below.
1023
1040
1024 self[name] = path(name, rawloc=loc)
1041 self[name] = path(name, rawloc=loc)
1025
1042
1026 # Handle default-push, which is a one-off that defines the push URL for
1043 # Handle default-push, which is a one-off that defines the push URL for
1027 # the "default" path.
1044 # the "default" path.
1028 defaultpush = ui.config('paths', 'default-push')
1045 defaultpush = ui.config('paths', 'default-push')
1029 if defaultpush:
1046 if defaultpush:
1030 # "default-push" can be defined without "default" entry. This is a
1047 # "default-push" can be defined without "default" entry. This is a
1031 # bit weird, but is allowed for backwards compatibility.
1048 # bit weird, but is allowed for backwards compatibility.
1032 if 'default' not in self:
1049 if 'default' not in self:
1033 self['default'] = path('default', rawloc=defaultpush)
1050 self['default'] = path('default', rawloc=defaultpush)
1034 self['default']._pushloc = defaultpush
1051 self['default']._pushloc = defaultpush
1035
1052
1036 def getpath(self, name, default=None):
1053 def getpath(self, name, default=None):
1037 """Return a ``path`` from a string, falling back to a default.
1054 """Return a ``path`` from a string, falling back to a default.
1038
1055
1039 ``name`` can be a named path or locations. Locations are filesystem
1056 ``name`` can be a named path or locations. Locations are filesystem
1040 paths or URIs.
1057 paths or URIs.
1041
1058
1042 Returns None if ``name`` is not a registered path, a URI, or a local
1059 Returns None if ``name`` is not a registered path, a URI, or a local
1043 path to a repo.
1060 path to a repo.
1044 """
1061 """
1045 # Only fall back to default if no path was requested.
1062 # Only fall back to default if no path was requested.
1046 if name is None:
1063 if name is None:
1047 if default:
1064 if default:
1048 try:
1065 try:
1049 return self[default]
1066 return self[default]
1050 except KeyError:
1067 except KeyError:
1051 return None
1068 return None
1052 else:
1069 else:
1053 return None
1070 return None
1054
1071
1055 # Most likely empty string.
1072 # Most likely empty string.
1056 # This may need to raise in the future.
1073 # This may need to raise in the future.
1057 if not name:
1074 if not name:
1058 return None
1075 return None
1059
1076
1060 try:
1077 try:
1061 return self[name]
1078 return self[name]
1062 except KeyError:
1079 except KeyError:
1063 # Try to resolve as a local path or URI.
1080 # Try to resolve as a local path or URI.
1064 try:
1081 try:
1065 return path(None, rawloc=name)
1082 return path(None, rawloc=name)
1066 except ValueError:
1083 except ValueError:
1067 raise error.RepoError(_('repository %s does not exist') %
1084 raise error.RepoError(_('repository %s does not exist') %
1068 name)
1085 name)
1069
1086
1070 assert False
1087 assert False
1071
1088
1072 class path(object):
1089 class path(object):
1073 """Represents an individual path and its configuration."""
1090 """Represents an individual path and its configuration."""
1074
1091
1075 def __init__(self, name, rawloc=None, pushloc=None):
1092 def __init__(self, name, rawloc=None, pushloc=None):
1076 """Construct a path from its config options.
1093 """Construct a path from its config options.
1077
1094
1078 ``name`` is the symbolic name of the path.
1095 ``name`` is the symbolic name of the path.
1079 ``rawloc`` is the raw location, as defined in the config.
1096 ``rawloc`` is the raw location, as defined in the config.
1080 ``pushloc`` is the raw locations pushes should be made to.
1097 ``pushloc`` is the raw locations pushes should be made to.
1081
1098
1082 If ``name`` is not defined, we require that the location be a) a local
1099 If ``name`` is not defined, we require that the location be a) a local
1083 filesystem path with a .hg directory or b) a URL. If not,
1100 filesystem path with a .hg directory or b) a URL. If not,
1084 ``ValueError`` is raised.
1101 ``ValueError`` is raised.
1085 """
1102 """
1086 if not rawloc:
1103 if not rawloc:
1087 raise ValueError('rawloc must be defined')
1104 raise ValueError('rawloc must be defined')
1088
1105
1089 # Locations may define branches via syntax <base>#<branch>.
1106 # Locations may define branches via syntax <base>#<branch>.
1090 u = util.url(rawloc)
1107 u = util.url(rawloc)
1091 branch = None
1108 branch = None
1092 if u.fragment:
1109 if u.fragment:
1093 branch = u.fragment
1110 branch = u.fragment
1094 u.fragment = None
1111 u.fragment = None
1095
1112
1096 self.url = u
1113 self.url = u
1097 self.branch = branch
1114 self.branch = branch
1098
1115
1099 self.name = name
1116 self.name = name
1100 self.rawloc = rawloc
1117 self.rawloc = rawloc
1101 self.loc = str(u)
1118 self.loc = str(u)
1102 self._pushloc = pushloc
1119 self._pushloc = pushloc
1103
1120
1104 # When given a raw location but not a symbolic name, validate the
1121 # When given a raw location but not a symbolic name, validate the
1105 # location is valid.
1122 # location is valid.
1106 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1123 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1107 raise ValueError('location is not a URL or path to a local '
1124 raise ValueError('location is not a URL or path to a local '
1108 'repo: %s' % rawloc)
1125 'repo: %s' % rawloc)
1109
1126
1110 def _isvalidlocalpath(self, path):
1127 def _isvalidlocalpath(self, path):
1111 """Returns True if the given path is a potentially valid repository.
1128 """Returns True if the given path is a potentially valid repository.
1112 This is its own function so that extensions can change the definition of
1129 This is its own function so that extensions can change the definition of
1113 'valid' in this case (like when pulling from a git repo into a hg
1130 'valid' in this case (like when pulling from a git repo into a hg
1114 one)."""
1131 one)."""
1115 return os.path.isdir(os.path.join(path, '.hg'))
1132 return os.path.isdir(os.path.join(path, '.hg'))
1116
1133
1117 @property
1134 @property
1118 def pushloc(self):
1135 def pushloc(self):
1119 return self._pushloc or self.loc
1136 return self._pushloc or self.loc
1120
1137
1121 # we instantiate one globally shared progress bar to avoid
1138 # we instantiate one globally shared progress bar to avoid
1122 # competing progress bars when multiple UI objects get created
1139 # competing progress bars when multiple UI objects get created
1123 _progresssingleton = None
1140 _progresssingleton = None
1124
1141
1125 def getprogbar(ui):
1142 def getprogbar(ui):
1126 global _progresssingleton
1143 global _progresssingleton
1127 if _progresssingleton is None:
1144 if _progresssingleton is None:
1128 # passing 'ui' object to the singleton is fishy,
1145 # passing 'ui' object to the singleton is fishy,
1129 # this is how the extension used to work but feel free to rework it.
1146 # this is how the extension used to work but feel free to rework it.
1130 _progresssingleton = progress.progbar(ui)
1147 _progresssingleton = progress.progbar(ui)
1131 return _progresssingleton
1148 return _progresssingleton
General Comments 0
You need to be logged in to leave comments. Login now