-
Notifications
You must be signed in to change notification settings - Fork 46
Description
Environment
- OS: Ubuntu 22.04
- Topiary: 0.6.1
Describe the bug
If I have a formatting rule that adds a hardline to a single-line node, the parent nodes do not update their context to multi-line. Softlines in the parent nodes do not expand to line breaks even though the node now spans multiple lines. This causes idempotence errors when formatting.
To Reproduce
Minimal reproducible example using my sqf grammar. It should be easily adaptable to other grammars as well.
Topiary query file:
(
(statement)
(statement) @prepend_hardline
)
; Use hardlines when code block has multiple statements
(code_block
"{" @append_hardline @append_indent_start
(code
(statement)
(statement)+
)
"}" @prepend_hardline @prepend_indent_end
)
; Use softlines when code block has only one statement
(code_block
"{" @append_spaced_softline @append_indent_start
(code
.
(statement)
.
)
"}" @prepend_spaced_softline @prepend_indent_end
)
Example input:
{a=1};
{
a=1
};
{a=1;b=2};
{{{a=1}}};
{{{
a=1
}}};
{{{a=1;b=2}}}
These formatting rules allow a code block with a single statement to either be single-line or multi-line, but blocks with multiple statements will always be formatted with line breaks to span multiple lines. A code block itself is also a statement and can be nested inside another code block.
Expected behavior
{ a=1 };
{
a=1
};
{
a=1;
b=2
};
{ { { a=1 } } };
{
{
{
a=1
}
}
};
{
{
{
a=1;
b=2
}
}
}
We would expect the code blocks with multiple statements and those with multi-line single statements to be formatted with line breaks, and those with single-line single statements with spaces.
Actual behavior
[2025-09-01T00:02:59Z ERROR topiary_core] Failed idempotence check
[2025-09-01T00:02:59Z ERROR topiary_core] Diff < left / right > :
{ a=1 };
{
a=1
};
{
a=1;
b=2
};
{ { { a=1 } } };
{
{
{
a=1
}
}
};
<{ { {
>{
> {
> {
a=1;
b=2
< } } }
> }
> }
>}
[2025-09-01T00:02:59Z ERROR topiary] The formatter did not produce the same
result when invoked twice (idempotence check).
We get an idempotence error. The last code block had multiple statements on a single line, so the innermost block gets turned multi-line. However the outer blocks are still in single-line mode and expand their softlines into spaces. On the second formatting round the innermost block is already spanning multiple lines so the outer blocks get correctly formatted with line breaks.
Workarounds
Running topiary format --skip-idempotence
twice formats everything correctly.
I can't come up with a hack to check for this case in the query file. This is a hard problem because the added hardline could be at any depth and becomes even harder if you also have something like arrays that could be either single or multi-line and could contain other code blocks.