Skip to content

Adding hardline does not update parent context to multi-line #1073

@Tuupertunut

Description

@Tuupertunut

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions