Repository navigation
✨ sphinx-codelinks: multi-line needs in source comments (@need … @endneed); the never-rendered @rst blocks are removed - #2152
Merged
Conversation
…shared corpus contract The pins for the multi-line needs the next commit builds, red by construction until then (the configuration and the record do not exist yet): - tests/data/extraction/multiline_needs.yaml, the shared extraction corpus file ubCode copies byte for byte: every language, the grammar's refusals, the precedence over one-line markers and @need-ids references, one libclang case. It replaces marked_rst.yaml, whose two snapshots go with it. - The normalized contract's marked_rst key becomes multiline_needs, so every existing extraction snapshot changes by that one key and nothing else. - Unit tests for run formation, prefix stripping and the block grammar; the configuration, the CLI payload and warning shape, the removed-key message, write rst ignoring the new records. - test_extract_rst and test_remove_leading_sequences go with their functions.
…move the never-rendered @rst blocks A need can now be written across the lines of a comment, and the analyser parses it before any markup parser runs: @need[<markup>] <type>: <title> :<key>: <value> <body in the declared markup> @endneed - analyse/multiline_parser.py forms comment runs (one block comment or docstring, or consecutive line comments with one delimiter and nothing but whitespace before them), strips prefixes by the comment's kind (the doxygen leader only when every line has one, so *emphasis* survives), and parses the block grammar, with six multiline_need_* refusals and notes. - The rows a block claims are blanked before the one-line and @need-ids extractors see the comment, so no line of a block is a one-line need or a reference. - MultilineNeed is a plain JSON record: the need (option values as directive strings, content, doctype), the resolved markup, and a source with a root-relative POSIX path, the open/close/body positions, option lines and scope. codelinks analyse writes it to marked_content.json. - get_multiline_needs and [analyse.multiline_needs] (start_sequence, end_sequence, default_markup, markups) replace get_rst and [analyse.marked_rst], which are refused with a message naming the cure. - SourceAnalyse.warnings holds one-line and multi-line warnings alike, with oneline_warnings kept as a read-only alias; the CLI prints both in one shape. - write rst ignores the new records. The src-trace directive is untouched: it renders multi-line needs in a following release.
… names the new module The Unreleased bullet says what an author writes, what codelinks analyse writes, that src-trace renders the needs in a following release, and what went: the @rst blocks, get_rst, [analyse.marked_rst], the rst records and the oneline_warnings name (kept as an alias for one release). AGENTS.md lists analyse/multiline_parser.py in the package tree, and the three places that named the removed MarkedRst, MarkedRstConfig and @rst blocks now name what replaced them.
…, one-line form, indented escape, markers when off, Rust rows) Red against the current code, each for its own reason: - a ` ***/` closer and a `/*****` banner are delimiter-only rows, dropped before the leader test (today the leader is not stripped and the need is lost); - a refused one-line form claims its own row (today it claims nothing); - the escape is tested on the indentation-stripped line, indentation kept (today an indented `\@endneed` keeps its backslash); - the two multi-line words join the marker check only when the feature is on (today `markers = ["@need"]` fails with the feature off); - a Rust `///` node spans its own row only, so the row above the open line neither holds claimed rows nor stands for the block.
…d leaders, one-line form, indented escape, markers when off The rules that change, each written into the corpus header and the docs page: - A block comment's first row holding only its opener (any stars, or /*!) and last row holding only its closer (stars then */) are delimiter rows, dropped before the leader test: a ` ***/` closer or a `/*****` banner no longer loses its need silently. `/*!` is stripped like `/**`. - When a block comment's lines do not all carry the `*` leader, an open word behind a star is multiline_need_header at its line, naming the cause, instead of nothing at all. - A refused one-line form claims its own row, so the one-line parser neither mints a need from it nor warns twice; it needs the close WORD at the line end (`foo@endneed` is no close). - The escape is tested past a body line's indentation, and the indentation is kept: the close word can be shown inside an indented literal block. - The two multi-line words join the marker mutual-exclusion check only when get_multiline_needs is on. - A Rust `///` node spans one row, so a record's comment is the open line's. Corpus: five new cases (option_lines_indented, reserved_option_keys, banner_block_comment, mixed_leaders_refused, libclang_trailing_comment_not_in_run); oneline_form_refused and escaped_close extended; the header states the lenient option indentation and its blank-line consequence, the whitespace and tag-matching rules, TAB indentation, and that a Python run is any bare string statement. Fences: a libclang record's column, a src_dir-rooted record, CRLF input, character columns past non-ASCII. test_analyse pins source.path against src_dir. Cleanups: the unused tests/data/analyse/marked_content.json, stale @rst text in two tests, the online_comment_style typo in convert_analyse_config.
…nterminated hint, and stars-only leader lines Red against the current code: - an open word behind a star in a block whose lines do not all carry the leader is consumed up to the first close line, which may carry the stars (today it hides nothing, so a `* @PARAM a, b` line still feeds the one-line parser); - with no close, it is multiline_need_unterminated naming the leader as the likely cause, as is an open on the opener's row of such a block; - a line of stars only inside a leader block counts as a leader line and becomes a blank line (today it makes the whole block leaderless).
…s-only separators are leader lines - In a block comment whose lines do not all carry the `*` leader, an open word behind a star is a multiline_need_header refusal that now consumes its lines up to the first close line, which may carry the stars too (`* @endneed`), as every other header refusal does: a `* @PARAM a, b` line in it no longer makes a one-line need, nor a `* @need-ids:` line a reference. Without such a close it is multiline_need_unterminated, and in such a block every unterminated message names the missing leader as the likely cause. - A line of stars only inside a leader block counts as a leader line and is read as a blank line, so a doxygen separator no longer makes the whole block leaderless. - Corpus header and docs say so; mixed_leaders_refused gains the two lines it now hides, cpp_doxygen_leader a separator in its body (both snapshots unchanged). Docs: the RST-bullet false positive and its cure; "whitespace" where any whitespace is accepted.
…red one-line form Red against the current code: - the unterminated message's leader hint is for a block with at least one leader line, not a plain block with no star (today every leaderless block gets it); - in a leaderless block, ` * @need req: A @endneed` is the one-line form, as everywhere else (today: unterminated).
…one-line form is the one-line form - CommentRun.mixed_leaders: a leaderless block in which some lines do carry the `*` leader. Only there does an unterminated message name the missing leader as the likely cause; a plain block with no star gets the plain message. - In a leaderless block, an open word behind stars whose line ends with the close word is refused as the one-line form and claims its row, as an open line is anywhere else: the refusal order is the same in both modes. The corpus header says so. - _STARS's docstring is back under _STARS (it had become a stray string below _STARS_ONLY's).
…for mixed blocks only The unterminated message names the missing `*` leader only in a mixed block (at least one line carries the leader or is stars only); a plain block's message has no hint. The header already scoped it with the code; this states the plain-block half explicitly, in the header ubCode implements from and on the docs page.
…hot shape On top of #2128, each multiline_needs.yaml case's snapshot is the marked content as dumped (its multiline-need records, beside any one-line need or reference) plus the [warnings] sidecar. The 37 cases' 74 files are regenerated in that shape. Projected back to the earlier normalized form (need, markup, open/close/first body lines, warning kinds and lines), they state the same facts as before for all 87 cases. The corpus header's paragraph on the snapshot says so, and that ubCode projects each record to {need, markup, start_line, end_line, content_start_line} for its parity test; the contract above it is unchanged.
Documentation build overview
20 files changed ·
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2152 +/- ##
==========================================
+ Coverage 93.05% 93.23% +0.17%
==========================================
Files 138 139 +1
Lines 20313 20689 +376
==========================================
+ Hits 18903 19289 +386
+ Misses 1410 1400 -10
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
ubmarco
self-requested a review
October 8, 2026 13:43
ubmarco
approved these changes
Oct 8, 2026
ubmarco
left a comment
Member
There was a problem hiding this comment.
Good one, we finally have a solution. Thanks a lot.
You even thought about the escape A body line \@endneed is the escape.
5 tasks done
chrisjsewell
added a commit
that referenced
this pull request
Oct 8, 2026
…oc field tags in docstrings are not needs (#2129) ## What `packages/sphinx-codelinks`: the production defects that snapshotting the real extraction output (#2128) exposed, fixed. - **Marker positions are physical.** A one-line need's or `@need-ids` reference's `source_map` columns count the characters before it on its line. They used to count from the start of the comment, so an indented marker, a comment after code, or a comment after a non-ASCII character reported a column that was not its column in the file. Each comment records the column, in characters, at which its text starts, on both the tree-sitter and the libclang path; it is added on the text's first row, and later rows start at column 0. A comment's rows are split on line breaks only, so a form feed no longer starts a row. `marked_content.json` changes accordingly. - **A need reference's span starts at its first id**, not at the whitespace after `@need-ids:`, and ends after its last id (`NeedIdRef.start_column` / `end_column`). A marker with no id after it is no reference. - **A block comment's closing `*/` is not marker text**: `/* @need-ids: REQ_1 */` named `*/` as a second id, and `/* @t, IMPL_1, impl */` had the type `impl */`. A one-line need on a comment's last line — the row of a block comment's `*/` or of a docstring's closing quotes — is no longer dropped. - **A Python docstring is read as its content**, without its prefix letters and quotes, so a marker on a one-row docstring, `"""@t, IMPL_1, impl"""`, no longer has the type `impl"""`. - **A field-shaped tag line in a Python docstring is a `docstring_tag` warning, not a need.** A line warns, and yields no need, only when all hold: it is inside a docstring (any bare string statement directly in a module, class or function body; never a `#` comment, never another language); nothing alphanumeric precedes the one-line start sequence; a tag of `PYTHON_DOCSTRING_TAGS` follows it; and the tag has the Epydoc field shape `:|[ \t]+[^ \t:]+:` — `:` directly (`@return: text`), or spaces or tabs, one word without space, tab or `:`, and `:` (`@param a: text`, `@raise Exc: text`, `@param नाम: text`). Every other line goes to the one-line parser as before: `# @todo fix the parser, IMPL_TODO, impl` is a need, so is the docstring line `@todo fix the parser, IMPL_DS, impl`, and so is `@return : text, IMPL_RET, impl`. Doxygen's comma form (`@param a the first, thing`) stays the phantom need it is in C++. The warning says to choose a start sequence the docstrings do not use. "Alphanumeric" here is Python's own notion, which differs from ubCode's on a few characters (combining marks, for one): a documented non-goal. Closes #2154 ## Why Any tool that reads the columns of `marked_content.json` — an editor jumping to a marker, say — was sent to the wrong place for every indented marker and every need reference. In a build the links are line-based, so there only the order in which unknown ids on one line are reported changes. A project with Epydoc-style Python docstrings got phantom needs from its `@param a: x, y` lines with the default `@` start sequence. ## Review of this PR, addressed on the branch (taken over 2026-10-08 with Marco's go) Rebased onto master (after #2128 and #2152): Marco's two commits replayed, the one conflict (`oneline_parser.py`: the warning kinds now live in `models.py`) resolved in place, and the one multi-line corpus case that holds `@need-ids` references re-captured at the new spans. Then: - `docstring_tag` is narrowed to the rule above. As first built it fired on every comment of a Python file and on any listed tag word, so `# @todo fix the parser, IMPL_TODO, impl` — a valid one-line need — and any docstring line opening with a listed tag word, such as `@todo fix the parser, IMPL_DS, impl`, became a warning instead of a need. A field-shaped docstring line such as `@return: x` still warns, by design. The field word is spelled so that this package and ubCode read it the same way in every script: a parameter named in Devanagari is a field word on both sides. The pins came first and were red against each earlier version. - A docstring is scanned as its content, the last line of a comment counts as terminated, a block comment's `*/` is not marker text, a comment's rows are split on `\n` only, and an empty `@need-ids:` is no reference — each the rule ubCode applies, each with a shared case. - The corpus README's two "known production quirks" are fixed by this PR and are gone; what remains is a rule of the corpus: no case puts two entries of one kind on one row (#2150). Its JSON example, and the examples in `docs/components/analyse.rst` (measured with `codelinks analyse`) and `write.rst`, show physical columns. - Nothing pinned the libclang column (setting it to 0 left the suite green); a test now checks that both engines report the same physical columns, with LF, CRLF and CR line endings, including a one-row block-comment need, a need on a `*/` row, a form feed and an empty marker. Found on the way, pre-existing and not changed here: production orders two entries of one kind on one row by tree-sitter's capture order (#2150); the scope walk can bind a marker to a declaration further down the file, past a decorated `def` or a `template <…>` too (#2151); a non-ASCII character before an inline YAML comment loses the comment (#2155). ## ubCode useblocks/ubcode#3014 implements every rule here — physical columns, reference spans from the first id, docstring content, terminated last lines, the `*/` rule, `\n`-only rows, no empty references, and the same `docstring_tag` (same tag list, the same field-shape pattern, docstrings only) — and re-vendors the corpus from master after this merges. Each tool keeps its own notion of "alphanumeric" before the start sequence and of the whitespace between `@need-ids:` ids (they differ on a few characters); the corpus says so and pins neither. ## Tests - `positions.yaml` (11 cases, new): indentation; code, or a non-ASCII character, before the comment; a non-ASCII title and id; trailing whitespace in a block comment; a one-line need and a reference on one row; a one-row block-comment need; a need on a block comment's `*/` row; a form feed inside a line. - `docstrings.yaml` (13 cases, new; its header states the rule): trailing whitespace; a docstring statement holding a second string; field-shaped tags warn (`@param a: the first, thing`, `@return: nothing`, a Devanagari parameter name, an `r"""…"""` one-row docstring); a tag word without the field shape, `@return : text`, `@Todo`, and a `#` comment's tag line (field-shaped or not) are needs; one-row docstrings' need and reference lose the quotes; a need on the closing-quote row. - `need_refs.yaml` gains `every_extractor`, `block_comment`, `space_separated_ids`, `marker_after_text`, `empty_marker_is_no_reference`, `line_comment_ending_in_closer`. - Unit: `docstring_tag()`'s field shape (26 rows, the same table as ubCode's); a field-shaped tag line in a `#` comment goes to the one-line parser; libclang columns equal tree-sitter columns (LF, CRLF, CR). `test_need_id_ref_record` expects the span from the first id. - `uv run poe test-codelinks`: **857 passed, 0 skipped** (the sphinx-7 cell too); `uv run poe docs-codelinks` builds with no warning. ## Checklist - [x] Every line of this change has been read: Marco's commits, the reviews and validations of 2026-10-08, and the takeover's line-by-line pass; it was not generated automatically from an issue. - [x] I ran the package's tests (`uv run poe test-<package>`) and they pass. - [x] Documentation is updated where behaviour or options change. - [x] The package's `docs/changelog.rst` has an entry under *Unreleased*. - [x] `uv run poe lint` and `uv run poe typecheck` pass. --------- Co-authored-by: Chris Sewell <chrisj_sewell@hotmail.com>
chrisjsewell
added a commit
that referenced
this pull request
Oct 9, 2026
…dy is parsed in its declared markup (#2168) ## What `src-trace` now creates a need from every multi-line need (`@need[<markup>] <type>: <title>` … `@endneed`) in the files it traces, with its body parsed in the markup the block declares, whatever the page's. - One-line and multi-line needs are one list, in the order the analysis already gives one-line needs (a block by its open line), and take the same steps: the id (written, or generated by Sphinx-Needs), one need per id (the earlier line keeps it whatever the kinds; `codelinks.duplicate_need` names the kind skipped), local/remote URL and the source page's `[docs]` link — at the open line. - Each block is one `ingest_need_record` call (on the unclaimed-markup fallback a second, in the page's own suffix, and a third, with no markup, when no parser claims that suffix either): its body as content in the markup its `doctype` names, anchored at the body's line in the source file (`content_source`; not for a block rendered through a template or with `jinja_content`, whose rendered text Sphinx-Needs anchors at the need), the need recorded at the directive (`docname`/`lineno`), as a one-line need is. - A block's options are the need directive's (core options + the project's extra and link fields), converted as `.. req::` converts them -- keys lowercased as docutils lowercases them, links and tags split, typed fields coerced, `jinja_content` a flag; an option naming one of the block's own keys (`:Type:`, `:Content:`) is ignored with a warning, the block's value standing; any other key (`parts`, `docname`, `arch`, and the directive's own `delete` and `title_from_content`) is ignored with a warning at its line; an empty `:id:` is refused with the directive's message (`invalid_option:`). This also keeps a `:parts:` option away from the record-seam crash #2165. - `needimport`'s two rules: a block rendered through a template (its own, or the project's default/predicate) is parsed as the page's markup; a markup no parser claims falls back to the page's markup -- still anchored at the body's source line, unless the page's own parser is neither reStructuredText nor MyST, when the body is parsed by that parser and its warnings sit at the page --, `doctype` kept, one warning per directive and markup (none for a hidden or blank block). - Every warning about a block's own content or options — the analyser's included — is `codelinks.multiline_need`; a skipped duplicate shares `codelinks.duplicate_need` with one-line needs; the fallback and floor warnings sit at the directive. - `ingest_need_record` is Sphinx-Needs 9.0.0's; the floor stays `>=8.5.0,<9` and the import is guarded: without it a directive warns once and creates its one-line needs (removed at the 9.0.0 floor bump, with `_make_hashed_id`). - Reference side (the first five commits, splittable, with the fix round's `eaff9a4f` + `20a5f6d7` + `94e712b3` for the `get_rst` warning's count, text and project name, and the non-empty suffix): the shared corpus header now says ubCode compares records in production shape; `<type>` and option keys are ASCII word characters on both sides (`[A-Za-z0-9_-]+`; Python's and Rust's `\w` differ on combining marks) with two shared cases; 13 shared cases for rules no case pinned; a `markups` suffix must not be empty; `get_rst = false` alone is ignored with one warning per project, naming it -- at configuration time in a build, per analysed project in the CLI -- saying it is no longer read and can be removed (a configuration scaffold wrote it into shared `ubproject.toml` files). ## Why #1885 asked whether marked-rst blocks rendered: they never did — `get_rst` extracted them and `src-trace` ignored them. Their replacement, multi-line needs (#2152), was extracted and dropped the same way, waiting for Sphinx-Needs to parse a need's content in its declared markup (#2135, `add_need(content=MarkupContent(…))` / `ingest_need_record`). This renders them. The docs gain the example #1885 asked for (a traced file with one block) and list what a body should not contain until #2130 refuses it. ## ubCode parity Measured on this branch (Sphinx 9.1 / myst-parser 5.1; Sphinx 7.4 / myst-parser 4.0 where noted); ubCode side = ubcode#3915's rulings. | behaviour | ubCode (#3915) | Sphinx (this PR) | register row? | |---|---|---|---| | body markup | the block's `doctype` via `[parse.parsers.*]` | the block's `doctype`, exact `source_suffix` key, always | residual: exact suffix vs case-insensitive longest match (clb2's row) | | unclaimed doctype | warn once per source, host parser | warn once per directive per markup at the directive, page parser, `doctype` kept | cadence (clb2's row) | | templated block | — | parsed as the page's markup (template and body) | yes, if ubCode differs | | nested need directive in a body | fenced | not fenced (a nested need is created, recorded at the page) | yes — until #2130 item 3 | | file-reading directives / raw / images | denied (c2) / off (c1) | not refused; `include` resolves against the page | yes — until #2130 items 1–2 | | host lexical environment | none | the page's substitutions and `rst_prolog` | existing needimport row | | labels in a body | register with the page | register with the page; duplicate → Sphinx's `duplicate label` at the source line | none | | diagnostics anchoring | source file + line | docutils/Sphinx messages at source file + line (absolute path), the unclaimed-markup fallback included (not on a page of a third parser); a body rendered through a template or with `jinja_content` at the need (page, directive line — Sphinx-Needs anchors rendered text there); myst-parser's own at the page + line (myst 5) / `<source>.rst` (myst 4) | yes for myst-logged — until #2130 item 4; yes for templated/Jinja bodies | | need location | directive's | `docname` = page, `lineno` = directive line | none | | `doctype` | block's | block's | none | | option conversion | "exactly as a directive's options" | the directive's option set and conversion; option keys are lowercased as docutils does (ubCode #3915 mirrors); other keys warned at their line, need kept; empty `:id:` refused | none | | unknown type / unknown option | open line / option line | open line (`invalid_type`) / option line | none (texts differ) | | duplicate id | (not stated) | one need per id, earlier `(row, file, column)` wins, warning at the skipped one's line naming its kind | check when #3915 lands | | URL / source link | line start | open line for `local-url`, `remote-url`, `[docs]` | none | | `<type>` / option key class | same as ubCode's producer branch (#3914, in review) | ASCII `[A-Za-z0-9_-]+` (shared cases) | none once #3914 merges | ## Tests - `tests/test_multiline_needs_render.py` (40 cases): the four page×body cells (HTML + `needs.json`), body diagnostics (OS separators), the option table (19 rows) + empty id + upper-case id + unknown type, duplicates across kinds + generated id, card order (two cases), `-j 2`, incremental, analyser subtype, unclaimed-markup fallback with its source anchor on an RST and a MyST page and on a page of a third parser, three template routes, the id predicted with `:full_title:`, the floor guard (with and without blocks). - 15 new shared extraction cases (30 snapshots); `get_rst = false` cases in `test_analyse_config.py` (converter silent, a two-project build warning twice fresh and incremental and naming each project, an empty `markups` suffix, `get_rst = 0` refused) and `test_cmd.py` (every project / `-p`). - No existing test or snapshot changed. 25 mutations, each red against a named pin, plus reviewers' 41 and the loop-ender's 4. - `poe test-codelinks`: 941 passed, 0 skipped (sphinx-9 and sphinx-7 cells); `poe import-check-codelinks` green at sphinx-needs 8.5.0; `poe docs-codelinks` 0 warnings. Closes #1885
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A need can now be written across the lines of a source comment, and
codelinks analyseextracts it:@need[<markup>] <type>: <title>, then:key: valueoptions (a deeper-indented line continues a value), then the body in the declared markup (dedented, never re-flowed),
then
@endneedalone on its line.@need[md]declares Markdown; the default is the project'sdefault_markup(
rst). A body line\@endneedis the escape.//,///,//!,#) with one delimiter and only whitespace before them — every supported language, and under the libclangengine too (a block in an inactive
#ifbranch is not extracted).doxygen
*leader only when every line carries one, so*emphasis*survives (the old@rststripping cut through thefirst
*on any line); banner rows (/*****,***/) are delimiters, and a block whose leader is not stripped reportsan open word behind a star instead of losing it.
@need-ids:references.multiline_need_header,_unterminated,_oneline_form,_markup,_duplicate_option,_nested_open.marked_content.jsongets one"type": "multiline-need"record per need —need(option values as thedirective strings written,
content,doctype), the resolvedmarkup, and asourcewith a root-relative POSIXpath,
root,commit, the open/close/body positions (1-based lines, 0-based columns), option lines and scope.get_multiline_needsand[analyse.multiline_needs](start_sequence,end_sequence,default_markup,markups).tests/data/extraction/multiline_needs.yaml, 37 cases, the shared contract; each case's snapshot is the record asdumped plus a warnings sidecar, the shape 🧪 sphinx-codelinks: snapshot the real extraction output, with marker scope association (moved from sphinx-codelinks#100) #2128 introduced; ubCode compares it verbatim (useblocks/ubcode#3014).
@rst … @endrstblocks,get_rst,[analyse.marked_rst](refused with a message naming thereplacement), the
"rst"records.SourceAnalyse.oneline_warningsis nowwarnings(old name kept as an alias forone release); the CLI prints every analyse warning in one shape.
write rstignores the new records.Why
#1885: the
@rstblocks were documented as a way to write needs in comments, but nothing ever rendered them, and theirextraction only worked inside one
/* */comment and corrupted*emphasis*. #1898 decision 1 settled the design: oneneed per comment block, a markup-neutral header parsed by codelinks, the body's markup declared per block — so the
record exists before any parser runs and the consumers treat it as a need imported through
needimport.The branch is built on #2128 (merged as
2f40ce27).Not yet
The
src-tracedirective does not render these needs: it does so in the next sphinx-codelinks slice, oncesphinx-needs 9.0.0 is released with #2135 (the step that parses a need's content in its declared markup); sphinx-codelinks
pins
sphinx-needs<9today. Until then a project that switches the extraction on gets the records fromcodelinks analyseonly.A
//!inner doc run binds to the next item's scope, as a one-line marker does (#2128's scope rule); the consumer slice decideswhether an inner doc comment should bind to its enclosing module instead.
Found on the way, all pre-existing: a stale extraction snapshot is invisible to
--snapshot-warn-unused(#2146); therow order of the snapshotted records (#2150) and the scope walk (#2151), both found in #2128's review.
ubCode
ubCode's producer is useblocks/ubcode#3914:
parse_marked_rstis a placeholder there (rust/ubc_codelinks/src/markers/rst.rs).It copies
packages/sphinx-codelinks/tests/data/extraction/multiline_needs.yamlbyte for byte intorust/ubc_codelinks/tests/fixtures/extraction/and adds it tosync_codelinks_expected.py'sSHARED_FIXTURES. Since #2128 eachsnapshot is the record as dumped, and useblocks/ubcode#3014 (Marco's open PR, being taken over) has ubCode compare that
production shape verbatim — so #3914 emits the full
multiline-needrecord (ruled on that issue today; the earlier five-keyprojection, useblocks/ubcode#3928, is withdrawn with #3014), and until #3914 lands
multiline_needs.yamlis listed asreference-only in ubCode's sync. Until
#3914 lands, ubCode's
deny_unknown_fieldsanalyse config rejects a sharedubproject.tomlthat setsget_multiline_needs.Tests
indentation, reserved keys, banner comments, mixed leaders, dedent, runs broken by code, by a delimiter change and
by a trailing comment, custom words and markups); precedence with the one-line and reference extractors on; two
libclang cases (active/inactive branches, a trailing comment).
default_markupmust be a key ofmarkups, the markers join the mutual-exclusion check when the feature is on, not forced on); the CLI record, warning shape and removed-keymessage;
write rstignoring the records.poe test-codelinks(0 skipped), the sphinx-7 cell,poe docs-codelinksunder-nW.Refs #1885
Refs #1898
Refs #1982
The header-first split follows the closed PR useblocks/sphinx-codelinks#66; the analysis-layer record and the
host-markup problem were first raised in #44 and #99.