FossilRepo
Expand Fossil wiki markup: enumeration lists, interwiki links, anchors, nowiki - # enumeration lists rendered as <ol> (separate from * bullet <ul>) - [wikipedia:Article] interwiki links → en.wikipedia.org - [#anchor] internal anchor links - [PageName] bare wiki links - <nowiki> tags stripped (content passes through) - Proper list type tracking (ul vs ol, switches correctly)
Commit
b311b008e340339b6e6a650ed1996314914323c6cdff62a21ab1bc1863d19d2c
Parent
c686b549a107c96…
1 file changed
+24
-6
+24
-6
| --- fossil/views.py | ||
| +++ fossil/views.py | ||
| @@ -57,31 +57,49 @@ | ||
| 57 | 57 | path = "/" + base_path + path |
| 58 | 58 | return f'<a href="{path}">{text}</a>' |
| 59 | 59 | |
| 60 | 60 | # Match [path | text] with flexible whitespace around the pipe |
| 61 | 61 | content = re.sub(r"\[([^\]\|]+?)\s*\|\s*([^\]]+?)\]", _fossil_link_replace, content) |
| 62 | + # Interwiki links: [wikipedia:Article] -> external link | |
| 63 | + content = re.sub(r"\[wikipedia:([^\]]+)\]", r'<a href="https://en.wikipedia.org/wiki/\1">\1</a>', content) | |
| 64 | + # Anchor links: [#anchor-name] -> local anchor | |
| 65 | + content = re.sub(r"\[#([^\]]+)\]", r'<a href="#\1">\1</a>', content) | |
| 66 | + # Bare wiki links: [PageName] (no pipe, not a URL) | |
| 67 | + content = re.sub(r"\[([A-Z][a-zA-Z0-9_]+)\]", r'<a href="\1">\1</a>', content) | |
| 68 | + # Verbatim blocks | |
| 62 | 69 | content = re.sub(r"<verbatim>(.*?)</verbatim>", r"<pre><code>\1</code></pre>", content, flags=re.DOTALL) |
| 70 | + # <nowiki> blocks — strip the tags, content passes through as-is | |
| 71 | + content = re.sub(r"<nowiki>(.*?)</nowiki>", r"\1", content, flags=re.DOTALL) | |
| 63 | 72 | |
| 64 | - # Convert Fossil wiki list syntax: lines starting with " * " to <ul><li> | |
| 73 | + # Convert Fossil wiki list syntax: * bullets and # enumeration | |
| 65 | 74 | lines = content.split("\n") |
| 66 | 75 | result = [] |
| 67 | 76 | in_list = False |
| 77 | + list_type = "ul" | |
| 68 | 78 | for line in lines: |
| 69 | 79 | stripped_line = line.strip() |
| 70 | - if re.match(r"^\*\s", stripped_line) or re.match(r"^\d+\.\s", stripped_line): | |
| 80 | + is_bullet = re.match(r"^\*\s", stripped_line) | |
| 81 | + is_enum = re.match(r"^#\s", stripped_line) or re.match(r"^\d+[\.\)]\s", stripped_line) | |
| 82 | + if is_bullet or is_enum: | |
| 83 | + new_type = "ol" if is_enum else "ul" | |
| 71 | 84 | if not in_list: |
| 72 | - result.append("<ul>") | |
| 85 | + list_type = new_type | |
| 86 | + result.append(f"<{list_type}>") | |
| 73 | 87 | in_list = True |
| 74 | - item_text = re.sub(r"^[\*\d+\.]\s*", "", stripped_line) | |
| 88 | + elif new_type != list_type: | |
| 89 | + result.append(f"</{list_type}>") | |
| 90 | + list_type = new_type | |
| 91 | + result.append(f"<{list_type}>") | |
| 92 | + item_text = re.sub(r"^[\*#\d+\.\)]\s*", "", stripped_line) | |
| 75 | 93 | result.append(f"<li>{item_text}</li>") |
| 76 | 94 | else: |
| 77 | 95 | if in_list: |
| 78 | - result.append("</ul>") | |
| 96 | + result.append(f"</{list_type}>") | |
| 79 | 97 | in_list = False |
| 80 | 98 | result.append(line) |
| 81 | 99 | if in_list: |
| 82 | - result.append("</ul>") | |
| 100 | + result.append(f"</{list_type}>") | |
| 83 | 101 | |
| 84 | 102 | content = "\n".join(result) |
| 85 | 103 | |
| 86 | 104 | # Wrap bare text blocks in <p> tags (lines not inside HTML tags) |
| 87 | 105 | content = re.sub(r"\n\n(?!<)", "\n\n<p>", content) |
| 88 | 106 |
| --- fossil/views.py | |
| +++ fossil/views.py | |
| @@ -57,31 +57,49 @@ | |
| 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) |
| 63 | |
| 64 | # Convert Fossil wiki list syntax: lines starting with " * " to <ul><li> |
| 65 | lines = content.split("\n") |
| 66 | result = [] |
| 67 | in_list = False |
| 68 | for line in lines: |
| 69 | stripped_line = line.strip() |
| 70 | if re.match(r"^\*\s", stripped_line) or re.match(r"^\d+\.\s", stripped_line): |
| 71 | if not in_list: |
| 72 | result.append("<ul>") |
| 73 | in_list = True |
| 74 | item_text = re.sub(r"^[\*\d+\.]\s*", "", stripped_line) |
| 75 | result.append(f"<li>{item_text}</li>") |
| 76 | else: |
| 77 | if in_list: |
| 78 | result.append("</ul>") |
| 79 | in_list = False |
| 80 | result.append(line) |
| 81 | if in_list: |
| 82 | result.append("</ul>") |
| 83 | |
| 84 | content = "\n".join(result) |
| 85 | |
| 86 | # Wrap bare text blocks in <p> tags (lines not inside HTML tags) |
| 87 | content = re.sub(r"\n\n(?!<)", "\n\n<p>", content) |
| 88 |
| --- fossil/views.py | |
| +++ fossil/views.py | |
| @@ -57,31 +57,49 @@ | |
| 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 | # Interwiki links: [wikipedia:Article] -> external link |
| 63 | content = re.sub(r"\[wikipedia:([^\]]+)\]", r'<a href="https://en.wikipedia.org/wiki/\1">\1</a>', content) |
| 64 | # Anchor links: [#anchor-name] -> local anchor |
| 65 | content = re.sub(r"\[#([^\]]+)\]", r'<a href="#\1">\1</a>', content) |
| 66 | # Bare wiki links: [PageName] (no pipe, not a URL) |
| 67 | content = re.sub(r"\[([A-Z][a-zA-Z0-9_]+)\]", r'<a href="\1">\1</a>', content) |
| 68 | # Verbatim blocks |
| 69 | content = re.sub(r"<verbatim>(.*?)</verbatim>", r"<pre><code>\1</code></pre>", content, flags=re.DOTALL) |
| 70 | # <nowiki> blocks — strip the tags, content passes through as-is |
| 71 | content = re.sub(r"<nowiki>(.*?)</nowiki>", r"\1", content, flags=re.DOTALL) |
| 72 | |
| 73 | # Convert Fossil wiki list syntax: * bullets and # enumeration |
| 74 | lines = content.split("\n") |
| 75 | result = [] |
| 76 | in_list = False |
| 77 | list_type = "ul" |
| 78 | for line in lines: |
| 79 | stripped_line = line.strip() |
| 80 | is_bullet = re.match(r"^\*\s", stripped_line) |
| 81 | is_enum = re.match(r"^#\s", stripped_line) or re.match(r"^\d+[\.\)]\s", stripped_line) |
| 82 | if is_bullet or is_enum: |
| 83 | new_type = "ol" if is_enum else "ul" |
| 84 | if not in_list: |
| 85 | list_type = new_type |
| 86 | result.append(f"<{list_type}>") |
| 87 | in_list = True |
| 88 | elif new_type != list_type: |
| 89 | result.append(f"</{list_type}>") |
| 90 | list_type = new_type |
| 91 | result.append(f"<{list_type}>") |
| 92 | item_text = re.sub(r"^[\*#\d+\.\)]\s*", "", stripped_line) |
| 93 | result.append(f"<li>{item_text}</li>") |
| 94 | else: |
| 95 | if in_list: |
| 96 | result.append(f"</{list_type}>") |
| 97 | in_list = False |
| 98 | result.append(line) |
| 99 | if in_list: |
| 100 | result.append(f"</{list_type}>") |
| 101 | |
| 102 | content = "\n".join(result) |
| 103 | |
| 104 | # Wrap bare text blocks in <p> tags (lines not inside HTML tags) |
| 105 | content = re.sub(r"\n\n(?!<)", "\n\n<p>", content) |
| 106 |