fix: indent content added with append/prepend - #343
Open
theRizwan wants to merge 1 commit into
Open
Conversation
`indent()` walked the original characters and the edited chunk contents, but
never looked at the `intro`/`outro` a chunk picks up from `appendLeft`,
`appendRight`, `prependLeft` and `prependRight`. Two things went wrong:
- a line that starts inside inserted content was never prefixed
- a line break inside inserted content did not start a new line as far as the
walk was concerned, so the *original* code after the insert lost its indent
Inserted content is now indented in output order — chunk `intro`, then the
content, then chunk `outro` — using the same helper as the string-level
`intro`/`outro`, which also keeps the line-start tracking accurate across it.
Two smaller fixes fall out of that:
- the replacer only skipped the indent when it was continuing a line, but it
applied that to every match, not just the one at offset 0; a match further
in always follows a line break, so `s.append('\nZ')` left `Z` unindented
- when a line starts at a chunk boundary the indent is appended to the intro
rather than prepended, so it lands in front of the content instead of in
front of an intro that has already been indented on its own
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.
indent()walks the original characters and the edited chunk contents, but it neverlooks at the
intro/outroa chunk picks up fromappendLeft,appendRight,prependLeftandprependRight. So inserted content is invisible to the walk, andtwo things go wrong:
is concerned, so the original code that follows the insert silently loses its
indent.
(2) is the damaging one — it mis-indents code the caller never touched.
Reproductions
All checked against the published
magic-string@1.2.3build.Wrapping a module and indenting it is the canonical use for
indent(), and here thelast two lines come out flush left.
The individual failures, all on
'a\nb\nc'withindent('>'):toString()indent('>')prependRight(2, 'Q\n')a\nQ\nb\nc>a\n>Q\n**b**\n>c>a\n>Q\n>b\n>cappendLeft(1, '\nQ')a\nQ\nb\nc>a\n**Q**\n>b\n>c>a\n>Q\n>b\n>cappendLeft(2, 'X')a\nXb\nc>a\n**X>b**\n>c>a\n>Xb\n>cappend('\nZ')a\nb\nc\nZ>a\n>b\n>c\n**Z**>a\n>b\n>c\n>ZThe last one does not even involve a chunk:
this.outrois run through the pattern,but the replacer's "am I continuing a line?" guard is applied to every match rather
than only the one at offset 0, and a match further into the string always follows a
line break.
Bundle#indentalready gets this right for its own intro(
index > 0 ? indentStr + match : match).The fix
Indent the pieces in output order — chunk
intro, then the content, then chunkoutro— with the same helper used for the string-levelintro/outro, so theline-start tracking stays accurate across inserted content. The helper also fixes the
offset-0 guard, and subsumes the ad-hoc flag update the edited-chunk branch was doing.
One knock-on: when a line starts at a chunk boundary the indent is now
appendRightedonto the intro rather than
prependRighted, so it lands directly in front of thecontent instead of in front of an intro that has already been indented in its own
right. That is what turns
X>binto>Xbin the third row above.Exclusion ranges are honoured for inserted content too: a chunk's
introis skippedwhen
chunk.startis excluded, itsoutrowhenchunk.end - 1is excluded, matchinghow the existing branches treat excluded original characters.
Evidence
Differential harness: random op sequences (
append/prepend/appendLeft/appendRight/prependLeft/prependRight/remove/overwrite) over seven originals,comparing
indent(prefix)against prefixing every line of that same MagicString's owntoString().A separate sweep generated hires sourcemaps for 3,603 indented cases and found no
out-of-bounds mapping segments.
262existing tests still pass with no changed expectations — nothing in the suitepinned the old behaviour. 9 tests added (271 total); 7 of them fail on master, the
other 2 are guards for behaviour that is deliberately preserved (content that
continues a line is not prefixed, and
indentStart: falsestill suppresses the firstindent even when the string opens with an insert).
Lint and typecheck clean.
Not addressed
A lone
\rleft behind by splitting a\r\npair is not treated as a line terminator,so nothing is indented after it. That is pre-existing and orthogonal —
indent()onlyever recognised
\n— so I have left it alone rather than widen this PR.