Flatten MkDocs Structure + --module-is-source Support #4

Merged
aetos merged 5 commits from fix-missing-index into main 2026-02-21 16:16:37 +00:00
12 changed files with 98 additions and 46 deletions
Showing only changes of commit c8f32bf4b9 - Show all commits

View File

@@ -1,31 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="pytest" type="tests" factoryName="py.test">
<module name="docforge" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<option name="SDK_HOME" value="" />
<option name="SDK_NAME" value="Python 3.13 (doc-forge)" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<EXTENSION ID="net.ashald.envfile">
<option name="IS_ENABLED" value="false" />
<option name="IS_SUBST" value="false" />
<option name="IS_PATH_MACRO_SUPPORTED" value="false" />
<option name="IS_IGNORE_MISSING_FILES" value="false" />
<option name="IS_ENABLE_EXPERIMENTAL_INTEGRATIONS" value="false" />
<ENTRIES>
<ENTRY IS_ENABLED="true" PARSER="runconfig" IS_EXECUTABLE="false" />
</ENTRIES>
</EXTENSION>
<option name="_new_keywords" value="&quot;&quot;" />
<option name="_new_parameters" value="&quot;&quot;" />
<option name="_new_additionalArguments" value="&quot;--cov-report term-missing --cov-report html:.cov/htmlcov --cov-report xml:.cov/coverage.xml&quot;" />
<option name="_new_target" value="&quot;C:\\Users\\vishe\\WorkSpace\\code\\aetos\\doc-forge\\tests&quot;" />
<option name="_new_targetType" value="&quot;PATH&quot;" />
<method v="2" />
</configuration>
</component>

View File

@@ -1,10 +1,15 @@
""" """
This module implements the MkDocsRenderer, which generates Markdown source files MkDocsRenderer
compatible with the MkDocs 'material' theme and 'mkdocstrings' extension.
Generates Markdown source files compatible with MkDocs Material
and mkdocstrings, ensuring:
- Root index.md always exists
- Parent package indexes are created automatically
- Child modules are linked in parent index files
""" """
from pathlib import Path from pathlib import Path
from docforge.models import Project from docforge.models import Project
@@ -16,6 +21,9 @@ class MkDocsRenderer:
name = "mkdocs" name = "mkdocs"
# -------------------------
# Public API
# -------------------------
def generate_sources(self, project: Project, out_dir: Path) -> None: def generate_sources(self, project: Project, out_dir: Path) -> None:
""" """
Produce a set of Markdown files in the output directory based on the Produce a set of Markdown files in the output directory based on the
@@ -25,6 +33,9 @@ class MkDocsRenderer:
project: The project models to render. project: The project models to render.
out_dir: Target directory for documentation files. out_dir: Target directory for documentation files.
""" """
out_dir.mkdir(parents=True, exist_ok=True)
self._ensure_root_index(project, out_dir)
modules = list(project.get_all_modules()) modules = list(project.get_all_modules())
paths = {m.path for m in modules} paths = {m.path for m in modules}
@@ -53,27 +64,26 @@ class MkDocsRenderer:
parts = module.path.split(".") parts = module.path.split(".")
if module.path in packages: if module.path in packages:
# package → index.md # Package → directory/index.md
dir_path = out_dir.joinpath(*parts) dir_path = out_dir.joinpath(*parts)
dir_path.mkdir(parents=True, exist_ok=True) dir_path.mkdir(parents=True, exist_ok=True)
md_path = dir_path / "index.md" md_path = dir_path / "index.md"
title = parts[-1].replace("_", " ").title() link_target = f"{parts[-1]}/"
else: else:
# leaf module → <name>.md # Leaf module → parent_dir/<name>.md
dir_path = out_dir.joinpath(*parts[:-1]) dir_path = out_dir.joinpath(*parts[:-1])
dir_path.mkdir(parents=True, exist_ok=True) dir_path.mkdir(parents=True, exist_ok=True)
md_path = dir_path / f"{parts[-1]}.md" md_path = dir_path / f"{parts[-1]}.md"
title = parts[-1].replace("_", " ").title() link_target = f"{parts[-1]}.md"
title = parts[-1].replace("_", " ").title()
content = self._render_markdown(title, module.path) content = self._render_markdown(title, module.path)
if md_path.exists() and md_path.read_text(encoding="utf-8") == content: if not md_path.exists() or md_path.read_text(encoding="utf-8") != content:
return
md_path.write_text(content, encoding="utf-8") md_path.write_text(content, encoding="utf-8")
self._ensure_parent_index(parts, out_dir, link_target, title)
def _render_markdown(self, title: str, module_path: str) -> str: def _render_markdown(self, title: str, module_path: str) -> str:
""" """
Generate the Markdown content for a module file. Generate the Markdown content for a module file.
@@ -89,3 +99,46 @@ class MkDocsRenderer:
f"# {title}\n\n" f"# {title}\n\n"
f"::: {module_path}\n" f"::: {module_path}\n"
) )
def _ensure_root_index(
self,
project: Project,
out_dir: Path
) -> None:
root_index = out_dir / "index.md"
if not root_index.exists():
root_index.write_text(
f"# {project.name}\n\n"
"## Modules\n\n",
encoding="utf-8",
)
def _ensure_parent_index(
self,
parts: list[str],
out_dir: Path,
link_target: str,
title: str,
) -> None:
if len(parts) == 1:
parent_index = out_dir / "index.md"
link = f"- [{title}]({link_target})\n"
else:
parent_dir = out_dir.joinpath(*parts[:-1])
parent_dir.mkdir(parents=True, exist_ok=True)
parent_index = parent_dir / "index.md"
link = f"- [{title}]({link_target})\n"
if not parent_index.exists():
parent_title = parts[-2].replace("_", " ").title()
parent_index.write_text(
f"# {parent_title}\n\n",
encoding="utf-8",
)
content = parent_index.read_text(encoding="utf-8")
if link not in content:
parent_index.write_text(content + link, encoding="utf-8")

