##// END OF EJS Templates
hgweb: always use "?" when writing session vars...
hgweb: always use "?" when writing session vars This code resolves a string to insert in URLs as part of a query string. Essentially, it resolves the {sessionvars} template keyword, which is used by hgweb templates to build a URL as a string. The whole approach here feels wrong because there's no way of knowing when this code runs how the final URL will look. There could be additional URL fragments added before this template keyword that add a query string component. Furthermore, I don't think there's *any* for req.url to have a query string. That's because the code that populates this variable only takes SCRIPT_NAME and REPO_NAME into account. The "?" character it is searching for would only be added if some code attempted to add QUERY_STRING to the URL. Hacking the code up to raise if "?" is present in the URL yields a clean test suite run. I'm not sure if we broke this code or if it has always been broken. Anyway, this commit removes support for emitting "&" as the first character in {sessionvars} and makes it always emit "?", which is what it was always doing before AFAICT. Differential Revision: https://phab.mercurial-scm.org/D2733

File last commit:

r36731:250f3168 default
r36823:ec46415e default
Show More
webutil.py
665 lines | 21.1 KiB | text/x-python | PythonLexer
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 # hgweb/webutil.py - utility library for the web interface.
#
# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
#
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
Yuya Nishihara
hgweb: use absolute_import
r27046 from __future__ import absolute_import
import copy
import difflib
import os
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162 import re
Yuya Nishihara
hgweb: use absolute_import
r27046
from ..i18n import _
from ..node import hex, nullid, short
from .common import (
ErrorResponse,
Denis Laxalde
hgweb: handle a "linerange" request parameter in filelog command...
r31665 HTTP_BAD_REQUEST,
Yuya Nishihara
hgweb: use absolute_import
r27046 HTTP_NOT_FOUND,
paritygen,
)
from .. import (
context,
error,
match,
Denis Laxalde
mdiff: add a hunkinrange helper function...
r31808 mdiff,
Yuya Nishihara
hgweb: use absolute_import
r27046 patch,
pathutil,
Augie Fackler
webutil: use pycompat.bytestr() instead of str()...
r34808 pycompat,
Yuya Nishihara
hgweb: use absolute_import
r27046 templatefilters,
av6
hgweb: display fate of obsolete changesets...
r35501 templatekw,
Yuya Nishihara
hgweb: use absolute_import
r27046 ui as uimod,
util,
)
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393 def up(p):
Augie Fackler
hgweb: fix up trailing slash detection on Python 3...
r36731 if p[0:1] != "/":
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393 p = "/" + p
Augie Fackler
hgweb: fix up trailing slash detection on Python 3...
r36731 if p[-1:] == "/":
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393 p = p[:-1]
up = os.path.dirname(p)
if up == "/":
return "/"
return up + "/"
Pierre-Yves David
hgweb: better names for _navseq arguments...
r18391 def _navseq(step, firststep=None):
if firststep:
yield firststep
if firststep >= 20 and firststep <= 40:
Pierre-Yves David
hgweb: ensure _navseq yield strictly increasing numbers...
r18392 firststep = 50
yield firststep
assert step > 0
assert firststep > 0
while step <= firststep:
step *= 10
Pierre-Yves David
hgweb: drop recursivity in _navseq...
r18390 while True:
Pierre-Yves David
hgweb: better names for _navseq arguments...
r18391 yield 1 * step
yield 3 * step
step *= 10
Pierre-Yves David
hgweb: move the `seq` function out of the revnavgen scope...
r18389
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 class revnav(object):
Pierre-Yves David
hgweb: document the revnavgen function
r18320
Pierre-Yves David
hgweb: pass repo object to revnav construction...
r18409 def __init__(self, repo):
Pierre-Yves David
hgweb: pass nodefunc to the revnav object...
r18404 """Navigation generation object
Pierre-Yves David
hgweb: pass repo object to revnav construction...
r18409 :repo: repo object we generate nav for
Pierre-Yves David
hgweb: pass nodefunc to the revnav object...
r18404 """
Pierre-Yves David
hgweb: pass repo object to revnav construction...
r18409 # used for hex generation
self._revlog = repo.changelog
Pierre-Yves David
hgweb: pass nodefunc to the revnav object...
r18404
Pierre-Yves David
hgweb: simplify the handling of empty repo...
r18406 def __nonzero__(self):
"""return True if any revision to navigate over"""
Pierre-Yves David
hgweb: handle filtered "0" rev in navigation...
r19094 return self._first() is not None
Gregory Szorc
py3: add __bool__ to every class defining __nonzero__...
r31476 __bool__ = __nonzero__
Pierre-Yves David
hgweb: handle filtered "0" rev in navigation...
r19094 def _first(self):
"""return the minimum non-filtered changeset or None"""
try:
timeless
py3: convert to next() function...
r29216 return next(iter(self._revlog))
Pierre-Yves David
hgweb: handle filtered "0" rev in navigation...
r19094 except StopIteration:
return None
Pierre-Yves David
hgweb: simplify the handling of empty repo...
r18406
Pierre-Yves David
hgweb: move hex creation into an object method...
r18405 def hex(self, rev):
Pierre-Yves David
hgweb: pass repo object to revnav construction...
r18409 return hex(self._revlog.node(rev))
Pierre-Yves David
hgweb: move hex creation into an object method...
r18405
Pierre-Yves David
hgweb: pass nodefunc to the revnav object...
r18404 def gen(self, pos, pagelen, limit):
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 """computes label and revision id for navigation link
Pierre-Yves David
hgweb: document the revnavgen function
r18320
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 :pos: is the revision relative to which we generate navigation.
:pagelen: the size of each navigation page
:limit: how far shall we link
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 The return is:
- a single element tuple
- containing a dictionary with a `before` and `after` key
- values are generator functions taking arbitrary number of kwargs
- yield items are dictionaries with `label` and `node` keys
"""
Pierre-Yves David
hgweb: simplify the handling of empty repo...
r18406 if not self:
# empty repo
return ({'before': (), 'after': ()},)
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425 targets = []
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 for f in _navseq(1, pagelen):
if f > limit:
break
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425 targets.append(pos + f)
targets.append(pos - f)
targets.sort()
Pierre-Yves David
hgweb: handle filtered "0" rev in navigation...
r19094 first = self._first()
navbefore = [("(%i)" % first, self.hex(first))]
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425 navafter = []
for rev in targets:
Pierre-Yves David
hgweb: ignore filtered revision in revnav...
r18426 if rev not in self._revlog:
continue
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425 if pos < rev < limit:
Pierre-Yves David
hgweb: fix navigation label (issue3792)...
r18503 navafter.append(("+%d" % abs(rev - pos), self.hex(rev)))
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425 if 0 < rev < pos:
Pierre-Yves David
hgweb: fix navigation label (issue3792)...
r18503 navbefore.append(("-%d" % abs(rev - pos), self.hex(rev)))
Pierre-Yves David
hgweb: generate revnav in two phase...
r18425
Nicolas Dumazet
hgweb: changenav: separate pages before and after the current position...
r10254
Pierre-Yves David
hgweb: move revnavgen into an object...
r18403 navafter.append(("tip", "tip"))
data = lambda i: {"label": i[0], "node": i[1]}
return ({'before': lambda **map: (data(i) for i in navbefore),
'after': lambda **map: (data(i) for i in navafter)},)
Dirkjan Ochtman
hgweb: refactor hgweb code
r6393
Pierre-Yves David
hgweb: introduction a filerevnav subclass...
r18408 class filerevnav(revnav):
Pierre-Yves David
hgweb: pass repo object to revnav construction...
r18409
def __init__(self, repo, path):
"""Navigation generation object
:repo: repo object we generate nav for
:path: path of the file we generate nav for
"""
# used for iteration
self._changelog = repo.unfiltered().changelog
# used for hex generation
self._revlog = repo.file(path)
def hex(self, rev):
return hex(self._changelog.node(self._revlog.linkrev(rev)))
av6
webutil: make _siblings into an object with __iter__ and __len__...
r27023 class _siblings(object):
Gregory Szorc
hgweb: don't use mutable default argument value
r31391 def __init__(self, siblings=None, hiderev=None):
Pierre-Yves David
hgweb: explicitly tests for None in webutil...
r31434 if siblings is None:
siblings = []
self.siblings = [s for s in siblings if s.node() != nullid]
av6
webutil: make _siblings into an object with __iter__ and __len__...
r27023 if len(self.siblings) == 1 and self.siblings[0].rev() == hiderev:
self.siblings = []
Pierre-Yves David
hgweb: introduction a filerevnav subclass...
r18408
av6
webutil: make _siblings into an object with __iter__ and __len__...
r27023 def __iter__(self):
for s in self.siblings:
d = {
'node': s.hex(),
'rev': s.rev(),
'user': s.user(),
'date': s.date(),
'description': s.description(),
'branch': s.branch(),
}
if util.safehasattr(s, 'path'):
d['file'] = s.path()
yield d
def __len__(self):
return len(self.siblings)
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
Gregory Szorc
hgweb: query string arguments to control whitespace for annotate...
r34391 def difffeatureopts(req, ui, section):
Jun Wu
hgweb: make fctx.annotate a separated function so it could be wrapped...
r30081 diffopts = patch.difffeatureopts(ui, untrusted=True,
Gregory Szorc
hgweb: query string arguments to control whitespace for annotate...
r34391 section=section, whitespace=True)
for k in ('ignorews', 'ignorewsamount', 'ignorewseol', 'ignoreblanklines'):
v = req.form.get(k, [None])[0]
if v is not None:
Gregory Szorc
hgweb: use parsebool for parsing diff query string options...
r34404 v = util.parsebool(v)
setattr(diffopts, k, v if v is not None else True)
Gregory Szorc
hgweb: query string arguments to control whitespace for annotate...
r34391
return diffopts
def annotate(req, fctx, ui):
diffopts = difffeatureopts(req, ui, 'annotate')
Jun Wu
hgweb: make fctx.annotate a separated function so it could be wrapped...
r30081 return fctx.annotate(follow=True, linenumber=True, diffopts=diffopts)
Dirkjan Ochtman
hgweb: simplify parents/children generation code
r7671 def parents(ctx, hide=None):
Anton Shestakov
hgweb: use introrev() for finding parents (issue4506)...
r24136 if isinstance(ctx, context.basefilectx):
introrev = ctx.introrev()
if ctx.changectx().rev() != introrev:
Matt Harbison
hgweb: replace 'ctx._repo' with 'ctx.repo()'
r24340 return _siblings([ctx.repo()[introrev]], hide)
Dirkjan Ochtman
hgweb: simplify parents/children generation code
r7671 return _siblings(ctx.parents(), hide)
def children(ctx, hide=None):
return _siblings(ctx.children(), hide)
Matt Mackall
hgweb: minor improvements for new web style...
r6434 def renamelink(fctx):
Matt Mackall
hgweb: fix merge breakage
r6437 r = fctx.renamed()
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 if r:
Augie Fackler
webutil: move from dict() construction to {} literals...
r20681 return [{'file': r[0], 'node': hex(r[1])}]
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 return []
def nodetagsdict(repo, node):
return [{"name": i} for i in repo.nodetags(node)]
Alexander Solovyov
hgweb: add display of bookmarks for changelog and changeset
r13596 def nodebookmarksdict(repo, node):
return [{"name": i} for i in repo.nodebookmarks(node)]
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 def nodebranchdict(repo, ctx):
branches = []
branch = ctx.branch()
# If this is an empty repo, ctx.node() == nullid,
Brodie Rao
localrepo: add branchtip() method for faster single-branch lookups...
r16719 # ctx.branch() == 'default'.
try:
branchnode = repo.branchtip(branch)
except error.RepoLookupError:
branchnode = None
if branchnode == ctx.node():
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 branches.append({"name": branch})
return branches
def nodeinbranch(repo, ctx):
branches = []
branch = ctx.branch()
Brodie Rao
localrepo: add branchtip() method for faster single-branch lookups...
r16719 try:
branchnode = repo.branchtip(branch)
except error.RepoLookupError:
branchnode = None
if branch != 'default' and branchnode != ctx.node():
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 branches.append({"name": branch})
return branches
def nodebranchnodefault(ctx):
branches = []
branch = ctx.branch()
if branch != 'default':
branches.append({"name": branch})
return branches
def showtag(repo, tmpl, t1, node=nullid, **args):
for t in repo.nodetags(node):
yield tmpl(t1, tag=t, **args)
Alexander Solovyov
hgweb: add display of bookmarks for changelog and changeset
r13596 def showbookmark(repo, tmpl, t1, node=nullid, **args):
for t in repo.nodebookmarks(node):
yield tmpl(t1, bookmark=t, **args)
av6
hgweb: move branchentries code from webcommands to webutil
r26129 def branchentries(repo, stripecount, limit=0):
tips = []
heads = repo.heads()
parity = paritygen(stripecount)
sortkey = lambda item: (not item[1], item[0].rev())
def entries(**map):
count = 0
if not tips:
for tag, hs, tip, closed in repo.branchmap().iterbranches():
tips.append((repo[tip], closed))
for ctx, closed in sorted(tips, key=sortkey, reverse=True):
if limit > 0 and count >= limit:
return
count += 1
if closed:
status = 'closed'
elif ctx.node() not in heads:
status = 'inactive'
else:
status = 'open'
yield {
timeless
py3: convert to next() function...
r29216 'parity': next(parity),
av6
hgweb: move branchentries code from webcommands to webutil
r26129 'branch': ctx.branch(),
'status': status,
'node': ctx.hex(),
'date': ctx.date()
}
return entries
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 def cleanpath(repo, path):
path = path.lstrip('/')
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 return pathutil.canonpath(repo.root, '', path)
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
av6
style: adjust whitespaces in webutil.py...
r25999 def changeidctx(repo, changeid):
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 try:
Matt Mackall
use repo[changeid] to get a changectx
r6747 ctx = repo[changeid]
Matt Mackall
error: move repo errors...
r7637 except error.RepoError:
Durham Goode
manifest: remove last uses of repo.manifest...
r30375 man = repo.manifestlog._revlog
Matt Mackall
linkrev: take a revision number rather than a hash
r7361 ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
Dirkjan Ochtman
hgweb: separate out utility functions
r6392
return ctx
av6
style: adjust whitespaces in webutil.py...
r25999 def changectx(repo, req):
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 changeid = "tip"
if 'node' in req.form:
changeid = req.form['node'][0]
av6
style: adjust whitespaces in webutil.py...
r25999 ipos = changeid.find(':')
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 if ipos != -1:
changeid = changeid[(ipos + 1):]
elif 'manifest' in req.form:
changeid = req.form['manifest'][0]
return changeidctx(repo, changeid)
def basechangectx(repo, req):
if 'node' in req.form:
changeid = req.form['node'][0]
av6
style: adjust whitespaces in webutil.py...
r25999 ipos = changeid.find(':')
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 if ipos != -1:
changeid = changeid[:ipos]
return changeidctx(repo, changeid)
return None
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 def filectx(repo, req):
Ross Lagerwall
hgweb: avoid traceback when file or node parameters are missing...
r17289 if 'file' not in req.form:
raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 path = cleanpath(repo, req.form['file'][0])
if 'node' in req.form:
changeid = req.form['node'][0]
Ross Lagerwall
hgweb: avoid traceback when file or node parameters are missing...
r17289 elif 'filenode' in req.form:
changeid = req.form['filenode'][0]
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 else:
Ross Lagerwall
hgweb: avoid traceback when file or node parameters are missing...
r17289 raise ErrorResponse(HTTP_NOT_FOUND, 'node or filenode not given')
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 try:
Matt Mackall
use repo[changeid] to get a changectx
r6747 fctx = repo[changeid][path]
Matt Mackall
error: move repo errors...
r7637 except error.RepoError:
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 fctx = repo.filectx(path, fileid=changeid)
return fctx
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310
Denis Laxalde
hgweb: handle a "linerange" request parameter in filelog command...
r31665 def linerange(req):
linerange = req.form.get('linerange')
if linerange is None:
return None
if len(linerange) > 1:
raise ErrorResponse(HTTP_BAD_REQUEST,
'redundant linerange parameter')
try:
fromline, toline = map(int, linerange[0].split(':', 1))
except ValueError:
raise ErrorResponse(HTTP_BAD_REQUEST,
'invalid linerange parameter')
try:
return util.processlinerange(fromline, toline)
except error.ParseError as exc:
Augie Fackler
py3: get bytes-repr of network errors portably...
r36272 raise ErrorResponse(HTTP_BAD_REQUEST, pycompat.bytestr(exc))
Denis Laxalde
hgweb: handle a "linerange" request parameter in filelog command...
r31665
def formatlinerange(fromline, toline):
return '%d:%d' % (fromline + 1, toline)
Yuya Nishihara
templatekw: switch obsfate-related template keywords to new API
r36612 def succsandmarkers(context, mapping):
repo = context.resource(mapping, 'repo')
for item in templatekw.showsuccsandmarkers(context, mapping):
av6
hgweb: link to successors of obsoleted changesets...
r35502 item['successors'] = _siblings(repo[successor]
for successor in item['successors'])
yield item
av6
hgweb: display fate of obsolete changesets...
r35501
Yuya Nishihara
templatekw: switch obsfate-related template keywords to new API
r36612 # teach templater succsandmarkers is switched to (context, mapping) API
succsandmarkers._requires = {'repo', 'ctx', 'templ'}
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 def commonentry(repo, ctx):
node = ctx.node()
return {
Yuya Nishihara
hgweb: make templater mostly compatible with log templates...
r36535 # TODO: perhaps ctx.changectx() should be assigned if ctx is a
# filectx, but I'm not pretty sure if that would always work because
# fctx.parents() != fctx.changectx.parents() for example.
'ctx': ctx,
'revcache': {},
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 'rev': ctx.rev(),
'node': hex(node),
'author': ctx.user(),
'desc': ctx.description(),
'date': ctx.date(),
'extra': ctx.extra(),
'phase': ctx.phasestr(),
av6
context: add obsolete() method to basefilectx...
r35087 'obsolete': ctx.obsolete(),
Yuya Nishihara
hgweb: make templater mostly compatible with log templates...
r36535 'succsandmarkers': succsandmarkers,
av6
hgweb: rename the main attribute of instabilities...
r35129 'instabilities': [{"instability": i} for i in ctx.instabilities()],
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 'branch': nodebranchnodefault(ctx),
'inbranch': nodeinbranch(repo, ctx),
'branches': nodebranchdict(repo, ctx),
'tags': nodetagsdict(repo, node),
'bookmarks': nodebookmarksdict(repo, node),
'parent': lambda **x: parents(ctx),
'child': lambda **x: children(ctx),
}
Gregory Szorc
hgweb: extract changelist entry generation into own function...
r23745 def changelistentry(web, ctx, tmpl):
'''Obtain a dictionary to be used for entries in a changelist.
This function is called when producing items for the "entries" list passed
to the "shortlog" and "changelog" templates.
'''
repo = web.repo
rev = ctx.rev()
n = ctx.node()
showtags = showtag(repo, tmpl, 'changelogtag', n)
files = listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 entry = commonentry(repo, ctx)
entry.update(
av6
hgweb: add parents to json-log (issue5074)...
r28709 allparents=lambda **x: parents(ctx),
av6
hgweb: move entry-preparing code from webcommands to webutils.commonentry()...
r27294 parent=lambda **x: parents(ctx, rev - 1),
child=lambda **x: children(ctx, rev + 1),
changelogtag=showtags,
files=files,
)
return entry
Gregory Szorc
hgweb: extract changelist entry generation into own function...
r23745
av6
hgweb: provide symrev (symbolic revision) property to the templates...
r25602 def symrevorshortnode(req, ctx):
if 'node' in req.form:
Yuya Nishihara
hgweb: do not import templatefilters.revescape and websub as symbol...
r27008 return templatefilters.revescape(req.form['node'][0])
av6
hgweb: provide symrev (symbolic revision) property to the templates...
r25602 else:
return short(ctx.node())
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 def changesetentry(web, req, tmpl, ctx):
'''Obtain a dictionary to be used to render the "changeset" template.'''
showtags = showtag(web.repo, tmpl, 'changesettag', ctx.node())
showbookmarks = showbookmark(web.repo, tmpl, 'changesetbookmark',
ctx.node())
showbranch = nodebranchnodefault(ctx)
files = []
parity = paritygen(web.stripecount)
for blockno, f in enumerate(ctx.files()):
av6
hgweb: rewrite `template = A and B or C` to be a proper ternary operator
r35315 template = 'filenodelink' if f in ctx else 'filenolink'
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 files.append(tmpl(template,
node=ctx.hex(), file=f, blockno=blockno + 1,
timeless
py3: convert to next() function...
r29216 parity=next(parity)))
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177
basectx = basechangectx(web.repo, req)
if basectx is None:
basectx = ctx.p1()
Boris Feld
configitems: register the 'web.style' config
r34243 style = web.config('web', 'style')
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 if 'style' in req.form:
style = req.form['style'][0]
Denis Laxalde
hgweb: handle "parity" internally in webutil.diffs()...
r31660 diff = diffs(web, tmpl, ctx, basectx, None, style)
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177
parity = paritygen(web.stripecount)
diffstatsgen = diffstatgen(ctx, basectx)
diffstats = diffstat(tmpl, ctx, diffstatsgen, parity)
return dict(
diff=diff,
av6
hgweb: provide symrev (symbolic revision) property to the templates...
r25602 symrev=symrevorshortnode(req, ctx),
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177 basenode=basectx.hex(),
changesettag=showtags,
changesetbookmark=showbookmarks,
changesetbranch=showbranch,
files=files,
diffsummary=lambda **x: diffsummary(diffstatsgen),
diffstat=diffstats,
archives=web.archivelist(ctx.hex()),
Pulkit Goyal
py3: use pycompat.strkwargs to convert kwargs keys to str...
r36452 **pycompat.strkwargs(commonentry(web.repo, ctx)))
Gregory Szorc
hgweb: extract changeset template mapping generation to own function...
r24177
Dirkjan Ochtman
hgweb: move another utility function into the webutil module
r7311 def listfilediffs(tmpl, files, node, max):
for f in files[:max]:
yield tmpl('filedifflink', node=hex(node), file=f)
if len(files) > max:
yield tmpl('fileellipses')
Denis Laxalde
hgweb: prefix line id by ctx shortnode in filelog when patches are shown...
r31727 def diffs(web, tmpl, ctx, basectx, files, style, linerange=None,
lineidprefix=''):
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310
Denis Laxalde
hgweb: use patch.diffhunks in webutil.diffs to simplify the algorithm...
r31276 def prettyprintlines(lines, blockno):
for lineno, l in enumerate(lines, 1):
Denis Laxalde
hgweb: start enumerate at 1 in webutil.diffs's inner function prettyprintlines
r31275 difflineno = "%d.%d" % (blockno, lineno)
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310 if l.startswith('+'):
ltype = "difflineplus"
elif l.startswith('-'):
ltype = "difflineminus"
elif l.startswith('@'):
ltype = "difflineat"
else:
ltype = "diffline"
yield tmpl(ltype,
line=l,
Denis Laxalde
hgweb: start enumerate at 1 in webutil.diffs's inner function prettyprintlines
r31275 lineno=lineno,
Denis Laxalde
hgweb: prefix line id by ctx shortnode in filelog when patches are shown...
r31727 lineid=lineidprefix + "l%s" % difflineno,
Gregory Szorc
hgweb: expose raw line numbers to templates...
r24712 linenumber="% 8s" % difflineno)
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310
Denis Laxalde
hgweb: handle "parity" internally in webutil.diffs()...
r31660 repo = web.repo
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310 if files:
m = match.exact(repo.root, repo.getcwd(), files)
else:
m = match.always(repo.root, repo.getcwd())
diffopts = patch.diffopts(repo.ui, untrusted=True)
Denis Laxalde
hgweb: explictly pass basectx in webutil.diffs...
r31082 node1 = basectx.node()
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310 node2 = ctx.node()
Denis Laxalde
hgweb: handle "parity" internally in webutil.diffs()...
r31660 parity = paritygen(web.stripecount)
Dirkjan Ochtman
hgweb: move the diffs() generator into webutil
r7310
Denis Laxalde
hgweb: use patch.diffhunks in webutil.diffs to simplify the algorithm...
r31276 diffhunks = patch.diffhunks(repo, node1, node2, m, opts=diffopts)
Denis Laxalde
diff: also yield file context objects in patch.trydiff() (API)...
r34856 for blockno, (fctx1, fctx2, header, hunks) in enumerate(diffhunks, 1):
Denis Laxalde
hgweb: use patch.diffhunks in webutil.diffs to simplify the algorithm...
r31276 if style != 'raw':
header = header[1:]
lines = [h + '\n' for h in header]
for hunkrange, hunklines in hunks:
Denis Laxalde
hgweb: add a 'linerange' parameter to webutil.diffs()...
r31666 if linerange is not None and hunkrange is not None:
s1, l1, s2, l2 = hunkrange
Denis Laxalde
mdiff: add a hunkinrange helper function...
r31808 if not mdiff.hunkinrange((s2, l2), linerange):
Denis Laxalde
hgweb: add a 'linerange' parameter to webutil.diffs()...
r31666 continue
Denis Laxalde
hgweb: use patch.diffhunks in webutil.diffs to simplify the algorithm...
r31276 lines.extend(hunklines)
if lines:
timeless
py3: convert to next() function...
r29216 yield tmpl('diffblock', parity=next(parity), blockno=blockno,
Denis Laxalde
hgweb: use patch.diffhunks in webutil.diffs to simplify the algorithm...
r31276 lines=prettyprintlines(lines, blockno))
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345
wujek srujek
hgweb: fixes traceback for invalid files by removing top-level template...
r17302 def compare(tmpl, context, leftlines, rightlines):
wujek srujek
hgweb: side-by-side comparison functionality...
r17202 '''Generator function that provides side-by-side comparison data.'''
def compline(type, leftlineno, leftline, rightlineno, rightline):
Augie Fackler
webutil: some %d instead of %s love on ints...
r36728 lineid = leftlineno and ("l%d" % leftlineno) or ''
lineid += rightlineno and ("r%d" % rightlineno) or ''
llno = '%d' % leftlineno if leftlineno else ''
rlno = '%d' % rightlineno if rightlineno else ''
wujek srujek
hgweb: side-by-side comparison functionality...
r17202 return tmpl('comparisonline',
type=type,
lineid=lineid,
Gregory Szorc
hgweb: expose raw line numbers to templates...
r24712 leftlineno=leftlineno,
Augie Fackler
webutil: some %d instead of %s love on ints...
r36728 leftlinenumber="% 6s" % llno,
wujek srujek
hgweb: side-by-side comparison functionality...
r17202 leftline=leftline or '',
Gregory Szorc
hgweb: expose raw line numbers to templates...
r24712 rightlineno=rightlineno,
Augie Fackler
webutil: some %d instead of %s love on ints...
r36728 rightlinenumber="% 6s" % rlno,
wujek srujek
hgweb: side-by-side comparison functionality...
r17202 rightline=rightline or '')
def getblock(opcodes):
for type, llo, lhi, rlo, rhi in opcodes:
len1 = lhi - llo
len2 = rhi - rlo
count = min(len1, len2)
for i in xrange(count):
yield compline(type=type,
leftlineno=llo + i + 1,
leftline=leftlines[llo + i],
rightlineno=rlo + i + 1,
rightline=rightlines[rlo + i])
if len1 > len2:
for i in xrange(llo + count, lhi):
yield compline(type=type,
leftlineno=i + 1,
leftline=leftlines[i],
rightlineno=None,
rightline=None)
elif len2 > len1:
for i in xrange(rlo + count, rhi):
yield compline(type=type,
leftlineno=None,
leftline=None,
rightlineno=i + 1,
rightline=rightlines[i])
s = difflib.SequenceMatcher(None, leftlines, rightlines)
if context < 0:
wujek srujek
hgweb: fixes traceback for invalid files by removing top-level template...
r17302 yield tmpl('comparisonblock', lines=getblock(s.get_opcodes()))
wujek srujek
hgweb: side-by-side comparison functionality...
r17202 else:
wujek srujek
hgweb: fixes traceback for invalid files by removing top-level template...
r17302 for oc in s.get_grouped_opcodes(n=context):
yield tmpl('comparisonblock', lines=getblock(oc))
wujek srujek
hgweb: side-by-side comparison functionality...
r17202
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 def diffstatgen(ctx, basectx):
Steven Brown
web: provide diff summary to the changeset page...
r14570 '''Generator function that provides the diffstat data.'''
Steven Brown
web: provide diffstat to the changeset page...
r14490
Yuya Nishihara
hgweb: disable diff.noprefix option for diffstat...
r35445 stats = patch.diffstatdata(
util.iterlines(ctx.diff(basectx, noprefix=False)))
Steven Brown
web: provide diffstat to the changeset page...
r14490 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
Steven Brown
web: provide diff summary to the changeset page...
r14570 while True:
yield stats, maxname, maxtotal, addtotal, removetotal, binary
def diffsummary(statgen):
'''Return a short summary of the diff.'''
timeless
py3: convert to next() function...
r29216 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
Steven Brown
web: provide diff summary to the changeset page...
r14570 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
len(stats), addtotal, removetotal)
def diffstat(tmpl, ctx, statgen, parity):
'''Return a diffstat template for each file in the diff.'''
timeless
py3: convert to next() function...
r29216 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
Steven Brown
web: include all files in the diffstat...
r14561 files = ctx.files()
Steven Brown
web: provide diffstat to the changeset page...
r14490
Steven Brown
web: include all files in the diffstat...
r14561 def pct(i):
if maxtotal == 0:
return 0
return (float(i) / maxtotal) * 100
Steven Brown
web: provide diffstat to the changeset page...
r14490
Steven Brown
web: provide the file number to the diffstat templates...
r14562 fileno = 0
Steven Brown
web: include all files in the diffstat...
r14561 for filename, adds, removes, isbinary in stats:
av6
hgweb: rewrite `template = A and B or C` to be a proper ternary operator
r35315 template = 'diffstatlink' if filename in files else 'diffstatnolink'
Steven Brown
web: include all files in the diffstat...
r14561 total = adds + removes
Steven Brown
web: provide the file number to the diffstat templates...
r14562 fileno += 1
yield tmpl(template, node=ctx.hex(), file=filename, fileno=fileno,
Steven Brown
web: include all files in the diffstat...
r14561 total=total, addpct=pct(adds), removepct=pct(removes),
timeless
py3: convert to next() function...
r29216 parity=next(parity))
Steven Brown
web: provide diffstat to the changeset page...
r14490
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345 class sessionvars(object):
def __init__(self, vars, start='?'):
self.start = start
self.vars = vars
def __getitem__(self, key):
return self.vars[key]
def __setitem__(self, key, value):
self.vars[key] = value
def __copy__(self):
return sessionvars(copy.copy(self.vars), self.start)
def __iter__(self):
separator = self.start
Mads Kiilerich
hgweb: generate query strings with parameters sorted by key
r18367 for key, value in sorted(self.vars.iteritems()):
Augie Fackler
webutil: use pycompat.bytestr() instead of str()...
r34808 yield {'name': key,
'value': pycompat.bytestr(value),
'separator': separator,
}
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345 separator = '&'
Augie Fackler
hgweb: fix hgweb_mod as well as hgwebdir_mod
r12691
Yuya Nishihara
hgweb: alias ui module as uimod...
r27007 class wsgiui(uimod.ui):
Augie Fackler
hgweb: fix hgweb_mod as well as hgwebdir_mod
r12691 # default termwidth breaks under mod_wsgi
def termwidth(self):
return 80
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162
def getwebsubs(repo):
websubtable = []
websubdefs = repo.ui.configitems('websub')
# we must maintain interhg backwards compatibility
websubdefs += repo.ui.configitems('interhg')
for key, pattern in websubdefs:
# grab the delimiter from the character after the "s"
Pulkit Goyal
py3: slice over bytes to prevent getting ascii values...
r36198 unesc = pattern[1:2]
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162 delim = re.escape(unesc)
# identify portions of the pattern, taking care to avoid escaped
# delimiters. the replace format and flags are optional, but
# delimiters are required.
match = re.match(
Pulkit Goyal
py3: add b'' to make sure regex pattern are bytes in hgweb/webutil.py...
r36201 br'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162 % (delim, delim, delim), pattern)
if not match:
repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
% (key, pattern))
continue
# we need to unescape the delimiter for regexp and format
Pulkit Goyal
py3: add b'' to make sure regex pattern are bytes in hgweb/webutil.py...
r36201 delim_re = re.compile(br'(?<!\\)\\%s' % delim)
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162 regexp = delim_re.sub(unesc, match.group(1))
format = delim_re.sub(unesc, match.group(2))
# the pattern allows for 6 regexp flags, so set them if necessary
flagin = match.group(3)
flags = 0
if flagin:
for flag in flagin.upper():
flags |= re.__dict__[flag]
try:
regexp = re.compile(regexp, flags)
websubtable.append((regexp, format))
except re.error:
repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
% (key, regexp))
return websubtable