ThirdParty/Pygments/pygments/formatters/html.py

changeset 4697
c2e9bf425554
parent 4172
4f20dba37ab6
child 5072
aab59042fefb
equal deleted inserted replaced
4696:bf4d19a7cade 4697:c2e9bf425554
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-2014 by the Pygments team, see AUTHORS. 8 :copyright: Copyright 2006-2015 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 from __future__ import print_function 12 from __future__ import print_function
13 13
34 ord('>'): u'>', 34 ord('>'): u'>',
35 ord('"'): u'"', 35 ord('"'): u'"',
36 ord("'"): u''', 36 ord("'"): u''',
37 } 37 }
38 38
39
39 def escape_html(text, table=_escape_html_table): 40 def escape_html(text, table=_escape_html_table):
40 """Escape &, <, > as well as single and double quotes for HTML.""" 41 """Escape &, <, > as well as single and double quotes for HTML."""
41 return text.translate(table) 42 return text.translate(table)
42
43 def get_random_id():
44 """Return a random id for javascript fields."""
45 from random import random
46 from time import time
47 try:
48 from hashlib import sha1 as sha
49 except ImportError:
50 import sha
51 sha = sha.new
52 return sha('%s|%s' % (random(), time())).hexdigest()
53 43
54 44
55 def _get_ttype_class(ttype): 45 def _get_ttype_class(ttype):
56 fname = STANDARD_TYPES.get(ttype) 46 fname = STANDARD_TYPES.get(ttype)
57 if fname: 47 if fname:
148 the style definitions inside a ``<style>`` tag, or in a separate file if 138 the style definitions inside a ``<style>`` tag, or in a separate file if
149 the `cssfile` option is given. 139 the `cssfile` option is given.
150 140
151 When `tagsfile` is set to the path of a ctags index file, it is used to 141 When `tagsfile` is set to the path of a ctags index file, it is used to
152 generate hyperlinks from names to their definition. You must enable 142 generate hyperlinks from names to their definition. You must enable
153 `anchorlines` and run ctags with the `-n` option for this to work. The 143 `lineanchors` and run ctags with the `-n` option for this to work. The
154 `python-ctags` module from PyPI must be installed to use this feature; 144 `python-ctags` module from PyPI must be installed to use this feature;
155 otherwise a `RuntimeError` will be raised. 145 otherwise a `RuntimeError` will be raised.
156 146
157 The `get_style_defs(arg='')` method of a `HtmlFormatter` returns a string 147 The `get_style_defs(arg='')` method of a `HtmlFormatter` returns a string
158 containing CSS rules for the CSS classes used by the formatter. The 148 containing CSS rules for the CSS classes used by the formatter. The
328 A string formatting pattern used to generate links to ctags definitions. 318 A string formatting pattern used to generate links to ctags definitions.
329 Available variables are `%(path)s`, `%(fname)s` and `%(fext)s`. 319 Available variables are `%(path)s`, `%(fname)s` and `%(fext)s`.
330 Defaults to an empty string, resulting in just `#prefix-number` links. 320 Defaults to an empty string, resulting in just `#prefix-number` links.
331 321
332 .. versionadded:: 1.6 322 .. versionadded:: 1.6
323
324 `filename`
325 A string used to generate a filename when rendering <pre> blocks,
326 for example if displaying source code.
327
328 .. versionadded:: 2.1
333 329
334 330
335 **Subclassing the HTML formatter** 331 **Subclassing the HTML formatter**
336 332
337 .. versionadded:: 0.7 333 .. versionadded:: 0.7
396 self.prestyles = self._decodeifneeded(options.get('prestyles', '')) 392 self.prestyles = self._decodeifneeded(options.get('prestyles', ''))
397 self.cssfile = self._decodeifneeded(options.get('cssfile', '')) 393 self.cssfile = self._decodeifneeded(options.get('cssfile', ''))
398 self.noclobber_cssfile = get_bool_opt(options, 'noclobber_cssfile', False) 394 self.noclobber_cssfile = get_bool_opt(options, 'noclobber_cssfile', False)
399 self.tagsfile = self._decodeifneeded(options.get('tagsfile', '')) 395 self.tagsfile = self._decodeifneeded(options.get('tagsfile', ''))
400 self.tagurlformat = self._decodeifneeded(options.get('tagurlformat', '')) 396 self.tagurlformat = self._decodeifneeded(options.get('tagurlformat', ''))
397 self.filename = self._decodeifneeded(options.get('filename', ''))
401 398
402 if self.tagsfile: 399 if self.tagsfile:
403 if not ctags: 400 if not ctags:
404 raise RuntimeError('The "ctags" package must to be installed ' 401 raise RuntimeError('The "ctags" package must to be installed '
405 'to be able to use the "tagsfile" feature.') 402 'to be able to use the "tagsfile" feature.')
435 the classprefix option.""" 432 the classprefix option."""
436 ttypeclass = _get_ttype_class(ttype) 433 ttypeclass = _get_ttype_class(ttype)
437 if ttypeclass: 434 if ttypeclass:
438 return self.classprefix + ttypeclass 435 return self.classprefix + ttypeclass
439 return '' 436 return ''
437
438 def _get_css_classes(self, ttype):
439 """Return the css classes of this token type prefixed with
440 the classprefix option."""
441 cls = self._get_css_class(ttype)
442 while ttype not in STANDARD_TYPES:
443 ttype = ttype.parent
444 cls = self._get_css_class(ttype) + ' ' + cls
445 return cls
440 446
441 def _create_stylesheet(self): 447 def _create_stylesheet(self):
442 t2c = self.ttype2class = {Token: ''} 448 t2c = self.ttype2class = {Token: ''}
443 c2s = self.class2style = {} 449 c2s = self.class2style = {}
444 for ttype, ndef in self.style: 450 for ttype, ndef in self.style:
520 # pseudo files, e.g. name == '<fdopen>' 526 # pseudo files, e.g. name == '<fdopen>'
521 raise AttributeError 527 raise AttributeError
522 cssfilename = os.path.join(os.path.dirname(filename), 528 cssfilename = os.path.join(os.path.dirname(filename),
523 self.cssfile) 529 self.cssfile)
524 except AttributeError: 530 except AttributeError:
525 print('Note: Cannot determine output file name, ' \ 531 print('Note: Cannot determine output file name, '
526 'using current directory as base for the CSS file name', 532 'using current directory as base for the CSS file name',
527 file=sys.stderr) 533 file=sys.stderr)
528 cssfilename = self.cssfile 534 cssfilename = self.cssfile
529 # write CSS file only if noclobber_cssfile isn't given as an option. 535 # write CSS file only if noclobber_cssfile isn't given as an option.
530 try: 536 try:
531 if not os.path.exists(cssfilename) or not self.noclobber_cssfile: 537 if not os.path.exists(cssfilename) or not self.noclobber_cssfile:
532 cf = open(cssfilename, "w") 538 cf = open(cssfilename, "w")
533 cf.write(CSSFILE_TEMPLATE % 539 cf.write(CSSFILE_TEMPLATE %
534 {'styledefs': self.get_style_defs('body')}) 540 {'styledefs': self.get_style_defs('body')})
535 cf.close() 541 cf.close()
536 except IOError as err: 542 except IOError as err:
537 err.strerror = 'Error writing CSS file: ' + err.strerror 543 err.strerror = 'Error writing CSS file: ' + err.strerror
538 raise 544 raise
539 545
540 yield 0, (DOC_HEADER_EXTERNALCSS % 546 yield 0, (DOC_HEADER_EXTERNALCSS %
541 dict(title = self.title, 547 dict(title=self.title,
542 cssfile = self.cssfile, 548 cssfile=self.cssfile,
543 encoding = self.encoding)) 549 encoding=self.encoding))
544 else: 550 else:
545 yield 0, (DOC_HEADER % 551 yield 0, (DOC_HEADER %
546 dict(title = self.title, 552 dict(title=self.title,
547 styledefs = self.get_style_defs('body'), 553 styledefs=self.get_style_defs('body'),
548 encoding = self.encoding)) 554 encoding=self.encoding))
549 555
550 for t, line in inner: 556 for t, line in inner:
551 yield t, line 557 yield t, line
552 yield 0, DOC_FOOTER 558 yield 0, DOC_FOOTER
553 559
622 mw = len(str(len(lines) + num - 1)) 628 mw = len(str(len(lines) + num - 1))
623 629
624 if self.noclasses: 630 if self.noclasses:
625 if sp: 631 if sp:
626 for t, line in lines: 632 for t, line in lines:
627 if num%sp == 0: 633 if num % sp == 0:
628 style = 'background-color: #ffffc0; padding: 0 5px 0 5px' 634 style = 'background-color: #ffffc0; padding: 0 5px 0 5px'
629 else: 635 else:
630 style = 'background-color: #f0f0f0; padding: 0 5px 0 5px' 636 style = 'background-color: #f0f0f0; padding: 0 5px 0 5px'
631 yield 1, '<span style="%s">%*s </span>' % ( 637 yield 1, '<span style="%s">%*s </span>' % (
632 style, mw, (num%st and ' ' or num)) + line 638 style, mw, (num % st and ' ' or num)) + line
633 num += 1 639 num += 1
634 else: 640 else:
635 for t, line in lines: 641 for t, line in lines:
636 yield 1, ('<span style="background-color: #f0f0f0; ' 642 yield 1, ('<span style="background-color: #f0f0f0; '
637 'padding: 0 5px 0 5px">%*s </span>' % ( 643 'padding: 0 5px 0 5px">%*s </span>' % (
638 mw, (num%st and ' ' or num)) + line) 644 mw, (num % st and ' ' or num)) + line)
639 num += 1 645 num += 1
640 elif sp: 646 elif sp:
641 for t, line in lines: 647 for t, line in lines:
642 yield 1, '<span class="lineno%s">%*s </span>' % ( 648 yield 1, '<span class="lineno%s">%*s </span>' % (
643 num%sp == 0 and ' special' or '', mw, 649 num % sp == 0 and ' special' or '', mw,
644 (num%st and ' ' or num)) + line 650 (num % st and ' ' or num)) + line
645 num += 1 651 num += 1
646 else: 652 else:
647 for t, line in lines: 653 for t, line in lines:
648 yield 1, '<span class="lineno">%*s </span>' % ( 654 yield 1, '<span class="lineno">%*s </span>' % (
649 mw, (num%st and ' ' or num)) + line 655 mw, (num % st and ' ' or num)) + line
650 num += 1 656 num += 1
651 657
652 def _wrap_lineanchors(self, inner): 658 def _wrap_lineanchors(self, inner):
653 s = self.lineanchors 659 s = self.lineanchors
654 i = self.linenostart - 1 # subtract 1 since we have to increment i 660 # subtract 1 since we have to increment i *before* yielding
655 # *before* yielding 661 i = self.linenostart - 1
656 for t, line in inner: 662 for t, line in inner:
657 if t: 663 if t:
658 i += 1 664 i += 1
659 yield 1, '<a name="%s-%d"></a>' % (s, i) + line 665 yield 1, '<a name="%s-%d"></a>' % (s, i) + line
660 else: 666 else:
671 yield 0, line 677 yield 0, line
672 678
673 def _wrap_div(self, inner): 679 def _wrap_div(self, inner):
674 style = [] 680 style = []
675 if (self.noclasses and not self.nobackground and 681 if (self.noclasses and not self.nobackground and
676 self.style.background_color is not None): 682 self.style.background_color is not None):
677 style.append('background: %s' % (self.style.background_color,)) 683 style.append('background: %s' % (self.style.background_color,))
678 if self.cssstyles: 684 if self.cssstyles:
679 style.append(self.cssstyles) 685 style.append(self.cssstyles)
680 style = '; '.join(style) 686 style = '; '.join(style)
681 687
682 yield 0, ('<div' + (self.cssclass and ' class="%s"' % self.cssclass) 688 yield 0, ('<div' + (self.cssclass and ' class="%s"' % self.cssclass) +
683 + (style and (' style="%s"' % style)) + '>') 689 (style and (' style="%s"' % style)) + '>')
684 for tup in inner: 690 for tup in inner:
685 yield tup 691 yield tup
686 yield 0, '</div>\n' 692 yield 0, '</div>\n'
687 693
688 def _wrap_pre(self, inner): 694 def _wrap_pre(self, inner):
691 style.append(self.prestyles) 697 style.append(self.prestyles)
692 if self.noclasses: 698 if self.noclasses:
693 style.append('line-height: 125%') 699 style.append('line-height: 125%')
694 style = '; '.join(style) 700 style = '; '.join(style)
695 701
702 if self.filename:
703 yield 0, ('<span class="filename">' + self.filename + '</span>')
704
696 yield 0, ('<pre' + (style and ' style="%s"' % style) + '>') 705 yield 0, ('<pre' + (style and ' style="%s"' % style) + '>')
697 for tup in inner: 706 for tup in inner:
698 yield tup 707 yield tup
699 yield 0, '</pre>' 708 yield 0, '</pre>'
700 709
710 c2s = self.class2style 719 c2s = self.class2style
711 escape_table = _escape_html_table 720 escape_table = _escape_html_table
712 tagsfile = self.tagsfile 721 tagsfile = self.tagsfile
713 722
714 lspan = '' 723 lspan = ''
715 line = '' 724 line = []
716 for ttype, value in tokensource: 725 for ttype, value in tokensource:
717 if nocls: 726 if nocls:
718 cclass = getcls(ttype) 727 cclass = getcls(ttype)
719 while cclass is None: 728 while cclass is None:
720 ttype = ttype.parent 729 ttype = ttype.parent
721 cclass = getcls(ttype) 730 cclass = getcls(ttype)
722 cspan = cclass and '<span style="%s">' % c2s[cclass][0] or '' 731 cspan = cclass and '<span style="%s">' % c2s[cclass][0] or ''
723 else: 732 else:
724 cls = self._get_css_class(ttype) 733 cls = self._get_css_classes(ttype)
725 cspan = cls and '<span class="%s">' % cls or '' 734 cspan = cls and '<span class="%s">' % cls or ''
726 735
727 parts = value.translate(escape_table).split('\n') 736 parts = value.translate(escape_table).split('\n')
728 737
729 if tagsfile and ttype in Token.Name: 738 if tagsfile and ttype in Token.Name:
741 750
742 # for all but the last line 751 # for all but the last line
743 for part in parts[:-1]: 752 for part in parts[:-1]:
744 if line: 753 if line:
745 if lspan != cspan: 754 if lspan != cspan:
746 line += (lspan and '</span>') + cspan + part + \ 755 line.extend(((lspan and '</span>'), cspan, part,
747 (cspan and '</span>') + lsep 756 (cspan and '</span>'), lsep))
748 else: # both are the same 757 else: # both are the same
749 line += part + (lspan and '</span>') + lsep 758 line.extend((part, (lspan and '</span>'), lsep))
750 yield 1, line 759 yield 1, ''.join(line)
751 line = '' 760 line = []
752 elif part: 761 elif part:
753 yield 1, cspan + part + (cspan and '</span>') + lsep 762 yield 1, ''.join((cspan, part, (cspan and '</span>'), lsep))
754 else: 763 else:
755 yield 1, lsep 764 yield 1, lsep
756 # for the last line 765 # for the last line
757 if line and parts[-1]: 766 if line and parts[-1]:
758 if lspan != cspan: 767 if lspan != cspan:
759 line += (lspan and '</span>') + cspan + parts[-1] 768 line.extend(((lspan and '</span>'), cspan, parts[-1]))
760 lspan = cspan 769 lspan = cspan
761 else: 770 else:
762 line += parts[-1] 771 line.append(parts[-1])
763 elif parts[-1]: 772 elif parts[-1]:
764 line = cspan + parts[-1] 773 line = [cspan, parts[-1]]
765 lspan = cspan 774 lspan = cspan
766 # else we neither have to open a new span nor set lspan 775 # else we neither have to open a new span nor set lspan
767 776
768 if line: 777 if line:
769 yield 1, line + (lspan and '</span>') + lsep 778 line.extend(((lspan and '</span>'), lsep))
779 yield 1, ''.join(line)
770 780
771 def _lookup_ctag(self, token): 781 def _lookup_ctag(self, token):
772 entry = ctags.TagEntry() 782 entry = ctags.TagEntry()
773 if self._ctags.find(entry, token, 0): 783 if self._ctags.find(entry, token, 0):
774 return entry['file'], entry['lineNumber'] 784 return entry['file'], entry['lineNumber']
783 hls = self.hl_lines 793 hls = self.hl_lines
784 794
785 for i, (t, value) in enumerate(tokensource): 795 for i, (t, value) in enumerate(tokensource):
786 if t != 1: 796 if t != 1:
787 yield t, value 797 yield t, value
788 if i + 1 in hls: # i + 1 because Python indexes start at 0 798 if i + 1 in hls: # i + 1 because Python indexes start at 0
789 if self.noclasses: 799 if self.noclasses:
790 style = '' 800 style = ''
791 if self.style.highlight_color is not None: 801 if self.style.highlight_color is not None:
792 style = (' style="background-color: %s"' % 802 style = (' style="background-color: %s"' %
793 (self.style.highlight_color,)) 803 (self.style.highlight_color,))

eric ide

mercurial