Skip to content

Preserve JSX tag syntax in autofmt - #5731

Open
nicoburns wants to merge 2 commits into
devin/1785758131-jsx-syntaxfrom
devin/1785764450-jsx-autofmt
Open

nicoburns wants to merge 2 commits into
devin/1785758131-jsx-syntaxfrom
devin/1785764450-jsx-autofmt

Conversation

@nicoburns

@nicoburns nicoburns commented Aug 3, 2026 •

Copy link
Copy Markdown
Member

Summary

Top of the stack #5883 → #5730 → this. Makes dx fmt/autofmt keep rsx written in the JSX/XML tag syntax in that syntax instead of rewriting it into block syntax.

write_element/write_component dispatch to a new write_tag when the node's NodeDelimiter (from #5730) is a tag. Mixed block/tag styles are preserved as written, since dispatch happens per node. Incomplete tags (which only parse with diagnostics) are not formatted.

Layout:

  • self-closing tags for empty bodies: <img src="image.png" />
  • a single short child is inlined: <h1>"Hello"</h1>
  • attributes stay in the open tag when short, otherwise one per line with the closing > / /> on its own line; multiline values (closures, long if chains) are indented relative to their attribute
  • attribute values are printed as name="literal" / name={expr}, shorthand collapses (disabled={disabled} → disabled), spreads as {..attrs}, dashed custom attributes unquoted (data-count="1"), and components without the turbofish (<Outlet<R>>)

Comments and blank lines are located from the tag tokens and kept, using the comment helpers from #5883 (write_comment_only_body, write_closing_line, has_leading_comments, inline_comment):

// Before a tag
<div
    // Before an attribute
    class="a"
    id={user_id} // After an attribute
    {..attrs} // After a spread
>
    <h1>"Hello"</h1> // After a tag

    <p> // After an open tag
        "text"
    </p>
    // Before the closing tag
</div>
<section>
    // Only a comment
</section>

Attributes that have comments are always put on their own lines, so the output is the same on every pass.

Comments in the middle of an open tag are kept too (second commit), mirroring what #5883 does for the block syntax. A comment after the tag name stays there, and one between = and a value stays above the value:

<div // After the name
    class= // After the equals sign
        "a"
    id=
        // Above the value
        {id}
>

Comments inside an if attribute value and inside for / if headers go through the code from #5883.

Tests: idempotency samples (tests/samples/jsx.rsx, tests/samples/jsx_comments.rsx) and normalization tests (tests/wrong/jsx-4sp*, tests/wrong/jsx-comments*).

Link to Devin session: https://dioxus.staging.devinenterprise.com/sessions/99eedb36d44345dc8ce0f3ed5c460bd6
Open in Devin Desktop: https://dioxus.staging.devinenterprise.com/desktop/session/99eedb36d44345dc8ce0f3ed5c460bd6?variant=devin-insiders
Requested by: @nicoburns

@nicoburns nicoburns self-assigned this Aug 3, 2026
@staging-devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

Nodes written in the tag syntax (`NodeDelimiter::Tag`) are written back out in that
syntax by a new `write_tag` instead of being rewritten into the block syntax.

- self-closing tags for empty bodies, a single short child is inlined
- attributes stay in the open tag when short, otherwise one per line with multiline
  values indented relative to their attribute
- comments before, after and inside of tags, on and between their attributes, and before
  their closing tag are kept, as are blank lines between sibling tags
- attributes that have comments are always put on their own lines so that the output is
  the same on every pass
@staging-devin-ai-integration
staging-devin-ai-integration Bot force-pushed the devin/1785764450-jsx-autofmt branch from 39afdd6 to f1ed4f2 Compare October 3, 2026 15:28

This branch has not been deployed

No deployments
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.

1 participant