Skip to content

fix: balance indent signals on multi-level jumps in embedded tagged templates#783

Merged
dsherret merged 1 commit into
dprint:mainfrom
bartlomieju:fix/embedded-template-indent-mismatch
May 20, 2026
Merged

fix: balance indent signals on multi-level jumps in embedded tagged templates#783
dsherret merged 1 commit into
dprint:mainfrom
bartlomieju:fix/embedded-template-indent-mismatch

Conversation

@bartlomieju
Copy link
Copy Markdown
Collaborator

When the external formatter's output transitions by more than one indent
level between adjacent lines, maybe_gen_tagged_tpl_with_external_formatter
emitted only a single StartIndent/FinishIndent signal but jumped
current_indent_level directly to the new level. The end-of-template
cleanup (while current_indent_level > 0 { FinishIndent }) then emitted
one FinishIndent per level, leaving the IR unbalanced and panicking
dprint-core's writer with finish_indent was called without a corresponding start_indent.

For example, the input

const a = html`<a><svg viewBox="0 0 16 16" width="20" height="20" fill="currentColor">
  <path d="" />
</svg></a>`;

produces lines at indent levels 0 → 2 → 1 from markup_fmt. The old code
pushed one StartIndent (for 0→2) and one FinishIndent (2→1), but cleanup
pushed two more FinishIndents (the 1→0 in the while loop plus the
trailing one closing the initial StartIndent) — three finishes against two
starts, which trips the assertion in dprint-core.

The fix replaces the single-step if/else if with while loops so
multi-level transitions emit one signal per level. Four regression specs
are added to tests/specs/external_formatter/html.txt covering the
original case, the same with an ${expr} placeholder, a three-level
single-line jump (<a><b><svg>…), and a template with sibling
multi-level jumps. Each case panics on the existing code and passes on
the fix; the rest of the spec suite (648 tests) continues to pass.

Fixes denoland/deno#29963.

…emplates

When the external formatter's output transitions by more than one indent
level between adjacent lines, `maybe_gen_tagged_tpl_with_external_formatter`
emitted only a single `StartIndent`/`FinishIndent` signal but jumped
`current_indent_level` to the new level. The end-of-template cleanup then
emitted one `FinishIndent` per level, leaving the IR unbalanced and
panicking dprint-core's writer with
"finish_indent was called without a corresponding start_indent".

For example, `html\`<a><svg ...><path/></svg></a>\`` produces lines at
indent levels 0 → 2 → 1: the old code pushed one StartIndent and one
FinishIndent, but cleanup pushed two more FinishIndents at the end —
three finishes against two starts.

Replace the single-step `if`/`else if` with `while` loops so multi-level
transitions emit one signal per level.

Fixes denoland/deno#29963.
Copy link
Copy Markdown
Member

@dsherret dsherret left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks!

@dsherret dsherret merged commit 6ef0487 into dprint:main May 20, 2026
2 checks passed
bartlomieju added a commit to denoland/deno that referenced this pull request May 20, 2026
`deno fmt` panics with `finish_indent was called without a corresponding
start_indent` when a tagged HTML/SVG template embeds content whose
formatted output transitions by more than one indent level between
adjacent lines. The minimal repro from #29963 is

```js
console.log(html`<a><svg viewBox="0 0 16 16" width="20" height="20" fill="currentColor">
  <path d="" />
</svg></a>`);
```

`markup_fmt` produces lines at indent levels 0 → 2 → 1, and
`dprint-plugin-typescript`'s embedded-template path emitted only a
single `StartIndent`/`FinishIndent` per transition while its
end-of-template cleanup emitted one signal per remaining level, leaving
the dprint IR unbalanced.

The fix is in `dprint-plugin-typescript`
(dprint/dprint-plugin-typescript#783) and is consumed here by bumping
the pin to `=0.96.1`. A spec regression test in
`tests/specs/fmt/embedded_html_multi_indent` covers the case using the
exact input from the issue and asserts the expected formatted output.

Closes #29963
Closes #33623
Closes #31820
Closes #30276
Closes #30980
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.

deno fmt panicked 2.4.0+

2 participants