├── .gitignore ├── .pre-commit-config.yaml ├── .swift-version ├── .swiftformat ├── .travis.yml ├── Examples ├── Commandant.swift ├── Mini.swift ├── SourceKittenFramework.swift ├── SwiftFormat.swift ├── SwiftLintFramework.swift └── Yams.swift ├── LICENSE ├── Makefile ├── Package.resolved ├── Package.swift ├── README.md ├── Sources └── ModuleInterface │ ├── Classes │ ├── AccessLevel.swift │ ├── ModuleInterface.swift │ └── SwiftDoc.swift │ ├── Commands │ ├── Clean.swift │ ├── Generate.swift │ └── Version.swift │ └── main.swift └── Tests ├── LinuxMain.swift └── ModuleInterfaceTests ├── Helpers.swift ├── VersionTests.swift └── XCTestManifests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | 70 | .swiftpm 71 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/nicklockwood/SwiftFormat 3 | rev: 0.40.14 4 | hooks: 5 | - id: swiftformat -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.1 2 | -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | --exclude Tests 2 | --allman false 3 | --binarygrouping none 4 | --closingparen balanced 5 | --commas always 6 | --conflictmarkers reject 7 | --decimalgrouping none 8 | --elseposition same-line 9 | --empty void 10 | --exponentcase lowercase 11 | --exponentgrouping disabled 12 | --fractiongrouping disabled 13 | --fragment false 14 | --header ignore 15 | --hexgrouping none 16 | --hexliteralcase uppercase 17 | --ifdef no-indent 18 | --importgrouping alphabetized 19 | --indent 4 20 | --indentcase false 21 | --linebreaks lf 22 | --octalgrouping none 23 | --operatorfunc spaced 24 | --patternlet hoist 25 | --ranges spaced 26 | --self remove 27 | --selfrequired 28 | --semicolons never 29 | --stripunusedargs always 30 | --trailingclosures 31 | --trimwhitespace always 32 | --wraparguments preserve 33 | --wrapcollections before-first 34 | --xcodeindentation disabled 35 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: shell 2 | 3 | jobs: 4 | include: 5 | - stage: Continuous Integration 6 | name: SwiftLint 7 | os: osx 8 | osx_image: xcode11 9 | script: 10 | - make lint 11 | - stage: Continuous Integration 12 | name: SwiftPM macOS 13 | os: osx 14 | osx_image: xcode11 15 | script: 16 | - swift test 17 | - stage: Continuous Integration 18 | name: SwiftPM Linux 19 | os: Linux 20 | dist: trusty 21 | install: 22 | - eval "$(curl -sL https://swiftenv.fuller.li/install.sh)" 23 | - swiftenv install 5.1 || true 24 | - swiftenv local 5.1 || true 25 | - swift --version 26 | script: 27 | - swift test 28 | - stage: Continuous Integration 29 | name: Installation from Source (macOS) 30 | os: osx 31 | osx_image: xcode11 32 | script: 33 | - git clone --depth 1 https://github.com/minuscorp/ModuleInterface.git 34 | - cd ModuleInterface 35 | - make install 36 | - moduleinterface version 37 | -------------------------------------------------------------------------------- /Examples/Commandant.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftOnoneSupport 3 | 4 | /// Applies `f` to the value in the given result. 5 | /// 6 | /// In the context of command-line option parsing, this is used to chain 7 | /// together the parsing of multiple arguments. See OptionsProtocol for an example. 8 | public func <*> (f: (T) -> U, value: Result>) -> Result> 9 | 10 | /// Applies the function in `f` to the value in the given result. 11 | /// 12 | /// In the context of command-line option parsing, this is used to chain 13 | /// together the parsing of multiple arguments. See OptionsProtocol for an example. 14 | public func <*> (f: Result<(T) -> U, Commandant.CommandantError>, value: Result>) -> Result> 15 | 16 | /// Describes an argument that can be provided on the command line. 17 | public struct Argument { 18 | /// The default value for this argument. This is the value that will be used 19 | /// if the argument is never explicitly specified on the command line. 20 | /// 21 | /// If this is nil, this argument is always required. 22 | public let defaultValue: T? 23 | 24 | /// A human-readable string describing the purpose of this argument. This will 25 | /// be shown in help messages. 26 | public let usage: String 27 | 28 | /// A human-readable string that describes this argument as a paramater shown 29 | /// in the list of possible parameters in help messages (e.g. for "paths", the 30 | /// user would see ). 31 | public let usageParameter: String? 32 | 33 | public init(defaultValue: T? = nil, usage: String, usageParameter: String? = nil) 34 | } 35 | 36 | /// Destructively parses a list of command-line arguments. 37 | public final class ArgumentParser { 38 | /// Initializes the generator from a simple list of command-line arguments. 39 | public init(_ arguments: [String]) 40 | } 41 | 42 | /// Represents a value that can be converted from a command-line argument. 43 | public protocol ArgumentProtocol { 44 | /// A human-readable name for this type. 45 | static var name: String { get } 46 | 47 | /// Attempts to parse a value from the given command-line argument. 48 | static func from(string: String) -> Self? 49 | } 50 | 51 | /// Describes the "mode" in which a command should run. 52 | public enum CommandMode { 53 | /// Options should be parsed from the given command-line arguments. 54 | case arguments(Commandant.ArgumentParser) 55 | 56 | /// Each option should record its usage information in an error, for 57 | /// presentation to the user. 58 | case usage 59 | } 60 | 61 | extension CommandMode { 62 | /// Evaluates the given argument in the given mode. 63 | /// 64 | /// If parsing command line arguments, and no value was specified on the command 65 | /// line, the argument's `defaultValue` is used. 66 | public static func <| (mode: Commandant.CommandMode, argument: Commandant.Argument) -> Result> where T: Commandant.ArgumentProtocol 67 | 68 | /// Evaluates the given argument list in the given mode. 69 | /// 70 | /// If parsing command line arguments, and no value was specified on the command 71 | /// line, the argument's `defaultValue` is used. 72 | public static func <| (mode: Commandant.CommandMode, argument: Commandant.Argument<[T]>) -> Result<[T], Commandant.CommandantError> where T: Commandant.ArgumentProtocol 73 | } 74 | 75 | extension CommandMode { 76 | /// Evaluates the given option in the given mode. 77 | /// 78 | /// If parsing command line arguments, and no value was specified on the command 79 | /// line, the option's `defaultValue` is used. 80 | public static func <| (mode: Commandant.CommandMode, option: Commandant.Option) -> Result> where T: Commandant.ArgumentProtocol 81 | 82 | /// Evaluates the given option in the given mode. 83 | /// 84 | /// If parsing command line arguments, and no value was specified on the command 85 | /// line, `nil` is used. 86 | public static func <| (mode: Commandant.CommandMode, option: Commandant.Option) -> Result> where T: Commandant.ArgumentProtocol 87 | 88 | /// Evaluates the given option in the given mode. 89 | /// 90 | /// If parsing command line arguments, and no value was specified on the command 91 | /// line, the option's `defaultValue` is used. 92 | public static func <| (mode: Commandant.CommandMode, option: Commandant.Option<[T]>) -> Result<[T], Commandant.CommandantError> where T: Commandant.ArgumentProtocol 93 | 94 | /// Evaluates the given option in the given mode. 95 | /// 96 | /// If parsing command line arguments, and no value was specified on the command 97 | /// line, `nil` is used. 98 | public static func <| (mode: Commandant.CommandMode, option: Commandant.Option<[T]?>) -> Result<[T]?, Commandant.CommandantError> where T: Commandant.ArgumentProtocol 99 | 100 | /// Evaluates the given boolean option in the given mode. 101 | /// 102 | /// If parsing command line arguments, and no value was specified on the command 103 | /// line, the option's `defaultValue` is used. 104 | public static func <| (mode: Commandant.CommandMode, option: Commandant.Option) -> Result> 105 | } 106 | 107 | extension CommandMode { 108 | /// Evaluates the given boolean switch in the given mode. 109 | /// 110 | /// If parsing command line arguments, and no value was specified on the command 111 | /// line, the option's `defaultValue` is used. 112 | public static func <| (mode: Commandant.CommandMode, option: Commandant.Switch) -> Result> 113 | } 114 | 115 | /// Represents a subcommand that can be executed with its own set of arguments. 116 | public protocol CommandProtocol { 117 | /// The command's options type. 118 | associatedtype Options: Commandant.OptionsProtocol 119 | 120 | associatedtype ClientError where Self.ClientError == Self.Options.ClientError 121 | 122 | /// The action that users should specify to use this subcommand (e.g., 123 | /// `help`). 124 | var verb: String { get } 125 | 126 | /// A human-readable, high-level description of what this command is used 127 | /// for. 128 | var function: String { get } 129 | 130 | /// Runs this subcommand with the given options. 131 | func run(_ options: Self.Options) -> Result 132 | } 133 | 134 | /// Maintains the list of commands available to run. 135 | public final class CommandRegistry where ClientError: Error { 136 | /// All available commands. 137 | public var commands: [Commandant.CommandWrapper] { get } 138 | 139 | public init() 140 | 141 | /// Registers the given commands, making those available to run. 142 | /// 143 | /// If another commands were already registered with the same `verb`s, those 144 | /// will be overwritten. 145 | public func register(_ commands: C...) -> Commandant.CommandRegistry where ClientError == C.ClientError, C: Commandant.CommandProtocol 146 | 147 | /// Runs the command corresponding to the given verb, passing it the given 148 | /// arguments. 149 | /// 150 | /// Returns the results of the execution, or nil if no such command exists. 151 | public func run(command verb: String, arguments: [String]) -> Result>? 152 | 153 | /// Returns the command matching the given verb, or nil if no such command 154 | /// is registered. 155 | public subscript(_: String) -> Commandant.CommandWrapper? { get } 156 | } 157 | 158 | extension CommandRegistry { 159 | /// Hands off execution to the CommandRegistry, by parsing CommandLine.arguments 160 | /// and then running whichever command has been identified in the argument 161 | /// list. 162 | /// 163 | /// If the chosen command executes successfully, the process will exit with 164 | /// a successful exit code. 165 | /// 166 | /// If the chosen command fails, the provided error handler will be invoked, 167 | /// then the process will exit with a failure exit code. 168 | /// 169 | /// If a matching command could not be found but there is any `executable-verb` 170 | /// style subcommand executable in the caller's `$PATH`, the subcommand will 171 | /// be executed. 172 | /// 173 | /// If a matching command could not be found or a usage error occurred, 174 | /// a helpful error message will be written to `stderr`, then the process 175 | /// will exit with a failure error code. 176 | public func main(defaultVerb: String, errorHandler: (ClientError) -> Void) -> Never 177 | 178 | /// Hands off execution to the CommandRegistry, by parsing `arguments` 179 | /// and then running whichever command has been identified in the argument 180 | /// list. 181 | /// 182 | /// If the chosen command executes successfully, the process will exit with 183 | /// a successful exit code. 184 | /// 185 | /// If the chosen command fails, the provided error handler will be invoked, 186 | /// then the process will exit with a failure exit code. 187 | /// 188 | /// If a matching command could not be found but there is any `executable-verb` 189 | /// style subcommand executable in the caller's `$PATH`, the subcommand will 190 | /// be executed. 191 | /// 192 | /// If a matching command could not be found or a usage error occurred, 193 | /// a helpful error message will be written to `stderr`, then the process 194 | /// will exit with a failure error code. 195 | public func main(arguments: [String], defaultVerb: String, errorHandler: (ClientError) -> Void) -> Never 196 | } 197 | 198 | /// A type-erased command. 199 | public struct CommandWrapper where ClientError: Error { 200 | public let verb: String 201 | 202 | public let function: String 203 | 204 | public let run: (Commandant.ArgumentParser) -> Result> 205 | 206 | public let usage: () -> Commandant.CommandantError? 207 | } 208 | 209 | /// Possible errors that can originate from Commandant. 210 | /// 211 | /// `ClientError` should be the type of error (if any) that can occur when 212 | /// running commands. 213 | public enum CommandantError: Error { 214 | /// An option was used incorrectly. 215 | case usageError(description: String) 216 | 217 | /// An error occurred while running a command. 218 | case commandError(ClientError) 219 | } 220 | 221 | extension CommandantError: CustomStringConvertible { 222 | /// A textual representation of this instance. 223 | /// 224 | /// Calling this property directly is discouraged. Instead, convert an 225 | /// instance of any type to a string by using the `String(describing:)` 226 | /// initializer. This initializer works with any type, and uses the custom 227 | /// `description` property for types that conform to 228 | /// `CustomStringConvertible`: 229 | /// 230 | /// struct Point: CustomStringConvertible { 231 | /// let x: Int, y: Int 232 | /// 233 | /// var description: String { 234 | /// return "(\(x), \(y))" 235 | /// } 236 | /// } 237 | /// 238 | /// let p = Point(x: 21, y: 30) 239 | /// let s = String(describing: p) 240 | /// print(s) 241 | /// // Prints "(21, 30)" 242 | /// 243 | /// The conversion of `p` to a string in the assignment to `s` uses the 244 | /// `Point` type's `description` property. 245 | public var description: String { get } 246 | } 247 | 248 | /// A basic implementation of a `help` command, using information available in a 249 | /// `CommandRegistry`. 250 | /// 251 | /// If you want to use this command, initialize it with the registry, then add 252 | /// it to that same registry: 253 | /// 254 | /// let commands: CommandRegistry = … 255 | /// let helpCommand = HelpCommand(registry: commands) 256 | /// commands.register(helpCommand) 257 | public struct HelpCommand: Commandant.CommandProtocol where ClientError: Error { 258 | /// The command's options type. 259 | public typealias Options = Commandant.HelpOptions 260 | 261 | /// The action that users should specify to use this subcommand (e.g., 262 | /// `help`). 263 | public let verb: String 264 | 265 | /// A human-readable, high-level description of what this command is used 266 | /// for. 267 | public let function: String 268 | 269 | /// Initializes the command to provide help from the given registry of 270 | /// commands. 271 | public init(registry: Commandant.CommandRegistry, function: String? = nil) 272 | 273 | /// Runs this subcommand with the given options. 274 | public func run(_ options: Commandant.HelpCommand.Options) -> Result 275 | } 276 | 277 | public struct HelpOptions: Commandant.OptionsProtocol where ClientError: Error { 278 | /// Evaluates this set of options in the given mode. 279 | /// 280 | /// Returns the parsed options or a `UsageError`. 281 | public static func evaluate(_ m: Commandant.CommandMode) -> Result, Commandant.CommandantError> 282 | } 283 | 284 | /// An `OptionsProtocol` that has no options. 285 | public struct NoOptions: Commandant.OptionsProtocol where ClientError: Error { 286 | public init() 287 | 288 | /// Evaluates this set of options in the given mode. 289 | /// 290 | /// Returns the parsed options or a `UsageError`. 291 | public static func evaluate(_ m: Commandant.CommandMode) -> Result, Commandant.CommandantError> 292 | } 293 | 294 | /// Describes an option that can be provided on the command line. 295 | public struct Option { 296 | /// The key that controls this option. For example, a key of `verbose` would 297 | /// be used for a `--verbose` option. 298 | public let key: String 299 | 300 | /// The default value for this option. This is the value that will be used 301 | /// if the option is never explicitly specified on the command line. 302 | public let defaultValue: T 303 | 304 | /// A human-readable string describing the purpose of this option. This will 305 | /// be shown in help messages. 306 | /// 307 | /// For boolean operations, this should describe the effect of _not_ using 308 | /// the default value (i.e., what will happen if you disable/enable the flag 309 | /// differently from the default). 310 | public let usage: String 311 | 312 | public init(key: String, defaultValue: T, usage: String) 313 | } 314 | 315 | extension Option: CustomStringConvertible { 316 | /// A textual representation of this instance. 317 | /// 318 | /// Calling this property directly is discouraged. Instead, convert an 319 | /// instance of any type to a string by using the `String(describing:)` 320 | /// initializer. This initializer works with any type, and uses the custom 321 | /// `description` property for types that conform to 322 | /// `CustomStringConvertible`: 323 | /// 324 | /// struct Point: CustomStringConvertible { 325 | /// let x: Int, y: Int 326 | /// 327 | /// var description: String { 328 | /// return "(\(x), \(y))" 329 | /// } 330 | /// } 331 | /// 332 | /// let p = Point(x: 21, y: 30) 333 | /// let s = String(describing: p) 334 | /// print(s) 335 | /// // Prints "(21, 30)" 336 | /// 337 | /// The conversion of `p` to a string in the assignment to `s` uses the 338 | /// `Point` type's `description` property. 339 | public var description: String { get } 340 | } 341 | 342 | /// Represents a record of options for a command, which can be parsed from 343 | /// a list of command-line arguments. 344 | /// 345 | /// This is most helpful when used in conjunction with the `Option` and `Switch` 346 | /// types, and `<*>` and `<|` combinators. 347 | /// 348 | /// Example: 349 | /// 350 | /// struct LogOptions: OptionsProtocol { 351 | /// let verbosity: Int 352 | /// let outputFilename: String 353 | /// let shouldDelete: Bool 354 | /// let logName: String 355 | /// 356 | /// static func create(_ verbosity: Int) -> (String) -> (Bool) -> (String) -> LogOptions { 357 | /// return { outputFilename in { shouldDelete in { logName in LogOptions(verbosity: verbosity, outputFilename: outputFilename, shouldDelete: shouldDelete, logName: logName) } } } 358 | /// } 359 | /// 360 | /// static func evaluate(_ m: CommandMode) -> Result> { 361 | /// return create 362 | /// <*> m <| Option(key: "verbose", defaultValue: 0, usage: "the verbosity level with which to read the logs") 363 | /// <*> m <| Option(key: "outputFilename", defaultValue: "", usage: "a file to print output to, instead of stdout") 364 | /// <*> m <| Switch(flag: "d", key: "delete", usage: "delete the logs when finished") 365 | /// <*> m <| Argument(usage: "the log to read") 366 | /// } 367 | /// } 368 | public protocol OptionsProtocol { 369 | associatedtype ClientError: Error 370 | 371 | /// Evaluates this set of options in the given mode. 372 | /// 373 | /// Returns the parsed options or a `UsageError`. 374 | static func evaluate(_ m: Commandant.CommandMode) -> Result> 375 | } 376 | 377 | /// Describes a parameterless command line flag that defaults to false and can only 378 | /// be switched on. Canonical examples include `--force` and `--recurse`. 379 | /// 380 | /// For a boolean toggle that can be enabled and disabled use `Option`. 381 | public struct Switch { 382 | /// The key that enables this switch. For example, a key of `verbose` would be 383 | /// used for a `--verbose` option. 384 | public let key: String 385 | 386 | /// Optional single letter flag that enables this switch. For example, `-v` would 387 | /// be used as a shorthand for `--verbose`. 388 | /// 389 | /// Multiple flags can be grouped together as a single argument and will split 390 | /// when parsing (e.g. `rm -rf` treats 'r' and 'f' as inidividual flags). 391 | public let flag: Character? 392 | 393 | /// A human-readable string describing the purpose of this option. This will 394 | /// be shown in help messages. 395 | public let usage: String 396 | 397 | public init(flag: Character? = nil, key: String, usage: String) 398 | } 399 | 400 | extension Switch: CustomStringConvertible { 401 | /// A textual representation of this instance. 402 | /// 403 | /// Calling this property directly is discouraged. Instead, convert an 404 | /// instance of any type to a string by using the `String(describing:)` 405 | /// initializer. This initializer works with any type, and uses the custom 406 | /// `description` property for types that conform to 407 | /// `CustomStringConvertible`: 408 | /// 409 | /// struct Point: CustomStringConvertible { 410 | /// let x: Int, y: Int 411 | /// 412 | /// var description: String { 413 | /// return "(\(x), \(y))" 414 | /// } 415 | /// } 416 | /// 417 | /// let p = Point(x: 21, y: 30) 418 | /// let s = String(describing: p) 419 | /// print(s) 420 | /// // Prints "(21, 30)" 421 | /// 422 | /// The conversion of `p` to a string in the assignment to `s` uses the 423 | /// `Point` type's `description` property. 424 | public var description: String { get } 425 | } 426 | 427 | extension Int: Commandant.ArgumentProtocol { 428 | /// A human-readable name for this type. 429 | public static let name: String 430 | 431 | /// Attempts to parse a value from the given command-line argument. 432 | public static func from(string: String) -> Int? 433 | } 434 | 435 | extension String: Commandant.ArgumentProtocol { 436 | /// A human-readable name for this type. 437 | public static let name: String 438 | 439 | /// Attempts to parse a value from the given command-line argument. 440 | public static func from(string: String) -> String? 441 | } 442 | 443 | extension RawRepresentable where Self: Commandant.ArgumentProtocol, Self.RawValue: StringProtocol { 444 | public static func from(string: String) -> Self? 445 | } 446 | 447 | extension RawRepresentable where Self: Commandant.ArgumentProtocol, Self.RawValue: FixedWidthInteger { 448 | public static func from(string: String) -> Self? 449 | } 450 | 451 | infix operator <*>: LogicalDisjunctionPrecedence 452 | 453 | infix operator <|: MultiplicationPrecedence 454 | -------------------------------------------------------------------------------- /Examples/Mini.swift: -------------------------------------------------------------------------------- 1 | import Dispatch 2 | import Foundation 3 | import NIOConcurrencyHelpers 4 | import RxSwift 5 | import SwiftOnoneSupport 6 | 7 | /** 8 | Protocol that has to be conformed by any object that can be dispatcher 9 | by a `Dispatcher` object. 10 | */ 11 | public protocol Action { 12 | /// Equality function between `Action` objects 13 | /// - Returns: If an `Action` is the same as other. 14 | func isEqual(to other: Mini.Action) -> Bool 15 | } 16 | 17 | extension Action { 18 | /// String used as tag of the given Action based on his name. 19 | /// - Returns: The name of the action as a String. 20 | public var innerTag: String { get } 21 | } 22 | 23 | extension Action { 24 | /// Equality operator between `Action` objects. 25 | /// - Returns: If the `Action`s are equal or not. 26 | public static func == (lhs: Self, rhs: Self) -> Bool 27 | } 28 | 29 | extension Action where Self: Equatable { 30 | /// Convenience `isEqual` implementation when the `Action` object 31 | /// implements `Equatable`. 32 | /// - Returns: Whether the `Action` object is the same as other. 33 | public func isEqual(to other: Mini.Action) -> Bool 34 | } 35 | 36 | public protocol Chain { 37 | var proceed: Mini.Next { get } 38 | } 39 | 40 | public protocol CompletableAction: Mini.Action, Mini.PayloadAction {} 41 | 42 | public final class Dispatcher { 43 | public struct DispatchMode { 44 | public enum UI { 45 | case sync 46 | 47 | case async 48 | } 49 | } 50 | 51 | public var subscriptionCount: Int { get } 52 | 53 | public static let defaultPriority: Int 54 | 55 | public init() 56 | 57 | public func add(middleware: Mini.Middleware) 58 | 59 | public func remove(middleware: Mini.Middleware) 60 | 61 | public func register(service: Mini.Service) 62 | 63 | public func unregister(service: Mini.Service) 64 | 65 | public func subscribe(priority: Int, tag: String, completion: @escaping (Mini.Action) -> Void) -> Mini.DispatcherSubscription 66 | 67 | public func registerInternal(subscription: Mini.DispatcherSubscription) -> Mini.DispatcherSubscription 68 | 69 | public func unregisterInternal(subscription: Mini.DispatcherSubscription) 70 | 71 | public func subscribe(completion: @escaping (T) -> Void) -> Mini.DispatcherSubscription where T: Mini.Action 72 | 73 | public func subscribe(tag: String, completion: @escaping (T) -> Void) -> Mini.DispatcherSubscription where T: Mini.Action 74 | 75 | public func subscribe(tag: String, completion: @escaping (Mini.Action) -> Void) -> Mini.DispatcherSubscription 76 | 77 | public func dispatch(_ action: Mini.Action, mode: Mini.Dispatcher.DispatchMode.UI) 78 | } 79 | 80 | public final class DispatcherSubscription: Comparable, RxSwift.Disposable { 81 | public let id: Int 82 | 83 | public let tag: String 84 | 85 | public init(dispatcher: Mini.Dispatcher, id: Int, priority: Int, tag: String, completion: @escaping (Mini.Action) -> Void) 86 | 87 | /// Dispose resource. 88 | public func dispose() 89 | 90 | public func on(_ action: Mini.Action) 91 | 92 | /// Returns a Boolean value indicating whether two values are equal. 93 | /// 94 | /// Equality is the inverse of inequality. For any values `a` and `b`, 95 | /// `a == b` implies that `a != b` is `false`. 96 | /// 97 | /// - Parameters: 98 | /// - lhs: A value to compare. 99 | /// - rhs: Another value to compare. 100 | public static func == (lhs: Mini.DispatcherSubscription, rhs: Mini.DispatcherSubscription) -> Bool 101 | 102 | /// Returns a Boolean value indicating whether the value of the first 103 | /// argument is greater than that of the second argument. 104 | /// 105 | /// - Parameters: 106 | /// - lhs: A value to compare. 107 | /// - rhs: Another value to compare. 108 | public static func > (lhs: Mini.DispatcherSubscription, rhs: Mini.DispatcherSubscription) -> Bool 109 | 110 | /// Returns a Boolean value indicating whether the value of the first 111 | /// argument is less than that of the second argument. 112 | /// 113 | /// This function is the only requirement of the `Comparable` protocol. The 114 | /// remainder of the relational operator functions are implemented by the 115 | /// standard library for any type that conforms to `Comparable`. 116 | /// 117 | /// - Parameters: 118 | /// - lhs: A value to compare. 119 | /// - rhs: Another value to compare. 120 | public static func < (lhs: Mini.DispatcherSubscription, rhs: Mini.DispatcherSubscription) -> Bool 121 | 122 | /// Returns a Boolean value indicating whether the value of the first 123 | /// argument is greater than or equal to that of the second argument. 124 | /// 125 | /// - Parameters: 126 | /// - lhs: A value to compare. 127 | /// - rhs: Another value to compare. 128 | public static func >= (lhs: Mini.DispatcherSubscription, rhs: Mini.DispatcherSubscription) -> Bool 129 | 130 | /// Returns a Boolean value indicating whether the value of the first 131 | /// argument is less than or equal to that of the second argument. 132 | /// 133 | /// - Parameters: 134 | /// - lhs: A value to compare. 135 | /// - rhs: Another value to compare. 136 | public static func <= (lhs: Mini.DispatcherSubscription, rhs: Mini.DispatcherSubscription) -> Bool 137 | } 138 | 139 | public protocol EmptyAction: Mini.Action, Mini.PayloadAction where Self.Payload == Void { 140 | init(promise: Mini.Promise) 141 | } 142 | 143 | extension EmptyAction { 144 | public init(promise _: Mini.Promise) 145 | } 146 | 147 | public final class ForwardingChain: Mini.Chain { 148 | public var proceed: Mini.Next { get } 149 | 150 | public init(next: @escaping Mini.Next) 151 | } 152 | 153 | public protocol Group: RxSwift.Disposable { 154 | var disposeBag: RxSwift.CompositeDisposable { get } 155 | } 156 | 157 | public protocol KeyedCompletableAction: Mini.Action, Mini.KeyedPayloadAction {} 158 | 159 | public protocol KeyedPayloadAction { 160 | associatedtype Payload 161 | 162 | associatedtype Key: Hashable 163 | 164 | init(promise: [Self.Key: Mini.Promise]) 165 | } 166 | 167 | public protocol Middleware { 168 | var id: UUID { get } 169 | 170 | var perform: Mini.MiddlewareChain { get } 171 | } 172 | 173 | public typealias MiddlewareChain = (Mini.Action, Mini.Chain) -> Mini.Action 174 | 175 | public typealias Next = (Mini.Action) -> Mini.Action 176 | 177 | /** 178 | An Ordered Set is a collection where all items in the set follow an ordering, 179 | usually ordered from 'least' to 'most'. The way you value and compare items 180 | can be user-defined. 181 | */ 182 | public class OrderedSet where T: Comparable { 183 | public init(initial: [T] = []) 184 | 185 | /// Returns the number of elements in the OrderedSet. 186 | public var count: Int { get } 187 | 188 | /// Inserts an item. Performance: O(n) 189 | public func insert(_ item: T) -> Bool 190 | 191 | /// Insert an array of items 192 | public func insert(_ items: [T]) -> Bool 193 | 194 | /// Removes an item if it exists. Performance: O(n) 195 | public func remove(_ item: T) -> Bool 196 | 197 | /// Returns true if and only if the item exists somewhere in the set. 198 | public func exists(_ item: T) -> Bool 199 | 200 | /// Returns the index of an item if it exists, or nil otherwise. 201 | public func indexOf(_ item: T) -> Int? 202 | 203 | /// Returns the item at the given index. 204 | /// Assertion fails if the index is out of the range of [0, count). 205 | public subscript(_: Int) -> T { get } 206 | 207 | /// Returns the 'maximum' or 'largest' value in the set. 208 | public var max: T? { get } 209 | 210 | /// Returns the 'minimum' or 'smallest' value in the set. 211 | public var min: T? { get } 212 | 213 | /// Returns the k-th largest element in the set, if k is in the range 214 | /// [1, count]. Returns nil otherwise. 215 | public func kLargest(element: Int) -> T? 216 | 217 | /// Returns the k-th smallest element in the set, if k is in the range 218 | /// [1, count]. Returns nil otherwise. 219 | public func kSmallest(element: Int) -> T? 220 | 221 | /// For each function 222 | public func forEach(_ body: (T) -> Void) 223 | 224 | /// Enumerated function 225 | public func enumerated() -> EnumeratedSequence<[T]> 226 | } 227 | 228 | public protocol PayloadAction { 229 | associatedtype Payload 230 | 231 | init(promise: Mini.Promise) 232 | } 233 | 234 | @dynamicMemberLookup public final class Promise: Mini.PromiseType { 235 | public typealias Element = T 236 | 237 | public class func value(_ value: T) -> Mini.Promise 238 | 239 | public class func error(_ error: Error) -> Mini.Promise 240 | 241 | public init(error: Error) 242 | 243 | public init() 244 | 245 | public class func idle(with options: [String: Any] = [:]) -> Mini.Promise 246 | 247 | public class func pending(options: [String: Any] = [:]) -> Mini.Promise 248 | 249 | public var result: Result? { get } 250 | 251 | /// - Note: `fulfill` do not trigger an object reassignment, 252 | /// so no notifications about it can be triggered. It is recommended 253 | /// to call the method `notify` afterwards. 254 | public func fulfill(_ value: T) -> Self 255 | 256 | /// - Note: `reject` do not trigger an object reassignment, 257 | /// so no notifications about it can be triggered. It is recommended 258 | /// to call the method `notify` afterwards. 259 | public func reject(_ error: Error) -> Self 260 | 261 | /// Resolves the current `Promise` with the optional `Result` parameter. 262 | /// - Returns: `self` or `nil` if no `result` was not provided. 263 | /// - Note: The optional parameter and restun value are helpers in order to 264 | /// make optional chaining in the `Reducer` context. 265 | public func resolve(_ result: Result?) -> Self? 266 | 267 | public subscript(dynamicMember member: String) -> T? 268 | } 269 | 270 | extension Promise { 271 | /** 272 | - Returns: `true` if the promise has not yet resolved nor pending. 273 | */ 274 | public var isIdle: Bool { get } 275 | 276 | /** 277 | - Returns: `true` if the promise has not yet resolved. 278 | */ 279 | public var isPending: Bool { get } 280 | 281 | /** 282 | - Returns: `true` if the promise has completed. 283 | */ 284 | public var isCompleted: Bool { get } 285 | 286 | /** 287 | - Returns: `true` if the promise has resolved. 288 | */ 289 | public var isResolved: Bool { get } 290 | 291 | /** 292 | - Returns: `true` if the promise was fulfilled. 293 | */ 294 | public var isFulfilled: Bool { get } 295 | 296 | /** 297 | - Returns: `true` if the promise was rejected. 298 | */ 299 | public var isRejected: Bool { get } 300 | 301 | /** 302 | - Returns: The value with which this promise was fulfilled or `nil` if this promise is pending or rejected. 303 | */ 304 | public var value: T? { get } 305 | 306 | /** 307 | - Returns: The error with which this promise was rejected or `nil` if this promise is pending or fulfilled. 308 | */ 309 | public var error: Error? { get } 310 | } 311 | 312 | extension Promise where T == () { 313 | public convenience init() 314 | 315 | public static func empty() -> Mini.Promise 316 | } 317 | 318 | extension Promise: Equatable where T == () { 319 | /// Returns a Boolean value indicating whether two values are equal. 320 | /// 321 | /// Equality is the inverse of inequality. For any values `a` and `b`, 322 | /// `a == b` implies that `a != b` is `false`. 323 | /// 324 | /// - Parameters: 325 | /// - lhs: A value to compare. 326 | /// - rhs: Another value to compare. 327 | public static func == (lhs: Mini.Promise, rhs: Mini.Promise) -> Bool 328 | } 329 | 330 | extension Promise where T: Equatable { 331 | public static func == (lhs: Mini.Promise, rhs: Mini.Promise) -> Bool 332 | } 333 | 334 | extension Promise { 335 | public func notify(to store: T) where T: Mini.StoreType 336 | } 337 | 338 | public protocol PromiseType { 339 | associatedtype Element 340 | 341 | var result: Result? { get } 342 | 343 | var isIdle: Bool { get } 344 | 345 | var isPending: Bool { get } 346 | 347 | var isResolved: Bool { get } 348 | 349 | var isFulfilled: Bool { get } 350 | 351 | var isRejected: Bool { get } 352 | 353 | var value: Self.Element? { get } 354 | 355 | var error: Error? { get } 356 | 357 | func resolve(_ result: Result?) -> Self? 358 | 359 | func fulfill(_ value: Self.Element) -> Self 360 | 361 | func reject(_ error: Error) -> Self 362 | } 363 | 364 | public enum Promises {} 365 | 366 | extension Promises { 367 | public enum Lifetime { 368 | case once 369 | 370 | case forever(ignoringOld: Bool) 371 | } 372 | } 373 | 374 | /** 375 | The `Reducer` defines the behavior to be executed when a certain 376 | `Action` object is received. 377 | */ 378 | public class Reducer: RxSwift.Disposable where A: Mini.Action { 379 | /// The `Action` type which the `Reducer` listens to. 380 | public let action: A.Type 381 | 382 | /// The `Dispatcher` object that sends the `Action` objects. 383 | public let dispatcher: Mini.Dispatcher 384 | 385 | /// The behavior to be executed when the `Dispatcher` sends a certain `Action` 386 | public let reducer: (A) -> Void 387 | 388 | /** 389 | Initializes a new `Reducer` object. 390 | - Parameter action: The `Action` type that will be listened to. 391 | - Parameter dispatcher: The `Dispatcher` that sends the `Action`. 392 | - Parameter reducer: The closure that will be executed when the `Dispatcher` 393 | sends the defined `Action` type. 394 | */ 395 | public init(of action: A.Type, on dispatcher: Mini.Dispatcher, reducer: @escaping (A) -> Void) 396 | 397 | /// Dispose resource. 398 | public func dispose() 399 | } 400 | 401 | public class ReducerGroup: Mini.Group { 402 | public let disposeBag: RxSwift.CompositeDisposable 403 | 404 | public init(_ builder: RxSwift.Disposable...) 405 | 406 | /// Dispose resource. 407 | public func dispose() 408 | } 409 | 410 | public final class RootChain: Mini.Chain { 411 | public var proceed: Mini.Next { get } 412 | 413 | public init(map: Mini.SubscriptionMap) 414 | } 415 | 416 | public protocol Service { 417 | var id: UUID { get } 418 | 419 | var perform: Mini.ServiceChain { get } 420 | } 421 | 422 | public typealias ServiceChain = (Mini.Action, Mini.Chain) -> Void 423 | 424 | /// Wrapper class to allow pass dictionaries with a memory reference 425 | public class SharedDictionary where Key: Hashable { 426 | public var innerDictionary: [Key: Value] 427 | 428 | public init() 429 | 430 | public func getOrPut(_ key: Key, defaultValue: @autoclosure () -> Value) -> Value 431 | 432 | public func get(withKey key: Key) -> Value? 433 | 434 | public subscript(_: Key, orPut _: @autoclosure () -> Value) -> Value { get } 435 | 436 | public subscript(_: Key) -> Value? { get } 437 | } 438 | 439 | public protocol StateType { 440 | func isEqual(to other: Mini.StateType) -> Bool 441 | } 442 | 443 | extension StateType where Self: Equatable { 444 | public func isEqual(to other: Mini.StateType) -> Bool 445 | } 446 | 447 | public class Store: RxSwift.ObservableType, Mini.StoreType where State: Mini.StateType, StoreController: RxSwift.Disposable { 448 | /// Type of elements in sequence. 449 | public typealias Element = State 450 | 451 | public typealias State = State 452 | 453 | public typealias StoreController = StoreController 454 | 455 | public typealias ObjectWillChangePublisher = RxSwift.BehaviorSubject 456 | 457 | public var objectWillChange: RxSwift.BehaviorSubject 458 | 459 | public let dispatcher: Mini.Dispatcher 460 | 461 | public var storeController: StoreController 462 | 463 | public var state: State 464 | 465 | public var initialState: State { get } 466 | 467 | public init(_ state: State, dispatcher: Mini.Dispatcher, storeController: StoreController) 468 | 469 | public var reducerGroup: Mini.ReducerGroup { get } 470 | 471 | public func notify() 472 | 473 | public func replayOnce() 474 | 475 | public func reset() 476 | 477 | /** 478 | Subscribes `observer` to receive events for this sequence. 479 | 480 | ### Grammar 481 | 482 | **Next\* (Error | Completed)?** 483 | 484 | * sequences can produce zero or more elements so zero or more `Next` events can be sent to `observer` 485 | * once an `Error` or `Completed` event is sent, the sequence terminates and can't produce any other elements 486 | 487 | It is possible that events are sent from different threads, but no two events can be sent concurrently to 488 | `observer`. 489 | 490 | ### Resource Management 491 | 492 | When sequence sends `Complete` or `Error` event all internal resources that compute sequence elements 493 | will be freed. 494 | 495 | To cancel production of sequence elements and free resources immediately, call `dispose` on returned 496 | subscription. 497 | 498 | - returns: Subscription for `observer` that can be used to cancel production of sequence elements and free resources. 499 | */ 500 | public func subscribe(_ observer: Observer) -> RxSwift.Disposable where State == Observer.Element, Observer: RxSwift.ObserverType 501 | } 502 | 503 | public protocol StoreType { 504 | associatedtype State: Mini.StateType 505 | 506 | associatedtype StoreController: RxSwift.Disposable 507 | 508 | var state: Self.State { get set } 509 | 510 | var dispatcher: Mini.Dispatcher { get } 511 | 512 | var reducerGroup: Mini.ReducerGroup { get } 513 | 514 | func replayOnce() 515 | } 516 | 517 | extension StoreType { 518 | /** 519 | Property responsible of reduce the `State` given a certain `Action` being triggered. 520 | ``` 521 | public var reducerGroup: ReducerGroup { 522 | ReducerGroup {[ 523 | Reducer(of: SomeAction.self, on: self.dispatcher) { (action: SomeAction) 524 | self.state = myCoolNewState 525 | }, 526 | Reducer(of: OtherAction.self, on: self.dispatcher) { (action: OtherAction) 527 | // Needed work 528 | self.state = myAnotherState 529 | } 530 | } 531 | ]} 532 | ``` 533 | - Note : The property has a default implementation which complies with the @_functionBuilder's current limitations, where no empty blocks can be produced in this iteration. 534 | */ 535 | public var reducerGroup: Mini.ReducerGroup { get } 536 | } 537 | 538 | public typealias SubscriptionMap = Mini.SharedDictionary?> 539 | 540 | extension Dictionary { 541 | /// Returns the value for the given key. If the key is not found in the map, calls the `defaultValue` function, 542 | /// puts its result into the map under the given key and returns it. 543 | public mutating func getOrPut(_ key: Key, defaultValue: @autoclosure () -> Value) -> Value 544 | 545 | public subscript(_: Key, orPut _: @autoclosure () -> Value) -> Value { mutating get } 546 | 547 | public subscript(unwrapping _: Key) -> Value! { get } 548 | } 549 | 550 | extension Dictionary where Value: Mini.PromiseType { 551 | public subscript(promise _: Key) -> Value { get } 552 | 553 | public func hasValue(for key: [Key: Value].Key) -> Bool 554 | 555 | public func resolve(with other: [Key: Value]) -> [Key: Value] 556 | 557 | public func mergingNew(with other: [Key: Value]) -> [Key: Value] 558 | } 559 | 560 | extension Dictionary where Value: Mini.PromiseType, Value.Element: Equatable { 561 | public static func == (lhs: [Key: Value], rhs: [Key: Value]) -> Bool 562 | } 563 | 564 | extension DispatchQueue { 565 | public static var isMain: Bool { get } 566 | } 567 | 568 | extension ObservableType { 569 | /// Take the first element that matches the filter function. 570 | /// 571 | /// - Parameter fn: Filter closure. 572 | /// - Returns: The first element that matches the filter. 573 | public func filterOne(_ condition: @escaping (Self.Element) -> Bool) -> RxSwift.Observable 574 | } 575 | 576 | extension ObservableType where Self.Element: Mini.StoreType, Self.Element: RxSwift.ObservableType, Self.Element.Element == Self.Element.State { 577 | public static func dispatch(using dispatcher: Mini.Dispatcher, factory action: @autoclosure @escaping () -> A, taskMap: @escaping (Self.Element.State) -> T?, on store: Self.Element, lifetime: Mini.Promises.Lifetime = .once) -> RxSwift.Observable where A: Mini.Action, T: Mini.Promise 578 | 579 | public static func dispatch(using dispatcher: Mini.Dispatcher, factory action: @autoclosure @escaping () -> A, key: K, taskMap: @escaping (Self.Element.State) -> [K: T], on store: Self.Element, lifetime: Mini.Promises.Lifetime = .once) -> RxSwift.Observable where A: Mini.Action, K: Hashable, T: Mini.Promise 580 | } 581 | 582 | extension PrimitiveSequenceType where Self: RxSwift.ObservableConvertibleType, Self.Trait == RxSwift.SingleTrait { 583 | public func dispatch(action: A.Type, on dispatcher: Mini.Dispatcher, mode: Mini.Dispatcher.DispatchMode.UI = .async, fillOnError errorPayload: A.Payload? = nil) -> RxSwift.Disposable where A: Mini.CompletableAction, Self.Element == A.Payload 584 | 585 | public func dispatch(action: A.Type, key: A.Key, on dispatcher: Mini.Dispatcher, mode: Mini.Dispatcher.DispatchMode.UI = .async, fillOnError errorPayload: A.Payload? = nil) -> RxSwift.Disposable where A: Mini.KeyedCompletableAction, Self.Element == A.Payload 586 | 587 | public func action(_ action: A.Type, fillOnError errorPayload: A.Payload? = nil) -> RxSwift.Single where A: Mini.CompletableAction, Self.Element == A.Payload 588 | } 589 | 590 | extension PrimitiveSequenceType where Self.Element == Never, Self.Trait == RxSwift.CompletableTrait { 591 | public func dispatch(action: A.Type, on dispatcher: Mini.Dispatcher, mode: Mini.Dispatcher.DispatchMode.UI = .async) -> RxSwift.Disposable where A: Mini.EmptyAction 592 | 593 | public func action(_ action: A.Type) -> RxSwift.Single where A: Mini.EmptyAction 594 | } 595 | -------------------------------------------------------------------------------- /Examples/SourceKittenFramework.swift: -------------------------------------------------------------------------------- 1 | import Clang_C 2 | import Dispatch 3 | import Foundation 4 | import SourceKit 5 | import SwiftOnoneSupport 6 | import SWXMLHash 7 | import Yams 8 | 9 | /// A [strict total order](http://en.wikipedia.org/wiki/Total_order#Strict_total_order) 10 | /// over instances of `Self`. 11 | public func < (lhs: SourceKittenFramework.SourceDeclaration, rhs: SourceKittenFramework.SourceDeclaration) -> Bool 12 | 13 | /// A [strict total order](http://en.wikipedia.org/wiki/Total_order#Strict_total_order) 14 | /// over instances of `Self`. 15 | public func < (lhs: SourceKittenFramework.SourceLocation, rhs: SourceKittenFramework.SourceLocation) -> Bool 16 | 17 | public func == (lhs: SourceKittenFramework.SourceLocation, rhs: SourceKittenFramework.SourceLocation) -> Bool 18 | 19 | /** 20 | Returns true if `lhs` Structure is equal to `rhs` Structure. 21 | 22 | - parameter lhs: Structure to compare to `rhs`. 23 | - parameter rhs: Structure to compare to `lhs`. 24 | 25 | - returns: True if `lhs` Structure is equal to `rhs` Structure. 26 | */ 27 | public func == (lhs: SourceKittenFramework.Structure, rhs: SourceKittenFramework.Structure) -> Bool 28 | 29 | /** 30 | Returns true if `lhs` SyntaxMap is equal to `rhs` SyntaxMap. 31 | 32 | - parameter lhs: SyntaxMap to compare to `rhs`. 33 | - parameter rhs: SyntaxMap to compare to `lhs`. 34 | 35 | - returns: True if `lhs` SyntaxMap is equal to `rhs` SyntaxMap. 36 | */ 37 | public func == (lhs: SourceKittenFramework.SyntaxMap, rhs: SourceKittenFramework.SyntaxMap) -> Bool 38 | 39 | /** 40 | Returns true if `lhs` SyntaxToken is equal to `rhs` SyntaxToken. 41 | 42 | - parameter lhs: SyntaxToken to compare to `rhs`. 43 | - parameter rhs: SyntaxToken to compare to `lhs`. 44 | 45 | - returns: True if `lhs` SyntaxToken is equal to `rhs` SyntaxToken. 46 | */ 47 | public func == (lhs: SourceKittenFramework.SyntaxToken, rhs: SourceKittenFramework.SyntaxToken) -> Bool 48 | 49 | public struct ClangAvailability { 50 | public let alwaysDeprecated: Bool 51 | 52 | public let alwaysUnavailable: Bool 53 | 54 | public let deprecationMessage: String? 55 | 56 | public let unavailableMessage: String? 57 | } 58 | 59 | /// Represents a group of CXTranslationUnits. 60 | public struct ClangTranslationUnit { 61 | public let declarations: [String: [SourceKittenFramework.SourceDeclaration]] 62 | 63 | /** 64 | Create a ClangTranslationUnit by passing Objective-C header files and clang compiler arguments. 65 | 66 | - parameter headerFiles: Objective-C header files to document. 67 | - parameter compilerArguments: Clang compiler arguments. 68 | */ 69 | public init(headerFiles: [String], compilerArguments: [String]) 70 | 71 | /** 72 | Failable initializer to create a ClangTranslationUnit by passing Objective-C header files and 73 | `xcodebuild` arguments. Optionally pass in a `path`. 74 | 75 | - parameter headerFiles: Objective-C header files to document. 76 | - parameter xcodeBuildArguments: The arguments necessary pass in to `xcodebuild` to link these header files. 77 | - parameter path: Path to run `xcodebuild` from. Uses current path by default. 78 | */ 79 | public init?(headerFiles: [String], xcodeBuildArguments: [String], inPath path: String = FileManager.default.currentDirectoryPath) 80 | } 81 | 82 | extension ClangTranslationUnit: CustomStringConvertible { 83 | /// A textual JSON representation of `ClangTranslationUnit`. 84 | public var description: String { get } 85 | } 86 | 87 | public struct CodeCompletionItem: CustomStringConvertible { 88 | public typealias NumBytesInt = Int64 89 | 90 | public let kind: String 91 | 92 | public let context: String 93 | 94 | public let name: String? 95 | 96 | public let descriptionKey: String? 97 | 98 | public let sourcetext: String? 99 | 100 | public let typeName: String? 101 | 102 | public let moduleName: String? 103 | 104 | public let docBrief: String? 105 | 106 | public let associatedUSRs: String? 107 | 108 | public let numBytesToErase: SourceKittenFramework.CodeCompletionItem.NumBytesInt? 109 | 110 | /// Dictionary representation of CodeCompletionItem. Useful for NSJSONSerialization. 111 | public var dictionaryValue: [String: Any] { get } 112 | 113 | /// A textual representation of this instance. 114 | /// 115 | /// Calling this property directly is discouraged. Instead, convert an 116 | /// instance of any type to a string by using the `String(describing:)` 117 | /// initializer. This initializer works with any type, and uses the custom 118 | /// `description` property for types that conform to 119 | /// `CustomStringConvertible`: 120 | /// 121 | /// struct Point: CustomStringConvertible { 122 | /// let x: Int, y: Int 123 | /// 124 | /// var description: String { 125 | /// return "(\(x), \(y))" 126 | /// } 127 | /// } 128 | /// 129 | /// let p = Point(x: 21, y: 30) 130 | /// let s = String(describing: p) 131 | /// print(s) 132 | /// // Prints "(21, 30)" 133 | /// 134 | /// The conversion of `p` to a string in the assignment to `s` uses the 135 | /// `Point` type's `description` property. 136 | public var description: String { get } 137 | 138 | public static func parse(response: [String: SourceKittenFramework.SourceKitRepresentable]) -> [SourceKittenFramework.CodeCompletionItem] 139 | } 140 | 141 | public struct Documentation { 142 | public let parameters: [SourceKittenFramework.Parameter] 143 | 144 | public let returnDiscussion: [SourceKittenFramework.Text] 145 | } 146 | 147 | /// Represents a source file. 148 | public final class File { 149 | /// File path. Nil if initialized directly with `File(contents:)`. 150 | public let path: String? 151 | 152 | /// File contents. 153 | public var contents: String 154 | 155 | /// File lines. 156 | public var lines: [SourceKittenFramework.Line] { get } 157 | 158 | /** 159 | Failable initializer by path. Fails if file contents could not be read as a UTF8 string. 160 | 161 | - parameter path: File path. 162 | */ 163 | public init?(path: String) 164 | 165 | public init(pathDeferringReading path: String) 166 | 167 | /** 168 | Initializer by file contents. File path is nil. 169 | 170 | - parameter contents: File contents. 171 | */ 172 | public init(contents: String) 173 | 174 | /// Formats the file. 175 | /// 176 | /// - Parameters: 177 | /// - trimmingTrailingWhitespace: Boolean 178 | /// - useTabs: Boolean 179 | /// - indentWidth: Int 180 | /// - Returns: formatted String 181 | /// - Throws: Request.Error 182 | public func format(trimmingTrailingWhitespace: Bool, useTabs: Bool, indentWidth: Int) throws -> String 183 | 184 | /** 185 | Parse source declaration string from SourceKit dictionary. 186 | 187 | - parameter dictionary: SourceKit dictionary to extract declaration from. 188 | 189 | - returns: Source declaration if successfully parsed. 190 | */ 191 | public func parseDeclaration(_ dictionary: [String: SourceKittenFramework.SourceKitRepresentable]) -> String? 192 | 193 | /** 194 | Parse line numbers containing the declaration's implementation from SourceKit dictionary. 195 | 196 | - parameter dictionary: SourceKit dictionary to extract declaration from. 197 | 198 | - returns: Line numbers containing the declaration's implementation. 199 | */ 200 | public func parseScopeRange(_ dictionary: [String: SourceKittenFramework.SourceKitRepresentable]) -> (start: Int, end: Int)? 201 | 202 | /** 203 | Returns a copy of the input dictionary with comment mark names, cursor.info information and 204 | parsed declarations for the top-level of the input dictionary and its substructures. 205 | 206 | - parameter dictionary: Dictionary to process. 207 | - parameter cursorInfoRequest: Cursor.Info request to get declaration information. 208 | */ 209 | public func process(dictionary: [String: SourceKittenFramework.SourceKitRepresentable], cursorInfoRequest: SourceKittenFramework.SourceKitObject? = nil, syntaxMap: SourceKittenFramework.SyntaxMap? = nil) -> [String: SourceKittenFramework.SourceKitRepresentable] 210 | } 211 | 212 | extension File: Hashable { 213 | /// Returns a Boolean value indicating whether two values are equal. 214 | /// 215 | /// Equality is the inverse of inequality. For any values `a` and `b`, 216 | /// `a == b` implies that `a != b` is `false`. 217 | /// 218 | /// - Parameters: 219 | /// - lhs: A value to compare. 220 | /// - rhs: Another value to compare. 221 | public static func == (lhs: SourceKittenFramework.File, rhs: SourceKittenFramework.File) -> Bool 222 | 223 | /// Hashes the essential components of this value by feeding them into the 224 | /// given hasher. 225 | /// 226 | /// Implement this method to conform to the `Hashable` protocol. The 227 | /// components used for hashing must be the same as the components compared 228 | /// in your type's `==` operator implementation. Call `hasher.combine(_:)` 229 | /// with each of these components. 230 | /// 231 | /// - Important: Never call `finalize()` on `hasher`. Doing so may become a 232 | /// compile-time error in the future. 233 | /// 234 | /// - Parameter hasher: The hasher to use when combining the components 235 | /// of this instance. 236 | public func hash(into hasher: inout Hasher) 237 | } 238 | 239 | /// File methods to generate and manipulate OffsetMap's. 240 | extension File { 241 | /** 242 | Creates an OffsetMap containing offset locations at which there are declarations that likely 243 | have documentation comments, but haven't been documented by SourceKitten yet. 244 | 245 | - parameter documentedTokenOffsets: Offsets where there are declarations that likely 246 | have documentation comments. 247 | - parameter dictionary: Docs dictionary to check for which offsets are already 248 | documented. 249 | 250 | - returns: OffsetMap containing offset locations at which there are declarations that likely 251 | have documentation comments, but haven't been documented by SourceKitten yet. 252 | */ 253 | public func makeOffsetMap(documentedTokenOffsets: [Int], dictionary: [String: SourceKittenFramework.SourceKitRepresentable]) -> SourceKittenFramework.OffsetMap 254 | } 255 | 256 | /// Language Enum. 257 | public enum Language { 258 | /// Swift. 259 | case swift 260 | 261 | /// Objective-C. 262 | case objc 263 | } 264 | 265 | /// Representation of line in String 266 | public struct Line { 267 | /// origin = 0 268 | public let index: Int 269 | 270 | /// Content 271 | public let content: String 272 | 273 | /// UTF16 based range in entire String. Equivalent to Range 274 | public let range: NSRange 275 | 276 | /// Byte based range in entire String. Equivalent to Range 277 | public let byteRange: NSRange 278 | } 279 | 280 | /// Represents source module to be documented. 281 | public struct Module { 282 | /// Module Name. 283 | public let name: String 284 | 285 | /// Compiler arguments required by SourceKit to process the source files in this Module. 286 | public let compilerArguments: [String] 287 | 288 | /// Source files to be documented in this Module. 289 | public let sourceFiles: [String] 290 | 291 | /// Documentation for this Module. Typically expensive computed property. 292 | public var docs: [SourceKittenFramework.SwiftDocs] { get } 293 | 294 | public init?(spmName: String, inPath path: String = FileManager.default.currentDirectoryPath) 295 | 296 | /** 297 | Failable initializer to create a Module by the arguments necessary pass in to `xcodebuild` to build it. 298 | Optionally pass in a `moduleName` and `path`. 299 | 300 | - parameter xcodeBuildArguments: The arguments necessary pass in to `xcodebuild` to build this Module. 301 | - parameter name: Module name. Will be parsed from `xcodebuild` output if nil. 302 | - parameter path: Path to run `xcodebuild` from. Uses current path by default. 303 | */ 304 | public init?(xcodeBuildArguments: [String], name: String? = nil, inPath path: String = FileManager.default.currentDirectoryPath) 305 | 306 | /** 307 | Initializer to create a Module by name and compiler arguments. 308 | 309 | - parameter name: Module name. 310 | - parameter compilerArguments: Compiler arguments required by SourceKit to process the source files in this Module. 311 | */ 312 | public init(name: String, compilerArguments: [String]) 313 | } 314 | 315 | extension Module: CustomStringConvertible { 316 | /// A textual representation of `Module`. 317 | public var description: String { get } 318 | } 319 | 320 | /** 321 | Objective-C declaration kinds. 322 | More or less equivalent to `SwiftDeclarationKind`, but with made up values because there's no such 323 | thing as SourceKit for Objective-C. 324 | */ 325 | public enum ObjCDeclarationKind: String { 326 | /// `category`. 327 | case category 328 | 329 | /// `class`. 330 | case `class` 331 | 332 | /// `constant`. 333 | case constant 334 | 335 | /// `enum`. 336 | case `enum` 337 | 338 | /// `enumcase`. 339 | case enumcase 340 | 341 | /// `initializer`. 342 | case initializer 343 | 344 | /// `method.class`. 345 | case methodClass 346 | 347 | /// `method.instance`. 348 | case methodInstance 349 | 350 | /// `property`. 351 | case property 352 | 353 | /// `protocol`. 354 | case `protocol` 355 | 356 | /// `typedef`. 357 | case typedef 358 | 359 | /// `function`. 360 | case function 361 | 362 | /// `mark`. 363 | case mark 364 | 365 | /// `struct` 366 | case `struct` 367 | 368 | /// `field` 369 | case field 370 | 371 | /// `ivar` 372 | case ivar 373 | 374 | /// `ModuleImport` 375 | case moduleImport 376 | 377 | /// `UnexposedDecl` 378 | case unexposedDecl 379 | 380 | public init(_ cursorKind: CXCursorKind) 381 | } 382 | 383 | /// Type that maps potentially documented declaration offsets to its closest parent offset. 384 | public typealias OffsetMap = [Int: Int] 385 | 386 | public struct Parameter { 387 | public let name: String 388 | 389 | public let discussion: [SourceKittenFramework.Text] 390 | } 391 | 392 | /// Represents a SourceKit request. 393 | public enum Request { 394 | /// An `editor.open` request for the given File. 395 | case editorOpen(file: SourceKittenFramework.File) 396 | 397 | /// A `cursorinfo` request for an offset in the given file, using the `arguments` given. 398 | case cursorInfo(file: String, offset: Int64, arguments: [String]) 399 | 400 | /// A `cursorinfo` request for a USR in the given file, using the `arguments` given. 401 | case cursorInfoUSR(file: String, usr: String, arguments: [String], cancelOnSubsequentRequest: Bool) 402 | 403 | /// A custom request by passing in the `SourceKitObject` directly. 404 | case customRequest(request: SourceKittenFramework.SourceKitObject) 405 | 406 | /// A request generated by sourcekit using the yaml representation. 407 | case yamlRequest(yaml: String) 408 | 409 | /// A `codecomplete` request by passing in the file name, contents, offset 410 | /// for which to generate code completion options and array of compiler arguments. 411 | case codeCompletionRequest(file: String, contents: String, offset: Int64, arguments: [String]) 412 | 413 | /// ObjC Swift Interface 414 | case interface(file: String, uuid: String, arguments: [String]) 415 | 416 | /// Find USR 417 | case findUSR(file: String, usr: String) 418 | 419 | /// Index 420 | case index(file: String, arguments: [String]) 421 | 422 | /// Format 423 | case format(file: String, line: Int64, useTabs: Bool, indentWidth: Int64) 424 | 425 | /// ReplaceText 426 | case replaceText(file: String, offset: Int64, length: Int64, sourceText: String) 427 | 428 | /// A documentation request for the given source text. 429 | case docInfo(text: String, arguments: [String]) 430 | 431 | /// A documentation request for the given module. 432 | case moduleInfo(module: String, arguments: [String]) 433 | 434 | /// Gets the serialized representation of the file's SwiftSyntax tree. JSON string if `byteTree` is false, 435 | /// binary data otherwise. 436 | case syntaxTree(file: SourceKittenFramework.File, byteTree: Bool) 437 | 438 | /** 439 | Sends the request to SourceKit and return the response as an [String: SourceKitRepresentable]. 440 | 441 | - returns: SourceKit output as a dictionary. 442 | - throws: Request.Error on fail () 443 | */ 444 | public func send() throws -> [String: SourceKittenFramework.SourceKitRepresentable] 445 | 446 | /// A enum representation of SOURCEKITD_ERROR_* 447 | public enum Error: Error, CustomStringConvertible { 448 | case connectionInterrupted(String?) 449 | 450 | case invalid(String?) 451 | 452 | case failed(String?) 453 | 454 | case cancelled(String?) 455 | 456 | case unknown(String?) 457 | 458 | /// A textual representation of `self`. 459 | public var description: String { get } 460 | } 461 | 462 | /** 463 | Sends the request to SourceKit and return the response as an [String: SourceKitRepresentable]. 464 | 465 | - returns: SourceKit output as a dictionary. 466 | - throws: Request.Error on fail () 467 | */ 468 | @available(*, deprecated, renamed: "send()") 469 | public func failableSend() throws -> [String: SourceKittenFramework.SourceKitRepresentable] 470 | } 471 | 472 | extension Request: CustomStringConvertible { 473 | /// A textual representation of `Request`. 474 | public var description: String { get } 475 | } 476 | 477 | /// Represents a source code declaration. 478 | public struct SourceDeclaration { 479 | public let type: SourceKittenFramework.ObjCDeclarationKind 480 | 481 | public let location: SourceKittenFramework.SourceLocation 482 | 483 | public let extent: (start: SourceKittenFramework.SourceLocation, end: SourceKittenFramework.SourceLocation) 484 | 485 | public let name: String? 486 | 487 | public let usr: String? 488 | 489 | public let declaration: String? 490 | 491 | public let documentation: SourceKittenFramework.Documentation? 492 | 493 | public let commentBody: String? 494 | 495 | public var children: [SourceKittenFramework.SourceDeclaration] 496 | 497 | public let annotations: [String]? 498 | 499 | public let swiftDeclaration: String? 500 | 501 | public let swiftName: String? 502 | 503 | public let availability: SourceKittenFramework.ClangAvailability? 504 | 505 | /// Range 506 | public var range: NSRange { get } 507 | 508 | /// Returns the USR for the auto-generated getter for this property. 509 | /// 510 | /// - warning: can only be invoked if `type == .Property`. 511 | public var getterUSR: String { get } 512 | 513 | /// Returns the USR for the auto-generated setter for this property. 514 | /// 515 | /// - warning: can only be invoked if `type == .Property`. 516 | public var setterUSR: String { get } 517 | } 518 | 519 | extension SourceDeclaration: Hashable { 520 | /// Hashes the essential components of this value by feeding them into the 521 | /// given hasher. 522 | /// 523 | /// Implement this method to conform to the `Hashable` protocol. The 524 | /// components used for hashing must be the same as the components compared 525 | /// in your type's `==` operator implementation. Call `hasher.combine(_:)` 526 | /// with each of these components. 527 | /// 528 | /// - Important: Never call `finalize()` on `hasher`. Doing so may become a 529 | /// compile-time error in the future. 530 | /// 531 | /// - Parameter hasher: The hasher to use when combining the components 532 | /// of this instance. 533 | public func hash(into hasher: inout Hasher) 534 | 535 | /// Returns a Boolean value indicating whether two values are equal. 536 | /// 537 | /// Equality is the inverse of inequality. For any values `a` and `b`, 538 | /// `a == b` implies that `a != b` is `false`. 539 | /// 540 | /// - Parameters: 541 | /// - lhs: A value to compare. 542 | /// - rhs: Another value to compare. 543 | public static func == (lhs: SourceKittenFramework.SourceDeclaration, rhs: SourceKittenFramework.SourceDeclaration) -> Bool 544 | } 545 | 546 | extension SourceDeclaration: Comparable {} 547 | 548 | /// Swift representation of sourcekitd_object_t 549 | public final class SourceKitObject { 550 | /// Updates the value stored in the dictionary for the given key, 551 | /// or adds a new key-value pair if the key does not exist. 552 | /// 553 | /// - Parameters: 554 | /// - value: The new value to add to the dictionary. 555 | /// - key: The key to associate with value. If key already exists in the dictionary, 556 | /// value replaces the existing associated value. If key isn't already a key of the dictionary 557 | public func updateValue(_ value: SourceKittenFramework.SourceKitObjectConvertible, forKey key: SourceKittenFramework.UID) 558 | 559 | public func updateValue(_ value: SourceKittenFramework.SourceKitObjectConvertible, forKey key: String) 560 | 561 | public func updateValue(_ value: SourceKittenFramework.SourceKitObjectConvertible, forKey key: T) where T: RawRepresentable, T.RawValue == String 562 | } 563 | 564 | extension SourceKitObject: SourceKittenFramework.SourceKitObjectConvertible { 565 | public var sourceKitObject: SourceKittenFramework.SourceKitObject? { get } 566 | } 567 | 568 | extension SourceKitObject: CustomStringConvertible { 569 | /// A textual representation of this instance. 570 | /// 571 | /// Calling this property directly is discouraged. Instead, convert an 572 | /// instance of any type to a string by using the `String(describing:)` 573 | /// initializer. This initializer works with any type, and uses the custom 574 | /// `description` property for types that conform to 575 | /// `CustomStringConvertible`: 576 | /// 577 | /// struct Point: CustomStringConvertible { 578 | /// let x: Int, y: Int 579 | /// 580 | /// var description: String { 581 | /// return "(\(x), \(y))" 582 | /// } 583 | /// } 584 | /// 585 | /// let p = Point(x: 21, y: 30) 586 | /// let s = String(describing: p) 587 | /// print(s) 588 | /// // Prints "(21, 30)" 589 | /// 590 | /// The conversion of `p` to a string in the assignment to `s` uses the 591 | /// `Point` type's `description` property. 592 | public var description: String { get } 593 | } 594 | 595 | extension SourceKitObject: ExpressibleByArrayLiteral { 596 | /// Creates an instance initialized with the given elements. 597 | public convenience init(arrayLiteral elements: SourceKittenFramework.SourceKitObject...) 598 | } 599 | 600 | extension SourceKitObject: ExpressibleByDictionaryLiteral { 601 | /// Creates an instance initialized with the given key-value pairs. 602 | public convenience init(dictionaryLiteral elements: (SourceKittenFramework.UID, SourceKittenFramework.SourceKitObjectConvertible)...) 603 | } 604 | 605 | extension SourceKitObject: ExpressibleByIntegerLiteral { 606 | /// Creates an instance initialized to the specified integer value. 607 | /// 608 | /// Do not call this initializer directly. Instead, initialize a variable or 609 | /// constant using an integer literal. For example: 610 | /// 611 | /// let x = 23 612 | /// 613 | /// In this example, the assignment to the `x` constant calls this integer 614 | /// literal initializer behind the scenes. 615 | /// 616 | /// - Parameter value: The value to create. 617 | public convenience init(integerLiteral value: IntegerLiteralType) 618 | } 619 | 620 | extension SourceKitObject: ExpressibleByStringLiteral { 621 | /// Creates an instance initialized to the given string value. 622 | /// 623 | /// - Parameter value: The value of the new instance. 624 | public convenience init(stringLiteral value: StringLiteralType) 625 | } 626 | 627 | public protocol SourceKitObjectConvertible { 628 | var sourceKitObject: SourceKittenFramework.SourceKitObject? { get } 629 | } 630 | 631 | public protocol SourceKitRepresentable { 632 | func isEqualTo(_ rhs: SourceKittenFramework.SourceKitRepresentable) -> Bool 633 | } 634 | 635 | extension SourceKitRepresentable { 636 | public func isEqualTo(_ rhs: SourceKittenFramework.SourceKitRepresentable) -> Bool 637 | } 638 | 639 | public struct SourceLocation { 640 | public let file: String 641 | 642 | public let line: UInt32 643 | 644 | public let column: UInt32 645 | 646 | public let offset: UInt32 647 | 648 | public func range(toEnd end: SourceKittenFramework.SourceLocation) -> NSRange 649 | } 650 | 651 | extension SourceLocation: Comparable {} 652 | 653 | /// Swift declaration kinds. 654 | /// Found in `strings SourceKitService | grep source.lang.swift.stmt.`. 655 | public enum StatementKind: String { 656 | /// `brace`. 657 | case brace 658 | 659 | /// `case`. 660 | case `case` 661 | 662 | /// `for`. 663 | case `for` 664 | 665 | /// `foreach`. 666 | case forEach 667 | 668 | /// `guard`. 669 | case `guard` 670 | 671 | /// `if`. 672 | case `if` 673 | 674 | /// `repeatewhile`. 675 | case repeatWhile 676 | 677 | /// `switch`. 678 | case `switch` 679 | 680 | /// `while`. 681 | case `while` 682 | } 683 | 684 | /// Represents the structural information in a Swift source file. 685 | public struct Structure { 686 | /// Structural information as an [String: SourceKitRepresentable]. 687 | public let dictionary: [String: SourceKittenFramework.SourceKitRepresentable] 688 | 689 | /** 690 | Create a Structure from a SourceKit `editor.open` response. 691 | 692 | - parameter sourceKitResponse: SourceKit `editor.open` response. 693 | */ 694 | public init(sourceKitResponse: [String: SourceKittenFramework.SourceKitRepresentable]) 695 | 696 | /** 697 | Initialize a Structure by passing in a File. 698 | 699 | - parameter file: File to parse for structural information. 700 | - throws: Request.Error 701 | */ 702 | public init(file: SourceKittenFramework.File) throws 703 | } 704 | 705 | extension Structure: CustomStringConvertible { 706 | /// A textual JSON representation of `Structure`. 707 | public var description: String { get } 708 | } 709 | 710 | extension Structure: Equatable {} 711 | 712 | /// Swift declaration attribute kinds. 713 | /// Found in `strings SourceKitService | grep source.decl.attribute.`. 714 | public enum SwiftDeclarationAttributeKind: String, CaseIterable { 715 | case ibaction 716 | 717 | case iboutlet 718 | 719 | case ibdesignable 720 | 721 | case ibinspectable 722 | 723 | case gkinspectable 724 | 725 | case objc 726 | 727 | case objcName 728 | 729 | case silgenName 730 | 731 | case available 732 | 733 | case final 734 | 735 | case required 736 | 737 | case optional 738 | 739 | case noreturn 740 | 741 | case epxorted 742 | 743 | case nsCopying 744 | 745 | case nsManaged 746 | 747 | case lazy 748 | 749 | case lldbDebuggerFunction 750 | 751 | case uiApplicationMain 752 | 753 | case unsafeNoObjcTaggedPointer 754 | 755 | case inline 756 | 757 | case semantics 758 | 759 | case dynamic 760 | 761 | case infix 762 | 763 | case prefix 764 | 765 | case postfix 766 | 767 | case transparent 768 | 769 | case requiresStoredProperyInits 770 | 771 | case nonobjc 772 | 773 | case fixedLayout 774 | 775 | case inlineable 776 | 777 | case specialize 778 | 779 | case objcMembers 780 | 781 | case mutating 782 | 783 | case nonmutating 784 | 785 | case convenience 786 | 787 | case override 788 | 789 | case silStored 790 | 791 | case weak 792 | 793 | case effects 794 | 795 | case objcBriged 796 | 797 | case nsApplicationMain 798 | 799 | case objcNonLazyRealization 800 | 801 | case synthesizedProtocol 802 | 803 | case testable 804 | 805 | case alignment 806 | 807 | case `rethrows` 808 | 809 | case swiftNativeObjcRuntimeBase 810 | 811 | case indirect 812 | 813 | case warnUnqualifiedAccess 814 | 815 | case cdecl 816 | 817 | case versioned 818 | 819 | case discardableResult 820 | 821 | case implements 822 | 823 | case objcRuntimeName 824 | 825 | case staticInitializeObjCMetadata 826 | 827 | case restatedObjCConformance 828 | 829 | case `private` 830 | 831 | case `fileprivate` 832 | 833 | case `internal` 834 | 835 | case `public` 836 | 837 | case open 838 | 839 | case setterPrivate 840 | 841 | case setterFilePrivate 842 | 843 | case setterInternal 844 | 845 | case setterPublic 846 | 847 | case setterOpen 848 | 849 | case optimize 850 | 851 | case consuming 852 | 853 | case implicitlyUnwrappedOptional 854 | 855 | case underscoredObjcNonLazyRealization 856 | 857 | case clangImporterSynthesizedType 858 | 859 | case forbidSerializingReference 860 | 861 | case usableFromInline 862 | 863 | case weakLinked 864 | 865 | case inlinable 866 | 867 | case dynamicMemberLookup 868 | 869 | case frozen 870 | 871 | case autoclosure 872 | 873 | case noescape 874 | 875 | case __raw_doc_comment 876 | 877 | case __setter_access 878 | 879 | case _borrowed 880 | 881 | case _dynamicReplacement 882 | 883 | case _effects 884 | 885 | case _hasInitialValue 886 | 887 | case _hasStorage 888 | 889 | case _nonoverride 890 | 891 | case _private 892 | 893 | case _show_in_interface 894 | 895 | case dynamicCallable 896 | } 897 | 898 | /// Swift declaration kinds. 899 | /// Found in `strings SourceKitService | grep source.lang.swift.decl.`. 900 | public enum SwiftDeclarationKind: String, CaseIterable { 901 | /// `associatedtype`. 902 | case `associatedtype` 903 | 904 | /// `class`. 905 | case `class` 906 | 907 | /// `enum`. 908 | case `enum` 909 | 910 | /// `enumcase`. 911 | case enumcase 912 | 913 | /// `enumelement`. 914 | case enumelement 915 | 916 | /// `extension`. 917 | case `extension` 918 | 919 | /// `extension.class`. 920 | case extensionClass 921 | 922 | /// `extension.enum`. 923 | case extensionEnum 924 | 925 | /// `extension.protocol`. 926 | case extensionProtocol 927 | 928 | /// `extension.struct`. 929 | case extensionStruct 930 | 931 | /// `function.accessor.address`. 932 | case functionAccessorAddress 933 | 934 | /// `function.accessor.didset`. 935 | case functionAccessorDidset 936 | 937 | /// `function.accessor.getter`. 938 | case functionAccessorGetter 939 | 940 | case functionAccessorModify 941 | 942 | /// `function.accessor.mutableaddress`. 943 | case functionAccessorMutableaddress 944 | 945 | case functionAccessorRead 946 | 947 | /// `function.accessor.setter`. 948 | case functionAccessorSetter 949 | 950 | /// `function.accessor.willset`. 951 | case functionAccessorWillset 952 | 953 | /// `function.constructor`. 954 | case functionConstructor 955 | 956 | /// `function.destructor`. 957 | case functionDestructor 958 | 959 | /// `function.free`. 960 | case functionFree 961 | 962 | /// `function.method.class`. 963 | case functionMethodClass 964 | 965 | /// `function.method.instance`. 966 | case functionMethodInstance 967 | 968 | /// `function.method.static`. 969 | case functionMethodStatic 970 | 971 | case functionOperator 972 | 973 | /// `function.operator.infix`. 974 | case functionOperatorInfix 975 | 976 | /// `function.operator.postfix`. 977 | case functionOperatorPostfix 978 | 979 | /// `function.operator.prefix`. 980 | case functionOperatorPrefix 981 | 982 | /// `function.subscript`. 983 | case functionSubscript 984 | 985 | /// `generic_type_param`. 986 | case genericTypeParam 987 | 988 | /// `module`. 989 | case module 990 | 991 | /// `precedencegroup`. 992 | case precedenceGroup 993 | 994 | /// `protocol`. 995 | case `protocol` 996 | 997 | /// `struct`. 998 | case `struct` 999 | 1000 | /// `typealias`. 1001 | case `typealias` 1002 | 1003 | /// `var.class`. 1004 | case varClass 1005 | 1006 | /// `var.global`. 1007 | case varGlobal 1008 | 1009 | /// `var.instance`. 1010 | case varInstance 1011 | 1012 | /// `var.local`. 1013 | case varLocal 1014 | 1015 | /// `var.parameter`. 1016 | case varParameter 1017 | 1018 | /// `var.static`. 1019 | case varStatic 1020 | } 1021 | 1022 | /// SourceKit response dictionary keys. 1023 | public enum SwiftDocKey: String { 1024 | /// Annotated declaration (String). 1025 | case annotatedDeclaration 1026 | 1027 | /// Body length (Int64). 1028 | case bodyLength 1029 | 1030 | /// Body offset (Int64). 1031 | case bodyOffset 1032 | 1033 | /// Diagnostic stage (String). 1034 | case diagnosticStage 1035 | 1036 | /// Elements ([[String: SourceKitRepresentable]]). 1037 | case elements 1038 | 1039 | /// File path (String). 1040 | case filePath 1041 | 1042 | /// Full XML docs (String). 1043 | case fullXMLDocs 1044 | 1045 | /// Kind (String). 1046 | case kind 1047 | 1048 | /// Length (Int64). 1049 | case length 1050 | 1051 | /// Name (String). 1052 | case name 1053 | 1054 | /// Name length (Int64). 1055 | case nameLength 1056 | 1057 | /// Name offset (Int64). 1058 | case nameOffset 1059 | 1060 | /// Offset (Int64). 1061 | case offset 1062 | 1063 | /// Substructure ([[String: SourceKitRepresentable]]). 1064 | case substructure 1065 | 1066 | /// Syntax map (NSData). 1067 | case syntaxMap 1068 | 1069 | /// Type name (String). 1070 | case typeName 1071 | 1072 | /// Inheritedtype ([SourceKitRepresentable]) 1073 | case inheritedtypes 1074 | 1075 | /// Column where the token's declaration begins (Int64). 1076 | case docColumn 1077 | 1078 | /// Documentation comment (String). 1079 | case documentationComment 1080 | 1081 | /// Declaration of documented token (String). 1082 | case docDeclaration 1083 | 1084 | /// Discussion documentation of documented token ([SourceKitRepresentable]). 1085 | case docDiscussion 1086 | 1087 | /// File where the documented token is located (String). 1088 | case docFile 1089 | 1090 | /// Line where the token's declaration begins (Int64). 1091 | case docLine 1092 | 1093 | /// Name of documented token (String). 1094 | case docName 1095 | 1096 | /// Parameters of documented token ([SourceKitRepresentable]). 1097 | case docParameters 1098 | 1099 | /// Parsed declaration (String). 1100 | case docResultDiscussion 1101 | 1102 | /// Parsed scope start (Int64). 1103 | case docType 1104 | 1105 | /// Parsed scope start end (Int64). 1106 | case usr 1107 | 1108 | /// Result discussion documentation of documented token ([SourceKitRepresentable]). 1109 | case parsedDeclaration 1110 | 1111 | /// Type of documented token (String). 1112 | case parsedScopeEnd 1113 | 1114 | /// USR of documented token (String). 1115 | case parsedScopeStart 1116 | 1117 | /// Swift Declaration (String). 1118 | case swiftDeclaration 1119 | 1120 | /// Swift Name (String). 1121 | case swiftName 1122 | 1123 | /// Always deprecated (Bool). 1124 | case alwaysDeprecated 1125 | 1126 | /// Always unavailable (Bool). 1127 | case alwaysUnavailable 1128 | 1129 | /// Always deprecated (String). 1130 | case deprecationMessage 1131 | 1132 | /// Always unavailable (String). 1133 | case unavailableMessage 1134 | 1135 | /// Annotations ([String]). 1136 | case annotations 1137 | } 1138 | 1139 | /// Represents docs for a Swift file. 1140 | public struct SwiftDocs { 1141 | /// Documented File. 1142 | public let file: SourceKittenFramework.File 1143 | 1144 | /// Docs information as an [String: SourceKitRepresentable]. 1145 | public let docsDictionary: [String: SourceKittenFramework.SourceKitRepresentable] 1146 | 1147 | /** 1148 | Create docs for the specified Swift file and compiler arguments. 1149 | 1150 | - parameter file: Swift file to document. 1151 | - parameter arguments: compiler arguments to pass to SourceKit. 1152 | */ 1153 | public init?(file: SourceKittenFramework.File, arguments: [String]) 1154 | 1155 | /** 1156 | Create docs for the specified Swift file, editor.open SourceKit response and cursor info request. 1157 | 1158 | - parameter file: Swift file to document. 1159 | - parameter dictionary: editor.open response from SourceKit. 1160 | - parameter cursorInfoRequest: SourceKit dictionary to use to send cursorinfo request. 1161 | */ 1162 | public init(file: SourceKittenFramework.File, dictionary: [String: SourceKittenFramework.SourceKitRepresentable], cursorInfoRequest: SourceKittenFramework.SourceKitObject?) 1163 | } 1164 | 1165 | extension SwiftDocs: CustomStringConvertible { 1166 | /// A textual JSON representation of `SwiftDocs`. 1167 | public var description: String { get } 1168 | } 1169 | 1170 | /// Syntax kind values. 1171 | /// Found in `strings SourceKitService | grep source.lang.swift.syntaxtype.`. 1172 | public enum SyntaxKind: String { 1173 | /// `argument`. 1174 | case argument 1175 | 1176 | /// `attribute.builtin`. 1177 | case attributeBuiltin 1178 | 1179 | /// `attribute.id`. 1180 | case attributeID 1181 | 1182 | /// `buildconfig.id`. 1183 | case buildconfigID 1184 | 1185 | /// `buildconfig.keyword`. 1186 | case buildconfigKeyword 1187 | 1188 | /// `comment`. 1189 | case comment 1190 | 1191 | /// `comment.mark`. 1192 | case commentMark 1193 | 1194 | /// `comment.url`. 1195 | case commentURL 1196 | 1197 | /// `doccomment`. 1198 | case docComment 1199 | 1200 | /// `doccomment.field`. 1201 | case docCommentField 1202 | 1203 | /// `identifier`. 1204 | case identifier 1205 | 1206 | /// `keyword`. 1207 | case keyword 1208 | 1209 | /// `number`. 1210 | case number 1211 | 1212 | /// `objectliteral` 1213 | case objectLiteral 1214 | 1215 | /// `parameter`. 1216 | case parameter 1217 | 1218 | /// `placeholder`. 1219 | case placeholder 1220 | 1221 | /// `string`. 1222 | case string 1223 | 1224 | /// `string_interpolation_anchor`. 1225 | case stringInterpolationAnchor 1226 | 1227 | /// `typeidentifier`. 1228 | case typeidentifier 1229 | 1230 | /// `pounddirective.keyword`. 1231 | case poundDirectiveKeyword 1232 | } 1233 | 1234 | /// Represents a Swift file's syntax information. 1235 | public struct SyntaxMap { 1236 | /// Array of SyntaxToken's. 1237 | public let tokens: [SourceKittenFramework.SyntaxToken] 1238 | 1239 | /** 1240 | Create a SyntaxMap by passing in tokens directly. 1241 | 1242 | - parameter tokens: Array of SyntaxToken's. 1243 | */ 1244 | public init(tokens: [SourceKittenFramework.SyntaxToken]) 1245 | 1246 | /** 1247 | Create a SyntaxMap by passing in NSData from a SourceKit `editor.open` response to be parsed. 1248 | 1249 | - parameter data: NSData from a SourceKit `editor.open` response 1250 | */ 1251 | public init(data: [SourceKittenFramework.SourceKitRepresentable]) 1252 | 1253 | /** 1254 | Create a SyntaxMap from a SourceKit `editor.open` response. 1255 | 1256 | - parameter sourceKitResponse: SourceKit `editor.open` response. 1257 | */ 1258 | public init(sourceKitResponse: [String: SourceKittenFramework.SourceKitRepresentable]) 1259 | 1260 | /** 1261 | Create a SyntaxMap from a File to be parsed. 1262 | 1263 | - parameter file: File to be parsed. 1264 | - throws: Request.Error 1265 | */ 1266 | public init(file: SourceKittenFramework.File) throws 1267 | } 1268 | 1269 | extension SyntaxMap: CustomStringConvertible { 1270 | /// A textual JSON representation of `SyntaxMap`. 1271 | public var description: String { get } 1272 | } 1273 | 1274 | extension SyntaxMap: Equatable {} 1275 | 1276 | /// Represents a single Swift syntax token. 1277 | public struct SyntaxToken { 1278 | /// Token type. See SyntaxKind. 1279 | public let type: String 1280 | 1281 | /// Token offset. 1282 | public let offset: Int 1283 | 1284 | /// Token length. 1285 | public let length: Int 1286 | 1287 | /// Dictionary representation of SyntaxToken. Useful for NSJSONSerialization. 1288 | public var dictionaryValue: [String: Any] { get } 1289 | 1290 | /** 1291 | Create a SyntaxToken by directly passing in its property values. 1292 | 1293 | - parameter type: Token type. See SyntaxKind. 1294 | - parameter offset: Token offset. 1295 | - parameter length: Token length. 1296 | */ 1297 | public init(type: String, offset: Int, length: Int) 1298 | } 1299 | 1300 | extension SyntaxToken: Equatable {} 1301 | 1302 | extension SyntaxToken: CustomStringConvertible { 1303 | /// A textual JSON representation of `SyntaxToken`. 1304 | public var description: String { get } 1305 | } 1306 | 1307 | public enum Text { 1308 | case para(String, String?) 1309 | 1310 | case verbatim(String) 1311 | } 1312 | 1313 | /// Swift representation of sourcekitd_uid_t 1314 | public struct UID: Hashable { 1315 | public init(_ string: String) 1316 | 1317 | public init(_ rawRepresentable: T) where T: RawRepresentable, T.RawValue == String 1318 | } 1319 | 1320 | extension UID: SourceKittenFramework.SourceKitObjectConvertible { 1321 | public var sourceKitObject: SourceKittenFramework.SourceKitObject? { get } 1322 | } 1323 | 1324 | extension UID: CustomStringConvertible { 1325 | /// A textual representation of this instance. 1326 | /// 1327 | /// Calling this property directly is discouraged. Instead, convert an 1328 | /// instance of any type to a string by using the `String(describing:)` 1329 | /// initializer. This initializer works with any type, and uses the custom 1330 | /// `description` property for types that conform to 1331 | /// `CustomStringConvertible`: 1332 | /// 1333 | /// struct Point: CustomStringConvertible { 1334 | /// let x: Int, y: Int 1335 | /// 1336 | /// var description: String { 1337 | /// return "(\(x), \(y))" 1338 | /// } 1339 | /// } 1340 | /// 1341 | /// let p = Point(x: 21, y: 30) 1342 | /// let s = String(describing: p) 1343 | /// print(s) 1344 | /// // Prints "(21, 30)" 1345 | /// 1346 | /// The conversion of `p` to a string in the assignment to `s` uses the 1347 | /// `Point` type's `description` property. 1348 | public var description: String { get } 1349 | } 1350 | 1351 | extension UID: ExpressibleByStringLiteral { 1352 | /// Creates an instance initialized to the given string value. 1353 | /// 1354 | /// - Parameter value: The value of the new instance. 1355 | public init(stringLiteral value: String) 1356 | } 1357 | 1358 | extension UID: SourceKittenFramework.UIDRepresentable { 1359 | public var uid: SourceKittenFramework.UID { get } 1360 | } 1361 | 1362 | public protocol UIDRepresentable { 1363 | var uid: SourceKittenFramework.UID { get } 1364 | } 1365 | 1366 | public struct Version { 1367 | public let value: String 1368 | 1369 | public static let current: SourceKittenFramework.Version 1370 | } 1371 | 1372 | public func declarationsToJSON(_ decl: [String: [SourceKittenFramework.SourceDeclaration]]) -> String 1373 | 1374 | public func insertMarks(declarations: [SourceKittenFramework.SourceDeclaration], limit: NSRange? = nil) -> [SourceKittenFramework.SourceDeclaration] 1375 | 1376 | /** 1377 | Parse XML from `key.doc.full_as_xml` from `cursor.info` request. 1378 | 1379 | - parameter xmlDocs: Contents of `key.doc.full_as_xml` from SourceKit. 1380 | 1381 | - returns: XML parsed as an `[String: SourceKitRepresentable]`. 1382 | */ 1383 | public func parseFullXMLDocs(_ xmlDocs: String) -> [String: SourceKittenFramework.SourceKitRepresentable]? 1384 | 1385 | /** 1386 | Extracts Objective-C header files and `xcodebuild` arguments from an array of header files followed by `xcodebuild` arguments. 1387 | 1388 | - parameter sourcekittenArguments: Array of Objective-C header files followed by `xcodebuild` arguments. 1389 | 1390 | - returns: Tuple of header files and xcodebuild arguments. 1391 | */ 1392 | public func parseHeaderFilesAndXcodebuildArguments(sourcekittenArguments: [String]) -> (headerFiles: [String], xcodebuildArguments: [String]) 1393 | 1394 | public func sdkPath() -> String 1395 | 1396 | /** 1397 | JSON Object to JSON String. 1398 | 1399 | - parameter object: Object to convert to JSON. 1400 | 1401 | - returns: JSON string representation of the input object. 1402 | */ 1403 | public func toJSON(_ object: Any) -> String 1404 | 1405 | /** 1406 | Convert [String: SourceKitRepresentable] to `NSDictionary`. 1407 | 1408 | - parameter dictionary: [String: SourceKitRepresentable] to convert. 1409 | 1410 | - returns: JSON-serializable value. 1411 | */ 1412 | public func toNSDictionary(_ dictionary: [String: SourceKittenFramework.SourceKitRepresentable]) -> NSDictionary 1413 | 1414 | extension CXString: CustomStringConvertible { 1415 | /// A textual representation of this instance. 1416 | /// 1417 | /// Calling this property directly is discouraged. Instead, convert an 1418 | /// instance of any type to a string by using the `String(describing:)` 1419 | /// initializer. This initializer works with any type, and uses the custom 1420 | /// `description` property for types that conform to 1421 | /// `CustomStringConvertible`: 1422 | /// 1423 | /// struct Point: CustomStringConvertible { 1424 | /// let x: Int, y: Int 1425 | /// 1426 | /// var description: String { 1427 | /// return "(\(x), \(y))" 1428 | /// } 1429 | /// } 1430 | /// 1431 | /// let p = Point(x: 21, y: 30) 1432 | /// let s = String(describing: p) 1433 | /// print(s) 1434 | /// // Prints "(21, 30)" 1435 | /// 1436 | /// The conversion of `p` to a string in the assignment to `s` uses the 1437 | /// `Point` type's `description` property. 1438 | public var description: String { get } 1439 | } 1440 | 1441 | extension Dictionary where Key == String, Value == SourceKittenFramework.SourceKitRepresentable { 1442 | public var referencedUSRs: [String] { get } 1443 | } 1444 | 1445 | extension Array { 1446 | public func bridge() -> NSArray 1447 | } 1448 | 1449 | extension CharacterSet { 1450 | public func bridge() -> NSCharacterSet 1451 | } 1452 | 1453 | extension Dictionary { 1454 | public func bridge() -> NSDictionary 1455 | } 1456 | 1457 | extension NSString { 1458 | public func bridge() -> String 1459 | } 1460 | 1461 | extension String { 1462 | public func bridge() -> NSString 1463 | } 1464 | 1465 | extension Array: SourceKittenFramework.SourceKitRepresentable {} 1466 | 1467 | extension Dictionary: SourceKittenFramework.SourceKitRepresentable {} 1468 | 1469 | extension String: SourceKittenFramework.SourceKitRepresentable {} 1470 | 1471 | extension Int64: SourceKittenFramework.SourceKitRepresentable {} 1472 | 1473 | extension Bool: SourceKittenFramework.SourceKitRepresentable {} 1474 | 1475 | extension Data: SourceKittenFramework.SourceKitRepresentable {} 1476 | 1477 | extension Array: SourceKittenFramework.SourceKitObjectConvertible where Element: SourceKittenFramework.SourceKitObjectConvertible { 1478 | public var sourceKitObject: SourceKittenFramework.SourceKitObject? { get } 1479 | } 1480 | 1481 | extension Dictionary: SourceKittenFramework.SourceKitObjectConvertible where Key: SourceKittenFramework.UIDRepresentable, Value: SourceKittenFramework.SourceKitObjectConvertible { 1482 | public var sourceKitObject: SourceKittenFramework.SourceKitObject? { get } 1483 | } 1484 | 1485 | extension Int: SourceKittenFramework.SourceKitObjectConvertible { 1486 | public var sourceKitObject: SourceKittenFramework.SourceKitObject? { get } 1487 | } 1488 | 1489 | extension Int64: SourceKittenFramework.SourceKitObjectConvertible { 1490 | public var sourceKitObject: SourceKittenFramework.SourceKitObject? { get } 1491 | } 1492 | 1493 | extension String: SourceKittenFramework.SourceKitObjectConvertible { 1494 | public var sourceKitObject: SourceKittenFramework.SourceKitObject? { get } 1495 | } 1496 | 1497 | extension NSString { 1498 | /** 1499 | Returns line number and character for utf16 based offset. 1500 | 1501 | - parameter offset: utf16 based index. 1502 | - parameter tabWidth: the width in spaces to expand tabs to. 1503 | */ 1504 | public func lineAndCharacter(forCharacterOffset offset: Int, expandingTabsToWidth tabWidth: Int = 1) -> (line: Int, character: Int)? 1505 | 1506 | /** 1507 | Returns line number and character for byte offset. 1508 | 1509 | - parameter offset: byte offset. 1510 | - parameter tabWidth: the width in spaces to expand tabs to. 1511 | */ 1512 | public func lineAndCharacter(forByteOffset offset: Int, expandingTabsToWidth tabWidth: Int = 1) -> (line: Int, character: Int)? 1513 | 1514 | /** 1515 | Returns a copy of `self` with the trailing contiguous characters belonging to `characterSet` 1516 | removed. 1517 | 1518 | - parameter characterSet: Character set to check for membership. 1519 | */ 1520 | public func trimmingTrailingCharacters(in characterSet: CharacterSet) -> String 1521 | 1522 | /** 1523 | Returns self represented as an absolute path. 1524 | 1525 | - parameter rootDirectory: Absolute parent path if not already an absolute path. 1526 | */ 1527 | public func absolutePathRepresentation(rootDirectory: String = FileManager.default.currentDirectoryPath) -> String 1528 | 1529 | /** 1530 | Converts a range of byte offsets in `self` to an `NSRange` suitable for filtering `self` as an 1531 | `NSString`. 1532 | 1533 | - parameter start: Starting byte offset. 1534 | - parameter length: Length of bytes to include in range. 1535 | 1536 | - returns: An equivalent `NSRange`. 1537 | */ 1538 | public func byteRangeToNSRange(start: Int, length: Int) -> NSRange? 1539 | 1540 | /** 1541 | Converts an `NSRange` suitable for filtering `self` as an 1542 | `NSString` to a range of byte offsets in `self`. 1543 | 1544 | - parameter start: Starting character index in the string. 1545 | - parameter length: Number of characters to include in range. 1546 | 1547 | - returns: An equivalent `NSRange`. 1548 | */ 1549 | public func NSRangeToByteRange(start: Int, length: Int) -> NSRange? 1550 | 1551 | /** 1552 | Returns a substring with the provided byte range. 1553 | 1554 | - parameter start: Starting byte offset. 1555 | - parameter length: Length of bytes to include in range. 1556 | */ 1557 | public func substringWithByteRange(start: Int, length: Int) -> String? 1558 | 1559 | /** 1560 | Returns a substring starting at the beginning of `start`'s line and ending at the end of `end`'s 1561 | line. Returns `start`'s entire line if `end` is nil. 1562 | 1563 | - parameter start: Starting byte offset. 1564 | - parameter length: Length of bytes to include in range. 1565 | */ 1566 | public func substringLinesWithByteRange(start: Int, length: Int) -> String? 1567 | 1568 | public func substringStartingLinesWithByteRange(start: Int, length: Int) -> String? 1569 | 1570 | /** 1571 | Returns line numbers containing starting and ending byte offsets. 1572 | 1573 | - parameter start: Starting byte offset. 1574 | - parameter length: Length of bytes to include in range. 1575 | */ 1576 | public func lineRangeWithByteRange(start: Int, length: Int) -> (start: Int, end: Int)? 1577 | 1578 | /** 1579 | Returns an array of Lines for each line in the file. 1580 | */ 1581 | public func lines() -> [SourceKittenFramework.Line] 1582 | 1583 | /** 1584 | Returns true if self is an Objective-C header file. 1585 | */ 1586 | public func isObjectiveCHeaderFile() -> Bool 1587 | 1588 | /** 1589 | Returns true if self is a Swift file. 1590 | */ 1591 | public func isSwiftFile() -> Bool 1592 | 1593 | /** 1594 | Returns a substring from a start and end SourceLocation. 1595 | */ 1596 | public func substringWithSourceRange(start: SourceKittenFramework.SourceLocation, end: SourceKittenFramework.SourceLocation) -> String? 1597 | } 1598 | 1599 | extension String { 1600 | /// Returns the `#pragma mark`s in the string. 1601 | /// Just the content; no leading dashes or leading `#pragma mark`. 1602 | public func pragmaMarks(filename: String, excludeRanges: [NSRange], limit: NSRange?) -> [SourceKittenFramework.SourceDeclaration] 1603 | 1604 | /** 1605 | Returns whether or not the `token` can be documented. Either because it is a 1606 | `SyntaxKind.Identifier` or because it is a function treated as a `SyntaxKind.Keyword`: 1607 | 1608 | - `subscript` 1609 | - `init` 1610 | - `deinit` 1611 | 1612 | - parameter token: Token to process. 1613 | */ 1614 | public func isTokenDocumentable(token: SourceKittenFramework.SyntaxToken) -> Bool 1615 | 1616 | /** 1617 | Find integer offsets of documented Swift tokens in self. 1618 | 1619 | - parameter syntaxMap: Syntax Map returned from SourceKit editor.open request. 1620 | 1621 | - returns: Array of documented token offsets. 1622 | */ 1623 | public func documentedTokenOffsets(syntaxMap: SourceKittenFramework.SyntaxMap) -> [Int] 1624 | 1625 | /** 1626 | Returns the body of the comment if the string is a comment. 1627 | 1628 | - parameter range: Range to restrict the search for a comment body. 1629 | */ 1630 | public func commentBody(range: NSRange? = nil) -> String? 1631 | 1632 | /// Returns a copy of `self` with the leading whitespace common in each line removed. 1633 | public func removingCommonLeadingWhitespaceFromLines() -> String 1634 | 1635 | /** 1636 | Returns the number of contiguous characters at the start of `self` belonging to `characterSet`. 1637 | 1638 | - parameter characterSet: Character set to check for membership. 1639 | */ 1640 | public func countOfLeadingCharacters(in characterSet: CharacterSet) -> Int 1641 | 1642 | /// A version of the string with backslash escapes removed. 1643 | public var unescaped: String { get } 1644 | } 1645 | 1646 | extension String: SourceKittenFramework.UIDRepresentable { 1647 | public var uid: SourceKittenFramework.UID { get } 1648 | } 1649 | -------------------------------------------------------------------------------- /Examples/SwiftFormat.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftOnoneSupport 3 | 4 | /// Argument type for stripping 5 | public enum ArgumentStrippingMode: String { 6 | case unnamedOnly 7 | 8 | case closureOnly 9 | 10 | case all 11 | } 12 | 13 | /// Public interface for the SwiftFormat command-line functions 14 | public struct CLI { 15 | /// Output type for printed content 16 | public enum OutputType { 17 | case info 18 | 19 | case success 20 | 21 | case error 22 | 23 | case warning 24 | 25 | case content 26 | 27 | case raw 28 | } 29 | 30 | /// Output handler - override this to intercept output from the CLI 31 | public static var print: (String, SwiftFormat.CLI.OutputType) -> Void 32 | 33 | /// Input handler - override this to inject input into the CLI 34 | /// Injected lines should include the terminating newline character 35 | public static var readLine: () -> String? 36 | 37 | /// Run the CLI with the specified input arguments 38 | public static func run(in directory: String, with args: [String] = CommandLine.arguments) -> SwiftFormat.ExitCode 39 | 40 | /// Run the CLI with the specified input string (this will be parsed into multiple arguments) 41 | public static func run(in directory: String, with argumentString: String) -> SwiftFormat.ExitCode 42 | } 43 | 44 | public enum ExitCode: Int32 { 45 | case ok 46 | 47 | case lintFailure 48 | 49 | case error 50 | } 51 | 52 | /// Callback for enumerateFiles() function 53 | public typealias FileEnumerationHandler = (URL, URL, SwiftFormat.Options) throws -> () throws -> Void 54 | 55 | /// File info, used for constructing header comments 56 | public struct FileInfo: Equatable { 57 | public init(fileName: String? = nil, creationDate: Date? = nil) 58 | 59 | /// Returns a Boolean value indicating whether two values are equal. 60 | /// 61 | /// Equality is the inverse of inequality. For any values `a` and `b`, 62 | /// `a == b` implies that `a != b` is `false`. 63 | /// 64 | /// - Parameters: 65 | /// - lhs: A value to compare. 66 | /// - rhs: Another value to compare. 67 | public static func == (lhs: SwiftFormat.FileInfo, rhs: SwiftFormat.FileInfo) -> Bool 68 | } 69 | 70 | /// File enumeration options 71 | public struct FileOptions { 72 | public var followSymlinks: Bool 73 | 74 | public var supportedFileExtensions: [String] 75 | 76 | public var excludedGlobs: [SwiftFormat.Glob] 77 | 78 | public var unexcludedGlobs: [SwiftFormat.Glob] 79 | 80 | @available(*, deprecated, message: "Use excludedGlobs property instead") 81 | public var excludedURLs: [URL] { get } 82 | 83 | public static let `default`: SwiftFormat.FileOptions 84 | 85 | @available(*, deprecated, message: "Use other init() method instead") 86 | public init(followSymlinks: Bool = false, supportedFileExtensions: [String] = ["swift"], excludedURLs: [URL]) 87 | 88 | public init(followSymlinks: Bool = false, supportedFileExtensions: [String] = ["swift"], excludedGlobs: [SwiftFormat.Glob] = [], unexcludedGlobs: [SwiftFormat.Glob] = []) 89 | } 90 | 91 | /// An enumeration of the types of error that may be thrown by SwiftFormat 92 | public enum FormatError: Error, CustomStringConvertible, LocalizedError, CustomNSError { 93 | case reading(String) 94 | 95 | case writing(String) 96 | 97 | case parsing(String) 98 | 99 | case options(String) 100 | 101 | /// A textual representation of this instance. 102 | /// 103 | /// Calling this property directly is discouraged. Instead, convert an 104 | /// instance of any type to a string by using the `String(describing:)` 105 | /// initializer. This initializer works with any type, and uses the custom 106 | /// `description` property for types that conform to 107 | /// `CustomStringConvertible`: 108 | /// 109 | /// struct Point: CustomStringConvertible { 110 | /// let x: Int, y: Int 111 | /// 112 | /// var description: String { 113 | /// return "(\(x), \(y))" 114 | /// } 115 | /// } 116 | /// 117 | /// let p = Point(x: 21, y: 30) 118 | /// let s = String(describing: p) 119 | /// print(s) 120 | /// // Prints "(21, 30)" 121 | /// 122 | /// The conversion of `p` to a string in the assignment to `s` uses the 123 | /// `Point` type's `description` property. 124 | public var description: String { get } 125 | 126 | public var localizedDescription: String { get } 127 | 128 | /// The user-info dictionary. 129 | public var errorUserInfo: [String: Any] { get } 130 | } 131 | 132 | /// Configuration options for formatting. These aren't actually used by the 133 | /// Formatter class itself, but it makes them available to the format rules. 134 | public struct FormatOptions: CustomStringConvertible { 135 | public var indent: String 136 | 137 | public var linebreak: String 138 | 139 | public var allowInlineSemicolons: Bool 140 | 141 | public var spaceAroundRangeOperators: Bool 142 | 143 | public var spaceAroundOperatorDeclarations: Bool 144 | 145 | public var useVoid: Bool 146 | 147 | public var indentCase: Bool 148 | 149 | public var trailingCommas: Bool 150 | 151 | public var indentComments: Bool 152 | 153 | public var truncateBlankLines: Bool 154 | 155 | public var insertBlankLines: Bool 156 | 157 | public var removeBlankLines: Bool 158 | 159 | public var allmanBraces: Bool 160 | 161 | public var fileHeader: SwiftFormat.HeaderStrippingMode 162 | 163 | public var ifdefIndent: SwiftFormat.IndentMode 164 | 165 | public var wrapArguments: SwiftFormat.WrapMode 166 | 167 | public var wrapCollections: SwiftFormat.WrapMode 168 | 169 | public var closingParenOnSameLine: Bool 170 | 171 | public var uppercaseHex: Bool 172 | 173 | public var uppercaseExponent: Bool 174 | 175 | public var decimalGrouping: SwiftFormat.Grouping 176 | 177 | public var binaryGrouping: SwiftFormat.Grouping 178 | 179 | public var octalGrouping: SwiftFormat.Grouping 180 | 181 | public var hexGrouping: SwiftFormat.Grouping 182 | 183 | public var fractionGrouping: Bool 184 | 185 | public var exponentGrouping: Bool 186 | 187 | public var hoistPatternLet: Bool 188 | 189 | public var stripUnusedArguments: SwiftFormat.ArgumentStrippingMode 190 | 191 | public var elseOnNextLine: Bool 192 | 193 | public var explicitSelf: SwiftFormat.SelfMode 194 | 195 | public var selfRequired: [String] 196 | 197 | public var experimentalRules: Bool 198 | 199 | public var importGrouping: SwiftFormat.ImportGrouping 200 | 201 | public var trailingClosures: [String] 202 | 203 | public var xcodeIndentation: Bool 204 | 205 | public var fragment: Bool 206 | 207 | public var ignoreConflictMarkers: Bool 208 | 209 | public var swiftVersion: SwiftFormat.Version 210 | 211 | public var fileInfo: SwiftFormat.FileInfo 212 | 213 | public static let `default`: SwiftFormat.FormatOptions 214 | 215 | public init(indent: String = " ", linebreak: String = "\n", allowInlineSemicolons: Bool = true, spaceAroundRangeOperators: Bool = true, spaceAroundOperatorDeclarations: Bool = true, useVoid: Bool = true, indentCase: Bool = false, trailingCommas: Bool = true, indentComments: Bool = true, truncateBlankLines: Bool = true, insertBlankLines: Bool = true, removeBlankLines: Bool = true, allmanBraces: Bool = false, fileHeader: SwiftFormat.HeaderStrippingMode = .ignore, ifdefIndent: SwiftFormat.IndentMode = .indent, wrapArguments: SwiftFormat.WrapMode = .preserve, wrapCollections: SwiftFormat.WrapMode = .preserve, closingParenOnSameLine: Bool = false, uppercaseHex: Bool = true, uppercaseExponent: Bool = false, decimalGrouping: SwiftFormat.Grouping = .group(3, 6), binaryGrouping: SwiftFormat.Grouping = .group(4, 8), octalGrouping: SwiftFormat.Grouping = .group(4, 8), hexGrouping: SwiftFormat.Grouping = .group(4, 8), fractionGrouping: Bool = false, exponentGrouping: Bool = false, hoistPatternLet: Bool = true, stripUnusedArguments: SwiftFormat.ArgumentStrippingMode = .all, elseOnNextLine: Bool = false, explicitSelf: SwiftFormat.SelfMode = .remove, selfRequired: [String] = [], experimentalRules: Bool = false, importGrouping: SwiftFormat.ImportGrouping = .alphabetized, trailingClosures: [String] = [], xcodeIndentation: Bool = false, fragment: Bool = false, ignoreConflictMarkers: Bool = false, swiftVersion: SwiftFormat.Version = .undefined, fileInfo: SwiftFormat.FileInfo = FileInfo()) 216 | 217 | public var allOptions: [String: Any] { get } 218 | 219 | /// A textual representation of this instance. 220 | /// 221 | /// Calling this property directly is discouraged. Instead, convert an 222 | /// instance of any type to a string by using the `String(describing:)` 223 | /// initializer. This initializer works with any type, and uses the custom 224 | /// `description` property for types that conform to 225 | /// `CustomStringConvertible`: 226 | /// 227 | /// struct Point: CustomStringConvertible { 228 | /// let x: Int, y: Int 229 | /// 230 | /// var description: String { 231 | /// return "(\(x), \(y))" 232 | /// } 233 | /// } 234 | /// 235 | /// let p = Point(x: 21, y: 30) 236 | /// let s = String(describing: p) 237 | /// print(s) 238 | /// // Prints "(21, 30)" 239 | /// 240 | /// The conversion of `p` to a string in the assignment to `s` uses the 241 | /// `Point` type's `description` property. 242 | public var description: String { get } 243 | } 244 | 245 | public final class FormatRule { 246 | public func apply(with formatter: SwiftFormat.Formatter) 247 | } 248 | 249 | public let FormatRules: SwiftFormat._FormatRules 250 | 251 | /// This is a utility class used for manipulating a tokenized source file. 252 | /// It doesn't actually contain any logic for formatting, but provides 253 | /// utility methods for enumerating and adding/removing/replacing tokens. 254 | /// The primary advantage it provides over operating on the token array 255 | /// directly is that it allows mutation during enumeration, and 256 | /// transparently handles changes that affect the current token index. 257 | public class Formatter: NSObject { 258 | /// The options that the formatter was initialized with 259 | public let options: SwiftFormat.FormatOptions 260 | 261 | /// The token array managed by the formatter (read-only) 262 | public private(set) var tokens: [SwiftFormat.Token] { 263 | get 264 | } 265 | 266 | /// Create a new formatter instance from a token array 267 | public init(_ tokens: [SwiftFormat.Token], options: SwiftFormat.FormatOptions = FormatOptions()) 268 | 269 | /// Returns the token at the specified index, or nil if index is invalid 270 | public func token(at index: Int) -> SwiftFormat.Token? 271 | 272 | /// Replaces the token at the specified index with one or more new tokens 273 | public func replaceToken(at index: Int, with tokens: SwiftFormat.Token...) 274 | 275 | /// Replaces the tokens in the specified range with new tokens 276 | public func replaceTokens(inRange range: Range, with tokens: [SwiftFormat.Token]) 277 | 278 | /// Replaces the tokens in the specified closed range with new tokens 279 | public func replaceTokens(inRange range: ClosedRange, with tokens: [SwiftFormat.Token]) 280 | 281 | /// Removes the token at the specified index 282 | public func removeToken(at index: Int) 283 | 284 | /// Removes the tokens in the specified range 285 | public func removeTokens(inRange range: Range) 286 | 287 | /// Removes the tokens in the specified closed range 288 | public func removeTokens(inRange range: ClosedRange) 289 | 290 | /// Removes the last token 291 | public func removeLastToken() 292 | 293 | /// Inserts an array of tokens at the specified index 294 | public func insertTokens(_ tokens: [SwiftFormat.Token], at index: Int) 295 | 296 | /// Inserts a single token at the specified index 297 | public func insertToken(_ token: SwiftFormat.Token, at index: Int) 298 | 299 | /// Loops through each token in the array. It is safe to mutate the token 300 | /// array inside the body block, but note that the index and token arguments 301 | /// may not reflect the current token any more after a mutation 302 | public func forEachToken(_ body: (Int, SwiftFormat.Token) -> Void) 303 | 304 | /// As above, but only loops through tokens that match the specified filter block 305 | public func forEachToken(where matching: (SwiftFormat.Token) -> Bool, _ body: (Int, SwiftFormat.Token) -> Void) 306 | 307 | /// As above, but only loops through tokens with the specified type and string 308 | public func forEach(_ token: SwiftFormat.Token, _ body: (Int, SwiftFormat.Token) -> Void) 309 | 310 | /// As above, but only loops through tokens with the specified type and string 311 | public func forEach(_ type: SwiftFormat.TokenType, _ body: (Int, SwiftFormat.Token) -> Void) 312 | 313 | /// Returns the index of the next token in the specified range that matches the block 314 | public func index(in range: CountableRange, where matches: (SwiftFormat.Token) -> Bool) -> Int? 315 | 316 | /// Returns the index of the next token at the current scope that matches the block 317 | public func index(after index: Int, where matches: (SwiftFormat.Token) -> Bool) -> Int? 318 | 319 | /// Returns the index of the next matching token in the specified range 320 | public func index(of token: SwiftFormat.Token, in range: CountableRange) -> Int? 321 | 322 | /// Returns the index of the next matching token at the current scope 323 | public func index(of token: SwiftFormat.Token, after index: Int) -> Int? 324 | 325 | /// Returns the index of the next token in the specified range of the specified type 326 | public func index(of type: SwiftFormat.TokenType, in range: CountableRange, if matches: (SwiftFormat.Token) -> Bool = { _ in true }) -> Int? 327 | 328 | /// Returns the index of the next token at the current scope of the specified type 329 | public func index(of type: SwiftFormat.TokenType, after index: Int, if matches: (SwiftFormat.Token) -> Bool = { _ in true }) -> Int? 330 | 331 | /// Returns the next token at the current scope that matches the block 332 | public func nextToken(after index: Int, where matches: (SwiftFormat.Token) -> Bool = { _ in true }) -> SwiftFormat.Token? 333 | 334 | /// Returns the next token at the current scope of the specified type 335 | public func next(_ type: SwiftFormat.TokenType, after index: Int, if matches: (SwiftFormat.Token) -> Bool = { _ in true }) -> SwiftFormat.Token? 336 | 337 | /// Returns the next token in the specified range of the specified type 338 | public func next(_ type: SwiftFormat.TokenType, in range: CountableRange, if matches: (SwiftFormat.Token) -> Bool = { _ in true }) -> SwiftFormat.Token? 339 | 340 | /// Returns the index of the last token in the specified range that matches the block 341 | public func lastIndex(in range: CountableRange, where matches: (SwiftFormat.Token) -> Bool) -> Int? 342 | 343 | /// Returns the index of the previous token at the current scope that matches the block 344 | public func index(before index: Int, where matches: (SwiftFormat.Token) -> Bool) -> Int? 345 | 346 | /// Returns the index of the last matching token in the specified range 347 | public func lastIndex(of token: SwiftFormat.Token, in range: CountableRange) -> Int? 348 | 349 | /// Returns the index of the previous matching token at the current scope 350 | public func index(of token: SwiftFormat.Token, before index: Int) -> Int? 351 | 352 | /// Returns the index of the last token in the specified range of the specified type 353 | public func lastIndex(of type: SwiftFormat.TokenType, in range: CountableRange, if matches: (SwiftFormat.Token) -> Bool = { _ in true }) -> Int? 354 | 355 | /// Returns the index of the previous token at the current scope of the specified type 356 | public func index(of type: SwiftFormat.TokenType, before index: Int, if matches: (SwiftFormat.Token) -> Bool = { _ in true }) -> Int? 357 | 358 | /// Returns the previous token at the current scope that matches the block 359 | public func lastToken(before index: Int, where matches: (SwiftFormat.Token) -> Bool) -> SwiftFormat.Token? 360 | 361 | /// Returns the previous token at the current scope of the specified type 362 | public func last(_ type: SwiftFormat.TokenType, before index: Int, if matches: (SwiftFormat.Token) -> Bool = { _ in true }) -> SwiftFormat.Token? 363 | 364 | /// Returns the previous token in the specified range of the specified type 365 | public func last(_ type: SwiftFormat.TokenType, in range: CountableRange, if matches: (SwiftFormat.Token) -> Bool = { _ in true }) -> SwiftFormat.Token? 366 | 367 | /// Returns the starting token for the containing scope at the specified index 368 | public func currentScope(at index: Int) -> SwiftFormat.Token? 369 | 370 | public func endOfScope(at index: Int) -> Int? 371 | 372 | /// Returns the index of the first token of the line containing the specified index 373 | public func startOfLine(at index: Int) -> Int 374 | 375 | /// Returns the space at the start of the line containing the specified index 376 | public func indentForLine(at index: Int) -> String 377 | } 378 | 379 | /// Glob type represents either an exact path or wildcard 380 | public enum Glob: CustomStringConvertible { 381 | case path(String) 382 | 383 | case regex(NSRegularExpression) 384 | 385 | public func matches(_ path: String) -> Bool 386 | 387 | /// A textual representation of this instance. 388 | /// 389 | /// Calling this property directly is discouraged. Instead, convert an 390 | /// instance of any type to a string by using the `String(describing:)` 391 | /// initializer. This initializer works with any type, and uses the custom 392 | /// `description` property for types that conform to 393 | /// `CustomStringConvertible`: 394 | /// 395 | /// struct Point: CustomStringConvertible { 396 | /// let x: Int, y: Int 397 | /// 398 | /// var description: String { 399 | /// return "(\(x), \(y))" 400 | /// } 401 | /// } 402 | /// 403 | /// let p = Point(x: 21, y: 30) 404 | /// let s = String(describing: p) 405 | /// print(s) 406 | /// // Prints "(21, 30)" 407 | /// 408 | /// The conversion of `p` to a string in the assignment to `s` uses the 409 | /// `Point` type's `description` property. 410 | public var description: String { get } 411 | } 412 | 413 | /// Grouping for numeric literals 414 | public enum Grouping: Equatable, RawRepresentable, CustomStringConvertible { 415 | case ignore 416 | 417 | case none 418 | 419 | case group(Int, Int) 420 | 421 | /// Creates a new instance with the specified raw value. 422 | /// 423 | /// If there is no value of the type that corresponds with the specified raw 424 | /// value, this initializer returns `nil`. For example: 425 | /// 426 | /// enum PaperSize: String { 427 | /// case A4, A5, Letter, Legal 428 | /// } 429 | /// 430 | /// print(PaperSize(rawValue: "Legal")) 431 | /// // Prints "Optional("PaperSize.Legal")" 432 | /// 433 | /// print(PaperSize(rawValue: "Tabloid")) 434 | /// // Prints "nil" 435 | /// 436 | /// - Parameter rawValue: The raw value to use for the new instance. 437 | public init?(rawValue: String) 438 | 439 | /// The corresponding value of the raw type. 440 | /// 441 | /// A new instance initialized with `rawValue` will be equivalent to this 442 | /// instance. For example: 443 | /// 444 | /// enum PaperSize: String { 445 | /// case A4, A5, Letter, Legal 446 | /// } 447 | /// 448 | /// let selectedSize = PaperSize.Letter 449 | /// print(selectedSize.rawValue) 450 | /// // Prints "Letter" 451 | /// 452 | /// print(selectedSize == PaperSize(rawValue: selectedSize.rawValue)!) 453 | /// // Prints "true" 454 | public var rawValue: String { get } 455 | 456 | /// A textual representation of this instance. 457 | /// 458 | /// Calling this property directly is discouraged. Instead, convert an 459 | /// instance of any type to a string by using the `String(describing:)` 460 | /// initializer. This initializer works with any type, and uses the custom 461 | /// `description` property for types that conform to 462 | /// `CustomStringConvertible`: 463 | /// 464 | /// struct Point: CustomStringConvertible { 465 | /// let x: Int, y: Int 466 | /// 467 | /// var description: String { 468 | /// return "(\(x), \(y))" 469 | /// } 470 | /// } 471 | /// 472 | /// let p = Point(x: 21, y: 30) 473 | /// let s = String(describing: p) 474 | /// print(s) 475 | /// // Prints "(21, 30)" 476 | /// 477 | /// The conversion of `p` to a string in the assignment to `s` uses the 478 | /// `Point` type's `description` property. 479 | public var description: String { get } 480 | 481 | /// Returns a Boolean value indicating whether two values are equal. 482 | /// 483 | /// Equality is the inverse of inequality. For any values `a` and `b`, 484 | /// `a == b` implies that `a != b` is `false`. 485 | /// 486 | /// - Parameters: 487 | /// - lhs: A value to compare. 488 | /// - rhs: Another value to compare. 489 | public static func == (lhs: SwiftFormat.Grouping, rhs: SwiftFormat.Grouping) -> Bool 490 | } 491 | 492 | /// Argument type for stripping 493 | public enum HeaderStrippingMode: Equatable, RawRepresentable, ExpressibleByStringLiteral { 494 | case ignore 495 | 496 | case replace(String) 497 | 498 | /// Creates an instance initialized to the given string value. 499 | /// 500 | /// - Parameter value: The value of the new instance. 501 | public init(stringLiteral value: String) 502 | 503 | /// Creates a new instance with the specified raw value. 504 | /// 505 | /// If there is no value of the type that corresponds with the specified raw 506 | /// value, this initializer returns `nil`. For example: 507 | /// 508 | /// enum PaperSize: String { 509 | /// case A4, A5, Letter, Legal 510 | /// } 511 | /// 512 | /// print(PaperSize(rawValue: "Legal")) 513 | /// // Prints "Optional("PaperSize.Legal")" 514 | /// 515 | /// print(PaperSize(rawValue: "Tabloid")) 516 | /// // Prints "nil" 517 | /// 518 | /// - Parameter rawValue: The raw value to use for the new instance. 519 | public init?(rawValue: String) 520 | 521 | /// The corresponding value of the raw type. 522 | /// 523 | /// A new instance initialized with `rawValue` will be equivalent to this 524 | /// instance. For example: 525 | /// 526 | /// enum PaperSize: String { 527 | /// case A4, A5, Letter, Legal 528 | /// } 529 | /// 530 | /// let selectedSize = PaperSize.Letter 531 | /// print(selectedSize.rawValue) 532 | /// // Prints "Letter" 533 | /// 534 | /// print(selectedSize == PaperSize(rawValue: selectedSize.rawValue)!) 535 | /// // Prints "true" 536 | public var rawValue: String { get } 537 | 538 | /// Returns a Boolean value indicating whether two values are equal. 539 | /// 540 | /// Equality is the inverse of inequality. For any values `a` and `b`, 541 | /// `a == b` implies that `a != b` is `false`. 542 | /// 543 | /// - Parameters: 544 | /// - lhs: A value to compare. 545 | /// - rhs: Another value to compare. 546 | public static func == (lhs: SwiftFormat.HeaderStrippingMode, rhs: SwiftFormat.HeaderStrippingMode) -> Bool 547 | } 548 | 549 | /// Grouping for sorting imports 550 | public enum ImportGrouping: String { 551 | case alphabetized 552 | 553 | case testableTop 554 | 555 | case testableBottom 556 | } 557 | 558 | /// The indenting mode to use for #if/#endif statements 559 | public enum IndentMode: String { 560 | case indent 561 | 562 | case noIndent 563 | 564 | case outdent 565 | 566 | /// Creates a new instance with the specified raw value. 567 | /// 568 | /// If there is no value of the type that corresponds with the specified raw 569 | /// value, this initializer returns `nil`. For example: 570 | /// 571 | /// enum PaperSize: String { 572 | /// case A4, A5, Letter, Legal 573 | /// } 574 | /// 575 | /// print(PaperSize(rawValue: "Legal")) 576 | /// // Prints "Optional("PaperSize.Legal")" 577 | /// 578 | /// print(PaperSize(rawValue: "Tabloid")) 579 | /// // Prints "nil" 580 | /// 581 | /// - Parameter rawValue: The raw value to use for the new instance. 582 | public init?(rawValue: String) 583 | } 584 | 585 | /// Numeric literal types 586 | public enum NumberType { 587 | case integer 588 | 589 | case decimal 590 | 591 | case binary 592 | 593 | case octal 594 | 595 | case hex 596 | } 597 | 598 | /// Symbol/operator types 599 | public enum OperatorType { 600 | case none 601 | 602 | case infix 603 | 604 | case prefix 605 | 606 | case postfix 607 | } 608 | 609 | /// All options 610 | public struct Options { 611 | public var fileOptions: SwiftFormat.FileOptions? 612 | 613 | public var formatOptions: SwiftFormat.FormatOptions? 614 | 615 | public var rules: Set? 616 | 617 | public static let `default`: SwiftFormat.Options 618 | 619 | public init(fileOptions: SwiftFormat.FileOptions? = nil, formatOptions: SwiftFormat.FormatOptions? = nil, rules: Set? = nil) 620 | } 621 | 622 | /// Self insertion mode 623 | public enum SelfMode: String { 624 | case insert 625 | 626 | case remove 627 | 628 | case initOnly 629 | } 630 | 631 | /// All token types 632 | public enum Token: Equatable { 633 | case number(String, SwiftFormat.NumberType) 634 | 635 | case linebreak(String) 636 | 637 | case startOfScope(String) 638 | 639 | case endOfScope(String) 640 | 641 | case delimiter(String) 642 | 643 | case `operator`(String, SwiftFormat.OperatorType) 644 | 645 | case stringBody(String) 646 | 647 | case keyword(String) 648 | 649 | case identifier(String) 650 | 651 | case space(String) 652 | 653 | case commentBody(String) 654 | 655 | case error(String) 656 | 657 | /// The original token string 658 | public var string: String { get } 659 | 660 | /// Returns the unescaped token string 661 | public func unescaped() -> String 662 | 663 | /// Test if token is of the specified type 664 | public func `is`(_ type: SwiftFormat.TokenType) -> Bool 665 | 666 | public var isAttribute: Bool { get } 667 | 668 | public var isDelimiter: Bool { get } 669 | 670 | public var isOperator: Bool { get } 671 | 672 | public var isUnwrapOperator: Bool { get } 673 | 674 | public var isRangeOperator: Bool { get } 675 | 676 | public var isNumber: Bool { get } 677 | 678 | public var isError: Bool { get } 679 | 680 | public var isStartOfScope: Bool { get } 681 | 682 | public var isEndOfScope: Bool { get } 683 | 684 | public var isKeyword: Bool { get } 685 | 686 | public var isIdentifier: Bool { get } 687 | 688 | public var isIdentifierOrKeyword: Bool { get } 689 | 690 | public var isSpace: Bool { get } 691 | 692 | public var isLinebreak: Bool { get } 693 | 694 | public var isEndOfStatement: Bool { get } 695 | 696 | public var isSpaceOrLinebreak: Bool { get } 697 | 698 | public var isSpaceOrComment: Bool { get } 699 | 700 | public var isSpaceOrCommentOrLinebreak: Bool { get } 701 | 702 | public var isCommentOrLinebreak: Bool { get } 703 | 704 | public func isOperator(_ string: String) -> Bool 705 | 706 | public func isOperator(ofType type: SwiftFormat.OperatorType) -> Bool 707 | 708 | public var isComment: Bool { get } 709 | 710 | public var isStringDelimiter: Bool { get } 711 | 712 | public var isMultilineStringDelimiter: Bool { get } 713 | 714 | public func isEndOfScope(_ token: SwiftFormat.Token) -> Bool 715 | 716 | /// Returns a Boolean value indicating whether two values are equal. 717 | /// 718 | /// Equality is the inverse of inequality. For any values `a` and `b`, 719 | /// `a == b` implies that `a != b` is `false`. 720 | /// 721 | /// - Parameters: 722 | /// - lhs: A value to compare. 723 | /// - rhs: Another value to compare. 724 | public static func == (lhs: SwiftFormat.Token, rhs: SwiftFormat.Token) -> Bool 725 | } 726 | 727 | /// Classes of token used for matching 728 | public enum TokenType { 729 | case space 730 | 731 | case linebreak 732 | 733 | case endOfStatement 734 | 735 | case startOfScope 736 | 737 | case endOfScope 738 | 739 | case keyword 740 | 741 | case delimiter 742 | 743 | case identifier 744 | 745 | case attribute 746 | 747 | case `operator` 748 | 749 | case unwrapOperator 750 | 751 | case rangeOperator 752 | 753 | case number 754 | 755 | case error 756 | 757 | case spaceOrComment 758 | 759 | case spaceOrLinebreak 760 | 761 | case spaceOrCommentOrLinebreak 762 | 763 | case identifierOrKeyword 764 | 765 | case nonSpace 766 | 767 | case nonSpaceOrComment 768 | 769 | case nonSpaceOrLinebreak 770 | 771 | case nonSpaceOrCommentOrLinebreak 772 | } 773 | 774 | /// Version number wrapper 775 | public struct Version: RawRepresentable, Comparable, ExpressibleByStringLiteral { 776 | /// The corresponding value of the raw type. 777 | /// 778 | /// A new instance initialized with `rawValue` will be equivalent to this 779 | /// instance. For example: 780 | /// 781 | /// enum PaperSize: String { 782 | /// case A4, A5, Letter, Legal 783 | /// } 784 | /// 785 | /// let selectedSize = PaperSize.Letter 786 | /// print(selectedSize.rawValue) 787 | /// // Prints "Letter" 788 | /// 789 | /// print(selectedSize == PaperSize(rawValue: selectedSize.rawValue)!) 790 | /// // Prints "true" 791 | public let rawValue: String 792 | 793 | public static let undefined: SwiftFormat.Version 794 | 795 | /// Creates an instance initialized to the given string value. 796 | /// 797 | /// - Parameter value: The value of the new instance. 798 | public init(stringLiteral value: String) 799 | 800 | /// Creates a new instance with the specified raw value. 801 | /// 802 | /// If there is no value of the type that corresponds with the specified raw 803 | /// value, this initializer returns `nil`. For example: 804 | /// 805 | /// enum PaperSize: String { 806 | /// case A4, A5, Letter, Legal 807 | /// } 808 | /// 809 | /// print(PaperSize(rawValue: "Legal")) 810 | /// // Prints "Optional("PaperSize.Legal")" 811 | /// 812 | /// print(PaperSize(rawValue: "Tabloid")) 813 | /// // Prints "nil" 814 | /// 815 | /// - Parameter rawValue: The raw value to use for the new instance. 816 | public init?(rawValue: String) 817 | 818 | /// Returns a Boolean value indicating whether two values are equal. 819 | /// 820 | /// Equality is the inverse of inequality. For any values `a` and `b`, 821 | /// `a == b` implies that `a != b` is `false`. 822 | /// 823 | /// - Parameters: 824 | /// - lhs: A value to compare. 825 | /// - rhs: Another value to compare. 826 | public static func == (lhs: SwiftFormat.Version, rhs: SwiftFormat.Version) -> Bool 827 | 828 | /// Returns a Boolean value indicating whether the value of the first 829 | /// argument is less than that of the second argument. 830 | /// 831 | /// This function is the only requirement of the `Comparable` protocol. The 832 | /// remainder of the relational operator functions are implemented by the 833 | /// standard library for any type that conforms to `Comparable`. 834 | /// 835 | /// - Parameters: 836 | /// - lhs: A value to compare. 837 | /// - rhs: Another value to compare. 838 | public static func < (lhs: SwiftFormat.Version, rhs: SwiftFormat.Version) -> Bool 839 | } 840 | 841 | /// Wrap mode for arguments 842 | public enum WrapMode: String { 843 | case beforeFirst 844 | 845 | case afterFirst 846 | 847 | case preserve 848 | 849 | case disabled 850 | 851 | /// Creates a new instance with the specified raw value. 852 | /// 853 | /// If there is no value of the type that corresponds with the specified raw 854 | /// value, this initializer returns `nil`. For example: 855 | /// 856 | /// enum PaperSize: String { 857 | /// case A4, A5, Letter, Legal 858 | /// } 859 | /// 860 | /// print(PaperSize(rawValue: "Legal")) 861 | /// // Prints "Optional("PaperSize.Legal")" 862 | /// 863 | /// print(PaperSize(rawValue: "Tabloid")) 864 | /// // Prints "nil" 865 | /// 866 | /// - Parameter rawValue: The raw value to use for the new instance. 867 | public init?(rawValue: String) 868 | } 869 | 870 | public struct _FormatRules { 871 | /// Implement the following rules with respect to the spacing around parens: 872 | /// * There is no space between an opening paren and the preceding identifier, 873 | /// unless the identifier is one of the specified keywords 874 | /// * There is no space between an opening paren and the preceding closing brace 875 | /// * There is no space between an opening paren and the preceding closing square bracket 876 | /// * There is space between a closing paren and following identifier 877 | /// * There is space between a closing paren and following opening brace 878 | /// * There is no space between a closing paren and following opening square bracket 879 | public let spaceAroundParens: SwiftFormat.FormatRule 880 | 881 | /// Remove space immediately inside parens 882 | public let spaceInsideParens: SwiftFormat.FormatRule 883 | 884 | /// Implement the following rules with respect to the spacing around square brackets: 885 | /// * There is no space between an opening bracket and the preceding identifier, 886 | /// unless the identifier is one of the specified keywords 887 | /// * There is no space between an opening bracket and the preceding closing brace 888 | /// * There is no space between an opening bracket and the preceding closing square bracket 889 | /// * There is space between a closing bracket and following identifier 890 | /// * There is space between a closing bracket and following opening brace 891 | public let spaceAroundBrackets: SwiftFormat.FormatRule 892 | 893 | /// Remove space immediately inside square brackets 894 | public let spaceInsideBrackets: SwiftFormat.FormatRule 895 | 896 | /// Ensure that there is space between an opening brace and the preceding 897 | /// identifier, and between a closing brace and the following identifier. 898 | public let spaceAroundBraces: SwiftFormat.FormatRule 899 | 900 | /// Ensure that there is space immediately inside braces 901 | public let spaceInsideBraces: SwiftFormat.FormatRule 902 | 903 | /// Ensure there is no space between an opening chevron and the preceding identifier 904 | public let spaceAroundGenerics: SwiftFormat.FormatRule 905 | 906 | /// Remove space immediately inside chevrons 907 | public let spaceInsideGenerics: SwiftFormat.FormatRule 908 | 909 | /// Implement the following rules with respect to the spacing around operators: 910 | /// * Infix operators are separated from their operands by a space on either 911 | /// side. Does not affect prefix/postfix operators, as required by syntax. 912 | /// * Delimiters, such as commas and colons, are consistently followed by a 913 | /// single space, unless it appears at the end of a line, and is not 914 | /// preceded by a space, unless it appears at the beginning of a line. 915 | public let spaceAroundOperators: SwiftFormat.FormatRule 916 | 917 | /// Add space around comments, except at the start or end of a line 918 | public let spaceAroundComments: SwiftFormat.FormatRule 919 | 920 | /// Add space inside comments, taking care not to mangle headerdoc or 921 | /// carefully preformatted comments, such as star boxes, etc. 922 | public let spaceInsideComments: SwiftFormat.FormatRule 923 | 924 | /// Adds or removes the space around range operators 925 | public let ranges: SwiftFormat.FormatRule 926 | 927 | /// Collapse all consecutive space characters to a single space, except at 928 | /// the start of a line or inside a comment or string, as these have no semantic 929 | /// meaning and lead to noise in commits. 930 | public let consecutiveSpaces: SwiftFormat.FormatRule 931 | 932 | /// Remove trailing space from the end of lines, as it has no semantic 933 | /// meaning and leads to noise in commits. 934 | public let trailingSpace: SwiftFormat.FormatRule 935 | 936 | /// Collapse all consecutive blank lines into a single blank line 937 | public let consecutiveBlankLines: SwiftFormat.FormatRule 938 | 939 | /// Remove blank lines immediately after an opening brace, bracket, paren or chevron 940 | public let blankLinesAtStartOfScope: SwiftFormat.FormatRule 941 | 942 | /// Remove blank lines immediately before a closing brace, bracket, paren or chevron 943 | /// unless it's followed by more code on the same line (e.g. } else { ) 944 | public let blankLinesAtEndOfScope: SwiftFormat.FormatRule 945 | 946 | /// Adds a blank line immediately after a closing brace, unless followed by another closing brace 947 | public let blankLinesBetweenScopes: SwiftFormat.FormatRule 948 | 949 | /// Adds a blank line around MARK: comments 950 | public let blankLinesAroundMark: SwiftFormat.FormatRule 951 | 952 | /// Always end file with a linebreak, to avoid incompatibility with certain unix tools: 953 | /// http://stackoverflow.com/questions/2287967/why-is-it-recommended-to-have-empty-line-in-the-end-of-file 954 | public let linebreakAtEndOfFile: SwiftFormat.FormatRule 955 | 956 | /// Indent code according to standard scope indenting rules. 957 | /// The type (tab or space) and level (2 spaces, 4 spaces, etc.) of the 958 | /// indenting can be configured with the `options` parameter of the formatter. 959 | public let indent: SwiftFormat.FormatRule 960 | 961 | public let braces: SwiftFormat.FormatRule 962 | 963 | /// Ensure that an `else` statement following `if { ... }` appears on the same line 964 | /// as the closing brace. This has no effect on the `else` part of a `guard` statement. 965 | /// Also applies to `catch` after `try` and `while` after `repeat`. 966 | public let elseOnSameLine: SwiftFormat.FormatRule 967 | 968 | /// Ensure that the last item in a multi-line array literal is followed by a comma. 969 | /// This is useful for preventing noise in commits when items are added to end of array. 970 | public let trailingCommas: SwiftFormat.FormatRule 971 | 972 | /// Ensure that TODO, MARK and FIXME comments are followed by a : as required 973 | public let todos: SwiftFormat.FormatRule 974 | 975 | /// Remove semicolons, except where doing so would change the meaning of the code 976 | public let semicolons: SwiftFormat.FormatRule 977 | 978 | /// Standardise linebreak characters as whatever is specified in the options (\n by default) 979 | public let linebreaks: SwiftFormat.FormatRule 980 | 981 | /// Standardise the order of property specifiers 982 | public let specifiers: SwiftFormat.FormatRule 983 | 984 | /// Convert closure arguments to trailing closure syntax where possible 985 | /// NOTE: Parens around trailing closures are sometimes required for disambiguation. 986 | /// SwiftFormat can't detect those cases, so `trailingClosures` is disabled by default 987 | public let trailingClosures: SwiftFormat.FormatRule 988 | 989 | /// Remove redundant parens around the arguments for loops, if statements, closures, etc. 990 | public let redundantParens: SwiftFormat.FormatRule 991 | 992 | /// Remove redundant `get {}` clause inside read-only computed property 993 | public let redundantGet: SwiftFormat.FormatRule 994 | 995 | /// Remove redundant `= nil` initialization for Optional properties 996 | public let redundantNilInit: SwiftFormat.FormatRule 997 | 998 | /// Remove redundant let/var for unnamed variables 999 | public let redundantLet: SwiftFormat.FormatRule 1000 | 1001 | /// Remove redundant pattern in case statements 1002 | public let redundantPattern: SwiftFormat.FormatRule 1003 | 1004 | /// Remove redundant raw string values for case statements 1005 | public let redundantRawValues: SwiftFormat.FormatRule 1006 | 1007 | /// Remove redundant void return values for function declarations 1008 | public let redundantVoidReturnType: SwiftFormat.FormatRule 1009 | 1010 | /// Remove redundant return keyword from single-line closures 1011 | public let redundantReturn: SwiftFormat.FormatRule 1012 | 1013 | /// Remove redundant backticks around non-keywords, or in places where keywords don't need escaping 1014 | public let redundantBackticks: SwiftFormat.FormatRule 1015 | 1016 | public let redundantSelf: SwiftFormat.FormatRule 1017 | 1018 | /// Replace unused arguments with an underscore 1019 | public let unusedArguments: SwiftFormat.FormatRule 1020 | 1021 | /// Move `let` and `var` inside patterns to the beginning 1022 | public let hoistPatternLet: SwiftFormat.FormatRule 1023 | 1024 | /// Normalize argument wrapping style 1025 | public let wrapArguments: SwiftFormat.FormatRule 1026 | 1027 | /// Normalize the use of void in closure arguments and return values 1028 | public let void: SwiftFormat.FormatRule 1029 | 1030 | /// Standardize formatting of numeric literals 1031 | public let numberFormatting: SwiftFormat.FormatRule 1032 | 1033 | /// Strip header comments from the file 1034 | public let fileHeader: SwiftFormat.FormatRule 1035 | 1036 | /// Strip redundant `.init` from type instantiations 1037 | public let redundantInit: SwiftFormat.FormatRule 1038 | 1039 | /// Sort import statements 1040 | public let sortedImports: SwiftFormat.FormatRule 1041 | 1042 | /// Remove duplicate import statements 1043 | public let duplicateImports: SwiftFormat.FormatRule 1044 | 1045 | /// Strip unnecessary `weak` from @IBOutlet properties (except delegates and datasources) 1046 | public let strongOutlets: SwiftFormat.FormatRule 1047 | 1048 | /// Remove white-space between empty braces 1049 | public let emptyBraces: SwiftFormat.FormatRule 1050 | 1051 | /// Replace the `&&` operator with `,` where applicable 1052 | public let andOperator: SwiftFormat.FormatRule 1053 | 1054 | /// Replace count == 0 with isEmpty 1055 | public let isEmpty: SwiftFormat.FormatRule 1056 | 1057 | /// Remove redundant `let error` from `catch` statements 1058 | public let redundantLetError: SwiftFormat.FormatRule 1059 | 1060 | /// Prefer `AnyObject` over `class` for class-based protocols 1061 | public let anyObjectProtocol: SwiftFormat.FormatRule 1062 | 1063 | /// Remove redundant `break` keyword from switch cases 1064 | public let redundantBreak: SwiftFormat.FormatRule 1065 | 1066 | /// Removed backticks from `self` when strongifying 1067 | public let strongifiedSelf: SwiftFormat.FormatRule 1068 | 1069 | /// Remove redundant @objc annotation 1070 | public let redundantObjc: SwiftFormat.FormatRule 1071 | 1072 | /// Replace Array, Dictionary and Optional with [T], [T: U] and T? 1073 | public let typeSugar: SwiftFormat.FormatRule 1074 | 1075 | /// Remove redundant access control level modifiers in extensions 1076 | public let redundantExtensionACL: SwiftFormat.FormatRule 1077 | 1078 | /// Replace `fileprivate` with `private` where possible 1079 | public let redundantFileprivate: SwiftFormat.FormatRule 1080 | 1081 | /// Reorders "yoda conditions" where constant is placed on lhs of a comparison 1082 | public let yodaConditions: SwiftFormat.FormatRule 1083 | 1084 | public let leadingDelimiters: SwiftFormat.FormatRule 1085 | } 1086 | 1087 | extension _FormatRules { 1088 | /// A Dictionary of rules by name 1089 | public var byName: [String: SwiftFormat.FormatRule] { get } 1090 | 1091 | /// All rules 1092 | public var all: [SwiftFormat.FormatRule] { get } 1093 | 1094 | /// Default active rules 1095 | public var `default`: [SwiftFormat.FormatRule] { get } 1096 | 1097 | /// Rules that are disabled by default 1098 | public var disabledByDefault: [String] { get } 1099 | 1100 | /// Just the specified rules 1101 | public func named(_ names: [String]) -> [SwiftFormat.FormatRule] 1102 | 1103 | /// All rules except those specified 1104 | public func all(except rules: [String]) -> [SwiftFormat.FormatRule] 1105 | 1106 | @available(*, deprecated, message: "Use named() method instead") 1107 | public func all(named: [String]) -> [SwiftFormat.FormatRule] 1108 | } 1109 | 1110 | /// Apply specified rules to a token array with optional callback 1111 | /// Useful for perfoming additional logic after each rule is applied 1112 | public func applyRules(_ rules: [SwiftFormat.FormatRule], to originalTokens: [SwiftFormat.Token], with options: SwiftFormat.FormatOptions, callback: ((Int, [SwiftFormat.Token]) -> Void)? = nil) throws -> [SwiftFormat.Token] 1113 | 1114 | /// Legacy file enumeration function 1115 | @available(*, deprecated, message: "Use other enumerateFiles() method instead") 1116 | public func enumerateFiles(withInputURL inputURL: URL, excluding excludedURLs: [URL] = [], outputURL: URL? = nil, options fileOptions: SwiftFormat.FileOptions = .default, concurrent: Bool = true, block: @escaping (URL, URL) throws -> () throws -> Void) -> [Error] 1117 | 1118 | /// Enumerate all swift files at the specified location and (optionally) calculate an output file URL for each. 1119 | /// Ignores the file if any of the excluded file URLs is a prefix of the input file URL. 1120 | /// 1121 | /// Files are enumerated concurrently. For convenience, the enumeration block returns a completion block, which 1122 | /// will be executed synchronously on the calling thread once enumeration is complete. 1123 | /// 1124 | /// Errors may be thrown by either the enumeration block or the completion block, and are gathered into an 1125 | /// array and returned after enumeration is complete, along with any errors generated by the function itself. 1126 | /// Throwing an error from inside either block does *not* terminate the enumeration. 1127 | public func enumerateFiles(withInputURL inputURL: URL, outputURL: URL? = nil, options baseOptions: SwiftFormat.Options = .default, concurrent: Bool = true, skipped: SwiftFormat.FileEnumerationHandler? = nil, handler: @escaping SwiftFormat.FileEnumerationHandler) -> [Error] 1128 | 1129 | /// Expand one or more comma-delimited file paths using glob syntax 1130 | public func expandGlobs(_ paths: String, in directory: String) -> [SwiftFormat.Glob] 1131 | 1132 | public func expandPath(_ path: String, in directory: String) -> URL 1133 | 1134 | /// Format a pre-parsed token array 1135 | /// Returns the formatted token array, and the number of edits made 1136 | public func format(_ tokens: [SwiftFormat.Token], rules: [SwiftFormat.FormatRule] = FormatRules.default, options: SwiftFormat.FormatOptions = .default) throws -> [SwiftFormat.Token] 1137 | 1138 | /// Format code with specified rules and options 1139 | public func format(_ source: String, rules: [SwiftFormat.FormatRule] = FormatRules.default, options: SwiftFormat.FormatOptions = .default) throws -> String 1140 | 1141 | /// Infer default options by examining the existing source 1142 | public func inferFormatOptions(from tokens: [SwiftFormat.Token]) -> SwiftFormat.FormatOptions 1143 | 1144 | /// Get line/column offset for token 1145 | /// Note: line indexes start at 1, columns start at zero 1146 | public func offsetForToken(at index: Int, in tokens: [SwiftFormat.Token]) -> (line: Int, column: Int) 1147 | 1148 | /// Process parsing errors 1149 | public func parsingError(for tokens: [SwiftFormat.Token], options: SwiftFormat.FormatOptions) -> SwiftFormat.FormatError? 1150 | 1151 | /// Convert a token array back into a string 1152 | public func sourceCode(for tokens: [SwiftFormat.Token]) -> String 1153 | 1154 | /// The standard SwiftFormat config file name 1155 | public let swiftFormatConfigurationFile: String 1156 | 1157 | /// The standard Swift version file name 1158 | public let swiftVersionFile: String 1159 | 1160 | public func tokenize(_ source: String) -> [SwiftFormat.Token] 1161 | 1162 | /// The current SwiftFormat version 1163 | public let version: String 1164 | 1165 | extension String { 1166 | /// Is this string a reserved keyword in Swift? 1167 | public var isSwiftKeyword: Bool { get } 1168 | 1169 | /// Is this string a keyword in some contexts? 1170 | public var isContextualKeyword: Bool { get } 1171 | } 1172 | 1173 | public var FormatRules: SwiftFormat._FormatRules 1174 | 1175 | /// The current SwiftFormat version 1176 | public var version: String 1177 | 1178 | /// The standard SwiftFormat config file name 1179 | public var swiftFormatConfigurationFile: String 1180 | 1181 | /// The standard Swift version file name 1182 | public var swiftVersionFile: String 1183 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TOOL_NAME = moduleinterface 2 | VERSION = 0.0.1 3 | 4 | PREFIX = /usr/local 5 | INSTALL_PATH = $(PREFIX)/bin/$(TOOL_NAME) 6 | BUILD_PATH = .build/release/$(TOOL_NAME) 7 | TAR_FILENAME = $(VERSION).tar.gz 8 | 9 | .PHONY: build docs 10 | 11 | install: build 12 | install -d "$(PREFIX)/bin" 13 | install -C -m 755 $(BUILD_PATH) $(INSTALL_PATH) 14 | 15 | build: 16 | swift build --disable-sandbox -c release 17 | 18 | uninstall: 19 | rm -f $(INSTALL_PATH) 20 | 21 | lint: 22 | swift run swiftformat . 23 | 24 | docs: 25 | swift run sourcedocs generate --clean --spm-module SourceDocsDemo --output-folder docs/reference --module-name-path 26 | 27 | xcode: 28 | swift package generate-xcodeproj --enable-code-coverage 29 | 30 | linuxmain: 31 | swift test --generate-linuxmain 32 | 33 | zip: build 34 | zip -D $(TOOL_NAME).macos.zip $(BUILD_PATH) 35 | 36 | get_sha: 37 | curl -OLs https://github.com/minuscorp/$(TOOL_NAME)/archive/$(VERSION).tar.gz 38 | shasum -a 256 $(TAR_FILENAME) | cut -f 1 -d " " > sha_$(VERSION).txt 39 | rm $(TAR_FILENAME) 40 | 41 | brew_push: get_sha 42 | SHA=$(shell cat sha_$(VERSION).txt); \ 43 | brew bump-formula-pr --url=https://github.com/minuscorp/$(TOOL_NAME)/archive/$(VERSION).tar.gz --sha256=$$SHA 44 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "Commandant", 6 | "repositoryURL": "https://github.com/Carthage/Commandant.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "ab68611013dec67413628ac87c1f29e8427bc8e4", 10 | "version": "0.17.0" 11 | } 12 | }, 13 | { 14 | "package": "Curry", 15 | "repositoryURL": "https://github.com/thoughtbot/Curry.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "4331dd50bc1db007db664a23f32e6f3df93d4e1a", 19 | "version": "4.0.2" 20 | } 21 | }, 22 | { 23 | "package": "CwlCatchException", 24 | "repositoryURL": "https://github.com/mattgallagher/CwlCatchException.git", 25 | "state": { 26 | "branch": null, 27 | "revision": "7cd2f8cacc4d22f21bc0b2309c3b18acf7957b66", 28 | "version": "1.2.0" 29 | } 30 | }, 31 | { 32 | "package": "CwlPreconditionTesting", 33 | "repositoryURL": "https://github.com/mattgallagher/CwlPreconditionTesting.git", 34 | "state": { 35 | "branch": null, 36 | "revision": "c228db5d2ad1b01ebc84435e823e6cca4e3db98b", 37 | "version": "1.2.0" 38 | } 39 | }, 40 | { 41 | "package": "MarkdownGenerator", 42 | "repositoryURL": "https://github.com/eneko/MarkdownGenerator.git", 43 | "state": { 44 | "branch": null, 45 | "revision": "1fc31be6b66245187c3f87f35d86b6aeac6dfe7f", 46 | "version": "0.5.0" 47 | } 48 | }, 49 | { 50 | "package": "Nimble", 51 | "repositoryURL": "https://github.com/Quick/Nimble.git", 52 | "state": { 53 | "branch": null, 54 | "revision": "6abeb3f5c03beba2b9e4dbe20886e773b5b629b6", 55 | "version": "8.0.4" 56 | } 57 | }, 58 | { 59 | "package": "Quick", 60 | "repositoryURL": "https://github.com/Quick/Quick.git", 61 | "state": { 62 | "branch": null, 63 | "revision": "33682c2f6230c60614861dfc61df267e11a1602f", 64 | "version": "2.2.0" 65 | } 66 | }, 67 | { 68 | "package": "Rainbow", 69 | "repositoryURL": "https://github.com/onevcat/Rainbow", 70 | "state": { 71 | "branch": null, 72 | "revision": "9c52c1952e9b2305d4507cf473392ac2d7c9b155", 73 | "version": "3.1.5" 74 | } 75 | }, 76 | { 77 | "package": "SourceDocs", 78 | "repositoryURL": "https://github.com/eneko/SourceDocs.git", 79 | "state": { 80 | "branch": null, 81 | "revision": "dfa6fdae84c555264ad9795dcb7d629339191c01", 82 | "version": "0.6.1" 83 | } 84 | }, 85 | { 86 | "package": "SourceKitten", 87 | "repositoryURL": "https://github.com/jpsim/SourceKitten.git", 88 | "state": { 89 | "branch": null, 90 | "revision": "356551fc513eb12ed779bb369f79cf86a3a01599", 91 | "version": "0.27.0" 92 | } 93 | }, 94 | { 95 | "package": "llbuild", 96 | "repositoryURL": "https://github.com/apple/swift-llbuild.git", 97 | "state": { 98 | "branch": null, 99 | "revision": "f1c9ad9a253cdf1aa89a7f5c99c30b4513b06ddb", 100 | "version": "0.1.1" 101 | } 102 | }, 103 | { 104 | "package": "SwiftPM", 105 | "repositoryURL": "https://github.com/apple/swift-package-manager.git", 106 | "state": { 107 | "branch": null, 108 | "revision": "8656a25cb906c1896339f950ac960ee1b4fe8034", 109 | "version": "0.4.0" 110 | } 111 | }, 112 | { 113 | "package": "SwiftFormat", 114 | "repositoryURL": "https://github.com/nicklockwood/SwiftFormat", 115 | "state": { 116 | "branch": null, 117 | "revision": "768be83fb2c232f13ad75dc15056c5909e374ef6", 118 | "version": "0.40.14" 119 | } 120 | }, 121 | { 122 | "package": "SWXMLHash", 123 | "repositoryURL": "https://github.com/drmohundro/SWXMLHash.git", 124 | "state": { 125 | "branch": null, 126 | "revision": "a4931e5c3bafbedeb1601d3bb76bbe835c6d475a", 127 | "version": "5.0.1" 128 | } 129 | }, 130 | { 131 | "package": "System", 132 | "repositoryURL": "https://github.com/eneko/System.git", 133 | "state": { 134 | "branch": null, 135 | "revision": "381352d9479fb9c75d99a5ae5503f7d560c62449", 136 | "version": "0.2.0" 137 | } 138 | }, 139 | { 140 | "package": "Yams", 141 | "repositoryURL": "https://github.com/jpsim/Yams.git", 142 | "state": { 143 | "branch": null, 144 | "revision": "c947a306d2e80ecb2c0859047b35c73b8e1ca27f", 145 | "version": "2.0.0" 146 | } 147 | } 148 | ] 149 | }, 150 | "version": 1 151 | } 152 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "ModuleInterface", 8 | products: [ 9 | .executable(name: "moduleinterface", targets: ["ModuleInterface"]), 10 | ], 11 | dependencies: [ 12 | // Dependencies declare other packages that this package depends on. 13 | .package(url: "https://github.com/jpsim/SourceKitten.git", from: "0.26.0"), 14 | .package(url: "https://github.com/Carthage/Commandant.git", from: "0.15.0"), 15 | .package(url: "https://github.com/onevcat/Rainbow", from: "3.0.0"), 16 | .package(url: "https://github.com/eneko/SourceDocs.git", from: "0.2.0"), 17 | .package(url: "https://github.com/thoughtbot/Curry.git", from: "4.0.1"), 18 | .package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.35.8"), 19 | ], 20 | targets: [ 21 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 22 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 23 | .target( 24 | name: "ModuleInterface", 25 | dependencies: ["SourceKittenFramework", "Commandant", "Rainbow", "Curry", "SwiftFormat"] 26 | ), 27 | .testTarget( 28 | name: "ModuleInterfaceTests", 29 | dependencies: ["ModuleInterface"] 30 | ), 31 | ] 32 | ) 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ModuleInterface 2 | [![Build Status](https://travis-ci.org/minuscorp/ModuleInterface.svg?branch=master)](https://travis-ci.org/minuscorp/ModuleInterface) 3 | ![GitHub release (latest by date)](https://img.shields.io/github/v/release/minuscorp/ModuleInterface) 4 | ![License](https://img.shields.io/static/v1?label=License&message=Apache&color=blue) 5 | ![Swift version](https://img.shields.io/badge/Swift-5.1-orange) 6 | ![Twitter Follow](https://img.shields.io/twitter/follow/minuscorp?style=social) 7 | 8 | Swift tool to generate Module Interfaces for Swift projects. 9 | 10 | ## What is a Module Interface 11 | 12 | A Module Interface is what we commonly get using the CMD+click on an `import` statement in our codebases. SourceKit generates the code on demand. It can be a great tool and source of documentation. 13 | 14 | [![asciicast](https://asciinema.org/a/284079.svg)](https://asciinema.org/a/284079) 15 | 16 | ### Examples 17 | 18 | * [Commandant](Examples/Commandant.swift) 19 | * [Mini](Examples/Mini.swift) 20 | * [SourceKittenFramework](Examples/SourceKittenFramework.swift) 21 | * [Yams](Examples/Yams.swift) 22 | * [SwiftLintFramework](Examples/SwiftLintFramework.swift) 23 | 24 | ## Usage 25 | 26 | To generate the module interface from your project, or library, run the `moduleinterface` command directly from the root your project. 27 | 28 | ``` 29 | $ cd ~/path/to/MyAppOrFramework 30 | $ moduleinterface generate 31 | ``` 32 | 33 | This command will analyze your MyAppOrFramework project and generate the module interface for the types that have the minimum access level defined. The module interface is written to the directory Documentation relative to the root of your project repository. 34 | 35 | ### Usage options 36 | 37 | ``` 38 | $ moduleinterface help 39 | Available commands: 40 | 41 | clean Delete the output folder and quit. 42 | generate Generates the Module Interface 43 | help Display general or command-specific help 44 | version Display the current version of ModuleInterface 45 | ``` 46 | 47 | Typing `moduleinterface help ` we get a list of all options for that command: 48 | 49 | ``` 50 | Generates the Swift Module Interface. 51 | 52 | [--spm-module (string)] 53 | Generate documentation for Swift Package Manager module. 54 | 55 | [--module-name (string)] 56 | Generate documentation for a Swift module. 57 | 58 | [--input-folder (string)] 59 | Path to the input directory (defaults to /Users/minuscorp/Documents/GitHub/ModuleInterface). 60 | 61 | [--output-folder (string)] 62 | Output directory (defaults to Documentation). 63 | 64 | [--min-acl (string)] 65 | The minimum access level to generate documentation. Defaults to public. 66 | 67 | --clean|-c 68 | Delete output folder before generating documentation. 69 | 70 | [[]] 71 | List of arguments to pass to xcodebuild. 72 | ``` 73 | 74 | Usually, for most Xcode projects, no parameters are needed at all. xcodebuild should be able to find the default project and scheme. 75 | 76 | If the command fails, try specifying the scheme (-scheme SchemeName) or the workspace. Any arguments passed to `moduleinterface` after `--` will be passed to xcodebuild without modification. 77 | 78 | `$ moduleinterface generate -- -scheme MyScheme` 79 | 80 | For Swift Package Manager modules, you can the module name using the --spm-module parameter. 81 | 82 | `$ moduleinterface generate --spm-module ModuleInterface` 83 | 84 | ## Installation 85 | 86 | ### Download Binary 87 | 88 | ``` 89 | $ curl -Ls https://github.com/minuscorp/ModuleInterface/releases/download/latest/moduleinterface.macos.zip -o /tmp/moduleinterface.macos.zip 90 | $ unzip -j -d /usr/local/bin /tmp/moduleinterface.macos.zip 91 | ``` 92 | 93 | ### From Sources 94 | Requirements: 95 | 96 | Swift 5.1 runtime and Xcode installed in your computer. 97 | 98 | ### Using Homebrew 99 | 100 | `brew tap minuscorp/moduleinterface` 101 | `brew install moduleinterface` 102 | 103 | ### Building with Swift Package Manager 104 | 105 | ``` 106 | $ git clone https://github.com/minuscorp/ModuleInterface.git 107 | $ cd ModuleInterface 108 | $ make 109 | ``` 110 | 111 | ## Contact 112 | 113 | Follow and contact me on Twitter at [@minuscorp](https://twitter.com/minuscorp). 114 | 115 | ## Contributions 116 | 117 | If you find an issue, just [open a ticket](https://github.com/minuscorp/ModuleInterface/issues/new) on it. Pull requests are warmly welcome as well. 118 | 119 | ## License 120 | 121 | ModuleInterface is licensed under the Apache 2.0. See [LICENSE](https://github.com/minuscorp/ModuleInterface/blob/master/LICENSE) for more info. 122 | 123 | ## Acknowledegments 124 | 125 | - To [@eneko](https://twitter.com/eneko) for giving me the tooling idea. 126 | - To [SourceKitten](https://github.com/jpsim/SourceKitten) for providing such an awesome Framework for dealing with SourceKit. 127 | - To [BQ](https://github.com/bq) for all the mentoring. 128 | -------------------------------------------------------------------------------- /Sources/ModuleInterface/Classes/AccessLevel.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright [2019] [Jorge Revuelta Herrero] 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import Foundation 18 | 19 | enum AccessLevel: String, CaseIterable { 20 | case `private` 21 | case `fileprivate` 22 | case `internal` 23 | case `public` 24 | case open 25 | 26 | init(accessiblityKey: String?) { 27 | switch accessiblityKey { 28 | case .some("source.lang.swift.accessibility.private"): 29 | self = .private 30 | case .some("source.lang.swift.accessibility.fileprivate"): 31 | self = .fileprivate 32 | case .some("source.lang.swift.accessibility.internal"): 33 | self = .internal 34 | case .some("source.lang.swift.accessibility.public"): 35 | self = .public 36 | case .some("source.lang.swift.accessibility.open"): 37 | self = .open 38 | default: 39 | self = .private 40 | } 41 | } 42 | 43 | var priority: Int { 44 | switch self { 45 | case .private: 46 | return 0 47 | case .fileprivate: 48 | return 1 49 | case .internal: 50 | return 2 51 | case .public: 52 | return 3 53 | case .open: 54 | return 4 55 | } 56 | } 57 | } 58 | 59 | extension AccessLevel: Comparable, Equatable { 60 | static func < (lhs: AccessLevel, rhs: AccessLevel) -> Bool { 61 | lhs.priority < rhs.priority 62 | } 63 | 64 | static func == (lhs: AccessLevel, rhs: AccessLevel) -> Bool { 65 | lhs.priority == rhs.priority 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Sources/ModuleInterface/Classes/ModuleInterface.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright [2019] [Jorge Revuelta Herrero] 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import Commandant 18 | import Foundation 19 | 20 | struct ModuleInterface { 21 | enum Error: Swift.Error { 22 | case `default`(String) 23 | 24 | var localizedDescription: String { 25 | if case let .default(description) = self { 26 | return description 27 | } 28 | fatalError() 29 | } 30 | } 31 | 32 | static let version: String = "0.0.4" 33 | static let defaultOutputPath = "Documentation" 34 | 35 | func run() { 36 | let registry = CommandRegistry() 37 | registry.register(CleanCommand()) 38 | registry.register(GenerateCommand()) 39 | registry.register(VersionCommand()) 40 | registry.register(HelpCommand(registry: registry)) 41 | 42 | registry.main(defaultVerb: "help") { error in 43 | fputs("\(error.localizedDescription)\n".red, stderr) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/ModuleInterface/Classes/SwiftDoc.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright [2019] [Jorge Revuelta Herrero] 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import Foundation 18 | import SourceKittenFramework 19 | 20 | typealias SwiftDoc = [String: Any] 21 | 22 | extension Dictionary where Key == String, Value == Any { 23 | func get(_ key: SwiftDocKey) -> T? { 24 | self[key.rawValue] as? T 25 | } 26 | 27 | var accessLevel: AccessLevel { 28 | let accessiblityKey = self["key.accessibility"] as? String 29 | return AccessLevel(accessiblityKey: accessiblityKey) 30 | } 31 | 32 | func isKind(of kind: SwiftDeclarationKind) -> Bool { 33 | SwiftDeclarationKind(rawValue: get(.kind) ?? "") == kind 34 | } 35 | 36 | func isKind(of kinds: SwiftDeclarationKind...) -> Bool { 37 | guard let value: String = get(.kind), let kind = SwiftDeclarationKind(rawValue: value) else { 38 | return false 39 | } 40 | return kinds.contains(kind) 41 | } 42 | } 43 | 44 | protocol SwiftDocInitializable { 45 | var dictionary: SwiftDoc { get } 46 | 47 | init?(dictionary: SwiftDoc) 48 | } 49 | 50 | extension SwiftDocInitializable { 51 | var docs: String? { 52 | dictionary.get(.documentationComment) 53 | } 54 | 55 | var declaration: String? { 56 | dictionary.get(.parsedDeclaration) 57 | } 58 | 59 | var subscructure: [SwiftDoc] { 60 | dictionary.get(.substructure) ?? [] 61 | } 62 | 63 | var isTopLevelDeclaration: Bool { 64 | guard let value: String = dictionary.get(.kind) else { return false } 65 | let kind = SwiftDeclarationKind(rawValue: value) 66 | switch kind { 67 | case .struct, .class, .enum, .protocol, .extension, .extensionClass, .extensionEnum, .extensionProtocol, .extensionStruct: 68 | return true 69 | default: 70 | return false 71 | } 72 | } 73 | } 74 | 75 | struct ModuleElement: SwiftDocInitializable { 76 | let dictionary: SwiftDoc 77 | 78 | init?(dictionary: SwiftDoc) { 79 | self.dictionary = dictionary 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Sources/ModuleInterface/Commands/Clean.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright [2019] [Jorge Revuelta Herrero] 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import Commandant 18 | import Curry 19 | import Foundation 20 | import Rainbow 21 | 22 | struct CleanCommandOptions: OptionsProtocol { 23 | let outputFolder: String 24 | let moduleName: String 25 | 26 | static func evaluate(_ mode: CommandMode) -> Result> { 27 | curry(self.init) 28 | <*> mode <| Option(key: "output-folder", defaultValue: ModuleInterface.defaultOutputPath, usage: "Output directory (defaults to \(ModuleInterface.defaultOutputPath)).") 29 | <*> mode <| Argument(usage: "The module's interface name to clean.") 30 | } 31 | } 32 | 33 | struct CleanCommand: CommandProtocol { 34 | typealias Options = CleanCommandOptions 35 | 36 | let verb: String = "clean" 37 | let function: String = "Deletes the module's interface and quits." 38 | 39 | func run(_ options: CleanCommandOptions) -> Result { 40 | do { 41 | try Self.removeModuleInterface(path: options.outputFolder, module: options.moduleName) 42 | return .success(()) 43 | } catch { 44 | return .failure(.default(error.localizedDescription)) 45 | } 46 | } 47 | 48 | static func removeModuleInterface(path: String, module: String) throws { 49 | let file = path + "/" + (module.contains(".swift") ? module : module + ".swift") 50 | if FileManager.default.fileExists(atPath: file) { 51 | fputs("Removing module interface at \(file)".green, stdout) 52 | do { 53 | try FileManager.default.removeItem(atPath: file) 54 | fputs("\t✅\n", stdout) 55 | } catch { 56 | fputs("\t❌\n", stdout) 57 | throw error 58 | } 59 | } else { 60 | fputs("Did not find \(module) at \(path) to delete.\n".cyan, stdout) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/ModuleInterface/Commands/Generate.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright [2019] [Jorge Revuelta Herrero] 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import Commandant 18 | import Curry 19 | import Foundation 20 | import Rainbow 21 | import SourceKittenFramework 22 | import SwiftFormat 23 | 24 | struct GenerateCommandOptions: OptionsProtocol { 25 | let spmModule: String? 26 | let moduleName: String? 27 | let inputFolder: String 28 | let outputFolder: String 29 | let minimumAccessLevel: String 30 | let clean: Bool 31 | let xcodeArguments: [String] 32 | 33 | static func evaluate(_ mode: CommandMode) -> Result> { 34 | curry(self.init) 35 | <*> mode <| Option(key: "spm-module", 36 | defaultValue: nil, 37 | usage: "Generate documentation for Swift Package Manager module.") 38 | <*> mode <| Option(key: "module-name", 39 | defaultValue: nil, 40 | usage: "Generate documentation for a Swift module.") 41 | <*> mode <| Option(key: "input-folder", 42 | defaultValue: FileManager.default.currentDirectoryPath, 43 | usage: "Path to the input directory (defaults to \(FileManager.default.currentDirectoryPath)).") 44 | <*> mode <| Option(key: "output-folder", 45 | defaultValue: ModuleInterface.defaultOutputPath, 46 | usage: "Output directory (defaults to \(ModuleInterface.defaultOutputPath)).") 47 | <*> mode <| Option(key: "min-acl", 48 | defaultValue: AccessLevel.public.rawValue, 49 | usage: "The minimum access level to generate documentation. Defaults to \(AccessLevel.public.rawValue).") 50 | <*> mode <| Switch(flag: "c", 51 | key: "clean", 52 | usage: "Delete output folder before generating documentation.") 53 | <*> mode <| Argument(defaultValue: [], 54 | usage: "List of arguments to pass to xcodebuild.") 55 | } 56 | } 57 | 58 | struct GenerateCommand: CommandProtocol { 59 | typealias Options = GenerateCommandOptions 60 | 61 | let verb: String = "generate" 62 | let function: String = "Generates the Swift Module Interface." 63 | 64 | func run(_ options: GenerateCommandOptions) -> Result { 65 | do { 66 | if let module = options.spmModule { 67 | let docs = try parseSPMModule(moduleName: module) 68 | try generateDocumentation(docs: docs, options: options, module: module) 69 | } else if let module = options.moduleName { 70 | let docs = try parseSwiftModule(moduleName: module, args: options.xcodeArguments, 71 | path: options.inputFolder) 72 | try generateDocumentation(docs: docs, options: options, module: module) 73 | } else { 74 | let docs = try parseXcodeProject(args: options.xcodeArguments, inputPath: options.inputFolder) 75 | try generateDocumentation(docs: docs, options: options, module: "") 76 | } 77 | return Result.success(()) 78 | } catch let error as ModuleInterface.Error { 79 | return Result.failure(error) 80 | } catch { 81 | return Result.failure(ModuleInterface.Error.default(error.localizedDescription)) 82 | } 83 | } 84 | 85 | private func interfaceForModule(_ module: String, compilerArguments: [String]) throws -> [String: SourceKitRepresentable] { 86 | try Request.customRequest(request: [ 87 | "key.request": UID("source.request.editor.open.interface"), 88 | "key.name": NSUUID().uuidString, 89 | "key.compilerargs": compilerArguments, 90 | "key.modulename": "\(module)", 91 | ]).send() 92 | } 93 | 94 | private func parseSPMModule(moduleName: String) throws -> [String: SourceKitRepresentable] { 95 | guard let module = Module(spmName: moduleName) else { 96 | let message = "Error: Failed to generate documentation for SPM module '\(moduleName)'." 97 | throw ModuleInterface.Error.default(message) 98 | } 99 | return try interfaceForModule(module.name, compilerArguments: module.compilerArguments) 100 | } 101 | 102 | private func parseSwiftModule(moduleName: String, args: [String], path: String) throws -> [String: SourceKitRepresentable] { 103 | guard let module = Module(xcodeBuildArguments: args, name: moduleName, inPath: path) else { 104 | let message = "Error: Failed to generate documentation for module '\(moduleName)'." 105 | throw ModuleInterface.Error.default(message) 106 | } 107 | return try interfaceForModule(module.name, compilerArguments: module.compilerArguments) 108 | } 109 | 110 | private func parseXcodeProject(args: [String], inputPath: String) throws -> [String: SourceKitRepresentable] { 111 | guard let module = Module(xcodeBuildArguments: args, name: nil, inPath: inputPath) else { 112 | throw ModuleInterface.Error.default("Error: Failed to generate documentation.") 113 | } 114 | return try interfaceForModule(module.name, compilerArguments: module.compilerArguments) 115 | } 116 | 117 | fileprivate func documentate(_ sourcetext: String, module: String, options: GenerateCommandOptions) throws { 118 | let output = sourcetext 119 | try createDirectory(path: options.outputFolder) 120 | try write(to: options.outputFolder, module: module, documentation: output) 121 | } 122 | 123 | private func generateDocumentation(docs: [String: SourceKitRepresentable], options: GenerateCommandOptions, module: String) throws { 124 | if options.clean { 125 | try CleanCommand.removeModuleInterface(path: options.outputFolder, module: module) 126 | } 127 | if let sourcetext = docs["key.sourcetext"] as? String { 128 | try documentate(sourcetext, module: module, options: options) 129 | } else if let dictionary = docs["key.annotations"] as? SwiftDoc, let sourcetext = process(dictionary: dictionary, options: options) { 130 | try documentate(sourcetext, module: module, options: options) 131 | } else { 132 | let message = "Unable to parse module named \(module)" 133 | throw ModuleInterface.Error.default(message) 134 | } 135 | } 136 | 137 | private func write(to path: String, module: String, documentation: String) throws { 138 | fputs("Generating Module Interface...\n".green, stdout) 139 | let fileName = module.isEmpty ? "Unknown" : module 140 | let fileExtension = ".swift" 141 | let contents = documentation 142 | try contents.write(toFile: path + "/" + fileName + fileExtension, atomically: true, encoding: .utf8) 143 | fputs("Fomatting...\n".green, stdout) 144 | let formatted = try format(contents) 145 | try formatted.write(toFile: path + "/" + fileName + fileExtension, atomically: true, encoding: .utf8) 146 | fputs("Done 🎉\n".green, stdout) 147 | } 148 | 149 | private func createDirectory(path: String) throws { 150 | guard !path.isEmpty else { 151 | return 152 | } 153 | if !FileManager.default.fileExists(atPath: path) { 154 | try FileManager.default.createDirectory(atPath: path, 155 | withIntermediateDirectories: true, 156 | attributes: nil) 157 | } 158 | } 159 | 160 | private func process(docs: [SwiftDocs], options: GenerateCommandOptions) -> [String] { 161 | let dictionaries = docs.compactMap { $0.docsDictionary.bridge() as? SwiftDoc } 162 | return process(dictionaries: dictionaries, options: options) 163 | } 164 | 165 | private func process(dictionaries: [SwiftDoc], options: GenerateCommandOptions) -> [String] { 166 | dictionaries.compactMap { process(dictionary: $0, options: options) } 167 | } 168 | 169 | private func process(dictionary: SwiftDoc, options: GenerateCommandOptions) -> String? { 170 | var doc: String = "" 171 | if dictionary.keys.contains(SwiftDocKey.kind.rawValue) { 172 | if let value: String = dictionary.get(.kind) { 173 | if case .enumcase = SwiftDeclarationKind(rawValue: value), let substructure = dictionary[SwiftDocKey.substructure.rawValue] as? [SwiftDoc] { 174 | return zip(substructure, substructure).compactMap { (decl: SwiftDoc, doc: SwiftDoc) -> String? in 175 | if let declaration: String = decl.get(.parsedDeclaration), let doc: String = doc.get(.documentationComment) { 176 | let formattedDoc = doc.split(separator: "\n").map { "/// \($0)" }.joined(separator: "\n") 177 | return "/// \(formattedDoc)\n\(declaration)" 178 | } 179 | return nil 180 | } 181 | .joined(separator: "\n") 182 | } 183 | } 184 | let minimumAccessLevel = AccessLevel(rawValue: options.minimumAccessLevel) ?? .public 185 | guard let element = ModuleElement(dictionary: dictionary) else { return nil } 186 | guard element.dictionary.accessLevel >= minimumAccessLevel else { return nil } 187 | guard let declaration = element.declaration else { return nil } 188 | // https://github.com/jpsim/SourceKitten/issues/633 189 | guard isBalanced(sequence: declaration) else { return nil } 190 | 191 | // Step 1: Documentation 192 | if let docs = element.docs { 193 | doc += docs.split(separator: "\n").map { "/// \($0)" }.joined(separator: "\n") 194 | doc += "\n" 195 | } 196 | // Step 2: Declaration 197 | fputs("\(declaration) ✔\n".green, stdout) 198 | doc += declaration 199 | doc += "\n" 200 | // Step 3: Substructure 201 | if !element.subscructure.isEmpty, element.isTopLevelDeclaration { 202 | doc += " {\n" 203 | doc += process(dictionaries: element.subscructure, options: options).joined(separator: "\n") 204 | doc += "\n}\n" 205 | } 206 | return doc 207 | } 208 | if let substructure = dictionary[SwiftDocKey.substructure.rawValue] as? [SwiftDoc] { 209 | return process(dictionaries: substructure, options: options).joined(separator: "\n") + "\n" 210 | } 211 | return nil 212 | } 213 | 214 | private func isBalanced(sequence: String) -> Bool { 215 | var stack = [Character]() 216 | for bracket in sequence { 217 | switch bracket { 218 | case "{", "[", "(": 219 | stack.append(bracket) 220 | case "}", "]", ")": 221 | if stack.isEmpty 222 | || (bracket == "}" && stack.last != "{") 223 | || (bracket == "]" && stack.last != "[") 224 | || (bracket == ")" && stack.last != "(") { 225 | return false 226 | } 227 | stack.removeLast() 228 | default: 229 | break 230 | } 231 | } 232 | return stack.isEmpty 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /Sources/ModuleInterface/Commands/Version.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright [2019] [Jorge Revuelta Herrero] 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import Commandant 18 | import Foundation 19 | import Rainbow 20 | 21 | /// Type that encapsulates the configuration and evaluation of the `version` subcommand. 22 | struct VersionCommand: CommandProtocol { 23 | typealias ClientError = ModuleInterface.Error 24 | 25 | let verb = "version" 26 | let function = "Display the current version of ModuleInterface" 27 | 28 | func run(_: NoOptions) -> Result { 29 | fputs("ModuleInterface v\(ModuleInterface.version)\n".cyan, stdout) 30 | return .success(()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/ModuleInterface/main.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright [2019] [Jorge Revuelta Herrero] 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | ModuleInterface().run() 18 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import ModuleInterfaceTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += ModuleInterfaceTests.__allTests() 7 | 8 | XCTMain(tests) 9 | -------------------------------------------------------------------------------- /Tests/ModuleInterfaceTests/Helpers.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Foundation 3 | 4 | extension XCTestCase { 5 | /// Path to the built products directory. 6 | var productsDirectory: URL { 7 | #if os(macOS) 8 | for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") { 9 | return bundle.bundleURL.deletingLastPathComponent() 10 | } 11 | fatalError("couldn't find the products directory") 12 | #else 13 | return Bundle.main.bundleURL 14 | #endif 15 | } 16 | 17 | /// Path to binary executable. 18 | var binaryURL: URL { 19 | return productsDirectory.appendingPathComponent("moduleinterface") 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Tests/ModuleInterfaceTests/VersionTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Foundation 3 | 4 | class VersionCommandTests: XCTestCase { 5 | 6 | func testVersion() throws { 7 | let process = Process() 8 | let pipe = Pipe() 9 | process.launchPath = binaryURL.path 10 | process.arguments = ["version"] 11 | process.standardOutput = pipe 12 | if #available(OSX 10.13, *) { 13 | try process.run() 14 | } else { 15 | process.waitUntilExit() 16 | } 17 | let outputData = pipe.fileHandleForReading.readDataToEndOfFile() 18 | let output = String(decoding: outputData, as: UTF8.self).trimmingCharacters(in: .newlines) 19 | XCTAssertEqual(output, "ModuleInterface v0.0.4") 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Tests/ModuleInterfaceTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | #if !canImport(ObjectiveC) 2 | import XCTest 3 | 4 | extension VersionCommandTests { 5 | // DO NOT MODIFY: This is autogenerated, use: 6 | // `swift test --generate-linuxmain` 7 | // to regenerate. 8 | static let __allTests__VersionCommandTests = [ 9 | ("testVersion", testVersion), 10 | ] 11 | } 12 | 13 | public func __allTests() -> [XCTestCaseEntry] { 14 | return [ 15 | testCase(VersionCommandTests.__allTests__VersionCommandTests), 16 | ] 17 | } 18 | #endif 19 | --------------------------------------------------------------------------------