Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pygments/formatters/html.py: 14%

412 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 06:07 +0000

1""" 

2 pygments.formatters.html 

3 ~~~~~~~~~~~~~~~~~~~~~~~~ 

4 

5 Formatter for HTML output. 

6 

7 :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS. 

8 :license: BSD, see LICENSE for details. 

9""" 

10 

11import functools 

12import os 

13import sys 

14import os.path 

15from io import StringIO 

16 

17from pygments.formatter import Formatter 

18from pygments.token import Token, Text, STANDARD_TYPES 

19from pygments.util import get_bool_opt, get_int_opt, get_list_opt 

20 

21try: 

22 import ctags 

23except ImportError: 

24 ctags = None 

25 

26__all__ = ['HtmlFormatter'] 

27 

28 

29_escape_html_table = { 

30 ord('&'): '&', 

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

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

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

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

35} 

36 

37 

38def escape_html(text, table=_escape_html_table): 

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

40 return text.translate(table) 

41 

42 

43def webify(color): 

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

45 return color 

46 else: 

47 return '#' + color 

48 

49 

50def _get_ttype_class(ttype): 

51 fname = STANDARD_TYPES.get(ttype) 

52 if fname: 

53 return fname 

54 aname = '' 

55 while fname is None: 

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

57 ttype = ttype.parent 

58 fname = STANDARD_TYPES.get(ttype) 

59 return fname + aname 

60 

61 

62CSSFILE_TEMPLATE = '''\ 

63/* 

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

65Copyright 2006-2022 by the Pygments team. 

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

67*/ 

68%(styledefs)s 

69''' 

70 

71DOC_HEADER = '''\ 

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

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

74<!-- 

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

76Copyright 2006-2022 by the Pygments team. 

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

78--> 

79<html> 

80<head> 

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

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

83 <style type="text/css"> 

84''' + CSSFILE_TEMPLATE + ''' 

85 </style> 

86</head> 

87<body> 

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

89 

90''' 

91 

92DOC_HEADER_EXTERNALCSS = '''\ 

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

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

95 

96<html> 

97<head> 

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

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

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

101</head> 

102<body> 

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

104 

105''' 

106 

107DOC_FOOTER = '''\ 

108</body> 

109</html> 

110''' 

111 

112 

113class HtmlFormatter(Formatter): 

