~ubuntu-branches/ubuntu/natty/moin/natty-updates

« back to all changes in this revision

Viewing changes to MoinMoin/support/pygments/formatters/html.py

  • Committer: Bazaar Package Importer
  • Author(s): Jonas Smedegaard
  • Date: 2008-06-22 21:17:13 UTC
  • mto: This revision was merged to the branch mainline in revision 18.
  • Revision ID: james.westby@ubuntu.com-20080622211713-inlv5k4eifxckelr
ImportĀ upstreamĀ versionĀ 1.7.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
"""
3
 
    pygments.formatters.html
4
 
    ~~~~~~~~~~~~~~~~~~~~~~~~
5
 
 
6
 
    Formatter for HTML output.
7
 
 
8
 
    :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS.
9
 
    :license: BSD, see LICENSE for details.
10
 
"""
11
 
 
12
 
import os
13
 
import sys
14
 
import StringIO
15
 
 
16
 
from pygments.formatter import Formatter
17
 
from pygments.token import Token, Text, STANDARD_TYPES
18
 
from pygments.util import get_bool_opt, get_int_opt, get_list_opt, bytes
19
 
 
20
 
 
21
 
__all__ = ['HtmlFormatter']
22
 
 
23
 
 
24
 
def escape_html(text):
25
 
    """Escape &, <, > as well as single and double quotes for HTML."""
26
 
    return text.replace('&', '&amp;').  \
27
 
                replace('<', '&lt;').   \
28
 
                replace('>', '&gt;').   \
29
 
                replace('"', '&quot;'). \
30
 
                replace("'", '&#39;')
31
 
 
32
 
 
33
 
def get_random_id():
34
 
    """Return a random id for javascript fields."""
35
 
    from random import random
36
 
    from time import time
37
 
    try:
38
 
        from hashlib import sha1 as sha
39
 
    except ImportError:
40
 
        import sha
41
 
        sha = sha.new
42
 
    return sha('%s|%s' % (random(), time())).hexdigest()
43
 
 
44
 
 
45
 
def _get_ttype_class(ttype):
46
 
    fname = STANDARD_TYPES.get(ttype)
47
 
    if fname:
48
 
        return fname
49
 
    aname = ''
50
 
    while fname is None:
51
 
        aname = '-' + ttype[-1] + aname
52
 
        ttype = ttype.parent
53
 
        fname = STANDARD_TYPES.get(ttype)
54
 
    return fname + aname
55
 
 
56
 
 
57
 
CSSFILE_TEMPLATE = '''\
58
 
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
59
 
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
60
 
pre { line-height: 125%%; }
61
 
%(styledefs)s
62
 
'''
63
 
 
64
 
DOC_HEADER = '''\
65
 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
66
 
   "http://www.w3.org/TR/html4/strict.dtd">
67
 
 
68
 
<html>
69
 
<head>
70
 
  <title>%(title)s</title>
71
 
  <meta http-equiv="content-type" content="text/html; charset=%(encoding)s">
72
 
  <style type="text/css">
73
 
''' + CSSFILE_TEMPLATE + '''
74
 
  </style>
75
 
</head>
76
 
<body>
77
 
<h2>%(title)s</h2>
78
 
 
79
 
'''
80
 
 
81
 
DOC_HEADER_EXTERNALCSS = '''\
82
 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
83
 
   "http://www.w3.org/TR/html4/strict.dtd">
84
 
 
85
 
<html>
86
 
<head>
87
 
  <title>%(title)s</title>
88
 
  <meta http-equiv="content-type" content="text/html; charset=%(encoding)s">
89
 
  <link rel="stylesheet" href="%(cssfile)s" type="text/css">
90
 
</head>
91
 
<body>
92
 
<h2>%(title)s</h2>
93
 
 
94
 
'''
95
 
 
96
 
DOC_FOOTER = '''\
97
 
</body>
98
 
</html>
99
 
'''
100
 
 
101
 
 
102
 
