Skip to content

Commit

Permalink
Merge SourceEdit and IncrementalEdit
Browse files Browse the repository at this point in the history
  • Loading branch information
kimdv committed Apr 20, 2024
1 parent 5bd6384 commit be77960
Show file tree
Hide file tree
Showing 7 changed files with 369 additions and 175 deletions.
8 changes: 2 additions & 6 deletions Sources/SwiftIDEUtils/FixItApplier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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..<endPosition, replacement: remainingEdit.replacement)
}
Expand All @@ -103,10 +103,6 @@ private extension SourceEdit {
return range.upperBound.utf8Offset
}

var replacementLength: Int {
return replacement.utf8.count
}

var replacementRange: Range<Int> {
return startUtf8Offset..<endUtf8Offset
}
Expand Down
28 changes: 14 additions & 14 deletions Sources/SwiftParser/IncrementalParseTransition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -299,11 +299,11 @@ public struct ConcurrentEdits: Sendable {

/// The raw concurrent edits. Are guaranteed to satisfy the requirements
/// stated above.
public let edits: [IncrementalEdit]
public let edits: [SourceEdit]

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

public init(fromSequential sequentialEdits: [IncrementalEdit]) {
public init(fromSequential sequentialEdits: [SourceEdit]) {
do {
try self.init(concurrent: Self.translateSequentialEditsToConcurrentEdits(sequentialEdits))
} catch {
Expand All @@ -332,7 +332,7 @@ public struct ConcurrentEdits: Sendable {
/// Construct a concurrent edits struct from a single edit. For a single edit,
/// there is no differentiation between being it being applied concurrently
/// or sequentially.
public init(_ single: IncrementalEdit) {
public init(_ single: SourceEdit) {
do {
try self.init(concurrent: [single])
} catch {
Expand All @@ -341,9 +341,9 @@ public struct ConcurrentEdits: Sendable {
}

private static func translateSequentialEditsToConcurrentEdits(
_ edits: [IncrementalEdit]
) -> [IncrementalEdit] {
var concurrentEdits: [IncrementalEdit] = []
_ edits: [SourceEdit]
) -> [SourceEdit] {
var concurrentEdits: [SourceEdit] = []
for editToAdd in edits {
var editToAdd = editToAdd
var editIndicesMergedWithNewEdit: [Int] = []
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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)
}
}
Expand Down
71 changes: 62 additions & 9 deletions Sources/SwiftSyntax/SourceEdit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<AbsolutePosition>
/// 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: replacement.utf8.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<AbsolutePosition> {
return Range(position: range.lowerBound, length: replacementLength)
}

/// Create an edit to replace `range` in the original source with
/// `replacement`.
public init(range: Range<AbsolutePosition>, replacement: String) {
self.range = range
self.replacement = replacement
self.replacementBytes = Array(replacement.utf8)
}

public init(range: Range<AbsolutePosition>, 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)..<AbsolutePosition(utf8Offset: range.endOffset)
self.replacementBytes = Array(repeating: UInt8(ascii: " "), count: replacementLength)
}

@available(*, deprecated, message: "Use SourceEdit(range:replacement:) instead")
public init(offset: Int, length: Int, replacementLength: Int) {
self.range = AbsolutePosition(utf8Offset: offset)..<AbsolutePosition(utf8Offset: offset + length)
self.replacementBytes = Array(repeating: UInt8(ascii: " "), count: replacementLength)
}

@available(*, deprecated, message: "Use SourceEdit(range:replacement:) instead")
public init(offset: Int, length: Int, replacement: [UInt8]) {
self.range = AbsolutePosition(utf8Offset: offset)..<AbsolutePosition(utf8Offset: offset + length)
self.replacementBytes = replacement
}

@available(*, deprecated, message: "Use SourceEdit(range:replacement:) instead")
public init(offset: Int, length: Int, replacement: String) {
self.init(offset: offset, length: length, replacement: Array(replacement.utf8))
}

public func intersectsOrTouchesRange(_ other: Range<AbsolutePosition>) -> Bool {
return self.range.upperBound.utf8Offset >= other.lowerBound.utf8Offset
&& self.range.lowerBound.utf8Offset <= other.upperBound.utf8Offset
}

public func intersectsRange(_ other: Range<AbsolutePosition>) -> 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
Expand Down
57 changes: 3 additions & 54 deletions Sources/SwiftSyntax/Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
//
//===----------------------------------------------------------------------===//

@available(*, deprecated, renamed: "SourceEdit")
public typealias IncrementalEdit = SourceEdit

@available(*, deprecated, message: "Use Range<AbsolutePosition> instead")
public typealias ByteSourceRange = Range<AbsolutePosition>

Expand Down Expand Up @@ -77,60 +80,6 @@ extension Range<AbsolutePosition> {
}
}

public struct IncrementalEdit: Equatable, Sendable {
/// The byte range of the original source buffer that the edit applies to.
public let range: Range<AbsolutePosition>

/// 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<AbsolutePosition> {
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<AbsolutePosition>, replacement: [UInt8]) {
self.range = range
self.replacement = replacement
}

public init(range: Range<AbsolutePosition>, replacement: String) {
self.init(range: range, replacement: Array(replacement.utf8))
}

public func intersectsOrTouchesRange(_ other: Range<AbsolutePosition>) -> Bool {
return self.range.overlapsOrTouches(other)
}

public func intersectsRange(_ other: Range<AbsolutePosition>) -> Bool {
return self.range.overlaps(other)
}
}

extension RawUnexpectedNodesSyntax {
/// Construct a ``RawUnexpectedNodesSyntax``with the given `elements`.
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 == "⏩️" }),
Expand All @@ -188,7 +188,7 @@ public func extractEditsAndSources(
{

originalSource += source[lastStartIndex..<startIndex]
let edit = IncrementalEdit(
let edit = SourceEdit(
range: Range(
position: AbsolutePosition(utf8Offset: originalSource.utf8.count),
length: SourceLength(utf8Length: source.utf8.distance(from: source.index(after: startIndex), to: separateIndex))
Expand Down Expand Up @@ -221,7 +221,7 @@ public func extractEditsAndSources(
/// `concurrent` specifies whether the edits should be interpreted as being
/// applied sequentially or concurrently.
public func applyEdits(
_ edits: [IncrementalEdit],
_ edits: [SourceEdit],
concurrent: Bool,
to testString: String
) -> String {
Expand All @@ -238,7 +238,7 @@ public func applyEdits(
for edit in edits {
assert(edit.range.upperBound.utf8Offset <= bytes.count)
bytes.removeSubrange(edit.range.lowerBound.utf8Offset..<edit.range.upperBound.utf8Offset)
bytes.insert(contentsOf: edit.replacement, at: edit.range.lowerBound.utf8Offset)
bytes.insert(contentsOf: edit.replacementBytes, at: edit.range.lowerBound.utf8Offset)
}
return String(bytes: bytes, encoding: .utf8)!
}

0 comments on commit be77960

Please sign in to comment.