114 r""" 

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

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

117 option. 

118 

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

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

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

122 Example: 

123 

124 .. sourcecode:: html 

125 

126 <div class="highlight" > 

127 <table><tr> 

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

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

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

131 <pre>1 

132 2</pre> 

133 </td> 

134 <td class="code"> 

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

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

137 </pre> 

138 </td> 

139 </tr></table></div> 

140 

141 (whitespace added to improve clarity). 

142 

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

144 

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

146 lines highlighted (as of Pygments 0.11). 

147 

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

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

150 the `cssfile` option is given. 

151 

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

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

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

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

156 otherwise a `RuntimeError` will be raised. 

157 

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

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

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

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

162 would result in the following CSS classes: 

163 

164 .. sourcecode:: css 

165 

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

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

168 ... 

169 

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

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

172 

173 .. sourcecode:: python 

174 

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

176 

177 The output would then look like this: 

178 

179 .. sourcecode:: css 

180 

181 div.syntax pre .kw, 

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

183 div.syntax pre .cm, 

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

185 ... 

186 

187 Additional options accepted: 

188 

189 `nowrap` 

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

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

192 

193 `full` 

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

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

196 

197 `title` 

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

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

200 

201 `style` 

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

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

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

205 `cssfile` exists. 

206 

207 `noclasses` 

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

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

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

211 (default: ``False``). 

212 

213 `classprefix` 

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

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

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

217 CSS class names for token types. 

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

219 

220 `cssclass` 

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

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

223 will be this class. 

224 

225 .. versionadded:: 0.9 

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

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

228 accordingly ``'highlighttable'``. 

229 

230 `cssstyles` 

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

232 

233 `prestyles` 

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

235 

236 .. versionadded:: 0.11 

237 

238 `cssfile` 

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

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

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

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

243 to this file instead of the HTML file. 

244 

245 .. versionadded:: 0.6 

246 

247 `noclobber_cssfile` 

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

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

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

251 

252 .. versionadded:: 1.1 

253 

254 `linenos` 

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

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

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

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

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

260 is *new in Pygments 0.8*). 

261 

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

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

264 means also ``True``). 

265 

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

267 

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

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

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

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

272 125%``). 

273 

274 `hl_lines` 

275 Specify a list of lines to be highlighted. The line numbers are always 

276 relative to the input (i.e. the first line is line 1) and are 

277 independent of `linenostart`. 

278 

279 .. versionadded:: 0.11 

280 

281 `linenostart` 

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

283 

284 `linenostep` 

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

286 

287 `linenospecial` 

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

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

290 

291 `nobackground` 

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

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

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

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

296 

297 .. versionadded:: 0.6 

298 

299 `lineseparator` 

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

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

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

303 

304 .. versionadded:: 0.7 

305 

306 `lineanchors` 

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

308 output line in an anchor tag with an ``id`` (and `name`) of ``foo-linenumber``. 

309 This allows easy linking to certain lines. 

310 

311 .. versionadded:: 0.9 

312 

313 `linespans` 

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

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

316 This allows easy access to lines via javascript. 

317 

318 .. versionadded:: 1.6 

319 

320 `anchorlinenos` 

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

322 combination with `linenos` and `lineanchors`. 

323 

324 `tagsfile` 

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

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

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

328 

329 .. versionadded:: 1.6 

330 

331 `tagurlformat` 

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

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

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

335 

336 .. versionadded:: 1.6 

337 

338 `filename` 

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

340 for example if displaying source code. If `linenos` is set to 

341 ``'table'`` then the filename will be rendered in an initial row 

342 containing a single `<th>` which spans both columns. 

343 

344 .. versionadded:: 2.1 

345 

346 `wrapcode` 

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

348 by the HTML5 specification. 

349 

350 .. versionadded:: 2.4 

351 

352 `debug_token_types` 

353 Add ``title`` attributes to all token ``<span>`` tags that show the 

354 name of the token. 

355 

356 .. versionadded:: 2.10 

357 

358 

359 **Subclassing the HTML formatter** 

360 

361 .. versionadded:: 0.7 

362 

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

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

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

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

367 formatted source code. 

368 

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

370 resulting HTML is output. 

371 

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

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

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

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

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

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

378 times. 

379 

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

381 

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

383 

384 .. sourcecode:: python 

385 

386 class CodeHtmlFormatter(HtmlFormatter): 

387 

388 def wrap(self, source, *, include_div): 

389 return self._wrap_code(source) 

390 

391 def _wrap_code(self, source): 

392 yield 0, '<code>' 

393 for i, t in source: 

394 if i == 1: 

395 # it's a line of formatted code 

396 t += '<br>' 

397 yield i, t 

398 yield 0, '</code>' 

399 

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

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

402 

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

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

405 HTML yielded by the wrapped generator is output. 

406 """ 

407 

408 name = 'HTML' 

409 aliases = ['html'] 

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

411 

412 def __init__(self, **options): 

413 Formatter.__init__(self, **options) 

414 self.title = self._decodeifneeded(self.title) 

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

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

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

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

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

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

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

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

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

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

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

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

427 self.span_element_openers = {} 

428 self.debug_token_types = get_bool_opt(options, 'debug_token_types', False) 

429 

430 if self.tagsfile: 

431 if not ctags: 

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

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

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

435 

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

437 if linenos == 'inline': 

438 self.linenos = 2 

439 elif linenos: 

440 # compatibility with <= 0.7 

441 self.linenos = 1 

442 else: 

443 self.linenos = 0 

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

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

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

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

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

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

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

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

452 self.hl_lines = set() 

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

454 try: 

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

456 except ValueError: 

457 pass 

458 

459 self._create_stylesheet() 

460 

461 def _get_css_class(self, ttype): 

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

