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