Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# -*- coding: utf-8 -*- 

2""" 

3 pygments.formatters.html 

4 ~~~~~~~~~~~~~~~~~~~~~~~~ 

5 

6 Formatter for HTML output. 

7 

8 :copyright: Copyright 2006-2021 by the Pygments team, see AUTHORS. 

9 :license: BSD, see LICENSE for details. 

10""" 

11 

12import functools 

13import os 

14import sys 

15import os.path 

16from io import StringIO 

17 

18from pygments.formatter import Formatter 

19from pygments.token import Token, Text, STANDARD_TYPES 

20from pygments.util import get_bool_opt, get_int_opt, get_list_opt 

21 

22try: 

23 import ctags 

24except ImportError: 

25 ctags = None 

26 

27__all__ = ['HtmlFormatter'] 

28 

29 

30_escape_html_table = { 

31 ord('&'): '&', 

32 ord('<'): '&lt;', 

33 ord('>'): '&gt;', 

34 ord('"'): '&quot;', 

35 ord("'"): '&#39;', 

36} 

37 

38 

39def escape_html(text, table=_escape_html_table): 

40 """Escape &, <, > as well as single and double quotes for HTML.""" 

41 return text.translate(table) 

42 

43 

44def webify(color): 

45 if color.startswith('calc') or color.startswith('var'): 

46 return color 

47 else: 

48 return '#' + color 

49 

50 

51def _get_ttype_class(ttype): 

52 fname = STANDARD_TYPES.get(ttype) 

53 if fname: 

54 return fname 

55 aname = '' 

56 while fname is None: 

57 aname = '-' + ttype[-1] + aname 

58 ttype = ttype.parent 

59 fname = STANDARD_TYPES.get(ttype) 

60 return fname + aname 

61 

62 

63CSSFILE_TEMPLATE = '''\ 

64/* 

65generated by Pygments <https://pygments.org/> 

66Copyright 2006-2021 by the Pygments team. 

67Licensed under the BSD license, see LICENSE for details. 

68*/ 

69%(styledefs)s 

70''' 

71 

72DOC_HEADER = '''\ 

73<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" 

74 "http://www.w3.org/TR/html4/strict.dtd"> 

75<!-- 

76generated by Pygments <https://pygments.org/> 

77Copyright 2006-2021 by the Pygments team. 

78Licensed under the BSD license, see LICENSE for details. 

79--> 

80<html> 

81<head> 

82 <title>%(title)s</title> 

83 <meta http-equiv="content-type" content="text/html; charset=%(encoding)s"> 

84 <style type="text/css"> 

85''' + CSSFILE_TEMPLATE + ''' 

86 </style> 

87</head> 

88<body> 

89<h2>%(title)s</h2> 

90 

91''' 

92 

93DOC_HEADER_EXTERNALCSS = '''\ 

94<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" 

95 "http://www.w3.org/TR/html4/strict.dtd"> 

96 

97<html> 

98<head> 

99 <title>%(title)s</title> 

100 <meta http-equiv="content-type" content="text/html; charset=%(encoding)s"> 

101 <link rel="stylesheet" href="%(cssfile)s" type="text/css"> 

102</head> 

103<body> 

104<h2>%(title)s</h2> 

105 

106''' 

107 

108DOC_FOOTER = '''\ 

109</body> 

110</html> 

111''' 

112 

113 

114class HtmlFormatter(Formatter): 

