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