##// END OF EJS Templates
fixed issue #492 missing `\ No newline at end of file` test at the end of new chunk...
fixed issue #492 missing `\ No newline at end of file` test at the end of new chunk in html diff

File last commit:

r2007:324ac367 beta
r2567:5fe0f744 beta
Show More
annotate.py
190 lines | 7.3 KiB | text/x-python | PythonLexer
# -*- coding: utf-8 -*-
"""
rhodecode.lib.annotate
~~~~~~~~~~~~~~~~~~~~~~
Anontation library for usage in rhodecode, previously part of vcs
:created_on: Dec 4, 2011
:author: marcink
:copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
:license: GPLv3, see COPYING for more details.
"""
from rhodecode.lib.vcs.exceptions import VCSError
from rhodecode.lib.vcs.nodes import FileNode
from pygments.formatters import HtmlFormatter
from pygments import highlight
import StringIO
def annotate_highlight(filenode, annotate_from_changeset_func=None,
order=None, headers=None, **options):
"""
Returns html portion containing annotated table with 3 columns: line
numbers, changeset information and pygmentized line of code.
:param filenode: FileNode object
:param annotate_from_changeset_func: function taking changeset and
returning single annotate cell; needs break line at the end
:param order: ordered sequence of ``ls`` (line numbers column),
``annotate`` (annotate column), ``code`` (code column); Default is
``['ls', 'annotate', 'code']``
:param headers: dictionary with headers (keys are whats in ``order``
parameter)
"""
options['linenos'] = True
formatter = AnnotateHtmlFormatter(filenode=filenode, order=order,
headers=headers,
annotate_from_changeset_func=annotate_from_changeset_func, **options)
lexer = filenode.lexer
highlighted = highlight(filenode.content, lexer, formatter)
return highlighted
class AnnotateHtmlFormatter(HtmlFormatter):
def __init__(self, filenode, annotate_from_changeset_func=None,
order=None, **options):
"""
If ``annotate_from_changeset_func`` is passed it should be a function
which returns string from the given changeset. For example, we may pass
following function as ``annotate_from_changeset_func``::
def changeset_to_anchor(changeset):
return '<a href="/changesets/%s/">%s</a>\n' %\
(changeset.id, changeset.id)
:param annotate_from_changeset_func: see above
:param order: (default: ``['ls', 'annotate', 'code']``); order of
columns;
:param options: standard pygment's HtmlFormatter options, there is
extra option tough, ``headers``. For instance we can pass::
formatter = AnnotateHtmlFormatter(filenode, headers={
'ls': '#',
'annotate': 'Annotate',
'code': 'Code',
})
"""
super(AnnotateHtmlFormatter, self).__init__(**options)
self.annotate_from_changeset_func = annotate_from_changeset_func
self.order = order or ('ls', 'annotate', 'code')
headers = options.pop('headers', None)
if headers and not ('ls' in headers and 'annotate' in headers and
'code' in headers):
raise ValueError("If headers option dict is specified it must "
"all 'ls', 'annotate' and 'code' keys")
self.headers = headers
if isinstance(filenode, FileNode):
self.filenode = filenode
else:
raise VCSError("This formatter expect FileNode parameter, not %r"
% type(filenode))
def annotate_from_changeset(self, changeset):
"""
Returns full html line for single changeset per annotated line.
"""
if self.annotate_from_changeset_func:
return self.annotate_from_changeset_func(changeset)
else:
return ''.join((changeset.id, '\n'))
def _wrap_tablelinenos(self, inner):
dummyoutfile = StringIO.StringIO()
lncount = 0
for t, line in inner:
if t:
lncount += 1
dummyoutfile.write(line)
fl = self.linenostart
mw = len(str(lncount + fl - 1))
sp = self.linenospecial
st = self.linenostep
la = self.lineanchors
aln = self.anchorlinenos
if sp:
lines = []
for i in range(fl, fl + lncount):
if i % st == 0:
if i % sp == 0:
if aln:
lines.append('<a href="#%s-%d" class="special">'
'%*d</a>' %
(la, i, mw, i))
else:
lines.append('<span class="special">'
'%*d</span>' % (mw, i))
else:
if aln:
lines.append('<a href="#%s-%d">'
'%*d</a>' % (la, i, mw, i))
else:
lines.append('%*d' % (mw, i))
else:
lines.append('')
ls = '\n'.join(lines)
else:
lines = []
for i in range(fl, fl + lncount):
if i % st == 0:
if aln:
lines.append('<a href="#%s-%d">%*d</a>' \
% (la, i, mw, i))
else:
lines.append('%*d' % (mw, i))
else:
lines.append('')
ls = '\n'.join(lines)
annotate_changesets = [tup[1] for tup in self.filenode.annotate]
# If pygments cropped last lines break we need do that too
ln_cs = len(annotate_changesets)
ln_ = len(ls.splitlines())
if ln_cs > ln_:
annotate_changesets = annotate_changesets[:ln_ - ln_cs]
annotate = ''.join((self.annotate_from_changeset(changeset)
for changeset in annotate_changesets))
# in case you wonder about the seemingly redundant <div> here:
# since the content in the other cell also is wrapped in a div,
# some browsers in some configurations seem to mess up the formatting.
'''
yield 0, ('<table class="%stable">' % self.cssclass +
'<tr><td class="linenos"><div class="linenodiv"><pre>' +
ls + '</pre></div></td>' +
'<td class="code">')
yield 0, dummyoutfile.getvalue()
yield 0, '</td></tr></table>'
'''
headers_row = []
if self.headers:
headers_row = ['<tr class="annotate-header">']
for key in self.order:
td = ''.join(('<td>', self.headers[key], '</td>'))
headers_row.append(td)
headers_row.append('</tr>')
body_row_start = ['<tr>']
for key in self.order:
if key == 'ls':
body_row_start.append(
'<td class="linenos"><div class="linenodiv"><pre>' +
ls + '</pre></div></td>')
elif key == 'annotate':
body_row_start.append(
'<td class="annotate"><div class="annotatediv"><pre>' +
annotate + '</pre></div></td>')
elif key == 'code':
body_row_start.append('<td class="code">')
yield 0, ('<table class="%stable">' % self.cssclass +
''.join(headers_row) +
''.join(body_row_start)
)
yield 0, dummyoutfile.getvalue()
yield 0, '</td></tr></table>'