Xcode8代碼格式化插件_maxzhang

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容