Compare commits

...

16 commits

Author SHA1 Message Date
Yuya Tanaka
3d7ada8364 Bump version 2017-03-28 23:22:20 +09:00
Yuya Tanaka
7fbccc4058 Merge pull request #16 from reejosamuel/master
Add Xcode 8.3 UUID
2017-03-28 23:19:42 +09:00
Yuya Tanaka
517d754a28 Add Xcode 8-8.2 UUIDs 2017-03-28 23:18:47 +09:00
Reejo Samuel
6c7cb2bb6c Add Xcode 8.3 UUID 2017-03-28 16:12:15 +02:00
Yuya Tanaka
642cb0c26d Fix README.md link 2016-10-15 21:01:44 +09:00
Yuya Tanaka
06a8dfa701 Update README.md and CHANGELOG.md 2016-10-15 21:00:43 +09:00
Yuya Tanaka
adbc152040 Add .swiftlint.yml 2016-10-15 20:55:18 +09:00
Yuya Tanaka
c6421c4132 Migrate code to Swift 3 and Xcode 8 2016-10-15 20:55:18 +09:00
Yuya Tanaka
7bccda5767 Add notice for Xcode 8 plugin issue 2016-07-24 11:55:38 +09:00
Yuya Tanaka
ef0d9f356f Fix code style 2016-04-18 16:54:01 +09:00
Yuya Tanaka
e4504a599b Fix README.md 2016-04-07 14:26:51 +09:00
Yuya Tanaka
bc71bf7ea0 Update README.md and CHANGELOG.md 2016-04-06 20:37:51 +09:00
Yuya Tanaka
98c95a2143 Set working directory to workspace root directory 2016-04-06 19:32:37 +09:00
Yuya Tanaka
4246f4cc72 Fix not checking whether swift file or not... 2016-04-06 19:23:40 +09:00
Yuya Tanaka
62d37329cc Extract NSError creation to errorWithMessage method 2016-04-06 18:28:48 +09:00
Yuya Tanaka
58d40869da Do not create temporary directory 2016-04-06 18:23:39 +09:00
14 changed files with 193 additions and 84 deletions

4
.swiftlint.yml Normal file
View file

@ -0,0 +1,4 @@
disabled_rules:
- line_length
- force_cast
- todo

View file

@ -1,6 +1,31 @@
Change Log Change Log
========== ==========
Version 0.2.1
----------------------------
- Added UUIDs for Xcode <=8.3.
Version 0.2.0
----------------------------
- Xcode 8 Support.
Version 0.1.2
----------------------------
- Fixed bug that saving non-swift file runs autocorrect.
- Set working directory to where workspace (or project) file is placed.
Version 0.1.1
----------------------------
- Rename plug-in name: SwiftLintAutoCorrect -> SwiftLintXcode.
Version 0.1.0 Version 0.1.0
---------------------------- ----------------------------

View file

