Coverage for hdl_registers/generator/html/page.py: 98%

47 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-03-12 11:11 +0000

1# -------------------------------------------------------------------------------------------------- 

2# Copyright (c) Lukas Vik. All rights reserved. 

3# 

4# This file is part of the hdl-registers project, an HDL register generator fast enough to run 

5# in real time. 

6# https://hdl-registers.com 

7# https://github.com/hdl-registers/hdl-registers 

8# -------------------------------------------------------------------------------------------------- 

9 

10from __future__ import annotations 

11 

12from typing import TYPE_CHECKING, Any 

13 

14from hdl_registers.register_modes import REGISTER_MODES 

15 

16from .constant_table import HtmlConstantTableGenerator 

17from .html_generator_common import HtmlGeneratorCommon 

18from .html_translator import HtmlTranslator 

19from .register_table import HtmlRegisterTableGenerator 

20 

21if TYPE_CHECKING: 

22 from pathlib import Path 

23 

24 

25class HtmlPageGenerator(HtmlGeneratorCommon): 

26 """ 

27 Generate a HTML page with register and constant information. 

28 See the :ref:`generator_html` article for usage details. 

29 """ 

30 

31 __version__ = "1.0.0" 

32 

33 SHORT_DESCRIPTION = "HTML page" 

34 

35 @property 

36 def output_file(self) -> Path: 

37 """ 

38 Result will be placed in this file. 

39 """ 

40 return self.output_folder / f"{self.name}_regs.html" 

41 

42 def get_code( 

43 self, 

44 **kwargs: Any, # noqa: ANN401, ARG002 

45 ) -> str: 

46 """ 

47 Get a complete HTML page with register and constant information. 

48 """ 

49 title = f"Documentation of {self.name} registers" 

50 

51 generated_info = HtmlTranslator().translate( 

52 " ".join(self._get_generated_source_info(use_rst_annotation=True)) 

53 ) 

54 

55 html = f"""\ 

56<!DOCTYPE html> 

57<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 

58<head> 

59 <meta charset="utf-8" /> 

60 <meta name="viewport" content="width=device-width, initial-scale=1" /> 

61 <title>{title}</title> 

62 <style type="text/css"> 

63{self.get_page_style()} 

64 </style> 

65</head> 

66<body> 

67 <h1>{title}</h1> 

68 <p>This document is a specification for the register interface of the FPGA module \ 

69<b>{self.name}</b>.</p> 

70{generated_info} 

71 <h2>Register modes</h2> 

72 <p>The following register modes are available.</p> 

73{self._get_mode_descriptions()} 

74""" 

75 

76 html += " <h2>Registers</h2>\n" 

77 if self.register_list.register_objects: 

78 register_table_generator = HtmlRegisterTableGenerator( 

79 register_list=self.register_list, output_folder=self.output_folder 

80 ) 

81 html += f""" 

82 <p>The following registers make up the register list.</p> 

83{register_table_generator.get_code()} 

84""" 

85 else: 

86 html += " <p>This module does not have any registers.</p>" 

87 

88 html += " <h2>Constants</h2>\n" 

89 if self.register_list.constants: 

90 constant_table_generator = HtmlConstantTableGenerator( 

91 register_list=self.register_list, output_folder=self.output_folder 

92 ) 

93 html += f""" 

94 <p>The following constants are part of the register interface.</p> 

95{constant_table_generator.get_code()}""" 

96 else: 

97 html += " <p>This module does not have any constants.</p>" 

98 

99 html += """ 

100</body> 

101</html> 

102""" 

103 

104 return html 

105 

106 @staticmethod 

107 def get_page_style( 

108 font_style: str | None = None, 

109 table_style: str | None = None, 

110 math_style: str | None = None, 

111 extra_style: str = "", 

112 ) -> str: 

113 """ 

114 Get a CSS style for the register pages. 

115 

116 Return: 

117 str: CSS code. 

118 """ 

