Xcode擴展 初償 — Source Editor Extensions

自從 Xcode 8 采用了系統(tǒng)完整性保護功能(SIP持隧,System Integrity Protection)桑涎,這意味著要將代碼注入到 Xcode 進程已經不可能??彬向,Alcatraz 業(yè)務也關閉了 ,現在敲碼都敲到手酸的 ( ′▽`)攻冷,幸而蘋果爸爸提供了自家的 Xcode Source Editor Extension開發(fā)插件

接下來捏娃胆,我們就實現一個簡單在光標位置插入屬性前綴的插件唄

一、創(chuàng)建插件

  • 創(chuàng)建一個 macOS 下的 Coaoa App等曼,作為容器 App
  • 設置工程參數:名為 LY_PropertyPrefix里烦,語言 Objective_C
  • 添加擴展 Target
    選擇 Editor -> add a new Target,選擇 Xcode Source Editor Extension 并創(chuàng)建


設置 Target 參數:名為 LY_PropertyPrefix_Extension
NOTE:這一步要注意的是 Team 要跟剛才創(chuàng)建的容器 App 的 Team 一致禁谦,當然也可以稍后再修改使得兩個 Target (LY_PropertyPrefix LY_PropertyPrefix_Extension)的簽名配置一致(General -> Signing),要不然插件測試運行時找不到菜單按鈕 ╮( ̄▽ ̄"")╭

二胁黑、插件的基礎與實現

  • 文件結構
    • Info.plist
      Command 的靜態(tài)配置信息,某個插件的菜單名稱州泊、標識符丧蘸、命令觸發(fā)時回調的類(NSExtension -> XCSourceEditorCommandDefinitions -> Item 某個 Item 對應一個 Command 的3個基本信息:XCSourceEditorExtensionPrincipalClassXCSourceEditorCommandIdentifier遥皂、XCSourceEditorCommandClassName

    • SourceEditorExtension

      1. SourceEditorExtension 管理插件的生命周期及對 Command 的動態(tài)配置(對應Info.plist里的靜態(tài)配置)触趴,其實就是實現 XCSourceEditorExtension 協議的兩個方法,默認注釋了
      2. extensionDidFinishLaunching 插件啟動時執(zhí)行的方法渴肉,有想插件啟動時做的工作可以放這里
      3. commandDefinitions 返回字典數組,相當于動態(tài)配置 Command
        其中有3個可配置的內容:XCSourceEditorCommandIdentifierKey 爽冕,XCSourceEditorCommandNameKey仇祭,XCSourceEditorCommandClassNameKey
    • SourceEditorCommand

      1. SourceEditorCommand 是一個實現了 XCSourceEditorCommand 協議的核心類,當插件被觸發(fā)時颈畸,回調代理方法 performCommandWithInvocation: completionHandler: 并從中獲取 XCSourceEditorCommandInvocation 這個消息所攜帶的環(huán)境信息乌奇,并根據消息內容進行響應處理,同時將結果通過 completionHandler block 告知Xcode即可
      2. XCSourceEditorCommandInvocation 命令消息所攜帶的環(huán)境信息眯娱,主要是 命令標識 commandIdentifier 和 命令可以操作的源文本緩沖區(qū)buffer礁苗,我們根據 commandIdentifier 判斷目前所要進行的指令,即Info.plist里的XCSourceEditorCommandIdentifier
      3. XCSourceTextBuffer 源文本緩沖區(qū)的信息
/*
內容統(tǒng)一標識符
public.c-header 表示來自.h文件
public.objective-c-source 表示來自.m文件
public.swift-source 表示來自.swift文件徙缴,可以根據這個標識判斷是否是swift    
*/
@property (readonly, copy) NSString *contentUTI;

//縮進字符個數试伙,可以根據此來調整光標位置陪竿,減少操作    
@property (readonly) NSInteger tabWidth;

//最最最核心的信息 — 當前編輯文件的所有文本行粘咖,可直接操作插入刪除替換
@property (readonly, strong) NSMutableArray <NSString *> *lines;

//緩沖區(qū)中的文本選中區(qū)域,可修改選中區(qū)域或者光標位置
@property (readonly, strong) NSMutableArray <XCSourceTextRange *> *selections;
  • 擴展過程
    從上面可以看到,實現擴展的過程還是比較簡單清晰的:

    1. 啟動擴展夺脾,回調 SourceEditorExtensionextensionDidFinishLaunching
    2. 菜單或快捷鍵觸發(fā) Command,回調 SourceEditorCommand
      performCommandWithInvocation 攔截環(huán)境信息
    3. 獲取當前環(huán)境信息 XCSourceEditorCommandInvocation搏讶,從 invocation 獲取緩沖區(qū)信息 buffer民逼,根據當前選中區(qū)域從 buffer 中獲取到你要的數據
    4. 處理(刪除、替換秀又、添加)并回塞數據到 buffer单寂,刷新Xcode環(huán)境
  • 屬性前綴插入實現
    初步嘗試選擇在光標位置插入屬性前綴相對比較簡單,但是工作中又相當實用吐辙,這里暫只針對 Objective-C ,一般就4種


    1. 獲取當前選中行或光標位置
     NSInteger startLine = invocation.buffer.selections.count ? invocation.buffer.selections.firstObject.start.line : -1;
     if (startLine >= invocation.buffer.lines.count) {
         startLine = invocation.buffer.lines.count - 1;
     }
    
    1. 在光標位置根據命令插入對應前綴宣决,若是光標所在行沒有其它內容,直接替換當前內容袱讹,若已有內容疲扎,則插入下一行
      NSString *propertyPrefix = @"";
      NSString *commandIdentifier = invocation.commandIdentifier;
      if ([commandIdentifier hasSuffix:@"LYPropertyPrefixForAssign"]) {
          propertyPrefix = @"@property (nonatomic, assign) ;";
      } else if ([commandIdentifier hasSuffix:@"LYPropertyPrefixForWeak"]) {
          propertyPrefix = @"@property (nonatomic, weak) ;";
      } else if ([commandIdentifier hasSuffix:@"LYPropertyPrefixForCopy"]) {
          propertyPrefix = @"@property (nonatomic, copy) ;";
      } else if ([commandIdentifier hasSuffix:@"LYPropertyPrefixForStrong"]) {
          propertyPrefix = @"@property (nonatomic, strong) ;";
      }
      NSString *line = [invocation.buffer.lines[startLine] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];;
      if (line.length > 0) {
          [invocation.buffer.lines insertObject:propertyPrefix atIndex:startLine + 1];
      } else {
          [invocation.buffer.lines replaceObjectAtIndex:startLine withObject:propertyPrefix];
      }
    
    1. 重新設置光標位置
    [invocation.buffer.selections removeAllObjects];
    XCSourceTextRange *range = [[XCSourceTextRange alloc] init];
    range.start = (XCSourceTextPosition){startLine, propertyPrefix.length - 1};
    range.end = range.start;
    [invocation.buffer.selections addObject:range];
    

    最后不要忘了completionHandler(nil);

三、插件運行與調試

至此屬性前綴插入的插件開發(fā)已經完成捷雕,最后把插件編譯運行到 Xcode 上面(選擇實現擴展的 Target LY_PropertyPrefix_Extension 運行)椒丧,會出現一個被調試的灰黑色 Xcode,隨意選擇一個項目救巷,即可在菜單Editor中看到插件二級菜單啦


接下來就是通過 Xcode -> Xcode Server... -> Key Bindings 為自己的插件添加快捷鍵
運行效果如下:

四壶熏、插件的安裝

插件開發(fā)完成了,怎么常駐 Xcode 供大家使用呢

  1. Products 文件夾下生成的 LY_PropertyPrefix.app 文件浦译,并右鍵進入 app 所在位置
  2. 將 1 中的 app 拷貝放至 應用程序 中棒假,雙擊 app 啟動即可(系統(tǒng)偏好設置中的擴展默認選中擴展,無需再次進行選中操作精盅,不過可以在這里取消擴展插件)

總結

畢竟是官方的帽哑,所以它可以上架 Mac App Store,但是功能略顯單薄叹俏,目前也僅僅只能文本編輯上進行一些操作妻枕,跟以往第三方插件有很大的差距,不過即使這樣粘驰,還是保持微笑??屡谐,心存感激!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末蝌数,一起剝皮案震驚了整個濱河市愕掏,隨后出現的幾起案子,更是在濱河造成了極大的恐慌顶伞,老刑警劉巖饵撑,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剑梳,死亡現場離奇詭異,居然都是意外死亡肄梨,警方通過查閱死者的電腦和手機阻荒,發(fā)現死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來众羡,“玉大人侨赡,你說我怎么就攤上這事×宦拢” “怎么了羊壹?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長齐婴。 經常有香客問我油猫,道長,這世上最難降的妖魔是什么柠偶? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任情妖,我火速辦了婚禮,結果婚禮上诱担,老公的妹妹穿的比我還像新娘毡证。我一直安慰自己,他們只是感情好蔫仙,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布料睛。 她就那樣靜靜地躺著,像睡著了一般摇邦。 火紅的嫁衣襯著肌膚如雪恤煞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天施籍,我揣著相機與錄音居扒,去河邊找鬼。 笑死丑慎,一個胖子當著我的面吹牛苔货,可吹牛的內容都是我干的。 我是一名探鬼主播立哑,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼姻灶!你這毒婦竟也來了铛绰?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤产喉,失蹤者是張志新(化名)和其女友劉穎捂掰,沒想到半個月后敢会,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡这嚣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年鸥昏,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姐帚。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡吏垮,死狀恐怖,靈堂內的尸體忽然破棺而出罐旗,到底是詐尸還是另有隱情膳汪,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布九秀,位于F島的核電站遗嗽,受9級特大地震影響,放射性物質發(fā)生泄漏鼓蜒。R本人自食惡果不足惜痹换,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望都弹。 院中可真熱鬧娇豫,春花似錦、人聲如沸缔杉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽或详。三九已至系羞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間霸琴,已是汗流浹背椒振。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留梧乘,地道東北人澎迎。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像选调,于是被迫代替她去往敵國和親夹供。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353