@ -8,14 +8,30 @@ Runs `swiftlint autocorrect --path CURRENT_FILE` before \*.swift file is saved.
![Screenshot](https://cloud.githubusercontent.com/assets/400558/14304460/d2a133dc-fbed-11e5-9573-2c21cce699e0.png) ![Screenshot](https://cloud.githubusercontent.com/assets/400558/14304460/d2a133dc-fbed-11e5-9573-2c21cce699e0.png)
IMPORTANT: Xcode 8 Installation
-------------------------------
Xcode 8 won't load any unsigned plugins without resigning Xcode itself.
https://github.com/alcatraz/Alcatraz/issues/475
See XVim's nice and simple installation doc to resign it..! (NOTE: AT YOUR OWN SECURITY RISK)
https://github.com/XVimProject/XVim/blob/3167408ade82cfef87acc704822da61af69688f8/INSTALL_Xcode8.md
INSTALLATION INSTALLATION
------------ ------------
```bash Install via [Alcatraz](https://github.com/alcatraz/Alcatraz), a package manager for Xcode.
# This plugin does not bundle swiftlint binary.
# Please ensure swiftlint is on PATH.
brew update && brew install swiftlint
This plugin does not bundle swiftlint binary. Please ensure swiftlint is on PATH.
```bash
brew update && brew install swiftlint
```
### Manual installation
```bash
git clone https://github.com/ypresto/SwiftLintXcode git clone https://github.com/ypresto/SwiftLintXcode
cd SwiftLintXcode cd SwiftLintXcode
# Build and install. # Build and install.

View file

@ -9,6 +9,8 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
0498717F1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498717E1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.m */; }; 0498717F1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498717E1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.m */; };
049871811CB28EBF00C5F7B5 /* Formatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 049871801CB28EBF00C5F7B5 /* Formatter.swift */; }; 049871811CB28EBF00C5F7B5 /* Formatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 049871801CB28EBF00C5F7B5 /* Formatter.swift */; };
04DFAD471CB508D1007998DF /* SwiftLintXcodeIDEHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 04DFAD461CB508D1007998DF /* SwiftLintXcodeIDEHelper.m */; };
04DFAD491CB50BC8007998DF /* errorHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DFAD481CB50BC8007998DF /* errorHelper.swift */; };
04E4BF341CB25D3200BC7305 /* SwiftLintXcode.xcscheme in Resources */ = {isa = PBXBuildFile; fileRef = 04E4BF331CB25D3200BC7305 /* SwiftLintXcode.xcscheme */; }; 04E4BF341CB25D3200BC7305 /* SwiftLintXcode.xcscheme in Resources */ = {isa = PBXBuildFile; fileRef = 04E4BF331CB25D3200BC7305 /* SwiftLintXcode.xcscheme */; };
04E4BF361CB25D3200BC7305 /* SwiftLintXcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E4BF351CB25D3200BC7305 /* SwiftLintXcode.swift */; }; 04E4BF361CB25D3200BC7305 /* SwiftLintXcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E4BF351CB25D3200BC7305 /* SwiftLintXcode.swift */; };
04E4BF381CB25D3200BC7305 /* NSObject_Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E4BF371CB25D3200BC7305 /* NSObject_Extension.swift */; }; 04E4BF381CB25D3200BC7305 /* NSObject_Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E4BF371CB25D3200BC7305 /* NSObject_Extension.swift */; };
@ -20,6 +22,9 @@
0498717D1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwiftLintXcodeTRVSXcode.h; sourceTree = "<group>"; }; 0498717D1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwiftLintXcodeTRVSXcode.h; sourceTree = "<group>"; };
0498717E1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SwiftLintXcodeTRVSXcode.m; sourceTree = "<group>"; }; 0498717E1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SwiftLintXcodeTRVSXcode.m; sourceTree = "<group>"; };
049871801CB28EBF00C5F7B5 /* Formatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Formatter.swift; sourceTree = "<group>"; }; 049871801CB28EBF00C5F7B5 /* Formatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Formatter.swift; sourceTree = "<group>"; };
04DFAD451CB508D1007998DF /* SwiftLintXcodeIDEHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwiftLintXcodeIDEHelper.h; sourceTree = "<group>"; };
04DFAD461CB508D1007998DF /* SwiftLintXcodeIDEHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SwiftLintXcodeIDEHelper.m; sourceTree = "<group>"; };
04DFAD481CB50BC8007998DF /* errorHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = errorHelper.swift; sourceTree = "<group>"; };
04E4BF2F1CB25D3100BC7305 /* SwiftLintXcode.xcplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftLintXcode.xcplugin; sourceTree = BUILT_PRODUCTS_DIR; }; 04E4BF2F1CB25D3100BC7305 /* SwiftLintXcode.xcplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftLintXcode.xcplugin; sourceTree = BUILT_PRODUCTS_DIR; };
04E4BF331CB25D3200BC7305 /* SwiftLintXcode.xcscheme */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = SwiftLintXcode.xcscheme; path = SwiftLintXcode.xcodeproj/xcshareddata/xcschemes/SwiftLintXcode.xcscheme; sourceTree = SOURCE_ROOT; }; 04E4BF331CB25D3200BC7305 /* SwiftLintXcode.xcscheme */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = SwiftLintXcode.xcscheme; path = SwiftLintXcode.xcodeproj/xcshareddata/xcschemes/SwiftLintXcode.xcscheme; sourceTree = SOURCE_ROOT; };
04E4BF351CB25D3200BC7305 /* SwiftLintXcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftLintXcode.swift; sourceTree = "<group>"; }; 04E4BF351CB25D3200BC7305 /* SwiftLintXcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftLintXcode.swift; sourceTree = "<group>"; };
@ -50,10 +55,13 @@
children = ( children = (
0498717D1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.h */, 0498717D1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.h */,
0498717E1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.m */, 0498717E1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.m */,
04DFAD451CB508D1007998DF /* SwiftLintXcodeIDEHelper.h */,
04DFAD461CB508D1007998DF /* SwiftLintXcodeIDEHelper.m */,
04E4BF351CB25D3200BC7305 /* SwiftLintXcode.swift */, 04E4BF351CB25D3200BC7305 /* SwiftLintXcode.swift */,
04E4BF371CB25D3200BC7305 /* NSObject_Extension.swift */, 04E4BF371CB25D3200BC7305 /* NSObject_Extension.swift */,
04E4BF401CB273A700BC7305 /* SaveHook.swift */, 04E4BF401CB273A700BC7305 /* SaveHook.swift */,
049871801CB28EBF00C5F7B5 /* Formatter.swift */, 049871801CB28EBF00C5F7B5 /* Formatter.swift */,
04DFAD481CB50BC8007998DF /* errorHelper.swift */,
04E4BF391CB25D3200BC7305 /* Info.plist */, 04E4BF391CB25D3200BC7305 /* Info.plist */,
04E4BF321CB25D3200BC7305 /* Supporting Files */, 04E4BF321CB25D3200BC7305 /* Supporting Files */,
0498717C1CB27F8800C5F7B5 /* SwiftLintXcode-Bridging-Header.h */, 0498717C1CB27F8800C5F7B5 /* SwiftLintXcode-Bridging-Header.h */,
@ -95,11 +103,12 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0730; LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0730; LastUpgradeCheck = 0800;
ORGANIZATIONNAME = "Yuya Tanaka"; ORGANIZATIONNAME = "Yuya Tanaka";
TargetAttributes = { TargetAttributes = {
04E4BF2E1CB25D3100BC7305 = { 04E4BF2E1CB25D3100BC7305 = {
CreatedOnToolsVersion = 7.3; CreatedOnToolsVersion = 7.3;
LastSwiftMigration = 0800;
}; };
}; };
}; };
@ -139,6 +148,8 @@
0498717F1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.m in Sources */, 0498717F1CB27F8900C5F7B5 /* SwiftLintXcodeTRVSXcode.m in Sources */,
04E4BF361CB25D3200BC7305 /* SwiftLintXcode.swift in Sources */, 04E4BF361CB25D3200BC7305 /* SwiftLintXcode.swift in Sources */,
049871811CB28EBF00C5F7B5 /* Formatter.swift in Sources */, 049871811CB28EBF00C5F7B5 /* Formatter.swift in Sources */,
04DFAD471CB508D1007998DF /* SwiftLintXcodeIDEHelper.m in Sources */,
04DFAD491CB50BC8007998DF /* errorHelper.swift in Sources */,
04E4BF411CB273A700BC7305 /* SaveHook.swift in Sources */, 04E4BF411CB273A700BC7305 /* SaveHook.swift in Sources */,
04E4BF381CB25D3200BC7305 /* NSObject_Extension.swift in Sources */, 04E4BF381CB25D3200BC7305 /* NSObject_Extension.swift in Sources */,
); );
@ -161,8 +172,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
@ -202,8 +215,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
@ -219,17 +234,18 @@
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
}; };
name = Release; name = Release;
}; };
04E4BF3D1CB25D3200BC7305 /* Debug */ = { 04E4BF3D1CB25D3200BC7305 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEPLOYMENT_LOCATION = YES; DEPLOYMENT_LOCATION = YES;
DSTROOT = "$(HOME)"; DSTROOT = "$(HOME)";
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
INFOPLIST_FILE = SwiftLintXcode/Info.plist; INFOPLIST_FILE = SwiftLintXcode/Info.plist;
INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins"; INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins";
LD_RUNPATH_SEARCH_PATHS = "$(DT_TOOLCHAIN_DIR)/usr/lib/swift/macosx"; LD_RUNPATH_SEARCH_PATHS = "$(DT_TOOLCHAIN_DIR)/usr/lib/swift/macosx";
@ -238,6 +254,7 @@
PRODUCT_NAME = SwiftLintXcode; PRODUCT_NAME = SwiftLintXcode;
SWIFT_OBJC_BRIDGING_HEADER = "SwiftLintXcode/SwiftLintXcode-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "SwiftLintXcode/SwiftLintXcode-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
WRAPPER_EXTENSION = xcplugin; WRAPPER_EXTENSION = xcplugin;
}; };
name = Debug; name = Debug;
@ -245,11 +262,11 @@
04E4BF3E1CB25D3200BC7305 /* Release */ = { 04E4BF3E1CB25D3200BC7305 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEPLOYMENT_LOCATION = YES; DEPLOYMENT_LOCATION = YES;
DSTROOT = "$(HOME)"; DSTROOT = "$(HOME)";
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
INFOPLIST_FILE = SwiftLintXcode/Info.plist; INFOPLIST_FILE = SwiftLintXcode/Info.plist;
INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins"; INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins";
LD_RUNPATH_SEARCH_PATHS = "$(DT_TOOLCHAIN_DIR)/usr/lib/swift/macosx"; LD_RUNPATH_SEARCH_PATHS = "$(DT_TOOLCHAIN_DIR)/usr/lib/swift/macosx";
@ -257,6 +274,7 @@
PRODUCT_BUNDLE_IDENTIFIER = net.ypresto.SwiftLintXcode; PRODUCT_BUNDLE_IDENTIFIER = net.ypresto.SwiftLintXcode;
PRODUCT_NAME = SwiftLintXcode; PRODUCT_NAME = SwiftLintXcode;
SWIFT_OBJC_BRIDGING_HEADER = "SwiftLintXcode/SwiftLintXcode-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "SwiftLintXcode/SwiftLintXcode-Bridging-Header.h";
SWIFT_VERSION = 3.0;
WRAPPER_EXTENSION = xcplugin; WRAPPER_EXTENSION = xcplugin;
}; };
name = Release; name = Release;

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0500" LastUpgradeVersion = "0800"
version = "2.0"> version = "2.0">
<BuildAction <BuildAction
parallelizeBuildables = "NO" parallelizeBuildables = "NO"

