google / yapf

A formatter for Python files
Apache License 2.0
13.76k stars 890 forks source link

Options for preferring hanging indents with one item per line when formating lists #134

Open Jbonnett opened 9 years ago

Jbonnett commented 9 years ago

I would like to add options/code so that yapf can be configured to prefer outputting something like

MyLongNamedTuple = (
    something.somethingelse,
    something.somethingelse,
    foo=(
        bar.somethingelse,
        something.asdfas,
    ),
)

over

MyLongNamedTuple = ( something.somethingelse,
    something.somethingelse, foo=( bar.somethingelse,
    something.asdfas),)
bwendling commented 9 years ago

@Jbonnett Do you have a general rule or a heuristic that can be applied here?

hayd commented 9 years ago

It looks like the OP is always wanting a line break after a paren, so newline_after_paren ? Or maybe force_newline_after = '({[' (as one may want this for other bracket types).

@Jbonnett Is that what you're after?

Jbonnett commented 9 years ago

I feel like a list should read EITHER left to right OR Up to down but not both, and I think its worth the trade off in vertical space. Of course anything that can fit on a single reasonable line, should be left on a single line. For example if the variables in foo in the above example where bar and asdf then even on the vertically stacked example i would want foo to be formatted as

foo=(bar, asdf)

not

foo=(
    bar,
    asdf,
)

Not everyone would agree with keeping lists 1 dimentional, so I would want this to be tune-able in style.

bwendling commented 9 years ago

Probably the best thing that can be done here is to use the heuristic that if the list ends in a comma then we will try to place them on individual lines. Otherwise, we will do "bin packing" on the items in the list.

MyLongNamedTuple = [
    something.somethingelse,
    something.somethingelse,
    [
        bar.somethingelse,
        something.asdfas,
    ],
]

as opposed to:

MyLongNamedTuple = [
    something.somethingelse, something.somethingelse, [
        bar.somethingelse, something.asdfas
    ]
]
Jbonnett commented 9 years ago

Well, first, comma's are dependent on the previous formatting of the code, which is not something i think we should rely on.

Second, i think it sort of misses the point. This feature would be more about attempting to make lists read in one direction. Either a list should read left to right OR up to down. So I would say:

yapf finds a list.

  1. Does the list fit on one line at the current indentation level? If so format it left to right.
  2. Are there more than a $screens_worth of items in the list? If not format it up to down, one item per line.
  3. Is there a nested lists, tuple or directory defined within the outer list which will not fit on a single line at the current indent. If so format the outer list up to down.
  4. We can not improve the situation. yapf should just "bin pack"

Though I might argue for special casing for imports

from big_module import ( 
    all, of, the, things, all, of, the, things, all, of, the, things, all,
    of, the, things,  all, of, the, things, all, of, the, things, all, of, the,
    things, all, of, the, things, all, of, the, things, all, of, the, things, 
)

Though, there is also the hanging indent vs ide spacing question to consider

thing = (
    hi,
    im,
    filler
)

vs

thing = (hi,
         im,
         filler)

which should probably ALSO be a tune-able style preference. With these silly short var names, its not a problem, but in the real world, we all know sometimes you end up having to break lines in a odd way to preserve what i like to call "IDE Spacing"

So preserve_list_directionality=a_weight and prefer_hanging_indent=a_weight

Which, after writing all of this, makes me think that maybe this should be two DIFFERENT tickets. One for the hanging indents, another for the list directionality.

dpercy commented 9 years ago

I feel like a list should read EITHER left to right OR Up to down but not both

:+1:

I would love a configuration option to make comma-separated things always be either

  1. all on one line
  2. one item per line
bwendling commented 9 years ago

Doh! This fell off my plate. Let me see what I can do about it...

Jbonnett commented 9 years ago

I am also willing to take a crack at a patch, so long as the general concept is agreed on first.

dpercy commented 9 years ago

I actually just realized that yapf already puts keyword arguments each on their own line. I'm content with this behavior, since functions with many arguments should be using keyword arguments anyway.

dpercy commented 9 years ago

On a related note, though, I noticed that yapf itself has to use yapf: disable in places like this: https://github.com/google/yapf/blob/master/yapf/yapflib/style.py#L120, probably to keep the close paren on its own line. It would be nice if this were configurable. This line looks relevant: https://github.com/google/yapf/blob/master/yapf/yapflib/format_decision_state.py#L129

bwendling commented 9 years ago

@Jbonnett I think your heuristic is not bad. It's definitely going in the correct direction. If you would like to take a crack at it, please do. Thanks! :-)

bwendling commented 9 years ago

@Jbonnett Have you been able to look at this recently? :-)

193 is a second request for this.

ambv commented 9 years ago

Isn't this supported now after #191 got merged? Add trailing commas and insert a newline before the first element.

bwendling commented 9 years ago

If I understand @Jbonnett's heuristic, #191 is a move in the right direction, but perhaps there are some edge cases that it won't address? I'm willing to bet that formatting data literals still needs some work. :-)

KelSolaar commented 7 years ago

Any news regarding this feature? We are heavy user of vertical lists and it would be a much appreciated option to have.