Vim で C# を書くなら OmniSharp で決まり!

Vim Advent Calendar 2012 の 173 日目の記事です。

今回は C# を書くのに便利な OmniSharp と言うツールを紹介します。これさえあれば、エディタとしての Visual Studio はもう必要ありません!

経緯

(興味ない人はここは飛ばしてインストールのところから読むと良いです)


先日、OmniSharp なるものの存在を教えてもらいました。

と言うわけで調査してみることにしました。
様々な罠にかかりつつ、ソースコードにちょろちょろ手を入れて試していくと…

動く、動くぞ!

これは素晴らしい。が、Vim script の部分とかあちこちイケてない…。

と言うわけで pull req を投げまくることに。

このことについて Lingr の Vim 部屋 で宣伝したところ…
日本人 Vimmer 達がものすごい勢いで pull request を投げ始めました。以下一部抜粋。これで全てではありません。

と言うわけで、精力的に Pull Request を取り込んでくれている作者の nosami さんのおかげもあって、発見時点からかなり改良されています*1

インストール

このプラグインは、サーバクライアント方式で動作します。ローカルにサーバを立てて、Vim とそのサーバで通信をして各種機能を提供します。

Vim

そのためまず、クライアントとなる Vim は +python が有効になった Vim が必要になります。Vim 内で以下のコマンドを実行すると、+python が有効かどうか確認できます。

:echo has('python')

1 が表示されたら、+python が使えます。0 だったら使えないので、何とかして使える Vim を調達してください。
ちなみに、現在 +python に依存しないようにする Pull Request が投げられています。代わりに curlwget に依存しますが、場合によっては +python よりも用意しやすいでしょう。

プラグイン

このプラグインはサーバが C# で書かれているため、これをビルドする必要があります。Windows では Visual StudioMacLinux では Mono が必要です。
サーバをビルドする必要があるので、インストールにはビルド機能もある neobundle をお勧めします。neobundle を使う場合、以下のように設定すればインストールと一緒にビルドも自動で行われます。*2

NeoBundleLazy 'nosami/Omnisharp', {
\   'autoload': {'filetypes': ['cs']},
\   'build': {
\     'windows': 'MSBuild.exe server/OmniSharp.sln /p:Platform="Any CPU"',
\     'mac': 'xbuild server/OmniSharp.sln',
\     'unix': 'xbuild server/OmniSharp.sln',
\   }
\ }
手動インストール

以下は、それ以外の方法(Vundle や pathogen、もっと古典的な手法など)で手動でインストールする手順です。
まずは通常のプラグインと同じようにインストールします。この辺りは割愛します。

サーバ

まず、サーバは submodule になっているので、更新する必要があります。

cd path/to/Omnisharp
git submodule update --init

その後、環境に応じて以下のようにサーバをビルドします。

Visual Studio
cd server
MSBuild.exe /p:Platform="Any CPU"