class HtmlFormatter(Formatter):
103
 
    r"""
104
 
    Format tokens as HTML 4 ``<span>`` tags within a ``<pre>`` tag, wrapped
105
 
    in a ``<div>`` tag. The ``<div>``'s CSS class can be set by the `cssclass`
106
 
    option.
107
 
 
108
 
    If the `linenos` option is set to ``"table"``, the ``<pre>`` is
109
 
    additionally wrapped inside a ``<table>`` which has one row and two
110
 
    cells: one containing the line numbers and one containing the code.
111
 
    Example:
112
 
 
113
 
    .. sourcecode:: html
114
 
 
115
 
        <div class="highlight" >
116
 
        <table><tr>
117
 
          <td class="linenos" title="click to toggle"
118
 
            onclick="with (this.firstChild.style)
119
 
                     { display = (display == '') ? 'none' : '' }">
120
 
            <pre>1
121
 
            2</pre>
122
 
          </td>
123
 
          <td class="code">
124
 
            <pre><span class="Ke">def </span><span class="NaFu">foo</span>(bar):
125
 
              <span class="Ke">pass</span>
126
 
            </pre>
127
 
          </td>
128
 
        </tr></table></div>
129
 
 
130
 
    (whitespace added to improve clarity).
131
 
 
132
 
    Wrapping can be disabled using the `nowrap` option.
133
 
 
134
 
    A list of lines can be specified using the `hl_lines` option to make these
135
 
    lines highlighted (as of Pygments 0.11).
136
 
 
137
 
    With the `full` option, a complete HTML 4 document is output, including
138
 
    the style definitions inside a ``<style>`` tag, or in a separate file if
139
 
    the `cssfile` option is given.
140
 
 
141
 
    The `get_style_defs(arg='')` method of a `HtmlFormatter` returns a string
142
 
    containing CSS rules for the CSS classes used by the formatter. The
143
 
    argument `arg` can be used to specify additional CSS selectors that
144
 
    are prepended to the classes. A call `fmter.get_style_defs('td .code')`
145
 
    would result in the following CSS classes:
146
 
 
147
 
    .. sourcecode:: css
148
 
 
149
 
        td .code .kw { font-weight: bold; color: #00FF00 }
150
 
        td .code .cm { color: #999999 }
151
 
        ...
152
 
 
153
 
    If you have Pygments 0.6 or higher, you can also pass a list or tuple to the
154
 
    `get_style_defs()` method to request multiple prefixes for the tokens:
155
 
 
156
 
    .. sourcecode:: python
157
 
 
158
 
        formatter.get_style_defs(['div.syntax pre', 'pre.syntax'])
159
 
 
160
 
    The output would then look like this:
161
 
 
162
 
    .. sourcecode:: css
163
 
 
164
 
        div.syntax pre .kw,
165
 
        pre.syntax .kw { font-weight: bold; color: #00FF00 }
166
 
        div.syntax pre .cm,
167
 
        pre.syntax .cm { color: #999999 }
168
 
        ...
169
 
 
170
 
    Additional options accepted:
171
 
 
172
 
    `nowrap`
173
 
        If set to ``True``, don't wrap the tokens at all, not even inside a ``<pre>``
174
 
        tag. This disables most other options (default: ``False``).
175
 
 
176
 
    `full`
177
 
        Tells the formatter to output a "full" document, i.e. a complete
178
 
        self-contained document (default: ``False``).
179
 
 
180
 
    `title`
181
 
        If `full` is true, the title that should be used to caption the
182
 
        document (default: ``''``).
183
 
 
184
 
    `style`
185
 
        The style to use, can be a string or a Style subclass (default:
186
 
        ``'default'``). This option has no effect if the `cssfile`
187
 
        and `noclobber_cssfile` option are given and the file specified in
188
 
        `cssfile` exists.
189
 
 
190
 
    `noclasses`
191
 
        If set to true, token ``<span>`` tags will not use CSS classes, but
192
 
        inline styles. This is not recommended for larger pieces of code since
193
 
        it increases output size by quite a bit (default: ``False``).
194
 
 
195
 
    `classprefix`
196
 
        Since the token types use relatively short class names, they may clash
197
 
        with some of your own class names. In this case you can use the
198
 
        `classprefix` option to give a string to prepend to all Pygments-generated
199
 
        CSS class names for token types.
200
 
        Note that this option also affects the output of `get_style_defs()`.
201
 
 
202
 
    `cssclass`
203
 
        CSS class for the wrapping ``<div>`` tag (default: ``'highlight'``).
204
 
        If you set this option, the default selector for `get_style_defs()`
205
 
        will be this class.
206
 
 
207
 
        *New in Pygments 0.9:* If you select the ``'table'`` line numbers, the
208
 
        wrapping table will have a CSS class of this string plus ``'table'``,
209
 
        the default is accordingly ``'highlighttable'``.
210
 
 
211
 
    `cssstyles`
212
 
        Inline CSS styles for the wrapping ``<div>`` tag (default: ``''``).
213
 
 
214
 
    `prestyles`
215
 
        Inline CSS styles for the ``<pre>`` tag (default: ``''``).  *New in
216
 
        Pygments 0.11.*
217
 
 
218
 
    `cssfile`
219
 
        If the `full` option is true and this option is given, it must be the
220
 
        name of an external file. If the filename does not include an absolute
221
 
        path, the file's path will be assumed to be relative to the main output
222
 
        file's path, if the latter can be found. The stylesheet is then written
223
 
        to this file instead of the HTML file. *New in Pygments 0.6.*
224
 
 
225
 
    `noclobber_cssfile`
226
 
        If `cssfile` is given and the specified file exists, the css file will
227
 
        not be overwritten. This allows the use of the `full` option in
228
 
        combination with a user specified css file. Default is ``False``.
229
 
        *New in Pygments 1.1.*
230
 
 
231
 
    `linenos`
232
 
        If set to ``'table'``, output line numbers as a table with two cells,
233
 
        one containing the line numbers, the other the whole code.  This is
234
 
        copy-and-paste-friendly, but may cause alignment problems with some
235
 
        browsers or fonts.  If set to ``'inline'``, the line numbers will be
236
 
        integrated in the ``<pre>`` tag that contains the code (that setting
237
 
        is *new in Pygments 0.8*).
238
 
 
239
 
        For compatibility with Pygments 0.7 and earlier, every true value
240
 
        except ``'inline'`` means the same as ``'table'`` (in particular, that
241
 
        means also ``True``).
242
 
 
243
 
        The default value is ``False``, which means no line numbers at all.
244
 
 
245
 
        **Note:** with the default ("table") line number mechanism, the line
246
 
        numbers and code can have different line heights in Internet Explorer
247
 
        unless you give the enclosing ``<pre>`` tags an explicit ``line-height``
248
 
        CSS property (you get the default line spacing with ``line-height:
249
 
        125%``).
250
 
 
251
 
    `hl_lines`
252
 
        Specify a list of lines to be highlighted.  *New in Pygments 0.11.*
253
 
 
254
 
    `linenostart`
255
 
        The line number for the first line (default: ``1``).
256
 
 
257
 
    `linenostep`
258
 
        If set to a number n > 1, only every nth line number is printed.
259
 
 
260
 
    `linenospecial`
261
 
        If set to a number n > 0, every nth line number is given the CSS
262
 
        class ``"special"`` (default: ``0``).
263
 
 
264
 
    `nobackground`
265
 
        If set to ``True``, the formatter won't output the background color
266
 
        for the wrapping element (this automatically defaults to ``False``
267
 
        when there is no wrapping element [eg: no argument for the
268
 
        `get_syntax_defs` method given]) (default: ``False``). *New in
269
 
        Pygments 0.6.*
270
 
 
271
 
    `lineseparator`
272
 
        This string is output between lines of code. It defaults to ``"\n"``,
273
 
        which is enough to break a line inside ``<pre>`` tags, but you can
274
 
        e.g. set it to ``"<br>"`` to get HTML line breaks. *New in Pygments
275
 
        0.7.*
276
 
 
277
 
    `lineanchors`
278
 
        If set to a nonempty string, e.g. ``foo``, the formatter will wrap each
279
 
        output line in an anchor tag with a ``name`` of ``foo-linenumber``.
280
 
        This allows easy linking to certain lines. *New in Pygments 0.9.*
281
 
 
282
 
    `anchorlinenos`
283
 
        If set to `True`, will wrap line numbers in <a> tags. Used in
284
 
        combination with `linenos` and `lineanchors`.
285
 
 
286
 
 
287
 
    **Subclassing the HTML formatter**
288
 
 
289
 
    *New in Pygments 0.7.*
290
 
 
291
 
    The HTML formatter is now built in a way that allows easy subclassing, thus
292
 
    customizing the output HTML code. The `format()` method calls
293
 
    `self._format_lines()` which returns a generator that yields tuples of ``(1,
294
 
    line)``, where the ``1`` indicates that the ``line`` is a line of the
295
 
    formatted source code.
296
 
 
297
 
    If the `nowrap` option is set, the generator is the iterated over and the
298
 
    resulting HTML is output.
299
 
 
300
 
    Otherwise, `format()` calls `self.wrap()`, which wraps the generator with
301
 
    other generators. These may add some HTML code to the one generated by
302
 
    `_format_lines()`, either by modifying the lines generated by the latter,
303
 
    then yielding them again with ``(1, line)``, and/or by yielding other HTML
304
 
    code before or after the lines, with ``(0, html)``. The distinction between
305
 
    source lines and other code makes it possible to wrap the generator multiple
306
 
    times.
307
 
 
308
 
    The default `wrap()` implementation adds a ``<div>`` and a ``<pre>`` tag.
309
 
 
310
 
    A custom `HtmlFormatter` subclass could look like this:
311
 
 
312
 
    .. sourcecode:: python
313
 
 
314
 
        class CodeHtmlFormatter(HtmlFormatter):
315
 
 
316
 
            def wrap(self, source, outfile):
317
 
                return self._wrap_code(source)
318
 
 
319
 
            def _wrap_code(self, source):
320
 
                yield 0, '<code>'
321
 
                for i, t in source:
322
 
                    if i == 1:
323
 
                        # it's a line of formatted code
324
 
                        t += '<br>'
325
 
                    yield i, t
326
 
                yield 0, '</code>'
327
 
 
328
 
    This results in wrapping the formatted lines with a ``<code>`` tag, where the
329
 
    source lines are broken using ``<br>`` tags.
330
 
 
331
 
    After calling `wrap()`, the `format()` method also adds the "line numbers"
332
 
    and/or "full document" wrappers if the respective options are set. Then, all
333
 
    HTML yielded by the wrapped generator is output.
334
 
    """
