Mirror the terminator-comment rule in the JavaScript runner's extents - #5
Merged
Conversation
Both libraries absorb a comment sitting after a statement's terminator on the same line into that statement rather than leaving it in the gap (idfkit-js#47). The Python one always did; the TypeScript one now does too. This runner inferred the extents from the two texts and assumed the old behaviour, that the writer copied the comment into the gap, so three edited cases failed against a library that had stopped doing that. The library was right and the runner was wrong. Now both sides run to the end of that comment, exactly as the writers do, with the written side one character further because `writeObject` emits the newline that ends its own comment line and the source's statement stops at the comment. One case needed more than that. When the changed region reaches the last statement, everything after the last statement the two still agree on belongs to it: an object appended at the end, the last statement reformatted, and a removal whose leftover gap the appended object then follows are three shapes of the same thing. Telling them apart by counting does not work, because removing one object and appending another leaves the same number of statements in the middle on each side, so the boundary is the terminator of the last agreeing statement instead. The Python runner is untouched: it takes its extents from the concrete syntax tree rather than from the text, so it never made this assumption.
The Python runner rebuilds the written text to locate the extents an edited case excludes, and checks its reconstruction against what the library actually wrote. The writer now reuses an object's own field comments, so a reconstruction that did not pass the original text built a different object and the self-check rejected the run, which is what it is for.
The written side was extended one character further than the source, to cover a newline the writer emitted and the source's statement did not. The writer no longer emits it: a statement's extent ends at its terminator or at the comment on that line, on either side, and the break belongs to the gap. One rule for both sides now, and the gap between two statements is compared rather than half excluded.
The span reconstruction joined objects with a fixed two newlines. An object at the end of a file is followed by one, so the last statement's span ran a line past where the writer actually put it and the self-check rejected runs that were correct. The node already carries whatever separated it from the next object; use that, falling back to the blank line only when there is nothing to reuse.
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.
Companion to idfkit/idfkit-js#48, which closes idfkit/idfkit-js#47.
Both libraries absorb a comment sitting after a statement's terminator on the same line into that statement, rather than leaving it in the gap. Python always did; TypeScript now does too.
This runner infers the extents an edited case excludes from the two texts, and it assumed the old TypeScript behaviour — that the writer copied that comment into the gap. Three edited cases failed against a library that had stopped. The library was right and the runner was wrong; nothing here changes what the corpus asserts.
What changed
Both sides now run to the end of that comment, as the writers do. The written side runs one character further, because
writeObjectemits the newline that ends its own comment line where the source's statement stops at the comment.preserve-edit-remove-and-addneeded more. When the changed region reaches the last statement, everything after the last statement the two still agree on belongs to it — an object appended at the end, the last statement reformatted, and a removal whose leftover gap the appended object then follows are three shapes of one thing. Counting cannot separate them: removing one object and appending another leaves the same number of statements in the middle on each side. So the boundary is the terminator of the last agreeing statement, the same offset on both sides.The cost is that the trailing text of a statement the comparison already matched is not compared. That is a statement it agreed on.
The Python runner is untouched
It takes its extents from the concrete syntax tree rather than from the text, so it never made this assumption. Both runners pass at
conformance-2026.10: 201 of 211 in TypeScript and 192 in Python, every failure an accepted entry, none stale. 115 Python and 117 JavaScript comparator tests pass, andvalidate_governance.pyis clean.