Mercurial > hg
annotate hgext/progress.py @ 10815:32b213b9b22c
ui: add ui.write() output labeling API
This adds output labeling support with the following methods:
- ui.write(..., label='topic.name topic2.name2 ...')
- ui.write_err(.., label=...)
- ui.popbuffer(labeled=False)
- ui.label(msg, label)
By adding an API to label output directly, the color extension can forgo
parsing command output and instead override the above methods to insert
ANSI color codes. GUI tools can also override the above methods and use
the labels to do GUI-specific styling.
popbuffer gains a labeled argument that, when set to True, returns its
buffered output with labels handled. In the case of the color extension,
this would return output with color codes embedded. For existing users
that use this method to capture and parse output, labels are discarded
and output returned as normal when labeled is False (the default).
Existing wrappers of ui.write() and ui.write_err() should make sure to
accept its new **opts argument.
author | Brodie Rao <brodie@bitheap.org> |
---|---|
date | Fri, 02 Apr 2010 15:22:00 -0500 |
parents | ca6ba6cac6cd |
children | 83af68e38be3 |
rev | line source |
---|---|
10434 | 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 | |
10450 | 21 This extension uses the progress information logged by hg commands |
22 to draw progress bars that are as informative as possible. Some progress | |
10434 | 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 | |
10656
f6ee02933af9
progress: document progress.disable config option
Augie Fackler <durin42@gmail.com>
parents:
10594
diff
changeset
|
35 disable = False # if true, don't show a progress bar |
10788
ca6ba6cac6cd
progress: use stderr instead of stdout; check stderr.isatty()
Augie Fackler <durin42@gmail.com>
parents:
10656
diff
changeset
|
36 assume-tty = False # if true, ALWAYS show a progress bar, unless |
ca6ba6cac6cd
progress: use stderr instead of stdout; check stderr.isatty()
Augie Fackler <durin42@gmail.com>
parents:
10656
diff
changeset
|
37 # disable is given |
10434 | 38 |
10471
132eb7128ad5
progress: use inline literals in help string
Martin Geisler <mg@lazybytes.net>
parents:
10464
diff
changeset
|
39 Valid entries for the format field are topic, bar, number, unit, and |
132eb7128ad5
progress: use inline literals in help string
Martin Geisler <mg@lazybytes.net>
parents:
10464
diff
changeset
|
40 item. item defaults to the last 20 characters of the item, but this |
132eb7128ad5
progress: use inline literals in help string
Martin Geisler <mg@lazybytes.net>
parents:
10464
diff
changeset
|
41 can be changed by adding either ``-<num>`` which would take the last |
132eb7128ad5
progress: use inline literals in help string
Martin Geisler <mg@lazybytes.net>
parents:
10464
diff
changeset
|
42 num characters, or ``+<num>`` for the first num characters. |
10434 | 43 """ |
44 | |
45 import sys | |
46 import time | |
47 | |
48 from mercurial import extensions | |
49 from mercurial import util | |
50 | |
51 def spacejoin(*args): | |
10452
59f8fff4f887
progress: simplify spacejoin()
Brodie Rao <me+hg@dackz.net>
parents:
10450
diff
changeset
|
52 return ' '.join(s for s in args if s) |
10434 | 53 |
54 class progbar(object): | |
55 def __init__(self, ui): | |
56 self.ui = ui | |
57 self.resetstate() | |
58 | |
59 def resetstate(self): | |
60 self.topics = [] | |
61 self.printed = False | |
62 self.lastprint = time.time() + float(self.ui.config( | |
63 'progress', 'delay', default=3)) | |
64 self.indetcount = 0 | |
65 self.refresh = float(self.ui.config( | |
66 'progress', 'refresh', default=0.1)) | |
67 self.order = self.ui.configlist( | |
68 'progress', 'format', | |
69 default=['topic', 'bar', 'number']) | |
70 | |
71 def show(self, topic, pos, item, unit, total): | |
72 termwidth = self.width() | |
73 self.printed = True | |
74 head = '' | |
75 needprogress = False | |
76 tail = '' | |
77 for indicator in self.order: | |
78 add = '' | |
79 if indicator == 'topic': | |
80 add = topic | |
81 elif indicator == 'number': | |
82 if total: | |
83 add = ('% ' + str(len(str(total))) + | |
84 's/%s') % (pos, total) | |
85 else: | |
86 add = str(pos) | |
87 elif indicator.startswith('item') and item: | |
88 slice = 'end' | |
89 if '-' in indicator: | |
90 wid = int(indicator.split('-')[1]) | |
91 elif '+' in indicator: | |
92 slice = 'beginning' | |
93 wid = int(indicator.split('+')[1]) | |
94 else: | |
95 wid = 20 | |
96 if slice == 'end': | |
97 add = item[-wid:] | |
98 else: | |
99 add = item[:wid] | |
100 add += (wid - len(add)) * ' ' | |
101 elif indicator == 'bar': | |
102 add = '' | |
103 needprogress = True | |
104 elif indicator == 'unit' and unit: | |
105 add = unit | |
106 if not needprogress: | |
107 head = spacejoin(head, add) | |
108 else: | |
109 tail = spacejoin(add, tail) | |
110 if needprogress: | |
111 used = 0 | |
112 if head: | |
113 used += len(head) + 1 | |
114 if tail: | |
115 used += len(tail) + 1 | |
116 progwidth = termwidth - used - 3 | |
117 if total: | |
118 amt = pos * progwidth // total | |
10453
7edc649f9f7e
progress: make determinate bar more like wget progress bar
Brodie Rao <me+hg@dackz.net>
parents:
10452
diff
changeset
|
119 bar = '=' * (amt - 1) |
7edc649f9f7e
progress: make determinate bar more like wget progress bar
Brodie Rao <me+hg@dackz.net>
parents:
10452
diff
changeset
|
120 if amt > 0: |
7edc649f9f7e
progress: make determinate bar more like wget progress bar
Brodie Rao <me+hg@dackz.net>
parents:
10452
diff
changeset
|
121 bar += '>' |
7edc649f9f7e
progress: make determinate bar more like wget progress bar
Brodie Rao <me+hg@dackz.net>
parents:
10452
diff
changeset
|
122 bar += ' ' * (progwidth - amt) |
10434 | 123 else: |
124 progwidth -= 3 | |
125 self.indetcount += 1 | |
126 # mod the count by twice the width so we can make the | |
127 # cursor bounce between the right and left sides | |
128 amt = self.indetcount % (2 * progwidth) | |
129 amt -= progwidth | |
130 bar = (' ' * int(progwidth - abs(amt)) + '<=>' + | |
131 ' ' * int(abs(amt))) | |
132 prog = ''.join(('[', bar , ']')) | |
133 out = spacejoin(head, prog, tail) | |
134 else: | |
135 out = spacejoin(head, tail) | |
10788
ca6ba6cac6cd
progress: use stderr instead of stdout; check stderr.isatty()
Augie Fackler <durin42@gmail.com>
parents:
10656
diff
changeset
|
136 sys.stderr.write('\r' + out[:termwidth]) |
ca6ba6cac6cd
progress: use stderr instead of stdout; check stderr.isatty()
Augie Fackler <durin42@gmail.com>
parents:
10656
diff
changeset
|
137 sys.stderr.flush() |
10434 | 138 |
139 def clear(self): | |
10788
ca6ba6cac6cd
progress: use stderr instead of stdout; check stderr.isatty()
Augie Fackler <durin42@gmail.com>
parents:
10656
diff
changeset
|
140 sys.stderr.write('\r%s\r' % (' ' * self.width())) |
10434 | 141 |
10439
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10434
diff
changeset
|
142 def complete(self): |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10434
diff
changeset
|
143 if self.ui.configbool('progress', 'clear-complete', default=True): |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10434
diff
changeset
|
144 self.clear() |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10434
diff
changeset
|
145 else: |
10788
ca6ba6cac6cd
progress: use stderr instead of stdout; check stderr.isatty()
Augie Fackler <durin42@gmail.com>
parents:
10656
diff
changeset
|
146 sys.stderr.write('\n') |
ca6ba6cac6cd
progress: use stderr instead of stdout; check stderr.isatty()
Augie Fackler <durin42@gmail.com>
parents:
10656
diff
changeset
|
147 sys.stderr.flush() |
10439
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10434
diff
changeset
|
148 |
10434 | 149 def width(self): |
150 tw = util.termwidth() | |
151 return min(int(self.ui.config('progress', 'width', default=tw)), tw) | |
152 | |
153 def progress(self, orig, topic, pos, item='', unit='', total=None): | |
10439
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10434
diff
changeset
|
154 if pos is None: |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10434
diff
changeset
|
155 if self.topics and self.topics[-1] == topic and self.printed: |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10434
diff
changeset
|
156 self.complete() |
10441
dc0d1ca2d378
progress: only reset state if finishing progress for the current topic
Augie Fackler <durin42@gmail.com>
parents:
10439
diff
changeset
|
157 self.resetstate() |
10439
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10434
diff
changeset
|
158 else: |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10434
diff
changeset
|
159 if topic not in self.topics: |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10434
diff
changeset
|
160 self.topics.append(topic) |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10434
diff
changeset
|
161 now = time.time() |
10464
149ad0a3ec91
progress: make progress.refresh=0 always display the progress line
Patrick Mezard <pmezard@gmail.com>
parents:
10463
diff
changeset
|
162 if (now - self.lastprint >= self.refresh |
149ad0a3ec91
progress: make progress.refresh=0 always display the progress line
Patrick Mezard <pmezard@gmail.com>
parents:
10463
diff
changeset
|
163 and topic == self.topics[-1]): |
10439
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10434
diff
changeset
|
164 self.lastprint = now |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10434
diff
changeset
|
165 self.show(topic, pos, item, unit, total) |
10434 | 166 return orig(topic, pos, item=item, unit=unit, total=total) |
167 | |
10815
32b213b9b22c
ui: add ui.write() output labeling API
Brodie Rao <brodie@bitheap.org>
parents:
10788
diff
changeset
|
168 def write(self, orig, *args, **opts): |
10434 | 169 if self.printed: |
170 self.clear() | |
10815
32b213b9b22c
ui: add ui.write() output labeling API
Brodie Rao <brodie@bitheap.org>
parents:
10788
diff
changeset
|
171 return orig(*args, **opts) |
10434 | 172 |
173 sharedprog = None | |
174 | |
175 def uisetup(ui): | |
10540
dd9d057465c1
progress: provide an explicit disable method for developers
Steve Borho <steve@borho.org>
parents:
10523
diff
changeset
|
176 # Apps that derive a class from ui.ui() can use |
dd9d057465c1
progress: provide an explicit disable method for developers
Steve Borho <steve@borho.org>
parents:
10523
diff
changeset
|
177 # setconfig('progress', 'disable', 'True') to disable this extension |
dd9d057465c1
progress: provide an explicit disable method for developers
Steve Borho <steve@borho.org>
parents:
10523
diff
changeset
|
178 if ui.configbool('progress', 'disable'): |
dd9d057465c1
progress: provide an explicit disable method for developers
Steve Borho <steve@borho.org>
parents:
10523
diff
changeset
|
179 return |
10788
ca6ba6cac6cd
progress: use stderr instead of stdout; check stderr.isatty()
Augie Fackler <durin42@gmail.com>
parents:
10656
diff
changeset
|
180 if ((sys.stderr.isatty() or ui.configbool('progress', 'assume-tty')) |
ca6ba6cac6cd
progress: use stderr instead of stdout; check stderr.isatty()
Augie Fackler <durin42@gmail.com>
parents:
10656
diff
changeset
|
181 and not ui.debugflag and not ui.quiet): |
10434 | 182 # we instantiate one globally shared progress bar to avoid |
183 # competing progress bars when multiple UI objects get created | |
184 global sharedprog | |
185 if not sharedprog: | |
186 sharedprog = progbar(ui) | |
187 extensions.wrapfunction(ui, 'progress', sharedprog.progress) | |
188 extensions.wrapfunction(ui, 'write', sharedprog.write) | |
10594
08716b8ba9fb
progress: clear progress before writing to stderr
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10540
diff
changeset
|
189 extensions.wrapfunction(ui, 'write_err', sharedprog.write) |
10434 | 190 |
191 def reposetup(ui, repo): | |
192 uisetup(repo.ui) |