FossilRepo

Fix relative links in Fossil docs: resolve against base_path - _render_fossil_content now accepts base_path for relative link resolution - ./glossary.md in www/concepts.wiki → /www/glossary.md (not /glossary.md) - Bare filenames (quickstart.wiki) also resolved against base_path - _rewrite_fossil_links handles /www/*.wiki → /fossil/docs/www/... - /help/command links route to docs - Bare .wiki/.md/.html paths route to /fossil/docs/www/

lmata 2026-04-06 22:34 trunk
Commit 23e8ddeccf99ae4f3a409517f8d616b390727bb79ef87560638a1aed784fb9b1
1 file changed +27 -5
+27 -5
--- fossil/views.py
+++ fossil/views.py
@@ -11,17 +11,19 @@
1111
1212
from .models import FossilRepository
1313
from .reader import FossilReader
1414
1515
16
-def _render_fossil_content(content: str, project_slug: str = "") -> str:
16
+def _render_fossil_content(content: str, project_slug: str = "", base_path: str = "") -> str:
1717
"""Render content that may be Fossil wiki markup, HTML, or Markdown.
1818
1919
Fossil wiki pages can contain:
2020
- Raw HTML (most Fossil wiki pages)
2121
- Fossil-specific markup: [link|text], <verbatim>...</verbatim>
2222
- Markdown (newer pages)
23
+
24
+ base_path: directory of the current file (e.g. "www/") for resolving relative links.
2325
"""
2426
if not content:
2527
return ""
2628
2729
# Detect format from the raw content BEFORE any transformations
@@ -31,11 +33,13 @@
3133
# Markdown: convert Fossil [path | text] links to markdown links first
3234
def _fossil_to_md_link(m):
3335
path = m.group(1).strip()
3436
text = m.group(2).strip()
3537
if path.startswith("./"):
36
- path = "/" + path[2:]
38
+ path = "/" + base_path + path[2:]
39
+ elif not path.startswith("/") and not path.startswith("http"):
40
+ path = "/" + base_path + path
3741
return f"[{text}]({path})"
3842
3943
content = re.sub(r"\[([^\]\|]+?)\s*\|\s*([^\]]+?)\]", _fossil_to_md_link, content)
4044
content = re.sub(r"<verbatim>(.*?)</verbatim>", r"```\n\1\n```", content, flags=re.DOTALL)
4145
html = md.markdown(content, extensions=["fenced_code", "tables", "toc"])
@@ -44,13 +48,15 @@
4448
# Fossil wiki / HTML: convert Fossil-specific syntax to HTML
4549
# Fossil links: [path | text] or [path|text] — spaces around pipe are optional
4650
def _fossil_link_replace(match):
4751
path = match.group(1).strip()
4852
text = match.group(2).strip()
49
- # Convert relative paths (./foo) to absolute (/foo)
53
+ # Convert relative paths to absolute using base_path
5054
if path.startswith("./"):
51
- path = "/" + path[2:]
55
+ path = "/" + base_path + path[2:]
56
+ elif not path.startswith("/") and not path.startswith("http"):
57
+ path = "/" + base_path + path
5258
return f'<a href="{path}">{text}</a>'
5359
5460
# Match [path | text] with flexible whitespace around the pipe
5561
content = re.sub(r"\[([^\]\|]+?)\s*\|\s*([^\]]+?)\]", _fossil_link_replace, content)
5662
content = re.sub(r"<verbatim>(.*?)</verbatim>", r"<pre><code>\1</code></pre>", content, flags=re.DOTALL)
@@ -143,10 +149,22 @@
143149
if url.startswith("/timeline"):
144150
return f'href="{base}/timeline/"'
145151
# /forum -> forum
146152
if url.startswith("/forumpost") or url.startswith("/forum"):
147153
return f'href="{base}/forum/"'
154
+ # /www/file.wiki or /www/subdir/file -> doc page viewer
155
+ m = re.match(r"/(www/.+)", url)
156
+ if m:
157
+ return f'href="{base}/docs/{m.group(1)}"'
158
+ # /help/command -> Fossil help (link to fossil docs)
159
+ m = re.match(r"/help/(.+)", url)
160
+ if m:
161
+ return f'href="{base}/docs/www/help.wiki"'
162
+ # Bare .wiki or .md file paths (from relative link resolution)
163
+ m = re.match(r"/([^/]+\.(?:wiki|md|html))", url)
164
+ if m:
165
+ return f'href="{base}/docs/www/{m.group(1)}"'
148166
# Keep external and unrecognized links as-is
149167
return match.group(0)
150168
151169
def replace_scheme_link(match):
152170
"""Handle Fossil URI schemes like forum:/forumpost/HASH, wiki:PageName, info:HASH."""
@@ -767,11 +785,15 @@
767785
try:
768786
content = content_bytes.decode("utf-8")
769787
except UnicodeDecodeError as e:
770788
raise Http404("Binary file cannot be rendered as documentation") from e
771789
772
- content_html = mark_safe(_render_fossil_content(content, project_slug=slug))
790
+ # Compute base_path for relative link resolution (e.g. "www/" for "www/concepts.wiki")
791
+ doc_base = "/".join(doc_path.split("/")[:-1])
792
+ if doc_base:
793
+ doc_base += "/"
794
+ content_html = mark_safe(_render_fossil_content(content, project_slug=slug, base_path=doc_base))
773795
774796
return render(
775797
request,
776798
"fossil/doc_page.html",
777799
{"project": project, "doc_path": doc_path, "content_html": content_html, "active_tab": "wiki"},
778800
--- fossil/views.py
+++ fossil/views.py
@@ -11,17 +11,19 @@
11
12 from .models import FossilRepository
13 from .reader import FossilReader
14
15
16 def _render_fossil_content(content: str, project_slug: str = "") -> str:
17 """Render content that may be Fossil wiki markup, HTML, or Markdown.
18
19 Fossil wiki pages can contain:
20 - Raw HTML (most Fossil wiki pages)
21 - Fossil-specific markup: [link|text], <verbatim>...</verbatim>
22 - Markdown (newer pages)
 
 
23 """
24 if not content:
25 return ""
26
27 # Detect format from the raw content BEFORE any transformations
@@ -31,11 +33,13 @@
31 # Markdown: convert Fossil [path | text] links to markdown links first
32 def _fossil_to_md_link(m):
33 path = m.group(1).strip()
34 text = m.group(2).strip()
35 if path.startswith("./"):
36 path = "/" + path[2:]
 
 
37 return f"[{text}]({path})"
38
39 content = re.sub(r"\[([^\]\|]+?)\s*\|\s*([^\]]+?)\]", _fossil_to_md_link, content)
40 content = re.sub(r"<verbatim>(.*?)</verbatim>", r"```\n\1\n```", content, flags=re.DOTALL)
41 html = md.markdown(content, extensions=["fenced_code", "tables", "toc"])
@@ -44,13 +48,15 @@
44 # Fossil wiki / HTML: convert Fossil-specific syntax to HTML
45 # Fossil links: [path | text] or [path|text] — spaces around pipe are optional
46 def _fossil_link_replace(match):
47 path = match.group(1).strip()
48 text = match.group(2).strip()
49 # Convert relative paths (./foo) to absolute (/foo)
50 if path.startswith("./"):
51 path = "/" + path[2:]
 
 
52 return f'<a href="{path}">{text}</a>'
53
54 # Match [path | text] with flexible whitespace around the pipe
55 content = re.sub(r"\[([^\]\|]+?)\s*\|\s*([^\]]+?)\]", _fossil_link_replace, content)
56 content = re.sub(r"<verbatim>(.*?)</verbatim>", r"<pre><code>\1</code></pre>", content, flags=re.DOTALL)
@@ -143,10 +149,22 @@
143 if url.startswith("/timeline"):
144 return f'href="{base}/timeline/"'
145 # /forum -> forum
146 if url.startswith("/forumpost") or url.startswith("/forum"):
147 return f'href="{base}/forum/"'
 
 
 
 
 
 
 
 
 
 
 
 
148 # Keep external and unrecognized links as-is
149 return match.group(0)
150
151 def replace_scheme_link(match):
152 """Handle Fossil URI schemes like forum:/forumpost/HASH, wiki:PageName, info:HASH."""
@@ -767,11 +785,15 @@
767 try:
768 content = content_bytes.decode("utf-8")
769 except UnicodeDecodeError as e:
770 raise Http404("Binary file cannot be rendered as documentation") from e
771
772 content_html = mark_safe(_render_fossil_content(content, project_slug=slug))
 
 
 
 
773
774 return render(
775 request,
776 "fossil/doc_page.html",
777 {"project": project, "doc_path": doc_path, "content_html": content_html, "active_tab": "wiki"},
778
--- fossil/views.py
+++ fossil/views.py
@@ -11,17 +11,19 @@
11
12 from .models import FossilRepository
13 from .reader import FossilReader
14
15
16 def _render_fossil_content(content: str, project_slug: str = "", base_path: str = "") -> str:
17 """Render content that may be Fossil wiki markup, HTML, or Markdown.
18
19 Fossil wiki pages can contain:
20 - Raw HTML (most Fossil wiki pages)
21 - Fossil-specific markup: [link|text], <verbatim>...</verbatim>
22 - Markdown (newer pages)
23
24 base_path: directory of the current file (e.g. "www/") for resolving relative links.
25 """
26 if not content:
27 return ""
28
29 # Detect format from the raw content BEFORE any transformations
@@ -31,11 +33,13 @@
33 # Markdown: convert Fossil [path | text] links to markdown links first
34 def _fossil_to_md_link(m):
35 path = m.group(1).strip()
36 text = m.group(2).strip()
37 if path.startswith("./"):
38 path = "/" + base_path + path[2:]
39 elif not path.startswith("/") and not path.startswith("http"):
40 path = "/" + base_path + path
41 return f"[{text}]({path})"
42
43 content = re.sub(r"\[([^\]\|]+?)\s*\|\s*([^\]]+?)\]", _fossil_to_md_link, content)
44 content = re.sub(r"<verbatim>(.*?)</verbatim>", r"```\n\1\n```", content, flags=re.DOTALL)
45 html = md.markdown(content, extensions=["fenced_code", "tables", "toc"])
@@ -44,13 +48,15 @@
48 # Fossil wiki / HTML: convert Fossil-specific syntax to HTML
49 # Fossil links: [path | text] or [path|text] — spaces around pipe are optional
50 def _fossil_link_replace(match):
51 path = match.group(1).strip()
52 text = match.group(2).strip()
53 # Convert relative paths to absolute using base_path
54 if path.startswith("./"):
55 path = "/" + base_path + path[2:]
56 elif not path.startswith("/") and not path.startswith("http"):
57 path = "/" + base_path + path
58 return f'<a href="{path}">{text}</a>'
59
60 # Match [path | text] with flexible whitespace around the pipe
61 content = re.sub(r"\[([^\]\|]+?)\s*\|\s*([^\]]+?)\]", _fossil_link_replace, content)
62 content = re.sub(r"<verbatim>(.*?)</verbatim>", r"<pre><code>\1</code></pre>", content, flags=re.DOTALL)
@@ -143,10 +149,22 @@
149 if url.startswith("/timeline"):
150 return f'href="{base}/timeline/"'
151 # /forum -> forum
152 if url.startswith("/forumpost") or url.startswith("/forum"):
153 return f'href="{base}/forum/"'
154 # /www/file.wiki or /www/subdir/file -> doc page viewer
155 m = re.match(r"/(www/.+)", url)
156 if m:
157 return f'href="{base}/docs/{m.group(1)}"'
158 # /help/command -> Fossil help (link to fossil docs)
159 m = re.match(r"/help/(.+)", url)
160 if m:
161 return f'href="{base}/docs/www/help.wiki"'
162 # Bare .wiki or .md file paths (from relative link resolution)
163 m = re.match(r"/([^/]+\.(?:wiki|md|html))", url)
164 if m:
165 return f'href="{base}/docs/www/{m.group(1)}"'
166 # Keep external and unrecognized links as-is
167 return match.group(0)
168
169 def replace_scheme_link(match):
170 """Handle Fossil URI schemes like forum:/forumpost/HASH, wiki:PageName, info:HASH."""
@@ -767,11 +785,15 @@
785 try:
786 content = content_bytes.decode("utf-8")
787 except UnicodeDecodeError as e:
788 raise Http404("Binary file cannot be rendered as documentation") from e
789
790 # Compute base_path for relative link resolution (e.g. "www/" for "www/concepts.wiki")
791 doc_base = "/".join(doc_path.split("/")[:-1])
792 if doc_base:
793 doc_base += "/"
794 content_html = mark_safe(_render_fossil_content(content, project_slug=slug, base_path=doc_base))
795
796 return render(
797 request,
798 "fossil/doc_page.html",
799 {"project": project, "doc_path": doc_path, "content_html": content_html, "active_tab": "wiki"},
800

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button