View file

@ -11,36 +11,32 @@ import Cocoa
final class Formatter { final class Formatter {
static var sharedInstance = Formatter() static var sharedInstance = Formatter()
private static let pathExtension = "SwiftLintXcode" private static let pathExtension = "SwiftLintXcode"
private let fileManager = FileManager.default
let fileManager = NSFileManager.defaultManager() private struct CursorPosition {
let tempDirURL: NSURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent("SwiftLintXcode-\(NSUUID().UUIDString)")
struct CursorPosition {
let line: Int let line: Int
let column: Int let column: Int
} }
class func isFormattableDocument(_ document: NSDocument) -> Bool {
class func isFormattableDocument(document: NSDocument) -> Bool { return document.fileURL?.pathExtension.lowercased() == "swift"
return (document.fileURL?.pathExtension?.lowercaseString == "swift") ?? false
} }
func tryFormatDocument(document: IDESourceCodeDocument) -> Bool { func tryFormatDocument(_ document: IDESourceCodeDocument) -> Bool {
do { do {
try formatDocument(document) try formatDocument(document)
return true return true
} catch let error as NSError { } catch let error as NSError {
NSAlert(error: error).runModal() NSAlert(error: error).runModal()
} catch { } catch {
NSAlert(error: NSError(domain: "net.ypresto.SwiftLintXcode", code: 0, userInfo: [ NSAlert(error: errorWithMessage("Unknown error occured: \(error)")).runModal()
NSLocalizedDescriptionKey: "Unknown error occured: \(error)"
])).runModal()
} }
return false return false
} }
func formatDocument(document: IDESourceCodeDocument) throws { func formatDocument(_ document: IDESourceCodeDocument) throws {
let textStorage: DVTSourceTextStorage = document.textStorage() let textStorage: DVTSourceTextStorage = document.textStorage()
let originalString = textStorage.string let originalString = textStorage.string
let formattedString = try formatString(originalString) let formattedString = try formatString(originalString)
@ -50,96 +46,88 @@ final class Formatter {
let cursorPosition = cursorPositionForSelectedRange(selectedRange, textStorage: textStorage) let cursorPosition = cursorPositionForSelectedRange(selectedRange, textStorage: textStorage)
textStorage.beginEditing() textStorage.beginEditing()
textStorage.replaceCharactersInRange(NSRange(location: 0, length: textStorage.length), withString: formattedString, withUndoManager: document.undoManager()) textStorage.replaceCharacters(in: NSRange(location: 0, length: textStorage.length), with: formattedString, withUndoManager: document.undoManager())
textStorage.endEditing() textStorage.endEditing()
let newLocation = locationForCursorPosition(cursorPosition, textStorage: textStorage) let newLocation = locationForCursorPosition(cursorPosition, textStorage: textStorage)
SwiftLintXcodeTRVSXcode.textView().setSelectedRange(NSRange(location: newLocation, length: 0)) SwiftLintXcodeTRVSXcode.textView().setSelectedRange(NSRange(location: newLocation, length: 0))
} }
private func cursorPositionForSelectedRange(selectedRange: NSRange, textStorage: DVTSourceTextStorage) -> CursorPosition { private func cursorPositionForSelectedRange(_ selectedRange: NSRange, textStorage: DVTSourceTextStorage) -> CursorPosition {
let line = textStorage.lineRangeForCharacterRange(selectedRange).location let line = textStorage.lineRange(forCharacterRange: selectedRange).location
let column = selectedRange.location - startLocationOfLine(line, textStorage: textStorage) let column = selectedRange.location - startLocationOfLine(line, textStorage: textStorage)
return CursorPosition(line: line, column: column) return CursorPosition(line: line, column: column)
} }
private func locationForCursorPosition(cursorPosition: CursorPosition, textStorage: DVTSourceTextStorage) -> Int { private func locationForCursorPosition(_ cursorPosition: CursorPosition, textStorage: DVTSourceTextStorage) -> Int {
let startOfLine = startLocationOfLine(cursorPosition.line, textStorage: textStorage) let startOfLine = startLocationOfLine(cursorPosition.line, textStorage: textStorage)
let locationOfNextLine = textStorage.characterRangeForLineRange(NSRange(location: cursorPosition.line + 1, length: 0)).location let locationOfNextLine = textStorage.characterRange(forLineRange: NSRange(location: cursorPosition.line + 1, length: 0)).location
// XXX: Can reach EOF..? Cursor position may be trimmed one charactor when cursor is on EOF. // XXX: Can reach EOF..? Cursor position may be trimmed one charactor when cursor is on EOF.
return min(startOfLine + cursorPosition.column, locationOfNextLine - 1) return min(startOfLine + cursorPosition.column, locationOfNextLine - 1)
} }
private func startLocationOfLine(line: Int, textStorage: DVTSourceTextStorage) -> Int { private func startLocationOfLine(_ line: Int, textStorage: DVTSourceTextStorage) -> Int {
return textStorage.characterRangeForLineRange(NSRange(location: line, length: 0)).location return textStorage.characterRange(forLineRange: NSRange(location: line, length: 0)).location
} }
private func formatString(string: String) throws -> String { private func formatString(_ string: String) throws -> String {
guard let workspaceRootDirectory = SwiftLintXcodeIDEHelper.currentWorkspaceURL()?.deletingLastPathComponent().path else {
throw errorWithMessage("Cannot determine project directory.")
}
return try withTempporaryFile { (filePath) in return try withTempporaryFile { (filePath) in
try string.writeToFile(filePath, atomically: false, encoding: NSUTF8StringEncoding) try string.write(toFile: filePath, atomically: false, encoding: String.Encoding.utf8)
let swiftlintPath = try self.getExecutableOnPath("swiftlint") let swiftlintPath = try self.getExecutableOnPath(name: "swiftlint", workingDirectory: workspaceRootDirectory)
let task = NSTask.launchedTaskWithLaunchPath(swiftlintPath, arguments: [ let task = Process()
"autocorrect", "--path", filePath task.launchPath = swiftlintPath
]) task.arguments = ["autocorrect", "--path", filePath]
task.currentDirectoryPath = workspaceRootDirectory
task.launch()
task.waitUntilExit() task.waitUntilExit()
if task.terminationStatus != 0 { if task.terminationStatus != 0 {
throw NSError(domain: "net.ypresto.SwiftLintXcode", code: 0, userInfo: [ throw errorWithMessage("Executing swiftlint exited with non-zero status.")
NSLocalizedDescriptionKey: "Executing swiftlint exited with non-zero status."
])
} }
return try String(contentsOfFile: filePath, encoding: NSUTF8StringEncoding) return try String(contentsOfFile: filePath, encoding: String.Encoding.utf8)
} }
} }
private func getExecutableOnPath(name: String) throws -> String { private func getExecutableOnPath(name: String, workingDirectory: String) throws -> String {
let pipe = NSPipe() let pipe = Pipe()
let task = NSTask() let task = Process()
task.launchPath = "/bin/bash" task.launchPath = "/bin/bash"
task.arguments = [ task.arguments = [
"-l", "-c", "which \(name)" "-l", "-c", "which \(name)"
] ]
task.currentDirectoryPath = workingDirectory
task.standardOutput = pipe task.standardOutput = pipe
task.launch() task.launch()
task.waitUntilExit() task.waitUntilExit()
if task.terminationStatus != 0 { if task.terminationStatus != 0 {
throw NSError(domain: "net.ypresto.SwiftLintXcode", code: 0, userInfo: [ throw errorWithMessage("Executing `which swiftlint` exited with non-zero status.")
NSLocalizedDescriptionKey: "Executing `which swiftlint` exited with non-zero status."
])
} }
let data = pipe.fileHandleForReading.readDataToEndOfFile() let data = pipe.fileHandleForReading.readDataToEndOfFile()
guard let pathString = String(data: data, encoding: NSUTF8StringEncoding) else { guard let pathString = String(data: data, encoding: String.Encoding.utf8) else {
throw NSError(domain: "net.ypresto.SwiftLintXcode", code: 0, userInfo: [ throw errorWithMessage("Cannot read result of `which swiftlint`.")
NSLocalizedDescriptionKey: "Cannot read result of `which swiftlint`."
])
} }
let path = pathString.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet()) let path = pathString.trimmingCharacters(in: CharacterSet.newlines)
if !fileManager.isExecutableFileAtPath(path) { if !fileManager.isExecutableFile(atPath: path) {
throw NSError(domain: "net.ypresto.SwiftLintXcode", code: 0, userInfo: [ throw errorWithMessage("swiftlint at \(path) is not executable.")
NSLocalizedDescriptionKey: "swiftlint at \(path) is not executable."
])
} }
return path return path
} }
private func withTempporaryFile<T>(@noescape callback: (filePath: String) throws -> T) throws -> T { private func withTempporaryFile<T>(_ callback: (_ filePath: String) throws -> T) throws -> T {
try ensureTemporaryDirectory()
let filePath = createTemporaryPath() let filePath = createTemporaryPath()
if fileManager.fileExistsAtPath(filePath) { if fileManager.fileExists(atPath: filePath) {
throw NSError(domain: "net.ypresto.SwiftLintXcode", code: 0, userInfo: [ throw errorWithMessage("Cannot write to \(filePath), file already exists.")
NSLocalizedDescriptionKey: "Cannot write to \(filePath), file already exists."
])
} }
defer { _ = try? fileManager.removeItemAtPath(filePath) } defer { _ = try? fileManager.removeItem(atPath: filePath) }
return try callback(filePath: filePath) return try callback(filePath)
} }
private func createTemporaryPath() -> String { private func createTemporaryPath() -> String {
return tempDirURL.URLByAppendingPathComponent(NSUUID().UUIDString).path! + ".swift" return URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
} .appendingPathComponent("SwiftLintXcode_\(UUID().uuidString).swift").path
private func ensureTemporaryDirectory() throws {
if fileManager.fileExistsAtPath(tempDirURL.path!) { return }
try fileManager.createDirectoryAtURL(tempDirURL, withIntermediateDirectories: true, attributes: nil)
} }
} }

