aboutsummaryrefslogtreecommitdiffstats
path: root/filters/html-converters/md2html
diff options
context:
space:
mode:
authorJason A. Dönerfield <no-replay@sl0p.foo>2026-07-18 21:07:20 +0200
committerJason A. Dönerfield <no-replay@sl0p.foo>2026-07-18 21:31:00 +0200
commite3364e2c4424b373f80ac04d1ab2e3f3d82328e1 (patch)
tree78bf7b8fbf87781e25fd287e6307b664b468f630 /filters/html-converters/md2html
parentmd2html: rewrite relative README links to the tree/ blob URL (diff)
downloadcgit-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/html-converters/md2html')
-rwxr-xr-xfilters/html-converters/md2html69
1 files changed, 44 insertions, 25 deletions
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)