├── .dockerignore ├── .github ├── dependabot.yml └── workflows │ ├── build.yml │ ├── docker.yml │ ├── release.yml │ ├── validate_pr.yml │ └── windows_release.yml ├── .gitignore ├── .pre-commit-hooks.yaml ├── .swiftformat ├── CHANGELOG.md ├── CONTRIBUTING.md ├── CommandLineTool ├── main.swift └── swiftformat ├── Dockerfile ├── EditorExtension ├── Application │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── icon_128x128.png │ │ │ ├── icon_128x128@2x.png │ │ │ ├── icon_16x16.png │ │ │ ├── icon_16x16@2x.png │ │ │ ├── icon_256x256.png │ │ │ ├── icon_256x256@2x.png │ │ │ ├── icon_32x32.png │ │ │ ├── icon_32x32@2x.png │ │ │ ├── icon_512x512.png │ │ │ └── icon_512x512@2x.png │ │ └── Contents.json │ ├── Base.lproj │ │ └── Main.storyboard │ ├── Info.plist │ ├── Source │ │ ├── AboutViewController.swift │ │ ├── AppDelegate.swift │ │ ├── BinarySelectionTableCellView.swift │ │ ├── FreeTextTableCellView.swift │ │ ├── ListSelectionTableCellView.swift │ │ ├── RulesViewController.swift │ │ └── UserSelection.swift │ └── SwiftFormatter.entitlements ├── Extension │ ├── CommandErrors.swift │ ├── FormatFileCommand.swift │ ├── FormatSelectionCommand.swift │ ├── Info.plist │ ├── LintFileCommand.swift │ ├── SourceEditorExtension.swift │ ├── SupportedContentUTIs.swift │ ├── SwiftFormatter.entitlements │ └── XCSourceTextBuffer+SwiftFormat.swift └── Shared │ ├── OptionsStore.swift │ └── RulesStore.swift ├── LICENSE.md ├── LinuxMain.swift ├── Package.swift ├── PerformanceTests └── PerformanceTests.swift ├── Platforms └── Windows │ ├── SwiftFormat.wixproj │ ├── SwiftFormat.wxs │ └── WixCodeSigning.targets ├── PluginTests ├── .gitignore ├── Package.swift ├── README.md ├── Sources │ └── PackageUsingPlugin │ │ └── NotFormattedFile.swift └── test.sh ├── Plugins └── SwiftFormatPlugin │ ├── Shared │ ├── CommandPlugin+Extension.swift │ └── PluginToolProviding.swift │ ├── SwiftFormatPlugin.swift │ └── SwiftFormatPluginXcode.swift ├── README.md ├── Rules.md ├── Scripts ├── build-linux-release.sh ├── get-version.sh ├── spm-artifact-bundle-info.template └── spm-artifact-bundle.sh ├── Snapshots ├── .swiftformat ├── Async │ ├── Actors.swift │ ├── AsyncAwait.swift │ ├── AsyncLet.swift │ ├── AsyncSequences.swift │ ├── EffectfulProperties.swift │ ├── PropertyWrappers.swift │ ├── README.md │ └── Sendable.swift ├── Config │ ├── .swiftformat │ ├── child │ │ ├── .swiftformat │ │ └── test.swift │ └── test.swift ├── Consumer │ ├── .swift-version │ ├── .swiftformat │ ├── Examples │ │ ├── JSON │ │ │ ├── compiled.swift │ │ │ ├── handwritten.swift │ │ │ ├── json.swift │ │ │ ├── main.swift │ │ │ └── shared.swift │ │ └── REPL │ │ │ ├── main.swift │ │ │ └── repl.swift │ ├── LICENSE.md │ ├── LinuxMain.swift │ ├── Package.swift │ ├── README.md │ ├── Sources │ │ ├── Consumer.h │ │ ├── Consumer.swift │ │ └── ConsumerCompiler.swift │ └── Tests │ │ ├── CompilerTests.swift │ │ ├── ConsumerTests.swift │ │ ├── PerformanceTests.swift │ │ └── XCTestManifests.swift ├── Euclid │ ├── .swift-version │ ├── .swiftformat │ ├── Example │ │ ├── AppDelegate.swift │ │ └── SceneViewController.swift │ ├── LICENSE.md │ ├── LinuxMain.swift │ ├── Package.swift │ ├── README.md │ ├── Sources │ │ ├── Bounds.swift │ │ ├── CSG.swift │ │ ├── CoreGraphics.swift │ │ ├── CoreText.swift │ │ ├── Euclid.h │ │ ├── Mesh.swift │ │ ├── Paths.swift │ │ ├── Plane.swift │ │ ├── Polygon.swift │ │ ├── SceneKit.swift │ │ ├── Shapes.swift │ │ ├── Transforms.swift │ │ ├── Utilities.swift │ │ ├── Vector.swift │ │ └── Vertex.swift │ └── Tests │ │ ├── CGPathTests.swift │ │ ├── CSGTests.swift │ │ ├── PathTests.swift │ │ ├── PlaneTests.swift │ │ ├── PolygonTests.swift │ │ ├── ShapeTests.swift │ │ ├── TextTests.swift │ │ ├── TransformTests.swift │ │ ├── UtilityTests.swift │ │ └── XCTestManifests.swift ├── Expression │ ├── .swift-version │ ├── .swiftformat │ ├── Benchmarks │ │ ├── BenchmarkUtils.swift │ │ ├── Benchmarks.swift │ │ └── PerformanceTests.swift │ ├── Examples │ │ ├── Benchmark │ │ │ ├── AppDelegate.swift │ │ │ └── ViewController.swift │ │ ├── Calculator │ │ │ ├── AppDelegate.swift │ │ │ └── ViewController.swift │ │ ├── Colors │ │ │ ├── AppDelegate.swift │ │ │ ├── ColorLabel.swift │ │ │ ├── UIColor+Expression.swift │ │ │ └── ViewController.swift │ │ ├── Layout │ │ │ ├── AppDelegate.swift │ │ │ ├── View+Layout.swift │ │ │ └── ViewController.swift │ │ └── REPL │ │ │ └── main.swift │ ├── LICENSE.md │ ├── LinuxMain.swift │ ├── Package.swift │ ├── README.md │ ├── Sources │ │ ├── AnyExpression.swift │ │ └── Expression.swift │ └── Tests │ │ ├── AnyExpressionTests.swift │ │ └── ExpressionTests.swift ├── Issues │ ├── 1061.swift │ ├── 1129.swift │ ├── 1183.swift │ ├── 1184.swift │ ├── 1201.swift │ ├── 1202.swift │ ├── 1205.swift │ ├── 1212.swift │ ├── 1254.swift │ ├── 1259.swift │ ├── 1269.swift │ ├── 1319.swift │ ├── 1491.swift │ ├── 1582.swift │ ├── 1644.swift │ ├── 406.swift │ ├── 411.swift │ ├── 637.swift │ ├── 645.swift │ ├── 678.swift │ ├── 681.swift │ ├── 682.swift │ ├── 687.swift │ ├── 697.swift │ ├── 780.swift │ └── 794.swift ├── Layout │ ├── .swift-version │ ├── .swiftformat │ ├── Benchmark │ │ ├── AppDelegate.swift │ │ └── ViewController.swift │ ├── EditorExtension │ │ ├── Application │ │ │ ├── AppDelegate.swift │ │ │ └── ViewController.swift │ │ └── Extension │ │ │ ├── FormatXMLCommand.swift │ │ │ └── SourceEditorExtension.swift │ ├── LICENSE.md │ ├── Layout │ │ ├── .swiftformat │ │ ├── CALayer+Layout.swift │ │ ├── Layout+Testing.swift │ │ ├── Layout+XML.swift │ │ ├── Layout.swift │ │ ├── LayoutBacked.swift │ │ ├── LayoutConsole.swift │ │ ├── LayoutDelegate.swift │ │ ├── LayoutError+LayoutNode.swift │ │ ├── LayoutError.swift │ │ ├── LayoutExpression.swift │ │ ├── LayoutLoader.swift │ │ ├── LayoutLoading.swift │ │ ├── LayoutManaged.swift │ │ ├── LayoutNode+Layout.swift │ │ ├── LayoutNode+XML.swift │ │ ├── LayoutNode.swift │ │ ├── LayoutViewController.swift │ │ ├── Properties.swift │ │ ├── ReloadManager.swift │ │ ├── RuntimeType.swift │ │ ├── RuntimeTypes.swift │ │ ├── Shared │ │ │ ├── ExpressionParser.swift │ │ │ ├── Files.swift │ │ │ ├── Shared.swift │ │ │ ├── StringUtils.swift │ │ │ ├── XMLNode+Layout.swift │ │ │ └── XMLParser.swift │ │ ├── TitleTextAttributes.swift │ │ ├── UICollectionView+Layout.swift │ │ ├── UIFont+Layout.swift │ │ ├── UIScrollView+Layout.swift │ │ ├── UIStackView+Layout.swift │ │ ├── UITableView+Layout.swift │ │ ├── UIView+Layout.swift │ │ ├── UIViewController+Layout.swift │ │ └── Utilities.swift │ ├── LayoutTests │ │ ├── ArrayExpressionTests.swift │ │ ├── AttributedStringExpressionTests.swift │ │ ├── ColorExpressionTests.swift │ │ ├── EnumExpressionTests.swift │ │ ├── FileTests.swift │ │ ├── FontExpressionTests.swift │ │ ├── GeometryExpressionTests.swift │ │ ├── ImageExpressionTests.swift │ │ ├── LayoutExpressionTests.swift │ │ ├── LayoutFrameTests.swift │ │ ├── LayoutMountingTests.swift │ │ ├── LayoutNodeTests.swift │ │ ├── Loading │ │ │ ├── LayoutLoaderTests.swift │ │ │ ├── LayoutViewControllerTests.swift │ │ │ └── ReloadManagerTests.swift │ │ ├── OptionSetExpressionTests.swift │ │ ├── OptionalExpressionTests.swift │ │ ├── PropertiesTests.swift │ │ ├── RuntimeTypeTests.swift │ │ ├── SelectorExpressionTests.swift │ │ ├── StackViewTests.swift │ │ ├── StateTests.swift │ │ ├── StringConstantTests.swift │ │ ├── TableViewTests.swift │ │ ├── UtilitiesTests.swift │ │ └── XMLTests.swift │ ├── LayoutTool │ │ ├── Common.swift │ │ ├── Formatter.swift │ │ ├── Renamer.swift │ │ ├── Strings.swift │ │ └── main.swift │ ├── LayoutToolTests │ │ ├── FormatterTests.swift │ │ ├── RenamerTests.swift │ │ ├── ReturnCodeTests.swift │ │ ├── StringsTests.swift │ │ └── ValidationTests.swift │ ├── PerformanceTests │ │ └── PerformanceTests.swift │ ├── README.md │ ├── SampleApp │ │ ├── AppDelegate.swift │ │ ├── BoxesViewController.swift │ │ ├── CollectionViewController.swift │ │ ├── ExamplesViewController.swift │ │ ├── PagesViewController.swift │ │ ├── TableViewController.swift │ │ └── UIColor+Hex.swift │ ├── Sandbox │ │ ├── AppDelegate.swift │ │ └── EditViewController.swift │ ├── TestFramework │ │ └── TestFramework.swift │ ├── UIDesigner │ │ ├── AppDelegate.swift │ │ ├── DesignViewController.swift │ │ ├── EditViewController.swift │ │ └── TreeViewController.swift │ └── UIKitSymbols │ │ └── UIKitSymbols.swift ├── Macros │ ├── MacroExamples.swift │ ├── Macros.swift │ └── OptionSetMacro.swift ├── Parsing │ ├── .swiftformat │ ├── LICENSE.md │ ├── README.md │ ├── Sources │ │ ├── Formatter.swift │ │ ├── Interpreter.swift │ │ ├── Lexer.swift │ │ ├── Parser.swift │ │ ├── Parsing.h │ │ └── Transpiler.swift │ └── Tests │ │ ├── FormatterTests.swift │ │ ├── InterpreterTests.swift │ │ ├── LexerTests.swift │ │ ├── ParserTests.swift │ │ └── TranspilerTests.swift ├── README.md ├── Sprinter │ ├── .swift-version │ ├── .swiftformat │ ├── LICENSE.md │ ├── Package.swift │ ├── README.md │ ├── Sources │ │ └── Sprinter.swift │ └── Tests │ │ ├── PerformanceTests.swift │ │ └── SprinterTests.swift └── SwiftUI │ ├── .swiftformat │ ├── Bookworm │ ├── AddBookView.swift │ ├── ContentView.swift │ ├── DetailView.swift │ ├── EmojiRatingView.swift │ └── RatingView.swift │ ├── CupcakeCorner │ ├── AddressView.swift │ ├── CheckoutView.swift │ ├── ContentView.swift │ └── Order.swift │ ├── GuessTheFlag │ └── ContentView.swift │ ├── Moonshot │ ├── Astronaut.swift │ ├── AstronautView.swift │ ├── Bundle+Decodable.swift │ ├── ContentView.swift │ ├── Mission.swift │ └── MissionView.swift │ ├── README.md │ └── WordScramble │ └── ContentView.swift ├── Sources ├── Arguments.swift ├── CommandLine.swift ├── Declaration.swift ├── DeclarationType.swift ├── EnumAssociable.swift ├── FormatRule.swift ├── Formatter.swift ├── FormattingHelpers.swift ├── GitFileInfo.swift ├── GithubActionsLogReporter.swift ├── Globs.swift ├── Inference.swift ├── Info.plist ├── JSONReporter.swift ├── OptionDescriptor.swift ├── Options.swift ├── ParsingHelpers.swift ├── Reporter.swift ├── RuleRegistry.generated.swift ├── Rules │ ├── Acronyms.swift │ ├── AndOperator.swift │ ├── AnyObjectProtocol.swift │ ├── ApplicationMain.swift │ ├── AssertionFailures.swift │ ├── BlankLineAfterImports.swift │ ├── BlankLineAfterSwitchCase.swift │ ├── BlankLinesAfterGuardStatements.swift │ ├── BlankLinesAroundMark.swift │ ├── BlankLinesAtEndOfScope.swift │ ├── BlankLinesAtStartOfScope.swift │ ├── BlankLinesBetweenChainedFunctions.swift │ ├── BlankLinesBetweenImports.swift │ ├── BlankLinesBetweenScopes.swift │ ├── BlockComments.swift │ ├── Braces.swift │ ├── ConditionalAssignment.swift │ ├── ConsecutiveBlankLines.swift │ ├── ConsecutiveSpaces.swift │ ├── ConsistentSwitchCaseSpacing.swift │ ├── DocComments.swift │ ├── DocCommentsBeforeModifiers.swift │ ├── DuplicateImports.swift │ ├── ElseOnSameLine.swift │ ├── EmptyBraces.swift │ ├── EmptyExtensions.swift │ ├── EnumNamespaces.swift │ ├── EnvironmentEntry.swift │ ├── ExtensionAccessControl.swift │ ├── FileHeader.swift │ ├── FileMacro.swift │ ├── GenericExtensions.swift │ ├── HeaderFileName.swift │ ├── HoistAwait.swift │ ├── HoistPatternLet.swift │ ├── HoistTry.swift │ ├── Indent.swift │ ├── InitCoderUnavailable.swift │ ├── IsEmpty.swift │ ├── LeadingDelimiters.swift │ ├── LinebreakAtEndOfFile.swift │ ├── Linebreaks.swift │ ├── MarkTypes.swift │ ├── ModifierOrder.swift │ ├── NoExplicitOwnership.swift │ ├── NumberFormatting.swift │ ├── OpaqueGenericParameters.swift │ ├── OrganizeDeclarations.swift │ ├── PreferCountWhere.swift │ ├── PreferForLoop.swift │ ├── PreferKeyPath.swift │ ├── PreferSwiftTesting.swift │ ├── PrivateStateVariables.swift │ ├── PropertyTypes.swift │ ├── RedundantBackticks.swift │ ├── RedundantBreak.swift │ ├── RedundantClosure.swift │ ├── RedundantEquatable.swift │ ├── RedundantExtensionACL.swift │ ├── RedundantFileprivate.swift │ ├── RedundantGet.swift │ ├── RedundantInit.swift │ ├── RedundantInternal.swift │ ├── RedundantLet.swift │ ├── RedundantLetError.swift │ ├── RedundantNilInit.swift │ ├── RedundantObjc.swift │ ├── RedundantOptionalBinding.swift │ ├── RedundantParens.swift │ ├── RedundantPattern.swift │ ├── RedundantProperty.swift │ ├── RedundantRawValues.swift │ ├── RedundantReturn.swift │ ├── RedundantSelf.swift │ ├── RedundantStaticSelf.swift │ ├── RedundantType.swift │ ├── RedundantTypedThrows.swift │ ├── RedundantVoidReturnType.swift │ ├── Semicolons.swift │ ├── SortDeclarations.swift │ ├── SortImports.swift │ ├── SortSwitchCases.swift │ ├── SortTypealiases.swift │ ├── SortedImports.swift │ ├── SortedSwitchCases.swift │ ├── SpaceAroundBraces.swift │ ├── SpaceAroundBrackets.swift │ ├── SpaceAroundComments.swift │ ├── SpaceAroundGenerics.swift │ ├── SpaceAroundOperators.swift │ ├── SpaceAroundParens.swift │ ├── SpaceInsideBraces.swift │ ├── SpaceInsideBrackets.swift │ ├── SpaceInsideComments.swift │ ├── SpaceInsideGenerics.swift │ ├── SpaceInsideParens.swift │ ├── Specifiers.swift │ ├── StrongOutlets.swift │ ├── StrongifiedSelf.swift │ ├── SwiftTestingTestCaseNames.swift │ ├── Todos.swift │ ├── TrailingClosures.swift │ ├── TrailingCommas.swift │ ├── TrailingSpace.swift │ ├── TypeSugar.swift │ ├── UnusedArguments.swift │ ├── UnusedPrivateDeclarations.swift │ ├── Void.swift │ ├── Wrap.swift │ ├── WrapArguments.swift │ ├── WrapAttributes.swift │ ├── WrapConditionalBodies.swift │ ├── WrapEnumCases.swift │ ├── WrapLoopBodies.swift │ ├── WrapMultilineConditionalAssignment.swift │ ├── WrapMultilineFunctionChains.swift │ ├── WrapMultilineStatementBraces.swift │ ├── WrapSingleLineComments.swift │ ├── WrapSwitchCases.swift │ └── YodaConditions.swift ├── Singularize.swift ├── SwiftFormat.h ├── SwiftFormat.swift ├── Tokenizer.swift └── XMLReporter.swift ├── SwiftFormat.podspec.json ├── SwiftFormat.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ ├── xcbaselines │ └── 015AF2B61DC6A538008F0A8C.xcbaseline │ │ ├── 07379008-D25E-40A7-9941-B249EB34763C.plist │ │ ├── 1F44D368-293A-49E8-B611-DF6CA453D87D.plist │ │ ├── B44E1705-A040-459A-9DD0-A29BA8CF0A9F.plist │ │ └── Info.plist │ └── xcschemes │ ├── Performance Tests.xcscheme │ ├── SwiftFormat (Command Line Tool).xcscheme │ ├── SwiftFormat (Editor Extension).xcscheme │ ├── SwiftFormat (Framework).xcscheme │ └── SwiftFormat for Xcode.xcscheme ├── Tests ├── ArgumentsTests.swift ├── BadConfig │ ├── .swiftformat │ └── Test.swift ├── CodeOrganizationTests.swift ├── CommandLineTests.swift ├── DeclarationV2Tests.swift ├── EnumAssociableTests.swift ├── FormatterTests.swift ├── GlobTest[5].txt ├── GlobsTests.swift ├── InferenceTests.swift ├── MetadataTests.swift ├── OptionDescriptorTests.swift ├── ParsingHelpersTests.swift ├── ProjectFilePaths.swift ├── ReporterTests.swift ├── Rules │ ├── AcronymsTests.swift │ ├── AndOperatorTests.swift │ ├── AnyObjectProtocolTests.swift │ ├── ApplicationMainTests.swift │ ├── AssertionFailuresTests.swift │ ├── BlankLineAfterImportsTests.swift │ ├── BlankLineAfterSwitchCaseTests.swift │ ├── BlankLinesAfterGuardStatementsTests.swift │ ├── BlankLinesAroundMarkTests.swift │ ├── BlankLinesAtEndOfScopeTests.swift │ ├── BlankLinesAtStartOfScopeTests.swift │ ├── BlankLinesBetweenChainedFunctionsTests.swift │ ├── BlankLinesBetweenImportsTests.swift │ ├── BlankLinesBetweenScopesTests.swift │ ├── BlockCommentsTests.swift │ ├── BracesTests.swift │ ├── ConditionalAssignmentTests.swift │ ├── ConsecutiveBlankLinesTests.swift │ ├── ConsecutiveSpacesTests.swift │ ├── ConsistentSwitchCaseSpacingTests.swift │ ├── DocCommentsBeforeModifiersTests.swift │ ├── DocCommentsTests.swift │ ├── DuplicateImportsTests.swift │ ├── ElseOnSameLineTests.swift │ ├── EmptyBracesTests.swift │ ├── EmptyExtensionsTests.swift │ ├── EnumNamespacesTests.swift │ ├── EnvironmentEntryTests.swift │ ├── ExtensionAccessControlTests.swift │ ├── FileHeaderTests.swift │ ├── FileMacroTests.swift │ ├── GenericExtensionsTests.swift │ ├── HeaderFileNameTests.swift │ ├── HoistAwaitTests.swift │ ├── HoistPatternLetTests.swift │ ├── HoistTryTests.swift │ ├── IndentTests.swift │ ├── InitCoderUnavailableTests.swift │ ├── IsEmptyTests.swift │ ├── LeadingDelimitersTests.swift │ ├── LinebreakAtEndOfFileTests.swift │ ├── LinebreaksTests.swift │ ├── MarkTypesTests.swift │ ├── ModifierOrderTests.swift │ ├── NoExplicitOwnershipTests.swift │ ├── NumberFormattingTests.swift │ ├── OpaqueGenericParametersTests.swift │ ├── OrganizeDeclarationsTests.swift │ ├── PreferCountWhereTests.swift │ ├── PreferForLoopTests.swift │ ├── PreferKeyPathTests.swift │ ├── PreferSwiftTestingTests.swift │ ├── PrivateStateVariablesTests.swift │ ├── PropertyTypesTests.swift │ ├── RedundantBackticksTests.swift │ ├── RedundantBreakTests.swift │ ├── RedundantClosureTests.swift │ ├── RedundantEquatableTests.swift │ ├── RedundantExtensionACLTests.swift │ ├── RedundantFileprivateTests.swift │ ├── RedundantGetTests.swift │ ├── RedundantInitTests.swift │ ├── RedundantInternalTests.swift │ ├── RedundantLetErrorTests.swift │ ├── RedundantLetTests.swift │ ├── RedundantNilInitTests.swift │ ├── RedundantObjcTests.swift │ ├── RedundantOptionalBindingTests.swift │ ├── RedundantParensTests.swift │ ├── RedundantPatternTests.swift │ ├── RedundantPropertyTests.swift │ ├── RedundantRawValuesTests.swift │ ├── RedundantReturnTests.swift │ ├── RedundantSelfTests.swift │ ├── RedundantStaticSelfTests.swift │ ├── RedundantTypeTests.swift │ ├── RedundantTypedThrowsTests.swift │ ├── RedundantVoidReturnTypeTests.swift │ ├── SemicolonsTests.swift │ ├── SortDeclarationsTests.swift │ ├── SortImportsTests.swift │ ├── SortSwitchCasesTests.swift │ ├── SortTypealiasesTests.swift │ ├── SpaceAroundBracesTests.swift │ ├── SpaceAroundBracketsTests.swift │ ├── SpaceAroundCommentsTests.swift │ ├── SpaceAroundGenericsTests.swift │ ├── SpaceAroundOperatorsTests.swift │ ├── SpaceAroundParensTests.swift │ ├── SpaceInsideBracesTests.swift │ ├── SpaceInsideBracketsTests.swift │ ├── SpaceInsideCommentsTests.swift │ ├── SpaceInsideGenericsTests.swift │ ├── SpaceInsideParensTests.swift │ ├── StrongOutletsTests.swift │ ├── StrongifiedSelfTests.swift │ ├── SwiftTestingTestCaseNamesTests.swift │ ├── TodosTests.swift │ ├── TrailingClosuresTests.swift │ ├── TrailingCommasTests.swift │ ├── TrailingSpaceTests.swift │ ├── TypeSugarTests.swift │ ├── UnusedArgumentsTests.swift │ ├── UnusedPrivateDeclarationsTests.swift │ ├── VoidTests.swift │ ├── WrapArgumentsTests.swift │ ├── WrapAttributesTests.swift │ ├── WrapConditionalBodiesTests.swift │ ├── WrapEnumCasesTests.swift │ ├── WrapLoopBodiesTests.swift │ ├── WrapMultilineConditionalAssignmentTests.swift │ ├── WrapMultilineFunctionChainsTests.swift │ ├── WrapMultilineStatementBracesTests.swift │ ├── WrapSingleLineCommentsTests.swift │ ├── WrapSwitchCasesTests.swift │ ├── WrapTests.swift │ └── YodaConditionsTests.swift ├── SingularizeTests.swift ├── SwiftFormatTests.swift ├── TokenizerTests.swift ├── VersionTests.swift ├── XCTestCase+testFormatting.swift └── ZRegressionTests.swift └── format.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .dockerignore 3 | .git 4 | .github 5 | .gitignore 6 | 7 | # Everything from .gitignore 8 | # OS X 9 | .DS_Store 10 | 11 | # Xcode 12 | build/ 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata 22 | *.xccheckout 23 | *.moved-aside 24 | DerivedData 25 | *.hmap 26 | *.ipa 27 | *.dot 28 | .build 29 | .swiftpm 30 | 31 | /Snapshots/Private 32 | swiftformat.artifactbundle.zip 33 | 34 | # AppCode 35 | .idea 36 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Keep GitHub Actions up to date with GitHub's Dependabot... 2 | # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot 3 | # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem 4 | version: 2 5 | updates: 6 | - package-ecosystem: github-actions 7 | directory: / 8 | groups: 9 | github-actions: 10 | patterns: 11 | - "*" # Group all Actions updates into a single larger pull request 12 | schedule: 13 | interval: weekly 14 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Create and publish a Docker image 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | env: 8 | REGISTRY: ghcr.io 9 | IMAGE_NAME: ${{ github.repository }} 10 | 11 | jobs: 12 | build-and-push-image: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | packages: write 17 | 18 | steps: 19 | - name: Checkout repository 20 | uses: actions/checkout@v4 21 | 22 | - name: Extract metadata (tags, labels) for Docker 23 | id: meta 24 | uses: docker/metadata-action@v5 25 | with: 26 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 27 | 28 | - name: Set up Docker Buildx 29 | id: buildx 30 | uses: docker/setup-buildx-action@v3 31 | 32 | - name: Log in to the Container registry 33 | uses: docker/login-action@v3 34 | with: 35 | registry: ${{ env.REGISTRY }} 36 | username: ${{ github.actor }} 37 | password: ${{ secrets.GITHUB_TOKEN }} 38 | 39 | - name: Build and push Docker image 40 | uses: docker/build-push-action@v6 41 | with: 42 | context: . 43 | platforms: linux/amd64, linux/arm64 44 | no-cache: true 45 | push: true 46 | tags: ${{ steps.meta.outputs.tags }} 47 | labels: ${{ steps.meta.outputs.labels }} 48 | provenance: false 49 | -------------------------------------------------------------------------------- /.github/workflows/validate_pr.yml: -------------------------------------------------------------------------------- 1 | name: "validate" 2 | 3 | on: 4 | pull_request: 5 | types: [opened, edited, synchronize, reopened] 6 | 7 | jobs: 8 | check-pr-base: 9 | name: "Pull request base is 'develop'" 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Fail if PR base is not 'develop' 13 | if: github.event.pull_request.base.ref != 'develop' 14 | run: | 15 | echo "❌ Pull request must target the 'develop' branch. Current base: '${{ github.event.pull_request.base.ref }}'" 16 | exit 1 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | *.moved-aside 17 | DerivedData 18 | *.hmap 19 | *.ipa 20 | *.dot 21 | .build 22 | .swiftpm 23 | 24 | /Snapshots/Private 25 | swiftformat.artifactbundle.zip 26 | 27 | # Stores temporary files during tests 28 | .temp/ 29 | 30 | # AppCode 31 | .idea 32 | -------------------------------------------------------------------------------- /.pre-commit-hooks.yaml: -------------------------------------------------------------------------------- 1 | - id: swiftformat 2 | name: SwiftFormat 3 | description: "Check swift files for formatting issues with SwiftFormat" 4 | entry: swiftformat 5 | language: swift 6 | types: [swift] 7 | -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | # file options 2 | 3 | --exclude Tests/XCTestManifests.swift,Tests/BadConfig,Snapshots,Build,PluginTests 4 | 5 | # format options 6 | 7 | --allman false 8 | --binarygrouping 4,8 9 | --commas always 10 | --decimalgrouping 3,6 11 | --elseposition same-line 12 | --voidtype void 13 | --exponentcase lowercase 14 | --exponentgrouping disabled 15 | --fractiongrouping disabled 16 | --header ignore 17 | --hexgrouping 4,8 18 | --hexliteralcase uppercase 19 | --ifdef indent 20 | --indent 4 21 | --indentcase false 22 | --importgrouping testable-bottom 23 | --linebreaks lf 24 | --maxwidth none 25 | --octalgrouping 4,8 26 | --operatorfunc spaced 27 | --patternlet hoist 28 | --ranges spaced 29 | --self remove 30 | --semicolons inline 31 | --stripunusedargs always 32 | --swiftversion 5.3 # TODO: Update to 5.7 after releasing Swift Format 0.56.0 33 | --trimwhitespace always 34 | --wraparguments preserve 35 | --wrapcollections preserve 36 | --wrapconditions after-first 37 | 38 | # rules 39 | 40 | --enable isEmpty 41 | -------------------------------------------------------------------------------- /CommandLineTool/swiftformat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicklockwood/SwiftFormat/1d02d0f54a5123c3ef67084b318f4421427b7a51/CommandLineTool/swiftformat -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | # Base image and static SDK have to be updated together. 4 | FROM --platform=$BUILDPLATFORM swift:6.0.3 AS builder 5 | WORKDIR /workspace 6 | RUN swift sdk install \ 7 | https://download.swift.org/swift-6.0.3-release/static-sdk/swift-6.0.3-RELEASE/swift-6.0.3-RELEASE_static-linux-0.0.1.artifactbundle.tar.gz \ 8 | --checksum 67f765e0030e661a7450f7e4877cfe008db4f57f177d5a08a6e26fd661cdd0bd 9 | 10 | COPY . /workspace 11 | ARG TARGETPLATFORM 12 | RUN --mount=type=cache,target=/workspace/.build,id=build-$TARGETPLATFORM \ 13 | ./Scripts/build-linux-release.sh && \ 14 | cp /workspace/.build/release/swiftformat /workspace 15 | 16 | # https://github.com/nicklockwood/SwiftFormat/issues/1930 17 | FROM scratch AS runner 18 | COPY --from=builder /workspace/swiftformat /usr/bin/swiftformat 19 | ENTRYPOINT [ "/usr/bin/swiftformat" ] 20 | CMD ["."] 21 | -------------------------------------------------------------------------------- /EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_16x16.png", 5 | "idiom" : "mac", 6 | "scale" : "1x", 7 | "size" : "16x16" 8 | }, 9 | { 10 | "filename" : "icon_16x16@2x.png", 11 | "idiom" : "mac", 12 | "scale" : "2x", 13 | "size" : "16x16" 14 | }, 15 | { 16 | "filename" : "icon_32x32.png", 17 | "idiom" : "mac", 18 | "scale" : "1x", 19 | "size" : "32x32" 20 | }, 21 | { 22 | "filename" : "icon_32x32@2x.png", 23 | "idiom" : "mac", 24 | "scale" : "2x", 25 | "size" : "32x32" 26 | }, 27 | { 28 | "filename" : "icon_128x128.png", 29 | "idiom" : "mac", 30 | "scale" : "1x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "filename" : "icon_128x128@2x.png", 35 | "idiom" : "mac", 36 | "scale" : "2x", 37 | "size" : "128x128" 38 | }, 39 | { 40 | "filename" : "icon_256x256.png", 41 | "idiom" : "mac", 42 | "scale" : "1x", 43 | "size" : "256x256" 44 | }, 45 | { 46 | "filename" : "icon_256x256@2x.png", 47 | "idiom" : "mac", 48 | "scale" : "2x", 49 | "size" : "256x256" 50 | }, 51 | { 52 | "filename" : "icon_512x512.png", 53 | "idiom" : "mac", 54 | "scale" : "1x", 55 | "size" : "512x512" 56 | }, 57 | { 58 | "filename" : "icon_512x512@2x.png", 59 | "idiom" : "mac", 60 | "scale" : "2x", 61 | "size" : "512x512" 62 | } 63 | ], 64 | "info" : { 65 | "author" : "xcode", 66 | "version" : 1 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicklockwood/SwiftFormat/1d02d0f54a5123c3ef67084b318f4421427b7a51/EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicklockwood/SwiftFormat/1d02d0f54a5123c3ef67084b318f4421427b7a51/EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicklockwood/SwiftFormat/1d02d0f54a5123c3ef67084b318f4421427b7a51/EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_16x16.png -------------------------------------------------------------------------------- /EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicklockwood/SwiftFormat/1d02d0f54a5123c3ef67084b318f4421427b7a51/EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicklockwood/SwiftFormat/1d02d0f54a5123c3ef67084b318f4421427b7a51/EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_256x256.png -------------------------------------------------------------------------------- /EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicklockwood/SwiftFormat/1d02d0f54a5123c3ef67084b318f4421427b7a51/EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicklockwood/SwiftFormat/1d02d0f54a5123c3ef67084b318f4421427b7a51/EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_32x32.png -------------------------------------------------------------------------------- /EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicklockwood/SwiftFormat/1d02d0f54a5123c3ef67084b318f4421427b7a51/EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicklockwood/SwiftFormat/1d02d0f54a5123c3ef67084b318f4421427b7a51/EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicklockwood/SwiftFormat/1d02d0f54a5123c3ef67084b318f4421427b7a51/EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /EditorExtension/Application/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /EditorExtension/Application/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDocumentTypes 8 | 9 | 10 | CFBundleTypeExtensions 11 | 12 | swiftformat 13 | 14 | CFBundleTypeName 15 | swiftformat 16 | CFBundleTypeRole 17 | Editor 18 | LSHandlerRank 19 | Default 20 | 21 | 22 | CFBundleExecutable 23 | $(EXECUTABLE_NAME) 24 | CFBundleIconFile 25 | 26 | CFBundleIdentifier 27 | $(PRODUCT_BUNDLE_IDENTIFIER) 28 | CFBundleInfoDictionaryVersion 29 | 6.0 30 | CFBundleName 31 | $(PRODUCT_NAME) 32 | CFBundlePackageType 33 | APPL 34 | CFBundleShortVersionString 35 | $(MARKETING_VERSION) 36 | CFBundleVersion 37 | $(CURRENT_PROJECT_VERSION) 38 | LSApplicationCategoryType 39 | public.app-category.developer-tools 40 | LSMinimumSystemVersion 41 | $(MACOSX_DEPLOYMENT_TARGET) 42 | NSHumanReadableCopyright 43 | Copyright © 2016 Nick Lockwood. All rights reserved. 44 | NSMainStoryboardFile 45 | Main 46 | NSPrincipalClass 47 | NSApplication 48 | 49 | 50 | -------------------------------------------------------------------------------- /EditorExtension/Application/SwiftFormatter.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.application-groups 8 | 9 | com.charcoaldesign.SwiftFormat 10 | 11 | com.apple.security.files.user-selected.read-write 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /EditorExtension/Extension/LintFileCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LintFileCommand.swift 3 | // Editor Extension 4 | // 5 | // Created by Nick Lockwood on 06/01/2020. 6 | // Copyright © 2020 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XcodeKit 11 | 12 | class LintFileCommand: NSObject, XCSourceEditorCommand { 13 | func perform(with invocation: XCSourceEditorCommandInvocation, completionHandler: @escaping (Error?) -> Void) { 14 | guard SupportedContentUTIs.contains(invocation.buffer.contentUTI) else { 15 | return completionHandler(FormatCommandError.notSwiftLanguage) 16 | } 17 | 18 | // Grab the selected source to format 19 | let sourceToFormat = invocation.buffer.completeBuffer 20 | let input = tokenize(sourceToFormat) 21 | 22 | // Get rules 23 | let rules = FormatRules.named(RulesStore().rules.compactMap { 24 | $0.isEnabled ? $0.name : nil 25 | }) 26 | 27 | // Get options 28 | let store = OptionsStore() 29 | var formatOptions = store.inferOptions ? inferFormatOptions(from: input) : store.formatOptions 30 | formatOptions.indent = invocation.buffer.indentationString 31 | formatOptions.tabWidth = invocation.buffer.tabWidth 32 | formatOptions.swiftVersion = store.formatOptions.swiftVersion 33 | 34 | // Apply linting 35 | do { 36 | let changes = try lint(input, rules: rules, options: formatOptions) 37 | if !changes.isEmpty { 38 | return completionHandler(FormatCommandError.lintWarnings(changes)) 39 | } 40 | return completionHandler(nil) 41 | } catch { 42 | return completionHandler(error) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /EditorExtension/Extension/SourceEditorExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SourceEditorExtension.swift 3 | // Swift Formatter 4 | // 5 | // Created by Tony Arnold on 5/10/16. 6 | // Copyright 2016 Nick Lockwood 7 | // 8 | // Distributed under the permissive MIT license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/nicklockwood/SwiftFormat 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy 14 | // of this software and associated documentation files (the "Software"), to deal 15 | // in the Software without restriction, including without limitation the rights 16 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | // copies of the Software, and to permit persons to whom the Software is 18 | // furnished to do so, subject to the following conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all 21 | // copies or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | // SOFTWARE. 30 | // 31 | 32 | import Foundation 33 | import XcodeKit 34 | 35 | class SourceEditorExtension: NSObject, XCSourceEditorExtension {} 36 | -------------------------------------------------------------------------------- /EditorExtension/Extension/SupportedContentUTIs.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SupportedContentUTIs.swift 3 | // Editor Extension 4 | // 5 | // Created by George Cox on 11/22/19. 6 | // Copyright © 2019 Nick Lockwood. All rights reserved. 7 | // 8 | // Distributed under the permissive MIT license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/nicklockwood/SwiftFormat 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy 14 | // of this software and associated documentation files (the "Software"), to deal 15 | // in the Software without restriction, including without limitation the rights 16 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | // copies of the Software, and to permit persons to whom the Software is 18 | // furnished to do so, subject to the following conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all 21 | // copies or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | // SOFTWARE. 30 | // 31 | 32 | import Foundation 33 | 34 | let SupportedContentUTIs = [ 35 | "public.swift-source", 36 | "com.apple.dt.playground", 37 | "com.apple.dt.playgroundpage", 38 | "com.apple.dt.swiftpm-package-manifest", 39 | ] 40 | -------------------------------------------------------------------------------- /EditorExtension/Extension/SwiftFormatter.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.application-groups 8 | 9 | com.charcoaldesign.SwiftFormat 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /EditorExtension/Extension/XCSourceTextBuffer+SwiftFormat.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XCSourceTextBuffer+SwiftFormat.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 21/10/2016. 6 | // Copyright © 2016 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XcodeKit 11 | 12 | extension XCSourceTextPosition { 13 | init(_ offset: SourceOffset) { 14 | self.init(line: offset.line - 1, column: offset.column - 1) 15 | } 16 | } 17 | 18 | extension SourceOffset { 19 | init(_ position: XCSourceTextPosition) { 20 | line = position.line + 1 21 | column = position.column + 1 22 | } 23 | } 24 | 25 | extension XCSourceTextBuffer { 26 | /// Calculates the indentation string representation for a given source text buffer 27 | var indentationString: String { 28 | if usesTabsForIndentation { 29 | let tabCount = indentationWidth / tabWidth 30 | if tabCount * tabWidth == indentationWidth { 31 | return String(repeating: "\t", count: tabCount) 32 | } 33 | } 34 | return String(repeating: " ", count: indentationWidth) 35 | } 36 | 37 | func newPosition(for position: XCSourceTextPosition, 38 | in tokens: [Token]) -> XCSourceTextPosition 39 | { 40 | let offset = newOffset(for: SourceOffset(position), in: tokens, tabWidth: tabWidth) 41 | return XCSourceTextPosition(offset) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Nick Lockwood 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LinuxMain.swift: -------------------------------------------------------------------------------- 1 | // Unused, but required for Mint install 2 | 3 | import XCTest 4 | 5 | import SwiftFormatTests 6 | 7 | var tests = [XCTestCaseEntry]() 8 | 9 | XCTMain(tests) 10 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.7 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "SwiftFormat", 6 | products: [ 7 | .executable(name: "swiftformat", targets: ["CommandLineTool"]), 8 | .library(name: "SwiftFormat", targets: ["SwiftFormat"]), 9 | .plugin(name: "SwiftFormatPlugin", targets: ["SwiftFormatPlugin"]), 10 | ], 11 | targets: [ 12 | .executableTarget( 13 | name: "CommandLineTool", dependencies: ["SwiftFormat"], path: "CommandLineTool", 14 | exclude: ["swiftformat"] 15 | ), 16 | .target(name: "SwiftFormat", path: "Sources", exclude: ["Info.plist"]), 17 | .testTarget( 18 | name: "SwiftFormatTests", 19 | dependencies: ["SwiftFormat"], 20 | path: "Tests", 21 | exclude: ["GlobTest[5].txt"] 22 | ), 23 | .plugin( 24 | name: "SwiftFormatPlugin", 25 | capability: .command( 26 | intent: .custom( 27 | verb: "swiftformat", description: "Formats Swift source files using SwiftFormat" 28 | ), 29 | permissions: [ 30 | .writeToPackageDirectory(reason: "This command reformats source files"), 31 | ] 32 | ), 33 | dependencies: [.target(name: "CommandLineTool")] 34 | ), 35 | ] 36 | ) 37 | -------------------------------------------------------------------------------- /Platforms/Windows/SwiftFormat.wixproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | 5 | 6 | 7 | amd64 8 | 0.0.0 9 | 10 | 11 | 12 | build\ 13 | build\obj\ 14 | 15 | 16 | 17 | 18 | 19 | 20 | ProductArchitecture=$(ProductArchitecture); 21 | ProductVersion=$(ProductVersion); 22 | SwiftFormatBuildDir=$(SwiftFormatBuildDir); 23 | SwiftRedistDir=$(SwiftRedistDir) 24 | 25 | 26 | 27 | 28 | x64 29 | 30 | 31 | arm64 32 | 500 33 | 34 | 35 | arm 36 | 500 37 | 38 | 39 | x86 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /PluginTests/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/config/registries.json 8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 9 | .netrc 10 | -------------------------------------------------------------------------------- /PluginTests/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.6 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "PackageUsingPlugin", 6 | products: [ 7 | .library( 8 | name: "PackageUsingPlugin", 9 | targets: ["PackageUsingPlugin"] 10 | ), 11 | ], 12 | dependencies: [ 13 | .package(path: "../"), 14 | ], 15 | targets: [ 16 | .target( 17 | name: "PackageUsingPlugin", 18 | dependencies: [] 19 | ), 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /PluginTests/README.md: -------------------------------------------------------------------------------- 1 | Run `test.sh` which fails if triggering Swift package plugin does not format `Sources/PackageUsingPlugin/NotFormattedFile.swift` as expected. -------------------------------------------------------------------------------- /PluginTests/Sources/PackageUsingPlugin/NotFormattedFile.swift: -------------------------------------------------------------------------------- 1 | public struct PluginTests { 2 | public private(set) var text="Hello, World!" 3 | 4 | public init() { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /PluginTests/test.sh: -------------------------------------------------------------------------------- 1 | swift package plugin --allow-writing-to-package-directory swiftformat --unexclude . 2 | if [[ `git status --porcelain` ]]; then 3 | # Expected changes 4 | git restore Sources/PackageUsingPlugin/NotFormattedFile.swift 5 | exit 0 6 | else 7 | # No changes - unexpected 8 | exit 1 9 | fi 10 | -------------------------------------------------------------------------------- /Scripts/build-linux-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | 5 | pushd "$(dirname "${BASH_SOURCE[0]}")/.." > /dev/null 6 | 7 | BUILD_ARGS=( 8 | --product swiftformat 9 | --configuration release 10 | -Xlinker -S 11 | ) 12 | 13 | if [[ -z "$TARGETPLATFORM" ]]; then 14 | ARCH="$(uname -m)" 15 | else 16 | if [[ "$TARGETPLATFORM" = "linux/amd64" ]]; then 17 | ARCH="x86_64" 18 | elif [[ "$TARGETPLATFORM" = "linux/arm64" ]]; then 19 | ARCH="aarch64" 20 | else 21 | echo "Unsupported target platform: $TARGETPLATFORM" 22 | exit 1 23 | fi 24 | fi 25 | BUILD_ARGS+=(--swift-sdk "${ARCH}-swift-linux-musl") 26 | 27 | swift build "${BUILD_ARGS[@]}" 28 | -------------------------------------------------------------------------------- /Scripts/get-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | REGEX="let swiftFormatVersion = \"([0-9]+.[0-9]+.[0-9]+)\"" 6 | while IFS= read -r line; do 7 | if [[ $line =~ $REGEX ]] 8 | then 9 | echo "${BASH_REMATCH[1]}" 10 | break 11 | fi 12 | done < Sources/SwiftFormat.swift 13 | -------------------------------------------------------------------------------- /Scripts/spm-artifact-bundle-info.template: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0", 3 | "artifacts": { 4 | "swiftformat": { 5 | "version": "__VERSION__", 6 | "type": "executable", 7 | "variants": [ 8 | { 9 | "path": "swiftformat-__VERSION__-macos/bin/swiftformat", 10 | "supportedTriples": ["x86_64-apple-macosx", "arm64-apple-macosx"] 11 | }, 12 | { 13 | "path": "swiftformat-__VERSION__-linux-gnu/bin/swiftformat_linux", 14 | "supportedTriples": ["x86_64-unknown-linux-gnu"] 15 | }, 16 | { 17 | "path": "swiftformat-__VERSION__-linux-gnu/bin/swiftformat_linux_aarch64", 18 | "supportedTriples": ["aarch64-unknown-linux-gnu"] 19 | } 20 | ] 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Scripts/spm-artifact-bundle.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | # By default, parses the current version from `Sources/SwiftFormat.swift`. 6 | # Can be overridden by passing in custom version number as argument, e.g. 7 | # `./Scripts/spm-artifact-bundle.sh VERSION_NUMBER`. 8 | VERSION=${1:-$(./Scripts/get-version.sh)} 9 | MAC_EXECUTABLE=${2:-CommandLineTool/swiftformat} 10 | LINUX_EXECUTABLE=${3:-CommandLineTool/swiftformat_linux} 11 | LINUX_AARCH64_EXECUTABLE=${4:-CommandLineTool/swiftformat_linux_aarch64} 12 | 13 | ARTIFACT_BUNDLE=swiftformat.artifactbundle 14 | INFO_TEMPLATE=Scripts/spm-artifact-bundle-info.template 15 | MAC_BINARY_OUTPUT_DIR=$ARTIFACT_BUNDLE/swiftformat-$VERSION-macos/bin 16 | LINUX_BINARY_OUTPUT_DIR=$ARTIFACT_BUNDLE/swiftformat-$VERSION-linux-gnu/bin 17 | LINUX_AARCH64_BINARY_OUTPUT_DIR=$ARTIFACT_BUNDLE/swiftformat-$VERSION-linux-gnu/bin 18 | 19 | rm -rf swiftformat.artifactbundle 20 | rm -rf swiftformat.artifactbundle.zip 21 | 22 | mkdir $ARTIFACT_BUNDLE 23 | 24 | # Copy license into bundle 25 | cp LICENSE.md $ARTIFACT_BUNDLE 26 | 27 | # Create bundle info.json from template, replacing version 28 | sed 's/__VERSION__/'"${VERSION}"'/g' $INFO_TEMPLATE > "${ARTIFACT_BUNDLE}/info.json" 29 | 30 | # Copy macOS SwiftFormat binary into bundle 31 | chmod +x $MAC_EXECUTABLE 32 | mkdir -p $MAC_BINARY_OUTPUT_DIR 33 | cp $MAC_EXECUTABLE $MAC_BINARY_OUTPUT_DIR 34 | 35 | # Copy Linux SwiftFormat binary into bundle 36 | chmod +x $LINUX_EXECUTABLE 37 | mkdir -p $LINUX_BINARY_OUTPUT_DIR 38 | cp $LINUX_EXECUTABLE $LINUX_BINARY_OUTPUT_DIR 39 | 40 | # Copy Linux AArch64 SwiftFormat binary into bundle 41 | chmod +x $LINUX_AARCH64_EXECUTABLE 42 | mkdir -p $LINUX_AARCH64_BINARY_OUTPUT_DIR 43 | cp $LINUX_AARCH64_EXECUTABLE $LINUX_AARCH64_BINARY_OUTPUT_DIR 44 | 45 | # Create ZIP using 7z 46 | 7z a -tzip -mx=9 "${ARTIFACT_BUNDLE}.zip" "$ARTIFACT_BUNDLE" 47 | 48 | rm -rf $ARTIFACT_BUNDLE 49 | -------------------------------------------------------------------------------- /Snapshots/.swiftformat: -------------------------------------------------------------------------------- 1 | --swiftversion 4.2 2 | -------------------------------------------------------------------------------- /Snapshots/Async/Actors.swift: -------------------------------------------------------------------------------- 1 | actor SafeCollector { 2 | var deck: Set 3 | 4 | init(deck: Set) { 5 | self.deck = deck 6 | } 7 | 8 | func send(card selected: String, to person: SafeCollector) async -> Bool { 9 | guard deck.contains(selected) else { return false } 10 | 11 | deck.remove(selected) 12 | await person.transfer(card: selected) 13 | return true 14 | } 15 | 16 | func transfer(card: String) { 17 | deck.insert(card) 18 | } 19 | } 20 | 21 | class NewDataController { 22 | @MainActor func save() { 23 | print("Saving data…") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Snapshots/Async/AsyncAwait.swift: -------------------------------------------------------------------------------- 1 | func processWeather() async { 2 | let records = await fetchWeatherHistory() 3 | let average = await calculateAverageTemperature(for: records) 4 | let response = await upload(result: average) 5 | print("Server response: \(response)") 6 | } 7 | 8 | enum UserError: Error { 9 | case invalidCount, dataTooLong 10 | } 11 | 12 | func fetchUsers(count: Int) async throws -> [String] { 13 | if count > 3 { 14 | // Don't attempt to fetch too many users 15 | throw UserError.invalidCount 16 | } 17 | 18 | // Complex networking code here; we'll just send back up to `count` users 19 | return Array(["Antoni", "Karamo", "Tan"].prefix(count)) 20 | } 21 | 22 | func save(users: [String]) async throws -> String { 23 | let savedUsers = users.joined(separator: ",") 24 | 25 | if savedUsers.count > 32 { 26 | throw UserError.dataTooLong 27 | } else { 28 | // Actual saving code would go here 29 | return "Saved \(savedUsers)!" 30 | } 31 | } 32 | 33 | func updateUsers() async { 34 | do { 35 | let users = try await fetchUsers(count: 3) 36 | let result = try await save(users: users) 37 | print(result) 38 | } catch { 39 | print("Oops!") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Snapshots/Async/AsyncLet.swift: -------------------------------------------------------------------------------- 1 | func printUserDetails() async { 2 | async let username = getUser() 3 | async let scores = getHighScores() 4 | async let friends = getFriends() 5 | 6 | let user = await UserData(name: username, friends: friends, highScores: scores) 7 | print("Hello, my name is \(user.name), and I have \(user.friends.count) friends!") 8 | } 9 | 10 | enum NumberError: Error { 11 | case outOfRange 12 | } 13 | 14 | func fibonacci(of number: Int) async throws -> Int { 15 | if number < 0 || number > 22 { 16 | throw NumberError.outOfRange 17 | } 18 | 19 | if number < 2 { return number } 20 | async let first = fibonacci(of: number - 2) 21 | async let second = fibonacci(of: number - 1) 22 | return try await first + second 23 | } 24 | -------------------------------------------------------------------------------- /Snapshots/Async/AsyncSequences.swift: -------------------------------------------------------------------------------- 1 | func printAllDoubles() async { 2 | for await number in DoubleGenerator() { 3 | print(number) 4 | } 5 | } 6 | 7 | func containsExactNumber() async { 8 | let doubles = DoubleGenerator() 9 | let match = await doubles.contains(16_777_216) 10 | print(match) 11 | } 12 | -------------------------------------------------------------------------------- /Snapshots/Async/EffectfulProperties.swift: -------------------------------------------------------------------------------- 1 | var contents: String { 2 | get async throws { 3 | guard let url = Bundle.main.url(forResource: filename, withExtension: nil) else { 4 | throw FileError.missing 5 | } 6 | 7 | do { 8 | return try String(contentsOf: url) 9 | } catch { 10 | throw FileError.unreadable 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Snapshots/Async/PropertyWrappers.swift: -------------------------------------------------------------------------------- 1 | func setScore2(@Clamped(range: 0 ... 100) to score: Int) { 2 | print("Setting score to \(score)") 3 | } 4 | -------------------------------------------------------------------------------- /Snapshots/Async/README.md: -------------------------------------------------------------------------------- 1 | Async/await test samples from https://www.hackingwithswift.com/articles/233/whats-new-in-swift-5-5 -------------------------------------------------------------------------------- /Snapshots/Async/Sendable.swift: -------------------------------------------------------------------------------- 1 | func runLater(_ function: @escaping @Sendable () -> Void) { 2 | DispatchQueue.global().asyncAfter(deadline: .now() + 3, execute: function) 3 | } 4 | -------------------------------------------------------------------------------- /Snapshots/Config/.swiftformat: -------------------------------------------------------------------------------- 1 | --nospaceoperators + -------------------------------------------------------------------------------- /Snapshots/Config/child/.swiftformat: -------------------------------------------------------------------------------- 1 | --nospaceoperators * -------------------------------------------------------------------------------- /Snapshots/Config/child/test.swift: -------------------------------------------------------------------------------- 1 | let foo = a + b*c 2 | -------------------------------------------------------------------------------- /Snapshots/Config/test.swift: -------------------------------------------------------------------------------- 1 | let foo = a+b * c 2 | -------------------------------------------------------------------------------- /Snapshots/Consumer/.swift-version: -------------------------------------------------------------------------------- 1 | 5.0 -------------------------------------------------------------------------------- /Snapshots/Consumer/.swiftformat: -------------------------------------------------------------------------------- 1 | --hexgrouping ignore 2 | --decimalgrouping ignore 3 | --ifdef no-indent 4 | 5 | --exclude Tests/XCTestManifests.swift,Examples/JSON/compiled.swift 6 | -------------------------------------------------------------------------------- /Snapshots/Consumer/Examples/JSON/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // JSON 4 | // 5 | // Created by Nick Lockwood on 01/03/2018. 6 | // Copyright © 2018 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Example input 12 | let input = """ 13 | { 14 | "foo": true, 15 | "bar": [0, 1, 2.0, -0.7, null, "hello world"], 16 | "baz": { 17 | "quux": 2e-006 18 | } 19 | } 20 | """ 21 | 22 | @discardableResult 23 | func time(_ block: () throws -> T) -> T { 24 | let time = CFAbsoluteTimeGetCurrent() 25 | var result: Any = () 26 | do { 27 | result = try block() 28 | } catch { 29 | print(error) 30 | } 31 | print(Int((CFAbsoluteTimeGetCurrent() - time) * 1000), terminator: " ms\n\n") 32 | return result as! T 33 | } 34 | 35 | // Evaluate json using interpreted parser 36 | time { try print("interpreted:", parseJSON(input)) } 37 | 38 | // Evaluate json using handwritten parser 39 | time { try print("handwritten:", parseJSON2(input)) } 40 | 41 | // Evaluate json using compiled parser 42 | time { try print("compiled:", parseJSON3(input)!) } 43 | 44 | /// Update compiled parser 45 | print("Recompiling parser...") 46 | let compiledSwiftFile = URL(fileURLWithPath: #file) 47 | .deletingLastPathComponent().appendingPathComponent("compiled.swift") 48 | let parser = time { compileJSONParser() } 49 | try parser.write(to: compiledSwiftFile, atomically: true, encoding: .utf8) 50 | -------------------------------------------------------------------------------- /Snapshots/Consumer/Examples/JSON/shared.swift: -------------------------------------------------------------------------------- 1 | // 2 | // shared.swift 3 | // JSON 4 | // 5 | // Created by Nick Lockwood on 01/03/2018. 6 | // Copyright © 2018 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | // This code is shared between the interpreted and compiled versions of the JSON parser 10 | 11 | /// JSON parsing errors 12 | enum JSONError: Swift.Error { 13 | case invalidNumber(String) 14 | case invalidCodePoint(String) 15 | } 16 | 17 | /// Labels 18 | enum JSONLabel: String { 19 | case null 20 | case boolean 21 | case number 22 | case string 23 | case json 24 | case array 25 | case object 26 | 27 | /// Internal types 28 | case unichar 29 | case keyValue 30 | } 31 | 32 | /// Transform 33 | func jsonTransform(_ name: JSONLabel, _ values: [Any]) throws -> Any? { 34 | switch name { 35 | case .json: 36 | return values[0] 37 | case .boolean: 38 | return values[0] as! String == "true" 39 | case .null: 40 | return nil as Any? as Any 41 | case .string: 42 | return (values as! [String]).joined() 43 | case .number: 44 | let value = values[0] as! String 45 | guard let number = Double(value) else { 46 | throw JSONError.invalidNumber(value) 47 | } 48 | return number 49 | case .array: 50 | return values 51 | case .object: 52 | return Dictionary(values as! [(String, Any)]) { $1 } 53 | case .keyValue: 54 | return (values[0] as! String, values[1]) 55 | case .unichar: 56 | let value = values[0] as! String 57 | guard let hex = UInt32(value, radix: 16), 58 | let char = UnicodeScalar(hex) 59 | else { 60 | throw JSONError.invalidCodePoint(value) 61 | } 62 | return String(char) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Snapshots/Consumer/Examples/REPL/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // REPL 4 | // 5 | // Created by Nick Lockwood on 02/03/2018. 6 | // Copyright © 2018 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Prevent control characters confusing parser 12 | private let start = UnicodeScalar(63232)! 13 | private let end = UnicodeScalar(63235)! 14 | private let cursorCharacters = CharacterSet(charactersIn: start ... end) 15 | 16 | /// Persistent state 17 | private let state = State() 18 | 19 | while true { 20 | print("> ", terminator: "") 21 | guard var input = readLine() else { break } 22 | input = String(input.unicodeScalars.filter { !cursorCharacters.contains($0) }) 23 | do { 24 | if let result = try evaluate(input, state: state) { 25 | print(result) 26 | } 27 | } catch { 28 | print("error: \(error)") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Snapshots/Consumer/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Nick Lockwood 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Snapshots/Consumer/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import ConsumerTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += ConsumerTests.__allTests() 7 | 8 | XCTMain(tests) 9 | -------------------------------------------------------------------------------- /Snapshots/Consumer/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "Consumer", 6 | products: [ 7 | .library(name: "Consumer", targets: ["Consumer"]), 8 | ], 9 | targets: [ 10 | .target(name: "Consumer", path: "Sources"), 11 | .testTarget(name: "ConsumerTests", dependencies: ["Consumer"], path: "Tests"), 12 | ] 13 | ) 14 | -------------------------------------------------------------------------------- /Snapshots/Consumer/Sources/Consumer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Consumer.h 3 | // Consumer 4 | // 5 | // Created by Nick Lockwood on 01/03/2018. 6 | // Copyright © 2018 Nick Lockwood. All rights reserved. 7 | // 8 | // Distributed under the permissive MIT license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/nicklockwood/Consumer 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy 14 | // of this software and associated documentation files (the "Software"), to deal 15 | // in the Software without restriction, including without limitation the rights 16 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | // copies of the Software, and to permit persons to whom the Software is 18 | // furnished to do so, subject to the following conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all 21 | // copies or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | // SOFTWARE. 30 | // 31 | 32 | #import 33 | 34 | //! Project version number for Consumer. 35 | FOUNDATION_EXPORT double ConsumerVersionNumber; 36 | 37 | //! Project version string for Consumer. 38 | FOUNDATION_EXPORT const unsigned char ConsumerVersionString[]; 39 | 40 | // In this header, you should import all the public headers of your framework using statements like #import 41 | 42 | 43 | -------------------------------------------------------------------------------- /Snapshots/Euclid/.swift-version: -------------------------------------------------------------------------------- 1 | 4.2 -------------------------------------------------------------------------------- /Snapshots/Euclid/.swiftformat: -------------------------------------------------------------------------------- 1 | --hexgrouping ignore 2 | --decimalgrouping ignore 3 | --enable isEmpty 4 | --ifdef no-indent 5 | -------------------------------------------------------------------------------- /Snapshots/Euclid/Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Created by Nick Lockwood on 11/12/2018. 6 | // Copyright © 2018 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | } 15 | -------------------------------------------------------------------------------- /Snapshots/Euclid/Example/SceneViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneViewController.swift 3 | // Example 4 | // 5 | // Created by Nick Lockwood on 11/12/2018. 6 | // Copyright © 2018 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Euclid 10 | import SceneKit 11 | import UIKit 12 | 13 | class SceneViewController: UIViewController { 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | 17 | // create a new scene 18 | let scene = SCNScene() 19 | 20 | // create and add a camera to the scene 21 | let cameraNode = SCNNode() 22 | cameraNode.camera = SCNCamera() 23 | scene.rootNode.addChildNode(cameraNode) 24 | 25 | // place the camera 26 | cameraNode.position = SCNVector3(x: 0, y: 0, z: 3) 27 | 28 | // create some geometry using Euclid 29 | let cube = Mesh.cube(size: 0.8, material: UIColor.red) 30 | let sphere = Mesh.sphere(slices: 40, material: UIColor.blue) 31 | let mesh = cube.subtract(sphere) 32 | 33 | // create SCNNode 34 | let geometry = SCNGeometry(mesh) { 35 | let material = SCNMaterial() 36 | material.diffuse.contents = $0 as? UIColor 37 | return material 38 | } 39 | let node = SCNNode(geometry: geometry) 40 | scene.rootNode.addChildNode(node) 41 | 42 | // configure the SCNView 43 | let scnView = view as! SCNView 44 | scnView.scene = scene 45 | scnView.autoenablesDefaultLighting = true 46 | scnView.allowsCameraControl = true 47 | scnView.showsStatistics = true 48 | scnView.backgroundColor = .white 49 | } 50 | 51 | override var shouldAutorotate: Bool { 52 | return true 53 | } 54 | 55 | override var prefersStatusBarHidden: Bool { 56 | return true 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Snapshots/Euclid/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Nick Lockwood 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Snapshots/Euclid/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import EuclidTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += EuclidTests.__allTests() 7 | 8 | XCTMain(tests) 9 | -------------------------------------------------------------------------------- /Snapshots/Euclid/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.2 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "Euclid", 6 | products: [ 7 | .library(name: "Euclid", targets: ["Euclid"]), 8 | ], 9 | targets: [ 10 | .target(name: "Euclid", path: "Sources"), 11 | .testTarget(name: "EuclidTests", dependencies: ["Euclid"], path: "Tests"), 12 | ] 13 | ) 14 | -------------------------------------------------------------------------------- /Snapshots/Euclid/Sources/Euclid.h: -------------------------------------------------------------------------------- 1 | // 2 | // Euclid.h 3 | // Euclid 4 | // 5 | // Created by Nick Lockwood on 11/12/2018. 6 | // Copyright © 2018 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Euclid. 12 | FOUNDATION_EXPORT double EuclidVersionNumber; 13 | 14 | //! Project version string for Euclid. 15 | FOUNDATION_EXPORT const unsigned char EuclidVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Snapshots/Euclid/Tests/PlaneTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlaneTests.swift 3 | // EuclidTests 4 | // 5 | // Created by Nick Lockwood on 19/12/2018. 6 | // Copyright © 2018 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Euclid 11 | 12 | class PlaneTests: XCTestCase { 13 | func testLinuxTestSuiteIncludesAllTests() { 14 | #if os(macOS) 15 | let thisClass = type(of: self) 16 | let linuxCount = thisClass.__allTests.count 17 | let darwinCount = thisClass.defaultTestSuite.testCaseCount 18 | XCTAssertEqual(linuxCount, darwinCount, "run swift test --generate-linuxmain") 19 | #endif 20 | } 21 | 22 | func testConcavePolygonClockwiseWinding() { 23 | var transform = Transform.identity 24 | var points = [Vector]() 25 | let sides = 5 26 | for _ in 0 ..< sides { 27 | points.append(Vector(0, -0.5).transformed(by: transform)) 28 | transform.rotate(by: .roll(.pi / Double(sides))) 29 | points.append(Vector(0, -1).transformed(by: transform)) 30 | transform.rotate(by: .roll(.pi / Double(sides))) 31 | } 32 | let plane = Plane(points: points) 33 | XCTAssertEqual(plane?.normal, Vector(0, 0, -1)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Snapshots/Euclid/Tests/TextTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TextTests.swift 3 | // EuclidTests 4 | // 5 | // Created by Nick Lockwood on 12/03/2019. 6 | // Copyright © 2019 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | #if canImport(CoreText) 10 | 11 | import Foundation 12 | import XCTest 13 | @testable import Euclid 14 | 15 | class TextTests: XCTestCase { 16 | func testTextPaths() { 17 | let text = NSAttributedString(string: "Hello") 18 | let paths = Path.text(text) 19 | XCTAssertEqual(paths.count, 5) 20 | XCTAssertEqual(paths.map { $0.subpaths.count }, [ 21 | 1, 2, 1, 1, 2, 22 | ]) 23 | } 24 | 25 | func testTextMeshWithAttributedString() { 26 | let text = "Hello" 27 | let font = CTFontCreateWithName("Helvetica" as CFString, 12, nil) 28 | let attributes = [NSAttributedString.Key.font: font] 29 | let string = NSAttributedString(string: text, attributes: attributes) 30 | let mesh = Mesh(text: string, depth: 1.0) 31 | XCTAssertEqual(mesh.bounds.min.z, -0.5) 32 | XCTAssertEqual(mesh.bounds.max.z, 0.5) 33 | XCTAssert(mesh.bounds.max.x > 20) 34 | XCTAssert(mesh.polygons.count > 150) 35 | } 36 | 37 | func testTextMeshWithString() { 38 | let text = "Hello" 39 | let font = CTFontCreateWithName("Helvetica" as CFString, 12, nil) 40 | let mesh = Mesh(text: text, font: font, depth: 1.0) 41 | XCTAssertEqual(mesh.bounds.min.z, -0.5) 42 | XCTAssertEqual(mesh.bounds.max.z, 0.5) 43 | XCTAssert(mesh.bounds.max.x > 20) 44 | XCTAssert(mesh.polygons.count > 150) 45 | } 46 | } 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /Snapshots/Expression/.swift-version: -------------------------------------------------------------------------------- 1 | 4.0 -------------------------------------------------------------------------------- /Snapshots/Expression/.swiftformat: -------------------------------------------------------------------------------- 1 | --hexgrouping ignore 2 | --decimalgrouping ignore 3 | --enable isEmpty 4 | --exclude Tests/XCTestManifests.swift -------------------------------------------------------------------------------- /Snapshots/Expression/Examples/Benchmark/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Benchmark 4 | // 5 | // Created by Nick Lockwood on 13/02/2018. 6 | // Copyright © 2018 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | } 15 | -------------------------------------------------------------------------------- /Snapshots/Expression/Examples/Calculator/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Calculator 4 | // 5 | // Created by Nick Lockwood on 17/09/2016. 6 | // Copyright © 2016 Nick Lockwood. All rights reserved. 7 | // 8 | // Distributed under the permissive MIT license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/nicklockwood/Expression 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy 14 | // of this software and associated documentation files (the "Software"), to deal 15 | // in the Software without restriction, including without limitation the rights 16 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | // copies of the Software, and to permit persons to whom the Software is 18 | // furnished to do so, subject to the following conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all 21 | // copies or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | // SOFTWARE. 30 | // 31 | 32 | import UIKit 33 | 34 | @UIApplicationMain 35 | class AppDelegate: UIResponder, UIApplicationDelegate { 36 | var window: UIWindow? 37 | } 38 | -------------------------------------------------------------------------------- /Snapshots/Expression/Examples/Colors/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Colors 4 | // 5 | // Created by Nick Lockwood on 30/09/2016. 6 | // Copyright © 2016 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | } 15 | -------------------------------------------------------------------------------- /Snapshots/Expression/Examples/Colors/ColorLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorLabel.swift 3 | // Colors 4 | // 5 | // Created by Nick Lockwood on 30/09/2016. 6 | // Copyright © 2016 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ColorLabel: UILabel { 12 | private func updateColor() { 13 | do { 14 | backgroundColor = .clear 15 | textColor = try UIColor(expression: text ?? "") 16 | } catch { 17 | backgroundColor = .red 18 | textColor = .black 19 | } 20 | } 21 | 22 | override var text: String? { 23 | get { return super.text } 24 | set { 25 | super.text = newValue 26 | updateColor() 27 | } 28 | } 29 | 30 | required init?(coder aDecoder: NSCoder) { 31 | super.init(coder: aDecoder) 32 | updateColor() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Snapshots/Expression/Examples/Colors/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Colors 4 | // 5 | // Created by Nick Lockwood on 30/09/2016. 6 | // Copyright © 2016 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | // Do any additional setup after loading the view, typically from a nib. 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Snapshots/Expression/Examples/Layout/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Layout 4 | // 5 | // Created by Nick Lockwood on 21/09/2016. 6 | // Copyright © 2016 Nick Lockwood. All rights reserved. 7 | // 8 | // Distributed under the permissive MIT license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/nicklockwood/Expression 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy 14 | // of this software and associated documentation files (the "Software"), to deal 15 | // in the Software without restriction, including without limitation the rights 16 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | // copies of the Software, and to permit persons to whom the Software is 18 | // furnished to do so, subject to the following conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all 21 | // copies or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | // SOFTWARE. 30 | // 31 | 32 | import UIKit 33 | 34 | @UIApplicationMain 35 | class AppDelegate: UIResponder, UIApplicationDelegate { 36 | var window: UIWindow? 37 | } 38 | -------------------------------------------------------------------------------- /Snapshots/Expression/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Nick Lockwood 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Snapshots/Expression/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import ExpressionTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += ExpressionTests.__allTests() 7 | 8 | XCTMain(tests) 9 | -------------------------------------------------------------------------------- /Snapshots/Expression/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "Expression", 6 | products: [ 7 | .library(name: "Expression", targets: ["Expression"]), 8 | ], 9 | targets: [ 10 | .target(name: "Expression", path: "Sources"), 11 | .testTarget(name: "ExpressionTests", dependencies: ["Expression"], path: "Tests"), 12 | ] 13 | ) 14 | -------------------------------------------------------------------------------- /Snapshots/Issues/1061.swift: -------------------------------------------------------------------------------- 1 | // swiftformat:options --indent 4 2 | // swiftformat:options --xcodeindentation disabled 3 | // swiftformat:options --self init-only 4 | // swiftformat:options --stripunusedargs closure-only 5 | // swiftformat:options --commas inline 6 | // swiftformat:options --wraparguments before-first 7 | // swiftformat:options --wrapcollections before-first 8 | 9 | class ViewController: UIViewController { 10 | override func viewDidLoad() { 11 | super.viewDidLoad() 12 | } 13 | 14 | private func handleGenericError(_ error: Error) { 15 | if let requestableError = error as? RequestableError, 16 | case let .underlying(error as NSError) = requestableError, // <--- throws error about missing '{' here 17 | error.code == NSURLErrorNotConnectedToInternet 18 | { 19 | print("Error handled") 20 | } else { 21 | showGenericError() 22 | } 23 | } 24 | 25 | private func errorMessageStringForError(_ error: Error?) -> String { 26 | let errorMessage: String 27 | // Get error message for statusCode error, Othrewise return generic error 28 | if let requestableError = error as? RequestableError, // <--- throws error about missing '{' here 29 | case let .statusCode(statusCode, _, _, error as GenericResponseError) = requestableError 30 | { 31 | print(error) 32 | print(statusCode) 33 | errorMessage = "errorMessageStringForErrorCode(status: statusCode, code: error.intCode)" 34 | } else { 35 | errorMessage = "systemUnavailable" 36 | } 37 | return errorMessage 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Snapshots/Issues/1129.swift: -------------------------------------------------------------------------------- 1 | public struct DataChangedNotificationPayload: NotificationPayload { 2 | private enum UserInfoKey: String { 3 | case insertedObjects 4 | case updatedObjects 5 | case deletedObjects 6 | } 7 | 8 | public let insertedObjects: [NSManagedObject] 9 | public let updatedObjects: [NSManagedObject] 10 | public let deletedObjects: [NSManagedObject] 11 | 12 | public init?(userInfo: [AnyHashable: Any]?) { 13 | if let insertedObjects = userInfo?[UserInfoKey.insertedObjects] as? [NSManagedObject], 14 | let updatedObjects = userInfo?[UserInfoKey.updatedObjects] as? [NSManagedObject], 15 | let deletedObjects = userInfo?[UserInfoKey.deletedObjects] as? [NSManagedObject] 16 | { 17 | self.insertedObjects = insertedObjects 18 | self.updatedObjects = updatedObjects 19 | self.deletedObjects = deletedObjects 20 | } else { 21 | return nil 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Snapshots/Issues/1183.swift: -------------------------------------------------------------------------------- 1 | func typeMismatch( 2 | for symbol: String, 3 | index: Int, 4 | expected types: [String], 5 | got: String 6 | ) -> RuntimeErrorType { 7 | var types = Set(types).sorted() 8 | if let index = types.firstIndex(of: "block") { 9 | types.append(types.remove(at: index)) 10 | } 11 | let expected: String 12 | switch types.count { 13 | case 1: 14 | expected = types[0] 15 | case 2: 16 | expected = "\(types[0]) or \(types[1])" 17 | default: 18 | expected = "\(types.dropLast().joined(separator: ", ")), or \(types.last!)" 19 | } 20 | return .typeMismatch(for: symbol, index: index, expected: expected, got: got) 21 | } 22 | -------------------------------------------------------------------------------- /Snapshots/Issues/1184.swift: -------------------------------------------------------------------------------- 1 | func foo(index: Int) { 2 | for index in 0 ..< 1 {} 3 | print(index) 4 | } 5 | -------------------------------------------------------------------------------- /Snapshots/Issues/1201.swift: -------------------------------------------------------------------------------- 1 | // MARK: - Actions 2 | 3 | public enum Actions: Hashable, Decodable { 4 | case redirect(URL?) 5 | case refresh 6 | case back 7 | 8 | // MARK: Public 9 | 10 | public var redirectURL: URL? { 11 | let path = /Self.redirect 12 | return path.extract(from: self)?.flatMap { $0 } 13 | } 14 | } 15 | 16 | public extension Core_Action_ComposedAction { 17 | var uiAction: Actions { 18 | switch component { 19 | case let .actionRedirect(redirect): 20 | return Actions.redirect(URL(string: redirect.link.value)) 21 | case .actionBack: 22 | return .back 23 | default: 24 | return .refresh 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Snapshots/Issues/1202.swift: -------------------------------------------------------------------------------- 1 | // swiftformat:disable wrapArguments,wrapMultilineStatementBraces 2 | 3 | private final class FadeTransitionStyle: NSObject, UIViewControllerAnimatedTransitioning { 4 | func transitionDuration(using _: UIViewControllerContextTransitioning?) -> TimeInterval { 5 | 0.3 6 | } 7 | 8 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 9 | guard let fromVC = transitionContext.viewController(forKey: .from), 10 | let toVC = transitionContext.viewController(forKey: .to) else { return } 11 | 12 | toVC.view.alpha = 0 13 | UIView.transition( 14 | with: transitionContext.containerView, 15 | duration: transitionDuration(using: transitionContext), 16 | options: []) { 17 | fromVC.view.alpha = 0 18 | transitionContext.containerView.addSubview(toVC.view) 19 | toVC.view.frame = transitionContext.finalFrame(for: toVC) 20 | toVC.view.alpha = 1 21 | } completion: { _ in 22 | transitionContext.completeTransition(true) 23 | fromVC.view.removeFromSuperview() 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Snapshots/Issues/1205.swift: -------------------------------------------------------------------------------- 1 | private extension FooOperator { 2 | var op: ((Double, Double) -> Double)? { 3 | switch self { 4 | case .unknown: 5 | return nil 6 | case .add: 7 | return (+) 8 | case .subtract: 9 | return (-) 10 | case .multiply: 11 | return (*) 12 | case .divide: 13 | return (/) 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Snapshots/Issues/1212.swift: -------------------------------------------------------------------------------- 1 | // swiftformat:disable all 2 | 3 | func getType(_ _type: ClassifyType, _ t1: Double? = nil, _ t2: Double? = nil) -> (type: ClassifyType, roots: [Double]?) { var type = _type if let t1 = t1 { var t1Ok = t1 > 0 && t1 < 1 var t2Ok = t2 == nil ? false : (t2! > 0 && t2! < 1) if (!(t1Ok || t2Ok) || type == .loop && !(t1Ok && t2Ok)) { type = .arch t1Ok = false t2Ok = false } let roots: [Double]? = t1Ok || t2Ok ? t1Ok && t2Ok ? t1 < t2! ? [t1, t2!] : [t2!, t1] : [t1Ok ? t1 : t2!] : nil return (type, roots) } else { return (type, nil) } } 4 | -------------------------------------------------------------------------------- /Snapshots/Issues/1254.swift: -------------------------------------------------------------------------------- 1 | // swiftformat:disable blankLinesBetweenScopes 2 | // swiftformat:disable wrapMultilineStatementBraces 3 | // swiftformat:options --commas false 4 | // swiftformat:options --xcodeindentation enabled 5 | // swiftformat:options --extensionacl on-declarations 6 | // swiftformat:options --modifierorder public,override 7 | // swiftformat:options --patternlet inline 8 | // swiftformat:options --trimwhitespace nonblank-lines 9 | 10 | func testing() { 11 | firstly { 12 | doSomething() 13 | } 14 | // then do something else 15 | .then { 16 | doSomethingElse() 17 | } 18 | // convert the thing 19 | .map { 20 | transform($0) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Snapshots/Issues/1259.swift: -------------------------------------------------------------------------------- 1 | func forcedSelection(for list: [Value], current selection: UserSelection) -> UserSelection { 2 | switch (list.count, list.first) { 3 | case (0, _), 4 | (_, nil): 5 | return .none 6 | case let (1, selection?): 7 | return .single(selection) 8 | default: 9 | return selection 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Snapshots/Issues/1269.swift: -------------------------------------------------------------------------------- 1 | // swiftformat:options --swiftversion 5.7 2 | 3 | struct Foo { 4 | func bar( 5 | _ value: V, 6 | _ work: () -> R 7 | ) -> R 8 | where Value == @Sendable () -> V, 9 | V: Sendable 10 | { 11 | work() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Snapshots/Issues/1319.swift: -------------------------------------------------------------------------------- 1 | // swiftformat:options --swiftversion 5.7 2 | // swiftformat:disable --redundantLet 3 | 4 | let _ = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/ 5 | -------------------------------------------------------------------------------- /Snapshots/Issues/1582.swift: -------------------------------------------------------------------------------- 1 | // swiftformat:options --swiftversion 5.9 2 | 3 | public enum ButtonState: Equatable { 4 | case normal 5 | case pressed // highlighted 6 | case selected 7 | case disabled // inactive 8 | #if os(macOS) 9 | case hovered 10 | #endif 11 | } 12 | 13 | func foo(buttonState: ButtonState) -> Int { 14 | let str: String 15 | let number: Int 16 | switch buttonState { 17 | #if os(macOS) 18 | case .normal, 19 | .hovered: 20 | str = "1" 21 | number = 1 22 | #else 23 | case .normal: 24 | str = "1" 25 | number = 1 26 | #endif 27 | 28 | case .pressed: 29 | str = "1" 30 | number = 1 31 | 32 | case .selected: 33 | return -1 34 | 35 | case .disabled: 36 | return -1 37 | } 38 | 39 | return 0 40 | } 41 | -------------------------------------------------------------------------------- /Snapshots/Issues/406.swift: -------------------------------------------------------------------------------- 1 | // swiftformat:options --allman true 2 | 3 | func foo() 4 | { 5 | return SessionManager 6 | .shared 7 | .validateSession() 8 | .switchMap 9 | { _ -> Observable in 10 | someCode() 11 | } 12 | .switchMap 13 | { 14 | otherCode() 15 | } 16 | } 17 | 18 | SessionManager 19 | .shared 20 | .validateSession() 21 | .switchMap 22 | { _ -> Observable in 23 | someCode() 24 | } 25 | .switchMap 26 | { 27 | otherCode() 28 | } 29 | 30 | // swiftformat:options --allman false 31 | 32 | SessionManager 33 | .shared 34 | .validateSession() 35 | .switchMap { _ -> Observable in 36 | someCode() 37 | } 38 | .switchMap { 39 | otherCode() 40 | } 41 | -------------------------------------------------------------------------------- /Snapshots/Issues/411.swift: -------------------------------------------------------------------------------- 1 | Array([] 2 | .map { 3 | "" 4 | }.something()) 5 | 6 | class Thing { 7 | private let somePublisher = 8 | Publishers.Factory { _ in 9 | // do some stuff here 10 | AnyCancellable {} 11 | } 12 | .share(replay: 1) 13 | .eraseToAnyPublisher() 14 | } 15 | -------------------------------------------------------------------------------- /Snapshots/Issues/637.swift: -------------------------------------------------------------------------------- 1 | class TestClass { 2 | init( 3 | foo: Foo 4 | ) where 5 | Foo: Fooable, 6 | Foo.Bar: Barable, 7 | Foo.Something == Something, 8 | Foo.SomethingElse == SomethingElse 9 | { 10 | self.foo = foo 11 | } 12 | } 13 | 14 | class MyClass: NSObject, 15 | FooProtocol, 16 | BarProtocol 17 | { 18 | func someFunction() {} 19 | } 20 | -------------------------------------------------------------------------------- /Snapshots/Issues/645.swift: -------------------------------------------------------------------------------- 1 | .update( 2 | a, { _ in 3 | s(…) // … 4 | } 5 | ) 6 | 7 | func a(s: String) -> JSON { 8 | return Data(s).utf8 9 | } 10 | 11 | func b(status: String, payload: String) -> String { 12 | return "\"data\": {\n\(status),\n\(payload)\n}" 13 | } 14 | 15 | let somethingReallyReallyLongJson = a(s: b(status: """ 16 | "status": { 17 | "success": [], 18 | "error": [] 19 | } 20 | """, 21 | """ 22 | "payload": { 23 | "header": "

