Skip to content

Proposal: Formatting options to enable "vertical formatting" and "grid formatting" #8197

@NeilGirdhar

Description

@NeilGirdhar

I realize that this may not be a popular feature request, but it's the only thing stopping me from using Black, and I love the idea of Black, so I feel compelled to propose it. Feel free to close this as "will not implement".

Fundamentally, Black has chosen to wrap lines in one of three ways:

  • single line:
    def super_softplus(x: JaxRealArray) -> JaxRealArray:
  • hanging single line:
      def geometry_energy(
          self, intermediate_explanation: FisherMessage, observation: FisherMessage, case: int
      ) -> RivalMessageEnergy:
  • and hanging multi-line:
      def infer_explanation_variationally(
          self,
          observation: FisherMessage,
          weights: Weights,
          *,
          key: None | KeyArray = None,
          use_variational_signal_noise: bool,
      ) -> RivalMessagePrediction:

This hanging formatting like mode 3 of isort. While mode 3 is very versatile, some people consider it harder to read than isort's mode 1: "vertical" (see e.g., numpy). My proposal is to try vertical formatting to see if all lines fit without wrapping, and if so prefer that to either of the hanging outputs. Thus, for the above examples, we have:

    def geometry_energy(self,
                        intermediate_explanation: FisherMessage,
                        observation: FisherMessage,
                        case: int
                        ) -> RivalMessageEnergy:

and

    def infer_explanation_variationally(self,
                                        observation: FisherMessage,
                                        weights: Weights,
                                        *,
                                        key: None | KeyArray = None,
                                        use_variational_signal_noise: bool,
                                        ) -> RivalMessagePrediction:

Of course legibility is in the eye of the beholder, but the two points in favour of vertical layout are that:

  • The arguments sit to the right of the function call, which makes the function call more visually obvious.
  • In the case of hanging multi-line, the vertical version uses fewer lines of vertical space. Minimizing vertical space means that more code is visible on the screen, which makes reasoning about large functions a lot easier since you don't need to scroll and memorize code.
  • In the case of hanging single line, the vertical layout puts each argument on its own line, which makes them easier to distinguish without looking for commas.

I concede that when there is a line that doesn't fit within the line length, hanging is definitely superior. I'm not proposing eliminating that. I'm proposing using vertical in particular cases. In short, my proposal is to format as follows:

  • Prefer single line if possible (as before)
  • Try vertical to see if all lines can be rendered without wrapping (proposed)
  • Try hanging multi-line otherwise (as before)

This algorithm would need to applied recursively for nested structures.

I realize that Black's rules are simple, but if the goal is to write code the way humans read and write code, I think a little bit of extra complexity in the rules would go a long way to making code easier for people to read. The downside of this proposal is longer diffs, but diffs are read a lot less often than code is read as it's being worked on.

--

Edit: Similarly, it would be nice to support grid formatting as another option. This comes in handy for NumPy arrays and imports:

from ...structure import (InferenceResult, InferenceResults, ListLabeler, LoggingManager, Plot,
                          Plotter, RLInference, Solver, TrainingResult, TrainingResults,
                          TrainingSolution, chooser_field, smooth_data, ui_dataclass)

np.array([[1, ...
           2, 1],
          [1, 1, 0],
          [0, 1, 1]])

Metadata

Metadata

Assignees

No one assigned

    Labels

    formatterRelated to the formatterwishNot on the current roadmap; maybe in the future

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions