2008-03-28 21:37:28 +03:00
|
|
|
# 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>
|
|
|
|
#
|
2009-04-26 03:08:54 +04:00
|
|
|
# This software may be used and distributed according to the terms of the
|
2010-01-20 07:20:08 +03:00
|
|
|
# GNU General Public License version 2 or any later version.
|
2008-03-28 21:37:28 +03:00
|
|
|
|
2015-10-31 16:07:40 +03:00
|
|
|
from __future__ import absolute_import
|
|
|
|
|
|
|
|
import copy
|
2012-07-08 19:17:02 +04:00
|
|
|
import difflib
|
2015-10-31 16:07:40 +03:00
|
|
|
import os
|
|
|
|
import re
|
|
|
|
|
|
|
|
from .. import (
|
|
|
|
context,
|
|
|
|
error,
|
|
|
|
match,
|
2017-04-01 13:24:59 +03:00
|
|
|
mdiff,
|
2015-10-31 16:07:40 +03:00
|
|
|
patch,
|
|
|
|
pathutil,
|
2017-10-17 05:44:06 +03:00
|
|
|
pycompat,
|
2015-10-31 16:07:40 +03:00
|
|
|
templatefilters,
|
|
|
|
ui as uimod,
|
|
|
|
util,
|
|
|
|
)
|
2018-07-06 03:45:27 +03:00
|
|
|
from ..i18n import _
|
|
|
|
from ..node import hex, nullid, short
|
|
|
|
from .common import HTTP_BAD_REQUEST, HTTP_NOT_FOUND, ErrorResponse, paritygen
|
|
|
|
|
2008-03-28 21:37:28 +03:00
|
|
|
|
flake8: enable F821 check
Summary:
This check is useful and detects real errors (ex. fbconduit). Unfortunately
`arc lint` will run it with both py2 and py3 so a lot of py2 builtins will
still be warned.
I didn't find a clean way to disable py3 check. So this diff tries to fix them.
For `xrange`, the change was done by a script:
```
import sys
import redbaron
headertypes = {'comment', 'endl', 'from_import', 'import', 'string',
'assignment', 'atomtrailers'}
xrangefix = '''try:
xrange(0)
except NameError:
xrange = range
'''
def isxrange(x):
try:
return x[0].value == 'xrange'
except Exception:
return False
def main(argv):
for i, path in enumerate(argv):
print('(%d/%d) scanning %s' % (i + 1, len(argv), path))
content = open(path).read()
try:
red = redbaron.RedBaron(content)
except Exception:
print(' warning: failed to parse')
continue
hasxrange = red.find('atomtrailersnode', value=isxrange)
hasxrangefix = 'xrange = range' in content
if hasxrangefix or not hasxrange:
print(' no need to change')
continue
# find a place to insert the compatibility statement
changed = False
for node in red:
if node.type in headertypes:
continue
# node.insert_before is an easier API, but it has bugs changing
# other "finally" and "except" positions. So do the insert
# manually.
# # node.insert_before(xrangefix)
line = node.absolute_bounding_box.top_left.line - 1
lines = content.splitlines(1)
content = ''.join(lines[:line]) + xrangefix + ''.join(lines[line:])
changed = True
break
if changed:
# "content" is faster than "red.dumps()"
open(path, 'w').write(content)
print(' updated')
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
```
For other py2 builtins that do not have a py3 equivalent, some `# noqa`
were added as a workaround for now.
Reviewed By: DurhamG
Differential Revision: D6934535
fbshipit-source-id: 546b62830af144bc8b46788d2e0fd00496838939
2018-02-10 04:31:44 +03:00
|
|
|
try:
|
|
|
|
xrange(0)
|
|
|
|
except NameError:
|
|
|
|
xrange = range
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2008-03-28 21:40:44 +03:00
|
|
|
def up(p):
|
|
|
|
if p[0] != "/":
|
|
|
|
p = "/" + p
|
|
|
|
if p[-1] == "/":
|
|
|
|
p = p[:-1]
|
|
|
|
up = os.path.dirname(p)
|
|
|
|
if up == "/":
|
|
|
|
return "/"
|
|
|
|
return up + "/"
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2013-01-10 21:47:48 +04:00
|
|
|
def _navseq(step, firststep=None):
|
|
|
|
if firststep:
|
|
|
|
yield firststep
|
|
|
|
if firststep >= 20 and firststep <= 40:
|
2013-01-10 21:52:17 +04:00
|
|
|
firststep = 50
|
|
|
|
yield firststep
|
|
|
|
assert step > 0
|
|
|
|
assert firststep > 0
|
|
|
|
while step <= firststep:
|
|
|
|
step *= 10
|
2013-01-10 21:44:26 +04:00
|
|
|
while True:
|
2013-01-10 21:47:48 +04:00
|
|
|
yield 1 * step
|
|
|
|
yield 3 * step
|
|
|
|
step *= 10
|
2013-01-15 15:11:18 +04:00
|
|
|
|
2013-01-14 00:36:35 +04:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
class revnav(object):
|
2013-01-16 16:18:22 +04:00
|
|
|
def __init__(self, repo):
|
2013-01-10 21:59:37 +04:00
|
|
|
"""Navigation generation object
|
|
|
|
|
2013-01-16 16:18:22 +04:00
|
|
|
:repo: repo object we generate nav for
|
2013-01-10 21:59:37 +04:00
|
|
|
"""
|
2013-01-16 16:18:22 +04:00
|
|
|
# used for hex generation
|
|
|
|
self._revlog = repo.changelog
|
2013-01-10 21:59:37 +04:00
|
|
|
|
2013-01-14 19:30:06 +04:00
|
|
|
def __nonzero__(self):
|
|
|
|
"""return True if any revision to navigate over"""
|
2013-04-30 15:53:49 +04:00
|
|
|
return self._first() is not None
|
|
|
|
|
2017-03-13 22:40:14 +03:00
|
|
|
__bool__ = __nonzero__
|
|
|
|
|
2013-04-30 15:53:49 +04:00
|
|
|
def _first(self):
|
|
|
|
"""return the minimum non-filtered changeset or None"""
|
|
|
|
try:
|
2016-05-17 00:30:53 +03:00
|
|
|
return next(iter(self._revlog))
|
2013-04-30 15:53:49 +04:00
|
|
|
except StopIteration:
|
|
|
|
return None
|
2013-01-14 19:30:06 +04:00
|
|
|
|
2013-01-10 21:54:50 +04:00
|
|
|
def hex(self, rev):
|
2013-01-16 16:18:22 +04:00
|
|
|
return hex(self._revlog.node(rev))
|
2013-01-10 21:54:50 +04:00
|
|
|
|
2013-01-10 21:59:37 +04:00
|
|
|
def gen(self, pos, pagelen, limit):
|
2013-01-16 00:17:18 +04:00
|
|
|
"""computes label and revision id for navigation link
|
2013-01-14 00:36:35 +04:00
|
|
|
|
2013-01-16 00:17:18 +04:00
|
|
|
:pos: is the revision relative to which we generate navigation.
|
|
|
|
:pagelen: the size of each navigation page
|
|
|
|
:limit: how far shall we link
|
2008-03-28 21:40:44 +03:00
|
|
|
|
2013-01-16 00:17:18 +04:00
|
|
|
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
|
|
|
|
"""
|
2013-01-14 19:30:06 +04:00
|
|
|
if not self:
|
|
|
|
# empty repo
|
2018-05-30 12:16:33 +03:00
|
|
|
return ({"before": (), "after": ()},)
|
2008-03-28 21:40:44 +03:00
|
|
|
|
2013-01-10 22:29:48 +04:00
|
|
|
targets = []
|
2013-01-16 00:17:18 +04:00
|
|
|
for f in _navseq(1, pagelen):
|
|
|
|
if f > limit:
|
|
|
|
break
|
2013-01-10 22:29:48 +04:00
|
|
|
targets.append(pos + f)
|
|
|
|
targets.append(pos - f)
|
|
|
|
targets.sort()
|
|
|
|
|
2013-04-30 15:53:49 +04:00
|
|
|
first = self._first()
|
|
|
|
navbefore = [("(%i)" % first, self.hex(first))]
|
2013-01-10 22:29:48 +04:00
|
|
|
navafter = []
|
|
|
|
for rev in targets:
|
2013-01-16 17:19:28 +04:00
|
|
|
if rev not in self._revlog:
|
|
|
|
continue
|
2013-01-10 22:29:48 +04:00
|
|
|
if pos < rev < limit:
|
2013-01-30 20:32:17 +04:00
|
|
|
navafter.append(("+%d" % abs(rev - pos), self.hex(rev)))
|
2013-01-10 22:29:48 +04:00
|
|
|
if 0 < rev < pos:
|
2013-01-30 20:32:17 +04:00
|
|
|
navbefore.append(("-%d" % abs(rev - pos), self.hex(rev)))
|
2013-01-10 22:29:48 +04:00
|
|
|
|
2013-01-16 00:17:18 +04:00
|
|
|
navafter.append(("tip", "tip"))
|
|
|
|
|
|
|
|
data = lambda i: {"label": i[0], "node": i[1]}
|
2018-05-30 12:16:33 +03:00
|
|
|
return (
|
|
|
|
{
|
|
|
|
"before": lambda **map: (data(i) for i in navbefore),
|
|
|
|
"after": lambda **map: (data(i) for i in navafter),
|
|
|
|
},
|
|
|
|
)
|
2008-03-28 21:40:44 +03:00
|
|
|
|
2013-01-16 16:18:22 +04:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
class filerevnav(revnav):
|
2013-01-16 16:18:22 +04:00
|
|
|
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)))
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2015-11-10 12:22:40 +03:00
|
|
|
class _siblings(object):
|
2017-03-13 07:52:17 +03:00
|
|
|
def __init__(self, siblings=None, hiderev=None):
|
2017-03-16 01:10:09 +03:00
|
|
|
if siblings is None:
|
|
|
|
siblings = []
|
|
|
|
self.siblings = [s for s in siblings if s.node() != nullid]
|
2015-11-10 12:22:40 +03:00
|
|
|
if len(self.siblings) == 1 and self.siblings[0].rev() == hiderev:
|
|
|
|
self.siblings = []
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
for s in self.siblings:
|
|
|
|
d = {
|
2018-05-30 12:16:33 +03:00
|
|
|
"node": s.hex(),
|
|
|
|
"rev": s.rev(),
|
|
|
|
"user": s.user(),
|
|
|
|
"date": s.date(),
|
|
|
|
"description": s.description(),
|
|
|
|
"branch": s.branch(),
|
2015-11-10 12:22:40 +03:00
|
|
|
}
|
2018-05-30 12:16:33 +03:00
|
|
|
if util.safehasattr(s, "path"):
|
|
|
|
d["file"] = s.path()
|
2015-11-10 12:22:40 +03:00
|
|
|
yield d
|
2013-01-14 19:55:48 +04:00
|
|
|
|
2015-11-10 12:22:40 +03:00
|
|
|
def __len__(self):
|
|
|
|
return len(self.siblings)
|
2008-03-28 21:37:28 +03:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2017-09-30 11:08:01 +03:00
|
|
|
def difffeatureopts(req, ui, section):
|
2018-05-30 12:16:33 +03:00
|
|
|
diffopts = patch.difffeatureopts(
|
|
|
|
ui, untrusted=True, section=section, whitespace=True
|
|
|
|
)
|
2017-09-30 11:08:01 +03:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
for k in ("ignorews", "ignorewsamount", "ignorewseol", "ignoreblanklines"):
|
2017-09-30 11:08:01 +03:00
|
|
|
v = req.form.get(k, [None])[0]
|
|
|
|
if v is not None:
|
2017-10-01 14:29:33 +03:00
|
|
|
v = util.parsebool(v)
|
|
|
|
setattr(diffopts, k, v if v is not None else True)
|
2017-09-30 11:08:01 +03:00
|
|
|
|
|
|
|
return diffopts
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2017-09-30 11:08:01 +03:00
|
|
|
def annotate(req, fctx, ui):
|
2018-05-30 12:16:33 +03:00
|
|
|
diffopts = difffeatureopts(req, ui, "annotate")
|
2016-10-08 18:10:34 +03:00
|
|
|
return fctx.annotate(follow=True, linenumber=True, diffopts=diffopts)
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2009-01-19 15:20:47 +03:00
|
|
|
def parents(ctx, hide=None):
|
2015-02-19 14:32:06 +03:00
|
|
|
if isinstance(ctx, context.basefilectx):
|
|
|
|
introrev = ctx.introrev()
|
|
|
|
if ctx.changectx().rev() != introrev:
|
2015-03-13 06:15:06 +03:00
|
|
|
return _siblings([ctx.repo()[introrev]], hide)
|
2009-01-19 15:20:47 +03:00
|
|
|
return _siblings(ctx.parents(), hide)
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2009-01-19 15:20:47 +03:00
|
|
|
def children(ctx, hide=None):
|
|
|
|
return _siblings(ctx.children(), hide)
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2008-04-01 06:49:33 +04:00
|
|
|
def renamelink(fctx):
|
2008-04-01 08:14:25 +04:00
|
|
|
r = fctx.renamed()
|
2008-03-28 21:37:28 +03:00
|
|
|
if r:
|
2018-05-30 12:16:33 +03:00
|
|
|
return [{"file": r[0], "node": hex(r[1])}]
|
2008-03-28 21:37:28 +03:00
|
|
|
return []
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2008-03-28 21:37:28 +03:00
|
|
|
def nodetagsdict(repo, node):
|
|
|
|
return [{"name": i} for i in repo.nodetags(node)]
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2011-03-12 13:19:44 +03:00
|
|
|
def nodebookmarksdict(repo, node):
|
|
|
|
return [{"name": i} for i in repo.nodebookmarks(node)]
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2008-03-28 21:37:28 +03:00
|
|
|
def nodebranchdict(repo, ctx):
|
|
|
|
branches = []
|
|
|
|
branch = ctx.branch()
|
|
|
|
# If this is an empty repo, ctx.node() == nullid,
|
2012-05-13 16:04:04 +04:00
|
|
|
# ctx.branch() == 'default'.
|
|
|
|
try:
|
|
|
|
branchnode = repo.branchtip(branch)
|
|
|
|
except error.RepoLookupError:
|
|
|
|
branchnode = None
|
|
|
|
if branchnode == ctx.node():
|
2008-03-28 21:37:28 +03:00
|
|
|
branches.append({"name": branch})
|
|
|
|
return branches
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2008-03-28 21:37:28 +03:00
|
|
|
def nodeinbranch(repo, ctx):
|
|
|
|
branches = []
|
|
|
|
branch = ctx.branch()
|
2012-05-13 16:04:04 +04:00
|
|
|
try:
|
|
|
|
branchnode = repo.branchtip(branch)
|
|
|
|
except error.RepoLookupError:
|
|
|
|
branchnode = None
|
2018-05-30 12:16:33 +03:00
|
|
|
if branch != "default" and branchnode != ctx.node():
|
2008-03-28 21:37:28 +03:00
|
|
|
branches.append({"name": branch})
|
|
|
|
return branches
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2008-03-28 21:37:28 +03:00
|
|
|
def nodebranchnodefault(ctx):
|
|
|
|
branches = []
|
|
|
|
branch = ctx.branch()
|
2018-05-30 12:16:33 +03:00
|
|
|
if branch != "default":
|
2008-03-28 21:37:28 +03:00
|
|
|
branches.append({"name": branch})
|
|
|
|
return branches
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2008-03-28 21:37:28 +03:00
|
|
|
def showtag(repo, tmpl, t1, node=nullid, **args):
|
|
|
|
for t in repo.nodetags(node):
|
|
|
|
yield tmpl(t1, tag=t, **args)
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2011-03-12 13:19:44 +03:00
|
|
|
def showbookmark(repo, tmpl, t1, node=nullid, **args):
|
|
|
|
for t in repo.nodebookmarks(node):
|
|
|
|
yield tmpl(t1, bookmark=t, **args)
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2015-09-01 17:28:45 +03:00
|
|
|
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:
|
2018-05-30 12:16:33 +03:00
|
|
|
status = "closed"
|
2015-09-01 17:28:45 +03:00
|
|
|
elif ctx.node() not in heads:
|
2018-05-30 12:16:33 +03:00
|
|
|
status = "inactive"
|
2015-09-01 17:28:45 +03:00
|
|
|
else:
|
2018-05-30 12:16:33 +03:00
|
|
|
status = "open"
|
2015-09-01 17:28:45 +03:00
|
|
|
yield {
|
2018-05-30 12:16:33 +03:00
|
|
|
"parity": next(parity),
|
|
|
|
"branch": ctx.branch(),
|
|
|
|
"status": status,
|
|
|
|
"node": ctx.hex(),
|
|
|
|
"date": ctx.date(),
|
2015-09-01 17:28:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return entries
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2008-03-28 21:37:28 +03:00
|
|
|
def cleanpath(repo, path):
|
2018-05-30 12:16:33 +03:00
|
|
|
path = path.lstrip("/")
|
|
|
|
return pathutil.canonpath(repo.root, "", path)
|
|
|
|
|
2008-03-28 21:37:28 +03:00
|
|
|
|
2015-08-11 08:19:42 +03:00
|
|
|
def changeidctx(repo, changeid):
|
2008-03-28 21:37:28 +03:00
|
|
|
try:
|
2008-06-26 23:35:46 +04:00
|
|
|
ctx = repo[changeid]
|
2009-01-12 19:42:31 +03:00
|
|
|
except error.RepoError:
|
2016-11-10 13:13:19 +03:00
|
|
|
man = repo.manifestlog._revlog
|
2008-11-13 00:19:14 +03:00
|
|
|
ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
|
2008-03-28 21:37:28 +03:00
|
|
|
|
|
|
|
return ctx
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2015-08-11 08:19:42 +03:00
|
|
|
def changectx(repo, req):
|
2012-11-13 02:05:39 +04:00
|
|
|
changeid = "tip"
|
2018-05-30 12:16:33 +03:00
|
|
|
if "node" in req.form:
|
|
|
|
changeid = req.form["node"][0]
|
|
|
|
ipos = changeid.find(":")
|
2012-11-13 02:05:39 +04:00
|
|
|
if ipos != -1:
|
2018-05-30 12:16:33 +03:00
|
|
|
changeid = changeid[(ipos + 1) :]
|
|
|
|
elif "manifest" in req.form:
|
|
|
|
changeid = req.form["manifest"][0]
|
2012-11-13 02:05:39 +04:00
|
|
|
|
|
|
|
return changeidctx(repo, changeid)
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2012-11-13 02:05:39 +04:00
|
|
|
def basechangectx(repo, req):
|
2018-05-30 12:16:33 +03:00
|
|
|
if "node" in req.form:
|
|
|
|
changeid = req.form["node"][0]
|
|
|
|
ipos = changeid.find(":")
|
2012-11-13 02:05:39 +04:00
|
|
|
if ipos != -1:
|
|
|
|
changeid = changeid[:ipos]
|
|
|
|
return changeidctx(repo, changeid)
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2008-03-28 21:37:28 +03:00
|
|
|
def filectx(repo, req):
|
2018-05-30 12:16:33 +03:00
|
|
|
if "file" not in req.form:
|
|
|
|
raise ErrorResponse(HTTP_NOT_FOUND, "file not given")
|
|
|
|
path = cleanpath(repo, req.form["file"][0])
|
|
|
|
if "node" in req.form:
|
|
|
|
changeid = req.form["node"][0]
|
|
|
|
elif "filenode" in req.form:
|
|
|
|
changeid = req.form["filenode"][0]
|
2012-07-30 13:02:10 +04:00
|
|
|
else:
|
2018-05-30 12:16:33 +03:00
|
|
|
raise ErrorResponse(HTTP_NOT_FOUND, "node or filenode not given")
|
2008-03-28 21:37:28 +03:00
|
|
|
try:
|
2008-06-26 23:35:46 +04:00
|
|
|
fctx = repo[changeid][path]
|
2009-01-12 19:42:31 +03:00
|
|
|
except error.RepoError:
|
2008-03-28 21:37:28 +03:00
|
|
|
fctx = repo.filectx(path, fileid=changeid)
|
|
|
|
|
|
|
|
return fctx
|
2008-11-03 22:41:48 +03:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2017-01-19 19:41:00 +03:00
|
|
|
def linerange(req):
|
2018-05-30 12:16:33 +03:00
|
|
|
linerange = req.form.get("linerange")
|
2017-01-19 19:41:00 +03:00
|
|
|
if linerange is None:
|
|
|
|
return None
|
|
|
|
if len(linerange) > 1:
|
2018-05-30 12:16:33 +03:00
|
|
|
raise ErrorResponse(HTTP_BAD_REQUEST, "redundant linerange parameter")
|
2017-01-19 19:41:00 +03:00
|
|
|
try:
|
2018-05-30 12:16:33 +03:00
|
|
|
fromline, toline = map(int, linerange[0].split(":", 1))
|
2017-01-19 19:41:00 +03:00
|
|
|
except ValueError:
|
2018-05-30 12:16:33 +03:00
|
|
|
raise ErrorResponse(HTTP_BAD_REQUEST, "invalid linerange parameter")
|
2017-01-19 19:41:00 +03:00
|
|
|
try:
|
|
|
|
return util.processlinerange(fromline, toline)
|
|
|
|
except error.ParseError as exc:
|
|
|
|
raise ErrorResponse(HTTP_BAD_REQUEST, str(exc))
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2017-01-19 19:41:00 +03:00
|
|
|
def formatlinerange(fromline, toline):
|
2018-05-30 12:16:33 +03:00
|
|
|
return "%d:%d" % (fromline + 1, toline)
|
|
|
|
|
2017-01-19 19:41:00 +03:00
|
|
|
|
2015-11-13 09:35:36 +03:00
|
|
|
def commonentry(repo, ctx):
|
|
|
|
node = ctx.node()
|
|
|
|
return {
|
2018-05-30 12:16:33 +03:00
|
|
|
"rev": ctx.rev(),
|
|
|
|
"node": hex(node),
|
|
|
|
"author": ctx.user(),
|
|
|
|
"desc": ctx.description(),
|
|
|
|
"date": ctx.date(),
|
|
|
|
"extra": ctx.extra(),
|
|
|
|
"phase": ctx.phasestr(),
|
|
|
|
"obsolete": ctx.obsolete(),
|
|
|
|
"instabilities": [{"instability": i} for i in ctx.instabilities()],
|
|
|
|
"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),
|
2015-11-13 09:35:36 +03:00
|
|
|
}
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2015-01-07 07:14:52 +03:00
|
|
|
def changelistentry(web, ctx, tmpl):
|
2018-05-30 12:16:33 +03:00
|
|
|
"""Obtain a dictionary to be used for entries in a changelist.
|
2015-01-07 07:14:52 +03:00
|
|
|
|
|
|
|
This function is called when producing items for the "entries" list passed
|
|
|
|
to the "shortlog" and "changelog" templates.
|
2018-05-30 12:16:33 +03:00
|
|
|
"""
|
2015-01-07 07:14:52 +03:00
|
|
|
repo = web.repo
|
|
|
|
rev = ctx.rev()
|
|
|
|
n = ctx.node()
|
2018-05-30 12:16:33 +03:00
|
|
|
showtags = showtag(repo, tmpl, "changelogtag", n)
|
2015-01-07 07:14:52 +03:00
|
|
|
files = listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
|
|
|
|
|
2015-11-13 09:35:36 +03:00
|
|
|
entry = commonentry(repo, ctx)
|
|
|
|
entry.update(
|
2016-03-31 13:09:09 +03:00
|
|
|
allparents=lambda **x: parents(ctx),
|
2015-11-13 09:35:36 +03:00
|
|
|
parent=lambda **x: parents(ctx, rev - 1),
|
|
|
|
child=lambda **x: children(ctx, rev + 1),
|
|
|
|
changelogtag=showtags,
|
|
|
|
files=files,
|
|
|
|
)
|
|
|
|
return entry
|
2015-01-07 07:14:52 +03:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
hgweb: provide symrev (symbolic revision) property to the templates
One of the features of hgweb is that current position in repo history is
remembered between separate requests. That is, links from /rev/<node_hash> lead
to /file/<node_hash> or /log/<node_hash>, so it's easy to dig deep into the
history. However, such links could only use node hashes and local revision
numbers, so while staying at one exact revision is easy, staying on top of the
changes is not, because hashes presumably can't change (local revision numbers
can, but probably not in a way you'd find useful for navigating).
So while you could use 'tip' or 'default' in a url, links on that page would be
permanent. This is not always desired (think /rev/tip or /graph/stable or
/log/@) and is sometimes just confusing (i.e. /log/<not the tip hash>, when
recent history is not displayed). And if user changed url deliberately to say
default instead of <some node hash>, the page ignores that fact and uses node
hash in its links, which means that navigation is, in a way, broken.
This new property, symrev, is used for storing current revision the way it was
specified, so then templates can use it in links and thus "not dereference" the
symbolic revision. It is an additional way to produce links, so not every link
needs to drop {node|short} in favor of {symrev}, many will still use node hash
(log and filelog entries, annotate lines, etc).
Some pages (e.g. summary, tags) always use the tip changeset for their context,
in such cases symrev is set to 'tip'. This is needed in case the pages want to
provide archive links.
highlight extension needs to be updated, since _filerevision now takes an
additional positional argument (signature "web, req, tmpl" is used by most of
webcommands.py functions).
More references to symbolic revisions and related gripes: issue2296, issue2826,
issue3594, issue3634.
2015-06-15 21:07:25 +03:00
|
|
|
def symrevorshortnode(req, ctx):
|
2018-05-30 12:16:33 +03:00
|
|
|
if "node" in req.form:
|
|
|
|
return templatefilters.revescape(req.form["node"][0])
|
hgweb: provide symrev (symbolic revision) property to the templates
One of the features of hgweb is that current position in repo history is
remembered between separate requests. That is, links from /rev/<node_hash> lead
to /file/<node_hash> or /log/<node_hash>, so it's easy to dig deep into the
history. However, such links could only use node hashes and local revision
numbers, so while staying at one exact revision is easy, staying on top of the
changes is not, because hashes presumably can't change (local revision numbers
can, but probably not in a way you'd find useful for navigating).
So while you could use 'tip' or 'default' in a url, links on that page would be
permanent. This is not always desired (think /rev/tip or /graph/stable or
/log/@) and is sometimes just confusing (i.e. /log/<not the tip hash>, when
recent history is not displayed). And if user changed url deliberately to say
default instead of <some node hash>, the page ignores that fact and uses node
hash in its links, which means that navigation is, in a way, broken.
This new property, symrev, is used for storing current revision the way it was
specified, so then templates can use it in links and thus "not dereference" the
symbolic revision. It is an additional way to produce links, so not every link
needs to drop {node|short} in favor of {symrev}, many will still use node hash
(log and filelog entries, annotate lines, etc).
Some pages (e.g. summary, tags) always use the tip changeset for their context,
in such cases symrev is set to 'tip'. This is needed in case the pages want to
provide archive links.
highlight extension needs to be updated, since _filerevision now takes an
additional positional argument (signature "web, req, tmpl" is used by most of
webcommands.py functions).
More references to symbolic revisions and related gripes: issue2296, issue2826,
issue3594, issue3634.
2015-06-15 21:07:25 +03:00
|
|
|
else:
|
|
|
|
return short(ctx.node())
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2015-03-03 02:07:18 +03:00
|
|
|
def changesetentry(web, req, tmpl, ctx):
|
2018-05-30 12:16:33 +03:00
|
|
|
"""Obtain a dictionary to be used to render the "changeset" template."""
|
2015-03-03 02:07:18 +03:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
showtags = showtag(web.repo, tmpl, "changesettag", ctx.node())
|
|
|
|
showbookmarks = showbookmark(web.repo, tmpl, "changesetbookmark", ctx.node())
|
2015-03-03 02:07:18 +03:00
|
|
|
showbranch = nodebranchnodefault(ctx)
|
|
|
|
|
|
|
|
files = []
|
|
|
|
parity = paritygen(web.stripecount)
|
|
|
|
for blockno, f in enumerate(ctx.files()):
|
2018-05-30 12:16:33 +03:00
|
|
|
template = "filenodelink" if f in ctx else "filenolink"
|
|
|
|
files.append(
|
|
|
|
tmpl(
|
|
|
|
template,
|
|
|
|
node=ctx.hex(),
|
|
|
|
file=f,
|
|
|
|
blockno=blockno + 1,
|
|
|
|
parity=next(parity),
|
|
|
|
)
|
|
|
|
)
|
2015-03-03 02:07:18 +03:00
|
|
|
|
|
|
|
basectx = basechangectx(web.repo, req)
|
|
|
|
if basectx is None:
|
|
|
|
basectx = ctx.p1()
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
style = web.config("web", "style")
|
|
|
|
if "style" in req.form:
|
|
|
|
style = req.form["style"][0]
|
2015-03-03 02:07:18 +03:00
|
|
|
|
2017-03-13 12:40:19 +03:00
|
|
|
diff = diffs(web, tmpl, ctx, basectx, None, style)
|
2015-03-03 02:07:18 +03:00
|
|
|
|
|
|
|
parity = paritygen(web.stripecount)
|
|
|
|
diffstatsgen = diffstatgen(ctx, basectx)
|
|
|
|
diffstats = diffstat(tmpl, ctx, diffstatsgen, parity)
|
|
|
|
|
|
|
|
return dict(
|
|
|
|
diff=diff,
|
hgweb: provide symrev (symbolic revision) property to the templates
One of the features of hgweb is that current position in repo history is
remembered between separate requests. That is, links from /rev/<node_hash> lead
to /file/<node_hash> or /log/<node_hash>, so it's easy to dig deep into the
history. However, such links could only use node hashes and local revision
numbers, so while staying at one exact revision is easy, staying on top of the
changes is not, because hashes presumably can't change (local revision numbers
can, but probably not in a way you'd find useful for navigating).
So while you could use 'tip' or 'default' in a url, links on that page would be
permanent. This is not always desired (think /rev/tip or /graph/stable or
/log/@) and is sometimes just confusing (i.e. /log/<not the tip hash>, when
recent history is not displayed). And if user changed url deliberately to say
default instead of <some node hash>, the page ignores that fact and uses node
hash in its links, which means that navigation is, in a way, broken.
This new property, symrev, is used for storing current revision the way it was
specified, so then templates can use it in links and thus "not dereference" the
symbolic revision. It is an additional way to produce links, so not every link
needs to drop {node|short} in favor of {symrev}, many will still use node hash
(log and filelog entries, annotate lines, etc).
Some pages (e.g. summary, tags) always use the tip changeset for their context,
in such cases symrev is set to 'tip'. This is needed in case the pages want to
provide archive links.
highlight extension needs to be updated, since _filerevision now takes an
additional positional argument (signature "web, req, tmpl" is used by most of
webcommands.py functions).
More references to symbolic revisions and related gripes: issue2296, issue2826,
issue3594, issue3634.
2015-06-15 21:07:25 +03:00
|
|
|
symrev=symrevorshortnode(req, ctx),
|
2015-03-03 02:07:18 +03:00
|
|
|
basenode=basectx.hex(),
|
|
|
|
changesettag=showtags,
|
|
|
|
changesetbookmark=showbookmarks,
|
|
|
|
changesetbranch=showbranch,
|
|
|
|
files=files,
|
|
|
|
diffsummary=lambda **x: diffsummary(diffstatsgen),
|
|
|
|
diffstat=diffstats,
|
|
|
|
archives=web.archivelist(ctx.hex()),
|
2018-05-30 12:16:33 +03:00
|
|
|
**commonentry(web.repo, ctx)
|
|
|
|
)
|
|
|
|
|
2015-03-03 02:07:18 +03:00
|
|
|
|
2008-11-03 22:31:53 +03:00
|
|
|
def listfilediffs(tmpl, files, node, max):
|
|
|
|
for f in files[:max]:
|
2018-05-30 12:16:33 +03:00
|
|
|
yield tmpl("filedifflink", node=hex(node), file=f)
|
2008-11-03 22:31:53 +03:00
|
|
|
if len(files) > max:
|
2018-05-30 12:16:33 +03:00
|
|
|
yield tmpl("fileellipses")
|
2008-11-03 22:31:53 +03:00
|
|
|
|
2008-11-03 22:41:48 +03:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
def diffs(web, tmpl, ctx, basectx, files, style, linerange=None, lineidprefix=""):
|
2017-03-06 11:28:33 +03:00
|
|
|
def prettyprintlines(lines, blockno):
|
|
|
|
for lineno, l in enumerate(lines, 1):
|
2017-03-06 11:44:39 +03:00
|
|
|
difflineno = "%d.%d" % (blockno, lineno)
|
2018-05-30 12:16:33 +03:00
|
|
|
if l.startswith("+"):
|
2008-11-03 22:41:48 +03:00
|
|
|
ltype = "difflineplus"
|
2018-05-30 12:16:33 +03:00
|
|
|
elif l.startswith("-"):
|
2008-11-03 22:41:48 +03:00
|
|
|
ltype = "difflineminus"
|
2018-05-30 12:16:33 +03:00
|
|
|
elif l.startswith("@"):
|
2008-11-03 22:41:48 +03:00
|
|
|
ltype = "difflineat"
|
|
|
|
else:
|
|
|
|
ltype = "diffline"
|
2018-05-30 12:16:33 +03:00
|
|
|
yield tmpl(
|
|
|
|
ltype,
|
|
|
|
line=l,
|
|
|
|
lineno=lineno,
|
|
|
|
lineid=lineidprefix + "l%s" % difflineno,
|
|
|
|
linenumber="% 8s" % difflineno,
|
|
|
|
)
|
2008-11-03 22:41:48 +03:00
|
|
|
|
2017-03-13 12:40:19 +03:00
|
|
|
repo = web.repo
|
2008-11-03 22:41:48 +03:00
|
|
|
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)
|
2017-01-17 19:25:48 +03:00
|
|
|
node1 = basectx.node()
|
2008-11-03 22:41:48 +03:00
|
|
|
node2 = ctx.node()
|
2017-03-13 12:40:19 +03:00
|
|
|
parity = paritygen(web.stripecount)
|
2008-11-03 22:41:48 +03:00
|
|
|
|
2017-03-06 11:28:33 +03:00
|
|
|
diffhunks = patch.diffhunks(repo, node1, node2, m, opts=diffopts)
|
2017-10-05 22:20:08 +03:00
|
|
|
for blockno, (fctx1, fctx2, header, hunks) in enumerate(diffhunks, 1):
|
2018-05-30 12:16:33 +03:00
|
|
|
if style != "raw":
|
2017-03-06 11:28:33 +03:00
|
|
|
header = header[1:]
|
2018-05-30 12:16:33 +03:00
|
|
|
lines = [h + "\n" for h in header]
|
2017-03-06 11:28:33 +03:00
|
|
|
for hunkrange, hunklines in hunks:
|
2017-03-13 17:15:49 +03:00
|
|
|
if linerange is not None and hunkrange is not None:
|
|
|
|
s1, l1, s2, l2 = hunkrange
|
2017-04-01 13:24:59 +03:00
|
|
|
if not mdiff.hunkinrange((s2, l2), linerange):
|
2017-03-13 17:15:49 +03:00
|
|
|
continue
|
2017-03-06 11:28:33 +03:00
|
|
|
lines.extend(hunklines)
|
|
|
|
if lines:
|
2018-05-30 12:16:33 +03:00
|
|
|
yield tmpl(
|
|
|
|
"diffblock",
|
|
|
|
parity=next(parity),
|
|
|
|
blockno=blockno,
|
|
|
|
lines=prettyprintlines(lines, blockno),
|
|
|
|
)
|
|
|
|
|
2008-11-08 01:31:12 +03:00
|
|
|
|
2012-07-31 16:14:15 +04:00
|
|
|
def compare(tmpl, context, leftlines, rightlines):
|
2018-05-30 12:16:33 +03:00
|
|
|
"""Generator function that provides side-by-side comparison data."""
|
2012-07-08 19:17:02 +04:00
|
|
|
|
|
|
|
def compline(type, leftlineno, leftline, rightlineno, rightline):
|
2018-05-30 12:16:33 +03:00
|
|
|
lineid = leftlineno and ("l%s" % leftlineno) or ""
|
|
|
|
lineid += rightlineno and ("r%s" % rightlineno) or ""
|
|
|
|
return tmpl(
|
|
|
|
"comparisonline",
|
|
|
|
type=type,
|
|
|
|
lineid=lineid,
|
|
|
|
leftlineno=leftlineno,
|
|
|
|
leftlinenumber="% 6s" % (leftlineno or ""),
|
|
|
|
leftline=leftline or "",
|
|
|
|
rightlineno=rightlineno,
|
|
|
|
rightlinenumber="% 6s" % (rightlineno or ""),
|
|
|
|
rightline=rightline or "",
|
|
|
|
)
|
2012-07-08 19:17:02 +04:00
|
|
|
|
|
|
|
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):
|
2018-05-30 12:16:33 +03:00
|
|
|
yield compline(
|
|
|
|
type=type,
|
|
|
|
leftlineno=llo + i + 1,
|
|
|
|
leftline=leftlines[llo + i],
|
|
|
|
rightlineno=rlo + i + 1,
|
|
|
|
rightline=rightlines[rlo + i],
|
|
|
|
)
|
2012-07-08 19:17:02 +04:00
|
|
|
if len1 > len2:
|
|
|
|
for i in xrange(llo + count, lhi):
|
2018-05-30 12:16:33 +03:00
|
|
|
yield compline(
|
|
|
|
type=type,
|
|
|
|
leftlineno=i + 1,
|
|
|
|
leftline=leftlines[i],
|
|
|
|
rightlineno=None,
|
|
|
|
rightline=None,
|
|
|
|
)
|
2012-07-08 19:17:02 +04:00
|
|
|
elif len2 > len1:
|
|
|
|
for i in xrange(rlo + count, rhi):
|
2018-05-30 12:16:33 +03:00
|
|
|
yield compline(
|
|
|
|
type=type,
|
|
|
|
leftlineno=None,
|
|
|
|
leftline=None,
|
|
|
|
rightlineno=i + 1,
|
|
|
|
rightline=rightlines[i],
|
|
|
|
)
|
2012-07-08 19:17:02 +04:00
|
|
|
|
|
|
|
s = difflib.SequenceMatcher(None, leftlines, rightlines)
|
|
|
|
if context < 0:
|
2018-05-30 12:16:33 +03:00
|
|
|
yield tmpl("comparisonblock", lines=getblock(s.get_opcodes()))
|
2012-07-08 19:17:02 +04:00
|
|
|
else:
|
2012-07-31 16:14:15 +04:00
|
|
|
for oc in s.get_grouped_opcodes(n=context):
|
2018-05-30 12:16:33 +03:00
|
|
|
yield tmpl("comparisonblock", lines=getblock(oc))
|
|
|
|
|
2012-07-08 19:17:02 +04:00
|
|
|
|
2012-11-13 02:05:39 +04:00
|
|
|
def diffstatgen(ctx, basectx):
|
2018-05-30 12:16:33 +03:00
|
|
|
"""Generator function that provides the diffstat data."""
|
2011-05-28 10:44:45 +04:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
stats = patch.diffstatdata(util.iterlines(ctx.diff(basectx, noprefix=False)))
|
2011-05-28 10:44:45 +04:00
|
|
|
maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
|
2011-06-11 17:11:43 +04:00
|
|
|
while True:
|
|
|
|
yield stats, maxname, maxtotal, addtotal, removetotal, binary
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2011-06-11 17:11:43 +04:00
|
|
|
def diffsummary(statgen):
|
2018-05-30 12:16:33 +03:00
|
|
|
"""Return a short summary of the diff."""
|
2011-06-11 17:11:43 +04:00
|
|
|
|
2016-05-17 00:30:53 +03:00
|
|
|
stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
|
2018-05-30 12:16:33 +03:00
|
|
|
return _(" %d files changed, %d insertions(+), %d deletions(-)\n") % (
|
|
|
|
len(stats),
|
|
|
|
addtotal,
|
|
|
|
removetotal,
|
|
|
|
)
|
|
|
|
|
2011-06-11 17:11:43 +04:00
|
|
|
|
|
|
|
def diffstat(tmpl, ctx, statgen, parity):
|
2018-05-30 12:16:33 +03:00
|
|
|
"""Return a diffstat template for each file in the diff."""
|
2011-06-11 17:11:43 +04:00
|
|
|
|
2016-05-17 00:30:53 +03:00
|
|
|
stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
|
2011-06-08 21:14:22 +04:00
|
|
|
files = ctx.files()
|
|
|
|
|
|
|
|
def pct(i):
|
|
|
|
if maxtotal == 0:
|
|
|
|
return 0
|
|
|
|
return (float(i) / maxtotal) * 100
|
|
|
|
|
2011-06-08 21:15:49 +04:00
|
|
|
fileno = 0
|
2011-06-08 21:14:22 +04:00
|
|
|
for filename, adds, removes, isbinary in stats:
|
2018-05-30 12:16:33 +03:00
|
|
|
template = "diffstatlink" if filename in files else "diffstatnolink"
|
2011-06-08 21:14:22 +04:00
|
|
|
total = adds + removes
|
2011-06-08 21:15:49 +04:00
|
|
|
fileno += 1
|
2018-05-30 12:16:33 +03:00
|
|
|
yield tmpl(
|
|
|
|
template,
|
|
|
|
node=ctx.hex(),
|
|
|
|
file=filename,
|
|
|
|
fileno=fileno,
|
|
|
|
total=total,
|
|
|
|
addpct=pct(adds),
|
|
|
|
removepct=pct(removes),
|
|
|
|
parity=next(parity),
|
|
|
|
)
|
|
|
|
|
2011-05-28 10:44:45 +04:00
|
|
|
|
2008-11-08 01:31:12 +03:00
|
|
|
class sessionvars(object):
|
2018-05-30 12:16:33 +03:00
|
|
|
def __init__(self, vars, start="?"):
|
2008-11-08 01:31:12 +03:00
|
|
|
self.start = start
|
|
|
|
self.vars = vars
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2008-11-08 01:31:12 +03:00
|
|
|
def __getitem__(self, key):
|
|
|
|
return self.vars[key]
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2008-11-08 01:31:12 +03:00
|
|
|
def __setitem__(self, key, value):
|
|
|
|
self.vars[key] = value
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2008-11-08 01:31:12 +03:00
|
|
|
def __copy__(self):
|
|
|
|
return sessionvars(copy.copy(self.vars), self.start)
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2008-11-08 01:31:12 +03:00
|
|
|
def __iter__(self):
|
|
|
|
separator = self.start
|
2012-12-12 05:38:14 +04:00
|
|
|
for key, value in sorted(self.vars.iteritems()):
|
2018-05-30 12:16:33 +03:00
|
|
|
yield {
|
|
|
|
"name": key,
|
|
|
|
"value": pycompat.bytestr(value),
|
|
|
|
"separator": separator,
|
2017-10-17 05:44:06 +03:00
|
|
|
}
|
2018-05-30 12:16:33 +03:00
|
|
|
separator = "&"
|
|
|
|
|
2010-10-10 19:42:44 +04:00
|
|
|
|
2015-11-01 07:58:32 +03:00
|
|
|
class wsgiui(uimod.ui):
|
2010-10-10 19:42:44 +04:00
|
|
|
# default termwidth breaks under mod_wsgi
|
|
|
|
def termwidth(self):
|
|
|
|
return 80
|
2015-08-23 01:40:33 +03:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2015-08-23 01:40:33 +03:00
|
|
|
def getwebsubs(repo):
|
|
|
|
websubtable = []
|
2018-05-30 12:16:33 +03:00
|
|
|
websubdefs = repo.ui.configitems("websub")
|
2015-08-23 01:40:33 +03:00
|
|
|
# we must maintain interhg backwards compatibility
|
2018-05-30 12:16:33 +03:00
|
|
|
websubdefs += repo.ui.configitems("interhg")
|
2015-08-23 01:40:33 +03:00
|
|
|
for key, pattern in websubdefs:
|
|
|
|
# grab the delimiter from the character after the "s"
|
|
|
|
unesc = pattern[1]
|
|
|
|
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(
|
2018-05-30 12:16:33 +03:00
|
|
|
r"^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$"
|
|
|
|
% (delim, delim, delim),
|
|
|
|
pattern,
|
|
|
|
)
|
2015-08-23 01:40:33 +03:00
|
|
|
if not match:
|
2018-05-30 12:16:33 +03:00
|
|
|
repo.ui.warn(_("websub: invalid pattern for %s: %s\n") % (key, pattern))
|
2015-08-23 01:40:33 +03:00
|
|
|
continue
|
|
|
|
|
|
|
|
# we need to unescape the delimiter for regexp and format
|
2018-05-30 12:16:33 +03:00
|
|
|
delim_re = re.compile(r"(?<!\\)\\%s" % delim)
|
2015-08-23 01:40:33 +03:00
|
|
|
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:
|
2018-05-30 12:16:33 +03:00
|
|
|
repo.ui.warn(_("websub: invalid regexp for %s: %s\n") % (key, regexp))
|
2015-08-23 01:40:33 +03:00
|
|
|
return websubtable
|