Diff of /docs/scripts/plugin.py [000000] .. [cad161]

Switch to unified view

a b/docs/scripts/plugin.py
1
import os
2
from pathlib import Path
3
4
import jedi
5
import mkdocs.config
6
import mkdocs.plugins
7
import mkdocs.structure
8
import mkdocs.structure.files
9
import mkdocs.structure.nav
10
import mkdocs.structure.pages
11
from bs4 import BeautifulSoup
12
13
14
def exclude_file(name):
15
    return name.startswith("assets/fragments/")
16
17
18
# Add the files from the project root
19
20
VIRTUAL_FILES = {}
21
REFERENCE_TEMPLATE = """
22
# `{ident}`
23
::: {ident}
24
    options:
25
        show_source: false
26
"""
27
28
29
def on_files(files: mkdocs.structure.files.Files, config: mkdocs.config.Config):
30
    """
31
    Recursively the navigation of the mkdocs config
32
    and recursively content of directories of page that point
33
    to directories.
34
35
    Parameters
36
    ----------
37
    config: mkdocs.config.Config
38
        The configuration object
39
    kwargs: dict
40
        Additional arguments
41
    """
42
43
    root = Path("edsnlp")
44
    reference_nav = []
45
    for path in sorted(root.rglob("*.py")):
46
        module_path = path.relative_to(root.parent).with_suffix("")
47
        doc_path = Path("reference") / path.relative_to(root.parent).with_suffix(".md")
48
        # full_doc_path = Path("docs/reference/") / doc_path
49
        parts = list(module_path.parts)
50
        current = reference_nav
51
        for part in parts[:-1]:
52
            sub = next((item[part] for item in current if part in item), None)
53
            if sub is None:
54
                current.append({part: []})
55
                sub = current[-1][part]
56
            current = sub
57
        if parts[-1] == "__init__":
58
            parts = parts[:-1]
59
            doc_path = doc_path.with_name("index.md")
60
            current.append({"index.md": str(doc_path)})
61
        elif parts[-1] == "__main__":
62
            continue
63
        else:
64
            current.append({parts[-1]: str(doc_path)})
65
        ident = ".".join(parts)
66
        os.makedirs(doc_path.parent, exist_ok=True)
67
        VIRTUAL_FILES[str(doc_path)] = REFERENCE_TEMPLATE.format(ident=ident)
68
69
    for item in config["nav"]:
70
        if not isinstance(item, dict):
71
            continue
72
        key = next(iter(item.keys()))
73
        if not isinstance(item[key], str):
74
            continue
75
        if item[key].strip("/") == "reference":
76
            item[key] = reference_nav
77
78
    VIRTUAL_FILES["contributing.md"] = Path("contributing.md").read_text()
79
    VIRTUAL_FILES["changelog.md"] = Path("changelog.md").read_text()
80
81
    return mkdocs.structure.files.Files(
82
        [file for file in files if not exclude_file(file.src_path)]
83
        + [
84
            mkdocs.structure.files.File(
85
                file,
86
                config["docs_dir"],
87
                config["site_dir"],
88
                config["use_directory_urls"],
89
            )
90
            for file in VIRTUAL_FILES
91
        ]
92
    )
93
94
95
def on_nav(nav, config, files):
96
    def rec(node):
97
        if isinstance(node, list):
98
            return [rec(item) for item in node]
99
        if node.is_section and node.title == "Code Reference":
100
            return
101
        if isinstance(node, mkdocs.structure.nav.Navigation):
102
            return rec(node.items)
103
        if isinstance(node, mkdocs.structure.nav.Section):
104
            if (
105
                len(node.children)
106
                and node.children[0].is_page
107
                and node.children[0].is_index
108
            ):
109
                first = node.children[0]
110
                link = mkdocs.structure.nav.Link(
111
                    title=first.title,
112
                    url=first.url,
113
                )
114
                link.is_index = True
115
                first.title = "Overview"
116
                node.children.insert(0, link)
117
            return rec(node.children)
118
119
    rec(nav.items)
120
121
122
def on_page_read_source(page, config):
123
    if page.file.src_path in VIRTUAL_FILES:
124
        return VIRTUAL_FILES[page.file.src_path]
125
    return None
126
127
128
# Get current git commit
129
GIT_COMMIT = os.popen("git rev-parse --short HEAD").read().strip()
130
131
132
@mkdocs.plugins.event_priority(-2000)
133
def on_post_page(
134
    output: str,
135
    page: mkdocs.structure.pages.Page,
136
    config: mkdocs.config.Config,
137
):
138
    """
139
    Add github links to the html output
140
    """
141
    # Find all the headings (h1, h2, ...) whose id starts with "edsnlp"
142
    soup = BeautifulSoup(output, "html.parser")
143
    for heading in soup.find_all(["h1", "h2", "h3", "h4", "h5", "h6"]):
144
        ref = heading.get("id", "")
145
        if ref.startswith("edsnlp.") and "--" not in ref:
146
            code = "import edsnlp; " + ref
147
            interpreter = jedi.Interpreter(code, namespaces=[{}])
148
            goto = interpreter.infer(1, len(code))
149
            try:
150
                file = goto[0].module_path.relative_to(Path.cwd())
151
            except Exception:
152
                goto = []
153
            if not goto:
154
                continue
155
            line = goto[0].line
156
            # Add a "[source]" span with a link to the source code in a new tab
157
            url = f"https://github.com/aphp/edsnlp/blob/{GIT_COMMIT}/{file}#L{line}"
158
            heading.append(
159
                BeautifulSoup(
160
                    f'<span class="sourced-heading-spacer"></span>'
161
                    f'<a href="{url}" target="_blank">[source]</a>',
162
                    features="html.parser",
163
                )
164
            )
165
            # add "sourced-heading" to heading class
166
            heading["class"] = heading.get("class", []) + ["sourced-heading"]
167
    return str(soup)