View file

@ -38,6 +38,11 @@
<string>0420B86A-AA43-4792-9ED0-6FE0F2B16A13</string> <string>0420B86A-AA43-4792-9ED0-6FE0F2B16A13</string>
<string>7265231C-39B4-402C-89E1-16167C4CC990</string> <string>7265231C-39B4-402C-89E1-16167C4CC990</string>
<string>ACA8656B-FEA8-4B6D-8E4A-93F4C95C362C</string> <string>ACA8656B-FEA8-4B6D-8E4A-93F4C95C362C</string>
<string>8A66E736-A720-4B3C-92F1-33D9962C69DF</string>
<string>65C57D32-1E9B-44B8-8C04-A27BA7AAE2C4</string>
<string>DA4FDFD8-C509-4D8B-8B55-84A7B66AE701</string>
<string>E0A62D1F-3C18-4D74-BFE5-A4167D643966</string>
<string>DFFB3951-EB0A-4C09-9DAC-5F2D28CC839C</string>
</array> </array>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string> <string>$(MACOSX_DEPLOYMENT_TARGET)</string>

View file

@ -8,12 +8,12 @@
import Foundation import Foundation
extension NSObject { extension NSObject {
class func pluginDidLoad(bundle: NSBundle) { class func pluginDidLoad(_ bundle: Bundle) {
let appName = NSBundle.mainBundle().infoDictionary?["CFBundleName"] as? NSString let appName = Bundle.main.infoDictionary?["CFBundleName"] as? NSString
if appName == "Xcode" { if appName == "Xcode" {
if sharedPlugin == nil { if sharedPlugin == nil {
sharedPlugin = SwiftLintXcode(bundle: bundle) sharedPlugin = SwiftLintXcode(bundle: bundle)
} }
} }
} }
} }

View file

@ -24,14 +24,14 @@ final class SaveHook {
if swizzled { return } if swizzled { return }
swizzled = true swizzled = true
let fromMethod = class_getInstanceMethod(NSDocument.self, #selector(NSDocument.saveDocumentWithDelegate(_:didSaveSelector:contextInfo:))) let fromMethod = class_getInstanceMethod(NSDocument.self, #selector(NSDocument.save(withDelegate:didSave:contextInfo:)))
let toMethod = class_getInstanceMethod(NSDocument.self, #selector(NSDocument.SwiftLintXcodeSaveDocumentWithDelegate(_:didSaveSelector:contextInfo:))) let toMethod = class_getInstanceMethod(NSDocument.self, #selector(NSDocument.swiftLintXcodeSaveDocument(delegate:didSaveSelector:contextInfo:)))
method_exchangeImplementations(fromMethod, toMethod) method_exchangeImplementations(fromMethod, toMethod)
} }
class func tryOnSaveDocument(document: NSDocument) -> Bool { class func tryOnSaveDocument(_ document: NSDocument) -> Bool {
if !enabled { return true } if !enabled { return true }
Formatter.isFormattableDocument(document) if !Formatter.isFormattableDocument(document) { return true }
let sourceCodeDocument: IDESourceCodeDocument = SwiftLintXcodeTRVSXcode.sourceCodeDocument() let sourceCodeDocument: IDESourceCodeDocument = SwiftLintXcodeTRVSXcode.sourceCodeDocument()
guard sourceCodeDocument == document else { return true } guard sourceCodeDocument == document else { return true }
return Formatter.sharedInstance.tryFormatDocument(sourceCodeDocument) return Formatter.sharedInstance.tryFormatDocument(sourceCodeDocument)
@ -41,10 +41,10 @@ final class SaveHook {
// https://github.com/travisjeffery/ClangFormat-Xcode/blob/a22114907592fb5d5b1043a4919d7be3e1496741/ClangFormat/NSDocument+TRVSClangFormat.m // https://github.com/travisjeffery/ClangFormat-Xcode/blob/a22114907592fb5d5b1043a4919d7be3e1496741/ClangFormat/NSDocument+TRVSClangFormat.m
extension NSDocument { extension NSDocument {
dynamic func SwiftLintXcodeSaveDocumentWithDelegate(delegate: AnyObject?, didSaveSelector: Selector, contextInfo: UnsafeMutablePointer<Void>) -> Void { dynamic func swiftLintXcodeSaveDocument(delegate: AnyObject?, didSaveSelector: Selector, contextInfo: UnsafeMutableRawPointer) -> Void {
if SaveHook.tryOnSaveDocument(self) { if SaveHook.tryOnSaveDocument(self) {
// NOTE: Call original method // NOTE: Call original method
SwiftLintXcodeSaveDocumentWithDelegate(delegate, didSaveSelector: didSaveSelector, contextInfo: contextInfo); swiftLintXcodeSaveDocument(delegate: delegate, didSaveSelector: didSaveSelector, contextInfo: contextInfo)
} }
} }
} }

View file

@ -3,3 +3,4 @@
// //
#import "SwiftLintXcodeTRVSXcode.h" #import "SwiftLintXcodeTRVSXcode.h"
#import "SwiftLintXcodeIDEHelper.h"

View file

@ -11,17 +11,17 @@ var sharedPlugin: SwiftLintXcode?
class SwiftLintXcode: NSObject { class SwiftLintXcode: NSObject {
var bundle: NSBundle var bundle: Bundle
lazy var center = NSNotificationCenter.defaultCenter() lazy var center = NotificationCenter.default
var enableMenuItem: NSMenuItem! var enableMenuItem: NSMenuItem!
var disableMenuItem: NSMenuItem! var disableMenuItem: NSMenuItem!
init(bundle: NSBundle) { init(bundle: Bundle) {
self.bundle = bundle self.bundle = bundle
super.init() super.init()
center.addObserver(self, selector: #selector(SwiftLintXcode.onApplicationDidFinishLaunching), name: NSApplicationDidFinishLaunchingNotification, object: nil) center.addObserver(self, selector: #selector(SwiftLintXcode.onApplicationDidFinishLaunching), name: NSNotification.Name.NSApplicationDidFinishLaunching, object: nil)
} }
deinit { deinit {
@ -40,7 +40,7 @@ class SwiftLintXcode: NSObject {
private func createMenuItems() { private func createMenuItems() {
removeObserver() removeObserver()
guard let item = NSApp.mainMenu!.itemWithTitle("Edit") else { return } guard let item = NSApp.mainMenu!.item(withTitle: "Edit") else { return }
let pluginMenu = NSMenu(title:"SwiftLintXcode") let pluginMenu = NSMenu(title:"SwiftLintXcode")
let pluginMenuItem = NSMenuItem(title:"SwiftLintXcode", action: nil, keyEquivalent: "") let pluginMenuItem = NSMenuItem(title:"SwiftLintXcode", action: nil, keyEquivalent: "")
@ -60,7 +60,7 @@ class SwiftLintXcode: NSObject {
pluginMenu.addItem(disableMenuItem) pluginMenu.addItem(disableMenuItem)
self.disableMenuItem = disableMenuItem self.disableMenuItem = disableMenuItem
item.submenu!.addItem(NSMenuItem.separatorItem()) item.submenu!.addItem(NSMenuItem.separator())
item.submenu!.addItem(pluginMenuItem) item.submenu!.addItem(pluginMenuItem)
updateMenuVisibility() updateMenuVisibility()
@ -83,7 +83,7 @@ class SwiftLintXcode: NSObject {
} }
func updateMenuVisibility() { func updateMenuVisibility() {
self.enableMenuItem.hidden = SaveHook.enabled self.enableMenuItem.isHidden = SaveHook.enabled
self.disableMenuItem.hidden = !SaveHook.enabled self.disableMenuItem.isHidden = !SaveHook.enabled
} }
} }

View file

@ -0,0 +1,15 @@
//
// SwiftLintXcodeIDEHelper.h
// SwiftLintXcode
//
// Created by yuya.tanaka on 2016/04/06.
// Copyright © 2016年 Yuya Tanaka. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface SwiftLintXcodeIDEHelper : NSObject
+ (nullable NSURL *)currentWorkspaceURL;
@end

View file

@ -0,0 +1,22 @@
//
// SwiftLintXcodeIDEHelper.m
// SwiftLintXcode
//
// Created by yuya.tanaka on 2016/04/06.
// Copyright © 2016 Yuya Tanaka. All rights reserved.
//
#import "SwiftLintXcodeIDEHelper.h"
#import "SwiftLintXcodeTRVSXcode.h"
@import Cocoa;
@implementation SwiftLintXcodeIDEHelper
+ (nullable NSURL *)currentWorkspaceURL
{
IDEWorkspaceWindowController *workspaceWindowController = (IDEWorkspaceWindowController *)[[NSApp keyWindow] windowController];
IDEWorkspace *workspace = [workspaceWindowController valueForKey:@"_workspace"];
return workspace.representingFilePath.fileURL;
}
@end

View file

@ -0,0 +1,15 @@
//
// errorHelper.swift
// SwiftLintXcode
//
// Created by yuya.tanaka on 2016/04/06.
// Copyright © 2016 Yuya Tanaka. All rights reserved.
//
import Foundation
func errorWithMessage(_ message: String) -> NSError {
return NSError(domain: "net.ypresto.SwiftLintXcode", code: 0, userInfo: [
NSLocalizedDescriptionKey: message
])
}