├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── Package.resolved ├── Package.swift ├── README.md ├── Sources ├── Conference │ └── Conference.swift ├── Engine │ ├── Indexer.swift │ ├── Processor.swift │ ├── Rewriter.swift │ ├── StoreIndexer.swift │ └── Visitor.swift ├── Utils │ ├── OutputStreams.swift │ └── Utils.swift └── reorder │ └── main.swift ├── TODO.md └── Tests ├── LinuxMain.swift └── ReorderTests ├── ReorderTests.swift └── XCTestManifests.swift /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [krzyzanowskim] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData 7 | .swiftpm/xcode 8 | output.swift -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Marcin Krzyzanowski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "IndexStoreDB", 6 | "repositoryURL": "https://github.com/apple/indexstore-db.git", 7 | "state": { 8 | "branch": "swift-5.1-branch", 9 | "revision": "5960ba789ea7983516c7d419ad73ae264fced8ed", 10 | "version": null 11 | } 12 | }, 13 | { 14 | "package": "SwiftSyntax", 15 | "repositoryURL": "https://github.com/apple/swift-syntax.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "3e3eb191fcdbecc6031522660c4ed6ce25282c25", 19 | "version": "0.50100.0" 20 | } 21 | } 22 | ] 23 | }, 24 | "version": 1 25 | } 26 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "reorder", 7 | dependencies: [ 8 | .package(url: "https://github.com/apple/swift-syntax.git", .exact("0.50100.0")), 9 | .package(url: "https://github.com/apple/indexstore-db.git", .branch("swift-5.1-branch")) // missing semver tag 10 | ], 11 | targets: [ 12 | .target(name: "reorder", dependencies: ["Engine"]), 13 | .target(name: "Utils", dependencies: ["SwiftSyntax"]), 14 | .target(name: "Engine", dependencies: ["SwiftSyntax", "Utils", "IndexStoreDB"]), 15 | .testTarget(name: "ReorderTests", dependencies: ["reorder"]), 16 | .target(name: "Conference") 17 | ] 18 | ) 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reorder Swift Code 2 | 3 | Presentation slides 4 | 5 | ‪https://speakerdeck.com/krzyzanowskim/swiftly-swift-manipulation-dot-dot-dot-with-swift 6 | -------------------------------------------------------------------------------- /Sources/Conference/Conference.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public class Conference { 4 | internal typealias Line = String 5 | 6 | public var name: String 7 | public let venue: String 8 | private var sponsors: Set 9 | private var costs: Array 10 | fileprivate var attendees: Set 11 | 12 | public init(name: String, venue: String) { 13 | self.name = name 14 | self.venue = venue 15 | self.attendees = [] 16 | self.sponsors = [] 17 | self.costs = [] 18 | } 19 | 20 | private init() { 21 | self.name = "FrenchKit" 22 | self.venue = "Bâtiment" 23 | self.attendees = [] 24 | self.sponsors = [] 25 | self.costs = [] 26 | } 27 | 28 | deinit { 29 | fatalError("Thank you for playing Wing Commander") 30 | } 31 | 32 | public enum FooPublic {} 33 | internal enum FooInternal {} 34 | private enum FooPrivate {} 35 | 36 | public func totalCosts() -> Decimal { 37 | self.costs.reduce(0, { $0 + $1 }) 38 | } 39 | 40 | internal func add(attendee: String) { 41 | self.attendees.insert(attendee) 42 | } 43 | 44 | internal func add(sponsor: String) { 45 | self.sponsors.insert(sponsor) 46 | } 47 | 48 | internal func add(cost: Decimal) { 49 | self.costs.append(cost) 50 | } 51 | 52 | internal func printStatement() { 53 | self.statement().forEach({ 54 | print($0) 55 | }) 56 | } 57 | 58 | private func statement() -> Array { 59 | var lines: Array = [] 60 | 61 | for attendee in self.attendees { 62 | lines.append("Attendee:\t\(attendee)") 63 | } 64 | 65 | for sponsor in self.sponsors { 66 | lines.append("Sponsor:\t\(sponsor)") 67 | } 68 | 69 | for cost in self.costs { 70 | lines.append("Cost:\t€\(cost)") 71 | } 72 | 73 | return lines 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Sources/Engine/Indexer.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftSyntax 3 | 4 | class Indexer: SyntaxVisitor { 5 | 6 | private var functions: Array = [] 7 | 8 | func visit(_ node: AssociatedtypeDeclSyntax) -> SyntaxVisitorContinueKind{ 9 | self.functions.append(node) 10 | return .skipChildren 11 | } 12 | 13 | func visit(_ node: SubscriptDeclSyntax) -> SyntaxVisitorContinueKind{ 14 | self.functions.append(node) 15 | return .skipChildren 16 | } 17 | 18 | // func visit(_ node: PrecedenceGroupDeclSyntax) -> SyntaxVisitorContinueKind{ 19 | // self.functions.append(node) 20 | // return .skipChildren 21 | // } 22 | 23 | func visit(_ node: OperatorDeclSyntax) -> SyntaxVisitorContinueKind{ 24 | self.functions.append(node) 25 | return .skipChildren 26 | } 27 | 28 | // func visit(_ node: ImportDeclSyntax) -> SyntaxVisitorContinueKind{ 29 | // self.functions.append(node) 30 | // return .skipChildren 31 | // } 32 | 33 | func visit(_ node: DeinitializerDeclSyntax) -> SyntaxVisitorContinueKind{ 34 | self.functions.append(node) 35 | return .skipChildren 36 | } 37 | 38 | func visit(_ node: InitializerDeclSyntax) -> SyntaxVisitorContinueKind { 39 | self.functions.append(node) 40 | return .skipChildren 41 | } 42 | 43 | func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind { 44 | self.functions.append(node) 45 | return .skipChildren 46 | } 47 | 48 | func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind { 49 | self.functions.append(node) 50 | return .skipChildren 51 | } 52 | 53 | func visit(_ node: TypealiasDeclSyntax) -> SyntaxVisitorContinueKind { 54 | self.functions.append(node) 55 | return .skipChildren 56 | } 57 | 58 | func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind { 59 | self.functions.append(node) 60 | return .skipChildren 61 | } 62 | 63 | func sorted() -> Array { 64 | self.functions.sorted(by: { (lhs, rhs) -> Bool in 65 | 66 | var lhsVisitor = Visitor() 67 | lhs.walk(&lhsVisitor) 68 | 69 | var rhsVisitor = Visitor() 70 | rhs.walk(&rhsVisitor) 71 | 72 | return lhsVisitor.accessLevel < rhsVisitor.accessLevel 73 | }).sorted(by: { (lhs, rhs) -> Bool in 74 | 75 | var lhsVisitor = Visitor() 76 | lhs.walk(&lhsVisitor) 77 | 78 | var rhsVisitor = Visitor() 79 | rhs.walk(&rhsVisitor) 80 | 81 | return lhsVisitor.declSyntax < rhsVisitor.declSyntax 82 | }) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Sources/Engine/Processor.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftSyntax 3 | import Utils 4 | 5 | public class Processor { 6 | 7 | private struct Error: Swift.Error { 8 | static let invalidInput = Error() 9 | } 10 | 11 | public init() { 12 | } 13 | 14 | public func process(_ data: Data) throws -> String { 15 | guard let source = String(data: data, encoding: .utf8) else { 16 | throw Error.invalidInput 17 | } 18 | 19 | let parsed: SourceFileSyntax = try SyntaxParser.parse(source: source) 20 | 21 | // Gather information about the file 22 | var visitor = Indexer() 23 | parsed.walk(&visitor) 24 | 25 | // Rewrite file using visitor 26 | let rewritten = Rewriter(visitor).visit(parsed) 27 | 28 | var output = StringOutputStream() 29 | rewritten.write(to: &output) 30 | return output.string 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/Engine/Rewriter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftSyntax 3 | 4 | class Rewriter: SyntaxRewriter { 5 | private let indexer: Indexer 6 | private var sortedFunctions: Array 7 | 8 | init(_ indexer: Indexer) { 9 | self.indexer = indexer 10 | self.sortedFunctions = indexer.sorted() 11 | } 12 | 13 | override func visit(_ node: DeinitializerDeclSyntax) -> DeclSyntax { 14 | sortedFunctions.removeFirst() 15 | } 16 | 17 | // override func visit(_ node: PrecedenceGroupDeclSyntax) -> DeclSyntax { 18 | // sortedFunctions.removeFirst() 19 | // } 20 | 21 | override func visit(_ node: OperatorDeclSyntax) -> DeclSyntax { 22 | sortedFunctions.removeFirst() 23 | } 24 | 25 | override func visit(_ node: AssociatedtypeDeclSyntax) -> DeclSyntax { 26 | sortedFunctions.removeFirst() 27 | } 28 | 29 | override func visit(_ node: SubscriptDeclSyntax) -> DeclSyntax { 30 | sortedFunctions.removeFirst() 31 | } 32 | 33 | // override func visit(_ node: ImportDeclSyntax) -> DeclSyntax { 34 | // sortedFunctions.removeFirst() 35 | // } 36 | 37 | override func visit(_ node: InitializerDeclSyntax) -> DeclSyntax { 38 | sortedFunctions.removeFirst() 39 | } 40 | 41 | override func visit(_ node: FunctionDeclSyntax) -> DeclSyntax { 42 | sortedFunctions.removeFirst() 43 | } 44 | 45 | override func visit(_ node: VariableDeclSyntax) -> DeclSyntax { 46 | sortedFunctions.removeFirst() 47 | } 48 | 49 | override func visit(_ node: TypealiasDeclSyntax) -> DeclSyntax { 50 | sortedFunctions.removeFirst() 51 | } 52 | 53 | override func visit(_ node: EnumDeclSyntax) -> DeclSyntax { 54 | sortedFunctions.removeFirst() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Sources/Engine/StoreIndexer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Marcin Krzyzanowski on 05/10/2019. 6 | // 7 | 8 | import Foundation 9 | import IndexStoreDB 10 | 11 | public struct StoreIndexer { 12 | 13 | public typealias Path = String 14 | 15 | public struct Function { 16 | public let symbol: Symbol 17 | public let location: SymbolLocation 18 | } 19 | 20 | private let indexURL: URL 21 | 22 | public init(_ indexURL: URL) { 23 | self.indexURL = indexURL 24 | } 25 | 26 | public func process(symbolName: String) -> [Path: String] { 27 | guard let lib = try? IndexStoreLibrary(dylibPath: "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libIndexStore.dylib"), 28 | let index = try? IndexStoreDB(storePath: indexURL.path, databasePath: NSTemporaryDirectory() + "index_\(getpid())", library: lib, listenToUnitEvents: false) 29 | else { 30 | fatalError("Cannot process.") 31 | } 32 | 33 | index.pollForUnitChangesAndWait() 34 | 35 | let functions = index.canonicalOccurrences(ofName: symbolName).flatMap { 36 | index.occurrences(relatedToUSR: $0.symbol.usr, roles: .all) 37 | .filter({ $0.symbol.kind == .instanceMethod && !$0.roles.contains(.implicit) }) 38 | .map({ Function(symbol: $0.symbol, location: $0.location) }) 39 | } 40 | 41 | let filesDictionary = Dictionary(grouping: functions, by: { $0.location.path }) 42 | 43 | var output: [Path: String] = [:] 44 | 45 | for filePath in filesDictionary.keys { 46 | output[filePath] = try? Processor().process(Data.init(contentsOf: URL(fileURLWithPath: filePath))) 47 | } 48 | 49 | return output 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Engine/Visitor.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftSyntax 3 | 4 | struct Visitor: SyntaxVisitor { 5 | 6 | enum FakeTokenKind : Int, Comparable { 7 | static func < (lhs: Visitor.FakeTokenKind, rhs: Visitor.FakeTokenKind) -> Bool { 8 | return lhs.rawValue < rhs.rawValue 9 | } 10 | 11 | case publicKeyword = 0 12 | case internalKeyword = 1 13 | case privateKeyword = 2 14 | case fileprivateKeyword = 3 15 | case other = 99 16 | } 17 | 18 | var accessLevel: FakeTokenKind = .other 19 | 20 | enum DeclSyntaxKind : Int, Comparable { 21 | static func < (lhs: Visitor.DeclSyntaxKind, rhs: Visitor.DeclSyntaxKind) -> Bool { 22 | return lhs.rawValue < rhs.rawValue 23 | } 24 | 25 | case isImport = 0 26 | case isAssociatedtype = 1 27 | case isTypealias = 2 28 | case isVariable = 10 29 | case isInitializer = 20 30 | case isDeinitializer = 30 31 | case isEnum = 40 32 | case isFunction = 70 33 | case isSubscript = 71 34 | case isOperator = 80 35 | case isPrecedenceGroup = 81 36 | case notReordered = 999 37 | } 38 | 39 | var declSyntax : DeclSyntaxKind = .notReordered 40 | 41 | mutating func visit(_ node: AssociatedtypeDeclSyntax) -> SyntaxVisitorContinueKind { 42 | 43 | self.declSyntax = .isAssociatedtype 44 | self.process(node.modifiers) 45 | return .skipChildren 46 | } 47 | 48 | mutating func visit(_ node: SubscriptDeclSyntax) -> SyntaxVisitorContinueKind { 49 | 50 | self.declSyntax = .isSubscript 51 | self.process(node.modifiers) 52 | return .skipChildren 53 | } 54 | 55 | // mutating func visit(_ node: ImportDeclSyntax) -> SyntaxVisitorContinueKind { 56 | // 57 | // self.declSyntax = .isImport 58 | // self.process(node.modifiers) 59 | // return .skipChildren 60 | // } 61 | 62 | mutating func visit(_ node: OperatorDeclSyntax) -> SyntaxVisitorContinueKind { 63 | 64 | self.declSyntax = .isOperator 65 | self.process(node.modifiers) 66 | return .skipChildren 67 | } 68 | 69 | // mutating func visit(_ node: PrecedenceGroupDeclSyntax) -> SyntaxVisitorContinueKind { 70 | // 71 | // self.declSyntax = .isPrecedenceGroup 72 | // self.process(node.modifiers) 73 | // return .skipChildren 74 | // } 75 | 76 | mutating func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind { 77 | 78 | self.declSyntax = .isVariable 79 | self.process(node.modifiers) 80 | return .skipChildren 81 | } 82 | 83 | mutating func visit(_ node: InitializerDeclSyntax) -> SyntaxVisitorContinueKind { 84 | 85 | self.declSyntax = .isInitializer 86 | self.process(node.modifiers) 87 | return .skipChildren 88 | } 89 | 90 | mutating func visit(_ node: DeinitializerDeclSyntax) -> SyntaxVisitorContinueKind { 91 | 92 | self.declSyntax = .isDeinitializer 93 | self.process(node.modifiers) 94 | return .skipChildren 95 | } 96 | 97 | mutating func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind { 98 | 99 | self.declSyntax = .isFunction 100 | self.process(node.modifiers) 101 | return .skipChildren 102 | } 103 | 104 | mutating func visit(_ node: TypealiasDeclSyntax) -> SyntaxVisitorContinueKind { 105 | 106 | self.declSyntax = .isTypealias 107 | self.process(node.modifiers) 108 | return .skipChildren 109 | } 110 | 111 | mutating func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind { 112 | 113 | self.declSyntax = .isEnum 114 | self.process(node.modifiers) 115 | return .skipChildren 116 | } 117 | 118 | private mutating func process(_ modifiers: ModifierListSyntax?) { 119 | guard let modifiers = modifiers else { 120 | return self.accessLevel = .internalKeyword 121 | } 122 | 123 | let isFilePrivate = modifiers.contains(where: { $0.name.tokenKind == .fileprivateKeyword }) 124 | let isPrivate = modifiers.contains(where: { $0.name.tokenKind == .privateKeyword }) 125 | let isPublic = modifiers.contains(where: { $0.name.tokenKind == .publicKeyword }) 126 | 127 | if isPublic { 128 | self.accessLevel = .publicKeyword 129 | } else if isPrivate { 130 | self.accessLevel = .privateKeyword 131 | } else if isFilePrivate { 132 | self.accessLevel = .fileprivateKeyword 133 | } else { 134 | self.accessLevel = .internalKeyword 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Sources/Utils/OutputStreams.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import var Darwin.stdout 3 | 4 | public struct StdoutOutputStream: TextOutputStream { 5 | public init() {} 6 | mutating public func write(_ string: String) { 7 | fputs(string, stdout) 8 | } 9 | } 10 | 11 | public struct StderrOutputStream: TextOutputStream { 12 | public init() {} 13 | mutating public func write(_ string: String) { 14 | fputs(string, stderr) 15 | } 16 | } 17 | 18 | public class StringOutputStream: TextOutputStream { 19 | public var string: String 20 | 21 | public init() { 22 | self.string = "" 23 | } 24 | 25 | public func write(_ string: String) { 26 | self.string.append(string) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Utils/Utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftSyntax 3 | 4 | extension TokenSequence: CustomDebugStringConvertible { 5 | public var debugDescription: String { 6 | self.reduce("") { (res: String, token: TokenSyntax) -> String in 7 | res + token.text + " " 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Sources/reorder/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // swift run reorder -file Sources/Conference/Conference.swift > output.swift 3 | // swift run reorder -index .build/x86_64-apple-macosx/debug/index/store 4 | // 5 | 6 | import Foundation 7 | import Engine 8 | import Utils 9 | 10 | guard let executablePath = CommandLine.arguments.first else { 11 | fatalError("Missing executable") 12 | } 13 | 14 | let args = Array(CommandLine.arguments.dropFirst()) 15 | let commands = stride(from: 0, to: args.count - 1, by: 2).map { (command: args[$0].dropFirst(), value: args[$0+1]) } 16 | 17 | var err = StderrOutputStream() 18 | var out = StdoutOutputStream() 19 | 20 | for (cmd, value) in commands { 21 | switch cmd { 22 | case "file": 23 | err.write("Processing \(value)\n") 24 | let content = try Data(contentsOf: URL(fileURLWithPath: value)) 25 | let result = try Processor().process(content) 26 | out.write(result) 27 | case "index": 28 | err.write("Processing \(value)\n") 29 | for (path, content) in StoreIndexer(URL(fileURLWithPath: value)).process(symbolName: "Conference") { 30 | out.write("File: \(path)\n") 31 | out.write(content) 32 | out.write("\n") 33 | } 34 | default: 35 | err.write("Error: Unknown command: \(cmd)\n") 36 | exit(1) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | ## Project 4 | 5 | - [ ] more better tests 6 | - [ ] simplifying coding in the project 7 | - [ ] documentation 8 | - [ ] support multiple DeclSyntax that need to visit their children in the same file 9 | 10 | ## Code Beautifiers 11 | 12 | - [ ] newline between DeclSyntax to be homogenous 13 | - [ ] sorting DeclSyntax 14 | - [ ] more flexible sorting for accessLevels 15 | 16 | ## DeclSyntax Support: 17 | 18 | ### DeclSyntax that doesn't need to visit their children: 19 | 20 | - [x] TypealiasDeclSyntax 21 | - [x] AssociatedtypeDeclSyntax 22 | - [x] FunctionDeclSyntax 23 | - [x] InitializerDeclSyntax 24 | - [x] DeinitializerDeclSyntax 25 | - [x] ImportDeclSyntax - not working properly 26 | - [x] VariableDeclSyntax 27 | - [x] OperatorDeclSyntax - not tested 28 | - [x] PrecedenceGroupDeclSyntax - not tested 29 | - [x] SubscriptDeclSyntax 30 | 31 | ### DeclSyntax that need to visit their children: 32 | 33 | - [ ] ClassDeclSyntax 34 | - [ ] StructDeclSyntax 35 | - [ ] ProtocolDeclSyntax 36 | - [ ] ExtensionDeclSyntax 37 | - [ ] EnumDeclSyntax - 50% the children part is not done 38 | 39 | ### DeclSyntax that I don't know what they represent: 40 | 41 | - [ ] AccessorDeclSyntax 42 | - [ ] IfConfigDeclSyntax 43 | - [ ] PoundErrorDeclSyntax 44 | - [ ] PoundWarningDeclSyntax 45 | - [ ] PoundSourceLocationDeclSyntax 46 | 47 | ### DeclSyntax that might not need to be ordered: 48 | 49 | - [ ] EnumCaseDeclSyntax 50 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import ReorderSwiftSyntaxTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += ReorderTests.allTests() 7 | XCTMain(tests) 8 | -------------------------------------------------------------------------------- /Tests/ReorderTests/ReorderTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import class Foundation.Bundle 3 | @testable import Engine 4 | 5 | final class ReorderSwiftSyntaxTests: XCTestCase { 6 | 7 | /// Returns path to the built products directory. 8 | private var productsDirectory: URL { 9 | #if os(macOS) 10 | for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") { 11 | return bundle.bundleURL.deletingLastPathComponent() 12 | } 13 | fatalError("couldn't find the products directory") 14 | #else 15 | return Bundle.main.bundleURL 16 | #endif 17 | } 18 | 19 | private lazy var exampleFileURL = self.productsDirectory.appendingPathComponent("../../../Sources/Conference/Conference.swift") 20 | 21 | func testExampleFile() { 22 | XCTAssertTrue(FileManager.default.fileExists(atPath: self.exampleFileURL.path)) 23 | } 24 | 25 | func testReorder() throws { 26 | let content = try Data(contentsOf: self.exampleFileURL) 27 | XCTAssertGreaterThan(content.count, 0) 28 | 29 | let result = try Processor().process(content) 30 | print(result) 31 | } 32 | 33 | static var allTests = [ 34 | ("testExampleFile", testExampleFile, 35 | "testReorder", testReorder), 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /Tests/ReorderTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(ReorderTests.allTests), 7 | ] 8 | } 9 | #endif 10 | --------------------------------------------------------------------------------