115 r""" 

116 Format tokens as HTML 4 ``<span>`` tags within a ``<pre>`` tag, wrapped 

117 in a ``<div>`` tag. The ``<div>``'s CSS class can be set by the `cssclass` 

118 option. 

119 

120 If the `linenos` option is set to ``"table"``, the ``<pre>`` is 

121 additionally wrapped inside a ``<table>`` which has one row and two 

122 cells: one containing the line numbers and one containing the code. 

123 Example: 

124 

125 .. sourcecode:: html 

126 

127 <div class="highlight" > 

128 <table><tr> 

129 <td class="linenos" title="click to toggle" 

130 onclick="with (this.firstChild.style) 

131 { display = (display == '') ? 'none' : '' }"> 

132 <pre>1 

133 2</pre> 

134 </td> 

135 <td class="code"> 

136 <pre><span class="Ke">def </span><span class="NaFu">foo</span>(bar): 

137 <span class="Ke">pass</span> 

138 </pre> 

139 </td> 

140 </tr></table></div> 

141 

142 (whitespace added to improve clarity). 

143 

144 Wrapping can be disabled using the `nowrap` option. 

145 

146 A list of lines can be specified using the `hl_lines` option to make these 

147 lines highlighted (as of Pygments 0.11). 

148 

149 With the `full` option, a complete HTML 4 document is output, including 

150 the style definitions inside a ``<style>`` tag, or in a separate file if 

151 the `cssfile` option is given. 

152 

153 When `tagsfile` is set to the path of a ctags index file, it is used to 

154 generate hyperlinks from names to their definition. You must enable 

155 `lineanchors` and run ctags with the `-n` option for this to work. The 

156 `python-ctags` module from PyPI must be installed to use this feature; 

157 otherwise a `RuntimeError` will be raised. 

158 

159 The `get_style_defs(arg='')` method of a `HtmlFormatter` returns a string 

160 containing CSS rules for the CSS classes used by the formatter. The 

161 argument `arg` can be used to specify additional CSS selectors that 

162 are prepended to the classes. A call `fmter.get_style_defs('td .code')` 

163 would result in the following CSS classes: 

164 

165 .. sourcecode:: css 

166 

167 td .code .kw { font-weight: bold; color: #00FF00 } 

168 td .code .cm { color: #999999 } 

169 ... 

170 

171 If you have Pygments 0.6 or higher, you can also pass a list or tuple to the 

172 `get_style_defs()` method to request multiple prefixes for the tokens: 

173 

174 .. sourcecode:: python 

175 

176 formatter.get_style_defs(['div.syntax pre', 'pre.syntax']) 

177 

178 The output would then look like this: 

179 

180 .. sourcecode:: css 

181 

182 div.syntax pre .kw, 

183 pre.syntax .kw { font-weight: bold; color: #00FF00 } 

184 div.syntax pre .cm, 

185 pre.syntax .cm { color: #999999 } 

186 ... 

187 

188 Additional options accepted: 

189 

190 `nowrap` 

191 If set to ``True``, don't wrap the tokens at all, not even inside a ``<pre>`` 

192 tag. This disables most other options (default: ``False``). 

193 

194 `full` 

195 Tells the formatter to output a "full" document, i.e. a complete 

196 self-contained document (default: ``False``). 

197 

198 `title` 

199 If `full` is true, the title that should be used to caption the 

200 document (default: ``''``). 

201 

202 `style` 

203 The style to use, can be a string or a Style subclass (default: 

204 ``'default'``). This option has no effect if the `cssfile` 

205 and `noclobber_cssfile` option are given and the file specified in 

206 `cssfile` exists. 

207 

208 `noclasses` 

209 If set to true, token ``<span>`` tags (as well as line number elements) 

210 will not use CSS classes, but inline styles. This is not recommended 

211 for larger pieces of code since it increases output size by quite a bit 

212 (default: ``False``). 

213 

214 `classprefix` 

215 Since the token types use relatively short class names, they may clash 

216 with some of your own class names. In this case you can use the 

217 `classprefix` option to give a string to prepend to all Pygments-generated 

218 CSS class names for token types. 

219 Note that this option also affects the output of `get_style_defs()`. 

220 

221 `cssclass` 

222 CSS class for the wrapping ``<div>`` tag (default: ``'highlight'``). 

223 If you set this option, the default selector for `get_style_defs()` 

224 will be this class. 

225 

226 .. versionadded:: 0.9 

227 If you select the ``'table'`` line numbers, the wrapping table will 

228 have a CSS class of this string plus ``'table'``, the default is 

229 accordingly ``'highlighttable'``. 

230 

231 `cssstyles` 

232 Inline CSS styles for the wrapping ``<div>`` tag (default: ``''``). 

233 

234 `prestyles` 

235 Inline CSS styles for the ``<pre>`` tag (default: ``''``). 

236 

237 .. versionadded:: 0.11 

238 

239 `cssfile` 

240 If the `full` option is true and this option is given, it must be the 

241 name of an external file. If the filename does not include an absolute 

242 path, the file's path will be assumed to be relative to the main output 

243 file's path, if the latter can be found. The stylesheet is then written 

244 to this file instead of the HTML file. 

245 

246 .. versionadded:: 0.6 

247 

248 `noclobber_cssfile` 

249 If `cssfile` is given and the specified file exists, the css file will 

250 not be overwritten. This allows the use of the `full` option in 

251 combination with a user specified css file. Default is ``False``. 

252 

253 .. versionadded:: 1.1 

254 

255 `linenos` 

256 If set to ``'table'``, output line numbers as a table with two cells, 

257 one containing the line numbers, the other the whole code. This is 

258 copy-and-paste-friendly, but may cause alignment problems with some 

259 browsers or fonts. If set to ``'inline'``, the line numbers will be 

260 integrated in the ``<pre>`` tag that contains the code (that setting 

261 is *new in Pygments 0.8*). 

262 

263 For compatibility with Pygments 0.7 and earlier, every true value 

264 except ``'inline'`` means the same as ``'table'`` (in particular, that 

265 means also ``True``). 

266 

267 The default value is ``False``, which means no line numbers at all. 

268 

269 **Note:** with the default ("table") line number mechanism, the line 

270 numbers and code can have different line heights in Internet Explorer 

271 unless you give the enclosing ``<pre>`` tags an explicit ``line-height`` 

272 CSS property (you get the default line spacing with ``line-height: 

273 125%``). 

274 

275 `hl_lines` 

276 Specify a list of lines to be highlighted. 

277 

278 .. versionadded:: 0.11 

279 

280 `linenostart` 

281 The line number for the first line (default: ``1``). 

282 

283 `linenostep` 

284 If set to a number n > 1, only every nth line number is printed. 

285 

286 `linenospecial` 

287 If set to a number n > 0, every nth line number is given the CSS 

288 class ``"special"`` (default: ``0``). 

289 

290 `nobackground` 

291 If set to ``True``, the formatter won't output the background color 

292 for the wrapping element (this automatically defaults to ``False`` 

293 when there is no wrapping element [eg: no argument for the 

294 `get_syntax_defs` method given]) (default: ``False``). 

295 

296 .. versionadded:: 0.6 

297 

298 `lineseparator` 

299 This string is output between lines of code. It defaults to ``"\n"``, 

300 which is enough to break a line inside ``<pre>`` tags, but you can 

301 e.g. set it to ``"<br>"`` to get HTML line breaks. 

302 

303 .. versionadded:: 0.7 

304 

305 `lineanchors` 

306 If set to a nonempty string, e.g. ``foo``, the formatter will wrap each 

307 output line in an anchor tag with a ``name`` of ``foo-linenumber``. 

308 This allows easy linking to certain lines. 

309 

310 .. versionadded:: 0.9 

311 

312 `linespans` 

313 If set to a nonempty string, e.g. ``foo``, the formatter will wrap each 

314 output line in a span tag with an ``id`` of ``foo-linenumber``. 

315 This allows easy access to lines via javascript. 

316 

317 .. versionadded:: 1.6 

318 

319 `anchorlinenos` 

320 If set to `True`, will wrap line numbers in <a> tags. Used in 

321 combination with `linenos` and `lineanchors`. 

322 

323 `tagsfile` 

324 If set to the path of a ctags file, wrap names in anchor tags that 

325 link to their definitions. `lineanchors` should be used, and the 

326 tags file should specify line numbers (see the `-n` option to ctags). 

327 

328 .. versionadded:: 1.6 

329 

330 `tagurlformat` 

331 A string formatting pattern used to generate links to ctags definitions. 

332 Available variables are `%(path)s`, `%(fname)s` and `%(fext)s`. 

333 Defaults to an empty string, resulting in just `#prefix-number` links. 

334 

335 .. versionadded:: 1.6 

336 

337 `filename` 

338 A string used to generate a filename when rendering ``<pre>`` blocks, 

339 for example if displaying source code. 

340 

341 .. versionadded:: 2.1 

342 

343 `wrapcode` 

344 Wrap the code inside ``<pre>`` blocks using ``<code>``, as recommended 

345 by the HTML5 specification. 

346 

347 .. versionadded:: 2.4 

348 

349 

350 **Subclassing the HTML formatter** 

351 

352 .. versionadded:: 0.7 

353 

354 The HTML formatter is now built in a way that allows easy subclassing, thus 

355 customizing the output HTML code. The `format()` method calls 

356 `self._format_lines()` which returns a generator that yields tuples of ``(1, 

357 line)``, where the ``1`` indicates that the ``line`` is a line of the 

358 formatted source code. 

359 

360 If the `nowrap` option is set, the generator is the iterated over and the 

361 resulting HTML is output. 

362 

363 Otherwise, `format()` calls `self.wrap()`, which wraps the generator with 

364 other generators. These may add some HTML code to the one generated by 

365 `_format_lines()`, either by modifying the lines generated by the latter, 

366 then yielding them again with ``(1, line)``, and/or by yielding other HTML 

367 code before or after the lines, with ``(0, html)``. The distinction between 

368 source lines and other code makes it possible to wrap the generator multiple 

369 times. 

370 

371 The default `wrap()` implementation adds a ``<div>`` and a ``<pre>`` tag. 

372 

373 A custom `HtmlFormatter` subclass could look like this: 

374 

375 .. sourcecode:: python 

376 

377 class CodeHtmlFormatter(HtmlFormatter): 

378 

379 def wrap(self, source, outfile): 

380 return self._wrap_code(source) 

381 

382 def _wrap_code(self, source): 

383 yield 0, '<code>' 

384 for i, t in source: 

385 if i == 1: 

386 # it's a line of formatted code 

387 t += '<br>' 

388 yield i, t 

389 yield 0, '</code>' 

390 

391 This results in wrapping the formatted lines with a ``<code>`` tag, where the 

392 source lines are broken using ``<br>`` tags. 

393 

394 After calling `wrap()`, the `format()` method also adds the "line numbers" 

395 and/or "full document" wrappers if the respective options are set. Then, all 

396 HTML yielded by the wrapped generator is output. 

397 """ 

