Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
html.py998 linesDownload Raw Back to formatters
1"""2    pygments.formatters.html3    ~~~~~~~~~~~~~~~~~~~~~~~~4 5    Formatter for HTML output.6 7    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.8    :license: BSD, see LICENSE for details.9"""10 11import functools12import os13import sys14import os.path15from io import StringIO16 17from pygments.formatter import Formatter18from pygments.token import Token, Text, STANDARD_TYPES19from pygments.util import get_bool_opt, get_int_opt, get_list_opt20 21import html22 23try:24    import ctags25except ImportError:26    ctags = None27 28__all__ = ['HtmlFormatter']29 30 31_escape_html_table = {32    ord('&'): '&amp;',33    ord('<'): '&lt;',34    ord('>'): '&gt;',35    ord('"'): '&quot;',36    ord("'"): '&#39;',37}38 39 40def escape_html(text, table=_escape_html_table):41    """Escape &, <, > as well as single and double quotes for HTML."""42    return text.translate(table)43 44 45def webify(color):46    if color.startswith('calc') or color.startswith('var'):47        return color48    else:49        # Check if the color can be shortened from 6 to 3 characters50        color = color.upper()51        if (len(color) == 6 and52            (    color[0] == color[1]53             and color[2] == color[3]54             and color[4] == color[5])):55            return f'#{color[0]}{color[2]}{color[4]}'56        else:57            return f'#{color}'58 59 60def _get_ttype_class(ttype):61    fname = STANDARD_TYPES.get(ttype)62    if fname:63        return fname64    aname = ''65    while fname is None:66        aname = '-' + ttype[-1] + aname67        ttype = ttype.parent68        fname = STANDARD_TYPES.get(ttype)69    return fname + aname70 71 72CSSFILE_TEMPLATE = '''\73/*74generated by Pygments <https://pygments.org/>75Copyright 2006-present by the Pygments team.76Licensed under the BSD license, see LICENSE for details.77*/78%(styledefs)s79'''80 81DOC_HEADER = '''\82<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"83   "http://www.w3.org/TR/html4/strict.dtd">84<!--85generated by Pygments <https://pygments.org/>86Copyright 2006-present by the Pygments team.87Licensed under the BSD license, see LICENSE for details.88-->89<html>90<head>91  <title>%(title)s</title>92  <meta http-equiv="content-type" content="text/html; charset=%(encoding)s">93  <style type="text/css">94''' + CSSFILE_TEMPLATE + '''95  </style>96</head>97<body>98<h2>%(title)s</h2>99 100'''101 102DOC_HEADER_EXTERNALCSS = '''\103<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"104   "http://www.w3.org/TR/html4/strict.dtd">105 106<html>107<head>108  <title>%(title)s</title>109  <meta http-equiv="content-type" content="text/html; charset=%(encoding)s">110  <link rel="stylesheet" href="%(cssfile)s" type="text/css">111</head>112<body>113<h2>%(title)s</h2>114 115'''116 117DOC_FOOTER = '''\118</body>119</html>120'''121 122 123class HtmlFormatter(Formatter):124    r"""125    Format tokens as HTML 4 ``<span>`` tags. By default, the content is enclosed126    in a ``<pre>`` tag, itself wrapped in a ``<div>`` tag (but see the `nowrap` option).127    The ``<div>``'s CSS class can be set by the `cssclass` option.128 129    If the `linenos` option is set to ``"table"``, the ``<pre>`` is130    additionally wrapped inside a ``<table>`` which has one row and two131    cells: one containing the line numbers and one containing the code.132    Example:133 134    .. sourcecode:: html135 136        <div class="highlight" >137        <table><tr>138          <td class="linenos" title="click to toggle"139            onclick="with (this.firstChild.style)140                     { display = (display == '') ? 'none' : '' }">141            <pre>1142            2</pre>143          </td>144          <td class="code">145            <pre><span class="Ke">def </span><span class="NaFu">foo</span>(bar):146              <span class="Ke">pass</span>147            </pre>148          </td>149        </tr></table></div>150 151    (whitespace added to improve clarity).152 153    A list of lines can be specified using the `hl_lines` option to make these154    lines highlighted (as of Pygments 0.11).155 156    With the `full` option, a complete HTML 4 document is output, including157    the style definitions inside a ``<style>`` tag, or in a separate file if158    the `cssfile` option is given.159 160    When `tagsfile` is set to the path of a ctags index file, it is used to161    generate hyperlinks from names to their definition.  You must enable162    `lineanchors` and run ctags with the `-n` option for this to work.  The163    `python-ctags` module from PyPI must be installed to use this feature;164    otherwise a `RuntimeError` will be raised.165 166    The `get_style_defs(arg='')` method of a `HtmlFormatter` returns a string167    containing CSS rules for the CSS classes used by the formatter. The168    argument `arg` can be used to specify additional CSS selectors that169    are prepended to the classes. A call `fmter.get_style_defs('td .code')`170    would result in the following CSS classes:171 172    .. sourcecode:: css173 174        td .code .kw { font-weight: bold; color: #00FF00 }175        td .code .cm { color: #999999 }176        ...177 178    If you have Pygments 0.6 or higher, you can also pass a list or tuple to the179    `get_style_defs()` method to request multiple prefixes for the tokens:180 181    .. sourcecode:: python182 183        formatter.get_style_defs(['div.syntax pre', 'pre.syntax'])184 185    The output would then look like this:186 187    .. sourcecode:: css188 189        div.syntax pre .kw,190        pre.syntax .kw { font-weight: bold; color: #00FF00 }191        div.syntax pre .cm,192        pre.syntax .cm { color: #999999 }193        ...194 195    Additional options accepted:196 197    `nowrap`198        If set to ``True``, don't add a ``<pre>`` and a ``<div>`` tag199        around the tokens. This disables most other options (default: ``False``).200 201    `full`202        Tells the formatter to output a "full" document, i.e. a complete203        self-contained document (default: ``False``).204 205    `title`206        If `full` is true, the title that should be used to caption the207        document (default: ``''``).208 209    `style`210        The style to use, can be a string or a Style subclass (default:211        ``'default'``). This option has no effect if the `cssfile`212        and `noclobber_cssfile` option are given and the file specified in213        `cssfile` exists.214 215    `noclasses`216        If set to true, token ``<span>`` tags (as well as line number elements)217        will not use CSS classes, but inline styles. This is not recommended218        for larger pieces of code since it increases output size by quite a bit219        (default: ``False``).220 221    `classprefix`222        Since the token types use relatively short class names, they may clash223        with some of your own class names. In this case you can use the224        `classprefix` option to give a string to prepend to all Pygments-generated225        CSS class names for token types.226        Note that this option also affects the output of `get_style_defs()`.227 228    `cssclass`229        CSS class for the wrapping ``<div>`` tag (default: ``'highlight'``).230        If you set this option, the default selector for `get_style_defs()`231        will be this class.232 233        .. versionadded:: 0.9234           If you select the ``'table'`` line numbers, the wrapping table will235           have a CSS class of this string plus ``'table'``, the default is236           accordingly ``'highlighttable'``.237 238    `cssstyles`239        Inline CSS styles for the wrapping ``<div>`` tag (default: ``''``).240 241    `prestyles`242        Inline CSS styles for the ``<pre>`` tag (default: ``''``).243 244        .. versionadded:: 0.11245 246    `cssfile`247        If the `full` option is true and this option is given, it must be the248        name of an external file. If the filename does not include an absolute249        path, the file's path will be assumed to be relative to the main output250        file's path, if the latter can be found. The stylesheet is then written251        to this file instead of the HTML file.252 253        .. versionadded:: 0.6254 255    `noclobber_cssfile`256        If `cssfile` is given and the specified file exists, the css file will257        not be overwritten. This allows the use of the `full` option in258        combination with a user specified css file. Default is ``False``.259 260        .. versionadded:: 1.1261 262    `linenos`263        If set to ``'table'``, output line numbers as a table with two cells,264        one containing the line numbers, the other the whole code.  This is265        copy-and-paste-friendly, but may cause alignment problems with some266        browsers or fonts.  If set to ``'inline'``, the line numbers will be267        integrated in the ``<pre>`` tag that contains the code (that setting268        is *new in Pygments 0.8*).269 270        For compatibility with Pygments 0.7 and earlier, every true value271        except ``'inline'`` means the same as ``'table'`` (in particular, that272        means also ``True``).273 274        The default value is ``False``, which means no line numbers at all.275 276        **Note:** with the default ("table") line number mechanism, the line277        numbers and code can have different line heights in Internet Explorer278        unless you give the enclosing ``<pre>`` tags an explicit ``line-height``279        CSS property (you get the default line spacing with ``line-height:280        125%``).281 282    `hl_lines`283        Specify a list of lines to be highlighted. The line numbers are always284        relative to the input (i.e. the first line is line 1) and are285        independent of `linenostart`.286 287        .. versionadded:: 0.11288 289    `linenostart`290        The line number for the first line (default: ``1``).291 292    `linenostep`293        If set to a number n > 1, only every nth line number is printed.294 295    `linenospecial`296        If set to a number n > 0, every nth line number is given the CSS297        class ``"special"`` (default: ``0``).298 299    `nobackground`300        If set to ``True``, the formatter won't output the background color301        for the wrapping element (this automatically defaults to ``False``302        when there is no wrapping element [eg: no argument for the303        `get_syntax_defs` method given]) (default: ``False``).304 305        .. versionadded:: 0.6306 307    `lineseparator`308        This string is output between lines of code. It defaults to ``"\n"``,309        which is enough to break a line inside ``<pre>`` tags, but you can310        e.g. set it to ``"<br>"`` to get HTML line breaks.311 312        .. versionadded:: 0.7313 314    `lineanchors`315        If set to a nonempty string, e.g. ``foo``, the formatter will wrap each316        output line in an anchor tag with an ``id`` (and `name`) of ``foo-linenumber``.317        This allows easy linking to certain lines.318 319        .. versionadded:: 0.9320 321    `linespans`322        If set to a nonempty string, e.g. ``foo``, the formatter will wrap each323        output line in a span tag with an ``id`` of ``foo-linenumber``.324        This allows easy access to lines via javascript.325 326        .. versionadded:: 1.6327 328    `anchorlinenos`329        If set to `True`, will wrap line numbers in <a> tags. Used in330        combination with `linenos` and `lineanchors`.331 332    `tagsfile`333        If set to the path of a ctags file, wrap names in anchor tags that334        link to their definitions. `lineanchors` should be used, and the335        tags file should specify line numbers (see the `-n` option to ctags).336        The tags file is assumed to be encoded in UTF-8.337 338        .. versionadded:: 1.6339 340    `tagurlformat`341        A string formatting pattern used to generate links to ctags definitions.342        Available variables are `%(path)s`, `%(fname)s` and `%(fext)s`.343        Defaults to an empty string, resulting in just `#prefix-number` links.344 345        .. versionadded:: 1.6346 347    `filename`348        A string used to generate a filename when rendering ``<pre>`` blocks,349        for example if displaying source code. If `linenos` is set to350        ``'table'`` then the filename will be rendered in an initial row351        containing a single `<th>` which spans both columns.352 353        .. versionadded:: 2.1354 355    `wrapcode`356        Wrap the code inside ``<pre>`` blocks using ``<code>``, as recommended357        by the HTML5 specification.358 359        .. versionadded:: 2.4360 361    `debug_token_types`362        Add ``title`` attributes to all token ``<span>`` tags that show the363        name of the token.364 365        .. versionadded:: 2.10366 367 368    **Subclassing the HTML formatter**369 370    .. versionadded:: 0.7371 372    The HTML formatter is now built in a way that allows easy subclassing, thus373    customizing the output HTML code. The `format()` method calls374    `self._format_lines()` which returns a generator that yields tuples of ``(1,375    line)``, where the ``1`` indicates that the ``line`` is a line of the376    formatted source code.377 378    If the `nowrap` option is set, the generator is the iterated over and the379    resulting HTML is output.380 381    Otherwise, `format()` calls `self.wrap()`, which wraps the generator with382    other generators. These may add some HTML code to the one generated by383    `_format_lines()`, either by modifying the lines generated by the latter,384    then yielding them again with ``(1, line)``, and/or by yielding other HTML385    code before or after the lines, with ``(0, html)``. The distinction between386    source lines and other code makes it possible to wrap the generator multiple387    times.388 389    The default `wrap()` implementation adds a ``<div>`` and a ``<pre>`` tag.390 391    A custom `HtmlFormatter` subclass could look like this:392 393    .. sourcecode:: python394 395        class CodeHtmlFormatter(HtmlFormatter):396 397            def wrap(self, source, *, include_div):398                return self._wrap_code(source)399 400            def _wrap_code(self, source):401                yield 0, '<code>'402                for i, t in source:403                    if i == 1:404                        # it's a line of formatted code405                        t += '<br>'406                    yield i, t407                yield 0, '</code>'408 409    This results in wrapping the formatted lines with a ``<code>`` tag, where the410    source lines are broken using ``<br>`` tags.411 412    After calling `wrap()`, the `format()` method also adds the "line numbers"413    and/or "full document" wrappers if the respective options are set. Then, all414    HTML yielded by the wrapped generator is output.415    """416 417    name = 'HTML'418    aliases = ['html']419    filenames = ['*.html', '*.htm']420 421    def __init__(self, **options):422        Formatter.__init__(self, **options)423        self.title = self._decodeifneeded(self.title)424        self.nowrap = get_bool_opt(options, 'nowrap', False)425        self.noclasses = get_bool_opt(options, 'noclasses', False)426        self.classprefix = options.get('classprefix', '')427        self.cssclass = html.escape(self._decodeifneeded(options.get('cssclass', 'highlight')))428        self.cssstyles = html.escape(self._decodeifneeded(options.get('cssstyles', '')))429        self.prestyles = self._decodeifneeded(options.get('prestyles', ''))430        self.cssfile = self._decodeifneeded(options.get('cssfile', ''))431        self.noclobber_cssfile = get_bool_opt(options, 'noclobber_cssfile', False)432        self.tagsfile = self._decodeifneeded(options.get('tagsfile', ''))433        self.tagurlformat = self._decodeifneeded(options.get('tagurlformat', ''))434        self.filename = html.escape(self._decodeifneeded(options.get('filename', '')))435        self.wrapcode = get_bool_opt(options, 'wrapcode', False)436        self.span_element_openers = {}437        self.debug_token_types = get_bool_opt(options, 'debug_token_types', False)438 439        if self.tagsfile:440            if not ctags:441                raise RuntimeError('The "ctags" package must to be installed '442                                   'to be able to use the "tagsfile" feature.')443            self._ctags = ctags.CTags(self.tagsfile)444 445        linenos = options.get('linenos', False)446        if linenos == 'inline':447            self.linenos = 2448        elif linenos:449            # compatibility with <= 0.7450            self.linenos = 1451        else:452            self.linenos = 0453        self.linenostart = abs(get_int_opt(options, 'linenostart', 1))454        self.linenostep = abs(get_int_opt(options, 'linenostep', 1))455        self.linenospecial = abs(get_int_opt(options, 'linenospecial', 0))456        self.nobackground = get_bool_opt(options, 'nobackground', False)457        self.lineseparator = html.escape(options.get('lineseparator', '\n'))458        self.lineanchors = html.escape(options.get('lineanchors', ''))459        self.linespans = html.escape(options.get('linespans', ''))460        self.anchorlinenos = get_bool_opt(options, 'anchorlinenos', False)461        self.hl_lines = set()462        for lineno in get_list_opt(options, 'hl_lines', []):463            try:464                self.hl_lines.add(int(lineno))465            except ValueError:466                pass467 468        self._create_stylesheet()469 470    def _get_css_class(self, ttype):471        """Return the css class of this token type prefixed with472        the classprefix option."""473        ttypeclass = _get_ttype_class(ttype)474        if ttypeclass:475            return self.classprefix + ttypeclass476        return ''477 478    def _get_css_classes(self, ttype):479        """Return the CSS classes of this token type prefixed with the classprefix option."""480        cls = self._get_css_class(ttype)481        while ttype not in STANDARD_TYPES:482            ttype = ttype.parent483            cls = self._get_css_class(ttype) + ' ' + cls484        return cls or ''485 486    def _get_css_inline_styles(self, ttype):487        """Return the inline CSS styles for this token type."""488        cclass = self.ttype2class.get(ttype)489        while cclass is None:490            ttype = ttype.parent491            cclass = self.ttype2class.get(ttype)492        return cclass or ''493 494    def _create_stylesheet(self):495        t2c = self.ttype2class = {Token: ''}496        c2s = self.class2style = {}497        for ttype, ndef in self.style:498            name = self._get_css_class(ttype)499            style = ''500            if ndef['color']:501                style += 'color: {}; '.format(webify(ndef['color']))502            if ndef['bold']:503                style += 'font-weight: bold; '504            if ndef['italic']:505                style += 'font-style: italic; '506            if ndef['underline']:507                style += 'text-decoration: underline; '508            if ndef['bgcolor']:509                style += 'background-color: {}; '.format(webify(ndef['bgcolor']))510            if ndef['border']:511                style += 'border: 1px solid {}; '.format(webify(ndef['border']))512            if style:513                t2c[ttype] = name514                # save len(ttype) to enable ordering the styles by515                # hierarchy (necessary for CSS cascading rules!)516                c2s[name] = (style[:-2], ttype, len(ttype))517 518    def get_style_defs(self, arg=None):519        """520        Return CSS style definitions for the classes produced by the current521        highlighting style. ``arg`` can be a string or list of selectors to522        insert before the token type classes.523        """524        style_lines = []525 526        style_lines.extend(self.get_linenos_style_defs())527        style_lines.extend(self.get_background_style_defs(arg))528        style_lines.extend(self.get_token_style_defs(arg))529 530        return '\n'.join(style_lines)531 532    def get_token_style_defs(self, arg=None):533        prefix = self.get_css_prefix(arg)534 535        styles = [536            (level, ttype, cls, style)537            for cls, (style, ttype, level) in self.class2style.items()538            if cls and style539        ]540        styles.sort()541 542        lines = [543            f'{prefix(cls)} {{ {style} }} /* {repr(ttype)[6:]} */'544            for (level, ttype, cls, style) in styles545        ]546 547        return lines548 549    def get_background_style_defs(self, arg=None):550        prefix = self.get_css_prefix(arg)551        bg_color = self.style.background_color552        hl_color = self.style.highlight_color553 554        lines = []555 556        if arg and not self.nobackground and bg_color is not None:557            text_style = ''558            if Text in self.ttype2class:559                text_style = ' ' + self.class2style[self.ttype2class[Text]][0]560            lines.insert(561                0, '{}{{ background: {};{} }}'.format(562                    prefix(''), bg_color, text_style563                )564            )565        if hl_color is not None:566            lines.insert(567                0, '{} {{ background-color: {} }}'.format(prefix('hll'), hl_color)568            )569 570        return lines571 572    def get_linenos_style_defs(self):573        lines = [574            f'pre {{ {self._pre_style} }}',575            f'td.linenos .normal {{ {self._linenos_style} }}',576            f'span.linenos {{ {self._linenos_style} }}',577            f'td.linenos .special {{ {self._linenos_special_style} }}',578            f'span.linenos.special {{ {self._linenos_special_style} }}',579        ]580 581        return lines582 583    def get_css_prefix(self, arg):584        if arg is None:585            arg = ('cssclass' in self.options and '.'+self.cssclass or '')586        if isinstance(arg, str):587            args = [arg]588        else:589            args = list(arg)590 591        def prefix(cls):592            if cls:593                cls = '.' + cls594            tmp = []595            for arg in args:596                tmp.append((arg and arg + ' ' or '') + cls)597            return ', '.join(tmp)598 599        return prefix600 601    @property602    def _pre_style(self):603        return 'line-height: 125%;'604 605    @property606    def _linenos_style(self):607        color = self.style.line_number_color608        background_color = self.style.line_number_background_color609        return f'color: {color}; background-color: {background_color}; padding-left: 5px; padding-right: 5px;'610 611    @property612    def _linenos_special_style(self):613        color = self.style.line_number_special_color614        background_color = self.style.line_number_special_background_color615        return f'color: {color}; background-color: {background_color}; padding-left: 5px; padding-right: 5px;'616 617    def _decodeifneeded(self, value):618        if isinstance(value, bytes):619            if self.encoding:620                return value.decode(self.encoding)621            return value.decode()622        return value623 624    def _wrap_full(self, inner, outfile):625        if self.cssfile:626            if os.path.isabs(self.cssfile):627                # it's an absolute filename628                cssfilename = self.cssfile629            else:630                try:631                    filename = outfile.name632                    if not filename or filename[0] == '<':633                        # pseudo files, e.g. name == '<fdopen>'634                        raise AttributeError635                    cssfilename = os.path.join(os.path.dirname(filename),636                                               self.cssfile)637                except AttributeError:638                    print('Note: Cannot determine output file name, '639                          'using current directory as base for the CSS file name',640                          file=sys.stderr)641                    cssfilename = self.cssfile642            # write CSS file only if noclobber_cssfile isn't given as an option.643            try:644                if not os.path.exists(cssfilename) or not self.noclobber_cssfile:645                    with open(cssfilename, "w", encoding="utf-8") as cf:646                        cf.write(CSSFILE_TEMPLATE %647                                 {'styledefs': self.get_style_defs('body')})648            except OSError as err:649                err.strerror = 'Error writing CSS file: ' + err.strerror650                raise651 652            yield 0, (DOC_HEADER_EXTERNALCSS %653                      dict(title=self.title,654                           cssfile=self.cssfile,655                           encoding=self.encoding))656        else:657            yield 0, (DOC_HEADER %658                      dict(title=self.title,659                           styledefs=self.get_style_defs('body'),660                           encoding=self.encoding))661 662        yield from inner663        yield 0, DOC_FOOTER664 665    def _wrap_tablelinenos(self, inner):666        dummyoutfile = StringIO()667        lncount = 0668        for t, line in inner:669            if t:670                lncount += 1671            dummyoutfile.write(line)672 673        fl = self.linenostart674        mw = len(str(lncount + fl - 1))675        sp = self.linenospecial676        st = self.linenostep677        anchor_name = self.lineanchors or self.linespans678        aln = self.anchorlinenos679        nocls = self.noclasses680 681        lines = []682 683        for i in range(fl, fl+lncount):684            print_line = i % st == 0685            special_line = sp and i % sp == 0686 687            if print_line:688                line = '%*d' % (mw, i)689                if aln:690                    line = '<a href="#%s-%d">%s</a>' % (anchor_name, i, line)691            else:692                line = ' ' * mw693 694            if nocls:695                if special_line:696                    style = f' style="{self._linenos_special_style}"'697                else:698                    style = f' style="{self._linenos_style}"'699            else:700                if special_line:701                    style = ' class="special"'702                else:703                    style = ' class="normal"'704 705            if style:706                line = f'<span{style}>{line}</span>'707 708            lines.append(line)709 710        ls = '\n'.join(lines)711 712        # If a filename was specified, we can't put it into the code table as it713        # would misalign the line numbers. Hence we emit a separate row for it.714        filename_tr = ""715        if self.filename:716            filename_tr = (717                '<tr><th colspan="2" class="filename">'718                '<span class="filename">' + self.filename + '</span>'719                '</th></tr>')720 721        # in case you wonder about the seemingly redundant <div> here: since the722        # content in the other cell also is wrapped in a div, some browsers in723        # some configurations seem to mess up the formatting...724        yield 0, (f'<table class="{self.cssclass}table">' + filename_tr +725            '<tr><td class="linenos"><div class="linenodiv"><pre>' +726            ls + '</pre></div></td><td class="code">')727        yield 0, '<div>'728        yield 0, dummyoutfile.getvalue()729        yield 0, '</div>'730        yield 0, '</td></tr></table>'731 732 733    def _wrap_inlinelinenos(self, inner):734        # need a list of lines since we need the width of a single number :(735        inner_lines = list(inner)736        sp = self.linenospecial737        st = self.linenostep738        num = self.linenostart739        mw = len(str(len(inner_lines) + num - 1))740        anchor_name = self.lineanchors or self.linespans741        aln = self.anchorlinenos742        nocls = self.noclasses743 744        for _, inner_line in inner_lines:745            print_line = num % st == 0746            special_line = sp and num % sp == 0747 748            if print_line:749                line = '%*d' % (mw, num)750            else:751                line = ' ' * mw752 753            if nocls:754                if special_line:755                    style = f' style="{self._linenos_special_style}"'756                else:757                    style = f' style="{self._linenos_style}"'758            else:759                if special_line:760                    style = ' class="linenos special"'761                else:762                    style = ' class="linenos"'763 764            if style:765                linenos = f'<span{style}>{line}</span>'766            else:767                linenos = line768 769            if aln:770                yield 1, ('<a href="#%s-%d">%s</a>' % (anchor_name, num, linenos) +771                          inner_line)772            else:773                yield 1, linenos + inner_line774            num += 1775 776    def _wrap_lineanchors(self, inner):777        s = self.lineanchors778        # subtract 1 since we have to increment i *before* yielding779        i = self.linenostart - 1780        for t, line in inner:781            if t:782                i += 1783                href = "" if self.linenos else ' href="#%s-%d"' % (s, i)784                yield 1, '<a id="%s-%d" name="%s-%d"%s></a>' % (s, i, s, i, href) + line785            else:786                yield 0, line787 788    def _wrap_linespans(self, inner):789        s = self.linespans790        i = self.linenostart - 1791        for t, line in inner:792            if t:793                i += 1794                yield 1, '<span id="%s-%d">%s</span>' % (s, i, line)795            else:796                yield 0, line797 798    def _wrap_div(self, inner):799        style = []800        if (self.noclasses and not self.nobackground and801                self.style.background_color is not None):802            style.append(f'background: {self.style.background_color}')803        if self.cssstyles:804            style.append(self.cssstyles)805        style = '; '.join(style)806 807        yield 0, ('<div' + (self.cssclass and f' class="{self.cssclass}"') +808                  (style and (f' style="{style}"')) + '>')809        yield from inner810        yield 0, '</div>\n'811 812    def _wrap_pre(self, inner):813        style = []814        if self.prestyles:815            style.append(self.prestyles)816        if self.noclasses:817            style.append(self._pre_style)818        style = '; '.join(style)819 820        if self.filename and self.linenos != 1:821            yield 0, ('<span class="filename">' + self.filename + '</span>')822 823        # the empty span here is to keep leading empty lines from being824        # ignored by HTML parsers825        yield 0, ('<pre' + (style and f' style="{style}"') + '><span></span>')826        yield from inner827        yield 0, '</pre>'828 829    def _wrap_code(self, inner):830        yield 0, '<code>'831        yield from inner832        yield 0, '</code>'833 834    @functools.lru_cache(maxsize=100)835    def _translate_parts(self, value):836        """HTML-escape a value and split it by newlines."""837        return value.translate(_escape_html_table).split('\n')838 839    def _format_lines(self, tokensource):840        """841        Just format the tokens, without any wrapping tags.842        Yield individual lines.843        """844        nocls = self.noclasses845        lsep = self.lineseparator846        tagsfile = self.tagsfile847 848        lspan = ''849        line = []850        for ttype, value in tokensource:851            try:852                cspan = self.span_element_openers[ttype]853            except KeyError:854                title = ' title="{}"'.format('.'.join(ttype)) if self.debug_token_types else ''855                if nocls:856                    css_style = self._get_css_inline_styles(ttype)857                    if css_style:858                        css_style = self.class2style[css_style][0]859                        cspan = f'<span style="{css_style}"{title}>'860                    else:861                        cspan = ''862                else:863                    css_class = self._get_css_classes(ttype)864                    if css_class:865                        cspan = f'<span class="{css_class}"{title}>'866                    else:867                        cspan = ''868                self.span_element_openers[ttype] = cspan869 870            parts = self._translate_parts(value)871 872            if tagsfile and ttype in Token.Name:873                filename, linenumber = self._lookup_ctag(value)874                if linenumber:875                    base, filename = os.path.split(filename)876                    if base:877                        base += '/'878                    filename, extension = os.path.splitext(filename)879                    url = self.tagurlformat % {'path': base, 'fname': filename,880                                               'fext': extension}881                    parts[0] = "<a href=\"%s#%s-%d\">%s" % \882                        (url, self.lineanchors, linenumber, parts[0])883                    parts[-1] = parts[-1] + "</a>"884 885            # for all but the last line886            for part in parts[:-1]:887                if line:888                    # Also check for part being non-empty, so we avoid creating889                    # empty <span> tags890                    if lspan != cspan and part:891                        line.extend(((lspan and '</span>'), cspan, part,892                                     (cspan and '</span>'), lsep))893                    else:  # both are the same, or the current part was empty894                        line.extend((part, (lspan and '</span>'), lsep))895                    yield 1, ''.join(line)896                    line = []897                elif part:898                    yield 1, ''.join((cspan, part, (cspan and '</span>'), lsep))899                else:900                    yield 1, lsep901            # for the last line902            if line and parts[-1]:903                if lspan != cspan:904                    line.extend(((lspan and '</span>'), cspan, parts[-1]))905                    lspan = cspan906                else:907                    line.append(parts[-1])908            elif parts[-1]:909                line = [cspan, parts[-1]]910                lspan = cspan911            # else we neither have to open a new span nor set lspan912 913        if line:914            line.extend(((lspan and '</span>'), lsep))915            yield 1, ''.join(line)916 917    def _lookup_ctag(self, token):918        entry = ctags.TagEntry()919        if self._ctags.find(entry, token.encode(), 0):920            return entry['file'].decode(), entry['lineNumber']921        else:922            return None, None923 924    def _highlight_lines(self, tokensource):925        """926        Highlighted the lines specified in the `hl_lines` option by927        post-processing the token stream coming from `_format_lines`.928        """929        hls = self.hl_lines930 931        for i, (t, value) in enumerate(tokensource):932            if t != 1:933                yield t, value934            if i + 1 in hls:  # i + 1 because Python indexes start at 0935                if self.noclasses:936                    style = ''937                    if self.style.highlight_color is not None:938                        style = (f' style="background-color: {self.style.highlight_color}"')939                    yield 1, f'<span{style}>{value}</span>'940                else:941                    yield 1, f'<span class="hll">{value}</span>'942            else:943                yield 1, value944 945    def wrap(self, source):946        """947        Wrap the ``source``, which is a generator yielding948        individual lines, in custom generators. See docstring949        for `format`. Can be overridden.950        """951 952        output = source953        if self.wrapcode:954            output = self._wrap_code(output)955 956        output = self._wrap_pre(output)957 958        return output959 960    def format_unencoded(self, tokensource, outfile):961        """962        The formatting process uses several nested generators; which of963        them are used is determined by the user's options.964 965        Each generator should take at least one argument, ``inner``,966        and wrap the pieces of text generated by this.967 968        Always yield 2-tuples: (code, text). If "code" is 1, the text969        is part of the original tokensource being highlighted, if it's970        0, the text is some piece of wrapping. This makes it possible to971        use several different wrappers that process the original source972        linewise, e.g. line number generators.973        """974        source = self._format_lines(tokensource)975 976        # As a special case, we wrap line numbers before line highlighting977        # so the line numbers get wrapped in the highlighting tag.978        if not self.nowrap and self.linenos == 2:979            source = self._wrap_inlinelinenos(source)980 981        if self.hl_lines:982            source = self._highlight_lines(source)983 984        if not self.nowrap:985            if self.lineanchors:986                source = self._wrap_lineanchors(source)987            if self.linespans:988                source = self._wrap_linespans(source)989            source = self.wrap(source)990            if self.linenos == 1:991                source = self._wrap_tablelinenos(source)992            source = self._wrap_div(source)993            if self.full:994                source = self._wrap_full(source, outfile)995 996        for t, piece in source:997            outfile.write(piece)998 
codekingpro/portable-devtools · Team Ai