├── .gitignore
├── AutoHeaderImportExtension
├── AutoHeaderImportExtension.entitlements
├── Info.plist
├── SourceEditorCommand.h
├── SourceEditorCommand.m
├── SourceEditorExtension.h
└── SourceEditorExtension.m
├── AutoImportExtension.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
├── AutoImportExtension
├── AppDelegate.h
├── AppDelegate.m
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Base.lproj
│ └── Main.storyboard
├── Info.plist
├── ViewController.h
├── ViewController.m
└── main.m
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | *.DS_Store
6 |
7 | ## Build generated
8 | build/
9 | DerivedData/
10 |
11 | ## Various settings
12 | *.pbxuser
13 | !default.pbxuser
14 | *.mode1v3
15 | !default.mode1v3
16 | *.mode2v3
17 | !default.mode2v3
18 | *.perspectivev3
19 | !default.perspectivev3
20 | xcuserdata/
21 |
22 | ## Other
23 | *.moved-aside
24 | *.xcuserstate
25 |
26 | ## Obj-C/Swift specific
27 | *.hmap
28 | *.ipa
29 | *.dSYM.zip
30 | *.dSYM
31 |
32 | # CocoaPods
33 | #
34 | # We recommend against adding the Pods directory to your .gitignore. However
35 | # you should judge for yourself, the pros and cons are mentioned at:
36 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
37 | #
38 | # Pods/
39 |
40 | # Carthage
41 | #
42 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
43 | # Carthage/Checkouts
44 |
45 | Carthage/Build
46 |
47 | # fastlane
48 | #
49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
50 | # screenshots whenever they are needed.
51 | # For more information about the recommended setup visit:
52 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
53 |
54 | fastlane/report.xml
55 | fastlane/screenshots
56 |
57 | #Code Injection
58 | #
59 | # After new code Injection tools there's a generated folder /iOSInjectionProject
60 | # https://github.com/johnno1962/injectionforxcode
61 |
62 | iOSInjectionProject/
63 |
--------------------------------------------------------------------------------
/AutoHeaderImportExtension/AutoHeaderImportExtension.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.assets.movies.read-only
8 |
9 | com.apple.security.assets.music.read-only
10 |
11 | com.apple.security.assets.pictures.read-only
12 |
13 | com.apple.security.device.bluetooth
14 |
15 | com.apple.security.device.camera
16 |
17 | com.apple.security.device.microphone
18 |
19 | com.apple.security.device.usb
20 |
21 | com.apple.security.files.downloads.read-only
22 |
23 | com.apple.security.files.user-selected.read-only
24 |
25 | com.apple.security.network.client
26 |
27 | com.apple.security.network.server
28 |
29 | com.apple.security.personal-information.addressbook
30 |
31 | com.apple.security.personal-information.calendars
32 |
33 | com.apple.security.personal-information.location
34 |
35 | com.apple.security.print
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/AutoHeaderImportExtension/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | AutoHeaderImportExtension
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | XPC!
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSExtension
26 |
27 | NSExtensionAttributes
28 |
29 | XCSourceEditorCommandDefinitions
30 |
31 |
32 | XCSourceEditorCommandClassName
33 | SourceEditorCommand
34 | XCSourceEditorCommandIdentifier
35 | $(PRODUCT_BUNDLE_IDENTIFIER).SourceEditorCommand
36 | XCSourceEditorCommandName
37 | Source Editor Command
38 |
39 |
40 | XCSourceEditorExtensionPrincipalClass
41 | SourceEditorExtension
42 |
43 | NSExtensionPointIdentifier
44 | com.apple.dt.Xcode.extension.source-editor
45 |
46 | NSHumanReadableCopyright
47 | Copyright © 2017年 WillkYang. All rights reserved.
48 |
49 |
50 |
--------------------------------------------------------------------------------
/AutoHeaderImportExtension/SourceEditorCommand.h:
--------------------------------------------------------------------------------
1 | //
2 | // SourceEditorCommand.h
3 | // AutoHeaderImportExtension
4 | //
5 | // Created by yate1996 on 2017/1/16.
6 | // Copyright © 2017年 WillkYang. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface SourceEditorCommand : NSObject
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/AutoHeaderImportExtension/SourceEditorCommand.m:
--------------------------------------------------------------------------------
1 | //
2 | // SourceEditorCommand.m
3 | // AutoHeaderImportExtension
4 | //
5 | // Created by yate1996 on 2017/1/16.
6 | // Copyright © 2017年 WillkYang. All rights reserved.
7 | //
8 |
9 | #import "SourceEditorCommand.h"
10 |
11 | @implementation SourceEditorCommand
12 |
13 | //TODO 检测头文件是否为父类类型~现有系统API无法检测~
14 | //暂不支持库文件,不支持协议检测
15 | //暂时忽略文件名含有+号的的情况,认为此类文件属于分类,所以没有进行过滤
16 | //如果后期系统支持了项目文件空间的文件访问和GUI支持,则插件可以发挥更大作用
17 |
18 | //已实现功能
19 | //1.删除无用的类头文件,要求类名和文件名一致
20 | //2.删除重复导入的头文件,只保留一个
21 | //3.忽略文件扩展,即包含+号的文件
22 |
23 | - (void)performCommandWithInvocation:(XCSourceEditorCommandInvocation *)invocation completionHandler:(void (^)(NSError * _Nullable nilOrError))completionHandler
24 | {
25 |
26 | //headerDict存放文本中所有的头文件
27 | NSMutableDictionary *headerDict = [NSMutableDictionary dictionary];
28 | //willCheckDict存放将要删除的头文件
29 | NSMutableDictionary *willCheckDict = [NSMutableDictionary dictionary];
30 |
31 | //遍历编辑器每一行
32 | for (int idx = 0; idx < invocation.buffer.lines.count; idx++) {
33 |
34 | NSString *lineCode = invocation.buffer.lines[idx];
35 |
36 | //若willCheckDict文件不为空,则进行是否使用了该头文件的判断
37 | if (willCheckDict.count > 0) {
38 | [willCheckDict enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull key, NSString * _Nonnull checkString, BOOL * _Nonnull stop) {
39 | if ([lineCode containsString:checkString]) {
40 | if (![lineCode containsString:@"#import"]) {
41 | if ([headerDict[checkString] isEqualToNumber: @1]) {
42 | //若使用了该头文件,则从willCheckDict字典中提出该项
43 | [willCheckDict removeObjectForKey:key];
44 | //同时设置该头文件已经检查过,若后续仍出现该头文件,则可以进行删除
45 | headerDict[checkString] = @0;
46 | }
47 | }
48 | }
49 | }];
50 |
51 | }
52 |
53 | //检测代码是否含有#import为头文件标志;+号我们认为是类扩展的标志
54 | if ([lineCode containsString:@"#import"] && ![lineCode containsString:@"+"]) {
55 | //解析获取类名
56 | NSRange range1 = [lineCode rangeOfString:@"\""];
57 | NSRange range2 = [lineCode rangeOfString:@"\"" options:NSBackwardsSearch];
58 | NSRange zeroRange = NSMakeRange(0, 0);
59 |
60 | if (!(NSEqualRanges(range1, zeroRange) || NSEqualRanges(range2, zeroRange))) {
61 | NSRange findRange = NSMakeRange(range1.location + 1, range2.location - range1.location - 3);
62 | NSString *classString = [lineCode substringWithRange:findRange];
63 | willCheckDict[@(idx)] = classString;
64 | headerDict[classString] = @1;
65 | }
66 | }
67 | }
68 |
69 |
70 | //取出需要删除的行
71 | NSMutableIndexSet *index = [NSMutableIndexSet indexSet];
72 | [willCheckDict.allKeys enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
73 | [index addIndex:obj.integerValue];
74 |
75 | }];
76 |
77 | //删除不符合条件的行
78 | [invocation.buffer.lines removeObjectsAtIndexes:index];
79 |
80 | //通知系统完成
81 | completionHandler(nil);
82 | }
83 |
84 | @end
85 |
--------------------------------------------------------------------------------
/AutoHeaderImportExtension/SourceEditorExtension.h:
--------------------------------------------------------------------------------
1 | //
2 | // SourceEditorExtension.h
3 | // AutoHeaderImportExtension
4 | //
5 | // Created by yate1996 on 2017/1/16.
6 | // Copyright © 2017年 WillkYang. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface SourceEditorExtension : NSObject
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/AutoHeaderImportExtension/SourceEditorExtension.m:
--------------------------------------------------------------------------------
1 | //
2 | // SourceEditorExtension.m
3 | // AutoHeaderImportExtension
4 | //
5 | // Created by yate1996 on 2017/1/16.
6 | // Copyright © 2017年 WillkYang. All rights reserved.
7 | //
8 |
9 | #import "SourceEditorExtension.h"
10 |
11 | @implementation SourceEditorExtension
12 |
13 | /*
14 | - (void)extensionDidFinishLaunching
15 | {
16 | // If your extension needs to do any work at launch, implement this optional method.
17 | }
18 | */
19 |
20 | /*
21 | - (NSArray *> *)commandDefinitions
22 | {
23 | // If your extension needs to return a collection of command definitions that differs from those in its Info.plist, implement this optional property getter.
24 | return @[];
25 | }
26 | */
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/AutoImportExtension.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 841D7BE81E2D009F005E9ECA /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 841D7BE71E2D009F005E9ECA /* AppDelegate.m */; };
11 | 841D7BEB1E2D009F005E9ECA /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 841D7BEA1E2D009F005E9ECA /* main.m */; };
12 | 841D7BEE1E2D009F005E9ECA /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 841D7BED1E2D009F005E9ECA /* ViewController.m */; };
13 | 841D7BF01E2D009F005E9ECA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 841D7BEF1E2D009F005E9ECA /* Assets.xcassets */; };
14 | 841D7BF31E2D009F005E9ECA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 841D7BF11E2D009F005E9ECA /* Main.storyboard */; };
15 | 841D7C011E2D00C6005E9ECA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 841D7C001E2D00C6005E9ECA /* Cocoa.framework */; };
16 | 841D7C071E2D00C6005E9ECA /* SourceEditorExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 841D7C061E2D00C6005E9ECA /* SourceEditorExtension.m */; };
17 | 841D7C0A1E2D00C6005E9ECA /* SourceEditorCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 841D7C091E2D00C6005E9ECA /* SourceEditorCommand.m */; };
18 | 841D7C0E1E2D00C6005E9ECA /* AutoHeaderImportExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 841D7BFE1E2D00C6005E9ECA /* AutoHeaderImportExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXContainerItemProxy section */
22 | 841D7C0C1E2D00C6005E9ECA /* PBXContainerItemProxy */ = {
23 | isa = PBXContainerItemProxy;
24 | containerPortal = 841D7BDB1E2D009F005E9ECA /* Project object */;
25 | proxyType = 1;
26 | remoteGlobalIDString = 841D7BFD1E2D00C6005E9ECA;
27 | remoteInfo = AutoHeaderImportExtension;
28 | };
29 | /* End PBXContainerItemProxy section */
30 |
31 | /* Begin PBXCopyFilesBuildPhase section */
32 | 841D7C121E2D00C6005E9ECA /* Embed App Extensions */ = {
33 | isa = PBXCopyFilesBuildPhase;
34 | buildActionMask = 2147483647;
35 | dstPath = "";
36 | dstSubfolderSpec = 13;
37 | files = (
38 | 841D7C0E1E2D00C6005E9ECA /* AutoHeaderImportExtension.appex in Embed App Extensions */,
39 | );
40 | name = "Embed App Extensions";
41 | runOnlyForDeploymentPostprocessing = 0;
42 | };
43 | /* End PBXCopyFilesBuildPhase section */
44 |
45 | /* Begin PBXFileReference section */
46 | 841D7BE31E2D009F005E9ECA /* AutoImportExtension.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AutoImportExtension.app; sourceTree = BUILT_PRODUCTS_DIR; };
47 | 841D7BE61E2D009F005E9ECA /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
48 | 841D7BE71E2D009F005E9ECA /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
49 | 841D7BEA1E2D009F005E9ECA /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
50 | 841D7BEC1E2D009F005E9ECA /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
51 | 841D7BED1E2D009F005E9ECA /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
52 | 841D7BEF1E2D009F005E9ECA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
53 | 841D7BF21E2D009F005E9ECA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
54 | 841D7BF41E2D009F005E9ECA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
55 | 841D7BFE1E2D00C6005E9ECA /* AutoHeaderImportExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = AutoHeaderImportExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
56 | 841D7C001E2D00C6005E9ECA /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
57 | 841D7C041E2D00C6005E9ECA /* AutoHeaderImportExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AutoHeaderImportExtension.entitlements; sourceTree = ""; };
58 | 841D7C051E2D00C6005E9ECA /* SourceEditorExtension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SourceEditorExtension.h; sourceTree = ""; };
59 | 841D7C061E2D00C6005E9ECA /* SourceEditorExtension.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SourceEditorExtension.m; sourceTree = ""; };
60 | 841D7C081E2D00C6005E9ECA /* SourceEditorCommand.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SourceEditorCommand.h; sourceTree = ""; };
61 | 841D7C091E2D00C6005E9ECA /* SourceEditorCommand.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SourceEditorCommand.m; sourceTree = ""; };
62 | 841D7C0B1E2D00C6005E9ECA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
63 | /* End PBXFileReference section */
64 |
65 | /* Begin PBXFrameworksBuildPhase section */
66 | 841D7BE01E2D009F005E9ECA /* Frameworks */ = {
67 | isa = PBXFrameworksBuildPhase;
68 | buildActionMask = 2147483647;
69 | files = (
70 | );
71 | runOnlyForDeploymentPostprocessing = 0;
72 | };
73 | 841D7BFB1E2D00C6005E9ECA /* Frameworks */ = {
74 | isa = PBXFrameworksBuildPhase;
75 | buildActionMask = 2147483647;
76 | files = (
77 | 841D7C011E2D00C6005E9ECA /* Cocoa.framework in Frameworks */,
78 | );
79 | runOnlyForDeploymentPostprocessing = 0;
80 | };
81 | /* End PBXFrameworksBuildPhase section */
82 |
83 | /* Begin PBXGroup section */
84 | 841D7BDA1E2D009F005E9ECA = {
85 | isa = PBXGroup;
86 | children = (
87 | 841D7BE51E2D009F005E9ECA /* AutoImportExtension */,
88 | 841D7C021E2D00C6005E9ECA /* AutoHeaderImportExtension */,
89 | 841D7BFF1E2D00C6005E9ECA /* Frameworks */,
90 | 841D7BE41E2D009F005E9ECA /* Products */,
91 | );
92 | sourceTree = "";
93 | };
94 | 841D7BE41E2D009F005E9ECA /* Products */ = {
95 | isa = PBXGroup;
96 | children = (
97 | 841D7BE31E2D009F005E9ECA /* AutoImportExtension.app */,
98 | 841D7BFE1E2D00C6005E9ECA /* AutoHeaderImportExtension.appex */,
99 | );
100 | name = Products;
101 | sourceTree = "";
102 | };
103 | 841D7BE51E2D009F005E9ECA /* AutoImportExtension */ = {
104 | isa = PBXGroup;
105 | children = (
106 | 841D7BE61E2D009F005E9ECA /* AppDelegate.h */,
107 | 841D7BE71E2D009F005E9ECA /* AppDelegate.m */,
108 | 841D7BEC1E2D009F005E9ECA /* ViewController.h */,
109 | 841D7BED1E2D009F005E9ECA /* ViewController.m */,
110 | 841D7BEF1E2D009F005E9ECA /* Assets.xcassets */,
111 | 841D7BF11E2D009F005E9ECA /* Main.storyboard */,
112 | 841D7BF41E2D009F005E9ECA /* Info.plist */,
113 | 841D7BE91E2D009F005E9ECA /* Supporting Files */,
114 | );
115 | path = AutoImportExtension;
116 | sourceTree = "";
117 | };
118 | 841D7BE91E2D009F005E9ECA /* Supporting Files */ = {
119 | isa = PBXGroup;
120 | children = (
121 | 841D7BEA1E2D009F005E9ECA /* main.m */,
122 | );
123 | name = "Supporting Files";
124 | sourceTree = "";
125 | };
126 | 841D7BFF1E2D00C6005E9ECA /* Frameworks */ = {
127 | isa = PBXGroup;
128 | children = (
129 | 841D7C001E2D00C6005E9ECA /* Cocoa.framework */,
130 | );
131 | name = Frameworks;
132 | sourceTree = "";
133 | };
134 | 841D7C021E2D00C6005E9ECA /* AutoHeaderImportExtension */ = {
135 | isa = PBXGroup;
136 | children = (
137 | 841D7C051E2D00C6005E9ECA /* SourceEditorExtension.h */,
138 | 841D7C061E2D00C6005E9ECA /* SourceEditorExtension.m */,
139 | 841D7C081E2D00C6005E9ECA /* SourceEditorCommand.h */,
140 | 841D7C091E2D00C6005E9ECA /* SourceEditorCommand.m */,
141 | 841D7C0B1E2D00C6005E9ECA /* Info.plist */,
142 | 841D7C031E2D00C6005E9ECA /* Supporting Files */,
143 | );
144 | path = AutoHeaderImportExtension;
145 | sourceTree = "";
146 | };
147 | 841D7C031E2D00C6005E9ECA /* Supporting Files */ = {
148 | isa = PBXGroup;
149 | children = (
150 | 841D7C041E2D00C6005E9ECA /* AutoHeaderImportExtension.entitlements */,
151 | );
152 | name = "Supporting Files";
153 | sourceTree = "";
154 | };
155 | /* End PBXGroup section */
156 |
157 | /* Begin PBXNativeTarget section */
158 | 841D7BE21E2D009F005E9ECA /* AutoImportExtension */ = {
159 | isa = PBXNativeTarget;
160 | buildConfigurationList = 841D7BF71E2D009F005E9ECA /* Build configuration list for PBXNativeTarget "AutoImportExtension" */;
161 | buildPhases = (
162 | 841D7BDF1E2D009F005E9ECA /* Sources */,
163 | 841D7BE01E2D009F005E9ECA /* Frameworks */,
164 | 841D7BE11E2D009F005E9ECA /* Resources */,
165 | 841D7C121E2D00C6005E9ECA /* Embed App Extensions */,
166 | );
167 | buildRules = (
168 | );
169 | dependencies = (
170 | 841D7C0D1E2D00C6005E9ECA /* PBXTargetDependency */,
171 | );
172 | name = AutoImportExtension;
173 | productName = AutoImportExtension;
174 | productReference = 841D7BE31E2D009F005E9ECA /* AutoImportExtension.app */;
175 | productType = "com.apple.product-type.application";
176 | };
177 | 841D7BFD1E2D00C6005E9ECA /* AutoHeaderImportExtension */ = {
178 | isa = PBXNativeTarget;
179 | buildConfigurationList = 841D7C0F1E2D00C6005E9ECA /* Build configuration list for PBXNativeTarget "AutoHeaderImportExtension" */;
180 | buildPhases = (
181 | 841D7BFA1E2D00C6005E9ECA /* Sources */,
182 | 841D7BFB1E2D00C6005E9ECA /* Frameworks */,
183 | 841D7BFC1E2D00C6005E9ECA /* Resources */,
184 | );
185 | buildRules = (
186 | );
187 | dependencies = (
188 | );
189 | name = AutoHeaderImportExtension;
190 | productName = AutoHeaderImportExtension;
191 | productReference = 841D7BFE1E2D00C6005E9ECA /* AutoHeaderImportExtension.appex */;
192 | productType = "com.apple.product-type.xcode-extension";
193 | };
194 | /* End PBXNativeTarget section */
195 |
196 | /* Begin PBXProject section */
197 | 841D7BDB1E2D009F005E9ECA /* Project object */ = {
198 | isa = PBXProject;
199 | attributes = {
200 | LastUpgradeCheck = 0820;
201 | ORGANIZATIONNAME = WillkYang;
202 | TargetAttributes = {
203 | 841D7BE21E2D009F005E9ECA = {
204 | CreatedOnToolsVersion = 8.2.1;
205 | DevelopmentTeam = N3VC6N6JE3;
206 | ProvisioningStyle = Automatic;
207 | };
208 | 841D7BFD1E2D00C6005E9ECA = {
209 | CreatedOnToolsVersion = 8.2.1;
210 | DevelopmentTeam = N3VC6N6JE3;
211 | ProvisioningStyle = Automatic;
212 | SystemCapabilities = {
213 | com.apple.ApplicationGroups.Mac = {
214 | enabled = 0;
215 | };
216 | };
217 | };
218 | };
219 | };
220 | buildConfigurationList = 841D7BDE1E2D009F005E9ECA /* Build configuration list for PBXProject "AutoImportExtension" */;
221 | compatibilityVersion = "Xcode 3.2";
222 | developmentRegion = English;
223 | hasScannedForEncodings = 0;
224 | knownRegions = (
225 | en,
226 | Base,
227 | );
228 | mainGroup = 841D7BDA1E2D009F005E9ECA;
229 | productRefGroup = 841D7BE41E2D009F005E9ECA /* Products */;
230 | projectDirPath = "";
231 | projectRoot = "";
232 | targets = (
233 | 841D7BE21E2D009F005E9ECA /* AutoImportExtension */,
234 | 841D7BFD1E2D00C6005E9ECA /* AutoHeaderImportExtension */,
235 | );
236 | };
237 | /* End PBXProject section */
238 |
239 | /* Begin PBXResourcesBuildPhase section */
240 | 841D7BE11E2D009F005E9ECA /* Resources */ = {
241 | isa = PBXResourcesBuildPhase;
242 | buildActionMask = 2147483647;
243 | files = (
244 | 841D7BF01E2D009F005E9ECA /* Assets.xcassets in Resources */,
245 | 841D7BF31E2D009F005E9ECA /* Main.storyboard in Resources */,
246 | );
247 | runOnlyForDeploymentPostprocessing = 0;
248 | };
249 | 841D7BFC1E2D00C6005E9ECA /* Resources */ = {
250 | isa = PBXResourcesBuildPhase;
251 | buildActionMask = 2147483647;
252 | files = (
253 | );
254 | runOnlyForDeploymentPostprocessing = 0;
255 | };
256 | /* End PBXResourcesBuildPhase section */
257 |
258 | /* Begin PBXSourcesBuildPhase section */
259 | 841D7BDF1E2D009F005E9ECA /* Sources */ = {
260 | isa = PBXSourcesBuildPhase;
261 | buildActionMask = 2147483647;
262 | files = (
263 | 841D7BEE1E2D009F005E9ECA /* ViewController.m in Sources */,
264 | 841D7BEB1E2D009F005E9ECA /* main.m in Sources */,
265 | 841D7BE81E2D009F005E9ECA /* AppDelegate.m in Sources */,
266 | );
267 | runOnlyForDeploymentPostprocessing = 0;
268 | };
269 | 841D7BFA1E2D00C6005E9ECA /* Sources */ = {
270 | isa = PBXSourcesBuildPhase;
271 | buildActionMask = 2147483647;
272 | files = (
273 | 841D7C071E2D00C6005E9ECA /* SourceEditorExtension.m in Sources */,
274 | 841D7C0A1E2D00C6005E9ECA /* SourceEditorCommand.m in Sources */,
275 | );
276 | runOnlyForDeploymentPostprocessing = 0;
277 | };
278 | /* End PBXSourcesBuildPhase section */
279 |
280 | /* Begin PBXTargetDependency section */
281 | 841D7C0D1E2D00C6005E9ECA /* PBXTargetDependency */ = {
282 | isa = PBXTargetDependency;
283 | target = 841D7BFD1E2D00C6005E9ECA /* AutoHeaderImportExtension */;
284 | targetProxy = 841D7C0C1E2D00C6005E9ECA /* PBXContainerItemProxy */;
285 | };
286 | /* End PBXTargetDependency section */
287 |
288 | /* Begin PBXVariantGroup section */
289 | 841D7BF11E2D009F005E9ECA /* Main.storyboard */ = {
290 | isa = PBXVariantGroup;
291 | children = (
292 | 841D7BF21E2D009F005E9ECA /* Base */,
293 | );
294 | name = Main.storyboard;
295 | sourceTree = "";
296 | };
297 | /* End PBXVariantGroup section */
298 |
299 | /* Begin XCBuildConfiguration section */
300 | 841D7BF51E2D009F005E9ECA /* Debug */ = {
301 | isa = XCBuildConfiguration;
302 | buildSettings = {
303 | ALWAYS_SEARCH_USER_PATHS = NO;
304 | CLANG_ANALYZER_NONNULL = YES;
305 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
306 | CLANG_CXX_LIBRARY = "libc++";
307 | CLANG_ENABLE_MODULES = YES;
308 | CLANG_ENABLE_OBJC_ARC = YES;
309 | CLANG_WARN_BOOL_CONVERSION = YES;
310 | CLANG_WARN_CONSTANT_CONVERSION = YES;
311 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
312 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
313 | CLANG_WARN_EMPTY_BODY = YES;
314 | CLANG_WARN_ENUM_CONVERSION = YES;
315 | CLANG_WARN_INFINITE_RECURSION = YES;
316 | CLANG_WARN_INT_CONVERSION = YES;
317 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
318 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
319 | CLANG_WARN_UNREACHABLE_CODE = YES;
320 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
321 | CODE_SIGN_IDENTITY = "-";
322 | COPY_PHASE_STRIP = NO;
323 | DEBUG_INFORMATION_FORMAT = dwarf;
324 | ENABLE_STRICT_OBJC_MSGSEND = YES;
325 | ENABLE_TESTABILITY = YES;
326 | GCC_C_LANGUAGE_STANDARD = gnu99;
327 | GCC_DYNAMIC_NO_PIC = NO;
328 | GCC_NO_COMMON_BLOCKS = YES;
329 | GCC_OPTIMIZATION_LEVEL = 0;
330 | GCC_PREPROCESSOR_DEFINITIONS = (
331 | "DEBUG=1",
332 | "$(inherited)",
333 | );
334 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
335 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
336 | GCC_WARN_UNDECLARED_SELECTOR = YES;
337 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
338 | GCC_WARN_UNUSED_FUNCTION = YES;
339 | GCC_WARN_UNUSED_VARIABLE = YES;
340 | MACOSX_DEPLOYMENT_TARGET = 10.12;
341 | MTL_ENABLE_DEBUG_INFO = YES;
342 | ONLY_ACTIVE_ARCH = YES;
343 | SDKROOT = macosx;
344 | };
345 | name = Debug;
346 | };
347 | 841D7BF61E2D009F005E9ECA /* Release */ = {
348 | isa = XCBuildConfiguration;
349 | buildSettings = {
350 | ALWAYS_SEARCH_USER_PATHS = NO;
351 | CLANG_ANALYZER_NONNULL = YES;
352 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
353 | CLANG_CXX_LIBRARY = "libc++";
354 | CLANG_ENABLE_MODULES = YES;
355 | CLANG_ENABLE_OBJC_ARC = YES;
356 | CLANG_WARN_BOOL_CONVERSION = YES;
357 | CLANG_WARN_CONSTANT_CONVERSION = YES;
358 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
359 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
360 | CLANG_WARN_EMPTY_BODY = YES;
361 | CLANG_WARN_ENUM_CONVERSION = YES;
362 | CLANG_WARN_INFINITE_RECURSION = YES;
363 | CLANG_WARN_INT_CONVERSION = YES;
364 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
365 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
366 | CLANG_WARN_UNREACHABLE_CODE = YES;
367 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
368 | CODE_SIGN_IDENTITY = "-";
369 | COPY_PHASE_STRIP = NO;
370 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
371 | ENABLE_NS_ASSERTIONS = NO;
372 | ENABLE_STRICT_OBJC_MSGSEND = YES;
373 | GCC_C_LANGUAGE_STANDARD = gnu99;
374 | GCC_NO_COMMON_BLOCKS = YES;
375 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
376 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
377 | GCC_WARN_UNDECLARED_SELECTOR = YES;
378 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
379 | GCC_WARN_UNUSED_FUNCTION = YES;
380 | GCC_WARN_UNUSED_VARIABLE = YES;
381 | MACOSX_DEPLOYMENT_TARGET = 10.12;
382 | MTL_ENABLE_DEBUG_INFO = NO;
383 | SDKROOT = macosx;
384 | };
385 | name = Release;
386 | };
387 | 841D7BF81E2D009F005E9ECA /* Debug */ = {
388 | isa = XCBuildConfiguration;
389 | buildSettings = {
390 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
391 | CODE_SIGN_IDENTITY = "Mac Developer";
392 | COMBINE_HIDPI_IMAGES = YES;
393 | DEVELOPMENT_TEAM = N3VC6N6JE3;
394 | INFOPLIST_FILE = AutoImportExtension/Info.plist;
395 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
396 | PRODUCT_BUNDLE_IDENTIFIER = com.WillkYang.AutoImportExtension;
397 | PRODUCT_NAME = "$(TARGET_NAME)";
398 | };
399 | name = Debug;
400 | };
401 | 841D7BF91E2D009F005E9ECA /* Release */ = {
402 | isa = XCBuildConfiguration;
403 | buildSettings = {
404 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
405 | CODE_SIGN_IDENTITY = "Mac Developer";
406 | COMBINE_HIDPI_IMAGES = YES;
407 | DEVELOPMENT_TEAM = N3VC6N6JE3;
408 | INFOPLIST_FILE = AutoImportExtension/Info.plist;
409 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
410 | PRODUCT_BUNDLE_IDENTIFIER = com.WillkYang.AutoImportExtension;
411 | PRODUCT_NAME = "$(TARGET_NAME)";
412 | };
413 | name = Release;
414 | };
415 | 841D7C101E2D00C6005E9ECA /* Debug */ = {
416 | isa = XCBuildConfiguration;
417 | buildSettings = {
418 | CODE_SIGN_ENTITLEMENTS = AutoHeaderImportExtension/AutoHeaderImportExtension.entitlements;
419 | CODE_SIGN_IDENTITY = "Mac Developer";
420 | COMBINE_HIDPI_IMAGES = YES;
421 | DEVELOPMENT_TEAM = N3VC6N6JE3;
422 | INFOPLIST_FILE = AutoHeaderImportExtension/Info.plist;
423 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks";
424 | MACOSX_DEPLOYMENT_TARGET = 10.12;
425 | PRODUCT_BUNDLE_IDENTIFIER = com.WillkYang.AutoImportExtension.AutoHeaderImportExtension;
426 | PRODUCT_NAME = "$(TARGET_NAME)";
427 | SKIP_INSTALL = YES;
428 | };
429 | name = Debug;
430 | };
431 | 841D7C111E2D00C6005E9ECA /* Release */ = {
432 | isa = XCBuildConfiguration;
433 | buildSettings = {
434 | CODE_SIGN_ENTITLEMENTS = AutoHeaderImportExtension/AutoHeaderImportExtension.entitlements;
435 | CODE_SIGN_IDENTITY = "Mac Developer";
436 | COMBINE_HIDPI_IMAGES = YES;
437 | DEVELOPMENT_TEAM = N3VC6N6JE3;
438 | INFOPLIST_FILE = AutoHeaderImportExtension/Info.plist;
439 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks";
440 | MACOSX_DEPLOYMENT_TARGET = 10.12;
441 | PRODUCT_BUNDLE_IDENTIFIER = com.WillkYang.AutoImportExtension.AutoHeaderImportExtension;
442 | PRODUCT_NAME = "$(TARGET_NAME)";
443 | SKIP_INSTALL = YES;
444 | };
445 | name = Release;
446 | };
447 | /* End XCBuildConfiguration section */
448 |
449 | /* Begin XCConfigurationList section */
450 | 841D7BDE1E2D009F005E9ECA /* Build configuration list for PBXProject "AutoImportExtension" */ = {
451 | isa = XCConfigurationList;
452 | buildConfigurations = (
453 | 841D7BF51E2D009F005E9ECA /* Debug */,
454 | 841D7BF61E2D009F005E9ECA /* Release */,
455 | );
456 | defaultConfigurationIsVisible = 0;
457 | defaultConfigurationName = Release;
458 | };
459 | 841D7BF71E2D009F005E9ECA /* Build configuration list for PBXNativeTarget "AutoImportExtension" */ = {
460 | isa = XCConfigurationList;
461 | buildConfigurations = (
462 | 841D7BF81E2D009F005E9ECA /* Debug */,
463 | 841D7BF91E2D009F005E9ECA /* Release */,
464 | );
465 | defaultConfigurationIsVisible = 0;
466 | };
467 | 841D7C0F1E2D00C6005E9ECA /* Build configuration list for PBXNativeTarget "AutoHeaderImportExtension" */ = {
468 | isa = XCConfigurationList;
469 | buildConfigurations = (
470 | 841D7C101E2D00C6005E9ECA /* Debug */,
471 | 841D7C111E2D00C6005E9ECA /* Release */,
472 | );
473 | defaultConfigurationIsVisible = 0;
474 | };
475 | /* End XCConfigurationList section */
476 | };
477 | rootObject = 841D7BDB1E2D009F005E9ECA /* Project object */;
478 | }
479 |
--------------------------------------------------------------------------------
/AutoImportExtension.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/AutoImportExtension/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // AutoImportExtension
4 | //
5 | // Created by yate1996 on 2017/1/16.
6 | // Copyright © 2017年 WillkYang. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : NSObject
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/AutoImportExtension/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // AutoImportExtension
4 | //
5 | // Created by yate1996 on 2017/1/16.
6 | // Copyright © 2017年 WillkYang. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
18 | // Insert code here to initialize your application
19 | }
20 |
21 |
22 | - (void)applicationWillTerminate:(NSNotification *)aNotification {
23 | // Insert code here to tear down your application
24 | }
25 |
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/AutoImportExtension/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/AutoImportExtension/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
--------------------------------------------------------------------------------
/AutoImportExtension/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright © 2017年 WillkYang. All rights reserved.
27 | NSMainStoryboardFile
28 | Main
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/AutoImportExtension/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // AutoImportExtension
4 | //
5 | // Created by yate1996 on 2017/1/16.
6 | // Copyright © 2017年 WillkYang. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : NSViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/AutoImportExtension/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // AutoImportExtension
4 | //
5 | // Created by yate1996 on 2017/1/16.
6 | // Copyright © 2017年 WillkYang. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 |
11 | @implementation ViewController
12 |
13 | - (void)viewDidLoad {
14 | [super viewDidLoad];
15 |
16 | // Do any additional setup after loading the view.
17 | }
18 |
19 |
20 | - (void)setRepresentedObject:(id)representedObject {
21 | [super setRepresentedObject:representedObject];
22 |
23 | // Update the view, if already loaded.
24 | }
25 |
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/AutoImportExtension/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // AutoImportExtension
4 | //
5 | // Created by yate1996 on 2017/1/16.
6 | // Copyright © 2017年 WillkYang. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | int main(int argc, const char * argv[]) {
12 | return NSApplicationMain(argc, argv);
13 | }
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 WillkYang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | >Xcode8开放了新的一个Extension:Xcode Source Editor Extension,目的是让开发者可以正规的自主为IDE编写插件,虽然说系统现提供的功能还比较拮据,但是不妨碍我们了解和使用,本文主要介绍Xcode Source Editor Extension的功能,并演示一个简单的插件的实现~
2 |
3 | ###### 一、实现功能
4 | 1、删除无用的类头文件,要求类名和文件名一致
5 |
6 | 2、删除重复导入的头文件,只保留一个
7 |
8 | ###### 二、编写代码
9 |
10 | 1、新建项目,然后新建一个Target,类型选择Xcode Source Editor Extension,完成之后设置target的签名和项目的签名一致。
11 |
12 | 
13 |
14 |
15 | 2、在info.plist中可以修改插件显示名称Bundle name和其它对Extension的设置。
16 |
17 | 
18 |
19 | 3、系统默认为我们生成SourceEditorCommand文件,此处我们也可以在info里边修改配置项,类似于项目中系统生成的Main.storyboard。插件的重点基本在:
20 | ```
21 | - (void)performCommandWithInvocation:(XCSourceEditorCommandInvocation *)invocation completionHandler:(void (^)(NSError * _Nullable nilOrError))completionHandler
22 | ```
23 | 用户调用我们的插件时,系统会回调这个方法
24 | ###### XCSourceEditorCommandInvocation
25 | >Information about the source editor command that the user invoked, such as the identifier of the command, the text buffer on which the command is to operate, and whether the command has been canceled by Xcode or the user.
26 |
27 | 其中invocation.buffer是编辑器的全部文本
28 | ```objc
29 | /** 当前编辑器的全部文件内容 */
30 | @property (readonly, strong) NSMutableArray *lines;
31 | /** 是当前选中的文本 */
32 | @property (readonly, strong) NSMutableArray *selections;
33 | ```
34 | 我们在回调方法中编写如下代码:
35 |
36 | 
37 |
38 | ```objc
39 | //headerDict存放文本中所有的头文件
40 | NSMutableDictionary *headerDict = [NSMutableDictionary dictionary];
41 | //willCheckDict存放将要删除的头文件
42 | NSMutableDictionary *willCheckDict = [NSMutableDictionary dictionary];
43 |
44 | //遍历编辑器每一行
45 | for (int idx = 0; idx < invocation.buffer.lines.count; idx++) {
46 |
47 | NSString *lineCode = invocation.buffer.lines[idx];
48 |
49 | //若willCheckDict文件不为空,则进行是否使用了该头文件的判断
50 | if (willCheckDict.count > 0) {
51 | [willCheckDict enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull key, NSString * _Nonnull checkString, BOOL * _Nonnull stop) {
52 | if ([lineCode containsString:checkString]) {
53 | if (![lineCode containsString:@"#import"]) {
54 | if ([headerDict[checkString] isEqualToNumber: @1]) {
55 | //若使用了该头文件,则从willCheckDict字典中提出该项
56 | [willCheckDict removeObjectForKey:key];
57 | //同时设置该头文件已经检查过,若后续仍出现该头文件,则可以进行删除
58 | headerDict[checkString] = @0;
59 | }
60 | }
61 | }
62 | }];
63 |
64 | }
65 |
66 | //检测代码是否含有#import为头文件标志;+号我们认为是类扩展的标志
67 | if ([lineCode containsString:@"#import"] && ![lineCode containsString:@"+"]) {
68 | //解析获取类名
69 | NSRange range1 = [lineCode rangeOfString:@"\""];
70 | NSRange range2 = [lineCode rangeOfString:@"\"" options:NSBackwardsSearch];
71 | NSRange zeroRange = NSMakeRange(0, 0);
72 |
73 | if (!(NSEqualRanges(range1, zeroRange) || NSEqualRanges(range2, zeroRange))) {
74 | NSRange findRange = NSMakeRange(range1.location + 1, range2.location - range1.location - 3);
75 | NSString *classString = [lineCode substringWithRange:findRange];
76 | willCheckDict[@(idx)] = classString;
77 | headerDict[classString] = @1;
78 | }
79 | }
80 | }
81 |
82 |
83 | //取出需要删除的行
84 | NSMutableIndexSet *index = [NSMutableIndexSet indexSet];
85 | [willCheckDict.allKeys enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
86 | [index addIndex:obj.integerValue];
87 |
88 | }];
89 |
90 | //删除不符合条件的行
91 | [invocation.buffer.lines removeObjectsAtIndexes:index];
92 |
93 | //通知系统完成
94 | completionHandler(nil);
95 | ```
96 |
97 | ##### 三、测试结果
98 |
99 | 1、运行,选择Xcode8
100 |
101 | 
102 |
103 | 2、可以看见灰色的Xcode实例。随便选择一个项目打开
104 |
105 | 
106 |
107 | 3、测试。测试文件中含有未使用的头文件和冗余的头文件
108 |
109 | 
110 |
111 | 4、Editor中选择插件运行
112 |
113 | 
114 |
115 | 5、检验运行结果
116 |
117 | 
118 |
119 | 啦啦啦,多余的头文件已经被成功检测到并且移除了了~
120 |
121 | ##### 四、总结
122 | 至此,我们完成并测试通过了一个简单的Xcode插件的编写。主要目的是简单了解和使用Xcode8的插件,如果觉得有用,可以找到product里边的文件复制出来打开,然后在系统设置辅助功能中启用,最后在Xcode中绑定快捷键即可食用。当然,功能十分简陋,还请大神勿怪~
123 |
124 | 不足:受限于系统现有API,运行插件时,只能获取到当前编辑的文件,无法获取整个项目文件来分析,故很多功能暂时无法实现,如支持更加智能的检测等等,以后系统若能提供项目空间的文件访问和GUI支持,则插件可以发挥更大作用~
125 |
126 | 知识链:
127 | [WWDC2016](https://developer.apple.com/videos/play/wwdc2016/414/)
128 | [iOS 10 Day By Day: Xcode Source Editor Extensions](http://www.cocoachina.com/ios/20161212/18344.html)
129 | [使用 Xcode Source Editor Extension开发Xcode 8 插件](http://blog.csdn.net/zhouzhoujianquan/article/details/52600763)
130 |
131 |
132 | 欢迎加群讨论其它~:578874451
133 |
--------------------------------------------------------------------------------