398 

399 name = 'HTML' 

400 aliases = ['html'] 

401 filenames = ['*.html', '*.htm'] 

402 

403 def __init__(self, **options): 

404 Formatter.__init__(self, **options) 

405 self.title = self._decodeifneeded(self.title) 

406 self.nowrap = get_bool_opt(options, 'nowrap', False) 

407 self.noclasses = get_bool_opt(options, 'noclasses', False) 

408 self.classprefix = options.get('classprefix', '') 

409 self.cssclass = self._decodeifneeded(options.get('cssclass', 'highlight')) 

410 self.cssstyles = self._decodeifneeded(options.get('cssstyles', '')) 

411 self.prestyles = self._decodeifneeded(options.get('prestyles', '')) 

412 self.cssfile = self._decodeifneeded(options.get('cssfile', '')) 

413 self.noclobber_cssfile = get_bool_opt(options, 'noclobber_cssfile', False) 

414 self.tagsfile = self._decodeifneeded(options.get('tagsfile', '')) 

415 self.tagurlformat = self._decodeifneeded(options.get('tagurlformat', '')) 

416 self.filename = self._decodeifneeded(options.get('filename', '')) 

417 self.wrapcode = get_bool_opt(options, 'wrapcode', False) 

418 self.span_element_openers = {} 

