Xcode 8前,開發(fā)者可以通過運行時注入代碼來實現(xiàn)添加插件,甚至在著名的插件管理工具Alcatraz上提交和分發(fā)插件。 但是,Xcode 8成為這種開始方式的終結者,因為它提供了自家的Xcode Source Editor Extension方式開發(fā)插件。 接下來,將詳細介紹如何在Xcode8下編寫插件,以編寫代碼格式化插件為例。不足之處,歡迎補充,先看下最終效果吧。

codeFormat_gif.gif
具體代碼實現(xiàn)
1.打開Xcode創(chuàng)建mac程序 -> macOS -> Cocoa Application,接著設置工程參數(shù)即可

codeFormat_1.png

codeFormat_2.png
接下來添加Target

codeFormat_3.png

codeFormat_4.png
配置Target名稱等信息,F(xiàn)inish完成,此時會彈出Scheme對話框,點擊Activate啟用。文件結構如下

codeFormat_5.png
認識插件 SourceEditorExtension類和插件的生命周期和配置有關,實現(xiàn)了XCSourceEditorExtension協(xié)議中的一個方法extensionDidFinishLaunching() 和一個變量commandDefinitions,默認是被注釋掉的。 方法extensionDidFinishLaunching()是指剛剛加載好插件但還未點擊插件按鈕時,可以執(zhí)行某些準備工作。 變量commandDefinitions返回字典類型的數(shù)組,可以為每個插件重寫名字、標識符和自定義類名等信息,相當于設置后面要介紹的的Info.plist文件中對應的XCSourceEditorCommandName、XCSourceEditorCommandIdentifier和XCSourceEditorCommandClassName信息。 SourceEditorCommand類實現(xiàn)了XCSourceEditorCommand協(xié)議中的perform方法,點擊插件按鈕所執(zhí)行的具體邏輯就是在這個方法中完成的,因此是實現(xiàn)插件功能的核心。 (http://www.maxzhang.site/img/blogImg/codeFormat_7.png)
思路,獲取需要格式化類文件的 當前代碼數(shù)組,數(shù)組元素為每一行代碼,對對象轉化成字符串,對字符串進行操作,從而實現(xiàn)格式化代碼。 具體代碼如下,
SourceEditorCommand.m文件代碼
#import "SourceEditorCommand.h"
#import "HandleFormatCodeMethod.h"
@interface SourceEditorCommand ()
@property (strong, nonatomic) NSMutableArray *allLinesCodeMArr;
@property (strong, nonatomic) HandleFormatCodeMethod *codeMethod;
@end
- (void)performCommandWithInvocation:(XCSourceEditorCommandInvocation *)invocation completionHandler:(void (^)(NSError * _Nullable nilOrError))completionHandler
{
// [self handleLLVMStyleFormatWithInvocation:invocation completionHandler:completionHandler];
[self formatAllClassFileCode:invocation];
completionHandler(nil);
}
- (NSMutableArray *)allLinesCodeMArr
{
if (!_allLinesCodeMArr) {
_allLinesCodeMArr = [NSMutableArray array];
}
return _allLinesCodeMArr;
}
- (HandleFormatCodeMethod *)codeMethod
{
if (!_codeMethod) {
_codeMethod = [[HandleFormatCodeMethod alloc] init];
}
return _codeMethod;
}
- (void)formatAllClassFileCode:(XCSourceEditorCommandInvocation *)invocation
{
if (invocation.buffer.lines.count == 0) {
return;
}
[self.allLinesCodeMArr removeAllObjects];
//處理NSTaggedPointerString 轉化為NSString
for (int i = 0; i < invocation.buffer.lines.count; i++) {
id stringValue = [invocation.buffer.lines objectAtIndex:i];
NSString *stringPointerValue = [NSString stringWithFormat:@"%@",stringValue];
[_allLinesCodeMArr addObject:stringPointerValue];
}
self.codeMethod.allLinesCodeMArr = self.allLinesCodeMArr;
self.codeMethod.invocation = invocation;
//處理implementation之后所有的換行
[self.codeMethod handlEunnecessaryNewLine];
//處理#import之前要預留一行空格的問題
[self.codeMethod handleBeforeFirstImportHasOnlyOneNewLine];
//處理最后一個#import與第一個@interface之間僅保留一個換行
[self.codeMethod handleBetweenTheLastImportAndNextCodeHasOnlyOneNewLine];
//處理@interface前后都有一個換行
[self.codeMethod handleBefroreInterfaceHasOnlyOneNewLine];
[self.codeMethod handleLaterInterfaceHasOnlyOneNewLine];
//處理implementation前后都有一個換行
[self.codeMethod handleBefroreImplementationHasOnlyOneNewLine];
[self.codeMethod handleLaterImplementationHasOnlyOneNewLine];
//處理@end前后都有一個換行
[self.codeMethod handleBefroreEndHasOnlyOneNewLine];
[self.codeMethod handleLaterEndHasOnlyOneNewLine];
//處理@property屬性申明時候的間距
[self.codeMethod handlePropertyLineSpaceMargin];
//處理每個方法開始的第一個花括號另起一行
[self.codeMethod handleMethodStartLocationTheFirstBraceNewLine];
//處理每個方法前有2個換行 每個方法后有2個換行
[self.codeMethod handleBeforeEveryMethodAndLaterEveryMethodHasOnlyTwoNewLine];
//處理#pragma 或者 #warning前后各空一行
[self.codeMethod handleBeforeAndLaterPragmaOrWarningHasNewLine];
}
- (void)handleLLVMStyleFormatWithInvocation:(XCSourceEditorCommandInvocation *)invocation completionHandler:(void (^)(NSError * _Nullable nilOrError))completionHandler
{
NSString *pathStr = [[NSBundle mainBundle] pathForResource:@"clang_format" ofType:nil];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:pathStr]) {
NSLog(@"=====fileExistsAtPath======");
}
NSPipe *errorPipe = [[NSPipe alloc] init];
NSPipe *outputPipe = [[NSPipe alloc] init];
NSPipe *inputPipe = [[NSPipe alloc] init];
NSTask *task = [[NSTask alloc] init];
task.standardError = errorPipe;
task.standardOutput = outputPipe;
task.launchPath = pathStr;
task.arguments = @[@"-style=llvm"];
task.standardInput = inputPipe;
NSFileHandle *stdinHandle = inputPipe.fileHandleForWriting;
NSString *stdin = invocation.buffer.completeBuffer;
NSData *data = [stdin dataUsingEncoding:NSUTF8StringEncoding];
[stdinHandle writeData:data];
[stdinHandle closeFile];
[task launch];
[task waitUntilExit];
[errorPipe.fileHandleForReading readDataToEndOfFile];
NSData *outputData = [outputPipe.fileHandleForReading readDataToEndOfFile];
NSString *resultStr = [[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding];
//--------------------------------------------
if ([invocation.buffer.contentUTI isEqualToString:@"public.objective-c-source"]) {
[invocation.buffer.lines removeAllObjects];
NSArray *lines = [resultStr componentsSeparatedByString:@"\n"];
[invocation.buffer.lines addObjectsFromArray:lines];
[invocation.buffer.selections removeAllObjects];
XCSourceTextRange *range = [[XCSourceTextRange alloc] init];
range.start = XCSourceTextPositionMake(0, 0);
range.end = XCSourceTextPositionMake(0, 0);
[invocation.buffer.selections addObject:range];
};
}
HandleFormatCodeMethod.h中寫具體的處理方法,這里只展示.h文件
#import <Foundation/Foundation.h>
#import <XcodeKit/XcodeKit.h>
@interface HandleFormatCodeMethod : NSObject
@property (strong, nonatomic) NSMutableArray *allLinesCodeMArr;
@property (strong, nonatomic) XCSourceEditorCommandInvocation *invocation;
/**
處理implementation之后所有的換行
*/
- (void)handlEunnecessaryNewLine;
/**
處理成第一個#import之前必須有唯一的一個換行
*/
- (void)handleBeforeFirstImportHasOnlyOneNewLine;
/**
處理最后一個#import與第一個interface之間僅保留一個換行
*/
- (void)handleBetweenTheLastImportAndNextCodeHasOnlyOneNewLine;
/**
處理interface前只有一個換行
*/
- (void)handleBefroreInterfaceHasOnlyOneNewLine;
/**
處理interface后只有一個換行
*/
- (void)handleLaterInterfaceHasOnlyOneNewLine;
/**
處理implementation前只有一個換行
*/
- (void)handleBefroreImplementationHasOnlyOneNewLine;
/**
處理implementation后只有一個換行
*/
- (void)handleLaterImplementationHasOnlyOneNewLine;
/**
處理end前只有一個換行
*/
- (void)handleBefroreEndHasOnlyOneNewLine;
/**
處理end后只有一個換行
*/
- (void)handleLaterEndHasOnlyOneNewLine;
/**
處理每個方法前僅有2個換行 處理每個方法后僅有2個換行
*/
- (void)handleBeforeEveryMethodAndLaterEveryMethodHasOnlyTwoNewLine;
/**
處理每個方法開始的第一個花括號另起一行
*/
- (void)handleMethodStartLocationTheFirstBraceNewLine;
/**
處理@property屬性申明時候的間距
*/
- (void)handlePropertyLineSpaceMargin;
/**
處理#pragma 或者 #warning前后各空一行
*/
- (void)handleBeforeAndLaterPragmaOrWarningHasNewLine;
@end
//完整項目代碼:https://github.com/maxzhang123/codeFormat
//轉載請注明出處,謝謝
by maxzhang