mvdan / sh

A shell parser, formatter, and interpreter with bash support; includes shfmt
https://pkg.go.dev/mvdan.cc/sh/v3
BSD 3-Clause "New" or "Revised" License
7.1k stars 336 forks source link

syntax: allow word iteration loops to keep all words in separate lines #582

Open macau23 opened 4 years ago

macau23 commented 4 years ago

Sometimes I have a very long for loop with lots of long items:

for i in aaaaaaaaaa bbbbbbbbb cccccccccccc dddddddddddddd eeeeeeeeeeee ; do

to keep it readable and sorted, I split them per-line:

for i in \
 aaaaaaaaaa \
 bbbbbbbbb \
 cccccccccccc \
 dddddddddddddd \
 eeeeeeeeeeee \
; do

shfmt reformats it to this:

for i in \
 aaaaaaaaaa \
 bbbbbbbbb \
 cccccccccccc \
 dddddddddddddd \
 eeeeeeeeeeee; do

which makes sorting it harder (the ";do" gets moved to the wrong place).

Could shfmt support not doing that for long for loops where the first line uses an "in \$"?

mvdan commented 4 years ago

This is an interesting request, but would we force the do to be on a separate line in the case where in is at the end of the line, or would we simply relax the formatting to allow do to be in either of the two places?

We're in a bit of a tough spot because the first change would be a breaking change (new shfmt v3 versions would contradict previous v3 versions), but the second change makes the formatting less strict/consistent, which isn't a good thing either.

mvdan commented 4 years ago

Here's an alternative:

sorted=(
    aaa
    bbb
    ccc
)
for i in "${sorted[@]}"; do
    ...
}

There's an extra step, but in my opinion the syntax is far nicer - no backslashes, and no need to worry about the in and do tokens. I don't think Bash as a language allows doing anything like this directly in a for loop.

macau23 commented 4 years ago

Fine by me. I would like it supported in shfmt but if it's a breaking change then I will use your alternative.

mvdan commented 4 years ago

So maybe shfmt should impose this as the "formatted" select, for,... solution?

As I explained in my earlier comment, I don't think we can do this anytime soon. It would be a breaking change in v3, and I don't have plans for a v4 right now. I also don't think it would be a good idea in general; forcing backslashes would likely upset a good portion of the users.

then I suggest that formatting respects the continuation line character \ (i.e: never remove it). Consider it a manual overwrite for the user to say "I know better"...?

That's the second option I was considering, but I was a bit on the fence. I generally agree with you that having to use an extra variable with an array is not very good. I still think that the backslash solution is somewhat hacky, but I guess that's just the shell language in general :)

Does someone want to take a crack at this second option? That is:

1) allow "word iteration" words to be on separate lines 2) allow the "do" token to be on a separate line if the "in" token was similarly followed by a newline

mvdan commented 4 years ago

But they can be accommodated with a non-default switch to the formatter, that orders shfmt "never to split lines"...

That also comes with its cost. The more formatting options the tool has, the harder it is to maintain, test, and use.

I assume that it implies that the ;do should be kept on the same line as the statement at all cost.

Not quite; see the two changes I explained at the end of my last comment. Both of those forms would be obeyed by the formatter:

# single-line form, what's done now
for x in foo bar baz; do
    echo $x
done

# many-line form; allowed as long as the iteration words are all separated from "in" and "do" by newlines
for x in \
    foo \
    bar \
    baz \
    ; do
    echo $x
done
mvdan commented 4 years ago

"would", implying that the second option still needs implementing, right?

Correct, hence the two changes proposed earlier as a way to implement it.

zackw commented 3 years ago

N.B. the proposed alternative is no good for -p mode (i.e. for scripts that must remain free of bashisms).

mvdan commented 3 years ago

Indeed, it's far from a direct replacement, but more of an alternative depending on one's situation.

macau23 commented 1 year ago

Close?