419 

420 if self.tagsfile: 

421 if not ctags: 

422 raise RuntimeError('The "ctags" package must to be installed ' 

423 'to be able to use the "tagsfile" feature.') 

424 self._ctags = ctags.CTags(self.tagsfile) 

425 

426 linenos = options.get('linenos', False) 

427 if linenos == 'inline': 

428 self.linenos = 2 

429 elif linenos: 

430 # compatibility with <= 0.7 

431 self.linenos = 1 

432 else: 

433 self.linenos = 0 

434 self.linenostart = abs(get_int_opt(options, 'linenostart', 1)) 

435 self.linenostep = abs(get_int_opt(options, 'linenostep', 1)) 

436 self.linenospecial = abs(get_int_opt(options, 'linenospecial', 0)) 

437 self.nobackground = get_bool_opt(options, 'nobackground', False) 

438 self.lineseparator = options.get('lineseparator', '\n') 

439 self.lineanchors = options.get('lineanchors', '') 

440 self.linespans = options.get('linespans', '') 

441 self.anchorlinenos = get_bool_opt(options, 'anchorlinenos', False) 

442 self.hl_lines = set() 

443 for lineno in get_list_opt(options, 'hl_lines', []): 

444 try: 

445 self.hl_lines.add(int(lineno)) 

446 except ValueError: 

447 pass 

448 

449 self._create_stylesheet() 

450 

451 def _get_css_class(self, ttype): 

452 """Return the css class of this token type prefixed with 

453 the classprefix option.""" 

454 ttypeclass = _get_ttype_class(ttype) 

455 if ttypeclass: 

456 return self.classprefix + ttypeclass 

