├── .gitignore ├── .swiftlint.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── SwiftLintXcode.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── SwiftLintXcode.xcscheme └── SwiftLintXcode ├── Formatter.swift ├── Info.plist ├── NSObject_Extension.swift ├── SaveHook.swift ├── SwiftLintXcode-Bridging-Header.h ├── SwiftLintXcode.swift ├── SwiftLintXcodeIDEHelper.h ├── SwiftLintXcodeIDEHelper.m ├── SwiftLintXcodeTRVSXcode.h ├── SwiftLintXcodeTRVSXcode.m └── errorHelper.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - line_length 3 | - force_cast 4 | - todo 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | Version 0.2.1 5 | ---------------------------- 6 | 7 | - Added UUIDs for Xcode <=8.3. 8 | 9 | 10 | Version 0.2.0 11 | ---------------------------- 12 | 13 | - Xcode 8 Support. 14 | 15 | 16 | Version 0.1.2 17 | ---------------------------- 18 | 19 | - Fixed bug that saving non-swift file runs autocorrect. 20 | - Set working directory to where workspace (or project) file is placed. 21 | 22 | 23 | Version 0.1.1 24 | ---------------------------- 25 | 26 | - Rename plug-in name: SwiftLintAutoCorrect -> SwiftLintXcode. 27 | 28 | 29 | Version 0.1.0 30 | ---------------------------- 31 | 32 | - Initial release. 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Yuya Tanaka 4 | 5 | (For TRVSXcode from ClangFormat-Xcode) 6 | Copyright (c) 2014 Travis Jeffery 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SwiftLintXcode 2 | ============== 3 | 4 | An Xcode plug-in to format your code using [SwiftLint](https://github.com/realm/SwiftLint). 5 | 6 | Runs `swiftlint autocorrect --path CURRENT_FILE` before \*.swift file is saved. 7 | 8 | ![Screenshot](https://cloud.githubusercontent.com/assets/400558/14304460/d2a133dc-fbed-11e5-9573-2c21cce699e0.png) 9 | 10 | 11 | IMPORTANT: Xcode 8 Installation 12 | ------------------------------- 13 | 14 | Xcode 8 won't load any unsigned plugins without resigning Xcode itself. 15 | https://github.com/alcatraz/Alcatraz/issues/475 16 | 17 | See XVim's nice and simple installation doc to resign it..! (NOTE: AT YOUR OWN SECURITY RISK) 18 | https://github.com/XVimProject/XVim/blob/3167408ade82cfef87acc704822da61af69688f8/INSTALL_Xcode8.md 19 | 20 | 21 | INSTALLATION 22 | ------------ 23 | 24 | Install via [Alcatraz](https://github.com/alcatraz/Alcatraz), a package manager for Xcode. 25 | 26 | This plugin does not bundle swiftlint binary. Please ensure swiftlint is on PATH. 27 | 28 | ```bash 29 | brew update && brew install swiftlint 30 | ``` 31 | 32 | ### Manual installation 33 | 34 | ```bash 35 | git clone https://github.com/ypresto/SwiftLintXcode 36 | cd SwiftLintXcode 37 | # Build and install. 38 | xcodebuild -configuration Release 39 | ``` 40 | 41 | To uninstall, just remove plug-in directory. 42 | 43 | ```bash 44 | rm -rf "$HOME/Library/Application Support/Developer/Shared/Xcode/Plug-ins/SwiftLintXcode.xcplugin" 45 | ``` 46 | 47 | 48 | THANKS 49 | ------ 50 | 51 | This plug-in contains TRVSXcode from [ClangFormat-Xcode](https://github.com/travisjeffery/ClangFormat-Xcode) 52 | to interact with Xcode internal interface. 53 | 54 | ClangFormat-Xcode is awesome plugin for auto-formatting Objective-C code..! 55 | 56 | 57 | LICENSE 58 | ------- 59 | 60 | ``` 61 | The MIT License (MIT) 62 | 63 | Copyright (c) 2016 Yuya Tanaka 64 | 65 | (For TRVSXcode from ClangFormat-Xcode) 66 | Copyright (c) 2014 Travis Jeffery 67 | 68 | Permission is hereby granted, free of charge, to any person obtaining a copy 69 | of this software and associated documentation files (the "Software"), to deal 70 | in the Software without restriction, including without limitation the rights 71 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 72 | copies of the Software, and to permit persons to whom the Software is 73 | furnished to do so, subject to the following conditions: 74 | 75 | The above copyright notice and this permission notice shall be included in 76 | all copies or substantial portions of the Software. 77 | 78 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 79 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 80 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 81 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 82 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 83 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 84 | THE SOFTWARE. 85 | ``` 86 | -------------------------------------------------------------------------------- /SwiftLintXcode.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0498717F1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498717E1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.m */; }; 11 | 049871811CB28EBF00C5F7B5 /* Formatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 049871801CB28EBF00C5F7B5 /* Formatter.swift */; }; 12 | 04DFAD471CB508D1007998DF /* SwiftLintXcodeIDEHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 04DFAD461CB508D1007998DF /* SwiftLintXcodeIDEHelper.m */; }; 13 | 04DFAD491CB50BC8007998DF /* errorHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DFAD481CB50BC8007998DF /* errorHelper.swift */; }; 14 | 04E4BF341CB25D3200BC7305 /* SwiftLintXcode.xcscheme in Resources */ = {isa = PBXBuildFile; fileRef = 04E4BF331CB25D3200BC7305 /* SwiftLintXcode.xcscheme */; }; 15 | 04E4BF361CB25D3200BC7305 /* SwiftLintXcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E4BF351CB25D3200BC7305 /* SwiftLintXcode.swift */; }; 16 | 04E4BF381CB25D3200BC7305 /* NSObject_Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E4BF371CB25D3200BC7305 /* NSObject_Extension.swift */; }; 17 | 04E4BF411CB273A700BC7305 /* SaveHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E4BF401CB273A700BC7305 /* SaveHook.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 0498717C1CB27F8800C5F7B5 /* SwiftLintXcode-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SwiftLintXcode-Bridging-Header.h"; sourceTree = ""; }; 22 | 0498717D1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwiftLintXcodeTRVSXcode.h; sourceTree = ""; }; 23 | 0498717E1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SwiftLintXcodeTRVSXcode.m; sourceTree = ""; }; 24 | 049871801CB28EBF00C5F7B5 /* Formatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Formatter.swift; sourceTree = ""; }; 25 | 04DFAD451CB508D1007998DF /* SwiftLintXcodeIDEHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwiftLintXcodeIDEHelper.h; sourceTree = ""; }; 26 | 04DFAD461CB508D1007998DF /* SwiftLintXcodeIDEHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SwiftLintXcodeIDEHelper.m; sourceTree = ""; }; 27 | 04DFAD481CB50BC8007998DF /* errorHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = errorHelper.swift; sourceTree = ""; }; 28 | 04E4BF2F1CB25D3100BC7305 /* SwiftLintXcode.xcplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftLintXcode.xcplugin; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 04E4BF331CB25D3200BC7305 /* SwiftLintXcode.xcscheme */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = SwiftLintXcode.xcscheme; path = SwiftLintXcode.xcodeproj/xcshareddata/xcschemes/SwiftLintXcode.xcscheme; sourceTree = SOURCE_ROOT; }; 30 | 04E4BF351CB25D3200BC7305 /* SwiftLintXcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftLintXcode.swift; sourceTree = ""; }; 31 | 04E4BF371CB25D3200BC7305 /* NSObject_Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSObject_Extension.swift; sourceTree = ""; }; 32 | 04E4BF391CB25D3200BC7305 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 04E4BF401CB273A700BC7305 /* SaveHook.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveHook.swift; sourceTree = ""; }; 34 | /* End PBXFileReference section */ 35 | 36 | /* Begin PBXGroup section */ 37 | 04E4BF271CB25D3100BC7305 = { 38 | isa = PBXGroup; 39 | children = ( 40 | 04E4BF311CB25D3200BC7305 /* SwiftLintXcode */, 41 | 04E4BF301CB25D3100BC7305 /* Products */, 42 | ); 43 | sourceTree = ""; 44 | }; 45 | 04E4BF301CB25D3100BC7305 /* Products */ = { 46 | isa = PBXGroup; 47 | children = ( 48 | 04E4BF2F1CB25D3100BC7305 /* SwiftLintXcode.xcplugin */, 49 | ); 50 | name = Products; 51 | sourceTree = ""; 52 | }; 53 | 04E4BF311CB25D3200BC7305 /* SwiftLintXcode */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | 0498717D1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.h */, 57 | 0498717E1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.m */, 58 | 04DFAD451CB508D1007998DF /* SwiftLintXcodeIDEHelper.h */, 59 | 04DFAD461CB508D1007998DF /* SwiftLintXcodeIDEHelper.m */, 60 | 04E4BF351CB25D3200BC7305 /* SwiftLintXcode.swift */, 61 | 04E4BF371CB25D3200BC7305 /* NSObject_Extension.swift */, 62 | 04E4BF401CB273A700BC7305 /* SaveHook.swift */, 63 | 049871801CB28EBF00C5F7B5 /* Formatter.swift */, 64 | 04DFAD481CB50BC8007998DF /* errorHelper.swift */, 65 | 04E4BF391CB25D3200BC7305 /* Info.plist */, 66 | 04E4BF321CB25D3200BC7305 /* Supporting Files */, 67 | 0498717C1CB27F8800C5F7B5 /* SwiftLintXcode-Bridging-Header.h */, 68 | ); 69 | path = SwiftLintXcode; 70 | sourceTree = ""; 71 | }; 72 | 04E4BF321CB25D3200BC7305 /* Supporting Files */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 04E4BF331CB25D3200BC7305 /* SwiftLintXcode.xcscheme */, 76 | ); 77 | name = "Supporting Files"; 78 | sourceTree = ""; 79 | }; 80 | /* End PBXGroup section */ 81 | 82 | /* Begin PBXNativeTarget section */ 83 | 04E4BF2E1CB25D3100BC7305 /* SwiftLintXcode */ = { 84 | isa = PBXNativeTarget; 85 | buildConfigurationList = 04E4BF3C1CB25D3200BC7305 /* Build configuration list for PBXNativeTarget "SwiftLintXcode" */; 86 | buildPhases = ( 87 | 04E4BF2C1CB25D3100BC7305 /* Sources */, 88 | 04E4BF2D1CB25D3100BC7305 /* Resources */, 89 | ); 90 | buildRules = ( 91 | ); 92 | dependencies = ( 93 | ); 94 | name = SwiftLintXcode; 95 | productName = SwiftLintXcode; 96 | productReference = 04E4BF2F1CB25D3100BC7305 /* SwiftLintXcode.xcplugin */; 97 | productType = "com.apple.product-type.bundle"; 98 | }; 99 | /* End PBXNativeTarget section */ 100 | 101 | /* Begin PBXProject section */ 102 | 04E4BF281CB25D3100BC7305 /* Project object */ = { 103 | isa = PBXProject; 104 | attributes = { 105 | LastSwiftUpdateCheck = 0730; 106 | LastUpgradeCheck = 0800; 107 | ORGANIZATIONNAME = "Yuya Tanaka"; 108 | TargetAttributes = { 109 | 04E4BF2E1CB25D3100BC7305 = { 110 | CreatedOnToolsVersion = 7.3; 111 | LastSwiftMigration = 0800; 112 | }; 113 | }; 114 | }; 115 | buildConfigurationList = 04E4BF2B1CB25D3100BC7305 /* Build configuration list for PBXProject "SwiftLintXcode" */; 116 | compatibilityVersion = "Xcode 3.2"; 117 | developmentRegion = English; 118 | hasScannedForEncodings = 0; 119 | knownRegions = ( 120 | en, 121 | ); 122 | mainGroup = 04E4BF271CB25D3100BC7305; 123 | productRefGroup = 04E4BF301CB25D3100BC7305 /* Products */; 124 | projectDirPath = ""; 125 | projectRoot = ""; 126 | targets = ( 127 | 04E4BF2E1CB25D3100BC7305 /* SwiftLintXcode */, 128 | ); 129 | }; 130 | /* End PBXProject section */ 131 | 132 | /* Begin PBXResourcesBuildPhase section */ 133 | 04E4BF2D1CB25D3100BC7305 /* Resources */ = { 134 | isa = PBXResourcesBuildPhase; 135 | buildActionMask = 2147483647; 136 | files = ( 137 | 04E4BF341CB25D3200BC7305 /* SwiftLintXcode.xcscheme in Resources */, 138 | ); 139 | runOnlyForDeploymentPostprocessing = 0; 140 | }; 141 | /* End PBXResourcesBuildPhase section */ 142 | 143 | /* Begin PBXSourcesBuildPhase section */ 144 | 04E4BF2C1CB25D3100BC7305 /* Sources */ = { 145 | isa = PBXSourcesBuildPhase; 146 | buildActionMask = 2147483647; 147 | files = ( 148 | 0498717F1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.m in Sources */, 149 | 04E4BF361CB25D3200BC7305 /* SwiftLintXcode.swift in Sources */, 150 | 049871811CB28EBF00C5F7B5 /* Formatter.swift in Sources */, 151 | 04DFAD471CB508D1007998DF /* SwiftLintXcodeIDEHelper.m in Sources */, 152 | 04DFAD491CB50BC8007998DF /* errorHelper.swift in Sources */, 153 | 04E4BF411CB273A700BC7305 /* SaveHook.swift in Sources */, 154 | 04E4BF381CB25D3200BC7305 /* NSObject_Extension.swift in Sources */, 155 | ); 156 | runOnlyForDeploymentPostprocessing = 0; 157 | }; 158 | /* End PBXSourcesBuildPhase section */ 159 | 160 | /* Begin XCBuildConfiguration section */ 161 | 04E4BF3A1CB25D3200BC7305 /* Debug */ = { 162 | isa = XCBuildConfiguration; 163 | buildSettings = { 164 | ALWAYS_SEARCH_USER_PATHS = NO; 165 | CLANG_ANALYZER_NONNULL = YES; 166 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 167 | CLANG_CXX_LIBRARY = "libc++"; 168 | CLANG_ENABLE_MODULES = YES; 169 | CLANG_ENABLE_OBJC_ARC = YES; 170 | CLANG_WARN_BOOL_CONVERSION = YES; 171 | CLANG_WARN_CONSTANT_CONVERSION = YES; 172 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 173 | CLANG_WARN_EMPTY_BODY = YES; 174 | CLANG_WARN_ENUM_CONVERSION = YES; 175 | CLANG_WARN_INFINITE_RECURSION = YES; 176 | CLANG_WARN_INT_CONVERSION = YES; 177 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 178 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 179 | CLANG_WARN_UNREACHABLE_CODE = YES; 180 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 181 | COPY_PHASE_STRIP = NO; 182 | DEBUG_INFORMATION_FORMAT = dwarf; 183 | ENABLE_STRICT_OBJC_MSGSEND = YES; 184 | ENABLE_TESTABILITY = YES; 185 | GCC_C_LANGUAGE_STANDARD = gnu99; 186 | GCC_DYNAMIC_NO_PIC = NO; 187 | GCC_NO_COMMON_BLOCKS = YES; 188 | GCC_OPTIMIZATION_LEVEL = 0; 189 | GCC_PREPROCESSOR_DEFINITIONS = ( 190 | "DEBUG=1", 191 | "$(inherited)", 192 | ); 193 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 194 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 195 | GCC_WARN_UNDECLARED_SELECTOR = YES; 196 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 197 | GCC_WARN_UNUSED_FUNCTION = YES; 198 | GCC_WARN_UNUSED_VARIABLE = YES; 199 | MTL_ENABLE_DEBUG_INFO = YES; 200 | ONLY_ACTIVE_ARCH = YES; 201 | }; 202 | name = Debug; 203 | }; 204 | 04E4BF3B1CB25D3200BC7305 /* Release */ = { 205 | isa = XCBuildConfiguration; 206 | buildSettings = { 207 | ALWAYS_SEARCH_USER_PATHS = NO; 208 | CLANG_ANALYZER_NONNULL = YES; 209 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 210 | CLANG_CXX_LIBRARY = "libc++"; 211 | CLANG_ENABLE_MODULES = YES; 212 | CLANG_ENABLE_OBJC_ARC = YES; 213 | CLANG_WARN_BOOL_CONVERSION = YES; 214 | CLANG_WARN_CONSTANT_CONVERSION = YES; 215 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 216 | CLANG_WARN_EMPTY_BODY = YES; 217 | CLANG_WARN_ENUM_CONVERSION = YES; 218 | CLANG_WARN_INFINITE_RECURSION = YES; 219 | CLANG_WARN_INT_CONVERSION = YES; 220 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 221 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 222 | CLANG_WARN_UNREACHABLE_CODE = YES; 223 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 224 | COPY_PHASE_STRIP = NO; 225 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 226 | ENABLE_NS_ASSERTIONS = NO; 227 | ENABLE_STRICT_OBJC_MSGSEND = YES; 228 | GCC_C_LANGUAGE_STANDARD = gnu99; 229 | GCC_NO_COMMON_BLOCKS = YES; 230 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 231 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 232 | GCC_WARN_UNDECLARED_SELECTOR = YES; 233 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 234 | GCC_WARN_UNUSED_FUNCTION = YES; 235 | GCC_WARN_UNUSED_VARIABLE = YES; 236 | MTL_ENABLE_DEBUG_INFO = NO; 237 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 238 | }; 239 | name = Release; 240 | }; 241 | 04E4BF3D1CB25D3200BC7305 /* Debug */ = { 242 | isa = XCBuildConfiguration; 243 | buildSettings = { 244 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 245 | CLANG_ENABLE_MODULES = YES; 246 | COMBINE_HIDPI_IMAGES = YES; 247 | DEPLOYMENT_LOCATION = YES; 248 | DSTROOT = "$(HOME)"; 249 | INFOPLIST_FILE = SwiftLintXcode/Info.plist; 250 | INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins"; 251 | LD_RUNPATH_SEARCH_PATHS = "$(DT_TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 252 | MACOSX_DEPLOYMENT_TARGET = 10.11; 253 | PRODUCT_BUNDLE_IDENTIFIER = net.ypresto.SwiftLintXcode; 254 | PRODUCT_NAME = SwiftLintXcode; 255 | SWIFT_OBJC_BRIDGING_HEADER = "SwiftLintXcode/SwiftLintXcode-Bridging-Header.h"; 256 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 257 | SWIFT_VERSION = 3.0; 258 | WRAPPER_EXTENSION = xcplugin; 259 | }; 260 | name = Debug; 261 | }; 262 | 04E4BF3E1CB25D3200BC7305 /* Release */ = { 263 | isa = XCBuildConfiguration; 264 | buildSettings = { 265 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 266 | CLANG_ENABLE_MODULES = YES; 267 | COMBINE_HIDPI_IMAGES = YES; 268 | DEPLOYMENT_LOCATION = YES; 269 | DSTROOT = "$(HOME)"; 270 | INFOPLIST_FILE = SwiftLintXcode/Info.plist; 271 | INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins"; 272 | LD_RUNPATH_SEARCH_PATHS = "$(DT_TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 273 | MACOSX_DEPLOYMENT_TARGET = 10.11; 274 | PRODUCT_BUNDLE_IDENTIFIER = net.ypresto.SwiftLintXcode; 275 | PRODUCT_NAME = SwiftLintXcode; 276 | SWIFT_OBJC_BRIDGING_HEADER = "SwiftLintXcode/SwiftLintXcode-Bridging-Header.h"; 277 | SWIFT_VERSION = 3.0; 278 | WRAPPER_EXTENSION = xcplugin; 279 | }; 280 | name = Release; 281 | }; 282 | /* End XCBuildConfiguration section */ 283 | 284 | /* Begin XCConfigurationList section */ 285 | 04E4BF2B1CB25D3100BC7305 /* Build configuration list for PBXProject "SwiftLintXcode" */ = { 286 | isa = XCConfigurationList; 287 | buildConfigurations = ( 288 | 04E4BF3A1CB25D3200BC7305 /* Debug */, 289 | 04E4BF3B1CB25D3200BC7305 /* Release */, 290 | ); 291 | defaultConfigurationIsVisible = 0; 292 | defaultConfigurationName = Release; 293 | }; 294 | 04E4BF3C1CB25D3200BC7305 /* Build configuration list for PBXNativeTarget "SwiftLintXcode" */ = { 295 | isa = XCConfigurationList; 296 | buildConfigurations = ( 297 | 04E4BF3D1CB25D3200BC7305 /* Debug */, 298 | 04E4BF3E1CB25D3200BC7305 /* Release */, 299 | ); 300 | defaultConfigurationIsVisible = 0; 301 | defaultConfigurationName = Release; 302 | }; 303 | /* End XCConfigurationList section */ 304 | }; 305 | rootObject = 04E4BF281CB25D3100BC7305 /* Project object */; 306 | } 307 | -------------------------------------------------------------------------------- /SwiftLintXcode.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftLintXcode.xcodeproj/xcshareddata/xcschemes/SwiftLintXcode.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 48 | 49 | 61 | 64 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /SwiftLintXcode/Formatter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Formatter.swift 3 | // SwiftLintXcode 4 | // 5 | // Created by yuya.tanaka on 2016/04/04. 6 | // Copyright (c) 2016 Yuya Tanaka. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Cocoa 11 | 12 | final class Formatter { 13 | static var sharedInstance = Formatter() 14 | 15 | private static let pathExtension = "SwiftLintXcode" 16 | private let fileManager = FileManager.default 17 | 18 | private struct CursorPosition { 19 | let line: Int 20 | let column: Int 21 | } 22 | 23 | class func isFormattableDocument(_ document: NSDocument) -> Bool { 24 | return document.fileURL?.pathExtension.lowercased() == "swift" 25 | } 26 | 27 | func tryFormatDocument(_ document: IDESourceCodeDocument) -> Bool { 28 | do { 29 | try formatDocument(document) 30 | return true 31 | } catch let error as NSError { 32 | NSAlert(error: error).runModal() 33 | } catch { 34 | NSAlert(error: errorWithMessage("Unknown error occured: \(error)")).runModal() 35 | } 36 | return false 37 | } 38 | 39 | func formatDocument(_ document: IDESourceCodeDocument) throws { 40 | let textStorage: DVTSourceTextStorage = document.textStorage() 41 | let originalString = textStorage.string 42 | let formattedString = try formatString(originalString) 43 | if formattedString == originalString { return } 44 | 45 | let selectedRange = SwiftLintXcodeTRVSXcode.textView().selectedRange() 46 | let cursorPosition = cursorPositionForSelectedRange(selectedRange, textStorage: textStorage) 47 | 48 | textStorage.beginEditing() 49 | textStorage.replaceCharacters(in: NSRange(location: 0, length: textStorage.length), with: formattedString, withUndoManager: document.undoManager()) 50 | textStorage.endEditing() 51 | 52 | let newLocation = locationForCursorPosition(cursorPosition, textStorage: textStorage) 53 | SwiftLintXcodeTRVSXcode.textView().setSelectedRange(NSRange(location: newLocation, length: 0)) 54 | } 55 | 56 | private func cursorPositionForSelectedRange(_ selectedRange: NSRange, textStorage: DVTSourceTextStorage) -> CursorPosition { 57 | let line = textStorage.lineRange(forCharacterRange: selectedRange).location 58 | let column = selectedRange.location - startLocationOfLine(line, textStorage: textStorage) 59 | return CursorPosition(line: line, column: column) 60 | } 61 | 62 | private func locationForCursorPosition(_ cursorPosition: CursorPosition, textStorage: DVTSourceTextStorage) -> Int { 63 | let startOfLine = startLocationOfLine(cursorPosition.line, textStorage: textStorage) 64 | let locationOfNextLine = textStorage.characterRange(forLineRange: NSRange(location: cursorPosition.line + 1, length: 0)).location 65 | // XXX: Can reach EOF..? Cursor position may be trimmed one charactor when cursor is on EOF. 66 | return min(startOfLine + cursorPosition.column, locationOfNextLine - 1) 67 | } 68 | 69 | private func startLocationOfLine(_ line: Int, textStorage: DVTSourceTextStorage) -> Int { 70 | return textStorage.characterRange(forLineRange: NSRange(location: line, length: 0)).location 71 | } 72 | 73 | private func formatString(_ string: String) throws -> String { 74 | guard let workspaceRootDirectory = SwiftLintXcodeIDEHelper.currentWorkspaceURL()?.deletingLastPathComponent().path else { 75 | throw errorWithMessage("Cannot determine project directory.") 76 | } 77 | 78 | return try withTempporaryFile { (filePath) in 79 | try string.write(toFile: filePath, atomically: false, encoding: String.Encoding.utf8) 80 | let swiftlintPath = try self.getExecutableOnPath(name: "swiftlint", workingDirectory: workspaceRootDirectory) 81 | let task = Process() 82 | task.launchPath = swiftlintPath 83 | task.arguments = ["autocorrect", "--path", filePath] 84 | task.currentDirectoryPath = workspaceRootDirectory 85 | task.launch() 86 | task.waitUntilExit() 87 | if task.terminationStatus != 0 { 88 | throw errorWithMessage("Executing swiftlint exited with non-zero status.") 89 | } 90 | return try String(contentsOfFile: filePath, encoding: String.Encoding.utf8) 91 | } 92 | } 93 | 94 | private func getExecutableOnPath(name: String, workingDirectory: String) throws -> String { 95 | let pipe = Pipe() 96 | let task = Process() 97 | task.launchPath = "/bin/bash" 98 | task.arguments = [ 99 | "-l", "-c", "which \(name)" 100 | ] 101 | task.currentDirectoryPath = workingDirectory 102 | task.standardOutput = pipe 103 | task.launch() 104 | task.waitUntilExit() 105 | if task.terminationStatus != 0 { 106 | throw errorWithMessage("Executing `which swiftlint` exited with non-zero status.") 107 | } 108 | 109 | let data = pipe.fileHandleForReading.readDataToEndOfFile() 110 | guard let pathString = String(data: data, encoding: String.Encoding.utf8) else { 111 | throw errorWithMessage("Cannot read result of `which swiftlint`.") 112 | } 113 | let path = pathString.trimmingCharacters(in: CharacterSet.newlines) 114 | if !fileManager.isExecutableFile(atPath: path) { 115 | throw errorWithMessage("swiftlint at \(path) is not executable.") 116 | } 117 | return path 118 | } 119 | 120 | private func withTempporaryFile(_ callback: (_ filePath: String) throws -> T) throws -> T { 121 | let filePath = createTemporaryPath() 122 | if fileManager.fileExists(atPath: filePath) { 123 | throw errorWithMessage("Cannot write to \(filePath), file already exists.") 124 | } 125 | defer { _ = try? fileManager.removeItem(atPath: filePath) } 126 | return try callback(filePath) 127 | } 128 | 129 | private func createTemporaryPath() -> String { 130 | return URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) 131 | .appendingPathComponent("SwiftLintXcode_\(UUID().uuidString).swift").path 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /SwiftLintXcode/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 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 | BNDL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | DVTPlugInCompatibilityUUIDs 26 | 27 | C4A681B0-4A26-480E-93EC-1218098B9AA0 28 | F41BD31E-2683-44B8-AE7F-5F09E919790E 29 | AD68E85B-441B-4301-B564-A45E4919A6AD 30 | A16FF353-8441-459E-A50C-B071F53F51B7 31 | 9F75337B-21B4-4ADC-B558-F9CADF7073A7 32 | E969541F-E6F9-4D25-8158-72DC3545A6C6 33 | 8DC44374-2B35-4C57-A6FE-2AD66A36AAD9 34 | AABB7188-E14E-4433-AD3B-5CD791EAD9A3 35 | 8DC44374-2B35-4C57-A6FE-2AD66A36AAD9 36 | AABB7188-E14E-4433-AD3B-5CD791EAD9A3 37 | 7FDF5C7A-131F-4ABB-9EDC-8C5F8F0B8A90 38 | 0420B86A-AA43-4792-9ED0-6FE0F2B16A13 39 | 7265231C-39B4-402C-89E1-16167C4CC990 40 | ACA8656B-FEA8-4B6D-8E4A-93F4C95C362C 41 | 8A66E736-A720-4B3C-92F1-33D9962C69DF 42 | 65C57D32-1E9B-44B8-8C04-A27BA7AAE2C4 43 | DA4FDFD8-C509-4D8B-8B55-84A7B66AE701 44 | E0A62D1F-3C18-4D74-BFE5-A4167D643966 45 | DFFB3951-EB0A-4C09-9DAC-5F2D28CC839C 46 | 47 | LSMinimumSystemVersion 48 | $(MACOSX_DEPLOYMENT_TARGET) 49 | NSPrincipalClass 50 | SwiftLintXcode 51 | XC4Compatible 52 | 53 | XCPluginHasUI 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /SwiftLintXcode/NSObject_Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject_Extension.swift 3 | // 4 | // Created by yuya.tanaka on 2016/04/04. 5 | // Copyright (c) 2016 Yuya Tanaka. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | extension NSObject { 11 | class func pluginDidLoad(_ bundle: Bundle) { 12 | let appName = Bundle.main.infoDictionary?["CFBundleName"] as? NSString 13 | if appName == "Xcode" { 14 | if sharedPlugin == nil { 15 | sharedPlugin = SwiftLintXcode(bundle: bundle) 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SwiftLintXcode/SaveHook.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SaveHook.swift 3 | // SwiftLintXcode 4 | // 5 | // Created by yuya.tanaka on 2016/04/04. 6 | // Copyright (c) 2016 Yuya Tanaka. All rights reserved. 7 | // 8 | 9 | // http://blog.waft.me/method-swizzling/ 10 | 11 | import Foundation 12 | import Cocoa 13 | 14 | final class SaveHook { 15 | 16 | private static var swizzled = false 17 | static var enabled = true 18 | 19 | private init() { 20 | fatalError() 21 | } 22 | 23 | class func swizzle() { 24 | if swizzled { return } 25 | swizzled = true 26 | 27 | let fromMethod = class_getInstanceMethod(NSDocument.self, #selector(NSDocument.save(withDelegate:didSave:contextInfo:))) 28 | let toMethod = class_getInstanceMethod(NSDocument.self, #selector(NSDocument.swiftLintXcodeSaveDocument(delegate:didSaveSelector:contextInfo:))) 29 | method_exchangeImplementations(fromMethod, toMethod) 30 | } 31 | 32 | class func tryOnSaveDocument(_ document: NSDocument) -> Bool { 33 | if !enabled { return true } 34 | if !Formatter.isFormattableDocument(document) { return true } 35 | let sourceCodeDocument: IDESourceCodeDocument = SwiftLintXcodeTRVSXcode.sourceCodeDocument() 36 | guard sourceCodeDocument == document else { return true } 37 | return Formatter.sharedInstance.tryFormatDocument(sourceCodeDocument) 38 | } 39 | } 40 | 41 | // https://github.com/travisjeffery/ClangFormat-Xcode/blob/a22114907592fb5d5b1043a4919d7be3e1496741/ClangFormat/NSDocument+TRVSClangFormat.m 42 | extension NSDocument { 43 | 44 | dynamic func swiftLintXcodeSaveDocument(delegate: AnyObject?, didSaveSelector: Selector, contextInfo: UnsafeMutableRawPointer) -> Void { 45 | if SaveHook.tryOnSaveDocument(self) { 46 | // NOTE: Call original method 47 | swiftLintXcodeSaveDocument(delegate: delegate, didSaveSelector: didSaveSelector, contextInfo: contextInfo) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /SwiftLintXcode/SwiftLintXcode-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "SwiftLintXcodeTRVSXcode.h" 6 | #import "SwiftLintXcodeIDEHelper.h" 7 | -------------------------------------------------------------------------------- /SwiftLintXcode/SwiftLintXcode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftLintXcode.swift 3 | // 4 | // Created by yuya.tanaka on 2016/04/04. 5 | // Copyright (c) 2016 Yuya Tanaka. All rights reserved. 6 | // 7 | 8 | import AppKit 9 | 10 | var sharedPlugin: SwiftLintXcode? 11 | 12 | class SwiftLintXcode: NSObject { 13 | 14 | var bundle: Bundle 15 | lazy var center = NotificationCenter.default 16 | 17 | var enableMenuItem: NSMenuItem! 18 | var disableMenuItem: NSMenuItem! 19 | 20 | init(bundle: Bundle) { 21 | self.bundle = bundle 22 | 23 | super.init() 24 | center.addObserver(self, selector: #selector(SwiftLintXcode.onApplicationDidFinishLaunching), name: NSNotification.Name.NSApplicationDidFinishLaunching, object: nil) 25 | } 26 | 27 | deinit { 28 | removeObserver() 29 | } 30 | 31 | private func removeObserver() { 32 | center.removeObserver(self) 33 | } 34 | 35 | func onApplicationDidFinishLaunching() { 36 | SaveHook.swizzle() 37 | createMenuItems() 38 | } 39 | 40 | private func createMenuItems() { 41 | removeObserver() 42 | 43 | guard let item = NSApp.mainMenu!.item(withTitle: "Edit") else { return } 44 | 45 | let pluginMenu = NSMenu(title:"SwiftLintXcode") 46 | let pluginMenuItem = NSMenuItem(title:"SwiftLintXcode", action: nil, keyEquivalent: "") 47 | pluginMenuItem.submenu = pluginMenu 48 | 49 | let autoCorrectMenuItem = NSMenuItem(title:"AutoCorrect Current File", action:#selector(SwiftLintXcode.doAutoCorrect), keyEquivalent:"") 50 | autoCorrectMenuItem.target = self 51 | pluginMenu.addItem(autoCorrectMenuItem) 52 | 53 | let enableMenuItem = NSMenuItem(title:"Enable AutoCorrect on Save", action:#selector(SwiftLintXcode.doEnableFormatOnSave), keyEquivalent:"") 54 | enableMenuItem.target = self 55 | pluginMenu.addItem(enableMenuItem) 56 | self.enableMenuItem = enableMenuItem 57 | 58 | let disableMenuItem = NSMenuItem(title:"Disable AutoCorrect on Save", action:#selector(SwiftLintXcode.doDisableFormatOnSave), keyEquivalent:"") 59 | disableMenuItem.target = self 60 | pluginMenu.addItem(disableMenuItem) 61 | self.disableMenuItem = disableMenuItem 62 | 63 | item.submenu!.addItem(NSMenuItem.separator()) 64 | item.submenu!.addItem(pluginMenuItem) 65 | 66 | updateMenuVisibility() 67 | } 68 | 69 | func doAutoCorrect() { 70 | let sourceCodeDocument: IDESourceCodeDocument = SwiftLintXcodeTRVSXcode.sourceCodeDocument() 71 | guard Formatter.isFormattableDocument(sourceCodeDocument) else { return } 72 | Formatter.sharedInstance.tryFormatDocument(sourceCodeDocument) 73 | } 74 | 75 | func doEnableFormatOnSave() { 76 | SaveHook.enabled = true 77 | updateMenuVisibility() 78 | } 79 | 80 | func doDisableFormatOnSave() { 81 | SaveHook.enabled = false 82 | updateMenuVisibility() 83 | } 84 | 85 | func updateMenuVisibility() { 86 | self.enableMenuItem.isHidden = SaveHook.enabled 87 | self.disableMenuItem.isHidden = !SaveHook.enabled 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /SwiftLintXcode/SwiftLintXcodeIDEHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftLintXcodeIDEHelper.h 3 | // SwiftLintXcode 4 | // 5 | // Created by yuya.tanaka on 2016/04/06. 6 | // Copyright © 2016年 Yuya Tanaka. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SwiftLintXcodeIDEHelper : NSObject 12 | 13 | + (nullable NSURL *)currentWorkspaceURL; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /SwiftLintXcode/SwiftLintXcodeIDEHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftLintXcodeIDEHelper.m 3 | // SwiftLintXcode 4 | // 5 | // Created by yuya.tanaka on 2016/04/06. 6 | // Copyright © 2016年 Yuya Tanaka. All rights reserved. 7 | // 8 | 9 | #import "SwiftLintXcodeIDEHelper.h" 10 | #import "SwiftLintXcodeTRVSXcode.h" 11 | @import Cocoa; 12 | 13 | @implementation SwiftLintXcodeIDEHelper 14 | 15 | + (nullable NSURL *)currentWorkspaceURL 16 | { 17 | IDEWorkspaceWindowController *workspaceWindowController = (IDEWorkspaceWindowController *)[[NSApp keyWindow] windowController]; 18 | IDEWorkspace *workspace = [workspaceWindowController valueForKey:@"_workspace"]; 19 | return workspace.representingFilePath.fileURL; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /SwiftLintXcode/SwiftLintXcodeTRVSXcode.h: -------------------------------------------------------------------------------- 1 | // 2 | // TRVSXcode.h 3 | // ClangFormat 4 | // 5 | // Created by Travis Jeffery on 1/9/14. 6 | // Copyright (c) 2014 Travis Jeffery. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // here be dragons... have to declare these private apis 12 | 13 | @interface DVTTextDocumentLocation : NSObject 14 | @property (readonly) NSRange characterRange; 15 | @property (readonly) NSRange lineRange; 16 | @end 17 | 18 | @interface DVTTextPreferences : NSObject 19 | + (id)preferences; 20 | @property BOOL trimWhitespaceOnlyLines; 21 | @property BOOL trimTrailingWhitespace; 22 | @property BOOL useSyntaxAwareIndenting; 23 | @end 24 | 25 | @interface DVTSourceTextStorage : NSTextStorage 26 | - (void)replaceCharactersInRange:(NSRange)range 27 | withString:(NSString *)string 28 | withUndoManager:(id)undoManager; 29 | - (NSRange)lineRangeForCharacterRange:(NSRange)range; 30 | - (NSRange)characterRangeForLineRange:(NSRange)range; 31 | - (void)indentCharacterRange:(NSRange)range undoManager:(id)undoManager; 32 | @end 33 | 34 | @interface DVTFileDataType : NSObject 35 | @property (readonly) NSString *identifier; 36 | @end 37 | 38 | @interface DVTFilePath : NSObject 39 | @property (readonly) NSURL *fileURL; 40 | @property (readonly) DVTFileDataType *fileDataTypePresumed; 41 | @end 42 | 43 | @interface IDEContainerItem : NSObject 44 | @property (readonly) DVTFilePath *resolvedFilePath; 45 | @end 46 | 47 | @interface IDEGroup : IDEContainerItem 48 | 49 | @end 50 | 51 | @interface IDEFileReference : IDEContainerItem 52 | 53 | @end 54 | 55 | @interface IDENavigableItem : NSObject 56 | @property (readonly) IDENavigableItem *parentItem; 57 | @property (readonly) id representedObject; 58 | @end 59 | 60 | @interface IDEFileNavigableItem : IDENavigableItem 61 | @property (readonly) DVTFileDataType *documentType; 62 | @property (readonly) NSURL *fileURL; 63 | @end 64 | 65 | @interface IDEStructureNavigator : NSObject 66 | @property (retain) NSArray *selectedObjects; 67 | @end 68 | 69 | @interface IDENavigableItemCoordinator : NSObject 70 | - (id)structureNavigableItemForDocumentURL:(id)arg1 71 | inWorkspace:(id)arg2 72 | error:(id *)arg3; 73 | @end 74 | 75 | @interface IDENavigatorArea : NSObject 76 | - (id)currentNavigator; 77 | @end 78 | 79 | @interface IDEWorkspaceTabController : NSObject 80 | @property (readonly) IDENavigatorArea *navigatorArea; 81 | @end 82 | 83 | @interface IDEDocumentController : NSDocumentController 84 | + (id)editorDocumentForNavigableItem:(id)arg1; 85 | + (id)retainedEditorDocumentForNavigableItem:(id)arg1 error:(id *)arg2; 86 | + (void)releaseEditorDocument:(id)arg1; 87 | @end 88 | 89 | @interface IDESourceCodeDocument : NSDocument 90 | - (DVTSourceTextStorage *)textStorage; 91 | - (NSUndoManager *)undoManager; 92 | @end 93 | 94 | @interface IDESourceCodeComparisonEditor : NSObject 95 | @property (readonly) NSTextView *keyTextView; 96 | @property (retain) NSDocument *primaryDocument; 97 | @end 98 | 99 | @interface IDESourceCodeEditor : NSObject 100 | @property (retain) NSTextView *textView; 101 | - (IDESourceCodeDocument *)sourceCodeDocument; 102 | @end 103 | 104 | @interface IDEEditorContext : NSObject 105 | - (id)editor; // returns the current editor. If the editor is the code editor, 106 | // the class is `IDESourceCodeEditor` 107 | @end 108 | 109 | @interface IDEEditorArea : NSObject 110 | - (IDEEditorContext *)lastActiveEditorContext; 111 | @end 112 | 113 | @interface IDEWorkspaceWindowController : NSObject 114 | @property (readonly) IDEWorkspaceTabController *activeWorkspaceTabController; 115 | - (IDEEditorArea *)editorArea; 116 | @end 117 | 118 | @interface IDEWorkspace : NSObject 119 | @property (readonly) DVTFilePath *representingFilePath; 120 | @end 121 | 122 | @interface IDEWorkspaceDocument : NSDocument 123 | @property (readonly) IDEWorkspace *workspace; 124 | @end 125 | 126 | @interface SwiftLintXcodeTRVSXcode : NSObject 127 | 128 | + (IDESourceCodeDocument *)sourceCodeDocument; 129 | + (NSTextView *)textView; 130 | + (BOOL)textViewHasSelection; 131 | + (NSRange)wholeRangeOfTextView; 132 | + (NSArray *)selectedFileNavigableItems; 133 | 134 | @end 135 | -------------------------------------------------------------------------------- /SwiftLintXcode/SwiftLintXcodeTRVSXcode.m: -------------------------------------------------------------------------------- 1 | // 2 | // TRVSXcode.m 3 | // ClangFormat 4 | // 5 | // Created by Travis Jeffery on 1/9/14. 6 | // Copyright (c) 2014 Travis Jeffery. All rights reserved. 7 | // 8 | 9 | #import "SwiftLintXcodeTRVSXcode.h" 10 | 11 | @implementation SwiftLintXcodeTRVSXcode 12 | 13 | + (id)currentEditor { 14 | if ([[self windowController] 15 | isKindOfClass:NSClassFromString(@"IDEWorkspaceWindowController")]) { 16 | IDEWorkspaceWindowController *workspaceController = 17 | (IDEWorkspaceWindowController *)[self windowController]; 18 | IDEEditorArea *editorArea = [workspaceController editorArea]; 19 | IDEEditorContext *editorContext = [editorArea lastActiveEditorContext]; 20 | return [editorContext editor]; 21 | } 22 | return nil; 23 | } 24 | 25 | + (IDESourceCodeDocument *)sourceCodeDocument { 26 | if ([[self currentEditor] 27 | isKindOfClass:NSClassFromString(@"IDESourceCodeEditor")]) { 28 | return [[self currentEditor] sourceCodeDocument]; 29 | } 30 | 31 | if ([[self currentEditor] 32 | isKindOfClass:NSClassFromString(@"IDESourceCodeComparisonEditor")] && 33 | [[[self currentEditor] primaryDocument] 34 | isKindOfClass:NSClassFromString(@"IDESourceCodeDocument")]) { 35 | return (IDESourceCodeDocument *)[[self currentEditor] primaryDocument]; 36 | } 37 | 38 | return nil; 39 | } 40 | 41 | + (NSTextView *)textView { 42 | if ([[self currentEditor] 43 | isKindOfClass:NSClassFromString(@"IDESourceCodeEditor")]) { 44 | return [[self currentEditor] textView]; 45 | } 46 | 47 | if ([[self currentEditor] 48 | isKindOfClass:NSClassFromString(@"IDESourceCodeComparisonEditor")]) { 49 | return [[self currentEditor] keyTextView]; 50 | } 51 | 52 | return nil; 53 | } 54 | 55 | + (BOOL)textViewHasSelection { 56 | return [[self textView] selectedRange].length > 0; 57 | } 58 | 59 | + (NSRange)wholeRangeOfTextView { 60 | return NSMakeRange(0, [[[self textView] textStorage] length]); 61 | } 62 | 63 | + (NSArray *)selectedFileNavigableItems { 64 | if (![[self windowController] 65 | isKindOfClass:NSClassFromString(@"IDEWorkspaceWindowController")]) 66 | return nil; 67 | 68 | IDEWorkspaceWindowController *workspaceController = 69 | (IDEWorkspaceWindowController *)[self windowController]; 70 | IDEWorkspaceTabController *workspaceTabController = 71 | [workspaceController activeWorkspaceTabController]; 72 | IDENavigatorArea *navigatorArea = [workspaceTabController navigatorArea]; 73 | id currentNavigator = [navigatorArea currentNavigator]; 74 | 75 | if (![currentNavigator 76 | isKindOfClass:NSClassFromString(@"IDEStructureNavigator")]) 77 | return nil; 78 | 79 | NSMutableArray *array = [NSMutableArray array]; 80 | 81 | [[currentNavigator selectedObjects] 82 | enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 83 | if (![obj isKindOfClass:NSClassFromString(@"IDEFileNavigableItem")]) 84 | return; 85 | 86 | IDEFileNavigableItem *fileNavigableItem = obj; 87 | NSString *uti = fileNavigableItem.documentType.identifier; 88 | if ([[NSWorkspace sharedWorkspace] 89 | type:uti 90 | conformsToType:(NSString *)kUTTypeSourceCode]) { 91 | [array addObject:fileNavigableItem]; 92 | } 93 | }]; 94 | 95 | return array; 96 | } 97 | 98 | + (NSWindowController *)windowController { 99 | return [[NSApp keyWindow] windowController]; 100 | } 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /SwiftLintXcode/errorHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // errorHelper.swift 3 | // SwiftLintXcode 4 | // 5 | // Created by yuya.tanaka on 2016/04/06. 6 | // Copyright © 2016年 Yuya Tanaka. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | func errorWithMessage(_ message: String) -> NSError { 12 | return NSError(domain: "net.ypresto.SwiftLintXcode", code: 0, userInfo: [ 13 | NSLocalizedDescriptionKey: message 14 | ]) 15 | } 16 | --------------------------------------------------------------------------------