##// END OF EJS Templates
codeblocks: add new code token rendering function that...
dan -
r1025:8ba7d016 default
parent child Browse files
Show More
@@ -0,0 +1,330 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2016 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22
23 from rhodecode.lib.codeblocks import (
24 tokenize_string, split_token_stream, rollup_tokenstream,
25 render_tokenstream)
26 from pygments.lexers import get_lexer_by_name
27
28
29 class TestTokenizeString(object):
30
31 python_code = '''
32 import this
33
34 var = 6
35 print "this"
36
37 '''
38
39 def test_tokenize_as_python(self):
40 lexer = get_lexer_by_name('python')
41 tokens = list(tokenize_string(self.python_code, lexer))
42
43 assert tokens == [
44 ('', u'\n'),
45 ('', u' '),
46 ('kn', u'import'),
47 ('', u' '),
48 ('nn', u'this'),
49 ('', u'\n'),
50 ('', u'\n'),
51 ('', u' '),
52 ('n', u'var'),
53 ('', u' '),
54 ('o', u'='),
55 ('', u' '),
56 ('mi', u'6'),
57 ('', u'\n'),
58 ('', u' '),
59 ('k', u'print'),
60 ('', u' '),
61 ('s2', u'"'),
62 ('s2', u'this'),
63 ('s2', u'"'),
64 ('', u'\n'),
65 ('', u'\n'),
66 ('', u' ')
67 ]
68
69 def test_tokenize_as_text(self):
70 lexer = get_lexer_by_name('text')
71 tokens = list(tokenize_string(self.python_code, lexer))
72
73 assert tokens == [
74 ('',
75 u'\n import this\n\n var = 6\n print "this"\n\n ')
76 ]
77
78
79 class TestSplitTokenStream(object):
80
81 def test_split_token_stream(self):
82 lines = list(split_token_stream(
83 [('type1', 'some\ntext'), ('type2', 'more\n')]))
84
85 assert lines == [
86 [('type1', u'some')],
87 [('type1', u'text'), ('type2', u'more')],
88 [('type2', u'')],
89 ]
90
91 def test_split_token_stream_other_char(self):
92 lines = list(split_token_stream(
93 [('type1', 'some\ntext'), ('type2', 'more\n')],
94 split_string='m'))
95
96 assert lines == [
97 [('type1', 'so')],
98 [('type1', 'e\ntext'), ('type2', '')],
99 [('type2', 'ore\n')],
100 ]
101
102 def test_split_token_stream_without_char(self):
103 lines = list(split_token_stream(
104 [('type1', 'some\ntext'), ('type2', 'more\n')],
105 split_string='z'))
106
107 assert lines == [
108 [('type1', 'some\ntext'), ('type2', 'more\n')]
109 ]
110
111 def test_split_token_stream_single(self):
112 lines = list(split_token_stream(
113 [('type1', '\n')], split_string='\n'))
114
115 assert lines == [
116 [('type1', '')],
117 [('type1', '')],
118 ]
119
120 def test_split_token_stream_single_repeat(self):
121 lines = list(split_token_stream(
122 [('type1', '\n\n\n')], split_string='\n'))
123
124 assert lines == [
125 [('type1', '')],
126 [('type1', '')],
127 [('type1', '')],
128 [('type1', '')],
129 ]
130
131 def test_split_token_stream_multiple_repeat(self):
132 lines = list(split_token_stream(
133 [('type1', '\n\n'), ('type2', '\n\n')], split_string='\n'))
134
135 assert lines == [
136 [('type1', '')],
137 [('type1', '')],
138 [('type1', ''), ('type2', '')],
139 [('type2', '')],
140 [('type2', '')],
141 ]
142
143
144 class TestRollupTokens(object):
145
146 @pytest.mark.parametrize('tokenstream,output', [
147 ([],
148 []),
149 ([('A', 'hell'), ('A', 'o')], [
150 ('A', [
151 ('', 'hello')]),
152 ]),
153 ([('A', 'hell'), ('B', 'o')], [
154 ('A', [
155 ('', 'hell')]),
156 ('B', [
157 ('', 'o')]),
158 ]),
159 ([('A', 'hel'), ('A', 'lo'), ('B', ' '), ('A', 'there')], [
160 ('A', [
161 ('', 'hello')]),
162 ('B', [
163 ('', ' ')]),
164 ('A', [
165 ('', 'there')]),
166 ]),
167 ])
168 def test_rollup_tokenstream_without_ops(self, tokenstream, output):
169 assert list(rollup_tokenstream(tokenstream)) == output
170
171 @pytest.mark.parametrize('tokenstream,output', [
172 ([],
173 []),
174 ([('A', '', 'hell'), ('A', '', 'o')], [
175 ('A', [
176 ('', 'hello')]),
177 ]),
178 ([('A', '', 'hell'), ('B', '', 'o')], [
179 ('A', [
180 ('', 'hell')]),
181 ('B', [
182 ('', 'o')]),
183 ]),
184 ([('A', '', 'h'), ('B', '', 'e'), ('C', '', 'y')], [
185 ('A', [
186 ('', 'h')]),
187 ('B', [
188 ('', 'e')]),
189 ('C', [
190 ('', 'y')]),
191 ]),
192 ([('A', '', 'h'), ('A', '', 'e'), ('C', '', 'y')], [
193 ('A', [
194 ('', 'he')]),
195 ('C', [
196 ('', 'y')]),
197 ]),
198 ([('A', 'ins', 'h'), ('A', 'ins', 'e')], [
199 ('A', [
200 ('ins', 'he')
201 ]),
202 ]),
203 ([('A', 'ins', 'h'), ('A', 'del', 'e')], [
204 ('A', [
205 ('ins', 'h'),
206 ('del', 'e')
207 ]),
208 ]),
209 ([('A', 'ins', 'h'), ('B', 'del', 'e'), ('B', 'del', 'y')], [
210 ('A', [
211 ('ins', 'h'),
212 ]),
213 ('B', [
214 ('del', 'ey'),
215 ]),
216 ]),
217 ([('A', 'ins', 'h'), ('A', 'del', 'e'), ('B', 'del', 'y')], [
218 ('A', [
219 ('ins', 'h'),
220 ('del', 'e'),
221 ]),
222 ('B', [
223 ('del', 'y'),
224 ]),
225 ]),
226 ([('A', '', 'some'), ('A', 'ins', 'new'), ('A', '', 'name')], [
227 ('A', [
228 ('', 'some'),
229 ('ins', 'new'),
230 ('', 'name'),
231 ]),
232 ]),
233 ])
234 def test_rollup_tokenstream_with_ops(self, tokenstream, output):
235 assert list(rollup_tokenstream(tokenstream)) == output
236
237
238 class TestRenderTokenStream(object):
239
240 @pytest.mark.parametrize('tokenstream,output', [
241 (
242 [],
243 '',
244 ),
245 (
246 [('', '', u'')],
247 '<span></span>',
248 ),
249 (
250 [('', '', u'text')],
251 '<span>text</span>',
252 ),
253 (
254 [('A', '', u'')],
255 '<span class="A"></span>',
256 ),
257 (
258 [('A', '', u'hello')],
259 '<span class="A">hello</span>',
260 ),
261 (
262 [('A', '', u'hel'), ('A', '', u'lo')],
263 '<span class="A">hello</span>',
264 ),
265 (
266 [('A', '', u'two\n'), ('A', '', u'lines')],
267 '<span class="A">two<nl>\n</nl>lines</span>',
268 ),
269 (
270 [('A', '', u'\nthree\n'), ('A', '', u'lines')],
271 '<span class="A"><nl>\n</nl>three<nl>\n</nl>lines</span>',
272 ),
273 (
274 [('', '', u'\n'), ('A', '', u'line')],
275 '<span><nl>\n</nl></span><span class="A">line</span>',
276 ),
277 (
278 [('', 'ins', u'\n'), ('A', '', u'line')],
279 '<span><ins><nl>\n</nl></ins></span><span class="A">line</span>',
280 ),
281 (
282 [('A', '', u'hel'), ('A', 'ins', u'lo')],
283 '<span class="A">hel<ins>lo</ins></span>',
284 ),
285 (
286 [('A', '', u'hel'), ('A', 'ins', u'l'), ('A', 'ins', u'o')],
287 '<span class="A">hel<ins>lo</ins></span>',
288 ),
289 (
290 [('A', '', u'hel'), ('A', 'ins', u'l'), ('A', 'del', u'o')],
291 '<span class="A">hel<ins>l</ins><del>o</del></span>',
292 ),
293 (
294 [('A', '', u'hel'), ('B', '', u'lo')],
295 '<span class="A">hel</span><span class="B">lo</span>',
296 ),
297 (
298 [('A', '', u'hel'), ('B', 'ins', u'lo')],
299 '<span class="A">hel</span><span class="B"><ins>lo</ins></span>',
300 ),
301 ])
302 def test_render_tokenstream_with_ops(self, tokenstream, output):
303 html = render_tokenstream(tokenstream)
304 assert html == output
305
306 @pytest.mark.parametrize('tokenstream,output', [
307 (
308 [('A', u'hel'), ('A', u'lo')],
309 '<span class="A">hello</span>',
310 ),
311 (
312 [('A', u'hel'), ('A', u'l'), ('A', u'o')],
313 '<span class="A">hello</span>',
314 ),
315 (
316 [('A', u'hel'), ('A', u'l'), ('A', u'o')],
317 '<span class="A">hello</span>',
318 ),
319 (
320 [('A', u'hel'), ('B', u'lo')],
321 '<span class="A">hel</span><span class="B">lo</span>',
322 ),
323 (
324 [('A', u'hel'), ('B', u'lo')],
325 '<span class="A">hel</span><span class="B">lo</span>',
326 ),
327 ])
328 def test_render_tokenstream_without_ops(self, tokenstream, output):
329 html = render_tokenstream(tokenstream)
330 assert html == output
@@ -1,147 +1,214 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21
21 import logging
22 22 from itertools import groupby
23 23
24 24 from pygments import lex
25 # PYGMENTS_TOKEN_TYPES is used in a hot loop keep attribute lookups to a minimum
26 from pygments.token import STANDARD_TYPES as PYGMENTS_TOKEN_TYPES
25 from pygments.formatters.html import _get_ttype_class as pygment_token_class
26 from rhodecode.lib.helpers import get_lexer_for_filenode, html_escape
27 from rhodecode.lib.utils2 import AttributeDict
28 from rhodecode.lib.vcs.nodes import FileNode
29 from pygments.lexers import get_lexer_by_name
30
31 plain_text_lexer = get_lexer_by_name(
32 'text', stripall=False, stripnl=False, ensurenl=False)
33
34
35 log = logging.getLogger()
27 36
28 from rhodecode.lib.helpers import get_lexer_for_filenode
29 37
30 def tokenize_file(content, lexer):
38 def filenode_as_lines_tokens(filenode, lexer=None):
39 lexer = lexer or get_lexer_for_filenode(filenode)
40 log.debug('Generating file node pygment tokens for %s, %s', lexer, filenode)
41 tokens = tokenize_string(filenode.content, get_lexer_for_filenode(filenode))
42 lines = split_token_stream(tokens, split_string='\n')
43 rv = list(lines)
44 return rv
45
46
47 def tokenize_string(content, lexer):
31 48 """
32 49 Use pygments to tokenize some content based on a lexer
33 50 ensuring all original new lines and whitespace is preserved
34 51 """
35 52
36 53 lexer.stripall = False
37 54 lexer.stripnl = False
38 55 lexer.ensurenl = False
39 return lex(content, lexer)
56 for token_type, token_text in lex(content, lexer):
57 yield pygment_token_class(token_type), token_text
40 58
41 59
42 def pygment_token_class(token_type):
43 """ Convert a pygments token type to html class name """
44
45 fname = PYGMENTS_TOKEN_TYPES.get(token_type)
46 if fname:
47 return fname
48
49 aname = ''
50 while fname is None:
51 aname = '-' + token_type[-1] + aname
52 token_type = token_type.parent
53 fname = PYGMENTS_TOKEN_TYPES.get(token_type)
54
55 return fname + aname
56
57
58 def tokens_as_lines(tokens, split_string=u'\n'):
60 def split_token_stream(tokens, split_string=u'\n'):
59 61 """
60 62 Take a list of (TokenType, text) tuples and split them by a string
61 63
62 eg. [(TEXT, 'some\ntext')] => [(TEXT, 'some'), (TEXT, 'text')]
64 >>> split_token_stream([(TEXT, 'some\ntext'), (TEXT, 'more\n')])
65 [(TEXT, 'some'), (TEXT, 'text'),
66 (TEXT, 'more'), (TEXT, 'text')]
63 67 """
64 68
65 69 buffer = []
66 for token_type, token_text in tokens:
70 for token_class, token_text in tokens:
67 71 parts = token_text.split(split_string)
68 72 for part in parts[:-1]:
69 buffer.append((token_type, part))
73 buffer.append((token_class, part))
70 74 yield buffer
71 75 buffer = []
72 76
73 buffer.append((token_type, parts[-1]))
77 buffer.append((token_class, parts[-1]))
74 78
75 79 if buffer:
76 80 yield buffer
77 81
78 82
79 def filenode_as_lines_tokens(filenode):
80 """
81 Return a generator of lines with pygment tokens for a filenode eg:
82
83 [
84 (1, line1_tokens_list),
85 (2, line1_tokens_list]),
86 ]
87 """
88
89 return enumerate(
90 tokens_as_lines(
91 tokenize_file(
92 filenode.content, get_lexer_for_filenode(filenode)
93 )
94 ),
95 1)
96
97
98 83 def filenode_as_annotated_lines_tokens(filenode):
99 84 """
100 85 Take a file node and return a list of annotations => lines, if no annotation
101 86 is found, it will be None.
102 87
103 88 eg:
104 89
105 90 [
106 91 (annotation1, [
107 92 (1, line1_tokens_list),
108 93 (2, line2_tokens_list),
109 94 ]),
110 95 (annotation2, [
111 96 (3, line1_tokens_list),
112 97 ]),
113 98 (None, [
114 99 (4, line1_tokens_list),
115 100 ]),
116 101 (annotation1, [
117 102 (5, line1_tokens_list),
118 103 (6, line2_tokens_list),
119 104 ])
120 105 ]
121 106 """
122 107
108 commit_cache = {} # cache commit_getter lookups
123 109
124 # cache commit_getter lookups
125 commit_cache = {}
126 110 def _get_annotation(commit_id, commit_getter):
127 111 if commit_id not in commit_cache:
128 112 commit_cache[commit_id] = commit_getter()
129 113 return commit_cache[commit_id]
130 114
131 115 annotation_lookup = {
132 116 line_no: _get_annotation(commit_id, commit_getter)
133 117 for line_no, commit_id, commit_getter, line_content
134 118 in filenode.annotate
135 119 }
136 120
137 121 annotations_lines = ((annotation_lookup.get(line_no), line_no, tokens)
138 122 for line_no, tokens
139 in filenode_as_lines_tokens(filenode))
123 in enumerate(filenode_as_lines_tokens(filenode), 1))
140 124
141 125 grouped_annotations_lines = groupby(annotations_lines, lambda x: x[0])
142 126
143 127 for annotation, group in grouped_annotations_lines:
144 128 yield (
145 129 annotation, [(line_no, tokens)
146 130 for (_, line_no, tokens) in group]
147 131 )
132
133
134 def render_tokenstream(tokenstream):
135 result = []
136 for token_class, token_ops_texts in rollup_tokenstream(tokenstream):
137
138 if token_class:
139 result.append(u'<span class="%s">' % token_class)
140 else:
141 result.append(u'<span>')
142
143 for op_tag, token_text in token_ops_texts:
144
145 if op_tag:
146 result.append(u'<%s>' % op_tag)
147
148 escaped_text = html_escape(token_text)
149 escaped_text = escaped_text.replace('\n', '<nl>\n</nl>')
150
151 result.append(escaped_text)
152
153 if op_tag:
154 result.append(u'</%s>' % op_tag)
155
156 result.append(u'</span>')
157
158 html = ''.join(result)
159 return html
160
161
162 def rollup_tokenstream(tokenstream):
163 """
164 Group a token stream of the format:
165
166 ('class', 'op', 'text')
167 or
168 ('class', 'text')
169
170 into
171
172 [('class1',
173 [('op1', 'text'),
174 ('op2', 'text')]),
175 ('class2',
176 [('op3', 'text')])]
177
178 This is used to get the minimal tags necessary when
179 rendering to html eg for a token stream ie.
180
181 <span class="A"><ins>he</ins>llo</span>
182 vs
183 <span class="A"><ins>he</ins></span><span class="A">llo</span>
184
185 If a 2 tuple is passed in, the output op will be an empty string.
186
187 eg:
188
189 >>> rollup_tokenstream([('classA', '', 'h'),
190 ('classA', 'del', 'ell'),
191 ('classA', '', 'o'),
192 ('classB', '', ' '),
193 ('classA', '', 'the'),
194 ('classA', '', 're'),
195 ])
196
197 [('classA', [('', 'h'), ('del', 'ell'), ('', 'o')],
198 ('classB', [('', ' ')],
199 ('classA', [('', 'there')]]
200
201 """
202 if tokenstream and len(tokenstream[0]) == 2:
203 tokenstream = ((t[0], '', t[1]) for t in tokenstream)
204
205 result = []
206 for token_class, op_list in groupby(tokenstream, lambda t: t[0]):
207 ops = []
208 for token_op, token_text_list in groupby(op_list, lambda o: o[1]):
209 text_buffer = []
210 for t_class, t_op, t_text in token_text_list:
211 text_buffer.append(t_text)
212 ops.append((token_op, ''.join(text_buffer)))
213 result.append((token_class, ops))
214 return result
@@ -1,749 +1,753 b''
1 1 // Default styles
2 2
3 3 .diff-collapse {
4 4 margin: @padding 0;
5 5 text-align: right;
6 6 }
7 7
8 8 .diff-container {
9 9 margin-bottom: @space;
10 10
11 11 .diffblock {
12 12 margin-bottom: @space;
13 13 }
14 14
15 15 &.hidden {
16 16 display: none;
17 17 overflow: hidden;
18 18 }
19 19 }
20 20
21 21 .compare_view_files {
22 22
23 23 .diff-container {
24 24
25 25 .diffblock {
26 26 margin-bottom: 0;
27 27 }
28 28 }
29 29 }
30 30
31 31 div.diffblock .sidebyside {
32 32 background: #ffffff;
33 33 }
34 34
35 35 div.diffblock {
36 36 overflow-x: auto;
37 37 overflow-y: hidden;
38 38 clear: both;
39 39 padding: 0px;
40 40 background: @grey6;
41 41 border: @border-thickness solid @grey5;
42 42 -webkit-border-radius: @border-radius @border-radius 0px 0px;
43 43 border-radius: @border-radius @border-radius 0px 0px;
44 44
45 45
46 46 .comments-number {
47 47 float: right;
48 48 }
49 49
50 50 // BEGIN CODE-HEADER STYLES
51 51
52 52 .code-header {
53 53 background: @grey6;
54 54 padding: 10px 0 10px 0;
55 55 height: auto;
56 56 width: 100%;
57 57
58 58 .hash {
59 59 float: left;
60 60 padding: 2px 0 0 2px;
61 61 }
62 62
63 63 .date {
64 64 float: left;
65 65 text-transform: uppercase;
66 66 padding: 4px 0px 0px 2px;
67 67 }
68 68
69 69 div {
70 70 margin-left: 4px;
71 71 }
72 72
73 73 div.compare_header {
74 74 min-height: 40px;
75 75 margin: 0;
76 76 padding: 0 @padding;
77 77
78 78 .drop-menu {
79 79 float:left;
80 80 display: block;
81 81 margin:0 0 @padding 0;
82 82 }
83 83
84 84 .compare-label {
85 85 float: left;
86 86 clear: both;
87 87 display: inline-block;
88 88 min-width: 5em;
89 89 margin: 0;
90 90 padding: @button-padding @button-padding @button-padding 0;
91 91 font-family: @text-semibold;
92 92 }
93 93
94 94 .compare-buttons {
95 95 float: left;
96 96 margin: 0;
97 97 padding: 0 0 @padding;
98 98
99 99 .btn {
100 100 margin: 0 @padding 0 0;
101 101 }
102 102 }
103 103 }
104 104
105 105 }
106 106
107 107 .parents {
108 108 float: left;
109 109 width: 100px;
110 110 font-weight: 400;
111 111 vertical-align: middle;
112 112 padding: 0px 2px 0px 2px;
113 113 background-color: @grey6;
114 114
115 115 #parent_link {
116 116 margin: 00px 2px;
117 117
118 118 &.double {
119 119 margin: 0px 2px;
120 120 }
121 121
122 122 &.disabled{
123 123 margin-right: @padding;
124 124 }
125 125 }
126 126 }
127 127
128 128 .children {
129 129 float: right;
130 130 width: 100px;
131 131 font-weight: 400;
132 132 vertical-align: middle;
133 133 text-align: right;
134 134 padding: 0px 2px 0px 2px;
135 135 background-color: @grey6;
136 136
137 137 #child_link {
138 138 margin: 0px 2px;
139 139
140 140 &.double {
141 141 margin: 0px 2px;
142 142 }
143 143
144 144 &.disabled{
145 145 margin-right: @padding;
146 146 }
147 147 }
148 148 }
149 149
150 150 .changeset_header {
151 151 height: 16px;
152 152
153 153 & > div{
154 154 margin-right: @padding;
155 155 }
156 156 }
157 157
158 158 .changeset_file {
159 159 text-align: left;
160 160 float: left;
161 161 padding: 0;
162 162
163 163 a{
164 164 display: inline-block;
165 165 margin-right: 0.5em;
166 166 }
167 167
168 168 #selected_mode{
169 169 margin-left: 0;
170 170 }
171 171 }
172 172
173 173 .diff-menu-wrapper {
174 174 float: left;
175 175 }
176 176
177 177 .diff-menu {
178 178 position: absolute;
179 179 background: none repeat scroll 0 0 #FFFFFF;
180 180 border-color: #003367 @grey3 @grey3;
181 181 border-right: 1px solid @grey3;
182 182 border-style: solid solid solid;
183 183 border-width: @border-thickness;
184 184 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
185 185 margin-top: 5px;
186 186 margin-left: 1px;
187 187 }
188 188
189 189 .diff-actions, .editor-actions {
190 190 float: left;
191 191
192 192 input{
193 193 margin: 0 0.5em 0 0;
194 194 }
195 195 }
196 196
197 197 // END CODE-HEADER STYLES
198 198
199 199 // BEGIN CODE-BODY STYLES
200 200
201 201 .code-body {
202 202 background: white;
203 203 padding: 0;
204 204 background-color: #ffffff;
205 205 position: relative;
206 206 max-width: none;
207 207 box-sizing: border-box;
208 208 // TODO: johbo: Parent has overflow: auto, this forces the child here
209 209 // to have the intended size and to scroll. Should be simplified.
210 210 width: 100%;
211 211 overflow-x: auto;
212 212 }
213 213
214 214 pre.raw {
215 215 background: white;
216 216 color: @grey1;
217 217 }
218 218 // END CODE-BODY STYLES
219 219
220 220 }
221 221
222 222
223 223 table.code-difftable {
224 224 border-collapse: collapse;
225 225 width: 99%;
226 226 border-radius: 0px !important;
227 227
228 228 td {
229 229 padding: 0 !important;
230 230 background: none !important;
231 231 border: 0 !important;
232 232 }
233 233
234 234 .context {
235 235 background: none repeat scroll 0 0 #DDE7EF;
236 236 }
237 237
238 238 .add {
239 239 background: none repeat scroll 0 0 #DDFFDD;
240 240
241 241 ins {
242 242 background: none repeat scroll 0 0 #AAFFAA;
243 243 text-decoration: none;
244 244 }
245 245 }
246 246
247 247 .del {
248 248 background: none repeat scroll 0 0 #FFDDDD;
249 249
250 250 del {
251 251 background: none repeat scroll 0 0 #FFAAAA;
252 252 text-decoration: none;
253 253 }
254 254 }
255 255
256 256 /** LINE NUMBERS **/
257 257 .lineno {
258 258 padding-left: 2px !important;
259 259 padding-right: 2px;
260 260 text-align: right;
261 261 width: 32px;
262 262 -moz-user-select: none;
263 263 -webkit-user-select: none;
264 264 border-right: @border-thickness solid @grey5 !important;
265 265 border-left: 0px solid #CCC !important;
266 266 border-top: 0px solid #CCC !important;
267 267 border-bottom: none !important;
268 268
269 269 a {
270 270 &:extend(pre);
271 271 text-align: right;
272 272 padding-right: 2px;
273 273 cursor: pointer;
274 274 display: block;
275 275 width: 32px;
276 276 }
277 277 }
278 278
279 279 .context {
280 280 cursor: auto;
281 281 &:extend(pre);
282 282 }
283 283
284 284 .lineno-inline {
285 285 background: none repeat scroll 0 0 #FFF !important;
286 286 padding-left: 2px;
287 287 padding-right: 2px;
288 288 text-align: right;
289 289 width: 30px;
290 290 -moz-user-select: none;
291 291 -webkit-user-select: none;
292 292 }
293 293
294 294 /** CODE **/
295 295 .code {
296 296 display: block;
297 297 width: 100%;
298 298
299 299 td {
300 300 margin: 0;
301 301 padding: 0;
302 302 }
303 303
304 304 pre {
305 305 margin: 0;
306 306 padding: 0;
307 307 margin-left: .5em;
308 308 }
309 309 }
310 310 }
311 311
312 312
313 313 // Comments
314 314
315 315 div.comment:target {
316 316 border-left: 6px solid @comment-highlight-color;
317 317 padding-left: 3px;
318 318 margin-left: -9px;
319 319 }
320 320
321 321 //TODO: anderson: can't get an absolute number out of anything, so had to put the
322 322 //current values that might change. But to make it clear I put as a calculation
323 323 @comment-max-width: 1065px;
324 324 @pr-extra-margin: 34px;
325 325 @pr-border-spacing: 4px;
326 326 @pr-comment-width: @comment-max-width - @pr-extra-margin - @pr-border-spacing;
327 327
328 328 // Pull Request
329 329 .cs_files .code-difftable {
330 330 border: @border-thickness solid @grey5; //borders only on PRs
331 331
332 332 .comment-inline-form,
333 333 div.comment {
334 334 width: @pr-comment-width;
335 335 }
336 336 }
337 337
338 338 // Changeset
339 339 .code-difftable {
340 340 .comment-inline-form,
341 341 div.comment {
342 342 width: @comment-max-width;
343 343 }
344 344 }
345 345
346 346 //Style page
347 347 @style-extra-margin: @sidebar-width + (@sidebarpadding * 3) + @padding;
348 348 #style-page .code-difftable{
349 349 .comment-inline-form,
350 350 div.comment {
351 351 width: @comment-max-width - @style-extra-margin;
352 352 }
353 353 }
354 354
355 355 #context-bar > h2 {
356 356 font-size: 20px;
357 357 }
358 358
359 359 #context-bar > h2> a {
360 360 font-size: 20px;
361 361 }
362 362 // end of defaults
363 363
364 364 .file_diff_buttons {
365 365 padding: 0 0 @padding;
366 366
367 367 .drop-menu {
368 368 float: left;
369 369 margin: 0 @padding 0 0;
370 370 }
371 371 .btn {
372 372 margin: 0 @padding 0 0;
373 373 }
374 374 }
375 375
376 376 .code-body.textarea.editor {
377 377 max-width: none;
378 378 padding: 15px;
379 379 }
380 380
381 381 td.injected_diff{
382 382 max-width: 1178px;
383 383 overflow-x: auto;
384 384 overflow-y: hidden;
385 385
386 386 div.diff-container,
387 387 div.diffblock{
388 388 max-width: 100%;
389 389 }
390 390
391 391 div.code-body {
392 392 max-width: 1124px;
393 393 overflow-x: auto;
394 394 overflow-y: hidden;
395 395 padding: 0;
396 396 }
397 397 div.diffblock {
398 398 border: none;
399 399 }
400 400
401 401 &.inline-form {
402 402 width: 99%
403 403 }
404 404 }
405 405
406 406
407 407 table.code-difftable {
408 408 width: 100%;
409 409 }
410 410
411 411 /** PYGMENTS COLORING **/
412 412 div.codeblock {
413 413
414 414 // TODO: johbo: Added interim to get rid of the margin around
415 415 // Select2 widgets. This needs further cleanup.
416 416 margin-top: @padding;
417 417
418 418 overflow: auto;
419 419 padding: 0px;
420 420 border: @border-thickness solid @grey5;
421 421 background: @grey6;
422 422 .border-radius(@border-radius);
423 423
424 424 #remove_gist {
425 425 float: right;
426 426 }
427 427
428 428 .author {
429 429 clear: both;
430 430 vertical-align: middle;
431 431 font-family: @text-bold;
432 432 }
433 433
434 434 .btn-mini {
435 435 float: left;
436 436 margin: 0 5px 0 0;
437 437 }
438 438
439 439 .code-header {
440 440 padding: @padding;
441 441 border-bottom: @border-thickness solid @grey5;
442 442
443 443 .rc-user {
444 444 min-width: 0;
445 445 margin-right: .5em;
446 446 }
447 447
448 448 .stats {
449 449 clear: both;
450 450 margin: 0 0 @padding 0;
451 451 padding: 0;
452 452 .left {
453 453 float: left;
454 454 clear: left;
455 455 max-width: 75%;
456 456 margin: 0 0 @padding 0;
457 457
458 458 &.item {
459 459 margin-right: @padding;
460 460 &.last { border-right: none; }
461 461 }
462 462 }
463 463 .buttons { float: right; }
464 464 .author {
465 465 height: 25px; margin-left: 15px; font-weight: bold;
466 466 }
467 467 }
468 468
469 469 .commit {
470 470 margin: 5px 0 0 26px;
471 471 font-weight: normal;
472 472 white-space: pre-wrap;
473 473 }
474 474 }
475 475
476 476 .message {
477 477 position: relative;
478 478 margin: @padding;
479 479
480 480 .codeblock-label {
481 481 margin: 0 0 1em 0;
482 482 }
483 483 }
484 484
485 485 .code-body {
486 486 padding: @padding;
487 487 background-color: #ffffff;
488 488 min-width: 100%;
489 489 box-sizing: border-box;
490 490 // TODO: johbo: Parent has overflow: auto, this forces the child here
491 491 // to have the intended size and to scroll. Should be simplified.
492 492 width: 100%;
493 493 overflow-x: auto;
494 494 }
495 495 }
496 496
497 497 .code-highlighttable,
498 498 div.codeblock {
499 499
500 500 &.readme {
501 501 background-color: white;
502 502 }
503 503
504 504 .markdown-block table {
505 505 border-collapse: collapse;
506 506
507 507 th,
508 508 td {
509 509 padding: .5em;
510 510 border: @border-thickness solid @border-default-color;
511 511 }
512 512 }
513 513
514 514 table {
515 515 border: 0px;
516 516 margin: 0;
517 517 letter-spacing: normal;
518 518
519 519
520 520 td {
521 521 border: 0px;
522 522 vertical-align: top;
523 523 }
524 524 }
525 525 }
526 526
527 527 div.codeblock .code-header .search-path { padding: 0 0 0 10px; }
528 528 div.search-code-body {
529 529 background-color: #ffffff; padding: 5px 0 5px 10px;
530 530 pre {
531 531 .match { background-color: #faffa6;}
532 532 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
533 533 }
534 534 .code-highlighttable {
535 535 border-collapse: collapse;
536 536
537 537 tr:hover {
538 538 background: #fafafa;
539 539 }
540 540 td.code {
541 541 padding-left: 10px;
542 542 }
543 543 td.line {
544 544 border-right: 1px solid #ccc !important;
545 545 padding-right: 10px;
546 546 text-align: right;
547 547 font-family: "Lucida Console",Monaco,monospace;
548 548 span {
549 549 white-space: pre-wrap;
550 550 color: #666666;
551 551 }
552 552 }
553 553 }
554 554 }
555 555
556 556 div.annotatediv { margin-left: 2px; margin-right: 4px; }
557 557 .code-highlight {
558 558 margin: 0; padding: 0; border-left: @border-thickness solid @grey5;
559 559 pre, .linenodiv pre { padding: 0 5px; margin: 0; }
560 560 pre div:target {background-color: @comment-highlight-color !important;}
561 561 }
562 562
563 563 .linenos a { text-decoration: none; }
564 564
565 565 .CodeMirror-selected { background: @rchighlightblue; }
566 566 .CodeMirror-focused .CodeMirror-selected { background: @rchighlightblue; }
567 567 .CodeMirror ::selection { background: @rchighlightblue; }
568 568 .CodeMirror ::-moz-selection { background: @rchighlightblue; }
569 569
570 570 .code { display: block; border:0px !important; }
571 571 .code-highlight, /* TODO: dan: merge codehilite into code-highlight */
572 572 .codehilite {
573 573 .hll { background-color: #ffffcc }
574 574 .c { color: #408080; font-style: italic } /* Comment */
575 575 .err, .codehilite .err { border: @border-thickness solid #FF0000 } /* Error */
576 576 .k { color: #008000; font-weight: bold } /* Keyword */
577 577 .o { color: #666666 } /* Operator */
578 578 .cm { color: #408080; font-style: italic } /* Comment.Multiline */
579 579 .cp { color: #BC7A00 } /* Comment.Preproc */
580 580 .c1 { color: #408080; font-style: italic } /* Comment.Single */
581 581 .cs { color: #408080; font-style: italic } /* Comment.Special */
582 582 .gd { color: #A00000 } /* Generic.Deleted */
583 583 .ge { font-style: italic } /* Generic.Emph */
584 584 .gr { color: #FF0000 } /* Generic.Error */
585 585 .gh { color: #000080; font-weight: bold } /* Generic.Heading */
586 586 .gi { color: #00A000 } /* Generic.Inserted */
587 587 .go { color: #808080 } /* Generic.Output */
588 588 .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
589 589 .gs { font-weight: bold } /* Generic.Strong */
590 590 .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
591 591 .gt { color: #0040D0 } /* Generic.Traceback */
592 592 .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
593 593 .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
594 594 .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
595 595 .kp { color: #008000 } /* Keyword.Pseudo */
596 596 .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
597 597 .kt { color: #B00040 } /* Keyword.Type */
598 598 .m { color: #666666 } /* Literal.Number */
599 599 .s { color: #BA2121 } /* Literal.String */
600 600 .na { color: #7D9029 } /* Name.Attribute */
601 601 .nb { color: #008000 } /* Name.Builtin */
602 602 .nc { color: #0000FF; font-weight: bold } /* Name.Class */
603 603 .no { color: #880000 } /* Name.Constant */
604 604 .nd { color: #AA22FF } /* Name.Decorator */
605 605 .ni { color: #999999; font-weight: bold } /* Name.Entity */
606 606 .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
607 607 .nf { color: #0000FF } /* Name.Function */
608 608 .nl { color: #A0A000 } /* Name.Label */
609 609 .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
610 610 .nt { color: #008000; font-weight: bold } /* Name.Tag */
611 611 .nv { color: #19177C } /* Name.Variable */
612 612 .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
613 613 .w { color: #bbbbbb } /* Text.Whitespace */
614 614 .mf { color: #666666 } /* Literal.Number.Float */
615 615 .mh { color: #666666 } /* Literal.Number.Hex */
616 616 .mi { color: #666666 } /* Literal.Number.Integer */
617 617 .mo { color: #666666 } /* Literal.Number.Oct */
618 618 .sb { color: #BA2121 } /* Literal.String.Backtick */
619 619 .sc { color: #BA2121 } /* Literal.String.Char */
620 620 .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
621 621 .s2 { color: #BA2121 } /* Literal.String.Double */
622 622 .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
623 623 .sh { color: #BA2121 } /* Literal.String.Heredoc */
624 624 .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
625 625 .sx { color: #008000 } /* Literal.String.Other */
626 626 .sr { color: #BB6688 } /* Literal.String.Regex */
627 627 .s1 { color: #BA2121 } /* Literal.String.Single */
628 628 .ss { color: #19177C } /* Literal.String.Symbol */
629 629 .bp { color: #008000 } /* Name.Builtin.Pseudo */
630 630 .vc { color: #19177C } /* Name.Variable.Class */
631 631 .vg { color: #19177C } /* Name.Variable.Global */
632 632 .vi { color: #19177C } /* Name.Variable.Instance */
633 633 .il { color: #666666 } /* Literal.Number.Integer.Long */
634 634 }
635 635
636 636 /* customized pre blocks for markdown/rst */
637 637 pre.literal-block, .codehilite pre{
638 638 padding: @padding;
639 639 border: 1px solid @grey6;
640 640 .border-radius(@border-radius);
641 641 background-color: @grey7;
642 642 }
643 643
644 644
645 645 /* START NEW CODE BLOCK CSS */
646 646
647 @cb-line-height: 18px;
648 @cb-line-code-padding: 10px;
649
647 650 table.cb {
648 651 width: 100%;
649 652 border-collapse: collapse;
650 653 margin-bottom: 10px;
651 654
652 655 * {
653 656 box-sizing: border-box;
654 657 }
655 658
656 659 /* intentionally general selector since .cb-line-selected must override it
657 660 and they both use !important since the td itself may have a random color
658 661 generated by annotation blocks. TLDR: if you change it, make sure
659 662 annotated block selection and line selection in file view still work */
660 663 .cb-line-fresh .cb-content {
661 664 background: white !important;
662 665 }
663 666
664 667 tr.cb-annotate {
665 668 border-top: 1px solid #eee;
666 669
667 670 &+ .cb-line {
668 671 border-top: 1px solid #eee;
669 672 }
670 673
671 674 &:first-child {
672 675 border-top: none;
673 676 &+ .cb-line {
674 677 border-top: none;
675 678 }
676 679 }
677 680 }
678 681
679 682 td {
680 683 vertical-align: top;
681 padding: 2px 10px;
684 padding: 0;
682 685
683 686 &.cb-content {
684 white-space: pre-wrap;
685 font-family: @font-family-monospace;
686 687 font-size: 12.35px;
687 688
688 span {
689 span.cb-code {
690 line-height: @cb-line-height;
691 padding-left: @cb-line-code-padding;
692 display: block;
693 white-space: pre-wrap;
694 font-family: @font-family-monospace;
689 695 word-break: break-word;
690 696 }
691 697 }
692 698
693 699 &.cb-lineno {
694 700 padding: 0;
695 height: 1px; /* this allows the <a> link to fill to 100% height of the td */
696 701 width: 50px;
697 702 color: rgba(0, 0, 0, 0.3);
698 703 text-align: right;
699 704 border-right: 1px solid #eee;
700 705 font-family: @font-family-monospace;
701 706
702 707 a::before {
703 708 content: attr(data-line-no);
704 709 }
705 &.cb-line-selected {
710 &.cb-line-selected a {
706 711 background: @comment-highlight-color !important;
707 712 }
708 713
709 714 a {
710 715 display: block;
711 height: 100%;
716 padding-right: @cb-line-code-padding;
717 line-height: @cb-line-height;
712 718 color: rgba(0, 0, 0, 0.3);
713 padding: 0 10px; /* vertical padding is 0 so that height: 100% works */
714 line-height: 18px; /* use this instead of vertical padding */
715 719 }
716 720 }
717 721
718 722 &.cb-content {
719 &.cb-line-selected {
723 &.cb-line-selected .cb-code {
720 724 background: @comment-highlight-color !important;
721 725 }
722 726 }
723 727
724 728 &.cb-annotate-info {
725 729 width: 320px;
726 730 min-width: 320px;
727 731 max-width: 320px;
728 732 padding: 5px 2px;
729 733 font-size: 13px;
730 734
731 735 strong.cb-annotate-message {
732 736 padding: 5px 0;
733 737 white-space: pre-line;
734 738 display: inline-block;
735 739 }
736 740 .rc-user {
737 741 float: none;
738 742 padding: 0 6px 0 17px;
739 743 min-width: auto;
740 744 min-height: auto;
741 745 }
742 746 }
743 747
744 748 &.cb-annotate-revision {
745 749 cursor: pointer;
746 750 text-align: right;
747 751 }
748 752 }
749 753 }
@@ -1,70 +1,68 b''
1 1 <%def name="render_line(line_num, tokens,
2 2 annotation=None,
3 3 bgcolor=None)">
4 4 <%
5 # avoid module lookups for performance
6 from rhodecode.lib.codeblocks import pygment_token_class
7 from rhodecode.lib.helpers import html_escape
5 from rhodecode.lib.codeblocks import render_tokenstream
6 # avoid module lookup for performance
7 html_escape = h.html_escape
8 8 %>
9 9 <tr class="cb-line cb-line-fresh"
10 10 %if annotation:
11 11 data-revision="${annotation.revision}"
12 12 %endif
13 13 >
14 14 <td class="cb-lineno" id="L${line_num}">
15 15 <a data-line-no="${line_num}" href="#L${line_num}"></a>
16 16 </td>
17 17 <td class="cb-content cb-content-fresh"
18 18 %if bgcolor:
19 19 style="background: ${bgcolor}"
20 20 %endif
21 >${
22 ''.join(
23 '<span class="%s">%s</span>' %
24 (pygment_token_class(token_type), html_escape(token_text))
25 for token_type, token_text in tokens) + '\n' | n
26 }</td>
27 ## this ugly list comp is necessary for performance
21 >
22 ## newline at end is necessary for highlight to work when line is empty
23 ## and for copy pasting code to work as expected
24 <span class="cb-code">${render_tokenstream(tokens)|n}${'\n'}</span>
25 </td>
28 26 </tr>
29 27 </%def>
30 28
31 29 <%def name="render_annotation_lines(annotation, lines, color_hasher)">
32 30 <%
33 31 rowspan = len(lines) + 1 # span the line's <tr> and annotation <tr>
34 32 %>
35 33 %if not annotation:
36 34 <tr class="cb-annotate">
37 35 <td class="cb-annotate-message" rowspan="${rowspan}"></td>
38 36 <td class="cb-annotate-revision" rowspan="${rowspan}"></td>
39 37 </tr>
40 38 %else:
41 39 <tr class="cb-annotate">
42 40 <td class="cb-annotate-info tooltip"
43 41 rowspan="${rowspan}"
44 42 title="Author: ${annotation.author | entity}<br>Date: ${annotation.date}<br>Message: ${annotation.message | entity}"
45 43 >
46 44 ${h.gravatar_with_user(annotation.author, 16) | n}
47 45 <strong class="cb-annotate-message">${
48 46 h.truncate(annotation.message, len(lines) * 30)
49 47 }</strong>
50 48 </td>
51 49 <td
52 50 class="cb-annotate-revision"
53 51 rowspan="${rowspan}"
54 52 data-revision="${annotation.revision}"
55 53 onclick="$('[data-revision=${annotation.revision}]').toggleClass('cb-line-fresh')"
56 54 style="background: ${color_hasher(annotation.raw_id)}">
57 55 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=annotation.raw_id)}">
58 56 r${annotation.revision}
59 57 </a>
60 58 </td>
61 59 </tr>
62 60 %endif
63 61
64 62 %for line_num, tokens in lines:
65 63 ${render_line(line_num, tokens,
66 64 bgcolor=color_hasher(annotation and annotation.raw_id or ''),
67 65 annotation=annotation,
68 66 )}
69 67 %endfor
70 68 </%def>
@@ -1,82 +1,82 b''
1 1 <%namespace name="sourceblock" file="/codeblocks/source.html"/>
2 2
3 3 <div id="codeblock" class="codeblock">
4 4 <div class="codeblock-header">
5 5 <div class="stats">
6 6 <span> <strong>${c.file}</strong></span>
7 7 <span> | ${c.file.lines()[0]} ${ungettext('line', 'lines', c.file.lines()[0])}</span>
8 8 <span> | ${h.format_byte_size_binary(c.file.size)}</span>
9 9 <span> | ${c.file.mimetype} </span>
10 10 <span class="item last"> | ${h.get_lexer_for_filenode(c.file).__class__.__name__}</span>
11 11 </div>
12 12 <div class="buttons">
13 13 <a id="file_history_overview" href="#">
14 14 ${_('History')}
15 15 </a>
16 16 <a id="file_history_overview_full" style="display: none" href="${h.url('changelog_file_home',repo_name=c.repo_name, revision=c.commit.raw_id, f_path=c.f_path)}">
17 17 ${_('Show Full History')}
18 18 </a> |
19 19 %if c.annotate:
20 20 ${h.link_to(_('Source'), h.url('files_home', repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
21 21 %else:
22 22 ${h.link_to(_('Annotation'), h.url('files_annotate_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
23 23 %endif
24 24 | ${h.link_to(_('Raw'), h.url('files_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
25 25 | <a href="${h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path)}">
26 26 ${_('Download')}
27 27 </a>
28 28
29 29 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
30 30 |
31 31 %if c.on_branch_head and c.branch_or_raw_id and not c.file.is_binary:
32 32 <a href="${h.url('files_edit_home',repo_name=c.repo_name,revision=c.branch_or_raw_id,f_path=c.f_path, anchor='edit')}">
33 33 ${_('Edit on Branch:%s') % c.branch_or_raw_id}
34 34 </a>
35 35 | <a class="btn-danger btn-link" href="${h.url('files_delete_home',repo_name=c.repo_name,revision=c.branch_or_raw_id,f_path=c.f_path, anchor='edit')}">${_('Delete')}
36 36 </a>
37 37 %elif c.on_branch_head and c.branch_or_raw_id and c.file.is_binary:
38 38 ${h.link_to(_('Edit'), '#', class_="btn btn-link disabled tooltip", title=_('Editing binary files not allowed'))}
39 39 | ${h.link_to(_('Delete'), h.url('files_delete_home',repo_name=c.repo_name,revision=c.branch_or_raw_id,f_path=c.f_path, anchor='edit'),class_="btn-danger btn-link")}
40 40 %else:
41 41 ${h.link_to(_('Edit'), '#', class_="btn btn-link disabled tooltip", title=_('Editing files allowed only when on branch head commit'))}
42 42 | ${h.link_to(_('Delete'), '#', class_="btn btn-danger btn-link disabled tooltip", title=_('Deleting files allowed only when on branch head commit'))}
43 43 %endif
44 44 %endif
45 45 </div>
46 46 </div>
47 47 <div id="file_history_container"></div>
48 48 <div class="code-body">
49 49 %if c.file.is_binary:
50 50 <div>
51 51 ${_('Binary file (%s)') % c.file.mimetype}
52 52 </div>
53 53 %else:
54 54 % if c.file.size < c.cut_off_limit:
55 55 %if c.renderer and not c.annotate:
56 56 ${h.render(c.file.content, renderer=c.renderer)}
57 57 %else:
58 58 <table class="cb codehilite">
59 59 %if c.annotate:
60 60 <% color_hasher = h.color_hasher() %>
61 61 %for annotation, lines in c.annotated_lines:
62 62 ${sourceblock.render_annotation_lines(annotation, lines, color_hasher)}
63 63 %endfor
64 64 %else:
65 %for line_num, tokens in c.lines:
65 %for line_num, tokens in enumerate(c.lines, 1):
66 66 ${sourceblock.render_line(line_num, tokens)}
67 67 %endfor
68 68 %endif
69 69 </table>
70 70 </div>
71 71 %endif
72 72 %else:
73 73 ${_('File is too big to display')} ${h.link_to(_('Show as raw'),
74 74 h.url('files_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
75 75 %endif
76 76 %endif
77 77 </div>
78 78 </div>
79 79
80 80 <script>
81 81 var source_page = true;
82 82 </script>
General Comments 0
You need to be logged in to leave comments. Login now