Skip to content

Commit 03431b8

Browse files
committed
Compare against expected output for formatting tests
The formatting request tests currently test for the specific list of changes returned by the server, but that list is tied to the differences algorithm used by the standard library, which isn't guaranteed to be stable. This change compares the expected result with a new string generated by applying the list of changes, which is stable.
1 parent 47ca76b commit 03431b8

File tree

2 files changed

+121
-37
lines changed

2 files changed

+121
-37
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
package import LanguageServerProtocol
14+
15+
extension String {
16+
/// Returns a new string obtained by applying the given text edits to this
17+
/// string.
18+
package func applying(_ edits: [TextEdit]) -> String {
19+
let sortedEdits = edits.sorted { lhs, rhs in
20+
lhs.range.lowerBound < rhs.range.lowerBound
21+
}
22+
23+
let lines = self.split(separator: /\v/, omittingEmptySubsequences: false)
24+
var result = ""
25+
var currentLine = 0
26+
var currentPos = lines[0].startIndex
27+
28+
Edits: for edit in sortedEdits {
29+
// Copy any unchanged lines into the result
30+
while currentLine < edit.range.lowerBound.line {
31+
result.append(String(lines[currentLine][currentPos...]))
32+
result.append("\n")
33+
currentLine += 1
34+
if currentLine == lines.count { break Edits }
35+
currentPos = lines[currentLine].startIndex
36+
}
37+
38+
// Copy any prefix in this line into the result
39+
let editStart = lines[currentLine].utf16
40+
.index(lines[currentLine].startIndex, offsetBy: edit.range.lowerBound.utf16index)
41+
let prefixRange = currentPos..<editStart
42+
let prefix = lines[currentLine][prefixRange]
43+
result += prefix
44+
45+
// Add the new text (if any)
46+
result += edit.newText
47+
48+
// Prepare the next cursor position
49+
currentLine = edit.range.upperBound.line
50+
currentPos = lines[currentLine].utf16
51+
.index(lines[currentLine].startIndex, offsetBy: edit.range.upperBound.utf16index)
52+
}
53+
54+
// Copy any remainder
55+
while currentLine < lines.count {
56+
result.append(String(lines[currentLine][currentPos...]))
57+
result.append("\n")
58+
currentLine += 1
59+
if currentLine == lines.count { break }
60+
currentPos = lines[currentLine].startIndex
61+
}
62+
63+
return result
64+
}
65+
}

Tests/SourceKitLSPTests/FormattingTests.swift

Lines changed: 56 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,13 @@ final class FormattingTests: XCTestCase {
2525
let testClient = try await TestSourceKitLSPClient()
2626
let uri = DocumentURI(for: .swift)
2727

28-
let positions = testClient.openDocument(
29-
"""
28+
let source = """
3029
struct S {
3130
1️⃣var foo: 2️⃣ 3️⃣Int
3231
4️⃣var bar: Int
3332
}5️⃣
34-
""",
35-
uri: uri
36-
)
33+
"""
34+
testClient.openDocument(source, uri: uri)
3735

3836
let response = try await testClient.send(
3937
DocumentFormattingRequest(
@@ -43,14 +41,19 @@ final class FormattingTests: XCTestCase {
4341
)
4442

4543
let edits = try XCTUnwrap(response)
44+
let (_, unmarkedSource) = extractMarkers(source)
45+
let formattedSource = unmarkedSource.applying(edits)
46+
4647
XCTAssertEqual(
47-
edits,
48-
[
49-
TextEdit(range: Range(positions["1️⃣"]), newText: " "),
50-
TextEdit(range: positions["2️⃣"]..<positions["3️⃣"], newText: ""),
51-
TextEdit(range: Range(positions["4️⃣"]), newText: " "),
52-
TextEdit(range: Range(positions["5️⃣"]), newText: "\n"),
53-
]
48+
formattedSource,
49+
"""
50+
struct S {
51+
var foo: Int
52+
var bar: Int
53+
}
54+
55+
56+
"""
5457
)
5558
}
5659

@@ -217,15 +220,13 @@ final class FormattingTests: XCTestCase {
217220
let testClient = try await TestSourceKitLSPClient()
218221
let uri = DocumentURI(for: .swift)
219222

220-
let positions = testClient.openDocument(
221-
"""
223+
let source = """
222224
1️⃣public 2️⃣extension Example {
223225
3️⃣func function() {}
224226
}
225227
226-
""",
227-
uri: uri
228-
)
228+
"""
229+
testClient.openDocument(source, uri: uri)
229230

230231
let response = try await testClient.send(
231232
DocumentFormattingRequest(
@@ -235,21 +236,26 @@ final class FormattingTests: XCTestCase {
235236
)
236237

237238
let edits = try XCTUnwrap(response)
239+
let (_, unmarkedSource) = extractMarkers(source)
240+
let formattedSource = unmarkedSource.applying(edits)
241+
238242
XCTAssertEqual(
239-
edits,
240-
[
241-
TextEdit(range: positions["1️⃣"]..<positions["2️⃣"], newText: ""),
242-
TextEdit(range: Range(positions["3️⃣"]), newText: "public "),
243-
]
243+
formattedSource,
244+
"""
245+
extension Example {
246+
public func function() {}
247+
}
248+
249+
250+
"""
244251
)
245252
}
246253

247254
func testMultiLineStringInsertion() async throws {
248255
let testClient = try await TestSourceKitLSPClient()
249256
let uri = DocumentURI(for: .swift)
250257

251-
let positions = testClient.openDocument(
252-
#"""
258+
let source = #"""
253259
_ = [
254260
Node(
255261
documentation: """
@@ -268,9 +274,8 @@ final class FormattingTests: XCTestCase {
268274
)
269275
]
270276
271-
"""#,
272-
uri: uri
273-
)
277+
"""#
278+
testClient.openDocument(source, uri: uri)
274279

275280
let response = try await testClient.send(
276281
DocumentFormattingRequest(
@@ -280,18 +285,32 @@ final class FormattingTests: XCTestCase {
280285
)
281286

282287
let edits = try XCTUnwrap(response)
288+
let (_, unmarkedSource) = extractMarkers(source)
289+
let formattedSource = unmarkedSource.applying(edits)
290+
283291
XCTAssertEqual(
284-
edits,
285-
[
286-
TextEdit(range: Range(positions["1️⃣"]), newText: " "),
287-
TextEdit(range: Range(positions["2️⃣"]), newText: " "),
288-
TextEdit(range: Range(positions["3️⃣"]), newText: " "),
289-
TextEdit(range: Range(positions["4️⃣"]), newText: " "),
290-
TextEdit(range: Range(positions["5️⃣"]), newText: " "),
291-
TextEdit(range: Range(positions["6️⃣"]), newText: "\n"),
292-
TextEdit(range: positions["7️⃣"]..<positions["8️⃣"], newText: ""),
293-
TextEdit(range: positions["9️⃣"]..<positions["🔟"], newText: ""),
292+
formattedSource,
293+
#"""
294+
_ = [
295+
Node(
296+
documentation: """
297+
A
298+
B
299+
C
300+
""",
301+
children: [
302+
Child(
303+
documentation: """
304+
A
305+
306+
"""
307+
)
308+
]
309+
)
294310
]
311+
312+
313+
"""#
295314
)
296315
}
297316

0 commit comments

Comments
 (0)