119 if font_style is None: 

120 font_style = """\ 

121html * { 

122 font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; 

123} 

124 

125a { 

126 color: #4C7AAF; 

127} 

128""" 

129 

130 if table_style is None: 

131 table_style = """\ 

132table { 

133 border-collapse: collapse; 

134} 

135 

136td, th { 

137 border-width: 0.1em; 

138 border-style: solid; 

139 border-color: #ddd; 

140 padding-left: 0.5em; 

141 padding-right: 0.5em; 

142 padding-top: 0; 

143 padding-bottom: 0; 

144} 

145 

146th { 

147 padding-top: 0.7em; 

148 padding-bottom: 0.7em; 

149 text-align: left; 

150 background-color: #4CAF50; 

151 color: white; 

152} 

153 

154tr:nth-child(odd) { 

155 background-color: #ffffff; 

156} 

157 

158tr:nth-child(even) { 

159 background-color: #ebebeb; 

160} 

161 

162td.array_header { 

163 border-top-width: 0.7em; 

164 border-top-color: #4C7AAF; 

165} 

166 

167td.array_footer { 

168 border-bottom-width: 0.7em; 

169 border-bottom-color: #4C7AAF; 

170} 

171 

172td > p { 

173 margin-top: 0.5em; 

174 margin-bottom: 0.5em; 

175 

176 &:not(:last-child) { 

177 margin-bottom: 0.9em; 

178 } 

179} 

180 

181li > p { 

182 margin-top: 0.4em; 

183 margin-bottom: 0.4em; 

184} 

185""" 

186 

187 if math_style is None: 