335
 
 
336
 
    name = 'HTML'
337
 
    aliases = ['html']
338
 
    filenames = ['*.html', '*.htm']
339
 
 
340
 
    def __init__(self, **options):
341
 
        Formatter.__init__(self, **options)
342
 
        self.title = self._decodeifneeded(self.title)
343
 
        self.nowrap = get_bool_opt(options, 'nowrap', False)
344
 
        self.noclasses = get_bool_opt(options, 'noclasses', False)
345
 
        self.classprefix = options.get('classprefix', '')
346
 
        self.cssclass = self._decodeifneeded(options.get('cssclass', 'highlight'))
347
 
        self.cssstyles = self._decodeifneeded(options.get('cssstyles', ''))
348
 
        self.prestyles = self._decodeifneeded(options.get('prestyles', ''))
349
 
        self.cssfile = self._decodeifneeded(options.get('cssfile', ''))
350
 
        self.noclobber_cssfile = get_bool_opt(options, 'noclobber_cssfile', False)
351
 
 
352
 
        linenos = options.get('linenos', False)
353
 
        if linenos == 'inline':
354
 
            self.linenos = 2
355
 
        elif linenos:
356
 
            # compatibility with <= 0.7
357
 
            self.linenos = 1
358
 
        else:
359
 
            self.linenos = 0
