##// END OF EJS Templates
progress: explain format strings to translators
Martin Geisler -
r13139:f4dd6aa1 default
parent child Browse files
Show More
@@ -1,245 +1,248 b''
1 # progress.py show progress bars for some actions
1 # progress.py show progress bars for some actions
2 #
2 #
3 # Copyright (C) 2010 Augie Fackler <durin42@gmail.com>
3 # Copyright (C) 2010 Augie Fackler <durin42@gmail.com>
4 #
4 #
5 # This program is free software; you can redistribute it and/or modify it
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the
6 # under the terms of the GNU General Public License as published by the
7 # Free Software Foundation; either version 2 of the License, or (at your
7 # Free Software Foundation; either version 2 of the License, or (at your
8 # option) any later version.
8 # option) any later version.
9 #
9 #
10 # This program is distributed in the hope that it will be useful, but
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13 # Public License for more details.
13 # Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License along
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc.,
16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
18
19 """show progress bars for some actions
19 """show progress bars for some actions
20
20
21 This extension uses the progress information logged by hg commands
21 This extension uses the progress information logged by hg commands
22 to draw progress bars that are as informative as possible. Some progress
22 to draw progress bars that are as informative as possible. Some progress
23 bars only offer indeterminate information, while others have a definite
23 bars only offer indeterminate information, while others have a definite
24 end point.
24 end point.
25
25
26 The following settings are available::
26 The following settings are available::
27
27
28 [progress]
28 [progress]
29 delay = 3 # number of seconds (float) before showing the progress bar
29 delay = 3 # number of seconds (float) before showing the progress bar
30 refresh = 0.1 # time in seconds between refreshes of the progress bar
30 refresh = 0.1 # time in seconds between refreshes of the progress bar
31 format = topic bar number # format of the progress bar
31 format = topic bar number # format of the progress bar
32 width = <none> # if set, the maximum width of the progress information
32 width = <none> # if set, the maximum width of the progress information
33 # (that is, min(width, term width) will be used)
33 # (that is, min(width, term width) will be used)
34 clear-complete = True # clear the progress bar after it's done
34 clear-complete = True # clear the progress bar after it's done
35 disable = False # if true, don't show a progress bar
35 disable = False # if true, don't show a progress bar
36 assume-tty = False # if true, ALWAYS show a progress bar, unless
36 assume-tty = False # if true, ALWAYS show a progress bar, unless
37 # disable is given
37 # disable is given
38
38
39 Valid entries for the format field are topic, bar, number, unit, and
39 Valid entries for the format field are topic, bar, number, unit, and
40 item. item defaults to the last 20 characters of the item, but this
40 item. item defaults to the last 20 characters of the item, but this
41 can be changed by adding either ``-<num>`` which would take the last
41 can be changed by adding either ``-<num>`` which would take the last
42 num characters, or ``+<num>`` for the first num characters.
42 num characters, or ``+<num>`` for the first num characters.
43 """
43 """
44
44
45 import sys
45 import sys
46 import time
46 import time
47
47
48 from mercurial.i18n import _
48 from mercurial.i18n import _
49 from mercurial import util
49 from mercurial import util
50
50
51 def spacejoin(*args):
51 def spacejoin(*args):
52 return ' '.join(s for s in args if s)
52 return ' '.join(s for s in args if s)
53
53
54 def shouldprint(ui):
54 def shouldprint(ui):
55 return (getattr(sys.stderr, 'isatty', None) and
55 return (getattr(sys.stderr, 'isatty', None) and
56 (sys.stderr.isatty() or ui.configbool('progress', 'assume-tty')))
56 (sys.stderr.isatty() or ui.configbool('progress', 'assume-tty')))
57
57
58 def fmtremaining(seconds):
58 def fmtremaining(seconds):
59 if seconds < 60:
59 if seconds < 60:
60 # i18n: format XX seconds as "XXs"
60 return _("%02ds") % (seconds)
61 return _("%02ds") % (seconds)
61 minutes = seconds // 60
62 minutes = seconds // 60
62 if minutes < 60:
63 if minutes < 60:
63 seconds -= minutes * 60
64 seconds -= minutes * 60
65 # i18n: format X minutes and YY seconds as "XmYYs"
64 return _("%dm%02ds") % (minutes, seconds)
66 return _("%dm%02ds") % (minutes, seconds)
65 # we're going to ignore seconds in this case
67 # we're going to ignore seconds in this case
66 minutes += 1
68 minutes += 1
67 hours = minutes // 60
69 hours = minutes // 60
68 minutes -= hours * 60
70 minutes -= hours * 60
71 # i18n: format X hours and YY minutes as "XhYYm"
69 return _("%dh%02dm") % (hours, minutes)
72 return _("%dh%02dm") % (hours, minutes)
70
73
71 class progbar(object):
74 class progbar(object):
72 def __init__(self, ui):
75 def __init__(self, ui):
73 self.ui = ui
76 self.ui = ui
74 self.resetstate()
77 self.resetstate()
75
78
76 def resetstate(self):
79 def resetstate(self):
77 self.topics = []
80 self.topics = []
78 self.topicstates = {}
81 self.topicstates = {}
79 self.starttimes = {}
82 self.starttimes = {}
80 self.startvals = {}
83 self.startvals = {}
81 self.printed = False
84 self.printed = False
82 self.lastprint = time.time() + float(self.ui.config(
85 self.lastprint = time.time() + float(self.ui.config(
83 'progress', 'delay', default=3))
86 'progress', 'delay', default=3))
84 self.indetcount = 0
87 self.indetcount = 0
85 self.refresh = float(self.ui.config(
88 self.refresh = float(self.ui.config(
86 'progress', 'refresh', default=0.1))
89 'progress', 'refresh', default=0.1))
87 self.order = self.ui.configlist(
90 self.order = self.ui.configlist(
88 'progress', 'format',
91 'progress', 'format',
89 default=['topic', 'bar', 'number'])
92 default=['topic', 'bar', 'number'])
90
93
91 def show(self, now, topic, pos, item, unit, total):
94 def show(self, now, topic, pos, item, unit, total):
92 if not shouldprint(self.ui):
95 if not shouldprint(self.ui):
93 return
96 return
94 termwidth = self.width()
97 termwidth = self.width()
95 self.printed = True
98 self.printed = True
96 head = ''
99 head = ''
97 needprogress = False
100 needprogress = False
98 tail = ''
101 tail = ''
99 for indicator in self.order:
102 for indicator in self.order:
100 add = ''
103 add = ''
101 if indicator == 'topic':
104 if indicator == 'topic':
102 add = topic
105 add = topic
103 elif indicator == 'number':
106 elif indicator == 'number':
104 if total:
107 if total:
105 add = ('% ' + str(len(str(total))) +
108 add = ('% ' + str(len(str(total))) +
106 's/%s') % (pos, total)
109 's/%s') % (pos, total)
107 else:
110 else:
108 add = str(pos)
111 add = str(pos)
109 elif indicator.startswith('item') and item:
112 elif indicator.startswith('item') and item:
110 slice = 'end'
113 slice = 'end'
111 if '-' in indicator:
114 if '-' in indicator:
112 wid = int(indicator.split('-')[1])
115 wid = int(indicator.split('-')[1])
113 elif '+' in indicator:
116 elif '+' in indicator:
114 slice = 'beginning'
117 slice = 'beginning'
115 wid = int(indicator.split('+')[1])
118 wid = int(indicator.split('+')[1])
116 else:
119 else:
117 wid = 20
120 wid = 20
118 if slice == 'end':
121 if slice == 'end':
119 add = item[-wid:]
122 add = item[-wid:]
120 else:
123 else:
121 add = item[:wid]
124 add = item[:wid]
122 add += (wid - len(add)) * ' '
125 add += (wid - len(add)) * ' '
123 elif indicator == 'bar':
126 elif indicator == 'bar':
124 add = ''
127 add = ''
125 needprogress = True
128 needprogress = True
126 elif indicator == 'unit' and unit:
129 elif indicator == 'unit' and unit:
127 add = unit
130 add = unit
128 if not needprogress:
131 if not needprogress:
129 head = spacejoin(head, add)
132 head = spacejoin(head, add)
130 else:
133 else:
131 tail = spacejoin(add, tail)
134 tail = spacejoin(add, tail)
132 if needprogress:
135 if needprogress:
133 used = 0
136 used = 0
134 if head:
137 if head:
135 used += len(head) + 1
138 used += len(head) + 1
136 if tail:
139 if tail:
137 used += len(tail) + 1
140 used += len(tail) + 1
138 progwidth = termwidth - used - 3
141 progwidth = termwidth - used - 3
139 if total and pos <= total:
142 if total and pos <= total:
140 initial = self.startvals[topic]
143 initial = self.startvals[topic]
141 target = total - initial
144 target = total - initial
142 delta = pos - initial
145 delta = pos - initial
143 if delta > 0:
146 if delta > 0:
144 elapsed = now - self.starttimes[topic]
147 elapsed = now - self.starttimes[topic]
145 if elapsed > float(
148 if elapsed > float(
146 self.ui.config('progress', 'estimate', default=2)):
149 self.ui.config('progress', 'estimate', default=2)):
147 seconds = (elapsed * (target - delta)) // delta + 1
150 seconds = (elapsed * (target - delta)) // delta + 1
148 remaining = fmtremaining(seconds)
151 remaining = fmtremaining(seconds)
149 progwidth -= len(remaining) + 1
152 progwidth -= len(remaining) + 1
150 tail = spacejoin(tail, remaining)
153 tail = spacejoin(tail, remaining)
151 amt = pos * progwidth // total
154 amt = pos * progwidth // total
152 bar = '=' * (amt - 1)
155 bar = '=' * (amt - 1)
153 if amt > 0:
156 if amt > 0:
154 bar += '>'
157 bar += '>'
155 bar += ' ' * (progwidth - amt)
158 bar += ' ' * (progwidth - amt)
156 else:
159 else:
157 progwidth -= 3
160 progwidth -= 3
158 self.indetcount += 1
161 self.indetcount += 1
159 # mod the count by twice the width so we can make the
162 # mod the count by twice the width so we can make the
160 # cursor bounce between the right and left sides
163 # cursor bounce between the right and left sides
161 amt = self.indetcount % (2 * progwidth)
164 amt = self.indetcount % (2 * progwidth)
162 amt -= progwidth
165 amt -= progwidth
163 bar = (' ' * int(progwidth - abs(amt)) + '<=>' +
166 bar = (' ' * int(progwidth - abs(amt)) + '<=>' +
164 ' ' * int(abs(amt)))
167 ' ' * int(abs(amt)))
165 prog = ''.join(('[', bar , ']'))
168 prog = ''.join(('[', bar , ']'))
166 out = spacejoin(head, prog, tail)
169 out = spacejoin(head, prog, tail)
167 else:
170 else:
168 out = spacejoin(head, tail)
171 out = spacejoin(head, tail)
169 sys.stderr.write('\r' + out[:termwidth])
172 sys.stderr.write('\r' + out[:termwidth])
170 sys.stderr.flush()
173 sys.stderr.flush()
171
174
172 def clear(self):
175 def clear(self):
173 if not shouldprint(self.ui):
176 if not shouldprint(self.ui):
174 return
177 return
175 sys.stderr.write('\r%s\r' % (' ' * self.width()))
178 sys.stderr.write('\r%s\r' % (' ' * self.width()))
176
179
177 def complete(self):
180 def complete(self):
178 if not shouldprint(self.ui):
181 if not shouldprint(self.ui):
179 return
182 return
180 if self.ui.configbool('progress', 'clear-complete', default=True):
183 if self.ui.configbool('progress', 'clear-complete', default=True):
181 self.clear()
184 self.clear()
182 else:
185 else:
183 sys.stderr.write('\n')
186 sys.stderr.write('\n')
184 sys.stderr.flush()
187 sys.stderr.flush()
185
188
186 def width(self):
189 def width(self):
187 tw = self.ui.termwidth()
190 tw = self.ui.termwidth()
188 return min(int(self.ui.config('progress', 'width', default=tw)), tw)
191 return min(int(self.ui.config('progress', 'width', default=tw)), tw)
189
192
190 def progress(self, topic, pos, item='', unit='', total=None):
193 def progress(self, topic, pos, item='', unit='', total=None):
191 now = time.time()
194 now = time.time()
192 if pos is None:
195 if pos is None:
193 self.starttimes.pop(topic, None)
196 self.starttimes.pop(topic, None)
194 self.startvals.pop(topic, None)
197 self.startvals.pop(topic, None)
195 self.topicstates.pop(topic, None)
198 self.topicstates.pop(topic, None)
196 # reset the progress bar if this is the outermost topic
199 # reset the progress bar if this is the outermost topic
197 if self.topics and self.topics[0] == topic and self.printed:
200 if self.topics and self.topics[0] == topic and self.printed:
198 self.complete()
201 self.complete()
199 self.resetstate()
202 self.resetstate()
200 # truncate the list of topics assuming all topics within
203 # truncate the list of topics assuming all topics within
201 # this one are also closed
204 # this one are also closed
202 if topic in self.topics:
205 if topic in self.topics:
203 self.topics = self.topics[:self.topics.index(topic)]
206 self.topics = self.topics[:self.topics.index(topic)]
204 else:
207 else:
205 if topic not in self.topics:
208 if topic not in self.topics:
206 self.starttimes[topic] = now
209 self.starttimes[topic] = now
207 self.startvals[topic] = pos
210 self.startvals[topic] = pos
208 self.topics.append(topic)
211 self.topics.append(topic)
209 self.topicstates[topic] = pos, item, unit, total
212 self.topicstates[topic] = pos, item, unit, total
210 if now - self.lastprint >= self.refresh and self.topics:
213 if now - self.lastprint >= self.refresh and self.topics:
211 self.lastprint = now
214 self.lastprint = now
212 current = self.topics[-1]
215 current = self.topics[-1]
213 self.show(now, topic, *self.topicstates[topic])
216 self.show(now, topic, *self.topicstates[topic])
214
217
215 def uisetup(ui):
218 def uisetup(ui):
216 class progressui(ui.__class__):
219 class progressui(ui.__class__):
217 _progbar = None
220 _progbar = None
218
221
219 def progress(self, *args, **opts):
222 def progress(self, *args, **opts):
220 self._progbar.progress(*args, **opts)
223 self._progbar.progress(*args, **opts)
221 return super(progressui, self).progress(*args, **opts)
224 return super(progressui, self).progress(*args, **opts)
222
225
223 def write(self, *args, **opts):
226 def write(self, *args, **opts):
224 if self._progbar.printed:
227 if self._progbar.printed:
225 self._progbar.clear()
228 self._progbar.clear()
226 return super(progressui, self).write(*args, **opts)
229 return super(progressui, self).write(*args, **opts)
227
230
228 def write_err(self, *args, **opts):
231 def write_err(self, *args, **opts):
229 if self._progbar.printed:
232 if self._progbar.printed:
230 self._progbar.clear()
233 self._progbar.clear()
231 return super(progressui, self).write_err(*args, **opts)
234 return super(progressui, self).write_err(*args, **opts)
232
235
233 # Apps that derive a class from ui.ui() can use
236 # Apps that derive a class from ui.ui() can use
234 # setconfig('progress', 'disable', 'True') to disable this extension
237 # setconfig('progress', 'disable', 'True') to disable this extension
235 if ui.configbool('progress', 'disable'):
238 if ui.configbool('progress', 'disable'):
236 return
239 return
237 if shouldprint(ui) and not ui.debugflag and not ui.quiet:
240 if shouldprint(ui) and not ui.debugflag and not ui.quiet:
238 ui.__class__ = progressui
241 ui.__class__ = progressui
239 # we instantiate one globally shared progress bar to avoid
242 # we instantiate one globally shared progress bar to avoid
240 # competing progress bars when multiple UI objects get created
243 # competing progress bars when multiple UI objects get created
241 if not progressui._progbar:
244 if not progressui._progbar:
242 progressui._progbar = progbar(ui)
245 progressui._progbar = progbar(ui)
243
246
244 def reposetup(ui, repo):
247 def reposetup(ui, repo):
245 uisetup(repo.ui)
248 uisetup(repo.ui)
General Comments 0
You need to be logged in to leave comments. Login now