##// END OF EJS Templates
progress: respect HGPLAIN
Matt Mackall -
r19404:b4744c3b default
parent child Browse files
Show More
@@ -1,295 +1,296 b''
1 1 # progress.py show progress bars for some actions
2 2 #
3 3 # Copyright (C) 2010 Augie Fackler <durin42@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 """show progress bars for some actions
9 9
10 10 This extension uses the progress information logged by hg commands
11 11 to draw progress bars that are as informative as possible. Some progress
12 12 bars only offer indeterminate information, while others have a definite
13 13 end point.
14 14
15 15 The following settings are available::
16 16
17 17 [progress]
18 18 delay = 3 # number of seconds (float) before showing the progress bar
19 19 changedelay = 1 # changedelay: minimum delay before showing a new topic.
20 20 # If set to less than 3 * refresh, that value will
21 21 # be used instead.
22 22 refresh = 0.1 # time in seconds between refreshes of the progress bar
23 23 format = topic bar number estimate # format of the progress bar
24 24 width = <none> # if set, the maximum width of the progress information
25 25 # (that is, min(width, term width) will be used)
26 26 clear-complete = True # clear the progress bar after it's done
27 27 disable = False # if true, don't show a progress bar
28 28 assume-tty = False # if true, ALWAYS show a progress bar, unless
29 29 # disable is given
30 30
31 31 Valid entries for the format field are topic, bar, number, unit,
32 32 estimate, speed, and item. item defaults to the last 20 characters of
33 33 the item, but this can be changed by adding either ``-<num>`` which
34 34 would take the last num characters, or ``+<num>`` for the first num
35 35 characters.
36 36 """
37 37
38 38 import sys
39 39 import time
40 40
41 41 from mercurial.i18n import _
42 42 testedwith = 'internal'
43 43
44 44 def spacejoin(*args):
45 45 return ' '.join(s for s in args if s)
46 46
47 47 def shouldprint(ui):
48 return ui._isatty(sys.stderr) or ui.configbool('progress', 'assume-tty')
48 return not ui.plain() and (ui._isatty(sys.stderr) or
49 ui.configbool('progress', 'assume-tty'))
49 50
50 51 def fmtremaining(seconds):
51 52 if seconds < 60:
52 53 # i18n: format XX seconds as "XXs"
53 54 return _("%02ds") % (seconds)
54 55 minutes = seconds // 60
55 56 if minutes < 60:
56 57 seconds -= minutes * 60
57 58 # i18n: format X minutes and YY seconds as "XmYYs"
58 59 return _("%dm%02ds") % (minutes, seconds)
59 60 # we're going to ignore seconds in this case
60 61 minutes += 1
61 62 hours = minutes // 60
62 63 minutes -= hours * 60
63 64 if hours < 30:
64 65 # i18n: format X hours and YY minutes as "XhYYm"
65 66 return _("%dh%02dm") % (hours, minutes)
66 67 # we're going to ignore minutes in this case
67 68 hours += 1
68 69 days = hours // 24
69 70 hours -= days * 24
70 71 if days < 15:
71 72 # i18n: format X days and YY hours as "XdYYh"
72 73 return _("%dd%02dh") % (days, hours)
73 74 # we're going to ignore hours in this case
74 75 days += 1
75 76 weeks = days // 7
76 77 days -= weeks * 7
77 78 if weeks < 55:
78 79 # i18n: format X weeks and YY days as "XwYYd"
79 80 return _("%dw%02dd") % (weeks, days)
80 81 # we're going to ignore days and treat a year as 52 weeks
81 82 weeks += 1
82 83 years = weeks // 52
83 84 weeks -= years * 52
84 85 # i18n: format X years and YY weeks as "XyYYw"
85 86 return _("%dy%02dw") % (years, weeks)
86 87
87 88 class progbar(object):
88 89 def __init__(self, ui):
89 90 self.ui = ui
90 91 self.resetstate()
91 92
92 93 def resetstate(self):
93 94 self.topics = []
94 95 self.topicstates = {}
95 96 self.starttimes = {}
96 97 self.startvals = {}
97 98 self.printed = False
98 99 self.lastprint = time.time() + float(self.ui.config(
99 100 'progress', 'delay', default=3))
100 101 self.lasttopic = None
101 102 self.indetcount = 0
102 103 self.refresh = float(self.ui.config(
103 104 'progress', 'refresh', default=0.1))
104 105 self.changedelay = max(3 * self.refresh,
105 106 float(self.ui.config(
106 107 'progress', 'changedelay', default=1)))
107 108 self.order = self.ui.configlist(
108 109 'progress', 'format',
109 110 default=['topic', 'bar', 'number', 'estimate'])
110 111
111 112 def show(self, now, topic, pos, item, unit, total):
112 113 if not shouldprint(self.ui):
113 114 return
114 115 termwidth = self.width()
115 116 self.printed = True
116 117 head = ''
117 118 needprogress = False
118 119 tail = ''
119 120 for indicator in self.order:
120 121 add = ''
121 122 if indicator == 'topic':
122 123 add = topic
123 124 elif indicator == 'number':
124 125 if total:
125 126 add = ('% ' + str(len(str(total))) +
126 127 's/%s') % (pos, total)
127 128 else:
128 129 add = str(pos)
129 130 elif indicator.startswith('item') and item:
130 131 slice = 'end'
131 132 if '-' in indicator:
132 133 wid = int(indicator.split('-')[1])
133 134 elif '+' in indicator:
134 135 slice = 'beginning'
135 136 wid = int(indicator.split('+')[1])
136 137 else:
137 138 wid = 20
138 139 if slice == 'end':
139 140 add = item[-wid:]
140 141 else:
141 142 add = item[:wid]
142 143 add += (wid - len(add)) * ' '
143 144 elif indicator == 'bar':
144 145 add = ''
145 146 needprogress = True
146 147 elif indicator == 'unit' and unit:
147 148 add = unit
148 149 elif indicator == 'estimate':
149 150 add = self.estimate(topic, pos, total, now)
150 151 elif indicator == 'speed':
151 152 add = self.speed(topic, pos, unit, now)
152 153 if not needprogress:
153 154 head = spacejoin(head, add)
154 155 else:
155 156 tail = spacejoin(tail, add)
156 157 if needprogress:
157 158 used = 0
158 159 if head:
159 160 used += len(head) + 1
160 161 if tail:
161 162 used += len(tail) + 1
162 163 progwidth = termwidth - used - 3
163 164 if total and pos <= total:
164 165 amt = pos * progwidth // total
165 166 bar = '=' * (amt - 1)
166 167 if amt > 0:
167 168 bar += '>'
168 169 bar += ' ' * (progwidth - amt)
169 170 else:
170 171 progwidth -= 3
171 172 self.indetcount += 1
172 173 # mod the count by twice the width so we can make the
173 174 # cursor bounce between the right and left sides
174 175 amt = self.indetcount % (2 * progwidth)
175 176 amt -= progwidth
176 177 bar = (' ' * int(progwidth - abs(amt)) + '<=>' +
177 178 ' ' * int(abs(amt)))
178 179 prog = ''.join(('[', bar , ']'))
179 180 out = spacejoin(head, prog, tail)
180 181 else:
181 182 out = spacejoin(head, tail)
182 183 sys.stderr.write('\r' + out[:termwidth])
183 184 self.lasttopic = topic
184 185 sys.stderr.flush()
185 186
186 187 def clear(self):
187 188 if not shouldprint(self.ui):
188 189 return
189 190 sys.stderr.write('\r%s\r' % (' ' * self.width()))
190 191
191 192 def complete(self):
192 193 if not shouldprint(self.ui):
193 194 return
194 195 if self.ui.configbool('progress', 'clear-complete', default=True):
195 196 self.clear()
196 197 else:
197 198 sys.stderr.write('\n')
198 199 sys.stderr.flush()
199 200
200 201 def width(self):
201 202 tw = self.ui.termwidth()
202 203 return min(int(self.ui.config('progress', 'width', default=tw)), tw)
203 204
204 205 def estimate(self, topic, pos, total, now):
205 206 if total is None:
206 207 return ''
207 208 initialpos = self.startvals[topic]
208 209 target = total - initialpos
209 210 delta = pos - initialpos
210 211 if delta > 0:
211 212 elapsed = now - self.starttimes[topic]
212 213 if elapsed > float(
213 214 self.ui.config('progress', 'estimate', default=2)):
214 215 seconds = (elapsed * (target - delta)) // delta + 1
215 216 return fmtremaining(seconds)
216 217 return ''
217 218
218 219 def speed(self, topic, pos, unit, now):
219 220 initialpos = self.startvals[topic]
220 221 delta = pos - initialpos
221 222 elapsed = now - self.starttimes[topic]
222 223 if elapsed > float(
223 224 self.ui.config('progress', 'estimate', default=2)):
224 225 return _('%d %s/sec') % (delta / elapsed, unit)
225 226 return ''
226 227
227 228 def progress(self, topic, pos, item='', unit='', total=None):
228 229 now = time.time()
229 230 if pos is None:
230 231 self.starttimes.pop(topic, None)
231 232 self.startvals.pop(topic, None)
232 233 self.topicstates.pop(topic, None)
233 234 # reset the progress bar if this is the outermost topic
234 235 if self.topics and self.topics[0] == topic and self.printed:
235 236 self.complete()
236 237 self.resetstate()
237 238 # truncate the list of topics assuming all topics within
238 239 # this one are also closed
239 240 if topic in self.topics:
240 241 self.topics = self.topics[:self.topics.index(topic)]
241 242 else:
242 243 if topic not in self.topics:
243 244 self.starttimes[topic] = now
244 245 self.startvals[topic] = pos
245 246 self.topics.append(topic)
246 247 self.topicstates[topic] = pos, item, unit, total
247 248 if now - self.lastprint >= self.refresh and self.topics:
248 249 if (self.lasttopic is None # first time we printed
249 250 # not a topic change
250 251 or topic == self.lasttopic
251 252 # it's been long enough we should print anyway
252 253 or now - self.lastprint >= self.changedelay):
253 254 self.lastprint = now
254 255 self.show(now, topic, *self.topicstates[topic])
255 256
256 257 _singleton = None
257 258
258 259 def uisetup(ui):
259 260 global _singleton
260 261 class progressui(ui.__class__):
261 262 _progbar = None
262 263
263 264 def _quiet(self):
264 265 return self.debugflag or self.quiet
265 266
266 267 def progress(self, *args, **opts):
267 268 if not self._quiet():
268 269 self._progbar.progress(*args, **opts)
269 270 return super(progressui, self).progress(*args, **opts)
270 271
271 272 def write(self, *args, **opts):
272 273 if not self._quiet() and self._progbar.printed:
273 274 self._progbar.clear()
274 275 return super(progressui, self).write(*args, **opts)
275 276
276 277 def write_err(self, *args, **opts):
277 278 if not self._quiet() and self._progbar.printed:
278 279 self._progbar.clear()
279 280 return super(progressui, self).write_err(*args, **opts)
280 281
281 282 # Apps that derive a class from ui.ui() can use
282 283 # setconfig('progress', 'disable', 'True') to disable this extension
283 284 if ui.configbool('progress', 'disable'):
284 285 return
285 286 if shouldprint(ui) and not ui.debugflag and not ui.quiet:
286 287 ui.__class__ = progressui
287 288 # we instantiate one globally shared progress bar to avoid
288 289 # competing progress bars when multiple UI objects get created
289 290 if not progressui._progbar:
290 291 if _singleton is None:
291 292 _singleton = progbar(ui)
292 293 progressui._progbar = _singleton
293 294
294 295 def reposetup(ui, repo):
295 296 uisetup(repo.ui)
General Comments 0
You need to be logged in to leave comments. Login now