Skip to content

Commit 2a0656a

Browse files
committed
even more fixes
1 parent 2c19c87 commit 2a0656a

4 files changed

Lines changed: 66 additions & 46 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ TAGS ?= dev
77
docs:
88
@echo "==> Deploying MkDocs documentation locally via mike (Tags: $(TAGS))..."
99
doxygen Doxyfile
10-
uv run python scripts/gen_concept_docs.py --xml documentation/xml --out docs/cpp-gl
10+
uv run python scripts/gen_concept_docs.py --config docs/concept_groups.json --xml documentation/xml --out docs/cpp-gl
1111
uv run mike deploy $(TAGS)
1212
@echo "==> Documentation deployed to local gh-pages branch."
1313

docs/concept_groups.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"GL": {
3+
"prefix": "gl::",
4+
"filename": "gl_traits.md",
5+
"anchor": "gl-traits-concepts-documentation",
6+
"title": "GL Traits & Concepts",
7+
"description": "This page documents the C++20 concepts and type traits used to constrain templates across the GL library."
8+
},
9+
"HGL": {
10+
"prefix": "hgl::",
11+
"filename": "hgl_traits.md",
12+
"anchor": "hgl-traits-concepts-documentation",
13+
"title": "HGL Traits & Concepts",
14+
"description": "This page documents the C++20 concepts and type traits used to constrain templates across the HGL library."
15+
}
16+
}

