121
|
1 # hgweb/webutil.py - utility library for the web interface.
|
|
2 #
|
|
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
|
|
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
|
|
5 #
|
|
6 # This software may be used and distributed according to the terms of the
|
|
7 # GNU General Public License version 2, incorporated herein by reference.
|
|
8
|
|
9 import os, copy
|
|
10 from mercurial import match, patch, util, error
|
|
11 from mercurial.node import hex, nullid
|
|
12
|
|
13 def up(p):
|
|
14 if p[0] != "/":
|
|
15 p = "/" + p
|
|
16 if p[-1] == "/":
|
|
17 p = p[:-1]
|
|
18 up = os.path.dirname(p)
|
|
19 if up == "/":
|
|
20 return "/"
|
|
21 return up + "/"
|
|
22
|
|
23 def revnavgen(pos, pagelen, limit, nodefunc):
|
|
24 def seq(factor, limit=None):
|
|
25 if limit:
|
|
26 yield limit
|
|
27 if limit >= 20 and limit <= 40:
|
|
28 yield 50
|
|
29 else:
|
|
30 yield 1 * factor
|
|
31 yield 3 * factor
|
|
32 for f in seq(factor * 10):
|
|
33 yield f
|
|
34
|
|
35 def nav(**map):
|
|
36 l = []
|
|
37 last = 0
|
|
38 for f in seq(1, pagelen):
|
|
39 if f < pagelen or f <= last:
|
|
40 continue
|
|
41 if f > limit:
|
|
42 break
|
|
43 last = f
|
|
44 if pos + f < limit:
|
|
45 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
|
|
46 if pos - f >= 0:
|
|
47 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
|
|
48
|
|
49 try:
|
|
50 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
|
|
51
|
|
52 for label, node in l:
|
|
53 yield {"label": label, "node": node}
|
|
54
|
|
55 yield {"label": "tip", "node": "tip"}
|
|
56 except error.RepoError:
|
|
57 pass
|
|
58
|
|
59 return nav
|
|
60
|
|
61 def _siblings(siblings=[], hiderev=None):
|
|
62 siblings = [s for s in siblings if s.node() != nullid]
|
|
63 if len(siblings) == 1 and siblings[0].rev() == hiderev:
|
|
64 return
|
|
65 for s in siblings:
|
|
66 d = {'node': hex(s.node()), 'rev': s.rev()}
|
|
67 d['user'] = s.user()
|
|
68 d['date'] = s.date()
|
|
69 d['description'] = s.description()
|
|
70 d['branch'] = s.branch()
|
|
71 if hasattr(s, 'path'):
|
|
72 d['file'] = s.path()
|
|
73 yield d
|
|
74
|
|
75 def parents(ctx, hide=None):
|
|
76 return _siblings(ctx.parents(), hide)
|
|
77
|
|
78 def children(ctx, hide=None):
|
|
79 return _siblings(ctx.children(), hide)
|
|
80
|
|
81 def renamelink(fctx):
|
|
82 r = fctx.renamed()
|
|
83 if r:
|
|
84 return [dict(file=r[0], node=hex(r[1]))]
|
|
85 return []
|
|
86
|
|
87 def nodetagsdict(repo, node):
|
|
88 return [{"name": i} for i in repo.nodetags(node)]
|
|
89
|
|
90 def nodebranchdict(repo, ctx):
|
|
91 branches = []
|
|
92 branch = ctx.branch()
|
|
93 # If this is an empty repo, ctx.node() == nullid,
|
|
94 # ctx.branch() == 'default', but branchtags() is
|
|
95 # an empty dict. Using dict.get avoids a traceback.
|
|
96 if repo.branchtags().get(branch) == ctx.node():
|
|
97 branches.append({"name": branch})
|
|
98 return branches
|
|
99
|
|
100 def nodeinbranch(repo, ctx):
|
|
101 branches = []
|
|
102 branch = ctx.branch()
|
|
103 if branch != 'default' and repo.branchtags().get(branch) != ctx.node():
|
|
104 branches.append({"name": branch})
|
|
105 return branches
|
|
106
|
|
107 def nodebranchnodefault(ctx):
|
|
108 branches = []
|
|
109 branch = ctx.branch()
|
|
110 if branch != 'default':
|
|
111 branches.append({"name": branch})
|
|
112 return branches
|
|
113
|
|
114 def showtag(repo, tmpl, t1, node=nullid, **args):
|
|
115 for t in repo.nodetags(node):
|
|
116 yield tmpl(t1, tag=t, **args)
|
|
117
|
|
118 def cleanpath(repo, path):
|
|
119 path = path.lstrip('/')
|
|
120 return util.canonpath(repo.root, '', path)
|
|
121
|
|
122 def changectx(repo, req):
|
|
123 changeid = "tip"
|
|
124 if 'node' in req.form:
|
|
125 changeid = req.form['node'][0]
|
|
126 elif 'manifest' in req.form:
|
|
127 changeid = req.form['manifest'][0]
|
|
128
|
|
129 try:
|
|
130 ctx = repo[changeid]
|
|
131 except error.RepoError:
|
|
132 man = repo.manifest
|
|
133 ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
|
|
134
|
|
135 return ctx
|
|
136
|
|
137 def filectx(repo, req):
|
|
138 path = cleanpath(repo, req.form['file'][0])
|
|
139 if 'node' in req.form:
|
|
140 changeid = req.form['node'][0]
|
|
141 else:
|
|
142 changeid = req.form['filenode'][0]
|
|
143 try:
|
|
144 fctx = repo[changeid][path]
|
|
145 except error.RepoError:
|
|
146 fctx = repo.filectx(path, fileid=changeid)
|
|
147
|
|
148 return fctx
|
|
149
|
|
150 def listfilediffs(tmpl, files, node, max):
|
|
151 for f in files[:max]:
|
|
152 yield tmpl('filedifflink', node=hex(node), file=f)
|
|
153 if len(files) > max:
|
|
154 yield tmpl('fileellipses')
|
|
155
|
|
156 def diffs(repo, tmpl, ctx, files, parity):
|
|
157
|
|
158 def countgen():
|
|
159 start = 1
|
|
160 while True:
|
|
161 yield start
|
|
162 start += 1
|
|
163
|
|
164 blockcount = countgen()
|
|
165 def prettyprintlines(diff):
|
|
166 blockno = blockcount.next()
|
|
167 for lineno, l in enumerate(diff.splitlines(True)):
|
|
168 lineno = "%d.%d" % (blockno, lineno + 1)
|
|
169 if l.startswith('+'):
|
|
170 ltype = "difflineplus"
|
|
171 elif l.startswith('-'):
|
|
172 ltype = "difflineminus"
|
|
173 elif l.startswith('@'):
|
|
174 ltype = "difflineat"
|
|
175 else:
|
|
176 ltype = "diffline"
|
|
177 yield tmpl(ltype,
|
|
178 line=l,
|
|
179 lineid="l%s" % lineno,
|
|
180 linenumber="% 8s" % lineno)
|
|
181
|
|
182 if files:
|
|
183 m = match.exact(repo.root, repo.getcwd(), files)
|
|
184 else:
|
|
185 m = match.always(repo.root, repo.getcwd())
|
|
186
|
|
187 diffopts = patch.diffopts(repo.ui, untrusted=True)
|
|
188 parents = ctx.parents()
|
|
189 node1 = parents and parents[0].node() or nullid
|
|
190 node2 = ctx.node()
|
|
191
|
|
192 block = []
|
|
193 for chunk in patch.diff(repo, node1, node2, m, opts=diffopts):
|
|
194 if chunk.startswith('diff') and block:
|
|
195 yield tmpl('diffblock', parity=parity.next(),
|
|
196 lines=prettyprintlines(''.join(block)))
|
|
197 block = []
|
|
198 if chunk.startswith('diff'):
|
|
199 chunk = ''.join(chunk.splitlines(True)[1:])
|
|
200 block.append(chunk)
|
|
201 yield tmpl('diffblock', parity=parity.next(),
|
|
202 lines=prettyprintlines(''.join(block)))
|
|
203
|
|
204 class sessionvars(object):
|
|
205 def __init__(self, vars, start='?'):
|
|
206 self.start = start
|
|
207 self.vars = vars
|
|
208 def __getitem__(self, key):
|
|
209 return self.vars[key]
|
|
210 def __setitem__(self, key, value):
|
|
211 self.vars[key] = value
|
|
212 def __copy__(self):
|
|
213 return sessionvars(copy.copy(self.vars), self.start)
|
|
214 def __iter__(self):
|
|
215 separator = self.start
|
|
216 for key, value in self.vars.iteritems():
|
|
217 yield {'name': key, 'value': str(value), 'separator': separator}
|
|
218 separator = '&'
|