├── .gitignore ├── Category ├── HKKeyboardEventSender.h ├── HKKeyboardEventSender.m ├── HKTextResult.h ├── HKTextResult.m ├── NSString+Snippet.h ├── NSString+Snippet.m ├── NSTextView+Snippet.h └── NSTextView+Snippet.m ├── HKSnippet.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcshareddata │ └── xcschemes │ │ └── HKSnippet.xcscheme └── xcuserdata │ ├── apple.xcuserdatad │ └── xcschemes │ │ └── xcschememanagement.plist │ └── hunk.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── HKSnippet ├── HKSnippet.h ├── HKSnippet.m ├── Info.plist └── default_snippets.plist ├── Images ├── demo1.gif ├── demo2.gif ├── setting1.png ├── setting2.png └── setting3.png ├── LICENSE ├── README.md └── Setting ├── HKSnippetEditViewController.h ├── HKSnippetEditViewController.m ├── HKSnippetEditViewController.xib ├── HKSnippetSetting.h ├── HKSnippetSetting.m ├── HKSnippetSettingController.h ├── HKSnippetSettingController.m └── HKSnippetSettingController.xib /.gitignore: -------------------------------------------------------------------------------- 1 | HKSnippet.xcodeproj/xcuserdata 2 | -------------------------------------------------------------------------------- /Category/HKKeyboardEventSender.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVKeyboardEventSender.h 3 | // VVDocumenter-Xcode 4 | // 5 | // Created by 王 巍 on 13-7-26. 6 | // 7 | // Copyright (c) 2015 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | #import 29 | 30 | 31 | @interface HKKeyboardEventSender : NSObject 32 | -(void) beginKeyBoradEvents; 33 | -(void) sendKeyCode:(NSInteger)keyCode; 34 | -(void) sendKeyCode:(NSInteger)keyCode withModifierCommand:(BOOL)command 35 | alt:(BOOL)alt 36 | shift:(BOOL)shift 37 | control:(BOOL)control; 38 | -(void) sendKeyCode:(NSInteger)keyCode withModifier:(NSInteger)modifierMask; 39 | -(void) endKeyBoradEvents; 40 | -(NSInteger) keyVCode; 41 | @end 42 | -------------------------------------------------------------------------------- /Category/HKKeyboardEventSender.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVKeyboardEventSender.m 3 | // VVDocumenter-Xcode 4 | // 5 | // Created by 王 巍 on 13-7-26. 6 | // 7 | // Copyright (c) 2015 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "HKKeyboardEventSender.h" 28 | 29 | @interface HKKeyboardEventSender() 30 | { 31 | CGEventSourceRef _source; 32 | CGEventTapLocation _location; 33 | } 34 | @end 35 | 36 | @implementation HKKeyboardEventSender 37 | -(void) beginKeyBoradEvents 38 | { 39 | _source = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState); 40 | _location = kCGHIDEventTap; 41 | } 42 | 43 | -(void) sendKeyCode:(NSInteger)keyCode 44 | { 45 | [self sendKeyCode:keyCode withModifier:0]; 46 | } 47 | 48 | -(void) sendKeyCode:(NSInteger)keyCode withModifierCommand:(BOOL)command 49 | alt:(BOOL)alt 50 | shift:(BOOL)shift 51 | control:(BOOL)control 52 | { 53 | NSInteger modifier = 0; 54 | if (command) { 55 | modifier = modifier ^ kCGEventFlagMaskCommand; 56 | } 57 | if (alt) { 58 | modifier = modifier ^ kCGEventFlagMaskAlternate; 59 | } 60 | if (shift) { 61 | modifier = modifier ^ kCGEventFlagMaskShift; 62 | } 63 | if (control) { 64 | modifier = modifier ^ kCGEventFlagMaskControl; 65 | } 66 | 67 | [self sendKeyCode:keyCode withModifier:modifier]; 68 | } 69 | 70 | -(void) sendKeyCode:(NSInteger)keyCode withModifier:(NSInteger)modifierMask 71 | { 72 | NSAssert(_source != NULL, @"You should call -beginKeyBoradEvents before sending a key event"); 73 | CGEventRef event; 74 | event = CGEventCreateKeyboardEvent(_source, keyCode, true); 75 | CGEventSetFlags(event, modifierMask); 76 | CGEventPost(_location, event); 77 | CFRelease(event); 78 | 79 | event = CGEventCreateKeyboardEvent(_source, keyCode, false); 80 | CGEventSetFlags(event, modifierMask); 81 | CGEventPost(_location, event); 82 | CFRelease(event); 83 | } 84 | 85 | -(void) endKeyBoradEvents 86 | { 87 | NSAssert(_source != NULL, @"You should call -beginKeyBoradEvents before end current keyborad event"); 88 | CFRelease(_source); 89 | _source = nil; 90 | } 91 | 92 | -(NSInteger) keyVCode 93 | { 94 | TISInputSourceRef inputSource = TISCopyCurrentKeyboardLayoutInputSource(); 95 | NSString *layoutID = (__bridge NSString *)TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); 96 | CFRelease(inputSource); 97 | 98 | // Possible dvorak layout SourceIDs: 99 | // com.apple.keylayout.Dvorak (System Qwerty) 100 | // But exclude: 101 | // com.apple.keylayout.DVORAK-QWERTYCMD (System Qwerty ⌘) 102 | // org.unknown.keylayout.DvorakImproved-Qwerty⌘ (http://www.macupdate.com/app/mac/24137/dvorak-improved-keyboard-layout) 103 | if ([layoutID localizedCaseInsensitiveContainsString:@"dvorak"] && ![layoutID localizedCaseInsensitiveContainsString: @"qwerty"]) { 104 | return kVK_ANSI_Period; 105 | } 106 | 107 | // Possible workman layout SourceIDs (https://github.com/ojbucao/Workman): 108 | // org.sil.ukelele.keyboardlayout.workman.workman 109 | // org.sil.ukelele.keyboardlayout.workman.workmanextended 110 | // org.sil.ukelele.keyboardlayout.workman.workman-io 111 | // org.sil.ukelele.keyboardlayout.workman.workman-p 112 | // org.sil.ukelele.keyboardlayout.workman.workman-pextended 113 | // org.sil.ukelele.keyboardlayout.workman.workman-dead 114 | if ([layoutID localizedCaseInsensitiveContainsString:@"workman"]) { 115 | return kVK_ANSI_B; 116 | } 117 | 118 | return kVK_ANSI_V; 119 | } 120 | @end 121 | -------------------------------------------------------------------------------- /Category/HKTextResult.h: -------------------------------------------------------------------------------- 1 | // 2 | // HKTextResult.h 3 | // HKSnippet 4 | // 5 | // Created by Hunk on 16/2/4. 6 | // Copyright © 2016年 Taobao.com. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HKTextResult : NSObject 12 | 13 | @property (nonatomic, assign) NSRange range; 14 | @property (nonatomic, copy) NSString *string; 15 | 16 | - (instancetype)initWithRange:(NSRange)range 17 | string:(NSString *)string; 18 | 19 | @end -------------------------------------------------------------------------------- /Category/HKTextResult.m: -------------------------------------------------------------------------------- 1 | // 2 | // HKTextResult.m 3 | // HKSnippet 4 | // 5 | // Created by Hunk on 16/2/4. 6 | // Copyright © 2016年 Taobao.com. All rights reserved. 7 | // 8 | 9 | #import "HKTextResult.h" 10 | 11 | @implementation HKTextResult 12 | 13 | - (instancetype)initWithRange:(NSRange)range 14 | string:(NSString *)string { 15 | if (self = [super init]) { 16 | _range = range; 17 | _string = string; 18 | } 19 | return self; 20 | } 21 | 22 | @end -------------------------------------------------------------------------------- /Category/NSString+Snippet.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+TextGetter.h 3 | // HKSnippet 4 | // 5 | // Created by Hunk on 16/2/4. 6 | // Copyright © 2016年 Taobao.com. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class HKTextResult; 12 | 13 | @interface NSString (Snippet) 14 | 15 | - (HKTextResult *)textResultOfCurrentLineAtLocation:(NSInteger)location; 16 | 17 | @end -------------------------------------------------------------------------------- /Category/NSString+Snippet.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+TextGetter.m 3 | // HKSnippet 4 | // 5 | // Created by Hunk on 16/2/4. 6 | // Copyright © 2016年 Taobao.com. All rights reserved. 7 | // 8 | 9 | #import "NSString+Snippet.h" 10 | #import "HKTextResult.h" 11 | 12 | @implementation NSString (Snippet) 13 | 14 | - (HKTextResult *)textResultOfCurrentLineAtLocation:(NSInteger)location { 15 | NSInteger curseLocation = location; 16 | NSRange range = NSMakeRange(0, curseLocation); 17 | NSRange currentLineRange = [self rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet] 18 | options:NSBackwardsSearch range:range]; 19 | NSString *line = nil; 20 | if (currentLineRange.location != NSNotFound) { 21 | NSRange lineRange = NSMakeRange(currentLineRange.location + 1, curseLocation - currentLineRange.location - 1); 22 | if (lineRange.location < [self length] && NSMaxRange(lineRange) < [self length]) { 23 | line = [self substringWithRange:lineRange]; 24 | line = [line stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 25 | return [[HKTextResult alloc] initWithRange:lineRange string:line]; 26 | } 27 | } 28 | return nil; 29 | } 30 | 31 | @end -------------------------------------------------------------------------------- /Category/NSTextView+Snippet.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSTextView+TextGetter.h 3 | // HKSnippet 4 | // 5 | // Created by Hunk on 16/2/4. 6 | // Copyright © 2016年 Taobao.com. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class HKTextResult; 12 | 13 | @interface NSTextView (Snippet) 14 | 15 | - (NSInteger)currentCurseLocation; 16 | - (HKTextResult *)textResultOfCurrentLine; 17 | 18 | @end -------------------------------------------------------------------------------- /Category/NSTextView+Snippet.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSTextView+TextGetter.m 3 | // HKSnippet 4 | // 5 | // Created by Hunk on 16/2/4. 6 | // Copyright © 2016年 Taobao.com. All rights reserved. 7 | // 8 | 9 | #import "NSTextView+Snippet.h" 10 | #import "NSString+Snippet.h" 11 | 12 | @implementation NSTextView (Snippet) 13 | 14 | - (NSInteger)currentCurseLocation { 15 | return [[self selectedRanges][0] rangeValue].location; 16 | } 17 | 18 | - (HKTextResult *)textResultOfCurrentLine { 19 | return [self.textStorage.string textResultOfCurrentLineAtLocation:[self currentCurseLocation]]; 20 | } 21 | 22 | @end -------------------------------------------------------------------------------- /HKSnippet.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 18194CAC1B169D94007A3FB0 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18194CAB1B169D94007A3FB0 /* AppKit.framework */; }; 11 | 18194CAE1B169D94007A3FB0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18194CAD1B169D94007A3FB0 /* Foundation.framework */; }; 12 | 18194CB61B169D94007A3FB0 /* HKSnippet.m in Sources */ = {isa = PBXBuildFile; fileRef = 18194CB51B169D94007A3FB0 /* HKSnippet.m */; }; 13 | 18194CC31B16A122007A3FB0 /* HKTextResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 18194CC21B16A122007A3FB0 /* HKTextResult.m */; }; 14 | 18B907F41B16F086009033BF /* HKSnippetSettingController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18B907F21B16F086009033BF /* HKSnippetSettingController.m */; }; 15 | 18B907F51B16F086009033BF /* HKSnippetSettingController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 18B907F31B16F086009033BF /* HKSnippetSettingController.xib */; }; 16 | 18BA9E501B16ACB300F471AF /* HKSnippetSetting.m in Sources */ = {isa = PBXBuildFile; fileRef = 18BA9E4F1B16ACB300F471AF /* HKSnippetSetting.m */; }; 17 | B40AE1641C708030004CE9B9 /* HKSnippetEditViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B40AE1621C708030004CE9B9 /* HKSnippetEditViewController.m */; }; 18 | B40AE1651C708030004CE9B9 /* HKSnippetEditViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B40AE1631C708030004CE9B9 /* HKSnippetEditViewController.xib */; }; 19 | B47099881C7969F30001329F /* default_snippets.plist in Resources */ = {isa = PBXBuildFile; fileRef = B47099871C7969F30001329F /* default_snippets.plist */; }; 20 | B4971C731C78C1D600B80D96 /* HKKeyboardEventSender.m in Sources */ = {isa = PBXBuildFile; fileRef = B4971C721C78C1D600B80D96 /* HKKeyboardEventSender.m */; }; 21 | B4CB74411C7057C60093E911 /* NSString+Snippet.m in Sources */ = {isa = PBXBuildFile; fileRef = B4CB743E1C7057C60093E911 /* NSString+Snippet.m */; }; 22 | B4CB74421C7057C60093E911 /* NSTextView+Snippet.m in Sources */ = {isa = PBXBuildFile; fileRef = B4CB74401C7057C60093E911 /* NSTextView+Snippet.m */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 18194CA81B169D94007A3FB0 /* HKSnippet.xcplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HKSnippet.xcplugin; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | 18194CAB1B169D94007A3FB0 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 28 | 18194CAD1B169D94007A3FB0 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 29 | 18194CB11B169D94007A3FB0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 30 | 18194CB41B169D94007A3FB0 /* HKSnippet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HKSnippet.h; sourceTree = ""; }; 31 | 18194CB51B169D94007A3FB0 /* HKSnippet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HKSnippet.m; sourceTree = ""; }; 32 | 18194CC11B16A122007A3FB0 /* HKTextResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HKTextResult.h; sourceTree = ""; }; 33 | 18194CC21B16A122007A3FB0 /* HKTextResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HKTextResult.m; sourceTree = ""; }; 34 | 18B907F11B16F086009033BF /* HKSnippetSettingController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HKSnippetSettingController.h; sourceTree = ""; }; 35 | 18B907F21B16F086009033BF /* HKSnippetSettingController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HKSnippetSettingController.m; sourceTree = ""; }; 36 | 18B907F31B16F086009033BF /* HKSnippetSettingController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HKSnippetSettingController.xib; sourceTree = ""; }; 37 | 18BA9E4E1B16ACB300F471AF /* HKSnippetSetting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HKSnippetSetting.h; sourceTree = ""; }; 38 | 18BA9E4F1B16ACB300F471AF /* HKSnippetSetting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HKSnippetSetting.m; sourceTree = ""; }; 39 | B40AE1611C708030004CE9B9 /* HKSnippetEditViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HKSnippetEditViewController.h; sourceTree = ""; }; 40 | B40AE1621C708030004CE9B9 /* HKSnippetEditViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HKSnippetEditViewController.m; sourceTree = ""; }; 41 | B40AE1631C708030004CE9B9 /* HKSnippetEditViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HKSnippetEditViewController.xib; sourceTree = ""; }; 42 | B47099871C7969F30001329F /* default_snippets.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = default_snippets.plist; sourceTree = ""; }; 43 | B4971C711C78C1D600B80D96 /* HKKeyboardEventSender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HKKeyboardEventSender.h; sourceTree = ""; }; 44 | B4971C721C78C1D600B80D96 /* HKKeyboardEventSender.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HKKeyboardEventSender.m; sourceTree = ""; }; 45 | B4CB743D1C7057C60093E911 /* NSString+Snippet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Snippet.h"; sourceTree = ""; }; 46 | B4CB743E1C7057C60093E911 /* NSString+Snippet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Snippet.m"; sourceTree = ""; }; 47 | B4CB743F1C7057C60093E911 /* NSTextView+Snippet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTextView+Snippet.h"; sourceTree = ""; }; 48 | B4CB74401C7057C60093E911 /* NSTextView+Snippet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTextView+Snippet.m"; sourceTree = ""; }; 49 | /* End PBXFileReference section */ 50 | 51 | /* Begin PBXFrameworksBuildPhase section */ 52 | 18194CA61B169D93007A3FB0 /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | 18194CAC1B169D94007A3FB0 /* AppKit.framework in Frameworks */, 57 | 18194CAE1B169D94007A3FB0 /* Foundation.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 18194C9F1B169D93007A3FB0 = { 65 | isa = PBXGroup; 66 | children = ( 67 | 18194CAF1B169D94007A3FB0 /* HKSnippet */, 68 | 18194CAA1B169D94007A3FB0 /* Frameworks */, 69 | 18194CA91B169D94007A3FB0 /* Products */, 70 | ); 71 | sourceTree = ""; 72 | }; 73 | 18194CA91B169D94007A3FB0 /* Products */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 18194CA81B169D94007A3FB0 /* HKSnippet.xcplugin */, 77 | ); 78 | name = Products; 79 | sourceTree = ""; 80 | }; 81 | 18194CAA1B169D94007A3FB0 /* Frameworks */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 18194CAB1B169D94007A3FB0 /* AppKit.framework */, 85 | 18194CAD1B169D94007A3FB0 /* Foundation.framework */, 86 | ); 87 | name = Frameworks; 88 | sourceTree = ""; 89 | }; 90 | 18194CAF1B169D94007A3FB0 /* HKSnippet */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 18BA9E4D1B16ACA700F471AF /* Setting */, 94 | 18194CBC1B16A0AE007A3FB0 /* Category */, 95 | 18194CB41B169D94007A3FB0 /* HKSnippet.h */, 96 | 18194CB51B169D94007A3FB0 /* HKSnippet.m */, 97 | 18194CB01B169D94007A3FB0 /* Supporting Files */, 98 | ); 99 | path = HKSnippet; 100 | sourceTree = ""; 101 | }; 102 | 18194CB01B169D94007A3FB0 /* Supporting Files */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | B47099871C7969F30001329F /* default_snippets.plist */, 106 | 18194CB11B169D94007A3FB0 /* Info.plist */, 107 | ); 108 | name = "Supporting Files"; 109 | sourceTree = ""; 110 | }; 111 | 18194CBC1B16A0AE007A3FB0 /* Category */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | B4971C711C78C1D600B80D96 /* HKKeyboardEventSender.h */, 115 | B4971C721C78C1D600B80D96 /* HKKeyboardEventSender.m */, 116 | B4CB743D1C7057C60093E911 /* NSString+Snippet.h */, 117 | B4CB743E1C7057C60093E911 /* NSString+Snippet.m */, 118 | B4CB743F1C7057C60093E911 /* NSTextView+Snippet.h */, 119 | B4CB74401C7057C60093E911 /* NSTextView+Snippet.m */, 120 | 18194CC11B16A122007A3FB0 /* HKTextResult.h */, 121 | 18194CC21B16A122007A3FB0 /* HKTextResult.m */, 122 | ); 123 | path = Category; 124 | sourceTree = SOURCE_ROOT; 125 | }; 126 | 18BA9E4D1B16ACA700F471AF /* Setting */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 18BA9E4E1B16ACB300F471AF /* HKSnippetSetting.h */, 130 | 18BA9E4F1B16ACB300F471AF /* HKSnippetSetting.m */, 131 | 18B907F11B16F086009033BF /* HKSnippetSettingController.h */, 132 | 18B907F21B16F086009033BF /* HKSnippetSettingController.m */, 133 | 18B907F31B16F086009033BF /* HKSnippetSettingController.xib */, 134 | B40AE1611C708030004CE9B9 /* HKSnippetEditViewController.h */, 135 | B40AE1621C708030004CE9B9 /* HKSnippetEditViewController.m */, 136 | B40AE1631C708030004CE9B9 /* HKSnippetEditViewController.xib */, 137 | ); 138 | path = Setting; 139 | sourceTree = SOURCE_ROOT; 140 | }; 141 | /* End PBXGroup section */ 142 | 143 | /* Begin PBXNativeTarget section */ 144 | 18194CA71B169D93007A3FB0 /* HKSnippet */ = { 145 | isa = PBXNativeTarget; 146 | buildConfigurationList = 18194CB91B169D94007A3FB0 /* Build configuration list for PBXNativeTarget "HKSnippet" */; 147 | buildPhases = ( 148 | 18194CA41B169D93007A3FB0 /* Sources */, 149 | 18194CA51B169D93007A3FB0 /* Resources */, 150 | 18194CA61B169D93007A3FB0 /* Frameworks */, 151 | ); 152 | buildRules = ( 153 | ); 154 | dependencies = ( 155 | ); 156 | name = HKSnippet; 157 | productName = HKSnippet; 158 | productReference = 18194CA81B169D94007A3FB0 /* HKSnippet.xcplugin */; 159 | productType = "com.apple.product-type.bundle"; 160 | }; 161 | /* End PBXNativeTarget section */ 162 | 163 | /* Begin PBXProject section */ 164 | 18194CA01B169D93007A3FB0 /* Project object */ = { 165 | isa = PBXProject; 166 | attributes = { 167 | CLASSPREFIX = HK; 168 | LastUpgradeCheck = 0720; 169 | ORGANIZATIONNAME = Taobao.com; 170 | TargetAttributes = { 171 | 18194CA71B169D93007A3FB0 = { 172 | CreatedOnToolsVersion = 6.3; 173 | }; 174 | }; 175 | }; 176 | buildConfigurationList = 18194CA31B169D93007A3FB0 /* Build configuration list for PBXProject "HKSnippet" */; 177 | compatibilityVersion = "Xcode 3.2"; 178 | developmentRegion = English; 179 | hasScannedForEncodings = 0; 180 | knownRegions = ( 181 | en, 182 | ); 183 | mainGroup = 18194C9F1B169D93007A3FB0; 184 | productRefGroup = 18194CA91B169D94007A3FB0 /* Products */; 185 | projectDirPath = ""; 186 | projectRoot = ""; 187 | targets = ( 188 | 18194CA71B169D93007A3FB0 /* HKSnippet */, 189 | ); 190 | }; 191 | /* End PBXProject section */ 192 | 193 | /* Begin PBXResourcesBuildPhase section */ 194 | 18194CA51B169D93007A3FB0 /* Resources */ = { 195 | isa = PBXResourcesBuildPhase; 196 | buildActionMask = 2147483647; 197 | files = ( 198 | B47099881C7969F30001329F /* default_snippets.plist in Resources */, 199 | 18B907F51B16F086009033BF /* HKSnippetSettingController.xib in Resources */, 200 | B40AE1651C708030004CE9B9 /* HKSnippetEditViewController.xib in Resources */, 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | }; 204 | /* End PBXResourcesBuildPhase section */ 205 | 206 | /* Begin PBXSourcesBuildPhase section */ 207 | 18194CA41B169D93007A3FB0 /* Sources */ = { 208 | isa = PBXSourcesBuildPhase; 209 | buildActionMask = 2147483647; 210 | files = ( 211 | 18BA9E501B16ACB300F471AF /* HKSnippetSetting.m in Sources */, 212 | B4CB74411C7057C60093E911 /* NSString+Snippet.m in Sources */, 213 | B4971C731C78C1D600B80D96 /* HKKeyboardEventSender.m in Sources */, 214 | B4CB74421C7057C60093E911 /* NSTextView+Snippet.m in Sources */, 215 | B40AE1641C708030004CE9B9 /* HKSnippetEditViewController.m in Sources */, 216 | 18B907F41B16F086009033BF /* HKSnippetSettingController.m in Sources */, 217 | 18194CB61B169D94007A3FB0 /* HKSnippet.m in Sources */, 218 | 18194CC31B16A122007A3FB0 /* HKTextResult.m in Sources */, 219 | ); 220 | runOnlyForDeploymentPostprocessing = 0; 221 | }; 222 | /* End PBXSourcesBuildPhase section */ 223 | 224 | /* Begin XCBuildConfiguration section */ 225 | 18194CB71B169D94007A3FB0 /* Debug */ = { 226 | isa = XCBuildConfiguration; 227 | buildSettings = { 228 | ALWAYS_SEARCH_USER_PATHS = NO; 229 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 230 | CLANG_CXX_LIBRARY = "libc++"; 231 | CLANG_ENABLE_MODULES = YES; 232 | CLANG_ENABLE_OBJC_ARC = YES; 233 | CLANG_WARN_BOOL_CONVERSION = YES; 234 | CLANG_WARN_CONSTANT_CONVERSION = YES; 235 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 236 | CLANG_WARN_EMPTY_BODY = YES; 237 | CLANG_WARN_ENUM_CONVERSION = YES; 238 | CLANG_WARN_INT_CONVERSION = YES; 239 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 240 | CLANG_WARN_UNREACHABLE_CODE = YES; 241 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 242 | COPY_PHASE_STRIP = NO; 243 | ENABLE_STRICT_OBJC_MSGSEND = YES; 244 | ENABLE_TESTABILITY = YES; 245 | GCC_C_LANGUAGE_STANDARD = gnu99; 246 | GCC_DYNAMIC_NO_PIC = NO; 247 | GCC_NO_COMMON_BLOCKS = YES; 248 | GCC_OPTIMIZATION_LEVEL = 0; 249 | GCC_PREPROCESSOR_DEFINITIONS = ( 250 | "DEBUG=1", 251 | "$(inherited)", 252 | ); 253 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 254 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 255 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 256 | GCC_WARN_UNDECLARED_SELECTOR = YES; 257 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 258 | GCC_WARN_UNUSED_FUNCTION = YES; 259 | GCC_WARN_UNUSED_VARIABLE = YES; 260 | MTL_ENABLE_DEBUG_INFO = YES; 261 | ONLY_ACTIVE_ARCH = YES; 262 | STRINGS_FILE_OUTPUT_ENCODING = "UTF-8"; 263 | }; 264 | name = Debug; 265 | }; 266 | 18194CB81B169D94007A3FB0 /* Release */ = { 267 | isa = XCBuildConfiguration; 268 | buildSettings = { 269 | ALWAYS_SEARCH_USER_PATHS = NO; 270 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 271 | CLANG_CXX_LIBRARY = "libc++"; 272 | CLANG_ENABLE_MODULES = YES; 273 | CLANG_ENABLE_OBJC_ARC = YES; 274 | CLANG_WARN_BOOL_CONVERSION = YES; 275 | CLANG_WARN_CONSTANT_CONVERSION = YES; 276 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 277 | CLANG_WARN_EMPTY_BODY = YES; 278 | CLANG_WARN_ENUM_CONVERSION = YES; 279 | CLANG_WARN_INT_CONVERSION = YES; 280 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 281 | CLANG_WARN_UNREACHABLE_CODE = YES; 282 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 283 | COPY_PHASE_STRIP = NO; 284 | ENABLE_NS_ASSERTIONS = NO; 285 | ENABLE_STRICT_OBJC_MSGSEND = YES; 286 | GCC_C_LANGUAGE_STANDARD = gnu99; 287 | GCC_NO_COMMON_BLOCKS = YES; 288 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 289 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 290 | GCC_WARN_UNDECLARED_SELECTOR = YES; 291 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 292 | GCC_WARN_UNUSED_FUNCTION = YES; 293 | GCC_WARN_UNUSED_VARIABLE = YES; 294 | MTL_ENABLE_DEBUG_INFO = NO; 295 | STRINGS_FILE_OUTPUT_ENCODING = "UTF-8"; 296 | }; 297 | name = Release; 298 | }; 299 | 18194CBA1B169D94007A3FB0 /* Debug */ = { 300 | isa = XCBuildConfiguration; 301 | buildSettings = { 302 | COMBINE_HIDPI_IMAGES = YES; 303 | DEPLOYMENT_LOCATION = YES; 304 | DSTROOT = "$(HOME)"; 305 | INFOPLIST_FILE = HKSnippet/Info.plist; 306 | INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins"; 307 | MACOSX_DEPLOYMENT_TARGET = 10.10; 308 | PRODUCT_BUNDLE_IDENTIFIER = "com.tiny-stone.$(PRODUCT_NAME:rfc1034identifier)"; 309 | PRODUCT_NAME = HKSnippet; 310 | WRAPPER_EXTENSION = xcplugin; 311 | }; 312 | name = Debug; 313 | }; 314 | 18194CBB1B169D94007A3FB0 /* Release */ = { 315 | isa = XCBuildConfiguration; 316 | buildSettings = { 317 | COMBINE_HIDPI_IMAGES = YES; 318 | DEPLOYMENT_LOCATION = YES; 319 | DSTROOT = "$(HOME)"; 320 | INFOPLIST_FILE = HKSnippet/Info.plist; 321 | INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins"; 322 | MACOSX_DEPLOYMENT_TARGET = 10.10; 323 | PRODUCT_BUNDLE_IDENTIFIER = "com.tiny-stone.$(PRODUCT_NAME:rfc1034identifier)"; 324 | PRODUCT_NAME = HKSnippet; 325 | WRAPPER_EXTENSION = xcplugin; 326 | }; 327 | name = Release; 328 | }; 329 | /* End XCBuildConfiguration section */ 330 | 331 | /* Begin XCConfigurationList section */ 332 | 18194CA31B169D93007A3FB0 /* Build configuration list for PBXProject "HKSnippet" */ = { 333 | isa = XCConfigurationList; 334 | buildConfigurations = ( 335 | 18194CB71B169D94007A3FB0 /* Debug */, 336 | 18194CB81B169D94007A3FB0 /* Release */, 337 | ); 338 | defaultConfigurationIsVisible = 0; 339 | defaultConfigurationName = Release; 340 | }; 341 | 18194CB91B169D94007A3FB0 /* Build configuration list for PBXNativeTarget "HKSnippet" */ = { 342 | isa = XCConfigurationList; 343 | buildConfigurations = ( 344 | 18194CBA1B169D94007A3FB0 /* Debug */, 345 | 18194CBB1B169D94007A3FB0 /* Release */, 346 | ); 347 | defaultConfigurationIsVisible = 0; 348 | defaultConfigurationName = Release; 349 | }; 350 | /* End XCConfigurationList section */ 351 | }; 352 | rootObject = 18194CA01B169D93007A3FB0 /* Project object */; 353 | } 354 | -------------------------------------------------------------------------------- /HKSnippet.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /HKSnippet.xcodeproj/xcshareddata/xcschemes/HKSnippet.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 | -------------------------------------------------------------------------------- /HKSnippet.xcodeproj/xcuserdata/apple.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SuppressBuildableAutocreation 6 | 7 | 18194CA71B169D93007A3FB0 8 | 9 | primary 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /HKSnippet.xcodeproj/xcuserdata/hunk.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SuppressBuildableAutocreation 6 | 7 | 18194CA71B169D93007A3FB0 8 | 9 | primary 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /HKSnippet/HKSnippet.h: -------------------------------------------------------------------------------- 1 | // 2 | // HKSnippet.h 3 | // HKSnippet 4 | // 5 | // Created by Hunk on 16/2/4. 6 | // Copyright © 2016年 Taobao.com. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HKSnippet : NSObject 12 | 13 | + (instancetype)sharedPlugin; 14 | 15 | @property (nonatomic, strong, readonly) NSBundle* bundle; 16 | 17 | @end -------------------------------------------------------------------------------- /HKSnippet/HKSnippet.m: -------------------------------------------------------------------------------- 1 | // 2 | // HKSnippet.m 3 | // HKSnippet 4 | // 5 | // Created by Hunk on 16/2/4. 6 | // Copyright © 2016年 Taobao.com. All rights reserved. 7 | // 8 | 9 | #import "HKSnippet.h" 10 | #import "HKTextResult.h" 11 | #import "HKSnippetSetting.h" 12 | #import "NSTextView+Snippet.h" 13 | #import "HKSnippetSettingController.h" 14 | #import "HKKeyboardEventSender.h" 15 | 16 | static HKSnippet *sharedPlugin; 17 | 18 | @interface HKSnippet() 19 | 20 | @property (nonatomic, strong, readwrite) NSBundle *bundle; 21 | @property (nonatomic, strong) HKSnippetSettingController *settingWindow; 22 | @property (nonatomic, strong) id eventMonitor; 23 | @property (nonatomic, assign) BOOL shouldReplace; 24 | 25 | @end 26 | 27 | @implementation HKSnippet 28 | 29 | #pragma mark - LifeCycle 30 | + (void)pluginDidLoad:(NSBundle *)plugin { 31 | static dispatch_once_t onceToken; 32 | NSString *currentApplicationName = [[NSBundle mainBundle] infoDictionary][@"CFBundleName"]; 33 | if ([currentApplicationName isEqual:@"Xcode"]) { 34 | dispatch_once(&onceToken, ^{ 35 | sharedPlugin = [[self alloc] initWithBundle:plugin]; 36 | }); 37 | } 38 | } 39 | 40 | + (instancetype)sharedPlugin { 41 | return sharedPlugin; 42 | } 43 | 44 | - (id)initWithBundle:(NSBundle *)plugin { 45 | if (self = [super init]) { 46 | _bundle = plugin; 47 | _shouldReplace = YES; 48 | [[NSNotificationCenter defaultCenter] addObserver:self 49 | selector:@selector(textStorageDidChange:) 50 | name:NSTextDidChangeNotification 51 | object:nil]; 52 | [[NSNotificationCenter defaultCenter] addObserver:self 53 | selector:@selector(xcodeDidLoad) 54 | name:NSApplicationDidFinishLaunchingNotification 55 | object:nil]; 56 | } 57 | return self; 58 | } 59 | 60 | - (void)dealloc { 61 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 62 | } 63 | 64 | - (void)xcodeDidLoad { 65 | [[NSNotificationCenter defaultCenter] removeObserver:self 66 | name:NSApplicationDidFinishLaunchingNotification 67 | object:nil]; 68 | [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 69 | [self addMenuItem]; 70 | }]; 71 | } 72 | 73 | #pragma mark - Notification Callback 74 | - (void)textStorageDidChange:(NSNotification *)notification { 75 | if (![[HKSnippetSetting defaultSetting] enabled]) { 76 | return; 77 | } 78 | 79 | if ([[notification object] isKindOfClass:[NSTextView class]]) { 80 | NSTextView *textView = (NSTextView *)[notification object]; 81 | HKTextResult *currentLineResult = [textView textResultOfCurrentLine]; 82 | NSString *cmdString = currentLineResult.string; 83 | 84 | // line start with "@", means no parameters 85 | if ([cmdString hasPrefix:@"@"]) { 86 | // replacement snippet exist 87 | if ([HKSnippetSetting defaultSetting].snippets[cmdString]) { 88 | [self pasteSnippet:[HKSnippetSetting defaultSetting].snippets[cmdString] 89 | byTriggerString:cmdString 90 | toTextView:textView 91 | withParameters:nil]; 92 | } 93 | } else { 94 | // Replace part (start with ^) exist 95 | if ([cmdString containsString:@"^"]) { 96 | NSUInteger replaceIndex = [cmdString rangeOfString:@"^"].location; 97 | NSString *replaceTrigger = [cmdString substringFromIndex:replaceIndex]; 98 | NSString *snippet = [HKSnippetSetting defaultSetting].snippets[replaceTrigger]; 99 | if (snippet) { 100 | [self pasteSnippet:snippet 101 | byTriggerString:replaceTrigger 102 | toTextView:textView 103 | withParameters:nil]; 104 | } 105 | } 106 | 107 | // Get parameters 108 | NSRange r = [cmdString rangeOfString:@"@" options:NSBackwardsSearch]; 109 | if (r.location > 0 && r.length == 1) { 110 | NSString *parameterString = [cmdString substringToIndex:r.location]; 111 | NSString *triggerString = [cmdString substringFromIndex:(r.location)]; 112 | NSArray *parameters = [parameterString componentsSeparatedByString:@"|"]; 113 | NSDictionary *snippets = [HKSnippetSetting defaultSetting].snippets; 114 | NSString *snippet = snippets[triggerString]; 115 | if (snippet) { 116 | [self pasteSnippet:[[self class] replacedSnippet:snippet withParameters:parameters] 117 | byTriggerString:cmdString 118 | toTextView:textView 119 | withParameters:parameters]; 120 | } 121 | } 122 | } 123 | } 124 | } 125 | 126 | 127 | #pragma mark - UI 128 | - (void)addMenuItem { 129 | NSMenu *mainMenu = [NSApp mainMenu]; 130 | NSMenuItem *pluginsMenuItem = [mainMenu itemWithTitle:@"Plugins"]; 131 | if (!pluginsMenuItem) { 132 | pluginsMenuItem = [[NSMenuItem alloc] init]; 133 | pluginsMenuItem.title = @"Plugins"; 134 | pluginsMenuItem.submenu = [[NSMenu alloc] initWithTitle:pluginsMenuItem.title]; 135 | NSInteger windowIndex = [mainMenu indexOfItemWithTitle:@"Window"]; 136 | [mainMenu insertItem:pluginsMenuItem atIndex:windowIndex]; 137 | } 138 | NSMenuItem *mainMenuItem = [[NSMenuItem alloc] initWithTitle:@"HKSnippet" 139 | action:@selector(showSettingWindow) 140 | keyEquivalent:@""]; 141 | [mainMenuItem setTarget:self]; 142 | [pluginsMenuItem.submenu addItem:mainMenuItem]; 143 | } 144 | 145 | - (void)showSettingWindow { 146 | self.settingWindow = [[HKSnippetSettingController alloc] initWithWindowNibName:@"HKSnippetSettingController"]; 147 | [self.settingWindow showWindow:self.settingWindow]; 148 | } 149 | 150 | #pragma mark - Private Method 151 | + (void)setPasteboardString:(NSString *)aString { 152 | NSPasteboard *thePasteboard = [NSPasteboard generalPasteboard]; 153 | [thePasteboard clearContents]; 154 | [thePasteboard declareTypes:@[NSStringPboardType] owner:nil]; 155 | [thePasteboard setString:aString forType:NSStringPboardType]; 156 | } 157 | 158 | + (NSString *)getPasteboardString { 159 | NSString *retValue = nil; 160 | NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; 161 | NSArray *types = [pasteboard types]; 162 | if ([types containsObject:NSStringPboardType]) { 163 | retValue = [pasteboard stringForType:NSStringPboardType]; 164 | } 165 | return retValue; 166 | } 167 | 168 | - (void)resetShouldReplace { 169 | self.shouldReplace = YES; 170 | } 171 | 172 | - (void)pasteSnippet:(NSString *)snippet 173 | byTriggerString:(NSString *)triggerString 174 | toTextView:(NSTextView *)textView 175 | withParameters:(NSArray *)parameters { 176 | 177 | if (!self.shouldReplace) { 178 | [self resetShouldReplace]; 179 | return; 180 | } 181 | 182 | NSUInteger length = triggerString.length; 183 | // save pasteboard string for restore 184 | NSString *oldPasteString = [[self class] getPasteboardString]; 185 | [[self class] setPasteboardString:snippet]; 186 | 187 | [textView setSelectedRange:NSMakeRange(textView.currentCurseLocation - length, length)]; 188 | 189 | //Begin to simulate keyborad pressing 190 | HKKeyboardEventSender *kes = [[HKKeyboardEventSender alloc] init]; 191 | [kes beginKeyBoradEvents]; 192 | 193 | //Cmd+delete Delete current line 194 | [kes sendKeyCode:kVK_Delete withModifierCommand:YES alt:NO shift:NO control:NO]; 195 | 196 | //Cmd+V, paste (which key to actually use is based on the current keyboard layout) 197 | NSInteger kKeyVCode = [kes keyVCode]; 198 | [kes sendKeyCode:kKeyVCode withModifierCommand:YES alt:NO shift:NO control:NO]; 199 | 200 | //The key down is just a defined finish signal by me. When we receive this key, 201 | //we know operation above is finished. 202 | [kes sendKeyCode:kVK_F19]; 203 | 204 | __weak typeof(self) weakSelf = self; 205 | self.eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask 206 | handler:^ NSEvent *(NSEvent *incomingEvent) { 207 | if ([incomingEvent type] == NSKeyDown && 208 | [incomingEvent keyCode] == kVK_F19) { 209 | //Finish signal arrived, no need to observe the event 210 | [NSEvent removeMonitor:weakSelf.eventMonitor]; 211 | weakSelf.eventMonitor = nil; 212 | 213 | //Restore previois patse board content 214 | if (oldPasteString) { 215 | [[self class] setPasteboardString:oldPasteString]; 216 | } 217 | if ([snippet containsString:@"<#"]) { 218 | //Set cursor before the inserted snippet. So we can use tab to begin edit. 219 | int snippetLength = (int)snippet.length; 220 | [textView setSelectedRange:NSMakeRange(textView.currentCurseLocation - snippetLength, 0)]; 221 | 222 | //Send a 'tab' after insert the snippet. For our lazy programmers. :-) 223 | [kes sendKeyCode:kVK_Tab]; 224 | [kes endKeyBoradEvents]; 225 | } 226 | 227 | weakSelf.shouldReplace = NO; 228 | //Invalidate the finish signal, in case you set it to do some other thing. 229 | return nil; 230 | } else { 231 | return incomingEvent; 232 | } 233 | }]; 234 | [self performSelector:@selector(resetShouldReplace) 235 | withObject:nil 236 | afterDelay:3.0f]; 237 | } 238 | 239 | + (NSString *)replacedSnippet:(NSString *)orgSnippet 240 | withParameters:(NSArray *)parameter { 241 | NSString *snippet = [NSString stringWithString:orgSnippet]; 242 | snippet = [snippet stringByReplacingOccurrencesOfString:@"<#name#>" 243 | withString:parameter[0]]; 244 | for (NSString *p in parameter) { 245 | NSString *uppercaseP = p.uppercaseString; 246 | 247 | if ([uppercaseP containsString:@"FONT"]) { 248 | snippet = [snippet stringByReplacingOccurrencesOfString:@"<#font#>" 249 | withString:p]; 250 | continue; 251 | } 252 | // replace color 253 | if ([uppercaseP containsString:@"COLOR"]) { 254 | snippet = [snippet stringByReplacingOccurrencesOfString:@"<#color#>" 255 | withString:p]; 256 | continue; 257 | } 258 | // replace image 259 | if ([uppercaseP containsString:@"UIIMAGE"]) { 260 | snippet = [snippet stringByReplacingOccurrencesOfString:@"<#image#>" 261 | withString:p]; 262 | continue; 263 | } 264 | // replace selector 265 | if ([uppercaseP containsString:@"SELECTOR"]) { 266 | snippet = [snippet stringByReplacingOccurrencesOfString:@"<#selector#>" 267 | withString:p]; 268 | continue; 269 | } 270 | } 271 | return snippet; 272 | } 273 | 274 | @end 275 | -------------------------------------------------------------------------------- /HKSnippet/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 | 37B30044-3B14-46BA-ABAA-F01000C27B63 28 | 640F884E-CE55-4B40-87C0-8869546CAB7A 29 | A2E4D43F-41F4-4FB9-BB94-7177011C9AED 30 | AD68E85B-441B-4301-B564-A45E4919A6AD 31 | C4A681B0-4A26-480E-93EC-1218098B9AA0 32 | FEC992CC-CA4A-4CFD-8881-77300FCB848A 33 | A16FF353-8441-459E-A50C-B071F53F51B7 34 | 992275C1-432A-4CF7-B659-D84ED6D42D3F 35 | 9F75337B-21B4-4ADC-B558-F9CADF7073A7 36 | E969541F-E6F9-4D25-8158-72DC3545A6C6 37 | 8DC44374-2B35-4C57-A6FE-2AD66A36AAD9 38 | 5EDAC44F-8E0B-42C9-9BEF-E9C12EEC4949 39 | 7FDF5C7A-131F-4ABB-9EDC-8C5F8F0B8A90 40 | AABB7188-E14E-4433-AD3B-5CD791EAD9A3 41 | 0420B86A-AA43-4792-9ED0-6FE0F2B16A13 42 | CC0D0F4F-05B3-431A-8F33-F84AFCB2C651 43 | 7265231C-39B4-402C-89E1-16167C4CC990 44 | 9AFF134A-08DC-4096-8CEE-62A4BB123046 45 | F41BD31E-2683-44B8-AE7F-5F09E919790E 46 | ACA8656B-FEA8-4B6D-8E4A-93F4C95C362C 47 | 1637F4D5-0B27-416B-A78D-498965D64877 48 | 8A66E736-A720-4B3C-92F1-33D9962C69DF 49 | E0A62D1F-3C18-4D74-BFE5-A4167D643966 50 | 51 | LSMinimumSystemVersion 52 | $(MACOSX_DEPLOYMENT_TARGET) 53 | NSPrincipalClass 54 | HKSnippet 55 | XC4Compatible 56 | 57 | XCPluginHasUI 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /HKSnippet/default_snippets.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | @ani 6 | [UIView animateWithDuration:<#(NSTimeInterval)#> 7 | animations:^{ 8 | <#code#> 9 | } completion:^(BOOL finished) { 10 | <#code#> 11 | }]; 12 | @button 13 | UIButton *<#name#> = [UIButton new]; 14 | <#name#>.backgroundColor = [UIColor clearColor]; 15 | [<#name#> setTitleColor:<#title color#> forState:UIControlStateNormal]; 16 | [<#name#> setTitle:<# title #> forState:UIControlStateNormal]; 17 | [<#name#> setImage:<#image#> forState:UIControlStateNormal]; 18 | @cf 19 | static CGFloat const <#name#> = <#value#>; 20 | @cs 21 | static NSString * const <#name#> = @"<#value#>"; 22 | @de 23 | - (void)dealloc { 24 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 25 | } 26 | @do 27 | do { 28 | <#statements#> 29 | } while (<#condition#>); 30 | @enum 31 | typedef enum : NSUInteger { 32 | <#MyEnumValueA#>, 33 | <#MyEnumValueB#>, 34 | <#MyEnumValueC#>, 35 | } <#MyEnum#>; 36 | @fb 37 | - (UIButton *)<#name#> { 38 | if(!_<#name#>) { 39 | _<#name#> = [UIButton new]; 40 | _<#name#>.layer.cornerRadius = <#radius#>; 41 | _<#name#>.layer.masksToBounds = YES; 42 | _<#name#>.backgroundColor = [UIColor clearColor]; 43 | [_<#name#> setTitleColor:<#title color#> forState:UIControlStateNormal]; 44 | [_<#name#> setTitle:<# title #> forState:UIControlStateNormal]; 45 | [_<#name#> setImage:<#image#> forState:UIControlStateNormal]; 46 | } 47 | return _<#name#>; 48 | } 49 | @fd 50 | - (NSString *)description 51 | { 52 | return [NSString stringWithFormat:@"<#format string#>", <#arguments#>]; 53 | } 54 | @ff 55 | - (<#type#> *)<#name#> { 56 | if(!_<#name#>) { 57 | <#Init Code#> 58 | } 59 | return _<#name#>; 60 | } 61 | @fi 62 | - (UIImageView *)<#name#> { 63 | if(!_<#name#>) { 64 | _<#name#> = [UIImageView new]; 65 | _<#name#>.layer.cornerRadius = <#radius#>; 66 | _<#name#>.layer.masksToBounds = YES; 67 | _<#name#>.backgroundColor = [UIColor clearColor]; 68 | _<#name#>.image = <#image#>; 69 | } 70 | return _<#name#>; 71 | } 72 | @fl 73 | - (UILabel *)<#name#> { 74 | if(!_<#name#>) { 75 | _<#name#> = [UILabel new]; 76 | _<#name#>.backgroundColor = [UIColor clearColor]; 77 | _<#name#>.textAlignment = NSTextAlignmentCenter; 78 | _<#name#>.numberOfLines = 0; 79 | _<#name#>.textColor = <#color#>; 80 | _<#name#>.font = <#font#>; 81 | _<#name#>.text = <#text#>; 82 | } 83 | return _<#name#>; 84 | } 85 | @for 86 | for (<#type#> *<#object#> in <#collection#>) { 87 | <#statements#> 88 | } 89 | @ft 90 | - (UITableView *)<#name#> { 91 | if(!_<#name#>) { 92 | _<#name#> = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped]; 93 | _<#name#>.backgroundColor = [UIColor clearColor]; 94 | _<#name#>.contentInset = UIEdgeInsetsMake(0, 0, 0, 0); 95 | _<#name#>.separatorStyle = UITableViewCellSeparatorStyleSingleLine; 96 | _<#name#>.separatorColor = <#color#>; 97 | _<#name#>.delegate = <#table delegate#>; 98 | _<#name#>.dataSource = <#table datasource#>; 99 | 100 | [_<#name#> registerClass:[<#class name#> class] forCellReuseIdentifier:<#cellId#>]; 101 | } 102 | return _<#name#>; 103 | } 104 | @fv 105 | - (UIView *)<#name#> { 106 | if(!_<#name#>) { 107 | _<#name#> = [UIView new]; 108 | _<#name#>.backgroundColor = <#color#>; 109 | } 110 | return _<#name#>; 111 | } 112 | @gmk 113 | #pragma mark - Getters & Setters 114 | @imageV 115 | UIImageView *<#name#> = [UIImageView new]; 116 | <#name#>.layer.cornerRadius = <#radius#>; 117 | <#name#>.layer.masksToBounds = YES; 118 | <#name#>.backgroundColor = [UIColor clearColor]; 119 | <#name#>.image = <#image#>; 120 | @init 121 | - (instancetype)init { 122 | self = [super init]; 123 | if (self) { 124 | <#statements#> 125 | } 126 | return self; 127 | } 128 | @it 129 | @interface <#name#> () 130 | 131 | <#Property#> 132 | 133 | @end 134 | @kvo 135 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 136 | { 137 | if (context == <#context#>) { 138 | <#code to be executed upon observing keypath#> 139 | } else { 140 | [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 141 | } 142 | } 143 | @label 144 | UILabel *<#name#> = [UILabel new]; 145 | <#name#>.backgroundColor = [UIColor clearColor]; 146 | <#name#>.textAlignment = NSTextAlignmentCenter; 147 | <#name#>.numberOfLines = 0; 148 | <#name#>.textColor = <#color#>; 149 | <#name#>.font = <#font#>; 150 | <#name#>.text = <#text#>; 151 | @lmk 152 | #pragma mark - LifeCycle 153 | @log 154 | NSLog(@"<#format#>",<#data#>); 155 | @ls 156 | - (void)layoutSubviews { 157 | [super layoutSubviews]; 158 | CGFloat w = self.frame.size.width; 159 | CGFloat h = self.frame.size.height; 160 | <#set subview frames#> 161 | } 162 | @lv 163 | - (void)loadView { 164 | [super loadView]; 165 | <#set subview frames#> 166 | } 167 | @mk 168 | #pragma mark - <#section title#> 169 | @model 170 | - (NSString *)apiVersion { 171 | return @"1.0"; 172 | } 173 | 174 | - (NSString *)methodName { 175 | return @""; 176 | } 177 | 178 | - (BOOL)useAuth { 179 | return YES; 180 | } 181 | 182 | - (NSInteger)pageSize { 183 | return 20; 184 | } 185 | 186 | - (NSDictionary *)dataParams { 187 | return @{}; 188 | } 189 | 190 | - (NSMutableArray *)constructDataArray:(NSDictionary *)requestData { 191 | NSMutableArray *tmpArray = [NSMutableArray array]; 192 | // NSArray *<#data list#> = requestData[@"data"]; 193 | // for (NSDictionary *dic in <#data list#> ) { 194 | // <#item class#> *item = [[<#item class#> alloc] init]; 195 | // [item tddAutoSetPropertySafety:dic]; 196 | // [tmpArray addObject:item]; 197 | // } 198 | return tmpArray; 199 | } 200 | @nsuser 201 | [[NSUserDefaults standardUserDefaults] setObject:<#object#> forKey:<#key#>]; 202 | @ob 203 | [[NSNotificationCenter defaultCenter] addObserver:self 204 | selector:@selector(<#selector#>) 205 | name:<#Notification#> 206 | object:nil]; 207 | @pa 208 | @property (assign) <#type#> <#value#>; 209 | @pc 210 | @property (copy) <#type#> *<#value#>; 211 | @pmk 212 | #pragma mark - Private Method 213 | @pna 214 | @property (nonatomic, assign) <#type#> <#value#>; 215 | @pnc 216 | @property (nonatomic, copy) <#type#> *<#value#>; 217 | @pns 218 | @property (nonatomic, strong) <#type#> *<#value#>; 219 | @pnw 220 | @property (nonatomic, weak) <#type#> *<#value#>; 221 | @pra 222 | @property (assign, readonly) <#type#> <#value#>; 223 | @prc 224 | @property (copy, readonly) <#type#> *<#value#>; 225 | @prna 226 | @property (nonatomic, assign, readonly) <#type#> <#value#>; 227 | @prnc 228 | @property (nonatomic, copy, readonly) <#type#> *<#value#>; 229 | @prns 230 | @property (nonatomic, strong, readonly) <#type#> *<#value#>; 231 | @prnw 232 | @property (nonatomic, weak, readonly) <#type#> *<#value#>; 233 | @prs 234 | @property (strong, readonly) <#type#> *<#value#>; 235 | @prw 236 | @property (weak, readonly) <#type#> *<#value#>; 237 | @ps 238 | @property (strong) <#type#> *<#value#>; 239 | @pw 240 | @property (weak) <#type#> *<#value#>; 241 | @qf 242 | - (void)<#name#> { 243 | 244 | } 245 | 246 | @singleton 247 | + (instancetype)sharedManager { 248 | static <#Class#> *_sharedInstance = nil; 249 | static dispatch_once_t onceToken; 250 | dispatch_once(&onceToken, ^{ 251 | if (!_sharedInstance) { 252 | _sharedInstance = [[<#Class#> alloc] init]; 253 | } 254 | }); 255 | return _sharedInstance; 256 | } 257 | @ss 258 | __strong typeof(weakSelf) strongSelf = weakSelf; 259 | @table 260 | UITableView *<#name#> = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped]; 261 | <#name#>.backgroundColor = [UIColor clearColor]; 262 | <#name#>.contentInset = UIEdgeInsetsMake(0, 0, 0, 0); 263 | <#name#>.separatorStyle = UITableViewCellSeparatorStyleSingleLine; 264 | <#name#>.separatorColor = <#color#>; 265 | <#name#>.delegate = <#table delegate#>; 266 | <#name#>.dataSource = <#table datasource#>; 267 | 268 | [<#name#> registerClass:[<#class name#> class] forCellReuseIdentifier:<#cellId#>]; 269 | @tdd 270 | #pragma mark - UITableViewDataSource 271 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 272 | return <#rows#>; 273 | } 274 | 275 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 276 | 277 | UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:<#CellId#>]; 278 | 279 | return cell; 280 | } 281 | 282 | #pragma mark - UITableViewDelegate 283 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 284 | return <#height#>; 285 | } 286 | 287 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 288 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 289 | 290 | } 291 | 292 | @try 293 | @try { 294 | <#Code that can potentially throw an exception#> 295 | } 296 | @catch (NSException *exception) { 297 | <#Handle an exception thrown in the @try block#> 298 | } 299 | @finally { 300 | <#Code that gets executed whether or not an exception is thrown#> 301 | } 302 | @view 303 | UIView *<#name#> = [UIView new]; 304 | <#name#>.backgroundColor = <#color#>; 305 | @vl 306 | - (void)viewWillLayoutSubviews { 307 | [super viewWillLayoutSubviews]; 308 | CGFloat w = self.view.frame.size.width; 309 | CGFloat h = self.view.frame.size.height; 310 | <# set subview frames #> 311 | } 312 | @while 313 | while (<#condition#>) { 314 | <#statements#> 315 | } 316 | @ws 317 | __weak typeof(self) weakSelf = self; 318 | ^a 319 | [[<#Class#> alloc] init]; 320 | ^s 321 | [NSString stringWithFormat:@"%@",<#value#>]; 322 | 323 | 324 | -------------------------------------------------------------------------------- /Images/demo1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hunk3000/HKSnippet/e990066cf8d34525f6d5cd2de102730b49ac1b66/Images/demo1.gif -------------------------------------------------------------------------------- /Images/demo2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hunk3000/HKSnippet/e990066cf8d34525f6d5cd2de102730b49ac1b66/Images/demo2.gif -------------------------------------------------------------------------------- /Images/setting1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hunk3000/HKSnippet/e990066cf8d34525f6d5cd2de102730b49ac1b66/Images/setting1.png -------------------------------------------------------------------------------- /Images/setting2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hunk3000/HKSnippet/e990066cf8d34525f6d5cd2de102730b49ac1b66/Images/setting2.png -------------------------------------------------------------------------------- /Images/setting3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hunk3000/HKSnippet/e990066cf8d34525f6d5cd2de102730b49ac1b66/Images/setting3.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HKSnippet - Xcode plug-in 2 | 3 | HKSnippet is a xcode plug-in for creating code snippet with triggers strings. 4 | 5 | ![Demo](Images/demo2.gif) 6 | 7 | 8 | ## What Can it do ? 9 | - minimize input for often used code snippet. 10 | - Input code sinppet with paramters. ( NEW! ) 11 | - define your own trigger & snippet. 12 | - export & import your customized snippet to and from config file. 13 | - support undo - redo operation. 14 | 15 | 16 | ## Install 17 | 18 | - The recommanded way to install HKSnippet is to use [Alcatraz](http://alcatraz.io/). Install Alcatraz followed by the instruction, restart your Xcode. You can find `HKSnippet` in the list. Just click the install button. 19 | 20 | - another way is to clone this repo, Build the project and it's done! 21 | 22 | - or you can also download from [this link](http://exibitioncenter-data.stor.sinaapp.com/download%2FHKSnippet.xcplugin.zip) and move the plugin to path 23 | `~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/` 24 | then restart Xcode 25 | 26 | ## Usage 27 | 28 | This plug-in is very easy to use. By typing the trigger string , you will get the corresponding snippet. 29 | 30 | for example , you want to write a property with noatomic and strong, you can type **@pns** 31 | , and you will get 32 | 33 | @property (noatomic, strong) <#type#> *<#name#> 34 | 35 | The belowing part is pre-defined triggers and snippet : 36 | 37 | * Strong 38 | 39 | ``` 40 | @ps @property (strong) type *value; 41 | 42 | @prs @property (strong, readonly) type *value; 43 | 44 | @pns @property (noatomic, strong) type *value; 45 | 46 | @prns @property (nonatomic, strong, readonly) type *value; 47 | 48 | 49 | ``` 50 | 51 | * Weak 52 | 53 | ``` 54 | @pw @property (weak) type *value; 55 | 56 | @prw @property (weak, readonly) type *value; 57 | 58 | @pnw @property (noatomic, weak) type *value; 59 | 60 | @prnw @property (nonatomic, weak, readonly) type *value; 61 | 62 | ``` 63 | 64 | * Copy 65 | 66 | ``` 67 | @pc @property (copy) type *value; 68 | 69 | @prc @property (copy, readonly) type *value; 70 | 71 | @pnc @property (noatomic, copy) type *value; 72 | 73 | @prnc @property (nonatomic, copy, readonly) type *value; 74 | 75 | ``` 76 | 77 | * Assign 78 | 79 | ``` 80 | @pa @property (assign) type *value; 81 | 82 | @pra @property (assign, readonly) type *value; 83 | 84 | @pna @property (noatomic, assign) type *value; 85 | 86 | @prna @property (nonatomic, assign, readonly) type *value; 87 | 88 | ``` 89 | * @ff - General Getter 90 | 91 | ``` 92 | @ff 93 | 94 | - (type *)name { 95 | if(!_name) { 96 | //Init Code 97 | } 98 | return _name; 99 | } 100 | 101 | ``` 102 | 103 | 104 | 105 | * @fv - UIView Getter 106 | 107 | ``` 108 | @fv 109 | 110 | - (UIView *)name { 111 | if(!_name) { 112 | _name = [UIView new]; 113 | _name.backgroundColor = color; 114 | } 115 | return _name; 116 | } 117 | 118 | ``` 119 | 120 | * @fl - UILabel Getter 121 | 122 | ``` 123 | @fl 124 | 125 | - (UILabel *)name { 126 | if(!_name) { 127 | _name = [UILabel new]; 128 | _name.backgroundColor = [UIColor clearColor]; 129 | _name.textAlignment = NSTextAlignmentCenter; 130 | _name.numberOfLines = 0; 131 | _name.textColor = color; 132 | _name.font = font; 133 | _name.text = text; 134 | } 135 | return _name; 136 | } 137 | 138 | ``` 139 | 140 | * @fi - UIImageView Getter 141 | 142 | ``` 143 | @fi 144 | 145 | - (UIImageView *)name { 146 | if(!_name) { 147 | _name = [UIImageView new]; 148 | _name.layer.cornerRadius = radius; 149 | _name.layer.masksToBounds = YES; 150 | _name.backgroundColor = [UIColor clearColor]; 151 | _name.image = image; 152 | } 153 | return _name; 154 | } 155 | 156 | ``` 157 | 158 | * @fb - UIButton Getter 159 | 160 | ``` 161 | @fb 162 | 163 | - (UIButton *)name { 164 | if(!_name) { 165 | _name = [UIButton new]; 166 | _name.layer.cornerRadius = radius; 167 | _name.layer.masksToBounds = YES; 168 | _name.backgroundColor = [UIColor clearColor]; 169 | [_name setTitleColor:title color forState:UIControlStateNormal]; 170 | [_name setTitle: title forState:UIControlStateNormal]; 171 | [_name setImage:image forState:UIControlStateNormal]; 172 | } 173 | return _name; 174 | } 175 | 176 | ``` 177 | 178 | * @ft - UITableView Getter 179 | 180 | ``` 181 | @ft 182 | 183 | - (UITableView *)name { 184 | if(!_name) { 185 | _name = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped]; 186 | _name.backgroundColor = [UIColor clearColor]; 187 | _name.contentInset = UIEdgeInsetsMake(0, 0, 0, 0); 188 | _name.separatorStyle = UITableViewCellSeparatorStyleSingleLine; 189 | _name.separatorColor = color; 190 | _name.delegate = table delegate; 191 | _name.dataSource = table datasource; 192 | 193 | [_name registerClass:[class name class] forCellReuseIdentifier:cellId]; 194 | } 195 | return _name; 196 | } 197 | 198 | ``` 199 | 200 | * Declear 201 | 202 | ``` 203 | @cs static NSString * const name = @\"value\"; 204 | 205 | @log NSLog(@\"format\",data); 206 | 207 | @ws __weak typeof(self) weakSelf = self; 208 | 209 | @ss __strong typeof(weakSelf) strongSelf = weakSelf; 210 | 211 | @mk #pragma mark - section title 212 | 213 | @pmk #pragma mark - Private Method 214 | 215 | @lmk #pragma mark - LifeCycle 216 | 217 | @gmk #pragma mark - Getters & Setters 218 | 219 | ``` 220 | 221 | * @lv - LoadView 222 | 223 | ``` 224 | @lv 225 | 226 | - (void)loadView { 227 | [super loadView]; 228 | } 229 | 230 | ``` 231 | 232 | * @ls - Layout Subviews 233 | 234 | ``` 235 | @ls 236 | 237 | - (void)layoutSubviews { 238 | [super layoutSubviews]; 239 | CGFloat w = self.frame.size.width; 240 | CGFloat h = self.frame.size.height; 241 | //set subview frames 242 | } 243 | 244 | ``` 245 | 246 | * @vl - ViewWillLayoutSubviews 247 | 248 | ``` 249 | @vl 250 | 251 | - (void)viewWillLayoutSubviews { 252 | [super viewWillLayoutSubviews]; 253 | CGFloat w = self.frame.size.width; 254 | CGFloat h = self.frame.size.height; 255 | //set subview frames 256 | } 257 | 258 | ``` 259 | 260 | * @init - Initialization 261 | 262 | ``` 263 | @init 264 | 265 | - (instancetype)init { 266 | self = [super init]; 267 | if (self) { 268 | //statements 269 | } 270 | return self; 271 | } 272 | 273 | ``` 274 | 275 | * @de - De-Init 276 | 277 | ``` 278 | @de 279 | 280 | - (void)dealloc { 281 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 282 | } 283 | 284 | ``` 285 | 286 | * @tdd - UITableView Delegate & Datasource 287 | 288 | ``` 289 | @tdd 290 | 291 | #pragma mark - UITableViewDataSource 292 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 293 | return rows; 294 | } 295 | 296 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 297 | 298 | UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellId]; 299 | 300 | return cell; 301 | } 302 | 303 | #pragma mark - UITableViewDelegate 304 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 305 | return height; 306 | } 307 | 308 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 309 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 310 | 311 | } 312 | 313 | ``` 314 | 315 | ## Advanced 316 | You can even input a snippet with parameters, the format is as below: 317 | 318 | **@triger** 319 | 320 | for example , you want to declare a property getter for a label named "usernameLabel", and the font for the label is [UIFont systemFontOfSize:12.0f], you can type 321 | 322 | usernameLabel,[UIFont systemFontOfSize:12.0f]@fl , and then the magic happens, you will got below code: 323 | 324 | ``` 325 | - (UILabel *)usernameLabel { 326 | if(!_usernameLabel) { 327 | _usernameLabel = [UILabel new]; 328 | _usernameLabel.backgroundColor = [UIColor clearColor]; 329 | _usernameLabel.textAlignment = NSTextAlignmentCenter; 330 | _usernameLabel.numberOfLines = 0; 331 | _usernameLabel.textColor = <#color#>; 332 | _usernameLabel.font = [UIFont systemFontOfSize:12.0f]; 333 | _usernameLabel.text = <#text#>; 334 | } 335 | return _usernameLabel; 336 | } 337 | ``` 338 | 339 | ## Setting 340 | After the install of the plug-in ,you will find the menu in the top "Plugins -> HKSnippet", Click that menu, you can see the belowing pannel where you can define your own triggers or edit the existing triggers & snippets. 341 | 342 | You can alse export your trigger & sniippet to a configuration file which can be use to share with others. 343 | 344 | ![Setting1](Images/setting1.png) 345 | 346 | 347 | You can search snippet with trigger 348 | ![Setting2](Images/setting2.png) 349 | 350 | Edit the snippet 351 | ![Setting3](Images/setting3.png) 352 | 353 | 354 | ## LICENCE 355 | 356 | HKSnippet is available under the MIT license. See the LICENSE file for more info. 357 | -------------------------------------------------------------------------------- /Setting/HKSnippetEditViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HKSnippetEditViewController.h 3 | // HKSnippet 4 | // 5 | // Created by Hunk on 16/2/14. 6 | // Copyright © 2016年 Taobao.com. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void(^save_snippet_block)(NSString *oldTrigger, NSString *newTrigger, NSString *snippet); 12 | 13 | @interface HKSnippetEditViewController : NSWindowController 14 | 15 | @property (nonatomic, weak) IBOutlet NSTextField *triggerTextField; 16 | @property (nonatomic, unsafe_unretained) IBOutlet NSTextView *snippetTextView; 17 | @property (nonatomic, copy) NSString *triggerString; 18 | @property (nonatomic, copy) NSString *snippet; 19 | @property (nonatomic, copy) save_snippet_block saveBlock; 20 | 21 | - (IBAction)saveSnippet:(id)sender; 22 | - (IBAction)cancelSnippet:(id)sender; 23 | 24 | @end -------------------------------------------------------------------------------- /Setting/HKSnippetEditViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HKSnippetEditViewController.m 3 | // HKSnippet 4 | // 5 | // Created by Hunk on 16/2/14. 6 | // Copyright © 2016年 Taobao.com. All rights reserved. 7 | // 8 | 9 | #import "HKSnippetEditViewController.h" 10 | #import "HKSnippetSetting.h" 11 | 12 | typedef void(^Void_Block)(void); 13 | 14 | @interface HKSnippetEditViewController () 15 | 16 | @end 17 | 18 | @implementation HKSnippetEditViewController 19 | 20 | - (void)windowDidLoad { 21 | [super windowDidLoad]; 22 | _snippetTextView.font = [NSFont systemFontOfSize:18.0f]; 23 | } 24 | 25 | - (IBAction)saveSnippet:(id)sender { 26 | // Check trigger conflict with system keyword 27 | for (NSString *sysTrigger in [HKSnippetSetting defaultSetting].systemTriggers) { 28 | if ([sysTrigger containsString:_triggerTextField.stringValue]) { 29 | NSString *msg = [NSString stringWithFormat:@"The trigger %@ has conflict with system keyword %@ , Continue?", 30 | _triggerTextField.stringValue, 31 | sysTrigger]; 32 | __weak typeof(self) weakSelf = self; 33 | [self showConfirmWithMessage:msg 34 | positiveAction:^{ 35 | if (weakSelf.saveBlock) { 36 | weakSelf.saveBlock(_triggerString, 37 | _triggerTextField.stringValue, 38 | _snippetTextView.textStorage.string); 39 | } 40 | [weakSelf close]; 41 | } negativeAction:nil]; 42 | return; 43 | } 44 | } 45 | 46 | if (self.saveBlock) { 47 | self.saveBlock(_triggerString, 48 | _triggerTextField.stringValue, 49 | _snippetTextView.textStorage.string); 50 | } 51 | [self close]; 52 | } 53 | 54 | - (IBAction)cancelSnippet:(id)sender { 55 | [self close]; 56 | } 57 | 58 | #pragma mark - Private Method 59 | - (void)showConfirmWithMessage:(NSString *)message 60 | positiveAction:(Void_Block)postiveAction 61 | negativeAction:(Void_Block)negativeAction { 62 | 63 | NSAlert *alert = [[NSAlert alloc] init]; 64 | [alert addButtonWithTitle:@"YES"]; 65 | [alert addButtonWithTitle:@"NO"]; 66 | [alert setMessageText:@"Warnning"]; 67 | [alert setInformativeText:message]; 68 | [alert setAlertStyle:NSWarningAlertStyle]; 69 | [alert beginSheetModalForWindow:self.window 70 | completionHandler:^(NSModalResponse returnCode) { 71 | if (returnCode == NSAlertFirstButtonReturn) { 72 | if (postiveAction) { 73 | postiveAction(); 74 | } 75 | } 76 | if (returnCode == NSAlertSecondButtonReturn) { 77 | if (negativeAction) { 78 | negativeAction(); 79 | } 80 | } 81 | }]; 82 | } 83 | 84 | #pragma mark - Getters & Setters 85 | - (void)setTriggerString:(NSString *)triggerString { 86 | _triggerString = triggerString; 87 | _triggerTextField.stringValue = _triggerString; 88 | } 89 | 90 | - (void)setSnippet:(NSString *)snippet { 91 | _snippet = snippet; 92 | NSAttributedString* attr = [[NSAttributedString alloc] initWithString:_snippet]; 93 | [_snippetTextView.textStorage appendAttributedString:attr]; 94 | } 95 | 96 | @end -------------------------------------------------------------------------------- /Setting/HKSnippetEditViewController.xib: -------------------------------------------------------------------------------- 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 | 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 | 93 | 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 | -------------------------------------------------------------------------------- /Setting/HKSnippetSetting.h: -------------------------------------------------------------------------------- 1 | // 2 | // HKSnippetSetting.h 3 | // HKSnippet 4 | // 5 | // Created by Hunk on 16/2/4. 6 | // Copyright © 2016年 Taobao.com. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern NSString *const kHKSnippetsKey; 12 | 13 | @interface HKSnippetSetting : NSObject 14 | 15 | @property (nonatomic, assign) BOOL enabled; 16 | @property (nonatomic, strong) NSMutableDictionary *snippets; 17 | @property (nonatomic, copy) NSArray *systemTriggers; 18 | 19 | + (HKSnippetSetting *)defaultSetting; 20 | - (void)sychronizeSetting; 21 | - (void)resetToDefaultSetting; 22 | 23 | @end -------------------------------------------------------------------------------- /Setting/HKSnippetSetting.m: -------------------------------------------------------------------------------- 1 | // 2 | // HKSnippetSetting.m 3 | // HKSnippet 4 | // 5 | // Created by Hunk on 16/2/4. 6 | // Copyright © 2016年 Taobao.com. All rights reserved. 7 | // 8 | 9 | #import "HKSnippetSetting.h" 10 | 11 | NSString * const kHKSnippetsKey = @"snippets"; 12 | NSString * const kHKSnippetEnabled = @"enabled"; 13 | 14 | @implementation HKSnippetSetting 15 | 16 | - (instancetype)init { 17 | self = [super init]; 18 | if (self) { 19 | _systemTriggers = @[@"@autoreleasepool", 20 | @"@catch", 21 | @"@class", 22 | @"@compatibility_alias", 23 | @"@defs", 24 | @"@dynamic", 25 | @"@encode", 26 | @"@end", 27 | @"@finally", 28 | @"@import", 29 | @"@interface", 30 | @"@implementation", 31 | @"@optional", 32 | @"@package", 33 | @"@private", 34 | @"@property", 35 | @"@protected", 36 | @"@protocol", 37 | @"@public", 38 | @"@required", 39 | @"@selector", 40 | @"@synchronized", 41 | @"@synthesize", 42 | @"@throw" 43 | ]; 44 | _snippets = [NSMutableDictionary dictionaryWithDictionary:[self defaultConfig]]; 45 | } 46 | return self; 47 | } 48 | 49 | + (HKSnippetSetting *)defaultSetting { 50 | static HKSnippetSetting *defaultSetting; 51 | static dispatch_once_t once; 52 | dispatch_once(&once, ^ { 53 | defaultSetting = [[[self class] alloc] init]; 54 | 55 | NSDictionary *defaults = @{kHKSnippetEnabled: @YES, 56 | kHKSnippetsKey : defaultSetting.snippets ?: @{}}; 57 | [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; 58 | }); 59 | return defaultSetting; 60 | } 61 | 62 | - (BOOL)enabled { 63 | return [[NSUserDefaults standardUserDefaults] boolForKey:kHKSnippetEnabled]; 64 | } 65 | 66 | - (void)setEnabled:(BOOL)enabled { 67 | [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:kHKSnippetEnabled]; 68 | [[NSUserDefaults standardUserDefaults] synchronize]; 69 | } 70 | 71 | - (void)sychronizeSetting { 72 | NSMutableDictionary *snippets = [[self class] defaultSetting].snippets; 73 | [[NSUserDefaults standardUserDefaults] setObject:snippets forKey:kHKSnippetsKey]; 74 | [[NSUserDefaults standardUserDefaults] synchronize]; 75 | } 76 | 77 | - (void)resetToDefaultSetting { 78 | [[NSUserDefaults standardUserDefaults] removeObjectForKey:kHKSnippetsKey]; 79 | [[NSUserDefaults standardUserDefaults] setObject:@{} forKey:kHKSnippetsKey]; 80 | [[NSUserDefaults standardUserDefaults] synchronize]; 81 | 82 | _snippets = nil; 83 | _snippets = [NSMutableDictionary dictionaryWithDictionary:[self defaultConfig]]; 84 | [[NSUserDefaults standardUserDefaults] setObject:[self defaultConfig] forKey:kHKSnippetsKey]; 85 | [self setEnabled:YES]; 86 | } 87 | 88 | - (NSDictionary *)defaultConfig { 89 | NSDictionary *config = [[NSUserDefaults standardUserDefaults] objectForKey:kHKSnippetsKey]; 90 | if (0 < config.count) { 91 | return config; 92 | } 93 | 94 | NSString *selfPath = @"~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/HKSnippet.xcplugin"; 95 | NSBundle *selfBundle = [NSBundle bundleWithPath:[selfPath stringByExpandingTildeInPath]]; 96 | NSString *default_snippet_file = [selfBundle pathForResource:@"default_snippets" 97 | ofType:@"plist"]; 98 | config = [NSDictionary dictionaryWithContentsOfFile:default_snippet_file]; 99 | return config; 100 | } 101 | 102 | @end -------------------------------------------------------------------------------- /Setting/HKSnippetSettingController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HKSnippetSettingController.h 3 | // HKSnippet 4 | // 5 | // Created by Hunk on 16/2/4. 6 | // Copyright © 2016年 Taobao.com. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface HKSnippetSettingController : NSWindowController 13 | 14 | @end -------------------------------------------------------------------------------- /Setting/HKSnippetSettingController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HKSnippetSettingController.m 3 | // HKSnippet 4 | // 5 | // Created by Hunk on 16/2/4. 6 | // Copyright © 2016年 Taobao.com. All rights reserved. 7 | // 8 | 9 | #import "HKSnippetSettingController.h" 10 | #import "HKSnippetSetting.h" 11 | #import "HKSnippetEditViewController.h" 12 | 13 | @interface HKSnippetSettingController () 14 | 15 | @property (nonatomic, weak) IBOutlet NSButton *btnEnabled; 16 | @property (nonatomic, weak) IBOutlet NSTableView *tableView; 17 | @property (nonatomic, weak) IBOutlet NSSearchField *searchField; 18 | 19 | @property (nonatomic, strong) HKSnippetEditViewController *snippetEditViewController; 20 | @property (nonatomic, strong) NSMutableArray *listOfKeys; 21 | 22 | @end 23 | 24 | @implementation HKSnippetSettingController 25 | 26 | #pragma mark - LifeCycle 27 | - (void)awakeFromNib { 28 | [super awakeFromNib]; 29 | _listOfKeys = [NSMutableArray array]; 30 | [self reloadDataWithKeyFilter:nil]; 31 | } 32 | 33 | - (void)windowDidLoad { 34 | [super windowDidLoad]; 35 | self.tableView.delegate = self; 36 | self.tableView.dataSource = self; 37 | self.btnEnabled.state = (NSCellStateValue)[[HKSnippetSetting defaultSetting] enabled]; 38 | } 39 | 40 | - (void)dealloc { 41 | _tableView.delegate = nil; 42 | _tableView.dataSource = nil; 43 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 44 | } 45 | 46 | #pragma mark - UI Action 47 | - (IBAction)btnEnabledPressed:(NSButton *)sender { 48 | [[HKSnippetSetting defaultSetting] setEnabled:sender.state]; 49 | } 50 | 51 | - (IBAction)btnResetPressed:(NSButton *)sender { 52 | [[HKSnippetSetting defaultSetting] resetToDefaultSetting]; 53 | self.btnEnabled.state = (NSCellStateValue)[[HKSnippetSetting defaultSetting] enabled]; 54 | [self reloadDataWithKeyFilter:nil]; 55 | } 56 | 57 | - (IBAction)addNewSnippet:(id)sender { 58 | [self showEditViewControllerWithTriggerString:@"" 59 | snippet:@"" 60 | sender:sender]; 61 | } 62 | 63 | - (IBAction)removeSelectedSnippet:(id)sender { 64 | NSInteger row = self.tableView.selectedRow; 65 | if (row == -1) { 66 | [self showErrorAlertWithMessage:@"You didn't select any row."]; 67 | return; 68 | } 69 | 70 | __weak typeof(self) weakSelf = self; 71 | NSAlert *alert = [[NSAlert alloc] init]; 72 | [alert addButtonWithTitle:@"OK"]; 73 | [alert addButtonWithTitle:@"Cancel"]; 74 | [alert setMessageText:@"Delete the snippet?"]; 75 | [alert setInformativeText:@"Deleted snippet cannot be restored."]; 76 | [alert setAlertStyle:NSWarningAlertStyle]; 77 | [alert beginSheetModalForWindow:self.window 78 | completionHandler:^(NSModalResponse returnCode) { 79 | if (returnCode == NSAlertFirstButtonReturn) { 80 | NSString *triggerString = _listOfKeys[row]; 81 | [[HKSnippetSetting defaultSetting].snippets removeObjectForKey:triggerString]; 82 | [[HKSnippetSetting defaultSetting] sychronizeSetting]; 83 | [weakSelf reloadDataWithKeyFilter:nil]; 84 | } 85 | }]; 86 | } 87 | 88 | - (IBAction)editSnippet:(id)sender { 89 | NSInteger row = self.tableView.selectedRow; 90 | if (row == -1) { 91 | [self showErrorAlertWithMessage:@"You didn't select any row."]; 92 | return; 93 | } 94 | 95 | NSString *triggerString = _listOfKeys[row]; 96 | NSString *snippetString = [HKSnippetSetting defaultSetting].snippets[triggerString]; 97 | [self showEditViewControllerWithTriggerString:triggerString 98 | snippet:snippetString 99 | sender:sender]; 100 | } 101 | 102 | - (IBAction)checkTriggers:(id)sender { 103 | NSMutableArray *conflictMessages = [NSMutableArray array]; 104 | 105 | // Check trigger conflict with system keyword 106 | for (NSString *trigger in _listOfKeys) { 107 | for (NSString *sysTrigger in [HKSnippetSetting defaultSetting].systemTriggers) { 108 | if ([sysTrigger containsString:trigger]) { 109 | [conflictMessages addObject:[NSString stringWithFormat:@"%@ conflict with %@", trigger, sysTrigger]]; 110 | } 111 | } 112 | } 113 | [self showErrorAlertWithMessage:[conflictMessages description]]; 114 | } 115 | - (IBAction)SearchTextFieldAction:(NSSearchField *)sender { 116 | //NSLog(@"SearchTextFieldAction %@",sender); 117 | if (sender.stringValue.length == 0) { 118 | [self reloadDataWithKeyFilter:nil]; 119 | } else { 120 | [self reloadDataWithKeyFilter:sender.stringValue]; 121 | } 122 | } 123 | 124 | #pragma mark - Private Method 125 | - (void)reloadDataWithKeyFilter:(NSString *)filter { 126 | NSArray *allKeys = [[HKSnippetSetting defaultSetting].snippets allKeys]; 127 | NSArray *sortedKeys = [allKeys sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { 128 | return [obj1 compare:obj2]; 129 | }]; 130 | [_listOfKeys removeAllObjects]; 131 | if (0 < filter.length) { 132 | for (NSString *key in sortedKeys) { 133 | if ([key containsString:filter]) { 134 | [_listOfKeys addObject:key]; 135 | } 136 | } 137 | } else { 138 | [_listOfKeys addObjectsFromArray:sortedKeys]; 139 | } 140 | 141 | [self.tableView reloadData]; 142 | } 143 | 144 | - (void)showEditViewControllerWithTriggerString:(NSString *)triggerString 145 | snippet:(NSString *)snippet 146 | sender:(id)sender { 147 | _snippetEditViewController = [[HKSnippetEditViewController alloc] initWithWindowNibName:@"HKSnippetEditViewController"]; 148 | 149 | NSRect windowFrame = [[self window] frame]; 150 | NSRect prefsFrame = [[_snippetEditViewController window] frame]; 151 | prefsFrame.origin = NSMakePoint(windowFrame.origin.x + (windowFrame.size.width - prefsFrame.size.width) / 2.0, 152 | NSMaxY(windowFrame) - NSHeight(prefsFrame) - 20.0); 153 | [[_snippetEditViewController window] setFrame:prefsFrame 154 | display:NO]; 155 | self.snippetEditViewController.triggerString = triggerString; 156 | self.snippetEditViewController.snippet = snippet; 157 | 158 | __weak typeof(self) weakSelf = self; 159 | self.snippetEditViewController.saveBlock = ^(NSString *oldTrigger, NSString *newTrigger, NSString *snippet) { 160 | // Check trigger length 161 | if (newTrigger.length < 2) { 162 | [weakSelf showErrorAlertWithMessage:@"Trigger is too short, 2 characters at least."]; 163 | return; 164 | } 165 | // trigger string changed, remove old trigger 166 | if (![oldTrigger isEqualToString:newTrigger]) { 167 | [[HKSnippetSetting defaultSetting].snippets removeObjectForKey:oldTrigger]; 168 | } 169 | 170 | [HKSnippetSetting defaultSetting].snippets[newTrigger] = snippet; 171 | [[HKSnippetSetting defaultSetting] sychronizeSetting]; 172 | [weakSelf reloadDataWithKeyFilter:nil]; 173 | }; 174 | [self.snippetEditViewController showWindow:sender]; 175 | } 176 | 177 | - (void)showErrorAlertWithMessage:(NSString *)message { 178 | NSAlert *alert = [[NSAlert alloc] init]; 179 | [alert addButtonWithTitle:@"OK"]; 180 | [alert setMessageText:@"Error"]; 181 | [alert setInformativeText:message]; 182 | [alert setAlertStyle:NSWarningAlertStyle]; 183 | [alert beginSheetModalForWindow:self.window 184 | completionHandler:^(NSModalResponse returnCode) { 185 | if (returnCode == NSAlertFirstButtonReturn) { 186 | } 187 | }]; 188 | } 189 | 190 | - (IBAction)exportSnippets:(id)sender { 191 | NSSavePanel *panel = [NSSavePanel savePanel]; 192 | [panel setNameFieldStringValue:@"Untitle.plist"]; 193 | [panel setMessage:@"Choose the path to save the config file."]; 194 | [panel setAllowsOtherFileTypes:YES]; 195 | [panel setAllowedFileTypes:@[@"plist"]]; 196 | [panel setExtensionHidden:YES]; 197 | [panel setCanCreateDirectories:YES]; 198 | [panel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result){ 199 | if (result == NSFileHandlingPanelOKButton) 200 | { 201 | NSString *path = [[panel URL] path]; 202 | [[HKSnippetSetting defaultSetting].snippets writeToFile:path 203 | atomically:YES]; 204 | } 205 | }]; 206 | } 207 | 208 | - (IBAction)importSnippets:(id)sender { 209 | NSOpenPanel *openPanel = [NSOpenPanel openPanel]; 210 | [openPanel setTitle:@"Choose a config file"]; 211 | [openPanel setCanChooseDirectories:NO]; 212 | 213 | if([openPanel runModal] == NSModalResponseOK) { 214 | NSURL *theFileURL = [openPanel URL]; 215 | NSDictionary *dic = [NSDictionary dictionaryWithContentsOfURL:theFileURL]; 216 | [HKSnippetSetting defaultSetting].snippets = [NSMutableDictionary dictionaryWithDictionary:dic]; 217 | [[HKSnippetSetting defaultSetting] sychronizeSetting]; 218 | [self reloadDataWithKeyFilter:nil]; 219 | } 220 | } 221 | 222 | #pragma mark - NSTableView Datasource 223 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { 224 | return _listOfKeys.count; 225 | } 226 | 227 | - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { 228 | 229 | NSString *key = _listOfKeys[row]; 230 | NSString *value = [HKSnippetSetting defaultSetting].snippets[key]; 231 | 232 | if(tableColumn == tableView.tableColumns[0]) { 233 | tableColumn.title = @"Trigger"; 234 | return key; 235 | } 236 | if (tableColumn == tableView.tableColumns[1]) { 237 | tableColumn.title = @"Snippet"; 238 | return value; 239 | } 240 | 241 | return nil; 242 | } 243 | 244 | #pragma mark - NSTableView Delegate 245 | 246 | - (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row { 247 | NSString *value = [HKSnippetSetting defaultSetting].snippets[_listOfKeys[row]]; 248 | NSArray *lines = [value componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; 249 | if (lines.count == 0 || lines.count == 1) { 250 | return 30; 251 | } 252 | return lines.count * 18; 253 | } 254 | 255 | @end -------------------------------------------------------------------------------- /Setting/HKSnippetSettingController.xib: -------------------------------------------------------------------------------- 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 | 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 | 89 | 93 | 94 | 95 | 96 | 97 | 98 | 111 | 124 | 137 | 150 | 163 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 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 | --------------------------------------------------------------------------------