Mercurial > hg
comparison mercurial/hgweb/webutil.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | 6ccf539aec71 |
children | 687b865b95ad |
comparison
equal
deleted
inserted
replaced
43075:57875cf423c9 | 43076:2372284d9457 |
---|---|
39 templateutil, | 39 templateutil, |
40 ui as uimod, | 40 ui as uimod, |
41 util, | 41 util, |
42 ) | 42 ) |
43 | 43 |
44 from ..utils import ( | 44 from ..utils import stringutil |
45 stringutil, | 45 |
46 archivespecs = util.sortdict( | |
47 ( | |
48 ('zip', ('application/zip', 'zip', '.zip', None)), | |
49 ('gz', ('application/x-gzip', 'tgz', '.tar.gz', None)), | |
50 ('bz2', ('application/x-bzip2', 'tbz2', '.tar.bz2', None)), | |
51 ) | |
46 ) | 52 ) |
47 | 53 |
48 archivespecs = util.sortdict(( | |
49 ('zip', ('application/zip', 'zip', '.zip', None)), | |
50 ('gz', ('application/x-gzip', 'tgz', '.tar.gz', None)), | |
51 ('bz2', ('application/x-bzip2', 'tbz2', '.tar.bz2', None)), | |
52 )) | |
53 | 54 |
54 def archivelist(ui, nodeid, url=None): | 55 def archivelist(ui, nodeid, url=None): |
55 allowed = ui.configlist('web', 'allow-archive', untrusted=True) | 56 allowed = ui.configlist('web', 'allow-archive', untrusted=True) |
56 archives = [] | 57 archives = [] |
57 | 58 |
58 for typ, spec in archivespecs.iteritems(): | 59 for typ, spec in archivespecs.iteritems(): |
59 if typ in allowed or ui.configbool('web', 'allow' + typ, | 60 if typ in allowed or ui.configbool( |
60 untrusted=True): | 61 'web', 'allow' + typ, untrusted=True |
61 archives.append({ | 62 ): |
62 'type': typ, | 63 archives.append( |
63 'extension': spec[2], | 64 {'type': typ, 'extension': spec[2], 'node': nodeid, 'url': url,} |
64 'node': nodeid, | 65 ) |
65 'url': url, | |
66 }) | |
67 | 66 |
68 return templateutil.mappinglist(archives) | 67 return templateutil.mappinglist(archives) |
68 | |
69 | 69 |
70 def up(p): | 70 def up(p): |
71 if p[0:1] != "/": | 71 if p[0:1] != "/": |
72 p = "/" + p | 72 p = "/" + p |
73 if p[-1:] == "/": | 73 if p[-1:] == "/": |
74 p = p[:-1] | 74 p = p[:-1] |
75 up = os.path.dirname(p) | 75 up = os.path.dirname(p) |
76 if up == "/": | 76 if up == "/": |
77 return "/" | 77 return "/" |
78 return up + "/" | 78 return up + "/" |
79 | |
79 | 80 |
80 def _navseq(step, firststep=None): | 81 def _navseq(step, firststep=None): |
81 if firststep: | 82 if firststep: |
82 yield firststep | 83 yield firststep |
83 if firststep >= 20 and firststep <= 40: | 84 if firststep >= 20 and firststep <= 40: |
90 while True: | 91 while True: |
91 yield 1 * step | 92 yield 1 * step |
92 yield 3 * step | 93 yield 3 * step |
93 step *= 10 | 94 step *= 10 |
94 | 95 |
96 | |
95 class revnav(object): | 97 class revnav(object): |
96 | |
97 def __init__(self, repo): | 98 def __init__(self, repo): |
98 """Navigation generation object | 99 """Navigation generation object |
99 | 100 |
100 :repo: repo object we generate nav for | 101 :repo: repo object we generate nav for |
101 """ | 102 """ |
130 - containing a dictionary with a `before` and `after` key | 131 - containing a dictionary with a `before` and `after` key |
131 - values are dictionaries with `label` and `node` keys | 132 - values are dictionaries with `label` and `node` keys |
132 """ | 133 """ |
133 if not self: | 134 if not self: |
134 # empty repo | 135 # empty repo |
135 return templateutil.mappinglist([ | 136 return templateutil.mappinglist( |
136 {'before': templateutil.mappinglist([]), | 137 [ |
137 'after': templateutil.mappinglist([])}, | 138 { |
138 ]) | 139 'before': templateutil.mappinglist([]), |
140 'after': templateutil.mappinglist([]), | |
141 }, | |
142 ] | |
143 ) | |
139 | 144 |
140 targets = [] | 145 targets = [] |
141 for f in _navseq(1, pagelen): | 146 for f in _navseq(1, pagelen): |
142 if f > limit: | 147 if f > limit: |
143 break | 148 break |
150 navafter = [] | 155 navafter = [] |
151 for rev in targets: | 156 for rev in targets: |
152 if rev not in self._revlog: | 157 if rev not in self._revlog: |
153 continue | 158 continue |
154 if pos < rev < limit: | 159 if pos < rev < limit: |
155 navafter.append({'label': '+%d' % abs(rev - pos), | 160 navafter.append( |
156 'node': self.hex(rev)}) | 161 {'label': '+%d' % abs(rev - pos), 'node': self.hex(rev)} |
162 ) | |
157 if 0 < rev < pos: | 163 if 0 < rev < pos: |
158 navbefore.append({'label': '-%d' % abs(rev - pos), | 164 navbefore.append( |
159 'node': self.hex(rev)}) | 165 {'label': '-%d' % abs(rev - pos), 'node': self.hex(rev)} |
166 ) | |
160 | 167 |
161 navafter.append({'label': 'tip', 'node': 'tip'}) | 168 navafter.append({'label': 'tip', 'node': 'tip'}) |
162 | 169 |
163 # TODO: maybe this can be a scalar object supporting tomap() | 170 # TODO: maybe this can be a scalar object supporting tomap() |
164 return templateutil.mappinglist([ | 171 return templateutil.mappinglist( |
165 {'before': templateutil.mappinglist(navbefore), | 172 [ |
166 'after': templateutil.mappinglist(navafter)}, | 173 { |
167 ]) | 174 'before': templateutil.mappinglist(navbefore), |
175 'after': templateutil.mappinglist(navafter), | |
176 }, | |
177 ] | |
178 ) | |
179 | |
168 | 180 |
169 class filerevnav(revnav): | 181 class filerevnav(revnav): |
170 | |
171 def __init__(self, repo, path): | 182 def __init__(self, repo, path): |
172 """Navigation generation object | 183 """Navigation generation object |
173 | 184 |
174 :repo: repo object we generate nav for | 185 :repo: repo object we generate nav for |
175 :path: path of the file we generate nav for | 186 :path: path of the file we generate nav for |
179 # used for hex generation | 190 # used for hex generation |
180 self._revlog = repo.file(path) | 191 self._revlog = repo.file(path) |
181 | 192 |
182 def hex(self, rev): | 193 def hex(self, rev): |
183 return hex(self._changelog.node(self._revlog.linkrev(rev))) | 194 return hex(self._changelog.node(self._revlog.linkrev(rev))) |
195 | |
184 | 196 |
185 # TODO: maybe this can be a wrapper class for changectx/filectx list, which | 197 # TODO: maybe this can be a wrapper class for changectx/filectx list, which |
186 # yields {'ctx': ctx} | 198 # yields {'ctx': ctx} |
187 def _ctxsgen(context, ctxs): | 199 def _ctxsgen(context, ctxs): |
188 for s in ctxs: | 200 for s in ctxs: |
196 } | 208 } |
197 if util.safehasattr(s, 'path'): | 209 if util.safehasattr(s, 'path'): |
198 d['file'] = s.path() | 210 d['file'] = s.path() |
199 yield d | 211 yield d |
200 | 212 |
213 | |
201 def _siblings(siblings=None, hiderev=None): | 214 def _siblings(siblings=None, hiderev=None): |
202 if siblings is None: | 215 if siblings is None: |
203 siblings = [] | 216 siblings = [] |
204 siblings = [s for s in siblings if s.node() != nullid] | 217 siblings = [s for s in siblings if s.node() != nullid] |
205 if len(siblings) == 1 and siblings[0].rev() == hiderev: | 218 if len(siblings) == 1 and siblings[0].rev() == hiderev: |
206 siblings = [] | 219 siblings = [] |
207 return templateutil.mappinggenerator(_ctxsgen, args=(siblings,)) | 220 return templateutil.mappinggenerator(_ctxsgen, args=(siblings,)) |
208 | 221 |
222 | |
209 def difffeatureopts(req, ui, section): | 223 def difffeatureopts(req, ui, section): |
210 diffopts = diffutil.difffeatureopts(ui, untrusted=True, | 224 diffopts = diffutil.difffeatureopts( |
211 section=section, whitespace=True) | 225 ui, untrusted=True, section=section, whitespace=True |
226 ) | |
212 | 227 |
213 for k in ('ignorews', 'ignorewsamount', 'ignorewseol', 'ignoreblanklines'): | 228 for k in ('ignorews', 'ignorewsamount', 'ignorewseol', 'ignoreblanklines'): |
214 v = req.qsparams.get(k) | 229 v = req.qsparams.get(k) |
215 if v is not None: | 230 if v is not None: |
216 v = stringutil.parsebool(v) | 231 v = stringutil.parsebool(v) |
217 setattr(diffopts, k, v if v is not None else True) | 232 setattr(diffopts, k, v if v is not None else True) |
218 | 233 |
219 return diffopts | 234 return diffopts |
220 | 235 |
236 | |
221 def annotate(req, fctx, ui): | 237 def annotate(req, fctx, ui): |
222 diffopts = difffeatureopts(req, ui, 'annotate') | 238 diffopts = difffeatureopts(req, ui, 'annotate') |
223 return fctx.annotate(follow=True, diffopts=diffopts) | 239 return fctx.annotate(follow=True, diffopts=diffopts) |
240 | |
224 | 241 |
225 def parents(ctx, hide=None): | 242 def parents(ctx, hide=None): |
226 if isinstance(ctx, context.basefilectx): | 243 if isinstance(ctx, context.basefilectx): |
227 introrev = ctx.introrev() | 244 introrev = ctx.introrev() |
228 if ctx.changectx().rev() != introrev: | 245 if ctx.changectx().rev() != introrev: |
229 return _siblings([ctx.repo()[introrev]], hide) | 246 return _siblings([ctx.repo()[introrev]], hide) |
230 return _siblings(ctx.parents(), hide) | 247 return _siblings(ctx.parents(), hide) |
231 | 248 |
249 | |
232 def children(ctx, hide=None): | 250 def children(ctx, hide=None): |
233 return _siblings(ctx.children(), hide) | 251 return _siblings(ctx.children(), hide) |
252 | |
234 | 253 |
235 def renamelink(fctx): | 254 def renamelink(fctx): |
236 r = fctx.renamed() | 255 r = fctx.renamed() |
237 if r: | 256 if r: |
238 return templateutil.mappinglist([{'file': r[0], 'node': hex(r[1])}]) | 257 return templateutil.mappinglist([{'file': r[0], 'node': hex(r[1])}]) |
239 return templateutil.mappinglist([]) | 258 return templateutil.mappinglist([]) |
240 | 259 |
260 | |
241 def nodetagsdict(repo, node): | 261 def nodetagsdict(repo, node): |
242 return templateutil.hybridlist(repo.nodetags(node), name='name') | 262 return templateutil.hybridlist(repo.nodetags(node), name='name') |
243 | 263 |
264 | |
244 def nodebookmarksdict(repo, node): | 265 def nodebookmarksdict(repo, node): |
245 return templateutil.hybridlist(repo.nodebookmarks(node), name='name') | 266 return templateutil.hybridlist(repo.nodebookmarks(node), name='name') |
267 | |
246 | 268 |
247 def nodebranchdict(repo, ctx): | 269 def nodebranchdict(repo, ctx): |
248 branches = [] | 270 branches = [] |
249 branch = ctx.branch() | 271 branch = ctx.branch() |
250 # If this is an empty repo, ctx.node() == nullid, | 272 # If this is an empty repo, ctx.node() == nullid, |
255 branchnode = None | 277 branchnode = None |
256 if branchnode == ctx.node(): | 278 if branchnode == ctx.node(): |
257 branches.append(branch) | 279 branches.append(branch) |
258 return templateutil.hybridlist(branches, name='name') | 280 return templateutil.hybridlist(branches, name='name') |
259 | 281 |
282 | |
260 def nodeinbranch(repo, ctx): | 283 def nodeinbranch(repo, ctx): |
261 branches = [] | 284 branches = [] |
262 branch = ctx.branch() | 285 branch = ctx.branch() |
263 try: | 286 try: |
264 branchnode = repo.branchtip(branch) | 287 branchnode = repo.branchtip(branch) |
266 branchnode = None | 289 branchnode = None |
267 if branch != 'default' and branchnode != ctx.node(): | 290 if branch != 'default' and branchnode != ctx.node(): |
268 branches.append(branch) | 291 branches.append(branch) |
269 return templateutil.hybridlist(branches, name='name') | 292 return templateutil.hybridlist(branches, name='name') |
270 | 293 |
294 | |
271 def nodebranchnodefault(ctx): | 295 def nodebranchnodefault(ctx): |
272 branches = [] | 296 branches = [] |
273 branch = ctx.branch() | 297 branch = ctx.branch() |
274 if branch != 'default': | 298 if branch != 'default': |
275 branches.append(branch) | 299 branches.append(branch) |
276 return templateutil.hybridlist(branches, name='name') | 300 return templateutil.hybridlist(branches, name='name') |
277 | 301 |
302 | |
278 def _nodenamesgen(context, f, node, name): | 303 def _nodenamesgen(context, f, node, name): |
279 for t in f(node): | 304 for t in f(node): |
280 yield {name: t} | 305 yield {name: t} |
281 | 306 |
307 | |
282 def showtag(repo, t1, node=nullid): | 308 def showtag(repo, t1, node=nullid): |
283 args = (repo.nodetags, node, 'tag') | 309 args = (repo.nodetags, node, 'tag') |
284 return templateutil.mappinggenerator(_nodenamesgen, args=args, name=t1) | 310 return templateutil.mappinggenerator(_nodenamesgen, args=args, name=t1) |
285 | 311 |
312 | |
286 def showbookmark(repo, t1, node=nullid): | 313 def showbookmark(repo, t1, node=nullid): |
287 args = (repo.nodebookmarks, node, 'bookmark') | 314 args = (repo.nodebookmarks, node, 'bookmark') |
288 return templateutil.mappinggenerator(_nodenamesgen, args=args, name=t1) | 315 return templateutil.mappinggenerator(_nodenamesgen, args=args, name=t1) |
316 | |
289 | 317 |
290 def branchentries(repo, stripecount, limit=0): | 318 def branchentries(repo, stripecount, limit=0): |
291 tips = [] | 319 tips = [] |
292 heads = repo.heads() | 320 heads = repo.heads() |
293 parity = paritygen(stripecount) | 321 parity = paritygen(stripecount) |
311 yield { | 339 yield { |
312 'parity': next(parity), | 340 'parity': next(parity), |
313 'branch': ctx.branch(), | 341 'branch': ctx.branch(), |
314 'status': status, | 342 'status': status, |
315 'node': ctx.hex(), | 343 'node': ctx.hex(), |
316 'date': ctx.date() | 344 'date': ctx.date(), |
317 } | 345 } |
318 | 346 |
319 return templateutil.mappinggenerator(entries) | 347 return templateutil.mappinggenerator(entries) |
348 | |
320 | 349 |
321 def cleanpath(repo, path): | 350 def cleanpath(repo, path): |
322 path = path.lstrip('/') | 351 path = path.lstrip('/') |
323 auditor = pathutil.pathauditor(repo.root, realfs=False) | 352 auditor = pathutil.pathauditor(repo.root, realfs=False) |
324 return pathutil.canonpath(repo.root, '', path, auditor=auditor) | 353 return pathutil.canonpath(repo.root, '', path, auditor=auditor) |
354 | |
325 | 355 |
326 def changectx(repo, req): | 356 def changectx(repo, req): |
327 changeid = "tip" | 357 changeid = "tip" |
328 if 'node' in req.qsparams: | 358 if 'node' in req.qsparams: |
329 changeid = req.qsparams['node'] | 359 changeid = req.qsparams['node'] |
330 ipos = changeid.find(':') | 360 ipos = changeid.find(':') |
331 if ipos != -1: | 361 if ipos != -1: |
332 changeid = changeid[(ipos + 1):] | 362 changeid = changeid[(ipos + 1) :] |
333 | 363 |
334 return scmutil.revsymbol(repo, changeid) | 364 return scmutil.revsymbol(repo, changeid) |
365 | |
335 | 366 |
336 def basechangectx(repo, req): | 367 def basechangectx(repo, req): |
337 if 'node' in req.qsparams: | 368 if 'node' in req.qsparams: |
338 changeid = req.qsparams['node'] | 369 changeid = req.qsparams['node'] |
339 ipos = changeid.find(':') | 370 ipos = changeid.find(':') |
340 if ipos != -1: | 371 if ipos != -1: |
341 changeid = changeid[:ipos] | 372 changeid = changeid[:ipos] |
342 return scmutil.revsymbol(repo, changeid) | 373 return scmutil.revsymbol(repo, changeid) |
343 | 374 |
344 return None | 375 return None |
376 | |
345 | 377 |
346 def filectx(repo, req): | 378 def filectx(repo, req): |
347 if 'file' not in req.qsparams: | 379 if 'file' not in req.qsparams: |
348 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given') | 380 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given') |
349 path = cleanpath(repo, req.qsparams['file']) | 381 path = cleanpath(repo, req.qsparams['file']) |
358 except error.RepoError: | 390 except error.RepoError: |
359 fctx = repo.filectx(path, fileid=changeid) | 391 fctx = repo.filectx(path, fileid=changeid) |
360 | 392 |
361 return fctx | 393 return fctx |
362 | 394 |
395 | |
363 def linerange(req): | 396 def linerange(req): |
364 linerange = req.qsparams.getall('linerange') | 397 linerange = req.qsparams.getall('linerange') |
365 if not linerange: | 398 if not linerange: |
366 return None | 399 return None |
367 if len(linerange) > 1: | 400 if len(linerange) > 1: |
368 raise ErrorResponse(HTTP_BAD_REQUEST, | 401 raise ErrorResponse(HTTP_BAD_REQUEST, 'redundant linerange parameter') |
369 'redundant linerange parameter') | |
370 try: | 402 try: |
371 fromline, toline = map(int, linerange[0].split(':', 1)) | 403 fromline, toline = map(int, linerange[0].split(':', 1)) |
372 except ValueError: | 404 except ValueError: |
373 raise ErrorResponse(HTTP_BAD_REQUEST, | 405 raise ErrorResponse(HTTP_BAD_REQUEST, 'invalid linerange parameter') |
374 'invalid linerange parameter') | |
375 try: | 406 try: |
376 return util.processlinerange(fromline, toline) | 407 return util.processlinerange(fromline, toline) |
377 except error.ParseError as exc: | 408 except error.ParseError as exc: |
378 raise ErrorResponse(HTTP_BAD_REQUEST, pycompat.bytestr(exc)) | 409 raise ErrorResponse(HTTP_BAD_REQUEST, pycompat.bytestr(exc)) |
379 | 410 |
411 | |
380 def formatlinerange(fromline, toline): | 412 def formatlinerange(fromline, toline): |
381 return '%d:%d' % (fromline + 1, toline) | 413 return '%d:%d' % (fromline + 1, toline) |
414 | |
382 | 415 |
383 def _succsandmarkersgen(context, mapping): | 416 def _succsandmarkersgen(context, mapping): |
384 repo = context.resource(mapping, 'repo') | 417 repo = context.resource(mapping, 'repo') |
385 itemmappings = templatekw.showsuccsandmarkers(context, mapping) | 418 itemmappings = templatekw.showsuccsandmarkers(context, mapping) |
386 for item in itemmappings.tovalue(context, mapping): | 419 for item in itemmappings.tovalue(context, mapping): |
387 item['successors'] = _siblings(repo[successor] | 420 item['successors'] = _siblings( |
388 for successor in item['successors']) | 421 repo[successor] for successor in item['successors'] |
422 ) | |
389 yield item | 423 yield item |
424 | |
390 | 425 |
391 def succsandmarkers(context, mapping): | 426 def succsandmarkers(context, mapping): |
392 return templateutil.mappinggenerator(_succsandmarkersgen, args=(mapping,)) | 427 return templateutil.mappinggenerator(_succsandmarkersgen, args=(mapping,)) |
393 | 428 |
429 | |
394 # teach templater succsandmarkers is switched to (context, mapping) API | 430 # teach templater succsandmarkers is switched to (context, mapping) API |
395 succsandmarkers._requires = {'repo', 'ctx'} | 431 succsandmarkers._requires = {'repo', 'ctx'} |
432 | |
396 | 433 |
397 def _whyunstablegen(context, mapping): | 434 def _whyunstablegen(context, mapping): |
398 repo = context.resource(mapping, 'repo') | 435 repo = context.resource(mapping, 'repo') |
399 ctx = context.resource(mapping, 'ctx') | 436 ctx = context.resource(mapping, 'ctx') |
400 | 437 |
402 for entry in entries: | 439 for entry in entries: |
403 if entry.get('divergentnodes'): | 440 if entry.get('divergentnodes'): |
404 entry['divergentnodes'] = _siblings(entry['divergentnodes']) | 441 entry['divergentnodes'] = _siblings(entry['divergentnodes']) |
405 yield entry | 442 yield entry |
406 | 443 |
444 | |
407 def whyunstable(context, mapping): | 445 def whyunstable(context, mapping): |
408 return templateutil.mappinggenerator(_whyunstablegen, args=(mapping,)) | 446 return templateutil.mappinggenerator(_whyunstablegen, args=(mapping,)) |
409 | 447 |
448 | |
410 whyunstable._requires = {'repo', 'ctx'} | 449 whyunstable._requires = {'repo', 'ctx'} |
450 | |
411 | 451 |
412 def commonentry(repo, ctx): | 452 def commonentry(repo, ctx): |
413 node = scmutil.binnode(ctx) | 453 node = scmutil.binnode(ctx) |
414 return { | 454 return { |
415 # TODO: perhaps ctx.changectx() should be assigned if ctx is a | 455 # TODO: perhaps ctx.changectx() should be assigned if ctx is a |
423 'date': ctx.date(), | 463 'date': ctx.date(), |
424 'extra': ctx.extra(), | 464 'extra': ctx.extra(), |
425 'phase': ctx.phasestr(), | 465 'phase': ctx.phasestr(), |
426 'obsolete': ctx.obsolete(), | 466 'obsolete': ctx.obsolete(), |
427 'succsandmarkers': succsandmarkers, | 467 'succsandmarkers': succsandmarkers, |
428 'instabilities': templateutil.hybridlist(ctx.instabilities(), | 468 'instabilities': templateutil.hybridlist( |
429 name='instability'), | 469 ctx.instabilities(), name='instability' |
470 ), | |
430 'whyunstable': whyunstable, | 471 'whyunstable': whyunstable, |
431 'branch': nodebranchnodefault(ctx), | 472 'branch': nodebranchnodefault(ctx), |
432 'inbranch': nodeinbranch(repo, ctx), | 473 'inbranch': nodeinbranch(repo, ctx), |
433 'branches': nodebranchdict(repo, ctx), | 474 'branches': nodebranchdict(repo, ctx), |
434 'tags': nodetagsdict(repo, node), | 475 'tags': nodetagsdict(repo, node), |
435 'bookmarks': nodebookmarksdict(repo, node), | 476 'bookmarks': nodebookmarksdict(repo, node), |
436 'parent': lambda context, mapping: parents(ctx), | 477 'parent': lambda context, mapping: parents(ctx), |
437 'child': lambda context, mapping: children(ctx), | 478 'child': lambda context, mapping: children(ctx), |
438 } | 479 } |
439 | 480 |
481 | |
440 def changelistentry(web, ctx): | 482 def changelistentry(web, ctx): |
441 '''Obtain a dictionary to be used for entries in a changelist. | 483 '''Obtain a dictionary to be used for entries in a changelist. |
442 | 484 |
443 This function is called when producing items for the "entries" list passed | 485 This function is called when producing items for the "entries" list passed |
444 to the "shortlog" and "changelog" templates. | 486 to the "shortlog" and "changelog" templates. |
448 n = scmutil.binnode(ctx) | 490 n = scmutil.binnode(ctx) |
449 showtags = showtag(repo, 'changelogtag', n) | 491 showtags = showtag(repo, 'changelogtag', n) |
450 files = listfilediffs(ctx.files(), n, web.maxfiles) | 492 files = listfilediffs(ctx.files(), n, web.maxfiles) |
451 | 493 |
452 entry = commonentry(repo, ctx) | 494 entry = commonentry(repo, ctx) |
453 entry.update({ | 495 entry.update( |
454 'allparents': lambda context, mapping: parents(ctx), | 496 { |
455 'parent': lambda context, mapping: parents(ctx, rev - 1), | 497 'allparents': lambda context, mapping: parents(ctx), |
456 'child': lambda context, mapping: children(ctx, rev + 1), | 498 'parent': lambda context, mapping: parents(ctx, rev - 1), |
457 'changelogtag': showtags, | 499 'child': lambda context, mapping: children(ctx, rev + 1), |
458 'files': files, | 500 'changelogtag': showtags, |
459 }) | 501 'files': files, |
502 } | |
503 ) | |
460 return entry | 504 return entry |
505 | |
461 | 506 |
462 def changelistentries(web, revs, maxcount, parityfn): | 507 def changelistentries(web, revs, maxcount, parityfn): |
463 """Emit up to N records for an iterable of revisions.""" | 508 """Emit up to N records for an iterable of revisions.""" |
464 repo = web.repo | 509 repo = web.repo |
465 | 510 |
472 | 517 |
473 entry = changelistentry(web, repo[rev]) | 518 entry = changelistentry(web, repo[rev]) |
474 entry['parity'] = next(parityfn) | 519 entry['parity'] = next(parityfn) |
475 | 520 |
476 yield entry | 521 yield entry |
522 | |
477 | 523 |
478 def symrevorshortnode(req, ctx): | 524 def symrevorshortnode(req, ctx): |
479 if 'node' in req.qsparams: | 525 if 'node' in req.qsparams: |
480 return templatefilters.revescape(req.qsparams['node']) | 526 return templatefilters.revescape(req.qsparams['node']) |
481 else: | 527 else: |
482 return short(scmutil.binnode(ctx)) | 528 return short(scmutil.binnode(ctx)) |
483 | 529 |
530 | |
484 def _listfilesgen(context, ctx, stripecount): | 531 def _listfilesgen(context, ctx, stripecount): |
485 parity = paritygen(stripecount) | 532 parity = paritygen(stripecount) |
486 for blockno, f in enumerate(ctx.files()): | 533 for blockno, f in enumerate(ctx.files()): |
487 template = 'filenodelink' if f in ctx else 'filenolink' | 534 template = 'filenodelink' if f in ctx else 'filenolink' |
488 yield context.process(template, { | 535 yield context.process( |
489 'node': ctx.hex(), | 536 template, |
490 'file': f, | 537 { |
491 'blockno': blockno + 1, | 538 'node': ctx.hex(), |
492 'parity': next(parity), | 539 'file': f, |
493 }) | 540 'blockno': blockno + 1, |
541 'parity': next(parity), | |
542 }, | |
543 ) | |
544 | |
494 | 545 |
495 def changesetentry(web, ctx): | 546 def changesetentry(web, ctx): |
496 '''Obtain a dictionary to be used to render the "changeset" template.''' | 547 '''Obtain a dictionary to be used to render the "changeset" template.''' |
497 | 548 |
498 showtags = showtag(web.repo, 'changesettag', scmutil.binnode(ctx)) | 549 showtags = showtag(web.repo, 'changesettag', scmutil.binnode(ctx)) |
499 showbookmarks = showbookmark(web.repo, 'changesetbookmark', | 550 showbookmarks = showbookmark( |
500 scmutil.binnode(ctx)) | 551 web.repo, 'changesetbookmark', scmutil.binnode(ctx) |
552 ) | |
501 showbranch = nodebranchnodefault(ctx) | 553 showbranch = nodebranchnodefault(ctx) |
502 | 554 |
503 basectx = basechangectx(web.repo, web.req) | 555 basectx = basechangectx(web.repo, web.req) |
504 if basectx is None: | 556 if basectx is None: |
505 basectx = ctx.p1() | 557 basectx = ctx.p1() |
519 symrev=symrevorshortnode(web.req, ctx), | 571 symrev=symrevorshortnode(web.req, ctx), |
520 basenode=basectx.hex(), | 572 basenode=basectx.hex(), |
521 changesettag=showtags, | 573 changesettag=showtags, |
522 changesetbookmark=showbookmarks, | 574 changesetbookmark=showbookmarks, |
523 changesetbranch=showbranch, | 575 changesetbranch=showbranch, |
524 files=templateutil.mappedgenerator(_listfilesgen, | 576 files=templateutil.mappedgenerator( |
525 args=(ctx, web.stripecount)), | 577 _listfilesgen, args=(ctx, web.stripecount) |
578 ), | |
526 diffsummary=lambda context, mapping: diffsummary(diffstatsgen), | 579 diffsummary=lambda context, mapping: diffsummary(diffstatsgen), |
527 diffstat=diffstats, | 580 diffstat=diffstats, |
528 archives=web.archivelist(ctx.hex()), | 581 archives=web.archivelist(ctx.hex()), |
529 **pycompat.strkwargs(commonentry(web.repo, ctx))) | 582 **pycompat.strkwargs(commonentry(web.repo, ctx)) |
583 ) | |
584 | |
530 | 585 |
531 def _listfilediffsgen(context, files, node, max): | 586 def _listfilediffsgen(context, files, node, max): |
532 for f in files[:max]: | 587 for f in files[:max]: |
533 yield context.process('filedifflink', {'node': hex(node), 'file': f}) | 588 yield context.process('filedifflink', {'node': hex(node), 'file': f}) |
534 if len(files) > max: | 589 if len(files) > max: |
535 yield context.process('fileellipses', {}) | 590 yield context.process('fileellipses', {}) |
536 | 591 |
592 | |
537 def listfilediffs(files, node, max): | 593 def listfilediffs(files, node, max): |
538 return templateutil.mappedgenerator(_listfilediffsgen, | 594 return templateutil.mappedgenerator( |
539 args=(files, node, max)) | 595 _listfilediffsgen, args=(files, node, max) |
596 ) | |
597 | |
540 | 598 |
541 def _prettyprintdifflines(context, lines, blockno, lineidprefix): | 599 def _prettyprintdifflines(context, lines, blockno, lineidprefix): |
542 for lineno, l in enumerate(lines, 1): | 600 for lineno, l in enumerate(lines, 1): |
543 difflineno = "%d.%d" % (blockno, lineno) | 601 difflineno = "%d.%d" % (blockno, lineno) |
544 if l.startswith('+'): | 602 if l.startswith('+'): |
547 ltype = "difflineminus" | 605 ltype = "difflineminus" |
548 elif l.startswith('@'): | 606 elif l.startswith('@'): |
549 ltype = "difflineat" | 607 ltype = "difflineat" |
550 else: | 608 else: |
551 ltype = "diffline" | 609 ltype = "diffline" |
552 yield context.process(ltype, { | 610 yield context.process( |
553 'line': l, | 611 ltype, |
554 'lineno': lineno, | 612 { |
555 'lineid': lineidprefix + "l%s" % difflineno, | 613 'line': l, |
556 'linenumber': "% 8s" % difflineno, | 614 'lineno': lineno, |
557 }) | 615 'lineid': lineidprefix + "l%s" % difflineno, |
558 | 616 'linenumber': "% 8s" % difflineno, |
559 def _diffsgen(context, repo, ctx, basectx, files, style, stripecount, | 617 }, |
560 linerange, lineidprefix): | 618 ) |
619 | |
620 | |
621 def _diffsgen( | |
622 context, | |
623 repo, | |
624 ctx, | |
625 basectx, | |
626 files, | |
627 style, | |
628 stripecount, | |
629 linerange, | |
630 lineidprefix, | |
631 ): | |
561 if files: | 632 if files: |
562 m = match.exact(files) | 633 m = match.exact(files) |
563 else: | 634 else: |
564 m = match.always() | 635 m = match.always() |
565 | 636 |
576 s1, l1, s2, l2 = hunkrange | 647 s1, l1, s2, l2 = hunkrange |
577 if not mdiff.hunkinrange((s2, l2), linerange): | 648 if not mdiff.hunkinrange((s2, l2), linerange): |
578 continue | 649 continue |
579 lines.extend(hunklines) | 650 lines.extend(hunklines) |
580 if lines: | 651 if lines: |
581 l = templateutil.mappedgenerator(_prettyprintdifflines, | 652 l = templateutil.mappedgenerator( |
582 args=(lines, blockno, | 653 _prettyprintdifflines, args=(lines, blockno, lineidprefix) |
583 lineidprefix)) | 654 ) |
584 yield { | 655 yield { |
585 'parity': next(parity), | 656 'parity': next(parity), |
586 'blockno': blockno, | 657 'blockno': blockno, |
587 'lines': l, | 658 'lines': l, |
588 } | 659 } |
589 | 660 |
661 | |
590 def diffs(web, ctx, basectx, files, style, linerange=None, lineidprefix=''): | 662 def diffs(web, ctx, basectx, files, style, linerange=None, lineidprefix=''): |
591 args = (web.repo, ctx, basectx, files, style, web.stripecount, | 663 args = ( |
592 linerange, lineidprefix) | 664 web.repo, |
665 ctx, | |
666 basectx, | |
667 files, | |
668 style, | |
669 web.stripecount, | |
670 linerange, | |
671 lineidprefix, | |
672 ) | |
593 return templateutil.mappinggenerator(_diffsgen, args=args, name='diffblock') | 673 return templateutil.mappinggenerator(_diffsgen, args=args, name='diffblock') |
674 | |
594 | 675 |
595 def _compline(type, leftlineno, leftline, rightlineno, rightline): | 676 def _compline(type, leftlineno, leftline, rightlineno, rightline): |
596 lineid = leftlineno and ("l%d" % leftlineno) or '' | 677 lineid = leftlineno and ("l%d" % leftlineno) or '' |
597 lineid += rightlineno and ("r%d" % rightlineno) or '' | 678 lineid += rightlineno and ("r%d" % rightlineno) or '' |
598 llno = '%d' % leftlineno if leftlineno else '' | 679 llno = '%d' % leftlineno if leftlineno else '' |
606 'rightlineno': rightlineno, | 687 'rightlineno': rightlineno, |
607 'rightlinenumber': "% 6s" % rlno, | 688 'rightlinenumber': "% 6s" % rlno, |
608 'rightline': rightline or '', | 689 'rightline': rightline or '', |
609 } | 690 } |
610 | 691 |
692 | |
611 def _getcompblockgen(context, leftlines, rightlines, opcodes): | 693 def _getcompblockgen(context, leftlines, rightlines, opcodes): |
612 for type, llo, lhi, rlo, rhi in opcodes: | 694 for type, llo, lhi, rlo, rhi in opcodes: |
613 type = pycompat.sysbytes(type) | 695 type = pycompat.sysbytes(type) |
614 len1 = lhi - llo | 696 len1 = lhi - llo |
615 len2 = rhi - rlo | 697 len2 = rhi - rlo |
616 count = min(len1, len2) | 698 count = min(len1, len2) |
617 for i in pycompat.xrange(count): | 699 for i in pycompat.xrange(count): |
618 yield _compline(type=type, | 700 yield _compline( |
619 leftlineno=llo + i + 1, | 701 type=type, |
620 leftline=leftlines[llo + i], | 702 leftlineno=llo + i + 1, |
621 rightlineno=rlo + i + 1, | 703 leftline=leftlines[llo + i], |
622 rightline=rightlines[rlo + i]) | 704 rightlineno=rlo + i + 1, |
705 rightline=rightlines[rlo + i], | |
706 ) | |
623 if len1 > len2: | 707 if len1 > len2: |
624 for i in pycompat.xrange(llo + count, lhi): | 708 for i in pycompat.xrange(llo + count, lhi): |
625 yield _compline(type=type, | 709 yield _compline( |
626 leftlineno=i + 1, | 710 type=type, |
627 leftline=leftlines[i], | 711 leftlineno=i + 1, |
628 rightlineno=None, | 712 leftline=leftlines[i], |
629 rightline=None) | 713 rightlineno=None, |
714 rightline=None, | |
715 ) | |
630 elif len2 > len1: | 716 elif len2 > len1: |
631 for i in pycompat.xrange(rlo + count, rhi): | 717 for i in pycompat.xrange(rlo + count, rhi): |
632 yield _compline(type=type, | 718 yield _compline( |
633 leftlineno=None, | 719 type=type, |
634 leftline=None, | 720 leftlineno=None, |
635 rightlineno=i + 1, | 721 leftline=None, |
636 rightline=rightlines[i]) | 722 rightlineno=i + 1, |
723 rightline=rightlines[i], | |
724 ) | |
725 | |
637 | 726 |
638 def _getcompblock(leftlines, rightlines, opcodes): | 727 def _getcompblock(leftlines, rightlines, opcodes): |
639 args = (leftlines, rightlines, opcodes) | 728 args = (leftlines, rightlines, opcodes) |
640 return templateutil.mappinggenerator(_getcompblockgen, args=args, | 729 return templateutil.mappinggenerator( |
641 name='comparisonline') | 730 _getcompblockgen, args=args, name='comparisonline' |
731 ) | |
732 | |
642 | 733 |
643 def _comparegen(context, contextnum, leftlines, rightlines): | 734 def _comparegen(context, contextnum, leftlines, rightlines): |
644 '''Generator function that provides side-by-side comparison data.''' | 735 '''Generator function that provides side-by-side comparison data.''' |
645 s = difflib.SequenceMatcher(None, leftlines, rightlines) | 736 s = difflib.SequenceMatcher(None, leftlines, rightlines) |
646 if contextnum < 0: | 737 if contextnum < 0: |
649 else: | 740 else: |
650 for oc in s.get_grouped_opcodes(n=contextnum): | 741 for oc in s.get_grouped_opcodes(n=contextnum): |
651 l = _getcompblock(leftlines, rightlines, oc) | 742 l = _getcompblock(leftlines, rightlines, oc) |
652 yield {'lines': l} | 743 yield {'lines': l} |
653 | 744 |
745 | |
654 def compare(contextnum, leftlines, rightlines): | 746 def compare(contextnum, leftlines, rightlines): |
655 args = (contextnum, leftlines, rightlines) | 747 args = (contextnum, leftlines, rightlines) |
656 return templateutil.mappinggenerator(_comparegen, args=args, | 748 return templateutil.mappinggenerator( |
657 name='comparisonblock') | 749 _comparegen, args=args, name='comparisonblock' |
750 ) | |
751 | |
658 | 752 |
659 def diffstatgen(ui, ctx, basectx): | 753 def diffstatgen(ui, ctx, basectx): |
660 '''Generator function that provides the diffstat data.''' | 754 '''Generator function that provides the diffstat data.''' |
661 | 755 |
662 diffopts = patch.diffopts(ui, {'noprefix': False}) | 756 diffopts = patch.diffopts(ui, {'noprefix': False}) |
663 stats = patch.diffstatdata( | 757 stats = patch.diffstatdata(util.iterlines(ctx.diff(basectx, opts=diffopts))) |
664 util.iterlines(ctx.diff(basectx, opts=diffopts))) | |
665 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats) | 758 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats) |
666 while True: | 759 while True: |
667 yield stats, maxname, maxtotal, addtotal, removetotal, binary | 760 yield stats, maxname, maxtotal, addtotal, removetotal, binary |
668 | 761 |
762 | |
669 def diffsummary(statgen): | 763 def diffsummary(statgen): |
670 '''Return a short summary of the diff.''' | 764 '''Return a short summary of the diff.''' |
671 | 765 |
672 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen) | 766 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen) |
673 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % ( | 767 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % ( |
674 len(stats), addtotal, removetotal) | 768 len(stats), |
769 addtotal, | |
770 removetotal, | |
771 ) | |
772 | |
675 | 773 |
676 def _diffstattmplgen(context, ctx, statgen, parity): | 774 def _diffstattmplgen(context, ctx, statgen, parity): |
677 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen) | 775 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen) |
678 files = ctx.files() | 776 files = ctx.files() |
679 | 777 |
685 fileno = 0 | 783 fileno = 0 |
686 for filename, adds, removes, isbinary in stats: | 784 for filename, adds, removes, isbinary in stats: |
687 template = 'diffstatlink' if filename in files else 'diffstatnolink' | 785 template = 'diffstatlink' if filename in files else 'diffstatnolink' |
688 total = adds + removes | 786 total = adds + removes |
689 fileno += 1 | 787 fileno += 1 |
690 yield context.process(template, { | 788 yield context.process( |
691 'node': ctx.hex(), | 789 template, |
692 'file': filename, | 790 { |
693 'fileno': fileno, | 791 'node': ctx.hex(), |
694 'total': total, | 792 'file': filename, |
695 'addpct': pct(adds), | 793 'fileno': fileno, |
696 'removepct': pct(removes), | 794 'total': total, |
697 'parity': next(parity), | 795 'addpct': pct(adds), |
698 }) | 796 'removepct': pct(removes), |
797 'parity': next(parity), | |
798 }, | |
799 ) | |
800 | |
699 | 801 |
700 def diffstat(ctx, statgen, parity): | 802 def diffstat(ctx, statgen, parity): |
701 '''Return a diffstat template for each file in the diff.''' | 803 '''Return a diffstat template for each file in the diff.''' |
702 args = (ctx, statgen, parity) | 804 args = (ctx, statgen, parity) |
703 return templateutil.mappedgenerator(_diffstattmplgen, args=args) | 805 return templateutil.mappedgenerator(_diffstattmplgen, args=args) |
704 | 806 |
807 | |
705 class sessionvars(templateutil.wrapped): | 808 class sessionvars(templateutil.wrapped): |
706 def __init__(self, vars, start='?'): | 809 def __init__(self, vars, start='?'): |
707 self._start = start | 810 self._start = start |
708 self._vars = vars | 811 self._vars = vars |
709 | 812 |
735 raise error.ParseError(_('not filterable')) | 838 raise error.ParseError(_('not filterable')) |
736 | 839 |
737 def itermaps(self, context): | 840 def itermaps(self, context): |
738 separator = self._start | 841 separator = self._start |
739 for key, value in sorted(self._vars.iteritems()): | 842 for key, value in sorted(self._vars.iteritems()): |
740 yield {'name': key, | 843 yield { |
741 'value': pycompat.bytestr(value), | 844 'name': key, |
742 'separator': separator, | 845 'value': pycompat.bytestr(value), |
846 'separator': separator, | |
743 } | 847 } |
744 separator = '&' | 848 separator = '&' |
745 | 849 |
746 def join(self, context, mapping, sep): | 850 def join(self, context, mapping, sep): |
747 # could be '{separator}{name}={value|urlescape}' | 851 # could be '{separator}{name}={value|urlescape}' |
753 def tobool(self, context, mapping): | 857 def tobool(self, context, mapping): |
754 return bool(self._vars) | 858 return bool(self._vars) |
755 | 859 |
756 def tovalue(self, context, mapping): | 860 def tovalue(self, context, mapping): |
757 return self._vars | 861 return self._vars |
862 | |
758 | 863 |
759 class wsgiui(uimod.ui): | 864 class wsgiui(uimod.ui): |
760 # default termwidth breaks under mod_wsgi | 865 # default termwidth breaks under mod_wsgi |
761 def termwidth(self): | 866 def termwidth(self): |
762 return 80 | 867 return 80 |
868 | |
763 | 869 |
764 def getwebsubs(repo): | 870 def getwebsubs(repo): |
765 websubtable = [] | 871 websubtable = [] |
766 websubdefs = repo.ui.configitems('websub') | 872 websubdefs = repo.ui.configitems('websub') |
767 # we must maintain interhg backwards compatibility | 873 # we must maintain interhg backwards compatibility |
774 # identify portions of the pattern, taking care to avoid escaped | 880 # identify portions of the pattern, taking care to avoid escaped |
775 # delimiters. the replace format and flags are optional, but | 881 # delimiters. the replace format and flags are optional, but |
776 # delimiters are required. | 882 # delimiters are required. |
777 match = re.match( | 883 match = re.match( |
778 br'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$' | 884 br'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$' |
779 % (delim, delim, delim), pattern) | 885 % (delim, delim, delim), |
886 pattern, | |
887 ) | |
780 if not match: | 888 if not match: |
781 repo.ui.warn(_("websub: invalid pattern for %s: %s\n") | 889 repo.ui.warn( |
782 % (key, pattern)) | 890 _("websub: invalid pattern for %s: %s\n") % (key, pattern) |
891 ) | |
783 continue | 892 continue |
784 | 893 |
785 # we need to unescape the delimiter for regexp and format | 894 # we need to unescape the delimiter for regexp and format |
786 delim_re = re.compile(br'(?<!\\)\\%s' % delim) | 895 delim_re = re.compile(br'(?<!\\)\\%s' % delim) |
787 regexp = delim_re.sub(unesc, match.group(1)) | 896 regexp = delim_re.sub(unesc, match.group(1)) |
796 | 905 |
797 try: | 906 try: |
798 regexp = re.compile(regexp, flags) | 907 regexp = re.compile(regexp, flags) |
799 websubtable.append((regexp, format)) | 908 websubtable.append((regexp, format)) |
800 except re.error: | 909 except re.error: |
801 repo.ui.warn(_("websub: invalid regexp for %s: %s\n") | 910 repo.ui.warn( |
802 % (key, regexp)) | 911 _("websub: invalid regexp for %s: %s\n") % (key, regexp) |
912 ) | |
803 return websubtable | 913 return websubtable |
804 | 914 |
915 | |
805 def getgraphnode(repo, ctx): | 916 def getgraphnode(repo, ctx): |
806 return (templatekw.getgraphnodecurrent(repo, ctx) + | 917 return templatekw.getgraphnodecurrent( |
807 templatekw.getgraphnodesymbol(ctx)) | 918 repo, ctx |
919 ) + templatekw.getgraphnodesymbol(ctx) |