463 the classprefix option.""" 

464 ttypeclass = _get_ttype_class(ttype) 

465 if ttypeclass: 

466 return self.classprefix + ttypeclass 

467 return '' 

468 

469 def _get_css_classes(self, ttype): 

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

471 cls = self._get_css_class(ttype) 

472 while ttype not in STANDARD_TYPES: 

473 ttype = ttype.parent 

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

475 return cls or '' 

476 

477 def _get_css_inline_styles(self, ttype): 

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

479 cclass = self.ttype2class.get(ttype) 

480 while cclass is None: 

481 ttype = ttype.parent 

482 cclass = self.ttype2class.get(ttype) 

483 return cclass or '' 

484 

485 def _create_stylesheet(self): 

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

487 c2s = self.class2style = {} 

488 for ttype, ndef in self.style: 

489 name = self._get_css_class(ttype) 

490 style = '' 

491 if ndef['color']: 

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

493 if ndef['bold']: 

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

495 if ndef['italic']: 

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

497 if ndef['underline']: 

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

499 if ndef['bgcolor']: 

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

501 if ndef['border']: 

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

503 if style: 

504 t2c[ttype] = name 

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

506 # hierarchy (necessary for CSS cascading rules!) 

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

508 

509 def get_style_defs(self, arg=None): 

510 """ 

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

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

513 insert before the token type classes. 

