astral-sh / ruff

An extremely fast Python linter and code formatter, written in Rust.
https://docs.astral.sh/ruff
MIT License
32.55k stars 1.08k forks source link

Option to vertically format arguments if wrapped. #9992

Open funkybob opened 8 months ago

funkybob commented 8 months ago

Would it be possible to add a formatter option so that once it decides to split arguments to a call onto a new line, each argument goes on its own line?

So, for example, when it chooses to reformat:

foo = bar(long_name=value, another_argument=1, something_else="12314")

into:

foo = bar(
    long_name=value, another_argument=1, something_else="12314"
)

it will instead produce:

foo = bar(
    long_name=value,
    another_argument=1,
    something_else="12314"
)

(and ideally also add the trailing comma, but that's a separate formatting option, I assume)

I know I can force this formatting to be retained by using the magic trailing comma, but I'd like to not have to think about this.

MichaReiser commented 8 months ago

Hy @funkybob

The main request in this issue is to have an option to disable the style where ruff tries to format all arguments on a single line before splitting each argument on a separate line. Ruff should already add the trailing comma as you want it to: It adds the trailing comma when each argument is on its own line, but it omits it when formatting all arguments on a single line (the style that you don't want to have).

We're still considering our position on how configurable our formatter should be. There are arguments for an opinionated formatter with very few options. But supporting many options also has its benefits. For now, our position is to avoid formatting style-related options, which includes the option you're asking for.

benblank commented 2 months ago

If ruff prefers to avoid configurable formatting, then I think there's a case to be made that "one argument per line" should be how calls like this are wrapped.

Basically, the point of wrapping code is to improve readability, right? Long, unbroken lines may extend past the edge of a reading area and also tend to be more difficult to parse visually. Good wrapping helps with both of those issues.

But this kind of "function name, then every-argument-on-a-single-line, then close parenthesis" wrapping doesn't really do a lot for line lengths. This is especially noticeable with short function names; in funkybob's second example, the indented arguments line is only seven characters shorter than the original! A few more keystrokes, and it'll need rewrapped anyway.

I also don't think it does hardly anything for visual parsing; again, looking at the examples above, it's really easy to quickly spot that there are three arguments in the last example. But I find myself having to sort of stop-and-count-the-equals-signs in the other two. Putting each argument on a separate line makes a big difference for readability, I believe.

Finally, there's also a sense in which it seems oddly inconsistent to me. It feels like there's a kind of "gap" between calls which are short enough to not need wrapped at all and those which are long enough that all the arguments can't be made to fit on a single line, anyway. In between the two, there's this kind of "partial wrapping" which triples the vertical space taken by the call but often doesn't significantly reduce the horizontal space, nor improve readability much.

TL;DR: If ruff is going to have only one wrapping behavior in this scenario, I think it should be the one funkybob describes, not it's current behavior… even though it would be a breaking change. 😓

alexei commented 1 week ago

Because sometimes ruff puts each argument on its own line, and sometimes it puts all arguments on the same line, I often find myself having to review the changes and make manual adjustments. I too support to either have an option to instruct ruff to lean on always adding the comma, or have that as the default behavior. This would help enforcing a consistent style, especially in larger brownfield projects.

jorenham commented 1 week ago

I wrote a simple libcst codemod that adds a trailing comma to such half-wrapped parameters, so that ruff format will place them onto separate lines after that:

https://github.com/jorenham/scipy-stubs/blob/dd90c64c05a8f75f684662c277278e2c35400d4f/codegen/mods.py#L97-L121

But clearly this is far from ideal; as it requires running ruff format twice, doesn't work if skip-magic-trailing-comma is set, and is very slow compared to ruff.