188 math_style = """\ 

189/* 

190* CSS below is taken from the HTML output of 'docutils'. 

191* Original code is released under the terms of the 2-Clause BSD license and is Copyright (C) 

192* 2009,2010 Alex Fernandez 

193* 2021 Gunter Milde 

194* 

195* Code is generated by running 

196* from docutils.core import publish_parts 

197* from docutils.writers.html5_polyglot import Writer 

198* print(publish_parts(rst, writer=Writer())["stylesheet"]) 

199* where 'rst' contains both inline and block math. 

200*/ 

201 

202/* Formulas */ 

203.formula { 

204 text-align: center; 

205 margin: 1.2em 0; 

206 line-height: 1.4; 

207} 

208span.formula { 

209 white-space: nowrap; 

210} 

211div.formula { 

212 padding: 0.5ex; 

213 margin-left: auto; 

214 margin-right: auto; 

215} 

216 

217/* Basic features */ 

218a.eqnumber { 

219 display: inline-block; 

220 float: right; 

221 clear: right; 

222 font-weight: bold; 

223} 

224span.unknown { 

225 color: #800000; 

226} 

227span.ignored, span.arraydef { 

228 display: none; 

229} 

230.phantom { 

231 visibility: hidden; 

232} 

233.formula i { 

234 letter-spacing: 0.1ex; 

235} 

236 

237/* Alignment */ 

238.align-left, .align-l { 

239 text-align: left; 

240} 

241.align-right, .align-r { 

242 text-align: right; 

243} 

244.align-center, .align-c { 

245 text-align: center; 

246} 

247 

248/* Structures */ 

249span.hspace { 

250 display: inline-block; 

251} 

252span.overline, span.bar { 

253 text-decoration: overline; 

254} 

255.fraction, .fullfraction, .textfraction { 

256 display: inline-block; 

257 vertical-align: middle; 

258 text-align: center; 

259} 

260span.formula .fraction, 

261.textfraction, 

262span.smallmatrix { 

263 font-size: 80%; 

264 line-height: 1; 

265} 

266span.numerator { 

267 display: block; 

268 line-height: 1; 

269} 

270span.denominator { 

271 display: block; 

272 line-height: 1; 

273 padding: 0ex; 

274 border-top: thin solid; 

275} 

276.formula sub, .formula sup { 

277 font-size: 80%; 

278} 

279sup.numerator, sup.unit { 

280 vertical-align: 80%; 

281} 

282sub.denominator, sub.unit { 

283 vertical-align: -20%; 

284} 

285span.smallsymbol { 

286 font-size: 75%; 

287 line-height: 75%; 

288} 

289span.boldsymbol { 

290 font-weight: bold; 

291} 

292span.sqrt { 

293 display: inline-block; 

294 vertical-align: middle; 

295 padding: 0.1ex; 

296} 

297sup.root { 

298 position: relative; 

299 left: 1.4ex; 

300} 

301span.radical { 

302 display: inline-block; 

303 padding: 0ex; 

304 /* font-size: 160%; for DejaVu, not required with STIX */ 

305 line-height: 100%; 

306 vertical-align: top; 

307 vertical-align: middle; 

308} 

309 

310span.root { 

311 display: inline-block; 

312 border-top: thin solid; 

313 padding: 0ex; 

314 vertical-align: middle; 

315} 

316div.formula .bigoperator, 

317.displaystyle .bigoperator, 

318.displaystyle .bigoperator { 

319 line-height: 120%; 

320 font-size: 140%; 

321 padding-right: 0.2ex; 

322} 

323span.fraction .bigoperator, 

324span.scriptstyle .bigoperator { 

325 line-height: inherit; 

326 font-size: inherit; 

327 padding-right: 0; 

328} 

329span.bigdelimiter { 

330 display: inline-block; 

331} 

332span.bigdelimiter.size1 { 

333 transform: scale(1, 1.2); 

334 line-height: 1.2; 

335} 

336span.bigdelimiter.size2 { 

337 transform: scale(1, 1.62); 

338 line-height: 1.62%; 

339 

340} 

341span.bigdelimiter.size3 { 

342 transform: scale(1, 2.05); 

343 line-height: 2.05%; 

344} 

345span.bigdelimiter.size4 { 

346 transform: scale(1, 2.47); 

347 line-height: 2.47%; 

348} 

349/* vertically stacked sub and superscript */ 

350span.scripts { 

351 display: inline-table; 

352 vertical-align: middle; 

353 padding-right: 0.2ex; 

354} 

355.script { 

356 display: table-row; 

357 text-align: left; 

358 line-height: 150%; 

359} 

360span.limits { 

361 display: inline-table; 

362 vertical-align: middle; 

363} 

364.limit { 

365 display: table-row; 

366 line-height: 99%; 

367} 

368sup.limit, sub.limit { 

369 line-height: 100%; 

370} 

371span.embellished, 

372span.embellished > .base { 

373 display: inline-block; 

374} 

375span.embellished > sup, 

376span.embellished > sub { 

377 display: inline-block; 

378 font-size: 100%; 

379 position: relative; 

380 bottom: 0.3em; 

381 width: 0px; 

382} 

383span.embellished > sub { 

384 top: 0.4em; 

385} 

386 

387/* Environments */ 

388span.array, span.bracketcases, span.binomial, span.environment { 

389 display: inline-table; 

390 text-align: center; 

391 vertical-align: middle; 

392} 

393span.arrayrow, span.binomrow { 

394 display: table-row; 

395 padding: 0; 

396 border: 0; 

397} 

398span.arraycell, span.bracket, span.case, span.binomcell, span.environmentcell { 

399 display: table-cell; 

400 padding: 0ex 0.2ex; 

401 line-height: 1; /* 99%; */ 

402 border: 0ex; 

403} 

404.environment.align > .arrayrow > .arraycell.align-l { 

405 padding-right: 2em; 

406} 

407 

408/* Inline binomials */ 

409span.binom { 

410 display: inline-block; 

411 vertical-align: middle; 

412 text-align: center; 

413 font-size: 80%; 

414} 

415span.binomstack { 

416 display: block; 

417 padding: 0em; 

418} 

419 

420/* Over- and underbraces */ 

421span.overbrace { 

422 border-top: 2pt solid; 

423} 

424span.underbrace { 

425 border-bottom: 2pt solid; 

426} 

427 

428/* Stackrel */ 

429span.stackrel { 

430 display: inline-block; 

431 text-align: center; 

432} 

433span.upstackrel { 

434 display: block; 

435 padding: 0em; 

436 font-size: 80%; 

437 line-height: 64%; 

438 position: relative; 

439 top: 0.15em; 

440 

441} 

442span.downstackrel { 

443 display: block; 

444 vertical-align: bottom; 

445 padding: 0em; 

446} 

447 

448/* Fonts */ 

449.formula { 

450 font-family: STIX, "DejaVu Serif", "DejaVu Math TeX Gyre", serif; 

451} 

452span.radical, /* ensure correct size of square-root sign */ 

453span.integral { /* upright integral signs for better alignment of indices */ 

454 font-family: "STIXIntegralsUp", STIX; 

455 /* font-size: 115%; match apparent size with DejaVu */ 

456} 

457span.bracket { 

458 /* some "STIX" and "DejaVu Math TeX Gyre" bracket pieces don't fit */ 

459 font-family: "DejaVu Serif", serif; 

460} 

461span.mathsf, span.textsf { 

462 font-family: sans-serif; 

463} 

464span.mathrm, span.textrm { 

465 font-family: STIX, "DejaVu Serif", "DejaVu Math TeX Gyre", serif; 

466} 

467span.mathtt, span.texttt { 

468 font-family: monospace; 

469} 

470span.text, span.textnormal, 

471span.mathsf, span.mathtt, span.mathrm { 

472 font-style: normal; 

473} 

474span.fraktur { 

475 font-family: "Lucida Blackletter", eufm10, blackletter; 

476} 

477span.blackboard { 

478 font-family: Blackboard, msbm10, serif; 

479} 

480span.scriptfont { 

481 font-family: "Monotype Corsiva", "Apple Chancery", "URW Chancery L", cursive; 

482 font-style: italic; 

483} 

484span.mathscr { 

485 font-family: MathJax_Script, rsfs10, cursive; 

486 font-style: italic; 

487} 

488span.textsc { 

489 font-variant: small-caps; 

490} 

491span.textsl { 

492 font-style: oblique; 

493} 

494 

495/* Colors */ 

496span.colorbox { 

497 display: inline-block; 

498 padding: 5px; 

499} 

500span.fbox { 

501 display: inline-block; 

502 border: thin solid black; 

503 padding: 2px; 

504} 

505span.boxed, span.framebox { 

506 display: inline-block; 

507 border: thin solid black; 

508 padding: 5px; 

509} 

510/* End docutils CSS */ 

511""" 

512 

513 return f"""\ 

514.literal { 

515 font-family: monospace; 

516 white-space: pre-wrap; 

517 color: #e74c3c; 

518 background-color: #ffffff; 

519 padding-left: 0.4em; 

520 padding-right: 0.4em; 

521 padding-top: 0.2em; 

522 padding-bottom: 0.2em; 

523 border-width: 0.1em; 

524 border-style: solid; 

525 border-color: #ddd; 

526} 

527 

528.literal > span.pre { 

529 white-space: nowrap; 

530} 

531 

532{font_style} 

533{table_style} 

534{math_style} 

535{extra_style}""" 

536 

537 @staticmethod 

538 def _get_mode_descriptions() -> str: 

539 html = """ 

540<table> 

541<thead> 

542 <tr> 

543 <th>Mode</th> 

544 <th>Description</th> 

545 </tr> 

546</thead> 

547<tbody>""" 

548 

549 for mode in REGISTER_MODES.values(): 

550 html += f""" 

551<tr> 

552 <td><p>{mode.name}</p></td> 

553 <td><p>{mode.description}</p></td> 

554</tr> 

555""" 

556 html += """ 

557</tbody> 

558</table>""" 

559 return html