summaryrefslogtreecommitdiff
path: root/pygments/formatters
diff options
context:
space:
mode:
Diffstat (limited to 'pygments/formatters')
-rw-r--r--pygments/formatters/__init__.py73
-rw-r--r--pygments/formatters/bbcode.py99
-rw-r--r--pygments/formatters/html.py280
-rw-r--r--pygments/formatters/latex.py191
-rw-r--r--pygments/formatters/other.py73
-rw-r--r--pygments/formatters/terminal.py98
6 files changed, 814 insertions, 0 deletions
diff --git a/pygments/formatters/__init__.py b/pygments/formatters/__init__.py
new file mode 100644
index 00000000..b701efb4
--- /dev/null
+++ b/pygments/formatters/__init__.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.formatters
+ ~~~~~~~~~~~~~~~~~~
+
+ Pygments formatters.
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+import os.path
+from pygments.formatters.html import HtmlFormatter
+from pygments.formatters.terminal import TerminalFormatter
+from pygments.formatters.latex import LatexFormatter
+from pygments.formatters.bbcode import BBCodeFormatter
+from pygments.formatters.other import NullFormatter, RawTokenFormatter
+
+
+def _doc_desc(obj):
+ res = ''
+ for line in obj.__doc__.strip().splitlines():
+ if line.strip(): res += line.strip() + " "
+ else: break
+ return res
+
+
+#: Map formatter classes to ``(longname, names, file extensions, descr)``.
+FORMATTERS = {
+ HtmlFormatter: ('HTML', ('html',), ('.htm', '.html'),
+ _doc_desc(HtmlFormatter)),
+ TerminalFormatter: ('Terminal', ('terminal', 'console'), (),
+ _doc_desc(TerminalFormatter)),
+ LatexFormatter: ('LaTeX', ('latex', 'tex'), ('.tex',),
+ _doc_desc(LatexFormatter)),
+ RawTokenFormatter: ('Raw tokens', ('raw', 'tokens'), ('.raw',),
+ _doc_desc(RawTokenFormatter)),
+ NullFormatter: ('Text only', ('text', 'null'), ('.txt',),
+ _doc_desc(NullFormatter)),
+ BBCodeFormatter: ('BBcode', ('bbcode', 'bb'), (),
+ _doc_desc(BBCodeFormatter))
+}
+
+
+_formatter_cache = {}
+
+def _init_formatter_cache():
+ if _formatter_cache: return
+ for cls, info in FORMATTERS.iteritems():
+ for alias in info[1]:
+ _formatter_cache[alias] = cls
+ for ext in info[2]:
+ _formatter_cache["/"+ext] = cls
+
+
+def get_formatter_by_name(name, **options):
+ _init_formatter_cache()
+ cls = _formatter_cache.get(name, None)
+ if not cls:
+ raise ValueError("No formatter found for name %r" % name)
+ return cls(**options)
+
+
+def get_formatter_for_filename(fn, **options):
+ _init_formatter_cache()
+ # try by filename extension
+ cls = _formatter_cache.get("/"+os.path.splitext(fn)[1], None)
+ if cls:
+ return cls(**options)
+ # try by whole file name
+ cls = _formatter_cache.get("/"+os.path.basename(fn), None)
+ if not cls:
+ raise ValueError("No formatter found for file name %r" % fn)
+ return cls(**options)
diff --git a/pygments/formatters/bbcode.py b/pygments/formatters/bbcode.py
new file mode 100644
index 00000000..61384c7f
--- /dev/null
+++ b/pygments/formatters/bbcode.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.formatters.bbcode
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ BBcode formatter.
+
+ :copyright: 2006 by Lukas Meuser.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+
+from pygments.formatter import Formatter
+from pygments.util import get_bool_opt
+
+__all__ = ['BBCodeFormatter']
+
+
+class BBCodeFormatter(Formatter):
+ """
+ Output BBCode tags with appropiate colors and formatting.
+
+ This formatter doesn't support background colors and borders, as there are
+ no common BBcodes 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:
+
+ ``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).
+ """
+
+ def __init__(self, **options):
+ Formatter.__init__(self, **options)
+ self._make_styles()
+ self._code = get_bool_opt(options, 'codetag', False)
+ self._mono = get_bool_opt(options, 'monofont', False)
+
+ def _make_styles(self):
+ self.styles = {}
+ for token, style in self.style._styles.iteritems():
+ start = end = ''
+ color, bold, italic, underline, bg, border = style
+ if color:
+ start += '[color=#%s]' % color
+ end = '[/color]' + end
+ if bold:
+ start += '[b]'
+ end = '[/b]' + end
+ if italic:
+ start += '[i]'
+ end = '[/i]' + end
+ if underline:
+ start += '[u]'
+ end = '[/u]' + end
+ # there are no common BBcodes for background-color and border
+
+ self.styles[token] = start, end
+
+ def format(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/pygments/formatters/html.py b/pygments/formatters/html.py
new file mode 100644
index 00000000..6223aba6
--- /dev/null
+++ b/pygments/formatters/html.py
@@ -0,0 +1,280 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.formatters.html
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Formatter for HTML output.
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+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
+
+
+__all__ = ['HtmlFormatter']
+
+
+def escape_html(text):
+ """Escape &, <, > as well as single and double quotes for HTML."""
+ return text.replace('&', '&amp;'). \
+ replace('<', '&lt;'). \
+ replace('>', '&gt;'). \
+ replace('"', '&quot;'). \
+ replace("'", '&#39;')
+
+
+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
+
+
+DOC_TEMPLATE = '''\
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+
+<html>
+<head>
+ <title>%(title)s</title>
+ <style type="text/css">
+td.linenos { background-color: #f0f0f0; padding-right: 10px; }
+%(styledefs)s
+ </style>
+</head>
+<body>
+<h2>%(title)s</h2>
+
+%(code)s
+
+</body>
+</html>
+'''
+
+
+class HtmlFormatter(Formatter):
+ """
+ Output HTML <span> tags with appropriate classes.
+
+ Additional options accepted:
+
+ ``nowrap``
+ If set to true, don't wrap the tokens at all. This disables
+ all other options (default: False).
+ ``noclasses``
+ If set to true, token <span>s will not use CSS classes, but
+ inline styles.
+ ``classprefix``
+ Prefix for token CSS classes, is prepended to all token style
+ classes (e.g. class="o" -> class="_o" if classprefix == '_')
+ (default: '').
+ ``cssclass``
+ CSS class for the wrapping <div> (default: 'highlight').
+ ``cssstyles``
+ Inline CSS styles for the wrapping <div>. (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
+ (default: 1).
+ ``linenospecial``
+ If set to a number n > 0, every nth line number is given a special
+ CSS class ``special`` (default: 0).
+ ``nobackground``
+ If set to ``True`` the formatter won't output the background color
+ for the overall element (this automatically defaults to ``False``
+ when there is no overall element [eg: no argument for the
+ `get_syntax_defs` method given]) (default: ``False``)
+ """
+
+ def __init__(self, **options):
+ Formatter.__init__(self, **options)
+ self.nowrap = get_bool_opt(options, 'nowrap', False)
+ self.noclasses = get_bool_opt(options, 'noclasses', False)
+ self.classprefix = options.get('classprefix', '')
+ self.cssclass = options.get('cssclass', 'highlight')
+ self.cssstyles = options.get('cssstyles', '')
+ 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.linenospecial = abs(get_int_opt(options, 'linenospecial', 0))
+ self.nobackground = get_bool_opt(options, 'nobackground', False)
+
+ 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 + STANDARD_TYPES.get(ttype) or _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=''):
+ """
+ Return CSS style definitions for the classes produced by the
+ current highlighting style. ``arg`` can be a string of selectors
+ to insert before the token type classes.
+ """
+ if arg:
+ arg += ' '
+ styles = [(level, ttype, cls, style)
+ for cls, (style, ttype, level) in self.class2style.iteritems()
+ if cls and style]
+ styles.sort()
+ lines = ['%s.%s { %s } /* %s */' % (arg, 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 }' %
+ (arg, self.style.background_color, text_style))
+ return '\n'.join(lines)
+
+ def _format_nowrap(self, tokensource, outfile, lnos=False):
+ lncount = 0
+ nocls = self.noclasses
+ # for <span style=""> lookup only
+ getcls = self.ttype2class.get
+ c2s = self.class2style
+
+ write = outfile.write
+ lspan = ''
+ for ttype, value in tokensource:
+ htmlvalue = escape_html(value)
+ if lnos:
+ lncount += value.count("\n")
+
+ if nocls:
+ cclass = getcls(ttype)
+ while cclass is None:
+ ttype = ttype.parent
+ cclass = getcls(ttype)
+ cspan = cclass and '<span style="%s">' % c2s[cclass][0]
+ else:
+ cls = self._get_css_class(ttype)
+ cspan = cls and '<span class="%s">' % cls
+
+ if cspan == lspan:
+ if not cspan:
+ write(htmlvalue)
+ else:
+ write(htmlvalue.replace('\n', '</span>\n' + cspan))
+ elif htmlvalue: # if no value, leave old span open
+ if lspan:
+ write('</span>')
+ lspan = cspan
+ if cspan:
+ htmlvalue = htmlvalue.replace('\n', '</span>\n' + cspan)
+ write(cspan + htmlvalue)
+ else:
+ write(htmlvalue)
+ if lspan:
+ write('</span>')
+ return lncount
+
+ def format(self, tokensource, outfile):
+ if self.nowrap:
+ self._format_nowrap(tokensource, outfile)
+ return
+
+ realoutfile = outfile
+ lnos = self.linenos
+ full = self.full
+
+ div = ('<div' + (self.cssclass and ' class="%s" ' % self.cssclass)
+ + (self.cssstyles and ' style="%s"' % self.cssstyles) + '>')
+ if full or lnos:
+ outfile = StringIO.StringIO()
+ else:
+ outfile.write(div)
+
+ outfile.write('<pre>')
+ lncount = self._format_nowrap(tokensource, outfile, lnos)
+ outfile.write('</pre>')
+
+ ret = ''
+ if lnos:
+ fl = self.linenostart
+ mw = len(str(lncount + fl - 1))
+ sp = self.linenospecial
+ st = self.linenostep
+ if sp:
+ ls = '\n'.join([(i%st == 0 and
+ (i%sp == 0 and '<span class="special">%*d</span>'
+ or '%*d') % (mw, i)
+ or '')
+ for i in range(fl, fl + lncount)])
+ else:
+ ls = '\n'.join([(i%st == 0 and ('%*d' % (mw, i)) or '')
+ for i in range(fl, fl + lncount)])
+
+ ret = div + ('<table><tr>'
+ '<td class="linenos" title="click to toggle" '
+ 'onclick="with (this.firstChild.style) { display = '
+ '''(display == '') ? 'none' : '' }"><pre>'''
+ + ls + '</pre></td><td class="code">')
+ ret += outfile.getvalue()
+ ret += '</td></tr></table>'
+
+ if full:
+ if not ret:
+ ret = div + outfile.getvalue() + '</div>\n'
+ realoutfile.write(DOC_TEMPLATE %
+ dict(title = self.title,
+ styledefs = self.get_style_defs('body'),
+ code = ret))
+ elif lnos:
+ realoutfile.write(ret + '</div>\n')
+ else:
+ realoutfile.write('</div>\n')
diff --git a/pygments/formatters/latex.py b/pygments/formatters/latex.py
new file mode 100644
index 00000000..84fe5d12
--- /dev/null
+++ b/pygments/formatters/latex.py
@@ -0,0 +1,191 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.formatters.latex
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Formatter for LaTeX fancyvrb output.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+import StringIO
+
+from pygments.formatter import Formatter
+from pygments.token import Token
+from pygments.util import get_bool_opt, get_int_opt
+
+
+__all__ = ['LatexFormatter']
+
+
+def escape_tex(text):
+ return text.replace('@', '\x00'). \
+ replace('[', '\x01'). \
+ replace(']', '\x02'). \
+ replace('\x00', '@at[]').\
+ replace('\x01', '@lb[]').\
+ replace('\x02', '@rb[]')
+
+
+DOC_TEMPLATE = r'''
+\documentclass{%(docclass)s}
+\usepackage{fancyvrb}
+\usepackage{color}
+%(preamble)s
+
+%(styledefs)s
+
+\begin{document}
+
+\section*{%(title)s}
+
+%(code)s
+\end{document}
+'''
+
+
+class LatexFormatter(Formatter):
+ """
+ Output LaTeX "color" and "fancyvrb" control sequences.
+ """
+
+ def __init__(self, **options):
+ """
+ Additional options accepted:
+
+ ``docclass``
+ If ``full`` is true, this is the document class to use (default: 'article').
+ ``preamble``
+ If ``full`` is true, this can be further preamble commands (default: '').
+ ``linenos``
+ If 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 (default: 1).
+ ``verboptions``
+ Additional options given to the Verbatim environment (default: '').
+ ``nobackground``
+ If set to ``True`` the formatter won't output the background color
+ for the overall element (default: ``False``)
+ Note that light colors on dark background with this option disabled
+ won't be readable very good.
+ """
+ Formatter.__init__(self, **options)
+ 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._create_stylecmds()
+
+
+ def _create_stylecmds(self):
+ t2c = self.ttype2cmd = {Token: ''}
+ c2d = self.cmd2def = {}
+
+ letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ first = iter(letters)
+ second = iter(letters)
+ firstl = first.next()
+
+ 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:
+ cmndef = '#1'
+ if ndef['bold']:
+ cmndef = r'\textbf{' + cmndef + '}'
+ if ndef['italic']:
+ cmndef = r'\textit{' + cmndef + '}'
+ if ndef['underline']:
+ cmndef = r'\underline{' + cmndef + '}'
+ if ndef['color']:
+ cmndef = r'\textcolor[rgb]{%s}{%s}' % (
+ rgbcolor(ndef['color']),
+ cmndef
+ )
+ if ndef['border']:
+ cmndef = r'\fcolorbox[rgb]{%s}{%s}{%s}' % (
+ rgbcolor(ndef['border']),
+ rgbcolor(ndef['bgcolor']),
+ cmndef
+ )
+ elif ndef['bgcolor']:
+ cmndef = r'\colorbox[rgb]{%s}{%s}' % (
+ rgbcolor(ndef['bgcolor']),
+ cmndef
+ )
+ if cmndef == '#1':
+ continue
+ try:
+ alias = 'C' + firstl + second.next()
+ except StopIteration:
+ firstl = first.next()
+ second = iter(letters)
+ alias = 'C' + firstl + second.next()
+ t2c[ttype] = alias
+ c2d[alias] = cmndef
+
+ def get_style_defs(self, arg=''):
+ """
+ Return the \\newcommand sequences needed to define the commands
+ used to format text in the verbatim environment. If ``arg`` is
+ given and true, use \\renewcommand instead.
+ """
+ nc = (arg and r'\renewcommand' or r'\newcommand')
+ return '%s\\at{@}\n%s\\lb{[}\n%s\\rb{]}\n' % (nc, nc, nc) + \
+ '\n'.join(['%s\\%s[1]{%s}' % (nc, alias, cmndef)
+ for alias, cmndef in self.cmd2def.iteritems()
+ if cmndef != '#1'])
+
+ def format(self, tokensource, outfile):
+ #XXX: add support for background colors!!!!!!!111!1
+
+ if self.full:
+ realoutfile = outfile
+ outfile = StringIO.StringIO()
+
+ outfile.write(r'\begin{Verbatim}[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.verboptions:
+ outfile.write(',' + self.verboptions)
+ outfile.write(']\n')
+
+ for ttype, value in tokensource:
+ value = escape_tex(value)
+ cmd = self.ttype2cmd.get(ttype)
+ while cmd is None:
+ ttype = ttype.parent
+ cmd = self.ttype2cmd.get(ttype)
+ if cmd:
+ spl = value.split('\n')
+ for line in spl[:-1]:
+ if line:
+ outfile.write("@%s[%s]" % (cmd, line))
+ outfile.write('\n')
+ if spl[-1]:
+ outfile.write("@%s[%s]" % (cmd, spl[-1]))
+ else:
+ outfile.write(value)
+
+ outfile.write('\n\\end{Verbatim}\n')
+
+ if self.full:
+ realoutfile.write(DOC_TEMPLATE %
+ dict(docclass = self.docclass,
+ preamble = self.preamble,
+ title = self.title,
+ styledefs = self.get_style_defs(),
+ code = outfile.getvalue()))
diff --git a/pygments/formatters/other.py b/pygments/formatters/other.py
new file mode 100644
index 00000000..eb8d72fc
--- /dev/null
+++ b/pygments/formatters/other.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.formatters.other
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Other formatters.
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.formatter import Formatter
+import StringIO
+
+__all__ = ['NullFormatter', 'RawTokenFormatter']
+
+
+class NullFormatter(Formatter):
+ """
+ Output the text unchanged without any formatting.
+ """
+ def format(self, tokensource, outfile):
+ for ttype, value in tokensource:
+ outfile.write(value)
+
+
+class RawTokenFormatter(Formatter):
+ """
+ Output a raw token representation for storing token streams.
+
+ The format is ``tokentype<TAB>repr(tokenstring)``
+
+ Additional options accepted:
+
+ ``compress``
+ If set to "gz" or "bz2", compress the token stream with
+ the given compression algorithm (default: '').
+ """
+
+ def __init__(self, **options):
+ Formatter.__init__(self, **options)
+ self.compress = options.get('compress', '')
+
+ def format(self, tokensource, outfile):
+ if self.compress == 'gz':
+ import gzip
+ outfile = gzip.GzipFile('', 'wb', 9, outfile)
+ write = outfile.write
+ flush = outfile.flush
+ 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
+
+ lasttype = None
+ lastval = ''
+ for ttype, value in tokensource:
+ if ttype is lasttype:
+ lastval += value
+ else:
+ if lasttype:
+ write("%s\t%r\n" % (lasttype, lastval))
+ lastval = value
+ lasttype = ttype
+ write("%s\t%r\n" % (lasttype, lastval))
+ flush()
diff --git a/pygments/formatters/terminal.py b/pygments/formatters/terminal.py
new file mode 100644
index 00000000..b4b0071e
--- /dev/null
+++ b/pygments/formatters/terminal.py
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.formatters.terminal
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Formatter for terminal output with ANSI sequences.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.formatter import Formatter
+from pygments.token import Keyword, Name, Comment, String, Error, \
+ Number, Operator, Generic, Token
+from pygments.console import ansiformat
+from pygments.util import get_bool_opt
+
+
+__all__ = ['TerminalFormatter']
+
+
+#: Map token types to a tuple of color values for light and dark
+#: backgrounds.
+TERMINAL_COLORS = {
+ Token: ('', ''),
+
+ Comment: ('lightgray', 'darkgray'),
+ Keyword: ('darkblue', 'blue'),
+ Keyword.Type: ('teal', 'turquoise'),
+ Operator.Word: ('purple', 'fuchsia'),
+ Name.Builtin: ('teal', 'turquoise'),
+ Name.Function: ('darkgreen', 'green'),
+ Name.Namespace: ('_teal_', '_turquoise_'),
+ Name.Class: ('_darkgreen_', '_green_'),
+ Name.Exception: ('teal', 'turquoise'),
+ Name.Decorator: ('darkgray', 'lightgray'),
+ Name.Variable: ('darkred', 'red'),
+ Name.Constant: ('darkred', 'red'),
+ Name.Attribute: ('teal', 'turquoise'),
+ Name.Tag: ('blue', 'blue'),
+ String: ('brown', 'brown'),
+ Number: ('darkblue', 'blue'),
+
+ Generic.Deleted: ('red', 'red'),
+ Generic.Inserted: ('darkgreen', 'green'),
+ Generic.Heading: ('**', '**'),
+ Generic.Subheading: ('*purple*', '*fuchsia*'),
+ Generic.Error: ('red', 'red'),
+
+ Error: ('_red_', '_red_'),
+}
+
+
+class TerminalFormatter(Formatter):
+ """
+ Output plain text with coloring ANSI sequences.
+ """
+
+ def __init__(self, **options):
+ """
+ Accepted options:
+
+ ``bg``
+ Set to ``'light'`` or ``'dark'`` depending on the
+ terminal's background.
+
+ ``colorscheme``
+ ``None`` or a dictionary mapping token types to
+ ``(lightbg, darkbg)`` color names.
+
+ ``debug``
+ If true, output "<<ERROR>>" after each error token.
+ """
+ Formatter.__init__(self, **options)
+ self.darkbg = options.get('bg', 'light') == 'dark'
+ self.colorscheme = options.get('colorscheme', None) or TERMINAL_COLORS
+ self.debug = get_bool_opt(options, 'debug', False)
+
+ def format(self, tokensource, outfile):
+ dbg = self.debug
+ 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(ansiformat(color, line))
+ outfile.write('\n')
+ if spl[-1]:
+ outfile.write(ansiformat(color, spl[-1]))
+ else:
+ outfile.write(value)
+ if dbg and ttype is Error:
+ outfile.write('<<ERROR>>')