Skip to content

Commit a002631

Browse files
authored
Merge pull request #2014 from ahoppen/ahoppen/index-at
Add a function to get the `SyntaxChildrenIndex` of the n-th element in a `SyntaxCollection`
2 parents 2980946 + cfec21e commit a002631

File tree

7 files changed

+42
-49
lines changed

7 files changed

+42
-49
lines changed

Sources/SwiftSyntax/SyntaxChildren.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,10 @@ struct RawSyntaxChildren: BidirectionalCollection {
189189
}
190190

191191
func index(after index: SyntaxChildrenIndex) -> SyntaxChildrenIndex {
192-
let node = parent.layoutView!.children[Int(index.data!.indexInParent)]
192+
guard let indexData = index.data else {
193+
preconditionFailure("Cannot get the index after the end index")
194+
}
195+
let node = parent.layoutView!.children[Int(indexData.indexInParent)]
193196
return self.index(index, advancedBy: node)
194197
}
195198

Sources/SwiftSyntax/SyntaxCollection.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,28 @@ extension SyntaxCollection {
105105
return node.indexInParent
106106
}
107107

108+
/// Returns the index of the n-th element in this collection.
109+
///
110+
/// The behavior is undefined if `offset` is greater than the number of
111+
/// elements in this collection.
112+
///
113+
/// - Complexity: O(`offset`) because all previous element need to be iterated
114+
/// to find the byte offset of the `offset`-th node within the source file.
115+
///
116+
/// - Note: Because getting the `n`-th element in a ``SyntaxCollection`` is
117+
/// not O(1), ``SyntaxCollection`` doesn’t provide a subscript to retrieve
118+
/// the `n`-th element. Such a subscript would mask the complexity of
119+
/// getting the `n`-th element.
120+
public func index(at offset: Int) -> SyntaxChildrenIndex {
121+
if offset > self.count / 2 {
122+
// If we are closer to the end of the collection, it's more efficient to
123+
// calculate the index starting from the back.
124+
return self.index(self.endIndex, offsetBy: -(self.count - offset))
125+
} else {
126+
return self.index(self.startIndex, offsetBy: offset)
127+
}
128+
}
129+
108130
/// Creates a new collection by appending the provided syntax element
109131
/// to the children.
110132
///

Sources/_SwiftSyntaxTestSupport/SyntaxCollection+AtIndex.swift.swift

Lines changed: 0 additions & 33 deletions
This file was deleted.

