Mercurial > hg
changeset 5532:40a06e39f010
extension for synax highlighting in the hgweb file revision view
Depends on the pygments syntax highlighting library:
http://pygments.org/
author | Adam Hupp <adam@hupp.org> |
---|---|
date | Sat, 10 Nov 2007 17:54:57 -0500 |
parents | a3fe91b4f6eb |
children | 6cf7d7fe7d3d |
files | hgext/highlight.py templates/static/highlight.css |
diffstat | 2 files changed, 212 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/highlight.py Sat Nov 10 17:54:57 2007 -0500 @@ -0,0 +1,153 @@ +""" +This is Mercurial extension for syntax highlighting in the file +revision view of hgweb. + +It depends on the pygments syntax highlighting library: +http://pygments.org/ + +To enable the extension add this to hgrc: + +[extensions] +hgext.highlight = + +There is a single configuration option: + +[web] +pygments_style = <style> + +The default is 'colorful'. If this is changed the corresponding CSS +file should be re-generated by running + +# pygmentize -f html -S <newstyle> + + +-- Adam Hupp <adam@hupp.org> + + +""" + +from mercurial import demandimport +demandimport.ignore.extend(['pkgutil', + 'pkg_resources', + '__main__',]) + +import mimetypes + +from mercurial.hgweb import hgweb_mod +from mercurial.hgweb.hgweb_mod import hgweb +from mercurial import util +from mercurial.hgweb.common import paritygen +from mercurial.node import hex + +from pygments import highlight +from pygments.util import ClassNotFound +from pygments.lexers import guess_lexer_for_filename, TextLexer +from pygments.formatters import HtmlFormatter + +SYNTAX_CSS = '\n<link rel="stylesheet" href="#staticurl#highlight.css" type="text/css" />' + +class StripedHtmlFormatter(HtmlFormatter): + + def __init__(self, stripecount, *args, **kwargs): + super(StripedHtmlFormatter, self).__init__(*args, **kwargs) + self.stripecount = stripecount + + def wrap(self, source, outfile): + yield 0, "<div class='highlight'>" + yield 0, "<pre>" + parity = paritygen(self.stripecount) + + for n, i in source: + if n == 1: + i = "<div class='parity%s'>%s</div>" % \ + (parity.next(), i) + yield n, i + + yield 0, "</pre>" + yield 0, "</div>" + + +def pygments_format(filename, rawtext, + forcetext=False, + stripecount=1, + style='colorful'): + + if not forcetext: + try: + lexer = guess_lexer_for_filename(filename, rawtext) + except ClassNotFound: + lexer = TextLexer() + else: + lexer = TextLexer() + + formatter = StripedHtmlFormatter(stripecount, + style=style, + linenos='inline') + + return highlight(rawtext, lexer, formatter) + + +""" +This reimplements hgweb.filerevision to use syntax highlighting +""" +def filerevision_pygments(self, fctx): + filename = fctx.path() + + rawtext = fctx.data() + text = rawtext + + mt = mimetypes.guess_type(filename)[0] + + if util.binary(text): + mt = mt or 'application/octet-stream' + text = "(binary:%s)" % mt + + # don't parse (binary:...) as anything + forcetext = True + else: + mt = mt or 'text/plain' + forcetext = False + + + def lines(text): + for line in text.splitlines(True): + yield {"line": line} + + style = self.config("web", "pygments_style", "colorful") + + text_formatted = lines(pygments_format(filename, text, + forcetext=forcetext, + stripecount=self.stripecount, + style=style)) + + # override per-line template + self.t.cache['fileline'] = '#line#' + + # append a <link ...> to the syntax highlighting css + old_header = ''.join(self.t('header')) + if SYNTAX_CSS not in old_header: + new_header = old_header + SYNTAX_CSS + self.t.cache['header'] = new_header + + + yield self.t("filerevision", + file=filename, + path=hgweb_mod._up(filename), # fixme: make public + text=text_formatted, + raw=rawtext, + mimetype=mt, + rev=fctx.rev(), + node=hex(fctx.node()), + author=fctx.user(), + date=fctx.date(), + desc=fctx.description(), + parent=self.siblings(fctx.parents()), + child=self.siblings(fctx.children()), + rename=self.renamelink(fctx.filelog(), + fctx.filenode()), + permissions=fctx.manifest().flags(filename)) + +# monkeypatch in the new version +# should be safer than overriding the method in a derived class +# and then patching the class +hgweb.filerevision = filerevision_pygments
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/static/highlight.css Sat Nov 10 17:54:57 2007 -0500 @@ -0,0 +1,59 @@ +.c { color: #808080 } /* Comment */ +.err { color: #F00000; background-color: #F0A0A0 } /* Error */ +.k { color: #008000; font-weight: bold } /* Keyword */ +.o { color: #303030 } /* Operator */ +.cm { color: #808080 } /* Comment.Multiline */ +.cp { color: #507090 } /* Comment.Preproc */ +.c1 { color: #808080 } /* Comment.Single */ +.cs { color: #cc0000; font-weight: bold } /* Comment.Special */ +.gd { color: #A00000 } /* Generic.Deleted */ +.ge { font-style: italic } /* Generic.Emph */ +.gr { color: #FF0000 } /* Generic.Error */ +.gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.gi { color: #00A000 } /* Generic.Inserted */ +.go { color: #808080 } /* Generic.Output */ +.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ +.gs { font-weight: bold } /* Generic.Strong */ +.gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.gt { color: #0040D0 } /* Generic.Traceback */ +.kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.kp { color: #003080; font-weight: bold } /* Keyword.Pseudo */ +.kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.kt { color: #303090; font-weight: bold } /* Keyword.Type */ +.m { color: #6000E0; font-weight: bold } /* Literal.Number */ +.s { background-color: #fff0f0 } /* Literal.String */ +.na { color: #0000C0 } /* Name.Attribute */ +.nb { color: #007020 } /* Name.Builtin */ +.nc { color: #B00060; font-weight: bold } /* Name.Class */ +.no { color: #003060; font-weight: bold } /* Name.Constant */ +.nd { color: #505050; font-weight: bold } /* Name.Decorator */ +.ni { color: #800000; font-weight: bold } /* Name.Entity */ +.ne { color: #F00000; font-weight: bold } /* Name.Exception */ +.nf { color: #0060B0; font-weight: bold } /* Name.Function */ +.nl { color: #907000; font-weight: bold } /* Name.Label */ +.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ +.nt { color: #007000 } /* Name.Tag */ +.nv { color: #906030 } /* Name.Variable */ +.ow { color: #000000; font-weight: bold } /* Operator.Word */ +.w { color: #bbbbbb } /* Text.Whitespace */ +.mf { color: #6000E0; font-weight: bold } /* Literal.Number.Float */ +.mh { color: #005080; font-weight: bold } /* Literal.Number.Hex */ +.mi { color: #0000D0; font-weight: bold } /* Literal.Number.Integer */ +.mo { color: #4000E0; font-weight: bold } /* Literal.Number.Oct */ +.sb { background-color: #fff0f0 } /* Literal.String.Backtick */ +.sc { color: #0040D0 } /* Literal.String.Char */ +.sd { color: #D04020 } /* Literal.String.Doc */ +.s2 { background-color: #fff0f0 } /* Literal.String.Double */ +.se { color: #606060; font-weight: bold; background-color: #fff0f0 } /* Literal.String.Escape */ +.sh { background-color: #fff0f0 } /* Literal.String.Heredoc */ +.si { background-color: #e0e0e0 } /* Literal.String.Interpol */ +.sx { color: #D02000; background-color: #fff0f0 } /* Literal.String.Other */ +.sr { color: #000000; background-color: #fff0ff } /* Literal.String.Regex */ +.s1 { background-color: #fff0f0 } /* Literal.String.Single */ +.ss { color: #A06000 } /* Literal.String.Symbol */ +.bp { color: #007020 } /* Name.Builtin.Pseudo */ +.vc { color: #306090 } /* Name.Variable.Class */ +.vg { color: #d07000; font-weight: bold } /* Name.Variable.Global */ +.vi { color: #3030B0 } /* Name.Variable.Instance */ +.il { color: #0000D0; font-weight: bold } /* Literal.Number.Integer.Long */