457 return '' 

458 

459 def _get_css_classes(self, ttype): 

460 """Return the CSS classes of this token type prefixed with the classprefix option.""" 

461 cls = self._get_css_class(ttype) 

462 while ttype not in STANDARD_TYPES: 

463 ttype = ttype.parent 

464 cls = self._get_css_class(ttype) + ' ' + cls 

465 return cls or '' 

466 

467 def _get_css_inline_styles(self, ttype): 

468 """Return the inline CSS styles for this token type.""" 

469 cclass = self.ttype2class.get(ttype) 

470 while cclass is None: 

471 ttype = ttype.parent 

472 cclass = self.ttype2class.get(ttype) 

473 return cclass or '' 

474 

475 def _create_stylesheet(self): 

476 t2c = self.ttype2class = {Token: ''} 

477 c2s = self.class2style = {} 

478 for ttype, ndef in self.style: 

479 name = self._get_css_class(ttype) 

480 style = '' 

481 if ndef['color']: 

482 style += 'color: %s; ' % webify(ndef['color']) 

483 if ndef['bold']: 

484 style += 'font-weight: bold; ' 

485 if ndef['italic']: 

486 style += 'font-style: italic; ' 

487 if ndef['underline']: 

488 style += 'text-decoration: underline; ' 

489 if ndef['bgcolor']: 

490 style += 'background-color: %s; ' % webify(ndef['bgcolor']) 

491 if ndef['border']: 

492 style += 'border: 1px solid %s; ' % webify(ndef['border']) 

493 if style: 

494 t2c[ttype] = name 

495 # save len(ttype) to enable ordering the styles by 

496 # hierarchy (necessary for CSS cascading rules!) 

497 c2s[name] = (style[:-2], ttype, len(ttype)) 

498 

499 def get_style_defs(self, arg=None): 

500 """ 

501 Return CSS style definitions for the classes produced by the current 

502 highlighting style. ``arg`` can be a string or list of selectors to 

503 insert before the token type classes. 

504 """ 

505 style_lines = [] 

506 

507 style_lines.extend(self.get_linenos_style_defs()) 

508 style_lines.extend(self.get_background_style_defs(arg)) 

509 style_lines.extend(self.get_token_style_defs(arg)) 

510 

511 return '\n'.join(style_lines) 

512 

513 def get_token_style_defs(self, arg=None): 

514 prefix = self.get_css_prefix(arg) 

515 

516 styles = [ 

517 (level, ttype, cls, style) 

518 for cls, (style, ttype, level) in self.class2style.items() 

519 if cls and style 

520 ] 

521 styles.sort() 

522 

523 lines = [ 

524 '%s { %s } /* %s */' % (prefix(cls), style, repr(ttype)[6:]) 

525 for (level, ttype, cls, style) in styles 

526 ] 

527 

528 return lines 

529 

530 def get_background_style_defs(self, arg=None): 

531 prefix = self.get_css_prefix(arg) 

532 bg_color = self.style.background_color 

533 hl_color = self.style.highlight_color 

534 

535 lines = [] 

536 

537 if arg and not self.nobackground and bg_color is not None: 

538 text_style = '' 

539 if Text in self.ttype2class: 

540 text_style = ' ' + self.class2style[self.ttype2class[Text]][0] 

541 lines.insert( 

542 0, '%s{ background: %s;%s }' % ( 

543 prefix(''), bg_color, text_style 

544 ) 

545 ) 

546 if hl_color is not None: 

547 lines.insert( 

548 0, '%s { background-color: %s }' % (prefix('hll'), hl_color) 

549 ) 

550 

551 return lines 

552 

553 def get_linenos_style_defs(self): 

554 lines = [ 

555 'pre { %s }' % self._pre_style, 

556 'td.linenos pre { %s }' % self._linenos_style, 

557 'span.linenos { %s }' % self._linenos_style, 

558 'td.linenos pre.special { %s }' % self._linenos_special_style, 

559 'span.linenos.special { %s }' % self._linenos_special_style, 

560 ] 

561 

562 return lines 

563 

564 def get_css_prefix(self, arg): 

565 if arg is None: 

566 arg = ('cssclass' in self.options and '.'+self.cssclass or '') 

567 if isinstance(arg, str): 

568 args = [arg] 

569 else: 

570 args = list(arg) 

571 

572 def prefix(cls): 

573 if cls: 

