diff options
| author | cyfraeviolae <cyfraeviolae> | 2024-04-03 03:10:44 -0400 | 
|---|---|---|
| committer | cyfraeviolae <cyfraeviolae> | 2024-04-03 03:10:44 -0400 | 
| commit | 6d7ba58f880be618ade07f8ea080fe8c4bf8a896 (patch) | |
| tree | b1c931051ffcebd2bd9d61d98d6233ffa289bbce /venv/lib/python3.11/site-packages/pygments/formatters | |
| parent | 4f884c9abc32990b4061a1bb6997b4b37e58ea0b (diff) | |
venv
Diffstat (limited to 'venv/lib/python3.11/site-packages/pygments/formatters')
28 files changed, 3851 insertions, 0 deletions
| diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/__init__.py b/venv/lib/python3.11/site-packages/pygments/formatters/__init__.py new file mode 100644 index 0000000..6e482a1 --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/__init__.py @@ -0,0 +1,158 @@ +""" +    pygments.formatters +    ~~~~~~~~~~~~~~~~~~~ + +    Pygments formatters. + +    :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. +    :license: BSD, see LICENSE for details. +""" + +import re +import sys +import types +import fnmatch +from os.path import basename + +from pygments.formatters._mapping import FORMATTERS +from pygments.plugin import find_plugin_formatters +from pygments.util import ClassNotFound + +__all__ = ['get_formatter_by_name', 'get_formatter_for_filename', +           'get_all_formatters', 'load_formatter_from_file'] + list(FORMATTERS) + +_formatter_cache = {}  # classes by name +_pattern_cache = {} + + +def _fn_matches(fn, glob): +    """Return whether the supplied file name fn matches pattern filename.""" +    if glob not in _pattern_cache: +        pattern = _pattern_cache[glob] = re.compile(fnmatch.translate(glob)) +        return pattern.match(fn) +    return _pattern_cache[glob].match(fn) + + +def _load_formatters(module_name): +    """Load a formatter (and all others in the module too).""" +    mod = __import__(module_name, None, None, ['__all__']) +    for formatter_name in mod.__all__: +        cls = getattr(mod, formatter_name) +        _formatter_cache[cls.name] = cls + + +def get_all_formatters(): +    """Return a generator for all formatter classes.""" +    # NB: this returns formatter classes, not info like get_all_lexers(). +    for info in FORMATTERS.values(): +        if info[1] not in _formatter_cache: +            _load_formatters(info[0]) +        yield _formatter_cache[info[1]] +    for _, formatter in find_plugin_formatters(): +        yield formatter + + +def find_formatter_class(alias): +    """Lookup a formatter by alias. + +    Returns None if not found. +    """ +    for module_name, name, aliases, _, _ in FORMATTERS.values(): +        if alias in aliases: +            if name not in _formatter_cache: +                _load_formatters(module_name) +            return _formatter_cache[name] +    for _, cls in find_plugin_formatters(): +        if alias in cls.aliases: +            return cls + + +def get_formatter_by_name(_alias, **options): +    """ +    Return an instance of a :class:`.Formatter` subclass that has `alias` in its +    aliases list. The formatter is given the `options` at its instantiation. + +    Will raise :exc:`pygments.util.ClassNotFound` if no formatter with that +    alias is found. +    """ +    cls = find_formatter_class(_alias) +    if cls is None: +        raise ClassNotFound("no formatter found for name %r" % _alias) +    return cls(**options) + + +def load_formatter_from_file(filename, formattername="CustomFormatter", **options): +    """ +    Return a `Formatter` subclass instance loaded from the provided file, relative +    to the current directory. + +    The file is expected to contain a Formatter class named ``formattername`` +    (by default, CustomFormatter). Users should be very careful with the input, because +    this method is equivalent to running ``eval()`` on the input file. The formatter is +    given the `options` at its instantiation. + +    :exc:`pygments.util.ClassNotFound` is raised if there are any errors loading +    the formatter. + +    .. versionadded:: 2.2 +    """ +    try: +        # This empty dict will contain the namespace for the exec'd file +        custom_namespace = {} +        with open(filename, 'rb') as f: +            exec(f.read(), custom_namespace) +        # Retrieve the class `formattername` from that namespace +        if formattername not in custom_namespace: +            raise ClassNotFound('no valid %s class found in %s' % +                                (formattername, filename)) +        formatter_class = custom_namespace[formattername] +        # And finally instantiate it with the options +        return formatter_class(**options) +    except OSError as err: +        raise ClassNotFound('cannot read %s: %s' % (filename, err)) +    except ClassNotFound: +        raise +    except Exception as err: +        raise ClassNotFound('error when loading custom formatter: %s' % err) + + +def get_formatter_for_filename(fn, **options): +    """ +    Return a :class:`.Formatter` subclass instance that has a filename pattern +    matching `fn`. The formatter is given the `options` at its instantiation. + +    Will raise :exc:`pygments.util.ClassNotFound` if no formatter for that filename +    is found. +    """ +    fn = basename(fn) +    for modname, name, _, filenames, _ in FORMATTERS.values(): +        for filename in filenames: +            if _fn_matches(fn, filename): +                if name not in _formatter_cache: +                    _load_formatters(modname) +                return _formatter_cache[name](**options) +    for _name, cls in find_plugin_formatters(): +        for filename in cls.filenames: +            if _fn_matches(fn, filename): +                return cls(**options) +    raise ClassNotFound("no formatter found for file name %r" % fn) + + +class _automodule(types.ModuleType): +    """Automatically import formatters.""" + +    def __getattr__(self, name): +        info = FORMATTERS.get(name) +        if info: +            _load_formatters(info[0]) +            cls = _formatter_cache[info[1]] +            setattr(self, name, cls) +            return cls +        raise AttributeError(name) + + +oldmod = sys.modules[__name__] +newmod = _automodule(__name__) +newmod.__dict__.update(oldmod.__dict__) +sys.modules[__name__] = newmod +del newmod.newmod, newmod.oldmod, newmod.sys, newmod.types diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/__init__.cpython-311.pyc b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/__init__.cpython-311.pycBinary files differ new file mode 100644 index 0000000..de3734e --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/__init__.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/_mapping.cpython-311.pyc b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/_mapping.cpython-311.pycBinary files differ new file mode 100644 index 0000000..268b46e --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/_mapping.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/bbcode.cpython-311.pyc b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/bbcode.cpython-311.pycBinary files differ new file mode 100644 index 0000000..f958bd3 --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/bbcode.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/groff.cpython-311.pyc b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/groff.cpython-311.pycBinary files differ new file mode 100644 index 0000000..d578aa0 --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/groff.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/html.cpython-311.pyc b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/html.cpython-311.pycBinary files differ new file mode 100644 index 0000000..42eb8bd --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/html.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/img.cpython-311.pyc b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/img.cpython-311.pycBinary files differ new file mode 100644 index 0000000..60484d1 --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/img.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/irc.cpython-311.pyc b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/irc.cpython-311.pycBinary files differ new file mode 100644 index 0000000..022a40e --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/irc.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/latex.cpython-311.pyc b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/latex.cpython-311.pycBinary files differ new file mode 100644 index 0000000..2bbb094 --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/latex.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/other.cpython-311.pyc b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/other.cpython-311.pycBinary files differ new file mode 100644 index 0000000..6bc4288 --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/other.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/pangomarkup.cpython-311.pyc b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/pangomarkup.cpython-311.pycBinary files differ new file mode 100644 index 0000000..8db5e7e --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/pangomarkup.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/rtf.cpython-311.pyc b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/rtf.cpython-311.pycBinary files differ new file mode 100644 index 0000000..173c930 --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/rtf.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/svg.cpython-311.pyc b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/svg.cpython-311.pycBinary files differ new file mode 100644 index 0000000..d579678 --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/svg.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/terminal.cpython-311.pyc b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/terminal.cpython-311.pycBinary files differ new file mode 100644 index 0000000..e46767f --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/terminal.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/terminal256.cpython-311.pyc b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/terminal256.cpython-311.pycBinary files differ new file mode 100644 index 0000000..560606f --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/__pycache__/terminal256.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/_mapping.py b/venv/lib/python3.11/site-packages/pygments/formatters/_mapping.py new file mode 100755 index 0000000..72ca840 --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/_mapping.py @@ -0,0 +1,23 @@ +# Automatically generated by scripts/gen_mapfiles.py. +# DO NOT EDIT BY HAND; run `tox -e mapfiles` instead. + +FORMATTERS = { +    'BBCodeFormatter': ('pygments.formatters.bbcode', 'BBCode', ('bbcode', 'bb'), (), 'Format tokens with BBcodes. These formatting codes are used by many bulletin boards, so you can highlight your sourcecode with pygments before posting it there.'), +    'BmpImageFormatter': ('pygments.formatters.img', 'img_bmp', ('bmp', 'bitmap'), ('*.bmp',), 'Create a bitmap image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'), +    'GifImageFormatter': ('pygments.formatters.img', 'img_gif', ('gif',), ('*.gif',), 'Create a GIF image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'), +    'GroffFormatter': ('pygments.formatters.groff', 'groff', ('groff', 'troff', 'roff'), (), 'Format tokens with groff escapes to change their color and font style.'), +    'HtmlFormatter': ('pygments.formatters.html', 'HTML', ('html',), ('*.html', '*.htm'), "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."), +    'IRCFormatter': ('pygments.formatters.irc', 'IRC', ('irc', 'IRC'), (), 'Format tokens with IRC color sequences'), +    'ImageFormatter': ('pygments.formatters.img', 'img', ('img', 'IMG', 'png'), ('*.png',), 'Create a PNG image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'), +    'JpgImageFormatter': ('pygments.formatters.img', 'img_jpg', ('jpg', 'jpeg'), ('*.jpg',), 'Create a JPEG image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'), +    'LatexFormatter': ('pygments.formatters.latex', 'LaTeX', ('latex', 'tex'), ('*.tex',), 'Format tokens as LaTeX code. This needs the `fancyvrb` and `color` standard packages.'), +    'NullFormatter': ('pygments.formatters.other', 'Text only', ('text', 'null'), ('*.txt',), 'Output the text unchanged without any formatting.'), +    'PangoMarkupFormatter': ('pygments.formatters.pangomarkup', 'Pango Markup', ('pango', 'pangomarkup'), (), 'Format tokens as Pango Markup code. It can then be rendered to an SVG.'), +    'RawTokenFormatter': ('pygments.formatters.other', 'Raw tokens', ('raw', 'tokens'), ('*.raw',), 'Format tokens as a raw representation for storing token streams.'), +    'RtfFormatter': ('pygments.formatters.rtf', 'RTF', ('rtf',), ('*.rtf',), 'Format tokens as RTF markup. This formatter automatically outputs full RTF documents with color information and other useful stuff. Perfect for Copy and Paste into Microsoft(R) Word(R) documents.'), +    'SvgFormatter': ('pygments.formatters.svg', 'SVG', ('svg',), ('*.svg',), 'Format tokens as an SVG graphics file.  This formatter is still experimental. Each line of code is a ``<text>`` element with explicit ``x`` and ``y`` coordinates containing ``<tspan>`` elements with the individual token styles.'), +    'Terminal256Formatter': ('pygments.formatters.terminal256', 'Terminal256', ('terminal256', 'console256', '256'), (), 'Format tokens with ANSI color sequences, for output in a 256-color terminal or console.  Like in `TerminalFormatter` color sequences are terminated at newlines, so that paging the output works correctly.'), +    'TerminalFormatter': ('pygments.formatters.terminal', 'Terminal', ('terminal', 'console'), (), 'Format tokens with ANSI color sequences, for output in a text console. Color sequences are terminated at newlines, so that paging the output works correctly.'), +    'TerminalTrueColorFormatter': ('pygments.formatters.terminal256', 'TerminalTrueColor', ('terminal16m', 'console16m', '16m'), (), 'Format tokens with ANSI color sequences, for output in a true-color terminal or console.  Like in `TerminalFormatter` color sequences are terminated at newlines, so that paging the output works correctly.'), +    'TestcaseFormatter': ('pygments.formatters.other', 'Testcase', ('testcase',), (), 'Format tokens as appropriate for a new testcase.'), +} diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/bbcode.py b/venv/lib/python3.11/site-packages/pygments/formatters/bbcode.py new file mode 100644 index 0000000..9ce4ebc --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/bbcode.py @@ -0,0 +1,108 @@ +""" +    pygments.formatters.bbcode +    ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +    BBcode formatter. + +    :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. +    :license: BSD, see LICENSE for details. +""" + + +from pygments.formatter import Formatter +from pygments.util import get_bool_opt + +__all__ = ['BBCodeFormatter'] + + +class BBCodeFormatter(Formatter): +    """ +    Format tokens with BBcodes. These formatting codes are used by many +    bulletin boards, so you can highlight your sourcecode with pygments before +    posting it there. + +    This formatter has no support for background colors and borders, as there +    are no common BBcode tags for that. + +    Some board systems (e.g. phpBB) don't support colors in their [code] tag, +    so you can't use the highlighting together with that tag. +    Text in a [code] tag usually is shown with a monospace font (which this +    formatter can do with the ``monofont`` option) and no spaces (which you +    need for indentation) are removed. + +    Additional options accepted: + +    `style` +        The style to use, can be a string or a Style subclass (default: +        ``'default'``). + +    `codetag` +        If set to true, put the output into ``[code]`` tags (default: +        ``false``) + +    `monofont` +        If set to true, add a tag to show the code with a monospace font +        (default: ``false``). +    """ +    name = 'BBCode' +    aliases = ['bbcode', 'bb'] +    filenames = [] + +    def __init__(self, **options): +        Formatter.__init__(self, **options) +        self._code = get_bool_opt(options, 'codetag', False) +        self._mono = get_bool_opt(options, 'monofont', False) + +        self.styles = {} +        self._make_styles() + +    def _make_styles(self): +        for ttype, ndef in self.style: +            start = end = '' +            if ndef['color']: +                start += '[color=#%s]' % ndef['color'] +                end = '[/color]' + end +            if ndef['bold']: +                start += '[b]' +                end = '[/b]' + end +            if ndef['italic']: +                start += '[i]' +                end = '[/i]' + end +            if ndef['underline']: +                start += '[u]' +                end = '[/u]' + end +            # there are no common BBcodes for background-color and border + +            self.styles[ttype] = start, end + +    def format_unencoded(self, tokensource, outfile): +        if self._code: +            outfile.write('[code]') +        if self._mono: +            outfile.write('[font=monospace]') + +        lastval = '' +        lasttype = None + +        for ttype, value in tokensource: +            while ttype not in self.styles: +                ttype = ttype.parent +            if ttype == lasttype: +                lastval += value +            else: +                if lastval: +                    start, end = self.styles[lasttype] +                    outfile.write(''.join((start, lastval, end))) +                lastval = value +                lasttype = ttype + +        if lastval: +            start, end = self.styles[lasttype] +            outfile.write(''.join((start, lastval, end))) + +        if self._mono: +            outfile.write('[/font]') +        if self._code: +            outfile.write('[/code]') +        if self._code or self._mono: +            outfile.write('\n') diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/groff.py b/venv/lib/python3.11/site-packages/pygments/formatters/groff.py new file mode 100644 index 0000000..687fd54 --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/groff.py @@ -0,0 +1,170 @@ +""" +    pygments.formatters.groff +    ~~~~~~~~~~~~~~~~~~~~~~~~~ + +    Formatter for groff output. + +    :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. +    :license: BSD, see LICENSE for details. +""" + +import math +from pygments.formatter import Formatter +from pygments.util import get_bool_opt, get_int_opt + +__all__ = ['GroffFormatter'] + + +class GroffFormatter(Formatter): +    """ +    Format tokens with groff escapes to change their color and font style. + +    .. versionadded:: 2.11 + +    Additional options accepted: + +    `style` +        The style to use, can be a string or a Style subclass (default: +        ``'default'``). + +    `monospaced` +        If set to true, monospace font will be used (default: ``true``). + +    `linenos` +        If set to true, print the line numbers (default: ``false``). + +    `wrap` +        Wrap lines to the specified number of characters. Disabled if set to 0 +        (default: ``0``). +    """ + +    name = 'groff' +    aliases = ['groff','troff','roff'] +    filenames = [] + +    def __init__(self, **options): +        Formatter.__init__(self, **options) + +        self.monospaced = get_bool_opt(options, 'monospaced', True) +        self.linenos = get_bool_opt(options, 'linenos', False) +        self._lineno = 0 +        self.wrap = get_int_opt(options, 'wrap', 0) +        self._linelen = 0 + +        self.styles = {} +        self._make_styles() + + +    def _make_styles(self): +        regular = '\\f[CR]' if self.monospaced else '\\f[R]' +        bold = '\\f[CB]' if self.monospaced else '\\f[B]' +        italic = '\\f[CI]' if self.monospaced else '\\f[I]' + +        for ttype, ndef in self.style: +            start = end = '' +            if ndef['color']: +                start += '\\m[%s]' % ndef['color'] +                end = '\\m[]' + end +            if ndef['bold']: +                start += bold +                end = regular + end +            if ndef['italic']: +                start += italic +                end = regular + end +            if ndef['bgcolor']: +                start += '\\M[%s]' % ndef['bgcolor'] +                end = '\\M[]' + end + +            self.styles[ttype] = start, end + + +    def _define_colors(self, outfile): +        colors = set() +        for _, ndef in self.style: +            if ndef['color'] is not None: +                colors.add(ndef['color']) + +        for color in sorted(colors): +            outfile.write('.defcolor ' + color + ' rgb #' + color + '\n') + + +    def _write_lineno(self, outfile): +        self._lineno += 1 +        outfile.write("%s% 4d " % (self._lineno != 1 and '\n' or '', self._lineno)) + + +    def _wrap_line(self, line): +        length = len(line.rstrip('\n')) +        space = '     ' if self.linenos else '' +        newline = '' + +        if length > self.wrap: +            for i in range(0, math.floor(length / self.wrap)): +                chunk = line[i*self.wrap:i*self.wrap+self.wrap] +                newline += (chunk + '\n' + space) +            remainder = length % self.wrap +            if remainder > 0: +                newline += line[-remainder-1:] +                self._linelen = remainder +        elif self._linelen + length > self.wrap: +            newline = ('\n' + space) + line +            self._linelen = length +        else: +            newline = line +            self._linelen += length + +        return newline + + +    def _escape_chars(self, text): +        text = text.replace('\\', '\\[u005C]'). \ +                    replace('.', '\\[char46]'). \ +                    replace('\'', '\\[u0027]'). \ +                    replace('`', '\\[u0060]'). \ +                    replace('~', '\\[u007E]') +        copy = text + +        for char in copy: +            if len(char) != len(char.encode()): +                uni = char.encode('unicode_escape') \ +                    .decode()[1:] \ +                    .replace('x', 'u00') \ +                    .upper() +                text = text.replace(char, '\\[u' + uni[1:] + ']') + +        return text + + +    def format_unencoded(self, tokensource, outfile): +        self._define_colors(outfile) + +        outfile.write('.nf\n\\f[CR]\n') + +        if self.linenos: +            self._write_lineno(outfile) + +        for ttype, value in tokensource: +            while ttype not in self.styles: +                ttype = ttype.parent +            start, end = self.styles[ttype] + +            for line in value.splitlines(True): +                if self.wrap > 0: +                    line = self._wrap_line(line) + +                if start and end: +                    text = self._escape_chars(line.rstrip('\n')) +                    if text != '': +                        outfile.write(''.join((start, text, end))) +                else: +                    outfile.write(self._escape_chars(line.rstrip('\n'))) + +                if line.endswith('\n'): +                    if self.linenos: +                        self._write_lineno(outfile) +                        self._linelen = 0 +                    else: +                        outfile.write('\n') +                        self._linelen = 0 + +        outfile.write('\n.fi') diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/html.py b/venv/lib/python3.11/site-packages/pygments/formatters/html.py new file mode 100644 index 0000000..df2469e --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/html.py @@ -0,0 +1,990 @@ +""" +    pygments.formatters.html +    ~~~~~~~~~~~~~~~~~~~~~~~~ + +    Formatter for HTML output. + +    :copyright: Copyright 2006-2023 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: +        return '#' + 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-2023 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-2023 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: %s; ' % 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: %s; ' % webify(ndef['bgcolor']) +            if ndef['border']: +                style += 'border: 1px solid %s; ' % 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 = [ +            '%s { %s } /* %s */' % (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, '%s{ background: %s;%s }' % ( +                    prefix(''), bg_color, text_style +                ) +            ) +        if hl_color is not None: +            lines.insert( +                0, '%s { background-color: %s }' % (prefix('hll'), hl_color) +            ) + +        return lines + +    def get_linenos_style_defs(self): +        lines = [ +            'pre { %s }' % self._pre_style, +            'td.linenos .normal { %s }' % self._linenos_style, +            'span.linenos { %s }' % self._linenos_style, +            'td.linenos .special { %s }' % self._linenos_special_style, +            'span.linenos.special { %s }' % 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): +        return 'color: %s; background-color: %s; padding-left: 5px; padding-right: 5px;' % ( +            self.style.line_number_color, +            self.style.line_number_background_color +        ) + +    @property +    def _linenos_special_style(self): +        return 'color: %s; background-color: %s; padding-left: 5px; padding-right: 5px;' % ( +            self.style.line_number_special_color, +            self.style.line_number_special_background_color +        ) + +    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 = ' style="%s"' % self._linenos_special_style +                else: +                    style = ' style="%s"' % self._linenos_style +            else: +                if special_line: +                    style = ' class="special"' +                else: +                    style = ' class="normal"' + +            if style: +                line = '<span%s>%s</span>' % (style, line) + +            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 = ' style="%s"' % self._linenos_special_style +                else: +                    style = ' style="%s"' % self._linenos_style +            else: +                if special_line: +                    style = ' class="linenos special"' +                else: +                    style = ' class="linenos"' + +            if style: +                linenos = '<span%s>%s</span>' % (style, line) +            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('background: %s' % (self.style.background_color,)) +        if self.cssstyles: +            style.append(self.cssstyles) +        style = '; '.join(style) + +        yield 0, ('<div' + (self.cssclass and ' class="%s"' % self.cssclass) + +                  (style and (' style="%s"' % 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 ' style="%s"' % 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="%s"' % '.'.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 = '<span style="%s"%s>' % (css_style, title) +                    else: +                        cspan = '' +                else: +                    css_class = self._get_css_classes(ttype) +                    if css_class: +                        cspan = '<span class="%s"%s>' % (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 = (' style="background-color: %s"' % +                                 (self.style.highlight_color,)) +                    yield 1, '<span%s>%s</span>' % (style, value) +                else: +                    yield 1, '<span class="hll">%s</span>' % value +            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) diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/img.py b/venv/lib/python3.11/site-packages/pygments/formatters/img.py new file mode 100644 index 0000000..dcf09da --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/img.py @@ -0,0 +1,684 @@ +""" +    pygments.formatters.img +    ~~~~~~~~~~~~~~~~~~~~~~~ + +    Formatter for Pixmap output. + +    :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. +    :license: BSD, see LICENSE for details. +""" +import os +import sys + +from pygments.formatter import Formatter +from pygments.util import get_bool_opt, get_int_opt, get_list_opt, \ +    get_choice_opt + +import subprocess + +# Import this carefully +try: +    from PIL import Image, ImageDraw, ImageFont +    pil_available = True +except ImportError: +    pil_available = False + +try: +    import _winreg +except ImportError: +    try: +        import winreg as _winreg +    except ImportError: +        _winreg = None + +__all__ = ['ImageFormatter', 'GifImageFormatter', 'JpgImageFormatter', +           'BmpImageFormatter'] + + +# For some unknown reason every font calls it something different +STYLES = { +    'NORMAL':     ['', 'Roman', 'Book', 'Normal', 'Regular', 'Medium'], +    'ITALIC':     ['Oblique', 'Italic'], +    'BOLD':       ['Bold'], +    'BOLDITALIC': ['Bold Oblique', 'Bold Italic'], +} + +# A sane default for modern systems +DEFAULT_FONT_NAME_NIX = 'DejaVu Sans Mono' +DEFAULT_FONT_NAME_WIN = 'Courier New' +DEFAULT_FONT_NAME_MAC = 'Menlo' + + +class PilNotAvailable(ImportError): +    """When Python imaging library is not available""" + + +class FontNotFound(Exception): +    """When there are no usable fonts specified""" + + +class FontManager: +    """ +    Manages a set of fonts: normal, italic, bold, etc... +    """ + +    def __init__(self, font_name, font_size=14): +        self.font_name = font_name +        self.font_size = font_size +        self.fonts = {} +        self.encoding = None +        self.variable = False +        if hasattr(font_name, 'read') or os.path.isfile(font_name): +            font = ImageFont.truetype(font_name, self.font_size) +            self.variable = True +            for style in STYLES: +                self.fonts[style] = font + +            return + +        if sys.platform.startswith('win'): +            if not font_name: +                self.font_name = DEFAULT_FONT_NAME_WIN +            self._create_win() +        elif sys.platform.startswith('darwin'): +            if not font_name: +                self.font_name = DEFAULT_FONT_NAME_MAC +            self._create_mac() +        else: +            if not font_name: +                self.font_name = DEFAULT_FONT_NAME_NIX +            self._create_nix() + +    def _get_nix_font_path(self, name, style): +        proc = subprocess.Popen(['fc-list', "%s:style=%s" % (name, style), 'file'], +                                stdout=subprocess.PIPE, stderr=None) +        stdout, _ = proc.communicate() +        if proc.returncode == 0: +            lines = stdout.splitlines() +            for line in lines: +                if line.startswith(b'Fontconfig warning:'): +                    continue +                path = line.decode().strip().strip(':') +                if path: +                    return path +            return None + +    def _create_nix(self): +        for name in STYLES['NORMAL']: +            path = self._get_nix_font_path(self.font_name, name) +            if path is not None: +                self.fonts['NORMAL'] = ImageFont.truetype(path, self.font_size) +                break +        else: +            raise FontNotFound('No usable fonts named: "%s"' % +                               self.font_name) +        for style in ('ITALIC', 'BOLD', 'BOLDITALIC'): +            for stylename in STYLES[style]: +                path = self._get_nix_font_path(self.font_name, stylename) +                if path is not None: +                    self.fonts[style] = ImageFont.truetype(path, self.font_size) +                    break +            else: +                if style == 'BOLDITALIC': +                    self.fonts[style] = self.fonts['BOLD'] +                else: +                    self.fonts[style] = self.fonts['NORMAL'] + +    def _get_mac_font_path(self, font_map, name, style): +        return font_map.get((name + ' ' + style).strip().lower()) + +    def _create_mac(self): +        font_map = {} +        for font_dir in (os.path.join(os.getenv("HOME"), 'Library/Fonts/'), +                         '/Library/Fonts/', '/System/Library/Fonts/'): +            font_map.update( +                (os.path.splitext(f)[0].lower(), os.path.join(font_dir, f)) +                for f in os.listdir(font_dir) +                if f.lower().endswith(('ttf', 'ttc'))) + +        for name in STYLES['NORMAL']: +            path = self._get_mac_font_path(font_map, self.font_name, name) +            if path is not None: +                self.fonts['NORMAL'] = ImageFont.truetype(path, self.font_size) +                break +        else: +            raise FontNotFound('No usable fonts named: "%s"' % +                               self.font_name) +        for style in ('ITALIC', 'BOLD', 'BOLDITALIC'): +            for stylename in STYLES[style]: +                path = self._get_mac_font_path(font_map, self.font_name, stylename) +                if path is not None: +                    self.fonts[style] = ImageFont.truetype(path, self.font_size) +                    break +            else: +                if style == 'BOLDITALIC': +                    self.fonts[style] = self.fonts['BOLD'] +                else: +                    self.fonts[style] = self.fonts['NORMAL'] + +    def _lookup_win(self, key, basename, styles, fail=False): +        for suffix in ('', ' (TrueType)'): +            for style in styles: +                try: +                    valname = '%s%s%s' % (basename, style and ' '+style, suffix) +                    val, _ = _winreg.QueryValueEx(key, valname) +                    return val +                except OSError: +                    continue +        else: +            if fail: +                raise FontNotFound('Font %s (%s) not found in registry' % +                                   (basename, styles[0])) +            return None + +    def _create_win(self): +        lookuperror = None +        keynames = [ (_winreg.HKEY_CURRENT_USER, r'Software\Microsoft\Windows NT\CurrentVersion\Fonts'), +                     (_winreg.HKEY_CURRENT_USER, r'Software\Microsoft\Windows\CurrentVersion\Fonts'), +                     (_winreg.HKEY_LOCAL_MACHINE, r'Software\Microsoft\Windows NT\CurrentVersion\Fonts'), +                     (_winreg.HKEY_LOCAL_MACHINE, r'Software\Microsoft\Windows\CurrentVersion\Fonts') ] +        for keyname in keynames: +            try: +                key = _winreg.OpenKey(*keyname) +                try: +                    path = self._lookup_win(key, self.font_name, STYLES['NORMAL'], True) +                    self.fonts['NORMAL'] = ImageFont.truetype(path, self.font_size) +                    for style in ('ITALIC', 'BOLD', 'BOLDITALIC'): +                        path = self._lookup_win(key, self.font_name, STYLES[style]) +                        if path: +                            self.fonts[style] = ImageFont.truetype(path, self.font_size) +                        else: +                            if style == 'BOLDITALIC': +                                self.fonts[style] = self.fonts['BOLD'] +                            else: +                                self.fonts[style] = self.fonts['NORMAL'] +                    return +                except FontNotFound as err: +                    lookuperror = err +                finally: +                    _winreg.CloseKey(key) +            except OSError: +                pass +        else: +            # If we get here, we checked all registry keys and had no luck +            # We can be in one of two situations now: +            # * All key lookups failed. In this case lookuperror is None and we +            #   will raise a generic error +            # * At least one lookup failed with a FontNotFound error. In this +            #   case, we will raise that as a more specific error +            if lookuperror: +                raise lookuperror +            raise FontNotFound('Can\'t open Windows font registry key') + +    def get_char_size(self): +        """ +        Get the character size. +        """ +        return self.get_text_size('M') + +    def get_text_size(self, text): +        """ +        Get the text size (width, height). +        """ +        font = self.fonts['NORMAL'] +        if hasattr(font, 'getbbox'):  # Pillow >= 9.2.0 +            return font.getbbox(text)[2:4] +        else: +            return font.getsize(text) + +    def get_font(self, bold, oblique): +        """ +        Get the font based on bold and italic flags. +        """ +        if bold and oblique: +            if self.variable: +                return self.get_style('BOLDITALIC') + +            return self.fonts['BOLDITALIC'] +        elif bold: +            if self.variable: +                return self.get_style('BOLD') + +            return self.fonts['BOLD'] +        elif oblique: +            if self.variable: +                return self.get_style('ITALIC') + +            return self.fonts['ITALIC'] +        else: +            if self.variable: +                return self.get_style('NORMAL') + +            return self.fonts['NORMAL'] + +    def get_style(self, style): +        """ +        Get the specified style of the font if it is a variable font. +        If not found, return the normal font. +        """ +        font = self.fonts[style] +        for style_name in STYLES[style]: +            try: +                font.set_variation_by_name(style_name) +                return font +            except ValueError: +                pass +            except OSError: +                return font + +        return font + + +class ImageFormatter(Formatter): +    """ +    Create a PNG image from source code. This uses the Python Imaging Library to +    generate a pixmap from the source code. + +    .. versionadded:: 0.10 + +    Additional options accepted: + +    `image_format` +        An image format to output to that is recognised by PIL, these include: + +        * "PNG" (default) +        * "JPEG" +        * "BMP" +        * "GIF" + +    `line_pad` +        The extra spacing (in pixels) between each line of text. + +        Default: 2 + +    `font_name` +        The font name to be used as the base font from which others, such as +        bold and italic fonts will be generated.  This really should be a +        monospace font to look sane. +        If a filename or a file-like object is specified, the user must +        provide different styles of the font. + +        Default: "Courier New" on Windows, "Menlo" on Mac OS, and +                 "DejaVu Sans Mono" on \\*nix + +    `font_size` +        The font size in points to be used. + +        Default: 14 + +    `image_pad` +        The padding, in pixels to be used at each edge of the resulting image. + +        Default: 10 + +    `line_numbers` +        Whether line numbers should be shown: True/False + +        Default: True + +    `line_number_start` +        The line number of the first line. + +        Default: 1 + +    `line_number_step` +        The step used when printing line numbers. + +        Default: 1 + +    `line_number_bg` +        The background colour (in "#123456" format) of the line number bar, or +        None to use the style background color. + +        Default: "#eed" + +    `line_number_fg` +        The text color of the line numbers (in "#123456"-like format). + +        Default: "#886" + +    `line_number_chars` +        The number of columns of line numbers allowable in the line number +        margin. + +        Default: 2 + +    `line_number_bold` +        Whether line numbers will be bold: True/False + +        Default: False + +    `line_number_italic` +        Whether line numbers will be italicized: True/False + +        Default: False + +    `line_number_separator` +        Whether a line will be drawn between the line number area and the +        source code area: True/False + +        Default: True + +    `line_number_pad` +        The horizontal padding (in pixels) between the line number margin, and +        the source code area. + +        Default: 6 + +    `hl_lines` +        Specify a list of lines to be highlighted. + +        .. versionadded:: 1.2 + +        Default: empty list + +    `hl_color` +        Specify the color for highlighting lines. + +        .. versionadded:: 1.2 + +        Default: highlight color of the selected style +    """ + +    # Required by the pygments mapper +    name = 'img' +    aliases = ['img', 'IMG', 'png'] +    filenames = ['*.png'] + +    unicodeoutput = False + +    default_image_format = 'png' + +    def __init__(self, **options): +        """ +        See the class docstring for explanation of options. +        """ +        if not pil_available: +            raise PilNotAvailable( +                'Python Imaging Library is required for this formatter') +        Formatter.__init__(self, **options) +        self.encoding = 'latin1'  # let pygments.format() do the right thing +        # Read the style +        self.styles = dict(self.style) +        if self.style.background_color is None: +            self.background_color = '#fff' +        else: +            self.background_color = self.style.background_color +        # Image options +        self.image_format = get_choice_opt( +            options, 'image_format', ['png', 'jpeg', 'gif', 'bmp'], +            self.default_image_format, normcase=True) +        self.image_pad = get_int_opt(options, 'image_pad', 10) +        self.line_pad = get_int_opt(options, 'line_pad', 2) +        # The fonts +        fontsize = get_int_opt(options, 'font_size', 14) +        self.fonts = FontManager(options.get('font_name', ''), fontsize) +        self.fontw, self.fonth = self.fonts.get_char_size() +        # Line number options +        self.line_number_fg = options.get('line_number_fg', '#886') +        self.line_number_bg = options.get('line_number_bg', '#eed') +        self.line_number_chars = get_int_opt(options, +                                             'line_number_chars', 2) +        self.line_number_bold = get_bool_opt(options, +                                             'line_number_bold', False) +        self.line_number_italic = get_bool_opt(options, +                                               'line_number_italic', False) +        self.line_number_pad = get_int_opt(options, 'line_number_pad', 6) +        self.line_numbers = get_bool_opt(options, 'line_numbers', True) +        self.line_number_separator = get_bool_opt(options, +                                                  'line_number_separator', True) +        self.line_number_step = get_int_opt(options, 'line_number_step', 1) +        self.line_number_start = get_int_opt(options, 'line_number_start', 1) +        if self.line_numbers: +            self.line_number_width = (self.fontw * self.line_number_chars + +                                      self.line_number_pad * 2) +        else: +            self.line_number_width = 0 +        self.hl_lines = [] +        hl_lines_str = get_list_opt(options, 'hl_lines', []) +        for line in hl_lines_str: +            try: +                self.hl_lines.append(int(line)) +            except ValueError: +                pass +        self.hl_color = options.get('hl_color', +                                    self.style.highlight_color) or '#f90' +        self.drawables = [] + +    def get_style_defs(self, arg=''): +        raise NotImplementedError('The -S option is meaningless for the image ' +                                  'formatter. Use -O style=<stylename> instead.') + +    def _get_line_height(self): +        """ +        Get the height of a line. +        """ +        return self.fonth + self.line_pad + +    def _get_line_y(self, lineno): +        """ +        Get the Y coordinate of a line number. +        """ +        return lineno * self._get_line_height() + self.image_pad + +    def _get_char_width(self): +        """ +        Get the width of a character. +        """ +        return self.fontw + +    def _get_char_x(self, linelength): +        """ +        Get the X coordinate of a character position. +        """ +        return linelength + self.image_pad + self.line_number_width + +    def _get_text_pos(self, linelength, lineno): +        """ +        Get the actual position for a character and line position. +        """ +        return self._get_char_x(linelength), self._get_line_y(lineno) + +    def _get_linenumber_pos(self, lineno): +        """ +        Get the actual position for the start of a line number. +        """ +        return (self.image_pad, self._get_line_y(lineno)) + +    def _get_text_color(self, style): +        """ +        Get the correct color for the token from the style. +        """ +        if style['color'] is not None: +            fill = '#' + style['color'] +        else: +            fill = '#000' +        return fill + +    def _get_text_bg_color(self, style): +        """ +        Get the correct background color for the token from the style. +        """ +        if style['bgcolor'] is not None: +            bg_color = '#' + style['bgcolor'] +        else: +            bg_color = None +        return bg_color + +    def _get_style_font(self, style): +        """ +        Get the correct font for the style. +        """ +        return self.fonts.get_font(style['bold'], style['italic']) + +    def _get_image_size(self, maxlinelength, maxlineno): +        """ +        Get the required image size. +        """ +        return (self._get_char_x(maxlinelength) + self.image_pad, +                self._get_line_y(maxlineno + 0) + self.image_pad) + +    def _draw_linenumber(self, posno, lineno): +        """ +        Remember a line number drawable to paint later. +        """ +        self._draw_text( +            self._get_linenumber_pos(posno), +            str(lineno).rjust(self.line_number_chars), +            font=self.fonts.get_font(self.line_number_bold, +                                     self.line_number_italic), +            text_fg=self.line_number_fg, +            text_bg=None, +        ) + +    def _draw_text(self, pos, text, font, text_fg, text_bg): +        """ +        Remember a single drawable tuple to paint later. +        """ +        self.drawables.append((pos, text, font, text_fg, text_bg)) + +    def _create_drawables(self, tokensource): +        """ +        Create drawables for the token content. +        """ +        lineno = charno = maxcharno = 0 +        maxlinelength = linelength = 0 +        for ttype, value in tokensource: +            while ttype not in self.styles: +                ttype = ttype.parent +            style = self.styles[ttype] +            # TODO: make sure tab expansion happens earlier in the chain.  It +            # really ought to be done on the input, as to do it right here is +            # quite complex. +            value = value.expandtabs(4) +            lines = value.splitlines(True) +            # print lines +            for i, line in enumerate(lines): +                temp = line.rstrip('\n') +                if temp: +                    self._draw_text( +                        self._get_text_pos(linelength, lineno), +                        temp, +                        font = self._get_style_font(style), +                        text_fg = self._get_text_color(style), +                        text_bg = self._get_text_bg_color(style), +                    ) +                    temp_width, _ = self.fonts.get_text_size(temp) +                    linelength += temp_width +                    maxlinelength = max(maxlinelength, linelength) +                    charno += len(temp) +                    maxcharno = max(maxcharno, charno) +                if line.endswith('\n'): +                    # add a line for each extra line in the value +                    linelength = 0 +                    charno = 0 +                    lineno += 1 +        self.maxlinelength = maxlinelength +        self.maxcharno = maxcharno +        self.maxlineno = lineno + +    def _draw_line_numbers(self): +        """ +        Create drawables for the line numbers. +        """ +        if not self.line_numbers: +            return +        for p in range(self.maxlineno): +            n = p + self.line_number_start +            if (n % self.line_number_step) == 0: +                self._draw_linenumber(p, n) + +    def _paint_line_number_bg(self, im): +        """ +        Paint the line number background on the image. +        """ +        if not self.line_numbers: +            return +        if self.line_number_fg is None: +            return +        draw = ImageDraw.Draw(im) +        recth = im.size[-1] +        rectw = self.image_pad + self.line_number_width - self.line_number_pad +        draw.rectangle([(0, 0), (rectw, recth)], +                       fill=self.line_number_bg) +        if self.line_number_separator: +            draw.line([(rectw, 0), (rectw, recth)], fill=self.line_number_fg) +        del draw + +    def format(self, tokensource, outfile): +        """ +        Format ``tokensource``, an iterable of ``(tokentype, tokenstring)`` +        tuples and write it into ``outfile``. + +        This implementation calculates where it should draw each token on the +        pixmap, then calculates the required pixmap size and draws the items. +        """ +        self._create_drawables(tokensource) +        self._draw_line_numbers() +        im = Image.new( +            'RGB', +            self._get_image_size(self.maxlinelength, self.maxlineno), +            self.background_color +        ) +        self._paint_line_number_bg(im) +        draw = ImageDraw.Draw(im) +        # Highlight +        if self.hl_lines: +            x = self.image_pad + self.line_number_width - self.line_number_pad + 1 +            recth = self._get_line_height() +            rectw = im.size[0] - x +            for linenumber in self.hl_lines: +                y = self._get_line_y(linenumber - 1) +                draw.rectangle([(x, y), (x + rectw, y + recth)], +                               fill=self.hl_color) +        for pos, value, font, text_fg, text_bg in self.drawables: +            if text_bg: +                text_size = draw.textsize(text=value, font=font) +                draw.rectangle([pos[0], pos[1], pos[0] + text_size[0], pos[1] + text_size[1]], fill=text_bg) +            draw.text(pos, value, font=font, fill=text_fg) +        im.save(outfile, self.image_format.upper()) + + +# Add one formatter per format, so that the "-f gif" option gives the correct result +# when used in pygmentize. + +class GifImageFormatter(ImageFormatter): +    """ +    Create a GIF image from source code. This uses the Python Imaging Library to +    generate a pixmap from the source code. + +    .. versionadded:: 1.0 +    """ + +    name = 'img_gif' +    aliases = ['gif'] +    filenames = ['*.gif'] +    default_image_format = 'gif' + + +class JpgImageFormatter(ImageFormatter): +    """ +    Create a JPEG image from source code. This uses the Python Imaging Library to +    generate a pixmap from the source code. + +    .. versionadded:: 1.0 +    """ + +    name = 'img_jpg' +    aliases = ['jpg', 'jpeg'] +    filenames = ['*.jpg'] +    default_image_format = 'jpeg' + + +class BmpImageFormatter(ImageFormatter): +    """ +    Create a bitmap image from source code. This uses the Python Imaging Library to +    generate a pixmap from the source code. + +    .. versionadded:: 1.0 +    """ + +    name = 'img_bmp' +    aliases = ['bmp', 'bitmap'] +    filenames = ['*.bmp'] +    default_image_format = 'bmp' diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/irc.py b/venv/lib/python3.11/site-packages/pygments/formatters/irc.py new file mode 100644 index 0000000..334aeef --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/irc.py @@ -0,0 +1,154 @@ +""" +    pygments.formatters.irc +    ~~~~~~~~~~~~~~~~~~~~~~~ + +    Formatter for IRC output + +    :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. +    :license: BSD, see LICENSE for details. +""" + +from pygments.formatter import Formatter +from pygments.token import Keyword, Name, Comment, String, Error, \ +    Number, Operator, Generic, Token, Whitespace +from pygments.util import get_choice_opt + + +__all__ = ['IRCFormatter'] + + +#: Map token types to a tuple of color values for light and dark +#: backgrounds. +IRC_COLORS = { +    Token:              ('',            ''), + +    Whitespace:         ('gray',   'brightblack'), +    Comment:            ('gray',   'brightblack'), +    Comment.Preproc:    ('cyan',        'brightcyan'), +    Keyword:            ('blue',    'brightblue'), +    Keyword.Type:       ('cyan',        'brightcyan'), +    Operator.Word:      ('magenta',      'brightcyan'), +    Name.Builtin:       ('cyan',        'brightcyan'), +    Name.Function:      ('green',   'brightgreen'), +    Name.Namespace:     ('_cyan_',      '_brightcyan_'), +    Name.Class:         ('_green_', '_brightgreen_'), +    Name.Exception:     ('cyan',        'brightcyan'), +    Name.Decorator:     ('brightblack',    'gray'), +    Name.Variable:      ('red',     'brightred'), +    Name.Constant:      ('red',     'brightred'), +    Name.Attribute:     ('cyan',        'brightcyan'), +    Name.Tag:           ('brightblue',        'brightblue'), +    String:             ('yellow',       'yellow'), +    Number:             ('blue',    'brightblue'), + +    Generic.Deleted:    ('brightred',        'brightred'), +    Generic.Inserted:   ('green',  'brightgreen'), +    Generic.Heading:    ('**',         '**'), +    Generic.Subheading: ('*magenta*',   '*brightmagenta*'), +    Generic.Error:      ('brightred',        'brightred'), + +    Error:              ('_brightred_',      '_brightred_'), +} + + +IRC_COLOR_MAP = { +    'white': 0, +    'black': 1, +    'blue': 2, +    'brightgreen': 3, +    'brightred': 4, +    'yellow': 5, +    'magenta': 6, +    'orange': 7, +    'green': 7, #compat w/ ansi +    'brightyellow': 8, +    'lightgreen': 9, +    'brightcyan': 9, # compat w/ ansi +    'cyan': 10, +    'lightblue': 11, +    'red': 11, # compat w/ ansi +    'brightblue': 12, +    'brightmagenta': 13, +    'brightblack': 14, +    'gray': 15, +} + +def ircformat(color, text): +    if len(color) < 1: +        return text +    add = sub = '' +    if '_' in color: # italic +        add += '\x1D' +        sub = '\x1D' + sub +        color = color.strip('_') +    if '*' in color: # bold +        add += '\x02' +        sub = '\x02' + sub +        color = color.strip('*') +    # underline (\x1F) not supported +    # backgrounds (\x03FF,BB) not supported +    if len(color) > 0: # actual color - may have issues with ircformat("red", "blah")+"10" type stuff +        add += '\x03' + str(IRC_COLOR_MAP[color]).zfill(2) +        sub = '\x03' + sub +    return add + text + sub +    return '<'+add+'>'+text+'</'+sub+'>' + + +class IRCFormatter(Formatter): +    r""" +    Format tokens with IRC color sequences + +    The `get_style_defs()` method doesn't do anything special since there is +    no support for common styles. + +    Options accepted: + +    `bg` +        Set to ``"light"`` or ``"dark"`` depending on the terminal's background +        (default: ``"light"``). + +    `colorscheme` +        A dictionary mapping token types to (lightbg, darkbg) color names or +        ``None`` (default: ``None`` = use builtin colorscheme). + +    `linenos` +        Set to ``True`` to have line numbers in the output as well +        (default: ``False`` = no line numbers). +    """ +    name = 'IRC' +    aliases = ['irc', 'IRC'] +    filenames = [] + +    def __init__(self, **options): +        Formatter.__init__(self, **options) +        self.darkbg = get_choice_opt(options, 'bg', +                                     ['light', 'dark'], 'light') == 'dark' +        self.colorscheme = options.get('colorscheme', None) or IRC_COLORS +        self.linenos = options.get('linenos', False) +        self._lineno = 0 + +    def _write_lineno(self, outfile): +        if self.linenos: +            self._lineno += 1 +            outfile.write("%04d: " % self._lineno) + +    def format_unencoded(self, tokensource, outfile): +        self._write_lineno(outfile) + +        for ttype, value in tokensource: +            color = self.colorscheme.get(ttype) +            while color is None: +                ttype = ttype[:-1] +                color = self.colorscheme.get(ttype) +            if color: +                color = color[self.darkbg] +                spl = value.split('\n') +                for line in spl[:-1]: +                    if line: +                        outfile.write(ircformat(color, line)) +                    outfile.write('\n') +                    self._write_lineno(outfile) +                if spl[-1]: +                    outfile.write(ircformat(color, spl[-1])) +            else: +                outfile.write(value) diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/latex.py b/venv/lib/python3.11/site-packages/pygments/formatters/latex.py new file mode 100644 index 0000000..b130bfa --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/latex.py @@ -0,0 +1,521 @@ +""" +    pygments.formatters.latex +    ~~~~~~~~~~~~~~~~~~~~~~~~~ + +    Formatter for LaTeX fancyvrb output. + +    :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. +    :license: BSD, see LICENSE for details. +""" + +from io import StringIO + +from pygments.formatter import Formatter +from pygments.lexer import Lexer, do_insertions +from pygments.token import Token, STANDARD_TYPES +from pygments.util import get_bool_opt, get_int_opt + + +__all__ = ['LatexFormatter'] + + +def escape_tex(text, commandprefix): +    return text.replace('\\', '\x00'). \ +                replace('{', '\x01'). \ +                replace('}', '\x02'). \ +                replace('\x00', r'\%sZbs{}' % commandprefix). \ +                replace('\x01', r'\%sZob{}' % commandprefix). \ +                replace('\x02', r'\%sZcb{}' % commandprefix). \ +                replace('^', r'\%sZca{}' % commandprefix). \ +                replace('_', r'\%sZus{}' % commandprefix). \ +                replace('&', r'\%sZam{}' % commandprefix). \ +                replace('<', r'\%sZlt{}' % commandprefix). \ +                replace('>', r'\%sZgt{}' % commandprefix). \ +                replace('#', r'\%sZsh{}' % commandprefix). \ +                replace('%', r'\%sZpc{}' % commandprefix). \ +                replace('$', r'\%sZdl{}' % commandprefix). \ +                replace('-', r'\%sZhy{}' % commandprefix). \ +                replace("'", r'\%sZsq{}' % commandprefix). \ +                replace('"', r'\%sZdq{}' % commandprefix). \ +                replace('~', r'\%sZti{}' % commandprefix) + + +DOC_TEMPLATE = r''' +\documentclass{%(docclass)s} +\usepackage{fancyvrb} +\usepackage{color} +\usepackage[%(encoding)s]{inputenc} +%(preamble)s + +%(styledefs)s + +\begin{document} + +\section*{%(title)s} + +%(code)s +\end{document} +''' + +## Small explanation of the mess below :) +# +# The previous version of the LaTeX formatter just assigned a command to +# each token type defined in the current style.  That obviously is +# problematic if the highlighted code is produced for a different style +# than the style commands themselves. +# +# This version works much like the HTML formatter which assigns multiple +# CSS classes to each <span> tag, from the most specific to the least +# specific token type, thus falling back to the parent token type if one +# is not defined.  Here, the classes are there too and use the same short +# forms given in token.STANDARD_TYPES. +# +# Highlighted code now only uses one custom command, which by default is +# \PY and selectable by the commandprefix option (and in addition the +# escapes \PYZat, \PYZlb and \PYZrb which haven't been renamed for +# backwards compatibility purposes). +# +# \PY has two arguments: the classes, separated by +, and the text to +# render in that style.  The classes are resolved into the respective +# style commands by magic, which serves to ignore unknown classes. +# +# The magic macros are: +# * \PY@it, \PY@bf, etc. are unconditionally wrapped around the text +#   to render in \PY@do.  Their definition determines the style. +# * \PY@reset resets \PY@it etc. to do nothing. +# * \PY@toks parses the list of classes, using magic inspired by the +#   keyval package (but modified to use plusses instead of commas +#   because fancyvrb redefines commas inside its environments). +# * \PY@tok processes one class, calling the \PY@tok@classname command +#   if it exists. +# * \PY@tok@classname sets the \PY@it etc. to reflect the chosen style +#   for its class. +# * \PY resets the style, parses the classnames and then calls \PY@do. +# +# Tip: to read this code, print it out in substituted form using e.g. +# >>> print STYLE_TEMPLATE % {'cp': 'PY'} + +STYLE_TEMPLATE = r''' +\makeatletter +\def\%(cp)s@reset{\let\%(cp)s@it=\relax \let\%(cp)s@bf=\relax%% +    \let\%(cp)s@ul=\relax \let\%(cp)s@tc=\relax%% +    \let\%(cp)s@bc=\relax \let\%(cp)s@ff=\relax} +\def\%(cp)s@tok#1{\csname %(cp)s@tok@#1\endcsname} +\def\%(cp)s@toks#1+{\ifx\relax#1\empty\else%% +    \%(cp)s@tok{#1}\expandafter\%(cp)s@toks\fi} +\def\%(cp)s@do#1{\%(cp)s@bc{\%(cp)s@tc{\%(cp)s@ul{%% +    \%(cp)s@it{\%(cp)s@bf{\%(cp)s@ff{#1}}}}}}} +\def\%(cp)s#1#2{\%(cp)s@reset\%(cp)s@toks#1+\relax+\%(cp)s@do{#2}} + +%(styles)s + +\def\%(cp)sZbs{\char`\\} +\def\%(cp)sZus{\char`\_} +\def\%(cp)sZob{\char`\{} +\def\%(cp)sZcb{\char`\}} +\def\%(cp)sZca{\char`\^} +\def\%(cp)sZam{\char`\&} +\def\%(cp)sZlt{\char`\<} +\def\%(cp)sZgt{\char`\>} +\def\%(cp)sZsh{\char`\#} +\def\%(cp)sZpc{\char`\%%} +\def\%(cp)sZdl{\char`\$} +\def\%(cp)sZhy{\char`\-} +\def\%(cp)sZsq{\char`\'} +\def\%(cp)sZdq{\char`\"} +\def\%(cp)sZti{\char`\~} +%% for compatibility with earlier versions +\def\%(cp)sZat{@} +\def\%(cp)sZlb{[} +\def\%(cp)sZrb{]} +\makeatother +''' + + +def _get_ttype_name(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 + + +class LatexFormatter(Formatter): +    r""" +    Format tokens as LaTeX code. This needs the `fancyvrb` and `color` +    standard packages. + +    Without the `full` option, code is formatted as one ``Verbatim`` +    environment, like this: + +    .. sourcecode:: latex + +        \begin{Verbatim}[commandchars=\\\{\}] +        \PY{k}{def }\PY{n+nf}{foo}(\PY{n}{bar}): +            \PY{k}{pass} +        \end{Verbatim} + +    Wrapping can be disabled using the `nowrap` option. + +    The special command used here (``\PY``) and all the other macros it needs +    are output by the `get_style_defs` method. + +    With the `full` option, a complete LaTeX document is output, including +    the command definitions in the preamble. + +    The `get_style_defs()` method of a `LatexFormatter` returns a string +    containing ``\def`` commands defining the macros needed inside the +    ``Verbatim`` environments. + +    Additional options accepted: + +    `nowrap` +        If set to ``True``, don't wrap the tokens at all, not even inside a +        ``\begin{Verbatim}`` environment. This disables most other options +        (default: ``False``). + +    `style` +        The style to use, can be a string or a Style subclass (default: +        ``'default'``). + +    `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: ``''``). + +    `docclass` +        If the `full` option is enabled, this is the document class to use +        (default: ``'article'``). + +    `preamble` +        If the `full` option is enabled, this can be further preamble commands, +        e.g. ``\usepackage`` (default: ``''``). + +    `linenos` +        If set to ``True``, output line numbers (default: ``False``). + +    `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. + +    `verboptions` +        Additional options given to the Verbatim environment (see the *fancyvrb* +        docs for possible values) (default: ``''``). + +    `commandprefix` +        The LaTeX commands used to produce colored output are constructed +        using this prefix and some letters (default: ``'PY'``). + +        .. versionadded:: 0.7 +        .. versionchanged:: 0.10 +           The default is now ``'PY'`` instead of ``'C'``. + +    `texcomments` +        If set to ``True``, enables LaTeX comment lines.  That is, LaTex markup +        in comment tokens is not escaped so that LaTeX can render it (default: +        ``False``). + +        .. versionadded:: 1.2 + +    `mathescape` +        If set to ``True``, enables LaTeX math mode escape in comments. That +        is, ``'$...$'`` inside a comment will trigger math mode (default: +        ``False``). + +        .. versionadded:: 1.2 + +    `escapeinside` +        If set to a string of length 2, enables escaping to LaTeX. Text +        delimited by these 2 characters is read as LaTeX code and +        typeset accordingly. It has no effect in string literals. It has +        no effect in comments if `texcomments` or `mathescape` is +        set. (default: ``''``). + +        .. versionadded:: 2.0 + +    `envname` +        Allows you to pick an alternative environment name replacing Verbatim. +        The alternate environment still has to support Verbatim's option syntax. +        (default: ``'Verbatim'``). + +        .. versionadded:: 2.0 +    """ +    name = 'LaTeX' +    aliases = ['latex', 'tex'] +    filenames = ['*.tex'] + +    def __init__(self, **options): +        Formatter.__init__(self, **options) +        self.nowrap = get_bool_opt(options, 'nowrap', False) +        self.docclass = options.get('docclass', 'article') +        self.preamble = options.get('preamble', '') +        self.linenos = get_bool_opt(options, 'linenos', False) +        self.linenostart = abs(get_int_opt(options, 'linenostart', 1)) +        self.linenostep = abs(get_int_opt(options, 'linenostep', 1)) +        self.verboptions = options.get('verboptions', '') +        self.nobackground = get_bool_opt(options, 'nobackground', False) +        self.commandprefix = options.get('commandprefix', 'PY') +        self.texcomments = get_bool_opt(options, 'texcomments', False) +        self.mathescape = get_bool_opt(options, 'mathescape', False) +        self.escapeinside = options.get('escapeinside', '') +        if len(self.escapeinside) == 2: +            self.left = self.escapeinside[0] +            self.right = self.escapeinside[1] +        else: +            self.escapeinside = '' +        self.envname = options.get('envname', 'Verbatim') + +        self._create_stylesheet() + +    def _create_stylesheet(self): +        t2n = self.ttype2name = {Token: ''} +        c2d = self.cmd2def = {} +        cp = self.commandprefix + +        def rgbcolor(col): +            if col: +                return ','.join(['%.2f' % (int(col[i] + col[i + 1], 16) / 255.0) +                                 for i in (0, 2, 4)]) +            else: +                return '1,1,1' + +        for ttype, ndef in self.style: +            name = _get_ttype_name(ttype) +            cmndef = '' +            if ndef['bold']: +                cmndef += r'\let\$$@bf=\textbf' +            if ndef['italic']: +                cmndef += r'\let\$$@it=\textit' +            if ndef['underline']: +                cmndef += r'\let\$$@ul=\underline' +            if ndef['roman']: +                cmndef += r'\let\$$@ff=\textrm' +            if ndef['sans']: +                cmndef += r'\let\$$@ff=\textsf' +            if ndef['mono']: +                cmndef += r'\let\$$@ff=\textsf' +            if ndef['color']: +                cmndef += (r'\def\$$@tc##1{\textcolor[rgb]{%s}{##1}}' % +                           rgbcolor(ndef['color'])) +            if ndef['border']: +                cmndef += (r'\def\$$@bc##1{{\setlength{\fboxsep}{\string -\fboxrule}' +                           r'\fcolorbox[rgb]{%s}{%s}{\strut ##1}}}' % +                           (rgbcolor(ndef['border']), +                            rgbcolor(ndef['bgcolor']))) +            elif ndef['bgcolor']: +                cmndef += (r'\def\$$@bc##1{{\setlength{\fboxsep}{0pt}' +                           r'\colorbox[rgb]{%s}{\strut ##1}}}' % +                           rgbcolor(ndef['bgcolor'])) +            if cmndef == '': +                continue +            cmndef = cmndef.replace('$$', cp) +            t2n[ttype] = name +            c2d[name] = cmndef + +    def get_style_defs(self, arg=''): +        """ +        Return the command sequences needed to define the commands +        used to format text in the verbatim environment. ``arg`` is ignored. +        """ +        cp = self.commandprefix +        styles = [] +        for name, definition in self.cmd2def.items(): +            styles.append(r'\@namedef{%s@tok@%s}{%s}' % (cp, name, definition)) +        return STYLE_TEMPLATE % {'cp': self.commandprefix, +                                 'styles': '\n'.join(styles)} + +    def format_unencoded(self, tokensource, outfile): +        # TODO: add support for background colors +        t2n = self.ttype2name +        cp = self.commandprefix + +        if self.full: +            realoutfile = outfile +            outfile = StringIO() + +        if not self.nowrap: +            outfile.write('\\begin{' + self.envname + '}[commandchars=\\\\\\{\\}') +            if self.linenos: +                start, step = self.linenostart, self.linenostep +                outfile.write(',numbers=left' + +                              (start and ',firstnumber=%d' % start or '') + +                              (step and ',stepnumber=%d' % step or '')) +            if self.mathescape or self.texcomments or self.escapeinside: +                outfile.write(',codes={\\catcode`\\$=3\\catcode`\\^=7' +                              '\\catcode`\\_=8\\relax}') +            if self.verboptions: +                outfile.write(',' + self.verboptions) +            outfile.write(']\n') + +        for ttype, value in tokensource: +            if ttype in Token.Comment: +                if self.texcomments: +                    # Try to guess comment starting lexeme and escape it ... +                    start = value[0:1] +                    for i in range(1, len(value)): +                        if start[0] != value[i]: +                            break +                        start += value[i] + +                    value = value[len(start):] +                    start = escape_tex(start, cp) + +                    # ... but do not escape inside comment. +                    value = start + value +                elif self.mathescape: +                    # Only escape parts not inside a math environment. +                    parts = value.split('$') +                    in_math = False +                    for i, part in enumerate(parts): +                        if not in_math: +                            parts[i] = escape_tex(part, cp) +                        in_math = not in_math +                    value = '$'.join(parts) +                elif self.escapeinside: +                    text = value +                    value = '' +                    while text: +                        a, sep1, text = text.partition(self.left) +                        if sep1: +                            b, sep2, text = text.partition(self.right) +                            if sep2: +                                value += escape_tex(a, cp) + b +                            else: +                                value += escape_tex(a + sep1 + b, cp) +                        else: +                            value += escape_tex(a, cp) +                else: +                    value = escape_tex(value, cp) +            elif ttype not in Token.Escape: +                value = escape_tex(value, cp) +            styles = [] +            while ttype is not Token: +                try: +                    styles.append(t2n[ttype]) +                except KeyError: +                    # not in current style +                    styles.append(_get_ttype_name(ttype)) +                ttype = ttype.parent +            styleval = '+'.join(reversed(styles)) +            if styleval: +                spl = value.split('\n') +                for line in spl[:-1]: +                    if line: +                        outfile.write("\\%s{%s}{%s}" % (cp, styleval, line)) +                    outfile.write('\n') +                if spl[-1]: +                    outfile.write("\\%s{%s}{%s}" % (cp, styleval, spl[-1])) +            else: +                outfile.write(value) + +        if not self.nowrap: +            outfile.write('\\end{' + self.envname + '}\n') + +        if self.full: +            encoding = self.encoding or 'utf8' +            # map known existings encodings from LaTeX distribution +            encoding = { +                'utf_8': 'utf8', +                'latin_1': 'latin1', +                'iso_8859_1': 'latin1', +            }.get(encoding.replace('-', '_'), encoding) +            realoutfile.write(DOC_TEMPLATE % +                dict(docclass  = self.docclass, +                     preamble  = self.preamble, +                     title     = self.title, +                     encoding  = encoding, +                     styledefs = self.get_style_defs(), +                     code      = outfile.getvalue())) + + +class LatexEmbeddedLexer(Lexer): +    """ +    This lexer takes one lexer as argument, the lexer for the language +    being formatted, and the left and right delimiters for escaped text. + +    First everything is scanned using the language lexer to obtain +    strings and comments. All other consecutive tokens are merged and +    the resulting text is scanned for escaped segments, which are given +    the Token.Escape type. Finally text that is not escaped is scanned +    again with the language lexer. +    """ +    def __init__(self, left, right, lang, **options): +        self.left = left +        self.right = right +        self.lang = lang +        Lexer.__init__(self, **options) + +    def get_tokens_unprocessed(self, text): +        # find and remove all the escape tokens (replace with an empty string) +        # this is very similar to DelegatingLexer.get_tokens_unprocessed. +        buffered = '' +        insertions = [] +        insertion_buf = [] +        for i, t, v in self._find_safe_escape_tokens(text): +            if t is None: +                if insertion_buf: +                    insertions.append((len(buffered), insertion_buf)) +                    insertion_buf = [] +                buffered += v +            else: +                insertion_buf.append((i, t, v)) +        if insertion_buf: +            insertions.append((len(buffered), insertion_buf)) +        return do_insertions(insertions, +                             self.lang.get_tokens_unprocessed(buffered)) + +    def _find_safe_escape_tokens(self, text): +        """ find escape tokens that are not in strings or comments """ +        for i, t, v in self._filter_to( +            self.lang.get_tokens_unprocessed(text), +            lambda t: t in Token.Comment or t in Token.String +        ): +            if t is None: +                for i2, t2, v2 in self._find_escape_tokens(v): +                    yield i + i2, t2, v2 +            else: +                yield i, None, v + +    def _filter_to(self, it, pred): +        """ Keep only the tokens that match `pred`, merge the others together """ +        buf = '' +        idx = 0 +        for i, t, v in it: +            if pred(t): +                if buf: +                    yield idx, None, buf +                    buf = '' +                yield i, t, v +            else: +                if not buf: +                    idx = i +                buf += v +        if buf: +            yield idx, None, buf + +    def _find_escape_tokens(self, text): +        """ Find escape tokens within text, give token=None otherwise """ +        index = 0 +        while text: +            a, sep1, text = text.partition(self.left) +            if a: +                yield index, None, a +                index += len(a) +            if sep1: +                b, sep2, text = text.partition(self.right) +                if sep2: +                    yield index + len(sep1), Token.Escape, b +                    index += len(sep1) + len(b) + len(sep2) +                else: +                    yield index, Token.Error, sep1 +                    index += len(sep1) +                    text = b diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/other.py b/venv/lib/python3.11/site-packages/pygments/formatters/other.py new file mode 100644 index 0000000..8004764 --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/other.py @@ -0,0 +1,161 @@ +""" +    pygments.formatters.other +    ~~~~~~~~~~~~~~~~~~~~~~~~~ + +    Other formatters: NullFormatter, RawTokenFormatter. + +    :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. +    :license: BSD, see LICENSE for details. +""" + +from pygments.formatter import Formatter +from pygments.util import get_choice_opt +from pygments.token import Token +from pygments.console import colorize + +__all__ = ['NullFormatter', 'RawTokenFormatter', 'TestcaseFormatter'] + + +class NullFormatter(Formatter): +    """ +    Output the text unchanged without any formatting. +    """ +    name = 'Text only' +    aliases = ['text', 'null'] +    filenames = ['*.txt'] + +    def format(self, tokensource, outfile): +        enc = self.encoding +        for ttype, value in tokensource: +            if enc: +                outfile.write(value.encode(enc)) +            else: +                outfile.write(value) + + +class RawTokenFormatter(Formatter): +    r""" +    Format tokens as a raw representation for storing token streams. + +    The format is ``tokentype<TAB>repr(tokenstring)\n``. The output can later +    be converted to a token stream with the `RawTokenLexer`, described in the +    :doc:`lexer list <lexers>`. + +    Only two options are accepted: + +    `compress` +        If set to ``'gz'`` or ``'bz2'``, compress the output with the given +        compression algorithm after encoding (default: ``''``). +    `error_color` +        If set to a color name, highlight error tokens using that color.  If +        set but with no value, defaults to ``'red'``. + +        .. versionadded:: 0.11 + +    """ +    name = 'Raw tokens' +    aliases = ['raw', 'tokens'] +    filenames = ['*.raw'] + +    unicodeoutput = False + +    def __init__(self, **options): +        Formatter.__init__(self, **options) +        # We ignore self.encoding if it is set, since it gets set for lexer +        # and formatter if given with -Oencoding on the command line. +        # The RawTokenFormatter outputs only ASCII. Override here. +        self.encoding = 'ascii'  # let pygments.format() do the right thing +        self.compress = get_choice_opt(options, 'compress', +                                       ['', 'none', 'gz', 'bz2'], '') +        self.error_color = options.get('error_color', None) +        if self.error_color is True: +            self.error_color = 'red' +        if self.error_color is not None: +            try: +                colorize(self.error_color, '') +            except KeyError: +                raise ValueError("Invalid color %r specified" % +                                 self.error_color) + +    def format(self, tokensource, outfile): +        try: +            outfile.write(b'') +        except TypeError: +            raise TypeError('The raw tokens formatter needs a binary ' +                            'output file') +        if self.compress == 'gz': +            import gzip +            outfile = gzip.GzipFile('', 'wb', 9, outfile) + +            write = outfile.write +            flush = outfile.close +        elif self.compress == 'bz2': +            import bz2 +            compressor = bz2.BZ2Compressor(9) + +            def write(text): +                outfile.write(compressor.compress(text)) + +            def flush(): +                outfile.write(compressor.flush()) +                outfile.flush() +        else: +            write = outfile.write +            flush = outfile.flush + +        if self.error_color: +            for ttype, value in tokensource: +                line = b"%r\t%r\n" % (ttype, value) +                if ttype is Token.Error: +                    write(colorize(self.error_color, line)) +                else: +                    write(line) +        else: +            for ttype, value in tokensource: +                write(b"%r\t%r\n" % (ttype, value)) +        flush() + + +TESTCASE_BEFORE = '''\ +    def testNeedsName(lexer): +        fragment = %r +        tokens = [ +''' +TESTCASE_AFTER = '''\ +        ] +        assert list(lexer.get_tokens(fragment)) == tokens +''' + + +class TestcaseFormatter(Formatter): +    """ +    Format tokens as appropriate for a new testcase. + +    .. versionadded:: 2.0 +    """ +    name = 'Testcase' +    aliases = ['testcase'] + +    def __init__(self, **options): +        Formatter.__init__(self, **options) +        if self.encoding is not None and self.encoding != 'utf-8': +            raise ValueError("Only None and utf-8 are allowed encodings.") + +    def format(self, tokensource, outfile): +        indentation = ' ' * 12 +        rawbuf = [] +        outbuf = [] +        for ttype, value in tokensource: +            rawbuf.append(value) +            outbuf.append('%s(%s, %r),\n' % (indentation, ttype, value)) + +        before = TESTCASE_BEFORE % (''.join(rawbuf),) +        during = ''.join(outbuf) +        after = TESTCASE_AFTER +        if self.encoding is None: +            outfile.write(before + during + after) +        else: +            outfile.write(before.encode('utf-8')) +            outfile.write(during.encode('utf-8')) +            outfile.write(after.encode('utf-8')) +        outfile.flush() diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/pangomarkup.py b/venv/lib/python3.11/site-packages/pygments/formatters/pangomarkup.py new file mode 100644 index 0000000..50872fe --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/pangomarkup.py @@ -0,0 +1,83 @@ +""" +    pygments.formatters.pangomarkup +    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +    Formatter for Pango markup output. + +    :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. +    :license: BSD, see LICENSE for details. +""" + +from pygments.formatter import Formatter + + +__all__ = ['PangoMarkupFormatter'] + + +_escape_table = { +    ord('&'): '&', +    ord('<'): '<', +} + + +def escape_special_chars(text, table=_escape_table): +    """Escape & and < for Pango Markup.""" +    return text.translate(table) + + +class PangoMarkupFormatter(Formatter): +    """ +    Format tokens as Pango Markup code. It can then be rendered to an SVG. + +    .. versionadded:: 2.9 +    """ + +    name = 'Pango Markup' +    aliases = ['pango', 'pangomarkup'] +    filenames = [] + +    def __init__(self, **options): +        Formatter.__init__(self, **options) + +        self.styles = {} + +        for token, style in self.style: +            start = '' +            end = '' +            if style['color']: +                start += '<span fgcolor="#%s">' % style['color'] +                end = '</span>' + end +            if style['bold']: +                start += '<b>' +                end = '</b>' + end +            if style['italic']: +                start += '<i>' +                end = '</i>' + end +            if style['underline']: +                start += '<u>' +                end = '</u>' + end +            self.styles[token] = (start, end) + +    def format_unencoded(self, tokensource, outfile): +        lastval = '' +        lasttype = None + +        outfile.write('<tt>') + +        for ttype, value in tokensource: +            while ttype not in self.styles: +                ttype = ttype.parent +            if ttype == lasttype: +                lastval += escape_special_chars(value) +            else: +                if lastval: +                    stylebegin, styleend = self.styles[lasttype] +                    outfile.write(stylebegin + lastval + styleend) +                lastval = escape_special_chars(value) +                lasttype = ttype + +        if lastval: +            stylebegin, styleend = self.styles[lasttype] +            outfile.write(stylebegin + lastval + styleend) + +        outfile.write('</tt>') diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/rtf.py b/venv/lib/python3.11/site-packages/pygments/formatters/rtf.py new file mode 100644 index 0000000..d3a83fa --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/rtf.py @@ -0,0 +1,146 @@ +""" +    pygments.formatters.rtf +    ~~~~~~~~~~~~~~~~~~~~~~~ + +    A formatter that generates RTF files. + +    :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. +    :license: BSD, see LICENSE for details. +""" + +from pygments.formatter import Formatter +from pygments.util import get_int_opt, surrogatepair + + +__all__ = ['RtfFormatter'] + + +class RtfFormatter(Formatter): +    """ +    Format tokens as RTF markup. This formatter automatically outputs full RTF +    documents with color information and other useful stuff. Perfect for Copy and +    Paste into Microsoft(R) Word(R) documents. + +    Please note that ``encoding`` and ``outencoding`` options are ignored. +    The RTF format is ASCII natively, but handles unicode characters correctly +    thanks to escape sequences. + +    .. versionadded:: 0.6 + +    Additional options accepted: + +    `style` +        The style to use, can be a string or a Style subclass (default: +        ``'default'``). + +    `fontface` +        The used font family, for example ``Bitstream Vera Sans``. Defaults to +        some generic font which is supposed to have fixed width. + +    `fontsize` +        Size of the font used. Size is specified in half points. The +        default is 24 half-points, giving a size 12 font. + +        .. versionadded:: 2.0 +    """ +    name = 'RTF' +    aliases = ['rtf'] +    filenames = ['*.rtf'] + +    def __init__(self, **options): +        r""" +        Additional options accepted: + +        ``fontface`` +            Name of the font used. Could for example be ``'Courier New'`` +            to further specify the default which is ``'\fmodern'``. The RTF +            specification claims that ``\fmodern`` are "Fixed-pitch serif +            and sans serif fonts". Hope every RTF implementation thinks +            the same about modern... + +        """ +        Formatter.__init__(self, **options) +        self.fontface = options.get('fontface') or '' +        self.fontsize = get_int_opt(options, 'fontsize', 0) + +    def _escape(self, text): +        return text.replace('\\', '\\\\') \ +                   .replace('{', '\\{') \ +                   .replace('}', '\\}') + +    def _escape_text(self, text): +        # empty strings, should give a small performance improvement +        if not text: +            return '' + +        # escape text +        text = self._escape(text) + +        buf = [] +        for c in text: +            cn = ord(c) +            if cn < (2**7): +                # ASCII character +                buf.append(str(c)) +            elif (2**7) <= cn < (2**16): +                # single unicode escape sequence +                buf.append('{\\u%d}' % cn) +            elif (2**16) <= cn: +                # RTF limits unicode to 16 bits. +                # Force surrogate pairs +                buf.append('{\\u%d}{\\u%d}' % surrogatepair(cn)) + +        return ''.join(buf).replace('\n', '\\par\n') + +    def format_unencoded(self, tokensource, outfile): +        # rtf 1.8 header +        outfile.write('{\\rtf1\\ansi\\uc0\\deff0' +                      '{\\fonttbl{\\f0\\fmodern\\fprq1\\fcharset0%s;}}' +                      '{\\colortbl;' % (self.fontface and +                                        ' ' + self._escape(self.fontface) or +                                        '')) + +        # convert colors and save them in a mapping to access them later. +        color_mapping = {} +        offset = 1 +        for _, style in self.style: +            for color in style['color'], style['bgcolor'], style['border']: +                if color and color not in color_mapping: +                    color_mapping[color] = offset +                    outfile.write('\\red%d\\green%d\\blue%d;' % ( +                        int(color[0:2], 16), +                        int(color[2:4], 16), +                        int(color[4:6], 16) +                    )) +                    offset += 1 +        outfile.write('}\\f0 ') +        if self.fontsize: +            outfile.write('\\fs%d' % self.fontsize) + +        # highlight stream +        for ttype, value in tokensource: +            while not self.style.styles_token(ttype) and ttype.parent: +                ttype = ttype.parent +            style = self.style.style_for_token(ttype) +            buf = [] +            if style['bgcolor']: +                buf.append('\\cb%d' % color_mapping[style['bgcolor']]) +            if style['color']: +                buf.append('\\cf%d' % color_mapping[style['color']]) +            if style['bold']: +                buf.append('\\b') +            if style['italic']: +                buf.append('\\i') +            if style['underline']: +                buf.append('\\ul') +            if style['border']: +                buf.append('\\chbrdr\\chcfpat%d' % +                           color_mapping[style['border']]) +            start = ''.join(buf) +            if start: +                outfile.write('{%s ' % start) +            outfile.write(self._escape_text(value)) +            if start: +                outfile.write('}') + +        outfile.write('}') diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/svg.py b/venv/lib/python3.11/site-packages/pygments/formatters/svg.py new file mode 100644 index 0000000..e3cd269 --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/svg.py @@ -0,0 +1,188 @@ +""" +    pygments.formatters.svg +    ~~~~~~~~~~~~~~~~~~~~~~~ + +    Formatter for SVG output. + +    :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. +    :license: BSD, see LICENSE for details. +""" + +from pygments.formatter import Formatter +from pygments.token import Comment +from pygments.util import get_bool_opt, get_int_opt + +__all__ = ['SvgFormatter'] + + +def escape_html(text): +    """Escape &, <, > as well as single and double quotes for HTML.""" +    return text.replace('&', '&').  \ +                replace('<', '<').   \ +                replace('>', '>').   \ +                replace('"', '"'). \ +                replace("'", ''') + + +class2style = {} + +class SvgFormatter(Formatter): +    """ +    Format tokens as an SVG graphics file.  This formatter is still experimental. +    Each line of code is a ``<text>`` element with explicit ``x`` and ``y`` +    coordinates containing ``<tspan>`` elements with the individual token styles. + +    By default, this formatter outputs a full SVG document including doctype +    declaration and the ``<svg>`` root element. + +    .. versionadded:: 0.9 + +    Additional options accepted: + +    `nowrap` +        Don't wrap the SVG ``<text>`` elements in ``<svg><g>`` elements and +        don't add a XML declaration and a doctype.  If true, the `fontfamily` +        and `fontsize` options are ignored.  Defaults to ``False``. + +    `fontfamily` +        The value to give the wrapping ``<g>`` element's ``font-family`` +        attribute, defaults to ``"monospace"``. + +    `fontsize` +        The value to give the wrapping ``<g>`` element's ``font-size`` +        attribute, defaults to ``"14px"``. + +    `linenos` +        If ``True``, add line numbers (default: ``False``). + +    `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. +         +    `linenowidth` +        Maximum width devoted to line numbers (default: ``3*ystep``, sufficient +        for up to 4-digit line numbers. Increase width for longer code blocks).   +         +    `xoffset` +        Starting offset in X direction, defaults to ``0``. + +    `yoffset` +        Starting offset in Y direction, defaults to the font size if it is given +        in pixels, or ``20`` else.  (This is necessary since text coordinates +        refer to the text baseline, not the top edge.) + +    `ystep` +        Offset to add to the Y coordinate for each subsequent line.  This should +        roughly be the text size plus 5.  It defaults to that value if the text +        size is given in pixels, or ``25`` else. + +    `spacehack` +        Convert spaces in the source to `` ``, which are non-breaking +        spaces.  SVG provides the ``xml:space`` attribute to control how +        whitespace inside tags is handled, in theory, the ``preserve`` value +        could be used to keep all whitespace as-is.  However, many current SVG +        viewers don't obey that rule, so this option is provided as a workaround +        and defaults to ``True``. +    """ +    name = 'SVG' +    aliases = ['svg'] +    filenames = ['*.svg'] + +    def __init__(self, **options): +        Formatter.__init__(self, **options) +        self.nowrap = get_bool_opt(options, 'nowrap', False) +        self.fontfamily = options.get('fontfamily', 'monospace') +        self.fontsize = options.get('fontsize', '14px') +        self.xoffset = get_int_opt(options, 'xoffset', 0) +        fs = self.fontsize.strip() +        if fs.endswith('px'): fs = fs[:-2].strip() +        try: +            int_fs = int(fs) +        except: +            int_fs = 20 +        self.yoffset = get_int_opt(options, 'yoffset', int_fs) +        self.ystep = get_int_opt(options, 'ystep', int_fs + 5) +        self.spacehack = get_bool_opt(options, 'spacehack', True) +        self.linenos = get_bool_opt(options,'linenos',False) +        self.linenostart = get_int_opt(options,'linenostart',1) +        self.linenostep = get_int_opt(options,'linenostep',1) +        self.linenowidth = get_int_opt(options,'linenowidth', 3*self.ystep) +        self._stylecache = {} + +    def format_unencoded(self, tokensource, outfile): +        """ +        Format ``tokensource``, an iterable of ``(tokentype, tokenstring)`` +        tuples and write it into ``outfile``. + +        For our implementation we put all lines in their own 'line group'. +        """ +        x = self.xoffset +        y = self.yoffset +        if not self.nowrap: +            if self.encoding: +                outfile.write('<?xml version="1.0" encoding="%s"?>\n' % +                              self.encoding) +            else: +                outfile.write('<?xml version="1.0"?>\n') +            outfile.write('<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" ' +                          '"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/' +                          'svg10.dtd">\n') +            outfile.write('<svg xmlns="http://www.w3.org/2000/svg">\n') +            outfile.write('<g font-family="%s" font-size="%s">\n' % +                          (self.fontfamily, self.fontsize)) +         +        counter = self.linenostart  +        counter_step = self.linenostep +        counter_style = self._get_style(Comment) +        line_x = x +         +        if self.linenos: +            if counter % counter_step == 0: +                outfile.write('<text x="%s" y="%s" %s text-anchor="end">%s</text>' % +                    (x+self.linenowidth,y,counter_style,counter)) +            line_x += self.linenowidth + self.ystep +            counter += 1 + +        outfile.write('<text x="%s" y="%s" xml:space="preserve">' % (line_x, y)) +        for ttype, value in tokensource: +            style = self._get_style(ttype) +            tspan = style and '<tspan' + style + '>' or '' +            tspanend = tspan and '</tspan>' or '' +            value = escape_html(value) +            if self.spacehack: +                value = value.expandtabs().replace(' ', ' ') +            parts = value.split('\n') +            for part in parts[:-1]: +                outfile.write(tspan + part + tspanend) +                y += self.ystep +                outfile.write('</text>\n') +                if self.linenos and counter % counter_step == 0: +                    outfile.write('<text x="%s" y="%s" text-anchor="end" %s>%s</text>' % +                        (x+self.linenowidth,y,counter_style,counter)) +                 +                counter += 1 +                outfile.write('<text x="%s" y="%s" ' 'xml:space="preserve">' % (line_x,y)) +            outfile.write(tspan + parts[-1] + tspanend) +        outfile.write('</text>') + +        if not self.nowrap: +            outfile.write('</g></svg>\n') + +    def _get_style(self, tokentype): +        if tokentype in self._stylecache: +            return self._stylecache[tokentype] +        otokentype = tokentype +        while not self.style.styles_token(tokentype): +            tokentype = tokentype.parent +        value = self.style.style_for_token(tokentype) +        result = '' +        if value['color']: +            result = ' fill="#' + value['color'] + '"' +        if value['bold']: +            result += ' font-weight="bold"' +        if value['italic']: +            result += ' font-style="italic"' +        self._stylecache[otokentype] = result +        return result diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/terminal.py b/venv/lib/python3.11/site-packages/pygments/formatters/terminal.py new file mode 100644 index 0000000..636f350 --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/terminal.py @@ -0,0 +1,127 @@ +""" +    pygments.formatters.terminal +    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +    Formatter for terminal output with ANSI sequences. + +    :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. +    :license: BSD, see LICENSE for details. +""" + +from pygments.formatter import Formatter +from pygments.token import Keyword, Name, Comment, String, Error, \ +    Number, Operator, Generic, Token, Whitespace +from pygments.console import ansiformat +from pygments.util import get_choice_opt + + +__all__ = ['TerminalFormatter'] + + +#: Map token types to a tuple of color values for light and dark +#: backgrounds. +TERMINAL_COLORS = { +    Token:              ('',            ''), + +    Whitespace:         ('gray',   'brightblack'), +    Comment:            ('gray',   'brightblack'), +    Comment.Preproc:    ('cyan',        'brightcyan'), +    Keyword:            ('blue',    'brightblue'), +    Keyword.Type:       ('cyan',        'brightcyan'), +    Operator.Word:      ('magenta',      'brightmagenta'), +    Name.Builtin:       ('cyan',        'brightcyan'), +    Name.Function:      ('green',   'brightgreen'), +    Name.Namespace:     ('_cyan_',      '_brightcyan_'), +    Name.Class:         ('_green_', '_brightgreen_'), +    Name.Exception:     ('cyan',        'brightcyan'), +    Name.Decorator:     ('brightblack',    'gray'), +    Name.Variable:      ('red',     'brightred'), +    Name.Constant:      ('red',     'brightred'), +    Name.Attribute:     ('cyan',        'brightcyan'), +    Name.Tag:           ('brightblue',        'brightblue'), +    String:             ('yellow',       'yellow'), +    Number:             ('blue',    'brightblue'), + +    Generic.Deleted:    ('brightred',        'brightred'), +    Generic.Inserted:   ('green',  'brightgreen'), +    Generic.Heading:    ('**',         '**'), +    Generic.Subheading: ('*magenta*',   '*brightmagenta*'), +    Generic.Prompt:     ('**',         '**'), +    Generic.Error:      ('brightred',        'brightred'), + +    Error:              ('_brightred_',      '_brightred_'), +} + + +class TerminalFormatter(Formatter): +    r""" +    Format tokens with ANSI color sequences, for output in a text console. +    Color sequences are terminated at newlines, so that paging the output +    works correctly. + +    The `get_style_defs()` method doesn't do anything special since there is +    no support for common styles. + +    Options accepted: + +    `bg` +        Set to ``"light"`` or ``"dark"`` depending on the terminal's background +        (default: ``"light"``). + +    `colorscheme` +        A dictionary mapping token types to (lightbg, darkbg) color names or +        ``None`` (default: ``None`` = use builtin colorscheme). + +    `linenos` +        Set to ``True`` to have line numbers on the terminal output as well +        (default: ``False`` = no line numbers). +    """ +    name = 'Terminal' +    aliases = ['terminal', 'console'] +    filenames = [] + +    def __init__(self, **options): +        Formatter.__init__(self, **options) +        self.darkbg = get_choice_opt(options, 'bg', +                                     ['light', 'dark'], 'light') == 'dark' +        self.colorscheme = options.get('colorscheme', None) or TERMINAL_COLORS +        self.linenos = options.get('linenos', False) +        self._lineno = 0 + +    def format(self, tokensource, outfile): +        return Formatter.format(self, tokensource, outfile) + +    def _write_lineno(self, outfile): +        self._lineno += 1 +        outfile.write("%s%04d: " % (self._lineno != 1 and '\n' or '', self._lineno)) + +    def _get_color(self, ttype): +        # self.colorscheme is a dict containing usually generic types, so we +        # have to walk the tree of dots.  The base Token type must be a key, +        # even if it's empty string, as in the default above. +        colors = self.colorscheme.get(ttype) +        while colors is None: +            ttype = ttype.parent +            colors = self.colorscheme.get(ttype) +        return colors[self.darkbg] + +    def format_unencoded(self, tokensource, outfile): +        if self.linenos: +            self._write_lineno(outfile) + +        for ttype, value in tokensource: +            color = self._get_color(ttype) + +            for line in value.splitlines(True): +                if color: +                    outfile.write(ansiformat(color, line.rstrip('\n'))) +                else: +                    outfile.write(line.rstrip('\n')) +                if line.endswith('\n'): +                    if self.linenos: +                        self._write_lineno(outfile) +                    else: +                        outfile.write('\n') + +        if self.linenos: +            outfile.write("\n") diff --git a/venv/lib/python3.11/site-packages/pygments/formatters/terminal256.py b/venv/lib/python3.11/site-packages/pygments/formatters/terminal256.py new file mode 100644 index 0000000..dba5b63 --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/formatters/terminal256.py @@ -0,0 +1,338 @@ +""" +    pygments.formatters.terminal256 +    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +    Formatter for 256-color terminal output with ANSI sequences. + +    RGB-to-XTERM color conversion routines adapted from xterm256-conv +    tool (http://frexx.de/xterm-256-notes/data/xterm256-conv2.tar.bz2) +    by Wolfgang Frisch. + +    Formatter version 1. + +    :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. +    :license: BSD, see LICENSE for details. +""" + +# TODO: +#  - Options to map style's bold/underline/italic/border attributes +#    to some ANSI attrbutes (something like 'italic=underline') +#  - An option to output "style RGB to xterm RGB/index" conversion table +#  - An option to indicate that we are running in "reverse background" +#    xterm. This means that default colors are white-on-black, not +#    black-on-while, so colors like "white background" need to be converted +#    to "white background, black foreground", etc... + +from pygments.formatter import Formatter +from pygments.console import codes +from pygments.style import ansicolors + + +__all__ = ['Terminal256Formatter', 'TerminalTrueColorFormatter'] + + +class EscapeSequence: +    def __init__(self, fg=None, bg=None, bold=False, underline=False, italic=False): +        self.fg = fg +        self.bg = bg +        self.bold = bold +        self.underline = underline +        self.italic = italic + +    def escape(self, attrs): +        if len(attrs): +            return "\x1b[" + ";".join(attrs) + "m" +        return "" + +    def color_string(self): +        attrs = [] +        if self.fg is not None: +            if self.fg in ansicolors: +                esc = codes[self.fg.replace('ansi','')] +                if ';01m' in esc: +                    self.bold = True +                # extract fg color code. +                attrs.append(esc[2:4]) +            else: +                attrs.extend(("38", "5", "%i" % self.fg)) +        if self.bg is not None: +            if self.bg in ansicolors: +                esc = codes[self.bg.replace('ansi','')] +                # extract fg color code, add 10 for bg. +                attrs.append(str(int(esc[2:4])+10)) +            else: +                attrs.extend(("48", "5", "%i" % self.bg)) +        if self.bold: +            attrs.append("01") +        if self.underline: +            attrs.append("04") +        if self.italic: +            attrs.append("03") +        return self.escape(attrs) + +    def true_color_string(self): +        attrs = [] +        if self.fg: +            attrs.extend(("38", "2", str(self.fg[0]), str(self.fg[1]), str(self.fg[2]))) +        if self.bg: +            attrs.extend(("48", "2", str(self.bg[0]), str(self.bg[1]), str(self.bg[2]))) +        if self.bold: +            attrs.append("01") +        if self.underline: +            attrs.append("04") +        if self.italic: +            attrs.append("03") +        return self.escape(attrs) + +    def reset_string(self): +        attrs = [] +        if self.fg is not None: +            attrs.append("39") +        if self.bg is not None: +            attrs.append("49") +        if self.bold or self.underline or self.italic: +            attrs.append("00") +        return self.escape(attrs) + + +class Terminal256Formatter(Formatter): +    """ +    Format tokens with ANSI color sequences, for output in a 256-color +    terminal or console.  Like in `TerminalFormatter` color sequences +    are terminated at newlines, so that paging the output works correctly. + +    The formatter takes colors from a style defined by the `style` option +    and converts them to nearest ANSI 256-color escape sequences. Bold and +    underline attributes from the style are preserved (and displayed). + +    .. versionadded:: 0.9 + +    .. versionchanged:: 2.2 +       If the used style defines foreground colors in the form ``#ansi*``, then +       `Terminal256Formatter` will map these to non extended foreground color. +       See :ref:`AnsiTerminalStyle` for more information. + +    .. versionchanged:: 2.4 +       The ANSI color names have been updated with names that are easier to +       understand and align with colornames of other projects and terminals. +       See :ref:`this table <new-ansi-color-names>` for more information. + + +    Options accepted: + +    `style` +        The style to use, can be a string or a Style subclass (default: +        ``'default'``). + +    `linenos` +        Set to ``True`` to have line numbers on the terminal output as well +        (default: ``False`` = no line numbers). +    """ +    name = 'Terminal256' +    aliases = ['terminal256', 'console256', '256'] +    filenames = [] + +    def __init__(self, **options): +        Formatter.__init__(self, **options) + +        self.xterm_colors = [] +        self.best_match = {} +        self.style_string = {} + +        self.usebold = 'nobold' not in options +        self.useunderline = 'nounderline' not in options +        self.useitalic = 'noitalic' not in options + +        self._build_color_table()  # build an RGB-to-256 color conversion table +        self._setup_styles()  # convert selected style's colors to term. colors + +        self.linenos = options.get('linenos', False) +        self._lineno = 0 + +    def _build_color_table(self): +        # colors 0..15: 16 basic colors + +        self.xterm_colors.append((0x00, 0x00, 0x00))  # 0 +        self.xterm_colors.append((0xcd, 0x00, 0x00))  # 1 +        self.xterm_colors.append((0x00, 0xcd, 0x00))  # 2 +        self.xterm_colors.append((0xcd, 0xcd, 0x00))  # 3 +        self.xterm_colors.append((0x00, 0x00, 0xee))  # 4 +        self.xterm_colors.append((0xcd, 0x00, 0xcd))  # 5 +        self.xterm_colors.append((0x00, 0xcd, 0xcd))  # 6 +        self.xterm_colors.append((0xe5, 0xe5, 0xe5))  # 7 +        self.xterm_colors.append((0x7f, 0x7f, 0x7f))  # 8 +        self.xterm_colors.append((0xff, 0x00, 0x00))  # 9 +        self.xterm_colors.append((0x00, 0xff, 0x00))  # 10 +        self.xterm_colors.append((0xff, 0xff, 0x00))  # 11 +        self.xterm_colors.append((0x5c, 0x5c, 0xff))  # 12 +        self.xterm_colors.append((0xff, 0x00, 0xff))  # 13 +        self.xterm_colors.append((0x00, 0xff, 0xff))  # 14 +        self.xterm_colors.append((0xff, 0xff, 0xff))  # 15 + +        # colors 16..232: the 6x6x6 color cube + +        valuerange = (0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff) + +        for i in range(217): +            r = valuerange[(i // 36) % 6] +            g = valuerange[(i // 6) % 6] +            b = valuerange[i % 6] +            self.xterm_colors.append((r, g, b)) + +        # colors 233..253: grayscale + +        for i in range(1, 22): +            v = 8 + i * 10 +            self.xterm_colors.append((v, v, v)) + +    def _closest_color(self, r, g, b): +        distance = 257*257*3  # "infinity" (>distance from #000000 to #ffffff) +        match = 0 + +        for i in range(0, 254): +            values = self.xterm_colors[i] + +            rd = r - values[0] +            gd = g - values[1] +            bd = b - values[2] +            d = rd*rd + gd*gd + bd*bd + +            if d < distance: +                match = i +                distance = d +        return match + +    def _color_index(self, color): +        index = self.best_match.get(color, None) +        if color in ansicolors: +            # strip the `ansi/#ansi` part and look up code +            index = color +            self.best_match[color] = index +        if index is None: +            try: +                rgb = int(str(color), 16) +            except ValueError: +                rgb = 0 + +            r = (rgb >> 16) & 0xff +            g = (rgb >> 8) & 0xff +            b = rgb & 0xff +            index = self._closest_color(r, g, b) +            self.best_match[color] = index +        return index + +    def _setup_styles(self): +        for ttype, ndef in self.style: +            escape = EscapeSequence() +            # get foreground from ansicolor if set +            if ndef['ansicolor']: +                escape.fg = self._color_index(ndef['ansicolor']) +            elif ndef['color']: +                escape.fg = self._color_index(ndef['color']) +            if ndef['bgansicolor']: +                escape.bg = self._color_index(ndef['bgansicolor']) +            elif ndef['bgcolor']: +                escape.bg = self._color_index(ndef['bgcolor']) +            if self.usebold and ndef['bold']: +                escape.bold = True +            if self.useunderline and ndef['underline']: +                escape.underline = True +            if self.useitalic and ndef['italic']: +                escape.italic = True +            self.style_string[str(ttype)] = (escape.color_string(), +                                             escape.reset_string()) + +    def _write_lineno(self, outfile): +        self._lineno += 1 +        outfile.write("%s%04d: " % (self._lineno != 1 and '\n' or '', self._lineno)) + +    def format(self, tokensource, outfile): +        return Formatter.format(self, tokensource, outfile) + +    def format_unencoded(self, tokensource, outfile): +        if self.linenos: +            self._write_lineno(outfile) + +        for ttype, value in tokensource: +            not_found = True +            while ttype and not_found: +                try: +                    # outfile.write( "<" + str(ttype) + ">" ) +                    on, off = self.style_string[str(ttype)] + +                    # Like TerminalFormatter, add "reset colors" escape sequence +                    # on newline. +                    spl = value.split('\n') +                    for line in spl[:-1]: +                        if line: +                            outfile.write(on + line + off) +                        if self.linenos: +                            self._write_lineno(outfile) +                        else: +                            outfile.write('\n') + +                    if spl[-1]: +                        outfile.write(on + spl[-1] + off) + +                    not_found = False +                    # outfile.write( '#' + str(ttype) + '#' ) + +                except KeyError: +                    # ottype = ttype +                    ttype = ttype.parent +                    # outfile.write( '!' + str(ottype) + '->' + str(ttype) + '!' ) + +            if not_found: +                outfile.write(value) + +        if self.linenos: +            outfile.write("\n") + + + +class TerminalTrueColorFormatter(Terminal256Formatter): +    r""" +    Format tokens with ANSI color sequences, for output in a true-color +    terminal or console.  Like in `TerminalFormatter` color sequences +    are terminated at newlines, so that paging the output works correctly. + +    .. versionadded:: 2.1 + +    Options accepted: + +    `style` +        The style to use, can be a string or a Style subclass (default: +        ``'default'``). +    """ +    name = 'TerminalTrueColor' +    aliases = ['terminal16m', 'console16m', '16m'] +    filenames = [] + +    def _build_color_table(self): +        pass + +    def _color_tuple(self, color): +        try: +            rgb = int(str(color), 16) +        except ValueError: +            return None +        r = (rgb >> 16) & 0xff +        g = (rgb >> 8) & 0xff +        b = rgb & 0xff +        return (r, g, b) + +    def _setup_styles(self): +        for ttype, ndef in self.style: +            escape = EscapeSequence() +            if ndef['color']: +                escape.fg = self._color_tuple(ndef['color']) +            if ndef['bgcolor']: +                escape.bg = self._color_tuple(ndef['bgcolor']) +            if self.usebold and ndef['bold']: +                escape.bold = True +            if self.useunderline and ndef['underline']: +                escape.underline = True +            if self.useitalic and ndef['italic']: +                escape.italic = True +            self.style_string[str(ttype)] = (escape.true_color_string(), +                                             escape.reset_string()) | 
