evennia/docs/pylib/auto_link_remapper.py
Griatch a452434ba8 Revert "Updated HTML docs."
This reverts commit 06bc3c8bcd.
2022-11-15 20:46:50 +01:00

281 lines
9.4 KiB
Python

"""
Build a TOC-tree; Sphinx requires it and this makes it easy to just
add/build/link new files without needing to explicitly add it to a toctree
directive somewhere.
"""
import re
from collections import defaultdict
from os.path import abspath, dirname
from os.path import join as pathjoin
from os.path import relpath
from pathlib import Path
from sphinx.errors import DocumentError
_IGNORE_FILES = []
_SOURCEDIR_NAME = "source"
_SOURCE_DIR = pathjoin(dirname(dirname(abspath(__file__))), _SOURCEDIR_NAME)
_TOC_FILE = pathjoin(_SOURCE_DIR, "toc.md")
_NO_REMAP_STARTSWITH = [
"http://",
"https://",
"github:",
"feature-request",
"report-bug",
"issue",
"bug-report",
]
# remove these prefixes from the url
_STRIP_PREFIX = [
"../../api/",
"../api/",
"./api/",
"api/",
"api:",
]
TXT_REMAPS = {
"Developer Central": "Evennia Components overview",
"Getting Started": "Setup Quickstart",
}
URL_REMAPS = {
"Developer-Central": "Components/Components-Overview",
"Tutorials": "Howtos/Howtos-Overview",
"../Howtos/Beginner-Tutorial/Directory-Overview": "Gamedir-Overview",
"Howtos/Beginner-Tutorial/Directory-Overview": "Gamedir-Overview",
"Beginner-Tutorial/Directory-Overview": "Gamedir-Overview",
"Directory-Overview": "Gamedir-Overview",
"../Setup/Getting-Started": "Setup-Quickstart",
"Setup/Getting-Started": "Setup-Quickstart",
"Getting-Started": "Setup-Quickstart",
"First-Steps-Coding": "Beginner-Tutorial-Part1",
"../Howtos/Beginner-Tutorial/Adding-Command-Tutorial": "Adding-Commands",
"Howtos/Beginner-Tutorial/Adding-Command-Tutorial": "Adding-Commands",
"Beginner-Tutorial/Adding-Command-Tutorial": "Adding-Commands",
"Adding-Command-Tutorial": "Adding-Commands",
"CmdSet": "Command-Sets",
"Spawner": "Prototypes",
"issue": "github:issue",
"issues": "github:issue",
"bug": "github:issue",
"bug-report": "github:issue",
"./Default-Command-Help": "api:evennia.commands.default#modules",
"../Components/Default-Command-Help": "api:evennia.commands.default#modules",
"../../../Components/Default-Command-Help": "api:evennia.commands.default#modules",
"./Locks.md#permissions": "Permissions",
"modules": "Default-Commands.md",
}
_USED_REFS = {}
_CURRFILE = None
def auto_link_remapper(no_autodoc=False):
"""
- Auto-Remaps links to fit with the actual document file structure. Requires
all doc files to have a unique name.
- Creates source/toc.md file
"""
global _CURRFILE
print(" -- Auto-Remapper starting.")
def _get_rel_source_ref(path):
"""Get the path relative the source/ dir"""
pathparts = path.split("/")
# we allow a max of 4 levels of nesting in the source dir
ind = pathparts[-5:].index(_SOURCEDIR_NAME)
# get the part after source/
pathparts = pathparts[-5 + 1 + ind :]
url = "/".join(pathparts)
# get the reference, without .md
url = url.rsplit(".", 1)[0]
return url
toc_map = {}
docref_map = defaultdict(dict)
for path in Path(_SOURCE_DIR).rglob("*.md"):
# find the source/ part of the path and strip it out
if path.name in _IGNORE_FILES:
# this is the name including .md
continue
sourcepath = path.as_posix()
# get name and url relative to source/
fname = path.name.rsplit(".", 1)[0]
src_url = _get_rel_source_ref(sourcepath)
# check for duplicate files
if fname in toc_map:
duplicate_src_url = toc_map[fname]
raise DocumentError(
f" Tried to add {src_url}.md, but a file {duplicate_src_url}.md already exists.\n"
" Evennia's auto-link-corrector does not accept doc-files with the same \n"
" name, even in different folders. Rename one.\n"
)
toc_map[fname] = src_url
# find relative links to all other files
for targetpath in Path(_SOURCE_DIR).rglob("*.md"):
targetname = targetpath.name.rsplit(".", 1)[0]
targetpath = targetpath.as_posix()
url = relpath(targetpath, dirname(sourcepath))
if not "/" in url:
# need to be explicit or there will be link ref collisions between
# e.g. TickerHandler page and TickerHandle api node
url = "./" + url
docref_map[sourcepath][targetname] = url.rsplit(".", 1)[0]
# normal reference-links [txt](urls)
ref_regex = re.compile(
r"\[(?P<txt>[\n\w -\[\]\`]+?)\]\((?P<url>.+?)\)", re.I + re.S + re.U + re.M
)
# in document references
ref_doc_regex = re.compile(
r"\[(?P<txt>[\n\w -\`]+?)\]:\s+?(?P<url>.+?)(?=$|\n)", re.I + re.S + re.U + re.M
)
def _sub(match):
# inline reference links
global _USED_REFS
grpdict = match.groupdict()
txt, url = grpdict["txt"], grpdict["url"]
txt = TXT_REMAPS.get(txt, txt)
url = URL_REMAPS.get(url, url)
for strip_prefix in _STRIP_PREFIX:
if url.startswith(strip_prefix):
url = url[len(strip_prefix) :]
if any(url.startswith(noremap) for noremap in _NO_REMAP_STARTSWITH):
# skip regular http/s urls etc
return f"[{txt}]({url})"
if url.startswith("evennia."):
# api link - we want to remove legacy #reference and remove .md
if "#" in url:
_, url = url.rsplit("#", 1)
if url.endswith(".md"):
url, _ = url.rsplit(".", 1)
return f"[{txt}]({url})"
fname, *part = url.rsplit("/", 1)
fname = part[0] if part else fname
fname, *anchor = fname.rsplit("#", 1)
if ".md" in fname:
fname = fname.rsplit(".", 1)[0]
if not _CURRFILE.endswith("toc.md"):
_USED_REFS[fname] = url
if _CURRFILE in docref_map and fname in docref_map[_CURRFILE]:
cfilename = _CURRFILE.rsplit("/", 1)[-1]
urlout = (
docref_map[_CURRFILE][fname] + ".md" + ("#" + anchor[0].lower() if anchor else "")
)
if urlout != url:
print(f" {cfilename}: [{txt}]({url}) -> [{txt}]({urlout})")
else:
urlout = url
return f"[{txt}]({urlout})"
def _sub_doc(match):
# reference links set at the bottom of the page
global _USED_REFS
grpdict = match.groupdict()
txt, url = grpdict["txt"], grpdict["url"]
txt = TXT_REMAPS.get(txt, txt)
url = URL_REMAPS.get(url, url)
for strip_prefix in _STRIP_PREFIX:
if url.startswith(strip_prefix):
url = url[len(strip_prefix) :]
if any(url.startswith(noremap) for noremap in _NO_REMAP_STARTSWITH):
return f"[{txt}]: {url}"
if "http" in url and "://" in url:
urlout = url
elif url.startswith("evennia."):
# api link - we want to remove legacy #reference
if "#" in url:
_, urlout = url.rsplit("#", 1)
else:
fname, *part = url.rsplit("/", 1)
fname = part[0] if part else fname
fname = fname.rsplit(".", 1)[0]
fname, *anchor = fname.rsplit("#", 1)
if not _CURRFILE.endswith("toc.md"):
_USED_REFS[fname] = url
if _CURRFILE in docref_map and fname in docref_map[_CURRFILE]:
cfilename = _CURRFILE.rsplit("/", 1)[-1]
urlout = docref_map[_CURRFILE][fname] + ("#" + anchor[0] if anchor else "")
if urlout != url:
print(f" {cfilename}: [{txt}]: {url} -> [{txt}]: {urlout}")
else:
urlout = url
return f"[{txt}]: {urlout}"
# replace / correct links in all files
count = 0
for path in sorted(Path(_SOURCE_DIR).rglob("*.md"), key=lambda p: p.name):
# from pudb import debugger;debugger.Debugger().set_trace()
_CURRFILE = path.as_posix()
with open(path, "r") as fil:
intxt = fil.read()
outtxt = ref_regex.sub(_sub, intxt)
outtxt = ref_doc_regex.sub(_sub_doc, outtxt)
if intxt != outtxt:
with open(path, "w") as fil:
fil.write(outtxt)
count += 1
print(f" -- Auto-relinked links in {path.name}")
if count > 0:
print(f" -- Auto-corrected links in {count} documents.")
for (fname, src_url) in sorted(toc_map.items(), key=lambda tup: tup[0]):
if fname not in _USED_REFS and not src_url.startswith("api/"):
print(f" ORPHANED DOC: no refs found to {src_url}.md")
# write tocfile
# with open(_TOC_FILE, "w") as fil:
# fil.write("```{toctree}\n")
# if not no_autodoc:
# fil.write("- [API root](api/evennia-api.rst)")
# for ref in sorted(toc_map.values()):
# if ref == "toc":
# continue
# # if not "/" in ref:
# # ref = "./" + ref
# # linkname = ref.replace("-", " ")
# fil.write(f"\n{ref}") # - [{linkname}]({ref})")
# # we add a self-reference so the toc itself is also a part of a toctree
# fil.write("\n```\n\n```{toctree}\n :hidden:\n\ntoc\n```")
# print(" -- File toc.md updated.")
print(" -- Auto-Remapper finished.")
if __name__ == "__main__":
auto_link_remapper()