View File

@@ -8,10 +8,16 @@ class MkDocsRenderer:
name: str name: str
def generate_sources(self, project: Project, out_dir: Path) -> None: ... def generate_sources(self, project: Project, out_dir: Path) -> None: ...
def _write_module( def _write_module(
self, self,
module: Module, module: Module,
packages: Set[str], packages: Set[str],
out_dir: Path, out_dir: Path,
) -> None: ... ) -> None: ...
def _render_markdown(self, title: str, module_path: str) -> str: ... def _render_markdown(self, title: str, module_path: str) -> str: ...
def _ensure_root_index(self, project, out_dir) -> None: ...
def _ensure_parent_index(self, parts, out_dir, link_target, title) -> None: ...

View File

@@ -1,3 +1,7 @@
# Cli # Cli
::: docforge.cli ::: docforge.cli
- [Commands](commands.md)
- [Main](main.md)
- [Mcp Utils](mcp_utils.md)
- [Mkdocs Utils](mkdocs_utils.md)

View File

@@ -1,3 +1,9 @@
# Docforge # Docforge
::: docforge ::: docforge
- [Cli](cli/)
- [Loaders](loaders/)
- [Models](models/)
- [Nav](nav/)
- [Renderers](renderers/)
- [Servers](servers/)

View File

@@ -1,3 +1,4 @@
# Loaders # Loaders
::: docforge.loaders ::: docforge.loaders
- [Griffe Loader](griffe_loader.md)

View File

@@ -1,3 +1,6 @@
# Models # Models
::: docforge.models ::: docforge.models
- [Module](module.md)
- [Object](object.md)
- [Project](project.md)

View File

@@ -1,3 +1,6 @@
# Nav # Nav
::: docforge.nav ::: docforge.nav
- [Mkdocs](mkdocs.md)
- [Resolver](resolver.md)
- [Spec](spec.md)

View File

@@ -1,3 +1,6 @@
# Renderers # Renderers
::: docforge.renderers ::: docforge.renderers
- [Base](base.md)
- [Mcp Renderer](mcp_renderer.md)
- [Mkdocs Renderer](mkdocs_renderer.md)

View File

@@ -1,3 +1,4 @@
# Servers # Servers
::: docforge.servers ::: docforge.servers
- [Mcp Server](mcp_server.md)

5
docs/index.md Normal file
View File

@@ -0,0 +1,5 @@
# docforge
## Modules
- [Docforge](docforge/)

View File

@@ -1,5 +1,3 @@
site_name: docforge
theme: theme:
name: material name: material
palette: palette:
@@ -33,7 +31,7 @@ plugins:
annotations_path: brief annotations_path: brief
show_root_heading: true show_root_heading: true
group_by_category: true group_by_category: true
site_name: docforge
nav: nav:
- Home: docforge/index.md - Home: docforge/index.md
- Loaders: - Loaders: