--- a/ThirdParty/Pygments/pygments/formatters/html.py Tue Jan 04 17:37:48 2011 +0100 +++ b/ThirdParty/Pygments/pygments/formatters/html.py Wed Jan 05 15:46:19 2011 +0100 @@ -1,723 +1,750 @@ -# -*- coding: utf-8 -*- -""" - pygments.formatters.html - ~~~~~~~~~~~~~~~~~~~~~~~~ - - Formatter for HTML output. - - :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import os -import sys -import io - -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, bytes - - -__all__ = ['HtmlFormatter'] - - -def escape_html(text): - """Escape &, <, > as well as single and double quotes for HTML.""" - return text.replace('&', '&'). \ - replace('<', '<'). \ - replace('>', '>'). \ - replace('"', '"'). \ - replace("'", ''') - - -def get_random_id(): - """Return a random id for javascript fields.""" - from random import random - from time import time - try: - from hashlib import sha1 as sha - except ImportError: - import sha - sha = sha.new - return sha('%s|%s' % (random(), time())).hexdigest() - - -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 = '''\ -td.linenos { background-color: #f0f0f0; padding-right: 10px; } -span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } -pre { line-height: 125%%; } -%(styledefs)s -''' - -DOC_HEADER = '''\ -<!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"> - <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 within a ``<pre>`` tag, wrapped - in a ``<div>`` tag. 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). - - Wrapping can be disabled using the `nowrap` option. - - 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. - - 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 wrap the tokens at all, not even inside a ``<pre>`` - tag. 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 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. - - *New in Pygments 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: ``''``). *New in - Pygments 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. *New in Pygments 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``. - *New in Pygments 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. *New in Pygments 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``). *New in - Pygments 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. *New in Pygments - 0.7.* - - `lineanchors` - If set to a nonempty string, e.g. ``foo``, the formatter will wrap each - output line in an anchor tag with a ``name`` of ``foo-linenumber``. - This allows easy linking to certain lines. *New in Pygments 0.9.* - - `anchorlinenos` - If set to `True`, will wrap line numbers in <a> tags. Used in - combination with `linenos` and `lineanchors`. - - - **Subclassing the HTML formatter** - - *New in Pygments 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, outfile): - 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) - - 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.anchorlinenos = options.get('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._class_cache = {} - self._create_stylesheet() - - def _get_css_class(self, ttype): - """Return the css class of this token type prefixed with - the classprefix option.""" - if ttype in self._class_cache: - return self._class_cache[ttype] - return self.classprefix + _get_ttype_class(ttype) - - def _create_stylesheet(self): - t2c = self.ttype2class = {Token: ''} - c2s = self.class2style = {} - cp = self.classprefix - for ttype, ndef in self.style: - name = cp + _get_ttype_class(ttype) - style = '' - if ndef['color']: - style += 'color: #%s; ' % 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; ' % ndef['bgcolor'] - if ndef['border']: - style += 'border: 1px solid #%s; ' % 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. - """ - 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) - - 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] - if arg and not self.nobackground and \ - self.style.background_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(''), self.style.background_color, text_style)) - if self.style.highlight_color is not None: - lines.insert(0, '%s.hll { background-color: %s }' % - (prefix(''), self.style.highlight_color)) - return '\n'.join(lines) - - 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: - cf = open(cssfilename, "w") - cf.write(CSSFILE_TEMPLATE % - {'styledefs': self.get_style_defs('body')}) - cf.close() - except IOError 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)) - - for t, line in inner: - yield t, line - yield 0, DOC_FOOTER - - def _wrap_tablelinenos(self, inner): - dummyoutfile = io.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 - la = self.lineanchors - aln = self.anchorlinenos - if sp: - lines = [] - - for i in range(fl, fl+lncount): - if i % st == 0: - if i % sp == 0: - if aln: - lines.append('<a href="#%s-%d" class="special">%*d</a>' % - (la, i, mw, i)) - else: - lines.append('<span class="special">%*d</span>' % (mw, i)) - else: - if aln: - lines.append('<a href="#%s-%d">%*d</a>' % (la, i, mw, i)) - else: - lines.append('%*d' % (mw, i)) - else: - lines.append('') - ls = '\n'.join(lines) - else: - lines = [] - for i in range(fl, fl+lncount): - if i % st == 0: - if aln: - lines.append('<a href="#%s-%d">%*d</a>' % (la, i, mw, i)) - else: - lines.append('%*d' % (mw, i)) - else: - lines.append('') - ls = '\n'.join(lines) - - # 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, ('<table class="%stable">' % self.cssclass + - '<tr><td class="linenos"><div class="linenodiv"><pre>' + - ls + '</pre></div></td><td class="code">') - yield 0, dummyoutfile.getvalue() - yield 0, '</td></tr></table>' - - def _wrap_inlinelinenos(self, inner): - # need a list of lines since we need the width of a single number :( - lines = list(inner) - sp = self.linenospecial - st = self.linenostep - num = self.linenostart - mw = len(str(len(lines) + num - 1)) - - if sp: - for t, line in lines: - yield 1, '<span class="lineno%s">%*s</span> ' % ( - num%sp == 0 and ' special' or '', mw, - (num%st and ' ' or num)) + line - num += 1 - else: - for t, line in lines: - yield 1, '<span class="lineno">%*s</span> ' % ( - mw, (num%st and ' ' or num)) + line - num += 1 - - def _wrap_lineanchors(self, inner): - s = self.lineanchors - i = 0 - for t, line in inner: - if t: - i += 1 - yield 1, '<a name="%s-%d"></a>' % (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)) + '>') - for tup in inner: - yield tup - yield 0, '</div>\n' - - def _wrap_pre(self, inner): - style = [] - if self.prestyles: - style.append(self.prestyles) - if self.noclasses: - style.append('line-height: 125%') - style = '; '.join(style) - - yield 0, ('<pre' + (style and ' style="%s"' % style) + '>') - for tup in inner: - yield tup - yield 0, '</pre>' - - def _format_lines(self, tokensource): - """ - Just format the tokens, without any wrapping tags. - Yield individual lines. - """ - nocls = self.noclasses - lsep = self.lineseparator - # for <span style=""> lookup only - getcls = self.ttype2class.get - c2s = self.class2style - - lspan = '' - line = '' - for ttype, value in tokensource: - if nocls: - cclass = getcls(ttype) - while cclass is None: - ttype = ttype.parent - cclass = getcls(ttype) - cspan = cclass and '<span style="%s">' % c2s[cclass][0] or '' - else: - cls = self._get_css_class(ttype) - cspan = cls and '<span class="%s">' % cls or '' - - parts = escape_html(value).split('\n') - - # for all but the last line - for part in parts[:-1]: - if line: - if lspan != cspan: - line += (lspan and '</span>') + cspan + part + \ - (cspan and '</span>') + lsep - else: # both are the same - line += part + (lspan and '</span>') + lsep - yield 1, line - line = '' - elif part: - yield 1, cspan + part + (cspan and '</span>') + lsep - else: - yield 1, lsep - # for the last line - if line and parts[-1]: - if lspan != cspan: - line += (lspan and '</span>') + cspan + parts[-1] - lspan = cspan - else: - line += 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: - yield 1, line + (lspan and '</span>') + lsep - - 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, outfile): - """ - Wrap the ``source``, which is a generator yielding - individual lines, in custom generators. See docstring - for `format`. Can be overridden. - """ - return self._wrap_div(self._wrap_pre(source)) - - 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) - if self.hl_lines: - source = self._highlight_lines(source) - if not self.nowrap: - if self.linenos == 2: - source = self._wrap_inlinelinenos(source) - if self.lineanchors: - source = self._wrap_lineanchors(source) - source = self.wrap(source, outfile) - if self.linenos == 1: - source = self._wrap_tablelinenos(source) - if self.full: - source = self._wrap_full(source, outfile) - - for t, piece in source: - outfile.write(piece) +# -*- coding: utf-8 -*- +""" + pygments.formatters.html + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Formatter for HTML output. + + :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import sys +import io + +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, bytes + + +__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 get_random_id(): + """Return a random id for javascript fields.""" + from random import random + from time import time + try: + from hashlib import sha1 as sha + except ImportError: + import sha + sha = sha.new + return sha('%s|%s' % (random(), time())).hexdigest() + + +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 = '''\ +td.linenos { background-color: #f0f0f0; padding-right: 10px; } +span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } +pre { line-height: 125%%; } +%(styledefs)s +''' + +DOC_HEADER = '''\ +<!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"> + <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 within a ``<pre>`` tag, wrapped + in a ``<div>`` tag. 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). + + Wrapping can be disabled using the `nowrap` option. + + 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. + + 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 wrap the tokens at all, not even inside a ``<pre>`` + tag. 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 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. + + *New in Pygments 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: ``''``). *New in + Pygments 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. *New in Pygments 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``. + *New in Pygments 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. *New in Pygments 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``). *New in + Pygments 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. *New in Pygments + 0.7.* + + `lineanchors` + If set to a nonempty string, e.g. ``foo``, the formatter will wrap each + output line in an anchor tag with a ``name`` of ``foo-linenumber``. + This allows easy linking to certain lines. *New in Pygments 0.9.* + + `anchorlinenos` + If set to `True`, will wrap line numbers in <a> tags. Used in + combination with `linenos` and `lineanchors`. + + + **Subclassing the HTML formatter** + + *New in Pygments 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, outfile): + 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) + + 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.anchorlinenos = options.get('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 _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; ' % 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; ' % ndef['bgcolor'] + if ndef['border']: + style += 'border: 1px solid #%s; ' % 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. + """ + 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) + + 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] + if arg and not self.nobackground and \ + self.style.background_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(''), self.style.background_color, text_style)) + if self.style.highlight_color is not None: + lines.insert(0, '%s.hll { background-color: %s }' % + (prefix(''), self.style.highlight_color)) + return '\n'.join(lines) + + 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: + cf = open(cssfilename, "w") + cf.write(CSSFILE_TEMPLATE % + {'styledefs': self.get_style_defs('body')}) + cf.close() + except IOError 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)) + + for t, line in inner: + yield t, line + yield 0, DOC_FOOTER + + def _wrap_tablelinenos(self, inner): + dummyoutfile = io.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 + la = self.lineanchors + aln = self.anchorlinenos + nocls = self.noclasses + if sp: + lines = [] + + for i in range(fl, fl+lncount): + if i % st == 0: + if i % sp == 0: + if aln: + lines.append('<a href="#%s-%d" class="special">%*d</a>' % + (la, i, mw, i)) + else: + lines.append('<span class="special">%*d</span>' % (mw, i)) + else: + if aln: + lines.append('<a href="#%s-%d">%*d</a>' % (la, i, mw, i)) + else: + lines.append('%*d' % (mw, i)) + else: + lines.append('') + ls = '\n'.join(lines) + else: + lines = [] + for i in range(fl, fl+lncount): + if i % st == 0: + if aln: + lines.append('<a href="#%s-%d">%*d</a>' % (la, i, mw, i)) + else: + lines.append('%*d' % (mw, i)) + else: + lines.append('') + ls = '\n'.join(lines) + + # 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... + if nocls: + yield 0, ('<table class="%stable">' % self.cssclass + + '<tr><td><div class="linenodiv" ' + 'style="background-color: #f0f0f0; padding-right: 10px">' + '<pre style="line-height: 125%">' + + ls + '</pre></div></td><td class="code">') + else: + yield 0, ('<table class="%stable">' % self.cssclass + + '<tr><td class="linenos"><div class="linenodiv"><pre>' + + ls + '</pre></div></td><td class="code">') + yield 0, dummyoutfile.getvalue() + yield 0, '</td></tr></table>' + + def _wrap_inlinelinenos(self, inner): + # need a list of lines since we need the width of a single number :( + lines = list(inner) + sp = self.linenospecial + st = self.linenostep + num = self.linenostart + mw = len(str(len(lines) + num - 1)) + + if self.noclasses: + if sp: + for t, line in lines: + if num%sp == 0: + style = 'background-color: #ffffc0; padding: 0 5px 0 5px' + else: + style = 'background-color: #f0f0f0; padding: 0 5px 0 5px' + yield 1, '<span style="%s">%*s</span> ' % ( + style, mw, (num%st and ' ' or num)) + line + num += 1 + else: + for t, line in lines: + yield 1, ('<span style="background-color: #f0f0f0; ' + 'padding: 0 5px 0 5px">%*s</span> ' % ( + mw, (num%st and ' ' or num)) + line) + num += 1 + elif sp: + for t, line in lines: + yield 1, '<span class="lineno%s">%*s</span> ' % ( + num%sp == 0 and ' special' or '', mw, + (num%st and ' ' or num)) + line + num += 1 + else: + for t, line in lines: + yield 1, '<span class="lineno">%*s</span> ' % ( + mw, (num%st and ' ' or num)) + line + num += 1 + + def _wrap_lineanchors(self, inner): + s = self.lineanchors + i = 0 + for t, line in inner: + if t: + i += 1 + yield 1, '<a name="%s-%d"></a>' % (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)) + '>') + for tup in inner: + yield tup + yield 0, '</div>\n' + + def _wrap_pre(self, inner): + style = [] + if self.prestyles: + style.append(self.prestyles) + if self.noclasses: + style.append('line-height: 125%') + style = '; '.join(style) + + yield 0, ('<pre' + (style and ' style="%s"' % style) + '>') + for tup in inner: + yield tup + yield 0, '</pre>' + + def _format_lines(self, tokensource): + """ + Just format the tokens, without any wrapping tags. + Yield individual lines. + """ + nocls = self.noclasses + lsep = self.lineseparator + # for <span style=""> lookup only + getcls = self.ttype2class.get + c2s = self.class2style + escape_table = _escape_html_table + + lspan = '' + line = '' + for ttype, value in tokensource: + if nocls: + cclass = getcls(ttype) + while cclass is None: + ttype = ttype.parent + cclass = getcls(ttype) + cspan = cclass and '<span style="%s">' % c2s[cclass][0] or '' + else: + cls = self._get_css_class(ttype) + cspan = cls and '<span class="%s">' % cls or '' + + parts = value.translate(escape_table).split('\n') + + # for all but the last line + for part in parts[:-1]: + if line: + if lspan != cspan: + line += (lspan and '</span>') + cspan + part + \ + (cspan and '</span>') + lsep + else: # both are the same + line += part + (lspan and '</span>') + lsep + yield 1, line + line = '' + elif part: + yield 1, cspan + part + (cspan and '</span>') + lsep + else: + yield 1, lsep + # for the last line + if line and parts[-1]: + if lspan != cspan: + line += (lspan and '</span>') + cspan + parts[-1] + lspan = cspan + else: + line += 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: + yield 1, line + (lspan and '</span>') + lsep + + 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, outfile): + """ + Wrap the ``source``, which is a generator yielding + individual lines, in custom generators. See docstring + for `format`. Can be overridden. + """ + return self._wrap_div(self._wrap_pre(source)) + + 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) + if self.hl_lines: + source = self._highlight_lines(source) + if not self.nowrap: + if self.linenos == 2: + source = self._wrap_inlinelinenos(source) + if self.lineanchors: + source = self._wrap_lineanchors(source) + source = self.wrap(source, outfile) + if self.linenos == 1: + source = self._wrap_tablelinenos(source) + if self.full: + source = self._wrap_full(source, outfile) + + for t, piece in source: + outfile.write(piece)