--- a +++ b/docs/scripts/plugin.py @@ -0,0 +1,167 @@ +import os +from pathlib import Path + +import jedi +import mkdocs.config +import mkdocs.plugins +import mkdocs.structure +import mkdocs.structure.files +import mkdocs.structure.nav +import mkdocs.structure.pages +from bs4 import BeautifulSoup + + +def exclude_file(name): + return name.startswith("assets/fragments/") + + +# Add the files from the project root + +VIRTUAL_FILES = {} +REFERENCE_TEMPLATE = """ +# `{ident}` +::: {ident} + options: + show_source: false +""" + + +def on_files(files: mkdocs.structure.files.Files, config: mkdocs.config.Config): + """ + Recursively the navigation of the mkdocs config + and recursively content of directories of page that point + to directories. + + Parameters + ---------- + config: mkdocs.config.Config + The configuration object + kwargs: dict + Additional arguments + """ + + root = Path("edsnlp") + reference_nav = [] + for path in sorted(root.rglob("*.py")): + module_path = path.relative_to(root.parent).with_suffix("") + doc_path = Path("reference") / path.relative_to(root.parent).with_suffix(".md") + # full_doc_path = Path("docs/reference/") / doc_path + parts = list(module_path.parts) + current = reference_nav + for part in parts[:-1]: + sub = next((item[part] for item in current if part in item), None) + if sub is None: + current.append({part: []}) + sub = current[-1][part] + current = sub + if parts[-1] == "__init__": + parts = parts[:-1] + doc_path = doc_path.with_name("index.md") + current.append({"index.md": str(doc_path)}) + elif parts[-1] == "__main__": + continue + else: + current.append({parts[-1]: str(doc_path)}) + ident = ".".join(parts) + os.makedirs(doc_path.parent, exist_ok=True) + VIRTUAL_FILES[str(doc_path)] = REFERENCE_TEMPLATE.format(ident=ident) + + for item in config["nav"]: + if not isinstance(item, dict): + continue + key = next(iter(item.keys())) + if not isinstance(item[key], str): + continue + if item[key].strip("/") == "reference": + item[key] = reference_nav + + VIRTUAL_FILES["contributing.md"] = Path("contributing.md").read_text() + VIRTUAL_FILES["changelog.md"] = Path("changelog.md").read_text() + + return mkdocs.structure.files.Files( + [file for file in files if not exclude_file(file.src_path)] + + [ + mkdocs.structure.files.File( + file, + config["docs_dir"], + config["site_dir"], + config["use_directory_urls"], + ) + for file in VIRTUAL_FILES + ] + ) + + +def on_nav(nav, config, files): + def rec(node): + if isinstance(node, list): + return [rec(item) for item in node] + if node.is_section and node.title == "Code Reference": + return + if isinstance(node, mkdocs.structure.nav.Navigation): + return rec(node.items) + if isinstance(node, mkdocs.structure.nav.Section): + if ( + len(node.children) + and node.children[0].is_page + and node.children[0].is_index + ): + first = node.children[0] + link = mkdocs.structure.nav.Link( + title=first.title, + url=first.url, + ) + link.is_index = True + first.title = "Overview" + node.children.insert(0, link) + return rec(node.children) + + rec(nav.items) + + +def on_page_read_source(page, config): + if page.file.src_path in VIRTUAL_FILES: + return VIRTUAL_FILES[page.file.src_path] + return None + + +# Get current git commit +GIT_COMMIT = os.popen("git rev-parse --short HEAD").read().strip() + + +@mkdocs.plugins.event_priority(-2000) +def on_post_page( + output: str, + page: mkdocs.structure.pages.Page, + config: mkdocs.config.Config, +): + """ + Add github links to the html output + """ + # Find all the headings (h1, h2, ...) whose id starts with "edsnlp" + soup = BeautifulSoup(output, "html.parser") + for heading in soup.find_all(["h1", "h2", "h3", "h4", "h5", "h6"]): + ref = heading.get("id", "") + if ref.startswith("edsnlp.") and "--" not in ref: + code = "import edsnlp; " + ref + interpreter = jedi.Interpreter(code, namespaces=[{}]) + goto = interpreter.infer(1, len(code)) + try: + file = goto[0].module_path.relative_to(Path.cwd()) + except Exception: + goto = [] + if not goto: + continue + line = goto[0].line + # Add a "[source]" span with a link to the source code in a new tab + url = f"https://github.com/aphp/edsnlp/blob/{GIT_COMMIT}/{file}#L{line}" + heading.append( + BeautifulSoup( + f'<span class="sourced-heading-spacer"></span>' + f'<a href="{url}" target="_blank">[source]</a>', + features="html.parser", + ) + ) + # add "sourced-heading" to heading class + heading["class"] = heading.get("class", []) + ["sourced-heading"] + return str(soup)