Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-13187] swift-format formats multiline pattern binding strangely #317

Closed
karwa opened this issue Jul 9, 2020 · 6 comments
Closed

[SR-13187] swift-format formats multiline pattern binding strangely #317

karwa opened this issue Jul 9, 2020 · 6 comments

Comments

@karwa
Copy link
Contributor

karwa commented Jul 9, 2020

Previous ID SR-13187
Radar None
Original Reporter @karwa
Type Improvement
Status Resolved
Resolution Duplicate
Additional Detail from JIRA
Votes 0
Component/s swift-format
Labels Improvement
Assignee None
Priority Medium

md5: efc73f24a3614c4076eeaca59176a65c

duplicates:

  • SR-12844 Scope-based indentation for if/guard/etc

Issue Description:

The following code:

        if let percentEncodedByte1 = iter.next(), ASCII(percentEncodedByte1)?.isHexDigit != true,
           let percentEncodedByte2 = iter.next(), ASCII(percentEncodedByte2)?.isHexDigit != true {
          callback.validationError(opaqueHost: .invalidPercentEscaping)
        }

Gets formatted as:

        if let percentEncodedByte1 = iter.next(), ASCII(percentEncodedByte1)?.isHexDigit != true,
          let percentEncodedByte2 = iter.next(), ASCII(percentEncodedByte2)?.isHexDigit != true
        {
          callback.validationError(opaqueHost: .invalidPercentEscaping)
        }

I don't like how it places the opening brace on its own line. Is there a way to control that?

Also, it would be nice if it would preserve the alignment of the 'let's.

@karwa
Copy link
Contributor Author

karwa commented Jul 9, 2020

@allevato

@allevato
Copy link
Collaborator

allevato commented Jul 9, 2020

This is the intended behavior under the (current) default configuration. The reasoning behind this is:

  • Continuation lines that do not begin with closing delimiters are indented at a fixed +2 from the line where their starting line.

  • When an open brace would be placed at the end of an indented continuation line, it is moved onto its own line so that the body of the brace-delimited block (which is also indented +2 from the starting line of that brace) are visually distinguishable from the preceding continuation lines.

  • Treating continuation line indentation as an invariant, the ability to distinguish the body of the statement from the continuation lines above it was/is felt to be a much higher priority for readability than always keeping the opening brace on the same line.

We can certainly add a configuration option to use keyword-length-based indentation variable for some statements and tie open-brace placement to that, though. I think that makes this request a dupe of SR-12844.

@karwa
Copy link
Contributor Author

karwa commented Jul 10, 2020

@allevato

> When an open brace would be placed at the end of an indented continuation line, it is moved onto its own line so that the body of the brace-delimited block (which is also indented +2 from the starting line of that brace) are visually distinguishable from the preceding continuation lines.

If the brace-delimited block is indented from the line with the open brace, they would still be visually distinguishable:

if let someCondition = blah(),
  someOtherConditions(),
  yetMoreConditions {
     braceDelimitedBlock()
     thisIsOkayIMO()
}

To me, that looks better than bumping the opening brace to a new line. It would be nice if there were some flag to say that I consider that sufficient.

@allevato
Copy link
Collaborator

I'm not personally aware of any indentation style in common use among curly-brace languages (that's not to say one doesn't exist) that does what you describe; bodies of brace delimited blocks are always indented uniformly from the level of the containing syntactic construct, not the level of the line that happened to contain the brace.

Instead, it's much more common to differentiate by having the continuation indent be larger than the block indent:

if let someCondition = blah(),
    someOtherConditions(),
    yetMoreConditions {
  braceDelimitedBlock()
}

Between the two choices, I'd much rather support this one as a configurable option, since it has significant precedent across other similar languages/style guides.

@karwa
Copy link
Contributor Author

karwa commented Jul 10, 2020

Unfortunately, that would not compose well with SR-12844, which would align the conditions. Indenting from the continuation line would:

if let someCondition = blah(),
   let anotherCondition = blah(),
   yetMoreConditions {
     braceDelimitedBlock()
}

Looks both tidy and unambiguous, IMHO.

@allevato
Copy link
Collaborator

To clarify, they wouldn't be intended to compose at all; if we make this configurable, users would choose either the current indentation strategy, the scope-based indentation strategy, or a fixed continuation line indentation strategy.

Your suggested approach suffers from the problem that conditions/bindings with complex expressions that themselves introduce a lot of nesting would potentially introduce a large shift in the indentation of the body:

if let someValue = thingThatReturnsAnOptional(),
   let otherValue = anotherOptionalReturningFunction(),
   let nextValue = someValue.method(
     firstTerm * secondTerm + thirdTerm - otherValue.method(
       aReallyLongArgumentName + anotherReallyLongArgumentName
         * moreReallyLongArgumentNames)) {
           bodyOfTheBlockStartsHere()
}

Whereas the other approaches (the current implementation, or the continuation-indent-is-larger-than-block-indent) cause the body to be pulled back to a reasonable location.

One can argue that if a condition is so complex it should be hoisted into a temporary variable or separate function anyway, and that may work for some basic conditions, but due to the way the scopes and sequencing of conditional bindings work in Swift, trying to pull those out might end up having a negative impact on the readability of the code.

Ultimately, the degree to which we support fine-tuning configurability in swift-format won't be unlimited because every option that gets added potentially interacts with other options, which can lead to a combinatorial explosion in the condition space we have to consider when deciding how to lay out certain nodes, as well as a combinatorial explosion of the tests to ensure that we don't break things for some particular configuration. In this case, I don't think the tidiness/unambiguity is as clear as the alternatives, nor is there much precedent for it.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@shahmishal shahmishal transferred this issue from apple/swift May 9, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants