nicklockwood / SwiftFormat

A command-line tool and Xcode Extension for formatting Swift code
MIT License
7.66k stars 623 forks source link

Rule Request: handle indentation (in function params) like Prettier #1564

Open mesqueeb opened 8 months ago

mesqueeb commented 8 months ago

I'm hoping to find a way to have SwiftFormat be more like Prettier and give way less freedom than it currently does.

There's many cases in SwiftFormat where depending on where you put a line break it will allow a whole variety of formatting methods. Eg. putting a line break before or after any single function param will make SwiftFormat auto format those params in different ways. With prettier there's only 2 ways, all params on one line if it fits, or all params with linebreaks and a single indentation.

What's the best way for me to achieve this? Is there some sort of plugin functionality for SwiftFormat so we could have a more restrictive rule set like Prettier? Ps: I use SwiftFormat for Xcode.app for my formatting currently.

Case 1

before format:

func rotationAngle(
  _ x: Float, _ y: Float) -> Float {
  return x * (.pi / 180) + y
}

current after format:

func rotationAngle(
  _ x: Float, _ y: Float) -> Float
{
  return x * (.pi / 180) + y
}

desired after format:

func rotationAngle(
  _ x: Float,
  _ y: Float,
) -> Float {
  return x * (.pi / 180) + y
}

With prettier, making a new line before the first function param, prettier will (1) make a line break behind ALL params and (2) only use 1 level of indentation for ALL params and (3) always use trailing commas even in functions

Case 2

before format:

func rotationAngle(_ x: Float,
                   _ y: Float) -> Float {
  return x * (.pi / 180) + y
}

current after format:

func rotationAngle(
  _ x: Float,
  _ y: Float) -> Float
{
  return x * (.pi / 180) + y
}

desired after format:

func rotationAngle(_ x: Float, _ y: Float) -> Float {
  return x * (.pi / 180) + y
}

With prettier, making a new line after the first function param, prettier will revert the params back to one line as long as they can fit the user-set max line-width (80 by default but I use 100)

mesqueeb commented 6 months ago

@nicklockwood I would like to try my hand at contributing this rule.

questions:

  1. I'm not sure if it should be a general --indentationstyle prettier rule or be more specifically aimed at function parameters.
    • I believe a more general indentationstyle approach to the rule is better, I can start by making sure it works well for the function parameters, open a draft PR, keep on building upon that for the other indentation parts.
  2. is there an existing rule for indentation in general or for function parameters specifically that I could look at, duplicate and tweak to build upon? Or better I start from scratch.
  3. Prettier has a --print-width option which is pretty important to decide if it should format the function params like case 1 or case 2 above. Since Prettier has less freedom, the existence of a max-width is important for prettier to decide wether to put stuff on one line or forcing multiple lines. Do you have any advice on implementing this as well, or shall I perhaps start my implementation with a fixed 100 character limit per line, and we can see in the future how to make this a user-settable option.

Thanks a lot! I look forward to start contributing the strict implementation similar to prettier. 🎉

nicklockwood commented 6 months ago

@mesqueeb in general I'm not opposed to making the function wrapping rule stricter, or at least supporting a mode of operation where it behaves more strictly. I think some of the behavior you want is actually already implemented, it's just behind options that are turned off by default.

The rule that handles wrapping function parameters is called wrapArguments and it has a lot of configuration options, which you can see here: https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#wrapArguments

Setting the --wrapparameters before-first option (the default is preserve) handles your Case 1 already.

What it won't do is unwrap function arguments again if they would fit on one line (your Case 2). That should possibly be implemented by a separate rule called unwrapArguments. The rules wouldn't conflict because only one case would ever apply to a given function, depending on the specified max line length.

  1. Prettier has a --print-width option

In SwiftFormat this option is called --maxwidth. It's listed undet the options for the wrap rule: https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#wrap

(The wrap rule internally invokes the wrapArguments rule for any line that exceeds the max width)