Tests/SwiftRefactorTest/ExpandEditorPlaceholdersTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ fileprivate func assertRefactorPlaceholderCall(
111111
) throws {
112112
var parser = Parser(expr)
113113
let call = try XCTUnwrap(ExprSyntax.parse(from: &parser).as(FunctionCallExprSyntax.self), file: file, line: line)
114-
let arg = try XCTUnwrap(call.arguments[placeholder].as(LabeledExprSyntax.self), file: file, line: line)
114+
let arg = try XCTUnwrap(call.arguments[call.arguments.index(at: placeholder)].as(LabeledExprSyntax.self), file: file, line: line)
115115
let token: TokenSyntax = try XCTUnwrap(arg.expression.as(EditorPlaceholderExprSyntax.self), file: file, line: line).placeholder
116116

117117
try assertRefactor(token, context: (), provider: ExpandEditorPlaceholders.self, expected: [SourceEdit.replace(call, with: expected)], file: file, line: line)
@@ -126,7 +126,7 @@ fileprivate func assertRefactorPlaceholderToken(
126126
) throws {
127127
var parser = Parser(expr)
128128
let call = try XCTUnwrap(ExprSyntax.parse(from: &parser).as(FunctionCallExprSyntax.self), file: file, line: line)
129-
let arg = try XCTUnwrap(call.arguments[placeholder].as(LabeledExprSyntax.self), file: file, line: line)
129+
let arg = try XCTUnwrap(call.arguments[call.arguments.index(at: placeholder)].as(LabeledExprSyntax.self), file: file, line: line)
130130
let token: TokenSyntax = try XCTUnwrap(arg.expression.as(EditorPlaceholderExprSyntax.self), file: file, line: line).placeholder
131131

132132
try assertRefactor(token, context: (), provider: ExpandEditorPlaceholders.self, expected: [SourceEdit.replace(token, with: expected)], file: file, line: line)

Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ public struct ErrorMacro: DeclarationMacro {
145145
let stringLiteral = firstElement.expression
146146
.as(StringLiteralExprSyntax.self),
147147
stringLiteral.segments.count == 1,
148-
case let .stringSegment(messageString) = stringLiteral.segments[0]
148+
case let .stringSegment(messageString) = stringLiteral.segments.first
149149
else {
150150
throw MacroExpansionErrorMessage("#error macro requires a string literal")
151151
}
@@ -170,7 +170,7 @@ struct DefineBitwidthNumberedStructsMacro: DeclarationMacro {
170170
let stringLiteral = firstElement.expression
171171
.as(StringLiteralExprSyntax.self),
172172
stringLiteral.segments.count == 1,
173-
case let .stringSegment(prefix) = stringLiteral.segments[0]
173+
case let .stringSegment(prefix) = stringLiteral.segments.first
174174
else {
175175
throw MacroExpansionErrorMessage(
176176
"#bitwidthNumberedStructs macro requires a string literal"

Tests/SwiftSyntaxTest/AbsolutePositionTests.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ public class AbsolutePositionTests: XCTestCase {
3333
statements: CodeBlockItemListSyntax(l),
3434
endOfFileToken: .endOfFileToken()
3535
)
36-
_ = root.statements[idx].position
37-
_ = root.statements[idx].totalLength.utf8Length
38-
_ = root.statements[idx].positionAfterSkippingLeadingTrivia
36+
let statements = root.statements
37+
_ = statements[statements.index(at: idx)].position
38+
_ = statements[statements.index(at: idx)].totalLength.utf8Length
39+
_ = statements[statements.index(at: idx)].positionAfterSkippingLeadingTrivia
3940
}
4041

4142
static let leadingTrivia = Trivia(pieces: [
@@ -76,7 +77,7 @@ public class AbsolutePositionTests: XCTestCase {
7677
let root = self.createSourceFile(idx + 1)
7778
XCTAssertEqual(3, root.leadingTrivia.count)
7879
XCTAssertEqual(0, root.trailingTrivia.count)
79-
let state = root.statements[idx]
80+
let state = root.statements[root.statements.index(at: idx)]
8081
XCTAssertEqual(3, state.leadingTrivia.count)
8182
XCTAssertEqual(2, state.trailingTrivia.count)
8283
XCTAssertEqual(
@@ -137,7 +138,7 @@ public class AbsolutePositionTests: XCTestCase {
137138
let filePath = "/tmp/test.swift"
138139
let root = self.createSourceFile(2)
139140
let converter = SourceLocationConverter(fileName: filePath, tree: root)
140-
let secondReturnStmt = root.statements[1]
141+
let secondReturnStmt = root.statements[root.statements.index(at: 1)]
141142
let startLoc = secondReturnStmt.startLocation(converter: converter)
142143
XCTAssertEqual(startLoc.line, 8)
143144
XCTAssertEqual(startLoc.column, 1)

Tests/SwiftSyntaxTest/SyntaxCollectionsTests.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public class SyntaxCollectionsTests: XCTestCase {
9090
assertSyntaxCollectionManipulation(
9191
initialElements: [0, 1, 2],
9292
transformation: {
93-
let index = $0.elements.index(after: $0.elements.startIndex)
93+
let index = $0.elements.index(at: 1)
9494
$0.elements.insert(intElement(5), at: index)
9595
},
9696
expectedElements: [0, 5, 1, 2]
@@ -119,7 +119,7 @@ public class SyntaxCollectionsTests: XCTestCase {
119119
assertSyntaxCollectionManipulation(
120120
initialElements: [0, 1, 2],
121121
transformation: {
122-
let index = $0.elements.index(after: $0.elements.startIndex)
122+
let index = $0.elements.index(at: 1)
123123
$0.elements.insert(contentsOf: [intElement(5), intElement(6)], at: index)
124124
},
125125
expectedElements: [0, 5, 6, 1, 2]
@@ -148,7 +148,7 @@ public class SyntaxCollectionsTests: XCTestCase {
148148
assertSyntaxCollectionManipulation(
149149
initialElements: [0, 1, 2],
150150
transformation: {
151-
let index = $0.elements.index(after: $0.elements.startIndex)
151+
let index = $0.elements.index(at: 1)
152152
$0.elements.remove(at: index)
153153
},
154154
expectedElements: [0, 2]
@@ -157,7 +157,7 @@ public class SyntaxCollectionsTests: XCTestCase {
157157
assertSyntaxCollectionManipulation(
158158
initialElements: [0, 1, 2],
159159
transformation: {
160-
let index = $0.elements.index($0.elements.startIndex, offsetBy: 2)
160+
let index = $0.elements.index(at: 2)
161161
$0.elements.remove(at: index)
162162
},
163163
expectedElements: [0, 1]
@@ -169,7 +169,7 @@ public class SyntaxCollectionsTests: XCTestCase {
169169
initialElements: [0, 1, 2],
170170
transformation: {
171171
let startIndex = $0.elements.startIndex
172-
let endIndex = $0.elements.index(after: $0.elements.startIndex)
172+
let endIndex = $0.elements.index(at: 1)
173173
$0.elements.removeSubrange(startIndex..<endIndex)
174174
},
175175
expectedElements: [1, 2]
@@ -179,7 +179,7 @@ public class SyntaxCollectionsTests: XCTestCase {
179179
initialElements: [0, 1, 2],
180180
transformation: {
181181
let startIndex = $0.elements.startIndex
182-
let endIndex = $0.elements.index($0.elements.startIndex, offsetBy: 2)
182+
let endIndex = $0.elements.index(at: 2)
183183
$0.elements.removeSubrange(startIndex..<endIndex)
184184
},
185185
expectedElements: [2]

0 commit comments

Comments
 (0)