Skip to content

Commit 6cf8188

Browse files
committed
Store the replacement string in IncrementalEdit
IIRC the main reason that `IncrementalEdit` didn’t contain the replacement string was that the edits needed to be transferred over XPC at some point and transferring strings over XPC is always a little tricky. Since the actual contents weren’t actually needed, the actual contents weren’t actually needed. I would like to use `ConcurrentEdits(fromSequential:)` in SourceKit-LSP and for that I need the actual replacement contents. Instead of rolling our own sequential to concurrent replacement (which is non-trivial), let’s re-use the one in SwiftSyntax.
1 parent 25ce3a2 commit 6cf8188

File tree

6 files changed

+127
-98
lines changed

6 files changed

+127
-98
lines changed

Release Notes/600.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
# Swift Syntax 600 Release Notes
22

33
## New APIs
4-
- FixIt now has a new computed propery named edits
5-
- Description: the edits represent the non-overlapping textual edits that need to be performed when the Fix-It is applied.
4+
- FixIt now has a new computed property named `edits`
5+
- Description: the `edits` represent the non-overlapping textual `edits` that need to be performed when the Fix-It is applied.
66
- Issue: https://github.com/apple/sourcekit-lsp/issues/909
77
- Pull Request: https://github.com/apple/swift-syntax/pull/2314
88

9-
- SourceEdit
10-
- Description: SourceEdit has been moved from SwiftRefactor to SwiftSyntax
9+
- `SourceEdit`
10+
- Description: `SourceEdit` has been moved from SwiftRefactor to SwiftSyntax
1111
- Issue: https://github.com/apple/sourcekit-lsp/issues/909
1212
- Pull Request: https://github.com/apple/swift-syntax/pull/2314
1313

@@ -58,6 +58,10 @@
5858
- Description: Uses heuristics to infer the indentation width used in a syntax tree.
5959
- Pull Request: https://github.com/apple/swift-syntax/pull/2514
6060

61+
- `IncrementalEdit` stores replacement text
62+
- Description: `IncrementalEdit` used to store the range that was replaced and the length of the replacement but not the replacement bytes by itself. `IncrementalEdit` now has a `replacement` property that contains the replacement bytes.
63+
- Pull Request: https://github.com/apple/swift-syntax/pull/2527
64+
6165
## API Behavior Changes
6266

6367
## Deprecations

Sources/SwiftParser/IncrementalParseTransition.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,17 +347,22 @@ public struct ConcurrentEdits: Sendable {
347347
if existingEdit.replacementRange.intersectsOrTouches(editToAdd.range) {
348348
let intersectionLength =
349349
existingEdit.replacementRange.intersected(editToAdd.range).length
350+
let replacement: [UInt8]
351+
replacement =
352+
existingEdit.replacement.prefix(max(0, editToAdd.offset - existingEdit.replacementRange.offset))
353+
+ editToAdd.replacement
354+
+ existingEdit.replacement.suffix(max(0, existingEdit.replacementRange.endOffset - editToAdd.endOffset))
350355
editToAdd = IncrementalEdit(
351356
offset: Swift.min(existingEdit.offset, editToAdd.offset),
352357
length: existingEdit.length + editToAdd.length - intersectionLength,
353-
replacementLength: existingEdit.replacementLength + editToAdd.replacementLength - intersectionLength
358+
replacement: replacement
354359
)
355360
editIndicesMergedWithNewEdit.append(index)
356361
} else if existingEdit.offset < editToAdd.endOffset {
357362
editToAdd = IncrementalEdit(
358363
offset: editToAdd.offset - existingEdit.replacementLength + existingEdit.length,
359364
length: editToAdd.length,
360-
replacementLength: editToAdd.replacementLength
365+
replacement: editToAdd.replacement
361366
)
362367
}
363368
}

Sources/SwiftSyntax/Utils.swift

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,12 @@ public struct ByteSourceRange: Equatable, Sendable {
5050
public struct IncrementalEdit: Equatable, Sendable {
5151
/// The byte range of the original source buffer that the edit applies to.
5252
public let range: ByteSourceRange
53+
54+
/// The UTF-8 bytes that should be inserted as part of the edit
55+
public let replacement: [UInt8]
56+
5357
/// The length of the edit replacement in UTF8 bytes.
54-
public let replacementLength: Int
58+
public var replacementLength: Int { replacement.count }
5559

5660
public var offset: Int { return range.offset }
5761

@@ -64,14 +68,25 @@ public struct IncrementalEdit: Equatable, Sendable {
6468
return ByteSourceRange(offset: offset, length: replacementLength)
6569
}
6670

71+
@available(*, deprecated, message: "Use IncrementalEdit(range:replacement:) instead")
6772
public init(range: ByteSourceRange, replacementLength: Int) {
6873
self.range = range
69-
self.replacementLength = replacementLength
74+
self.replacement = Array(repeating: UInt8(ascii: " "), count: replacementLength)
7075
}
7176

77+
@available(*, deprecated, message: "Use IncrementalEdit(offset:length:replacement:) instead")
7278
public init(offset: Int, length: Int, replacementLength: Int) {
7379
self.range = ByteSourceRange(offset: offset, length: length)
74-
self.replacementLength = replacementLength
80+
self.replacement = Array(repeating: UInt8(ascii: " "), count: replacementLength)
81+
}
82+
83+
public init(offset: Int, length: Int, replacement: [UInt8]) {
84+
self.range = ByteSourceRange(offset: offset, length: length)
85+
self.replacement = replacement
86+
}
87+
88+
public init(offset: Int, length: Int, replacement: String) {
89+
self.init(offset: offset, length: length, replacement: Array(replacement.utf8))
7590
}
7691

7792
public func intersectsOrTouchesRange(_ other: ByteSourceRange) -> Bool {

Sources/_SwiftSyntaxTestSupport/IncrementalParseTestUtils.swift

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,7 @@ public func extractEditsAndSources(from source: String) -> (edits: ConcurrentEdi
182182
from: source.index(after: startIndex),
183183
to: separateIndex
184184
),
185-
replacementLength: source.utf8.distance(
186-
from: source.index(after: separateIndex),
187-
to: endIndex
188-
)
185+
replacement: Array(source.utf8[source.index(after: separateIndex)..<endIndex])
189186
)
190187
originalSource += source[source.index(after: startIndex)..<separateIndex]
191188

@@ -213,12 +210,8 @@ public func extractEditsAndSources(from source: String) -> (edits: ConcurrentEdi
213210
public func applyEdits(
214211
_ edits: [IncrementalEdit],
215212
concurrent: Bool,
216-
to testString: String,
217-
replacementChar: Character = "?"
213+
to testString: String
218214
) -> String {
219-
guard let replacementAscii = replacementChar.asciiValue else {
220-
fatalError("replacementChar must be an ASCII character")
221-
}
222215
var edits = edits
223216
if concurrent {
224217
XCTAssert(ConcurrentEdits._isValidConcurrentEditArray(edits))
@@ -232,7 +225,7 @@ public func applyEdits(
232225
for edit in edits {
233226
assert(edit.endOffset <= bytes.count)
234227
bytes.removeSubrange(edit.offset..<edit.endOffset)
235-
bytes.insert(contentsOf: [UInt8](repeating: replacementAscii, count: edit.replacementLength), at: edit.offset)
228+
bytes.insert(contentsOf: edit.replacement, at: edit.offset)
236229
}
237230
return String(bytes: bytes, encoding: .utf8)!
238231
}

0 commit comments

Comments
 (0)