Skip to content

Commit 84aed58

Browse files
authored
Merge pull request #2604 from kimdv/kimdv/2532-merge-incrementaledit-and-sourceedit
Merge `IncrementalEdit` and`SourceEdit`
2 parents caab6bb + 0c04746 commit 84aed58

File tree

9 files changed

+376
-177
lines changed

9 files changed

+376
-177
lines changed

Release Notes/600.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@
124124
- Description: Types can have multiple specifiers now and the syntax tree has been modified to reflect that.
125125
- Pull request: https://github.com/apple/swift-syntax/pull/2433
126126

127-
- ` CanImportExprSyntax` and `CanImportVersionInfoSyntax`
127+
- `CanImportExprSyntax` and `CanImportVersionInfoSyntax`
128128
- Description: Instead of parsing `canImport` inside `#if` directives as a special expression node, parse it as a functionc call expression. This is in-line with how the `swift(>=6.0)` and `compiler(>=6.0)` directives are parsed.
129129
- Pull request: https://github.com/apple/swift-syntax/pull/2025
130130

@@ -137,7 +137,7 @@
137137
- Pull request: https://github.com/apple/swift-syntax/pull/2587
138138

139139
- `ByteSourceRange` deprecated in favor of `Range<AbsolutePosition>`
140-
- Description: `ByteSourceRange` is being dropped for `Range<AbsolutePosition>`, where the latter clearly signifies that it uses UTF-8 byte positions. `Range<AbsolutePosition>` has deprecated compatibility layers to make it API-compatible with `ByteSourceRange`
140+
- Description: `ByteSourceRange` is being dropped for `Range<AbsolutePosition>`, where the latter clearly signifies that it uses UTF-8 byte positions. `Range<AbsolutePosition>` has deprecated compatibility layers to make it API-compatible with `ByteSourceRange`
141141
- Pull request: https://github.com/apple/swift-syntax/pull/2587
142142

143143
## API-Incompatible Changes

Release Notes/601.md

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111

1212
## Deprecations
1313

14+
- `IncrementalEdit` deprecated in favor of `SourceEdit`
15+
- Description: `IncrementalEdit` is being dropped for `SourceEdit`. `SourceEdit` has deprecated compatibility layers to make it API-compatible with `IncrementalEdit`
16+
- Issue: https://github.com/apple/swift-syntax/issues/2532
17+
- Pull request: https://github.com/apple/swift-syntax/pull/2604
18+
1419
## API-Incompatible Changes
1520

1621
- Moved `Radix` and `IntegerLiteralExprSyntax.radix` from `SwiftRefactor` to `SwiftSyntax`.

Sources/SwiftIDEUtils/FixItApplier.swift

