Nekostack

Golang, Swift, Vim, Cat, Curry, Whisky.

Swift Package Manager なプロジェクトを Neovim で補完する

Posted on 20 December 2016.

この記事は Swift Advent Calendar 2016 - Qiita の 20 日目の記事です.

追記

SourceKitten のオプション --spm-module を使わずに, autocomplete-swift 側で Swift Package Manager に対応して v0.11.0 をリリースしました. 通常どおり, オリジナルの SourceKitten と一緒に利用すれば, 外部のライブラリや他のファイル由来の型・関数・定数などの補完が利用できます.

1. はじめに

今年のはじめくらいに autocomplete-swift という deoplete.nvim の補完ソース1をつくって, それからちまちまとメンテしてきました. 現状でも, autocomplete-swift を quickrun などと組み合わせると, ちょっとしたコードを試すのになかなか良い感じに使えるんじゃないかと思います. その一方で, 「他のファイルやモジュールで実装されている型や関数などを補完するところまでできておらず, プロダクトのコードを書いたりするのにはいまいち使えない」という状態が続いていたのが気になっていました.

そんなタイミングで, ちょうど autocomplete-swift が利用している SourceKittencomplete コマンド に --spm-module というオプションが追加されていたので, これを使って Swift Package Manager (SPM) モジュールベースの補完を試してみました.

2. --spm-module を使った SPM モジュールベースの補完

みんな大好き Xcode で利用可能な Swift の補完機能などを提供している SourceKit ですが, SourceKitten はその SourceKit とやりとりするためのライブラリ・コマンドラインツールです. はじめに書いたように, autocomplete-swift では SourceKitten を利用してコードの補完を行なっています.

この SourceKitten なのですが, 先月にリリースされた 0.15.0 以降には, autocomplete-swift にとって嬉しい以下のような更新が入っています.

  • Add –spm-module [ModuleName] flag to complete to automatically detect compiler flags for Swift Package Manager modules. swift build must be run prior to support detection.
  • Now builds and passes most tests on Linux using the Swift Package Manager with Swift 3.0. This requires libsourcekitdInProc.so to be built and located in /usr/lib, or in another location specified by the LINUX_SOURCEKIT_LIB_PATH environment variable. A preconfigured Docker image is available on Docker Hub by the ID of norionomura/sourcekit:30.

Release 0.15.0: Linux Litter · jpsim/SourceKitten

つまり, SPM モジュールベースの補完をするのに, わざわざ自分でコンパイル引数を組み立てなくて良く2, また Linux がサポートされたことにより, SourceKit での Swift の補完が各種エディタで利用になっているはず (こちらはまだ未確認ですが). となると, autocomplete-swift から, 開いているファイルを含むモジュールの名前を引き当てて, SourceKitten に --spm-module で渡してやれば, 楽に他のファイルやモジュール由来の型・関数などを補完できるはずです.

3. autocomplete-swift から --spm-module を使う

以下, とりあえず試しということで雑に変更を入れてみた様子です. Commandant のソースコードに追加したファイルで試しています.

まず, 外部のライブラリ由来の型である Result のメソッドの補完…

completion-dependency

そして, 別のファイルで定義されている型の補完…

completion-file

「ひとまず」動かすことができました. 「ひとまず」というのは, SourceKitten をフォークして少しワークアラウンドを入れたためです. 個人的には Vim や Neovim で編集中のファイルは, 個人的に勝手に保存されたくないので, バッファーを書き出した一時ファイルのパスかバッファーの中身を直接 SourceKitten に渡すようにしてるのですが, そうすると --spm-module を付与して complete を叩いた場合, 補完結果が返されないようです.3 現状, SourceKit 側の知識をほぼ持っていないので, 妥当な対応がわかり次第 pull-req つくって送るなり, autocomplete-swift 側で対応するなりしようと思っています.

今回利用した実装は, 以下からどうぞ. そのうち綺麗にして master に取り込みます.

  • https://github.com/mitsuse/autocomplete-swift/tree/support-spm
  • https://github.com/mitsuse/SourceKitten/tree/replace-content-of-editing-file

autocomplete-swift が使う SourceKitten のパスを $PATH で解決できないものに切り替える場合, Neovim で autocomplete_swift#sourcekitten_command に SourceKitten のパスをセットしてあげると良いです. また, SourceKitten が --spm-module で補完するとき, Package.swift があるディレクトリ直下の .build/debug.yaml をみているので, 現状では初回・ファイル構成が変わったら一度 swift build して上げる必要があるようです.

あと, 気になるところとしては, 初回の補完がちょっと遅いところでしょう. とはいえ非同期なので, 編集されるのがブロックされる訳でもないしそこまで大きな問題ではないかなと思っています (もちろん速いことに越したことはないですが). また, 未確認なのでおそらくですが, SourceKitten が Linux でも動くようになっているようなので, Linux でも同じように補完できるのではないでしょうか.

4. まとめ

さて, この記事では以下のことを書きました.

  • SourceKitten 0.15.0 から --spm-module というオプションが追加されている
  • --spm-module を使って Neovim にて依存関係にあるライブラリ・他のファイル由来の補完候補を表示できた
  • Linux でも同様の環境が動く可能性がありそう

お気に入りのエディタで快適にかける言語が増えるのはとても良いですね! Vim で Swift を書きたい人の参考になれば幸いです.

1: 元々は neocomplete の補完ソースでした. 補完で同期されるのは辛いということで, deoplete のみをサポートするようになったので, deoplete-swift との違いが現状そんなにありません.

2: 実際のところは, Package.swift があるディレクトリ以下の .build/debug.yaml があり, そこにコンパイル引数を組み立てるのに必要な情報はあるので, 自前でやっても大変ではないかも. とはいえ, わざわざエディタ側でやらなくて良いのに越したことはないですね.

3: 通常, --file--text で渡した場合, そのパスや適当なファイル名がコンパイル引数として利用されるようですが, --spm-module を使った場合, そのモジュール内部のファイルがコンパイル引数に利用されるので, 編集中のファイルの中身を補完に利用できないというような話にみえています.