diff options
| author | Jason A. Dönerfield <no-replay@sl0p.foo> | 2026-07-18 21:07:20 +0200 |
|---|---|---|
| committer | Jason A. Dönerfield <no-replay@sl0p.foo> | 2026-07-18 21:31:00 +0200 |
| commit | e3364e2c4424b373f80ac04d1ab2e3f3d82328e1 (patch) | |
| tree | 78bf7b8fbf87781e25fd287e6307b664b468f630 /filters | |
| parent | md2html: rewrite relative README links to the tree/ blob URL (diff) | |
| download | cgit-e3364e2c4424b373f80ac04d1ab2e3f3d82328e1.tar.gz cgit-e3364e2c4424b373f80ac04d1ab2e3f3d82328e1.tar.xz cgit-e3364e2c4424b373f80ac04d1ab2e3f3d82328e1.zip | |
md2html: render linked .md via the about view, fix relative base
Links to markdown files now point at /<repo>/about/<path> (rendered
through the about-filter) instead of /<repo>/tree/<path> (raw blob), so
clicking a README link to another .md shows rendered markdown.
Also resolve relative links against the rendered file's own directory:
about-formatting.sh now forwards the filename cgit passes ($1) to
md2html as argv[1], and md2html uses dirname(argv[1]) as the base. This
makes links *inside* a rendered subpage (docs/foo.md -> ./bar.md,
../README.md, images) resolve correctly. Non-markdown files and
directories still go to tree/; images to plain/; about/ links carry no
?h= since the about view renders against the configured readme ref.
Diffstat (limited to 'filters')
| -rwxr-xr-x | filters/about-formatting.sh | 2 | ||||
| -rwxr-xr-x | filters/html-converters/md2html | 69 |
2 files changed, 45 insertions, 26 deletions
diff --git a/filters/about-formatting.sh b/filters/about-formatting.sh index 85daf9c..c6c4f9c 100755 --- a/filters/about-formatting.sh +++ b/filters/about-formatting.sh @@ -19,7 +19,7 @@ cd "$(dirname $0)/html-converters/" case "$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')" in - *.markdown|*.mdown|*.md|*.mkd) exec ./md2html; ;; + *.markdown|*.mdown|*.md|*.mkd) exec ./md2html "$1"; ;; *.rst) exec ./rst2html; ;; *.[1-9]) exec ./man2html; ;; *.htm|*.html) exec cat; ;; diff --git a/filters/html-converters/md2html b/filters/html-converters/md2html index 90021a7..79d4a17 100755 --- a/filters/html-converters/md2html +++ b/filters/html-converters/md2html @@ -358,17 +358,21 @@ html = markdown.markdown( "markdown.extensions.codehilite":{"css_class":"highlight"}}) # A README's relative paths point at repo content, but a browser resolves them -# against the page the readme is shown on (/<repo>/about/ or the summary page -# /<repo>/), not against the repo root — so they 404. cgit serves blobs only via -# its `plain`/`tree` commands, so rewrite relative references to absolute cgit -# URLs (absolute so it works both on the about page and the summary page): -# <img src> -> /<repo>/plain/<path>?h=<branch> (raw bytes for the browser) -# <a href> -> /<repo>/tree/<path>?h=<branch> (browsable file/dir view) -# Relative hrefs are resolved as paths against the repo root; a href that climbs -# out of the repo with ".." (e.g. ../sibling) is resolved against the repo's URL -# location instead (-> /sibling), matching how sibling repos are addressed. -# cgit exports the repo context to about-filters via these environment vars; if -# they're absent we leave the references untouched. +# against the page the readme is shown on (/<repo>/about/... or the summary page +# /<repo>/), not against the file's own location — so they 404 or hit the wrong +# file. cgit serves this content through three commands, so rewrite relative +# references to absolute cgit URLs (absolute so they work on the about page and +# the summary page alike): +# <img src> -> /<repo>/plain/<path>?h=<branch> (raw bytes) +# <a href> *.md -> /<repo>/about/<path> (rendered markdown) +# <a href> other/dir -> /<repo>/tree/<path>?h=<branch> (browsable blob view) +# cgit hands the about-filter the path of the file being rendered as argv[1] +# (README.md, or docs/foo.md for a rendered subpage), so relative links resolve +# against *that* file's directory and links inside a rendered subpage work too. +# A href that climbs out of the repo with ".." (e.g. ../sibling) is resolved +# against the repo's URL location (-> /sibling). The about view renders against +# the configured readme ref, so about/ links carry no ?h=. If the repo context +# env vars are absent we leave the references untouched. import os, re from urllib.parse import urljoin repo = os.environ.get("CGIT_REPO_URL", "").strip("/") @@ -377,16 +381,16 @@ if repo: repo_root = "/" + repo + "/" plain = repo_root + "plain/" query = ("?h=" + branch) if branch else "" + # directory of the file being rendered; relative links resolve against it. + # argv[1] is the repo-relative readme/subpage path cgit passes the filter. + cur = sys.argv[1] if len(sys.argv) > 1 else "" + base_dir = "" if cur.startswith("/") else os.path.dirname(cur) + # markdown files are rendered via the about view; everything else is a blob + md_ext = re.compile(r"\.(?:md|markdown|mdown|mkd)$", re.I) # leave absolute, protocol-relative, root-relative, anchor, query-only and # scheme (mailto:, http:, data:, ...) URLs alone skip = re.compile(r"^(?:[a-z][a-z0-9+.\-]*:|//|/|[#?])", re.I) - def rewrite_img(m): - url = m.group(2) - if not url or skip.match(url): - return m.group(0) - return m.group(1) + plain + re.sub(r"^\./", "", url) + query + m.group(3) - def _split_frag(url): # split off a trailing #fragment (kept verbatim on the rewritten URL) if "#" in url: @@ -394,11 +398,13 @@ if repo: return path, "#" + frag return url, "" - def _normalize(path): - # resolve "." / ".." against the repo root; returns (climbs, cleaned) - # where `climbs` counts ".." segments that escaped above the repo root. + def _resolve(path): + # resolve `path` (relative to base_dir) into a repo-root-relative path, + # collapsing "." / ".."; returns (climbs, cleaned) where `climbs` counts + # ".." segments that escaped above the repo root. + combined = (base_dir + "/" + path) if base_dir else path out, climbs = [], 0 - for seg in path.split("/"): + for seg in combined.split("/"): if seg in ("", "."): continue if seg == "..": @@ -410,19 +416,32 @@ if repo: out.append(seg) return climbs, "/".join(out) + def rewrite_img(m): + url = m.group(2) + if not url or skip.match(url): + return m.group(0) + path, frag = _split_frag(url) + climbs, cleaned = _resolve(path) + if climbs or not cleaned: + return m.group(0) + return m.group(1) + plain + cleaned + query + frag + m.group(3) + def rewrite_href(m): url = m.group(2) if not url or skip.match(url): return m.group(0) path, frag = _split_frag(url) - climbs, cleaned = _normalize(path) + climbs, cleaned = _resolve(path) if climbs: # escapes the repo (sibling repo etc.): resolve against repo location dest = urljoin(repo_root, "../" * climbs + cleaned) - elif cleaned: - dest = repo_root + "tree/" + cleaned + query - else: + elif not cleaned: dest = repo_root + (query or "") + elif md_ext.search(cleaned): + # rendered through the about-filter (renders against the readme ref) + dest = repo_root + "about/" + cleaned + else: + dest = repo_root + "tree/" + cleaned + query return m.group(1) + dest + frag + m.group(3) html = re.sub(r'(<img\b[^>]*?\bsrc=")([^"]*)(")', rewrite_img, html, flags=re.I) |
