diff --git a/Sources/_SwiftSyntaxTestSupport/SourceEditsTestUtils.swift b/Sources/_SwiftSyntaxTestSupport/SourceEditsTestUtils.swift new file mode 100644 index 00000000000..e9624418159 --- /dev/null +++ b/Sources/_SwiftSyntaxTestSupport/SourceEditsTestUtils.swift @@ -0,0 +1,94 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 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 +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax +import XCTest + +/// Get `ConcurrentEdits` in source whose edited zones are marked with markers +/// Also extract the markers from source to get original source and edited source +/// +/// `⏩️` is *start marker*, `⏸️` is *separate marker*, `⏪️` is *end marker* +/// Contents between `⏩️` and `⏸️` are source text that before modification, contents +/// betwwen `⏸️` and `⏪️` are source text that after modification +/// i.e. `⏩️foo⏸️bar⏪️`, the original source is `foo` and the edited source is `bar` +public func getEditsAndSources(_ source: String) -> (edits: ConcurrentEdits, orignialSource: Substring, editedSource: Substring) { + var editedSource = Substring() + var originalSource = Substring() + var concurrentEdits: [SourceEdit] = [] + + var lastStartIndex = source.startIndex + while let startIndex = source[lastStartIndex...].firstIndex(where: { $0 == "⏩️" }), + let separateIndex = source[startIndex...].firstIndex(where: { $0 == "⏸️" }), + let endIndex = source[separateIndex...].firstIndex(where: { $0 == "⏪️" }) + { + + originalSource += source[lastStartIndex.. String { + guard let replacementAscii = replacementChar.asciiValue else { + fatalError("replacementChar must be an ASCII character") + } + var edits = edits + if concurrent { + XCTAssert(ConcurrentEdits._isValidConcurrentEditArray(edits)) + + // If the edits are concurrent, sorted and not overlapping (as guaranteed by + // the check above, we can apply them sequentially to the string in reverse + // order because later edits don't affect earlier edits. + edits = edits.reversed() + } + var bytes = Array(testString.utf8) + for edit in edits { + assert(edit.endOffset <= bytes.count) + bytes.removeSubrange(edit.offset.. String { - guard let replacementAscii = replacementChar.asciiValue else { - fatalError("replacementChar must be an ASCII character") - } - var edits = edits - if concurrent { - XCTAssert(ConcurrentEdits._isValidConcurrentEditArray(edits)) - - // If the edits are concurrent, sorted and not overlapping (as guaranteed by - // the check above, we can apply them sequentially to the string in reverse - // order because later edits don't affect earlier edits. - edits = edits.reversed() - } - var bytes = Array(testString.utf8) - for edit in edits { - assert(edit.endOffset <= bytes.count) - bytes.removeSubrange(edit.offset..