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-2020 by the Pygments team, see AUTHORS. |
8 :copyright: Copyright 2006-2021 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 functools |
12 import os |
13 import os |
13 import sys |
14 import sys |
14 import os.path |
15 import os.path |
15 from io import StringIO |
16 from io import StringIO |
16 |
17 |
60 |
61 |
61 |
62 |
62 CSSFILE_TEMPLATE = '''\ |
63 CSSFILE_TEMPLATE = '''\ |
63 /* |
64 /* |
64 generated by Pygments <https://pygments.org/> |
65 generated by Pygments <https://pygments.org/> |
65 Copyright 2006-2020 by the Pygments team. |
66 Copyright 2006-2021 by the Pygments team. |
66 Licensed under the BSD license, see LICENSE for details. |
67 Licensed under the BSD license, see LICENSE for details. |
67 */ |
68 */ |
68 %(styledefs)s |
69 %(styledefs)s |
69 ''' |
70 ''' |
70 |
71 |
71 DOC_HEADER = '''\ |
72 DOC_HEADER = '''\ |
72 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" |
73 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" |
73 "http://www.w3.org/TR/html4/strict.dtd"> |
74 "http://www.w3.org/TR/html4/strict.dtd"> |
74 <!-- |
75 <!-- |
75 generated by Pygments <https://pygments.org/> |
76 generated by Pygments <https://pygments.org/> |
76 Copyright 2006-2020 by the Pygments team. |
77 Copyright 2006-2021 by the Pygments team. |
77 Licensed under the BSD license, see LICENSE for details. |
78 Licensed under the BSD license, see LICENSE for details. |
78 --> |
79 --> |
79 <html> |
80 <html> |
80 <head> |
81 <head> |
81 <title>%(title)s</title> |
82 <title>%(title)s</title> |
412 self.noclobber_cssfile = get_bool_opt(options, 'noclobber_cssfile', False) |
413 self.noclobber_cssfile = get_bool_opt(options, 'noclobber_cssfile', False) |
413 self.tagsfile = self._decodeifneeded(options.get('tagsfile', '')) |
414 self.tagsfile = self._decodeifneeded(options.get('tagsfile', '')) |
414 self.tagurlformat = self._decodeifneeded(options.get('tagurlformat', '')) |
415 self.tagurlformat = self._decodeifneeded(options.get('tagurlformat', '')) |
415 self.filename = self._decodeifneeded(options.get('filename', '')) |
416 self.filename = self._decodeifneeded(options.get('filename', '')) |
416 self.wrapcode = get_bool_opt(options, 'wrapcode', False) |
417 self.wrapcode = get_bool_opt(options, 'wrapcode', False) |
|
418 self.span_element_openers = {} |
417 |
419 |
418 if self.tagsfile: |
420 if self.tagsfile: |
419 if not ctags: |
421 if not ctags: |
420 raise RuntimeError('The "ctags" package must to be installed ' |
422 raise RuntimeError('The "ctags" package must to be installed ' |
421 'to be able to use the "tagsfile" feature.') |
423 'to be able to use the "tagsfile" feature.') |
453 if ttypeclass: |
455 if ttypeclass: |
454 return self.classprefix + ttypeclass |
456 return self.classprefix + ttypeclass |
455 return '' |
457 return '' |
456 |
458 |
457 def _get_css_classes(self, ttype): |
459 def _get_css_classes(self, ttype): |
458 """Return the css classes of this token type prefixed with |
460 """Return the CSS classes of this token type prefixed with the classprefix option.""" |
459 the classprefix option.""" |
|
460 cls = self._get_css_class(ttype) |
461 cls = self._get_css_class(ttype) |
461 while ttype not in STANDARD_TYPES: |
462 while ttype not in STANDARD_TYPES: |
462 ttype = ttype.parent |
463 ttype = ttype.parent |
463 cls = self._get_css_class(ttype) + ' ' + cls |
464 cls = self._get_css_class(ttype) + ' ' + cls |
464 return cls |
465 return cls or '' |
|
466 |
|
467 def _get_css_inline_styles(self, ttype): |
|
468 """Return the inline CSS styles for this token type.""" |
|
469 cclass = self.ttype2class.get(ttype) |
|
470 while cclass is None: |
|
471 ttype = ttype.parent |
|
472 cclass = self.ttype2class.get(ttype) |
|
473 return cclass or '' |
465 |
474 |
466 def _create_stylesheet(self): |
475 def _create_stylesheet(self): |
467 t2c = self.ttype2class = {Token: ''} |
476 t2c = self.ttype2class = {Token: ''} |
468 c2s = self.class2style = {} |
477 c2s = self.class2style = {} |
469 for ttype, ndef in self.style: |
478 for ttype, ndef in self.style: |
570 |
579 |
571 return prefix |
580 return prefix |
572 |
581 |
573 @property |
582 @property |
574 def _pre_style(self): |
583 def _pre_style(self): |
575 return 'line-height: 125%; margin: 0;' |
584 return 'line-height: 125%;' |
576 |
585 |
577 @property |
586 @property |
578 def _linenos_style(self): |
587 def _linenos_style(self): |
579 return 'color: %s; background-color: %s; padding: 0 5px 0 5px;' % ( |
588 return 'color: %s; background-color: %s; padding-left: 5px; padding-right: 5px;' % ( |
580 self.style.line_number_color, |
589 self.style.line_number_color, |
581 self.style.line_number_background_color |
590 self.style.line_number_background_color |
582 ) |
591 ) |
583 |
592 |
584 @property |
593 @property |
585 def _linenos_special_style(self): |
594 def _linenos_special_style(self): |
586 return 'color: %s; background-color: %s; padding: 0 5px 0 5px;' % ( |
595 return 'color: %s; background-color: %s; padding-left: 5px; padding-right: 5px;' % ( |
587 self.style.line_number_special_color, |
596 self.style.line_number_special_color, |
588 self.style.line_number_special_background_color |
597 self.style.line_number_special_background_color |
589 ) |
598 ) |
590 |
599 |
591 def _decodeifneeded(self, value): |
600 def _decodeifneeded(self, value): |
784 def _wrap_code(self, inner): |
793 def _wrap_code(self, inner): |
785 yield 0, '<code>' |
794 yield 0, '<code>' |
786 yield from inner |
795 yield from inner |
787 yield 0, '</code>' |
796 yield 0, '</code>' |
788 |
797 |
|
798 @functools.lru_cache(maxsize=100) |
|
799 def _translate_parts(self, value): |
|
800 """HTML-escape a value and split it by newlines.""" |
|
801 return value.translate(_escape_html_table).split('\n') |
|
802 |
789 def _format_lines(self, tokensource): |
803 def _format_lines(self, tokensource): |
790 """ |
804 """ |
791 Just format the tokens, without any wrapping tags. |
805 Just format the tokens, without any wrapping tags. |
792 Yield individual lines. |
806 Yield individual lines. |
793 """ |
807 """ |
794 nocls = self.noclasses |
808 nocls = self.noclasses |
795 lsep = self.lineseparator |
809 lsep = self.lineseparator |
796 # for <span style=""> lookup only |
|
797 getcls = self.ttype2class.get |
|
798 c2s = self.class2style |
|
799 escape_table = _escape_html_table |
|
800 tagsfile = self.tagsfile |
810 tagsfile = self.tagsfile |
801 |
811 |
802 lspan = '' |
812 lspan = '' |
803 line = [] |
813 line = [] |
804 for ttype, value in tokensource: |
814 for ttype, value in tokensource: |
805 if nocls: |
815 try: |
806 cclass = getcls(ttype) |
816 cspan = self.span_element_openers[ttype] |
807 while cclass is None: |
817 except KeyError: |
808 ttype = ttype.parent |
818 if nocls: |
809 cclass = getcls(ttype) |
819 css_style = self._get_css_inline_styles(ttype) |
810 cspan = cclass and '<span style="%s">' % c2s[cclass][0] or '' |
820 cspan = css_style and '<span style="%s">' % self.class2style[css_style][0] or '' |
811 else: |
821 else: |
812 cls = self._get_css_classes(ttype) |
822 css_class = self._get_css_classes(ttype) |
813 cspan = cls and '<span class="%s">' % cls or '' |
823 cspan = css_class and '<span class="%s">' % css_class or '' |
814 |
824 self.span_element_openers[ttype] = cspan |
815 parts = value.translate(escape_table).split('\n') |
825 |
|
826 parts = self._translate_parts(value) |
816 |
827 |
817 if tagsfile and ttype in Token.Name: |
828 if tagsfile and ttype in Token.Name: |
818 filename, linenumber = self._lookup_ctag(value) |
829 filename, linenumber = self._lookup_ctag(value) |
819 if linenumber: |
830 if linenumber: |
820 base, filename = os.path.split(filename) |
831 base, filename = os.path.split(filename) |