574 cls = '.' + cls 

575 tmp = [] 

576 for arg in args: 

577 tmp.append((arg and arg + ' ' or '') + cls) 

578 return ', '.join(tmp) 

579 

580 return prefix 

581 

582 @property 

583 def _pre_style(self): 

584 return 'line-height: 125%;' 

585 

586 @property 

587 def _linenos_style(self): 

588 return 'color: %s; background-color: %s; padding-left: 5px; padding-right: 5px;' % ( 

589 self.style.line_number_color, 

590 self.style.line_number_background_color 

591 ) 

592 

593 @property 

594 def _linenos_special_style(self): 

595 return 'color: %s; background-color: %s; padding-left: 5px; padding-right: 5px;' % ( 

596 self.style.line_number_special_color, 

597 self.style.line_number_special_background_color 

598 ) 

599 

600 def _decodeifneeded(self, value): 

601 if isinstance(value, bytes): 

602 if self.encoding: 

603 return value.decode(self.encoding) 

604 return value.decode() 

605 return value 

606 

607 def _wrap_full(self, inner, outfile): 

608 if self.cssfile: 

609 if os.path.isabs(self.cssfile): 

610 # it's an absolute filename 

611 cssfilename = self.cssfile 

612 else: 

613 try: 

614 filename = outfile.name 

615 if not filename or filename[0] == '<': 

616 # pseudo files, e.g. name == '<fdopen>' 

617 raise AttributeError 

618 cssfilename = os.path.join(os.path.dirname(filename), 

619 self.cssfile) 

620 except AttributeError: 

621 print('Note: Cannot determine output file name, ' 

622 'using current directory as base for the CSS file name', 

623 file=sys.stderr) 

624 cssfilename = self.cssfile 

625 # write CSS file only if noclobber_cssfile isn't given as an option. 

626 try: 

627 if not os.path.exists(cssfilename) or not self.noclobber_cssfile: 

628 with open(cssfilename, "w") as cf: 

629 cf.write(CSSFILE_TEMPLATE % 

630 {'styledefs': self.get_style_defs('body')}) 

631 except IOError as err: 

632 err.strerror = 'Error writing CSS file: ' + err.strerror 

633 raise 

634 

635 yield 0, (DOC_HEADER_EXTERNALCSS % 

636 dict(title=self.title, 

637 cssfile=self.cssfile, 

638 encoding=self.encoding)) 

639 else: 

640 yield 0, (DOC_HEADER % 

641 dict(title=self.title, 

642 styledefs=self.get_style_defs('body'), 

643 encoding=self.encoding)) 

644 

645 yield from inner 

646 yield 0, DOC_FOOTER 

647 

648 def _wrap_tablelinenos(self, inner): 

649 dummyoutfile = StringIO() 

650 lncount = 0 

651 for t, line in inner: 

652 if t: 

653 lncount += 1 

654 dummyoutfile.write(line) 

655 

656 fl = self.linenostart 

657 mw = len(str(lncount + fl - 1)) 

658 sp = self.linenospecial 

659 st = self.linenostep 

660 la = self.lineanchors 

661 aln = self.anchorlinenos 

662 nocls = self.noclasses 

663 

664 lines = [] 

665 

666 for i in range(fl, fl+lncount): 

667 print_line = i % st == 0 

668 special_line = sp and i % sp == 0 

669 

670 if print_line: 

671 line = '%*d' % (mw, i) 

672 if aln: 

673 line = '<a href="#%s-%d">%s</a>' % (la, i, line) 

674 else: 

675 line = ' ' * mw 

676 

677 if nocls: 

678 if special_line: 

679 style = ' style="%s"' % self._linenos_special_style 

680 else: 

681 style = ' style="%s"' % self._linenos_style 

682 else: 

683 if special_line: 

684 style = ' class="special"' 

685 else: 

686 style = '' 

687 

688 if style: 

689 line = '<span%s>%s</span>' % (style, line) 

690 

691 lines.append(line) 

692 

693 ls = '\n'.join(lines) 

694 

695 # in case you wonder about the seemingly redundant <div> here: since the 

696 # content in the other cell also is wrapped in a div, some browsers in 

697 # some configurations seem to mess up the formatting... 

698 yield 0, ( 

699 '<table class="%stable">' % self.cssclass + 

700 '<tr><td class="linenos"><div class="linenodiv"><pre>' + 

701 ls + '</pre></div></td><td class="code">' 

702 ) 