360
 
        self.linenostart = abs(get_int_opt(options, 'linenostart', 1))
361
 
        self.linenostep = abs(get_int_opt(options, 'linenostep', 1))
362
 
        self.linenospecial = abs(get_int_opt(options, 'linenospecial', 0))
363
 
        self.nobackground = get_bool_opt(options, 'nobackground', False)
364
 
        self.lineseparator = options.get('lineseparator', '\n')
365
 
        self.lineanchors = options.get('lineanchors', '')
366
 
        self.anchorlinenos = options.get('anchorlinenos', False)
367
 
        self.hl_lines = set()
368
 
        for lineno in get_list_opt(options, 'hl_lines', []):
369
 
            try:
370
 
                self.hl_lines.add(int(lineno))
371
 
            except ValueError:
372
 
                pass
373
 
 
374
 
        self._class_cache = {}
375
 
        self._create_stylesheet()
376
 
 
377
 
    def _get_css_class(self, ttype):
378
 
        """Return the css class of this token type prefixed with
379
 
        the classprefix option."""
380
 
        if ttype in self._class_cache:
381
 
            return self._class_cache[ttype]
382
 
        return self.classprefix + _get_ttype_class(ttype)
383
 
 
384
 
    def _create_stylesheet(self):
385
 
        t2c = self.ttype2class = {Token: ''}
