23
23
import bzrlib.errors
24
24
import bzrlib.textfile
26
from paste.httpexceptions import HTTPBadRequest, HTTPServerError
27
from paste.httpexceptions import HTTPBadRequest, HTTPServerError, HTTPMovedPermanently
28
29
from loggerhead.controllers import TemplatedBranchView
31
from loggerhead.highlight import highlight
29
34
from loggerhead import util
32
class AnnotateUI(TemplatedBranchView):
34
template_path = 'loggerhead.templates.annotate'
36
def annotate_file(self, file_id, revid):
37
class ViewUI(TemplatedBranchView):
39
template_path = 'loggerhead.templates.view'
41
def tree_for(self, file_id, revid):
41
42
file_revid = self._history.get_inventory(revid)[file_id].revision
42
tree = self._history._branch.repository.revision_tree(file_revid)
45
bzrlib.textfile.check_text_lines(tree.get_file_lines(file_id))
43
return self._history._branch.repository.revision_tree(file_revid)
45
def text_lines(self, file_id, revid):
46
file_name = os.path.basename(self._history.get_path(revid, file_id))
48
tree = self.tree_for(file_id, revid)
49
file_text = tree.get_file_text(file_id)
52
file_text = file_text.decode(encoding)
53
except UnicodeDecodeError:
54
encoding = 'iso-8859-15'
55
file_text = file_text.decode(encoding)
57
file_lines = bzrlib.osutils.split_lines(file_text)
58
# This can throw bzrlib.errors.BinaryFile (which our caller catches).
59
bzrlib.textfile.check_text_lines(file_lines)
61
if highlight is not None:
62
hl_lines = highlight(file_name, file_text, encoding)
63
# highlight strips off extra newlines at the end of the file.
64
extra_lines = len(file_lines) - len(hl_lines)
65
hl_lines.extend([u''] * extra_lines)
67
hl_lines = map(util.html_escape, file_lines)
71
def file_contents(self, file_id, revid):
73
file_lines = self.text_lines(file_id, revid)
46
74
except bzrlib.errors.BinaryFile:
47
# bail out; this isn't displayable text
48
yield util.Container(parity=0, lineno=1, status='same',
49
text='(This is a binary file.)',
50
change=util.Container())
54
last_line_revid = None
55
for line_revid, text in tree.annotate_iter(file_id):
56
if line_revid == last_line_revid:
57
# remember which lines have a new revno and which don't
62
last_line_revid = line_revid
63
if line_revid in change_cache:
64
change = change_cache[line_revid]
66
change = self._history.get_changes([line_revid])[0]
67
change_cache[line_revid] = change
70
parity=parity, lineno=lineno, status=status,
71
change=change, text=util.fixed_width(text))
74
self.log.debug('annotate: %r secs' % (time.time() - z))
75
# bail out; this isn't displayable text
76
return ['(This is a binary file.)']
76
80
def get_values(self, path, kwargs, headers):
77
81
history = self._history
115
119
raise HTTPServerError('Could not fetch changes')
116
120
branch_breadcrumbs = util.branch_breadcrumbs(path, inv, 'files')
122
if inv[file_id].kind == "directory":
123
raise HTTPMovedPermanently(self._branch.context_url(['/files', revno_url, path]))
126
# In AnnotateUI, "annotated" is a generator giving revision
127
# numbers per lines, but the template checks if "annotated" is
128
# true or not before using it, so we have to define it here also.
119
130
'revno_url': revno_url,
120
131
'file_id': file_id,
122
133
'filename': filename,
123
134
'navigation': navigation,
124
135
'change': change,
125
'contents': list(self.annotate_file(file_id, revid)),
136
'contents': self.file_contents(file_id, revid),
126
137
'fileview_active': True,
127
138
'directory_breadcrumbs': directory_breadcrumbs,
128
139
'branch_breadcrumbs': branch_breadcrumbs,