Skip to content

Commit a23a644

Browse files
committed
revise --help output (and doc)
apply sonarcloud suggestions
1 parent 4dd3689 commit a23a644

File tree

3 files changed

+172
-14
lines changed

3 files changed

+172
-14
lines changed

clang_tools/main.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ def get_parser() -> argparse.ArgumentParser:
2020
"-i",
2121
"--install",
2222
metavar="VERSION",
23-
help="Install clang-tools about a specific version.",
23+
help="Install clang-tools about a specific version. This can be in the form of"
24+
" a semantic version specification (``x.y.z``, ``x.y``, ``x``) or a path that "
25+
"points to the directory where the binaries already exist.",
2426
)
2527
parser.add_argument(
2628
"-t",

docs/_static/extra_css.css

+43
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,46 @@ thead {
33
background-color: var(--md-accent-bg-color--light);
44
color: var(--md-default-bg-color);
55
}
6+
7+
.md-typeset .mdx-badge {
8+
font-size: .85em
9+
}
10+
11+
.md-typeset .mdx-badge--right {
12+
float: right;
13+
margin-left: .35em
14+
}
15+
16+
[dir=ltr] .md-typeset .mdx-badge__icon {
17+
border-top-left-radius: .1rem;
18+
border-bottom-left-radius: .1rem;
19+
}
20+
21+
[dir=rtl] .md-typeset .mdx-badge__icon {
22+
border-top-right-radius: .1rem;
23+
border-bottom-right-radius: .1rem;
24+
}
25+
26+
.md-typeset .mdx-badge__icon {
27+
background: var(--md-accent-fg-color--transparent);
28+
padding: .2rem;
29+
}
30+
31+
.md-typeset .mdx-badge__icon:last-child {
32+
border-radius: .1rem;
33+
}
34+
35+
[dir=ltr] .md-typeset .mdx-badge__text {
36+
border-top-right-radius: .1rem;
37+
border-bottom-right-radius: .1rem;
38+
}
39+
40+
[dir=rtl] .md-typeset .mdx-badge__text {
41+
border-top-left-radius: .1rem;
42+
border-bottom-left-radius: .1rem;
43+
}
44+
45+
.md-typeset .mdx-badge__text {
46+
box-shadow: 0 0 0 1px inset var(--md-accent-fg-color--transparent);
47+
padding: .2rem .3rem;
48+
}

docs/conf.py

+126-13
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,14 @@
44
# list see the documentation:
55
# https://www.sphinx-doc.org/en/master/usage/configuration.html
66

7+
from argparse import _StoreTrueAction
8+
from io import StringIO
79
from pathlib import Path
10+
from typing import Optional
11+
import docutils
812
from sphinx.application import Sphinx
13+
from sphinx.util.docutils import SphinxRole
14+
from sphinx_immaterial.inline_icons import load_svg_into_builder_env
915
from clang_tools.main import get_parser
1016

1117
# -- Path setup --------------------------------------------------------------
@@ -111,19 +117,126 @@
111117
# pylint: disable=protected-access
112118

113119

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+
114202
def setup(app: Sphinx):
115203
"""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+
128208
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

Comments
 (0)