386
 
        c2s = self.class2style = {}
387
 
        cp = self.classprefix
388
 
        for ttype, ndef in self.style:
389
 
            name = cp + _get_ttype_class(ttype)
390
 
            style = ''
391
 
            if ndef['color']:
392
 
                style += 'color: #%s; ' % ndef['color']
393
 
            if ndef['bold']:
394
 
                style += 'font-weight: bold; '
395
 
            if ndef['italic']:
396
 
                style += 'font-style: italic; '
397
 
            if ndef['underline']:
398
 
                style += 'text-decoration: underline; '
399
 
            if ndef['bgcolor']:
400
 
                style += 'background-color: #%s; ' % ndef['bgcolor']
401
 
            if ndef['border']:
402
 
                style += 'border: 1px solid #%s; ' % ndef['border']
403
 
            if style:
404
 
                t2c[ttype] = name
405
 
                # save len(ttype) to enable ordering the styles by
406
 
                # hierarchy (necessary for CSS cascading rules!)
407
 
                c2s[name] = (style[:-2], ttype, len(ttype))
408
 
 
409
 
    def get_style_defs(self, arg=None):
410
 
        """
411
 
        Return CSS style definitions for the classes produced by the current
412
 
        highlighting style. ``arg`` can be a string or list of selectors to
413
 
        insert before the token type classes.
414
 
        """
415
 
        if arg is None:
416
 
            arg = ('cssclass' in self.options and '.'+self.cssclass or '')
417
 
        if isinstance(arg, basestring):
418
 
            args = [arg]
419
 
        else:
420
 
            args = list(arg)
421
 
 
422
 
        def prefix(cls):
423
 
            if cls:
424
 
                cls = '.' + cls
425
 
            tmp = []
426
 
            for arg in args:
427
 
                tmp.append((arg and arg + ' ' or '') + cls)
428
 
            return ', '.join(tmp)
429
 
 
430
 
        styles = [(level, ttype, cls, style)
431
 
                  for cls, (style, ttype, level) in self.class2style.iteritems()
432
 
                  if cls and style]
433
 
        styles.sort()
434
 
        lines = ['%s { %s } /* %s */' % (prefix(cls), style, repr(ttype)[6:])
435
 
                 for (level, ttype, cls, style) in styles]
436
 
        if arg and not self.nobackground and \
437
 
           self.style.background_color is not None:
438
 
            text_style = ''
439
 
            if Text in self.ttype2class:
440
 
                text_style = ' ' + self.class2style[self.ttype2class[Text]][0]
