Xcode8 开放了新的一个Extension:Xcode Source Editor Extension,可以让我们自己开发Xcode 插件,但是目前开发的API 还比较少,但是也不妨碍我们了解和使用。本文主要介绍如何使用Xcode Source Editor Extension 实现一个简单的自动生成代码的插件。
效果

实现
新建一个MacOS 宿主项目,再新建一个Target,类型为Xcode Source Editor Extension,并且设置target的签名和宿主App的签名一致。


目录结构

系统默认为我们生成SourceEditorCommand 文件,其中这个方法就当做主入口,当调用插件时会执行这个方法。但是只能获取到当前编辑文件的内容,不能获取整个工程。
func perform(with invocation: XCSourceEditorCommandInvocation, completionHandler: @escaping (Error?) -> Void ) -> Void {
// Implement your command here, invoking the completion handler when done. Pass it nil on success, and an NSError on failure.
if invocation.commandIdentifier == InsetCodeKey {
handleInsertCode(invocation: invocation)
}else{
NSWorkspace.shared.open(URL.init(fileURLWithPath: "/Applications/GenerateCode.app"))
}
completionHandler(nil)
}
核心代码如下,大致逻辑是获取当前输入的key,根据提前录入的Map,查找对应Value,并且替换当前行内容。
func handleInsertCode(invocation: XCSourceEditorCommandInvocation){
let selection = invocation.buffer.selections.firstObject as! XCSourceTextRange
let totalLines = invocation.buffer.lines
let curIndex = selection.start.line
var curLineContent = totalLines[curIndex] as! String
let mapKey = curLineContent.trimmingCharacters(in: .whitespacesAndNewlines)
let map = SharedUserDefault.shared.mapping
if let value = map[mapKey] {
let arr = convertToLines(string: value)
var numberOfSpaceIndent = curLineContent.range(of: mapKey)!.lowerBound.encodedOffset
var indentStr = ""
while numberOfSpaceIndent > 0 {
indentStr = indentStr.appending(" ")
numberOfSpaceIndent -= 1
}
if arr.isEmpty{
return
}
if let range = curLineContent.range(of: mapKey){
curLineContent.replaceSubrange(range, with: arr[0])
totalLines[curIndex] = curLineContent
}
for i in 1..<arr.count{
let insetStr = indentStr + arr[i]
totalLines.insert(insetStr, at: curIndex + i)
}
}
}
录入界面

在主工程中,创建录入界面,整体是个NSTableView,展示保存好的Map,双击某一行或者点击添加按钮,进入编辑页面,主要用NSTextField 实现

全局保存的Map 存储在UserDefaults 里,但是宿主和扩展共享数据的话,需要suiteName 相同才行,需要在App Group 里设置相同的key。
测试
-
先运行主App,测试添加数据是否正常
-
运行插件,选择Xcode,会出来一个灰色的Xcode,随便打开一个项目

输入key,运行插件,正确生成代码。

也可以绑定快键键

使用

将app 拷入应用程序里,并且将设置里Xcode 扩展选上,重启Xcode,就能在Editor 里使用啦

总结
插件主要参考了EasyCode,站在了巨人的肩膀上。但是目前EasyCode 好像Swift 不能用,因为媳妇开发用的纯Swift,所以主要写给她用的,目前插件的功能还很简单,但是基本可以使用了,后续可以继续优化!
网友评论