##// END OF EJS Templates
ui: fix comment about non-interactive prompts...
Matt Mackall -
r22675:4e64d255 default
parent child Browse files
Show More
@@ -1,892 +1,892 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 i18n import _
8 from i18n import _
9 import errno, getpass, os, socket, sys, tempfile, traceback
9 import errno, getpass, os, socket, sys, tempfile, traceback
10 import config, scmutil, util, error, formatter
10 import config, scmutil, util, error, formatter
11 from node import hex
11 from node import hex
12
12
13 samplehgrcs = {
13 samplehgrcs = {
14 'user':
14 'user':
15 """# example user config (see "hg help config" for more info)
15 """# example user config (see "hg help config" for more info)
16 [ui]
16 [ui]
17 # name and email, e.g.
17 # name and email, e.g.
18 # username = Jane Doe <jdoe@example.com>
18 # username = Jane Doe <jdoe@example.com>
19 username =
19 username =
20
20
21 [extensions]
21 [extensions]
22 # uncomment these lines to enable some popular extensions
22 # uncomment these lines to enable some popular extensions
23 # (see "hg help extensions" for more info)
23 # (see "hg help extensions" for more info)
24 #
24 #
25 # pager =
25 # pager =
26 # progress =
26 # progress =
27 # color =""",
27 # color =""",
28
28
29 'local':
29 'local':
30 """# example repository config (see "hg help config" for more info)
30 """# example repository config (see "hg help config" for more info)
31 """,
31 """,
32
32
33 'global':
33 'global':
34 """# example system-wide hg config (see "hg help config" for more info)
34 """# example system-wide hg config (see "hg help config" for more info)
35
35
36 [extensions]
36 [extensions]
37 # uncomment these lines to enable some popular extensions
37 # uncomment these lines to enable some popular extensions
38 # (see "hg help extensions" for more info)
38 # (see "hg help extensions" for more info)
39 #
39 #
40 # blackbox =
40 # blackbox =
41 # progress =
41 # progress =
42 # color =
42 # color =
43 # pager =""",
43 # pager =""",
44 }
44 }
45
45
46 class ui(object):
46 class ui(object):
47 def __init__(self, src=None):
47 def __init__(self, src=None):
48 # _buffers: used for temporary capture of output
48 # _buffers: used for temporary capture of output
49 self._buffers = []
49 self._buffers = []
50 # _bufferstates: Should the temporary capture includes stderr
50 # _bufferstates: Should the temporary capture includes stderr
51 self._bufferstates = []
51 self._bufferstates = []
52 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
52 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
53 self._reportuntrusted = True
53 self._reportuntrusted = True
54 self._ocfg = config.config() # overlay
54 self._ocfg = config.config() # overlay
55 self._tcfg = config.config() # trusted
55 self._tcfg = config.config() # trusted
56 self._ucfg = config.config() # untrusted
56 self._ucfg = config.config() # untrusted
57 self._trustusers = set()
57 self._trustusers = set()
58 self._trustgroups = set()
58 self._trustgroups = set()
59 self.callhooks = True
59 self.callhooks = True
60
60
61 if src:
61 if src:
62 self.fout = src.fout
62 self.fout = src.fout
63 self.ferr = src.ferr
63 self.ferr = src.ferr
64 self.fin = src.fin
64 self.fin = src.fin
65
65
66 self._tcfg = src._tcfg.copy()
66 self._tcfg = src._tcfg.copy()
67 self._ucfg = src._ucfg.copy()
67 self._ucfg = src._ucfg.copy()
68 self._ocfg = src._ocfg.copy()
68 self._ocfg = src._ocfg.copy()
69 self._trustusers = src._trustusers.copy()
69 self._trustusers = src._trustusers.copy()
70 self._trustgroups = src._trustgroups.copy()
70 self._trustgroups = src._trustgroups.copy()
71 self.environ = src.environ
71 self.environ = src.environ
72 self.callhooks = src.callhooks
72 self.callhooks = src.callhooks
73 self.fixconfig()
73 self.fixconfig()
74 else:
74 else:
75 self.fout = sys.stdout
75 self.fout = sys.stdout
76 self.ferr = sys.stderr
76 self.ferr = sys.stderr
77 self.fin = sys.stdin
77 self.fin = sys.stdin
78
78
79 # shared read-only environment
79 # shared read-only environment
80 self.environ = os.environ
80 self.environ = os.environ
81 # we always trust global config files
81 # we always trust global config files
82 for f in scmutil.rcpath():
82 for f in scmutil.rcpath():
83 self.readconfig(f, trust=True)
83 self.readconfig(f, trust=True)
84
84
85 def copy(self):
85 def copy(self):
86 return self.__class__(self)
86 return self.__class__(self)
87
87
88 def formatter(self, topic, opts):
88 def formatter(self, topic, opts):
89 return formatter.formatter(self, topic, opts)
89 return formatter.formatter(self, topic, opts)
90
90
91 def _trusted(self, fp, f):
91 def _trusted(self, fp, f):
92 st = util.fstat(fp)
92 st = util.fstat(fp)
93 if util.isowner(st):
93 if util.isowner(st):
94 return True
94 return True
95
95
96 tusers, tgroups = self._trustusers, self._trustgroups
96 tusers, tgroups = self._trustusers, self._trustgroups
97 if '*' in tusers or '*' in tgroups:
97 if '*' in tusers or '*' in tgroups:
98 return True
98 return True
99
99
100 user = util.username(st.st_uid)
100 user = util.username(st.st_uid)
101 group = util.groupname(st.st_gid)
101 group = util.groupname(st.st_gid)
102 if user in tusers or group in tgroups or user == util.username():
102 if user in tusers or group in tgroups or user == util.username():
103 return True
103 return True
104
104
105 if self._reportuntrusted:
105 if self._reportuntrusted:
106 self.warn(_('not trusting file %s from untrusted '
106 self.warn(_('not trusting file %s from untrusted '
107 'user %s, group %s\n') % (f, user, group))
107 'user %s, group %s\n') % (f, user, group))
108 return False
108 return False
109
109
110 def readconfig(self, filename, root=None, trust=False,
110 def readconfig(self, filename, root=None, trust=False,
111 sections=None, remap=None):
111 sections=None, remap=None):
112 try:
112 try:
113 fp = open(filename)
113 fp = open(filename)
114 except IOError:
114 except IOError:
115 if not sections: # ignore unless we were looking for something
115 if not sections: # ignore unless we were looking for something
116 return
116 return
117 raise
117 raise
118
118
119 cfg = config.config()
119 cfg = config.config()
120 trusted = sections or trust or self._trusted(fp, filename)
120 trusted = sections or trust or self._trusted(fp, filename)
121
121
122 try:
122 try:
123 cfg.read(filename, fp, sections=sections, remap=remap)
123 cfg.read(filename, fp, sections=sections, remap=remap)
124 fp.close()
124 fp.close()
125 except error.ConfigError, inst:
125 except error.ConfigError, inst:
126 if trusted:
126 if trusted:
127 raise
127 raise
128 self.warn(_("ignored: %s\n") % str(inst))
128 self.warn(_("ignored: %s\n") % str(inst))
129
129
130 if self.plain():
130 if self.plain():
131 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
131 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
132 'logtemplate', 'style',
132 'logtemplate', 'style',
133 'traceback', 'verbose'):
133 'traceback', 'verbose'):
134 if k in cfg['ui']:
134 if k in cfg['ui']:
135 del cfg['ui'][k]
135 del cfg['ui'][k]
136 for k, v in cfg.items('defaults'):
136 for k, v in cfg.items('defaults'):
137 del cfg['defaults'][k]
137 del cfg['defaults'][k]
138 # Don't remove aliases from the configuration if in the exceptionlist
138 # Don't remove aliases from the configuration if in the exceptionlist
139 if self.plain('alias'):
139 if self.plain('alias'):
140 for k, v in cfg.items('alias'):
140 for k, v in cfg.items('alias'):
141 del cfg['alias'][k]
141 del cfg['alias'][k]
142
142
143 if trusted:
143 if trusted:
144 self._tcfg.update(cfg)
144 self._tcfg.update(cfg)
145 self._tcfg.update(self._ocfg)
145 self._tcfg.update(self._ocfg)
146 self._ucfg.update(cfg)
146 self._ucfg.update(cfg)
147 self._ucfg.update(self._ocfg)
147 self._ucfg.update(self._ocfg)
148
148
149 if root is None:
149 if root is None:
150 root = os.path.expanduser('~')
150 root = os.path.expanduser('~')
151 self.fixconfig(root=root)
151 self.fixconfig(root=root)
152
152
153 def fixconfig(self, root=None, section=None):
153 def fixconfig(self, root=None, section=None):
154 if section in (None, 'paths'):
154 if section in (None, 'paths'):
155 # expand vars and ~
155 # expand vars and ~
156 # translate paths relative to root (or home) into absolute paths
156 # translate paths relative to root (or home) into absolute paths
157 root = root or os.getcwd()
157 root = root or os.getcwd()
158 for c in self._tcfg, self._ucfg, self._ocfg:
158 for c in self._tcfg, self._ucfg, self._ocfg:
159 for n, p in c.items('paths'):
159 for n, p in c.items('paths'):
160 if not p:
160 if not p:
161 continue
161 continue
162 if '%%' in p:
162 if '%%' in p:
163 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
163 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
164 % (n, p, self.configsource('paths', n)))
164 % (n, p, self.configsource('paths', n)))
165 p = p.replace('%%', '%')
165 p = p.replace('%%', '%')
166 p = util.expandpath(p)
166 p = util.expandpath(p)
167 if not util.hasscheme(p) and not os.path.isabs(p):
167 if not util.hasscheme(p) and not os.path.isabs(p):
168 p = os.path.normpath(os.path.join(root, p))
168 p = os.path.normpath(os.path.join(root, p))
169 c.set("paths", n, p)
169 c.set("paths", n, p)
170
170
171 if section in (None, 'ui'):
171 if section in (None, 'ui'):
172 # update ui options
172 # update ui options
173 self.debugflag = self.configbool('ui', 'debug')
173 self.debugflag = self.configbool('ui', 'debug')
174 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
174 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
175 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
175 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
176 if self.verbose and self.quiet:
176 if self.verbose and self.quiet:
177 self.quiet = self.verbose = False
177 self.quiet = self.verbose = False
178 self._reportuntrusted = self.debugflag or self.configbool("ui",
178 self._reportuntrusted = self.debugflag or self.configbool("ui",
179 "report_untrusted", True)
179 "report_untrusted", True)
180 self.tracebackflag = self.configbool('ui', 'traceback', False)
180 self.tracebackflag = self.configbool('ui', 'traceback', False)
181
181
182 if section in (None, 'trusted'):
182 if section in (None, 'trusted'):
183 # update trust information
183 # update trust information
184 self._trustusers.update(self.configlist('trusted', 'users'))
184 self._trustusers.update(self.configlist('trusted', 'users'))
185 self._trustgroups.update(self.configlist('trusted', 'groups'))
185 self._trustgroups.update(self.configlist('trusted', 'groups'))
186
186
187 def backupconfig(self, section, item):
187 def backupconfig(self, section, item):
188 return (self._ocfg.backup(section, item),
188 return (self._ocfg.backup(section, item),
189 self._tcfg.backup(section, item),
189 self._tcfg.backup(section, item),
190 self._ucfg.backup(section, item),)
190 self._ucfg.backup(section, item),)
191 def restoreconfig(self, data):
191 def restoreconfig(self, data):
192 self._ocfg.restore(data[0])
192 self._ocfg.restore(data[0])
193 self._tcfg.restore(data[1])
193 self._tcfg.restore(data[1])
194 self._ucfg.restore(data[2])
194 self._ucfg.restore(data[2])
195
195
196 def setconfig(self, section, name, value, source=''):
196 def setconfig(self, section, name, value, source=''):
197 for cfg in (self._ocfg, self._tcfg, self._ucfg):
197 for cfg in (self._ocfg, self._tcfg, self._ucfg):
198 cfg.set(section, name, value, source)
198 cfg.set(section, name, value, source)
199 self.fixconfig(section=section)
199 self.fixconfig(section=section)
200
200
201 def _data(self, untrusted):
201 def _data(self, untrusted):
202 return untrusted and self._ucfg or self._tcfg
202 return untrusted and self._ucfg or self._tcfg
203
203
204 def configsource(self, section, name, untrusted=False):
204 def configsource(self, section, name, untrusted=False):
205 return self._data(untrusted).source(section, name) or 'none'
205 return self._data(untrusted).source(section, name) or 'none'
206
206
207 def config(self, section, name, default=None, untrusted=False):
207 def config(self, section, name, default=None, untrusted=False):
208 if isinstance(name, list):
208 if isinstance(name, list):
209 alternates = name
209 alternates = name
210 else:
210 else:
211 alternates = [name]
211 alternates = [name]
212
212
213 for n in alternates:
213 for n in alternates:
214 value = self._data(untrusted).get(section, n, None)
214 value = self._data(untrusted).get(section, n, None)
215 if value is not None:
215 if value is not None:
216 name = n
216 name = n
217 break
217 break
218 else:
218 else:
219 value = default
219 value = default
220
220
221 if self.debugflag and not untrusted and self._reportuntrusted:
221 if self.debugflag and not untrusted and self._reportuntrusted:
222 for n in alternates:
222 for n in alternates:
223 uvalue = self._ucfg.get(section, n)
223 uvalue = self._ucfg.get(section, n)
224 if uvalue is not None and uvalue != value:
224 if uvalue is not None and uvalue != value:
225 self.debug("ignoring untrusted configuration option "
225 self.debug("ignoring untrusted configuration option "
226 "%s.%s = %s\n" % (section, n, uvalue))
226 "%s.%s = %s\n" % (section, n, uvalue))
227 return value
227 return value
228
228
229 def configpath(self, section, name, default=None, untrusted=False):
229 def configpath(self, section, name, default=None, untrusted=False):
230 'get a path config item, expanded relative to repo root or config file'
230 'get a path config item, expanded relative to repo root or config file'
231 v = self.config(section, name, default, untrusted)
231 v = self.config(section, name, default, untrusted)
232 if v is None:
232 if v is None:
233 return None
233 return None
234 if not os.path.isabs(v) or "://" not in v:
234 if not os.path.isabs(v) or "://" not in v:
235 src = self.configsource(section, name, untrusted)
235 src = self.configsource(section, name, untrusted)
236 if ':' in src:
236 if ':' in src:
237 base = os.path.dirname(src.rsplit(':')[0])
237 base = os.path.dirname(src.rsplit(':')[0])
238 v = os.path.join(base, os.path.expanduser(v))
238 v = os.path.join(base, os.path.expanduser(v))
239 return v
239 return v
240
240
241 def configbool(self, section, name, default=False, untrusted=False):
241 def configbool(self, section, name, default=False, untrusted=False):
242 """parse a configuration element as a boolean
242 """parse a configuration element as a boolean
243
243
244 >>> u = ui(); s = 'foo'
244 >>> u = ui(); s = 'foo'
245 >>> u.setconfig(s, 'true', 'yes')
245 >>> u.setconfig(s, 'true', 'yes')
246 >>> u.configbool(s, 'true')
246 >>> u.configbool(s, 'true')
247 True
247 True
248 >>> u.setconfig(s, 'false', 'no')
248 >>> u.setconfig(s, 'false', 'no')
249 >>> u.configbool(s, 'false')
249 >>> u.configbool(s, 'false')
250 False
250 False
251 >>> u.configbool(s, 'unknown')
251 >>> u.configbool(s, 'unknown')
252 False
252 False
253 >>> u.configbool(s, 'unknown', True)
253 >>> u.configbool(s, 'unknown', True)
254 True
254 True
255 >>> u.setconfig(s, 'invalid', 'somevalue')
255 >>> u.setconfig(s, 'invalid', 'somevalue')
256 >>> u.configbool(s, 'invalid')
256 >>> u.configbool(s, 'invalid')
257 Traceback (most recent call last):
257 Traceback (most recent call last):
258 ...
258 ...
259 ConfigError: foo.invalid is not a boolean ('somevalue')
259 ConfigError: foo.invalid is not a boolean ('somevalue')
260 """
260 """
261
261
262 v = self.config(section, name, None, untrusted)
262 v = self.config(section, name, None, untrusted)
263 if v is None:
263 if v is None:
264 return default
264 return default
265 if isinstance(v, bool):
265 if isinstance(v, bool):
266 return v
266 return v
267 b = util.parsebool(v)
267 b = util.parsebool(v)
268 if b is None:
268 if b is None:
269 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
269 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
270 % (section, name, v))
270 % (section, name, v))
271 return b
271 return b
272
272
273 def configint(self, section, name, default=None, untrusted=False):
273 def configint(self, section, name, default=None, untrusted=False):
274 """parse a configuration element as an integer
274 """parse a configuration element as an integer
275
275
276 >>> u = ui(); s = 'foo'
276 >>> u = ui(); s = 'foo'
277 >>> u.setconfig(s, 'int1', '42')
277 >>> u.setconfig(s, 'int1', '42')
278 >>> u.configint(s, 'int1')
278 >>> u.configint(s, 'int1')
279 42
279 42
280 >>> u.setconfig(s, 'int2', '-42')
280 >>> u.setconfig(s, 'int2', '-42')
281 >>> u.configint(s, 'int2')
281 >>> u.configint(s, 'int2')
282 -42
282 -42
283 >>> u.configint(s, 'unknown', 7)
283 >>> u.configint(s, 'unknown', 7)
284 7
284 7
285 >>> u.setconfig(s, 'invalid', 'somevalue')
285 >>> u.setconfig(s, 'invalid', 'somevalue')
286 >>> u.configint(s, 'invalid')
286 >>> u.configint(s, 'invalid')
287 Traceback (most recent call last):
287 Traceback (most recent call last):
288 ...
288 ...
289 ConfigError: foo.invalid is not an integer ('somevalue')
289 ConfigError: foo.invalid is not an integer ('somevalue')
290 """
290 """
291
291
292 v = self.config(section, name, None, untrusted)
292 v = self.config(section, name, None, untrusted)
293 if v is None:
293 if v is None:
294 return default
294 return default
295 try:
295 try:
296 return int(v)
296 return int(v)
297 except ValueError:
297 except ValueError:
298 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
298 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
299 % (section, name, v))
299 % (section, name, v))
300
300
301 def configbytes(self, section, name, default=0, untrusted=False):
301 def configbytes(self, section, name, default=0, untrusted=False):
302 """parse a configuration element as a quantity in bytes
302 """parse a configuration element as a quantity in bytes
303
303
304 Units can be specified as b (bytes), k or kb (kilobytes), m or
304 Units can be specified as b (bytes), k or kb (kilobytes), m or
305 mb (megabytes), g or gb (gigabytes).
305 mb (megabytes), g or gb (gigabytes).
306
306
307 >>> u = ui(); s = 'foo'
307 >>> u = ui(); s = 'foo'
308 >>> u.setconfig(s, 'val1', '42')
308 >>> u.setconfig(s, 'val1', '42')
309 >>> u.configbytes(s, 'val1')
309 >>> u.configbytes(s, 'val1')
310 42
310 42
311 >>> u.setconfig(s, 'val2', '42.5 kb')
311 >>> u.setconfig(s, 'val2', '42.5 kb')
312 >>> u.configbytes(s, 'val2')
312 >>> u.configbytes(s, 'val2')
313 43520
313 43520
314 >>> u.configbytes(s, 'unknown', '7 MB')
314 >>> u.configbytes(s, 'unknown', '7 MB')
315 7340032
315 7340032
316 >>> u.setconfig(s, 'invalid', 'somevalue')
316 >>> u.setconfig(s, 'invalid', 'somevalue')
317 >>> u.configbytes(s, 'invalid')
317 >>> u.configbytes(s, 'invalid')
318 Traceback (most recent call last):
318 Traceback (most recent call last):
319 ...
319 ...
320 ConfigError: foo.invalid is not a byte quantity ('somevalue')
320 ConfigError: foo.invalid is not a byte quantity ('somevalue')
321 """
321 """
322
322
323 value = self.config(section, name)
323 value = self.config(section, name)
324 if value is None:
324 if value is None:
325 if not isinstance(default, str):
325 if not isinstance(default, str):
326 return default
326 return default
327 value = default
327 value = default
328 try:
328 try:
329 return util.sizetoint(value)
329 return util.sizetoint(value)
330 except error.ParseError:
330 except error.ParseError:
331 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
331 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
332 % (section, name, value))
332 % (section, name, value))
333
333
334 def configlist(self, section, name, default=None, untrusted=False):
334 def configlist(self, section, name, default=None, untrusted=False):
335 """parse a configuration element as a list of comma/space separated
335 """parse a configuration element as a list of comma/space separated
336 strings
336 strings
337
337
338 >>> u = ui(); s = 'foo'
338 >>> u = ui(); s = 'foo'
339 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
339 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
340 >>> u.configlist(s, 'list1')
340 >>> u.configlist(s, 'list1')
341 ['this', 'is', 'a small', 'test']
341 ['this', 'is', 'a small', 'test']
342 """
342 """
343
343
344 def _parse_plain(parts, s, offset):
344 def _parse_plain(parts, s, offset):
345 whitespace = False
345 whitespace = False
346 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
346 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
347 whitespace = True
347 whitespace = True
348 offset += 1
348 offset += 1
349 if offset >= len(s):
349 if offset >= len(s):
350 return None, parts, offset
350 return None, parts, offset
351 if whitespace:
351 if whitespace:
352 parts.append('')
352 parts.append('')
353 if s[offset] == '"' and not parts[-1]:
353 if s[offset] == '"' and not parts[-1]:
354 return _parse_quote, parts, offset + 1
354 return _parse_quote, parts, offset + 1
355 elif s[offset] == '"' and parts[-1][-1] == '\\':
355 elif s[offset] == '"' and parts[-1][-1] == '\\':
356 parts[-1] = parts[-1][:-1] + s[offset]
356 parts[-1] = parts[-1][:-1] + s[offset]
357 return _parse_plain, parts, offset + 1
357 return _parse_plain, parts, offset + 1
358 parts[-1] += s[offset]
358 parts[-1] += s[offset]
359 return _parse_plain, parts, offset + 1
359 return _parse_plain, parts, offset + 1
360
360
361 def _parse_quote(parts, s, offset):
361 def _parse_quote(parts, s, offset):
362 if offset < len(s) and s[offset] == '"': # ""
362 if offset < len(s) and s[offset] == '"': # ""
363 parts.append('')
363 parts.append('')
364 offset += 1
364 offset += 1
365 while offset < len(s) and (s[offset].isspace() or
365 while offset < len(s) and (s[offset].isspace() or
366 s[offset] == ','):
366 s[offset] == ','):
367 offset += 1
367 offset += 1
368 return _parse_plain, parts, offset
368 return _parse_plain, parts, offset
369
369
370 while offset < len(s) and s[offset] != '"':
370 while offset < len(s) and s[offset] != '"':
371 if (s[offset] == '\\' and offset + 1 < len(s)
371 if (s[offset] == '\\' and offset + 1 < len(s)
372 and s[offset + 1] == '"'):
372 and s[offset + 1] == '"'):
373 offset += 1
373 offset += 1
374 parts[-1] += '"'
374 parts[-1] += '"'
375 else:
375 else:
376 parts[-1] += s[offset]
376 parts[-1] += s[offset]
377 offset += 1
377 offset += 1
378
378
379 if offset >= len(s):
379 if offset >= len(s):
380 real_parts = _configlist(parts[-1])
380 real_parts = _configlist(parts[-1])
381 if not real_parts:
381 if not real_parts:
382 parts[-1] = '"'
382 parts[-1] = '"'
383 else:
383 else:
384 real_parts[0] = '"' + real_parts[0]
384 real_parts[0] = '"' + real_parts[0]
385 parts = parts[:-1]
385 parts = parts[:-1]
386 parts.extend(real_parts)
386 parts.extend(real_parts)
387 return None, parts, offset
387 return None, parts, offset
388
388
389 offset += 1
389 offset += 1
390 while offset < len(s) and s[offset] in [' ', ',']:
390 while offset < len(s) and s[offset] in [' ', ',']:
391 offset += 1
391 offset += 1
392
392
393 if offset < len(s):
393 if offset < len(s):
394 if offset + 1 == len(s) and s[offset] == '"':
394 if offset + 1 == len(s) and s[offset] == '"':
395 parts[-1] += '"'
395 parts[-1] += '"'
396 offset += 1
396 offset += 1
397 else:
397 else:
398 parts.append('')
398 parts.append('')
399 else:
399 else:
400 return None, parts, offset
400 return None, parts, offset
401
401
402 return _parse_plain, parts, offset
402 return _parse_plain, parts, offset
403
403
404 def _configlist(s):
404 def _configlist(s):
405 s = s.rstrip(' ,')
405 s = s.rstrip(' ,')
406 if not s:
406 if not s:
407 return []
407 return []
408 parser, parts, offset = _parse_plain, [''], 0
408 parser, parts, offset = _parse_plain, [''], 0
409 while parser:
409 while parser:
410 parser, parts, offset = parser(parts, s, offset)
410 parser, parts, offset = parser(parts, s, offset)
411 return parts
411 return parts
412
412
413 result = self.config(section, name, untrusted=untrusted)
413 result = self.config(section, name, untrusted=untrusted)
414 if result is None:
414 if result is None:
415 result = default or []
415 result = default or []
416 if isinstance(result, basestring):
416 if isinstance(result, basestring):
417 result = _configlist(result.lstrip(' ,\n'))
417 result = _configlist(result.lstrip(' ,\n'))
418 if result is None:
418 if result is None:
419 result = default or []
419 result = default or []
420 return result
420 return result
421
421
422 def has_section(self, section, untrusted=False):
422 def has_section(self, section, untrusted=False):
423 '''tell whether section exists in config.'''
423 '''tell whether section exists in config.'''
424 return section in self._data(untrusted)
424 return section in self._data(untrusted)
425
425
426 def configitems(self, section, untrusted=False):
426 def configitems(self, section, untrusted=False):
427 items = self._data(untrusted).items(section)
427 items = self._data(untrusted).items(section)
428 if self.debugflag and not untrusted and self._reportuntrusted:
428 if self.debugflag and not untrusted and self._reportuntrusted:
429 for k, v in self._ucfg.items(section):
429 for k, v in self._ucfg.items(section):
430 if self._tcfg.get(section, k) != v:
430 if self._tcfg.get(section, k) != v:
431 self.debug("ignoring untrusted configuration option "
431 self.debug("ignoring untrusted configuration option "
432 "%s.%s = %s\n" % (section, k, v))
432 "%s.%s = %s\n" % (section, k, v))
433 return items
433 return items
434
434
435 def walkconfig(self, untrusted=False):
435 def walkconfig(self, untrusted=False):
436 cfg = self._data(untrusted)
436 cfg = self._data(untrusted)
437 for section in cfg.sections():
437 for section in cfg.sections():
438 for name, value in self.configitems(section, untrusted):
438 for name, value in self.configitems(section, untrusted):
439 yield section, name, value
439 yield section, name, value
440
440
441 def plain(self, feature=None):
441 def plain(self, feature=None):
442 '''is plain mode active?
442 '''is plain mode active?
443
443
444 Plain mode means that all configuration variables which affect
444 Plain mode means that all configuration variables which affect
445 the behavior and output of Mercurial should be
445 the behavior and output of Mercurial should be
446 ignored. Additionally, the output should be stable,
446 ignored. Additionally, the output should be stable,
447 reproducible and suitable for use in scripts or applications.
447 reproducible and suitable for use in scripts or applications.
448
448
449 The only way to trigger plain mode is by setting either the
449 The only way to trigger plain mode is by setting either the
450 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
450 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
451
451
452 The return value can either be
452 The return value can either be
453 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
453 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
454 - True otherwise
454 - True otherwise
455 '''
455 '''
456 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
456 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
457 return False
457 return False
458 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
458 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
459 if feature and exceptions:
459 if feature and exceptions:
460 return feature not in exceptions
460 return feature not in exceptions
461 return True
461 return True
462
462
463 def username(self):
463 def username(self):
464 """Return default username to be used in commits.
464 """Return default username to be used in commits.
465
465
466 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
466 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
467 and stop searching if one of these is set.
467 and stop searching if one of these is set.
468 If not found and ui.askusername is True, ask the user, else use
468 If not found and ui.askusername is True, ask the user, else use
469 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
469 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
470 """
470 """
471 user = os.environ.get("HGUSER")
471 user = os.environ.get("HGUSER")
472 if user is None:
472 if user is None:
473 user = self.config("ui", ["username", "user"])
473 user = self.config("ui", ["username", "user"])
474 if user is not None:
474 if user is not None:
475 user = os.path.expandvars(user)
475 user = os.path.expandvars(user)
476 if user is None:
476 if user is None:
477 user = os.environ.get("EMAIL")
477 user = os.environ.get("EMAIL")
478 if user is None and self.configbool("ui", "askusername"):
478 if user is None and self.configbool("ui", "askusername"):
479 user = self.prompt(_("enter a commit username:"), default=None)
479 user = self.prompt(_("enter a commit username:"), default=None)
480 if user is None and not self.interactive():
480 if user is None and not self.interactive():
481 try:
481 try:
482 user = '%s@%s' % (util.getuser(), socket.getfqdn())
482 user = '%s@%s' % (util.getuser(), socket.getfqdn())
483 self.warn(_("no username found, using '%s' instead\n") % user)
483 self.warn(_("no username found, using '%s' instead\n") % user)
484 except KeyError:
484 except KeyError:
485 pass
485 pass
486 if not user:
486 if not user:
487 raise util.Abort(_('no username supplied'),
487 raise util.Abort(_('no username supplied'),
488 hint=_('use "hg config --edit" '
488 hint=_('use "hg config --edit" '
489 'to set your username'))
489 'to set your username'))
490 if "\n" in user:
490 if "\n" in user:
491 raise util.Abort(_("username %s contains a newline\n") % repr(user))
491 raise util.Abort(_("username %s contains a newline\n") % repr(user))
492 return user
492 return user
493
493
494 def shortuser(self, user):
494 def shortuser(self, user):
495 """Return a short representation of a user name or email address."""
495 """Return a short representation of a user name or email address."""
496 if not self.verbose:
496 if not self.verbose:
497 user = util.shortuser(user)
497 user = util.shortuser(user)
498 return user
498 return user
499
499
500 def expandpath(self, loc, default=None):
500 def expandpath(self, loc, default=None):
501 """Return repository location relative to cwd or from [paths]"""
501 """Return repository location relative to cwd or from [paths]"""
502 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
502 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
503 return loc
503 return loc
504
504
505 path = self.config('paths', loc)
505 path = self.config('paths', loc)
506 if not path and default is not None:
506 if not path and default is not None:
507 path = self.config('paths', default)
507 path = self.config('paths', default)
508 return path or loc
508 return path or loc
509
509
510 def pushbuffer(self, error=False):
510 def pushbuffer(self, error=False):
511 """install a buffer to capture standar output of the ui object
511 """install a buffer to capture standar output of the ui object
512
512
513 If error is True, the error output will be captured too."""
513 If error is True, the error output will be captured too."""
514 self._buffers.append([])
514 self._buffers.append([])
515 self._bufferstates.append(error)
515 self._bufferstates.append(error)
516
516
517 def popbuffer(self, labeled=False):
517 def popbuffer(self, labeled=False):
518 '''pop the last buffer and return the buffered output
518 '''pop the last buffer and return the buffered output
519
519
520 If labeled is True, any labels associated with buffered
520 If labeled is True, any labels associated with buffered
521 output will be handled. By default, this has no effect
521 output will be handled. By default, this has no effect
522 on the output returned, but extensions and GUI tools may
522 on the output returned, but extensions and GUI tools may
523 handle this argument and returned styled output. If output
523 handle this argument and returned styled output. If output
524 is being buffered so it can be captured and parsed or
524 is being buffered so it can be captured and parsed or
525 processed, labeled should not be set to True.
525 processed, labeled should not be set to True.
526 '''
526 '''
527 self._bufferstates.pop()
527 self._bufferstates.pop()
528 return "".join(self._buffers.pop())
528 return "".join(self._buffers.pop())
529
529
530 def write(self, *args, **opts):
530 def write(self, *args, **opts):
531 '''write args to output
531 '''write args to output
532
532
533 By default, this method simply writes to the buffer or stdout,
533 By default, this method simply writes to the buffer or stdout,
534 but extensions or GUI tools may override this method,
534 but extensions or GUI tools may override this method,
535 write_err(), popbuffer(), and label() to style output from
535 write_err(), popbuffer(), and label() to style output from
536 various parts of hg.
536 various parts of hg.
537
537
538 An optional keyword argument, "label", can be passed in.
538 An optional keyword argument, "label", can be passed in.
539 This should be a string containing label names separated by
539 This should be a string containing label names separated by
540 space. Label names take the form of "topic.type". For example,
540 space. Label names take the form of "topic.type". For example,
541 ui.debug() issues a label of "ui.debug".
541 ui.debug() issues a label of "ui.debug".
542
542
543 When labeling output for a specific command, a label of
543 When labeling output for a specific command, a label of
544 "cmdname.type" is recommended. For example, status issues
544 "cmdname.type" is recommended. For example, status issues
545 a label of "status.modified" for modified files.
545 a label of "status.modified" for modified files.
546 '''
546 '''
547 if self._buffers:
547 if self._buffers:
548 self._buffers[-1].extend([str(a) for a in args])
548 self._buffers[-1].extend([str(a) for a in args])
549 else:
549 else:
550 for a in args:
550 for a in args:
551 self.fout.write(str(a))
551 self.fout.write(str(a))
552
552
553 def write_err(self, *args, **opts):
553 def write_err(self, *args, **opts):
554 try:
554 try:
555 if self._bufferstates and self._bufferstates[-1]:
555 if self._bufferstates and self._bufferstates[-1]:
556 return self.write(*args, **opts)
556 return self.write(*args, **opts)
557 if not getattr(self.fout, 'closed', False):
557 if not getattr(self.fout, 'closed', False):
558 self.fout.flush()
558 self.fout.flush()
559 for a in args:
559 for a in args:
560 self.ferr.write(str(a))
560 self.ferr.write(str(a))
561 # stderr may be buffered under win32 when redirected to files,
561 # stderr may be buffered under win32 when redirected to files,
562 # including stdout.
562 # including stdout.
563 if not getattr(self.ferr, 'closed', False):
563 if not getattr(self.ferr, 'closed', False):
564 self.ferr.flush()
564 self.ferr.flush()
565 except IOError, inst:
565 except IOError, inst:
566 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
566 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
567 raise
567 raise
568
568
569 def flush(self):
569 def flush(self):
570 try: self.fout.flush()
570 try: self.fout.flush()
571 except (IOError, ValueError): pass
571 except (IOError, ValueError): pass
572 try: self.ferr.flush()
572 try: self.ferr.flush()
573 except (IOError, ValueError): pass
573 except (IOError, ValueError): pass
574
574
575 def _isatty(self, fh):
575 def _isatty(self, fh):
576 if self.configbool('ui', 'nontty', False):
576 if self.configbool('ui', 'nontty', False):
577 return False
577 return False
578 return util.isatty(fh)
578 return util.isatty(fh)
579
579
580 def interactive(self):
580 def interactive(self):
581 '''is interactive input allowed?
581 '''is interactive input allowed?
582
582
583 An interactive session is a session where input can be reasonably read
583 An interactive session is a session where input can be reasonably read
584 from `sys.stdin'. If this function returns false, any attempt to read
584 from `sys.stdin'. If this function returns false, any attempt to read
585 from stdin should fail with an error, unless a sensible default has been
585 from stdin should fail with an error, unless a sensible default has been
586 specified.
586 specified.
587
587
588 Interactiveness is triggered by the value of the `ui.interactive'
588 Interactiveness is triggered by the value of the `ui.interactive'
589 configuration variable or - if it is unset - when `sys.stdin' points
589 configuration variable or - if it is unset - when `sys.stdin' points
590 to a terminal device.
590 to a terminal device.
591
591
592 This function refers to input only; for output, see `ui.formatted()'.
592 This function refers to input only; for output, see `ui.formatted()'.
593 '''
593 '''
594 i = self.configbool("ui", "interactive", None)
594 i = self.configbool("ui", "interactive", None)
595 if i is None:
595 if i is None:
596 # some environments replace stdin without implementing isatty
596 # some environments replace stdin without implementing isatty
597 # usually those are non-interactive
597 # usually those are non-interactive
598 return self._isatty(self.fin)
598 return self._isatty(self.fin)
599
599
600 return i
600 return i
601
601
602 def termwidth(self):
602 def termwidth(self):
603 '''how wide is the terminal in columns?
603 '''how wide is the terminal in columns?
604 '''
604 '''
605 if 'COLUMNS' in os.environ:
605 if 'COLUMNS' in os.environ:
606 try:
606 try:
607 return int(os.environ['COLUMNS'])
607 return int(os.environ['COLUMNS'])
608 except ValueError:
608 except ValueError:
609 pass
609 pass
610 return util.termwidth()
610 return util.termwidth()
611
611
612 def formatted(self):
612 def formatted(self):
613 '''should formatted output be used?
613 '''should formatted output be used?
614
614
615 It is often desirable to format the output to suite the output medium.
615 It is often desirable to format the output to suite the output medium.
616 Examples of this are truncating long lines or colorizing messages.
616 Examples of this are truncating long lines or colorizing messages.
617 However, this is not often not desirable when piping output into other
617 However, this is not often not desirable when piping output into other
618 utilities, e.g. `grep'.
618 utilities, e.g. `grep'.
619
619
620 Formatted output is triggered by the value of the `ui.formatted'
620 Formatted output is triggered by the value of the `ui.formatted'
621 configuration variable or - if it is unset - when `sys.stdout' points
621 configuration variable or - if it is unset - when `sys.stdout' points
622 to a terminal device. Please note that `ui.formatted' should be
622 to a terminal device. Please note that `ui.formatted' should be
623 considered an implementation detail; it is not intended for use outside
623 considered an implementation detail; it is not intended for use outside
624 Mercurial or its extensions.
624 Mercurial or its extensions.
625
625
626 This function refers to output only; for input, see `ui.interactive()'.
626 This function refers to output only; for input, see `ui.interactive()'.
627 This function always returns false when in plain mode, see `ui.plain()'.
627 This function always returns false when in plain mode, see `ui.plain()'.
628 '''
628 '''
629 if self.plain():
629 if self.plain():
630 return False
630 return False
631
631
632 i = self.configbool("ui", "formatted", None)
632 i = self.configbool("ui", "formatted", None)
633 if i is None:
633 if i is None:
634 # some environments replace stdout without implementing isatty
634 # some environments replace stdout without implementing isatty
635 # usually those are non-interactive
635 # usually those are non-interactive
636 return self._isatty(self.fout)
636 return self._isatty(self.fout)
637
637
638 return i
638 return i
639
639
640 def _readline(self, prompt=''):
640 def _readline(self, prompt=''):
641 if self._isatty(self.fin):
641 if self._isatty(self.fin):
642 try:
642 try:
643 # magically add command line editing support, where
643 # magically add command line editing support, where
644 # available
644 # available
645 import readline
645 import readline
646 # force demandimport to really load the module
646 # force demandimport to really load the module
647 readline.read_history_file
647 readline.read_history_file
648 # windows sometimes raises something other than ImportError
648 # windows sometimes raises something other than ImportError
649 except Exception:
649 except Exception:
650 pass
650 pass
651
651
652 # call write() so output goes through subclassed implementation
652 # call write() so output goes through subclassed implementation
653 # e.g. color extension on Windows
653 # e.g. color extension on Windows
654 self.write(prompt)
654 self.write(prompt)
655
655
656 # instead of trying to emulate raw_input, swap (self.fin,
656 # instead of trying to emulate raw_input, swap (self.fin,
657 # self.fout) with (sys.stdin, sys.stdout)
657 # self.fout) with (sys.stdin, sys.stdout)
658 oldin = sys.stdin
658 oldin = sys.stdin
659 oldout = sys.stdout
659 oldout = sys.stdout
660 sys.stdin = self.fin
660 sys.stdin = self.fin
661 sys.stdout = self.fout
661 sys.stdout = self.fout
662 # prompt ' ' must exist; otherwise readline may delete entire line
662 # prompt ' ' must exist; otherwise readline may delete entire line
663 # - http://bugs.python.org/issue12833
663 # - http://bugs.python.org/issue12833
664 line = raw_input(' ')
664 line = raw_input(' ')
665 sys.stdin = oldin
665 sys.stdin = oldin
666 sys.stdout = oldout
666 sys.stdout = oldout
667
667
668 # When stdin is in binary mode on Windows, it can cause
668 # When stdin is in binary mode on Windows, it can cause
669 # raw_input() to emit an extra trailing carriage return
669 # raw_input() to emit an extra trailing carriage return
670 if os.linesep == '\r\n' and line and line[-1] == '\r':
670 if os.linesep == '\r\n' and line and line[-1] == '\r':
671 line = line[:-1]
671 line = line[:-1]
672 return line
672 return line
673
673
674 def prompt(self, msg, default="y"):
674 def prompt(self, msg, default="y"):
675 """Prompt user with msg, read response.
675 """Prompt user with msg, read response.
676 If ui is not interactive, the default is returned.
676 If ui is not interactive, the default is returned.
677 """
677 """
678 if not self.interactive():
678 if not self.interactive():
679 self.write(msg, ' ', default, "\n")
679 self.write(msg, ' ', default, "\n")
680 return default
680 return default
681 try:
681 try:
682 r = self._readline(self.label(msg, 'ui.prompt'))
682 r = self._readline(self.label(msg, 'ui.prompt'))
683 if not r:
683 if not r:
684 r = default
684 r = default
685 # sometimes self.interactive disagrees with isatty,
685 # sometimes self.interactive disagrees with isatty,
686 # show default response
686 # show response provided on stdin when simulating
687 if not util.isatty(self.fin):
687 if not util.isatty(self.fin):
688 self.write(r, "\n")
688 self.write(r, "\n")
689 return r
689 return r
690 except EOFError:
690 except EOFError:
691 raise util.Abort(_('response expected'))
691 raise util.Abort(_('response expected'))
692
692
693 @staticmethod
693 @staticmethod
694 def extractchoices(prompt):
694 def extractchoices(prompt):
695 """Extract prompt message and list of choices from specified prompt.
695 """Extract prompt message and list of choices from specified prompt.
696
696
697 This returns tuple "(message, choices)", and "choices" is the
697 This returns tuple "(message, choices)", and "choices" is the
698 list of tuple "(response character, text without &)".
698 list of tuple "(response character, text without &)".
699 """
699 """
700 parts = prompt.split('$$')
700 parts = prompt.split('$$')
701 msg = parts[0].rstrip(' ')
701 msg = parts[0].rstrip(' ')
702 choices = [p.strip(' ') for p in parts[1:]]
702 choices = [p.strip(' ') for p in parts[1:]]
703 return (msg,
703 return (msg,
704 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
704 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
705 for s in choices])
705 for s in choices])
706
706
707 def promptchoice(self, prompt, default=0):
707 def promptchoice(self, prompt, default=0):
708 """Prompt user with a message, read response, and ensure it matches
708 """Prompt user with a message, read response, and ensure it matches
709 one of the provided choices. The prompt is formatted as follows:
709 one of the provided choices. The prompt is formatted as follows:
710
710
711 "would you like fries with that (Yn)? $$ &Yes $$ &No"
711 "would you like fries with that (Yn)? $$ &Yes $$ &No"
712
712
713 The index of the choice is returned. Responses are case
713 The index of the choice is returned. Responses are case
714 insensitive. If ui is not interactive, the default is
714 insensitive. If ui is not interactive, the default is
715 returned.
715 returned.
716 """
716 """
717
717
718 msg, choices = self.extractchoices(prompt)
718 msg, choices = self.extractchoices(prompt)
719 resps = [r for r, t in choices]
719 resps = [r for r, t in choices]
720 while True:
720 while True:
721 r = self.prompt(msg, resps[default])
721 r = self.prompt(msg, resps[default])
722 if r.lower() in resps:
722 if r.lower() in resps:
723 return resps.index(r.lower())
723 return resps.index(r.lower())
724 self.write(_("unrecognized response\n"))
724 self.write(_("unrecognized response\n"))
725
725
726 def getpass(self, prompt=None, default=None):
726 def getpass(self, prompt=None, default=None):
727 if not self.interactive():
727 if not self.interactive():
728 return default
728 return default
729 try:
729 try:
730 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
730 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
731 # disable getpass() only if explicitly specified. it's still valid
731 # disable getpass() only if explicitly specified. it's still valid
732 # to interact with tty even if fin is not a tty.
732 # to interact with tty even if fin is not a tty.
733 if self.configbool('ui', 'nontty'):
733 if self.configbool('ui', 'nontty'):
734 return self.fin.readline().rstrip('\n')
734 return self.fin.readline().rstrip('\n')
735 else:
735 else:
736 return getpass.getpass('')
736 return getpass.getpass('')
737 except EOFError:
737 except EOFError:
738 raise util.Abort(_('response expected'))
738 raise util.Abort(_('response expected'))
739 def status(self, *msg, **opts):
739 def status(self, *msg, **opts):
740 '''write status message to output (if ui.quiet is False)
740 '''write status message to output (if ui.quiet is False)
741
741
742 This adds an output label of "ui.status".
742 This adds an output label of "ui.status".
743 '''
743 '''
744 if not self.quiet:
744 if not self.quiet:
745 opts['label'] = opts.get('label', '') + ' ui.status'
745 opts['label'] = opts.get('label', '') + ' ui.status'
746 self.write(*msg, **opts)
746 self.write(*msg, **opts)
747 def warn(self, *msg, **opts):
747 def warn(self, *msg, **opts):
748 '''write warning message to output (stderr)
748 '''write warning message to output (stderr)
749
749
750 This adds an output label of "ui.warning".
750 This adds an output label of "ui.warning".
751 '''
751 '''
752 opts['label'] = opts.get('label', '') + ' ui.warning'
752 opts['label'] = opts.get('label', '') + ' ui.warning'
753 self.write_err(*msg, **opts)
753 self.write_err(*msg, **opts)
754 def note(self, *msg, **opts):
754 def note(self, *msg, **opts):
755 '''write note to output (if ui.verbose is True)
755 '''write note to output (if ui.verbose is True)
756
756
757 This adds an output label of "ui.note".
757 This adds an output label of "ui.note".
758 '''
758 '''
759 if self.verbose:
759 if self.verbose:
760 opts['label'] = opts.get('label', '') + ' ui.note'
760 opts['label'] = opts.get('label', '') + ' ui.note'
761 self.write(*msg, **opts)
761 self.write(*msg, **opts)
762 def debug(self, *msg, **opts):
762 def debug(self, *msg, **opts):
763 '''write debug message to output (if ui.debugflag is True)
763 '''write debug message to output (if ui.debugflag is True)
764
764
765 This adds an output label of "ui.debug".
765 This adds an output label of "ui.debug".
766 '''
766 '''
767 if self.debugflag:
767 if self.debugflag:
768 opts['label'] = opts.get('label', '') + ' ui.debug'
768 opts['label'] = opts.get('label', '') + ' ui.debug'
769 self.write(*msg, **opts)
769 self.write(*msg, **opts)
770 def edit(self, text, user, extra={}, editform=None):
770 def edit(self, text, user, extra={}, editform=None):
771 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
771 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
772 text=True)
772 text=True)
773 try:
773 try:
774 f = os.fdopen(fd, "w")
774 f = os.fdopen(fd, "w")
775 f.write(text)
775 f.write(text)
776 f.close()
776 f.close()
777
777
778 environ = {'HGUSER': user}
778 environ = {'HGUSER': user}
779 if 'transplant_source' in extra:
779 if 'transplant_source' in extra:
780 environ.update({'HGREVISION': hex(extra['transplant_source'])})
780 environ.update({'HGREVISION': hex(extra['transplant_source'])})
781 for label in ('source', 'rebase_source'):
781 for label in ('source', 'rebase_source'):
782 if label in extra:
782 if label in extra:
783 environ.update({'HGREVISION': extra[label]})
783 environ.update({'HGREVISION': extra[label]})
784 break
784 break
785 if editform:
785 if editform:
786 environ.update({'HGEDITFORM': editform})
786 environ.update({'HGEDITFORM': editform})
787
787
788 editor = self.geteditor()
788 editor = self.geteditor()
789
789
790 util.system("%s \"%s\"" % (editor, name),
790 util.system("%s \"%s\"" % (editor, name),
791 environ=environ,
791 environ=environ,
792 onerr=util.Abort, errprefix=_("edit failed"),
792 onerr=util.Abort, errprefix=_("edit failed"),
793 out=self.fout)
793 out=self.fout)
794
794
795 f = open(name)
795 f = open(name)
796 t = f.read()
796 t = f.read()
797 f.close()
797 f.close()
798 finally:
798 finally:
799 os.unlink(name)
799 os.unlink(name)
800
800
801 return t
801 return t
802
802
803 def traceback(self, exc=None, force=False):
803 def traceback(self, exc=None, force=False):
804 '''print exception traceback if traceback printing enabled or forced.
804 '''print exception traceback if traceback printing enabled or forced.
805 only to call in exception handler. returns true if traceback
805 only to call in exception handler. returns true if traceback
806 printed.'''
806 printed.'''
807 if self.tracebackflag or force:
807 if self.tracebackflag or force:
808 if exc is None:
808 if exc is None:
809 exc = sys.exc_info()
809 exc = sys.exc_info()
810 cause = getattr(exc[1], 'cause', None)
810 cause = getattr(exc[1], 'cause', None)
811
811
812 if cause is not None:
812 if cause is not None:
813 causetb = traceback.format_tb(cause[2])
813 causetb = traceback.format_tb(cause[2])
814 exctb = traceback.format_tb(exc[2])
814 exctb = traceback.format_tb(exc[2])
815 exconly = traceback.format_exception_only(cause[0], cause[1])
815 exconly = traceback.format_exception_only(cause[0], cause[1])
816
816
817 # exclude frame where 'exc' was chained and rethrown from exctb
817 # exclude frame where 'exc' was chained and rethrown from exctb
818 self.write_err('Traceback (most recent call last):\n',
818 self.write_err('Traceback (most recent call last):\n',
819 ''.join(exctb[:-1]),
819 ''.join(exctb[:-1]),
820 ''.join(causetb),
820 ''.join(causetb),
821 ''.join(exconly))
821 ''.join(exconly))
822 else:
822 else:
823 traceback.print_exception(exc[0], exc[1], exc[2],
823 traceback.print_exception(exc[0], exc[1], exc[2],
824 file=self.ferr)
824 file=self.ferr)
825 return self.tracebackflag or force
825 return self.tracebackflag or force
826
826
827 def geteditor(self):
827 def geteditor(self):
828 '''return editor to use'''
828 '''return editor to use'''
829 if sys.platform == 'plan9':
829 if sys.platform == 'plan9':
830 # vi is the MIPS instruction simulator on Plan 9. We
830 # vi is the MIPS instruction simulator on Plan 9. We
831 # instead default to E to plumb commit messages to
831 # instead default to E to plumb commit messages to
832 # avoid confusion.
832 # avoid confusion.
833 editor = 'E'
833 editor = 'E'
834 else:
834 else:
835 editor = 'vi'
835 editor = 'vi'
836 return (os.environ.get("HGEDITOR") or
836 return (os.environ.get("HGEDITOR") or
837 self.config("ui", "editor") or
837 self.config("ui", "editor") or
838 os.environ.get("VISUAL") or
838 os.environ.get("VISUAL") or
839 os.environ.get("EDITOR", editor))
839 os.environ.get("EDITOR", editor))
840
840
841 def progress(self, topic, pos, item="", unit="", total=None):
841 def progress(self, topic, pos, item="", unit="", total=None):
842 '''show a progress message
842 '''show a progress message
843
843
844 With stock hg, this is simply a debug message that is hidden
844 With stock hg, this is simply a debug message that is hidden
845 by default, but with extensions or GUI tools it may be
845 by default, but with extensions or GUI tools it may be
846 visible. 'topic' is the current operation, 'item' is a
846 visible. 'topic' is the current operation, 'item' is a
847 non-numeric marker of the current position (i.e. the currently
847 non-numeric marker of the current position (i.e. the currently
848 in-process file), 'pos' is the current numeric position (i.e.
848 in-process file), 'pos' is the current numeric position (i.e.
849 revision, bytes, etc.), unit is a corresponding unit label,
849 revision, bytes, etc.), unit is a corresponding unit label,
850 and total is the highest expected pos.
850 and total is the highest expected pos.
851
851
852 Multiple nested topics may be active at a time.
852 Multiple nested topics may be active at a time.
853
853
854 All topics should be marked closed by setting pos to None at
854 All topics should be marked closed by setting pos to None at
855 termination.
855 termination.
856 '''
856 '''
857
857
858 if pos is None or not self.debugflag:
858 if pos is None or not self.debugflag:
859 return
859 return
860
860
861 if unit:
861 if unit:
862 unit = ' ' + unit
862 unit = ' ' + unit
863 if item:
863 if item:
864 item = ' ' + item
864 item = ' ' + item
865
865
866 if total:
866 if total:
867 pct = 100.0 * pos / total
867 pct = 100.0 * pos / total
868 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
868 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
869 % (topic, item, pos, total, unit, pct))
869 % (topic, item, pos, total, unit, pct))
870 else:
870 else:
871 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
871 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
872
872
873 def log(self, service, *msg, **opts):
873 def log(self, service, *msg, **opts):
874 '''hook for logging facility extensions
874 '''hook for logging facility extensions
875
875
876 service should be a readily-identifiable subsystem, which will
876 service should be a readily-identifiable subsystem, which will
877 allow filtering.
877 allow filtering.
878 message should be a newline-terminated string to log.
878 message should be a newline-terminated string to log.
879 '''
879 '''
880 pass
880 pass
881
881
882 def label(self, msg, label):
882 def label(self, msg, label):
883 '''style msg based on supplied label
883 '''style msg based on supplied label
884
884
885 Like ui.write(), this just returns msg unchanged, but extensions
885 Like ui.write(), this just returns msg unchanged, but extensions
886 and GUI tools can override it to allow styling output without
886 and GUI tools can override it to allow styling output without
887 writing it.
887 writing it.
888
888
889 ui.write(s, 'label') is equivalent to
889 ui.write(s, 'label') is equivalent to
890 ui.write(ui.label(s, 'label')).
890 ui.write(ui.label(s, 'label')).
891 '''
891 '''
892 return msg
892 return msg
General Comments 0
You need to be logged in to leave comments. Login now