+2-6
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,10 @@ public enum FixItApplier {
7878
// shift it by the current edit's difference in length.
7979
if edit.endUtf8Offset <= remainingEdit.startUtf8Offset {
8080
let startPosition = AbsolutePosition(
81-
utf8Offset: remainingEdit.startUtf8Offset - edit.replacementRange.count + edit.replacementLength
81+
utf8Offset: remainingEdit.startUtf8Offset - edit.replacementRange.count + edit.replacementLength.utf8Length
8282
)
8383
let endPosition = AbsolutePosition(
84-
utf8Offset: remainingEdit.endUtf8Offset - edit.replacementRange.count + edit.replacementLength
84+
utf8Offset: remainingEdit.endUtf8Offset - edit.replacementRange.count + edit.replacementLength.utf8Length
8585
)
8686
return SourceEdit(range: startPosition..<endPosition, replacement: remainingEdit.replacement)
8787
}
@@ -103,10 +103,6 @@ private extension SourceEdit {
103103
return range.upperBound.utf8Offset
104104
}
105105

106-
var replacementLength: Int {
107-
return replacement.utf8.count
108-
}
109-
110106
var replacementRange: Range<Int> {
111107
return startUtf8Offset..<endUtf8Offset
112108
}

Sources/SwiftParser/IncrementalParseTransition.swift

+14-14
Original file line numberDiff line numberDiff line change
@@ -299,11 +299,11 @@ public struct ConcurrentEdits: Sendable {
299299

300300
/// The raw concurrent edits. Are guaranteed to satisfy the requirements
301301
/// stated above.
302-
public let edits: [IncrementalEdit]
302+
public let edits: [SourceEdit]
303303

304304
/// Initialize this struct from edits that are already in a concurrent form
305305
/// and are guaranteed to satisfy the requirements posed above.
306-
public init(concurrent: [IncrementalEdit]) throws {
306+
public init(concurrent: [SourceEdit]) throws {
307307
if !Self.isValidConcurrentEditArray(concurrent) {
308308
throw ConcurrentEditsError.editsNotConcurrent
309309
}
@@ -319,7 +319,7 @@ public struct ConcurrentEdits: Sendable {
319319
/// - insert 'z' at offset 2
320320
/// to '012345' results in 'xyz012345'.
321321

322-
public init(fromSequential sequentialEdits: [IncrementalEdit]) {
322+
public init(fromSequential sequentialEdits: [SourceEdit]) {
323323
do {
324324
try self.init(concurrent: Self.translateSequentialEditsToConcurrentEdits(sequentialEdits))
325325
} catch {
@@ -332,7 +332,7 @@ public struct ConcurrentEdits: Sendable {
332332
/// Construct a concurrent edits struct from a single edit. For a single edit,
333333
/// there is no differentiation between being it being applied concurrently
334334
/// or sequentially.
335-
public init(_ single: IncrementalEdit) {
335+
public init(_ single: SourceEdit) {
336336
do {
337337
try self.init(concurrent: [single])
338338
} catch {
@@ -341,9 +341,9 @@ public struct ConcurrentEdits: Sendable {
341341
}
342342

343343
private static func translateSequentialEditsToConcurrentEdits(
344-
_ edits: [IncrementalEdit]
345-
) -> [IncrementalEdit] {
346-
var concurrentEdits: [IncrementalEdit] = []
344+
_ edits: [SourceEdit]
345+
) -> [SourceEdit] {
346+
var concurrentEdits: [SourceEdit] = []
347347
for editToAdd in edits {
348348
var editToAdd = editToAdd
349349
var editIndicesMergedWithNewEdit: [Int] = []
@@ -353,14 +353,14 @@ public struct ConcurrentEdits: Sendable {
353353
existingEdit.replacementRange.clamped(to: editToAdd.range).length
354354
let replacement: [UInt8]
355355
replacement =
356-
existingEdit.replacement.prefix(
356+
existingEdit.replacementBytes.prefix(
357357
max(0, editToAdd.range.lowerBound.utf8Offset - existingEdit.replacementRange.lowerBound.utf8Offset)
358358
)
359-
+ editToAdd.replacement
360-
+ existingEdit.replacement.suffix(
359+
+ editToAdd.replacementBytes
360+
+ existingEdit.replacementBytes.suffix(
361361
max(0, existingEdit.replacementRange.upperBound.utf8Offset - editToAdd.range.upperBound.utf8Offset)
362362
)
363-
editToAdd = IncrementalEdit(
363+
editToAdd = SourceEdit(
364364
range: Range(
365365
position: Swift.min(existingEdit.range.lowerBound, editToAdd.range.lowerBound),
366366
length: existingEdit.range.length + editToAdd.range.length - intersectionLength
@@ -369,7 +369,7 @@ public struct ConcurrentEdits: Sendable {
369369
)
370370
editIndicesMergedWithNewEdit.append(index)
371371
} else if existingEdit.range.lowerBound < editToAdd.range.upperBound {
372-
editToAdd = IncrementalEdit(
372+
editToAdd = SourceEdit(
373373
range: Range(
374374
position: editToAdd.range.lowerBound + existingEdit.range.length - existingEdit.replacementLength,
375375
length: editToAdd.range.length
@@ -392,7 +392,7 @@ public struct ConcurrentEdits: Sendable {
392392
return concurrentEdits
393393
}
394394

395-
private static func isValidConcurrentEditArray(_ edits: [IncrementalEdit]) -> Bool {
395+
private static func isValidConcurrentEditArray(_ edits: [SourceEdit]) -> Bool {
396396
// Not quite sure if we should disallow creating an `IncrementalParseTransition`
397397
// object without edits but there doesn't seem to be much benefit if we do,
398398
// and there are 'lit' tests that want to test incremental re-parsing without edits.
@@ -412,7 +412,7 @@ public struct ConcurrentEdits: Sendable {
412412
}
413413

414414
/// **Public for testing purposes only**
415-
public static func _isValidConcurrentEditArray(_ edits: [IncrementalEdit]) -> Bool {
415+
public static func _isValidConcurrentEditArray(_ edits: [SourceEdit]) -> Bool {
416416
return isValidConcurrentEditArray(edits)
417417
}
418418
}

Sources/SwiftSyntax/SourceEdit.swift

+62-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -13,22 +13,75 @@
1313
/// A textual edit to the original source represented by a range and a
1414
/// replacement.
1515
public struct SourceEdit: Equatable, Sendable {
16-
/// The half-open range that this edit applies to.
16+
/// The byte range of the original source buffer that the edit applies to.
1717
public let range: Range<AbsolutePosition>
18-
/// The text to replace the original range with. Empty for a deletion.
19-
public let replacement: String
2018

21-
/// Length of the original source range that this edit applies to. Zero if
22-
/// this is an addition.
23-
public var length: SourceLength {
24-
return SourceLength(utf8Length: range.upperBound.utf8Offset - range.lowerBound.utf8Offset)
19+
/// The UTF-8 bytes that should be inserted as part of the edit
20+
public let replacementBytes: [UInt8]
21+
22+
/// A string representation of the replacement
23+
public var replacement: String { return String(decoding: replacementBytes, as: UTF8.self) }
24+
25+
/// The length of the edit replacement in UTF8 bytes.
26+
public var replacementLength: SourceLength { return SourceLength(utf8Length: replacementBytes.count) }
27+
28+
@available(*, deprecated, renamed: "range.lowerBound.utf8Offset")
29+
public var offset: Int { return range.offset }
30+
31+
@available(*, deprecated, renamed: "replacementLength")
32+
public var length: Int { return range.length.utf8Length }
33+
34+
@available(*, deprecated, renamed: "range.upperBound.utf8Offset")
35+
public var endOffset: Int { return range.endOffset }
36+
37+
/// After the edit has been applied the range of the replacement text.
38+
public var replacementRange: Range<AbsolutePosition> {
39+
return Range(position: range.lowerBound, length: replacementLength)
2540
}
2641

2742
/// Create an edit to replace `range` in the original source with
2843
/// `replacement`.
2944
public init(range: Range<AbsolutePosition>, replacement: String) {
3045
self.range = range
31-
self.replacement = replacement
46+
self.replacementBytes = Array(replacement.utf8)
47+
}
48+
49+
public init(range: Range<AbsolutePosition>, replacement: [UInt8]) {
50+
self.range = range
51+
self.replacementBytes = replacement
52+
}
53+
54+
@available(*, deprecated, message: "Use SourceEdit(range:replacement:) instead")
55+
public init(range: ByteSourceRange, replacementLength: Int) {
56+
self.range = AbsolutePosition(utf8Offset: range.offset)..<AbsolutePosition(utf8Offset: range.endOffset)
57+
self.replacementBytes = Array(repeating: UInt8(ascii: " "), count: replacementLength)
58+
}
59+
60+
@available(*, deprecated, message: "Use SourceEdit(range:replacement:) instead")
61+
public init(offset: Int, length: Int, replacementLength: Int) {
62+
self.range = AbsolutePosition(utf8Offset: offset)..<AbsolutePosition(utf8Offset: offset + length)
63+
self.replacementBytes = Array(repeating: UInt8(ascii: " "), count: replacementLength)
64+
}
65+
66+
@available(*, deprecated, message: "Use SourceEdit(range:replacement:) instead")
67+
public init(offset: Int, length: Int, replacement: [UInt8]) {
68+
self.range = AbsolutePosition(utf8Offset: offset)..<AbsolutePosition(utf8Offset: offset + length)
69+
self.replacementBytes = replacement
70+
}
71+
72+
@available(*, deprecated, message: "Use SourceEdit(range:replacement:) instead")
73+
public init(offset: Int, length: Int, replacement: String) {
74+
self.init(offset: offset, length: length, replacement: Array(replacement.utf8))
75+
}
76+
77+
public func intersectsOrTouchesRange(_ other: Range<AbsolutePosition>) -> Bool {
78+
return self.range.upperBound.utf8Offset >= other.lowerBound.utf8Offset
79+
&& self.range.lowerBound.utf8Offset <= other.upperBound.utf8Offset
80+
}
81+
82+
public func intersectsRange(_ other: Range<AbsolutePosition>) -> Bool {
83+
return self.range.upperBound.utf8Offset > other.lowerBound.utf8Offset
84+
&& self.range.lowerBound.utf8Offset < other.upperBound.utf8Offset
3285
}
3386

3487
/// Convenience function to create a textual addition after the given node

Sources/SwiftSyntax/Utils.swift

+3-54
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
@available(*, deprecated, renamed: "SourceEdit")
14+
public typealias IncrementalEdit = SourceEdit
15+
1316
@available(*, deprecated, message: "Use Range<AbsolutePosition> instead")
1417
public typealias ByteSourceRange = Range<AbsolutePosition>
1518

@@ -77,60 +80,6 @@ extension Range<AbsolutePosition> {
7780
}
7881
}
7982

80-
public struct IncrementalEdit: Equatable, Sendable {
81-
/// The byte range of the original source buffer that the edit applies to.
82-
public let range: Range<AbsolutePosition>
83-
84-
/// The UTF-8 bytes that should be inserted as part of the edit
85-
public let replacement: [UInt8]
86-
87-
/// The length of the edit replacement in UTF-8 bytes.
88-
public var replacementLength: SourceLength { SourceLength(utf8Length: replacement.count) }
89-
90-
@available(*, deprecated, message: "Use range.lowerBound.utf8Offset instead")
91-
public var offset: Int { return range.offset }
92-
93-
@available(*, deprecated, message: "Use range.utf8Length instead")
94-
public var length: Int { return range.length.utf8Length }
95-
96-
@available(*, deprecated, message: "Use range.upperBound.utf8Offset instead")
97-
public var endOffset: Int { return range.endOffset }
98-
99-
/// After the edit has been applied the range of the replacement text.
100-
public var replacementRange: Range<AbsolutePosition> {
101-
return Range(position: range.lowerBound, length: replacementLength)
102-
}
103-
104-
@available(*, deprecated, message: "Use IncrementalEdit(range:replacement:) instead")
105-
public init(range: ByteSourceRange, replacementLength: Int) {
106-
self.range = range
107-
self.replacement = Array(repeating: UInt8(ascii: " "), count: replacementLength)
108-
}
109-
110-
@available(*, deprecated, message: "Use IncrementalEdit(range:replacement:) instead")
111-
public init(offset: Int, length: Int, replacementLength: Int) {
112-
self.range = ByteSourceRange(offset: offset, length: length)
113-
self.replacement = Array(repeating: UInt8(ascii: " "), count: replacementLength)
114-
}
115-
116-
public init(range: Range<AbsolutePosition>, replacement: [UInt8]) {
117-
self.range = range
118-
self.replacement = replacement
119-
}
120-
121-
public init(range: Range<AbsolutePosition>, replacement: String) {
122-
self.init(range: range, replacement: Array(replacement.utf8))
123-
}
124-
125-
public func intersectsOrTouchesRange(_ other: Range<AbsolutePosition>) -> Bool {
126-
return self.range.overlapsOrTouches(other)
127-
}
128-
129-
public func intersectsRange(_ other: Range<AbsolutePosition>) -> Bool {
130-
return self.range.overlaps(other)
131-
}
132-
}
133-
13483
extension RawUnexpectedNodesSyntax {
13584
/// Construct a ``RawUnexpectedNodesSyntax``with the given `elements`.
13685
///

Sources/_SwiftSyntaxTestSupport/IncrementalParseTestUtils.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public func extractEditsAndSources(
179179
) -> (edits: ConcurrentEdits, originalSource: Substring, editedSource: Substring) {
180180
var editedSource = Substring()
181181
var originalSource = Substring()
182-
var concurrentEdits: [IncrementalEdit] = []
182+
var concurrentEdits: [SourceEdit] = []
183183

184184
var lastStartIndex = source.startIndex
185185
while let startIndex = source[lastStartIndex...].firstIndex(where: { $0 == "⏩️" }),
@@ -188,7 +188,7 @@ public func extractEditsAndSources(
188188
{
189189

190190
originalSource += source[lastStartIndex..<startIndex]
191-
let edit = IncrementalEdit(
191+
let edit = SourceEdit(
192192
range: Range(
193193
position: AbsolutePosition(utf8Offset: originalSource.utf8.count),
194194
length: SourceLength(utf8Length: source.utf8.distance(from: source.index(after: startIndex), to: separateIndex))
@@ -221,7 +221,7 @@ public func extractEditsAndSources(
221221
/// `concurrent` specifies whether the edits should be interpreted as being
222222
/// applied sequentially or concurrently.
223223
public func applyEdits(
224-
_ edits: [IncrementalEdit],
224+
_ edits: [SourceEdit],
225225
concurrent: Bool,
226226
to testString: String
227227
) -> String {
@@ -238,7 +238,7 @@ public func applyEdits(
238238
for edit in edits {
239239
assert(edit.range.upperBound.utf8Offset <= bytes.count)
240240
bytes.removeSubrange(edit.range.lowerBound.utf8Offset..<edit.range.upperBound.utf8Offset)
241-
bytes.insert(contentsOf: edit.replacement, at: edit.range.lowerBound.utf8Offset)
241+
bytes.insert(contentsOf: edit.replacementBytes, at: edit.range.lowerBound.utf8Offset)
242242
}
243243
return String(bytes: bytes, encoding: .utf8)!
244244
}

0 commit comments

Comments
 (0)