├── .gitignore ├── LICENCE ├── README.md ├── XcodeFormatter.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcbaselines │ └── 8507ECFD1D8B2631003A7DEA.xcbaseline │ ├── FB9686D4-F7C3-452E-A88A-8068778B237D.plist │ └── Info.plist ├── XcodeFormatter ├── Core │ ├── CodeAnalyzer │ │ ├── CodeBlock.swift │ │ ├── CodeBlockAnalyzer.swift │ │ ├── CodePosition.swift │ │ └── EmptyLineCorrection.swift │ ├── LintFileComment.swift │ ├── LintLine.swift │ ├── LintSpace.swift │ └── Regex │ │ ├── Extensions.swift │ │ ├── MatchCorrector.swift │ │ ├── MatchPattern.swift │ │ └── RegexpMatch.swift └── Supporting files │ ├── AppDelegate.swift │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── XFormatter-1.png │ │ ├── XFormatter-2.png │ │ ├── XFormatter-3.png │ │ ├── XFormatter-4.png │ │ ├── XFormatter-5.png │ │ ├── XFormatter-6.png │ │ ├── XFormatter-7.png │ │ ├── XFormatter-8.png │ │ ├── XFormatter-9.png │ │ └── XFormatter.png │ ├── Base.lproj │ └── Main.storyboard │ ├── Info.plist │ └── ViewController.swift ├── XcodeFormatterExtension ├── Info.plist ├── SourceEditorCommand.swift ├── SourceEditorExtension.swift ├── Supporting Files │ └── SwiftyLinter.entitlements └── XcodeFormatterExtension.entitlements └── XcodeFormatterTests ├── CodeBlockAnalyzerTests.swift ├── ColonSpacerTests.swift ├── CommaSpaceTests.swift ├── CommentLintTests.swift ├── EmptyLineInsertionTests.swift ├── Info.plist ├── PrivatizerTests.swift ├── ReturnArrowSpacerTests.swift ├── TernaryOperatorSpacerTests.swift └── TrailingCurlyBracketTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build 3 | xcuserdata/ 4 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Vijaya Prakash Kandel 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Xcode-Formatter (~>Swinter) 2 | [![Platform](https://img.shields.io/badge/platform-xcode8-blue.svg?style=flat 3 | )](https://developer.apple.com/iphone/index.action) 4 | [![Language](https://img.shields.io/badge/language-swift3-brightgreen.svg?style=flat 5 | )](https://developer.apple.com/swift) 6 | [![License](http://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat 7 | )](http://mit-license.org) 8 | [![Twitter](https://img.shields.io/badge/twitter-@kandelvijaya-blue.svg?style=flat)](http://twitter.com/kandelvijaya) 9 | 10 | Xcode Swift Formatter according to Coding Guidelines specified by Swift and Zalando iOS team. 11 | (This is compatible with Swift3, Xcode 8 and macOS Sierra ) 12 | 13 | ### Notes: 14 | - Uses SourceKit extension that was made public in 2016 WWDC 15 | - This is a file based Linter. 16 | 17 | 18 | ### Features: 19 | 1. Makes classes `final` unless they are explicitly ```open``` declared. 20 | *TODO: Add inheritance based finlization (requires whole module lookup)* 21 | 2. In type declaration 22 | * `let a:String` -> `let a: String` 23 | * `let a : String` -> `let a: String` 24 | 3. In Dictionary 25 | * `[String:AnyObject]` -> `[String: AnyObject]` 26 | * `["a" : "apple"]` -> `["a": "apple"]` 27 | 4. Before/After Comma 28 | * `sum(a: Int,b:Int)` -> `sum(a: Int, b:Int)` 29 | * `sum(a: Int, b:Int)` -> `sum(a: Int, b:Int)` 30 | * `sum(a: Int ,b:Int)` -> `sum(a: Int, b:Int)` 31 | 5. Before/After return type symbol 32 | * `)->Int{` -> `) -> Int{` 33 | * `) -> Int{` -> `) -> Int{` 34 | 6. Trailing `{` symbol 35 | * `do(){` -> `do() {` 36 | * `do() {` -> `do() {` 37 | * `class A{` -> `class A {` 38 | * `x.map{--}.filter{--}` -> `x.map {--}. {--}` 39 | * `else{` -> `else {` 40 | 7. Auto Format Comment at the begining of the file (Only If the Default XCode generated exists.) 41 | 8. Insert empty line 42 | * empty line before and after Type creation 43 | * empty line before and after the ending } of that type 44 | 45 | ```// 46 | // CommentLintTests.swift 47 | // Copyright © 2016 SomeCompany. All rights reserved. 48 | // 49 | 50 | import Foundation 51 | 52 | final class EmptyLineCorrection { 53 | 54 | private var currentLineIndex = 0 55 | 56 | private enum Direction { 57 | 58 | case up, down 59 | 60 | } 61 | 62 | } 63 | 64 | ``` 65 | 9. Sub-commands for Xcode to pick individual lint items 66 | 10. Turn off linting and correction if the match is inside a string quote/literal 67 | 68 | ## Using and Customization 69 | 70 | 1 Feel free to build and run for yourself. 71 | 2 If you want to customize how the autocorrector works then `LintSpace`, `LintLine`, `LineFileComment` are the places you should be heading. 72 | * All these files have a regex pattern and requires a dictionary of `[LineIndex: ReplaceString]` as `rules`. Thats it! 73 | * For example: to format in such way `[Int : String]`, change the dictionary rule at `LintSpace` with `[1: " ", 3: " "]` where 1 and 3 are capture groups of the regex. 74 | 75 | ## Contributing 76 | 1. Ensure to write Unit Tests for every single public API that you write. Beware of edge cases. 77 | 2. Dont test private methods. 78 | 3. Its usually better to start by reading Unit Tests for specific area before actually doing something. Unit Tests are `the` documentation. 79 | 80 | ## In progress 81 | 3. Adding support to have empty before and after each `//MARK:` annoataion 82 | 83 | ## TODO 84 | 1. Performance of the whole linting/correcting process 85 | 2. Refactoring of methods that add empty lines before/after 86 | -------------------------------------------------------------------------------- /XcodeFormatter.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2857BC161D8BF1B800E22971 /* ReturnArrowSpacerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2857BC151D8BF1B800E22971 /* ReturnArrowSpacerTests.swift */; }; 11 | 28DCA3291D9696470005EE8C /* PrivatizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28DCA3281D9696470005EE8C /* PrivatizerTests.swift */; }; 12 | 8507ECF11D8B2631003A7DEA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ECF01D8B2631003A7DEA /* AppDelegate.swift */; }; 13 | 8507ECF31D8B2631003A7DEA /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ECF21D8B2631003A7DEA /* ViewController.swift */; }; 14 | 8507ECF51D8B2631003A7DEA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8507ECF41D8B2631003A7DEA /* Assets.xcassets */; }; 15 | 8507ECF81D8B2631003A7DEA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8507ECF61D8B2631003A7DEA /* Main.storyboard */; }; 16 | 8507ED0E1D8B2695003A7DEA /* LintSpace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ED0D1D8B2695003A7DEA /* LintSpace.swift */; }; 17 | 8507ED101D8B26B5003A7DEA /* ColonSpacerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ED0F1D8B26B5003A7DEA /* ColonSpacerTests.swift */; }; 18 | 8507ED191D8B274C003A7DEA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8507ED181D8B274C003A7DEA /* Cocoa.framework */; }; 19 | 8507ED1E1D8B274C003A7DEA /* SourceEditorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ED1D1D8B274C003A7DEA /* SourceEditorExtension.swift */; }; 20 | 8507ED201D8B274C003A7DEA /* SourceEditorCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ED1F1D8B274C003A7DEA /* SourceEditorCommand.swift */; }; 21 | 8507ED241D8B274C003A7DEA /* XFormatter.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 8507ED161D8B274C003A7DEA /* XFormatter.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 22 | 8507ED291D8B27E3003A7DEA /* LintSpace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ED0D1D8B2695003A7DEA /* LintSpace.swift */; }; 23 | 8507ED2A1D8B27E8003A7DEA /* LintSpace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ED0D1D8B2695003A7DEA /* LintSpace.swift */; }; 24 | 8507ED2F1D8BC153003A7DEA /* CommaSpaceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ED2E1D8BC153003A7DEA /* CommaSpaceTests.swift */; }; 25 | 8507ED311D8C8198003A7DEA /* TrailingCurlyBracketTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ED301D8C8198003A7DEA /* TrailingCurlyBracketTests.swift */; }; 26 | 8507ED331D8D8906003A7DEA /* MatchCorrector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ED321D8D8906003A7DEA /* MatchCorrector.swift */; }; 27 | 8507ED341D8D965B003A7DEA /* MatchCorrector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ED321D8D8906003A7DEA /* MatchCorrector.swift */; }; 28 | 8507ED351D8D9663003A7DEA /* MatchCorrector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ED321D8D8906003A7DEA /* MatchCorrector.swift */; }; 29 | 8507ED391D8E77AB003A7DEA /* LintFileComment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ED381D8E77AB003A7DEA /* LintFileComment.swift */; }; 30 | 8507ED3A1D8E798B003A7DEA /* LintFileComment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ED381D8E77AB003A7DEA /* LintFileComment.swift */; }; 31 | 8507ED3B1D8E798B003A7DEA /* LintFileComment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ED381D8E77AB003A7DEA /* LintFileComment.swift */; }; 32 | 8507ED3D1D8ED9C3003A7DEA /* CommentLintTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8507ED3C1D8ED9C3003A7DEA /* CommentLintTests.swift */; }; 33 | 858BDDA61D9B00C5005A6CB5 /* RegexpMatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858BDDA51D9B00C5005A6CB5 /* RegexpMatch.swift */; }; 34 | 858BDDA81D9B010B005A6CB5 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858BDDA71D9B010B005A6CB5 /* Extensions.swift */; }; 35 | 858BDDA91D9B01A3005A6CB5 /* RegexpMatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858BDDA51D9B00C5005A6CB5 /* RegexpMatch.swift */; }; 36 | 858BDDAA1D9B01A4005A6CB5 /* RegexpMatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858BDDA51D9B00C5005A6CB5 /* RegexpMatch.swift */; }; 37 | 858BDDAB1D9B01A7005A6CB5 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858BDDA71D9B010B005A6CB5 /* Extensions.swift */; }; 38 | 858BDDAC1D9B01A7005A6CB5 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858BDDA71D9B010B005A6CB5 /* Extensions.swift */; }; 39 | 858BDDAE1D9B0657005A6CB5 /* MatchPattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858BDDAD1D9B0657005A6CB5 /* MatchPattern.swift */; }; 40 | 858BDDAF1D9B0671005A6CB5 /* MatchPattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858BDDAD1D9B0657005A6CB5 /* MatchPattern.swift */; }; 41 | 858BDDB01D9B0671005A6CB5 /* MatchPattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858BDDAD1D9B0657005A6CB5 /* MatchPattern.swift */; }; 42 | 8598D7611DA053E500AD8B65 /* TernaryOperatorSpacerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8598D7601DA053E500AD8B65 /* TernaryOperatorSpacerTests.swift */; }; 43 | 85E19CC51D8EF6EA00F11938 /* CodeBlockAnalyzer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CC41D8EF6EA00F11938 /* CodeBlockAnalyzer.swift */; }; 44 | 85E19CC91D8F024E00F11938 /* LintLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CC81D8F024E00F11938 /* LintLine.swift */; }; 45 | 85E19CCA1D8F07B700F11938 /* LintLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CC81D8F024E00F11938 /* LintLine.swift */; }; 46 | 85E19CCB1D8F07B900F11938 /* LintLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CC81D8F024E00F11938 /* LintLine.swift */; }; 47 | 85E19CCC1D8F07BE00F11938 /* CodeBlockAnalyzer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CC41D8EF6EA00F11938 /* CodeBlockAnalyzer.swift */; }; 48 | 85E19CCD1D8F07BE00F11938 /* CodeBlockAnalyzer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CC41D8EF6EA00F11938 /* CodeBlockAnalyzer.swift */; }; 49 | 85E19CCF1D8F1D3400F11938 /* CodeBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CCE1D8F1D3400F11938 /* CodeBlock.swift */; }; 50 | 85E19CD01D8F1D3900F11938 /* CodeBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CCE1D8F1D3400F11938 /* CodeBlock.swift */; }; 51 | 85E19CD11D8F1D3A00F11938 /* CodeBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CCE1D8F1D3400F11938 /* CodeBlock.swift */; }; 52 | 85E19CD31D8F1D6200F11938 /* CodePosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CD21D8F1D6200F11938 /* CodePosition.swift */; }; 53 | 85E19CD41D8F1D6B00F11938 /* CodePosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CD21D8F1D6200F11938 /* CodePosition.swift */; }; 54 | 85E19CD51D8F1D6B00F11938 /* CodePosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CD21D8F1D6200F11938 /* CodePosition.swift */; }; 55 | 85E19CD71D90716200F11938 /* EmptyLineCorrection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CD61D90716200F11938 /* EmptyLineCorrection.swift */; }; 56 | 85E19CD81D90717C00F11938 /* EmptyLineCorrection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CD61D90716200F11938 /* EmptyLineCorrection.swift */; }; 57 | 85E19CD91D90717D00F11938 /* EmptyLineCorrection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CD61D90716200F11938 /* EmptyLineCorrection.swift */; }; 58 | 85E19CDD1D91CB5200F11938 /* EmptyLineInsertionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CDC1D91CB5200F11938 /* EmptyLineInsertionTests.swift */; }; 59 | 85E19CE11D91D79C00F11938 /* CodeBlockAnalyzerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E19CE01D91D79B00F11938 /* CodeBlockAnalyzerTests.swift */; }; 60 | /* End PBXBuildFile section */ 61 | 62 | /* Begin PBXContainerItemProxy section */ 63 | 8507ECFF1D8B2631003A7DEA /* PBXContainerItemProxy */ = { 64 | isa = PBXContainerItemProxy; 65 | containerPortal = 8507ECE51D8B2631003A7DEA /* Project object */; 66 | proxyType = 1; 67 | remoteGlobalIDString = 8507ECEC1D8B2631003A7DEA; 68 | remoteInfo = Swinter; 69 | }; 70 | 8507ED221D8B274C003A7DEA /* PBXContainerItemProxy */ = { 71 | isa = PBXContainerItemProxy; 72 | containerPortal = 8507ECE51D8B2631003A7DEA /* Project object */; 73 | proxyType = 1; 74 | remoteGlobalIDString = 8507ED151D8B274C003A7DEA; 75 | remoteInfo = SwiftyLinter; 76 | }; 77 | /* End PBXContainerItemProxy section */ 78 | 79 | /* Begin PBXCopyFilesBuildPhase section */ 80 | 8507ED281D8B274C003A7DEA /* Embed App Extensions */ = { 81 | isa = PBXCopyFilesBuildPhase; 82 | buildActionMask = 2147483647; 83 | dstPath = ""; 84 | dstSubfolderSpec = 13; 85 | files = ( 86 | 8507ED241D8B274C003A7DEA /* XFormatter.appex in Embed App Extensions */, 87 | ); 88 | name = "Embed App Extensions"; 89 | runOnlyForDeploymentPostprocessing = 0; 90 | }; 91 | /* End PBXCopyFilesBuildPhase section */ 92 | 93 | /* Begin PBXFileReference section */ 94 | 2857BC151D8BF1B800E22971 /* ReturnArrowSpacerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReturnArrowSpacerTests.swift; sourceTree = ""; }; 95 | 28DCA3281D9696470005EE8C /* PrivatizerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivatizerTests.swift; sourceTree = ""; }; 96 | 8507ECED1D8B2631003A7DEA /* XcodeFormatter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XcodeFormatter.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97 | 8507ECF01D8B2631003A7DEA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 98 | 8507ECF21D8B2631003A7DEA /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 99 | 8507ECF41D8B2631003A7DEA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 100 | 8507ECF71D8B2631003A7DEA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 101 | 8507ECF91D8B2631003A7DEA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 102 | 8507ECFE1D8B2631003A7DEA /* XcodeFormatterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XcodeFormatterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 103 | 8507ED041D8B2631003A7DEA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 104 | 8507ED0D1D8B2695003A7DEA /* LintSpace.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LintSpace.swift; sourceTree = ""; }; 105 | 8507ED0F1D8B26B5003A7DEA /* ColonSpacerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColonSpacerTests.swift; sourceTree = ""; }; 106 | 8507ED161D8B274C003A7DEA /* XFormatter.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = XFormatter.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 107 | 8507ED181D8B274C003A7DEA /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 108 | 8507ED1C1D8B274C003A7DEA /* SwiftyLinter.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SwiftyLinter.entitlements; sourceTree = ""; }; 109 | 8507ED1D1D8B274C003A7DEA /* SourceEditorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceEditorExtension.swift; sourceTree = ""; }; 110 | 8507ED1F1D8B274C003A7DEA /* SourceEditorCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceEditorCommand.swift; sourceTree = ""; }; 111 | 8507ED211D8B274C003A7DEA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 112 | 8507ED2E1D8BC153003A7DEA /* CommaSpaceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommaSpaceTests.swift; sourceTree = ""; }; 113 | 8507ED301D8C8198003A7DEA /* TrailingCurlyBracketTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrailingCurlyBracketTests.swift; sourceTree = ""; }; 114 | 8507ED321D8D8906003A7DEA /* MatchCorrector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MatchCorrector.swift; sourceTree = ""; }; 115 | 8507ED381D8E77AB003A7DEA /* LintFileComment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LintFileComment.swift; sourceTree = ""; }; 116 | 8507ED3C1D8ED9C3003A7DEA /* CommentLintTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommentLintTests.swift; sourceTree = ""; }; 117 | 858BDDA51D9B00C5005A6CB5 /* RegexpMatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegexpMatch.swift; sourceTree = ""; }; 118 | 858BDDA71D9B010B005A6CB5 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; 119 | 858BDDAD1D9B0657005A6CB5 /* MatchPattern.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MatchPattern.swift; sourceTree = ""; }; 120 | 8598D7601DA053E500AD8B65 /* TernaryOperatorSpacerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TernaryOperatorSpacerTests.swift; sourceTree = ""; }; 121 | 85E19CC41D8EF6EA00F11938 /* CodeBlockAnalyzer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodeBlockAnalyzer.swift; sourceTree = ""; }; 122 | 85E19CC81D8F024E00F11938 /* LintLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LintLine.swift; sourceTree = ""; }; 123 | 85E19CCE1D8F1D3400F11938 /* CodeBlock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodeBlock.swift; sourceTree = ""; }; 124 | 85E19CD21D8F1D6200F11938 /* CodePosition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodePosition.swift; sourceTree = ""; }; 125 | 85E19CD61D90716200F11938 /* EmptyLineCorrection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyLineCorrection.swift; sourceTree = ""; }; 126 | 85E19CDC1D91CB5200F11938 /* EmptyLineInsertionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyLineInsertionTests.swift; sourceTree = ""; }; 127 | 85E19CE01D91D79B00F11938 /* CodeBlockAnalyzerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodeBlockAnalyzerTests.swift; sourceTree = ""; }; 128 | /* End PBXFileReference section */ 129 | 130 | /* Begin PBXFrameworksBuildPhase section */ 131 | 8507ECEA1D8B2631003A7DEA /* Frameworks */ = { 132 | isa = PBXFrameworksBuildPhase; 133 | buildActionMask = 2147483647; 134 | files = ( 135 | ); 136 | runOnlyForDeploymentPostprocessing = 0; 137 | }; 138 | 8507ECFB1D8B2631003A7DEA /* Frameworks */ = { 139 | isa = PBXFrameworksBuildPhase; 140 | buildActionMask = 2147483647; 141 | files = ( 142 | ); 143 | runOnlyForDeploymentPostprocessing = 0; 144 | }; 145 | 8507ED131D8B274C003A7DEA /* Frameworks */ = { 146 | isa = PBXFrameworksBuildPhase; 147 | buildActionMask = 2147483647; 148 | files = ( 149 | 8507ED191D8B274C003A7DEA /* Cocoa.framework in Frameworks */, 150 | ); 151 | runOnlyForDeploymentPostprocessing = 0; 152 | }; 153 | /* End PBXFrameworksBuildPhase section */ 154 | 155 | /* Begin PBXGroup section */ 156 | 8507ECE41D8B2631003A7DEA = { 157 | isa = PBXGroup; 158 | children = ( 159 | 8507ED171D8B274C003A7DEA /* Frameworks */, 160 | 8507ECEE1D8B2631003A7DEA /* Products */, 161 | 8507ECEF1D8B2631003A7DEA /* XcodeFormatter */, 162 | 8507ED1A1D8B274C003A7DEA /* XcodeFormatterExtension */, 163 | 8507ED011D8B2631003A7DEA /* XcodeFormatterTests */, 164 | ); 165 | sourceTree = ""; 166 | }; 167 | 8507ECEE1D8B2631003A7DEA /* Products */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | 8507ECED1D8B2631003A7DEA /* XcodeFormatter.app */, 171 | 8507ECFE1D8B2631003A7DEA /* XcodeFormatterTests.xctest */, 172 | 8507ED161D8B274C003A7DEA /* XFormatter.appex */, 173 | ); 174 | name = Products; 175 | sourceTree = ""; 176 | }; 177 | 8507ECEF1D8B2631003A7DEA /* XcodeFormatter */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | 8507ED111D8B26EB003A7DEA /* Core */, 181 | 8507ED2B1D8B3C2D003A7DEA /* Supporting files */, 182 | ); 183 | path = XcodeFormatter; 184 | sourceTree = ""; 185 | }; 186 | 8507ED011D8B2631003A7DEA /* XcodeFormatterTests */ = { 187 | isa = PBXGroup; 188 | children = ( 189 | 85E19CE01D91D79B00F11938 /* CodeBlockAnalyzerTests.swift */, 190 | 8507ED0F1D8B26B5003A7DEA /* ColonSpacerTests.swift */, 191 | 8507ED2E1D8BC153003A7DEA /* CommaSpaceTests.swift */, 192 | 8507ED3C1D8ED9C3003A7DEA /* CommentLintTests.swift */, 193 | 85E19CDC1D91CB5200F11938 /* EmptyLineInsertionTests.swift */, 194 | 8507ED041D8B2631003A7DEA /* Info.plist */, 195 | 28DCA3281D9696470005EE8C /* PrivatizerTests.swift */, 196 | 2857BC151D8BF1B800E22971 /* ReturnArrowSpacerTests.swift */, 197 | 8598D7601DA053E500AD8B65 /* TernaryOperatorSpacerTests.swift */, 198 | 8507ED301D8C8198003A7DEA /* TrailingCurlyBracketTests.swift */, 199 | ); 200 | path = XcodeFormatterTests; 201 | sourceTree = ""; 202 | }; 203 | 8507ED111D8B26EB003A7DEA /* Core */ = { 204 | isa = PBXGroup; 205 | children = ( 206 | 85E19CC61D8F015300F11938 /* CodeAnalyzer */, 207 | 85E19CC71D8F016500F11938 /* Regex */, 208 | 8507ED381D8E77AB003A7DEA /* LintFileComment.swift */, 209 | 85E19CC81D8F024E00F11938 /* LintLine.swift */, 210 | 8507ED0D1D8B2695003A7DEA /* LintSpace.swift */, 211 | ); 212 | path = Core; 213 | sourceTree = ""; 214 | }; 215 | 8507ED171D8B274C003A7DEA /* Frameworks */ = { 216 | isa = PBXGroup; 217 | children = ( 218 | 8507ED181D8B274C003A7DEA /* Cocoa.framework */, 219 | ); 220 | name = Frameworks; 221 | sourceTree = ""; 222 | }; 223 | 8507ED1A1D8B274C003A7DEA /* XcodeFormatterExtension */ = { 224 | isa = PBXGroup; 225 | children = ( 226 | 8507ED1B1D8B274C003A7DEA /* Supporting Files */, 227 | 8507ED211D8B274C003A7DEA /* Info.plist */, 228 | 8507ED1F1D8B274C003A7DEA /* SourceEditorCommand.swift */, 229 | 8507ED1D1D8B274C003A7DEA /* SourceEditorExtension.swift */, 230 | ); 231 | path = XcodeFormatterExtension; 232 | sourceTree = ""; 233 | }; 234 | 8507ED1B1D8B274C003A7DEA /* Supporting Files */ = { 235 | isa = PBXGroup; 236 | children = ( 237 | 8507ED1C1D8B274C003A7DEA /* SwiftyLinter.entitlements */, 238 | ); 239 | path = "Supporting Files"; 240 | sourceTree = ""; 241 | }; 242 | 8507ED2B1D8B3C2D003A7DEA /* Supporting files */ = { 243 | isa = PBXGroup; 244 | children = ( 245 | 8507ECF01D8B2631003A7DEA /* AppDelegate.swift */, 246 | 8507ECF41D8B2631003A7DEA /* Assets.xcassets */, 247 | 8507ECF91D8B2631003A7DEA /* Info.plist */, 248 | 8507ECF61D8B2631003A7DEA /* Main.storyboard */, 249 | 8507ECF21D8B2631003A7DEA /* ViewController.swift */, 250 | ); 251 | path = "Supporting files"; 252 | sourceTree = ""; 253 | }; 254 | 85E19CC61D8F015300F11938 /* CodeAnalyzer */ = { 255 | isa = PBXGroup; 256 | children = ( 257 | 85E19CCE1D8F1D3400F11938 /* CodeBlock.swift */, 258 | 85E19CC41D8EF6EA00F11938 /* CodeBlockAnalyzer.swift */, 259 | 85E19CD21D8F1D6200F11938 /* CodePosition.swift */, 260 | 85E19CD61D90716200F11938 /* EmptyLineCorrection.swift */, 261 | ); 262 | path = CodeAnalyzer; 263 | sourceTree = ""; 264 | }; 265 | 85E19CC71D8F016500F11938 /* Regex */ = { 266 | isa = PBXGroup; 267 | children = ( 268 | 858BDDA71D9B010B005A6CB5 /* Extensions.swift */, 269 | 8507ED321D8D8906003A7DEA /* MatchCorrector.swift */, 270 | 858BDDAD1D9B0657005A6CB5 /* MatchPattern.swift */, 271 | 858BDDA51D9B00C5005A6CB5 /* RegexpMatch.swift */, 272 | ); 273 | path = Regex; 274 | sourceTree = ""; 275 | }; 276 | /* End PBXGroup section */ 277 | 278 | /* Begin PBXNativeTarget section */ 279 | 8507ECEC1D8B2631003A7DEA /* XcodeFormatter */ = { 280 | isa = PBXNativeTarget; 281 | buildConfigurationList = 8507ED071D8B2631003A7DEA /* Build configuration list for PBXNativeTarget "XcodeFormatter" */; 282 | buildPhases = ( 283 | 8507ECE91D8B2631003A7DEA /* Sources */, 284 | 8507ECEA1D8B2631003A7DEA /* Frameworks */, 285 | 8507ECEB1D8B2631003A7DEA /* Resources */, 286 | 8507ED281D8B274C003A7DEA /* Embed App Extensions */, 287 | ); 288 | buildRules = ( 289 | ); 290 | dependencies = ( 291 | 8507ED231D8B274C003A7DEA /* PBXTargetDependency */, 292 | ); 293 | name = XcodeFormatter; 294 | productName = Swinter; 295 | productReference = 8507ECED1D8B2631003A7DEA /* XcodeFormatter.app */; 296 | productType = "com.apple.product-type.application"; 297 | }; 298 | 8507ECFD1D8B2631003A7DEA /* XcodeFormatterTests */ = { 299 | isa = PBXNativeTarget; 300 | buildConfigurationList = 8507ED0A1D8B2631003A7DEA /* Build configuration list for PBXNativeTarget "XcodeFormatterTests" */; 301 | buildPhases = ( 302 | 8507ECFA1D8B2631003A7DEA /* Sources */, 303 | 8507ECFB1D8B2631003A7DEA /* Frameworks */, 304 | 8507ECFC1D8B2631003A7DEA /* Resources */, 305 | ); 306 | buildRules = ( 307 | ); 308 | dependencies = ( 309 | 8507ED001D8B2631003A7DEA /* PBXTargetDependency */, 310 | ); 311 | name = XcodeFormatterTests; 312 | productName = SwinterTests; 313 | productReference = 8507ECFE1D8B2631003A7DEA /* XcodeFormatterTests.xctest */; 314 | productType = "com.apple.product-type.bundle.unit-test"; 315 | }; 316 | 8507ED151D8B274C003A7DEA /* XFormatter */ = { 317 | isa = PBXNativeTarget; 318 | buildConfigurationList = 8507ED251D8B274C003A7DEA /* Build configuration list for PBXNativeTarget "XFormatter" */; 319 | buildPhases = ( 320 | 8507ED121D8B274C003A7DEA /* Sources */, 321 | 8507ED131D8B274C003A7DEA /* Frameworks */, 322 | 8507ED141D8B274C003A7DEA /* Resources */, 323 | ); 324 | buildRules = ( 325 | ); 326 | dependencies = ( 327 | ); 328 | name = XFormatter; 329 | productName = SwiftyLinter; 330 | productReference = 8507ED161D8B274C003A7DEA /* XFormatter.appex */; 331 | productType = "com.apple.product-type.xcode-extension"; 332 | }; 333 | /* End PBXNativeTarget section */ 334 | 335 | /* Begin PBXProject section */ 336 | 8507ECE51D8B2631003A7DEA /* Project object */ = { 337 | isa = PBXProject; 338 | attributes = { 339 | LastSwiftUpdateCheck = 0800; 340 | LastUpgradeCheck = 0800; 341 | ORGANIZATIONNAME = "Vijaya Prakash Kandel"; 342 | TargetAttributes = { 343 | 8507ECEC1D8B2631003A7DEA = { 344 | CreatedOnToolsVersion = 8.0; 345 | DevelopmentTeam = 46ETJSLJJM; 346 | ProvisioningStyle = Automatic; 347 | }; 348 | 8507ECFD1D8B2631003A7DEA = { 349 | CreatedOnToolsVersion = 8.0; 350 | DevelopmentTeam = 46ETJSLJJM; 351 | ProvisioningStyle = Automatic; 352 | TestTargetID = 8507ECEC1D8B2631003A7DEA; 353 | }; 354 | 8507ED151D8B274C003A7DEA = { 355 | CreatedOnToolsVersion = 8.0; 356 | DevelopmentTeam = 46ETJSLJJM; 357 | ProvisioningStyle = Automatic; 358 | }; 359 | }; 360 | }; 361 | buildConfigurationList = 8507ECE81D8B2631003A7DEA /* Build configuration list for PBXProject "XcodeFormatter" */; 362 | compatibilityVersion = "Xcode 3.2"; 363 | developmentRegion = English; 364 | hasScannedForEncodings = 0; 365 | knownRegions = ( 366 | en, 367 | Base, 368 | ); 369 | mainGroup = 8507ECE41D8B2631003A7DEA; 370 | productRefGroup = 8507ECEE1D8B2631003A7DEA /* Products */; 371 | projectDirPath = ""; 372 | projectRoot = ""; 373 | targets = ( 374 | 8507ECEC1D8B2631003A7DEA /* XcodeFormatter */, 375 | 8507ECFD1D8B2631003A7DEA /* XcodeFormatterTests */, 376 | 8507ED151D8B274C003A7DEA /* XFormatter */, 377 | ); 378 | }; 379 | /* End PBXProject section */ 380 | 381 | /* Begin PBXResourcesBuildPhase section */ 382 | 8507ECEB1D8B2631003A7DEA /* Resources */ = { 383 | isa = PBXResourcesBuildPhase; 384 | buildActionMask = 2147483647; 385 | files = ( 386 | 8507ECF51D8B2631003A7DEA /* Assets.xcassets in Resources */, 387 | 8507ECF81D8B2631003A7DEA /* Main.storyboard in Resources */, 388 | ); 389 | runOnlyForDeploymentPostprocessing = 0; 390 | }; 391 | 8507ECFC1D8B2631003A7DEA /* Resources */ = { 392 | isa = PBXResourcesBuildPhase; 393 | buildActionMask = 2147483647; 394 | files = ( 395 | ); 396 | runOnlyForDeploymentPostprocessing = 0; 397 | }; 398 | 8507ED141D8B274C003A7DEA /* Resources */ = { 399 | isa = PBXResourcesBuildPhase; 400 | buildActionMask = 2147483647; 401 | files = ( 402 | ); 403 | runOnlyForDeploymentPostprocessing = 0; 404 | }; 405 | /* End PBXResourcesBuildPhase section */ 406 | 407 | /* Begin PBXSourcesBuildPhase section */ 408 | 8507ECE91D8B2631003A7DEA /* Sources */ = { 409 | isa = PBXSourcesBuildPhase; 410 | buildActionMask = 2147483647; 411 | files = ( 412 | 858BDDAE1D9B0657005A6CB5 /* MatchPattern.swift in Sources */, 413 | 85E19CCF1D8F1D3400F11938 /* CodeBlock.swift in Sources */, 414 | 85E19CD71D90716200F11938 /* EmptyLineCorrection.swift in Sources */, 415 | 8507ED331D8D8906003A7DEA /* MatchCorrector.swift in Sources */, 416 | 85E19CC91D8F024E00F11938 /* LintLine.swift in Sources */, 417 | 8507ECF31D8B2631003A7DEA /* ViewController.swift in Sources */, 418 | 85E19CC51D8EF6EA00F11938 /* CodeBlockAnalyzer.swift in Sources */, 419 | 85E19CD31D8F1D6200F11938 /* CodePosition.swift in Sources */, 420 | 858BDDA81D9B010B005A6CB5 /* Extensions.swift in Sources */, 421 | 8507ED391D8E77AB003A7DEA /* LintFileComment.swift in Sources */, 422 | 8507ED0E1D8B2695003A7DEA /* LintSpace.swift in Sources */, 423 | 8507ECF11D8B2631003A7DEA /* AppDelegate.swift in Sources */, 424 | 858BDDA61D9B00C5005A6CB5 /* RegexpMatch.swift in Sources */, 425 | ); 426 | runOnlyForDeploymentPostprocessing = 0; 427 | }; 428 | 8507ECFA1D8B2631003A7DEA /* Sources */ = { 429 | isa = PBXSourcesBuildPhase; 430 | buildActionMask = 2147483647; 431 | files = ( 432 | 8507ED2A1D8B27E8003A7DEA /* LintSpace.swift in Sources */, 433 | 858BDDAB1D9B01A7005A6CB5 /* Extensions.swift in Sources */, 434 | 8598D7611DA053E500AD8B65 /* TernaryOperatorSpacerTests.swift in Sources */, 435 | 8507ED3B1D8E798B003A7DEA /* LintFileComment.swift in Sources */, 436 | 85E19CD81D90717C00F11938 /* EmptyLineCorrection.swift in Sources */, 437 | 28DCA3291D9696470005EE8C /* PrivatizerTests.swift in Sources */, 438 | 8507ED2F1D8BC153003A7DEA /* CommaSpaceTests.swift in Sources */, 439 | 8507ED341D8D965B003A7DEA /* MatchCorrector.swift in Sources */, 440 | 8507ED311D8C8198003A7DEA /* TrailingCurlyBracketTests.swift in Sources */, 441 | 85E19CD41D8F1D6B00F11938 /* CodePosition.swift in Sources */, 442 | 85E19CD01D8F1D3900F11938 /* CodeBlock.swift in Sources */, 443 | 8507ED101D8B26B5003A7DEA /* ColonSpacerTests.swift in Sources */, 444 | 85E19CDD1D91CB5200F11938 /* EmptyLineInsertionTests.swift in Sources */, 445 | 858BDDA91D9B01A3005A6CB5 /* RegexpMatch.swift in Sources */, 446 | 85E19CCD1D8F07BE00F11938 /* CodeBlockAnalyzer.swift in Sources */, 447 | 85E19CE11D91D79C00F11938 /* CodeBlockAnalyzerTests.swift in Sources */, 448 | 8507ED3D1D8ED9C3003A7DEA /* CommentLintTests.swift in Sources */, 449 | 2857BC161D8BF1B800E22971 /* ReturnArrowSpacerTests.swift in Sources */, 450 | 85E19CCB1D8F07B900F11938 /* LintLine.swift in Sources */, 451 | 858BDDAF1D9B0671005A6CB5 /* MatchPattern.swift in Sources */, 452 | ); 453 | runOnlyForDeploymentPostprocessing = 0; 454 | }; 455 | 8507ED121D8B274C003A7DEA /* Sources */ = { 456 | isa = PBXSourcesBuildPhase; 457 | buildActionMask = 2147483647; 458 | files = ( 459 | 858BDDB01D9B0671005A6CB5 /* MatchPattern.swift in Sources */, 460 | 85E19CD11D8F1D3A00F11938 /* CodeBlock.swift in Sources */, 461 | 85E19CD91D90717D00F11938 /* EmptyLineCorrection.swift in Sources */, 462 | 85E19CCC1D8F07BE00F11938 /* CodeBlockAnalyzer.swift in Sources */, 463 | 8507ED1E1D8B274C003A7DEA /* SourceEditorExtension.swift in Sources */, 464 | 8507ED291D8B27E3003A7DEA /* LintSpace.swift in Sources */, 465 | 85E19CCA1D8F07B700F11938 /* LintLine.swift in Sources */, 466 | 85E19CD51D8F1D6B00F11938 /* CodePosition.swift in Sources */, 467 | 858BDDAC1D9B01A7005A6CB5 /* Extensions.swift in Sources */, 468 | 8507ED3A1D8E798B003A7DEA /* LintFileComment.swift in Sources */, 469 | 8507ED351D8D9663003A7DEA /* MatchCorrector.swift in Sources */, 470 | 8507ED201D8B274C003A7DEA /* SourceEditorCommand.swift in Sources */, 471 | 858BDDAA1D9B01A4005A6CB5 /* RegexpMatch.swift in Sources */, 472 | ); 473 | runOnlyForDeploymentPostprocessing = 0; 474 | }; 475 | /* End PBXSourcesBuildPhase section */ 476 | 477 | /* Begin PBXTargetDependency section */ 478 | 8507ED001D8B2631003A7DEA /* PBXTargetDependency */ = { 479 | isa = PBXTargetDependency; 480 | target = 8507ECEC1D8B2631003A7DEA /* XcodeFormatter */; 481 | targetProxy = 8507ECFF1D8B2631003A7DEA /* PBXContainerItemProxy */; 482 | }; 483 | 8507ED231D8B274C003A7DEA /* PBXTargetDependency */ = { 484 | isa = PBXTargetDependency; 485 | target = 8507ED151D8B274C003A7DEA /* XFormatter */; 486 | targetProxy = 8507ED221D8B274C003A7DEA /* PBXContainerItemProxy */; 487 | }; 488 | /* End PBXTargetDependency section */ 489 | 490 | /* Begin PBXVariantGroup section */ 491 | 8507ECF61D8B2631003A7DEA /* Main.storyboard */ = { 492 | isa = PBXVariantGroup; 493 | children = ( 494 | 8507ECF71D8B2631003A7DEA /* Base */, 495 | ); 496 | name = Main.storyboard; 497 | path = .; 498 | sourceTree = ""; 499 | }; 500 | /* End PBXVariantGroup section */ 501 | 502 | /* Begin XCBuildConfiguration section */ 503 | 8507ED051D8B2631003A7DEA /* Debug */ = { 504 | isa = XCBuildConfiguration; 505 | buildSettings = { 506 | ALWAYS_SEARCH_USER_PATHS = NO; 507 | CLANG_ANALYZER_NONNULL = YES; 508 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 509 | CLANG_CXX_LIBRARY = "libc++"; 510 | CLANG_ENABLE_MODULES = YES; 511 | CLANG_ENABLE_OBJC_ARC = YES; 512 | CLANG_WARN_BOOL_CONVERSION = YES; 513 | CLANG_WARN_CONSTANT_CONVERSION = YES; 514 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 515 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 516 | CLANG_WARN_EMPTY_BODY = YES; 517 | CLANG_WARN_ENUM_CONVERSION = YES; 518 | CLANG_WARN_INFINITE_RECURSION = YES; 519 | CLANG_WARN_INT_CONVERSION = YES; 520 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 521 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 522 | CLANG_WARN_UNREACHABLE_CODE = YES; 523 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 524 | CODE_SIGN_IDENTITY = "-"; 525 | COPY_PHASE_STRIP = NO; 526 | DEBUG_INFORMATION_FORMAT = dwarf; 527 | ENABLE_STRICT_OBJC_MSGSEND = YES; 528 | ENABLE_TESTABILITY = YES; 529 | GCC_C_LANGUAGE_STANDARD = gnu99; 530 | GCC_DYNAMIC_NO_PIC = NO; 531 | GCC_NO_COMMON_BLOCKS = YES; 532 | GCC_OPTIMIZATION_LEVEL = 0; 533 | GCC_PREPROCESSOR_DEFINITIONS = ( 534 | "DEBUG=1", 535 | "$(inherited)", 536 | ); 537 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 538 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 539 | GCC_WARN_UNDECLARED_SELECTOR = YES; 540 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 541 | GCC_WARN_UNUSED_FUNCTION = YES; 542 | GCC_WARN_UNUSED_VARIABLE = YES; 543 | MACOSX_DEPLOYMENT_TARGET = 10.12; 544 | MTL_ENABLE_DEBUG_INFO = YES; 545 | ONLY_ACTIVE_ARCH = YES; 546 | SDKROOT = macosx; 547 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 548 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 549 | }; 550 | name = Debug; 551 | }; 552 | 8507ED061D8B2631003A7DEA /* Release */ = { 553 | isa = XCBuildConfiguration; 554 | buildSettings = { 555 | ALWAYS_SEARCH_USER_PATHS = NO; 556 | CLANG_ANALYZER_NONNULL = YES; 557 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 558 | CLANG_CXX_LIBRARY = "libc++"; 559 | CLANG_ENABLE_MODULES = YES; 560 | CLANG_ENABLE_OBJC_ARC = YES; 561 | CLANG_WARN_BOOL_CONVERSION = YES; 562 | CLANG_WARN_CONSTANT_CONVERSION = YES; 563 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 564 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 565 | CLANG_WARN_EMPTY_BODY = YES; 566 | CLANG_WARN_ENUM_CONVERSION = YES; 567 | CLANG_WARN_INFINITE_RECURSION = YES; 568 | CLANG_WARN_INT_CONVERSION = YES; 569 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 570 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 571 | CLANG_WARN_UNREACHABLE_CODE = YES; 572 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 573 | CODE_SIGN_IDENTITY = "-"; 574 | COPY_PHASE_STRIP = NO; 575 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 576 | ENABLE_NS_ASSERTIONS = NO; 577 | ENABLE_STRICT_OBJC_MSGSEND = YES; 578 | GCC_C_LANGUAGE_STANDARD = gnu99; 579 | GCC_NO_COMMON_BLOCKS = YES; 580 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 581 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 582 | GCC_WARN_UNDECLARED_SELECTOR = YES; 583 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 584 | GCC_WARN_UNUSED_FUNCTION = YES; 585 | GCC_WARN_UNUSED_VARIABLE = YES; 586 | MACOSX_DEPLOYMENT_TARGET = 10.12; 587 | MTL_ENABLE_DEBUG_INFO = NO; 588 | SDKROOT = macosx; 589 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 590 | }; 591 | name = Release; 592 | }; 593 | 8507ED081D8B2631003A7DEA /* Debug */ = { 594 | isa = XCBuildConfiguration; 595 | buildSettings = { 596 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 597 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 598 | CODE_SIGN_IDENTITY = "Mac Developer"; 599 | COMBINE_HIDPI_IMAGES = YES; 600 | DEVELOPMENT_TEAM = 46ETJSLJJM; 601 | INFOPLIST_FILE = "XcodeFormatter/Supporting files/Info.plist"; 602 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 603 | MACOSX_DEPLOYMENT_TARGET = 10.11; 604 | PRODUCT_BUNDLE_IDENTIFIER = com.kandelvijaya.XcodeFormatter; 605 | PRODUCT_NAME = "$(TARGET_NAME)"; 606 | SWIFT_VERSION = 3.0; 607 | }; 608 | name = Debug; 609 | }; 610 | 8507ED091D8B2631003A7DEA /* Release */ = { 611 | isa = XCBuildConfiguration; 612 | buildSettings = { 613 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 614 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 615 | CODE_SIGN_IDENTITY = "Mac Developer"; 616 | COMBINE_HIDPI_IMAGES = YES; 617 | DEVELOPMENT_TEAM = 46ETJSLJJM; 618 | INFOPLIST_FILE = "XcodeFormatter/Supporting files/Info.plist"; 619 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 620 | MACOSX_DEPLOYMENT_TARGET = 10.11; 621 | PRODUCT_BUNDLE_IDENTIFIER = com.kandelvijaya.XcodeFormatter; 622 | PRODUCT_NAME = "$(TARGET_NAME)"; 623 | SWIFT_VERSION = 3.0; 624 | }; 625 | name = Release; 626 | }; 627 | 8507ED0B1D8B2631003A7DEA /* Debug */ = { 628 | isa = XCBuildConfiguration; 629 | buildSettings = { 630 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 631 | BUNDLE_LOADER = "$(TEST_HOST)"; 632 | COMBINE_HIDPI_IMAGES = YES; 633 | DEVELOPMENT_TEAM = 46ETJSLJJM; 634 | INFOPLIST_FILE = XcodeFormatterTests/Info.plist; 635 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 636 | PRODUCT_BUNDLE_IDENTIFIER = com.kandelvijaya.SwinterTests; 637 | PRODUCT_NAME = "$(TARGET_NAME)"; 638 | SWIFT_VERSION = 3.0; 639 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/XcodeFormatter.app/Contents/MacOS/XcodeFormatter"; 640 | }; 641 | name = Debug; 642 | }; 643 | 8507ED0C1D8B2631003A7DEA /* Release */ = { 644 | isa = XCBuildConfiguration; 645 | buildSettings = { 646 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 647 | BUNDLE_LOADER = "$(TEST_HOST)"; 648 | COMBINE_HIDPI_IMAGES = YES; 649 | DEVELOPMENT_TEAM = 46ETJSLJJM; 650 | INFOPLIST_FILE = XcodeFormatterTests/Info.plist; 651 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 652 | PRODUCT_BUNDLE_IDENTIFIER = com.kandelvijaya.SwinterTests; 653 | PRODUCT_NAME = "$(TARGET_NAME)"; 654 | SWIFT_VERSION = 3.0; 655 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/XcodeFormatter.app/Contents/MacOS/XcodeFormatter"; 656 | }; 657 | name = Release; 658 | }; 659 | 8507ED261D8B274C003A7DEA /* Debug */ = { 660 | isa = XCBuildConfiguration; 661 | buildSettings = { 662 | CODE_SIGN_ENTITLEMENTS = XcodeFormatterExtension/XcodeFormatterExtension.entitlements; 663 | CODE_SIGN_IDENTITY = "Mac Developer"; 664 | COMBINE_HIDPI_IMAGES = YES; 665 | DEVELOPMENT_TEAM = 46ETJSLJJM; 666 | INFOPLIST_FILE = XcodeFormatterExtension/Info.plist; 667 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks"; 668 | MACOSX_DEPLOYMENT_TARGET = 10.11; 669 | PRODUCT_BUNDLE_IDENTIFIER = com.kandelvijaya.XcodeFormatter.Formatter; 670 | PRODUCT_NAME = "$(TARGET_NAME)"; 671 | SKIP_INSTALL = YES; 672 | SWIFT_VERSION = 3.0; 673 | }; 674 | name = Debug; 675 | }; 676 | 8507ED271D8B274C003A7DEA /* Release */ = { 677 | isa = XCBuildConfiguration; 678 | buildSettings = { 679 | CODE_SIGN_ENTITLEMENTS = XcodeFormatterExtension/XcodeFormatterExtension.entitlements; 680 | CODE_SIGN_IDENTITY = "Mac Developer"; 681 | COMBINE_HIDPI_IMAGES = YES; 682 | DEVELOPMENT_TEAM = 46ETJSLJJM; 683 | INFOPLIST_FILE = XcodeFormatterExtension/Info.plist; 684 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks"; 685 | MACOSX_DEPLOYMENT_TARGET = 10.11; 686 | PRODUCT_BUNDLE_IDENTIFIER = com.kandelvijaya.XcodeFormatter.Formatter; 687 | PRODUCT_NAME = "$(TARGET_NAME)"; 688 | SKIP_INSTALL = YES; 689 | SWIFT_VERSION = 3.0; 690 | }; 691 | name = Release; 692 | }; 693 | /* End XCBuildConfiguration section */ 694 | 695 | /* Begin XCConfigurationList section */ 696 | 8507ECE81D8B2631003A7DEA /* Build configuration list for PBXProject "XcodeFormatter" */ = { 697 | isa = XCConfigurationList; 698 | buildConfigurations = ( 699 | 8507ED051D8B2631003A7DEA /* Debug */, 700 | 8507ED061D8B2631003A7DEA /* Release */, 701 | ); 702 | defaultConfigurationIsVisible = 0; 703 | defaultConfigurationName = Release; 704 | }; 705 | 8507ED071D8B2631003A7DEA /* Build configuration list for PBXNativeTarget "XcodeFormatter" */ = { 706 | isa = XCConfigurationList; 707 | buildConfigurations = ( 708 | 8507ED081D8B2631003A7DEA /* Debug */, 709 | 8507ED091D8B2631003A7DEA /* Release */, 710 | ); 711 | defaultConfigurationIsVisible = 0; 712 | defaultConfigurationName = Release; 713 | }; 714 | 8507ED0A1D8B2631003A7DEA /* Build configuration list for PBXNativeTarget "XcodeFormatterTests" */ = { 715 | isa = XCConfigurationList; 716 | buildConfigurations = ( 717 | 8507ED0B1D8B2631003A7DEA /* Debug */, 718 | 8507ED0C1D8B2631003A7DEA /* Release */, 719 | ); 720 | defaultConfigurationIsVisible = 0; 721 | defaultConfigurationName = Release; 722 | }; 723 | 8507ED251D8B274C003A7DEA /* Build configuration list for PBXNativeTarget "XFormatter" */ = { 724 | isa = XCConfigurationList; 725 | buildConfigurations = ( 726 | 8507ED261D8B274C003A7DEA /* Debug */, 727 | 8507ED271D8B274C003A7DEA /* Release */, 728 | ); 729 | defaultConfigurationIsVisible = 0; 730 | defaultConfigurationName = Release; 731 | }; 732 | /* End XCConfigurationList section */ 733 | }; 734 | rootObject = 8507ECE51D8B2631003A7DEA /* Project object */; 735 | } 736 | -------------------------------------------------------------------------------- /XcodeFormatter.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /XcodeFormatter.xcodeproj/xcshareddata/xcbaselines/8507ECFD1D8B2631003A7DEA.xcbaseline/FB9686D4-F7C3-452E-A88A-8068778B237D.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | classNames 6 | 7 | CodeBlockAnalyzerTests 8 | 9 | testPerformanceOfCodeBlockAnalyzer() 10 | 11 | com.apple.XCTPerformanceMetric_WallClockTime 12 | 13 | baselineAverage 14 | 0.08 15 | baselineIntegrationDisplayName 16 | Local Baseline 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /XcodeFormatter.xcodeproj/xcshareddata/xcbaselines/8507ECFD1D8B2631003A7DEA.xcbaseline/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | runDestinationsByUUID 6 | 7 | FB9686D4-F7C3-452E-A88A-8068778B237D 8 | 9 | localComputer 10 | 11 | busSpeedInMHz 12 | 100 13 | cpuCount 14 | 1 15 | cpuKind 16 | Intel Core i7 17 | cpuSpeedInMHz 18 | 2000 19 | logicalCPUCoresPerPackage 20 | 8 21 | modelCode 22 | MacBookPro11,2 23 | physicalCPUCoresPerPackage 24 | 4 25 | platformIdentifier 26 | com.apple.platform.macosx 27 | 28 | targetArchitecture 29 | x86_64 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /XcodeFormatter/Core/CodeAnalyzer/CodeBlock.swift: -------------------------------------------------------------------------------- 1 | // CodeBlock.swift 2 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 3 | // 4 | 5 | import Foundation 6 | 7 | enum CodeBlockType: String { 8 | 9 | case ProtocolKind = "protocol" 10 | case ClassKind = "class" 11 | case StructKind = "struct" 12 | case EnumKind = "enum" 13 | case ExtensionKind = "extension" 14 | case FunctionKind = "func" 15 | case OtherKind = "" //NOTSET 16 | 17 | static var primaries: [CodeBlockType] { 18 | return [.ProtocolKind, .ClassKind, .StructKind, .EnumKind, .ExtensionKind] 19 | } 20 | 21 | //Other is not part of all 22 | static var all: [CodeBlockType] { 23 | return primaries + [.FunctionKind] 24 | } 25 | 26 | } 27 | 28 | struct CodeBlock { 29 | 30 | let start: CodePosition 31 | let end: CodePosition 32 | var type: CodeBlockType? 33 | 34 | init(startPosition: CodePosition, endPosition: CodePosition) { 35 | start = startPosition 36 | end = endPosition 37 | type = typeForCodeBlock() 38 | } 39 | 40 | ///Type information is usually encoded into the starting line. 41 | ///Not doing so is a bad coding design in this projects case. 42 | /// For example: 43 | /// 44 | /// class A { 45 | /// let x: Int 46 | /// } 47 | /// 48 | /// Similar is the case for all constructs in Swift 49 | ///Note: 50 | ///This method is only concious about Types of primary things 51 | ///which include Class, Enum, Struct, Protocol, Extension. 52 | ///The rest are categorized into Other. 53 | /// 54 | ///Beware of things like checking against .class 55 | /// 56 | /// 57 | private func typeForCodeBlock() -> CodeBlockType { 58 | let typeInfoLine = start.lineContent 59 | 60 | //In all case, Type are followed with Space. 61 | //exclude lines like these `X.map{ $0.class }.forEach{` although there is a class keyword 62 | let includedTypeRep = CodeBlockType.all.map{ $0.rawValue }.filter{ 63 | typeInfoLine.contains($0 + " ") && !typeInfoLine.contains("."+$0) 64 | } 65 | 66 | //NOTE: Anticipating that a line might have two keywords for code blocks like `class func` 67 | let intermediate = includedTypeRep.reduce(""){ $0 + $1} 68 | 69 | // to exclude `class func dosomething() { }` to have line separators 70 | if intermediate == CodeBlockType.ClassKind.rawValue + CodeBlockType.FunctionKind.rawValue { 71 | return CodeBlockType.FunctionKind 72 | } 73 | 74 | ///to exclude `protocol A: class{` to have line separators 75 | if intermediate == CodeBlockType.ProtocolKind.rawValue + CodeBlockType.ClassKind.rawValue { 76 | return CodeBlockType.ProtocolKind 77 | } 78 | 79 | if let foundTypeRep = includedTypeRep.first { 80 | return CodeBlockType(rawValue: foundTypeRep)! 81 | } else { 82 | return CodeBlockType.OtherKind 83 | } 84 | } 85 | 86 | } 87 | 88 | //MARK:- empty line analyzers 89 | extension CodeBlock { 90 | 91 | func lineIndexesOfEmptyLinesAtStart(content: [String]) -> [Int] { 92 | var accumulator = [Int]() 93 | var currentLineIndex = self.start.line + 1 94 | 95 | while content[currentLineIndex] == "/n" { 96 | accumulator.append(currentLineIndex) 97 | currentLineIndex += 1 98 | } 99 | return accumulator 100 | } 101 | 102 | func lineIndexesOfEmptyLinesBeforeEnd(content: [String]) -> [Int] { 103 | var accumulator = [Int]() 104 | var currentLineIndex = self.start.line - 1 105 | 106 | while content[currentLineIndex] == "/n" { 107 | accumulator.append(currentLineIndex) 108 | currentLineIndex -= 1 109 | } 110 | return accumulator 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /XcodeFormatter/Core/CodeAnalyzer/CodeBlockAnalyzer.swift: -------------------------------------------------------------------------------- 1 | // CodeBlockAnalyzer.swift 2 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 3 | // 4 | 5 | import Foundation 6 | 7 | enum CodeBlockIndicator: String { 8 | 9 | case opening = "{" 10 | case closing = "}" 11 | 12 | } 13 | 14 | final class CodeBlockAnalyzer { 15 | 16 | private var toBeAnalysedCodePosition = [CodePosition]() 17 | 18 | public private(set) var identifiedCodeBlocks = [CodeBlock]() 19 | 20 | func codeBlocks(for content: [String]) -> [CodeBlock] { 21 | for (lineIndex, lineContent) in content.enumerated() { 22 | 23 | if lineContent.isInsideComment() { continue } 24 | 25 | //Early Exit 26 | if !lineContent.contains(CodeBlockIndicator.opening.rawValue) && !lineContent.contains(CodeBlockIndicator.closing.rawValue) { 27 | continue 28 | } 29 | 30 | //TODO: this method takes a lot of CPU cycles. Find a alternative. 31 | for (section, char) in lineContent.characters.enumerated() where !isCharacterInsideStringQuote(at: section, onLine: lineContent) { 32 | switch String(char) { 33 | case CodeBlockIndicator.opening.rawValue: 34 | let pos = CodePosition(lineContent: lineContent, line: lineIndex, section: section, indicator: .opening) 35 | add(codePosition: pos) 36 | case CodeBlockIndicator.closing.rawValue: 37 | let pos = CodePosition(lineContent: lineContent, line: lineIndex, section: section, indicator: .closing) 38 | add(codePosition: pos) 39 | default: 40 | break 41 | } 42 | } 43 | 44 | } 45 | 46 | return identifiedCodeBlocks 47 | } 48 | 49 | private func add(codePosition: CodePosition) { 50 | if toBeAnalysedCodePosition.isEmpty { 51 | toBeAnalysedCodePosition.append(codePosition) 52 | return 53 | } 54 | 55 | guard let lastCodePosition = toBeAnalysedCodePosition.last else { 56 | return 57 | } 58 | 59 | if CodePosition.arePair(openingPosition: lastCodePosition, endingPosition: codePosition) { 60 | let codeBlock = CodeBlock(startPosition: lastCodePosition, endPosition: codePosition) 61 | identifiedCodeBlocks.append(codeBlock) 62 | toBeAnalysedCodePosition.removeLast() 63 | } else { 64 | toBeAnalysedCodePosition.append(codePosition) 65 | } 66 | } 67 | 68 | private func isCharacterInsideStringQuote(at section: Int, onLine line: String) -> Bool { 69 | let startIndex = line.index(line.startIndex, offsetBy: section) 70 | let endIndex = line.index(line.startIndex, offsetBy: section + 1) 71 | let range = Range(uncheckedBounds: (startIndex, endIndex)) 72 | return RegexpMatch.isMatch(atRange: range, insideQuoteStringOnLine: line) 73 | } 74 | 75 | } 76 | 77 | -------------------------------------------------------------------------------- /XcodeFormatter/Core/CodeAnalyzer/CodePosition.swift: -------------------------------------------------------------------------------- 1 | // CodePosition.swift 2 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 3 | // 4 | 5 | import Foundation 6 | 7 | struct CodePosition { 8 | 9 | let lineContent: String 10 | let line: Int 11 | let section: Int 12 | let indicator: CodeBlockIndicator 13 | 14 | static func arePair(openingPosition: CodePosition, endingPosition: CodePosition) -> Bool { 15 | switch (openingPosition.indicator, endingPosition.indicator) { 16 | case (.opening, .closing): 17 | return true 18 | default: 19 | return false 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /XcodeFormatter/Core/CodeAnalyzer/EmptyLineCorrection.swift: -------------------------------------------------------------------------------- 1 | // EmptyLineCorrection.swift 2 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 3 | // 4 | 5 | import Foundation 6 | 7 | enum CodeDirection { 8 | case down, up, both 9 | } 10 | 11 | class EmptyLineCorrection { 12 | 13 | typealias CodePositonToCorrect = (CodePosition, CodeDirection) 14 | 15 | private let mutableContent: NSMutableArray 16 | private var codePositionsToCorrect: [CodePositonToCorrect] = [] 17 | private var offset = 0 18 | private var currentLineIndex = 0 19 | 20 | /// Create a EmptyLineCorrection Object that can correct empty lines 21 | /// 22 | /// - parameter mutableContent: Array of strings 23 | init(mutableContent: NSMutableArray) { 24 | self.mutableContent = mutableContent 25 | } 26 | 27 | /// Add code positions where empty lines should be added or removed 28 | /// depending on the direction parameter that is passed. Provided 29 | /// codePositos will be sorted in ascending order before correction. 30 | /// Calling this method multiple times will add the unique values 31 | /// and apply the correction. 32 | /// NOTE: 2 code positions with same lineIndex but different section/column 33 | /// values will not add/remove 2 lines. It just adds the work. 34 | /// 35 | /// - parameter codePositons: [CodePosition] 36 | /// - parameter direction: CodeDirection. which direction to correct. 37 | func add(codePositons: [CodePosition], withDirectionToCorrect direction: CodeDirection) { 38 | let newPosition = codePositons.map{ ($0, direction) } 39 | codePositionsToCorrect.append(contentsOf: newPosition) 40 | } 41 | 42 | func correct() { 43 | codePositionsToCorrect.sorted { (cp1, cp2) -> Bool in 44 | return cp1.0.line < cp2.0.line 45 | } 46 | .forEach { pos in 47 | if pos.1 == .both { 48 | correctEmptySpaceAbove(position: pos.0) 49 | correctEmptySpaceBelow(position: pos.0) 50 | } else if pos.1 == .up { 51 | correctEmptySpaceAbove(position: pos.0) 52 | } else if pos.1 == .down { 53 | correctEmptySpaceAbove(position: pos.0) 54 | } 55 | } 56 | } 57 | 58 | //TODO: Refactor these 2 algorithms into a simplified model 59 | private func correctEmptySpaceAbove(position: CodePosition) { 60 | let currentSearchLineIndex = correctedLineIndex(for: position) - 1 //looking upwards 61 | let indexToInsertEmptyLine = correctedLineIndex(for: position) 62 | var indicesOfEmptyLines = [Int]() 63 | var indicesOfCommentLines = [Int]() 64 | 65 | //When the code block starts the file. 66 | if currentSearchLineIndex < 0 { 67 | addEmptySpace(at: 0) 68 | return 69 | } 70 | 71 | for index in stride(from: currentSearchLineIndex, to: -1, by: -1) { 72 | if isEmpty(line: mutableContent[index] as! String) { 73 | indicesOfEmptyLines.append(index) 74 | }else if (mutableContent[index] as! String).isInsideComment() { 75 | //If we encountered emptyLines before finding comments then stop this loop 76 | if indicesOfEmptyLines.count > 0 { 77 | break 78 | }else { 79 | indicesOfCommentLines.append(index) 80 | continue 81 | } 82 | 83 | }else { 84 | break 85 | } 86 | } 87 | 88 | if indicesOfEmptyLines.count == 0 { 89 | addEmptySpace(at: indexToInsertEmptyLine - indicesOfCommentLines.count) 90 | } else if indicesOfEmptyLines.count == 1{ 91 | return 92 | } else { 93 | removeAllEmptySpace(atIndices: indicesOfEmptyLines) 94 | addEmptySpace(at: correctedLineIndex(for: position)) 95 | } 96 | } 97 | 98 | private func correctEmptySpaceBelow(position: CodePosition) { 99 | var currentSearchLineIndex = correctedLineIndex(for: position) + 1 //looking downwards 100 | var indicesOfEmptyLines = [Int]() //Weired calculation of 14 billion indexes 101 | 102 | //When there is no EOF empty line then add one and return 103 | guard currentSearchLineIndex < mutableContent.count else { 104 | mutableContent.add("\n") 105 | return 106 | } 107 | 108 | while isEmpty(line: mutableContent[currentSearchLineIndex] as! String) { //TODO: match for whitespace []*\n$ 109 | indicesOfEmptyLines.append(currentSearchLineIndex) 110 | if currentSearchLineIndex == mutableContent.count - 1 { 111 | break 112 | } else { 113 | currentSearchLineIndex += 1 114 | } 115 | } 116 | 117 | if indicesOfEmptyLines.count == 0 { 118 | addEmptySpace(at: correctedLineIndex(for: position) + 1) 119 | } else if indicesOfEmptyLines.count == 1{ 120 | return 121 | } else { 122 | removeAllEmptySpace(atIndices: indicesOfEmptyLines) 123 | //using correctedPosition is wrong here 124 | addEmptySpace(at: indicesOfEmptyLines[0]) 125 | } 126 | } 127 | 128 | private func correctedLineIndex(for position: CodePosition) -> Int { 129 | return position.line + offset 130 | } 131 | 132 | private func addEmptySpace(at index: Int) { 133 | offset += 1 134 | mutableContent.insert("\n", at: index) 135 | } 136 | 137 | private func removeAllEmptySpace(atIndices indices: [Int]) { 138 | offset += -indices.count 139 | //Removing indices.first or every index is a error index out of bound. 140 | indices.forEach { _ in mutableContent.removeObject(at: indices.min()! ) } 141 | } 142 | 143 | private func isEmpty(line: String) -> Bool { 144 | let trimmedLine = line.trimmingCharacters(in: CharacterSet(charactersIn: " ")) 145 | return trimmedLine == "\n" 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /XcodeFormatter/Core/LintFileComment.swift: -------------------------------------------------------------------------------- 1 | // LintFileComment.swift 2 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 3 | // 4 | 5 | import Foundation 6 | 7 | final class LintFileComment { 8 | 9 | func extractNewCommentLines(from content: String) -> [String] { 10 | guard let regex = MatchPattern.fileComment.regex else { 11 | return [] 12 | } 13 | let commentMatches = RegexpMatch.findAllMatches(in: content, with: regex) 14 | 15 | guard var firstMatches = commentMatches.first?.matches else { return [] } 16 | //0 -> gives whole comment which matched 17 | firstMatches.removeFirst() 18 | return firstMatches.reduce( [String]() ){ $0 + [$1.content] } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /XcodeFormatter/Core/LintLine.swift: -------------------------------------------------------------------------------- 1 | // LintLine.swift 2 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 3 | // 4 | 5 | import Foundation 6 | 7 | final class LintLine { 8 | 9 | private var content: NSMutableArray? 10 | 11 | private lazy var allCodeBlocks: [CodeBlock] = { 12 | guard let content = self.content else { return [] } 13 | let codeLines = content.reduce([String]()) { 14 | $0 + [String(describing: $1)] 15 | } 16 | return CodeBlockAnalyzer().codeBlocks(for: codeLines) 17 | 18 | }() 19 | 20 | func ensureProperEmptyLines(in content: NSMutableArray) { 21 | self.content = content 22 | let primaryPositon = primaryCodeBlockPositons() 23 | let functionPosition = functionCodeBlockPositions() 24 | let corrector = EmptyLineCorrection(mutableContent: content) 25 | 26 | corrector.add(codePositons: primaryPositon, withDirectionToCorrect: .both) 27 | corrector.add(codePositons: functionPosition, withDirectionToCorrect: .up) 28 | 29 | corrector.correct() //The magical mutator 30 | } 31 | 32 | /// returns [CodePosition] for CodeBlocks that require empty line above and below 33 | private func primaryCodeBlockPositons() -> [CodePosition] { 34 | 35 | let allPrimaryCodeBlocksSpanningMultilpleLines = allCodeBlocks.filter { 36 | if let thisType = $0.type, CodeBlockType.primaries.contains(thisType) { 37 | return $0.start.line != $0.end.line 38 | } 39 | return false 40 | } 41 | let primaries = allPrimaryCodeBlocksSpanningMultilpleLines 42 | 43 | //Each codeblock has start and end Position 44 | return primaries.map{ [$0.start, $0.end] }.flatMap{ $0 } 45 | } 46 | 47 | 48 | /// returns [CodePositon] of Function CodeBlocks with just start position to correct 49 | /// for empty line above it. 50 | private func functionCodeBlockPositions() -> [CodePosition] { 51 | let allFuncCodeBlocks = allCodeBlocks.filter { 52 | if let thisType = $0.type, thisType == CodeBlockType.FunctionKind { 53 | return true 54 | } 55 | return false 56 | } 57 | 58 | //We dont care about the end position for functions 59 | return allFuncCodeBlocks.map{ $0.start } 60 | } 61 | 62 | } 63 | 64 | -------------------------------------------------------------------------------- /XcodeFormatter/Core/LintSpace.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColonSpacer.swift 3 | // SwiftyLinter 4 | // 5 | // Created by Vijaya Prakash Kandel on 9/15/16. 6 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class LintSpace { 12 | 13 | // For performance reasons they are static let. 14 | private struct Constants { 15 | static let colonRegex = MatchPattern.colon.regex 16 | static let commaRegex = MatchPattern.comma.regex 17 | static let functionReturnArrowRegex = MatchPattern.functionReturnArrow.regex 18 | static let trailingCurlyBracketRegex = MatchPattern.trailingCurlyBracket.regex 19 | static let ternaryOperatorRegex = MatchPattern.ternaryOperator.regex 20 | } 21 | 22 | func correctColonSpace(line: String) -> String { 23 | guard let regex = Constants.colonRegex else { return line } 24 | guard line.contains(":") else { return line } 25 | let correctionInfo = MatchCorrectionInfo(regex: regex, forString: line, correctionRules: [1: "", 3: " "]) 26 | return MatchCorrector.correct(with: correctionInfo) 27 | } 28 | 29 | func correctCommaSeparation(line: String) -> String { 30 | guard let regex = Constants.commaRegex else { return line } 31 | guard line.contains(",") else { return line } 32 | let correctionInfo = MatchCorrectionInfo(regex:regex, forString: line, correctionRules: [1: "", 3: " "] ) 33 | return MatchCorrector.correct(with: correctionInfo) 34 | } 35 | 36 | func correctFunctionReturnArrow(line: String) -> String { 37 | guard let regex = Constants.functionReturnArrowRegex else { return line } 38 | guard line.contains("->") else { return line } 39 | let correctionInfo = MatchCorrectionInfo(regex:regex, forString: line, correctionRules: [1: " ", 3: " "] ) 40 | return MatchCorrector.correct(with: correctionInfo) 41 | } 42 | 43 | func correctTrailingCurlyBracket(line: String) -> String { 44 | guard let regex = Constants.trailingCurlyBracketRegex else { return line } 45 | guard line.contains("{") else { return line } 46 | let correctionInfo = MatchCorrectionInfo(regex:regex, forString: line, correctionRules: [2: " "] ) 47 | return MatchCorrector.correct(with: correctionInfo) 48 | } 49 | 50 | /// For correct results, dont call colonCorrector after this method. 51 | func correctTernaryOperator(line: String) -> String { 52 | guard let regex = Constants.ternaryOperatorRegex else { return line } 53 | guard line.contains(":") && line.contains("?") else { return line } 54 | let correctionInfo = MatchCorrectionInfo(regex: regex, forString: line, correctionRules: [1: " ", 2: " ", 3: " ", 4: " "]) 55 | return MatchCorrector.correct(with: correctionInfo) 56 | } 57 | 58 | } 59 | 60 | -------------------------------------------------------------------------------- /XcodeFormatter/Core/Regex/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 4 | // 5 | 6 | import Foundation 7 | 8 | 9 | public extension NSRange { 10 | 11 | //More effiecent version required 12 | func toRange(forString: String) -> Range { 13 | let lowerIndex = forString.index(forString.startIndex, offsetBy: location) 14 | let upperIndex = forString.index(forString.startIndex, offsetBy: location + length) 15 | return Range(uncheckedBounds: (lowerIndex, upperIndex)) 16 | } 17 | 18 | } 19 | 20 | public extension NSRegularExpression { 21 | 22 | static func regexFrom(pattern: String) -> NSRegularExpression? { 23 | do { 24 | let regex = try NSRegularExpression(pattern: pattern, options: []) 25 | return regex 26 | } catch { 27 | print("Bad Regular Expression \(pattern)") 28 | return nil 29 | } 30 | } 31 | 32 | } 33 | 34 | extension String { 35 | 36 | func isInsideComment() -> Bool { 37 | let trimmedSelf = self.trimmingCharacters(in: CharacterSet.whitespaces) 38 | return trimmedSelf.hasPrefix("//") 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /XcodeFormatter/Core/Regex/MatchCorrector.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RegexpReplacer.swift 3 | // Swinter 4 | // 5 | // Created by Vijaya Prakash Kandel on 9/17/16. 6 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | typealias MatchCorrectionRule = [Int: String] 12 | 13 | struct MatchCorrectionInfo { 14 | let regex: NSRegularExpression 15 | let line: String 16 | let rules: MatchCorrectionRule //correction rule 17 | 18 | init(regex: NSRegularExpression, forString line: String, correctionRules rules: [Int: String]) { 19 | self.regex = regex 20 | self.line = line 21 | self.rules = rules 22 | } 23 | } 24 | 25 | final class MatchCorrector { 26 | 27 | static func correct(with correctionInfo: MatchCorrectionInfo) -> String { 28 | let matches = RegexpMatch.findAllMatches(in: correctionInfo.line, with: correctionInfo.regex, ignoringMatch: .insideStringOrComment) 29 | let corrected = correct(line: correctionInfo.line, at: matches, with: correctionInfo.rules) 30 | return corrected 31 | } 32 | 33 | private static func correct(line: String, at matches: [Match], with rules: MatchCorrectionRule) -> String { 34 | var correctedLine = line 35 | var offset = 0 36 | 37 | matches.forEach { 38 | for (index, cpRange) in $0.matches.enumerated() { 39 | 40 | // Only indeces with provided rule are corrected. 41 | if let replaceMent = rules[index] { 42 | let currentRange = rangeFrom(range: cpRange.range, forStrings: [line, correctedLine], offset: offset) 43 | let currentOffset = -cpRange.content.characters.count + replaceMent.characters.count 44 | offset += currentOffset 45 | correctedLine.replaceSubrange(currentRange, with: replaceMent) 46 | } 47 | 48 | } 49 | } 50 | return correctedLine 51 | } 52 | 53 | private static func rangeFrom(range: Range, forStrings strings: [String], offset: Int) -> Range { 54 | //fatal error: cannot decrement invalid index :: caused while removing a lot of spaces towards the end side 55 | //fatal error: cannot increment beyond endIndex :: caused while adding spaces towards the end side 56 | //To eradicate these problem we will always convert range from the largest of the provided string 57 | let baseString = strings.sorted(by: {$0.characters.count > $01.characters.count}).first! 58 | let lowerIndex = baseString.index(range.lowerBound, offsetBy: offset) 59 | let upperIndex = baseString.index(range.upperBound, offsetBy: offset) 60 | return Range(uncheckedBounds: (lowerIndex, upperIndex)) 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /XcodeFormatter/Core/Regex/MatchPattern.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MatchPattern.swift 3 | // Swinter 4 | // 5 | // Created by Vijaya Prakash Kandel on 9/27/16. 6 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Try it on www.regex101.com 12 | /// Capture Groups are important. They are used as index 13 | /// in MatchCorrectionRule. That index starts from 1. Not 0. 14 | enum MatchPattern: String { 15 | 16 | /// Space matcher patterns 17 | /// capture groups 1 , 2 , 3 matches lhs space, identifier and rhs space 18 | /// Example: let i : Int 19 | /// CP1 = ` ` CP2 = `:` CP3 = ` ` 20 | /// 21 | case colon = "[\\S]([ ]*)(:)([ ]*)(?=[\\S])" 22 | case comma = "[\\S]([ ]*)(,)([ ]*)(?=[\\S])" 23 | case functionReturnArrow = "[\\S]([ ]*)(->)([ ]*)(?=[\\S])" 24 | case trailingCurlyBracket = "([^ \\.\\(\\[])([ ]*)\\{$" 25 | 26 | 27 | /// Ternary Operator pattern 28 | /// capture groups 1,2,3,4 matches the space inbetween from left to right 29 | /// Example: true ? 1 : 0 30 | /// CP1 = ` ` CP2 = ` ` CP3 = ` ` CP4 = ` ` 31 | /// 32 | /// This wont match 33 | /// [someOpt?.map{ $0 } : "xyz"] 34 | /// let dict = [(model as? AModelType) ?? AModelType : BModelType ] 35 | /// 36 | case ternaryOperator = "[^\\?](? 15 | } 16 | 17 | struct IgnoreMatch: OptionSet { 18 | var rawValue: UInt8 19 | 20 | static let insideSingleComment = IgnoreMatch(rawValue: 1 << 0) 21 | static let insideStringQuote = IgnoreMatch(rawValue: 1 << 1) 22 | 23 | static let insideStringOrComment:IgnoreMatch = [.insideSingleComment, .insideStringQuote] 24 | } 25 | 26 | final class RegexpMatch { 27 | 28 | private static let quoteRegex = MatchPattern.stringQuote.regex 29 | private static let commentRegex = MatchPattern.singleLineComment.regex 30 | 31 | static func findAllMatches(in contentString: String, with regex: NSRegularExpression, ignoringMatch: IgnoreMatch = []) -> [Match] { 32 | var matches: [Match] = [] 33 | 34 | //early exit when comment 35 | if ignoringMatch.contains(.insideSingleComment) && contentString.isInsideComment() { 36 | return matches 37 | } 38 | 39 | regex.enumerateMatches(in: contentString, options: .reportCompletion, range: NSMakeRange(0, contentString.characters.count), using: { (textCheckingResult, flags, status) in 40 | 41 | if let textChecking = textCheckingResult { 42 | let cpRanges = (0.., insideQuoteStringOnLine line: String) -> Bool { 71 | guard line.contains(String(Character("\""))) else { return false } 72 | guard let regex = quoteRegex else { return false } 73 | let substringUntilMatch = line.substring(to: range.lowerBound) 74 | let leftQuoteCount = regex.numberOfMatches(in: substringUntilMatch, options: [], range: NSMakeRange(0, substringUntilMatch.characters.count) ) 75 | return leftQuoteCount % 2 != 0 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /XcodeFormatter/Supporting files/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Swinter 4 | // 5 | // Created by Vijaya Prakash Kandel on 9/15/16. 6 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { } 13 | 14 | -------------------------------------------------------------------------------- /XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "XFormatter-6.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "XFormatter-7.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "XFormatter-8.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "XFormatter-9.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "XFormatter-5.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "XFormatter-4.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "XFormatter.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "XFormatter-3.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "XFormatter-1.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "XFormatter-2.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kandelvijaya/XcodeFormatter/1a66dfbb325bded2f64ad5a60f0f191e69f121e5/XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-1.png -------------------------------------------------------------------------------- /XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kandelvijaya/XcodeFormatter/1a66dfbb325bded2f64ad5a60f0f191e69f121e5/XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-2.png -------------------------------------------------------------------------------- /XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kandelvijaya/XcodeFormatter/1a66dfbb325bded2f64ad5a60f0f191e69f121e5/XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-3.png -------------------------------------------------------------------------------- /XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kandelvijaya/XcodeFormatter/1a66dfbb325bded2f64ad5a60f0f191e69f121e5/XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-4.png -------------------------------------------------------------------------------- /XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kandelvijaya/XcodeFormatter/1a66dfbb325bded2f64ad5a60f0f191e69f121e5/XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-5.png -------------------------------------------------------------------------------- /XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kandelvijaya/XcodeFormatter/1a66dfbb325bded2f64ad5a60f0f191e69f121e5/XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-6.png -------------------------------------------------------------------------------- /XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kandelvijaya/XcodeFormatter/1a66dfbb325bded2f64ad5a60f0f191e69f121e5/XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-7.png -------------------------------------------------------------------------------- /XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kandelvijaya/XcodeFormatter/1a66dfbb325bded2f64ad5a60f0f191e69f121e5/XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-8.png -------------------------------------------------------------------------------- /XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kandelvijaya/XcodeFormatter/1a66dfbb325bded2f64ad5a60f0f191e69f121e5/XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter-9.png -------------------------------------------------------------------------------- /XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kandelvijaya/XcodeFormatter/1a66dfbb325bded2f64ad5a60f0f191e69f121e5/XcodeFormatter/Supporting files/Assets.xcassets/AppIcon.appiconset/XFormatter.png -------------------------------------------------------------------------------- /XcodeFormatter/Supporting files/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | Default 509 | 510 | 511 | 512 | 513 | 514 | 515 | Left to Right 516 | 517 | 518 | 519 | 520 | 521 | 522 | Right to Left 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | Default 534 | 535 | 536 | 537 | 538 | 539 | 540 | Left to Right 541 | 542 | 543 | 544 | 545 | 546 | 547 | Right to Left 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | Thanks for Trying out! You can close this dialog and Restart Xcode 8+. You will find Swinter under Editor Menu when you are editing any source file. Enjoy!! 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | Thanks for Trying out! You can close this dialog and Restart Xcode 8+. You will find Swinter under Editor Menu when you are editing any source file. Enjoy!! 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | -------------------------------------------------------------------------------- /XcodeFormatter/Supporting files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSApplicationCategoryType 24 | public.app-category.developer-tools 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /XcodeFormatter/Supporting files/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Swinter 4 | // 5 | // Created by Vijaya Prakash Kandel on 9/15/16. 6 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ViewController: NSViewController { } 12 | 13 | -------------------------------------------------------------------------------- /XcodeFormatterExtension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | XFormatter 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSExtension 26 | 27 | NSExtensionAttributes 28 | 29 | XCSourceEditorCommandDefinitions 30 | 31 | 32 | XCSourceEditorCommandClassName 33 | $(PRODUCT_MODULE_NAME).SourceEditorCommand 34 | XCSourceEditorCommandIdentifier 35 | $(PRODUCT_BUNDLE_IDENTIFIER).LintAll 36 | XCSourceEditorCommandName 37 | Swiftly Lint File 38 | 39 | 40 | XCSourceEditorExtensionPrincipalClass 41 | $(PRODUCT_MODULE_NAME).SourceEditorExtension 42 | 43 | NSExtensionPointIdentifier 44 | com.apple.dt.Xcode.extension.source-editor 45 | 46 | NSHumanReadableCopyright 47 | Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 48 | 49 | 50 | -------------------------------------------------------------------------------- /XcodeFormatterExtension/SourceEditorCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SourceEditorCommand.swift 3 | // Linter 4 | // 5 | // Created by Vijaya Prakash Kandel on 9/11/16. 6 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XcodeKit 11 | 12 | class SourceEditorCommand: NSObject, XCSourceEditorCommand { 13 | 14 | func perform(with invocation: XCSourceEditorCommandInvocation, completionHandler: @escaping (Error?) -> Void ) -> Void { 15 | 16 | switch invocation.commandIdentifier { 17 | 18 | case CommandIdentifier.finilizeClass.rawValue: 19 | markClassFinalUnlessOpenSpecified(invocation: invocation) 20 | 21 | case CommandIdentifier.correctInterSpace.rawValue: 22 | ensureProperSpacing(invocation: invocation) 23 | 24 | case CommandIdentifier.correctEmptyLine.rawValue: 25 | ensureProperEmptyLines(invocation: invocation) 26 | 27 | case CommandIdentifier.correctComment.rawValue: 28 | ensureProperFileComment(invocation: invocation) 29 | 30 | case CommandIdentifier.swiftlyLintAll.rawValue: 31 | ensureProperSpacing(invocation: invocation) 32 | ensureProperEmptyLines(invocation: invocation) 33 | ensureProperFileComment(invocation: invocation) 34 | 35 | default: 36 | break 37 | } 38 | 39 | completionHandler(nil) 40 | } 41 | 42 | //MARK:- Private Methods 43 | 44 | fileprivate func forEachLine(invocation: XCSourceEditorCommandInvocation, check: (String) -> String?) { 45 | let originalLines = invocation.buffer.lines 46 | var changed: [Int: String] = [:] 47 | 48 | for (counter, lineContent) in originalLines.enumerated() { 49 | let lineString = String(describing: lineContent) 50 | if let modified = check(lineString) { 51 | changed[counter] = modified 52 | } 53 | } 54 | 55 | //mdofiy the lines array 56 | changed.forEach{ invocation.buffer.lines[$0] = $1 } 57 | } 58 | } 59 | 60 | 61 | extension SourceEditorCommand { 62 | 63 | /// Makes class declaration final 64 | /// Note: This doesnot check for 2 things 65 | /// 1. Inheritance tree. This could be added sometime later. 66 | /// 2. Inner classes are also finilized 67 | /// 68 | /// - parameter invocation: Text Buffer 69 | func markClassFinalUnlessOpenSpecified(invocation: XCSourceEditorCommandInvocation) { 70 | forEachLine(invocation: invocation) { lineString in 71 | var string = lineString 72 | let contains = ["class ", " class "] 73 | let doesnotContain = ["final ", "open ", ".class"] 74 | 75 | //If the line has " class " or "class " 76 | //And it doesnot have "final ", "open " or ".class" then it sounds like a class declaration 77 | guard string.contains(contains[0]) || string.contains(contains[1]) else { return nil } 78 | 79 | //Check if line contains keyword inside a quote 80 | guard !string.contains("\"") else { return nil } 81 | 82 | //TODO: classes inside should not be finilized 83 | guard doesnotContain.map(string.contains).filter({ $0 == true }).count == 0 else { return nil} 84 | let range = string.range(of: "class") 85 | let startIndex = range?.lowerBound 86 | string.insert(contentsOf: "final ", at: startIndex!) 87 | return string 88 | } 89 | } 90 | 91 | } 92 | 93 | extension SourceEditorCommand { 94 | 95 | /// Checks for the proper spacing in mostly 4 places 96 | /// 1. Trailing ) { in function declaration 97 | /// 2. Type information i.e. let x: String 98 | /// 3. Dictionary type info i.e [String: Int] or ["a": "apple"] 99 | /// 4. Space after a comma i.e ["a": "apple", "b": "ball"] or sum(1, 2) 100 | /// 101 | /// - parameter invocation: Text Buffer 102 | func ensureProperSpacing(invocation: XCSourceEditorCommandInvocation) { 103 | let spaceLinter = LintSpace() 104 | 105 | forEachLine(invocation: invocation) { strings in 106 | 107 | let allCorrected = [strings] 108 | .map { spaceLinter.correctColonSpace(line: $0) } 109 | .map { spaceLinter.correctCommaSeparation(line: $0) } 110 | .map { spaceLinter.correctTernaryOperator(line: $0) } 111 | .map { spaceLinter.correctFunctionReturnArrow(line: $0) } 112 | .map { spaceLinter.correctTrailingCurlyBracket(line: $0) } 113 | .first 114 | 115 | return allCorrected 116 | } 117 | } 118 | 119 | } 120 | 121 | extension SourceEditorCommand { 122 | 123 | //NOTE: App seems to crash when mutating the complete buffer directly. 124 | func ensureProperFileComment(invocation: XCSourceEditorCommandInvocation) { 125 | let newFileCommentLines = LintFileComment().extractNewCommentLines(from: invocation.buffer.completeBuffer) 126 | guard !newFileCommentLines.isEmpty else { return } 127 | //There usually are 7 lines of default Xcode comment template. 128 | (0..<7).forEach{ _ in invocation.buffer.lines.removeObject(at: 0) } 129 | 130 | for value in newFileCommentLines.reversed() { 131 | invocation.buffer.lines.insert(value, at: 0) 132 | } 133 | } 134 | 135 | } 136 | 137 | extension SourceEditorCommand { 138 | 139 | //1. Ensure empty lines at opening and ending of code blocks for Types 140 | // Protocol | enum | struct | class 141 | //2. Ensure 1 empty line at the EOF 142 | //3. Ensure 1 empty line between functions 143 | //4. Ensure 1 empty line before and after 144 | // 145 | // NOTE: Mutation was preferred over to immutability assignment 146 | // due to the fact that app crashed randomly when doing so 147 | // 148 | // invocation.buffer.lines.removeAll() 149 | // correctedLines.forEach { 150 | // invocation.buffer.lines.add($0) 151 | // } 152 | // 153 | // 154 | func ensureProperEmptyLines(invocation: XCSourceEditorCommandInvocation) { 155 | LintLine().ensureProperEmptyLines(in: invocation.buffer.lines) 156 | } 157 | 158 | } 159 | 160 | 161 | extension String: Collection {} 162 | -------------------------------------------------------------------------------- /XcodeFormatterExtension/SourceEditorExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SourceEditorExtension.swift 3 | // Linter 4 | // 5 | // Created by Vijaya Prakash Kandel on 9/11/16. 6 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XcodeKit 11 | 12 | enum CommandIdentifier: String { 13 | 14 | case swiftlyLintAll = "lintAll" 15 | case finilizeClass = "finilizeClass" 16 | case correctEmptyLine = "correctEmptyLine" 17 | case correctInterSpace = "correctInterSpace" 18 | case correctComment = "correctComment" 19 | 20 | } 21 | 22 | enum CommandName: String { 23 | 24 | case swiftlyLintAll = "All(Line, Comment, Space) [1.0]" 25 | case finilizeClass = "Finilize Class" 26 | case correctEmptyLine = "Correct Empty Line" 27 | case correctInterSpace = "Correct Inter Space" 28 | case correctComment = "Correct Comment" 29 | 30 | } 31 | 32 | struct Command { 33 | 34 | static let moduleName = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String ?? "" 35 | 36 | //File Name 37 | static let className = moduleName + ".SourceEditorCommand" 38 | 39 | //Command Name and identifier 40 | static let swiftlyLintAll = (CommandName.swiftlyLintAll, CommandIdentifier.swiftlyLintAll) 41 | static let finilizeClass = (CommandName.finilizeClass, CommandIdentifier.finilizeClass) 42 | static let correctEmptyLines = (CommandName.correctEmptyLine, CommandIdentifier.correctEmptyLine) 43 | static let correctInterSpaces = (CommandName.correctInterSpace, CommandIdentifier.correctInterSpace) 44 | static let correctComment = (CommandName.correctComment, CommandIdentifier.correctComment) 45 | 46 | private static func all() -> [(CommandName, CommandIdentifier)] { 47 | return [swiftlyLintAll, finilizeClass, correctEmptyLines, correctInterSpaces, correctComment] 48 | } 49 | 50 | static func allCommands() -> [[XCSourceEditorCommandDefinitionKey: Any]] { 51 | return all().map { 52 | [XCSourceEditorCommandDefinitionKey.classNameKey: className, 53 | XCSourceEditorCommandDefinitionKey.identifierKey: $0.1.rawValue, 54 | XCSourceEditorCommandDefinitionKey.nameKey: $0.0.rawValue 55 | ] 56 | } 57 | } 58 | 59 | } 60 | 61 | class SourceEditorExtension: NSObject, XCSourceEditorExtension { 62 | 63 | func extensionDidFinishLaunching() { 64 | 65 | } 66 | 67 | var commandDefinitions: [[XCSourceEditorCommandDefinitionKey: Any]] { 68 | return Command.allCommands() 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /XcodeFormatterExtension/Supporting Files/SwiftyLinter.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /XcodeFormatterExtension/XcodeFormatterExtension.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | -------------------------------------------------------------------------------- /XcodeFormatterTests/CodeBlockAnalyzerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodeAnalyzerTests.swift 3 | // Swinter 4 | // 5 | // Created by Vijaya Prakash Kandel on 9/20/16. 6 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import XcodeFormatter 11 | 12 | extension CodePosition: Equatable { 13 | 14 | static func ==(lhs: CodePosition, rhs: CodePosition) -> Bool { 15 | let conditions = [lhs.line == rhs.line, lhs.indicator == rhs.indicator, lhs.section == rhs.section, lhs.lineContent == rhs.lineContent] 16 | let satisfied = conditions.filter { $0 } 17 | return satisfied.count == conditions.count 18 | } 19 | 20 | } 21 | 22 | class CodeBlockAnalyzerTests: XCTestCase { 23 | 24 | func testThat_AnEmptyClass_IsCountedAsSingleCodeBlock() { 25 | let input = ["class A{\n", "}\n"] 26 | 27 | let opening = CodePosition(lineContent: "class A{\n", line: 0, section: 7, indicator: .opening) 28 | let closing = CodePosition(lineContent: "}\n", line: 1, section: 0, indicator: .closing) 29 | let expected = CodeBlock(startPosition: opening, endPosition: closing) 30 | 31 | guard let output = CodeBlockAnalyzer().codeBlocks(for: input).first else { 32 | XCTFail("Couldnt get codeblock from a empty class declaration") 33 | return 34 | } 35 | 36 | XCTAssertEqual(expected.start, output.start) 37 | XCTAssertEqual(expected.end, output.end) 38 | XCTAssertEqual(expected.type, output.type) 39 | 40 | } 41 | 42 | func testThat_IncompleteClassDeclaration_IsNotCountedAsCodeBlock() { 43 | let input = ["class A{\n", "private let x:Int"] 44 | 45 | let output = CodeBlockAnalyzer().codeBlocks(for: input) 46 | 47 | XCTAssert(output.count == 0) 48 | } 49 | 50 | func testThat_EmptyProtocolDefinedInOneLine_IsCountedAsCodeBlock() { 51 | let input = ["protocol Empty { }\n"] 52 | 53 | let opening = CodePosition(lineContent: "protocol Empty { }\n", line: 0, section: 15, indicator: .opening) 54 | let closing = CodePosition(lineContent: "protocol Empty { }\n", line: 0, section: 17, indicator: .closing) 55 | let expected = CodeBlock(startPosition: opening, endPosition: closing) 56 | 57 | guard let output = CodeBlockAnalyzer().codeBlocks(for: input).first else { 58 | XCTFail("Couldnt get codeblock from a empty class declaration") 59 | return 60 | } 61 | 62 | XCTAssertEqual(expected.start, output.start) 63 | XCTAssertEqual(expected.end, output.end) 64 | XCTAssertEqual(output.type, CodeBlockType.ProtocolKind) 65 | } 66 | 67 | func testThat_FunctionDeclaration_HasTypeOfFunctionKind() { 68 | let input = ["func do(){\n","}\n"] 69 | 70 | guard let output = CodeBlockAnalyzer().codeBlocks(for: input).first else { 71 | XCTFail("Something went wrong during code analyzation") 72 | return 73 | } 74 | 75 | XCTAssert(output.type == CodeBlockType.FunctionKind) 76 | } 77 | 78 | func testThat_IfElseBlock_HasTypeOfOther() { 79 | let input = ["if isThat(){\n","}else{\n", "}\n"] 80 | 81 | let outputs = CodeBlockAnalyzer().codeBlocks(for: input) 82 | 83 | outputs.forEach{ 84 | XCTAssertNotNil($0.type) 85 | XCTAssertEqual($0.type!, CodeBlockType.OtherKind) 86 | } 87 | 88 | } 89 | 90 | func testThat_StructDeclaration_HasStructType() { 91 | let input = ["struct A { }\n"] 92 | 93 | let output = CodeBlockAnalyzer().codeBlocks(for: input).first! 94 | 95 | XCTAssertNotNil(output.type) 96 | XCTAssertEqual(output.type!, CodeBlockType.StructKind) 97 | 98 | } 99 | 100 | func testThat_EnumDeclaration_HasEnumType() { 101 | let input = ["enum A { case .a; }\n"] 102 | 103 | let output = CodeBlockAnalyzer().codeBlocks(for: input).first! 104 | 105 | XCTAssertNotNil(output.type) 106 | XCTAssertEqual(output.type!, CodeBlockType.EnumKind) 107 | 108 | } 109 | 110 | func testThat_ExtensionHas_ExtensionType() { 111 | let input = ["extension A: AnyObject { }\n"] 112 | 113 | let output = CodeBlockAnalyzer().codeBlocks(for: input).first! 114 | 115 | XCTAssertNotNil(output.type) 116 | XCTAssertEqual(output.type!, CodeBlockType.ExtensionKind) 117 | 118 | } 119 | 120 | //Complex tests with inner blocks 121 | 122 | func testThat_InnerClass_IsAlsoIdentifiedAsCodeBlock() { 123 | let input = ["class A{ class B{} }\n"] 124 | 125 | let output = CodeBlockAnalyzer().codeBlocks(for: input) 126 | 127 | XCTAssert(output.count == 2) 128 | 129 | output.forEach { 130 | XCTAssertEqual($0.type!, CodeBlockType.ClassKind) 131 | } 132 | } 133 | 134 | //Nested codeblocks are identified from inside out. 135 | //Innermost will be identified and inserted first than the outermost. 136 | func testThat_NestedEnumInsideAStruct_IsIdentified() { 137 | let input = ["struct A {\n", "enum Type{\n", "case .a, .b\n", "}\n", "}\n", "\n"] 138 | 139 | let output = CodeBlockAnalyzer().codeBlocks(for: input) 140 | 141 | let enumCodeBlock = output[0] 142 | let structCodeBlock = output[1] 143 | 144 | XCTAssert(structCodeBlock.type! == CodeBlockType.StructKind) 145 | XCTAssert(enumCodeBlock.type! == CodeBlockType.EnumKind) 146 | 147 | } 148 | 149 | func testThat_ProtocolConformingToClass_IsIdentified() { 150 | let input = ["protocol A: class {\n", " }\n"] 151 | let output = CodeBlockAnalyzer().codeBlocks(for: input) 152 | 153 | XCTAssert(output[0].type! == CodeBlockType.ProtocolKind) 154 | } 155 | 156 | func testPerformanceOfCodeBlockAnalyzer() { 157 | let singleItem = ["struct A {\n", "enum Type{\n", "case .a, .b\n", "}\n", "}\n", "\n"] 158 | let inputCode = (0..<1000).map{_ in singleItem }.reduce([String]()) { $0 + $1 } 159 | 160 | self.measure { 161 | let output = CodeBlockAnalyzer().codeBlocks(for: inputCode) 162 | XCTAssert(output.count == 2000) 163 | } 164 | } 165 | 166 | func testThat_DeclarationInsideAStringQuote_AreNotCounted() { 167 | let input = ["let a = \"protocol A: class {\"\n", "\" }\"\n"] 168 | let output = CodeBlockAnalyzer().codeBlocks(for: input) 169 | XCTAssert(output.count == 0) 170 | } 171 | 172 | func testThat_DeclarationInsideAStringQuote_Mismatched_AreNotCounted() { 173 | let input = ["let a = \"protocol A: class {\"\n", "}\n"] 174 | let output = CodeBlockAnalyzer().codeBlocks(for: input) 175 | XCTAssert(output.count == 0) 176 | } 177 | 178 | func testThat_DeclarationInsideSingleComment_AreNotCounted() { 179 | let input = ["/// protocol A: class {\n", "/// }\n"] 180 | let output = CodeBlockAnalyzer().codeBlocks(for: input) 181 | XCTAssert(output.count == 0) 182 | } 183 | 184 | } 185 | -------------------------------------------------------------------------------- /XcodeFormatterTests/ColonSpacerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpacerTests.swift 3 | // 4 | // Created by Vijaya Prakash Kandel on 9/15/16. 5 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 6 | // 7 | 8 | import XCTest 9 | @testable import XcodeFormatter 10 | 11 | class ColonSpacerTests: XCTestCase { 12 | 13 | func testThat_SimpleColon_IsCorrected() { 14 | let input = "a : Int" 15 | let expected = "a: Int" 16 | assertThat(input: input, producesOutput: expected) 17 | } 18 | 19 | func testThat_ColonFollowedByNoSpace_isSpaceSeparated() { 20 | let input = "let a:String?" 21 | let expected = "let a: String?" 22 | assertThat(input: input, producesOutput: expected) 23 | } 24 | 25 | func testThat_ColonFollowedByMultipleSpace_isTrimmedToOneSpace() { 26 | let input = "let a: String?" 27 | let expected = "let a: String?" 28 | assertThat(input: input, producesOutput: expected) 29 | } 30 | 31 | func testThat_ColonFollowedByMultipleSpace_isTrimmed() { 32 | let input = ["let a: Int", "let b: String?", "[String: AnyObject]"] 33 | let expected = ["let a: Int", "let b: String?", "[String: AnyObject]"] 34 | assertThat(input: input, producesOutput: expected) 35 | } 36 | 37 | func testThat_ColonFollowedByOneSpace_isReturnedSame() { 38 | let input = ["let a: Int", "func do(a: Int, b: [String: AnyObject]) {"] 39 | let expected = input 40 | assertThat(input: input, producesOutput: expected) 41 | } 42 | 43 | func testThat_MultipleVoilationInSingleLine_areCorrected() { 44 | let input = "func do(a:Int, b: Int) -> Int {" 45 | let expected = "func do(a: Int, b: Int) -> Int {" 46 | assertThat(input: input, producesOutput: expected) 47 | } 48 | 49 | func testThat_MultipleComplexVoilationInSinleLine_areCorrected() { 50 | let input = "func structFromJSON(json: [String:AnyObject], struct:SomeStruct, completion:(Bool->Void)) {" 51 | let expected = "func structFromJSON(json: [String: AnyObject], struct: SomeStruct, completion: (Bool->Void)) {" 52 | assertThat(input: input, producesOutput: expected) 53 | } 54 | 55 | func testThat_ColonInMiddle_IsCorrected() { 56 | let input = "[String : Int]" 57 | let expected = "[String: Int]" 58 | assertThat(input: input, producesOutput: expected) 59 | } 60 | 61 | func testThat_MultipleColonInMiddle_AreCorrected() { 62 | let input = "[0 : Apple, 1 :Ball, 3:Cat, 4 : Dog, 5: Elephant]" 63 | let expected = "[0: Apple, 1: Ball, 3: Cat, 4: Dog, 5: Elephant]" 64 | assertThat(input: input, producesOutput: expected) 65 | } 66 | 67 | //MARK:- Quoted String 68 | func testThat_ColonInsideStringBlock_IsNotCorrected() { 69 | let input = "let urlString = \"https://www.google.com\"" 70 | let expected = input 71 | assertThat(input: input, producesOutput: expected) 72 | } 73 | 74 | func testThat_ColonAfterMark_IsNotCorrected() { 75 | let input = "//MARK:- thathing \n" 76 | let expected = input 77 | assertThat(input: input, producesOutput: expected) 78 | } 79 | 80 | //MARK:- Testers 81 | private func assertThat(input: [String], producesOutput expected: [String]) { 82 | for (input1, expected1) in zip(input, expected) { 83 | assertThat(input: input1, producesOutput: expected1) 84 | } 85 | } 86 | 87 | private func assertThat(input: String, producesOutput expected: String) { 88 | let output = LintSpace().correctColonSpace(line: input) 89 | XCTAssertEqual(expected, output) 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /XcodeFormatterTests/CommaSpaceTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommaSpaceTests.swift 3 | // Swinter 4 | // 5 | // Created by Vijaya Prakash Kandel on 9/16/16. 6 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import XcodeFormatter 11 | 12 | final class CommaSpaceTests: XCTestCase { 13 | 14 | func testThat_CommaAndNoSpace_isSpaceSeparated() { 15 | let input = "func sum(a: Int,b: Int)" 16 | let expected = "func sum(a: Int, b: Int)" 17 | assertThat(input: input, produces: expected) 18 | } 19 | 20 | func testThat_ProperSpacedComma_remainsSame() { 21 | let input = "(a: Int, b: Int, c: Int)" 22 | let expected = input 23 | assertThat(input: input, produces: expected) 24 | } 25 | 26 | func testThat_OnlyMisspacedCommas_areCorrected() { 27 | let input = "func sum(a:Int,b: Int, c:Int,d: Int)" 28 | let expected = "func sum(a:Int, b: Int, c:Int, d: Int)" 29 | assertThat(input: input, produces: expected) 30 | } 31 | 32 | func testThat_SingleLineWithMultipleImproperCommas_areCorrected() { 33 | let input = "func sum(a:Int,b:Int,c:Int,d:String, e:Int, f: String, f:Int,g:Int, h: Int)" 34 | let expected = "func sum(a:Int, b:Int, c:Int, d:String, e:Int, f: String, f:Int, g:Int, h: Int)" 35 | assertThat(input: input, produces: expected) 36 | } 37 | 38 | func testThat_SpaceBeforeComma_IsTrimmed() { 39 | let input = "(x:Int , y: Int)" 40 | let expected = "(x:Int, y: Int)" 41 | assertThat(input: input, produces: expected) 42 | } 43 | 44 | //MARK:- String Quoted Comma 45 | 46 | func testThat_CommaInsideStringQuote_IsNotCorrected(){ 47 | let input = "let someQuote = \"When i say that,.... I mean it\"" 48 | let expected = input 49 | assertThat(input: input, produces: expected) 50 | } 51 | 52 | func testThat_CommaInsideComment_IsNotCorrected() { 53 | let input = "//MARK:- UITableViewDelegate,UITableViewDataSource" 54 | let expected = input 55 | assertThat(input: input, produces: expected) 56 | } 57 | 58 | //MARK:- Testers 59 | private func assertThat(input: [String], produces expected: [String]) { 60 | for (input1, expected1) in zip(input, expected) { 61 | assertThat(input: input1, produces: expected1) 62 | } 63 | } 64 | 65 | private func assertThat(input: String, produces expected: String) { 66 | let output = LintSpace().correctCommaSeparation(line: input) 67 | XCTAssertEqual(expected, output) 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /XcodeFormatterTests/CommentLintTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommentLintTests.swift 3 | // Swinter 4 | // 5 | // Created by Vijaya Prakash Kandel on 9/18/16. 6 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import XcodeFormatter 11 | 12 | class CommentLintTests: XCTestCase { 13 | 14 | let defaultXcodeComment = "//\n// ColonSpacer.swift\n// SwiftyLinter\n//\n// Created by Vijaya Prakash Kandel on 9/15/16.\n// Copyright © 2016 Vijaya Prakash Kandel. All rights reserved.\n//\n" 15 | let notDefaultXcodeComment = "// TestComment.swift\n// Copyright © 2016 Vijaya Prakash Kandel. All rights reserved.\n//\n\n" 16 | let onlySwiftContent = "import Foundation\n\nfinal class ColonSpacer {\n \n func doThat() {\n \n }\n \n}\n" 17 | 18 | 19 | func testThat_NewCommentLines_AreExtracted_FromDefaultXCodeGeneratedComment() { 20 | let input = defaultXcodeComment + onlySwiftContent 21 | let expected = ["//\n","// Copyright © 2016 Vijaya Prakash Kandel. All rights reserved.\n//\n"] 22 | 23 | let output = LintFileComment().extractNewCommentLines(from: input) 24 | 25 | XCTAssertEqual(expected, output) 26 | } 27 | 28 | func testThat_NewCommentLines_AreNotExtractedWhen_FileHasNoComments() { 29 | let input = onlySwiftContent 30 | let expected = [String]() 31 | 32 | let output = LintFileComment().extractNewCommentLines(from: input) 33 | 34 | XCTAssertEqual(expected, output) 35 | } 36 | 37 | func testThat_NewCommentsLines_AreNotExtracted_WhenDefaultComments_AreNotAtStartOfFile() { 38 | let inputs = [" \n\n " + defaultXcodeComment + onlySwiftContent, onlySwiftContent + defaultXcodeComment + onlySwiftContent] 39 | let expectedOutputs = [[String](), [String]()] 40 | 41 | 42 | let outputs = inputs.map{ LintFileComment().extractNewCommentLines(from: $0) } 43 | 44 | for (expected, output) in zip(expectedOutputs, outputs) { 45 | XCTAssertEqual(expected, output) 46 | } 47 | } 48 | 49 | func testThat_NewCommentLines_AreNotExtracted_WhenThereIs_CustomCommentTemplate() { 50 | let input = notDefaultXcodeComment + onlySwiftContent 51 | let expected = [String]() 52 | 53 | let output = LintFileComment().extractNewCommentLines(from: input) 54 | 55 | XCTAssertEqual(expected, output) 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /XcodeFormatterTests/EmptyLineInsertionTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmptyLineInsertionTests.swift 3 | // Swinter 4 | // 5 | // Created by Vijaya Prakash Kandel on 9/20/16. 6 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import XcodeFormatter 11 | 12 | class LintLineTests: XCTestCase { 13 | 14 | let EMP = "\n" 15 | 16 | func testThat_SingleDeclaration_hasEmptyLineAboveAndBelow() { 17 | let input = ["class A {\n", "}\n"] 18 | let expected = [EMP, "class A {\n", EMP,"}\n",EMP] 19 | assertThat(inputCode: input, produces: expected) 20 | } 21 | 22 | func testhThat_SingleDeclarationWithMultipleEmptyLines_HasOnlyOneLineBeforeAndAfter() { 23 | let input = [EMP,EMP,EMP,"class A {\n",EMP,EMP,EMP, "}\n",EMP,EMP,EMP] 24 | let expected = [EMP, "class A {\n", EMP,"}\n",EMP] 25 | assertThat(inputCode: input, produces: expected) 26 | } 27 | 28 | func testThat_IncompleteCodeBlock_IsNotCorrectedAtAll() { 29 | let input = ["class A {\n", "let a: Int"] 30 | let expected = input 31 | assertThat(inputCode: input, produces: expected) 32 | } 33 | 34 | func testThat_CodeBlockCompleteOnSameLine_IsNotCorrectedAtAll() { 35 | let input = ["class A { } \n"] 36 | let expected = input 37 | assertThat(inputCode: input, produces: expected) 38 | } 39 | 40 | func testThat_ClassCodeBlock_IsCorrected() { 41 | let input = ["class A {\n", "}\n"] 42 | let expected = [EMP, "class A {\n", EMP ,"}\n",EMP] 43 | assertThat(inputCode: input, produces: expected) 44 | } 45 | 46 | func testThat_StructCodeBlock_IsCorrected() { 47 | let input = ["struct A {\n", "}\n"] 48 | let expected = [EMP, "struct A {\n", EMP,"}\n", EMP] 49 | assertThat(inputCode: input, produces: expected) 50 | } 51 | 52 | func testThat_ProtocolCodeBlock_IsCorrected() { 53 | let input = ["protocol A {\n", "func doThat()\n", "}\n"] 54 | let expected = [EMP, "protocol A {\n",EMP, "func doThat()\n" ,EMP,"}\n",EMP] 55 | assertThat(inputCode: input, produces: expected) 56 | } 57 | 58 | func testThat_ExtensionCodeBlock_IsCorrected() { 59 | let input = ["extension A{\n", "func doThat(){}\n", "}\n"] 60 | let expected = [EMP, "extension A{\n",EMP, "func doThat(){}\n" ,EMP,"}\n",EMP] 61 | assertThat(inputCode: input, produces: expected) 62 | } 63 | 64 | func testThat_EnumCodeBlock_IsCorrected() { 65 | let input = ["enum A{\n", "case .a, .b\n", "}\n"] 66 | let expected = [EMP, "enum A{\n", EMP, "case .a, .b\n" ,EMP,"}\n", EMP] 67 | assertThat(inputCode: input, produces: expected) 68 | } 69 | 70 | //Failure Case 71 | 72 | func testThat_FucntionBlock_doesNotGetEmptyLinesInsideTheBlock() { 73 | let input = ["func do() {\n","someMagic()" ,"}\n"] 74 | let expected = [EMP,"func do() {\n","someMagic()" ,"}\n" ] 75 | assertThat(inputCode: input, produces: expected) 76 | } 77 | 78 | func testThat_IfBlock_doesNotGetEmptyLines() { 79 | let input = ["if do() {\n","someMagic()" ,"} else {\n", "then clause here \n", "}\n"] 80 | let expected = input 81 | assertThat(inputCode: input, produces: expected) 82 | } 83 | 84 | //Complex cases 85 | 86 | //NOTE: Shall we put a empty Line at the end of the file if there is none explicitly like in the test case above that 87 | // ends with a single liner protocol. 88 | func testThat_MultipleTypesInSingleFile_AreCorrectedProperly() { 89 | let input = ["final class A {\n"," \n", " let a: Int\n","}\n", "final class B {\n", " let b: Int\n", "}\n", "protocol A { }\n"] 90 | 91 | let expected = [EMP,"final class A {\n"," \n", " let a: Int\n", EMP,"}\n", EMP,"final class B {\n", EMP, " let b: Int\n", EMP, "}\n", EMP, "protocol A { }\n"] 92 | 93 | assertThat(inputCode: input, produces: expected) 94 | } 95 | 96 | //NOTE: Shall we preserve the indentation spaces. CTRL + I will bring back this magic. 97 | func testThat_MultipleTypesWithMultipleMixedSpaces_WillBeCorrected() { 98 | let input = ["final class A {\n",EMP, EMP," \n", " let a: Int\n", EMP, EMP,EMP,"}\n", EMP, EMP,EMP, "final class B {\n", " let b: Int\n", "}\n",EMP, EMP,EMP,EMP, "protocol A { }\n"] 99 | 100 | let expected = [EMP,"final class A {\n","\n", " let a: Int\n", EMP,"}\n", EMP,"final class B {\n", EMP, " let b: Int\n", EMP, "}\n", EMP, "protocol A { }\n"] 101 | 102 | assertThat(inputCode: input, produces: expected) 103 | } 104 | 105 | func testThat_EmptyLineIsInsertedAboveFunctionWithoutEmptyLine() { 106 | let input = ["func dothis(){}\n"] 107 | let expected = [EMP, "func dothis(){}\n"] 108 | assertThat(inputCode: input, produces: expected) 109 | } 110 | 111 | func testThat_TwoFunctionsWithoutEmptyLineInBetween_AreSeparatedByOneEmptyLine() { 112 | let input = ["func do(){}\n", "func doThat(){}\n"] 113 | let expected = [EMP,"func do(){}\n",EMP, "func doThat(){}\n"] 114 | assertThat(inputCode: input, produces: expected) 115 | } 116 | 117 | func testThat_CommentAndClassDeclaration_AreNotSeparatedWhileInsertingEmptyLines() { 118 | let input = [EMP,"//this is comment \n", "class A { \n", EMP,"}\n", EMP] 119 | let expected = input 120 | assertThat(inputCode: input, produces: expected) 121 | } 122 | 123 | func testThat_CommentAndClassDeclaration_AreProperlyCorrected() { 124 | let input = ["//this is comment \n", "class A { \n", EMP,"}\n", EMP] 125 | let expected = [EMP] + input 126 | assertThat(inputCode: input, produces: expected) 127 | } 128 | 129 | func assertThat_CommentAndTypes_AreProperlyCorrected() { 130 | let input = [EMP, EMP, "//Comment\n", "struct A {\n", "something \n", "}\n", EMP] 131 | let expected = Array(input.dropFirst()) 132 | assertThat(inputCode: input, produces: expected) 133 | } 134 | 135 | func testThat_CommentAndTypesInComplexStructure_AreProperlyCorrected() { 136 | let input = ["let a: Int\n", " //Comment\n", "struct A {\n", "something \n", "}\n", EMP] 137 | let expected = ["let a: Int\n", EMP, " //Comment\n", "struct A {\n", EMP, "something \n", EMP, "}\n", EMP] 138 | assertThat(inputCode: input, produces: expected) 139 | } 140 | 141 | func testThat_DeclarationInsideStringQuotes_AreNotTouched() { 142 | let input = ["\" class A { \" \n", "\"}\" \n"] 143 | let expected = input 144 | assertThat(inputCode: input, produces: expected) 145 | } 146 | 147 | 148 | //MARK:- tester 149 | 150 | func assertThat(inputCode input: [String], produces expected: [String]) { 151 | let mutableInput = NSMutableArray(array: input) 152 | LintLine().ensureProperEmptyLines(in: mutableInput) 153 | 154 | let output = mutableInput.copy() as! [String] 155 | XCTAssertEqual(expected, output) 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /XcodeFormatterTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /XcodeFormatterTests/PrivatizerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrivatizerTests.swift 3 | // Swinter 4 | // 5 | // Created by Vijaya Prakash Kandel on 24/09/2016. 6 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class PrivatizerTests: XCTestCase { 12 | 13 | func testThat_ProtocolConformingToClass_IsNotPrivatized() { 14 | 15 | } 16 | 17 | func testThat_OpenClass_IsNotPrivatized() { 18 | 19 | } 20 | 21 | func testThat_ClassKeywordInsideString_IsNotPrivatized() { 22 | 23 | } 24 | 25 | func testThat_DotClass_IsNotPrivatized() { 26 | 27 | } 28 | 29 | func testThat_ClassFunc_IsNotPrivatized() { 30 | 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /XcodeFormatterTests/ReturnArrowSpacerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReturnArrowSpacerTests.swift 3 | // Swinter 4 | // 5 | // Created by Vijaya Prakash Kandel on 16/09/16. 6 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import XcodeFormatter 11 | 12 | class ReturnArrowSpacerTests: XCTestCase { 13 | 14 | func testThat_NoSpaceAfterArrow_isReplacedWithSingleSpace() { 15 | let input = "func some() ->A" 16 | let expected = "func some() -> A" 17 | assertThat(input: input, produces: expected) 18 | } 19 | 20 | func testThat_MultipleSpacesAfterArrow_isReplacedWithSingleSpace() { 21 | let input = "func some()-> A" 22 | let expected = "func some() -> A" 23 | assertThat(input: input, produces: expected) 24 | } 25 | 26 | func testThat_ProperSpacedArrow_isUnchanged() { 27 | let input = "func that() -> This" 28 | let expected = input 29 | assertThat(input: input, produces: expected) 30 | } 31 | 32 | func testThat_MultipleArrows_areFormattedProperly() { 33 | let input = "func madmap(closure: (Element ->U?), anotherClosure: A->Bool) -> E" 34 | let expected = "func madmap(closure: (Element -> U?), anotherClosure: A -> Bool) -> E" 35 | assertThat(input: input, produces: expected) 36 | } 37 | 38 | func testThat_ArrowInsideStringQuote_IsNotCorrected() { 39 | let input = "\"func that() ->this\"" 40 | let expected = input 41 | assertThat(input: input, produces: expected) 42 | } 43 | 44 | private func assertThat(input: String, produces expected: String) { 45 | let output = LintSpace().correctFunctionReturnArrow(line: input) 46 | XCTAssertEqual(expected, output) 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /XcodeFormatterTests/TernaryOperatorSpacerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TernaryOperatorSpacerTests.swift 3 | // Swinter 4 | // 5 | // Created by Vijaya Prakash Kandel on 10/1/16. 6 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import XcodeFormatter 11 | 12 | class TernaryOperatorSpacerTests: XCTestCase { 13 | 14 | func testThat_SimpleTernaryOperator_IsCorrectedWithProperSpaces() { 15 | let input = "let a = true?1:2\n" 16 | let expected = "let a = true ? 1 : 2\n" 17 | assertThat(input, produces: expected) 18 | } 19 | 20 | func testThat_SimpleTernaryOperator_WithProperSpacing_IsUntouched() { 21 | let input = "let a = true ? true : false \n" 22 | let expected = input 23 | assertThat(input, produces: expected) 24 | } 25 | 26 | func testThat_NestedTernaryOperator_IsCorrectedProperly() { 27 | let input = "let that = ( true?false:true)?1:2\n" 28 | let expected = "let that = ( true ? false : true) ? 1 : 2\n" 29 | assertThat(input, produces: expected) 30 | } 31 | 32 | func testThat_NestedTernaryWithMultipleSpaces_Arecorrected() { 33 | let input = " ( true ? false : true )? 12: 24 \n" 34 | let expected = " ( true ? false : true ) ? 12 : 24 \n" 35 | assertThat(input, produces: expected) 36 | } 37 | 38 | func testThat_MutlipleTernaryOperators_NotSeeminglyAllowedByCompiler_AreAlsoCorrected() { 39 | let input = "let a = (true ?true : false)?true : false ? 1:2\n" 40 | let expected = "let a = (true ? true : false) ? true : false ? 1 : 2\n" 41 | assertThat(input, produces: expected) 42 | } 43 | 44 | func testThat_ImproperTernaryOperator_AreNotcorrected() { 45 | let input = "z ? : a" 46 | let expected = input 47 | assertThat(input, produces: expected) 48 | } 49 | 50 | func testThat_NearlyTernary_IsNotTouched() { 51 | let input = "a?:b\n" 52 | let expected = "a?:b\n" 53 | assertThat(input, produces: expected) 54 | } 55 | 56 | func testThat_OptionalChaing_IsNotTreatedForTernaryOperation() { 57 | let input = "let b = thisOne?.map({$0.that()})flatmap{$0} : asd \n" 58 | let expected = input 59 | assertThat(input, produces: expected) 60 | } 61 | 62 | func testThat_OptionalDictionaryLookup_AreNotTreatedForTernaryOperation() { 63 | let input = "let b = dictionaryOptional?[\"some\"] : that \n" 64 | let expected = input 65 | assertThat(input, produces: expected) 66 | } 67 | 68 | func testThat_OptionalDownCast_InsideDictionary_IsNotTreatedForTernaryOperation() { 69 | let input = "let dict = [apple as? ballType : DefaultBallType] \n" 70 | let expected = input 71 | assertThat(input, produces: expected) 72 | } 73 | 74 | func testThat_TheseAre_NotTreatedForTernaryOperation() { 75 | let inputs = ["[(model as? AModelType) ?? AModelType : BModelType ]"] 76 | asserThat(inputs, matches: inputs) 77 | } 78 | 79 | private func asserThat(_ inputs: [String], matches expected: [String]) { 80 | for index in inputs.indices { 81 | assertThat(inputs[index], produces: expected[index]) 82 | } 83 | } 84 | 85 | private func assertThat(_ input: String, produces expected: String) { 86 | let output = LintSpace().correctTernaryOperator(line: input) 87 | XCTAssert(output == expected) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /XcodeFormatterTests/TrailingCurlyBracketTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrailingCurlyBracketTests.swift 3 | // Swinter 4 | // 5 | // Created by Vijaya Prakash Kandel on 9/16/16. 6 | // Copyright © 2016 Vijaya Prakash Kandel. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import XcodeFormatter 11 | 12 | final class TrailingCurlyBracketTests: XCTestCase { 13 | 14 | func testThat_ClassDeclarationWithoutSpace_IsCorrected() { 15 | let input = "class A{" 16 | let expected = "class A {" 17 | 18 | let output = LintSpace().correctTrailingCurlyBracket(line: input) 19 | 20 | XCTAssertEqual(expected, output) 21 | } 22 | 23 | func tesThat_FunctionDeclarationBodyStart_IsCorrected() { 24 | let input = "func sum(a: Int, b: Int) -> Int{" 25 | let expected = "func sum(a: Int, b: Int) -> Int {" 26 | 27 | let output = LintSpace().correctTrailingCurlyBracket(line: input) 28 | 29 | XCTAssertEqual(expected, output) 30 | } 31 | 32 | func testThat_ClassDeclarationWithConformance_IsCorrected() { 33 | let input = "class A: UITableViewController, UITableViewDelegate{" 34 | let expected = "class A: UITableViewController, UITableViewDelegate {" 35 | 36 | let output = LintSpace().correctTrailingCurlyBracket(line: input) 37 | 38 | XCTAssertEqual(expected, output) 39 | } 40 | 41 | func testThat_OnlyCurlyBrackets_IsCorrected() { 42 | let input = "func sum(a: Int,b:Int) ->Int{" 43 | let expected = "func sum(a: Int,b:Int) ->Int {" 44 | 45 | let output = LintSpace().correctTrailingCurlyBracket(line: input) 46 | 47 | XCTAssertEqual(expected, output) 48 | } 49 | 50 | func testThat_MapConstruct_IsNotCorrected() { 51 | let input = "a.map{ $0.something()}.filter({ }).then{" 52 | let expected = "a.map{ $0.something()}.filter({ }).then {" 53 | 54 | let output = LintSpace().correctTrailingCurlyBracket(line: input) 55 | 56 | XCTAssertEqual(expected, output) 57 | } 58 | 59 | } 60 | --------------------------------------------------------------------------------