##// END OF EJS Templates
ui: add "extractchoices()" to share the logic to extract choices from prompt
FUJIWARA Katsunori -
r20265:e5803150 default
parent child Browse files
Show More
@@ -1,815 +1,827 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 (see "hg help config")'))
452 raise util.Abort(_('no username supplied (see "hg help config")'))
453 if "\n" in user:
453 if "\n" in user:
454 raise util.Abort(_("username %s contains a newline\n") % repr(user))
454 raise util.Abort(_("username %s contains a newline\n") % repr(user))
455 return user
455 return user
456
456
457 def shortuser(self, user):
457 def shortuser(self, user):
458 """Return a short representation of a user name or email address."""
458 """Return a short representation of a user name or email address."""
459 if not self.verbose:
459 if not self.verbose:
460 user = util.shortuser(user)
460 user = util.shortuser(user)
461 return user
461 return user
462
462
463 def expandpath(self, loc, default=None):
463 def expandpath(self, loc, default=None):
464 """Return repository location relative to cwd or from [paths]"""
464 """Return repository location relative to cwd or from [paths]"""
465 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
465 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
466 return loc
466 return loc
467
467
468 path = self.config('paths', loc)
468 path = self.config('paths', loc)
469 if not path and default is not None:
469 if not path and default is not None:
470 path = self.config('paths', default)
470 path = self.config('paths', default)
471 return path or loc
471 return path or loc
472
472
473 def pushbuffer(self):
473 def pushbuffer(self):
474 self._buffers.append([])
474 self._buffers.append([])
475
475
476 def popbuffer(self, labeled=False):
476 def popbuffer(self, labeled=False):
477 '''pop the last buffer and return the buffered output
477 '''pop the last buffer and return the buffered output
478
478
479 If labeled is True, any labels associated with buffered
479 If labeled is True, any labels associated with buffered
480 output will be handled. By default, this has no effect
480 output will be handled. By default, this has no effect
481 on the output returned, but extensions and GUI tools may
481 on the output returned, but extensions and GUI tools may
482 handle this argument and returned styled output. If output
482 handle this argument and returned styled output. If output
483 is being buffered so it can be captured and parsed or
483 is being buffered so it can be captured and parsed or
484 processed, labeled should not be set to True.
484 processed, labeled should not be set to True.
485 '''
485 '''
486 return "".join(self._buffers.pop())
486 return "".join(self._buffers.pop())
487
487
488 def write(self, *args, **opts):
488 def write(self, *args, **opts):
489 '''write args to output
489 '''write args to output
490
490
491 By default, this method simply writes to the buffer or stdout,
491 By default, this method simply writes to the buffer or stdout,
492 but extensions or GUI tools may override this method,
492 but extensions or GUI tools may override this method,
493 write_err(), popbuffer(), and label() to style output from
493 write_err(), popbuffer(), and label() to style output from
494 various parts of hg.
494 various parts of hg.
495
495
496 An optional keyword argument, "label", can be passed in.
496 An optional keyword argument, "label", can be passed in.
497 This should be a string containing label names separated by
497 This should be a string containing label names separated by
498 space. Label names take the form of "topic.type". For example,
498 space. Label names take the form of "topic.type". For example,
499 ui.debug() issues a label of "ui.debug".
499 ui.debug() issues a label of "ui.debug".
500
500
501 When labeling output for a specific command, a label of
501 When labeling output for a specific command, a label of
502 "cmdname.type" is recommended. For example, status issues
502 "cmdname.type" is recommended. For example, status issues
503 a label of "status.modified" for modified files.
503 a label of "status.modified" for modified files.
504 '''
504 '''
505 if self._buffers:
505 if self._buffers:
506 self._buffers[-1].extend([str(a) for a in args])
506 self._buffers[-1].extend([str(a) for a in args])
507 else:
507 else:
508 for a in args:
508 for a in args:
509 self.fout.write(str(a))
509 self.fout.write(str(a))
510
510
511 def write_err(self, *args, **opts):
511 def write_err(self, *args, **opts):
512 try:
512 try:
513 if not getattr(self.fout, 'closed', False):
513 if not getattr(self.fout, 'closed', False):
514 self.fout.flush()
514 self.fout.flush()
515 for a in args:
515 for a in args:
516 self.ferr.write(str(a))
516 self.ferr.write(str(a))
517 # stderr may be buffered under win32 when redirected to files,
517 # stderr may be buffered under win32 when redirected to files,
518 # including stdout.
518 # including stdout.
519 if not getattr(self.ferr, 'closed', False):
519 if not getattr(self.ferr, 'closed', False):
520 self.ferr.flush()
520 self.ferr.flush()
521 except IOError, inst:
521 except IOError, inst:
522 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
522 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
523 raise
523 raise
524
524
525 def flush(self):
525 def flush(self):
526 try: self.fout.flush()
526 try: self.fout.flush()
527 except (IOError, ValueError): pass
527 except (IOError, ValueError): pass
528 try: self.ferr.flush()
528 try: self.ferr.flush()
529 except (IOError, ValueError): pass
529 except (IOError, ValueError): pass
530
530
531 def _isatty(self, fh):
531 def _isatty(self, fh):
532 if self.configbool('ui', 'nontty', False):
532 if self.configbool('ui', 'nontty', False):
533 return False
533 return False
534 return util.isatty(fh)
534 return util.isatty(fh)
535
535
536 def interactive(self):
536 def interactive(self):
537 '''is interactive input allowed?
537 '''is interactive input allowed?
538
538
539 An interactive session is a session where input can be reasonably read
539 An interactive session is a session where input can be reasonably read
540 from `sys.stdin'. If this function returns false, any attempt to read
540 from `sys.stdin'. If this function returns false, any attempt to read
541 from stdin should fail with an error, unless a sensible default has been
541 from stdin should fail with an error, unless a sensible default has been
542 specified.
542 specified.
543
543
544 Interactiveness is triggered by the value of the `ui.interactive'
544 Interactiveness is triggered by the value of the `ui.interactive'
545 configuration variable or - if it is unset - when `sys.stdin' points
545 configuration variable or - if it is unset - when `sys.stdin' points
546 to a terminal device.
546 to a terminal device.
547
547
548 This function refers to input only; for output, see `ui.formatted()'.
548 This function refers to input only; for output, see `ui.formatted()'.
549 '''
549 '''
550 i = self.configbool("ui", "interactive", None)
550 i = self.configbool("ui", "interactive", None)
551 if i is None:
551 if i is None:
552 # some environments replace stdin without implementing isatty
552 # some environments replace stdin without implementing isatty
553 # usually those are non-interactive
553 # usually those are non-interactive
554 return self._isatty(self.fin)
554 return self._isatty(self.fin)
555
555
556 return i
556 return i
557
557
558 def termwidth(self):
558 def termwidth(self):
559 '''how wide is the terminal in columns?
559 '''how wide is the terminal in columns?
560 '''
560 '''
561 if 'COLUMNS' in os.environ:
561 if 'COLUMNS' in os.environ:
562 try:
562 try:
563 return int(os.environ['COLUMNS'])
563 return int(os.environ['COLUMNS'])
564 except ValueError:
564 except ValueError:
565 pass
565 pass
566 return util.termwidth()
566 return util.termwidth()
567
567
568 def formatted(self):
568 def formatted(self):
569 '''should formatted output be used?
569 '''should formatted output be used?
570
570
571 It is often desirable to format the output to suite the output medium.
571 It is often desirable to format the output to suite the output medium.
572 Examples of this are truncating long lines or colorizing messages.
572 Examples of this are truncating long lines or colorizing messages.
573 However, this is not often not desirable when piping output into other
573 However, this is not often not desirable when piping output into other
574 utilities, e.g. `grep'.
574 utilities, e.g. `grep'.
575
575
576 Formatted output is triggered by the value of the `ui.formatted'
576 Formatted output is triggered by the value of the `ui.formatted'
577 configuration variable or - if it is unset - when `sys.stdout' points
577 configuration variable or - if it is unset - when `sys.stdout' points
578 to a terminal device. Please note that `ui.formatted' should be
578 to a terminal device. Please note that `ui.formatted' should be
579 considered an implementation detail; it is not intended for use outside
579 considered an implementation detail; it is not intended for use outside
580 Mercurial or its extensions.
580 Mercurial or its extensions.
581
581
582 This function refers to output only; for input, see `ui.interactive()'.
582 This function refers to output only; for input, see `ui.interactive()'.
583 This function always returns false when in plain mode, see `ui.plain()'.
583 This function always returns false when in plain mode, see `ui.plain()'.
584 '''
584 '''
585 if self.plain():
585 if self.plain():
586 return False
586 return False
587
587
588 i = self.configbool("ui", "formatted", None)
588 i = self.configbool("ui", "formatted", None)
589 if i is None:
589 if i is None:
590 # some environments replace stdout without implementing isatty
590 # some environments replace stdout without implementing isatty
591 # usually those are non-interactive
591 # usually those are non-interactive
592 return self._isatty(self.fout)
592 return self._isatty(self.fout)
593
593
594 return i
594 return i
595
595
596 def _readline(self, prompt=''):
596 def _readline(self, prompt=''):
597 if self._isatty(self.fin):
597 if self._isatty(self.fin):
598 try:
598 try:
599 # magically add command line editing support, where
599 # magically add command line editing support, where
600 # available
600 # available
601 import readline
601 import readline
602 # force demandimport to really load the module
602 # force demandimport to really load the module
603 readline.read_history_file
603 readline.read_history_file
604 # windows sometimes raises something other than ImportError
604 # windows sometimes raises something other than ImportError
605 except Exception:
605 except Exception:
606 pass
606 pass
607
607
608 # call write() so output goes through subclassed implementation
608 # call write() so output goes through subclassed implementation
609 # e.g. color extension on Windows
609 # e.g. color extension on Windows
610 self.write(prompt)
610 self.write(prompt)
611
611
612 # instead of trying to emulate raw_input, swap (self.fin,
612 # instead of trying to emulate raw_input, swap (self.fin,
613 # self.fout) with (sys.stdin, sys.stdout)
613 # self.fout) with (sys.stdin, sys.stdout)
614 oldin = sys.stdin
614 oldin = sys.stdin
615 oldout = sys.stdout
615 oldout = sys.stdout
616 sys.stdin = self.fin
616 sys.stdin = self.fin
617 sys.stdout = self.fout
617 sys.stdout = self.fout
618 line = raw_input(' ')
618 line = raw_input(' ')
619 sys.stdin = oldin
619 sys.stdin = oldin
620 sys.stdout = oldout
620 sys.stdout = oldout
621
621
622 # When stdin is in binary mode on Windows, it can cause
622 # When stdin is in binary mode on Windows, it can cause
623 # raw_input() to emit an extra trailing carriage return
623 # raw_input() to emit an extra trailing carriage return
624 if os.linesep == '\r\n' and line and line[-1] == '\r':
624 if os.linesep == '\r\n' and line and line[-1] == '\r':
625 line = line[:-1]
625 line = line[:-1]
626 return line
626 return line
627
627
628 def prompt(self, msg, default="y"):
628 def prompt(self, msg, default="y"):
629 """Prompt user with msg, read response.
629 """Prompt user with msg, read response.
630 If ui is not interactive, the default is returned.
630 If ui is not interactive, the default is returned.
631 """
631 """
632 if not self.interactive():
632 if not self.interactive():
633 self.write(msg, ' ', default, "\n")
633 self.write(msg, ' ', default, "\n")
634 return default
634 return default
635 try:
635 try:
636 r = self._readline(self.label(msg, 'ui.prompt'))
636 r = self._readline(self.label(msg, 'ui.prompt'))
637 if not r:
637 if not r:
638 return default
638 return default
639 return r
639 return r
640 except EOFError:
640 except EOFError:
641 raise util.Abort(_('response expected'))
641 raise util.Abort(_('response expected'))
642
642
643 @staticmethod
644 def extractchoices(prompt):
645 """Extract prompt message and list of choices from specified prompt.
646
647 This returns tuple "(message, choices)", and "choices" is the
648 list of tuple "(response character, text without &)".
649 """
650 parts = prompt.split('$$')
651 msg = parts[0].rstrip(' ')
652 choices = [p.strip(' ') for p in parts[1:]]
653 return (msg,
654 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
655 for s in choices])
656
643 def promptchoice(self, prompt, default=0):
657 def promptchoice(self, prompt, default=0):
644 """Prompt user with a message, read response, and ensure it matches
658 """Prompt user with a message, read response, and ensure it matches
645 one of the provided choices. The prompt is formatted as follows:
659 one of the provided choices. The prompt is formatted as follows:
646
660
647 "would you like fries with that (Yn)? $$ &Yes $$ &No"
661 "would you like fries with that (Yn)? $$ &Yes $$ &No"
648
662
649 The index of the choice is returned. Responses are case
663 The index of the choice is returned. Responses are case
650 insensitive. If ui is not interactive, the default is
664 insensitive. If ui is not interactive, the default is
651 returned.
665 returned.
652 """
666 """
653
667
654 parts = prompt.split('$$')
668 msg, choices = self.extractchoices(prompt)
655 msg = parts[0].rstrip(' ')
669 resps = [r for r, t in choices]
656 choices = [p.strip(' ') for p in parts[1:]]
657 resps = [s[s.index('&') + 1].lower() for s in choices]
658 while True:
670 while True:
659 r = self.prompt(msg, resps[default])
671 r = self.prompt(msg, resps[default])
660 if r.lower() in resps:
672 if r.lower() in resps:
661 return resps.index(r.lower())
673 return resps.index(r.lower())
662 self.write(_("unrecognized response\n"))
674 self.write(_("unrecognized response\n"))
663
675
664 def getpass(self, prompt=None, default=None):
676 def getpass(self, prompt=None, default=None):
665 if not self.interactive():
677 if not self.interactive():
666 return default
678 return default
667 try:
679 try:
668 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
680 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
669 return getpass.getpass('')
681 return getpass.getpass('')
670 except EOFError:
682 except EOFError:
671 raise util.Abort(_('response expected'))
683 raise util.Abort(_('response expected'))
672 def status(self, *msg, **opts):
684 def status(self, *msg, **opts):
673 '''write status message to output (if ui.quiet is False)
685 '''write status message to output (if ui.quiet is False)
674
686
675 This adds an output label of "ui.status".
687 This adds an output label of "ui.status".
676 '''
688 '''
677 if not self.quiet:
689 if not self.quiet:
678 opts['label'] = opts.get('label', '') + ' ui.status'
690 opts['label'] = opts.get('label', '') + ' ui.status'
679 self.write(*msg, **opts)
691 self.write(*msg, **opts)
680 def warn(self, *msg, **opts):
692 def warn(self, *msg, **opts):
681 '''write warning message to output (stderr)
693 '''write warning message to output (stderr)
682
694
683 This adds an output label of "ui.warning".
695 This adds an output label of "ui.warning".
684 '''
696 '''
685 opts['label'] = opts.get('label', '') + ' ui.warning'
697 opts['label'] = opts.get('label', '') + ' ui.warning'
686 self.write_err(*msg, **opts)
698 self.write_err(*msg, **opts)
687 def note(self, *msg, **opts):
699 def note(self, *msg, **opts):
688 '''write note to output (if ui.verbose is True)
700 '''write note to output (if ui.verbose is True)
689
701
690 This adds an output label of "ui.note".
702 This adds an output label of "ui.note".
691 '''
703 '''
692 if self.verbose:
704 if self.verbose:
693 opts['label'] = opts.get('label', '') + ' ui.note'
705 opts['label'] = opts.get('label', '') + ' ui.note'
694 self.write(*msg, **opts)
706 self.write(*msg, **opts)
695 def debug(self, *msg, **opts):
707 def debug(self, *msg, **opts):
696 '''write debug message to output (if ui.debugflag is True)
708 '''write debug message to output (if ui.debugflag is True)
697
709
698 This adds an output label of "ui.debug".
710 This adds an output label of "ui.debug".
699 '''
711 '''
700 if self.debugflag:
712 if self.debugflag:
701 opts['label'] = opts.get('label', '') + ' ui.debug'
713 opts['label'] = opts.get('label', '') + ' ui.debug'
702 self.write(*msg, **opts)
714 self.write(*msg, **opts)
703 def edit(self, text, user):
715 def edit(self, text, user):
704 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
716 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
705 text=True)
717 text=True)
706 try:
718 try:
707 f = os.fdopen(fd, "w")
719 f = os.fdopen(fd, "w")
708 f.write(text)
720 f.write(text)
709 f.close()
721 f.close()
710
722
711 editor = self.geteditor()
723 editor = self.geteditor()
712
724
713 util.system("%s \"%s\"" % (editor, name),
725 util.system("%s \"%s\"" % (editor, name),
714 environ={'HGUSER': user},
726 environ={'HGUSER': user},
715 onerr=util.Abort, errprefix=_("edit failed"),
727 onerr=util.Abort, errprefix=_("edit failed"),
716 out=self.fout)
728 out=self.fout)
717
729
718 f = open(name)
730 f = open(name)
719 t = f.read()
731 t = f.read()
720 f.close()
732 f.close()
721 finally:
733 finally:
722 os.unlink(name)
734 os.unlink(name)
723
735
724 return t
736 return t
725
737
726 def traceback(self, exc=None, force=False):
738 def traceback(self, exc=None, force=False):
727 '''print exception traceback if traceback printing enabled or forced.
739 '''print exception traceback if traceback printing enabled or forced.
728 only to call in exception handler. returns true if traceback
740 only to call in exception handler. returns true if traceback
729 printed.'''
741 printed.'''
730 if self.tracebackflag or force:
742 if self.tracebackflag or force:
731 if exc is None:
743 if exc is None:
732 exc = sys.exc_info()
744 exc = sys.exc_info()
733 cause = getattr(exc[1], 'cause', None)
745 cause = getattr(exc[1], 'cause', None)
734
746
735 if cause is not None:
747 if cause is not None:
736 causetb = traceback.format_tb(cause[2])
748 causetb = traceback.format_tb(cause[2])
737 exctb = traceback.format_tb(exc[2])
749 exctb = traceback.format_tb(exc[2])
738 exconly = traceback.format_exception_only(cause[0], cause[1])
750 exconly = traceback.format_exception_only(cause[0], cause[1])
739
751
740 # exclude frame where 'exc' was chained and rethrown from exctb
752 # exclude frame where 'exc' was chained and rethrown from exctb
741 self.write_err('Traceback (most recent call last):\n',
753 self.write_err('Traceback (most recent call last):\n',
742 ''.join(exctb[:-1]),
754 ''.join(exctb[:-1]),
743 ''.join(causetb),
755 ''.join(causetb),
744 ''.join(exconly))
756 ''.join(exconly))
745 else:
757 else:
746 traceback.print_exception(exc[0], exc[1], exc[2],
758 traceback.print_exception(exc[0], exc[1], exc[2],
747 file=self.ferr)
759 file=self.ferr)
748 return self.tracebackflag or force
760 return self.tracebackflag or force
749
761
750 def geteditor(self):
762 def geteditor(self):
751 '''return editor to use'''
763 '''return editor to use'''
752 if sys.platform == 'plan9':
764 if sys.platform == 'plan9':
753 # vi is the MIPS instruction simulator on Plan 9. We
765 # vi is the MIPS instruction simulator on Plan 9. We
754 # instead default to E to plumb commit messages to
766 # instead default to E to plumb commit messages to
755 # avoid confusion.
767 # avoid confusion.
756 editor = 'E'
768 editor = 'E'
757 else:
769 else:
758 editor = 'vi'
770 editor = 'vi'
759 return (os.environ.get("HGEDITOR") or
771 return (os.environ.get("HGEDITOR") or
760 self.config("ui", "editor") or
772 self.config("ui", "editor") or
761 os.environ.get("VISUAL") or
773 os.environ.get("VISUAL") or
762 os.environ.get("EDITOR", editor))
774 os.environ.get("EDITOR", editor))
763
775
764 def progress(self, topic, pos, item="", unit="", total=None):
776 def progress(self, topic, pos, item="", unit="", total=None):
765 '''show a progress message
777 '''show a progress message
766
778
767 With stock hg, this is simply a debug message that is hidden
779 With stock hg, this is simply a debug message that is hidden
768 by default, but with extensions or GUI tools it may be
780 by default, but with extensions or GUI tools it may be
769 visible. 'topic' is the current operation, 'item' is a
781 visible. 'topic' is the current operation, 'item' is a
770 non-numeric marker of the current position (i.e. the currently
782 non-numeric marker of the current position (i.e. the currently
771 in-process file), 'pos' is the current numeric position (i.e.
783 in-process file), 'pos' is the current numeric position (i.e.
772 revision, bytes, etc.), unit is a corresponding unit label,
784 revision, bytes, etc.), unit is a corresponding unit label,
773 and total is the highest expected pos.
785 and total is the highest expected pos.
774
786
775 Multiple nested topics may be active at a time.
787 Multiple nested topics may be active at a time.
776
788
777 All topics should be marked closed by setting pos to None at
789 All topics should be marked closed by setting pos to None at
778 termination.
790 termination.
779 '''
791 '''
780
792
781 if pos is None or not self.debugflag:
793 if pos is None or not self.debugflag:
782 return
794 return
783
795
784 if unit:
796 if unit:
785 unit = ' ' + unit
797 unit = ' ' + unit
786 if item:
798 if item:
787 item = ' ' + item
799 item = ' ' + item
788
800
789 if total:
801 if total:
790 pct = 100.0 * pos / total
802 pct = 100.0 * pos / total
791 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
803 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
792 % (topic, item, pos, total, unit, pct))
804 % (topic, item, pos, total, unit, pct))
793 else:
805 else:
794 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
806 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
795
807
796 def log(self, service, *msg, **opts):
808 def log(self, service, *msg, **opts):
797 '''hook for logging facility extensions
809 '''hook for logging facility extensions
798
810
799 service should be a readily-identifiable subsystem, which will
811 service should be a readily-identifiable subsystem, which will
800 allow filtering.
812 allow filtering.
801 message should be a newline-terminated string to log.
813 message should be a newline-terminated string to log.
802 '''
814 '''
803 pass
815 pass
804
816
805 def label(self, msg, label):
817 def label(self, msg, label):
806 '''style msg based on supplied label
818 '''style msg based on supplied label
807
819
808 Like ui.write(), this just returns msg unchanged, but extensions
820 Like ui.write(), this just returns msg unchanged, but extensions
809 and GUI tools can override it to allow styling output without
821 and GUI tools can override it to allow styling output without
810 writing it.
822 writing it.
811
823
812 ui.write(s, 'label') is equivalent to
824 ui.write(s, 'label') is equivalent to
813 ui.write(ui.label(s, 'label')).
825 ui.write(ui.label(s, 'label')).
814 '''
826 '''
815 return msg
827 return msg
General Comments 0
You need to be logged in to leave comments. Login now