441
 
            lines.insert(0, '%s { background: %s;%s }' %
442
 
                         (prefix(''), self.style.background_color, text_style))
443
 
        if self.style.highlight_color is not None:
444
 
            lines.insert(0, '%s.hll { background-color: %s }' %
445
 
                         (prefix(''), self.style.highlight_color))
446
 
        return '\n'.join(lines)
447
 
 
448
 
    def _decodeifneeded(self, value):
449
 
        if isinstance(value, bytes):
450
 
            if self.encoding:
451
 
                return value.decode(self.encoding)
452
 
            return value.decode()
453
 
        return value
454
 
 
455
 
    def _wrap_full(self, inner, outfile):
456
 
        if self.cssfile:
457
 
            if os.path.isabs(self.cssfile):
458
 
                # it's an absolute filename
459
 
                cssfilename = self.cssfile
460
 
            else:
461
 
                try:
462
 
                    filename = outfile.name
463
 
                    if not filename or filename[0] == '<':
464
 
                        # pseudo files, e.g. name == '<fdopen>'
465
 
                        raise AttributeError
466
 
                    cssfilename = os.path.join(os.path.dirname(filename),
467
 
                                               self.cssfile)
468
 
                except AttributeError:
469
 
                    print >>sys.stderr, 'Note: Cannot determine output file name, ' \
470
 
                          'using current directory as base for the CSS file name'
471
 
                    cssfilename = self.cssfile
472
 
            # write CSS file only if noclobber_cssfile isn't given as an option.
473
 
            try:
474
 
                if not os.path.exists(cssfilename) or not self.noclobber_cssfile:
475
 
                    cf = open(cssfilename, "w")
476
 
                    cf.write(CSSFILE_TEMPLATE %
477
 
                            {'styledefs': self.get_style_defs('body')})
478
 
                    cf.close()
479
 
            except IOError, err:
480
 
                err.strerror = 'Error writing CSS file: ' + err.strerror
481
 
                raise
482
 
 
483
 
            yield 0, (DOC_HEADER_EXTERNALCSS %
484
 
                      dict(title     = self.title,
485
 
                           cssfile   = self.cssfile,
486
 
                           encoding  = self.encoding))
487
 
        else:
488
 
            yield 0, (DOC_HEADER %
489
 
                      dict(title     = self.title,
490
 
                           styledefs = self.get_style_defs('body'),
491
 
                           encoding  = self.encoding))
492
 
 
493
 
        for t, line in inner:
494
 
            yield t, line
495
 
        yield 0, DOC_FOOTER
496
 
 
497
 
    def _wrap_tablelinenos(self, inner):
498
 
        dummyoutfile = StringIO.StringIO()
499
 
        lncount = 0
500
 
        for t, line in inner:
501
 
            if t:
502
 
                lncount += 1
503
 
            dummyoutfile.write(line)
504
 
 
505
 
        fl = self.linenostart
506
 
        mw = len(str(lncount + fl - 1))
507
 
        sp = self.linenospecial
508
 
        st = self.linenostep
509
 
        la = self.lineanchors
510
 
        aln = self.anchorlinenos
511
 
        if sp:
512
 
            lines = []
513
 
 
514
 
            for i in range(fl, fl+lncount):
515
 
                if i % st == 0:
516
 
                    if i % sp == 0:
517
 
                        if aln:
518
 
                            lines.append('<a href="#%s-%d" class="special">%*d</a>' %
519
 
                                         (la, i, mw, i))
520
 
                        else:
521
 
                            lines.append('<span class="special">%*d</span>' % (mw, i))
522
 
                    else:
523
 
                        if aln:
524
 
                            lines.append('<a href="#%s-%d">%*d</a>' % (la, i, mw, i))
525
 
                        else:
526
 
                            lines.append('%*d' % (mw, i))
527
 
                else:
528
 
                    lines.append('')
529
 
            ls = '\n'.join(lines)
530
 
        else:
531
 
            lines = []
532
 
            for i in range(fl, fl+lncount):
533
 
                if i % st == 0:
