FossilRepo
Fix Fossil wiki link parsing: handle spaces and relative paths - Support [path | text] with flexible whitespace around the pipe - Convert relative paths (./foo.wiki) to absolute (/foo.wiki) - Works in both Fossil wiki/HTML mode and markdown mode - Fixes links in embedded docs like concepts.wiki
Commit
9313e3219cb27211dcbf07446228492b1b9421c6591883a7216e48478fb97318
Parent
a22a045da262aa0…
1 file changed
+20
-4
+20
-4
| --- fossil/views.py | ||
| +++ fossil/views.py | ||
| @@ -26,19 +26,35 @@ | ||
| 26 | 26 | |
| 27 | 27 | # Detect format from the raw content BEFORE any transformations |
| 28 | 28 | is_markdown = _is_markdown(content) |
| 29 | 29 | |
| 30 | 30 | if is_markdown: |
| 31 | - # Markdown: convert Fossil [/path|text] links to markdown links first | |
| 32 | - content = re.sub(r"\[(/[^|\]]+)\|([^\]]+)\]", r"[\2](\1)", content) | |
| 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) | |
| 33 | 40 | content = re.sub(r"<verbatim>(.*?)</verbatim>", r"```\n\1\n```", content, flags=re.DOTALL) |
| 34 | 41 | html = md.markdown(content, extensions=["fenced_code", "tables", "toc"]) |
| 35 | 42 | return _rewrite_fossil_links(html, project_slug) if project_slug else html |
| 36 | 43 | |
| 37 | 44 | # Fossil wiki / HTML: convert Fossil-specific syntax to HTML |
| 38 | - content = re.sub(r"\[(/[^|\]]+)\|([^\]]+)\]", r'<a href="\1">\2</a>', content) | |
| 39 | - content = re.sub(r"\[(https?://[^|\]]+)\|([^\]]+)\]", r'<a href="\1">\2</a>', content) | |
| 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) | |
| 40 | 56 | content = re.sub(r"<verbatim>(.*?)</verbatim>", r"<pre><code>\1</code></pre>", content, flags=re.DOTALL) |
| 41 | 57 | |
| 42 | 58 | # Convert Fossil wiki list syntax: lines starting with " * " to <ul><li> |
| 43 | 59 | lines = content.split("\n") |
| 44 | 60 | result = [] |
| 45 | 61 |
| --- fossil/views.py | |
| +++ fossil/views.py | |
| @@ -26,19 +26,35 @@ | |
| 26 | |
| 27 | # Detect format from the raw content BEFORE any transformations |
| 28 | is_markdown = _is_markdown(content) |
| 29 | |
| 30 | if is_markdown: |
| 31 | # Markdown: convert Fossil [/path|text] links to markdown links first |
| 32 | content = re.sub(r"\[(/[^|\]]+)\|([^\]]+)\]", r"[\2](\1)", content) |
| 33 | content = re.sub(r"<verbatim>(.*?)</verbatim>", r"```\n\1\n```", content, flags=re.DOTALL) |
| 34 | html = md.markdown(content, extensions=["fenced_code", "tables", "toc"]) |
| 35 | return _rewrite_fossil_links(html, project_slug) if project_slug else html |
| 36 | |
| 37 | # Fossil wiki / HTML: convert Fossil-specific syntax to HTML |
| 38 | content = re.sub(r"\[(/[^|\]]+)\|([^\]]+)\]", r'<a href="\1">\2</a>', content) |
| 39 | content = re.sub(r"\[(https?://[^|\]]+)\|([^\]]+)\]", r'<a href="\1">\2</a>', content) |
| 40 | content = re.sub(r"<verbatim>(.*?)</verbatim>", r"<pre><code>\1</code></pre>", content, flags=re.DOTALL) |
| 41 | |
| 42 | # Convert Fossil wiki list syntax: lines starting with " * " to <ul><li> |
| 43 | lines = content.split("\n") |
| 44 | result = [] |
| 45 |
| --- fossil/views.py | |
| +++ fossil/views.py | |
| @@ -26,19 +26,35 @@ | |
| 26 | |
| 27 | # Detect format from the raw content BEFORE any transformations |
| 28 | is_markdown = _is_markdown(content) |
| 29 | |
| 30 | if is_markdown: |
| 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"]) |
| 42 | return _rewrite_fossil_links(html, project_slug) if project_slug else html |
| 43 | |
| 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) |
| 57 | |
| 58 | # Convert Fossil wiki list syntax: lines starting with " * " to <ul><li> |
| 59 | lines = content.split("\n") |
| 60 | result = [] |
| 61 |