SourceKitについての話

[Swift その2](http://qiita.com/advent-calendar/2015/swift2)の14日目を担当させていただく@ushisantoasobuと申します。よろしくお願いします

# 概要

今日のテーマは`SourceKit`になります。
先月`SourceKit`の機能を利用できる`SourceKitten`というライブラリについてLTをしてきました

http://www.slideshare.net/ssusera7b1a1/source-kitten

このアドベントカレンダーでは、それを文章としてまとめあげたものにすればまあいいだろうと高を括っていましたが、Swiftのオープンソース化に伴い`SourceKit`自体のソースも読めるようになったので、そこらへんについてもほんのちょっと触れるようにはしたいと思います

# SourceKitとは

## あの憎っくき？

![sourcekit_terminated.jpg](https://qiita-image-store.s3.amazonaws.com/0/50827/fdaef68f-f6d8-e7b7-c54f-a8519ec3b8e4.jpeg)

Swiftが世に出はじめたころからXcodeでSwiftを書いてきた人で、このクラッシュモーダルに遭遇したことはないという人はおそらくいないと思います。
意気揚々とSwiftを書いていると、突然このようなモーダルが表示されてXcodeが動かなくなるという・・・。クラッシュメッセージが、

> SourceKitService Terminated

ということから、当時「SourceKitのせいでXcodeが動かなくなった、SourceKitさん頼むよー」といった内容のツイートが多く散見されましたが、自分も**あの憎っくきSourceKitめ**みたいな認識が冗談半分にありました

## Xcodeのサポートツール

`SourceKit`とは一言で言うと、**Swiftのコードをパース・解析して、その結果をもとに開発者をサポートしてくれるツール**になります。
Xcodeの`6.x`以降、実は`SourceKit`は別プロセスとしてXcodeとともに起動していて（正確にはSwiftのファイルをロードしたとき）、[XPC](https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingXPCServices.html)経由で処理のやりとりをしています

- シンタックスハイライト
- オートコンプリート（補完）
- 定義元ジャンプ
- コードフォーマット

など、当たり前のように提供されているIDEの機能は、Xcode `6.x`以降でSwiftを書くときにはSourceKitによるものということですね（Xcodeの`6.x`以前（あくまで`LLVM/Clang`以降）については、ここらへんがよくわかっていないのですが、コンパイラであるClang（`LibClang`？）がやっていくれていたという認識です）

## プロセスが起動していることを確認する

Xcode（`6.x`以降）を起動して`.swfit`のファイルを開くと、以下のようなコマンドで`SourceKitService`が起動していることが確認できます。

```
$ ps aux | grep sourcekit
ushisantoasobu  70314   0.0  0.0  2423356    208 s014  R+    6:17PM   0:00.00 grep sourcekit
ushisantoasobu  70311   0.0  0.1  2514388   4764   ??  Ss    6:17PM   0:00.21 /Applications/Xcode 7.0.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/sourcekitd.framework/Versions/A/XPCServices/SourceKitService.xpc/Contents/MacOS/SourceKitService
```

## ログを出力してみる

Xcode・`SourceKit`間の処理のやりとりをログ出力することができます。以下のようにXcodeを起動してみてください

> export SOURCEKIT_LOGGING=3 && /Applications/Xcode\ 7.0.1.app/Contents/MacOS/Xcode

`.swift`のファイルをいろいろいじると（ファイルを開く、カーソルをコードの上に合わせる、だけでも）ログが出力されると思います

```
sourcekit: [2:sourcekitd_send_request_sync-before:1299:666.2319] {
  key.request: source.request.editor.formattext,
  key.name: "/Users/ushisantoasobu/Documents/Twitilized/Twitilized/models/Setting.swift",
  key.line: 13,
  key.length: 1,
  key.sourcetext: "",
  key.editor.format.options: {
    key.editor.format.indentwidth: 4,
    key.editor.format.tabwidth: 4,
    key.editor.format.usetabs: 0
  }
}
...
```

`key.request`をみていくと、各処理でどのようなことがやりとりされているのかがわかると思います（`source.request.indexsource`、`source.request.cursorinfo`、`source.request.editor.replacetext`などなど）

## ソースが読めるようになった

先述したように、Swiftがオープンソース化されて`SourceKit`自体のソースも読めるようになりました

https://github.com/apple/swift/tree/master/tools/SourceKit

# `SourceKitten`とは

https://github.com/jpsim/SourceKitten

`Realm`の`JP Simard`さんという方がつくったもので、`SourceKit`の機能を利用することができるライブラリになります。
`SourceKit`はこれまで（オープンソース化以前）は完全にドキュメントも存在しない非公開のAPIだったのですが、先述のログ出力の結果から解析してつくったもののようです。[こちら](http://www.jpsim.com/uncovering-sourcekit/)に詳しく説明があります。

## コマンドラインツールを使ってみる

`SourceKitten`はコマンドラインツールとしても提供されているので、少し試してみましょう

```
$ brew install sourcekitten
```

でインストールできます。`help`を叩いてみましょう。提供されているコマンドがわかります

```
$ sourcekitten help
Available commands:

   complete    Generate code completion options.
   doc         Print Swift docs as JSON or Objective-C docs as XML
   help        Display general or command-specific help
   structure   Print Swift structure information as JSON
   syntax      Print Swift syntax information as JSON
   version     Display the current version of SourceKitten
```

次に`syntax`コマンドを打ってみましょう。以下のように`Hoge.swift`ファイルを指定します

```
$ sourcekitten syntax --file ./Hoge.swift
```

以下のような結果が表示されます（長いので後半は省略）

```json
[
  {
    "offset" : 0,
    "length" : 3,
    "type" : "source.lang.swift.syntaxtype.comment"
  },
  {
    "offset" : 3,
    "length" : 26,
    "type" : "source.lang.swift.syntaxtype.comment"
  },
  {
    "offset" : 29,
    "length" : 15,
    "type" : "source.lang.swift.syntaxtype.comment"
  },
  {
    "offset" : 44,
    "length" : 3,
    "type" : "source.lang.swift.syntaxtype.comment"
  },
  {
    "offset" : 47,
    "length" : 43,
    "type" : "source.lang.swift.syntaxtype.comment"
  },
  {
    "offset" : 90,
    "length" : 61,
    "type" : "source.lang.swift.syntaxtype.comment"
  },
  {
    "offset" : 151,
    "length" : 3,
    "type" : "source.lang.swift.syntaxtype.comment"
  },
  {
    "offset" : 155,
    "length" : 6,
    "type" : "source.lang.swift.syntaxtype.keyword"
  },
  {
    "offset" : 162,
    "length" : 5,
    "type" : "source.lang.swift.syntaxtype.identifier"
  },
  {
    "offset" : 168,
    "length" : 6,
    "type" : "source.lang.swift.syntaxtype.keyword"
  },
  {
    "offset" : 175,
    "length" : 13,
    "type" : "source.lang.swift.syntaxtype.identifier"
  },
  {
    "offset" : 190,
    "length" : 16,
    "type" : "source.lang.swift.syntaxtype.comment"
  },
  {
    "offset" : 207,
    "length" : 4,
    "type" : "source.lang.swift.syntaxtype.keyword"
  },
  {
    "offset" : 212,
    "length" : 14,
    "type" : "source.lang.swift.syntaxtype.identifier"
  },
  {
    "offset" : 232,
    "length" : 7,
    "type" : "source.lang.swift.syntaxtype.typeidentifier"
  },

  （長いので省略...）

]
```

# `SourceKitten`を利用してできること

さて、`SourceKitten`を利用してなにができるのでしょうか？
さきの`syntax`コマンドの出力結果をみて「それがどうしたの？」と思った方も多いはずです。
`SourceKitten`はあくまで`SourceKit`のラッパライブラリとしての責務を担うもので、それを利用したより実践的なライブラリを２つ紹介して終わりたいと思います（ともに、作者というか一番のコントリビュータは同じ`JP Simard`さんです）

## SwiftLint

https://github.com/realm/SwiftLint/

SwiftのLinterです。使い方等は以前Qiitaも書きました（[こちら](http://qiita.com/ushisantoasobu/items/b494c9cf7d78a968b373)）ので、良ければそちらをみてください

## jazzy

https://github.com/realm/jazzy

SwiftDocのジェネレータです。Appleのリファレンスと同じようなドキュメントを生成してくれるようです