703 yield 0, dummyoutfile.getvalue() 

704 yield 0, '</td></tr></table>' 

705 

706 def _wrap_inlinelinenos(self, inner): 

707 # need a list of lines since we need the width of a single number :( 

708 inner_lines = list(inner) 

709 sp = self.linenospecial 

710 st = self.linenostep 

711 num = self.linenostart 

712 mw = len(str(len(inner_lines) + num - 1)) 

713 nocls = self.noclasses 

714 

715 for _, inner_line in inner_lines: 

716 print_line = num % st == 0 

717 special_line = sp and num % sp == 0 

718 

719 if print_line: 

720 line = '%*d' % (mw, num) 

721 else: 

722 line = ' ' * mw 

723 

724 if nocls: 

725 if special_line: 

726 style = ' style="%s"' % self._linenos_special_style 

727 else: 

728 style = ' style="%s"' % self._linenos_style 

729 else: 

730 if special_line: 

731 style = ' class="linenos special"' 

732 else: 

733 style = ' class="linenos"' 

734 

735 if style: 

736 yield 1, '<span%s>%s</span>' % (style, line) + inner_line 

737 else: 

738 yield 1, line + inner_line 

739 num += 1 

740 

741 def _wrap_lineanchors(self, inner): 

742 s = self.lineanchors 

743 # subtract 1 since we have to increment i *before* yielding 

744 i = self.linenostart - 1 

745 for t, line in inner: 

746 if t: 

747 i += 1 

748 yield 1, '<a name="%s-%d"></a>' % (s, i) + line 

749 else: 

750 yield 0, line 

751 

752 def _wrap_linespans(self, inner): 

753 s = self.linespans 

754 i = self.linenostart - 1 

755 for t, line in inner: 

756 if t: 

757 i += 1 

758 yield 1, '<span id="%s-%d">%s</span>' % (s, i, line) 

759 else: 

760 yield 0, line 

761 

762 def _wrap_div(self, inner): 

763 style = [] 

764 if (self.noclasses and not self.nobackground and 

765 self.style.background_color is not None): 

766 style.append('background: %s' % (self.style.background_color,)) 

767 if self.cssstyles: 

768 style.append(self.cssstyles) 

769 style = '; '.join(style) 

770 

771 yield 0, ('<div' + (self.cssclass and ' class="%s"' % self.cssclass) + 

772 (style and (' style="%s"' % style)) + '>') 

773 yield from inner 

774 yield 0, '</div>\n' 

775 

776 def _wrap_pre(self, inner): 

777 style = [] 

778 if self.prestyles: 

779 style.append(self.prestyles) 

780 if self.noclasses: 

781 style.append(self._pre_style) 

782 style = '; '.join(style) 

783 

784 if self.filename: 

785 yield 0, ('<span class="filename">' + self.filename + '</span>') 

786 

787 # the empty span here is to keep leading empty lines from being 

788 # ignored by HTML parsers 

789 yield 0, ('<pre' + (style and ' style="%s"' % style) + '><span></span>') 

790 yield from inner 

791 yield 0, '</pre>' 

792 

793 def _wrap_code(self, inner): 

794 yield 0, '<code>' 

795 yield from inner 

796 yield 0, '</code>' 

797 

798 @functools.lru_cache(maxsize=100) 

799 def _translate_parts(self, value): 

800 """HTML-escape a value and split it by newlines.""" 

801 return value.translate(_escape_html_table).split('\n') 

802 

803 def _format_lines(self, tokensource): 

804 """ 

805 Just format the tokens, without any wrapping tags. 

806 Yield individual lines. 

807 """ 

808 nocls = self.noclasses 

809 lsep = self.lineseparator 

810 tagsfile = self.tagsfile 

811 

812 lspan = '' 

813 line = [] 

814 for ttype, value in tokensource: 

815 try: 

816 cspan = self.span_element_openers[ttype] 

817 except KeyError: 

818 if nocls: 

819 css_style = self._get_css_inline_styles(ttype) 

820 cspan = css_style and '<span style="%s">' % self.class2style[css_style][0] or '' 

821 else: 

822 css_class = self._get_css_classes(ttype) 

823 cspan = css_class and '<span class="%s">' % css_class or '' 

824 self.span_element_openers[ttype] = cspan 

825 

826 parts = self._translate_parts(value) 

827 

