Any rustfmt options for nicer formatting of multiline string literals?

I want to write a bunch of tests that look like this:

    t.check("cut", "
        hello
        ctrl+x
    ");

of course, if I ask rustfmt to reformat, what I get is

    t.check(
        "cut",
        "
        hello
        ctrl+x
    ",
    );

Is there some option, even perhaps a nightly one, to make rustfmt be more reasonable here?

I know that I can rustfmt::skip the whole thing, but I'd rather not. I also know that I can use a macro with a custom syntax, like

    check!["cut" -> "
        hello
        ctrl+x
    "];

which won't get reformatted, but I'd rather avoid that as well.

Any other options I should consider?

Aside: Zig's multiline string literals dodgle the problem nicely, a really great syntactic discovery.

1 Like

Rustfmt is not touching your string literal (it can't otherwise it would change the data). I think this one is fn_params_layout. When the function parameters span multiple lines, it puts each parameter on its own line and indents them.

If you don't want that, you could move the long string to a variable.

let s = "
        hello
        ctrl+x
    ";
t.check("cut", s);

Or replace the newlines with \n.

t.check("cut", "\n        hello\n        ctrl+x\n    ");

I don't have anything actionable to suggest, just some comment replies and issue links.

In my view this is somewhat a subcase of a larger rustfmt shortcoming[1] in that it has no way to respect input newlines, and AFAICT explicit opposition to gaining that ability. (There's some exceptions such as comments.)


It could still "touch" it in terms of changing literal newlines to \n or vice-versa, say.

That's for declarations. Here's an issue for calls. And a related issue with lots of others marked as duplicates or otherwise linked.


  1. IMNSHO ↩︎

Yeah, agree with that analysis. The way I see it:

rustfmt: mostly completely disregards input newlines

gofmt: preserve input newlines

zig fmt: use input to select several variants of formatting (eg, adding a trailing comma forces argument per line, removing it formats call in a single line)

I haven’t used go one, but zigs seems like a sweet spot: you sort-of interactively guide the tool to the formatting you want.

I still think that writing newline-preserving formatter that can also deal with incomplete syntax and only format select syntax nodes, based on rust-analyzer’s syntax tree, would be a useful thing to do!

5 Likes

I often turn to indoc! for this kind of thing:

    t.check("cut", indoc! {"
        hello
        ctrl+x
    "});

But rustfmt still mangles it :frowning:

    t.check(
            "cut",
            indoc! {"
            hello
            ctrl+x
        "},
        );
    }
2 Likes