", 24 | "div": "
" 25 | } 26 | """) 27 | ) 28 | -------------------------------------------------------------------------------- /Snapshots/Issues/678.swift: -------------------------------------------------------------------------------- 1 | // swiftformat:options --swiftversion 5.2 2 | 3 | final class Value { 4 | private let provider: () -> T 5 | 6 | init(provider: @escaping () -> T) { 7 | self.provider = provider 8 | } 9 | } 10 | 11 | final class Consumer { 12 | private(set) lazy var value = Value { [unowned self] in 13 | self.someProvider() 14 | } 15 | 16 | private func someProvider() -> String { 17 | "string" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Snapshots/Issues/681.swift: -------------------------------------------------------------------------------- 1 | someController = SomeController(action: { [unowned self] in 2 | self.value = true; self.actionCalled() 3 | }, 4 | otherParameter: parameter) 5 | -------------------------------------------------------------------------------- /Snapshots/Issues/682.swift: -------------------------------------------------------------------------------- 1 | enum Flag: String, Codable { 2 | case `init` 3 | } 4 | 5 | let flags: [Flag] = [.`init`] 6 | -------------------------------------------------------------------------------- /Snapshots/Issues/687.swift: -------------------------------------------------------------------------------- 1 | // swiftformat:options --self init-only 2 | 3 | protocol Test where Self: UIViewController { 4 | static func abc() -> Self 5 | } 6 | -------------------------------------------------------------------------------- /Snapshots/Issues/794.swift: -------------------------------------------------------------------------------- 1 | protocol P1 {} 2 | 3 | extension P1 { 4 | public func f() {} 5 | } 6 | 7 | public protocol P2 { 8 | public func f() 9 | } 10 | 11 | public struct S: P1, P2 {} 12 | -------------------------------------------------------------------------------- /Snapshots/Layout/.swift-version: -------------------------------------------------------------------------------- 1 | 4.0 2 | -------------------------------------------------------------------------------- /Snapshots/Layout/.swiftformat: -------------------------------------------------------------------------------- 1 | --header "// Copyright © 2017 Schibsted. All rights reserved." 2 | --binarygrouping 8,8 3 | --decimalgrouping ignore 4 | --importgrouping testable-bottom 5 | --modifierorder public,override 6 | -------------------------------------------------------------------------------- /Snapshots/Layout/Benchmark/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import UIKit 4 | 5 | @UIApplicationMain 6 | class AppDelegate: UIResponder, UIApplicationDelegate { 7 | var window: UIWindow? 8 | 9 | func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 10 | window = UIWindow() 11 | window?.rootViewController = ViewController() 12 | window?.makeKeyAndVisible() 13 | return true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Snapshots/Layout/EditorExtension/Application/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import Cocoa 4 | 5 | @NSApplicationMain 6 | class AppDelegate: NSObject, NSApplicationDelegate {} 7 | -------------------------------------------------------------------------------- /Snapshots/Layout/EditorExtension/Application/ViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import Cocoa 4 | 5 | class ViewController: NSViewController {} 6 | -------------------------------------------------------------------------------- /Snapshots/Layout/EditorExtension/Extension/SourceEditorExtension.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import Foundation 4 | import XcodeKit 5 | 6 | class SourceEditorExtension: NSObject, XCSourceEditorExtension {} 7 | -------------------------------------------------------------------------------- /Snapshots/Layout/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Schibsted Products and Technology 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Snapshots/Layout/Layout/.swiftformat: -------------------------------------------------------------------------------- 1 | --exclude Vendor 2 | -------------------------------------------------------------------------------- /Snapshots/Layout/Layout/Layout+Testing.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | public extension Layout { 4 | /// Clear all Layout caches 5 | static func clearAllCaches() { 6 | Expression.clearCache() 7 | clearParsedExpressionCache() 8 | clearLayoutExpressionCache() 9 | clearRuntimeTypeCache() 10 | clearExpressionTypes() 11 | clearLayoutLoaderCache() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Snapshots/Layout/Layout/Layout.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import Foundation 4 | 5 | /// Internal struct used to store 6 | /// serialized layouts 7 | public struct Layout { 8 | var className: String 9 | var id: String? 10 | var expressions: [String: String] 11 | var parameters: [String: RuntimeType] 12 | var macros: [String: String] 13 | var children: [Layout] 14 | var body: String? 15 | var xmlPath: String? 16 | var templatePath: String? 17 | var childrenTagIndex: Int? 18 | var relativePath: String? 19 | var rootURL: URL? 20 | 21 | func getClass() throws -> AnyClass { 22 | guard let cls: AnyClass = classFromString(className) else { 23 | throw LayoutError.message("Unknown class \(className)") 24 | } 25 | return cls 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Snapshots/Layout/Layout/LayoutBacked.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import Foundation 4 | 5 | /// Protocol for views or controllers that are backed by a LayoutNode 6 | /// Exposes the node reference so that the view can update itself 7 | public protocol LayoutBacked: class { 8 | /** weak */ var layoutNode: LayoutNode? { get } 9 | } 10 | 11 | public extension LayoutBacked where Self: NSObject { 12 | /// Default implementation of the layoutNode property 13 | internal(set) weak var layoutNode: LayoutNode? { 14 | get { return _layoutNode } 15 | set { _setLayoutNode(layoutNode, retained: false) } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Snapshots/Layout/Layout/LayoutDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import Foundation 4 | 5 | /// Optional delegate protocol to be implemented by a LayoutNode's owner 6 | public protocol LayoutDelegate: class { 7 | /// Handle errors produced by the layout during an update 8 | /// The default implementation displays a red box error alert using the LayoutConsole 9 | func layoutError(_ error: LayoutError) 10 | 11 | /// A variable or constant value inherited from the delegate 12 | /// Layout will call this method for any expression symbol that it doesn't 13 | /// recognize. If the method returns nil, an error will be thrown 14 | func layoutValue(forKey key: String) throws -> Any? 15 | } 16 | 17 | public extension LayoutDelegate { 18 | /// Default error handler implementation - bubbles error up to the first responder 19 | /// that will handle it, or displays LayoutConsole if no handler is found 20 | func layoutError(_ error: LayoutError) { 21 | DispatchQueue.main.async { 22 | var responder = (self as? UIResponder)?.next 23 | while responder != nil { 24 | if let errorHandler = responder as? LayoutLoading { 25 | errorHandler.layoutError(error) 26 | return 27 | } 28 | responder = responder?.next ?? (responder as? UIViewController)?.parent 29 | } 30 | LayoutConsole.showError(error) 31 | } 32 | } 33 | 34 | /// Default implementation - returns nothing 35 | func layoutValue(forKey _: String) throws -> Any? { 36 | return nil 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Snapshots/Layout/Layout/LayoutNode+XML.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import Foundation 4 | 5 | public extension LayoutNode { 6 | @available(*, deprecated, renamed: "init(xmlData:)") 7 | static func with(xmlData: Data, url: URL? = nil, relativeTo: String? = #file) throws -> LayoutNode { 8 | return try LayoutNode( 9 | layout: Layout(xmlData: xmlData, url: url, relativeTo: relativeTo) 10 | ) 11 | } 12 | 13 | /// Creates a LayoutNode from a parse XML file 14 | /// The optional `url` parameter tells Layout where the node was loded from 15 | /// The optional` relativeTo` parameter helps to locate the original source file 16 | convenience init(xmlData: Data, url: URL? = nil, relativeTo: String? = #file) throws { 17 | try self.init(layout: Layout(xmlData: xmlData, url: url, relativeTo: relativeTo)) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Snapshots/Layout/Layout/LayoutViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import UIKit 4 | 5 | @available(*, deprecated, message: "Use the LayoutLoading protocol instead") 6 | open class LayoutViewController: UIViewController, LayoutLoading { 7 | /// Called immediately after the layoutNode is set. Will not be called 8 | /// in the event of an error, or if layoutNode is set to nil 9 | open func layoutDidLoad(_: LayoutNode) { 10 | // Mimic old behaviour if not overridden 11 | layoutDidLoad() 12 | } 13 | 14 | /// Called immediately after the layoutNode is set. Will not be called 15 | /// in the event of an error, or if layoutNode is set to nil 16 | @available(*, deprecated, message: "Use layoutDidLoad(_ layoutNode:) instead") 17 | open func layoutDidLoad() { 18 | // Override in subclass 19 | } 20 | 21 | /// Default error handler implementation - bubbles error up to the first responder 22 | /// that will handle it, or displays LayoutConsole if no handler is found 23 | open func layoutError(_ error: LayoutError) { 24 | DispatchQueue.main.async { 25 | var responder = self.next 26 | while responder != nil { 27 | if let errorHandler = responder as? LayoutLoading { 28 | errorHandler.layoutError(error) 29 | return 30 | } 31 | responder = responder?.next ?? (responder as? UIViewController)?.parent 32 | } 33 | LayoutConsole.showError(error) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Snapshots/Layout/Layout/Shared/Shared.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | /// Expressions that affect layout 4 | /// These are common to every Layout node 5 | /// These are all of type CGFloat, apart from `center` which is a CGPoint 6 | let layoutSymbols: Set = [ 7 | "left", "right", "leading", "trailing", 8 | "width", "top", "bottom", "height", "center", 9 | "center.x", "center.y", "firstBaseline", "lastBaseline", 10 | ] 11 | 12 | /// HTML tags that should not contain children 13 | /// http://w3c.github.io/html/syntax.html#void-elements 14 | let emptyHTMLTags: Set = [ 15 | "area", "base", "br", "col", "embed", "hr", 16 | "img", "input", "link", "meta", "param", 17 | "source", "track", "wbr", 18 | ] 19 | -------------------------------------------------------------------------------- /Snapshots/Layout/Layout/TitleTextAttributes.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import UIKit 4 | 5 | /// Common attributes shared by any view with a titleTextAttributes property 6 | /// The purpose of this protocol is to ensure consistent support between components 7 | @objc protocol TitleTextAttributes { 8 | var titleColor: UIColor? { get set } 9 | var titleFont: UIFont? { get set } 10 | } 11 | -------------------------------------------------------------------------------- /Snapshots/Layout/LayoutTests/EnumExpressionTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import XCTest 4 | @testable import Layout 5 | 6 | class EnumExpressionTests: XCTestCase { 7 | func testContentModeLiteral() { 8 | let node = LayoutNode( 9 | expressions: [ 10 | "contentMode": "center", 11 | ] 12 | ) 13 | let expected = UIView.ContentMode.center 14 | XCTAssertEqual(try node.value(forSymbol: "contentMode") as? UIView.ContentMode, expected) 15 | } 16 | 17 | func testContentModeConstant() { 18 | let expected = UIView.ContentMode.center 19 | let node = LayoutNode( 20 | constants: [ 21 | "mode": expected, 22 | ], 23 | expressions: [ 24 | "contentMode": "mode", 25 | ] 26 | ) 27 | XCTAssertEqual(try node.value(forSymbol: "contentMode") as? UIView.ContentMode, expected) 28 | } 29 | 30 | func testReturnKeyLiteral() { 31 | let node = LayoutNode( 32 | view: UITextField(), 33 | expressions: [ 34 | "returnKeyType": "go", 35 | ] 36 | ) 37 | let expected = UIReturnKeyType.go 38 | XCTAssertEqual(try node.value(forSymbol: "returnKeyType") as? UIReturnKeyType, expected) 39 | } 40 | 41 | func testReturnKeyConstant() { 42 | let expected = UIReturnKeyType.go 43 | let node = LayoutNode( 44 | view: UITextField(), 45 | constants: [ 46 | "type": expected, 47 | ], 48 | expressions: [ 49 | "returnKeyType": "type", 50 | ] 51 | ) 52 | XCTAssertEqual(try node.value(forSymbol: "returnKeyType") as? UIReturnKeyType, expected) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Snapshots/Layout/LayoutTests/OptionSetExpressionTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import XCTest 4 | @testable import Layout 5 | 6 | class OptionSetExpressionTests: XCTestCase { 7 | func testSingleDataDetectorType() { 8 | let node = LayoutNode( 9 | view: UITextView(), 10 | expressions: [ 11 | "dataDetectorTypes": "phoneNumber", 12 | ] 13 | ) 14 | XCTAssertEqual(try node.value(forSymbol: "dataDetectorTypes") as? UIDataDetectorTypes, .phoneNumber) 15 | } 16 | 17 | func testMultipleDataDetectorTypes() { 18 | let node = LayoutNode( 19 | view: UITextView(), 20 | expressions: [ 21 | "dataDetectorTypes": "phoneNumber, address, link", 22 | ] 23 | ) 24 | XCTAssertEqual(try node.value(forSymbol: "dataDetectorTypes") as? UIDataDetectorTypes, [.phoneNumber, .address, .link]) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Snapshots/Layout/LayoutTests/SelectorExpressionTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import XCTest 4 | @testable import Layout 5 | 6 | private class TestView: UIView { 7 | @objc var action: Selector? 8 | } 9 | 10 | private class TestViewController: UIViewController { 11 | @objc func foo(_: UIView) { 12 | print("It works!") 13 | } 14 | } 15 | 16 | class SelectorExpressionTests: XCTestCase { 17 | func testSetControlAction() { 18 | let node = LayoutNode(view: UIControl(), expressions: ["touchUpInside": "foo:"]) 19 | let viewController = TestViewController() 20 | XCTAssertNoThrow(try node.mount(in: viewController)) 21 | let control = node.view as! UIControl 22 | XCTAssertEqual(control.actions(forTarget: viewController, forControlEvent: .touchUpInside)?.first, "foo:") 23 | } 24 | 25 | func testSetCustomSelector() { 26 | let node = LayoutNode(view: TestView(), expressions: ["action": "foo:"]) 27 | node.update() 28 | XCTAssertNotNil(node.view.action) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Snapshots/Layout/LayoutTests/TableViewTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import UIKit 4 | import XCTest 5 | @testable import Layout 6 | 7 | class TableViewController: UIViewController, LayoutLoading, UITableViewDataSource, UITableViewDelegate { 8 | @objc var tableView: UITableView? 9 | var didLoadRows = false 10 | 11 | override func viewDidLoad() { 12 | super.viewDidLoad() 13 | edgesForExtendedLayout = [] 14 | loadLayout(named: "TableViewTest.xml", bundle: Bundle(for: type(of: self))) 15 | } 16 | 17 | func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int { 18 | didLoadRows = true 19 | return 5 20 | } 21 | 22 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 23 | let node = tableView.dequeueReusableCellNode(withIdentifier: "testCell", for: indexPath) 24 | node.setState([]) 25 | return node.view as! UITableViewCell 26 | } 27 | } 28 | 29 | class TableViewTests: XCTestCase { 30 | func testTableCellSizing() { 31 | let vc = TableViewController() 32 | vc.view.frame = CGRect(x: 0, y: 0, width: 512, height: 512) 33 | XCTAssertNotNil(vc.tableView) 34 | vc.tableView?.reloadData() 35 | XCTAssert(vc.didLoadRows) 36 | guard let cell = vc.tableView?.cellForRow(at: IndexPath(row: 0, section: 0)) else { 37 | XCTFail() 38 | return 39 | } 40 | XCTAssertEqual(cell.frame.width, 512) 41 | guard let cellLayoutView = cell.contentView.subviews.first else { 42 | XCTFail() 43 | return 44 | } 45 | XCTAssertNotNil(cellLayoutView._layoutNode) 46 | XCTAssertEqual(cellLayoutView.frame.width, 512) 47 | XCTAssertEqual(cellLayoutView.frame.origin.x, 0) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Snapshots/Layout/LayoutToolTests/RenamerTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import XCTest 4 | 5 | class RenamerTests: XCTestCase { 6 | func testRenameStandaloneVariable() { 7 | let input = "" 8 | let expected = "\n" 9 | let output = try! rename("foo", to: "bar", in: input) 10 | XCTAssertEqual(output, expected) 11 | } 12 | 13 | func testRenameVariableInExpression() { 14 | let input = "" 15 | let expected = "\n" 16 | let output = try! rename("foo", to: "bar", in: input) 17 | XCTAssertEqual(output, expected) 18 | } 19 | 20 | func testNoRenameTextInStringExpression() { 21 | let input = "" 22 | let expected = "\n" 23 | let output = try! rename("foo", to: "bar", in: input) 24 | XCTAssertEqual(output, expected) 25 | } 26 | 27 | func testRenameVariableInEscapedStringExpression() { 28 | let input = "" 29 | let expected = "\n" 30 | let output = try! rename("foo", to: "bar", in: input) 31 | XCTAssertEqual(output, expected) 32 | } 33 | 34 | func testRenameClass() { 35 | let input = "" 36 | let expected = "\n" 37 | let output = try! rename("Foo", to: "Bar", in: input) 38 | XCTAssertEqual(output, expected) 39 | } 40 | 41 | func testNoRenameHTML() { 42 | let input = "\n
foo
\n
\n" 43 | let expected = "\n
foo
\n
\n" 44 | let output = try! rename("center", to: "centered", in: input) 45 | XCTAssertEqual(output, expected) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Snapshots/Layout/LayoutToolTests/ReturnCodeTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import XCTest 4 | 5 | class ReturnCodeTests: XCTestCase { 6 | func testReturnsSuccessCodeForExpectedInput() { 7 | XCTAssertEqual(processArguments(["LayoutTool", "version"]), .success) 8 | } 9 | 10 | func testReturnsErrorCodeWhenErrorsOccur() { 11 | XCTAssertEqual(processArguments(["LayoutTool"]), .failure) 12 | XCTAssertEqual(processArguments(["LayoutTool", "format"]), .failure) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Snapshots/Layout/LayoutToolTests/StringsTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import XCTest 4 | 5 | class StringsTests: XCTestCase { 6 | func testFindStringsInAttributes() { 7 | let input = "" 8 | let output = ["bar", "foo"] 9 | XCTAssertEqual(try strings(in: input), output) 10 | } 11 | 12 | func testFindStringsInBody() { 13 | let input = "{strings.foo} {strings.bar}" 14 | let output = ["bar", "foo"] 15 | XCTAssertEqual(try strings(in: input), output) 16 | } 17 | 18 | func testIgnoreDuplicateStrings() { 19 | let input = "{strings.foo} {strings.bar} {strings.foo}" 20 | let output = ["bar", "foo"] 21 | XCTAssertEqual(try strings(in: input), output) 22 | } 23 | 24 | func testFindEscapedString() { 25 | let input = "" 26 | let output = ["hello\nworld"] 27 | XCTAssertEqual(try strings(in: input), output) 28 | } 29 | 30 | func testFindParameterizedString() { 31 | let input = "" 32 | let output = ["foo"] 33 | XCTAssertEqual(try strings(in: input), output) 34 | } 35 | 36 | func testFindEscapedParameterizedString() { 37 | let input = "" 38 | let output = ["foo"] 39 | XCTAssertEqual(try strings(in: input), output) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Snapshots/Layout/SampleApp/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import Layout 4 | import UIKit 5 | 6 | #if !swift(>=4.2) 7 | 8 | extension UIApplication { 9 | typealias LaunchOptionsKey = UIApplicationLaunchOptionsKey 10 | } 11 | 12 | #endif 13 | 14 | @UIApplicationMain 15 | class AppDelegate: UIResponder, UIApplicationDelegate { 16 | var window: UIWindow? 17 | 18 | func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 19 | LayoutNode.useLegacyLayoutMode = false 20 | 21 | window = UIWindow() 22 | window?.rootViewController = ExamplesViewController() 23 | window?.makeKeyAndVisible() 24 | return true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Snapshots/Layout/SampleApp/BoxesViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import Layout 4 | import UIKit 5 | 6 | class BoxesViewController: UIViewController { 7 | var toggled = false { 8 | didSet { 9 | layoutNode?.setState(["isToggled": toggled]) 10 | } 11 | } 12 | 13 | @IBOutlet var layoutNode: LayoutNode? { 14 | didSet { 15 | layoutNode?.setState(["isToggled": toggled]) 16 | } 17 | } 18 | 19 | @IBAction func setToggled() { 20 | UIView.animate(withDuration: 0.4) { 21 | self.toggled = true 22 | } 23 | } 24 | 25 | @IBAction func setUntoggled() { 26 | UIView.animate(withDuration: 0.4) { 27 | self.toggled = false 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Snapshots/Layout/SampleApp/CollectionViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import Layout 4 | import UIKit 5 | 6 | private let images = [ 7 | UIImage(named: "Boxes"), 8 | UIImage(named: "Pages"), 9 | UIImage(named: "Text"), 10 | UIImage(named: "Table"), 11 | UIImage(named: "Collection"), 12 | UIImage(named: "Rocket"), 13 | ] 14 | 15 | class CollectionViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource { 16 | @IBOutlet var collectionView: UICollectionView? { 17 | didSet { 18 | collectionView?.registerLayout( 19 | named: "CollectionCell.xml", 20 | forCellReuseIdentifier: "standaloneCell" 21 | ) 22 | } 23 | } 24 | 25 | func collectionView(_: UICollectionView, numberOfItemsInSection _: Int) -> Int { 26 | return 500 27 | } 28 | 29 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 30 | let identifier = (indexPath.row % 2 == 0) ? "templateCell" : "standaloneCell" 31 | let node = collectionView.dequeueReusableCellNode(withIdentifier: identifier, for: indexPath) 32 | let image = images[indexPath.row % images.count]! 33 | 34 | node.setState([ 35 | "row": indexPath.row, 36 | "image": image, 37 | "whiteImage": image.withRenderingMode(.alwaysOriginal), 38 | ]) 39 | 40 | return node.view as! UICollectionViewCell 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Snapshots/Layout/SampleApp/PagesViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import UIKit 4 | 5 | class PagesViewController: UIViewController, UIScrollViewDelegate { 6 | @IBOutlet var scrollView: UIScrollView? 7 | @IBOutlet var pageControl: UIPageControl? 8 | 9 | func scrollViewDidScroll(_ scrollView: UIScrollView) { 10 | if scrollView === self.scrollView { 11 | pageControl?.currentPage = Int(round(scrollView.contentOffset.x / scrollView.frame.width)) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Snapshots/Layout/SampleApp/TableViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import Layout 4 | import UIKit 5 | 6 | private let images = [ 7 | UIImage(named: "Boxes"), 8 | UIImage(named: "Pages"), 9 | UIImage(named: "Text"), 10 | UIImage(named: "Table"), 11 | UIImage(named: "Collection"), 12 | UIImage(named: "Rocket"), 13 | ] 14 | 15 | class TableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { 16 | @IBOutlet var tableView: UITableView? { 17 | didSet { 18 | tableView?.registerLayout( 19 | named: "TableCell.xml", 20 | forCellReuseIdentifier: "standaloneCell" 21 | ) 22 | } 23 | } 24 | 25 | func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int { 26 | return 500 27 | } 28 | 29 | func tableView(_ tableView: UITableView, viewForHeaderInSection _: Int) -> UIView? { 30 | let node = tableView.dequeueReusableHeaderFooterNode(withIdentifier: "templateHeader") 31 | return node?.view as? UITableViewHeaderFooterView 32 | } 33 | 34 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 35 | let identifier = (indexPath.row % 2 == 0) ? "templateCell" : "standaloneCell" 36 | let node = tableView.dequeueReusableCellNode(withIdentifier: identifier, for: indexPath) 37 | let image = images[indexPath.row % images.count]! 38 | 39 | node.setState([ 40 | "row": indexPath.row, 41 | "image": image, 42 | "whiteImage": image.withRenderingMode(.alwaysOriginal), 43 | ]) 44 | 45 | return node.view as! UITableViewCell 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Snapshots/Layout/SampleApp/UIColor+Hex.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import UIKit 4 | 5 | public extension UIColor { 6 | convenience init?(hexString: String) { 7 | if hexString.hasPrefix("#") { 8 | var string = String(hexString.dropFirst()) 9 | switch string.count { 10 | case 3: 11 | string += "f" 12 | fallthrough 13 | case 4: 14 | let chars = Array(string) 15 | let red = chars[0] 16 | let green = chars[1] 17 | let blue = chars[2] 18 | let alpha = chars[3] 19 | string = "\(red)\(red)\(green)\(green)\(blue)\(blue)\(alpha)\(alpha)" 20 | case 6: 21 | string += "ff" 22 | case 8: 23 | break 24 | default: 25 | return nil 26 | } 27 | if let rgba = Double("0x" + string).flatMap({ UInt32(exactly: $0) }) { 28 | let red = CGFloat((rgba & 0xFF00_0000) >> 24) / 255 29 | let green = CGFloat((rgba & 0x00FF_0000) >> 16) / 255 30 | let blue = CGFloat((rgba & 0x0000_FF00) >> 8) / 255 31 | let alpha = CGFloat((rgba & 0x0000_00FF) >> 0) / 255 32 | self.init(red: red, green: green, blue: blue, alpha: alpha) 33 | return 34 | } 35 | } 36 | return nil 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Snapshots/Layout/Sandbox/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import UIKit 4 | 5 | @UIApplicationMain 6 | class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate { 7 | var window: UIWindow? 8 | 9 | func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 10 | window = UIWindow() 11 | 12 | let editController = EditViewController() 13 | window?.rootViewController = UINavigationController(rootViewController: editController) 14 | editController.showPreview(animated: false) 15 | 16 | window?.makeKeyAndVisible() 17 | return true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Snapshots/Layout/TestFramework/TestFramework.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import Foundation 4 | 5 | public class TestFrameworkClass {} 6 | -------------------------------------------------------------------------------- /Snapshots/Layout/UIDesigner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Schibsted. All rights reserved. 2 | 3 | import UIKit 4 | 5 | @UIApplicationMain 6 | class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate { 7 | var window: UIWindow? 8 | 9 | func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 10 | precondition(UI_USER_INTERFACE_IDIOM() == .pad, 11 | "This application is designed for iPad only") 12 | 13 | window = UIWindow() 14 | 15 | let splitViewController = UISplitViewController() 16 | let navigationController = UINavigationController() 17 | navigationController.viewControllers = [TreeViewController()] 18 | splitViewController.viewControllers = [navigationController, DesignViewController()] 19 | splitViewController.delegate = self 20 | 21 | // Hide the tree view for now, as it needs some improvements 22 | splitViewController.preferredDisplayMode = .primaryHidden 23 | 24 | window?.rootViewController = splitViewController 25 | window?.makeKeyAndVisible() 26 | 27 | return true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Snapshots/Parsing/.swiftformat: -------------------------------------------------------------------------------- 1 | # format options 2 | 3 | --patternlet inline 4 | --self insert 5 | 6 | # rules 7 | 8 | --disable trailingClosures, blankLinesAtStartOfScope, sortedImports 9 | -------------------------------------------------------------------------------- /Snapshots/Parsing/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Nick Lockwood 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Snapshots/Parsing/Sources/Formatter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Formatter.swift 3 | // Parsing 4 | // 5 | // Created by Nick Lockwood on 04/09/2018. 6 | // Copyright © 2018 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: interface 12 | 13 | public func format(_ program: [Statement]) -> String { 14 | var output = "" 15 | for statement in program { 16 | output.append(statement.description + "\n") 17 | } 18 | return output 19 | } 20 | 21 | // MARK: implementation 22 | 23 | extension Statement: CustomStringConvertible { 24 | 25 | public var description: String { 26 | switch self { 27 | case .declaration(name: let name, value: let expression): 28 | return "let \(name) = \(expression)" 29 | case .print(let expression): 30 | return "print \(expression)" 31 | } 32 | } 33 | } 34 | 35 | extension Expression: CustomStringConvertible { 36 | 37 | public var description: String { 38 | switch self { 39 | case .number(let double): 40 | return String(format: "%g", double) 41 | case .string(let string): 42 | let escapedString = string 43 | .replacingOccurrences(of: "\\", with: "\\\\") 44 | .replacingOccurrences(of: "\"", with: "\\\"") 45 | return "\"\(escapedString)\"" 46 | case .variable(let name): 47 | return name 48 | case .addition(lhs: let lhs, rhs: let rhs): 49 | return "\(lhs) + \(rhs)" 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Snapshots/Parsing/Sources/Parsing.h: -------------------------------------------------------------------------------- 1 | // 2 | // Parsing.h 3 | // Parsing 4 | // 5 | // Created by Nick Lockwood on 03/09/2018. 6 | // Copyright © 2018 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Parsing. 12 | FOUNDATION_EXPORT double ParsingVersionNumber; 13 | 14 | //! Project version string for Parsing. 15 | FOUNDATION_EXPORT const unsigned char ParsingVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Snapshots/Parsing/Tests/FormatterTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FormatterTests.swift 3 | // ParsingTests 4 | // 5 | // Created by Nick Lockwood on 04/09/2018. 6 | // Copyright © 2018 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Parsing 11 | 12 | class FormatterTests: XCTestCase { 13 | 14 | func testFormatting() throws { 15 | let input = """ 16 | let foo = 5 + 6 17 | let bar = "hello\\\\world" 18 | let baz = "goodbye \\"world\\"" 19 | print foo + bar + baz 20 | """ 21 | let program = try parse(input) 22 | let output = format(program) 23 | XCTAssertEqual(output, input + "\n") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Snapshots/README.md: -------------------------------------------------------------------------------- 1 | The files in this directory constitute a regression test suite, used to check for unintended changes to SwiftFormat's behavior. 2 | 3 | Projects in this directory include only the swift files and documentation/licence information. There are missing their project files and other resources, and in most cases will not compile. If you are interested in using these libraries, you will find them on Github. 4 | 5 | If you would like to add your own projects to this test suit, please [open a pull request](https://nicklockwood/SwiftFormat). Be sure to remove all non-swift files apart from documentation and SwiftFormat configuration. 6 | -------------------------------------------------------------------------------- /Snapshots/Sprinter/.swift-version: -------------------------------------------------------------------------------- 1 | 4.0 -------------------------------------------------------------------------------- /Snapshots/Sprinter/.swiftformat: -------------------------------------------------------------------------------- 1 | --hexgrouping ignore 2 | --decimalgrouping ignore 3 | --enable isEmpty 4 | --ifdef no-indent 5 | -------------------------------------------------------------------------------- /Snapshots/Sprinter/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Nick Lockwood 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Snapshots/Sprinter/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "Sprinter" 6 | ) 7 | -------------------------------------------------------------------------------- /Snapshots/SwiftUI/.swiftformat: -------------------------------------------------------------------------------- 1 | --swiftversion 5.2 2 | --indent 2 -------------------------------------------------------------------------------- /Snapshots/SwiftUI/Bookworm/EmojiRatingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmojiRatingView.swift 3 | // Bookworm 4 | // 5 | // Created by Nick Lockwood on 30/06/2020. 6 | // Copyright © 2020 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct EmojiRatingView: View { 12 | let rating: Int16 13 | 14 | var body: some View { 15 | switch rating { 16 | case 1: 17 | return Text("😴") 18 | case 2: 19 | return Text("☹️") 20 | case 3: 21 | return Text("😐") 22 | case 4: 23 | return Text("😄") 24 | default: 25 | return Text("🤩") 26 | } 27 | } 28 | } 29 | 30 | struct EmojiRatingView_Previews: PreviewProvider { 31 | static var previews: some View { 32 | EmojiRatingView(rating: 3) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Snapshots/SwiftUI/Bookworm/RatingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RatingView.swift 3 | // Bookworm 4 | // 5 | // Created by Nick Lockwood on 30/06/2020. 6 | // Copyright © 2020 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct RatingView: View { 12 | @Binding var rating: Int 13 | 14 | var label = "" 15 | 16 | var maximumRating = 5 17 | 18 | var offImage: Image? 19 | var onImage = Image(systemName: "star.fill") 20 | 21 | var offColor = Color.gray 22 | var onColor = Color.yellow 23 | 24 | var body: some View { 25 | HStack { 26 | if label.isEmpty == false { 27 | Text(label) 28 | } 29 | 30 | ForEach(1 ..< maximumRating + 1) { number in 31 | self.image(for: number) 32 | .foregroundColor(number > self.rating ? self.offColor : self.onColor) 33 | .onTapGesture { 34 | self.rating = number 35 | } 36 | } 37 | } 38 | } 39 | 40 | func image(for number: Int) -> Image { 41 | number > rating ? offImage ?? onImage : onImage 42 | } 43 | } 44 | 45 | struct RatingView_Previews: PreviewProvider { 46 | static var previews: some View { 47 | RatingView(rating: .constant(4)) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Snapshots/SwiftUI/CupcakeCorner/AddressView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddressView.swift 3 | // CupcakeCorner 4 | // 5 | // Created by Nick Lockwood on 28/06/2020. 6 | // Copyright © 2020 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct AddressView: View { 12 | @ObservedObject var order = Order() 13 | 14 | var body: some View { 15 | Form { 16 | Section { 17 | TextField("Name", text: $order.data.name) 18 | TextField("Street Address", text: $order.data.streetAddress) 19 | TextField("City", text: $order.data.city) 20 | TextField("Zip", text: $order.data.zip) 21 | } 22 | 23 | Section { 24 | NavigationLink(destination: CheckoutView(order: order)) { 25 | Text("Check out") 26 | } 27 | }.disabled(!order.data.hasValidAddress) 28 | } 29 | .navigationBarTitle("Delivery details", displayMode: .inline) 30 | } 31 | } 32 | 33 | struct AddressView_Previews: PreviewProvider { 34 | static var previews: some View { 35 | AddressView(order: Order()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Snapshots/SwiftUI/CupcakeCorner/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // CupcakeCorner 4 | // 5 | // Created by Nick Lockwood on 28/06/2020. 6 | // Copyright © 2020 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct ContentView: View { 12 | @ObservedObject var order = Order() 13 | 14 | var body: some View { 15 | NavigationView { 16 | Form { 17 | Section { 18 | Picker("Select you cake type", selection: $order.data.type) { 19 | ForEach(0 ..< Order.types.count) { 20 | Text(Order.types[$0]) 21 | } 22 | } 23 | 24 | Stepper(value: $order.data.quantity, in: 3 ... 20) { 25 | Text("Number of cakes: \(order.data.quantity)") 26 | } 27 | } 28 | 29 | Section { 30 | Toggle(isOn: $order.data.specialRequestEnabled.animation()) { 31 | Text("Any special requests?") 32 | } 33 | 34 | if order.data.specialRequestEnabled { 35 | Toggle(isOn: $order.data.extraFrosting) { 36 | Text("Add extra frosting") 37 | } 38 | 39 | Toggle(isOn: $order.data.addSprinkles) { 40 | Text("Add extra sprinkles") 41 | } 42 | } 43 | } 44 | 45 | Section { 46 | NavigationLink(destination: AddressView(order: order)) { 47 | Text("Delivery details") 48 | } 49 | } 50 | } 51 | .navigationBarTitle("Cupcake Corner") 52 | } 53 | } 54 | } 55 | 56 | struct ContentView_Previews: PreviewProvider { 57 | static var previews: some View { 58 | ContentView() 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Snapshots/SwiftUI/CupcakeCorner/Order.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Order.swift 3 | // CupcakeCorner 4 | // 5 | // Created by Nick Lockwood on 28/06/2020. 6 | // Copyright © 2020 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Order: ObservableObject, Codable { 12 | struct Data: Codable { 13 | var type = 0 14 | var quantity = 3 15 | 16 | var specialRequestEnabled = false { 17 | didSet { 18 | if specialRequestEnabled == false { 19 | extraFrosting = false 20 | addSprinkles = false 21 | } 22 | } 23 | } 24 | 25 | var extraFrosting = false 26 | var addSprinkles = false 27 | 28 | var name = "" 29 | var streetAddress = "" 30 | var city = "" 31 | var zip = "" 32 | 33 | var hasValidAddress: Bool { 34 | if name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty 35 | || streetAddress.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty 36 | || city.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty 37 | || zip.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty 38 | { 39 | return false 40 | } 41 | return true 42 | } 43 | 44 | var cost: Double { 45 | var cost = Double(quantity * 2) 46 | cost += Double(type) / 2 47 | 48 | if extraFrosting { 49 | cost += Double(quantity) 50 | } 51 | 52 | if addSprinkles { 53 | cost += Double(quantity) / 2 54 | } 55 | 56 | return cost 57 | } 58 | } 59 | 60 | static let types = ["Vanilla", "Strawberry", "Chocolate", "Rainbow"] 61 | 62 | @Published var data = Data() 63 | 64 | init() {} 65 | 66 | func encode(to encoder: Encoder) throws { 67 | try data.encode(to: encoder) 68 | } 69 | 70 | required init(from decoder: Decoder) throws { 71 | data = try Data(from: decoder) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Snapshots/SwiftUI/Moonshot/Astronaut.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Astronaut.swift 3 | // Moonshot 4 | // 5 | // Created by Nick Lockwood on 27/06/2020. 6 | // Copyright © 2020 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | struct Astronaut: Codable, Identifiable { 10 | var id: String 11 | var name: String 12 | var description: String 13 | } 14 | -------------------------------------------------------------------------------- /Snapshots/SwiftUI/Moonshot/Bundle+Decodable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bundle+Decodable.swift 3 | // Moonshot 4 | // 5 | // Created by Nick Lockwood on 27/06/2020. 6 | // Copyright © 2020 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Bundle { 12 | func decode(_ file: String) -> T { 13 | guard let url = url(forResource: file, withExtension: nil) else { 14 | fatalError("Failed to locate \(file) in bundle.") 15 | } 16 | 17 | guard let data = try? Data(contentsOf: url) else { 18 | fatalError("Failed to load \(file) from bundle.") 19 | } 20 | 21 | let decoder = JSONDecoder() 22 | let dateFormatter = DateFormatter() 23 | dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) 24 | dateFormatter.locale = Locale(identifier: "en_US_POSIX") 25 | dateFormatter.dateFormat = "y-MM-dd" 26 | decoder.dateDecodingStrategy = .formatted(dateFormatter) 27 | guard let loaded = try? decoder.decode(T.self, from: data) else { 28 | fatalError("Failed to decode \(file) from bundle.") 29 | } 30 | 31 | return loaded 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Snapshots/SwiftUI/Moonshot/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // Moonshot 4 | // 5 | // Created by Nick Lockwood on 27/06/2020. 6 | // Copyright © 2020 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct ContentView: View { 12 | let astronauts: [Astronaut] = Bundle.main.decode("astronauts.json") 13 | let missions: [Mission] = Bundle.main.decode("missions.json") 14 | 15 | @State var showCrew = false 16 | 17 | var body: some View { 18 | NavigationView { 19 | List(missions) { mission in 20 | NavigationLink( 21 | destination: MissionView(mission: mission) 22 | ) { 23 | Image(mission.image) 24 | .resizable() 25 | .scaledToFit() 26 | .frame(width: 44, height: 44) 27 | 28 | VStack(alignment: .leading) { 29 | Text(mission.displayName) 30 | .font(.headline) 31 | Text(self.showCrew ? 32 | self.astronauts(in: mission) : 33 | mission.formattedLaunchDate 34 | ) 35 | } 36 | } 37 | } 38 | .navigationBarTitle("Moonshot") 39 | .navigationBarItems(trailing: Button(action: { 40 | self.showCrew.toggle() 41 | }) { 42 | Text(self.showCrew ? "Show Date" : "Show Crew") 43 | }) 44 | } 45 | } 46 | 47 | func astronauts(in mission: Mission) -> String { 48 | astronauts.filter { astronaut in 49 | mission.crew.contains(where: { $0.name == astronaut.id }) 50 | } 51 | .map(\.name) 52 | .joined(separator: ", ") 53 | } 54 | } 55 | 56 | struct ContentView_Previews: PreviewProvider { 57 | static var previews: some View { 58 | ContentView() 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Snapshots/SwiftUI/Moonshot/Mission.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mission.swift 3 | // Moonshot 4 | // 5 | // Created by Nick Lockwood on 27/06/2020. 6 | // Copyright © 2020 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Mission: Codable, Identifiable { 12 | struct CrewRole: Codable { 13 | var name: String 14 | var role: String 15 | } 16 | 17 | var id: Int 18 | var launchDate: Date? 19 | var crew: [CrewRole] 20 | var description: String 21 | 22 | var displayName: String { 23 | "Apollo \(id)" 24 | } 25 | 26 | var image: String { 27 | "apollo\(id)" 28 | } 29 | 30 | var formattedLaunchDate: String { 31 | guard let date = launchDate else { 32 | return "N/A" 33 | } 34 | let formatter = DateFormatter() 35 | formatter.dateStyle = .long 36 | return formatter.string(from: date) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Snapshots/SwiftUI/README.md: -------------------------------------------------------------------------------- 1 | These are simple SwiftUI projects based on Paul Hudson's [100 days of SwiftUI](https://www.hackingwithswift.com/100/swiftui) tutorial series. -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2016 Nick Lockwood. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Sources/Rules/AnyObjectProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnyObjectProtocol.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 1/23/19. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Prefer `AnyObject` over `class` for class-based protocols 13 | static let anyObjectProtocol = FormatRule( 14 | help: "Prefer `AnyObject` over `class` in protocol definitions." 15 | ) { formatter in 16 | formatter.forEach(.keyword("protocol")) { i, _ in 17 | guard formatter.options.swiftVersion >= "4.1", 18 | let nameIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: i, if: { 19 | $0.isIdentifier 20 | }), let colonIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: nameIndex, if: { 21 | $0 == .delimiter(":") 22 | }), let classIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: colonIndex, if: { 23 | $0 == .keyword("class") 24 | }) 25 | else { 26 | return 27 | } 28 | formatter.replaceToken(at: classIndex, with: .identifier("AnyObject")) 29 | } 30 | } examples: { 31 | """ 32 | ```diff 33 | - protocol Foo: class {} 34 | + protocol Foo: AnyObject {} 35 | ``` 36 | 37 | **NOTE:** The guideline to use `AnyObject` instead of `class` was only 38 | introduced in Swift 4.1, so the `anyObjectProtocol` rule is disabled unless the 39 | swift version is set to 4.1 or above. 40 | """ 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Rules/ApplicationMain.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApplicationMain.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 5/20/23. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Replace the obsolete `@UIApplicationMain` and `@NSApplicationMain` 13 | /// attributes with `@main` in Swift 5.3 and above, per SE-0383 14 | static let applicationMain = FormatRule( 15 | help: """ 16 | Replace obsolete @UIApplicationMain and @NSApplicationMain attributes 17 | with @main for Swift 5.3 and above. 18 | """ 19 | ) { formatter in 20 | guard formatter.options.swiftVersion >= "5.3" else { 21 | return 22 | } 23 | formatter.forEachToken(where: { 24 | [ 25 | .keyword("@UIApplicationMain"), 26 | .keyword("@NSApplicationMain"), 27 | ].contains($0) 28 | }) { i, _ in 29 | formatter.replaceToken(at: i, with: .keyword("@main")) 30 | } 31 | } examples: { 32 | """ 33 | ```diff 34 | - @UIApplicationMain 35 | + @main 36 | class AppDelegate: UIResponder, UIApplicationDelegate {} 37 | ``` 38 | """ 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/Rules/BlankLineAfterImports.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BlankLineAfterImports.swift 3 | // SwiftFormat 4 | // 5 | // Created by Tsungyu Yu on 5/1/22. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Insert blank line after import statements 13 | static let blankLineAfterImports = FormatRule( 14 | help: "Insert blank line after import statements.", 15 | sharedOptions: ["linebreaks"] 16 | ) { formatter in 17 | formatter.forEach(.keyword("import")) { currentImportIndex, _ in 18 | guard let endOfLine = formatter.index(of: .linebreak, after: currentImportIndex), 19 | var nextIndex = formatter.index(of: .nonSpace, after: endOfLine) 20 | else { 21 | return 22 | } 23 | var keyword: Token = formatter.tokens[nextIndex] 24 | while keyword == .startOfScope("#if") || keyword.isModifierKeyword || keyword.isAttribute, 25 | let index = formatter.index(of: .keyword, after: nextIndex) 26 | { 27 | nextIndex = index 28 | keyword = formatter.tokens[nextIndex] 29 | } 30 | switch formatter.tokens[nextIndex] { 31 | case .linebreak, .keyword("import"), .keyword("#else"), .keyword("#elseif"), .endOfScope("#endif"): 32 | break 33 | default: 34 | formatter.insertLinebreak(at: endOfLine) 35 | } 36 | } 37 | } examples: { 38 | """ 39 | ```diff 40 | import A 41 | import B 42 | @testable import D 43 | + 44 | class Foo { 45 | // foo 46 | } 47 | ``` 48 | """ 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/Rules/BlankLinesBetweenImports.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BlankLinesBetweenImports.swift 3 | // SwiftFormat 4 | // 5 | // Created by Huy Vo on 9/28/21. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Remove blank lines between import statements 13 | static let blankLinesBetweenImports = FormatRule( 14 | help: """ 15 | Remove blank lines between import statements. 16 | """, 17 | disabledByDefault: true, 18 | sharedOptions: ["linebreaks"] 19 | ) { formatter in 20 | formatter.forEach(.keyword("import")) { currentImportIndex, _ in 21 | guard let endOfLine = formatter.index(of: .linebreak, after: currentImportIndex), 22 | let nextImportIndex = formatter.index(of: .nonSpaceOrLinebreak, after: endOfLine, if: { 23 | $0 == .keyword("@testable") || $0 == .keyword("import") 24 | }) 25 | else { 26 | return 27 | } 28 | 29 | formatter.replaceTokens(in: endOfLine ..< nextImportIndex, with: formatter.linebreakToken(for: currentImportIndex + 1)) 30 | } 31 | } examples: { 32 | """ 33 | ```diff 34 | import A 35 | - 36 | import B 37 | import C 38 | - 39 | - 40 | @testable import D 41 | import E 42 | ``` 43 | """ 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/Rules/ConsecutiveBlankLines.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConsecutiveBlankLines.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 8/30/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Collapse all consecutive blank lines into a single blank line 13 | static let consecutiveBlankLines = FormatRule( 14 | help: "Replace consecutive blank lines with a single blank line." 15 | ) { formatter in 16 | formatter.forEach(.linebreak) { i, _ in 17 | guard let prevIndex = formatter.index(of: .nonSpace, before: i, if: { $0.isLinebreak }) else { 18 | return 19 | } 20 | if let scope = formatter.currentScope(at: i), scope.isMultilineStringDelimiter { 21 | return 22 | } 23 | if let nextIndex = formatter.index(of: .nonSpace, after: i) { 24 | if formatter.tokens[nextIndex].isLinebreak { 25 | formatter.removeTokens(in: i + 1 ... nextIndex) 26 | } 27 | } else if !formatter.options.fragment { 28 | formatter.removeTokens(in: i ..< formatter.tokens.count) 29 | } 30 | } 31 | } examples: { 32 | """ 33 | ```diff 34 | func foo() { 35 | let x = "bar" 36 | - 37 | 38 | print(x) 39 | } 40 | 41 | func foo() { 42 | let x = "bar" 43 | 44 | print(x) 45 | } 46 | ``` 47 | """ 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/Rules/DuplicateImports.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DuplicateImports.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 2/7/18. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Remove duplicate import statements 13 | static let duplicateImports = FormatRule( 14 | help: "Remove duplicate import statements." 15 | ) { formatter in 16 | for var importRanges in formatter.parseImports().reversed() { 17 | for i in importRanges.indices.reversed() { 18 | let range = importRanges.remove(at: i) 19 | guard let j = importRanges.firstIndex(where: { $0.module == range.module }) else { 20 | continue 21 | } 22 | let range2 = importRanges[j] 23 | if Set(range.attributes).isSubset(of: range2.attributes) { 24 | formatter.removeTokens(in: range.range) 25 | continue 26 | } 27 | if j >= i { 28 | formatter.removeTokens(in: range2.range) 29 | importRanges.remove(at: j) 30 | } 31 | importRanges.append(range) 32 | } 33 | } 34 | } examples: { 35 | """ 36 | ```diff 37 | import Foo 38 | import Bar 39 | - import Foo 40 | ``` 41 | 42 | ```diff 43 | import B 44 | #if os(iOS) 45 | import A 46 | - import B 47 | #endif 48 | ``` 49 | """ 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Rules/EmptyBraces.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmptyBraces.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 8/2/18. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Remove white-space between empty braces 13 | static let emptyBraces = FormatRule( 14 | help: "Remove whitespace inside empty braces.", 15 | options: ["emptybraces"], 16 | sharedOptions: ["linebreaks"] 17 | ) { formatter in 18 | formatter.forEach(.startOfScope("{")) { i, _ in 19 | guard let closingIndex = formatter.index(of: .nonSpaceOrLinebreak, after: i, if: { 20 | $0 == .endOfScope("}") 21 | }) else { 22 | return 23 | } 24 | if let token = formatter.next(.nonSpaceOrComment, after: closingIndex), 25 | [.keyword("else"), .keyword("catch")].contains(token) 26 | { 27 | return 28 | } 29 | let range = i + 1 ..< closingIndex 30 | switch formatter.options.emptyBracesSpacing { 31 | case .noSpace: 32 | formatter.removeTokens(in: range) 33 | case .spaced: 34 | formatter.replaceTokens(in: range, with: .space(" ")) 35 | case .linebreak: 36 | formatter.insertSpace(formatter.currentIndentForLine(at: i), at: range.endIndex) 37 | formatter.replaceTokens(in: range, with: formatter.linebreakToken(for: i + 1)) 38 | } 39 | } 40 | } examples: { 41 | """ 42 | ```diff 43 | - func foo() { 44 | - 45 | - } 46 | 47 | + func foo() {} 48 | ``` 49 | """ 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Rules/EmptyExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmptyExtensions.swift 3 | // SwiftFormat 4 | // 5 | // Created by Manny Lopez on 7/30/24. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Remove empty, non-conforming, extensions. 13 | static let emptyExtensions = FormatRule( 14 | help: "Remove empty, non-conforming, extensions.", 15 | disabledByDefault: true, 16 | orderAfter: [.unusedPrivateDeclarations] 17 | ) { formatter in 18 | var emptyExtensions = [TypeDeclaration]() 19 | 20 | for declaration in formatter.parseDeclarations() { 21 | guard declaration.keyword == "extension", 22 | let extensionDeclaration = declaration.asTypeDeclaration, 23 | extensionDeclaration.body.isEmpty, 24 | // Ensure that it is not a macro 25 | !extensionDeclaration.modifiers.contains(where: { $0.first == "@" }) 26 | else { continue } 27 | 28 | // Ensure that the extension does not conform to any protocols 29 | guard extensionDeclaration.conformances.isEmpty else { continue } 30 | 31 | emptyExtensions.append(extensionDeclaration) 32 | } 33 | 34 | for emptyExtension in emptyExtensions { 35 | emptyExtension.remove() 36 | } 37 | } examples: { 38 | """ 39 | ```diff 40 | - extension String {} 41 | - 42 | extension String: Equatable {} 43 | ``` 44 | """ 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Rules/FileMacro.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FileMacro.swift 3 | // SwiftFormat 4 | // 5 | // Created by Cal Stephens on 9/14/24. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | static let fileMacro = FormatRule( 13 | help: "Prefer either #file or #fileID, which have the same behavior in Swift 6 and later.", 14 | options: ["filemacro"] 15 | ) { formatter in 16 | // In the Swift 6 lanaguage mode and later, `#file` and `#fileID` have the same behavior. 17 | guard formatter.options.languageMode >= "6" else { 18 | return 19 | } 20 | 21 | if formatter.options.preferFileMacro { 22 | formatter.forEach(.keyword("#fileID")) { index, _ in 23 | formatter.replaceToken(at: index, with: .keyword("#file")) 24 | } 25 | } else { 26 | formatter.forEach(.keyword("#file")) { index, _ in 27 | formatter.replaceToken(at: index, with: .keyword("#fileID")) 28 | } 29 | } 30 | } examples: { 31 | """ 32 | ```diff 33 | // --filemacro #file 34 | - func foo(file: StaticString = #fileID) { ... } 35 | + func foo(file: StaticString = #file) { ... } 36 | 37 | // --filemacro #fileID 38 | - func foo(file: StaticString = #file) { ... } 39 | + func foo(file: StaticString = #fileID) { ... } 40 | ``` 41 | """ 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/Rules/HeaderFileName.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HeaderFileName.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 5/3/23. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Ensure file name reference in header matches actual file name 13 | static let headerFileName = FormatRule( 14 | help: "Ensure file name in header comment matches the actual file name.", 15 | runOnceOnly: true, 16 | orderAfter: [.fileHeader] 17 | ) { formatter in 18 | guard let fileName = formatter.options.fileInfo.fileName, 19 | let headerRange = formatter.headerCommentTokenRange(includingDirectives: ["*"]), 20 | fileName.hasSuffix(".swift") 21 | else { 22 | return 23 | } 24 | 25 | for i in headerRange { 26 | guard case let .commentBody(body) = formatter.tokens[i] else { 27 | continue 28 | } 29 | if body.hasSuffix(".swift"), body != fileName, !body.contains(where: { " /".contains($0) }) { 30 | formatter.replaceToken(at: i, with: .commentBody(fileName)) 31 | } 32 | } 33 | } examples: { 34 | """ 35 | For a file named `Bar.swift`: 36 | 37 | ```diff 38 | - // Foo.swift 39 | + // Bar.swift 40 | // SwiftFormat 41 | // 42 | // Created by Nick Lockwood on 5/3/23. 43 | 44 | struct Bar {} 45 | ``` 46 | """ 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/Rules/HoistAwait.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HoistAwait.swift 3 | // SwiftFormat 4 | // 5 | // Created by Facundo Menzella on 2/9/23. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Reposition `await` keyword outside of the current scope. 13 | static let hoistAwait = FormatRule( 14 | help: "Move inline `await` keyword(s) to start of expression.", 15 | options: ["asynccapturing"] 16 | ) { formatter in 17 | guard formatter.options.swiftVersion >= "5.5" else { return } 18 | 19 | formatter.forEachToken(where: { 20 | $0 == .startOfScope("(") || $0 == .startOfScope("[") 21 | }) { i, _ in 22 | formatter.hoistEffectKeyword("await", inScopeAt: i) { prevIndex in 23 | formatter.isSymbol(at: prevIndex, in: formatter.options.asyncCapturing) 24 | } 25 | } 26 | } examples: { 27 | """ 28 | ```diff 29 | - greet(await forename, await surname) 30 | + await greet(forename, surname) 31 | ``` 32 | 33 | ```diff 34 | - let foo = String(try await getFoo()) 35 | + let foo = await String(try getFoo()) 36 | ``` 37 | """ 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Rules/HoistTry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HoistTry.swift 3 | // SwiftFormat 4 | // 5 | // Created by Facundo Menzella on 2/25/23. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | static let hoistTry = FormatRule( 13 | help: "Move inline `try` keyword(s) to start of expression.", 14 | options: ["throwcapturing"] 15 | ) { formatter in 16 | let names = formatter.options.throwCapturing.union(["expect"]) 17 | formatter.forEachToken(where: { 18 | $0 == .startOfScope("(") || $0 == .startOfScope("[") 19 | }) { i, _ in 20 | formatter.hoistEffectKeyword("try", inScopeAt: i) { prevIndex in 21 | guard case let .identifier(name) = formatter.tokens[prevIndex] else { 22 | return false 23 | } 24 | return name.hasPrefix("XCTAssert") || formatter.isSymbol(at: prevIndex, in: names) 25 | } 26 | } 27 | } examples: { 28 | """ 29 | ```diff 30 | - foo(try bar(), try baz()) 31 | + try foo(bar(), baz()) 32 | ``` 33 | 34 | ```diff 35 | - let foo = String(try await getFoo()) 36 | + let foo = try String(await getFoo()) 37 | ``` 38 | """ 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/Rules/LeadingDelimiters.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LeadingDelimiters.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 3/11/19. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | static let leadingDelimiters = FormatRule( 13 | help: "Move leading delimiters to the end of the previous line.", 14 | sharedOptions: ["linebreaks"] 15 | ) { formatter in 16 | formatter.forEach(.delimiter) { i, _ in 17 | guard let endOfLine = formatter.index(of: .nonSpace, before: i, if: { 18 | $0.isLinebreak 19 | }) else { 20 | return 21 | } 22 | let nextIndex = formatter.index(of: .nonSpace, after: i) ?? (i + 1) 23 | formatter.insertSpace(formatter.currentIndentForLine(at: i), at: nextIndex) 24 | formatter.insertLinebreak(at: nextIndex) 25 | formatter.removeTokens(in: i + 1 ..< nextIndex) 26 | guard case .commentBody? = formatter.last(.nonSpace, before: endOfLine) else { 27 | formatter.removeTokens(in: endOfLine ..< i) 28 | return 29 | } 30 | let startIndex = formatter.index(of: .nonSpaceOrComment, before: endOfLine) ?? -1 31 | formatter.removeTokens(in: endOfLine ..< i) 32 | let comment = Array(formatter.tokens[startIndex + 1 ..< endOfLine]) 33 | formatter.insert(comment, at: endOfLine + 1) 34 | formatter.removeTokens(in: startIndex + 1 ..< endOfLine) 35 | } 36 | } examples: { 37 | """ 38 | ```diff 39 | - guard let foo = maybeFoo // first 40 | - , let bar = maybeBar else { ... } 41 | 42 | + guard let foo = maybeFoo, // first 43 | + let bar = maybeBar else { ... } 44 | ``` 45 | """ 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/Rules/LinebreakAtEndOfFile.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LinebreakAtEndOfFile.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 8/22/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Always end file with a linebreak, to avoid incompatibility with certain unix tools: 13 | /// http://stackoverflow.com/questions/2287967/why-is-it-recommended-to-have-empty-line-in-the-end-of-file 14 | static let linebreakAtEndOfFile = FormatRule( 15 | help: "Add empty blank line at end of file.", 16 | sharedOptions: ["linebreaks"] 17 | ) { formatter in 18 | guard !formatter.options.fragment else { return } 19 | var wasLinebreak = true 20 | formatter.forEachToken(onlyWhereEnabled: false) { _, token in 21 | switch token { 22 | case .linebreak: 23 | wasLinebreak = true 24 | case .space: 25 | break 26 | default: 27 | wasLinebreak = false 28 | } 29 | } 30 | if formatter.isEnabled, !wasLinebreak { 31 | formatter.insertLinebreak(at: formatter.tokens.count) 32 | } 33 | } examples: { 34 | """ 35 | ```diff 36 | struct Foo {↩ 37 | let bar: Bar↩ 38 | - } 39 | + }↩ 40 | + 41 | ``` 42 | """ 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/Rules/Linebreaks.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Linebreaks.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 8/25/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Standardise linebreak characters as whatever is specified in the options (\n by default) 13 | static let linebreaks = FormatRule( 14 | help: "Use specified linebreak character for all linebreaks (CR, LF or CRLF).", 15 | options: ["linebreaks"] 16 | ) { formatter in 17 | formatter.forEach(.linebreak) { i, _ in 18 | formatter.replaceToken(at: i, with: formatter.linebreakToken(for: i)) 19 | } 20 | } examples: { 21 | nil 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Rules/PrivateStateVariables.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrivateStateVariables.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Dave Paul on 9/13/24. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | static let privateStateVariables = FormatRule( 13 | help: "Adds `private` access control to @State properties without existing access control modifiers.", 14 | disabledByDefault: true 15 | ) { formatter in 16 | formatter.forEachToken(where: { $0 == .keyword("@State") || $0 == .keyword("@StateObject") }) { stateIndex, _ in 17 | guard let keywordIndex = formatter.index(after: stateIndex, where: { 18 | $0 == .keyword("let") || $0 == .keyword("var") 19 | }) else { return } 20 | 21 | // Don't override any existing access control: 22 | guard !formatter.tokens[stateIndex ..< keywordIndex].contains(where: { 23 | _FormatRules.aclModifiers.contains($0.string) || _FormatRules.aclSetterModifiers.contains($0.string) 24 | }) else { 25 | return 26 | } 27 | 28 | // Check for @Previewable - we won't modify @Previewable macros. 29 | guard !formatter.modifiersForDeclaration(at: keywordIndex, contains: "@Previewable") else { 30 | return 31 | } 32 | 33 | formatter.insert([.keyword("private"), .space(" ")], at: keywordIndex) 34 | } 35 | } examples: { 36 | """ 37 | ```diff 38 | - @State var anInt: Int 39 | + @State private var anInt: Int 40 | 41 | - @StateObject var myInstance: MyObject 42 | + @StateObject private var myInstace: MyObject 43 | ``` 44 | """ 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Rules/RedundantBackticks.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedundantBackticks.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 3/7/17. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Remove redundant backticks around non-keywords, or in places where keywords don't need escaping 13 | static let redundantBackticks = FormatRule( 14 | help: "Remove redundant backticks around identifiers." 15 | ) { formatter in 16 | formatter.forEach(.identifier) { i, token in 17 | guard token.string.first == "`", !formatter.backticksRequired(at: i) else { 18 | return 19 | } 20 | formatter.replaceToken(at: i, with: .identifier(token.unescaped())) 21 | } 22 | } examples: { 23 | """ 24 | ```diff 25 | - let `infix` = bar 26 | + let infix = bar 27 | ``` 28 | 29 | ```diff 30 | - func foo(with `default`: Int) {} 31 | + func foo(with default: Int) {} 32 | ``` 33 | """ 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/Rules/RedundantBreak.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedundantBreak.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 1/23/19. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Remove redundant `break` keyword from switch cases 13 | static let redundantBreak = FormatRule( 14 | help: "Remove redundant `break` in switch case." 15 | ) { formatter in 16 | formatter.forEach(.keyword("break")) { i, _ in 17 | guard formatter.last(.nonSpaceOrCommentOrLinebreak, before: i) != .startOfScope(":"), 18 | formatter.next(.nonSpaceOrCommentOrLinebreak, after: i)?.isEndOfScope == true, 19 | var startIndex = formatter.index(of: .nonSpace, before: i), 20 | let endIndex = formatter.index(of: .nonSpace, after: i), 21 | formatter.currentScope(at: i) == .startOfScope(":") 22 | else { 23 | return 24 | } 25 | if !formatter.tokens[startIndex].isLinebreak || !formatter.tokens[endIndex].isLinebreak { 26 | startIndex += 1 27 | } 28 | formatter.removeTokens(in: startIndex ..< endIndex) 29 | } 30 | } examples: { 31 | """ 32 | ```diff 33 | switch foo { 34 | case bar: 35 | print("bar") 36 | - break 37 | default: 38 | print("default") 39 | - break 40 | } 41 | ``` 42 | """ 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/Rules/RedundantExtensionACL.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedundantExtensionACL.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 2/3/19. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Remove redundant access control level modifiers in extensions 13 | static let redundantExtensionACL = FormatRule( 14 | help: "Remove redundant access control modifiers." 15 | ) { formatter in 16 | formatter.forEach(.keyword("extension")) { i, _ in 17 | var acl = "" 18 | guard formatter.modifiersForDeclaration(at: i, contains: { 19 | acl = $1 20 | return _FormatRules.aclModifiers.contains(acl) 21 | }), let startIndex = formatter.index(of: .startOfScope("{"), after: i), 22 | var endIndex = formatter.index(of: .endOfScope("}"), after: startIndex) else { 23 | return 24 | } 25 | if acl == "private" { acl = "fileprivate" } 26 | while let aclIndex = formatter.lastIndex(of: .keyword(acl), in: startIndex + 1 ..< endIndex) { 27 | formatter.removeToken(at: aclIndex) 28 | if formatter.token(at: aclIndex)?.isSpace == true { 29 | formatter.removeToken(at: aclIndex) 30 | } 31 | endIndex = aclIndex 32 | } 33 | } 34 | } examples: { 35 | """ 36 | ```diff 37 | public extension URL { 38 | - public func queryParameter(_ name: String) -> String { ... } 39 | } 40 | 41 | public extension URL { 42 | + func queryParameter(_ name: String) -> String { ... } 43 | } 44 | ``` 45 | """ 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/Rules/RedundantGet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedundantGet.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 11/15/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Remove redundant `get {}` clause inside read-only computed property 13 | static let redundantGet = FormatRule( 14 | help: "Remove unneeded `get` clause inside computed properties." 15 | ) { formatter in 16 | formatter.forEach(.identifier("get")) { i, _ in 17 | if formatter.isAccessorKeyword(at: i, checkKeyword: false), 18 | let prevIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, before: i, if: { 19 | $0 == .startOfScope("{") 20 | }), let openIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: i, if: { 21 | $0 == .startOfScope("{") 22 | }), 23 | let closeIndex = formatter.index(of: .endOfScope("}"), after: openIndex), 24 | let nextIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: closeIndex, if: { 25 | $0 == .endOfScope("}") 26 | }) 27 | { 28 | formatter.removeTokens(in: closeIndex ..< nextIndex) 29 | formatter.removeTokens(in: prevIndex + 1 ... openIndex) 30 | // TODO: fix-up indenting of lines in between removed braces 31 | } 32 | } 33 | } examples: { 34 | """ 35 | ```diff 36 | var foo: Int { 37 | - get { 38 | - return 5 39 | - } 40 | } 41 | 42 | var foo: Int { 43 | + return 5 44 | } 45 | ``` 46 | """ 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/Rules/RedundantLetError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedundantLetError.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 12/16/18. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Remove redundant `let error` from `catch` statements 13 | static let redundantLetError = FormatRule( 14 | help: "Remove redundant `let error` from `catch` clause." 15 | 16 | ) { formatter in 17 | formatter.forEach(.keyword("catch")) { i, _ in 18 | if let letIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: i, if: { 19 | $0 == .keyword("let") 20 | }), let errorIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: letIndex, if: { 21 | $0 == .identifier("error") 22 | }), let scopeIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: errorIndex, if: { 23 | $0 == .startOfScope("{") 24 | }) { 25 | formatter.removeTokens(in: letIndex ..< scopeIndex) 26 | } 27 | } 28 | } examples: { 29 | """ 30 | ```diff 31 | - do { ... } catch let error { log(error) } 32 | + do { ... } catch { log(error) } 33 | ``` 34 | """ 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/Rules/RedundantStaticSelf.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedundantStaticSelf.swift 3 | // SwiftFormat 4 | // 5 | // Created by Šimon Javora on 4/29/23. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Remove redundant Self keyword 13 | static let redundantStaticSelf = FormatRule( 14 | help: "Remove explicit `Self` where applicable." 15 | ) { formatter in 16 | formatter.addOrRemoveSelf(static: true) 17 | } examples: { 18 | """ 19 | ```diff 20 | enum Foo { 21 | static let bar = Bar() 22 | 23 | static func baaz() -> Bar { 24 | - Self.bar() 25 | + bar() 26 | } 27 | } 28 | ``` 29 | """ 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/Rules/SortedImports.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SortedImports.swift 3 | // SwiftFormat 4 | // 5 | // Created by Pablo Carcelén on 11/22/17. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Deprecated 13 | static let sortedImports = FormatRule( 14 | help: "Sort import statements alphabetically.", 15 | deprecationMessage: "Use sortImports instead.", 16 | options: ["importgrouping"], 17 | sharedOptions: ["linebreaks"] 18 | ) { formatter in 19 | _ = formatter.options.importGrouping 20 | _ = formatter.options.linebreak 21 | FormatRule.sortImports.apply(with: formatter) 22 | } examples: { 23 | nil 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Rules/SortedSwitchCases.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SortedSwitchCases.swift 3 | // SwiftFormat 4 | // 5 | // Created by Facundo Menzella on 9/22/20. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Deprecated 13 | static let sortedSwitchCases = FormatRule( 14 | help: "Sort switch cases alphabetically.", 15 | deprecationMessage: "Use sortSwitchCases instead." 16 | ) { formatter in 17 | FormatRule.sortSwitchCases.apply(with: formatter) 18 | } examples: { 19 | nil 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Rules/SpaceAroundBraces.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpaceAroundBraces.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 8/22/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Ensure that there is space between an opening brace and the preceding 13 | /// identifier, and between a closing brace and the following identifier. 14 | static let spaceAroundBraces = FormatRule( 15 | help: "Add or remove space around curly braces." 16 | ) { formatter in 17 | formatter.forEach(.startOfScope("{")) { i, _ in 18 | if let prevToken = formatter.token(at: i - 1) { 19 | switch prevToken { 20 | case .space, .linebreak, .operator(_, .prefix), .operator(_, .infix), 21 | .startOfScope where !prevToken.isStringDelimiter: 22 | break 23 | default: 24 | formatter.insert(.space(" "), at: i) 25 | } 26 | } 27 | } 28 | formatter.forEach(.endOfScope("}")) { i, _ in 29 | if let nextToken = formatter.token(at: i + 1) { 30 | switch nextToken { 31 | case .identifier, .keyword: 32 | formatter.insert(.space(" "), at: i + 1) 33 | default: 34 | break 35 | } 36 | } 37 | } 38 | } examples: { 39 | """ 40 | ```diff 41 | - foo.filter{ return true }.map{ $0 } 42 | + foo.filter { return true }.map { $0 } 43 | ``` 44 | 45 | ```diff 46 | - foo( {} ) 47 | + foo({}) 48 | ``` 49 | """ 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Rules/SpaceAroundGenerics.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpaceAroundGenerics.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 8/22/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Ensure there is no space between an opening chevron and the preceding identifier 13 | static let spaceAroundGenerics = FormatRule( 14 | help: "Remove space around angle brackets." 15 | ) { formatter in 16 | formatter.forEach(.startOfScope("<")) { i, _ in 17 | if formatter.token(at: i - 1)?.isSpace == true, 18 | formatter.token(at: i - 2)?.isIdentifierOrKeyword == true 19 | { 20 | formatter.removeToken(at: i - 1) 21 | } 22 | } 23 | } examples: { 24 | """ 25 | ```diff 26 | - Foo () 27 | + Foo() 28 | ``` 29 | """ 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/Rules/SpaceInsideBraces.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpaceInsideBraces.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 8/22/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Ensure that there is space immediately inside braces 13 | static let spaceInsideBraces = FormatRule( 14 | help: "Add space inside curly braces." 15 | ) { formatter in 16 | formatter.forEach(.startOfScope("{")) { i, _ in 17 | if let nextToken = formatter.token(at: i + 1) { 18 | if !nextToken.isSpaceOrLinebreak, 19 | ![.endOfScope("}"), .startOfScope("{")].contains(nextToken) 20 | { 21 | formatter.insert(.space(" "), at: i + 1) 22 | } 23 | } 24 | } 25 | formatter.forEach(.endOfScope("}")) { i, _ in 26 | if let prevToken = formatter.token(at: i - 1) { 27 | if !prevToken.isSpaceOrLinebreak, 28 | ![.endOfScope("}"), .startOfScope("{")].contains(prevToken) 29 | { 30 | formatter.insert(.space(" "), at: i) 31 | } 32 | } 33 | } 34 | } examples: { 35 | """ 36 | ```diff 37 | - foo.filter {return true} 38 | + foo.filter { return true } 39 | ``` 40 | """ 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Rules/SpaceInsideBrackets.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpaceInsideBrackets.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 8/22/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Remove space immediately inside square brackets 13 | static let spaceInsideBrackets = FormatRule( 14 | help: "Remove space inside square brackets." 15 | ) { formatter in 16 | formatter.forEach(.startOfScope("[")) { i, _ in 17 | if formatter.token(at: i + 1)?.isSpace == true, 18 | formatter.token(at: i + 2)?.isComment == false 19 | { 20 | formatter.removeToken(at: i + 1) 21 | } 22 | } 23 | formatter.forEach(.endOfScope("]")) { i, _ in 24 | if formatter.token(at: i - 1)?.isSpace == true, 25 | formatter.token(at: i - 2)?.isCommentOrLinebreak == false 26 | { 27 | formatter.removeToken(at: i - 1) 28 | } 29 | } 30 | } examples: { 31 | """ 32 | ```diff 33 | - [ 1, 2, 3 ] 34 | + [1, 2, 3] 35 | ``` 36 | """ 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/Rules/SpaceInsideGenerics.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpaceInsideGenerics.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 8/22/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Remove space immediately inside chevrons 13 | static let spaceInsideGenerics = FormatRule( 14 | help: "Remove space inside angle brackets." 15 | ) { formatter in 16 | formatter.forEach(.startOfScope("<")) { i, _ in 17 | if formatter.token(at: i + 1)?.isSpace == true { 18 | formatter.removeToken(at: i + 1) 19 | } 20 | } 21 | formatter.forEach(.endOfScope(">")) { i, _ in 22 | if formatter.token(at: i - 1)?.isSpace == true, 23 | formatter.token(at: i - 2)?.isLinebreak == false 24 | { 25 | formatter.removeToken(at: i - 1) 26 | } 27 | } 28 | } examples: { 29 | """ 30 | ```diff 31 | - Foo< Bar, Baz > 32 | + Foo 33 | ``` 34 | """ 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/Rules/SpaceInsideParens.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpaceInsideParens.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 8/22/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Remove space immediately inside parens 13 | static let spaceInsideParens = FormatRule( 14 | help: "Remove space inside parentheses." 15 | ) { formatter in 16 | formatter.forEach(.startOfScope("(")) { i, _ in 17 | if formatter.token(at: i + 1)?.isSpace == true, 18 | formatter.token(at: i + 2)?.isComment == false 19 | { 20 | formatter.removeToken(at: i + 1) 21 | } 22 | } 23 | formatter.forEach(.endOfScope(")")) { i, _ in 24 | if formatter.token(at: i - 1)?.isSpace == true, 25 | formatter.token(at: i - 2)?.isCommentOrLinebreak == false 26 | { 27 | formatter.removeToken(at: i - 1) 28 | } 29 | } 30 | } examples: { 31 | """ 32 | ```diff 33 | - ( a, b) 34 | + (a, b) 35 | ``` 36 | """ 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/Rules/Specifiers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Specifiers.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 9/6/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Deprecated 13 | static let specifiers = FormatRule( 14 | help: "Use consistent ordering for member modifiers.", 15 | deprecationMessage: "Use modifierOrder instead.", 16 | options: ["modifierorder"] 17 | ) { formatter in 18 | _ = formatter.options.modifierOrder 19 | FormatRule.modifierOrder.apply(with: formatter) 20 | } examples: { 21 | nil 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Rules/StrongOutlets.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StrongOutlets.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 11/24/17. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Strip unnecessary `weak` from @IBOutlet properties (except delegates and datasources) 13 | static let strongOutlets = FormatRule( 14 | help: "Remove `weak` modifier from `@IBOutlet` properties." 15 | ) { formatter in 16 | formatter.forEach(.keyword("@IBOutlet")) { i, _ in 17 | guard let varIndex = formatter.index(of: .keyword("var"), after: i), 18 | let weakIndex = (i ..< varIndex).first(where: { formatter.tokens[$0] == .identifier("weak") }), 19 | case let .identifier(name)? = formatter.next(.identifier, after: varIndex) 20 | else { 21 | return 22 | } 23 | let lowercased = name.lowercased() 24 | if lowercased.hasSuffix("delegate") || lowercased.hasSuffix("datasource") { 25 | return 26 | } 27 | if formatter.tokens[weakIndex + 1].isSpace { 28 | formatter.removeToken(at: weakIndex + 1) 29 | } else if formatter.tokens[weakIndex - 1].isSpace { 30 | formatter.removeToken(at: weakIndex - 1) 31 | } 32 | formatter.removeToken(at: weakIndex) 33 | } 34 | } examples: { 35 | """ 36 | As per Apple's recommendation 37 | (https://developer.apple.com/videos/play/wwdc2015/407/ @ 32:30). 38 | 39 | ```diff 40 | - @IBOutlet weak var label: UILabel! 41 | + @IBOutlet var label: UILabel! 42 | ``` 43 | """ 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/Rules/StrongifiedSelf.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StrongifiedSelf.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 1/24/19. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Removed backticks from `self` when strongifying 13 | static let strongifiedSelf = FormatRule( 14 | help: "Remove backticks around `self` in Optional unwrap expressions." 15 | ) { formatter in 16 | formatter.forEach(.identifier("`self`")) { i, _ in 17 | guard formatter.options.swiftVersion >= "4.2", 18 | let equalIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: i, if: { 19 | $0 == .operator("=", .infix) 20 | }), formatter.next(.nonSpaceOrCommentOrLinebreak, after: equalIndex) == .identifier("self"), 21 | formatter.isConditionalStatement(at: i) 22 | else { 23 | return 24 | } 25 | formatter.replaceToken(at: i, with: .identifier("self")) 26 | } 27 | } examples: { 28 | """ 29 | ```diff 30 | - guard let `self` = self else { return } 31 | + guard let self = self else { return } 32 | ``` 33 | 34 | **NOTE:** assignment to un-escaped `self` is only supported in Swift 4.2 and 35 | above, so the `strongifiedSelf` rule is disabled unless the Swift version is 36 | set to 4.2 or above. 37 | """ 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Rules/SwiftTestingTestCaseNames.swift: -------------------------------------------------------------------------------- 1 | // Created by Cal Stephens on 2/19/25. 2 | // Copyright © 2025 Airbnb Inc. All rights reserved. 3 | 4 | public extension FormatRule { 5 | static let swiftTestingTestCaseNames = FormatRule( 6 | help: "In Swift Testing, don't prefix @Test methods with 'test'." 7 | ) { formatter in 8 | guard formatter.hasImport("Testing") else { return } 9 | 10 | formatter.forEach(.keyword("func")) { funcKeywordIndex, _ in 11 | if formatter.modifiersForDeclaration(at: funcKeywordIndex, contains: "@Test") { 12 | formatter.removeTestPrefix(fromFunctionAt: funcKeywordIndex) 13 | } 14 | } 15 | } examples: { 16 | """ 17 | ```diff 18 | import Testing 19 | 20 | struct MyFeatureTests { 21 | - @Test func testMyFeatureHasNoBugs() { 22 | + @Test func myFeatureHasNoBugs() { 23 | let myFeature = MyFeature() 24 | myFeature.runAction() 25 | #expect(!myFeature.hasBugs, "My feature has no bugs") 26 | #expect(myFeature.crashes.isEmpty, "My feature doesn't crash") 27 | #expect(myFeature.crashReport == nil) 28 | } 29 | } 30 | ``` 31 | """ 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Rules/TrailingSpace.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrailingSpace.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 11/24/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Remove trailing space from the end of lines, as it has no semantic 13 | /// meaning and leads to noise in commits. 14 | static let trailingSpace = FormatRule( 15 | help: "Remove trailing space at end of a line.", 16 | orderAfter: [.wrap, .wrapArguments], 17 | options: ["trimwhitespace"] 18 | ) { formatter in 19 | formatter.forEach(.space) { i, _ in 20 | if formatter.token(at: i + 1)?.isLinebreak ?? true, 21 | formatter.options.truncateBlankLines || formatter.token(at: i - 1)?.isLinebreak == false 22 | { 23 | formatter.removeToken(at: i) 24 | } 25 | } 26 | } examples: { 27 | """ 28 | ```diff 29 | - let foo: Foo␣ 30 | + let foo: Foo 31 | - ␣␣␣␣ 32 | + 33 | - func bar() {␣␣ 34 | + func bar() { 35 | ␣␣␣␣print("foo") 36 | } 37 | ``` 38 | """ 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/Rules/WrapConditionalBodies.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WrapConditionalBodies.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 11/6/21. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | static let wrapConditionalBodies = FormatRule( 13 | help: "Wrap the bodies of inline conditional statements onto a new line.", 14 | disabledByDefault: true, 15 | sharedOptions: ["linebreaks", "indent"] 16 | ) { formatter in 17 | formatter.forEachToken(where: { [.keyword("if"), .keyword("else")].contains($0) }) { i, _ in 18 | guard let startIndex = formatter.index(of: .startOfScope("{"), after: i) else { 19 | return formatter.fatalError("Expected {", at: i) 20 | } 21 | formatter.wrapStatementBody(at: startIndex) 22 | } 23 | } examples: { 24 | """ 25 | ```diff 26 | - guard let foo = bar else { return baz } 27 | + guard let foo = bar else { 28 | + return baz 29 | + } 30 | ``` 31 | 32 | ```diff 33 | - if foo { return bar } 34 | + if foo { 35 | + return bar 36 | + } 37 | ``` 38 | """ 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/Rules/WrapLoopBodies.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WrapLoopBodies.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 1/3/24. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | static let wrapLoopBodies = FormatRule( 13 | help: "Wrap the bodies of inline loop statements onto a new line.", 14 | orderAfter: [.preferForLoop], 15 | sharedOptions: ["linebreaks", "indent"] 16 | ) { formatter in 17 | formatter.forEachToken(where: { [ 18 | .keyword("for"), 19 | .keyword("while"), 20 | .keyword("repeat"), 21 | ].contains($0) }) { i, token in 22 | if let startIndex = formatter.index(of: .startOfScope("{"), after: i) { 23 | formatter.wrapStatementBody(at: startIndex) 24 | } else if token == .keyword("for") { 25 | return formatter.fatalError("Expected {", at: i) 26 | } 27 | } 28 | } examples: { 29 | """ 30 | ```diff 31 | - for foo in array { print(foo) } 32 | + for foo in array { 33 | + print(foo) 34 | + } 35 | ``` 36 | 37 | ```diff 38 | - while let foo = bar.next() { print(foo) } 39 | + while let foo = bar.next() { 40 | + print(foo) 41 | + } 42 | ``` 43 | """ 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/Rules/WrapSwitchCases.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WrapSwitchCases.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 8/28/20. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension FormatRule { 12 | /// Writes one switch case per line 13 | static let wrapSwitchCases = FormatRule( 14 | help: "Wrap comma-delimited switch cases onto multiple lines.", 15 | disabledByDefault: true, 16 | sharedOptions: ["linebreaks", "tabwidth", "indent", "smarttabs"] 17 | ) { formatter in 18 | formatter.forEach(.endOfScope("case")) { i, _ in 19 | guard var endIndex = formatter.index(of: .startOfScope(":"), after: i) else { return } 20 | let lineStart = formatter.startOfLine(at: i) 21 | let indent = formatter.spaceEquivalentToTokens(from: lineStart, upTo: i + 2) 22 | 23 | var startIndex = i 24 | while let commaIndex = formatter.index(of: .delimiter(","), in: startIndex + 1 ..< endIndex), 25 | let nextIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: commaIndex) 26 | { 27 | if formatter.index(of: .linebreak, in: commaIndex ..< nextIndex) == nil { 28 | formatter.insertLinebreak(at: commaIndex + 1) 29 | let delta = formatter.insertSpace(indent, at: commaIndex + 2) 30 | endIndex += 1 + delta 31 | } 32 | startIndex = commaIndex 33 | } 34 | } 35 | } examples: { 36 | """ 37 | ```diff 38 | switch foo { 39 | - case .bar, .baz: 40 | break 41 | } 42 | 43 | switch foo { 44 | + case .foo, 45 | + .bar: 46 | break 47 | } 48 | ``` 49 | """ 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/SwiftFormat.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftFormat.h 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 12/08/2016. 6 | // Copyright 2016 Nick Lockwood 7 | // 8 | // Distributed under the permissive MIT license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/nicklockwood/SwiftFormat 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy 14 | // of this software and associated documentation files (the "Software"), to deal 15 | // in the Software without restriction, including without limitation the rights 16 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | // copies of the Software, and to permit persons to whom the Software is 18 | // furnished to do so, subject to the following conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all 21 | // copies or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | // SOFTWARE. 30 | // 31 | 32 | #import 33 | 34 | //! Project version number for SwiftFormat. 35 | FOUNDATION_EXPORT double SwiftFormatVersionNumber; 36 | 37 | //! Project version string for SwiftFormat. 38 | FOUNDATION_EXPORT const unsigned char SwiftFormatVersionString[]; 39 | -------------------------------------------------------------------------------- /SwiftFormat.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SwiftFormat", 3 | "version": "0.56.2", 4 | "license": { 5 | "type": "MIT", 6 | "file": "LICENSE.md" 7 | }, 8 | "summary": "Mac and iOS library for formatting Swift source code.", 9 | "homepage": "https://github.com/nicklockwood/SwiftFormat", 10 | "authors": "Nick Lockwood", 11 | "source": { 12 | "git": "https://github.com/nicklockwood/SwiftFormat.git", 13 | "tag": "0.56.2" 14 | }, 15 | "default_subspecs": "Core", 16 | "subspecs": [ 17 | { 18 | "name": "Core", 19 | "source_files": "Sources/**/*.swift" 20 | }, 21 | { 22 | "name": "CLI", 23 | "preserve_paths": "CommandLineTool/swiftformat", 24 | "platforms": { 25 | "ios": "11.0", 26 | "tvos": "11.0", 27 | "osx": "10.14" 28 | } 29 | } 30 | ], 31 | "platforms": { 32 | "ios": "11.0", 33 | "tvos": "11.0", 34 | "osx": "10.14" 35 | }, 36 | "swift_versions": [ 37 | "5.3", "5.4", "5.5", "5.6", "5.7", "5.8", "5.9", "5.10" 38 | ], 39 | "requires_arc": true 40 | } 41 | -------------------------------------------------------------------------------- /SwiftFormat.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftFormat.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftFormat.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | PreviewsEnabled 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Tests/BadConfig/.swiftformat: -------------------------------------------------------------------------------- 1 | --disable ifdef -------------------------------------------------------------------------------- /Tests/BadConfig/Test.swift: -------------------------------------------------------------------------------- 1 | //Badly formatted code 2 | func 3 | foo (bar :Int){} 4 | -------------------------------------------------------------------------------- /Tests/GlobTest[5].txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicklockwood/SwiftFormat/1d02d0f54a5123c3ef67084b318f4421427b7a51/Tests/GlobTest[5].txt -------------------------------------------------------------------------------- /Tests/ProjectFilePaths.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProjectFilePaths.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Cal Stephens on 8/3/24. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | @testable import SwiftFormat 11 | 12 | let projectDirectory = URL(fileURLWithPath: #file) 13 | .deletingLastPathComponent().deletingLastPathComponent() 14 | 15 | let projectURL = projectDirectory 16 | .appendingPathComponent("SwiftFormat.xcodeproj") 17 | .appendingPathComponent("project.pbxproj") 18 | 19 | let allSourceFiles = allSwiftFiles(inDirectory: "Sources") 20 | let allRuleFiles = allSwiftFiles(inDirectory: "Sources/Rules") 21 | 22 | let allTestFiles = allSwiftFiles(inDirectory: "Tests") 23 | let allRuleTestFiles = allSwiftFiles(inDirectory: "Tests/Rules") 24 | 25 | let changeLogURL = 26 | projectDirectory.appendingPathComponent("CHANGELOG.md") 27 | 28 | let podspecURL = 29 | projectDirectory.appendingPathComponent("SwiftFormat.podspec.json") 30 | 31 | let rulesURL = 32 | projectDirectory.appendingPathComponent("Rules.md") 33 | 34 | let rulesFile = 35 | try! String(contentsOf: rulesURL, encoding: .utf8) 36 | 37 | let ruleRegistryURL = 38 | projectDirectory.appendingPathComponent("Sources/RuleRegistry.generated.swift") 39 | 40 | private func allSwiftFiles(inDirectory directory: String) -> [URL] { 41 | var swiftFiles: [URL] = [] 42 | let directory = projectDirectory.appendingPathComponent(directory) 43 | let errors = enumerateFiles(withInputURL: directory) { fileURL, _, _ in 44 | { 45 | guard fileURL.pathExtension == "swift" else { return } 46 | swiftFiles.append(fileURL) 47 | } 48 | } 49 | assert(errors.isEmpty, "Encountered errors accessing files in \(directory): \(errors)") 50 | assert(!swiftFiles.isEmpty, "Could not load files in \(directory)") 51 | return swiftFiles 52 | } 53 | -------------------------------------------------------------------------------- /Tests/Rules/ApplicationMainTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApplicationMainTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 5/20/23. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class ApplicationMainTests: XCTestCase { 13 | func testUIApplicationMainReplacedByMain() { 14 | let input = """ 15 | @UIApplicationMain 16 | class AppDelegate: UIResponder, UIApplicationDelegate {} 17 | """ 18 | let output = """ 19 | @main 20 | class AppDelegate: UIResponder, UIApplicationDelegate {} 21 | """ 22 | let options = FormatOptions(swiftVersion: "5.3") 23 | testFormatting(for: input, output, rule: .applicationMain, options: options) 24 | } 25 | 26 | func testNSApplicationMainReplacedByMain() { 27 | let input = """ 28 | @NSApplicationMain 29 | class AppDelegate: NSObject, NSApplicationDelegate {} 30 | """ 31 | let output = """ 32 | @main 33 | class AppDelegate: NSObject, NSApplicationDelegate {} 34 | """ 35 | let options = FormatOptions(swiftVersion: "5.3") 36 | testFormatting(for: input, output, rule: .applicationMain, options: options) 37 | } 38 | 39 | func testNSApplicationMainNotReplacedInSwift5_2() { 40 | let input = """ 41 | @NSApplicationMain 42 | class AppDelegate: NSObject, NSApplicationDelegate {} 43 | """ 44 | let options = FormatOptions(swiftVersion: "5.2") 45 | testFormatting(for: input, rule: .applicationMain, options: options) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Tests/Rules/BlankLinesBetweenChainedFunctionsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BlankLinesBetweenChainedFunctionsTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 7/28/23. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class BlankLinesBetweenChainedFunctionsTests: XCTestCase { 13 | func testBlankLinesBetweenChainedFunctions() { 14 | let input = """ 15 | [0, 1, 2] 16 | .map { $0 * 2 } 17 | 18 | 19 | 20 | .map { $0 * 3 } 21 | """ 22 | let output1 = """ 23 | [0, 1, 2] 24 | .map { $0 * 2 } 25 | .map { $0 * 3 } 26 | """ 27 | let output2 = """ 28 | [0, 1, 2] 29 | .map { $0 * 2 } 30 | .map { $0 * 3 } 31 | """ 32 | testFormatting(for: input, [output1, output2], rules: [.blankLinesBetweenChainedFunctions]) 33 | } 34 | 35 | func testBlankLinesWithCommentsBetweenChainedFunctions() { 36 | let input = """ 37 | [0, 1, 2] 38 | .map { $0 * 2 } 39 | 40 | // Multiplies by 3 41 | 42 | .map { $0 * 3 } 43 | """ 44 | let output = """ 45 | [0, 1, 2] 46 | .map { $0 * 2 } 47 | // Multiplies by 3 48 | .map { $0 * 3 } 49 | """ 50 | testFormatting(for: input, output, rule: .blankLinesBetweenChainedFunctions) 51 | } 52 | 53 | func testBlankLinesWithMarkCommentBetweenChainedFunctions() { 54 | let input = """ 55 | [0, 1, 2] 56 | .map { $0 * 2 } 57 | 58 | // MARK: hello 59 | 60 | .map { $0 * 3 } 61 | """ 62 | testFormatting(for: input, rules: [.blankLinesBetweenChainedFunctions, .blankLinesAroundMark]) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Tests/Rules/FileMacroTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FileMacroTests.swift 3 | // SwiftFormat 4 | // 5 | // Created by Cal Stephens on 9/14/24. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | final class FileMacroTests: XCTestCase { 13 | func testPreservesFileMacroInSwift5Mode() { 14 | let input = """ 15 | func foo(file: StaticString = #fileID) { 16 | print(file) 17 | } 18 | 19 | func bar(file: StaticString = #file) { 20 | print(file) 21 | } 22 | """ 23 | 24 | let options = FormatOptions(languageMode: "5") 25 | testFormatting(for: input, rule: .fileMacro, options: options) 26 | } 27 | 28 | func testUpdatesFileIDInSwift6Mode() { 29 | let input = """ 30 | func foo(file: StaticString = #fileID) { 31 | print(file) 32 | } 33 | """ 34 | 35 | let output = """ 36 | func foo(file: StaticString = #file) { 37 | print(file) 38 | } 39 | """ 40 | 41 | let options = FormatOptions(preferFileMacro: true, languageMode: "6") 42 | testFormatting(for: input, output, rule: .fileMacro, options: options) 43 | } 44 | 45 | func testPreferFileID() { 46 | let input = """ 47 | func foo(file: StaticString = #file) { 48 | print(file) 49 | } 50 | """ 51 | 52 | let output = """ 53 | func foo(file: StaticString = #fileID) { 54 | print(file) 55 | } 56 | """ 57 | 58 | let options = FormatOptions(preferFileMacro: false, languageMode: "6") 59 | testFormatting(for: input, output, rule: .fileMacro, options: options) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Tests/Rules/HeaderFileNameTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HeaderFileNameTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 5/3/23. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class HeaderFileNameTests: XCTestCase { 13 | func testHeaderFileNameReplaced() { 14 | let input = """ 15 | // MyFile.swift 16 | 17 | let foo = bar 18 | """ 19 | let output = """ 20 | // YourFile.swift 21 | 22 | let foo = bar 23 | """ 24 | let options = FormatOptions(fileInfo: FileInfo(filePath: "~/YourFile.swift")) 25 | testFormatting(for: input, output, rule: .headerFileName, options: options) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tests/Rules/LeadingDelimitersTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LeadingDelimitersTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 3/11/19. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class LeadingDelimitersTests: XCTestCase { 13 | func testLeadingCommaMovedToPreviousLine() { 14 | let input = """ 15 | let foo = 5 16 | , bar = 6 17 | """ 18 | let output = """ 19 | let foo = 5, 20 | bar = 6 21 | """ 22 | testFormatting(for: input, output, rule: .leadingDelimiters) 23 | } 24 | 25 | func testLeadingColonFollowedByCommentMovedToPreviousLine() { 26 | let input = """ 27 | let foo 28 | : /* string */ String 29 | """ 30 | let output = """ 31 | let foo: 32 | /* string */ String 33 | """ 34 | testFormatting(for: input, output, rule: .leadingDelimiters) 35 | } 36 | 37 | func testCommaMovedBeforeCommentIfLineEndsInComment() { 38 | let input = """ 39 | let foo = 5 // first 40 | , bar = 6 41 | """ 42 | let output = """ 43 | let foo = 5, // first 44 | bar = 6 45 | """ 46 | testFormatting(for: input, output, rule: .leadingDelimiters) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Tests/Rules/LinebreakAtEndOfFileTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LinebreakAtEndOfFileTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 8/22/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class LinebreakAtEndOfFileTests: XCTestCase { 13 | func testLinebreakAtEndOfFile() { 14 | let input = "foo\nbar" 15 | let output = "foo\nbar\n" 16 | testFormatting(for: input, output, rule: .linebreakAtEndOfFile) 17 | } 18 | 19 | func testNoLinebreakAtEndOfFragment() { 20 | let input = "foo\nbar" 21 | let options = FormatOptions(fragment: true) 22 | testFormatting(for: input, rule: .linebreakAtEndOfFile, options: options) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests/Rules/LinebreaksTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LinebreaksTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 8/25/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class LinebreaksTests: XCTestCase { 13 | func testCarriageReturn() { 14 | let input = "foo\rbar" 15 | let output = "foo\nbar" 16 | testFormatting(for: input, output, rule: .linebreaks) 17 | } 18 | 19 | func testCarriageReturnLinefeed() { 20 | let input = "foo\r\nbar" 21 | let output = "foo\nbar" 22 | testFormatting(for: input, output, rule: .linebreaks) 23 | } 24 | 25 | func testVerticalTab() { 26 | let input = "foo\u{000B}bar" 27 | let output = "foo\nbar" 28 | testFormatting(for: input, output, rule: .linebreaks) 29 | } 30 | 31 | func testFormfeed() { 32 | let input = "foo\u{000C}bar" 33 | let output = "foo\nbar" 34 | testFormatting(for: input, output, rule: .linebreaks) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/Rules/RedundantExtensionACLTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedundantExtensionACLTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 2/3/19. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class RedundantExtensionACLTests: XCTestCase { 13 | func testPublicExtensionMemberACLStripped() { 14 | let input = """ 15 | public extension Foo { 16 | public var bar: Int { 5 } 17 | private static let baz = "baz" 18 | public func quux() {} 19 | } 20 | """ 21 | let output = """ 22 | public extension Foo { 23 | var bar: Int { 5 } 24 | private static let baz = "baz" 25 | func quux() {} 26 | } 27 | """ 28 | testFormatting(for: input, output, rule: .redundantExtensionACL) 29 | } 30 | 31 | func testPrivateExtensionMemberACLNotStrippedUnlessFileprivate() { 32 | let input = """ 33 | private extension Foo { 34 | fileprivate var bar: Int { 5 } 35 | private static let baz = "baz" 36 | fileprivate func quux() {} 37 | } 38 | """ 39 | let output = """ 40 | private extension Foo { 41 | var bar: Int { 5 } 42 | private static let baz = "baz" 43 | func quux() {} 44 | } 45 | """ 46 | testFormatting(for: input, output, rule: .redundantExtensionACL) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Tests/Rules/RedundantLetErrorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedundantLetErrorTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 12/16/18. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class RedundantLetErrorTests: XCTestCase { 13 | func testCatchLetError() { 14 | let input = "do {} catch let error {}" 15 | let output = "do {} catch {}" 16 | testFormatting(for: input, output, rule: .redundantLetError) 17 | } 18 | 19 | func testCatchLetErrorWithTypedThrows() { 20 | let input = "do throws(Foo) {} catch let error {}" 21 | let output = "do throws(Foo) {} catch {}" 22 | testFormatting(for: input, output, rule: .redundantLetError) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests/Rules/RedundantRawValuesTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedundantRawValuesTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 12/22/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class RedundantRawValuesTests: XCTestCase { 13 | func testRemoveRedundantRawString() { 14 | let input = "enum Foo: String {\n case bar = \"bar\"\n case baz = \"baz\"\n}" 15 | let output = "enum Foo: String {\n case bar\n case baz\n}" 16 | testFormatting(for: input, output, rule: .redundantRawValues) 17 | } 18 | 19 | func testRemoveCommaDelimitedCaseRawStringCases() { 20 | let input = "enum Foo: String { case bar = \"bar\", baz = \"baz\" }" 21 | let output = "enum Foo: String { case bar, baz }" 22 | testFormatting(for: input, output, rule: .redundantRawValues, 23 | exclude: [.wrapEnumCases]) 24 | } 25 | 26 | func testRemoveBacktickCaseRawStringCases() { 27 | let input = "enum Foo: String { case `as` = \"as\", `let` = \"let\" }" 28 | let output = "enum Foo: String { case `as`, `let` }" 29 | testFormatting(for: input, output, rule: .redundantRawValues, 30 | exclude: [.wrapEnumCases]) 31 | } 32 | 33 | func testNoRemoveRawStringIfNameDoesntMatch() { 34 | let input = "enum Foo: String {\n case bar = \"foo\"\n}" 35 | testFormatting(for: input, rule: .redundantRawValues) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Tests/Rules/RedundantTypedThrowsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedundantTypedThrowsTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Miguel Jimenez on 6/8/24. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class RedundantTypedThrowsTests: XCTestCase { 13 | func testRemovesRedundantNeverTypeThrows() { 14 | let input = """ 15 | func foo() throws(Never) -> Int { 16 | 0 17 | } 18 | """ 19 | 20 | let output = """ 21 | func foo() -> Int { 22 | 0 23 | } 24 | """ 25 | 26 | let options = FormatOptions(swiftVersion: "6.0") 27 | testFormatting(for: input, output, rule: .redundantTypedThrows, options: options) 28 | } 29 | 30 | func testRemovesRedundantAnyErrorTypeThrows() { 31 | let input = """ 32 | func foo() throws(any Error) -> Int { 33 | throw MyError.foo 34 | } 35 | """ 36 | 37 | let output = """ 38 | func foo() throws -> Int { 39 | throw MyError.foo 40 | } 41 | """ 42 | 43 | let options = FormatOptions(swiftVersion: "6.0") 44 | testFormatting(for: input, output, rule: .redundantTypedThrows, options: options) 45 | } 46 | 47 | func testDontRemovesNonRedundantErrorTypeThrows() { 48 | let input = """ 49 | func bar() throws(BarError) -> Foo { 50 | throw .foo 51 | } 52 | 53 | func foo() throws(Error) -> Int { 54 | throw MyError.foo 55 | } 56 | """ 57 | 58 | let options = FormatOptions(swiftVersion: "6.0") 59 | testFormatting(for: input, rule: .redundantTypedThrows, options: options) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Tests/Rules/SpaceAroundCommentsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpaceAroundCommentsTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 8/31/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class SpaceAroundCommentsTests: XCTestCase { 13 | func testSpaceAroundCommentInParens() { 14 | let input = "(/* foo */)" 15 | let output = "( /* foo */ )" 16 | testFormatting(for: input, output, rule: .spaceAroundComments, 17 | exclude: [.redundantParens]) 18 | } 19 | 20 | func testNoSpaceAroundCommentAtStartAndEndOfFile() { 21 | let input = "/* foo */" 22 | testFormatting(for: input, rule: .spaceAroundComments) 23 | } 24 | 25 | func testNoSpaceAroundCommentBeforeComma() { 26 | let input = "(foo /* foo */ , bar)" 27 | let output = "(foo /* foo */, bar)" 28 | testFormatting(for: input, output, rule: .spaceAroundComments) 29 | } 30 | 31 | func testSpaceAroundSingleLineComment() { 32 | let input = "func foo() {// comment\n}" 33 | let output = "func foo() { // comment\n}" 34 | testFormatting(for: input, output, rule: .spaceAroundComments) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/Rules/SpaceAroundGenericsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpaceAroundGenericsTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 8/22/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class SpaceAroundGenericsTests: XCTestCase { 13 | func testSpaceAroundGenerics() { 14 | let input = "Foo >" 15 | let output = "Foo>" 16 | testFormatting(for: input, output, rule: .spaceAroundGenerics) 17 | } 18 | 19 | func testSpaceAroundGenericsFollowedByAndOperator() { 20 | let input = "if foo is Foo && baz {}" 21 | testFormatting(for: input, rule: .spaceAroundGenerics, exclude: [.andOperator]) 22 | } 23 | 24 | func testSpaceAroundGenericResultBuilder() { 25 | let input = "func foo(@SomeResultBuilder builder: () -> Void) {}" 26 | testFormatting(for: input, rule: .spaceAroundGenerics) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/Rules/SpaceInsideBracesTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpaceInsideBracesTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 8/22/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class SpaceInsideBracesTests: XCTestCase { 13 | func testSpaceInsideBraces() { 14 | let input = "foo({bar})" 15 | let output = "foo({ bar })" 16 | testFormatting(for: input, output, rule: .spaceInsideBraces, exclude: [.trailingClosures]) 17 | } 18 | 19 | func testNoExtraSpaceInsidebraces() { 20 | let input = "{ foo }" 21 | testFormatting(for: input, rule: .spaceInsideBraces, exclude: [.trailingClosures]) 22 | } 23 | 24 | func testNoSpaceAddedInsideEmptybraces() { 25 | let input = "foo({})" 26 | testFormatting(for: input, rule: .spaceInsideBraces, exclude: [.trailingClosures]) 27 | } 28 | 29 | func testNoSpaceAddedBetweenDoublebraces() { 30 | let input = "func foo() -> () -> Void {{ bar() }}" 31 | testFormatting(for: input, rule: .spaceInsideBraces) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tests/Rules/SpaceInsideBracketsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpaceInsideBracketsTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 8/22/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class SpaceInsideBracketsTests: XCTestCase { 13 | func testSpaceInsideBrackets() { 14 | let input = "foo[ 5 ]" 15 | let output = "foo[5]" 16 | testFormatting(for: input, output, rule: .spaceInsideBrackets) 17 | } 18 | 19 | func testSpaceInsideWrappedArray() { 20 | let input = "[ foo,\n bar ]" 21 | let output = "[foo,\n bar]" 22 | let options = FormatOptions(wrapCollections: .disabled) 23 | testFormatting(for: input, output, rule: .spaceInsideBrackets, options: options) 24 | } 25 | 26 | func testSpaceBeforeCommentInsideWrappedArray() { 27 | let input = "[ // foo\n bar,\n]" 28 | let options = FormatOptions(wrapCollections: .disabled) 29 | testFormatting(for: input, rule: .spaceInsideBrackets, options: options) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tests/Rules/SpaceInsideGenericsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpaceInsideGenericsTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 8/22/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class SpaceInsideGenericsTests: XCTestCase { 13 | func testSpaceInsideGenerics() { 14 | let input = "Foo< Bar< Baz > >" 15 | let output = "Foo>" 16 | testFormatting(for: input, output, rule: .spaceInsideGenerics) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Tests/Rules/SpaceInsideParensTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpaceInsideParensTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 8/22/16. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class SpaceInsideParensTests: XCTestCase { 13 | func testSpaceInsideParens() { 14 | let input = "( 1, ( 2, 3 ) )" 15 | let output = "(1, (2, 3))" 16 | testFormatting(for: input, output, rule: .spaceInsideParens) 17 | } 18 | 19 | func testSpaceBeforeCommentInsideParens() { 20 | let input = "( /* foo */ 1, 2 )" 21 | let output = "( /* foo */ 1, 2)" 22 | testFormatting(for: input, output, rule: .spaceInsideParens) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests/Rules/WrapLoopBodiesTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WrapLoopBodiesTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 1/3/24. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class WrapLoopBodiesTests: XCTestCase { 13 | func testWrapForLoop() { 14 | let input = "for foo in bar { print(foo) }" 15 | let output = """ 16 | for foo in bar { 17 | print(foo) 18 | } 19 | """ 20 | testFormatting(for: input, output, rule: .wrapLoopBodies) 21 | } 22 | 23 | func testWrapWhileLoop() { 24 | let input = "while let foo = bar.next() { print(foo) }" 25 | let output = """ 26 | while let foo = bar.next() { 27 | print(foo) 28 | } 29 | """ 30 | testFormatting(for: input, output, rule: .wrapLoopBodies) 31 | } 32 | 33 | func testWrapRepeatWhileLoop() { 34 | let input = "repeat { print(foo) } while condition()" 35 | let output = """ 36 | repeat { 37 | print(foo) 38 | } while condition() 39 | """ 40 | testFormatting(for: input, output, rule: .wrapLoopBodies) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Tests/Rules/WrapSwitchCasesTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WrapSwitchCasesTests.swift 3 | // SwiftFormatTests 4 | // 5 | // Created by Nick Lockwood on 8/28/20. 6 | // Copyright © 2024 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class WrapSwitchCasesTests: XCTestCase { 13 | func testMultilineSwitchCases() { 14 | let input = """ 15 | func foo() { 16 | switch bar { 17 | case .a(_), .b, "c": 18 | print("") 19 | case .d: 20 | print("") 21 | } 22 | } 23 | """ 24 | let output = """ 25 | func foo() { 26 | switch bar { 27 | case .a(_), 28 | .b, 29 | "c": 30 | print("") 31 | case .d: 32 | print("") 33 | } 34 | } 35 | """ 36 | testFormatting(for: input, output, rule: .wrapSwitchCases) 37 | } 38 | 39 | func testIfAfterSwitchCaseNotWrapped() { 40 | let input = """ 41 | switch foo { 42 | case "foo": 43 | print("") 44 | default: 45 | print("") 46 | } 47 | if let foo = bar, foo != .baz { 48 | throw error 49 | } 50 | """ 51 | testFormatting(for: input, rule: .wrapSwitchCases) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Tests/VersionTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VersionTests.swift 3 | // SwiftFormat 4 | // 5 | // Created by Nick Lockwood on 28/01/2019. 6 | // Copyright © 2019 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFormat 11 | 12 | class VersionTests: XCTestCase { 13 | // MARK: Version parsing 14 | 15 | func testParseEmptyVersion() throws { 16 | let version = Version(rawValue: "") 17 | XCTAssertNil(version) 18 | } 19 | 20 | func testParseOrdinaryVersion() throws { 21 | let version = Version(rawValue: "4.2") 22 | XCTAssertEqual(version, "4.2") 23 | } 24 | 25 | func testParsePaddedVersion() throws { 26 | let version = Version(rawValue: " 4.2 ") 27 | XCTAssertEqual(version, "4.2") 28 | } 29 | 30 | func testParseThreePartVersion() throws { 31 | let version = Version(rawValue: "3.1.5") 32 | XCTAssertNotNil(version) 33 | XCTAssertEqual(version, "3.1.5") 34 | } 35 | 36 | func testParsePreviewVersion() throws { 37 | let version = Version(rawValue: "3.0-PREVIEW-4") 38 | XCTAssertNotNil(version) 39 | XCTAssertEqual(version, "3.0-PREVIEW-4") 40 | } 41 | 42 | func testComparison() throws { 43 | let version = Version(rawValue: "3.1.5") 44 | XCTAssertLessThan(version ?? "0", "3.2") 45 | XCTAssertGreaterThan(version ?? "0", "3.1.4") 46 | } 47 | 48 | func testPreviewComparison() throws { 49 | let version = Version(rawValue: "3.0-PREVIEW-4") 50 | XCTAssertLessThan(version ?? "0", "4.0") 51 | XCTAssertGreaterThan(version ?? "0", "2.0") 52 | } 53 | 54 | func testWildcardVersion() throws { 55 | let version = Version(rawValue: "3.x") 56 | XCTAssertNotNil(version) 57 | XCTAssertLessThan(version ?? "0", "4.0") 58 | XCTAssertGreaterThan(version ?? "0", "2.0") 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /format.sh: -------------------------------------------------------------------------------- 1 | if [[ -z "${TRAVIS}" ]]; then 2 | CommandLineTool/swiftformat . --cache ignore 3 | fi 4 | --------------------------------------------------------------------------------