Record node end positions (EndLine/EndColumn) - #387
reuvenharrison wants to merge 1 commit into
Conversation
Node reports where an element starts but not where it ends, so a consumer cannot extract a whole collection from its source. Given a node for an entire mapping, there is no way to know which line the mapping stops on. Add EndLine/EndColumn alongside Line/Column, populated from the parser event's end_mark: at construction for scalars and aliases, and for mappings and sequences from the last child's end, so the span reaches the end of the actual content. Two cases need care, and both have tests: A block collection's MAPPING-END/SEQUENCE-END token sits at the start of the line following the dedent, which overshoots the element. Taking the last child's end instead keeps the span tight. Empty collections have no last child and fall back to the token's mark. A flow collection closes with an explicit } or ], and there the END token's mark is just past the delimiter, which is exactly right. So flow uses the token and block uses the last child. Both are 1-based, like Line and Column, and both are zero when the source carried no position (a textless tree built in memory). end_position_test.go covers scalars, block and flow collections, nesting, block scalars, empty collections and aliases. TestNodeRoundtrip clears the new fields before comparing, since its expected literals predate them; end positions are covered by the new test rather than by duplicating every literal there.
013bea4 to
e0f6010
Compare
|
Rebased onto current Two things changed in the rebase, both in tests:
I checked the converted assertions still bite rather than merely passing: introducing an off-by-one in the block-collection end produces 16 distinct failures, and the suite is green with it reverted. |
|
Validated the port against the fork it came from, since "the tests pass" only shows the new tests agree with themselves. I walked both node trees in lockstep — kind, value,
The edge cases target the parts most likely to diverge: literal/folded/keep/strip block scalars, empty and nested flow collections, a file with no trailing newline, anchors and merge keys, multi-document streams, trailing and standalone comments, quoting and implicit types, and sequences of mappings. Also worth noting the corpora are not neutral: this branch is the source of a downstream implementation that has been slicing OpenAPI operations out of source files for months, and that consumer's own tests pass against the positions being compared here. So the agreement is between this PR and code with real usage behind it, not between two fresh implementations. Not claiming this proves correctness — a systematic error present in both would be invisible to a differential — but it rules out the port having dropped or altered anything in transit, which was the risk I could actually test. |
|
One thing in here is not purely additive, and it deserves calling out rather than leaving for review to find. Besides the two new fields, the change in That is necessary — otherwise a
Empirically the existing suite agrees: it passes unchanged, including the encode and roundtrip tests, which would notice if emitted output shifted. If you would rather not change the token at all, the alternative is to leave |
|
Thank you for working on this. It's a great addition. That said, I'm likely to reply the same thing as I did here: TL;DR; the v3 is considered stale and will recieve no runtime code changes except security fixes. Could you consider opening this in the |
|
Reopened against Worth flagging that #391 is not a straight port, because I also checked the port against this branch rather than only against its own tests, walking both node trees in lockstep over kind, value, |
|
Please close this one. We will review the other one. Be patient, everyone is busy with real life in summer |
Problem
Nodereports where an element starts (Line/Column) but not where it ends. Given a node for an entire mapping there is no way to know which line the mapping stops on, so a consumer cannot extract a whole collection from its source text.That is the gap this fills: given a node, take the exact source span it occupies.
Change
EndLine/EndColumnalongsideLine/Column, populated from the parser event'send_mark— at construction for scalars and aliases, and for mappings and sequences from the last child's end so the span reaches the end of the actual content.Both are 1-based like
Line/Column, and both are zero when the source carried no position (a textless tree built in memory).Two cases that need care
Block collections. The
MAPPING-END/SEQUENCE-ENDtoken sits at the start of the line following the dedent, so using it overshoots the element — a mapping would appear to extend into whatever comes next. Taking the last child's end keeps the span tight. Empty collections have no last child and fall back to the token's mark.Flow collections. These close with an explicit
}or], and there the END token's mark is just past the delimiter, which is exactly right. So flow uses the token and block uses the last child.Both behaviours have tests.
Tests
end_position_test.gocovers scalars, block and flow collections, nesting, block scalars, empty collections and aliases.TestNodeRoundtripclears the new fields before comparing. Its expected node literals predateEndLine/EndColumnand do not set them; end positions are covered by the new test rather than by duplicating positions across every literal there. Happy to change that if you would rather see them spelled out.Notes
Additive: two new fields and no change to existing behaviour, so nothing that ignores them is affected.
This has been running in a downstream fork for some months, used to slice an OpenAPI operation or schema out of its source file for display. Offering it here because it is a general parser capability rather than anything specific to that use, and carrying it in a fork is the wrong place for it.
Draft while I get review on my side — happy to take feedback before then, and to split the test-file change out if that is easier to review.