514 """ 

515 style_lines = [] 

516 

517 style_lines.extend(self.get_linenos_style_defs()) 

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

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

520 

521 return '\n'.join(style_lines) 

522 

523 def get_token_style_defs(self, arg=None): 

524 prefix = self.get_css_prefix(arg) 

525 

526 styles = [ 

527 (level, ttype, cls, style) 

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

529 if cls and style 

530 ] 

531 styles.sort() 

532 

533 lines = [ 

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

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

536 ] 

537 

538 return lines 

539 

540 def get_background_style_defs(self, arg=None): 

541 prefix = self.get_css_prefix(arg) 

542 bg_color = self.style.background_color 

543 hl_color = self.style.highlight_color 

544 

545 lines = [] 

546 

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

548 text_style = '' 

549 if Text in self.ttype2class: 

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

551 lines.insert( 

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

553 prefix(''), bg_color, text_style 

554 ) 

555 ) 

556 if hl_color is not None: 

557 lines.insert( 

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

559 ) 

560 

561 return lines 

562 

563 def get_linenos_style_defs(self): 

564 lines = [ 

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

566 'td.linenos .normal { %s }' % self._linenos_style, 

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

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

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

570 ] 

571 

572 return lines 

573 

574 def get_css_prefix(self, arg): 

575 if arg is None: 

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

577 if isinstance(arg, str): 

578 args = [arg] 

579 else: 

580 args = list(arg) 

581 

582 def prefix(cls): 

583 if cls: 

584 cls = '.' + cls 

585 tmp = [] 

586 for arg in args: 

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

588 return ', '.join(tmp) 

589 

590 return prefix 

591 

592 @property 

593 def _pre_style(self): 

594 return 'line-height: 125%;' 

595 

596 @property 

597 def _linenos_style(self): 

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

599 self.style.line_number_color, 

600 self.style.line_number_background_color 

601 ) 

602 

603 @property 

604 def _linenos_special_style(self): 

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

606 self.style.line_number_special_color, 

607 self.style.line_number_special_background_color 

608 ) 

609 

610 def _decodeifneeded(self, value): 

611 if isinstance(value, bytes): 

612 if self.encoding: 

613 return value.decode(self.encoding) 

614 return value.decode() 

615 return value 

616 

617 def _wrap_full(self, inner, outfile): 

618 if self.cssfile: 

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

620 # it's an absolute filename 

621 cssfilename = self.cssfile 

622 else: 

623 try: 

624 filename = outfile.name 

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

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

627 raise AttributeError 

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

629 self.cssfile) 

630 except AttributeError: 

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

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

633 file=sys.stderr) 

634 cssfilename = self.cssfile 

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

636 try: 

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

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

639 cf.write(CSSFILE_TEMPLATE % 

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

641 except OSError as err: 

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

643 raise 

644 

645 yield 0, (DOC_HEADER_EXTERNALCSS % 

646 dict(title=self.title, 

647 cssfile=self.cssfile, 

648 encoding=self.encoding)) 

649 else: 

650 yield 0, (DOC_HEADER % 

651 dict(title=self.title, 

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

653 encoding=self.encoding)) 

654 

655 yield from inner 

656 yield 0, DOC_FOOTER 

657 

658 def _wrap_tablelinenos(self, inner): 

659 dummyoutfile = StringIO() 

660 lncount = 0 

661 for t, line in inner: 

662 if t: 

663 lncount += 1 

664 dummyoutfile.write(line) 

665 

666 fl = self.linenostart 

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

668 sp = self.linenospecial 

669 st = self.linenostep 

670 anchor_name = self.lineanchors or self.linespans 

671 aln = self.anchorlinenos 

672 nocls = self.noclasses 

673 

674 lines = [] 

675 

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

677 print_line = i % st == 0 

678 special_line = sp and i % sp == 0 

679 

680 if print_line: 

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

682 if aln: 

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

684 else: 

685 line = ' ' * mw 

686 

687 if nocls: 

688 if special_line: 

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

690 else: 

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

692 else: 

693 if special_line: 

694 style = ' class="special"' 

695 else: 

696 style = ' class="normal"' 

697 

698 if style: 

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

700 

701 lines.append(line) 

702 

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

704 

705 # If a filename was specified, we can't put it into the code table as it 

706 # would misalign the line numbers. Hence we emit a separate row for it. 

707 filename_tr = "" 

708 if self.filename: 

709 filename_tr = ( 

710 '<tr><th colspan="2" class="filename">' 

711 '<span class="filename">' + self.filename + '</span>' 

712 '</th></tr>') 

713 

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

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

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

717 yield 0, (f'<table class="{self.cssclass}table">' + filename_tr + 

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

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

720 yield 0, '<div>' 

721 yield 0, dummyoutfile.getvalue() 

722 yield 0, '</div>' 

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

724 

725 

726 def _wrap_inlinelinenos(self, inner): 

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

728 inner_lines = list(inner) 

729 sp = self.linenospecial 

730 st = self.linenostep 

731 num = self.linenostart 

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

733 anchor_name = self.lineanchors or self.linespans 

734 aln = self.anchorlinenos 

735 nocls = self.noclasses 

736 

737 for _, inner_line in inner_lines: 

738 print_line = num % st == 0 

739 special_line = sp and num % sp == 0 

740 

741 if print_line: 

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

743 else: 

744 line = ' ' * mw 

745 

746 if nocls: 

747 if special_line: 

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

749 else: 

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

751 else: 

752 if special_line: 

753 style = ' class="linenos special"' 

754 else: 

755 style = ' class="linenos"' 

756 

757 if style: 

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

759 else: 

760 linenos = line 

761 

762 if aln: 

763 yield 1, ('<a href="#%s-%d">%s</a>' % (anchor_name, num, linenos) + 

764 inner_line) 

765 else: 

766 yield 1, linenos + inner_line 

767 num += 1 

768 

769 def _wrap_lineanchors(self, inner): 

770 s = self.lineanchors 

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

772 i = self.linenostart - 1 

773 for t, line in inner: 

774 if t: 

775 i += 1 

776 href = "" if self.linenos else ' href="#%s-%d"' % (s, i) 

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

778 else: 

779 yield 0, line 

780 

781 def _wrap_linespans(self, inner): 

782 s = self.linespans 

783 i = self.linenostart - 1 

784 for t, line in inner: 

785 if t: 

786 i += 1 

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

788 else: 

789 yield 0, line 

790 

791 def _wrap_div(self, inner): 

792 style = [] 

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

794 self.style.background_color is not None): 

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

796 if self.cssstyles: 

797 style.append(self.cssstyles) 

798 style = '; '.join(style) 

799 

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

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

802 yield from inner 

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

804 

805 def _wrap_pre(self, inner): 

806 style = [] 

807 if self.prestyles: 

808 style.append(self.prestyles) 

809 if self.noclasses: 

810 style.append(self._pre_style) 

811 style = '; '.join(style) 

812 

813 if self.filename and self.linenos != 1: 

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

815 

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

817 # ignored by HTML parsers 

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

819 yield from inner 

820 yield 0, '</pre>' 

821 

822 def _wrap_code(self, inner): 

823 yield 0, '<code>' 

824 yield from inner 

825 yield 0, '</code>' 

826 

827 @functools.lru_cache(maxsize=100) 

828 def _translate_parts(self, value): 

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

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

831 

832 def _format_lines(self, tokensource): 

833 """ 

