comparison hgext/progress.py @ 10434:ad104a786d35

Progress bar extension
author Augie Fackler <durin42@gmail.com>
date Fri, 12 Feb 2010 21:53:32 -0600
parents
children 509f4ed56509
comparison
equal deleted inserted replaced
10433:767fbacb3ddc 10434:ad104a786d35
1 # progress.py show progress bars for some actions
2 #
3 # Copyright (C) 2010 Augie Fackler <durin42@gmail.com>
4 #
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
7 # Free Software Foundation; either version 2 of the License, or (at your
8 # option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13 # Public License for more details.
14 #
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.,
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
19 """show progress bars for some actions
20
21 This extension uses the progress information commands can log with hg
22 to draw progres bars that are as informative as possible. Some progress
23 bars only offer indeterminate information, while others have a definite
24 end point.
25
26 The following settings are available::
27
28 [progress]
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
31 format = topic bar number # format of the progress bar
32 width = <none> # if set, the maximum width of the progress information
33 # (that is, min(width, term width) will be used)
34 clear-complete = True # clear the progress bar after it's done
35
36 Valid entries for the format field are topic, bar, number, unit, and item.
37 item defaults to the last 20 characters of the item, but this can be
38 changed by adding either -<num> which would take the last num characters,
39 or +<num> for the first num characters.
40 """
41
42 import math
43 import sys
44 import time
45
46 from mercurial import extensions
47 from mercurial import util
48
49 def spacejoin(*args):
50 ret = ''
51 for s in args:
52 if s:
53 if ret:
54 ret += ' '
55 ret += s
56 return ret
57
58 class progbar(object):
59 def __init__(self, ui):
60 self.ui = ui
61 self.resetstate()
62
63 def resetstate(self):
64 self.topics = []
65 self.printed = False
66 self.lastprint = time.time() + float(self.ui.config(
67 'progress', 'delay', default=3))
68 self.indetcount = 0
69 self.refresh = float(self.ui.config(
70 'progress', 'refresh', default=0.1))
71 self.order = self.ui.configlist(
72 'progress', 'format',
73 default=['topic', 'bar', 'number'])
74
75 def show(self, topic, pos, item, unit, total):
76 termwidth = self.width()
77 self.printed = True
78 head = ''
79 needprogress = False
80 tail = ''
81 for indicator in self.order:
82 add = ''
83 if indicator == 'topic':
84 add = topic
85 elif indicator == 'number':
86 if total:
87 add = ('% ' + str(len(str(total))) +
88 's/%s') % (pos, total)
89 else:
90 add = str(pos)
91 elif indicator.startswith('item') and item:
92 slice = 'end'
93 if '-' in indicator:
94 wid = int(indicator.split('-')[1])
95 elif '+' in indicator:
96 slice = 'beginning'
97 wid = int(indicator.split('+')[1])
98 else:
99 wid = 20
100 if slice == 'end':
101 add = item[-wid:]
102 else:
103 add = item[:wid]
104 add += (wid - len(add)) * ' '
105 elif indicator == 'bar':
106 add = ''
107 needprogress = True
108 elif indicator == 'unit' and unit:
109 add = unit
110 if not needprogress:
111 head = spacejoin(head, add)
112 else:
113 tail = spacejoin(add, tail)
114 if needprogress:
115 used = 0
116 if head:
117 used += len(head) + 1
118 if tail:
119 used += len(tail) + 1
120 progwidth = termwidth - used - 3
121 if total:
122 amt = pos * progwidth // total
123 bar = '=' * (amt) + ' ' * (progwidth - amt)
124 else:
125 progwidth -= 3
126 self.indetcount += 1
127 # mod the count by twice the width so we can make the
128 # cursor bounce between the right and left sides
129 amt = self.indetcount % (2 * progwidth)
130 amt -= progwidth
131 bar = (' ' * int(progwidth - abs(amt)) + '<=>' +
132 ' ' * int(abs(amt)))
133 prog = ''.join(('[', bar , ']'))
134 out = spacejoin(head, prog, tail)
135 else:
136 out = spacejoin(head, tail)
137 sys.stdout.write('\r' + out[:termwidth])
138 sys.stdout.flush()
139
140 def clear(self):
141 sys.stdout.write('\r%s\r' % (' ' * self.width()))
142
143 def width(self):
144 tw = util.termwidth()
145 return min(int(self.ui.config('progress', 'width', default=tw)), tw)
146
147 def progress(self, orig, topic, pos, item='', unit='', total=None):
148 now = time.time()
149 if pos is None and self.topics[-1] == topic and self.printed:
150 if self.ui.configbool('progress', 'clear-complete', default=True):
151 self.clear()
152 else:
153 sys.stdout.write('\n')
154 sys.stdout.flush()
155 self.resetstate()
156 elif topic not in self.topics:
157 self.topics.append(topic)
158
159 if now - self.lastprint > 0.1 and topic == self.topics[-1]:
160 self.lastprint = now
161 self.show(topic, pos, item, unit, total)
162 return orig(topic, pos, item=item, unit=unit, total=total)
163
164 def write(self, orig, *args):
165 if self.printed:
166 self.clear()
167 return orig(*args)
168
169 sharedprog = None
170
171 def uisetup(ui):
172 if ui.interactive() and not ui.debugflag:
173 # we instantiate one globally shared progress bar to avoid
174 # competing progress bars when multiple UI objects get created
175 global sharedprog
176 if not sharedprog:
177 sharedprog = progbar(ui)
178 extensions.wrapfunction(ui, 'progress', sharedprog.progress)
179 extensions.wrapfunction(ui, 'write', sharedprog.write)
180
181 def reposetup(ui, repo):
182 uisetup(repo.ui)