From 000cf8e8c2a9440ce73ad246c7d5aa79e7987796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Milde?= Date: Wed, 1 Oct 2025 15:16:25 +0200 Subject: [PATCH 1/2] Add test cases for hyperlinks to sections in reStructuredText. Currently, internal links to sections fail for the rST converter. Add test cases for what needs to be fixed. The expected results were determined/tested with a test document in a wiki instance. --- src/moin/converters/_tests/test_rst_in.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/moin/converters/_tests/test_rst_in.py b/src/moin/converters/_tests/test_rst_in.py index 5e8913b5c..ff6b0a1be 100644 --- a/src/moin/converters/_tests/test_rst_in.py +++ b/src/moin/converters/_tests/test_rst_in.py @@ -226,6 +226,23 @@ def test_field_list(self, input, output): "`Whitespace is\nnormalized & Case is KEPT.`_", '

Whitespace is\nnormalized & Case is KEPT.

', ), + ( # in rST, matching the reference text is case insensitive: + "Chapter 1\n===============\n\nA reference to `chapter 1`_.\n", + 'Chapter 1

A reference to chapter 1.

', + ), + ( # check handling of non-ASCII chars: + "τίτλος\n^^^^^^\n\nA reference to `τίτλος`_.\n", + 'τίτλος

A reference to τίτλος.

', + ), + ( + "§ With % strange & siLLY \n" + "--------------------------------\n\n" + "Reference to `§ With % strange\n" + "& siLLY \\<title>`_.\n", + '<page><body><h outline-level="1">§ With % strange & siLLY <title></h>' + '<p>Reference to <a xlink:href="wiki.local:#A.2BAKc_With_.25_strange_.26_siLLY_.3Ctitle.3E">§ With % strange\n' + "& siLLY <title></a>.</p></body></page>", + ), ( "http://www.python.org/", '<page><body><p><a xlink:href="http://www.python.org/">http://www.python.org/</a></p></body></page>', From 69f545082a45302e1a1c168e6eab10d00cca8aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Milde?= <milde@users.sf.net> Date: Wed, 1 Oct 2025 17:27:29 +0200 Subject: [PATCH 2/2] rST converter: Fix handling of internal cross-references. The "refid" attribute works fine with explicit targets. IDs of section headings are generated by Moin using a different normalization function. Find the matching section element and create a refid "a la Moin". --- src/moin/converters/_tests/test_rst_in.py | 2 +- src/moin/converters/rst_in.py | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/moin/converters/_tests/test_rst_in.py b/src/moin/converters/_tests/test_rst_in.py index ff6b0a1be..c1ba52a48 100644 --- a/src/moin/converters/_tests/test_rst_in.py +++ b/src/moin/converters/_tests/test_rst_in.py @@ -232,7 +232,7 @@ def test_field_list(self, input, output): ), ( # check handling of non-ASCII chars: "τίτλος\n^^^^^^\n\nA reference to `τίτλος`_.\n", - '<page><body><h outline-level="1">τίτλος</h><p>A reference to <a xlink:href="wiki.local:#http://127.0.0.1:5000/rST-hyperlink-tests#A.2BA8QDrwPEA7sDvwPC-">τίτλος</a>.</p></body></page>', + '<page><body><h outline-level="1">τίτλος</h><p>A reference to <a xlink:href="wiki.local:#A.2BA8QDrwPEA7sDvwPC-">τίτλος</a>.</p></body></page>', ), ( "§ With % strange & siLLY <title>\n" diff --git a/src/moin/converters/rst_in.py b/src/moin/converters/rst_in.py index c3271fab6..992e774f6 100644 --- a/src/moin/converters/rst_in.py +++ b/src/moin/converters/rst_in.py @@ -31,7 +31,7 @@ from moin.utils.iri import Iri from moin.utils.tree import html, moin_page, xlink, xinclude from moin.utils.mime import Type, type_moin_document -from moin.wikiutil import normalize_pagename +from moin.wikiutil import anchor_name_from_text, normalize_pagename from . import default_registry from ._util import allowed_uri_scheme, decode_data, normalize_split_text @@ -581,9 +581,18 @@ def visit_reference(self, node): if not allowed_uri_scheme(refuri): self.visit_error(node) return - if refuri == "": - # build a link to a heading or an explicitly defined anchor - refuri = Iri(scheme="wiki.local", fragment=node.attributes["name"].replace(" ", "_")) + + if refuri == "" and "refid" in node: + # internal cross-links + refid = node["refid"] + target_node = node.document.ids[refid] + # "refid" works fine with explicit anchors but the IDs given to + # section headings use the normalization function from Moin, not Docutils. + if isinstance(target_node, nodes.section): + title = target_node[0] + refid = anchor_name_from_text(title.astext()) + refuri = Iri(scheme="wiki.local", fragment=refid) + if isinstance(refuri, str) and refuri.startswith("http"): if "://" not in refuri: refuri = refuri.split(":")[1] @@ -789,6 +798,7 @@ class Parser(docutils.parsers.rst.Parser): without matching target__. __ https://docutils.sourceforge.io/docs/api/transforms.html + __ https://docutils.sourceforge.io/docs/ref/doctree.html#target """ config_section = "MoinMoin parser"