diff --git a/Release Notes/600.md b/Release Notes/600.md index ee4e5e5a8c2..360d13e6aeb 100644 --- a/Release Notes/600.md +++ b/Release Notes/600.md @@ -124,7 +124,7 @@ - Description: Types can have multiple specifiers now and the syntax tree has been modified to reflect that. - Pull request: https://github.com/apple/swift-syntax/pull/2433 -- ` CanImportExprSyntax` and `CanImportVersionInfoSyntax` +- `CanImportExprSyntax` and `CanImportVersionInfoSyntax` - 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. - Pull request: https://github.com/apple/swift-syntax/pull/2025 @@ -137,7 +137,7 @@ - Pull request: https://github.com/apple/swift-syntax/pull/2587 - `ByteSourceRange` deprecated in favor of `Range` - - Description: `ByteSourceRange` is being dropped for `Range`, where the latter clearly signifies that it uses UTF-8 byte positions. `Range` has deprecated compatibility layers to make it API-compatible with `ByteSourceRange` + - Description: `ByteSourceRange` is being dropped for `Range`, where the latter clearly signifies that it uses UTF-8 byte positions. `Range` has deprecated compatibility layers to make it API-compatible with `ByteSourceRange` - Pull request: https://github.com/apple/swift-syntax/pull/2587 ## API-Incompatible Changes diff --git a/Release Notes/601.md b/Release Notes/601.md index a26d42cde2e..9bf8ee872d9 100644 --- a/Release Notes/601.md +++ b/Release Notes/601.md @@ -11,6 +11,11 @@ ## Deprecations +- `IncrementalEdit` deprecated in favor of `SourceEdit` + - Description: `IncrementalEdit` is being dropped for `SourceEdit`. `SourceEdit` has deprecated compatibility layers to make it API-compatible with `IncrementalEdit` + - Issue: https://github.com/apple/swift-syntax/issues/2532 + - Pull request: https://github.com/apple/swift-syntax/pull/2604 + ## API-Incompatible Changes - Moved `Radix` and `IntegerLiteralExprSyntax.radix` from `SwiftRefactor` to `SwiftSyntax`. diff --git a/Sources/SwiftIDEUtils/FixItApplier.swift b/Sources/SwiftIDEUtils/FixItApplier.swift index 650803ef301..5a755ce2a60 100644 --- a/Sources/SwiftIDEUtils/FixItApplier.swift +++ b/Sources/SwiftIDEUtils/FixItApplier.swift @@ -78,10 +78,10 @@ public enum FixItApplier { // shift it by the current edit's difference in length. if edit.endUtf8Offset <= remainingEdit.startUtf8Offset { let startPosition = AbsolutePosition( - utf8Offset: remainingEdit.startUtf8Offset - edit.replacementRange.count + edit.replacementLength + utf8Offset: remainingEdit.startUtf8Offset - edit.replacementRange.count + edit.replacementLength.utf8Length ) let endPosition = AbsolutePosition( - utf8Offset: remainingEdit.endUtf8Offset - edit.replacementRange.count + edit.replacementLength + utf8Offset: remainingEdit.endUtf8Offset - edit.replacementRange.count + edit.replacementLength.utf8Length ) return SourceEdit(range: startPosition.. { return startUtf8Offset.. [IncrementalEdit] { - var concurrentEdits: [IncrementalEdit] = [] + _ edits: [SourceEdit] + ) -> [SourceEdit] { + var concurrentEdits: [SourceEdit] = [] for editToAdd in edits { var editToAdd = editToAdd var editIndicesMergedWithNewEdit: [Int] = [] @@ -353,14 +353,14 @@ public struct ConcurrentEdits: Sendable { existingEdit.replacementRange.clamped(to: editToAdd.range).length let replacement: [UInt8] replacement = - existingEdit.replacement.prefix( + existingEdit.replacementBytes.prefix( max(0, editToAdd.range.lowerBound.utf8Offset - existingEdit.replacementRange.lowerBound.utf8Offset) ) - + editToAdd.replacement - + existingEdit.replacement.suffix( + + editToAdd.replacementBytes + + existingEdit.replacementBytes.suffix( max(0, existingEdit.replacementRange.upperBound.utf8Offset - editToAdd.range.upperBound.utf8Offset) ) - editToAdd = IncrementalEdit( + editToAdd = SourceEdit( range: Range( position: Swift.min(existingEdit.range.lowerBound, editToAdd.range.lowerBound), length: existingEdit.range.length + editToAdd.range.length - intersectionLength @@ -369,7 +369,7 @@ public struct ConcurrentEdits: Sendable { ) editIndicesMergedWithNewEdit.append(index) } else if existingEdit.range.lowerBound < editToAdd.range.upperBound { - editToAdd = IncrementalEdit( + editToAdd = SourceEdit( range: Range( position: editToAdd.range.lowerBound + existingEdit.range.length - existingEdit.replacementLength, length: editToAdd.range.length @@ -392,7 +392,7 @@ public struct ConcurrentEdits: Sendable { return concurrentEdits } - private static func isValidConcurrentEditArray(_ edits: [IncrementalEdit]) -> Bool { + private static func isValidConcurrentEditArray(_ edits: [SourceEdit]) -> Bool { // Not quite sure if we should disallow creating an `IncrementalParseTransition` // object without edits but there doesn't seem to be much benefit if we do, // and there are 'lit' tests that want to test incremental re-parsing without edits. @@ -412,7 +412,7 @@ public struct ConcurrentEdits: Sendable { } /// **Public for testing purposes only** - public static func _isValidConcurrentEditArray(_ edits: [IncrementalEdit]) -> Bool { + public static func _isValidConcurrentEditArray(_ edits: [SourceEdit]) -> Bool { return isValidConcurrentEditArray(edits) } } diff --git a/Sources/SwiftSyntax/SourceEdit.swift b/Sources/SwiftSyntax/SourceEdit.swift index 7cad887ad38..8b65e97708d 100644 --- a/Sources/SwiftSyntax/SourceEdit.swift +++ b/Sources/SwiftSyntax/SourceEdit.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -13,22 +13,75 @@ /// A textual edit to the original source represented by a range and a /// replacement. public struct SourceEdit: Equatable, Sendable { - /// The half-open range that this edit applies to. + /// The byte range of the original source buffer that the edit applies to. public let range: Range - /// The text to replace the original range with. Empty for a deletion. - public let replacement: String - /// Length of the original source range that this edit applies to. Zero if - /// this is an addition. - public var length: SourceLength { - return SourceLength(utf8Length: range.upperBound.utf8Offset - range.lowerBound.utf8Offset) + /// The UTF-8 bytes that should be inserted as part of the edit + public let replacementBytes: [UInt8] + + /// A string representation of the replacement + public var replacement: String { return String(decoding: replacementBytes, as: UTF8.self) } + + /// The length of the edit replacement in UTF8 bytes. + public var replacementLength: SourceLength { return SourceLength(utf8Length: replacementBytes.count) } + + @available(*, deprecated, renamed: "range.lowerBound.utf8Offset") + public var offset: Int { return range.offset } + + @available(*, deprecated, renamed: "replacementLength") + public var length: Int { return range.length.utf8Length } + + @available(*, deprecated, renamed: "range.upperBound.utf8Offset") + public var endOffset: Int { return range.endOffset } + + /// After the edit has been applied the range of the replacement text. + public var replacementRange: Range { + return Range(position: range.lowerBound, length: replacementLength) } /// Create an edit to replace `range` in the original source with /// `replacement`. public init(range: Range, replacement: String) { self.range = range - self.replacement = replacement + self.replacementBytes = Array(replacement.utf8) + } + + public init(range: Range, replacement: [UInt8]) { + self.range = range + self.replacementBytes = replacement + } + + @available(*, deprecated, message: "Use SourceEdit(range:replacement:) instead") + public init(range: ByteSourceRange, replacementLength: Int) { + self.range = AbsolutePosition(utf8Offset: range.offset)..) -> Bool { + return self.range.upperBound.utf8Offset >= other.lowerBound.utf8Offset + && self.range.lowerBound.utf8Offset <= other.upperBound.utf8Offset + } + + public func intersectsRange(_ other: Range) -> Bool { + return self.range.upperBound.utf8Offset > other.lowerBound.utf8Offset + && self.range.lowerBound.utf8Offset < other.upperBound.utf8Offset } /// Convenience function to create a textual addition after the given node diff --git a/Sources/SwiftSyntax/Utils.swift b/Sources/SwiftSyntax/Utils.swift index 93a981e2116..3be00281ca9 100644 --- a/Sources/SwiftSyntax/Utils.swift +++ b/Sources/SwiftSyntax/Utils.swift @@ -10,6 +10,9 @@ // //===----------------------------------------------------------------------===// +@available(*, deprecated, renamed: "SourceEdit") +public typealias IncrementalEdit = SourceEdit + @available(*, deprecated, message: "Use Range instead") public typealias ByteSourceRange = Range @@ -77,60 +80,6 @@ extension Range { } } -public struct IncrementalEdit: Equatable, Sendable { - /// The byte range of the original source buffer that the edit applies to. - public let range: Range - - /// The UTF-8 bytes that should be inserted as part of the edit - public let replacement: [UInt8] - - /// The length of the edit replacement in UTF-8 bytes. - public var replacementLength: SourceLength { SourceLength(utf8Length: replacement.count) } - - @available(*, deprecated, message: "Use range.lowerBound.utf8Offset instead") - public var offset: Int { return range.offset } - - @available(*, deprecated, message: "Use range.utf8Length instead") - public var length: Int { return range.length.utf8Length } - - @available(*, deprecated, message: "Use range.upperBound.utf8Offset instead") - public var endOffset: Int { return range.endOffset } - - /// After the edit has been applied the range of the replacement text. - public var replacementRange: Range { - return Range(position: range.lowerBound, length: replacementLength) - } - - @available(*, deprecated, message: "Use IncrementalEdit(range:replacement:) instead") - public init(range: ByteSourceRange, replacementLength: Int) { - self.range = range - self.replacement = Array(repeating: UInt8(ascii: " "), count: replacementLength) - } - - @available(*, deprecated, message: "Use IncrementalEdit(range:replacement:) instead") - public init(offset: Int, length: Int, replacementLength: Int) { - self.range = ByteSourceRange(offset: offset, length: length) - self.replacement = Array(repeating: UInt8(ascii: " "), count: replacementLength) - } - - public init(range: Range, replacement: [UInt8]) { - self.range = range - self.replacement = replacement - } - - public init(range: Range, replacement: String) { - self.init(range: range, replacement: Array(replacement.utf8)) - } - - public func intersectsOrTouchesRange(_ other: Range) -> Bool { - return self.range.overlapsOrTouches(other) - } - - public func intersectsRange(_ other: Range) -> Bool { - return self.range.overlaps(other) - } -} - extension RawUnexpectedNodesSyntax { /// Construct a ``RawUnexpectedNodesSyntax``with the given `elements`. /// diff --git a/Sources/_SwiftSyntaxTestSupport/IncrementalParseTestUtils.swift b/Sources/_SwiftSyntaxTestSupport/IncrementalParseTestUtils.swift index 99384809968..c8fdf6b37f8 100644 --- a/Sources/_SwiftSyntaxTestSupport/IncrementalParseTestUtils.swift +++ b/Sources/_SwiftSyntaxTestSupport/IncrementalParseTestUtils.swift @@ -179,7 +179,7 @@ public func extractEditsAndSources( ) -> (edits: ConcurrentEdits, originalSource: Substring, editedSource: Substring) { var editedSource = Substring() var originalSource = Substring() - var concurrentEdits: [IncrementalEdit] = [] + var concurrentEdits: [SourceEdit] = [] var lastStartIndex = source.startIndex while let startIndex = source[lastStartIndex...].firstIndex(where: { $0 == "โฉ๏ธ" }), @@ -188,7 +188,7 @@ public func extractEditsAndSources( { originalSource += source[lastStartIndex.. String { @@ -238,7 +238,7 @@ public func applyEdits( for edit in edits { assert(edit.range.upperBound.utf8Offset <= bytes.count) bytes.removeSubrange(edit.range.lowerBound.utf8Offset..