|
4 | 4 | # list see the documentation:
|
5 | 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
|
6 | 6 |
|
| 7 | +from argparse import _StoreTrueAction |
| 8 | +from io import StringIO |
7 | 9 | from pathlib import Path
|
| 10 | +from typing import Optional |
| 11 | +import docutils |
8 | 12 | from sphinx.application import Sphinx
|
| 13 | +from sphinx.util.docutils import SphinxRole |
| 14 | +from sphinx_immaterial.inline_icons import load_svg_into_builder_env |
9 | 15 | from clang_tools.main import get_parser
|
10 | 16 |
|
11 | 17 | # -- Path setup --------------------------------------------------------------
|
|
111 | 117 | # pylint: disable=protected-access
|
112 | 118 |
|
113 | 119 |
|
| 120 | +class CliBadge(SphinxRole): |
| 121 | + badge_type: str |
| 122 | + badge_icon: Optional[str] = None |
| 123 | + href: Optional[str] = None |
| 124 | + href_title: Optional[str] = None |
| 125 | + |
| 126 | + def run(self): |
| 127 | + is_linked = "" |
| 128 | + if self.href is not None and self.href_title is not None: |
| 129 | + is_linked = ( |
| 130 | + f'<a href="{self.href}{self.text}" ' + f'title="{self.href_title}">' |
| 131 | + ) |
| 132 | + head = '<span class="mdx-badge__icon">' |
| 133 | + if not self.badge_icon: |
| 134 | + head += self.badge_type.title() |
| 135 | + else: |
| 136 | + head += is_linked |
| 137 | + head += ( |
| 138 | + f'<span class="md-icon si-icon-inline {self.badge_icon}"></span></a>' |
| 139 | + ) |
| 140 | + head += "</span>" |
| 141 | + header = docutils.nodes.raw( |
| 142 | + self.rawtext, |
| 143 | + f'<span class="mdx-badge">{head}<span class="mdx-badge__text">' |
| 144 | + + is_linked |
| 145 | + + (self.text if self.badge_type in ["version", "switch"] else ""), |
| 146 | + format="html", |
| 147 | + ) |
| 148 | + if self.badge_type not in ["version", "switch"]: |
| 149 | + code, sys_msgs = docutils.parsers.rst.roles.code_role( |
| 150 | + role="code", |
| 151 | + rawtext=self.rawtext, |
| 152 | + text=self.text, |
| 153 | + lineno=self.lineno, |
| 154 | + inliner=self.inliner, |
| 155 | + options={"language": "text", "classes": ["highlight"]}, |
| 156 | + content=self.content, |
| 157 | + ) |
| 158 | + else: |
| 159 | + code, sys_msgs = ([], []) |
| 160 | + tail = "</span></span>" |
| 161 | + if self.href is not None and self.href_title is not None: |
| 162 | + tail = "</a>" + tail |
| 163 | + trailer = docutils.nodes.raw(self.rawtext, tail, format="html") |
| 164 | + return ([header, *code, trailer], sys_msgs) |
| 165 | + |
| 166 | + |
| 167 | +class CliBadgeVersion(CliBadge): |
| 168 | + badge_type = "version" |
| 169 | + href = "https://github.com/cpp-linter/clang-tools-pip/releases/v" |
| 170 | + href_title = "Minimum Version" |
| 171 | + |
| 172 | + def run(self): |
| 173 | + self.badge_icon = load_svg_into_builder_env( |
| 174 | + self.env.app.builder, "material/tag-outline" |
| 175 | + ) |
| 176 | + return super().run() |
| 177 | + |
| 178 | + |
| 179 | +class CliBadgeDefault(CliBadge): |
| 180 | + badge_type = "Default" |
| 181 | + |
| 182 | + |
| 183 | +class CliBadgeSwitch(CliBadge): |
| 184 | + badge_type = "switch" |
| 185 | + |
| 186 | + def run(self): |
| 187 | + self.badge_icon = load_svg_into_builder_env( |
| 188 | + self.env.app.builder, "material/toggle-switch" |
| 189 | + ) |
| 190 | + return super().run() |
| 191 | + |
| 192 | + |
| 193 | +REQUIRED_VERSIONS = { |
| 194 | + "0.1.0": ["install"], |
| 195 | + "0.2.0": ["directory"], |
| 196 | + "0.3.0": ["overwrite"], |
| 197 | + "0.5.0": ["no_progress_bar", "uninstall"], |
| 198 | + "0.11.0": ["tool"], |
| 199 | +} |
| 200 | + |
| 201 | + |
114 | 202 | def setup(app: Sphinx):
|
115 | 203 | """Generate a doc from the executable script's ``--help`` output."""
|
116 |
| - parser = get_parser() |
117 |
| - # print(parser.format_help()) |
118 |
| - formatter = parser._get_formatter() |
119 |
| - doc = "Command Line Interface Options\n==============================\n\n" |
120 |
| - for arg in parser._actions: |
121 |
| - doc += f"\n.. option:: {formatter._format_action_invocation(arg)}\n\n" |
122 |
| - if arg.default != "==SUPPRESS==": |
123 |
| - doc += f" :Default: ``{repr(arg.default)}``\n\n" |
124 |
| - description = ( |
125 |
| - "" if arg.help is None else " %s\n" % (arg.help.replace("\n", "\n ")) |
126 |
| - ) |
127 |
| - doc += description |
| 204 | + app.add_role("badge-version", CliBadgeVersion()) |
| 205 | + app.add_role("badge-default", CliBadgeDefault()) |
| 206 | + app.add_role("badge-switch", CliBadgeSwitch()) |
| 207 | + |
128 | 208 | cli_doc = Path(app.srcdir, "cli_args.rst")
|
129 |
| - cli_doc.write_text(doc, encoding="utf-8") |
| 209 | + with open(cli_doc, mode="w") as doc: |
| 210 | + doc.write("Command Line Interface Options\n==============================\n\n") |
| 211 | + parser = get_parser() |
| 212 | + doc.write(".. code-block:: text\n :caption: Usage\n :class: no-copy\n\n") |
| 213 | + parser.prog = "clang-tools" |
| 214 | + str_buf = StringIO() |
| 215 | + parser.print_usage(str_buf) |
| 216 | + usage = str_buf.getvalue() |
| 217 | + start = usage.find(parser.prog) |
| 218 | + for line in usage.splitlines(): |
| 219 | + doc.write(f" {line[start:]}\n") |
| 220 | + |
| 221 | + args = parser._optionals._actions |
| 222 | + for arg in args: |
| 223 | + aliases = arg.option_strings |
| 224 | + if not aliases or arg.default == "==SUPPRESS==": |
| 225 | + continue |
| 226 | + assert arg.help is not None |
| 227 | + doc.write("\n.. std:option:: " + ", ".join(aliases) + "\n") |
| 228 | + req_ver = "0.1.0" |
| 229 | + for ver, names in REQUIRED_VERSIONS.items(): |
| 230 | + if arg.dest in names: |
| 231 | + req_ver = ver |
| 232 | + break |
| 233 | + doc.write(f"\n :badge-version:`{req_ver}` ") |
| 234 | + if arg.default: |
| 235 | + default = arg.default |
| 236 | + if isinstance(arg.default, list): |
| 237 | + default = " ".join(arg.default) |
| 238 | + doc.write(f":badge-default:`{default}` ") |
| 239 | + if isinstance(arg, _StoreTrueAction): |
| 240 | + doc.write(":badge-switch:`Accepts no value` ") |
| 241 | + doc.write("\n\n ") |
| 242 | + doc.write("\n ".join(arg.help.splitlines()) + "\n") |
0 commit comments