If you want to write a multiline string in a way that lets you edit it exactly as it's printed (on the terminal), it's common to write something like this:
fn printsomething() {
println!(
"line__1
line______2
line________3
line___________4"
);
}
Unfortunately the indenter does not leave this as is but tries and indents it into something that makes it impossible to edit, unless you remember to not indent the string (or the region containing it).
Result with rustfmt 0.3.0:
fn printsomething() {
println!("line__1
line______2
line________3
line___________4");
}
We currently leave strings unformatted if they fit within the max width and don't need to be repositioned. It seems a reasonable heuristic to also not reformat if the string starts at column 0 (as in your example).
I have an other problem with multiline strings. In:
const USAGE: &'static str = "
toyunda-player.
Usage:
toyunda-player [options] <file>
toyunda-player -h | --help
toyunda-player --version
Options:
-h --help Show this screen.
--version Show version.
--invert Invert the screen.
";
Out:
const USAGE: &'static str = "
toyunda-player.
Usage:
toyunda-player [options] <file>
\
toyunda-player -h | --help
toyunda-player --version
Options:
-h \
--help Show this screen.
--version Show version.
\
--invert Invert the screen.
";
Some more examples:
fn main() {
x.f("
Article(id, title).
Vote(user, id).
VC(id, votes) = votes:COUNT(u, Vote(u, id)).
AwV(id, title, votes) = Article(id, title) * VC(id, votes).
Vote_n(user, id, strength=1) <- Vote(user, id).
VS(id, score) = score:SUM(strength, Vote_n+(_, id, strength)).
AwV_n(id, title, score) = Article(id, title) * VS(id, score).
").unwrap();
}
becomes
fn main() {
x.f("
Article(id, title).
Vote(user, id).
VC(id, votes) = votes:COUNT(u, Vote(u, id)).
\
AwV(id, title, votes) = Article(id, title) * VC(id, votes).
Vote_n(user, id, \
strength=1) <- Vote(user, id).
VS(id, score) = score:SUM(strength, Vote_n+(_, id, \
strength)).
AwV_n(id, title, score) = Article(id, title) * VS(id, score).
")
.unwrap();
}
But this remains untouched:
fn main() {
x.f("
Article(id, title).
Vote(user, id).
VC(id, votes) = votes:COUNT(u, Vote(u, id)).
AwV(id, title, votes) = Article(id, title) * VC(id, votes).
Vote_n(user, id, strength=1) <- Vote(user, id).
VS(id, score) = score:SUM(strength, Vote_n+(_, id, strength)).
AwV_n(id, title, score) = Article(id, title) * VS(id, score).
");
}
And so does this (beyond the .unwrap() being moved and indented):
fn main() {
f("
Article(id, title).
Vote(user, id).
VC(id, votes) = votes:COUNT(u, Vote(u, id)).
AwV(id, title, votes) = Article(id, title) * VC(id, votes).
Vote_n(user, id, strength=1) <- Vote(user, id).
VS(id, score) = score:SUM(strength, Vote_n+(_, id, strength)).
AwV_n(id, title, score) = Article(id, title) * VS(id, score).
").unwrap();
}
I encountered the same problem when using docopt.
Currently I am always using #[rustfmt_skip] to exclude that part from reformatting.
#[rustfmt_skip]
const USAGE: &'static str = "
...
";
@duesee I do the same.
This is now fixed if using the default options.
Using master and no config specified, the sample function listed above still mis-indents for me if I do not prefix the function with #[cfg_attr(rustfmt, rustfmt_skip)]. It's different than 0.3.0 though.
fn printsomething() {
println!(
"line__1
line______2
line________3
line___________4"
);
}
fn printsomething() {
println!("line__1
line______2
line________3
line___________4");
}
At the very least the first linebreak of the println!() call should be preserved. I suppose the closing ); can be argued to be stylistically acceptable this way.
That is pretty much the expected behviour - the string is moved and the println is reformatted, but the string body is left alone.
Does that mean the first line of the string is not considered part of the string body?
It is, but the string is started where rustfmt thinks the first argument should go, in this case, immediately after the (.
OK. Given that this isn't the expected output, I suppose it should be configurable then. So, looking for a switch, seeing how fn_args_paren_newline is default true and how it doesn't do what I think its help string says it would, I have to ask: The description sounds like function calls or definitions would have their arguments listed starting after fn a_fn(\n, meaning first argument would always be on a separate line. That doesn't match what rustfmt does in default-config mode. Can you shed some light if that option can be used to get the desired output.
How do you feel about more linebreak options ala clang-format?
It might be that fn_args_paren_newline is not respected for macros (only functions). It might also be a bug, non-default options tend to regress. I must admit, from the description, I would expect that to be false by default and I'm not sure what effect it is meant to have. Maybe looking for uses of it in the tests would shed some light on what it is meant to do.
How I feel on more options depends on the options themselves, I don't object on principle, but I want to make sure each option carries its weight.
I would also expect it to be false, given the default output.
@tuncer If you want every line of the string to line up, why settle for the initial " throwing off the first line by one character? rustfmt seems to happily preserve the following:
fn printsomething() {
println!("\
line__1
line______2
line________3
line___________4");
}
@andersk, this one seems to work best if we ignore the closing "); being at column 0.
fn printsomething() {
println!("\
line__1
line______2
line________3
line___________4\
");
}
Either way. Given that these styles are already preserved, do you still think you need extra options?
No, it's sufficient.
Resolved, closing.
with docopt strings, it is still necessary to prefix the string with
#[cfg_attr(rustfmt, rustfmt_skip)]
const USAGE: &str = "
because with
#[rustfmt::skip]
const USAGE: &str = "
it skips the whole file for me. if this is intended behaviour, i believe it is worth revisiting.
Most helpful comment
@tuncer If you want every line of the string to line up, why settle for the initial
"throwing off the first line by one character? rustfmt seems to happily preserve the following: