3 pygments.formatters.html |
3 pygments.formatters.html |
4 ~~~~~~~~~~~~~~~~~~~~~~~~ |
4 ~~~~~~~~~~~~~~~~~~~~~~~~ |
5 |
5 |
6 Formatter for HTML output. |
6 Formatter for HTML output. |
7 |
7 |
8 :copyright: Copyright 2006-2012 by the Pygments team, see AUTHORS. |
8 :copyright: Copyright 2006-2013 by the Pygments team, see AUTHORS. |
9 :license: BSD, see LICENSE for details. |
9 :license: BSD, see LICENSE for details. |
10 """ |
10 """ |
11 |
11 |
12 import os |
12 import os |
13 import sys |
13 import sys |
|
14 import os.path |
14 import io |
15 import io |
15 |
16 |
16 from pygments.formatter import Formatter |
17 from pygments.formatter import Formatter |
17 from pygments.token import Token, Text, STANDARD_TYPES |
18 from pygments.token import Token, Text, STANDARD_TYPES |
18 from pygments.util import get_bool_opt, get_int_opt, get_list_opt, bytes |
19 from pygments.util import get_bool_opt, get_int_opt, get_list_opt, bytes |
19 |
20 |
|
21 try: |
|
22 import ctags |
|
23 except ImportError: |
|
24 ctags = None |
20 |
25 |
21 __all__ = ['HtmlFormatter'] |
26 __all__ = ['HtmlFormatter'] |
22 |
27 |
23 |
28 |
24 _escape_html_table = { |
29 _escape_html_table = { |
139 |
144 |
140 With the `full` option, a complete HTML 4 document is output, including |
145 With the `full` option, a complete HTML 4 document is output, including |
141 the style definitions inside a ``<style>`` tag, or in a separate file if |
146 the style definitions inside a ``<style>`` tag, or in a separate file if |
142 the `cssfile` option is given. |
147 the `cssfile` option is given. |
143 |
148 |
|
149 When `tagsfile` is set to the path of a ctags index file, it is used to |
|
150 generate hyperlinks from names to their definition. You must enable |
|
151 `anchorlines` and run ctags with the `-n` option for this to work. The |
|
152 `python-ctags` module from PyPI must be installed to use this feature; |
|
153 otherwise a `RuntimeError` will be raised. |
|
154 |
144 The `get_style_defs(arg='')` method of a `HtmlFormatter` returns a string |
155 The `get_style_defs(arg='')` method of a `HtmlFormatter` returns a string |
145 containing CSS rules for the CSS classes used by the formatter. The |
156 containing CSS rules for the CSS classes used by the formatter. The |
146 argument `arg` can be used to specify additional CSS selectors that |
157 argument `arg` can be used to specify additional CSS selectors that |
147 are prepended to the classes. A call `fmter.get_style_defs('td .code')` |
158 are prepended to the classes. A call `fmter.get_style_defs('td .code')` |
148 would result in the following CSS classes: |
159 would result in the following CSS classes: |
280 `lineanchors` |
291 `lineanchors` |
281 If set to a nonempty string, e.g. ``foo``, the formatter will wrap each |
292 If set to a nonempty string, e.g. ``foo``, the formatter will wrap each |
282 output line in an anchor tag with a ``name`` of ``foo-linenumber``. |
293 output line in an anchor tag with a ``name`` of ``foo-linenumber``. |
283 This allows easy linking to certain lines. *New in Pygments 0.9.* |
294 This allows easy linking to certain lines. *New in Pygments 0.9.* |
284 |
295 |
|
296 `linespans` |
|
297 If set to a nonempty string, e.g. ``foo``, the formatter will wrap each |
|
298 output line in a span tag with an ``id`` of ``foo-linenumber``. |
|
299 This allows easy access to lines via javascript. *New in Pygments 1.6.* |
|
300 |
285 `anchorlinenos` |
301 `anchorlinenos` |
286 If set to `True`, will wrap line numbers in <a> tags. Used in |
302 If set to `True`, will wrap line numbers in <a> tags. Used in |
287 combination with `linenos` and `lineanchors`. |
303 combination with `linenos` and `lineanchors`. |
|
304 |
|
305 `tagsfile` |
|
306 If set to the path of a ctags file, wrap names in anchor tags that |
|
307 link to their definitions. `lineanchors` should be used, and the |
|
308 tags file should specify line numbers (see the `-n` option to ctags). |
|
309 *New in Pygments 1.6.* |
|
310 |
|
311 `tagurlformat` |
|
312 A string formatting pattern used to generate links to ctags definitions. |
|
313 Available variables are `%(path)s`, `%(fname)s` and `%(fext)s`. |
|
314 Defaults to an empty string, resulting in just `#prefix-number` links. |
|
315 *New in Pygments 1.6.* |
288 |
316 |
289 |
317 |
290 **Subclassing the HTML formatter** |
318 **Subclassing the HTML formatter** |
291 |
319 |
292 *New in Pygments 0.7.* |
320 *New in Pygments 0.7.* |
349 self.cssclass = self._decodeifneeded(options.get('cssclass', 'highlight')) |
377 self.cssclass = self._decodeifneeded(options.get('cssclass', 'highlight')) |
350 self.cssstyles = self._decodeifneeded(options.get('cssstyles', '')) |
378 self.cssstyles = self._decodeifneeded(options.get('cssstyles', '')) |
351 self.prestyles = self._decodeifneeded(options.get('prestyles', '')) |
379 self.prestyles = self._decodeifneeded(options.get('prestyles', '')) |
352 self.cssfile = self._decodeifneeded(options.get('cssfile', '')) |
380 self.cssfile = self._decodeifneeded(options.get('cssfile', '')) |
353 self.noclobber_cssfile = get_bool_opt(options, 'noclobber_cssfile', False) |
381 self.noclobber_cssfile = get_bool_opt(options, 'noclobber_cssfile', False) |
|
382 self.tagsfile = self._decodeifneeded(options.get('tagsfile', '')) |
|
383 self.tagurlformat = self._decodeifneeded(options.get('tagurlformat', '')) |
|
384 |
|
385 if self.tagsfile: |
|
386 if not ctags: |
|
387 raise RuntimeError('The "ctags" package must to be installed ' |
|
388 'to be able to use the "tagsfile" feature.') |
|
389 self._ctags = ctags.CTags(self.tagsfile) |
354 |
390 |
355 linenos = options.get('linenos', False) |
391 linenos = options.get('linenos', False) |
356 if linenos == 'inline': |
392 if linenos == 'inline': |
357 self.linenos = 2 |
393 self.linenos = 2 |
358 elif linenos: |
394 elif linenos: |
364 self.linenostep = abs(get_int_opt(options, 'linenostep', 1)) |
400 self.linenostep = abs(get_int_opt(options, 'linenostep', 1)) |
365 self.linenospecial = abs(get_int_opt(options, 'linenospecial', 0)) |
401 self.linenospecial = abs(get_int_opt(options, 'linenospecial', 0)) |
366 self.nobackground = get_bool_opt(options, 'nobackground', False) |
402 self.nobackground = get_bool_opt(options, 'nobackground', False) |
367 self.lineseparator = options.get('lineseparator', '\n') |
403 self.lineseparator = options.get('lineseparator', '\n') |
368 self.lineanchors = options.get('lineanchors', '') |
404 self.lineanchors = options.get('lineanchors', '') |
|
405 self.linespans = options.get('linespans', '') |
369 self.anchorlinenos = options.get('anchorlinenos', False) |
406 self.anchorlinenos = options.get('anchorlinenos', False) |
370 self.hl_lines = set() |
407 self.hl_lines = set() |
371 for lineno in get_list_opt(options, 'hl_lines', []): |
408 for lineno in get_list_opt(options, 'hl_lines', []): |
372 try: |
409 try: |
373 self.hl_lines.add(int(lineno)) |
410 self.hl_lines.add(int(lineno)) |
594 mw, (num%st and ' ' or num)) + line |
631 mw, (num%st and ' ' or num)) + line |
595 num += 1 |
632 num += 1 |
596 |
633 |
597 def _wrap_lineanchors(self, inner): |
634 def _wrap_lineanchors(self, inner): |
598 s = self.lineanchors |
635 s = self.lineanchors |
599 i = self.linenostart - 1 # subtract 1 since we have to increment i *before* yielding |
636 i = self.linenostart - 1 # subtract 1 since we have to increment i |
|
637 # *before* yielding |
600 for t, line in inner: |
638 for t, line in inner: |
601 if t: |
639 if t: |
602 i += 1 |
640 i += 1 |
603 yield 1, '<a name="%s-%d"></a>' % (s, i) + line |
641 yield 1, '<a name="%s-%d"></a>' % (s, i) + line |
|
642 else: |
|
643 yield 0, line |
|
644 |
|
645 def _wrap_linespans(self, inner): |
|
646 s = self.linespans |
|
647 i = self.linenostart - 1 |
|
648 for t, line in inner: |
|
649 if t: |
|
650 i += 1 |
|
651 yield 1, '<span id="%s-%d">%s</span>' % (s, i, line) |
604 else: |
652 else: |
605 yield 0, line |
653 yield 0, line |
606 |
654 |
607 def _wrap_div(self, inner): |
655 def _wrap_div(self, inner): |
608 style = [] |
656 style = [] |
641 lsep = self.lineseparator |
689 lsep = self.lineseparator |
642 # for <span style=""> lookup only |
690 # for <span style=""> lookup only |
643 getcls = self.ttype2class.get |
691 getcls = self.ttype2class.get |
644 c2s = self.class2style |
692 c2s = self.class2style |
645 escape_table = _escape_html_table |
693 escape_table = _escape_html_table |
|
694 tagsfile = self.tagsfile |
646 |
695 |
647 lspan = '' |
696 lspan = '' |
648 line = '' |
697 line = '' |
649 for ttype, value in tokensource: |
698 for ttype, value in tokensource: |
650 if nocls: |
699 if nocls: |
656 else: |
705 else: |
657 cls = self._get_css_class(ttype) |
706 cls = self._get_css_class(ttype) |
658 cspan = cls and '<span class="%s">' % cls or '' |
707 cspan = cls and '<span class="%s">' % cls or '' |
659 |
708 |
660 parts = value.translate(escape_table).split('\n') |
709 parts = value.translate(escape_table).split('\n') |
|
710 |
|
711 if tagsfile and ttype in Token.Name: |
|
712 filename, linenumber = self._lookup_ctag(value) |
|
713 if linenumber: |
|
714 base, filename = os.path.split(filename) |
|
715 if base: |
|
716 base += '/' |
|
717 filename, extension = os.path.splitext(filename) |
|
718 url = self.tagurlformat % {'path': base, 'fname': filename, |
|
719 'fext': extension} |
|
720 parts[0] = "<a href=\"%s#%s-%d\">%s" % \ |
|
721 (url, self.lineanchors, linenumber, parts[0]) |
|
722 parts[-1] = parts[-1] + "</a>" |
661 |
723 |
662 # for all but the last line |
724 # for all but the last line |
663 for part in parts[:-1]: |
725 for part in parts[:-1]: |
664 if line: |
726 if line: |
665 if lspan != cspan: |
727 if lspan != cspan: |
686 # else we neither have to open a new span nor set lspan |
748 # else we neither have to open a new span nor set lspan |
687 |
749 |
688 if line: |
750 if line: |
689 yield 1, line + (lspan and '</span>') + lsep |
751 yield 1, line + (lspan and '</span>') + lsep |
690 |
752 |
|
753 def _lookup_ctag(self, token): |
|
754 entry = ctags.TagEntry() |
|
755 if self._ctags.find(entry, token, 0): |
|
756 return entry['file'], entry['lineNumber'] |
|
757 else: |
|
758 return None, None |
|
759 |
691 def _highlight_lines(self, tokensource): |
760 def _highlight_lines(self, tokensource): |
692 """ |
761 """ |
693 Highlighted the lines specified in the `hl_lines` option by |
762 Highlighted the lines specified in the `hl_lines` option by |
694 post-processing the token stream coming from `_format_lines`. |
763 post-processing the token stream coming from `_format_lines`. |
695 """ |
764 """ |
738 if not self.nowrap: |
807 if not self.nowrap: |
739 if self.linenos == 2: |
808 if self.linenos == 2: |
740 source = self._wrap_inlinelinenos(source) |
809 source = self._wrap_inlinelinenos(source) |
741 if self.lineanchors: |
810 if self.lineanchors: |
742 source = self._wrap_lineanchors(source) |
811 source = self._wrap_lineanchors(source) |
|
812 if self.linespans: |
|
813 source = self._wrap_linespans(source) |
743 source = self.wrap(source, outfile) |
814 source = self.wrap(source, outfile) |
744 if self.linenos == 1: |
815 if self.linenos == 1: |
745 source = self._wrap_tablelinenos(source) |
816 source = self._wrap_tablelinenos(source) |
746 if self.full: |
817 if self.full: |
747 source = self._wrap_full(source, outfile) |
818 source = self._wrap_full(source, outfile) |