Posted at

SourceKitについての話

More than 3 years have passed since last update.

Swift その2の14日目を担当させていただく@ushisantoasobuと申します。よろしくお願いします


概要

今日のテーマはSourceKitになります。

先月SourceKitの機能を利用できるSourceKittenというライブラリについてLTをしてきました

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

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


SourceKitとは


あの憎っくき?

sourcekit_terminated.jpg

Swiftが世に出はじめたころからXCodeでSwiftを書いてきた人で、このクラッシュモーダルに遭遇したことはないという人はおそらくいないと思います。

意気揚々とSwiftを書いていると、突然このようなモーダルが表示されてXCodeが動かなくなるという・・・。クラッシュメッセージが、


SourceKitService Terminated


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


XCodeのサポートツール

SourceKitとは一言で言うと、Swiftのコードをパース・解析して、その結果をもとに開発者をサポートしてくれるツールになります。

XCodeの6.x以降、実はSourceKitは別プロセスとしてXCodeとともに起動していて(正確にはSwiftのファイルをロードしたとき)、XPC経由で処理のやりとりをしています


  • シンタックスハイライト

  • オートコンプリート(補完)

  • 定義元ジャンプ

  • コードフォーマット

など、当たり前のように提供されている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.indexsourcesource.request.cursorinfosource.request.editor.replacetextなどなど)


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

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

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


SourceKittenとは

https://github.com/jpsim/SourceKitten

RealmJP Simardさんという方がつくったもので、SourceKitの機能を利用することができるライブラリになります。

SourceKitはこれまで(オープンソース化以前)は完全にドキュメントも存在しない非公開のAPIだったのですが、先述のログ出力の結果から解析してつくったもののようです。こちらに詳しく説明があります。


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

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

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

[

{
"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のラッパライブラリとしての責務を担うもので、それを利用したより実践的なライブラリを2つ紹介して終わりたいと思います(ともに、作者というか一番のコントリビュータは同じJP Simardさんです)


SwiftLint

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

SwiftのLinterです。使い方等は以前Qiitaも書きました(こちら)ので、良ければそちらをみてください


jazzy

https://github.com/realm/jazzy

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