Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pygments/formatters/html.py: 14%
412 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:07 +0000
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:07 +0000
1"""
2 pygments.formatters.html
3 ~~~~~~~~~~~~~~~~~~~~~~~~
5 Formatter for HTML output.
7 :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
8 :license: BSD, see LICENSE for details.
9"""
11import functools
12import os
13import sys
14import os.path
15from io import StringIO
17from pygments.formatter import Formatter
18from pygments.token import Token, Text, STANDARD_TYPES
19from pygments.util import get_bool_opt, get_int_opt, get_list_opt
21try:
22 import ctags
23except ImportError:
24 ctags = None
26__all__ = ['HtmlFormatter']
29_escape_html_table = {
30 ord('&'): '&',
31 ord('<'): '<',
32 ord('>'): '>',
33 ord('"'): '"',
34 ord("'"): ''',
35}
38def escape_html(text, table=_escape_html_table):
39 """Escape &, <, > as well as single and double quotes for HTML."""
40 return text.translate(table)
43def webify(color):
44 if color.startswith('calc') or color.startswith('var'):
45 return color
46 else:
47 return '#' + color
50def _get_ttype_class(ttype):
51 fname = STANDARD_TYPES.get(ttype)
52 if fname:
53 return fname
54 aname = ''
55 while fname is None:
56 aname = '-' + ttype[-1] + aname
57 ttype = ttype.parent
58 fname = STANDARD_TYPES.get(ttype)
59 return fname + aname
62CSSFILE_TEMPLATE = '''\
63/*
64generated by Pygments <https://pygments.org/>
65Copyright 2006-2022 by the Pygments team.
66Licensed under the BSD license, see LICENSE for details.
67*/
68%(styledefs)s
69'''
71DOC_HEADER = '''\
72<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
73 "http://www.w3.org/TR/html4/strict.dtd">
74<!--
75generated by Pygments <https://pygments.org/>
76Copyright 2006-2022 by the Pygments team.
77Licensed under the BSD license, see LICENSE for details.
78-->
79<html>
80<head>
81 <title>%(title)s</title>
82 <meta http-equiv="content-type" content="text/html; charset=%(encoding)s">
83 <style type="text/css">
84''' + CSSFILE_TEMPLATE + '''
85 </style>
86</head>
87<body>
88<h2>%(title)s</h2>
90'''
92DOC_HEADER_EXTERNALCSS = '''\
93<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
94 "http://www.w3.org/TR/html4/strict.dtd">
96<html>
97<head>
98 <title>%(title)s</title>
99 <meta http-equiv="content-type" content="text/html; charset=%(encoding)s">
100 <link rel="stylesheet" href="%(cssfile)s" type="text/css">
101</head>
102<body>
103<h2>%(title)s</h2>
105'''
107DOC_FOOTER = '''\
108</body>
109</html>
110'''
113class HtmlFormatter(Formatter):
114 r"""
115 Format tokens as HTML 4 ``<span>`` tags within a ``<pre>`` tag, wrapped
116 in a ``<div>`` tag. The ``<div>``'s CSS class can be set by the `cssclass`
117 option.
119 If the `linenos` option is set to ``"table"``, the ``<pre>`` is
120 additionally wrapped inside a ``<table>`` which has one row and two
121 cells: one containing the line numbers and one containing the code.
122 Example:
124 .. sourcecode:: html
126 <div class="highlight" >
127 <table><tr>
128 <td class="linenos" title="click to toggle"
129 onclick="with (this.firstChild.style)
130 { display = (display == '') ? 'none' : '' }">
131 <pre>1
132 2</pre>
133 </td>
134 <td class="code">
135 <pre><span class="Ke">def </span><span class="NaFu">foo</span>(bar):
136 <span class="Ke">pass</span>
137 </pre>
138 </td>
139 </tr></table></div>
141 (whitespace added to improve clarity).
143 Wrapping can be disabled using the `nowrap` option.
145 A list of lines can be specified using the `hl_lines` option to make these
146 lines highlighted (as of Pygments 0.11).
148 With the `full` option, a complete HTML 4 document is output, including
149 the style definitions inside a ``<style>`` tag, or in a separate file if
150 the `cssfile` option is given.
152 When `tagsfile` is set to the path of a ctags index file, it is used to
153 generate hyperlinks from names to their definition. You must enable
154 `lineanchors` and run ctags with the `-n` option for this to work. The
155 `python-ctags` module from PyPI must be installed to use this feature;
156 otherwise a `RuntimeError` will be raised.
158 The `get_style_defs(arg='')` method of a `HtmlFormatter` returns a string
159 containing CSS rules for the CSS classes used by the formatter. The
160 argument `arg` can be used to specify additional CSS selectors that
161 are prepended to the classes. A call `fmter.get_style_defs('td .code')`
162 would result in the following CSS classes:
164 .. sourcecode:: css
166 td .code .kw { font-weight: bold; color: #00FF00 }
167 td .code .cm { color: #999999 }
168 ...
170 If you have Pygments 0.6 or higher, you can also pass a list or tuple to the
171 `get_style_defs()` method to request multiple prefixes for the tokens:
173 .. sourcecode:: python
175 formatter.get_style_defs(['div.syntax pre', 'pre.syntax'])
177 The output would then look like this:
179 .. sourcecode:: css
181 div.syntax pre .kw,
182 pre.syntax .kw { font-weight: bold; color: #00FF00 }
183 div.syntax pre .cm,
184 pre.syntax .cm { color: #999999 }
185 ...
187 Additional options accepted:
189 `nowrap`
190 If set to ``True``, don't wrap the tokens at all, not even inside a ``<pre>``
191 tag. This disables most other options (default: ``False``).
193 `full`
194 Tells the formatter to output a "full" document, i.e. a complete
195 self-contained document (default: ``False``).
197 `title`
198 If `full` is true, the title that should be used to caption the
199 document (default: ``''``).
201 `style`
202 The style to use, can be a string or a Style subclass (default:
203 ``'default'``). This option has no effect if the `cssfile`
204 and `noclobber_cssfile` option are given and the file specified in
205 `cssfile` exists.
207 `noclasses`
208 If set to true, token ``<span>`` tags (as well as line number elements)
209 will not use CSS classes, but inline styles. This is not recommended
210 for larger pieces of code since it increases output size by quite a bit
211 (default: ``False``).
213 `classprefix`
214 Since the token types use relatively short class names, they may clash
215 with some of your own class names. In this case you can use the
216 `classprefix` option to give a string to prepend to all Pygments-generated
217 CSS class names for token types.
218 Note that this option also affects the output of `get_style_defs()`.
220 `cssclass`
221 CSS class for the wrapping ``<div>`` tag (default: ``'highlight'``).
222 If you set this option, the default selector for `get_style_defs()`
223 will be this class.
225 .. versionadded:: 0.9
226 If you select the ``'table'`` line numbers, the wrapping table will
227 have a CSS class of this string plus ``'table'``, the default is
228 accordingly ``'highlighttable'``.
230 `cssstyles`
231 Inline CSS styles for the wrapping ``<div>`` tag (default: ``''``).
233 `prestyles`
234 Inline CSS styles for the ``<pre>`` tag (default: ``''``).
236 .. versionadded:: 0.11
238 `cssfile`
239 If the `full` option is true and this option is given, it must be the
240 name of an external file. If the filename does not include an absolute
241 path, the file's path will be assumed to be relative to the main output
242 file's path, if the latter can be found. The stylesheet is then written
243 to this file instead of the HTML file.
245 .. versionadded:: 0.6
247 `noclobber_cssfile`
248 If `cssfile` is given and the specified file exists, the css file will
249 not be overwritten. This allows the use of the `full` option in
250 combination with a user specified css file. Default is ``False``.
252 .. versionadded:: 1.1
254 `linenos`
255 If set to ``'table'``, output line numbers as a table with two cells,
256 one containing the line numbers, the other the whole code. This is
257 copy-and-paste-friendly, but may cause alignment problems with some
258 browsers or fonts. If set to ``'inline'``, the line numbers will be
259 integrated in the ``<pre>`` tag that contains the code (that setting
260 is *new in Pygments 0.8*).
262 For compatibility with Pygments 0.7 and earlier, every true value
263 except ``'inline'`` means the same as ``'table'`` (in particular, that
264 means also ``True``).
266 The default value is ``False``, which means no line numbers at all.
268 **Note:** with the default ("table") line number mechanism, the line
269 numbers and code can have different line heights in Internet Explorer
270 unless you give the enclosing ``<pre>`` tags an explicit ``line-height``
271 CSS property (you get the default line spacing with ``line-height:
272 125%``).
274 `hl_lines`
275 Specify a list of lines to be highlighted. The line numbers are always
276 relative to the input (i.e. the first line is line 1) and are
277 independent of `linenostart`.
279 .. versionadded:: 0.11
281 `linenostart`
282 The line number for the first line (default: ``1``).
284 `linenostep`
285 If set to a number n > 1, only every nth line number is printed.
287 `linenospecial`
288 If set to a number n > 0, every nth line number is given the CSS
289 class ``"special"`` (default: ``0``).
291 `nobackground`
292 If set to ``True``, the formatter won't output the background color
293 for the wrapping element (this automatically defaults to ``False``
294 when there is no wrapping element [eg: no argument for the
295 `get_syntax_defs` method given]) (default: ``False``).
297 .. versionadded:: 0.6
299 `lineseparator`
300 This string is output between lines of code. It defaults to ``"\n"``,
301 which is enough to break a line inside ``<pre>`` tags, but you can
302 e.g. set it to ``"<br>"`` to get HTML line breaks.
304 .. versionadded:: 0.7
306 `lineanchors`
307 If set to a nonempty string, e.g. ``foo``, the formatter will wrap each
308 output line in an anchor tag with an ``id`` (and `name`) of ``foo-linenumber``.
309 This allows easy linking to certain lines.
311 .. versionadded:: 0.9
313 `linespans`
314 If set to a nonempty string, e.g. ``foo``, the formatter will wrap each
315 output line in a span tag with an ``id`` of ``foo-linenumber``.
316 This allows easy access to lines via javascript.
318 .. versionadded:: 1.6
320 `anchorlinenos`
321 If set to `True`, will wrap line numbers in <a> tags. Used in
322 combination with `linenos` and `lineanchors`.
324 `tagsfile`
325 If set to the path of a ctags file, wrap names in anchor tags that
326 link to their definitions. `lineanchors` should be used, and the
327 tags file should specify line numbers (see the `-n` option to ctags).
329 .. versionadded:: 1.6
331 `tagurlformat`
332 A string formatting pattern used to generate links to ctags definitions.
333 Available variables are `%(path)s`, `%(fname)s` and `%(fext)s`.
334 Defaults to an empty string, resulting in just `#prefix-number` links.
336 .. versionadded:: 1.6
338 `filename`
339 A string used to generate a filename when rendering ``<pre>`` blocks,
340 for example if displaying source code. If `linenos` is set to
341 ``'table'`` then the filename will be rendered in an initial row
342 containing a single `<th>` which spans both columns.
344 .. versionadded:: 2.1
346 `wrapcode`
347 Wrap the code inside ``<pre>`` blocks using ``<code>``, as recommended
348 by the HTML5 specification.
350 .. versionadded:: 2.4
352 `debug_token_types`
353 Add ``title`` attributes to all token ``<span>`` tags that show the
354 name of the token.
356 .. versionadded:: 2.10
359 **Subclassing the HTML formatter**
361 .. versionadded:: 0.7
363 The HTML formatter is now built in a way that allows easy subclassing, thus
364 customizing the output HTML code. The `format()` method calls
365 `self._format_lines()` which returns a generator that yields tuples of ``(1,
366 line)``, where the ``1`` indicates that the ``line`` is a line of the
367 formatted source code.
369 If the `nowrap` option is set, the generator is the iterated over and the
370 resulting HTML is output.
372 Otherwise, `format()` calls `self.wrap()`, which wraps the generator with
373 other generators. These may add some HTML code to the one generated by
374 `_format_lines()`, either by modifying the lines generated by the latter,
375 then yielding them again with ``(1, line)``, and/or by yielding other HTML
376 code before or after the lines, with ``(0, html)``. The distinction between
377 source lines and other code makes it possible to wrap the generator multiple
378 times.
380 The default `wrap()` implementation adds a ``<div>`` and a ``<pre>`` tag.
382 A custom `HtmlFormatter` subclass could look like this:
384 .. sourcecode:: python
386 class CodeHtmlFormatter(HtmlFormatter):
388 def wrap(self, source, *, include_div):
389 return self._wrap_code(source)
391 def _wrap_code(self, source):
392 yield 0, '<code>'
393 for i, t in source:
394 if i == 1:
395 # it's a line of formatted code
396 t += '<br>'
397 yield i, t
398 yield 0, '</code>'
400 This results in wrapping the formatted lines with a ``<code>`` tag, where the
401 source lines are broken using ``<br>`` tags.
403 After calling `wrap()`, the `format()` method also adds the "line numbers"
404 and/or "full document" wrappers if the respective options are set. Then, all
405 HTML yielded by the wrapped generator is output.
406 """
408 name = 'HTML'
409 aliases = ['html']
410 filenames = ['*.html', '*.htm']
412 def __init__(self, **options):
413 Formatter.__init__(self, **options)
414 self.title = self._decodeifneeded(self.title)
415 self.nowrap = get_bool_opt(options, 'nowrap', False)
416 self.noclasses = get_bool_opt(options, 'noclasses', False)
417 self.classprefix = options.get('classprefix', '')
418 self.cssclass = self._decodeifneeded(options.get('cssclass', 'highlight'))
419 self.cssstyles = self._decodeifneeded(options.get('cssstyles', ''))
420 self.prestyles = self._decodeifneeded(options.get('prestyles', ''))
421 self.cssfile = self._decodeifneeded(options.get('cssfile', ''))
422 self.noclobber_cssfile = get_bool_opt(options, 'noclobber_cssfile', False)
423 self.tagsfile = self._decodeifneeded(options.get('tagsfile', ''))
424 self.tagurlformat = self._decodeifneeded(options.get('tagurlformat', ''))
425 self.filename = self._decodeifneeded(options.get('filename', ''))
426 self.wrapcode = get_bool_opt(options, 'wrapcode', False)
427 self.span_element_openers = {}
428 self.debug_token_types = get_bool_opt(options, 'debug_token_types', False)
430 if self.tagsfile:
431 if not ctags:
432 raise RuntimeError('The "ctags" package must to be installed '
433 'to be able to use the "tagsfile" feature.')
434 self._ctags = ctags.CTags(self.tagsfile)
436 linenos = options.get('linenos', False)
437 if linenos == 'inline':
438 self.linenos = 2
439 elif linenos:
440 # compatibility with <= 0.7
441 self.linenos = 1
442 else:
443 self.linenos = 0
444 self.linenostart = abs(get_int_opt(options, 'linenostart', 1))
445 self.linenostep = abs(get_int_opt(options, 'linenostep', 1))
446 self.linenospecial = abs(get_int_opt(options, 'linenospecial', 0))
447 self.nobackground = get_bool_opt(options, 'nobackground', False)
448 self.lineseparator = options.get('lineseparator', '\n')
449 self.lineanchors = options.get('lineanchors', '')
450 self.linespans = options.get('linespans', '')
451 self.anchorlinenos = get_bool_opt(options, 'anchorlinenos', False)
452 self.hl_lines = set()
453 for lineno in get_list_opt(options, 'hl_lines', []):
454 try:
455 self.hl_lines.add(int(lineno))
456 except ValueError:
457 pass
459 self._create_stylesheet()
461 def _get_css_class(self, ttype):
462 """Return the css class of this token type prefixed with
463 the classprefix option."""
464 ttypeclass = _get_ttype_class(ttype)
465 if ttypeclass:
466 return self.classprefix + ttypeclass
467 return ''
469 def _get_css_classes(self, ttype):
470 """Return the CSS classes of this token type prefixed with the classprefix option."""
471 cls = self._get_css_class(ttype)
472 while ttype not in STANDARD_TYPES:
473 ttype = ttype.parent
474 cls = self._get_css_class(ttype) + ' ' + cls
475 return cls or ''
477 def _get_css_inline_styles(self, ttype):
478 """Return the inline CSS styles for this token type."""
479 cclass = self.ttype2class.get(ttype)
480 while cclass is None:
481 ttype = ttype.parent
482 cclass = self.ttype2class.get(ttype)
483 return cclass or ''
485 def _create_stylesheet(self):
486 t2c = self.ttype2class = {Token: ''}
487 c2s = self.class2style = {}
488 for ttype, ndef in self.style:
489 name = self._get_css_class(ttype)
490 style = ''
491 if ndef['color']:
492 style += 'color: %s; ' % webify(ndef['color'])
493 if ndef['bold']:
494 style += 'font-weight: bold; '
495 if ndef['italic']:
496 style += 'font-style: italic; '
497 if ndef['underline']:
498 style += 'text-decoration: underline; '
499 if ndef['bgcolor']:
500 style += 'background-color: %s; ' % webify(ndef['bgcolor'])
501 if ndef['border']:
502 style += 'border: 1px solid %s; ' % webify(ndef['border'])
503 if style:
504 t2c[ttype] = name
505 # save len(ttype) to enable ordering the styles by
506 # hierarchy (necessary for CSS cascading rules!)
507 c2s[name] = (style[:-2], ttype, len(ttype))
509 def get_style_defs(self, arg=None):
510 """
511 Return CSS style definitions for the classes produced by the current
512 highlighting style. ``arg`` can be a string or list of selectors to
513 insert before the token type classes.
514 """
515 style_lines = []
517 style_lines.extend(self.get_linenos_style_defs())
518 style_lines.extend(self.get_background_style_defs(arg))
519 style_lines.extend(self.get_token_style_defs(arg))
521 return '\n'.join(style_lines)
523 def get_token_style_defs(self, arg=None):
524 prefix = self.get_css_prefix(arg)
526 styles = [
527 (level, ttype, cls, style)
528 for cls, (style, ttype, level) in self.class2style.items()
529 if cls and style
530 ]
531 styles.sort()
533 lines = [
534 '%s { %s } /* %s */' % (prefix(cls), style, repr(ttype)[6:])
535 for (level, ttype, cls, style) in styles
536 ]
538 return lines
540 def get_background_style_defs(self, arg=None):
541 prefix = self.get_css_prefix(arg)
542 bg_color = self.style.background_color
543 hl_color = self.style.highlight_color
545 lines = []
547 if arg and not self.nobackground and bg_color is not None:
548 text_style = ''
549 if Text in self.ttype2class:
550 text_style = ' ' + self.class2style[self.ttype2class[Text]][0]
551 lines.insert(
552 0, '%s{ background: %s;%s }' % (
553 prefix(''), bg_color, text_style
554 )
555 )
556 if hl_color is not None:
557 lines.insert(
558 0, '%s { background-color: %s }' % (prefix('hll'), hl_color)
559 )
561 return lines
563 def get_linenos_style_defs(self):
564 lines = [
565 'pre { %s }' % self._pre_style,
566 'td.linenos .normal { %s }' % self._linenos_style,
567 'span.linenos { %s }' % self._linenos_style,
568 'td.linenos .special { %s }' % self._linenos_special_style,
569 'span.linenos.special { %s }' % self._linenos_special_style,
570 ]
572 return lines
574 def get_css_prefix(self, arg):
575 if arg is None:
576 arg = ('cssclass' in self.options and '.'+self.cssclass or '')
577 if isinstance(arg, str):
578 args = [arg]
579 else:
580 args = list(arg)
582 def prefix(cls):
583 if cls:
584 cls = '.' + cls
585 tmp = []
586 for arg in args:
587 tmp.append((arg and arg + ' ' or '') + cls)
588 return ', '.join(tmp)
590 return prefix
592 @property
593 def _pre_style(self):
594 return 'line-height: 125%;'
596 @property
597 def _linenos_style(self):
598 return 'color: %s; background-color: %s; padding-left: 5px; padding-right: 5px;' % (
599 self.style.line_number_color,
600 self.style.line_number_background_color
601 )
603 @property
604 def _linenos_special_style(self):
605 return 'color: %s; background-color: %s; padding-left: 5px; padding-right: 5px;' % (
606 self.style.line_number_special_color,
607 self.style.line_number_special_background_color
608 )
610 def _decodeifneeded(self, value):
611 if isinstance(value, bytes):
612 if self.encoding:
613 return value.decode(self.encoding)
614 return value.decode()
615 return value
617 def _wrap_full(self, inner, outfile):
618 if self.cssfile:
619 if os.path.isabs(self.cssfile):
620 # it's an absolute filename
621 cssfilename = self.cssfile
622 else:
623 try:
624 filename = outfile.name
625 if not filename or filename[0] == '<':
626 # pseudo files, e.g. name == '<fdopen>'
627 raise AttributeError
628 cssfilename = os.path.join(os.path.dirname(filename),
629 self.cssfile)
630 except AttributeError:
631 print('Note: Cannot determine output file name, '
632 'using current directory as base for the CSS file name',
633 file=sys.stderr)
634 cssfilename = self.cssfile
635 # write CSS file only if noclobber_cssfile isn't given as an option.
636 try:
637 if not os.path.exists(cssfilename) or not self.noclobber_cssfile:
638 with open(cssfilename, "w") as cf:
639 cf.write(CSSFILE_TEMPLATE %
640 {'styledefs': self.get_style_defs('body')})
641 except OSError as err:
642 err.strerror = 'Error writing CSS file: ' + err.strerror
643 raise
645 yield 0, (DOC_HEADER_EXTERNALCSS %
646 dict(title=self.title,
647 cssfile=self.cssfile,
648 encoding=self.encoding))
649 else:
650 yield 0, (DOC_HEADER %
651 dict(title=self.title,
652 styledefs=self.get_style_defs('body'),
653 encoding=self.encoding))
655 yield from inner
656 yield 0, DOC_FOOTER
658 def _wrap_tablelinenos(self, inner):
659 dummyoutfile = StringIO()
660 lncount = 0
661 for t, line in inner:
662 if t:
663 lncount += 1
664 dummyoutfile.write(line)
666 fl = self.linenostart
667 mw = len(str(lncount + fl - 1))
668 sp = self.linenospecial
669 st = self.linenostep
670 anchor_name = self.lineanchors or self.linespans
671 aln = self.anchorlinenos
672 nocls = self.noclasses
674 lines = []
676 for i in range(fl, fl+lncount):
677 print_line = i % st == 0
678 special_line = sp and i % sp == 0
680 if print_line:
681 line = '%*d' % (mw, i)
682 if aln:
683 line = '<a href="#%s-%d">%s</a>' % (anchor_name, i, line)
684 else:
685 line = ' ' * mw
687 if nocls:
688 if special_line:
689 style = ' style="%s"' % self._linenos_special_style
690 else:
691 style = ' style="%s"' % self._linenos_style
692 else:
693 if special_line:
694 style = ' class="special"'
695 else:
696 style = ' class="normal"'
698 if style:
699 line = '<span%s>%s</span>' % (style, line)
701 lines.append(line)
703 ls = '\n'.join(lines)
705 # If a filename was specified, we can't put it into the code table as it
706 # would misalign the line numbers. Hence we emit a separate row for it.
707 filename_tr = ""
708 if self.filename:
709 filename_tr = (
710 '<tr><th colspan="2" class="filename">'
711 '<span class="filename">' + self.filename + '</span>'
712 '</th></tr>')
714 # in case you wonder about the seemingly redundant <div> here: since the
715 # content in the other cell also is wrapped in a div, some browsers in
716 # some configurations seem to mess up the formatting...
717 yield 0, (f'<table class="{self.cssclass}table">' + filename_tr +
718 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
719 ls + '</pre></div></td><td class="code">')
720 yield 0, '<div>'
721 yield 0, dummyoutfile.getvalue()
722 yield 0, '</div>'
723 yield 0, '</td></tr></table>'
726 def _wrap_inlinelinenos(self, inner):
727 # need a list of lines since we need the width of a single number :(
728 inner_lines = list(inner)
729 sp = self.linenospecial
730 st = self.linenostep
731 num = self.linenostart
732 mw = len(str(len(inner_lines) + num - 1))
733 anchor_name = self.lineanchors or self.linespans
734 aln = self.anchorlinenos
735 nocls = self.noclasses
737 for _, inner_line in inner_lines:
738 print_line = num % st == 0
739 special_line = sp and num % sp == 0
741 if print_line:
742 line = '%*d' % (mw, num)
743 else:
744 line = ' ' * mw
746 if nocls:
747 if special_line:
748 style = ' style="%s"' % self._linenos_special_style
749 else:
750 style = ' style="%s"' % self._linenos_style
751 else:
752 if special_line:
753 style = ' class="linenos special"'
754 else:
755 style = ' class="linenos"'
757 if style:
758 linenos = '<span%s>%s</span>' % (style, line)
759 else:
760 linenos = line
762 if aln:
763 yield 1, ('<a href="#%s-%d">%s</a>' % (anchor_name, num, linenos) +
764 inner_line)
765 else:
766 yield 1, linenos + inner_line
767 num += 1
769 def _wrap_lineanchors(self, inner):
770 s = self.lineanchors
771 # subtract 1 since we have to increment i *before* yielding
772 i = self.linenostart - 1
773 for t, line in inner:
774 if t:
775 i += 1
776 href = "" if self.linenos else ' href="#%s-%d"' % (s, i)
777 yield 1, '<a id="%s-%d" name="%s-%d"%s></a>' % (s, i, s, i, href) + line
778 else:
779 yield 0, line
781 def _wrap_linespans(self, inner):
782 s = self.linespans
783 i = self.linenostart - 1
784 for t, line in inner:
785 if t:
786 i += 1
787 yield 1, '<span id="%s-%d">%s</span>' % (s, i, line)
788 else:
789 yield 0, line
791 def _wrap_div(self, inner):
792 style = []
793 if (self.noclasses and not self.nobackground and
794 self.style.background_color is not None):
795 style.append('background: %s' % (self.style.background_color,))
796 if self.cssstyles:
797 style.append(self.cssstyles)
798 style = '; '.join(style)
800 yield 0, ('<div' + (self.cssclass and ' class="%s"' % self.cssclass) +
801 (style and (' style="%s"' % style)) + '>')
802 yield from inner
803 yield 0, '</div>\n'
805 def _wrap_pre(self, inner):
806 style = []
807 if self.prestyles:
808 style.append(self.prestyles)
809 if self.noclasses:
810 style.append(self._pre_style)
811 style = '; '.join(style)
813 if self.filename and self.linenos != 1:
814 yield 0, ('<span class="filename">' + self.filename + '</span>')
816 # the empty span here is to keep leading empty lines from being
817 # ignored by HTML parsers
818 yield 0, ('<pre' + (style and ' style="%s"' % style) + '><span></span>')
819 yield from inner
820 yield 0, '</pre>'
822 def _wrap_code(self, inner):
823 yield 0, '<code>'
824 yield from inner
825 yield 0, '</code>'
827 @functools.lru_cache(maxsize=100)
828 def _translate_parts(self, value):
829 """HTML-escape a value and split it by newlines."""
830 return value.translate(_escape_html_table).split('\n')
832 def _format_lines(self, tokensource):
833 """
834 Just format the tokens, without any wrapping tags.
835 Yield individual lines.
836 """
837 nocls = self.noclasses
838 lsep = self.lineseparator
839 tagsfile = self.tagsfile
841 lspan = ''
842 line = []
843 for ttype, value in tokensource:
844 try:
845 cspan = self.span_element_openers[ttype]
846 except KeyError:
847 title = ' title="%s"' % '.'.join(ttype) if self.debug_token_types else ''
848 if nocls:
849 css_style = self._get_css_inline_styles(ttype)
850 if css_style:
851 css_style = self.class2style[css_style][0]
852 cspan = '<span style="%s"%s>' % (css_style, title)
853 else:
854 cspan = ''
855 else:
856 css_class = self._get_css_classes(ttype)
857 if css_class:
858 cspan = '<span class="%s"%s>' % (css_class, title)
859 else:
860 cspan = ''
861 self.span_element_openers[ttype] = cspan
863 parts = self._translate_parts(value)
865 if tagsfile and ttype in Token.Name:
866 filename, linenumber = self._lookup_ctag(value)
867 if linenumber:
868 base, filename = os.path.split(filename)
869 if base:
870 base += '/'
871 filename, extension = os.path.splitext(filename)
872 url = self.tagurlformat % {'path': base, 'fname': filename,
873 'fext': extension}
874 parts[0] = "<a href=\"%s#%s-%d\">%s" % \
875 (url, self.lineanchors, linenumber, parts[0])
876 parts[-1] = parts[-1] + "</a>"
878 # for all but the last line
879 for part in parts[:-1]:
880 if line:
881 # Also check for part being non-empty, so we avoid creating
882 # empty <span> tags
883 if lspan != cspan and part:
884 line.extend(((lspan and '</span>'), cspan, part,
885 (cspan and '</span>'), lsep))
886 else: # both are the same, or the current part was empty
887 line.extend((part, (lspan and '</span>'), lsep))
888 yield 1, ''.join(line)
889 line = []
890 elif part:
891 yield 1, ''.join((cspan, part, (cspan and '</span>'), lsep))
892 else:
893 yield 1, lsep
894 # for the last line
895 if line and parts[-1]:
896 if lspan != cspan:
897 line.extend(((lspan and '</span>'), cspan, parts[-1]))
898 lspan = cspan
899 else:
900 line.append(parts[-1])
901 elif parts[-1]:
902 line = [cspan, parts[-1]]
903 lspan = cspan
904 # else we neither have to open a new span nor set lspan
906 if line:
907 line.extend(((lspan and '</span>'), lsep))
908 yield 1, ''.join(line)
910 def _lookup_ctag(self, token):
911 entry = ctags.TagEntry()
912 if self._ctags.find(entry, token.encode(), 0):
913 return entry['file'], entry['lineNumber']
914 else:
915 return None, None
917 def _highlight_lines(self, tokensource):
918 """
919 Highlighted the lines specified in the `hl_lines` option by
920 post-processing the token stream coming from `_format_lines`.
921 """
922 hls = self.hl_lines
924 for i, (t, value) in enumerate(tokensource):
925 if t != 1:
926 yield t, value
927 if i + 1 in hls: # i + 1 because Python indexes start at 0
928 if self.noclasses:
929 style = ''
930 if self.style.highlight_color is not None:
931 style = (' style="background-color: %s"' %
932 (self.style.highlight_color,))
933 yield 1, '<span%s>%s</span>' % (style, value)
934 else:
935 yield 1, '<span class="hll">%s</span>' % value
936 else:
937 yield 1, value
939 def wrap(self, source):
940 """
941 Wrap the ``source``, which is a generator yielding
942 individual lines, in custom generators. See docstring
943 for `format`. Can be overridden.
944 """
946 output = source
947 if self.wrapcode:
948 output = self._wrap_code(output)
950 output = self._wrap_pre(output)
952 return output
954 def format_unencoded(self, tokensource, outfile):
955 """
956 The formatting process uses several nested generators; which of
957 them are used is determined by the user's options.
959 Each generator should take at least one argument, ``inner``,
960 and wrap the pieces of text generated by this.
962 Always yield 2-tuples: (code, text). If "code" is 1, the text
963 is part of the original tokensource being highlighted, if it's
964 0, the text is some piece of wrapping. This makes it possible to
965 use several different wrappers that process the original source
966 linewise, e.g. line number generators.
967 """
968 source = self._format_lines(tokensource)
970 # As a special case, we wrap line numbers before line highlighting
971 # so the line numbers get wrapped in the highlighting tag.
972 if not self.nowrap and self.linenos == 2:
973 source = self._wrap_inlinelinenos(source)
975 if self.hl_lines:
976 source = self._highlight_lines(source)
978 if not self.nowrap:
979 if self.lineanchors:
980 source = self._wrap_lineanchors(source)
981 if self.linespans:
982 source = self._wrap_linespans(source)
983 source = self.wrap(source)
984 if self.linenos == 1:
985 source = self._wrap_tablelinenos(source)
986 source = self._wrap_div(source)
987 if self.full:
988 source = self._wrap_full(source, outfile)
990 for t, piece in source:
991 outfile.write(piece)