##// END OF EJS Templates
web: provide the file number to the diffstat templates...
Steven Brown -
r14562:fccd3b96 default
parent child Browse files
Show More
@@ -1,252 +1,254
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.node import hex, nullid
11 from mercurial.node import hex, nullid
12
12
13 def up(p):
13 def up(p):
14 if p[0] != "/":
14 if p[0] != "/":
15 p = "/" + p
15 p = "/" + p
16 if p[-1] == "/":
16 if p[-1] == "/":
17 p = p[:-1]
17 p = p[:-1]
18 up = os.path.dirname(p)
18 up = os.path.dirname(p)
19 if up == "/":
19 if up == "/":
20 return "/"
20 return "/"
21 return up + "/"
21 return up + "/"
22
22
23 def revnavgen(pos, pagelen, limit, nodefunc):
23 def revnavgen(pos, pagelen, limit, nodefunc):
24 def seq(factor, limit=None):
24 def seq(factor, limit=None):
25 if limit:
25 if limit:
26 yield limit
26 yield limit
27 if limit >= 20 and limit <= 40:
27 if limit >= 20 and limit <= 40:
28 yield 50
28 yield 50
29 else:
29 else:
30 yield 1 * factor
30 yield 1 * factor
31 yield 3 * factor
31 yield 3 * factor
32 for f in seq(factor * 10):
32 for f in seq(factor * 10):
33 yield f
33 yield f
34
34
35 navbefore = []
35 navbefore = []
36 navafter = []
36 navafter = []
37
37
38 last = 0
38 last = 0
39 for f in seq(1, pagelen):
39 for f in seq(1, pagelen):
40 if f < pagelen or f <= last:
40 if f < pagelen or f <= last:
41 continue
41 continue
42 if f > limit:
42 if f > limit:
43 break
43 break
44 last = f
44 last = f
45 if pos + f < limit:
45 if pos + f < limit:
46 navafter.append(("+%d" % f, hex(nodefunc(pos + f).node())))
46 navafter.append(("+%d" % f, hex(nodefunc(pos + f).node())))
47 if pos - f >= 0:
47 if pos - f >= 0:
48 navbefore.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
48 navbefore.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
49
49
50 navafter.append(("tip", "tip"))
50 navafter.append(("tip", "tip"))
51 try:
51 try:
52 navbefore.insert(0, ("(0)", hex(nodefunc('0').node())))
52 navbefore.insert(0, ("(0)", hex(nodefunc('0').node())))
53 except error.RepoError:
53 except error.RepoError:
54 pass
54 pass
55
55
56 def gen(l):
56 def gen(l):
57 def f(**map):
57 def f(**map):
58 for label, node in l:
58 for label, node in l:
59 yield {"label": label, "node": node}
59 yield {"label": label, "node": node}
60 return f
60 return f
61
61
62 return (dict(before=gen(navbefore), after=gen(navafter)),)
62 return (dict(before=gen(navbefore), after=gen(navafter)),)
63
63
64 def _siblings(siblings=[], hiderev=None):
64 def _siblings(siblings=[], hiderev=None):
65 siblings = [s for s in siblings if s.node() != nullid]
65 siblings = [s for s in siblings if s.node() != nullid]
66 if len(siblings) == 1 and siblings[0].rev() == hiderev:
66 if len(siblings) == 1 and siblings[0].rev() == hiderev:
67 return
67 return
68 for s in siblings:
68 for s in siblings:
69 d = {'node': s.hex(), 'rev': s.rev()}
69 d = {'node': s.hex(), 'rev': s.rev()}
70 d['user'] = s.user()
70 d['user'] = s.user()
71 d['date'] = s.date()
71 d['date'] = s.date()
72 d['description'] = s.description()
72 d['description'] = s.description()
73 d['branch'] = s.branch()
73 d['branch'] = s.branch()
74 if hasattr(s, 'path'):
74 if hasattr(s, 'path'):
75 d['file'] = s.path()
75 d['file'] = s.path()
76 yield d
76 yield d
77
77
78 def parents(ctx, hide=None):
78 def parents(ctx, hide=None):
79 return _siblings(ctx.parents(), hide)
79 return _siblings(ctx.parents(), hide)
80
80
81 def children(ctx, hide=None):
81 def children(ctx, hide=None):
82 return _siblings(ctx.children(), hide)
82 return _siblings(ctx.children(), hide)
83
83
84 def renamelink(fctx):
84 def renamelink(fctx):
85 r = fctx.renamed()
85 r = fctx.renamed()
86 if r:
86 if r:
87 return [dict(file=r[0], node=hex(r[1]))]
87 return [dict(file=r[0], node=hex(r[1]))]
88 return []
88 return []
89
89
90 def nodetagsdict(repo, node):
90 def nodetagsdict(repo, node):
91 return [{"name": i} for i in repo.nodetags(node)]
91 return [{"name": i} for i in repo.nodetags(node)]
92
92
93 def nodebookmarksdict(repo, node):
93 def nodebookmarksdict(repo, node):
94 return [{"name": i} for i in repo.nodebookmarks(node)]
94 return [{"name": i} for i in repo.nodebookmarks(node)]
95
95
96 def nodebranchdict(repo, ctx):
96 def nodebranchdict(repo, ctx):
97 branches = []
97 branches = []
98 branch = ctx.branch()
98 branch = ctx.branch()
99 # If this is an empty repo, ctx.node() == nullid,
99 # If this is an empty repo, ctx.node() == nullid,
100 # ctx.branch() == 'default', but branchtags() is
100 # ctx.branch() == 'default', but branchtags() is
101 # an empty dict. Using dict.get avoids a traceback.
101 # an empty dict. Using dict.get avoids a traceback.
102 if repo.branchtags().get(branch) == ctx.node():
102 if repo.branchtags().get(branch) == ctx.node():
103 branches.append({"name": branch})
103 branches.append({"name": branch})
104 return branches
104 return branches
105
105
106 def nodeinbranch(repo, ctx):
106 def nodeinbranch(repo, ctx):
107 branches = []
107 branches = []
108 branch = ctx.branch()
108 branch = ctx.branch()
109 if branch != 'default' and repo.branchtags().get(branch) != ctx.node():
109 if branch != 'default' and repo.branchtags().get(branch) != ctx.node():
110 branches.append({"name": branch})
110 branches.append({"name": branch})
111 return branches
111 return branches
112
112
113 def nodebranchnodefault(ctx):
113 def nodebranchnodefault(ctx):
114 branches = []
114 branches = []
115 branch = ctx.branch()
115 branch = ctx.branch()
116 if branch != 'default':
116 if branch != 'default':
117 branches.append({"name": branch})
117 branches.append({"name": branch})
118 return branches
118 return branches
119
119
120 def showtag(repo, tmpl, t1, node=nullid, **args):
120 def showtag(repo, tmpl, t1, node=nullid, **args):
121 for t in repo.nodetags(node):
121 for t in repo.nodetags(node):
122 yield tmpl(t1, tag=t, **args)
122 yield tmpl(t1, tag=t, **args)
123
123
124 def showbookmark(repo, tmpl, t1, node=nullid, **args):
124 def showbookmark(repo, tmpl, t1, node=nullid, **args):
125 for t in repo.nodebookmarks(node):
125 for t in repo.nodebookmarks(node):
126 yield tmpl(t1, bookmark=t, **args)
126 yield tmpl(t1, bookmark=t, **args)
127
127
128 def cleanpath(repo, path):
128 def cleanpath(repo, path):
129 path = path.lstrip('/')
129 path = path.lstrip('/')
130 return scmutil.canonpath(repo.root, '', path)
130 return scmutil.canonpath(repo.root, '', path)
131
131
132 def changectx(repo, req):
132 def changectx(repo, req):
133 changeid = "tip"
133 changeid = "tip"
134 if 'node' in req.form:
134 if 'node' in req.form:
135 changeid = req.form['node'][0]
135 changeid = req.form['node'][0]
136 elif 'manifest' in req.form:
136 elif 'manifest' in req.form:
137 changeid = req.form['manifest'][0]
137 changeid = req.form['manifest'][0]
138
138
139 try:
139 try:
140 ctx = repo[changeid]
140 ctx = repo[changeid]
141 except error.RepoError:
141 except error.RepoError:
142 man = repo.manifest
142 man = repo.manifest
143 ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
143 ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
144
144
145 return ctx
145 return ctx
146
146
147 def filectx(repo, req):
147 def filectx(repo, req):
148 path = cleanpath(repo, req.form['file'][0])
148 path = cleanpath(repo, req.form['file'][0])
149 if 'node' in req.form:
149 if 'node' in req.form:
150 changeid = req.form['node'][0]
150 changeid = req.form['node'][0]
151 else:
151 else:
152 changeid = req.form['filenode'][0]
152 changeid = req.form['filenode'][0]
153 try:
153 try:
154 fctx = repo[changeid][path]
154 fctx = repo[changeid][path]
155 except error.RepoError:
155 except error.RepoError:
156 fctx = repo.filectx(path, fileid=changeid)
156 fctx = repo.filectx(path, fileid=changeid)
157
157
158 return fctx
158 return fctx
159
159
160 def listfilediffs(tmpl, files, node, max):
160 def listfilediffs(tmpl, files, node, max):
161 for f in files[:max]:
161 for f in files[:max]:
162 yield tmpl('filedifflink', node=hex(node), file=f)
162 yield tmpl('filedifflink', node=hex(node), file=f)
163 if len(files) > max:
163 if len(files) > max:
164 yield tmpl('fileellipses')
164 yield tmpl('fileellipses')
165
165
166 def diffs(repo, tmpl, ctx, files, parity, style):
166 def diffs(repo, tmpl, ctx, files, parity, style):
167
167
168 def countgen():
168 def countgen():
169 start = 1
169 start = 1
170 while True:
170 while True:
171 yield start
171 yield start
172 start += 1
172 start += 1
173
173
174 blockcount = countgen()
174 blockcount = countgen()
175 def prettyprintlines(diff):
175 def prettyprintlines(diff):
176 blockno = blockcount.next()
176 blockno = blockcount.next()
177 for lineno, l in enumerate(diff.splitlines(True)):
177 for lineno, l in enumerate(diff.splitlines(True)):
178 lineno = "%d.%d" % (blockno, lineno + 1)
178 lineno = "%d.%d" % (blockno, lineno + 1)
179 if l.startswith('+'):
179 if l.startswith('+'):
180 ltype = "difflineplus"
180 ltype = "difflineplus"
181 elif l.startswith('-'):
181 elif l.startswith('-'):
182 ltype = "difflineminus"
182 ltype = "difflineminus"
183 elif l.startswith('@'):
183 elif l.startswith('@'):
184 ltype = "difflineat"
184 ltype = "difflineat"
185 else:
185 else:
186 ltype = "diffline"
186 ltype = "diffline"
187 yield tmpl(ltype,
187 yield tmpl(ltype,
188 line=l,
188 line=l,
189 lineid="l%s" % lineno,
189 lineid="l%s" % lineno,
190 linenumber="% 8s" % lineno)
190 linenumber="% 8s" % lineno)
191
191
192 if files:
192 if files:
193 m = match.exact(repo.root, repo.getcwd(), files)
193 m = match.exact(repo.root, repo.getcwd(), files)
194 else:
194 else:
195 m = match.always(repo.root, repo.getcwd())
195 m = match.always(repo.root, repo.getcwd())
196
196
197 diffopts = patch.diffopts(repo.ui, untrusted=True)
197 diffopts = patch.diffopts(repo.ui, untrusted=True)
198 parents = ctx.parents()
198 parents = ctx.parents()
199 node1 = parents and parents[0].node() or nullid
199 node1 = parents and parents[0].node() or nullid
200 node2 = ctx.node()
200 node2 = ctx.node()
201
201
202 block = []
202 block = []
203 for chunk in patch.diff(repo, node1, node2, m, opts=diffopts):
203 for chunk in patch.diff(repo, node1, node2, m, opts=diffopts):
204 if chunk.startswith('diff') and block:
204 if chunk.startswith('diff') and block:
205 yield tmpl('diffblock', parity=parity.next(),
205 yield tmpl('diffblock', parity=parity.next(),
206 lines=prettyprintlines(''.join(block)))
206 lines=prettyprintlines(''.join(block)))
207 block = []
207 block = []
208 if chunk.startswith('diff') and style != 'raw':
208 if chunk.startswith('diff') and style != 'raw':
209 chunk = ''.join(chunk.splitlines(True)[1:])
209 chunk = ''.join(chunk.splitlines(True)[1:])
210 block.append(chunk)
210 block.append(chunk)
211 yield tmpl('diffblock', parity=parity.next(),
211 yield tmpl('diffblock', parity=parity.next(),
212 lines=prettyprintlines(''.join(block)))
212 lines=prettyprintlines(''.join(block)))
213
213
214 def diffstat(tmpl, ctx, parity):
214 def diffstat(tmpl, ctx, parity):
215 '''Return a diffstat template for each file in the diff.'''
215 '''Return a diffstat template for each file in the diff.'''
216
216
217 stats = patch.diffstatdata(util.iterlines(ctx.diff()))
217 stats = patch.diffstatdata(util.iterlines(ctx.diff()))
218 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
218 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
219 files = ctx.files()
219 files = ctx.files()
220
220
221 def pct(i):
221 def pct(i):
222 if maxtotal == 0:
222 if maxtotal == 0:
223 return 0
223 return 0
224 return (float(i) / maxtotal) * 100
224 return (float(i) / maxtotal) * 100
225
225
226 fileno = 0
226 for filename, adds, removes, isbinary in stats:
227 for filename, adds, removes, isbinary in stats:
227 template = filename in files and 'diffstatlink' or 'diffstatnolink'
228 template = filename in files and 'diffstatlink' or 'diffstatnolink'
228 total = adds + removes
229 total = adds + removes
229 yield tmpl(template, node=ctx.hex(), file=filename,
230 fileno += 1
231 yield tmpl(template, node=ctx.hex(), file=filename, fileno=fileno,
230 total=total, addpct=pct(adds), removepct=pct(removes),
232 total=total, addpct=pct(adds), removepct=pct(removes),
231 parity=parity.next())
233 parity=parity.next())
232
234
233 class sessionvars(object):
235 class sessionvars(object):
234 def __init__(self, vars, start='?'):
236 def __init__(self, vars, start='?'):
235 self.start = start
237 self.start = start
236 self.vars = vars
238 self.vars = vars
237 def __getitem__(self, key):
239 def __getitem__(self, key):
238 return self.vars[key]
240 return self.vars[key]
239 def __setitem__(self, key, value):
241 def __setitem__(self, key, value):
240 self.vars[key] = value
242 self.vars[key] = value
241 def __copy__(self):
243 def __copy__(self):
242 return sessionvars(copy.copy(self.vars), self.start)
244 return sessionvars(copy.copy(self.vars), self.start)
243 def __iter__(self):
245 def __iter__(self):
244 separator = self.start
246 separator = self.start
245 for key, value in self.vars.iteritems():
247 for key, value in self.vars.iteritems():
246 yield {'name': key, 'value': str(value), 'separator': separator}
248 yield {'name': key, 'value': str(value), 'separator': separator}
247 separator = '&'
249 separator = '&'
248
250
249 class wsgiui(ui.ui):
251 class wsgiui(ui.ui):
250 # default termwidth breaks under mod_wsgi
252 # default termwidth breaks under mod_wsgi
251 def termwidth(self):
253 def termwidth(self):
252 return 80
254 return 80
General Comments 0
You need to be logged in to leave comments. Login now