Skip to content

fix(autofmt): formatting idempotency for long if/else attrs, empty bodies, and split_line_attributes#5509

Open
micahkepe wants to merge 3 commits into
DioxusLabs:mainfrom
micahkepe:fix/autofmt-idempotency
Open

fix(autofmt): formatting idempotency for long if/else attrs, empty bodies, and split_line_attributes#5509
micahkepe wants to merge 3 commits into
DioxusLabs:mainfrom
micahkepe:fix/autofmt-idempotency

Conversation

@micahkepe
Copy link
Copy Markdown

@micahkepe micahkepe commented Apr 24, 2026

Five fixes for non-idempotent formatting where running the formatter
twice produces different output:

  1. Long if/else attribute values inlined unconditionally.
    write_attribute_if_chain always wrote if cond { val } else { val }
    on one line. For long values (e.g. Tailwind classes) this exceeds 80
    chars, causing ShortOptimization to oscillate. Now checks inline
    length against a budget and uses a deterministic multiline layout:

    class:
        if active {
            "long-class-string"
        } else {
            "another-long-class-string"
        },
  2. Blank lines in empty component bodies.
    write_todo_body emitted a newline even with no comments between
    braces. Now only emits when actual // comments are found.

  3. split_line_attributes override on empty blocks.
    The override forced NoOpt even for Empty blocks (no attrs,
    children, or spreads), causing Foo {} to expand with blank lines.
    Now skips the override when the block is Empty.

  4. Spurious blank lines from trailing comments logic.
    accumulate_full_line_comments collected empty lines even when no
    // comments existed. The trailing comment paths in write_rsx_block
    and write_trailing_body_comments now check for real comments before
    emitting.

  5. Spurious blank line in NoOpt path for children-only blocks.
    Elements with children but no attributes (e.g. li { button { ... } })
    got a blank line after the opening brace because NoOpt emitted
    new_line() for attributes then another new_line() before children.
    Now skips the extra newline when there are no attributes or spreads.

Fixes #5508

Test plan

  • long_if_else_attr.rsx: twoway + idempotency
  • empty_component_body.rsx: twoway + idempotency
  • empty_braces_oneliner.rsx: twoway + 3-pass idempotency
  • empty_braces_match_arm.rsx: 3-pass idempotency
  • All 90 existing + new tests pass
  • Tested on a ~4K LOC Dioxus codebase with split_line_attributes = true:
    idempotent across all files after formatting

@micahkepe micahkepe requested a review from a team as a code owner April 24, 2026 04:14
…dies, and split_line_attributes

Three fixes for non-idempotent formatting where running the formatter
twice produces different output:

1. `write_attribute_if_chain` unconditionally inlined if/else attribute
   values on one line. For long values this exceeds 80 chars, causing
   ShortOptimization to oscillate. Now checks inline length against a
   budget and uses a deterministic multiline layout when exceeded.

2. `write_todo_body` emitted a newline even with no comments between
   braces, causing blank lines in empty component bodies. Now only
   emits when actual `//` comments are found.

3. The `split_line_attributes` override forced `NoOpt` even for empty
   blocks, causing `Foo {}` to expand with blank lines. Now skips the
   override when the block is `Empty`.

Fixes DioxusLabs#5508
@micahkepe micahkepe force-pushed the fix/autofmt-idempotency branch from 203a777 to 7434eda Compare April 24, 2026 22:42
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.

dioxus-autofmt: formatting is not idempotent — oscillates between two states

1 participant