834 Just format the tokens, without any wrapping tags. 

835 Yield individual lines. 

836 """ 

837 nocls = self.noclasses 

838 lsep = self.lineseparator 

839 tagsfile = self.tagsfile 

840 

841 lspan = '' 

842 line = [] 

843 for ttype, value in tokensource: 

844 try: 

845 cspan = self.span_element_openers[ttype] 

846 except KeyError: 

847 title = ' title="%s"' % '.'.join(ttype) if self.debug_token_types else '' 

848 if nocls: 

849 css_style = self._get_css_inline_styles(ttype) 

850 if css_style: 

851 css_style = self.class2style[css_style][0] 

852 cspan = '<span style="%s"%s>' % (css_style, title) 

853 else: 

854 cspan = '' 

855 else: 

856 css_class = self._get_css_classes(ttype) 

857 if css_class: 

858 cspan = '<span class="%s"%s>' % (css_class, title) 

859 else: 

860 cspan = '' 

861 self.span_element_openers[ttype] = cspan 

862 

863 parts = self._translate_parts(value) 

864 

865 if tagsfile and ttype in Token.Name: 

866 filename, linenumber = self._lookup_ctag(value) 

867 if linenumber: 

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

869 if base: 

870 base += '/' 

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

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

873 'fext': extension} 

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

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

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

877 

878 # for all but the last line 

879 for part in parts[:-1]: 

880 if line: 

881 # Also check for part being non-empty, so we avoid creating 

882 # empty <span> tags 

883 if lspan != cspan and part: 

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

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

886 else: # both are the same, or the current part was empty 

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

888 yield 1, ''.join(line) 

889 line = [] 

890 elif part: 

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

892 else: 

893 yield 1, lsep 

894 # for the last line 

895 if line and parts[-1]: 

896 if lspan != cspan: 

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

898 lspan = cspan 

899 else: 

900 line.append(parts[-1]) 

901 elif parts[-1]: 

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

903 lspan = cspan 

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

905 

906 if line: 

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

908 yield 1, ''.join(line) 

909 

910 def _lookup_ctag(self, token): 

911 entry = ctags.TagEntry() 

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

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

914 else: 

915 return None, None 

916 

917 def _highlight_lines(self, tokensource): 

918 """ 

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

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

921 """ 

922 hls = self.hl_lines 

923 

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

925 if t != 1: 

926 yield t, value 

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

928 if self.noclasses: 

929 style = '' 

930 if self.style.highlight_color is not None: 

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

932 (self.style.highlight_color,)) 

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

934 else: 

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

936 else: 

937 yield 1, value 

938 

939 def wrap(self, source): 

940 """ 

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

942 individual lines, in custom generators. See docstring 

943 for `format`. Can be overridden. 

944 """ 

945 

946 output = source 

947 if self.wrapcode: 

948 output = self._wrap_code(output) 

949 

950 output = self._wrap_pre(output) 

951 

952 return output 

953 

954 def format_unencoded(self, tokensource, outfile): 

955 """ 

956 The formatting process uses several nested generators; which of 

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

958 

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

960 and wrap the pieces of text generated by this. 

961 

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

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

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

965 use several different wrappers that process the original source 

966 linewise, e.g. line number generators. 

967 """ 

968 source = self._format_lines(tokensource) 

969 

970 # As a special case, we wrap line numbers before line highlighting 

971 # so the line numbers get wrapped in the highlighting tag. 

972 if not self.nowrap and self.linenos == 2: 

973 source = self._wrap_inlinelinenos(source) 

974 

975 if self.hl_lines: 

976 source = self._highlight_lines(source) 

977 

978 if not self.nowrap: 

979 if self.lineanchors: 

980 source = self._wrap_lineanchors(source) 

981 if self.linespans: 

982 source = self._wrap_linespans(source) 

983 source = self.wrap(source) 

984 if self.linenos == 1: 

985 source = self._wrap_tablelinenos(source) 

986 source = self._wrap_div(source) 

987 if self.full: 

988 source = self._wrap_full(source, outfile) 

989 

990 for t, piece in source: 

991 outfile.write(piece)