diff --git a/book.toml b/book.toml index 47d02920b623d..1a1eaf2d99afb 100644 --- a/book.toml +++ b/book.toml @@ -15,3 +15,6 @@ fold = { enable = true} git-repository-url = "https://github.com/lowrisc/opentitan" edit-url-template = "https://github.com/lowrisc/opentitan/edit/master/{path}" curly-quotes = true + +[preprocessor.readme2index] +command = "./util/mdbook_readme2index.py" diff --git a/util/mdbook/__init__.py b/util/mdbook/__init__.py new file mode 100644 index 0000000000000..fee181e0fc6d2 --- /dev/null +++ b/util/mdbook/__init__.py @@ -0,0 +1,4 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +"""Common module for mdbook pre-processors.""" diff --git a/util/mdbook/utils.py b/util/mdbook/utils.py new file mode 100644 index 0000000000000..cf0c320350fdd --- /dev/null +++ b/util/mdbook/utils.py @@ -0,0 +1,18 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +"""Common utilities used by mdbook pre-processors.""" + +from typing import List, Any, Dict, Generator + + +def chapters(items: List[Dict[str, Any]]) -> Generator[Dict[str, Any], None, None]: + """Recursively yields all chapters""" + for chapter in (item.get("Chapter") for item in items): + if not chapter: + continue + + for sub_chapter in chapters(chapter["sub_items"]): + yield sub_chapter + + yield chapter diff --git a/util/mdbook_readme2index.py b/util/mdbook_readme2index.py new file mode 100755 index 0000000000000..d436318a1bdc2 --- /dev/null +++ b/util/mdbook_readme2index.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +"""mdbook preprocessor that converts README.md to index.md + +We use the built-in `index` preprocessor to rename our `README.md` files to `index.md`, +but it doesn't currently fixup links: https://github.com/rust-lang/mdBook/issues/984. +This is a temporary preprocessor to regex replace those links until upstream gets fixed. +""" + +import json +import sys +import re + +import mdbook.utils as md_utils + +# Finds all markdown links, `[...](...)`, +# which link to a file called readme.md +# To check it isn't a link no colon, `:`, is allowed before the readme.md . +# `#` and '?' also aren't allowed before the readme.md, +# in case `readme.md` is not the filename but in fact a fragment or selector. +# It matches the link content before and after the readme into groups +# so that it can be substituted back into the file. +RM2IDX_PATTERN_INLINE = re.compile( + r"(\[[^\]]*\]\([^\)|#|:|\?]*)readme(\.md[^\)]*\))", + re.IGNORECASE, +) + +# Similar to the pattern above but for `[...]: ...` style links. +RM2IDX_PATTERN_NOT_INLINE = re.compile( + r"^(\[[^\]]*\]:\s+[^\n|#|:|\?]*)readme(\.md[^\n]*$)", + re.IGNORECASE | re.MULTILINE, +) + + +def main() -> None: + if len(sys.argv) > 2: + if (sys.argv[1], sys.argv[2]) == ("supports", "html"): + sys.exit(0) + else: + sys.exit(1) + + # load both the context and the book from stdin + context, book = json.load(sys.stdin) + + for chapter in md_utils.chapters(book["sections"]): + chapter["content"] = RM2IDX_PATTERN_INLINE.sub(r"\1index\2", chapter["content"]) + chapter["content"] = RM2IDX_PATTERN_NOT_INLINE.sub(r"\1index\2", chapter["content"]) + + # dump the book into stdout + print(json.dumps(book)) + + +if __name__ == "__main__": + main()