介绍
Xcode是每天成千上万的开发人员使用的主要IDE(集成开发环境)。 这是一个了不起的工具,但是有时您想要自定义其某些功能和行为以更好地适合您的工作流程。
在Xcode 7之前,可以在运行时将代码注入Xcode以创建插件。 可以通过一个名为Alcatraz的出色应用程序来提交和分发插件。 在Xcode 8中不再可能。
Xcode 8会验证每个库和捆绑软件,以防止未经您允许运行恶意代码。 当Xcode启动时,先前安装的Alcatraz插件将不再加载。 尽管并没有失去一切,但苹果公司还在WWDC上宣布了开发Xcode源代码编辑器扩展的可能性,以便每个人都可以扩展现有的源代码编辑功能。 让我们来看看使用这些扩展可以实现什么。
1.入门
Xcode 8源代码编辑器扩展是朝正确方向迈出的第一步。 如果您使用Xcode已有一段时间,您可能会发现自己希望在Xcode中自动执行特定任务。 源代码编辑器扩展允许第三方应用程序修改源文件,这正是您加快工作流程所需的。
目前,扩展只能与源代码交互。 这意味着并非所有可通过Alcatraz获得的插件都可以由源代码编辑器扩展替换。 但是谁知道未来会带来什么。
重要的是要了解每个扩展都必须包含在macOS应用中。 例如,您可以在MacOS应用程序中添加有关扩展功能的首选项和说明,并通过Mac App Store进行分发。 另请注意,每个扩展都在单独的进程中运行。 如果扩展崩溃,则不会使Xcode崩溃。 相反,它将显示一条消息,说明扩展程序无法完成其工作。
此外,扩展没有用户界面 ,它们只能在用户调用您的命令时直接修改代码。 例如,它们不能在后台运行。
我建议观看WWDC 2016会议上有关源代码编辑器扩展的信息 。 它不仅说明了如何开始开发Xcode源代码编辑器扩展,还显示了加快开发速度的提示和快捷方式。
2.概述
在本教程中,我们将开发一个扩展,以清理Swift中的闭包语法。 Xcode用括号自动完成闭包语法,但是为了简洁起见,可以省略它们。 通过将其包装到源代码编辑器扩展中,可以轻松地自动完成此任务。
对我们将要开发的内容的简短解释是一个扩展,它可以将任何闭包转换为更简单,更简洁的语法。 看下面的例子。
// Before
session.dataTask(with: url) { (data, response, error) in
}
// After
session.dataTask(with: url) { data, response, error in
}
3.项目设置
不用说,本教程需要Xcode8。您可以从Apple的开发人员网站上下载它。 它可以在OS X 10.11和macOS 10.12上运行。
创建一个类型为Cocoa Application的新OS X项目,并将其命名为CleanClosureSyntax 。 确保已将项目的语言设置为Swift 。 在本教程中,我们将使用新的Swift 3语法。
我们暂时将macOS应用留空,我们可以专注于创建Xcode源代码编辑器扩展。 从“ 文件”菜单中,选择“ 新建”>“目标...” 。 在左侧边栏中,选择OS X,然后从列表中选择Xcode Source Editor Extension 。
单击下一步,然后将产品名称设置为Cleaner 。 将为您创建一个新目标。 如果Xcode询问您是否应激活新创建的Scheme,请单击“ 激活” 。
4.项目结构
让我们首先分析一下刚刚为我们创建的Xcode。 展开Cleaner文件夹以查看其内容。
在本教程中,我们不会修改SourceEditorExtension.swift ,但可以用来进一步自定义扩展。 extensionDidFinishLaunching()
程序启动后,将立即调用extensionDidFinishLaunching()
方法,以便您可以根据需要执行任何初始化。 如果要动态显示或隐藏某些命令,则可以使用commandDefinitions
属性getter。
SourceEditorCommand.swift是扩展名。 在此文件中,您将实现扩展的逻辑。 用户启动您的扩展程序时,将调用perform(with:completionHandler:)
方法。 XCSourceEditorCommandInvocation
对象包含一个buffer
属性,该属性用于访问当前所选文件中的源代码。 如果一切顺利,则应使用值nil
调用完成处理程序,否则将其传递给NSError
实例。
5.实施扩展
既然项目包含了所有必需的目标,我们就可以开始编写扩展了。 回顾一下,我们想从Swift文件中的所有闭包中删除括号。 这可以通过三个步骤完成:
- 查找包含闭包的行
- 从该特定行中删除两个括号
- 替换回修改的行
让我们开始吧。
我们可以使用正则表达式(正则表达式)来解析每一行代码,并查看其中是否包含闭包。 如果您想了解有关正则表达式的更多信息,可以参考Akiel的有关Swift和正则表达式的教程。 您可以使用RegExr测试您的正则表达式。 看看下面的屏幕截图,看看我如何测试我的正则表达式。
打开SourceEditorCommand.swift并修改perform(with:completionHandler:)
方法,如下所示:
func perform(with invocation: XCSourceEditorCommandInvocation, completionHandler: (NSError?) -> Void ) -> Void {
var updatedLineIndexes = [Int]()
// 1. Find lines that contain a closure syntax
for lineIndex in 0 ..< invocation.buffer.lines.count {
let line = invocation.buffer.lines[lineIndex] as! NSString
do {
let regex = try RegularExpression(pattern: "\\{.*\\(.+\\).+in", options: .caseInsensitive)
let range = NSRange(0 ..< line.length)
let results = regex.matches(in: line as String, options: .reportProgress, range: range)
// 2. When a closure is found, clean up its syntax
_ = results.map { result in
let cleanLine = line.remove(characters: ["(", ")"], in: result.range)
updatedLineIndexes.append(lineIndex)
invocation.buffer.lines[lineIndex] = cleanLine
}
} catch {
completionHandler(error as NSError)
}
}
// 3. If at least a line was changed, create an array of changes and pass it to the buffer selections
if !updatedLineIndexes.isEmpty {
let updatedSelections: [XCSourceTextRange] = updatedLineIndexes.map { lineIndex in
let lineSelection = XCSourceTextRange()
lineSelection.start = XCSourceTextPosition(line: lineIndex, column: 0)
lineSelection.end = XCSourceTextPosition(line: lineIndex, column: 0)
return lineSelection
}
invocation.buffer.selections.setArray(updatedSelections)
}
completionHandler(nil)
}
查找带有关闭句法的行
我们首先创建一个Int
值并将其包含将包含修改后的行的行索引的数组。 这是因为我们不想替换所有行。 我们只想替换我们修改的行。
我们枚举了invocation.buffer
对象的所有行,并尝试找到RegularExpression
对象的匹配项。 如果我从正则表达式中删除转义字符,则如下所示:
{.*(.+).+in
当字符串具有以下特征时,此正则表达式匹配:
- 它有一个大括号(
{
),后跟0个或多个字符,除了换行符(\n
)。 - 必须再次找到一个开放的括号(
(
),后接0个或多个字符,该部分应包含闭包的参数。 - 然后,我们需要找到一个右括号(
)
,后接0个或多个字符,它们是可选的返回类型。 - 最后,应该找到
in
关键字。
如果RegularExpression
对象无法找到匹配(例如,如果正则表达式是无效的),我们称之为completionHandler
与错误作为参数。 如果在一行上找到与所有这些条件都匹配的字符串,则说明我们已正确定位了闭包。
清理语法
找到匹配项后,我们在NSString
上调用一个实用程序方法,该方法删除括号。 我们还需要传递匹配范围,以避免在闭包之外删除其他一些括号。
更新行
代码的最后部分检查是否至少更改了一行。 如果是这样,我们调用setArray()
来替代新行和正确的索引。 用值nil
调用完成处理程序,以便Xcode知道一切顺利。
我们仍然必须在NSString
上实现remove(characters:range:)
方法。 让我们将此扩展名添加到文件顶部。
extension NSString {
// Remove the given characters in the range
func remove(characters: [Character], in range: NSRange) -> NSString {
var cleanString = self
for char in characters {
cleanString = cleanString.replacingOccurrences(of: String(char), with: "", options: .caseInsensitiveSearch, range: range)
}
return cleanString
}
}
此方法对要删除的每个字符在NSString
上调用replacingOccurrences(of:with:range:)
。
6.测试
Xcode 8带有测试扩展的出色解决方案。 首先,如果您正在运行OS X 10.11 El Capitan ,请打开Terminal ,执行以下命令,然后重新启动Mac。
sudo /usr/libexec/xpccachectl
完成此操作后,通过选择适当的方案来构建和运行扩展。 当它询问要运行哪个应用程序时,搜索Xcode并确保选择Xcode 8的beta版本。将启动一个新的Xcode版本,该应用程序图标显示为灰色,以便您可以识别正在测试哪个Xcode实例。延期。
在新的Xcode实例中,创建一个新项目或打开一个现有项目,然后转到Editor> Clean Closure> Source Editor Command 。 确保在当前聚焦的Swift文件中至少有一个闭包以查看结果。 如您在以下动画中所见,我们的扩展程序有效。
源代码编辑器命令是命令的默认名称。 您可以在扩展的Info.plist文件中对其进行修改。 打开它,并将字符串更改为Clean Syntax 。
我们还可以分配一个快捷方式来自动调用“ 清理语法”命令。 打开Xcode的首选项,然后选择Key Bindings选项卡。 搜索“ 干净语法” ,命令将出现。 单击右侧的按钮,然后按要使用的快捷方式,例如Command-Alt-Shift- + 。 现在,您可以返回到源文件,然后按该快捷方式直接调用它。
7.技巧和窍门
在撰写本文时,Xcode 8和源代码编辑器扩展仍处于beta中。 以下提示可以帮助您调试可能遇到的一些问题。
如果在Xcode测试实例中无法选择扩展,请终止com.apple.dt.Xcode.AttachToXPCService进程,然后再次运行扩展。
仅替换回您在缓冲区中修改的行。 这使扩展程序运行得更快,并且被Xcode杀死的机会更少。 如果Xcode认为扩展程序的命令花费的时间太长,则可能发生后者。
如果你想显示多个命令,分配给每个指令以不同的标识和使用commandIdentifier
的物业XCSourceEditorCommandInvocation
对象识别用户触发哪一个。
结论
创建Xcode源代码编辑器扩展非常容易。 如果您可以通过创建源代码编辑器扩展来改善工作流程并加快开发速度,请继续进行。 苹果公司为开发人员提供了一种通过Mac App Store共享签名插件的新方法,这样您就可以发布自己的作品,并看着其他开发人员从中受益。
翻译自: https://code.tutsplus.com/tutorials/how-to-create-an-xcode-source-editor-extension--cms-26772