534
 
                    if aln:
535
 
                        lines.append('<a href="#%s-%d">%*d</a>' % (la, i, mw, i))
536
 
                    else:
537
 
                        lines.append('%*d' % (mw, i))
538
 
                else:
539
 
                    lines.append('')
540
 
            ls = '\n'.join(lines)
541
 
 
542
 
        # in case you wonder about the seemingly redundant <div> here: since the
543
 
        # content in the other cell also is wrapped in a div, some browsers in
544
 
        # some configurations seem to mess up the formatting...
545
 
        yield 0, ('<table class="%stable">' % self.cssclass +
546
 
                  '<tr><td class="linenos"><div class="linenodiv"><pre>' +
547
 
                  ls + '</pre></div></td><td class="code">')
548
 
        yield 0, dummyoutfile.getvalue()
549
 
        yield 0, '</td></tr></table>'
550
 
 
551
 
    def _wrap_inlinelinenos(self, inner):
552
 
        # need a list of lines since we need the width of a single number :(
553
 
        lines = list(inner)
554
 
        sp = self.linenospecial
555
 
        st = self.linenostep
556
 
        num = self.linenostart
557
 
        mw = len(str(len(lines) + num - 1))
558
 
 
559
 
        if sp:
560
 
            for t, line in lines:
561
 
                yield 1, '<span class="lineno%s">%*s</span> ' % (
562
 
                    num%sp == 0 and ' special' or '', mw,
563
 
                    (num%st and ' ' or num)) + line
564
 
                num += 1
565
 
        else:
566
 
            for t, line in lines:
567
 
                yield 1, '<span class="lineno">%*s</span> ' % (
568
 
                    mw, (num%st and ' ' or num)) + line
569
 
                num += 1
570
 
 
571
 
    def _wrap_lineanchors(self, inner):
572
 
        s = self.lineanchors
573
 
        i = 0
574
 
        for t, line in inner:
575
 
            if t:
576
 
                i += 1
577
 
                yield 1, '<a name="%s-%d"></a>' % (s, i) + line
578
 
            else:
579
 
                yield 0, line
580
 
 
581
 
    def _wrap_div(self, inner):
582
 
        style = []
583
 
        if (self.noclasses and not self.nobackground and
584
 
            self.style.background_color is not None):
585
 
            style.append('background: %s' % (self.style.background_color,))
586
 
        if self.cssstyles:
587
 
            style.append(self.cssstyles)
588
 
        style = '; '.join(style)
589
 
 
590
 
        yield 0, ('<div' + (self.cssclass and ' class="%s"' % self.cssclass)
591
 
                  + (style and (' style="%s"' % style)) + '>')
592
 
        for tup in inner:
593
 
            yield tup
594
 
        yield 0, '</div>\n'
595
 
 
596
 
    def _wrap_pre(self, inner):
597
 
        style = []
598
 
        if self.prestyles:
599
 
            style.append(self.prestyles)
600
 
        if self.noclasses:
601
 
            style.append('line-height: 125%')
602
 
        style = '; '.join(style)
603
 
 
604
 
        yield 0, ('<pre' + (style and ' style="%s"' % style) + '>')
605
 
        for tup in inner:
606
 
            yield tup
607
 
        yield 0, '</pre>'
608
 
 
609
 
    def _format_lines(self, tokensource):
610
 
        """
611
 
        Just format the tokens, without any wrapping tags.
612
 
        Yield individual lines.
613
 
        """
614
 
        nocls = self.noclasses
615
 
        lsep = self.lineseparator
616
 
        # for <span style=""> lookup only
617
 
        getcls = self.ttype2class.get
618
 
        c2s = self.class2style
619
 
 
620
 
        lspan = ''
621
 
        line = ''
622
 
        for ttype, value in tokensource:
623
 
            if nocls:
624
 
                cclass = getcls(ttype)
625
 
                while cclass is None:
626
 
                    ttype = ttype.parent
627
 
                    cclass = getcls(ttype)
628
 
                cspan = cclass and '<span style="%s">' % c2s[cclass][0] or ''
