grim/hgwebplus

Start the packaging and release tasks

2020-04-10, Gary Kramlich
0e516e743754
Parents a1104ec57f23
Children 54bf3dd559b0
Start the packaging and release tasks
  • +340 -0
    LICENSE
  • +0 -240
    hgkeeper.py
  • +23 -0
    setup.py
  • +240 -0
    src/hgwebplus.py
  • --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/LICENSE Fri Apr 10 17:44:19 2020 -0500
    @@ -0,0 +1,340 @@
    + GNU GENERAL PUBLIC LICENSE
    + Version 2, June 1991
    +
    + Copyright (C) 1989, 1991 Free Software Foundation, Inc.
    + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    + Everyone is permitted to copy and distribute verbatim copies
    + of this license document, but changing it is not allowed.
    +
    + Preamble
    +
    + The licenses for most software are designed to take away your
    +freedom to share and change it. By contrast, the GNU General Public
    +License is intended to guarantee your freedom to share and change free
    +software--to make sure the software is free for all its users. This
    +General Public License applies to most of the Free Software
    +Foundation's software and to any other program whose authors commit to
    +using it. (Some other Free Software Foundation software is covered by
    +the GNU Library General Public License instead.) You can apply it to
    +your programs, too.
    +
    + When we speak of free software, we are referring to freedom, not
    +price. Our General Public Licenses are designed to make sure that you
    +have the freedom to distribute copies of free software (and charge for
    +this service if you wish), that you receive source code or can get it
    +if you want it, that you can change the software or use pieces of it
    +in new free programs; and that you know you can do these things.
    +
    + To protect your rights, we need to make restrictions that forbid
    +anyone to deny you these rights or to ask you to surrender the rights.
    +These restrictions translate to certain responsibilities for you if you
    +distribute copies of the software, or if you modify it.
    +
    + For example, if you distribute copies of such a program, whether
    +gratis or for a fee, you must give the recipients all the rights that
    +you have. You must make sure that they, too, receive or can get the
    +source code. And you must show them these terms so they know their
    +rights.
    +
    + We protect your rights with two steps: (1) copyright the software, and
    +(2) offer you this license which gives you legal permission to copy,
    +distribute and/or modify the software.
    +
    + Also, for each author's protection and ours, we want to make certain
    +that everyone understands that there is no warranty for this free
    +software. If the software is modified by someone else and passed on, we
    +want its recipients to know that what they have is not the original, so
    +that any problems introduced by others will not reflect on the original
    +authors' reputations.
    +
    + Finally, any free program is threatened constantly by software
    +patents. We wish to avoid the danger that redistributors of a free
    +program will individually obtain patent licenses, in effect making the
    +program proprietary. To prevent this, we have made it clear that any
    +patent must be licensed for everyone's free use or not licensed at all.
    +
    + The precise terms and conditions for copying, distribution and
    +modification follow.
    +
    + GNU GENERAL PUBLIC LICENSE
    + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
    +
    + 0. This License applies to any program or other work which contains
    +a notice placed by the copyright holder saying it may be distributed
    +under the terms of this General Public License. The "Program", below,
    +refers to any such program or work, and a "work based on the Program"
    +means either the Program or any derivative work under copyright law:
    +that is to say, a work containing the Program or a portion of it,
    +either verbatim or with modifications and/or translated into another
    +language. (Hereinafter, translation is included without limitation in
    +the term "modification".) Each licensee is addressed as "you".
    +
    +Activities other than copying, distribution and modification are not
    +covered by this License; they are outside its scope. The act of
    +running the Program is not restricted, and the output from the Program
    +is covered only if its contents constitute a work based on the
    +Program (independent of having been made by running the Program).
    +Whether that is true depends on what the Program does.
    +
    + 1. You may copy and distribute verbatim copies of the Program's
    +source code as you receive it, in any medium, provided that you
    +conspicuously and appropriately publish on each copy an appropriate
    +copyright notice and disclaimer of warranty; keep intact all the
    +notices that refer to this License and to the absence of any warranty;
    +and give any other recipients of the Program a copy of this License
    +along with the Program.
    +
    +You may charge a fee for the physical act of transferring a copy, and
    +you may at your option offer warranty protection in exchange for a fee.
    +
    + 2. You may modify your copy or copies of the Program or any portion
    +of it, thus forming a work based on the Program, and copy and
    +distribute such modifications or work under the terms of Section 1
    +above, provided that you also meet all of these conditions:
    +
    + a) You must cause the modified files to carry prominent notices
    + stating that you changed the files and the date of any change.
    +
    + b) You must cause any work that you distribute or publish, that in
    + whole or in part contains or is derived from the Program or any
    + part thereof, to be licensed as a whole at no charge to all third
    + parties under the terms of this License.
    +
    + c) If the modified program normally reads commands interactively
    + when run, you must cause it, when started running for such
    + interactive use in the most ordinary way, to print or display an
    + announcement including an appropriate copyright notice and a
    + notice that there is no warranty (or else, saying that you provide
    + a warranty) and that users may redistribute the program under
    + these conditions, and telling the user how to view a copy of this
    + License. (Exception: if the Program itself is interactive but
    + does not normally print such an announcement, your work based on
    + the Program is not required to print an announcement.)
    +
    +These requirements apply to the modified work as a whole. If
    +identifiable sections of that work are not derived from the Program,
    +and can be reasonably considered independent and separate works in
    +themselves, then this License, and its terms, do not apply to those
    +sections when you distribute them as separate works. But when you
    +distribute the same sections as part of a whole which is a work based
    +on the Program, the distribution of the whole must be on the terms of
    +this License, whose permissions for other licensees extend to the
    +entire whole, and thus to each and every part regardless of who wrote it.
    +
    +Thus, it is not the intent of this section to claim rights or contest
    +your rights to work written entirely by you; rather, the intent is to
    +exercise the right to control the distribution of derivative or
    +collective works based on the Program.
    +
    +In addition, mere aggregation of another work not based on the Program
    +with the Program (or with a work based on the Program) on a volume of
    +a storage or distribution medium does not bring the other work under
    +the scope of this License.
    +
    + 3. You may copy and distribute the Program (or a work based on it,
    +under Section 2) in object code or executable form under the terms of
    +Sections 1 and 2 above provided that you also do one of the following:
    +
    + a) Accompany it with the complete corresponding machine-readable
    + source code, which must be distributed under the terms of Sections
    + 1 and 2 above on a medium customarily used for software interchange; or,
    +
    + b) Accompany it with a written offer, valid for at least three
    + years, to give any third party, for a charge no more than your
    + cost of physically performing source distribution, a complete
    + machine-readable copy of the corresponding source code, to be
    + distributed under the terms of Sections 1 and 2 above on a medium
    + customarily used for software interchange; or,
    +
    + c) Accompany it with the information you received as to the offer
    + to distribute corresponding source code. (This alternative is
    + allowed only for noncommercial distribution and only if you
    + received the program in object code or executable form with such
    + an offer, in accord with Subsection b above.)
    +
    +The source code for a work means the preferred form of the work for
    +making modifications to it. For an executable work, complete source
    +code means all the source code for all modules it contains, plus any
    +associated interface definition files, plus the scripts used to
    +control compilation and installation of the executable. However, as a
    +special exception, the source code distributed need not include
    +anything that is normally distributed (in either source or binary
    +form) with the major components (compiler, kernel, and so on) of the
    +operating system on which the executable runs, unless that component
    +itself accompanies the executable.
    +
    +If distribution of executable or object code is made by offering
    +access to copy from a designated place, then offering equivalent
    +access to copy the source code from the same place counts as
    +distribution of the source code, even though third parties are not
    +compelled to copy the source along with the object code.
    +
    + 4. You may not copy, modify, sublicense, or distribute the Program
    +except as expressly provided under this License. Any attempt
    +otherwise to copy, modify, sublicense or distribute the Program is
    +void, and will automatically terminate your rights under this License.
    +However, parties who have received copies, or rights, from you under
    +this License will not have their licenses terminated so long as such
    +parties remain in full compliance.
    +
    + 5. You are not required to accept this License, since you have not
    +signed it. However, nothing else grants you permission to modify or
    +distribute the Program or its derivative works. These actions are
    +prohibited by law if you do not accept this License. Therefore, by
    +modifying or distributing the Program (or any work based on the
    +Program), you indicate your acceptance of this License to do so, and
    +all its terms and conditions for copying, distributing or modifying
    +the Program or works based on it.
    +
    + 6. Each time you redistribute the Program (or any work based on the
    +Program), the recipient automatically receives a license from the
    +original licensor to copy, distribute or modify the Program subject to
    +these terms and conditions. You may not impose any further
    +restrictions on the recipients' exercise of the rights granted herein.
    +You are not responsible for enforcing compliance by third parties to
    +this License.
    +
    + 7. If, as a consequence of a court judgment or allegation of patent
    +infringement or for any other reason (not limited to patent issues),
    +conditions are imposed on you (whether by court order, agreement or
    +otherwise) that contradict the conditions of this License, they do not
    +excuse you from the conditions of this License. If you cannot
    +distribute so as to satisfy simultaneously your obligations under this
    +License and any other pertinent obligations, then as a consequence you
    +may not distribute the Program at all. For example, if a patent
    +license would not permit royalty-free redistribution of the Program by
    +all those who receive copies directly or indirectly through you, then
    +the only way you could satisfy both it and this License would be to
    +refrain entirely from distribution of the Program.
    +
    +If any portion of this section is held invalid or unenforceable under
    +any particular circumstance, the balance of the section is intended to
    +apply and the section as a whole is intended to apply in other
    +circumstances.
    +
    +It is not the purpose of this section to induce you to infringe any
    +patents or other property right claims or to contest validity of any
    +such claims; this section has the sole purpose of protecting the
    +integrity of the free software distribution system, which is
    +implemented by public license practices. Many people have made
    +generous contributions to the wide range of software distributed
    +through that system in reliance on consistent application of that
    +system; it is up to the author/donor to decide if he or she is willing
    +to distribute software through any other system and a licensee cannot
    +impose that choice.
    +
    +This section is intended to make thoroughly clear what is believed to
    +be a consequence of the rest of this License.
    +
    + 8. If the distribution and/or use of the Program is restricted in
    +certain countries either by patents or by copyrighted interfaces, the
    +original copyright holder who places the Program under this License
    +may add an explicit geographical distribution limitation excluding
    +those countries, so that distribution is permitted only in or among
    +countries not thus excluded. In such case, this License incorporates
    +the limitation as if written in the body of this License.
    +
    + 9. The Free Software Foundation may publish revised and/or new versions
    +of the General Public License from time to time. Such new versions will
    +be similar in spirit to the present version, but may differ in detail to
    +address new problems or concerns.
    +
    +Each version is given a distinguishing version number. If the Program
    +specifies a version number of this License which applies to it and "any
    +later version", you have the option of following the terms and conditions
    +either of that version or of any later version published by the Free
    +Software Foundation. If the Program does not specify a version number of
    +this License, you may choose any version ever published by the Free Software
    +Foundation.
    +
    + 10. If you wish to incorporate parts of the Program into other free
    +programs whose distribution conditions are different, write to the author
    +to ask for permission. For software which is copyrighted by the Free
    +Software Foundation, write to the Free Software Foundation; we sometimes
    +make exceptions for this. Our decision will be guided by the two goals
    +of preserving the free status of all derivatives of our free software and
    +of promoting the sharing and reuse of software generally.
    +
    + NO WARRANTY
    +
    + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
    +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
    +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
    +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
    +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
    +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
    +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
    +REPAIR OR CORRECTION.
    +
    + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
    +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
    +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
    +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
    +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
    +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
    +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
    +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
    +POSSIBILITY OF SUCH DAMAGES.
    +
    + END OF TERMS AND CONDITIONS
    +
    + How to Apply These Terms to Your New Programs
    +
    + If you develop a new program, and you want it to be of the greatest
    +possible use to the public, the best way to achieve this is to make it
    +free software which everyone can redistribute and change under these terms.
    +
    + To do so, attach the following notices to the program. It is safest
    +to attach them to the start of each source file to most effectively
    +convey the exclusion of warranty; and each file should have at least
    +the "copyright" line and a pointer to where the full notice is found.
    +
    + <one line to give the program's name and a brief idea of what it does.>
    + Copyright (C) <year> <name of author>
    +
    + This program is free software; you can redistribute it and/or modify
    + it under the terms of the GNU General Public License as published by
    + the Free Software Foundation; either version 2 of the License, or
    + (at your option) any later version.
    +
    + This program is distributed in the hope that it will be useful,
    + but WITHOUT ANY WARRANTY; without even the implied warranty of
    + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + GNU General Public License for more details.
    +
    + You should have received a copy of the GNU General Public License
    + along with this program; if not, write to the Free Software
    + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    +
    +
    +Also add information on how to contact you by electronic and paper mail.
    +
    +If the program is interactive, make it output a short notice like this
    +when it starts in an interactive mode:
    +
    + Gnomovision version 69, Copyright (C) year name of author
    + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    + This is free software, and you are welcome to redistribute it
    + under certain conditions; type `show c' for details.
    +
    +The hypothetical commands `show w' and `show c' should show the appropriate
    +parts of the General Public License. Of course, the commands you use may
    +be called something other than `show w' and `show c'; they could even be
    +mouse-clicks or menu items--whatever suits your program.
    +
    +You should also get your employer (if you work as a programmer) or your
    +school, if any, to sign a "copyright disclaimer" for the program, if
    +necessary. Here is a sample; alter the names:
    +
    + Yoyodyne, Inc., hereby disclaims all copyright interest in the program
    + `Gnomovision' (which makes passes at compilers) written by James Hacker.
    +
    + <signature of Ty Coon>, 1 April 1989
    + Ty Coon, President of Vice
    +
    +This General Public License does not permit incorporating your program into
    +proprietary programs. If your program is a subroutine library, you may
    +consider it more useful to permit linking proprietary applications with the
    +library. If this is what you want to do, use the GNU Library General
    +Public License instead of this License.
    --- a/hgkeeper.py Wed Apr 08 02:41:19 2020 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,240 +0,0 @@
    -# vi:et:ts=4 sw=4 sts=4
    -
    -import re
    -
    -from hashlib import md5 as hashlib_md5
    -
    -from mercurial import (
    - extensions,
    - match,
    - mdiff,
    - patch,
    - registrar,
    - templateutil
    -)
    -from mercurial.hgweb import webutil, hgweb_mod
    -from mercurial.hgweb.common import paritygen
    -
    -import cmarkgfm
    -
    -filters = {}
    -templatefilter = registrar.templatefilter(filters)
    -
    -
    -@templatefilter(b'md5', intype=bytes)
    -def md5(text):
    - return hashlib_md5(text).hexdigest().encode('ascii')
    -
    -
    -def _diffstattmplgen(orig, context, ctx, statgen, parity):
    - stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
    - files = ctx.files()
    -
    - def pct(i):
    - if maxtotal == 0:
    - return 0
    - return (float(i) / maxtotal) * 100
    -
    - fileno = 0
    - for filename, adds, removes, isbinary in stats:
    - template = b'diffstatlink' if filename in files else b'diffstatnolink'
    - total = adds + removes
    - fileno += 1
    -
    - data = {
    - b'node': ctx.hex(),
    - b'file': filename,
    - b'fileno': fileno,
    - b'total': total,
    - b'added': adds,
    - b'addpct': pct(adds),
    - b'removed': removes,
    - b'removepct': pct(removes),
    - b'parity': next(parity),
    - b'binary': isbinary,
    - }
    -
    - yield context.process(template, data)
    -
    -
    -# right now we just capture the starting line numbers as that's all we need
    -RE_DIFF = re.compile(r'^@@ -(\d+),\d+ \+(\d+),\d+ @@')
    -
    -
    -def _prettyprintdifflines(context, lines, blockno, lineidprefix, filea, reva,
    - fileb, revb):
    - linea = 0
    - lineb = 0
    -
    - seena = False
    - seenb = False
    -
    - for lineno, l in enumerate(lines, 1):
    - showa = True
    - showb = True
    -
    - match = RE_DIFF.match(l)
    - if match is not None:
    - ltype = b'difflineat'
    - groups = match.groups()
    - linea = int(groups[0])
    - lineb = int(groups[1])
    - showa = False
    - showb = False
    - seena = False
    - seenb = False
    - elif l.startswith(b'---'):
    - ltype = b'difflineminus'
    - showa = False
    - showb = False
    - elif l.startswith(b'+++'):
    - ltype = b'difflineplus'
    - showa = False
    - showb = False
    - elif l.startswith(b'-'):
    - # only increment if we've already seen a minus line
    - if seena:
    - linea += 1
    - ltype = b'difflineminus'
    - showb = False
    - seena = True
    - elif l.startswith(b'+'):
    - # only increment if we've already seen a plus line
    - if seenb:
    - lineb += 1
    - ltype = b'difflineplus'
    - showa = False
    - seenb = True
    - else:
    - # only increment if we've already seen a plus/minus line
    - if seena:
    - linea += 1
    - if seenb:
    - lineb += 1
    - seena = True
    - seenb = True
    -
    - ltype = b'diffline'
    -
    - difflineno = b"%d.%d" % (blockno, lineno)
    -
    - data = {
    - b'filea': filea,
    - b'fileb': fileb,
    - b'line': l,
    - b'linea': showa and linea or b'',
    - b'lineb': showb and lineb or b'',
    - b'lineid': lineidprefix + b"l%s" % difflineno,
    - b'lineno': lineno,
    - b'linenumber': b"% 8s" % difflineno,
    - b'reva': reva,
    - b'revb': revb,
    - }
    -
    - yield context.process(ltype, data)
    -
    -
    -def _diffsgen(
    - orig,
    - context,
    - repo,
    - ctx,
    - basectx,
    - files,
    - style,
    - stripecount,
    - linerange,
    - lineidprefix,
    -):
    - if files:
    - m = match.exact(files)
    - else:
    - m = match.always()
    -
    - diffopts = patch.diffopts(repo.ui, untrusted=True)
    - parity = paritygen(stripecount)
    -
    - diffhunks = patch.diffhunks(repo, basectx, ctx, m, opts=diffopts)
    - for blockno, (fctx1, fctx2, header, hunks) in enumerate(diffhunks, 1):
    - filea, fileb = b'/dev/null', b'/dev/null'
    - reva, revb = b'', b''
    -
    - if fctx1 is not None:
    - filea = fctx1.path()
    - reva = fctx1.hex()
    -
    - if fctx2 is not None:
    - fileb = fctx2.path()
    - revb = fctx2.hex()
    -
    - if style != b'raw':
    - header = header[1:]
    - lines = [h + b'\n' for h in header]
    - for hunkrange, hunklines in hunks:
    - if linerange is not None and hunkrange is not None:
    - s1, l1, s2, l2 = hunkrange
    - if not mdiff.hunkinrange((s2, l2), linerange):
    - continue
    - lines.extend(hunklines)
    -
    - args = (lines, blockno, lineidprefix, filea, reva, fileb, revb)
    - l = templateutil.mappedgenerator(_prettyprintdifflines, args)
    -
    - changetype = b'modified'
    - if filea == b'/dev/null':
    - changetype = b'added'
    - elif fileb == b'/dev/null':
    - changetype = b'removed'
    - elif filea != fileb:
    - changetype = b'renamed'
    -
    - data = {
    - b'blockno': blockno,
    - b'changetype': changetype,
    - b'filea': filea,
    - b'fileb': fileb,
    - b'lines': l,
    - b'parity': next(parity),
    - b'reva': reva,
    - b'revb': revb,
    - }
    -
    - yield data
    -
    -
    -def repo_custom_templater(orig, self, req):
    - templater = orig(self, req)
    -
    - templatekeyword = registrar.templatekeyword(templater._proc._defaults)
    -
    - @templatekeyword(b'readme', requires={b'ctx', b'repo'})
    - def readme(context, mapping):
    - ctx = context.resource(mapping, b'ctx')
    -
    - # we iterate the files instead of a fileset, because we want to
    - # deterministically render readmes in the same order if there are more
    - # than one in a repository. With the fileset, we'd have to run through
    - # the generator and call .lower on each item again, and then we'd lose
    - # the ability to fallback if the prioritized one failed to render.
    - for filename in ctx:
    - lower = filename.lower()
    - if lower == b'readme.md':
    - raw_utf8 = ctx[filename].data().decode('utf-8')
    - return cmarkgfm.markdown_to_html(raw_utf8).encode('utf-8')
    -
    - if lower in [b'readme.txt', b'readme', b'readme.md']:
    - return ctx[filename].data().replace(b'\n', b'<br/>\n')
    -
    - return templater
    -
    -
    -def extsetup(ui):
    - extensions.wrapfunction(webutil, '_diffstattmplgen', _diffstattmplgen)
    - extensions.wrapfunction(webutil, '_diffsgen', _diffsgen)
    -
    - # this nasty but amazingly working hack is from av6
    - extensions.wrapfunction(
    - hgweb_mod.requestcontext,
    - 'templater',
    - repo_custom_templater,
    - )
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/setup.py Fri Apr 10 17:44:19 2020 -0500
    @@ -0,0 +1,23 @@
    +#!/usr/bin/env python3
    +
    +from setuptools import setup
    +
    +setup(
    + name = 'hgwebplus',
    + version = '0.1.0',
    + author = 'Gary Kramlich',
    + author_email = 'grim@reaperworld.com',
    + url = 'https://keep.imfreedom.org/grim/hgwebplus',
    + description = 'Mercurial plugin to add additional functionality to hgweb',
    + package_dir = {'hgext3rd': 'src'},
    + packages = ['hgext3rd'],
    + license = 'GPLv2',
    + classifiers = [
    + 'Environment :: Plugins',
    + 'Intended Audience :: Developers',
    + 'Operating System :: OS Independent',
    + 'Programming Language :: Python',
    + 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',
    + 'Topic :: Software Development :: Version Control',
    + ]
    +)
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/src/hgwebplus.py Fri Apr 10 17:44:19 2020 -0500
    @@ -0,0 +1,240 @@
    +# vi:et:ts=4 sw=4 sts=4
    +
    +import re
    +
    +from hashlib import md5 as hashlib_md5
    +
    +from mercurial import (
    + extensions,
    + match,
    + mdiff,
    + patch,
    + registrar,
    + templateutil
    +)
    +from mercurial.hgweb import webutil, hgweb_mod
    +from mercurial.hgweb.common import paritygen
    +
    +import cmarkgfm
    +
    +filters = {}
    +templatefilter = registrar.templatefilter(filters)
    +
    +
    +@templatefilter(b'md5', intype=bytes)
    +def md5(text):
    + return hashlib_md5(text).hexdigest().encode('ascii')
    +
    +
    +def _diffstattmplgen(orig, context, ctx, statgen, parity):
    + stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
    + files = ctx.files()
    +
    + def pct(i):
    + if maxtotal == 0:
    + return 0
    + return (float(i) / maxtotal) * 100
    +
    + fileno = 0
    + for filename, adds, removes, isbinary in stats:
    + template = b'diffstatlink' if filename in files else b'diffstatnolink'
    + total = adds + removes
    + fileno += 1
    +
    + data = {
    + b'node': ctx.hex(),
    + b'file': filename,
    + b'fileno': fileno,
    + b'total': total,
    + b'added': adds,
    + b'addpct': pct(adds),
    + b'removed': removes,
    + b'removepct': pct(removes),
    + b'parity': next(parity),
    + b'binary': isbinary,
    + }
    +
    + yield context.process(template, data)
    +
    +
    +# right now we just capture the starting line numbers as that's all we need
    +RE_DIFF = re.compile(r'^@@ -(\d+),\d+ \+(\d+),\d+ @@')
    +
    +
    +def _prettyprintdifflines(context, lines, blockno, lineidprefix, filea, reva,
    + fileb, revb):
    + linea = 0
    + lineb = 0
    +
    + seena = False
    + seenb = False
    +
    + for lineno, l in enumerate(lines, 1):
    + showa = True
    + showb = True
    +
    + match = RE_DIFF.match(l)
    + if match is not None:
    + ltype = b'difflineat'
    + groups = match.groups()
    + linea = int(groups[0])
    + lineb = int(groups[1])
    + showa = False
    + showb = False
    + seena = False
    + seenb = False
    + elif l.startswith(b'---'):
    + ltype = b'difflineminus'
    + showa = False
    + showb = False
    + elif l.startswith(b'+++'):
    + ltype = b'difflineplus'
    + showa = False
    + showb = False
    + elif l.startswith(b'-'):
    + # only increment if we've already seen a minus line
    + if seena:
    + linea += 1
    + ltype = b'difflineminus'
    + showb = False
    + seena = True
    + elif l.startswith(b'+'):
    + # only increment if we've already seen a plus line
    + if seenb:
    + lineb += 1
    + ltype = b'difflineplus'
    + showa = False
    + seenb = True
    + else:
    + # only increment if we've already seen a plus/minus line
    + if seena:
    + linea += 1
    + if seenb:
    + lineb += 1
    + seena = True
    + seenb = True
    +
    + ltype = b'diffline'
    +
    + difflineno = b"%d.%d" % (blockno, lineno)
    +
    + data = {
    + b'filea': filea,
    + b'fileb': fileb,
    + b'line': l,
    + b'linea': showa and linea or b'',
    + b'lineb': showb and lineb or b'',
    + b'lineid': lineidprefix + b"l%s" % difflineno,
    + b'lineno': lineno,
    + b'linenumber': b"% 8s" % difflineno,
    + b'reva': reva,
    + b'revb': revb,
    + }
    +
    + yield context.process(ltype, data)
    +
    +
    +def _diffsgen(
    + orig,
    + context,
    + repo,
    + ctx,
    + basectx,
    + files,
    + style,
    + stripecount,
    + linerange,
    + lineidprefix,
    +):
    + if files:
    + m = match.exact(files)
    + else:
    + m = match.always()
    +
    + diffopts = patch.diffopts(repo.ui, untrusted=True)
    + parity = paritygen(stripecount)
    +
    + diffhunks = patch.diffhunks(repo, basectx, ctx, m, opts=diffopts)
    + for blockno, (fctx1, fctx2, header, hunks) in enumerate(diffhunks, 1):
    + filea, fileb = b'/dev/null', b'/dev/null'
    + reva, revb = b'', b''
    +
    + if fctx1 is not None:
    + filea = fctx1.path()
    + reva = fctx1.hex()
    +
    + if fctx2 is not None:
    + fileb = fctx2.path()
    + revb = fctx2.hex()
    +
    + if style != b'raw':
    + header = header[1:]
    + lines = [h + b'\n' for h in header]
    + for hunkrange, hunklines in hunks:
    + if linerange is not None and hunkrange is not None:
    + s1, l1, s2, l2 = hunkrange
    + if not mdiff.hunkinrange((s2, l2), linerange):
    + continue
    + lines.extend(hunklines)
    +
    + args = (lines, blockno, lineidprefix, filea, reva, fileb, revb)
    + l = templateutil.mappedgenerator(_prettyprintdifflines, args)
    +
    + changetype = b'modified'
    + if filea == b'/dev/null':
    + changetype = b'added'
    + elif fileb == b'/dev/null':
    + changetype = b'removed'
    + elif filea != fileb:
    + changetype = b'renamed'
    +
    + data = {
    + b'blockno': blockno,
    + b'changetype': changetype,
    + b'filea': filea,
    + b'fileb': fileb,
    + b'lines': l,
    + b'parity': next(parity),
    + b'reva': reva,
    + b'revb': revb,
    + }
    +
    + yield data
    +
    +
    +def repo_custom_templater(orig, self, req):
    + templater = orig(self, req)
    +
    + templatekeyword = registrar.templatekeyword(templater._proc._defaults)
    +
    + @templatekeyword(b'readme', requires={b'ctx', b'repo'})
    + def readme(context, mapping):
    + ctx = context.resource(mapping, b'ctx')
    +
    + # we iterate the files instead of a fileset, because we want to
    + # deterministically render readmes in the same order if there are more
    + # than one in a repository. With the fileset, we'd have to run through
    + # the generator and call .lower on each item again, and then we'd lose
    + # the ability to fallback if the prioritized one failed to render.
    + for filename in ctx:
    + lower = filename.lower()
    + if lower == b'readme.md':
    + raw_utf8 = ctx[filename].data().decode('utf-8')
    + return cmarkgfm.markdown_to_html(raw_utf8).encode('utf-8')
    +
    + if lower in [b'readme.txt', b'readme', b'readme.md']:
    + return ctx[filename].data().replace(b'\n', b'<br/>\n')
    +
    + return templater
    +
    +
    +def extsetup(ui):
    + extensions.wrapfunction(webutil, '_diffstattmplgen', _diffstattmplgen)
    + extensions.wrapfunction(webutil, '_diffsgen', _diffsgen)
    +
    + # this nasty but amazingly working hack is from av6
    + extensions.wrapfunction(
    + hgweb_mod.requestcontext,
    + 'templater',
    + repo_custom_templater,
    + )