vim-users.jp

Hack #229: 動的型付け言語Rubyでメソッド名などを自動補完する

Posted at 2011/09/15
このエントリーをはてなブックマークに追加

問題

静的型付け言語Haskellでの自動補完はHack #211で紹介しました。このときは補完候補の取得にghc-modという外部コマンドを用いました。補完候補を自動的に出力するためにneocomplcacheというVimプラギンを用いました。

動的型付け言語Rubyにおける自動補完はどのようにすれば達成できるでしょうか。Rubyのように非常に静的な解析が難しい言語では、メソッド名などの候補の取得が非常に困難であることが知られています。そもそも現在参照している式あるいは変数がどのクラスに属しているか、ほとんどのケースでは静的に決定することができません。クラスが分かったとしても、その変数のみが特異メソッドを持っているかもしれません。

解決(1/2)

Rubyでメソッド名を手動補完するために使えるものは、標準添付のvim-rubyというVimプラギンと、m2ymさんによって開発されたRSenseがあります。

vim-rubyは、+rubyであるVim環境(*1)における補完機能を提供します。 Ruby組み込み定数・クラス・グローバル関数(*2)などが補完できます。 詳しくは:h ft-ruby-omniを参照してください(*3)。 補完関数はオムニ補完として定義されているので、デフォルトの設定では、挿入モードで<C-x><C-o>と打鍵することで補完機能を実行することができます。

vim-rubyが諦めた部分は

  • 編集中ファイル内で定義された定数・クラス・メソッドなどへの補完
  • 文脈に応じたメソッド名の補完 (例えば1という数値オブジェクトに対してはFixnumクラスに定義されたメソッドのみが補完されるべき)

です。これらに対応するためには、Rubyのパーサなどを実装する必要があり、かなり大変です。またそもそも処理に時間がかかりすぎ、補完機構としはやりすぎと判断したのかもしれません(*4)。

図1: vim-rubyが正しく補完候補を生成している例

図2: vim-rubyが正しくない補完候補を生成してしまっている例

一方RSenseは、vim-rubyが提供している機能にさらに加えて、上記の諦めた部分にも対応しています。

図3: RSenseが正しく補完候補を生成している例

RSenseの補完関数もオムニ補完として定義されています。デフォルトでは、挿入モード時に<C-x><C-o>と打鍵することで補完のポップアップがでてきます。なお、初回起動時は若干時間がかかりますが、二回目からは非常にすばやいです(*5)。

解決(2/2)

手動補完がいかに実用的でないかは過去のVim Hacksで散々と述べられてきました。

通常の補完ではユーザーが明示的に補完のためのキーを押す必要がありました。つま り、「頭が補完をする」と考えなければ補完ができないのです。これにより、作業効 率が落ちてしまいます。それならばシステムが自動的に判断して、補完のためのキー を押したらどうでしょうか。これでユーザーはやらなければならない作業のみに集中 することができます。Vim7よりオムニ補完が実装されたので、Visual Studioのように 「関数やメンバを補完」は実現できるようになりました。しかし、そこには「自動的 に」が欠けているのです。

Shougo 2009 /vim-users-jp/2009/07/20/Hack-44.html

自動補完を用いましょう。RSenseはneocomplcacheと連携することで自動補完として使うことができます。

RSenseを公式ドキュメントに従ってインストールした上で、~/.vimrcに以下の記述を行ないましょう。

if !exists('g:neocomplcache_omni_patterns')
  let g:neocomplcache_omni_patterns = {}
endif
let g:rsenseUseOmniFunc = 1
if filereadable(expand('~/git/rsense/bin/rsense'))
  let g:rsenseHome = expand('~/git/rsense')

  let g:neocomplcache_omni_patterns.ruby = '[^. *\t]\.\w*\|\h\w*::'
endif

ただし、'~/git/rsense/bin/rsense''~/git/rsense'の部分を自身の環境に合致する内容にしてください。

なお、この設定は、neocomplcache作者により書かれた以下の記事を参考にしています。

http://vinarian.blogspot.com/2010/03/rsenseneocomplcache.html

補足

RSenseですら、特異メソッドには対応していないようです。

a = Object.new
def a.hello
  :world
end
a.

ここからhelloを補完することはできません。(*6)

脚注

  • *1 :versionして+rubyがあるかどうか調べてみてください。まあ、不必要です。
  • *2 本記事の読者がRuby使いには限らないことを考慮して”グローバル関数”と記述しましたが、実際にはRubyに関数は存在しません。Kernelなどに定義されているpなどのメソッドを便宜的にそのように読んでみました。
  • *3 なお、ドキュメントに書かれているからといって実際に動作するとは限りません。私の環境では実際に試してみると動作しないものがたくさんありました…。
  • *4 g:rubycomplete_buffer_loadingg:rubycomplete_classes_in_globalを設定し、かつ+rubyな環境であればそれらにも対応しているとドキュメントに書かれています。お試しください。常用するのはかなり難しいようです。
  • *5 初回起動時にこっそりと補完サーバを立ち上げるためです。なお、Windows環境の場合は特別な設定が必要なようです。詳しくは公式ドキュメントを参照ください。
  • *6 この例だとhelloメソッドが同じバッファで定義されているのでneocomplcacheのバッファ内キーワード補完が働き、helloが補完対象にあらわれます。が、これはオブジェクトaだけでなくすべての対象に働いてしまいます。また、helloの定義がそのバッファ内でないなら、当然ながらお手上げです。

参考文献

Vanrb Lightning Talk Slides: Ruby and Vim

非常にわかりやすい図がたくさんあります。

ujihisa

もどる
blog comments powered by Disqus