Xcode の FileMerge.app で Swift のシンタクスハイライトを復活させる

# はじめに

macOS の開発ツールである Xcode には開発に役立つツールが色々と入っていて、その中に diff やマージ作業を GUI で行う FileMerge.app というツールがある。「なんちゃら code を立ち上げるまでもないなぁ」というときに、`opendiff file1 file2` でターミナルからサクッと立ち上げられるので、個人的には重宝している。左右のペインで2つのファイルを見比べることができるのはもちろん、行単位だけでなく文字単位で差分をハイライトしてくれるので、差分が把握しやすい。

ところが、いつの頃からか（Xcode 9 ぐらいではちゃんと動いていたと思う）、Swift ソースコードを指定して opendiff を起動すると、ターミナルに大量のエラーメッセージが吐き出されるようになり、シンタクスハイライトや関数や変数の宣言の選択ができなくなってしまった（とりあえず、標準エラー出力を /dev/null にリダイレクトする alias をして使っているが）。

Xcode のベータを含む新しいバージョンが出るたびに、Feedback Assistant で報告しているし、他の人も [Apple Developer Forums](https://forums.developer.apple.com) で報告しているのだが、一向に直してくれる気配がない。原因はなんとなく分かっていたので、直してみることにした。

# 環境

+ macOS 10.14.6 (18G3020) + Xcode 11.3.1 (11C505)
+ macOS 10.15.4 (19E266) + Xcode 11.4 (11E146)
+ macOS 10.15.5 Beta (19F72f) + Xcode 11.5 Beta (11N605c)


# 原因

opendiff で吐き出されるエラーメッセージは以下のような感じ。

```
$ opendiff file1.swift file2.swift
2020-02-11 18:24:52.980 FileMerge[59271:5815923] Couldn't load language spec for '<DVTSourceCodeLanguage:0x7fd1912b2340:'Xcode.SourceCodeLanguage.Swift'>'
...
（同じエラーメッセージが延々と吐き出される）
...
```

Swift の language spec が読み込めないと怒られている。Objective C などでは怒られることはない。試しに、Objective C の language spec とやらを探してみる。こういう場合、[Rust 製の fd](https://github.com/sharkdp/fd.git) という高速版 find コマンドが便利。

```
$ cd /Applications/Xcode.app/Contents
$ fd lang | grep -i spec | grep -i objective | grep -vi c++
SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/ObjectiveC.xclangspec
SharedFrameworks/SourceModel.framework/Versions/A/Resources/LanguageSpecifications/ObjectiveC.xclangspec
Developer/Platforms/WatchSimulator.platform/Developer/Library/PrivateFrameworks/DVTFoundation.framework/ObjectiveC.xclangspec
Developer/Platforms/AppleTVSimulator.platform/Developer/Library/PrivateFrameworks/DVTFoundation.framework/ObjectiveC.xclangspec
Developer/Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/DVTFoundation.framework/ObjectiveC.xclangspec
Developer/Platforms/MacOSX.platform/Developer/iOSSupport/Library/PrivateFrameworks/DVTFoundation.framework/Versions/A/Resources/ObjectiveC.xclangspec
```

`.xclangspec` という拡張子を持つファイルが language spec らしいので、Swift のものがあるかどうかを探してみる。

```
$ fd -e xclangspec | grep -i swift
SharedFrameworks/SourceModel.framework/Versions/A/Resources/LanguageSpecifications/Swift.xclangspec
SharedFrameworks/SourceModel.framework/Versions/A/Resources/LanguageSpecifications/SwiftDocumentationMarkup.xclangspec
```

Swift の language spec は存在するようだが、Objective C の場合と比べてみると、DVTFoundation.framework に Swift の language spec が存在しないのが原因のようだ。

# 適当に直してみる

SharedFrameworks/SourceModel.framework/Versions/A/Resources/LanguageSpecifications/Swift.xclangspec を SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/ にコピーしてみる。そうすると...

```
$ opendiff file1.swift file2.swift
2020-02-11 19:08:56.115 FileMerge[59516:5884912] [MT] DVTSourceScanner: Couldn't load language spec for 'xcode.lang.comment.recursive' (from '')
```

エラーが１つに減った。DVTFoundation.framework の下にある他の言語の language spec ファイルをみても、xcode.lang.comment.recursive という記述はないので、コピーしてきた Swift.xclangspec 中の xcode.lang.comment.recursive という記述がある行を // でコメントアウトする。

```
$ diff -U0 Swift.xclangspec,bak Swift.xclangspec
--- Swift.xclangspec,bak	2020-02-11 18:53:48.000000000 +0900
+++ Swift.xclangspec	2020-02-11 19:09:56.000000000 +0900
@@ -196 +196 @@
-                "xcode.lang.comment.recursive",
+                //"xcode.lang.comment.recursive",
@@ -218 +218 @@
-                "xcode.lang.comment.recursive",
+                //"xcode.lang.comment.recursive",
```

```
$ opendiff file1.swift file2.swift
$
```

とりあえずエラーは出なくなったし、シンタクスハイライトもできているようだ。

# おわりに

関数や変数の宣言などが正しく抽出できない場合があるようだし、そもそも、Xcode.app パッケージの中身を直接いじっているので、あくまでも、自己責任で。

それにしても、Apple さん、このぐらい、さっさと直してくれないかなぁ...