629
 
            else:
630
 
                cls = self._get_css_class(ttype)
631
 
                cspan = cls and '<span class="%s">' % cls or ''
632
 
 
633
 
            parts = escape_html(value).split('\n')
634
 
 
635
 
            # for all but the last line
636
 
            for part in parts[:-1]:
637
 
                if line:
638
 
                    if lspan != cspan:
639
 
                        line += (lspan and '</span>') + cspan + part + \
640
 
                                (cspan and '</span>') + lsep
641
 
                    else: # both are the same
642
 
                        line += part + (lspan and '</span>') + lsep
643
 
                    yield 1, line
644
 
                    line = ''
645
 
                elif part:
646
 
                    yield 1, cspan + part + (cspan and '</span>') + lsep
647
 
                else:
648
 
                    yield 1, lsep
649
 
            # for the last line
650
 
            if line and parts[-1]:
651
 
                if lspan != cspan:
652
 
                    line += (lspan and '</span>') + cspan + parts[-1]
653
 
                    lspan = cspan
654
 
                else:
655
 
                    line += parts[-1]
656
 
            elif parts[-1]:
657
 
                line = cspan + parts[-1]
658
 
                lspan = cspan
659
 
            # else we neither have to open a new span nor set lspan
660
 
 
661
 
        if line:
662
 
            yield 1, line + (lspan and '</span>') + lsep
663
 
 
664
 
    def _highlight_lines(self, tokensource):
665
 
        """
666
 
        Highlighted the lines specified in the `hl_lines` option by
667
 
        post-processing the token stream coming from `_format_lines`.
668
 
        """
669
 
        hls = self.hl_lines
670
 
 
671
 
        for i, (t, value) in enumerate(tokensource):
672
 
            if t != 1:
673
 
                yield t, value
674
 
            if i + 1 in hls: # i + 1 because Python indexes start at 0
675
 
                if self.noclasses:
676
 
                    style = ''
677
 
                    if self.style.highlight_color is not None:
678
 
                        style = (' style="background-color: %s"' %
679
 
                                 (self.style.highlight_color,))
680
 
                    yield 1, '<span%s>%s</span>' % (style, value)
681
 
                else:
682
 
                    yield 1, '<span class="hll">%s</span>' % value
683
 
            else:
684
 
                yield 1, value
685
 
 
686
 
    def wrap(self, source, outfile):
687
 
        """
688
 
        Wrap the ``source``, which is a generator yielding
689
 
        individual lines, in custom generators. See docstring
690
 
        for `format`. Can be overridden.
691
 
        """
692
 
        return self._wrap_div(self._wrap_pre(source))
693
 
 
694
 
    def format_unencoded(self, tokensource, outfile):
695
 
        """
696
 
        The formatting process uses several nested generators; which of
697
 
        them are used is determined by the user's options.
698
 
 
699
 
        Each generator should take at least one argument, ``inner``,
700
 
        and wrap the pieces of text generated by this.
701
 
 
702
 
        Always yield 2-tuples: (code, text). If "code" is 1, the text
703
 
        is part of the original tokensource being highlighted, if it's
704
 
        0, the text is some piece of wrapping. This makes it possible to
705
 
        use several different wrappers that process the original source
706
 
        linewise, e.g. line number generators.
707
 
        """
708
 
        source = self._format_lines(tokensource)
709
 
        if self.hl_lines:
710
 
            source = self._highlight_lines(source)
711
 
        if not self.nowrap:
712
 
            if self.linenos == 2:
713
 
                source = self._wrap_inlinelinenos(source)
714
 
            if self.lineanchors:
715
 
                source = self._wrap_lineanchors(source)
716
 
            source = self.wrap(source, outfile)
717
 
            if self.linenos == 1:
718
 
                source = self._wrap_tablelinenos(source)
719
 
            if self.full:
720
 
                source = self._wrap_full(source, outfile)
721
 
 
722
 
        for t, piece in source:
723
 
            outfile.write(piece)