Skip to content

Better lineno in needs JSON - #1692

Open
sebastiansetzer wants to merge 4 commits into
useblocks:masterfrom
sebastiansetzer:lineno_experiments
Open

Better lineno in needs JSON#1692
sebastiansetzer wants to merge 4 commits into
useblocks:masterfrom
sebastiansetzer:lineno_experiments

Conversation

@sebastiansetzer

Copy link
Copy Markdown

This should fix #1349.

The fix for the include and rst-prolog use cases is simple.

I just wonder if anyone relies on having unique line numbers for one "docname" and docname being a full rst document, not an include file. If that's the case, maybe instead of fixing the lineno attribute, we should add a new attribute (maybe call it "source position"?)
On the other hand, the only reason I can think of why you would not want include file names as "source" attribute is if you had one that you included in multiple places. And if you do that, you won't have needs in there, because those would then get duplicated.

The fix for the list2need use case is abusing the "source" string to pass line number information from the list2needs directive to the generated needs directives.
I considered passing this information through a generated "option", but when I declare this in needs_extra_options, it shows up for all needs (as "None" when unused) in the JSON, so that's a bit ugly.

I guess the best solution would be to refactor the list2need directive to create the needs objects directly, instead of generating rst text which is then injected back into the parser. This would avoid the need to pass line numbers, and it would even avoid calling state_machine.insert_input, so it wouldn't corrupt line numbers for subsequent needs.
Of course, corrupted line numbers of subsequent needs are already fixed by using state_machine.get_source_and_line, and you'd want to keep this for the include and rst-prolog use cases, so the important reason for the refactoring is just avoiding to encode line numbers in the source string.

Before attempting to do this refactoring, I would like to understand why the current solution with state_machine.insert_input was chosen. Directly creating the needs objects seems more obvious, so I wonder if I'm missing the reason for Chesterton's fence here.

My test setup is attached - not integrated into the sphinx-needs test suite so far.
rst-lineno-bug-tests.zip

This function converts the "virtual" lineno into a real line number
as it appears in the source file, even in the presence of code
injected with state_machine.insert_input.
The injected code has line numbers starting from 1 again,
and the returned "source" name is the name of an include file,
if that's where the injected code is from.

The list2need test is extended by one normal need, following the needs
created with list2needs. Previously, this test would have failed
because list2needs uses insert_input, messing up line numbers for
everything following later.
It is not used anymore, and the next commit will turn the commented out
line unusable because you need to iterate over each line to get
line numbers.
@sebastiansetzer
sebastiansetzer force-pushed the lineno_experiments branch 5 times, most recently from 5d7a6b7 to 6b24b65 Compare August 7, 2026 21:23
The line numbers are known in the list2need directive (A), but they are
output into needs in class NeedDirective later (B), when the test
inserted with state_machine.insert_input has been parsed.
Somehow they must be passed from A to B.
This is solved by encoding a mapping of wrong to correct line numbers
in the path string which gets passed from A to B already.

You can't simply put it into the lineno attribute, because there is
only one lineno attribute for all needs in the whole list2needs directive,
and that gets mangled by state_machine.insert_input.

You could maybe pass the information through a new global variable,
but I don't know how well that would play with parallel build. Are
global variables written at A available at B in parallel builds?

You can't split up the state_machine.insert_input into multiple ones,
one for each generated need, with the lineno tweaked to give the right
output, because then the parent-child hierarchy between the generated
needs gets lost - each state_machine.insert_input starts from the same
indentation level.

The cleanest solution maybe would be to get rid of
state_machine.insert_input alltogether and generate the needs directly.
But that would be a bigger change that I can't judge the implications for.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lineno gets shifted if rst_prolog is present

1 participant