include/gl/types/properties.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -284,10 +284,10 @@ concept c_has_non_empty_properties = requires {
284284
/// @brief Requirements for properties that support binary coloring algorithms.
285285
///
286286
/// Requires a property type that:
287-
/// 1. Satisfies @ref c_properties.
287+
/// 1. Satisfies @ref gl::traits::c_properties.
288288
/// 2. Has a nested `color_type`.
289289
/// 3. Has a public `color` member of the `color_type` type.
290-
/// 4. Supports construction and comparison with `gl::binary_color`.
290+
/// 4. Supports construction and comparison with @ref gl::binary_color.
291291
///
292292
/// @tparam T The type to evaluate against the concept.
293293
template <typename Properties>
@@ -302,8 +302,8 @@ concept c_binary_color_properties_type = c_properties<Properties> and requires(P
302302
/// @brief Requirements for properties that support arithmetic weight values.
303303
///
304304
/// Requires a property type that:
305-
/// 1. Satisfies @ref c_properties.
306-
/// 2. Has a nested `weight_type` that satisfies @ref c_arithmetic.
305+
/// 1. Satisfies @ref gl::traits::c_properties.
306+
/// 2. Has a nested `weight_type` that satisfies @ref gl::traits::c_arithmetic.
307307
/// 3. Has a public `weight` member of the `weight_type` type.
308308
///
309309
/// @tparam T The type to evaluate against the concept.

scripts/gen_concept_docs.py

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,10 @@
11
import xml.etree.ElementTree as ET
22
import argparse
33
import re
4+
import json
45
from pathlib import Path
56
from dataclasses import dataclass
67

7-
GROUPS = {
8-
"GL": {
9-
"prefix": "gl::",
10-
"filename": "gl_traits.md",
11-
"anchor": "gl-traits-concepts-documentation",
12-
"title": "GL Traits & Concepts",
13-
"description": "This page documents the C++20 concepts and type traits used to constrain templates across the GL library.",
14-
},
15-
"HGL": {
16-
"prefix": "hgl::",
17-
"filename": "hgl_traits.md",
18-
"anchor": "hgl-traits-concepts-documentation",
19-
"title": "HGL Traits & Concepts",
20-
"description": "This page documents the C++20 concepts and type traits used to constrain templates across the HGL library.",
21-
},
22-
}
23-
24-
258
@dataclass
269
class TParamDescriptor:
2710
name: str
@@ -36,9 +19,8 @@ class ConceptDescriptor:
3619
params: list[TParamDescriptor]
3720
definition: str
3821

39-
4022
class ConceptParser:
41-
def __init__(self, xml_dir: Path, out_dir: Path, groups: dict = GROUPS):
23+
def __init__(self, xml_dir: Path, out_dir: Path, groups: dict):
4224
self.xml_dir = xml_dir
4325
self.out_dir = out_dir
4426
self.groups = groups
@@ -85,23 +67,46 @@ def _xml_to_md(self, elem: ET.Element) -> str:
8567
res += f"`{ref_text}`"
8668

8769
elif child.tag in ["computeroutput", "preformatted"]:
88-
res += f"`{self._xml_to_md(child)}`"
70+
inner = self._xml_to_md(child)
71+
72+
# SMART LINK MERGE:
73+
# Prevents wrapping a Markdown link in backticks, which breaks the link.
74+
# Instead, it places backticks INSIDE the brackets.
75+
match = re.fullmatch(r'\[(`?)(.*?)\1\]\((.*?)\)', inner.strip())
76+
if match:
77+
res += f"[`{match.group(2)}`]({match.group(3)})"
78+
elif "](" in inner:
79+
# Failsafe for complex strings containing links
80+
res += inner
81+
else:
82+
res += f"`{inner}`"
83+
8984
elif child.tag == "bold":
9085
res += f"**{self._xml_to_md(child)}**"
86+
9187
elif child.tag == "emphasis":
9288
res += f"*{self._xml_to_md(child)}*"
89+
9390
elif child.tag == "itemizedlist":
9491
res += "\n\n"
9592
for item in child.findall("listitem"):
9693
res += f"- {self._xml_to_md(item).strip()}\n"
9794
res += "\n\n"
95+
96+
elif child.tag == "orderedlist":
97+
res += "\n\n"
98+
for i, item in enumerate(child.findall("listitem"), start=1):
99+
res += f"{i}. {self._xml_to_md(item).strip()}\n"
100+
res += "\n\n"
101+
98102
elif child.tag == "blockquote":
99103
bq_text = self._xml_to_md(child).strip()
100104
res += (
101105
"\n\n"
102106
+ "\n".join(f"> {line}" for line in bq_text.splitlines())
103107
+ "\n\n"
104108
)
109+
105110
elif child.tag == "simplesect":
106111
kind = child.get("kind", "note").upper()
107112
sect_text = self._xml_to_md(child).strip()
@@ -110,10 +115,13 @@ def _xml_to_md(self, elem: ET.Element) -> str:
110115
+ "\n".join(f"> {line}" for line in sect_text.splitlines())
111116
+ "\n\n"
112117
)
118+
113119
elif child.tag == "para":
114120
res += self._xml_to_md(child).strip() + "\n\n"
121+
115122
elif child.tag == "title":
116123
res += f"### {self._xml_to_md(child).strip()}\n\n"
124+
117125
else:
118126
res += self._xml_to_md(child)
119127

@@ -136,14 +144,10 @@ def _parse_concept_xml(self, xml_path: Path) -> ConceptDescriptor | None:
136144

137145
detailed_desc = root.find("detaileddescription")
138146
if detailed_desc is not None:
139-
for param_list in detailed_desc.findall(
140-
'.//parameterlist[@kind="templateparam"]'
141-
):
147+
for param_list in detailed_desc.findall('.//parameterlist[@kind="templateparam"]'):
142148
for item in param_list.findall("parameteritem"):
143149
p_name = self._xml_to_md(item.find(".//parametername")).strip()
144-
p_desc = self._xml_to_md(
145-
item.find(".//parameterdescription")
146-
).strip()
150+
p_desc = self._xml_to_md(item.find(".//parameterdescription")).strip()
147151
params.append(TParamDescriptor(name=p_name, desc=p_desc))
148152
param_list.clear()
149153

@@ -161,9 +165,7 @@ def _parse_concept_xml(self, xml_path: Path) -> ConceptDescriptor | None:
161165
tpl_nodes = root.findall(".//templateparamlist/param")
162166
tpl_strings = [self._get_text(p).strip() for p in tpl_nodes]
163167
template_decl += ", ".join(tpl_strings) + ">\n"
164-
definition = (
165-
f"{template_decl}concept {name.split('::')[-1]} = {constraint};"
166-
)
168+
definition = f"{template_decl}concept {name.split('::')[-1]} = {constraint};"
167169

168170
return ConceptDescriptor(
169171
name=name,
@@ -178,9 +180,7 @@ def process(self):
178180
"""Main execution flow: builds registry, parses data, and generates markdown."""
179181
index_xml = self.xml_dir / "index.xml"
180182
if not index_xml.exists():
181-
print(
182-
f"Error: Could not find Doxygen index at {index_xml}. Run Doxygen first."
183-
)
183+
print(f"Error: Could not find Doxygen index at {index_xml}. Run Doxygen first.")
184184
return
185185

186186
tree = ET.parse(index_xml)
@@ -266,16 +266,20 @@ def _generate_index_file(self):
266266
index_path.write_text(md, encoding="utf-8")
267267
print(f"Generated {index_path} (API Index)")
268268

269-
270269
if __name__ == "__main__":
271270
parser = argparse.ArgumentParser()
272-
parser.add_argument(
273-
"--xml", default="xml", type=Path, help="Path to Doxygen XML output"
274-
)
275-
parser.add_argument(
276-
"--out", default="docs/cpp-gl", type=Path, help="Path to MkDocs output folder"
277-
)
271+
parser.add_argument("--xml", default="xml", type=Path, help="Path to Doxygen XML output")
272+
parser.add_argument("--out", default="docs/cpp-gl", type=Path, help="Path to MkDocs output folder")
273+
parser.add_argument("--config", default="groups.json", type=Path, help="Path to the groups JSON configuration file")
278274
args = parser.parse_args()
279275

280-
app = ConceptParser(args.xml, args.out)
276+
# Load configuration from JSON
277+
if not args.config.exists():
278+
print(f"Error: Configuration file '{args.config}' not found.")
279+
exit(1)
280+
281+
with open(args.config, "r", encoding="utf-8") as f:
282+
groups_config = json.load(f)
283+
284+
app = ConceptParser(args.xml, args.out, groups=groups_config)
281285
app.process()

0 commit comments

Comments
 (0)