通常は MSBuild.exe だけでいけるようですが、私の環境では /p を指定する必要がありました。
Windows Vista 以降の人は、サーバやVimを管理者権限で動かすのでなければ、以下の設定が必要です。
管理者権限のコマンドプロンプトで実行します。(参考: https://subtech.g.hatena.ne.jp/motemen/20101125/1290694952 )

netsh http add urlacl url=http://+:2000/ user=thinca

ユーザ名は適時読み替えてください。ポート番号は OmniSharp がデフォルトで使うものです。

Mono
cd server
xbuild


server/OmniSharp/bin/Debug/OmniSharp.exe が生成されていれば成功です。

使い方

サーバの起動

まずはサーバを起動します。サーバは起動する際にプロジェクトの .sln ファイルを指定する必要があります。

path/to/OmniSharp/server/OmniSharp/bin/Debug/OmniSharp.exe -s path/to/project.sln

mono を使っている場合は mono 経由で起動する必要があります。

mono path/to/OmniSharp/server/OmniSharp/bin/Debug/OmniSharp.exe -s path/to/project.sln

Vim 内から起動することも可能です。C#ソースコードを開いているファイル*3で、以下のようにします。

:OmniSharpStartServer
" もしくは
:OmniSharpStartServerSolution foo.sln

前者の場合は現在開いているファイルの位置から .sln ファイルを自動的に探して開きます。
また、.sln に存在しないファイル名を与えてもとりあえずは動くようです。この場合でも標準的なライブラリ周りの補完は可能のようです。

コード補完

オムニ補完が利用可能です。コードの任意の場所で、 を入力すると、その文脈に応じた補完が行えます。


[ gifアニメーション提供: id:osyo-manga ]
neocomplcache を使うことで、自動補完も可能です。以下は設定例です。

if !exists('g:neocomplcache_force_omni_patterns')
  let g:neocomplcache_force_omni_patterns = {}
endif
let g:neocomplcache_force_omni_patterns.cs = '[^.]\.\%(\u\{2,}\)\?'
コマンド

C# を編集しているバッファで以下のコマンドが使えます。私自身全ての機能を試したわけではないので、私がよくわかっていない部分については備考に書いておきました。コマンドはほぼ全て、現在のカーソル位置に対して動作します。

コマンド 説明 備考
:OmniSharpGotoDefinition コードの定義へジャンプします。 超便利。
:OmniSharpFindUsages コードの参照元を検索します。 quickfix に出る。超便利。
:OmniSharpFindImplementations 型を実装している型を検索します。 インターフェース上で実行すると、インターフェースを実装している型のリストが出たりする。
:OmniSharpFindSyntaxErrors 簡易構文チェックを実行します。 構文チェックであり、ビルドエラーは出ない。デフォルトだと保存時に自動される。
:OmniSharpGetCodeActions カーソル位置に応じた様々なアクションを実行します。 Visual Studio でおなじみの、using 補完や using 並び換えなど。
:OmniSharpTypeLookup 型情報をメッセージ領域に表示します。 コマンド打つ領域のこと。
:OmniSharpBuild ビルドします。 ビルドエラーが quickfix で出るようになってるらしいけど私の環境だとうまく出ない。
:OmniSharpRename 変数等をリネームします。 微妙にうまく動かず。
:OmniSharpReloadSolution ソリューションをリロードします。 編集していて、ジャンプがずれたりうまくいかなくなってきたら実行してみると良いかも。
:OmniSharpCodeFormat コードフォーマットします。 動作未確認。
:OmniSharpStartServer サーバを実行します。 dispatch.vim に依存。 vimprocが使えるようにする pull req あり。
:OmniSharpStopServer サーバを停止します。  
:OmniSharpAddToProject ファイルをプロジェクトに追加します。 動作未確認。

特によく使うものに関してはキーマッピングしておくと良いでしょう。例えば Visual Studio 風にするには以下のような感じです。*4

" Windowsの場合は ~/vimfiles/after/ftplugin/cs.vim に、
" それ以外では ~/.vim/after/ftplugin/cs.vim に書く
nnoremap <silent> <buffer> <F12> :OmniSharpGotoDefinition<CR>
nnoremap <silent> <buffer> <S-F12> :OmniSharpFindUsages<CR>

私はとりあえず一通りって感じで以下のようにしています。

nnoremap <silent> <buffer> ma :OmniSharpAddToProject<CR>
nnoremap <silent> <buffer> mb :OmniSharpBuild<CR>
nnoremap <silent> <buffer> mc :OmniSharpFindSyntaxErrors<CR>
nnoremap <silent> <buffer> mf :OmniSharpCodeFormat<CR>
nnoremap <silent> <buffer> mj :OmniSharpGotoDefinition<CR>
nnoremap <silent> <buffer> <C-w>mj <C-w>s:OmniSharpGotoDefinition<CR>
nnoremap <silent> <buffer> mi :OmniSharpFindImplementations<CR>
nnoremap <silent> <buffer> mr :OmniSharpRename<CR>
nnoremap <silent> <buffer> mt :OmniSharpTypeLookup<CR>
nnoremap <silent> <buffer> mu :OmniSharpFindUsages<CR>
nnoremap <silent> <buffer> mx :OmniSharpGetCodeActions<CR>

まとめ

まだまだうまく動いてない機能もあったりしますが、現段階ですでにかなり実用的です。実際私は OmniSharp を導入してから最近は Visual Studio を立ち上げなくなりました。
動かない部分については今後もガンガン修正投げて行こうと思います。
C#Vim で書きたい人は、是非お試しあれ!

*1:日本人以外の人もいっぱい Pull Request してる模様

*2:mac の設定は私が環境を持っていないので動作未確認です

*3:filetypeがcsのファイル

*4:が、Vim でわざわざ遠い に割り当てる必要もないでしょう