Skip to content

Add support for InlineArray type sugar #1043

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1732,6 +1732,14 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
return .visitChildren
}

override func visit(_ node: InlineArrayTypeSyntax) -> SyntaxVisitorContinueKind {
after(node.leftSquare, tokens: .break(.open, size: 0), .open)
before(node.separator, tokens: .space)
after(node.separator, tokens: .break)
before(node.rightSquare, tokens: .break(.close, size: 0), .close)
return .visitChildren
}

override func visit(_ node: TupleTypeSyntax) -> SyntaxVisitorContinueKind {
after(node.leftParen, tokens: .break(.open, size: 0), .open)
before(node.rightParen, tokens: .break(.close, size: 0), .close)
Expand Down
68 changes: 68 additions & 0 deletions Tests/SwiftFormatTests/PrettyPrint/ArrayDeclTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -302,4 +302,72 @@ final class ArrayDeclTests: PrettyPrintTestCase {

assertPrettyPrintEqual(input: input, expected: expected, linelength: 32)
}

func testInlineArrayTypeSugar() {
let input =
"""
let a: [3 of Int]
let a: [[3 of Int]]
let a: [3 of [3 of Int]]
let a: [n of Int]
let fiveIntegers: [5 of _] = .init(repeating: 99)
let fourBytes: [_ of Int8] = [1, 2, 3, 4]
let fourIntegers: [_ of _] = [1, 2, 3, 4]
let fiveDoubles = [5 of _](repeating: 1.23)

"""

let expected =
"""
let a: [3 of Int]
let a: [[3 of Int]]
let a: [3 of [3 of Int]]
let a: [n of Int]
let fiveIntegers: [5 of _] = .init(repeating: 99)
let fourBytes: [_ of Int8] = [1, 2, 3, 4]
let fourIntegers: [_ of _] = [1, 2, 3, 4]
let fiveDoubles = [5 of _](repeating: 1.23)

"""

assertPrettyPrintEqual(input: input, expected: expected, linelength: 80)
}

func testInlineArrayTypeSugarWhenLineLengthExceeded() {
let input =
"""
let a: [3 of VeryLongGenericTypeNameThatCausesWrapping]
let a: [3 of [3 of VeryLongGenericTypeNameThatCausesWrapping]]
let a = [3 of VeryLongGenericTypeNameThatCausesWrapping](repeating: foo)

"""

let expected =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, the last two of these look incorrect. I would expect the following:

let a:
  [3 of
    VeryLongGenericTypeNameThatCausesWrapping]
let a:
  [3 of
    [3 of
      VeryLongGenericTypeNameThatCausesWrapping]]
      // ^ needed to be indented more
let a =
  [
    3 of
      VeryLongGenericTypeNameThatCausesWrapping
  ](
    repeating:
      foo
  )

The first two are still a little weird, but is similar to how we handle ArrayTypeSyntax—that is, we don't handle it at all and just let whatever's inside it determine how it wraps. That means that even outside of inline array sugar, the thing inside the brackets in let a: [LongThing] wraps very differently from let a = [LongThing](). Let's ignore that for now.

The last one is sort of tricky, but we need that line break before the ] in that case to satisfy our other rules about how continuation lines force a break if there would be an indented line following it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Array types are parsed as ArrayTypeSyntax when used in type declarations, and as ArrayExprSyntax when used with initializers.
This causes formatting differences between let a: [LongThing] and let a = [LongThing]().

On the other hand, InlineArray is always parsed as InlineArrayTypeSyntax, regardless of context.
So if we want to match the formatting behavior of regular arrays, we may need to make InlineArrayTypeSyntax explicitly check whether its parent conforms to CallingExprSyntaxProtocol.

What do you think about having a consistent line-breaking behavior for inline arrays—regardless of where they’re used—like in the latest commit?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I suppose that's a reasonable default for now. It's not optimal, but it'll at least always be "correct" (in terms of following our line wrapping rules), and the chances of someone having this kind of situation aren't that high. We can revisit it later with some of the other types of nodes. Thanks!

Copy link
Member Author

@TTOzzi TTOzzi Jul 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line breaks may seem excessive because the linelength limit is set to an extremely low value (5),
but with a more reasonable setting (like 40), the output looks like this:

      let a:
        [3 of
          VeryLongGenericTypeNameThatCausesWrapping]
      let a:
        [3 of
          [3 of
          VeryLongGenericTypeNameThatCausesWrapping]]
          // ^ It still seems like we need to add some indentation here, though 😅 
      let a = [3 of
        VeryLongGenericTypeNameThatCausesWrapping](
          repeating: foo)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, it looks like that with the latest commit? I wouldn't expect that—the .open/.close groups you added inside the brackets should always force 3 of onto the next line if any part of that group breaks, and it should also force the closing bracket to wrap down.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, my apologies — I ran the tests after accidentally rolling back the commit.
It's working as expected now. Sorry for the confusion 🙇

"""
let a:
[
3 of
VeryLongGenericTypeNameThatCausesWrapping
]
let a:
[
3 of
[
3 of
VeryLongGenericTypeNameThatCausesWrapping
]
]
let a =
[
3 of
VeryLongGenericTypeNameThatCausesWrapping
](
repeating:
foo
)

"""

assertPrettyPrintEqual(input: input, expected: expected, linelength: 5)
}
}
Loading