From 3170afc88ef0bd45a72f39fc04447410d17515a3 Mon Sep 17 00:00:00 2001 From: Patrick Dahlke Date: Fri, 2 Oct 2026 10:32:11 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20sphinx-codelinks:=20report=20a?= =?UTF-8?q?=20marked-rst=20block=20on=20the=20row=20of=20its=20start=20mar?= =?UTF-8?q?ker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit extract_rst took the row of `@rst` as `len(text[:start_idx].splitlines())`, which counts a trailing partial line too. Any text before the marker on its own line -- a one-line `// @rst ... @endrst`, or a doxygen ` * ` prefix -- put the block one row too low. Count newlines instead. Three snapshots carried the wrong row and are corrected, and test_extract_rst now asserts the row it already listed. Closes #1982 --- packages/sphinx-codelinks/docs/changelog.rst | 11 +++++++++++ .../src/sphinx_codelinks/analyse/utils.py | 4 +++- .../test_analyse[src_dir0-src_paths0].anchors.json | 4 ++-- ...on_fixture[marked_rst-multi_line_rst_doxygen].json | 4 ++-- ...xtraction_fixture[marked_rst-single_line_rst].json | 4 ++-- .../tests/test_analyse_long_source.py | 4 +--- packages/sphinx-codelinks/tests/test_analyse_utils.py | 11 ++++++++++- 7 files changed, 31 insertions(+), 11 deletions(-) diff --git a/packages/sphinx-codelinks/docs/changelog.rst b/packages/sphinx-codelinks/docs/changelog.rst index c16116b6e..75a798af1 100644 --- a/packages/sphinx-codelinks/docs/changelog.rst +++ b/packages/sphinx-codelinks/docs/changelog.rst @@ -59,6 +59,17 @@ New and Improved Sphinx-Needs workspace declares and type-checks against. There is no upper bound: Sphinx caps docutils per series itself. +- 🐛 A marked-rst block with text before its start marker on the same line was reported + one row too low + (`#1982 `__). + + The row of ``@rst`` was taken as the number of lines before it, and ``splitlines()`` + counts a partial line too -- so a one-line block such as ``// @rst … @endrst``, or an + ``@rst`` behind a doxygen ``*`` prefix, landed on the row below. The row is now the + number of newlines before the marker. The source maps in ``marked_content.json`` and the + blob links built from them move up by one row for such blocks; a block whose ``@rst`` + starts its own line is unaffected. + - 🐛 ``src_trace_projects`` declared directly in ``conf.py`` are normalised before ``src-trace`` reads them. diff --git a/packages/sphinx-codelinks/src/sphinx_codelinks/analyse/utils.py b/packages/sphinx-codelinks/src/sphinx_codelinks/analyse/utils.py index 35c617ea0..8f5f5b881 100644 --- a/packages/sphinx-codelinks/src/sphinx_codelinks/analyse/utils.py +++ b/packages/sphinx-codelinks/src/sphinx_codelinks/analyse/utils.py @@ -570,7 +570,9 @@ def extract_rst( if start_idx == -1 or end_idx == -1: return None rst_text = text[start_idx + len(start_marker) : end_idx] - row_offset = len(text[:start_idx].splitlines()) + # count newlines, not lines: text before a one-line block on the comment's first + # row holds no newline, yet splitlines() would count it as one line + row_offset = text[:start_idx].count(UNIX_NEWLINE) if not rst_text.strip(): # empty string is out of the interest return None diff --git a/packages/sphinx-codelinks/tests/__snapshots__/test_analyse/test_analyse[src_dir0-src_paths0].anchors.json b/packages/sphinx-codelinks/tests/__snapshots__/test_analyse/test_analyse[src_dir0-src_paths0].anchors.json index 1904a625d..b536d61ff 100644 --- a/packages/sphinx-codelinks/tests/__snapshots__/test_analyse/test_analyse[src_dir0-src_paths0].anchors.json +++ b/packages/sphinx-codelinks/tests/__snapshots__/test_analyse/test_analyse[src_dir0-src_paths0].anchors.json @@ -21,11 +21,11 @@ "remote_url": null, "source_map": { "start": { - "row": 13, + "row": 12, "column": 7 }, "end": { - "row": 13, + "row": 12, "column": 40 } }, diff --git a/packages/sphinx-codelinks/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[marked_rst-multi_line_rst_doxygen].json b/packages/sphinx-codelinks/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[marked_rst-multi_line_rst_doxygen].json index 52dc46030..9fcfdc19d 100644 --- a/packages/sphinx-codelinks/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[marked_rst-multi_line_rst_doxygen].json +++ b/packages/sphinx-codelinks/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[marked_rst-multi_line_rst_doxygen].json @@ -4,8 +4,8 @@ "marked_rst": [ { "content": " .. impl:: Multi\n :id: M_1\n\n Body line.\n ", - "start_line": 3, - "end_line": 3 + "start_line": 2, + "end_line": 2 } ], "warnings": [] diff --git a/packages/sphinx-codelinks/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[marked_rst-single_line_rst].json b/packages/sphinx-codelinks/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[marked_rst-single_line_rst].json index 63dc6db85..0904ede42 100644 --- a/packages/sphinx-codelinks/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[marked_rst-single_line_rst].json +++ b/packages/sphinx-codelinks/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[marked_rst-single_line_rst].json @@ -4,8 +4,8 @@ "marked_rst": [ { "content": " .. impl:: X\\n :id: Y\\n", - "start_line": 2, - "end_line": 2 + "start_line": 1, + "end_line": 1 } ], "warnings": [] diff --git a/packages/sphinx-codelinks/tests/test_analyse_long_source.py b/packages/sphinx-codelinks/tests/test_analyse_long_source.py index 367eaea0b..24ecc13dc 100644 --- a/packages/sphinx-codelinks/tests/test_analyse_long_source.py +++ b/packages/sphinx-codelinks/tests/test_analyse_long_source.py @@ -102,9 +102,7 @@ def _cpp_source() -> tuple[str, list[Marker]]: expected.append(Marker("need-id-refs", len(lines), f"REQ_CPP_{i}")) lines.append(f" @need-ids: REQ_CPP_{i}") lines.append("*/") - # a marked-rst block, reported at the row of its start sequence. It sits on a line - # of its own: with text before it on that line, extract_rst reports the row - # below, which is a separate matter from the one this module fences + # a marked-rst block, reported at the row of its start sequence lines.append("/*") expected.append(Marker("rst", len(lines), f"rst_{i}")) lines.append("@rst") diff --git a/packages/sphinx-codelinks/tests/test_analyse_utils.py b/packages/sphinx-codelinks/tests/test_analyse_utils.py index 921b6df00..d7aaf2959 100644 --- a/packages/sphinx-codelinks/tests/test_analyse_utils.py +++ b/packages/sphinx-codelinks/tests/test_analyse_utils.py @@ -1339,7 +1339,15 @@ def test_remove_leading_sequences(text, leading_sequences, result): """, ["@rst", "@endrst"], """.. impl:: oneline rst text""", - {"row_offset": 0, "start_idx": 5, "end_idx": 31}, + {"row_offset": 1, "start_idx": 5, "end_idx": 31}, + ), + ( + # a one-line block after a comment prefix: the text before the start + # marker holds no newline, so the block is on the comment's first row + """// @rst .. impl:: oneline rst text @endrst""", + ["@rst", "@endrst"], + """ .. impl:: oneline rst text """, + {"row_offset": 0, "start_idx": 7, "end_idx": 35}, ), ], ) @@ -1347,6 +1355,7 @@ def test_extract_rst(text, rst_markers, rst_text, positions): extracted_rst = utils.extract_rst(text, rst_markers[0], rst_markers[1]) assert extracted_rst is not None assert extracted_rst["rst_text"] == rst_text + assert extracted_rst["row_offset"] == positions["row_offset"] assert extracted_rst["start_idx"] == positions["start_idx"] assert extracted_rst["end_idx"] == positions["end_idx"]