828 if tagsfile and ttype in Token.Name: 

829 filename, linenumber = self._lookup_ctag(value) 

830 if linenumber: 

831 base, filename = os.path.split(filename) 

832 if base: 

833 base += '/' 

834 filename, extension = os.path.splitext(filename) 

835 url = self.tagurlformat % {'path': base, 'fname': filename, 

836 'fext': extension} 

837 parts[0] = "<a href=\"%s#%s-%d\">%s" % \ 

838 (url, self.lineanchors, linenumber, parts[0]) 

839 parts[-1] = parts[-1] + "</a>" 

840 

841 # for all but the last line 

842 for part in parts[:-1]: 

843 if line: 

844 if lspan != cspan: 

845 line.extend(((lspan and '</span>'), cspan, part, 

846 (cspan and '</span>'), lsep)) 

847 else: # both are the same 

848 line.extend((part, (lspan and '</span>'), lsep)) 

849 yield 1, ''.join(line) 

850 line = [] 

851 elif part: 

852 yield 1, ''.join((cspan, part, (cspan and '</span>'), lsep)) 

853 else: 

854 yield 1, lsep 

855 # for the last line 

856 if line and parts[-1]: 

857 if lspan != cspan: 

858 line.extend(((lspan and '</span>'), cspan, parts[-1])) 

859 lspan = cspan 

860 else: 

861 line.append(parts[-1]) 

862 elif parts[-1]: 

863 line = [cspan, parts[-1]] 

864 lspan = cspan 

865 # else we neither have to open a new span nor set lspan 

866 

867 if line: 

868 line.extend(((lspan and '</span>'), lsep)) 

869 yield 1, ''.join(line) 

870 

871 def _lookup_ctag(self, token): 

872 entry = ctags.TagEntry() 

873 if self._ctags.find(entry, token, 0): 

874 return entry['file'], entry['lineNumber'] 

875 else: 

876 return None, None 

877 

878 def _highlight_lines(self, tokensource): 

879 """ 

880 Highlighted the lines specified in the `hl_lines` option by 

881 post-processing the token stream coming from `_format_lines`. 

882 """ 

883 hls = self.hl_lines 

884 

885 for i, (t, value) in enumerate(tokensource): 

886 if t != 1: 

887 yield t, value 

888 if i + 1 in hls: # i + 1 because Python indexes start at 0 

889 if self.noclasses: 

890 style = '' 

891 if self.style.highlight_color is not None: 

892 style = (' style="background-color: %s"' % 

893 (self.style.highlight_color,)) 

894 yield 1, '<span%s>%s</span>' % (style, value) 

895 else: 

896 yield 1, '<span class="hll">%s</span>' % value 

897 else: 

898 yield 1, value 

899 

900 def wrap(self, source, outfile): 

901 """ 

902 Wrap the ``source``, which is a generator yielding 

903 individual lines, in custom generators. See docstring 

904 for `format`. Can be overridden. 

905 """ 

906 if self.wrapcode: 

907 return self._wrap_div(self._wrap_pre(self._wrap_code(source))) 

908 else: 

909 return self._wrap_div(self._wrap_pre(source)) 

910 

911 def format_unencoded(self, tokensource, outfile): 

912 """ 

913 The formatting process uses several nested generators; which of 

914 them are used is determined by the user's options. 

915 

916 Each generator should take at least one argument, ``inner``, 

917 and wrap the pieces of text generated by this. 

918 

919 Always yield 2-tuples: (code, text). If "code" is 1, the text 

920 is part of the original tokensource being highlighted, if it's 

921 0, the text is some piece of wrapping. This makes it possible to 

922 use several different wrappers that process the original source 

923 linewise, e.g. line number generators. 

924 """ 

925 source = self._format_lines(tokensource) 

926 if self.hl_lines: 

927 source = self._highlight_lines(source) 

928 if not self.nowrap: 

929 if self.linenos == 2: 

930 source = self._wrap_inlinelinenos(source) 

931 if self.lineanchors: 

932 source = self._wrap_lineanchors(source) 

933 if self.linespans: 

934 source = self._wrap_linespans(source) 

935 source = self.wrap(source, outfile) 

936 if self.linenos == 1: 

937 source = self._wrap_tablelinenos(source) 

938 if self.full: 

939 source = self._wrap_full(source, outfile) 

940 

941 for t, piece in source: 

942 outfile.write(piece)