##// END OF EJS Templates
hgweb: move hex creation into an object method...
Pierre-Yves David -
r18405:1eaf0d01 default
parent child Browse files
Show More
@@ -1,370 +1,371 b''
1 # hgweb/webutil.py - utility library for the web interface.
1 # hgweb/webutil.py - utility library for the web interface.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 import os, copy
9 import os, copy
10 from mercurial import match, patch, scmutil, error, ui, util
10 from mercurial import match, patch, scmutil, error, ui, util
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12 from mercurial.node import hex, nullid
12 from mercurial.node import hex, nullid
13 from common import ErrorResponse
13 from common import ErrorResponse
14 from common import HTTP_NOT_FOUND
14 from common import HTTP_NOT_FOUND
15 import difflib
15 import difflib
16
16
17 def up(p):
17 def up(p):
18 if p[0] != "/":
18 if p[0] != "/":
19 p = "/" + p
19 p = "/" + p
20 if p[-1] == "/":
20 if p[-1] == "/":
21 p = p[:-1]
21 p = p[:-1]
22 up = os.path.dirname(p)
22 up = os.path.dirname(p)
23 if up == "/":
23 if up == "/":
24 return "/"
24 return "/"
25 return up + "/"
25 return up + "/"
26
26
27 def _navseq(step, firststep=None):
27 def _navseq(step, firststep=None):
28 if firststep:
28 if firststep:
29 yield firststep
29 yield firststep
30 if firststep >= 20 and firststep <= 40:
30 if firststep >= 20 and firststep <= 40:
31 firststep = 50
31 firststep = 50
32 yield firststep
32 yield firststep
33 assert step > 0
33 assert step > 0
34 assert firststep > 0
34 assert firststep > 0
35 while step <= firststep:
35 while step <= firststep:
36 step *= 10
36 step *= 10
37 while True:
37 while True:
38 yield 1 * step
38 yield 1 * step
39 yield 3 * step
39 yield 3 * step
40 step *= 10
40 step *= 10
41
41
42 class revnav(object):
42 class revnav(object):
43
43
44 def __init__(self, nodefunc):
44 def __init__(self, nodefunc):
45 """Navigation generation object
45 """Navigation generation object
46
46
47 :nodefun: factory for a changectx from a revision
47 :nodefun: factory for a changectx from a revision
48 """
48 """
49 self.nodefunc = nodefunc
49 self.nodefunc = nodefunc
50
50
51 def hex(self, rev):
52 return self.nodefunc(rev).hex()
53
51 def gen(self, pos, pagelen, limit):
54 def gen(self, pos, pagelen, limit):
52 """computes label and revision id for navigation link
55 """computes label and revision id for navigation link
53
56
54 :pos: is the revision relative to which we generate navigation.
57 :pos: is the revision relative to which we generate navigation.
55 :pagelen: the size of each navigation page
58 :pagelen: the size of each navigation page
56 :limit: how far shall we link
59 :limit: how far shall we link
57
60
58 The return is:
61 The return is:
59 - a single element tuple
62 - a single element tuple
60 - containing a dictionary with a `before` and `after` key
63 - containing a dictionary with a `before` and `after` key
61 - values are generator functions taking arbitrary number of kwargs
64 - values are generator functions taking arbitrary number of kwargs
62 - yield items are dictionaries with `label` and `node` keys
65 - yield items are dictionaries with `label` and `node` keys
63 """
66 """
64
67
65 navbefore = []
68 navbefore = []
66 navafter = []
69 navafter = []
67
70
68 for f in _navseq(1, pagelen):
71 for f in _navseq(1, pagelen):
69 if f > limit:
72 if f > limit:
70 break
73 break
71 if pos + f < limit:
74 if pos + f < limit:
72 navafter.append(("+%d" % f,
75 navafter.append(("+%d" % f, self.hex(pos + f)))
73 hex(self.nodefunc(pos + f).node())))
74 if pos - f >= 0:
76 if pos - f >= 0:
75 navbefore.insert(0, ("-%d" % f,
77 navbefore.insert(0, ("-%d" % f, self.hex(pos - f)))
76 hex(self.nodefunc(pos - f).node())))
77
78
78 navafter.append(("tip", "tip"))
79 navafter.append(("tip", "tip"))
79 try:
80 try:
80 navbefore.insert(0, ("(0)", hex(self.nodefunc(0).node())))
81 navbefore.insert(0, ("(0)", self.hex(0)))
81 except error.RepoError:
82 except error.RepoError:
82 pass
83 pass
83
84
84 data = lambda i: {"label": i[0], "node": i[1]}
85 data = lambda i: {"label": i[0], "node": i[1]}
85 return ({'before': lambda **map: (data(i) for i in navbefore),
86 return ({'before': lambda **map: (data(i) for i in navbefore),
86 'after': lambda **map: (data(i) for i in navafter)},)
87 'after': lambda **map: (data(i) for i in navafter)},)
87
88
88 def _siblings(siblings=[], hiderev=None):
89 def _siblings(siblings=[], hiderev=None):
89 siblings = [s for s in siblings if s.node() != nullid]
90 siblings = [s for s in siblings if s.node() != nullid]
90 if len(siblings) == 1 and siblings[0].rev() == hiderev:
91 if len(siblings) == 1 and siblings[0].rev() == hiderev:
91 return
92 return
92 for s in siblings:
93 for s in siblings:
93 d = {'node': s.hex(), 'rev': s.rev()}
94 d = {'node': s.hex(), 'rev': s.rev()}
94 d['user'] = s.user()
95 d['user'] = s.user()
95 d['date'] = s.date()
96 d['date'] = s.date()
96 d['description'] = s.description()
97 d['description'] = s.description()
97 d['branch'] = s.branch()
98 d['branch'] = s.branch()
98 if util.safehasattr(s, 'path'):
99 if util.safehasattr(s, 'path'):
99 d['file'] = s.path()
100 d['file'] = s.path()
100 yield d
101 yield d
101
102
102 def parents(ctx, hide=None):
103 def parents(ctx, hide=None):
103 return _siblings(ctx.parents(), hide)
104 return _siblings(ctx.parents(), hide)
104
105
105 def children(ctx, hide=None):
106 def children(ctx, hide=None):
106 return _siblings(ctx.children(), hide)
107 return _siblings(ctx.children(), hide)
107
108
108 def renamelink(fctx):
109 def renamelink(fctx):
109 r = fctx.renamed()
110 r = fctx.renamed()
110 if r:
111 if r:
111 return [dict(file=r[0], node=hex(r[1]))]
112 return [dict(file=r[0], node=hex(r[1]))]
112 return []
113 return []
113
114
114 def nodetagsdict(repo, node):
115 def nodetagsdict(repo, node):
115 return [{"name": i} for i in repo.nodetags(node)]
116 return [{"name": i} for i in repo.nodetags(node)]
116
117
117 def nodebookmarksdict(repo, node):
118 def nodebookmarksdict(repo, node):
118 return [{"name": i} for i in repo.nodebookmarks(node)]
119 return [{"name": i} for i in repo.nodebookmarks(node)]
119
120
120 def nodebranchdict(repo, ctx):
121 def nodebranchdict(repo, ctx):
121 branches = []
122 branches = []
122 branch = ctx.branch()
123 branch = ctx.branch()
123 # If this is an empty repo, ctx.node() == nullid,
124 # If this is an empty repo, ctx.node() == nullid,
124 # ctx.branch() == 'default'.
125 # ctx.branch() == 'default'.
125 try:
126 try:
126 branchnode = repo.branchtip(branch)
127 branchnode = repo.branchtip(branch)
127 except error.RepoLookupError:
128 except error.RepoLookupError:
128 branchnode = None
129 branchnode = None
129 if branchnode == ctx.node():
130 if branchnode == ctx.node():
130 branches.append({"name": branch})
131 branches.append({"name": branch})
131 return branches
132 return branches
132
133
133 def nodeinbranch(repo, ctx):
134 def nodeinbranch(repo, ctx):
134 branches = []
135 branches = []
135 branch = ctx.branch()
136 branch = ctx.branch()
136 try:
137 try:
137 branchnode = repo.branchtip(branch)
138 branchnode = repo.branchtip(branch)
138 except error.RepoLookupError:
139 except error.RepoLookupError:
139 branchnode = None
140 branchnode = None
140 if branch != 'default' and branchnode != ctx.node():
141 if branch != 'default' and branchnode != ctx.node():
141 branches.append({"name": branch})
142 branches.append({"name": branch})
142 return branches
143 return branches
143
144
144 def nodebranchnodefault(ctx):
145 def nodebranchnodefault(ctx):
145 branches = []
146 branches = []
146 branch = ctx.branch()
147 branch = ctx.branch()
147 if branch != 'default':
148 if branch != 'default':
148 branches.append({"name": branch})
149 branches.append({"name": branch})
149 return branches
150 return branches
150
151
151 def showtag(repo, tmpl, t1, node=nullid, **args):
152 def showtag(repo, tmpl, t1, node=nullid, **args):
152 for t in repo.nodetags(node):
153 for t in repo.nodetags(node):
153 yield tmpl(t1, tag=t, **args)
154 yield tmpl(t1, tag=t, **args)
154
155
155 def showbookmark(repo, tmpl, t1, node=nullid, **args):
156 def showbookmark(repo, tmpl, t1, node=nullid, **args):
156 for t in repo.nodebookmarks(node):
157 for t in repo.nodebookmarks(node):
157 yield tmpl(t1, bookmark=t, **args)
158 yield tmpl(t1, bookmark=t, **args)
158
159
159 def cleanpath(repo, path):
160 def cleanpath(repo, path):
160 path = path.lstrip('/')
161 path = path.lstrip('/')
161 return scmutil.canonpath(repo.root, '', path)
162 return scmutil.canonpath(repo.root, '', path)
162
163
163 def changeidctx (repo, changeid):
164 def changeidctx (repo, changeid):
164 try:
165 try:
165 ctx = repo[changeid]
166 ctx = repo[changeid]
166 except error.RepoError:
167 except error.RepoError:
167 man = repo.manifest
168 man = repo.manifest
168 ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
169 ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
169
170
170 return ctx
171 return ctx
171
172
172 def changectx (repo, req):
173 def changectx (repo, req):
173 changeid = "tip"
174 changeid = "tip"
174 if 'node' in req.form:
175 if 'node' in req.form:
175 changeid = req.form['node'][0]
176 changeid = req.form['node'][0]
176 ipos=changeid.find(':')
177 ipos=changeid.find(':')
177 if ipos != -1:
178 if ipos != -1:
178 changeid = changeid[(ipos + 1):]
179 changeid = changeid[(ipos + 1):]
179 elif 'manifest' in req.form:
180 elif 'manifest' in req.form:
180 changeid = req.form['manifest'][0]
181 changeid = req.form['manifest'][0]
181
182
182 return changeidctx(repo, changeid)
183 return changeidctx(repo, changeid)
183
184
184 def basechangectx(repo, req):
185 def basechangectx(repo, req):
185 if 'node' in req.form:
186 if 'node' in req.form:
186 changeid = req.form['node'][0]
187 changeid = req.form['node'][0]
187 ipos=changeid.find(':')
188 ipos=changeid.find(':')
188 if ipos != -1:
189 if ipos != -1:
189 changeid = changeid[:ipos]
190 changeid = changeid[:ipos]
190 return changeidctx(repo, changeid)
191 return changeidctx(repo, changeid)
191
192
192 return None
193 return None
193
194
194 def filectx(repo, req):
195 def filectx(repo, req):
195 if 'file' not in req.form:
196 if 'file' not in req.form:
196 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
197 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
197 path = cleanpath(repo, req.form['file'][0])
198 path = cleanpath(repo, req.form['file'][0])
198 if 'node' in req.form:
199 if 'node' in req.form:
199 changeid = req.form['node'][0]
200 changeid = req.form['node'][0]
200 elif 'filenode' in req.form:
201 elif 'filenode' in req.form:
201 changeid = req.form['filenode'][0]
202 changeid = req.form['filenode'][0]
202 else:
203 else:
203 raise ErrorResponse(HTTP_NOT_FOUND, 'node or filenode not given')
204 raise ErrorResponse(HTTP_NOT_FOUND, 'node or filenode not given')
204 try:
205 try:
205 fctx = repo[changeid][path]
206 fctx = repo[changeid][path]
206 except error.RepoError:
207 except error.RepoError:
207 fctx = repo.filectx(path, fileid=changeid)
208 fctx = repo.filectx(path, fileid=changeid)
208
209
209 return fctx
210 return fctx
210
211
211 def listfilediffs(tmpl, files, node, max):
212 def listfilediffs(tmpl, files, node, max):
212 for f in files[:max]:
213 for f in files[:max]:
213 yield tmpl('filedifflink', node=hex(node), file=f)
214 yield tmpl('filedifflink', node=hex(node), file=f)
214 if len(files) > max:
215 if len(files) > max:
215 yield tmpl('fileellipses')
216 yield tmpl('fileellipses')
216
217
217 def diffs(repo, tmpl, ctx, basectx, files, parity, style):
218 def diffs(repo, tmpl, ctx, basectx, files, parity, style):
218
219
219 def countgen():
220 def countgen():
220 start = 1
221 start = 1
221 while True:
222 while True:
222 yield start
223 yield start
223 start += 1
224 start += 1
224
225
225 blockcount = countgen()
226 blockcount = countgen()
226 def prettyprintlines(diff, blockno):
227 def prettyprintlines(diff, blockno):
227 for lineno, l in enumerate(diff.splitlines(True)):
228 for lineno, l in enumerate(diff.splitlines(True)):
228 lineno = "%d.%d" % (blockno, lineno + 1)
229 lineno = "%d.%d" % (blockno, lineno + 1)
229 if l.startswith('+'):
230 if l.startswith('+'):
230 ltype = "difflineplus"
231 ltype = "difflineplus"
231 elif l.startswith('-'):
232 elif l.startswith('-'):
232 ltype = "difflineminus"
233 ltype = "difflineminus"
233 elif l.startswith('@'):
234 elif l.startswith('@'):
234 ltype = "difflineat"
235 ltype = "difflineat"
235 else:
236 else:
236 ltype = "diffline"
237 ltype = "diffline"
237 yield tmpl(ltype,
238 yield tmpl(ltype,
238 line=l,
239 line=l,
239 lineid="l%s" % lineno,
240 lineid="l%s" % lineno,
240 linenumber="% 8s" % lineno)
241 linenumber="% 8s" % lineno)
241
242
242 if files:
243 if files:
243 m = match.exact(repo.root, repo.getcwd(), files)
244 m = match.exact(repo.root, repo.getcwd(), files)
244 else:
245 else:
245 m = match.always(repo.root, repo.getcwd())
246 m = match.always(repo.root, repo.getcwd())
246
247
247 diffopts = patch.diffopts(repo.ui, untrusted=True)
248 diffopts = patch.diffopts(repo.ui, untrusted=True)
248 if basectx is None:
249 if basectx is None:
249 parents = ctx.parents()
250 parents = ctx.parents()
250 node1 = parents and parents[0].node() or nullid
251 node1 = parents and parents[0].node() or nullid
251 else:
252 else:
252 node1 = basectx.node()
253 node1 = basectx.node()
253 node2 = ctx.node()
254 node2 = ctx.node()
254
255
255 block = []
256 block = []
256 for chunk in patch.diff(repo, node1, node2, m, opts=diffopts):
257 for chunk in patch.diff(repo, node1, node2, m, opts=diffopts):
257 if chunk.startswith('diff') and block:
258 if chunk.startswith('diff') and block:
258 blockno = blockcount.next()
259 blockno = blockcount.next()
259 yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
260 yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
260 lines=prettyprintlines(''.join(block), blockno))
261 lines=prettyprintlines(''.join(block), blockno))
261 block = []
262 block = []
262 if chunk.startswith('diff') and style != 'raw':
263 if chunk.startswith('diff') and style != 'raw':
263 chunk = ''.join(chunk.splitlines(True)[1:])
264 chunk = ''.join(chunk.splitlines(True)[1:])
264 block.append(chunk)
265 block.append(chunk)
265 blockno = blockcount.next()
266 blockno = blockcount.next()
266 yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
267 yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
267 lines=prettyprintlines(''.join(block), blockno))
268 lines=prettyprintlines(''.join(block), blockno))
268
269
269 def compare(tmpl, context, leftlines, rightlines):
270 def compare(tmpl, context, leftlines, rightlines):
270 '''Generator function that provides side-by-side comparison data.'''
271 '''Generator function that provides side-by-side comparison data.'''
271
272
272 def compline(type, leftlineno, leftline, rightlineno, rightline):
273 def compline(type, leftlineno, leftline, rightlineno, rightline):
273 lineid = leftlineno and ("l%s" % leftlineno) or ''
274 lineid = leftlineno and ("l%s" % leftlineno) or ''
274 lineid += rightlineno and ("r%s" % rightlineno) or ''
275 lineid += rightlineno and ("r%s" % rightlineno) or ''
275 return tmpl('comparisonline',
276 return tmpl('comparisonline',
276 type=type,
277 type=type,
277 lineid=lineid,
278 lineid=lineid,
278 leftlinenumber="% 6s" % (leftlineno or ''),
279 leftlinenumber="% 6s" % (leftlineno or ''),
279 leftline=leftline or '',
280 leftline=leftline or '',
280 rightlinenumber="% 6s" % (rightlineno or ''),
281 rightlinenumber="% 6s" % (rightlineno or ''),
281 rightline=rightline or '')
282 rightline=rightline or '')
282
283
283 def getblock(opcodes):
284 def getblock(opcodes):
284 for type, llo, lhi, rlo, rhi in opcodes:
285 for type, llo, lhi, rlo, rhi in opcodes:
285 len1 = lhi - llo
286 len1 = lhi - llo
286 len2 = rhi - rlo
287 len2 = rhi - rlo
287 count = min(len1, len2)
288 count = min(len1, len2)
288 for i in xrange(count):
289 for i in xrange(count):
289 yield compline(type=type,
290 yield compline(type=type,
290 leftlineno=llo + i + 1,
291 leftlineno=llo + i + 1,
291 leftline=leftlines[llo + i],
292 leftline=leftlines[llo + i],
292 rightlineno=rlo + i + 1,
293 rightlineno=rlo + i + 1,
293 rightline=rightlines[rlo + i])
294 rightline=rightlines[rlo + i])
294 if len1 > len2:
295 if len1 > len2:
295 for i in xrange(llo + count, lhi):
296 for i in xrange(llo + count, lhi):
296 yield compline(type=type,
297 yield compline(type=type,
297 leftlineno=i + 1,
298 leftlineno=i + 1,
298 leftline=leftlines[i],
299 leftline=leftlines[i],
299 rightlineno=None,
300 rightlineno=None,
300 rightline=None)
301 rightline=None)
301 elif len2 > len1:
302 elif len2 > len1:
302 for i in xrange(rlo + count, rhi):
303 for i in xrange(rlo + count, rhi):
303 yield compline(type=type,
304 yield compline(type=type,
304 leftlineno=None,
305 leftlineno=None,
305 leftline=None,
306 leftline=None,
306 rightlineno=i + 1,
307 rightlineno=i + 1,
307 rightline=rightlines[i])
308 rightline=rightlines[i])
308
309
309 s = difflib.SequenceMatcher(None, leftlines, rightlines)
310 s = difflib.SequenceMatcher(None, leftlines, rightlines)
310 if context < 0:
311 if context < 0:
311 yield tmpl('comparisonblock', lines=getblock(s.get_opcodes()))
312 yield tmpl('comparisonblock', lines=getblock(s.get_opcodes()))
312 else:
313 else:
313 for oc in s.get_grouped_opcodes(n=context):
314 for oc in s.get_grouped_opcodes(n=context):
314 yield tmpl('comparisonblock', lines=getblock(oc))
315 yield tmpl('comparisonblock', lines=getblock(oc))
315
316
316 def diffstatgen(ctx, basectx):
317 def diffstatgen(ctx, basectx):
317 '''Generator function that provides the diffstat data.'''
318 '''Generator function that provides the diffstat data.'''
318
319
319 stats = patch.diffstatdata(util.iterlines(ctx.diff(basectx)))
320 stats = patch.diffstatdata(util.iterlines(ctx.diff(basectx)))
320 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
321 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
321 while True:
322 while True:
322 yield stats, maxname, maxtotal, addtotal, removetotal, binary
323 yield stats, maxname, maxtotal, addtotal, removetotal, binary
323
324
324 def diffsummary(statgen):
325 def diffsummary(statgen):
325 '''Return a short summary of the diff.'''
326 '''Return a short summary of the diff.'''
326
327
327 stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
328 stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
328 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
329 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
329 len(stats), addtotal, removetotal)
330 len(stats), addtotal, removetotal)
330
331
331 def diffstat(tmpl, ctx, statgen, parity):
332 def diffstat(tmpl, ctx, statgen, parity):
332 '''Return a diffstat template for each file in the diff.'''
333 '''Return a diffstat template for each file in the diff.'''
333
334
334 stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
335 stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
335 files = ctx.files()
336 files = ctx.files()
336
337
337 def pct(i):
338 def pct(i):
338 if maxtotal == 0:
339 if maxtotal == 0:
339 return 0
340 return 0
340 return (float(i) / maxtotal) * 100
341 return (float(i) / maxtotal) * 100
341
342
342 fileno = 0
343 fileno = 0
343 for filename, adds, removes, isbinary in stats:
344 for filename, adds, removes, isbinary in stats:
344 template = filename in files and 'diffstatlink' or 'diffstatnolink'
345 template = filename in files and 'diffstatlink' or 'diffstatnolink'
345 total = adds + removes
346 total = adds + removes
346 fileno += 1
347 fileno += 1
347 yield tmpl(template, node=ctx.hex(), file=filename, fileno=fileno,
348 yield tmpl(template, node=ctx.hex(), file=filename, fileno=fileno,
348 total=total, addpct=pct(adds), removepct=pct(removes),
349 total=total, addpct=pct(adds), removepct=pct(removes),
349 parity=parity.next())
350 parity=parity.next())
350
351
351 class sessionvars(object):
352 class sessionvars(object):
352 def __init__(self, vars, start='?'):
353 def __init__(self, vars, start='?'):
353 self.start = start
354 self.start = start
354 self.vars = vars
355 self.vars = vars
355 def __getitem__(self, key):
356 def __getitem__(self, key):
356 return self.vars[key]
357 return self.vars[key]
357 def __setitem__(self, key, value):
358 def __setitem__(self, key, value):
358 self.vars[key] = value
359 self.vars[key] = value
359 def __copy__(self):
360 def __copy__(self):
360 return sessionvars(copy.copy(self.vars), self.start)
361 return sessionvars(copy.copy(self.vars), self.start)
361 def __iter__(self):
362 def __iter__(self):
362 separator = self.start
363 separator = self.start
363 for key, value in sorted(self.vars.iteritems()):
364 for key, value in sorted(self.vars.iteritems()):
364 yield {'name': key, 'value': str(value), 'separator': separator}
365 yield {'name': key, 'value': str(value), 'separator': separator}
365 separator = '&'
366 separator = '&'
366
367
367 class wsgiui(ui.ui):
368 class wsgiui(ui.ui):
368 # default termwidth breaks under mod_wsgi
369 # default termwidth breaks under mod_wsgi
369 def termwidth(self):
370 def termwidth(self):
370 return 80
371 return 80
General Comments 0
You need to be logged in to leave comments. Login now