You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			996 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			996 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Python
		
	
"""
 | 
						|
    pygments.formatters.html
 | 
						|
    ~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
 | 
						|
    Formatter for HTML output.
 | 
						|
 | 
						|
    :copyright: Copyright 2006-2025 by the Pygments team, see AUTHORS.
 | 
						|
    :license: BSD, see LICENSE for details.
 | 
						|
"""
 | 
						|
 | 
						|
import functools
 | 
						|
import os
 | 
						|
import sys
 | 
						|
import os.path
 | 
						|
from io import StringIO
 | 
						|
 | 
						|
from pygments.formatter import Formatter
 | 
						|
from pygments.token import Token, Text, STANDARD_TYPES
 | 
						|
from pygments.util import get_bool_opt, get_int_opt, get_list_opt
 | 
						|
 | 
						|
try:
 | 
						|
    import ctags
 | 
						|
except ImportError:
 | 
						|
    ctags = None
 | 
						|
 | 
						|
__all__ = ['HtmlFormatter']
 | 
						|
 | 
						|
 | 
						|
_escape_html_table = {
 | 
						|
    ord('&'): '&',
 | 
						|
    ord('<'): '<',
 | 
						|
    ord('>'): '>',
 | 
						|
    ord('"'): '"',
 | 
						|
    ord("'"): ''',
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
def escape_html(text, table=_escape_html_table):
 | 
						|
    """Escape &, <, > as well as single and double quotes for HTML."""
 | 
						|
    return text.translate(table)
 | 
						|
 | 
						|
 | 
						|
def webify(color):
 | 
						|
    if color.startswith('calc') or color.startswith('var'):
 | 
						|
        return color
 | 
						|
    else:
 | 
						|
        # Check if the color can be shortened from 6 to 3 characters
 | 
						|
        color = color.upper()
 | 
						|
        if (len(color) == 6 and
 | 
						|
            (    color[0] == color[1]
 | 
						|
             and color[2] == color[3]
 | 
						|
             and color[4] == color[5])):
 | 
						|
            return f'#{color[0]}{color[2]}{color[4]}'
 | 
						|
        else:
 | 
						|
            return f'#{color}'
 | 
						|
 | 
						|
 | 
						|
def _get_ttype_class(ttype):
 | 
						|
    fname = STANDARD_TYPES.get(ttype)
 | 
						|
    if fname:
 | 
						|
        return fname
 | 
						|
    aname = ''
 | 
						|
    while fname is None:
 | 
						|
        aname = '-' + ttype[-1] + aname
 | 
						|
        ttype = ttype.parent
 | 
						|
        fname = STANDARD_TYPES.get(ttype)
 | 
						|
    return fname + aname
 | 
						|
 | 
						|
 | 
						|
CSSFILE_TEMPLATE = '''\
 | 
						|
/*
 | 
						|
generated by Pygments <https://pygments.org/>
 | 
						|
Copyright 2006-2025 by the Pygments team.
 | 
						|
Licensed under the BSD license, see LICENSE for details.
 | 
						|
*/
 | 
						|
%(styledefs)s
 | 
						|
'''
 | 
						|
 | 
						|
DOC_HEADER = '''\
 | 
						|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
 | 
						|
   "http://www.w3.org/TR/html4/strict.dtd">
 | 
						|
<!--
 | 
						|
generated by Pygments <https://pygments.org/>
 | 
						|
Copyright 2006-2025 by the Pygments team.
 | 
						|
Licensed under the BSD license, see LICENSE for details.
 | 
						|
-->
 | 
						|
<html>
 | 
						|
<head>
 | 
						|
  <title>%(title)s</title>
 | 
						|
  <meta http-equiv="content-type" content="text/html; charset=%(encoding)s">
 | 
						|
  <style type="text/css">
 | 
						|
''' + CSSFILE_TEMPLATE + '''
 | 
						|
  </style>
 | 
						|
</head>
 | 
						|
<body>
 | 
						|
<h2>%(title)s</h2>
 | 
						|
 | 
						|
'''
 | 
						|
 | 
						|
DOC_HEADER_EXTERNALCSS = '''\
 | 
						|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
 | 
						|
   "http://www.w3.org/TR/html4/strict.dtd">
 | 
						|
 | 
						|
<html>
 | 
						|
<head>
 | 
						|
  <title>%(title)s</title>
 | 
						|
  <meta http-equiv="content-type" content="text/html; charset=%(encoding)s">
 | 
						|
  <link rel="stylesheet" href="%(cssfile)s" type="text/css">
 | 
						|
</head>
 | 
						|
<body>
 | 
						|
<h2>%(title)s</h2>
 | 
						|
 | 
						|
'''
 | 
						|
 | 
						|
DOC_FOOTER = '''\
 | 
						|
</body>
 | 
						|
</html>
 | 
						|
'''
 | 
						|
 | 
						|
 | 
						|
class HtmlFormatter(Formatter):
 | 
						|
    r"""
 | 
						|
    Format tokens as HTML 4 ``<span>`` tags. By default, the content is enclosed
 | 
						|
    in a ``<pre>`` tag, itself wrapped in a ``<div>`` tag (but see the `nowrap` option).
 | 
						|
    The ``<div>``'s CSS class can be set by the `cssclass` option.
 | 
						|
 | 
						|
    If the `linenos` option is set to ``"table"``, the ``<pre>`` is
 | 
						|
    additionally wrapped inside a ``<table>`` which has one row and two
 | 
						|
    cells: one containing the line numbers and one containing the code.
 | 
						|
    Example:
 | 
						|
 | 
						|
    .. sourcecode:: html
 | 
						|
 | 
						|
        <div class="highlight" >
 | 
						|
        <table><tr>
 | 
						|
          <td class="linenos" title="click to toggle"
 | 
						|
            onclick="with (this.firstChild.style)
 | 
						|
                     { display = (display == '') ? 'none' : '' }">
 | 
						|
            <pre>1
 | 
						|
            2</pre>
 | 
						|
          </td>
 | 
						|
          <td class="code">
 | 
						|
            <pre><span class="Ke">def </span><span class="NaFu">foo</span>(bar):
 | 
						|
              <span class="Ke">pass</span>
 | 
						|
            </pre>
 | 
						|
          </td>
 | 
						|
        </tr></table></div>
 | 
						|
 | 
						|
    (whitespace added to improve clarity).
 | 
						|
 | 
						|
    A list of lines can be specified using the `hl_lines` option to make these
 | 
						|
    lines highlighted (as of Pygments 0.11).
 | 
						|
 | 
						|
    With the `full` option, a complete HTML 4 document is output, including
 | 
						|
    the style definitions inside a ``<style>`` tag, or in a separate file if
 | 
						|
    the `cssfile` option is given.
 | 
						|
 | 
						|
    When `tagsfile` is set to the path of a ctags index file, it is used to
 | 
						|
    generate hyperlinks from names to their definition.  You must enable
 | 
						|
    `lineanchors` and run ctags with the `-n` option for this to work.  The
 | 
						|
    `python-ctags` module from PyPI must be installed to use this feature;
 | 
						|
    otherwise a `RuntimeError` will be raised.
 | 
						|
 | 
						|
    The `get_style_defs(arg='')` method of a `HtmlFormatter` returns a string
 | 
						|
    containing CSS rules for the CSS classes used by the formatter. The
 | 
						|
    argument `arg` can be used to specify additional CSS selectors that
 | 
						|
    are prepended to the classes. A call `fmter.get_style_defs('td .code')`
 | 
						|
    would result in the following CSS classes:
 | 
						|
 | 
						|
    .. sourcecode:: css
 | 
						|
 | 
						|
        td .code .kw { font-weight: bold; color: #00FF00 }
 | 
						|
        td .code .cm { color: #999999 }
 | 
						|
        ...
 | 
						|
 | 
						|
    If you have Pygments 0.6 or higher, you can also pass a list or tuple to the
 | 
						|
    `get_style_defs()` method to request multiple prefixes for the tokens:
 | 
						|
 | 
						|
    .. sourcecode:: python
 | 
						|
 | 
						|
        formatter.get_style_defs(['div.syntax pre', 'pre.syntax'])
 | 
						|
 | 
						|
    The output would then look like this:
 | 
						|
 | 
						|
    .. sourcecode:: css
 | 
						|
 | 
						|
        div.syntax pre .kw,
 | 
						|
        pre.syntax .kw { font-weight: bold; color: #00FF00 }
 | 
						|
        div.syntax pre .cm,
 | 
						|
        pre.syntax .cm { color: #999999 }
 | 
						|
        ...
 | 
						|
 | 
						|
    Additional options accepted:
 | 
						|
 | 
						|
    `nowrap`
 | 
						|
        If set to ``True``, don't add a ``<pre>`` and a ``<div>`` tag
 | 
						|
        around the tokens. This disables most other options (default: ``False``).
 | 
						|
 | 
						|
    `full`
 | 
						|
        Tells the formatter to output a "full" document, i.e. a complete
 | 
						|
        self-contained document (default: ``False``).
 | 
						|
 | 
						|
    `title`
 | 
						|
        If `full` is true, the title that should be used to caption the
 | 
						|
        document (default: ``''``).
 | 
						|
 | 
						|
    `style`
 | 
						|
        The style to use, can be a string or a Style subclass (default:
 | 
						|
        ``'default'``). This option has no effect if the `cssfile`
 | 
						|
        and `noclobber_cssfile` option are given and the file specified in
 | 
						|
        `cssfile` exists.
 | 
						|
 | 
						|
    `noclasses`
 | 
						|
        If set to true, token ``<span>`` tags (as well as line number elements)
 | 
						|
        will not use CSS classes, but inline styles. This is not recommended
 | 
						|
        for larger pieces of code since it increases output size by quite a bit
 | 
						|
        (default: ``False``).
 | 
						|
 | 
						|
    `classprefix`
 | 
						|
        Since the token types use relatively short class names, they may clash
 | 
						|
        with some of your own class names. In this case you can use the
 | 
						|
        `classprefix` option to give a string to prepend to all Pygments-generated
 | 
						|
        CSS class names for token types.
 | 
						|
        Note that this option also affects the output of `get_style_defs()`.
 | 
						|
 | 
						|
    `cssclass`
 | 
						|
        CSS class for the wrapping ``<div>`` tag (default: ``'highlight'``).
 | 
						|
        If you set this option, the default selector for `get_style_defs()`
 | 
						|
        will be this class.
 | 
						|
 | 
						|
        .. versionadded:: 0.9
 | 
						|
           If you select the ``'table'`` line numbers, the wrapping table will
 | 
						|
           have a CSS class of this string plus ``'table'``, the default is
 | 
						|
           accordingly ``'highlighttable'``.
 | 
						|
 | 
						|
    `cssstyles`
 | 
						|
        Inline CSS styles for the wrapping ``<div>`` tag (default: ``''``).
 | 
						|
 | 
						|
    `prestyles`
 | 
						|
        Inline CSS styles for the ``<pre>`` tag (default: ``''``).
 | 
						|
 | 
						|
        .. versionadded:: 0.11
 | 
						|
 | 
						|
    `cssfile`
 | 
						|
        If the `full` option is true and this option is given, it must be the
 | 
						|
        name of an external file. If the filename does not include an absolute
 | 
						|
        path, the file's path will be assumed to be relative to the main output
 | 
						|
        file's path, if the latter can be found. The stylesheet is then written
 | 
						|
        to this file instead of the HTML file.
 | 
						|
 | 
						|
        .. versionadded:: 0.6
 | 
						|
 | 
						|
    `noclobber_cssfile`
 | 
						|
        If `cssfile` is given and the specified file exists, the css file will
 | 
						|
        not be overwritten. This allows the use of the `full` option in
 | 
						|
        combination with a user specified css file. Default is ``False``.
 | 
						|
 | 
						|
        .. versionadded:: 1.1
 | 
						|
 | 
						|
    `linenos`
 | 
						|
        If set to ``'table'``, output line numbers as a table with two cells,
 | 
						|
        one containing the line numbers, the other the whole code.  This is
 | 
						|
        copy-and-paste-friendly, but may cause alignment problems with some
 | 
						|
        browsers or fonts.  If set to ``'inline'``, the line numbers will be
 | 
						|
        integrated in the ``<pre>`` tag that contains the code (that setting
 | 
						|
        is *new in Pygments 0.8*).
 | 
						|
 | 
						|
        For compatibility with Pygments 0.7 and earlier, every true value
 | 
						|
        except ``'inline'`` means the same as ``'table'`` (in particular, that
 | 
						|
        means also ``True``).
 | 
						|
 | 
						|
        The default value is ``False``, which means no line numbers at all.
 | 
						|
 | 
						|
        **Note:** with the default ("table") line number mechanism, the line
 | 
						|
        numbers and code can have different line heights in Internet Explorer
 | 
						|
        unless you give the enclosing ``<pre>`` tags an explicit ``line-height``
 | 
						|
        CSS property (you get the default line spacing with ``line-height:
 | 
						|
        125%``).
 | 
						|
 | 
						|
    `hl_lines`
 | 
						|
        Specify a list of lines to be highlighted. The line numbers are always
 | 
						|
        relative to the input (i.e. the first line is line 1) and are
 | 
						|
        independent of `linenostart`.
 | 
						|
 | 
						|
        .. versionadded:: 0.11
 | 
						|
 | 
						|
    `linenostart`
 | 
						|
        The line number for the first line (default: ``1``).
 | 
						|
 | 
						|
    `linenostep`
 | 
						|
        If set to a number n > 1, only every nth line number is printed.
 | 
						|
 | 
						|
    `linenospecial`
 | 
						|
        If set to a number n > 0, every nth line number is given the CSS
 | 
						|
        class ``"special"`` (default: ``0``).
 | 
						|
 | 
						|
    `nobackground`
 | 
						|
        If set to ``True``, the formatter won't output the background color
 | 
						|
        for the wrapping element (this automatically defaults to ``False``
 | 
						|
        when there is no wrapping element [eg: no argument for the
 | 
						|
        `get_syntax_defs` method given]) (default: ``False``).
 | 
						|
 | 
						|
        .. versionadded:: 0.6
 | 
						|
 | 
						|
    `lineseparator`
 | 
						|
        This string is output between lines of code. It defaults to ``"\n"``,
 | 
						|
        which is enough to break a line inside ``<pre>`` tags, but you can
 | 
						|
        e.g. set it to ``"<br>"`` to get HTML line breaks.
 | 
						|
 | 
						|
        .. versionadded:: 0.7
 | 
						|
 | 
						|
    `lineanchors`
 | 
						|
        If set to a nonempty string, e.g. ``foo``, the formatter will wrap each
 | 
						|
        output line in an anchor tag with an ``id`` (and `name`) of ``foo-linenumber``.
 | 
						|
        This allows easy linking to certain lines.
 | 
						|
 | 
						|
        .. versionadded:: 0.9
 | 
						|
 | 
						|
    `linespans`
 | 
						|
        If set to a nonempty string, e.g. ``foo``, the formatter will wrap each
 | 
						|
        output line in a span tag with an ``id`` of ``foo-linenumber``.
 | 
						|
        This allows easy access to lines via javascript.
 | 
						|
 | 
						|
        .. versionadded:: 1.6
 | 
						|
 | 
						|
    `anchorlinenos`
 | 
						|
        If set to `True`, will wrap line numbers in <a> tags. Used in
 | 
						|
        combination with `linenos` and `lineanchors`.
 | 
						|
 | 
						|
    `tagsfile`
 | 
						|
        If set to the path of a ctags file, wrap names in anchor tags that
 | 
						|
        link to their definitions. `lineanchors` should be used, and the
 | 
						|
        tags file should specify line numbers (see the `-n` option to ctags).
 | 
						|
        The tags file is assumed to be encoded in UTF-8.
 | 
						|
 | 
						|
        .. versionadded:: 1.6
 | 
						|
 | 
						|
    `tagurlformat`
 | 
						|
        A string formatting pattern used to generate links to ctags definitions.
 | 
						|
        Available variables are `%(path)s`, `%(fname)s` and `%(fext)s`.
 | 
						|
        Defaults to an empty string, resulting in just `#prefix-number` links.
 | 
						|
 | 
						|
        .. versionadded:: 1.6
 | 
						|
 | 
						|
    `filename`
 | 
						|
        A string used to generate a filename when rendering ``<pre>`` blocks,
 | 
						|
        for example if displaying source code. If `linenos` is set to
 | 
						|
        ``'table'`` then the filename will be rendered in an initial row
 | 
						|
        containing a single `<th>` which spans both columns.
 | 
						|
 | 
						|
        .. versionadded:: 2.1
 | 
						|
 | 
						|
    `wrapcode`
 | 
						|
        Wrap the code inside ``<pre>`` blocks using ``<code>``, as recommended
 | 
						|
        by the HTML5 specification.
 | 
						|
 | 
						|
        .. versionadded:: 2.4
 | 
						|
 | 
						|
    `debug_token_types`
 | 
						|
        Add ``title`` attributes to all token ``<span>`` tags that show the
 | 
						|
        name of the token.
 | 
						|
 | 
						|
        .. versionadded:: 2.10
 | 
						|
 | 
						|
 | 
						|
    **Subclassing the HTML formatter**
 | 
						|
 | 
						|
    .. versionadded:: 0.7
 | 
						|
 | 
						|
    The HTML formatter is now built in a way that allows easy subclassing, thus
 | 
						|
    customizing the output HTML code. The `format()` method calls
 | 
						|
    `self._format_lines()` which returns a generator that yields tuples of ``(1,
 | 
						|
    line)``, where the ``1`` indicates that the ``line`` is a line of the
 | 
						|
    formatted source code.
 | 
						|
 | 
						|
    If the `nowrap` option is set, the generator is the iterated over and the
 | 
						|
    resulting HTML is output.
 | 
						|
 | 
						|
    Otherwise, `format()` calls `self.wrap()`, which wraps the generator with
 | 
						|
    other generators. These may add some HTML code to the one generated by
 | 
						|
    `_format_lines()`, either by modifying the lines generated by the latter,
 | 
						|
    then yielding them again with ``(1, line)``, and/or by yielding other HTML
 | 
						|
    code before or after the lines, with ``(0, html)``. The distinction between
 | 
						|
    source lines and other code makes it possible to wrap the generator multiple
 | 
						|
    times.
 | 
						|
 | 
						|
    The default `wrap()` implementation adds a ``<div>`` and a ``<pre>`` tag.
 | 
						|
 | 
						|
    A custom `HtmlFormatter` subclass could look like this:
 | 
						|
 | 
						|
    .. sourcecode:: python
 | 
						|
 | 
						|
        class CodeHtmlFormatter(HtmlFormatter):
 | 
						|
 | 
						|
            def wrap(self, source, *, include_div):
 | 
						|
                return self._wrap_code(source)
 | 
						|
 | 
						|
            def _wrap_code(self, source):
 | 
						|
                yield 0, '<code>'
 | 
						|
                for i, t in source:
 | 
						|
                    if i == 1:
 | 
						|
                        # it's a line of formatted code
 | 
						|
                        t += '<br>'
 | 
						|
                    yield i, t
 | 
						|
                yield 0, '</code>'
 | 
						|
 | 
						|
    This results in wrapping the formatted lines with a ``<code>`` tag, where the
 | 
						|
    source lines are broken using ``<br>`` tags.
 | 
						|
 | 
						|
    After calling `wrap()`, the `format()` method also adds the "line numbers"
 | 
						|
    and/or "full document" wrappers if the respective options are set. Then, all
 | 
						|
    HTML yielded by the wrapped generator is output.
 | 
						|
    """
 | 
						|
 | 
						|
    name = 'HTML'
 | 
						|
    aliases = ['html']
 | 
						|
    filenames = ['*.html', '*.htm']
 | 
						|
 | 
						|
    def __init__(self, **options):
 | 
						|
        Formatter.__init__(self, **options)
 | 
						|
        self.title = self._decodeifneeded(self.title)
 | 
						|
        self.nowrap = get_bool_opt(options, 'nowrap', False)
 | 
						|
        self.noclasses = get_bool_opt(options, 'noclasses', False)
 | 
						|
        self.classprefix = options.get('classprefix', '')
 | 
						|
        self.cssclass = self._decodeifneeded(options.get('cssclass', 'highlight'))
 | 
						|
        self.cssstyles = self._decodeifneeded(options.get('cssstyles', ''))
 | 
						|
        self.prestyles = self._decodeifneeded(options.get('prestyles', ''))
 | 
						|
        self.cssfile = self._decodeifneeded(options.get('cssfile', ''))
 | 
						|
        self.noclobber_cssfile = get_bool_opt(options, 'noclobber_cssfile', False)
 | 
						|
        self.tagsfile = self._decodeifneeded(options.get('tagsfile', ''))
 | 
						|
        self.tagurlformat = self._decodeifneeded(options.get('tagurlformat', ''))
 | 
						|
        self.filename = self._decodeifneeded(options.get('filename', ''))
 | 
						|
        self.wrapcode = get_bool_opt(options, 'wrapcode', False)
 | 
						|
        self.span_element_openers = {}
 | 
						|
        self.debug_token_types = get_bool_opt(options, 'debug_token_types', False)
 | 
						|
 | 
						|
        if self.tagsfile:
 | 
						|
            if not ctags:
 | 
						|
                raise RuntimeError('The "ctags" package must to be installed '
 | 
						|
                                   'to be able to use the "tagsfile" feature.')
 | 
						|
            self._ctags = ctags.CTags(self.tagsfile)
 | 
						|
 | 
						|
        linenos = options.get('linenos', False)
 | 
						|
        if linenos == 'inline':
 | 
						|
            self.linenos = 2
 | 
						|
        elif linenos:
 | 
						|
            # compatibility with <= 0.7
 | 
						|
            self.linenos = 1
 | 
						|
        else:
 | 
						|
            self.linenos = 0
 | 
						|
        self.linenostart = abs(get_int_opt(options, 'linenostart', 1))
 | 
						|
        self.linenostep = abs(get_int_opt(options, 'linenostep', 1))
 | 
						|
        self.linenospecial = abs(get_int_opt(options, 'linenospecial', 0))
 | 
						|
        self.nobackground = get_bool_opt(options, 'nobackground', False)
 | 
						|
        self.lineseparator = options.get('lineseparator', '\n')
 | 
						|
        self.lineanchors = options.get('lineanchors', '')
 | 
						|
        self.linespans = options.get('linespans', '')
 | 
						|
        self.anchorlinenos = get_bool_opt(options, 'anchorlinenos', False)
 | 
						|
        self.hl_lines = set()
 | 
						|
        for lineno in get_list_opt(options, 'hl_lines', []):
 | 
						|
            try:
 | 
						|
                self.hl_lines.add(int(lineno))
 | 
						|
            except ValueError:
 | 
						|
                pass
 | 
						|
 | 
						|
        self._create_stylesheet()
 | 
						|
 | 
						|
    def _get_css_class(self, ttype):
 | 
						|
        """Return the css class of this token type prefixed with
 | 
						|
        the classprefix option."""
 | 
						|
        ttypeclass = _get_ttype_class(ttype)
 | 
						|
        if ttypeclass:
 | 
						|
            return self.classprefix + ttypeclass
 | 
						|
        return ''
 | 
						|
 | 
						|
    def _get_css_classes(self, ttype):
 | 
						|
        """Return the CSS classes of this token type prefixed with the classprefix option."""
 | 
						|
        cls = self._get_css_class(ttype)
 | 
						|
        while ttype not in STANDARD_TYPES:
 | 
						|
            ttype = ttype.parent
 | 
						|
            cls = self._get_css_class(ttype) + ' ' + cls
 | 
						|
        return cls or ''
 | 
						|
 | 
						|
    def _get_css_inline_styles(self, ttype):
 | 
						|
        """Return the inline CSS styles for this token type."""
 | 
						|
        cclass = self.ttype2class.get(ttype)
 | 
						|
        while cclass is None:
 | 
						|
            ttype = ttype.parent
 | 
						|
            cclass = self.ttype2class.get(ttype)
 | 
						|
        return cclass or ''
 | 
						|
 | 
						|
    def _create_stylesheet(self):
 | 
						|
        t2c = self.ttype2class = {Token: ''}
 | 
						|
        c2s = self.class2style = {}
 | 
						|
        for ttype, ndef in self.style:
 | 
						|
            name = self._get_css_class(ttype)
 | 
						|
            style = ''
 | 
						|
            if ndef['color']:
 | 
						|
                style += 'color: {}; '.format(webify(ndef['color']))
 | 
						|
            if ndef['bold']:
 | 
						|
                style += 'font-weight: bold; '
 | 
						|
            if ndef['italic']:
 | 
						|
                style += 'font-style: italic; '
 | 
						|
            if ndef['underline']:
 | 
						|
                style += 'text-decoration: underline; '
 | 
						|
            if ndef['bgcolor']:
 | 
						|
                style += 'background-color: {}; '.format(webify(ndef['bgcolor']))
 | 
						|
            if ndef['border']:
 | 
						|
                style += 'border: 1px solid {}; '.format(webify(ndef['border']))
 | 
						|
            if style:
 | 
						|
                t2c[ttype] = name
 | 
						|
                # save len(ttype) to enable ordering the styles by
 | 
						|
                # hierarchy (necessary for CSS cascading rules!)
 | 
						|
                c2s[name] = (style[:-2], ttype, len(ttype))
 | 
						|
 | 
						|
    def get_style_defs(self, arg=None):
 | 
						|
        """
 | 
						|
        Return CSS style definitions for the classes produced by the current
 | 
						|
        highlighting style. ``arg`` can be a string or list of selectors to
 | 
						|
        insert before the token type classes.
 | 
						|
        """
 | 
						|
        style_lines = []
 | 
						|
 | 
						|
        style_lines.extend(self.get_linenos_style_defs())
 | 
						|
        style_lines.extend(self.get_background_style_defs(arg))
 | 
						|
        style_lines.extend(self.get_token_style_defs(arg))
 | 
						|
 | 
						|
        return '\n'.join(style_lines)
 | 
						|
 | 
						|
    def get_token_style_defs(self, arg=None):
 | 
						|
        prefix = self.get_css_prefix(arg)
 | 
						|
 | 
						|
        styles = [
 | 
						|
            (level, ttype, cls, style)
 | 
						|
            for cls, (style, ttype, level) in self.class2style.items()
 | 
						|
            if cls and style
 | 
						|
        ]
 | 
						|
        styles.sort()
 | 
						|
 | 
						|
        lines = [
 | 
						|
            f'{prefix(cls)} {{ {style} }} /* {repr(ttype)[6:]} */'
 | 
						|
            for (level, ttype, cls, style) in styles
 | 
						|
        ]
 | 
						|
 | 
						|
        return lines
 | 
						|
 | 
						|
    def get_background_style_defs(self, arg=None):
 | 
						|
        prefix = self.get_css_prefix(arg)
 | 
						|
        bg_color = self.style.background_color
 | 
						|
        hl_color = self.style.highlight_color
 | 
						|
 | 
						|
        lines = []
 | 
						|
 | 
						|
        if arg and not self.nobackground and bg_color is not None:
 | 
						|
            text_style = ''
 | 
						|
            if Text in self.ttype2class:
 | 
						|
                text_style = ' ' + self.class2style[self.ttype2class[Text]][0]
 | 
						|
            lines.insert(
 | 
						|
                0, '{}{{ background: {};{} }}'.format(
 | 
						|
                    prefix(''), bg_color, text_style
 | 
						|
                )
 | 
						|
            )
 | 
						|
        if hl_color is not None:
 | 
						|
            lines.insert(
 | 
						|
                0, '{} {{ background-color: {} }}'.format(prefix('hll'), hl_color)
 | 
						|
            )
 | 
						|
 | 
						|
        return lines
 | 
						|
 | 
						|
    def get_linenos_style_defs(self):
 | 
						|
        lines = [
 | 
						|
            f'pre {{ {self._pre_style} }}',
 | 
						|
            f'td.linenos .normal {{ {self._linenos_style} }}',
 | 
						|
            f'span.linenos {{ {self._linenos_style} }}',
 | 
						|
            f'td.linenos .special {{ {self._linenos_special_style} }}',
 | 
						|
            f'span.linenos.special {{ {self._linenos_special_style} }}',
 | 
						|
        ]
 | 
						|
 | 
						|
        return lines
 | 
						|
 | 
						|
    def get_css_prefix(self, arg):
 | 
						|
        if arg is None:
 | 
						|
            arg = ('cssclass' in self.options and '.'+self.cssclass or '')
 | 
						|
        if isinstance(arg, str):
 | 
						|
            args = [arg]
 | 
						|
        else:
 | 
						|
            args = list(arg)
 | 
						|
 | 
						|
        def prefix(cls):
 | 
						|
            if cls:
 | 
						|
                cls = '.' + cls
 | 
						|
            tmp = []
 | 
						|
            for arg in args:
 | 
						|
                tmp.append((arg and arg + ' ' or '') + cls)
 | 
						|
            return ', '.join(tmp)
 | 
						|
 | 
						|
        return prefix
 | 
						|
 | 
						|
    @property
 | 
						|
    def _pre_style(self):
 | 
						|
        return 'line-height: 125%;'
 | 
						|
 | 
						|
    @property
 | 
						|
    def _linenos_style(self):
 | 
						|
        color = self.style.line_number_color
 | 
						|
        background_color = self.style.line_number_background_color
 | 
						|
        return f'color: {color}; background-color: {background_color}; padding-left: 5px; padding-right: 5px;'
 | 
						|
 | 
						|
    @property
 | 
						|
    def _linenos_special_style(self):
 | 
						|
        color = self.style.line_number_special_color
 | 
						|
        background_color = self.style.line_number_special_background_color
 | 
						|
        return f'color: {color}; background-color: {background_color}; padding-left: 5px; padding-right: 5px;'
 | 
						|
 | 
						|
    def _decodeifneeded(self, value):
 | 
						|
        if isinstance(value, bytes):
 | 
						|
            if self.encoding:
 | 
						|
                return value.decode(self.encoding)
 | 
						|
            return value.decode()
 | 
						|
        return value
 | 
						|
 | 
						|
    def _wrap_full(self, inner, outfile):
 | 
						|
        if self.cssfile:
 | 
						|
            if os.path.isabs(self.cssfile):
 | 
						|
                # it's an absolute filename
 | 
						|
                cssfilename = self.cssfile
 | 
						|
            else:
 | 
						|
                try:
 | 
						|
                    filename = outfile.name
 | 
						|
                    if not filename or filename[0] == '<':
 | 
						|
                        # pseudo files, e.g. name == '<fdopen>'
 | 
						|
                        raise AttributeError
 | 
						|
                    cssfilename = os.path.join(os.path.dirname(filename),
 | 
						|
                                               self.cssfile)
 | 
						|
                except AttributeError:
 | 
						|
                    print('Note: Cannot determine output file name, '
 | 
						|
                          'using current directory as base for the CSS file name',
 | 
						|
                          file=sys.stderr)
 | 
						|
                    cssfilename = self.cssfile
 | 
						|
            # write CSS file only if noclobber_cssfile isn't given as an option.
 | 
						|
            try:
 | 
						|
                if not os.path.exists(cssfilename) or not self.noclobber_cssfile:
 | 
						|
                    with open(cssfilename, "w", encoding="utf-8") as cf:
 | 
						|
                        cf.write(CSSFILE_TEMPLATE %
 | 
						|
                                 {'styledefs': self.get_style_defs('body')})
 | 
						|
            except OSError as err:
 | 
						|
                err.strerror = 'Error writing CSS file: ' + err.strerror
 | 
						|
                raise
 | 
						|
 | 
						|
            yield 0, (DOC_HEADER_EXTERNALCSS %
 | 
						|
                      dict(title=self.title,
 | 
						|
                           cssfile=self.cssfile,
 | 
						|
                           encoding=self.encoding))
 | 
						|
        else:
 | 
						|
            yield 0, (DOC_HEADER %
 | 
						|
                      dict(title=self.title,
 | 
						|
                           styledefs=self.get_style_defs('body'),
 | 
						|
                           encoding=self.encoding))
 | 
						|
 | 
						|
        yield from inner
 | 
						|
        yield 0, DOC_FOOTER
 | 
						|
 | 
						|
    def _wrap_tablelinenos(self, inner):
 | 
						|
        dummyoutfile = StringIO()
 | 
						|
        lncount = 0
 | 
						|
        for t, line in inner:
 | 
						|
            if t:
 | 
						|
                lncount += 1
 | 
						|
            dummyoutfile.write(line)
 | 
						|
 | 
						|
        fl = self.linenostart
 | 
						|
        mw = len(str(lncount + fl - 1))
 | 
						|
        sp = self.linenospecial
 | 
						|
        st = self.linenostep
 | 
						|
        anchor_name = self.lineanchors or self.linespans
 | 
						|
        aln = self.anchorlinenos
 | 
						|
        nocls = self.noclasses
 | 
						|
 | 
						|
        lines = []
 | 
						|
 | 
						|
        for i in range(fl, fl+lncount):
 | 
						|
            print_line = i % st == 0
 | 
						|
            special_line = sp and i % sp == 0
 | 
						|
 | 
						|
            if print_line:
 | 
						|
                line = '%*d' % (mw, i)
 | 
						|
                if aln:
 | 
						|
                    line = '<a href="#%s-%d">%s</a>' % (anchor_name, i, line)
 | 
						|
            else:
 | 
						|
                line = ' ' * mw
 | 
						|
 | 
						|
            if nocls:
 | 
						|
                if special_line:
 | 
						|
                    style = f' style="{self._linenos_special_style}"'
 | 
						|
                else:
 | 
						|
                    style = f' style="{self._linenos_style}"'
 | 
						|
            else:
 | 
						|
                if special_line:
 | 
						|
                    style = ' class="special"'
 | 
						|
                else:
 | 
						|
                    style = ' class="normal"'
 | 
						|
 | 
						|
            if style:
 | 
						|
                line = f'<span{style}>{line}</span>'
 | 
						|
 | 
						|
            lines.append(line)
 | 
						|
 | 
						|
        ls = '\n'.join(lines)
 | 
						|
 | 
						|
        # If a filename was specified, we can't put it into the code table as it
 | 
						|
        # would misalign the line numbers. Hence we emit a separate row for it.
 | 
						|
        filename_tr = ""
 | 
						|
        if self.filename:
 | 
						|
            filename_tr = (
 | 
						|
                '<tr><th colspan="2" class="filename">'
 | 
						|
                '<span class="filename">' + self.filename + '</span>'
 | 
						|
                '</th></tr>')
 | 
						|
 | 
						|
        # in case you wonder about the seemingly redundant <div> here: since the
 | 
						|
        # content in the other cell also is wrapped in a div, some browsers in
 | 
						|
        # some configurations seem to mess up the formatting...
 | 
						|
        yield 0, (f'<table class="{self.cssclass}table">' + filename_tr +
 | 
						|
            '<tr><td class="linenos"><div class="linenodiv"><pre>' +
 | 
						|
            ls + '</pre></div></td><td class="code">')
 | 
						|
        yield 0, '<div>'
 | 
						|
        yield 0, dummyoutfile.getvalue()
 | 
						|
        yield 0, '</div>'
 | 
						|
        yield 0, '</td></tr></table>'
 | 
						|
 | 
						|
 | 
						|
    def _wrap_inlinelinenos(self, inner):
 | 
						|
        # need a list of lines since we need the width of a single number :(
 | 
						|
        inner_lines = list(inner)
 | 
						|
        sp = self.linenospecial
 | 
						|
        st = self.linenostep
 | 
						|
        num = self.linenostart
 | 
						|
        mw = len(str(len(inner_lines) + num - 1))
 | 
						|
        anchor_name = self.lineanchors or self.linespans
 | 
						|
        aln = self.anchorlinenos
 | 
						|
        nocls = self.noclasses
 | 
						|
 | 
						|
        for _, inner_line in inner_lines:
 | 
						|
            print_line = num % st == 0
 | 
						|
            special_line = sp and num % sp == 0
 | 
						|
 | 
						|
            if print_line:
 | 
						|
                line = '%*d' % (mw, num)
 | 
						|
            else:
 | 
						|
                line = ' ' * mw
 | 
						|
 | 
						|
            if nocls:
 | 
						|
                if special_line:
 | 
						|
                    style = f' style="{self._linenos_special_style}"'
 | 
						|
                else:
 | 
						|
                    style = f' style="{self._linenos_style}"'
 | 
						|
            else:
 | 
						|
                if special_line:
 | 
						|
                    style = ' class="linenos special"'
 | 
						|
                else:
 | 
						|
                    style = ' class="linenos"'
 | 
						|
 | 
						|
            if style:
 | 
						|
                linenos = f'<span{style}>{line}</span>'
 | 
						|
            else:
 | 
						|
                linenos = line
 | 
						|
 | 
						|
            if aln:
 | 
						|
                yield 1, ('<a href="#%s-%d">%s</a>' % (anchor_name, num, linenos) +
 | 
						|
                          inner_line)
 | 
						|
            else:
 | 
						|
                yield 1, linenos + inner_line
 | 
						|
            num += 1
 | 
						|
 | 
						|
    def _wrap_lineanchors(self, inner):
 | 
						|
        s = self.lineanchors
 | 
						|
        # subtract 1 since we have to increment i *before* yielding
 | 
						|
        i = self.linenostart - 1
 | 
						|
        for t, line in inner:
 | 
						|
            if t:
 | 
						|
                i += 1
 | 
						|
                href = "" if self.linenos else ' href="#%s-%d"' % (s, i)
 | 
						|
                yield 1, '<a id="%s-%d" name="%s-%d"%s></a>' % (s, i, s, i, href) + line
 | 
						|
            else:
 | 
						|
                yield 0, line
 | 
						|
 | 
						|
    def _wrap_linespans(self, inner):
 | 
						|
        s = self.linespans
 | 
						|
        i = self.linenostart - 1
 | 
						|
        for t, line in inner:
 | 
						|
            if t:
 | 
						|
                i += 1
 | 
						|
                yield 1, '<span id="%s-%d">%s</span>' % (s, i, line)
 | 
						|
            else:
 | 
						|
                yield 0, line
 | 
						|
 | 
						|
    def _wrap_div(self, inner):
 | 
						|
        style = []
 | 
						|
        if (self.noclasses and not self.nobackground and
 | 
						|
                self.style.background_color is not None):
 | 
						|
            style.append(f'background: {self.style.background_color}')
 | 
						|
        if self.cssstyles:
 | 
						|
            style.append(self.cssstyles)
 | 
						|
        style = '; '.join(style)
 | 
						|
 | 
						|
        yield 0, ('<div' + (self.cssclass and f' class="{self.cssclass}"') +
 | 
						|
                  (style and (f' style="{style}"')) + '>')
 | 
						|
        yield from inner
 | 
						|
        yield 0, '</div>\n'
 | 
						|
 | 
						|
    def _wrap_pre(self, inner):
 | 
						|
        style = []
 | 
						|
        if self.prestyles:
 | 
						|
            style.append(self.prestyles)
 | 
						|
        if self.noclasses:
 | 
						|
            style.append(self._pre_style)
 | 
						|
        style = '; '.join(style)
 | 
						|
 | 
						|
        if self.filename and self.linenos != 1:
 | 
						|
            yield 0, ('<span class="filename">' + self.filename + '</span>')
 | 
						|
 | 
						|
        # the empty span here is to keep leading empty lines from being
 | 
						|
        # ignored by HTML parsers
 | 
						|
        yield 0, ('<pre' + (style and f' style="{style}"') + '><span></span>')
 | 
						|
        yield from inner
 | 
						|
        yield 0, '</pre>'
 | 
						|
 | 
						|
    def _wrap_code(self, inner):
 | 
						|
        yield 0, '<code>'
 | 
						|
        yield from inner
 | 
						|
        yield 0, '</code>'
 | 
						|
 | 
						|
    @functools.lru_cache(maxsize=100)
 | 
						|
    def _translate_parts(self, value):
 | 
						|
        """HTML-escape a value and split it by newlines."""
 | 
						|
        return value.translate(_escape_html_table).split('\n')
 | 
						|
 | 
						|
    def _format_lines(self, tokensource):
 | 
						|
        """
 | 
						|
        Just format the tokens, without any wrapping tags.
 | 
						|
        Yield individual lines.
 | 
						|
        """
 | 
						|
        nocls = self.noclasses
 | 
						|
        lsep = self.lineseparator
 | 
						|
        tagsfile = self.tagsfile
 | 
						|
 | 
						|
        lspan = ''
 | 
						|
        line = []
 | 
						|
        for ttype, value in tokensource:
 | 
						|
            try:
 | 
						|
                cspan = self.span_element_openers[ttype]
 | 
						|
            except KeyError:
 | 
						|
                title = ' title="{}"'.format('.'.join(ttype)) if self.debug_token_types else ''
 | 
						|
                if nocls:
 | 
						|
                    css_style = self._get_css_inline_styles(ttype)
 | 
						|
                    if css_style:
 | 
						|
                        css_style = self.class2style[css_style][0]
 | 
						|
                        cspan = f'<span style="{css_style}"{title}>'
 | 
						|
                    else:
 | 
						|
                        cspan = ''
 | 
						|
                else:
 | 
						|
                    css_class = self._get_css_classes(ttype)
 | 
						|
                    if css_class:
 | 
						|
                        cspan = f'<span class="{css_class}"{title}>'
 | 
						|
                    else:
 | 
						|
                        cspan = ''
 | 
						|
                self.span_element_openers[ttype] = cspan
 | 
						|
 | 
						|
            parts = self._translate_parts(value)
 | 
						|
 | 
						|
            if tagsfile and ttype in Token.Name:
 | 
						|
                filename, linenumber = self._lookup_ctag(value)
 | 
						|
                if linenumber:
 | 
						|
                    base, filename = os.path.split(filename)
 | 
						|
                    if base:
 | 
						|
                        base += '/'
 | 
						|
                    filename, extension = os.path.splitext(filename)
 | 
						|
                    url = self.tagurlformat % {'path': base, 'fname': filename,
 | 
						|
                                               'fext': extension}
 | 
						|
                    parts[0] = "<a href=\"%s#%s-%d\">%s" % \
 | 
						|
                        (url, self.lineanchors, linenumber, parts[0])
 | 
						|
                    parts[-1] = parts[-1] + "</a>"
 | 
						|
 | 
						|
            # for all but the last line
 | 
						|
            for part in parts[:-1]:
 | 
						|
                if line:
 | 
						|
                    # Also check for part being non-empty, so we avoid creating
 | 
						|
                    # empty <span> tags
 | 
						|
                    if lspan != cspan and part:
 | 
						|
                        line.extend(((lspan and '</span>'), cspan, part,
 | 
						|
                                     (cspan and '</span>'), lsep))
 | 
						|
                    else:  # both are the same, or the current part was empty
 | 
						|
                        line.extend((part, (lspan and '</span>'), lsep))
 | 
						|
                    yield 1, ''.join(line)
 | 
						|
                    line = []
 | 
						|
                elif part:
 | 
						|
                    yield 1, ''.join((cspan, part, (cspan and '</span>'), lsep))
 | 
						|
                else:
 | 
						|
                    yield 1, lsep
 | 
						|
            # for the last line
 | 
						|
            if line and parts[-1]:
 | 
						|
                if lspan != cspan:
 | 
						|
                    line.extend(((lspan and '</span>'), cspan, parts[-1]))
 | 
						|
                    lspan = cspan
 | 
						|
                else:
 | 
						|
                    line.append(parts[-1])
 | 
						|
            elif parts[-1]:
 | 
						|
                line = [cspan, parts[-1]]
 | 
						|
                lspan = cspan
 | 
						|
            # else we neither have to open a new span nor set lspan
 | 
						|
 | 
						|
        if line:
 | 
						|
            line.extend(((lspan and '</span>'), lsep))
 | 
						|
            yield 1, ''.join(line)
 | 
						|
 | 
						|
    def _lookup_ctag(self, token):
 | 
						|
        entry = ctags.TagEntry()
 | 
						|
        if self._ctags.find(entry, token.encode(), 0):
 | 
						|
            return entry['file'].decode(), entry['lineNumber']
 | 
						|
        else:
 | 
						|
            return None, None
 | 
						|
 | 
						|
    def _highlight_lines(self, tokensource):
 | 
						|
        """
 | 
						|
        Highlighted the lines specified in the `hl_lines` option by
 | 
						|
        post-processing the token stream coming from `_format_lines`.
 | 
						|
        """
 | 
						|
        hls = self.hl_lines
 | 
						|
 | 
						|
        for i, (t, value) in enumerate(tokensource):
 | 
						|
            if t != 1:
 | 
						|
                yield t, value
 | 
						|
            if i + 1 in hls:  # i + 1 because Python indexes start at 0
 | 
						|
                if self.noclasses:
 | 
						|
                    style = ''
 | 
						|
                    if self.style.highlight_color is not None:
 | 
						|
                        style = (f' style="background-color: {self.style.highlight_color}"')
 | 
						|
                    yield 1, f'<span{style}>{value}</span>'
 | 
						|
                else:
 | 
						|
                    yield 1, f'<span class="hll">{value}</span>'
 | 
						|
            else:
 | 
						|
                yield 1, value
 | 
						|
 | 
						|
    def wrap(self, source):
 | 
						|
        """
 | 
						|
        Wrap the ``source``, which is a generator yielding
 | 
						|
        individual lines, in custom generators. See docstring
 | 
						|
        for `format`. Can be overridden.
 | 
						|
        """
 | 
						|
 | 
						|
        output = source
 | 
						|
        if self.wrapcode:
 | 
						|
            output = self._wrap_code(output)
 | 
						|
 | 
						|
        output = self._wrap_pre(output)
 | 
						|
 | 
						|
        return output
 | 
						|
 | 
						|
    def format_unencoded(self, tokensource, outfile):
 | 
						|
        """
 | 
						|
        The formatting process uses several nested generators; which of
 | 
						|
        them are used is determined by the user's options.
 | 
						|
 | 
						|
        Each generator should take at least one argument, ``inner``,
 | 
						|
        and wrap the pieces of text generated by this.
 | 
						|
 | 
						|
        Always yield 2-tuples: (code, text). If "code" is 1, the text
 | 
						|
        is part of the original tokensource being highlighted, if it's
 | 
						|
        0, the text is some piece of wrapping. This makes it possible to
 | 
						|
        use several different wrappers that process the original source
 | 
						|
        linewise, e.g. line number generators.
 | 
						|
        """
 | 
						|
        source = self._format_lines(tokensource)
 | 
						|
 | 
						|
        # As a special case, we wrap line numbers before line highlighting
 | 
						|
        # so the line numbers get wrapped in the highlighting tag.
 | 
						|
        if not self.nowrap and self.linenos == 2:
 | 
						|
            source = self._wrap_inlinelinenos(source)
 | 
						|
 | 
						|
        if self.hl_lines:
 | 
						|
            source = self._highlight_lines(source)
 | 
						|
 | 
						|
        if not self.nowrap:
 | 
						|
            if self.lineanchors:
 | 
						|
                source = self._wrap_lineanchors(source)
 | 
						|
            if self.linespans:
 | 
						|
                source = self._wrap_linespans(source)
 | 
						|
            source = self.wrap(source)
 | 
						|
            if self.linenos == 1:
 | 
						|
                source = self._wrap_tablelinenos(source)
 | 
						|
            source = self._wrap_div(source)
 | 
						|
            if self.full:
 | 
						|
                source = self._wrap_full(source, outfile)
 | 
						|
 | 
						|
        for t, piece in source:
 | 
						|
            outfile.write(piece)
 |