Changeset 1764
- Timestamp:
- 06/06/2005 03:05:42 PM (3 years ago)
- Location:
- trunk
- Files:
-
- 6 modified
-
htdocs/css/code.css (modified) (1 diff)
-
trac/Browser.py (modified) (1 diff)
-
trac/mimeview/api.py (modified) (11 diffs)
-
trac/mimeview/enscript.py (modified) (2 diffs)
-
trac/mimeview/php.py (modified) (1 diff)
-
trac/mimeview/silvercity.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/htdocs/css/code.css
r1519 r1764 1 .code-block { 1 table.code-block-block { 2 border: 1px solid #ddd; 3 border-spacing: 0; 4 border-top: 0; 5 empty-cells: show; 6 font-size: 12px; 7 line-height: 130%; 8 padding: 0; 9 margin: 0 auto; 10 width: 100%; 11 } 12 table.code-block th { 13 border-right: 1px solid #d7d7d7; 14 border-bottom: 1px solid #998; 15 font-size: 11px; 16 } 17 table.code-block thead th { 18 background: #eee; 19 border-top: 1px solid #d7d7d7; 20 color: #999; 21 padding: 0 .25em; 22 text-align: center; 23 white-space: nowrap; 24 } 25 table.code-block tbody th { 26 background: #eed; 27 color: #886; 28 font-weight: normal; 29 padding: 0 .5em; 30 text-align: right; 31 vertical-align: top; 32 } 33 table.code-block tbody td { 34 background: #fff; 35 font: normal 11px monospace; 36 overflow: hidden; 37 padding: 1px 2px; 38 vertical-align: top; 39 } 40 41 div.code-block { 2 42 border: 1px dotted #d7d7d7; 3 43 margin: 1em 0; -
trunk/trac/Browser.py
r1763 r1764 230 230 else: 231 231 preview = Mimeview(self.env).render(req, mime_type, content, 232 node.name, node.rev) 232 node.name, node.rev, 233 annotations=['lineno']) 233 234 req.hdf['file.preview'] = preview 234 235 -
trunk/trac/mimeview/api.py
r1697 r1764 23 23 # 24 24 25 from __future__ import generators 26 import re 27 try: 28 from cStringIO import StringIO 29 except ImportError: 30 from StringIO import StringIO 31 25 32 from trac.core import * 33 from trac.util import enum, escape 26 34 27 35 __all__ = ['get_charset', 'get_mimetype', 'is_binary', 'Mimeview'] … … 91 99 92 100 def get_charset(mimetype): 93 """ 94 Returns the character encoding included in the given content type string, 101 """Return the character encoding included in the given content type string, 95 102 or `None` if `mimetype` is `None` or empty or if no charset information is 96 103 available. … … 102 109 103 110 def get_mimetype(filename): 111 """Guess the most probable MIME type of a file with the given name.""" 104 112 try: 105 113 suffix = filename.split('.')[-1] … … 112 120 113 121 def is_binary(str): 114 """ 115 Try to detect content by checking the first thousand bytes for zeroes. 116 """ 122 """Detect binary content by checking the first thousand bytes for zeroes.""" 117 123 for i in range(0, min(len(str), 1000)): 118 124 if str[i] == '\0': … … 120 126 return False 121 127 122 123 128 class IHTMLPreviewRenderer(Interface): 129 """Extension point interface for components that add HTML renderers of 130 specific content types to the `Mimeview` component. 124 131 """ 125 Extension point interface for components that add HTML renderers of specific126 content types to the `Mimeview` component.127 """128 132 129 133 def get_quality_ratio(mimetype): 130 """ 131 Return the level of support this renderer provides for the content of 134 """Return the level of support this renderer provides for the content of 132 135 the specified MIME type. The return value must be a number between 0 133 136 and 9, where 0 means no support and 9 means "perfect" support. … … 135 138 136 139 def render(req, mimetype, content, filename=None, rev=None): 137 """ 138 Render an XHTML preview of the given content of the specified MIME type, 139 and return the generated XHTML text as a string. 140 """Render an XHTML preview of the given content of the specified MIME 141 type. 142 143 Can return the generated XHTML text as a single string or as an iterable 144 that yields strings. In the latter case, the list will be considered to 145 correspond to lines of text in the original content. 140 146 141 147 The `filename` and `rev` parameters are provided for renderers that … … 144 150 """ 145 151 152 class IHTMLPreviewAnnotator(Interface): 153 """Extension point interface for components that can annotate an XHTML 154 representation of file contents with additional information.""" 155 156 def get_annotation_type(): 157 """Return a (type, label, description) tuple that defines the type of 158 annotation and provides human readable names. The `type` element should 159 be unique to the annotator. The `label` element is used as column 160 heading for the table, while `description` is used as a display name to 161 let the user toggle the appearance of the annotation type. 162 """ 163 164 def annotate_line(number, content): 165 """Return the XHTML markup for the table cell that contains the 166 annotation data.""" 167 146 168 147 169 class Mimeview(Component): … … 149 171 150 172 renderers = ExtensionPoint(IHTMLPreviewRenderer) 151 152 def render(self, req, mimetype, content, filename=None, rev=None): 173 annotators = ExtensionPoint(IHTMLPreviewAnnotator) 174 175 # Public API 176 177 def get_annotation_types(self): 178 """Generator that returns all available annotation types.""" 179 for annotator in self.annotators: 180 yield annotator.get_annotation_type() 181 182 def render(self, req, mimetype, content, filename=None, rev=None, 183 annotations=None): 184 """Render an XHTML preview of the given content of the specified MIME 185 type, selecting the most appropriate `IHTMLPreviewRenderer` 186 implementation available for the given MIME type. 187 188 Return a string containing the XHTML text. 189 """ 153 190 if not content: 154 191 return '' … … 168 205 self.log.debug('Trying to render HTML preview using %s' 169 206 % renderer.__class__.__name__) 170 return renderer.render(req, mimetype, content, filename, rev) 207 result = renderer.render(req, mimetype, content, filename, rev) 208 break 171 209 except Exception, e: 172 210 self.log.warning('HTML preview using %s failed (%s)' 173 211 % (renderer, e)) 174 212 175 return None 213 if result and not isinstance(result, (str, unicode)): 214 if annotations: 215 return self._annotate(result, annotations) 216 else: 217 buf = StringIO() 218 buf.write('<div class="code-block"><pre>') 219 for line in result: 220 buf.write(line + '\n') 221 buf.write('<pre></div>') 222 return buf.getvalue() 223 224 return result 225 226 def _annotate(self, lines, annotations): 227 buf = StringIO() 228 buf.write('<table class="code-block listing"><thead><tr>') 229 annotators = [] 230 for annotator in self.annotators: 231 atype, alabel, adesc = annotator.get_annotation_type() 232 if atype in annotations: 233 buf.write('<th class="%s">%s</th>' % (atype, alabel)) 234 annotators.append(annotator) 235 buf.write('<th class="content"> </th>') 236 buf.write('</tr></thead><tbody>') 237 238 space_re = re.compile(' ( +)|^ ') 239 def htmlify(match): 240 div, mod = divmod(len(match.group(0)), 2) 241 return div * ' ' + mod * ' ' 242 243 for num, line in enum(_html_splitlines(lines)): 244 cells = [] 245 for annotator in annotators: 246 cells.append(annotator.annotate_line(num + 1, line)) 247 cells.append('<td>%s</td>\n' % space_re.sub(htmlify, line)) 248 buf.write('<tr>' + '\n'.join(cells) + '</tr>') 249 buf.write('</tbody></table>') 250 return buf.getvalue() 251 252 253 def _html_splitlines(lines): 254 """Tracks open and close tags in lines of HTML text and yields lines that 255 have no tags spanning more than one line.""" 256 open_tag_re = re.compile(r'<(\w+)\s.*?[^/]?>') 257 close_tag_re = re.compile(r'</(\w+)>') 258 open_tags = [] 259 for line in lines: 260 # Reopen tags still open from the previous line 261 for tag in open_tags: 262 line = tag.group(0) + line 263 open_tags = [] 264 265 # Find all tags opened on this line 266 for tag in open_tag_re.finditer(line): 267 open_tags.append(tag) 268 269 # Find all tags closed on this line 270 for ctag in close_tag_re.finditer(line): 271 for otag in open_tags: 272 if otag.group(1) == ctag.group(1): 273 open_tags.remove(otag) 274 break 275 276 # Close all tags still open at the end of line, they'll get reopened at 277 # the beginning of the next line 278 for tag in open_tags: 279 line += '</%s>' % tag.group(1) 280 281 yield line 282 283 284 class LineNumberAnnotator(Component): 285 """Text annotator that adds a column with line numbers.""" 286 implements(IHTMLPreviewAnnotator) 287 288 # ITextAnnotator methods 289 290 def get_annotation_type(self): 291 return 'lineno', 'Line', 'Line numbers' 292 293 def annotate_line(self, number, content): 294 return '<th id="l%s">%s</th>' % (number, number) 176 295 177 296 178 297 class PlainTextRenderer(Component): 298 """HTML preview renderer for plain text, and fallback for any kind of text 299 for which no more specific renderer is available. 179 300 """ 180 HTML preview renderer for plain text, and fallback for any kind of text for181 which no more specific renderer is available.182 """183 184 301 implements(IHTMLPreviewRenderer) 185 302 … … 190 307 if is_binary(content): 191 308 self.env.log.debug("Binary data; no preview available") 192 return ''309 return 193 310 194 311 self.env.log.debug("Using default plain text mimeviewer") 195 312 from trac.util import escape 196 return '<pre class="code-block">' + escape(content) + '</pre>' 313 for line in content.splitlines(): 314 yield line 197 315 198 316 199 317 class ImageRenderer(Component): 200 """ 201 Inline image display. 202 """ 203 318 """Inline image display.""" 204 319 implements(IHTMLPreviewRenderer) 205 320 … … 218 333 219 334 class WikiTextRenderer(Component): 220 """ 221 Render files containing Trac's own Wiki formatting markup. 222 """ 223 335 """Render files containing Trac's own Wiki formatting markup.""" 224 336 implements(IHTMLPreviewRenderer) 225 337 -
trunk/trac/mimeview/enscript.py
r1620 r1764 91 91 r'(?P<var><FONT COLOR="#DA70D6">)', 92 92 r'(?P<font><FONT.*?>)', 93 r'(?P<endfont></FONT>)' ,93 r'(?P<endfont></FONT>)' 94 94 ] 95 95 rules = classmethod(rules) … … 115 115 np = NaivePopen(cmdline, content, capturestderr=1) 116 116 if np.errorlevel or np.err: 117 err = 'Running (%s) failed: %s, %s.' % (cmdline, np.errorlevel, np.err) 117 err = 'Running (%s) failed: %s, %s.' % (cmdline, np.errorlevel, 118 np.err) 118 119 raise Exception, err 119 120 odata = np.out 120 121 121 122 # Strip header and footer 122 i = odata.find('< /H1>')123 beg = i > 0 and i + 7123 i = odata.find('<PRE>') 124 beg = i > 0 and i + 6 124 125 i = odata.rfind('</PRE>') 125 end = i > 0 and i + 6or len(odata)126 end = i > 0 and i or len(odata) 126 127 127 128 odata = EnscriptDeuglifier().format(odata[beg:end]) 128 return '<div class="code-block">' + odata + '</div>' 129 for line in odata.splitlines(): 130 yield line -
trunk/trac/mimeview/php.py
r1620 r1764 67 67 np = NaivePopen(cmdline, content, capturestderr=1) 68 68 if np.errorlevel or np.err: 69 err = 'Running (%s) failed: %s, %s.' % (cmdline, np.errorlevel, np.err) 69 err = 'Running (%s) failed: %s, %s.' % (cmdline, np.errorlevel, 70 np.err) 70 71 raise Exception, err 71 72 odata = np.out 72 73 73 74 # Strip header 74 beg = odata.find('<code>') 75 odata = PhpDeuglifier().format(odata[beg:]) 76 return '<div class="code-block">' + odata + '</div>' 75 html = PhpDeuglifier().format(odata.splitlines()[1]) 76 for line in html.split('<br />'): 77 # PHP generates _way_ too many non-breaking spaces... 78 # We don't need them anyway, so replace them by normal spaces 79 yield line.replace(' ', ' ') -
trunk/trac/mimeview/silvercity.py
r1620 r1764 23 23 # Get it at: http://silvercity.sourceforge.net/ 24 24 # 25 26 import re 27 try: 28 from cStringIO import StringIO 29 except ImportError: 30 from StringIO import StringIO 25 31 26 32 from trac.core import * … … 83 89 raise Exception, err 84 90 85 try:86 from cStringIO import StringIO87 except ImportError:88 from StringIO import StringIO89 90 91 buf = StringIO() 91 92 generator().generate_html(buf, content) 92 return '<div class="code-block">%s</div>\n' % buf.getvalue() 93 94 br_re = re.compile(r'<br\s*/?>$', re.MULTILINE) 95 span_default_re = re.compile(r'<span class="p_default">(.*?)</span>', 96 re.DOTALL) 97 html = span_default_re.sub(r'\1', br_re.sub('', buf.getvalue())) 98 99 for line in html.splitlines(): 100 # SilverCity generates _way_ too many non-breaking spaces... 101 # We don't need them anyway, so replace them by normal spaces 102 yield line.replace(' ', ' ')
