Help us understand the problem. What is going on with this article?

uniteでfile_include的な挙動を実装する

More than 5 years have passed since last update.

導入

あけvim!
この記事はVim Advent Calendar 2013の34日目の記事です。
前日は @ne_sachirou さんの Vimで、文字数をstatuslineやlightline.vimに表示する でした。

さて、去年のVACで紹介した拙作pluginであるところのunite-javaimportに対して、
速度改善の目的でfile_include的な挙動を実装しましたので、今日はその話をしたいと思います。

リポジトリはこちら unite-javaimport

モチベーション

javaのインポート可能なクラスの数というのは、どうしても巨大になります。
最低限、jdk標準ライブラリに含まれるpublicクラスだけと考えたとしても、1万クラス以上あります。
unite.vimで1万以上の候補を表示させようとすると、世界が止まります(stop the world!)。
なので、何かしらの回避策を用意する必要がありました。
lingrのvim部屋で何か良い方法は無いかと質問を投げたところ、file_include的な挙動にすれば表示する候補数を絞れるんじゃないかと有り難い指摘を頂きました。

実装方針

javaのインポートの仕組みを考えてみます。
基本的にインポート可能なクラスは、publicなクラスのみです。
publicなクラスにネストしたpublicなクラスもインポート可能です。
またクラスはパッケージで修飾することが推奨されます。
これらの事実から、方針を以下のように定めました。

  1. 最初にuniteインタフェイス上にパッケージ名のみを列挙
  2. 選択されたパッケージ内に存在するクラスを列挙
  3. 選択されたクラス用のインポート文を挿入

以上の仕様にすることで、表示され得る候補数を、最大で数千程度まで減らすことができます。
数千程度なら、stop the worldを回避できそうです。

実装方法

では、早速本題であるところのfile_include的な挙動の実装方法について書いていきたいと思います。

まず、パッケージ名の列挙用のunite-kindを作成します。
名前は適当にexpandableとしました。

autoload/unite/kinds/expandable.vim
let s:kind= {
\   'name': 'expandable',
\   'default_action': 'expand',
\   'action_table': {},
\}

function! unite#kinds#expandable#define()
    return s:kind
endfunction

let s:action_expand= {
\   'is_start': 1,
\   'is_selectable': 0,
\}

function! s:action_expand.func(candidate)
    " unite-javaimportを再帰的に呼び出す
    " 通常の呼び出しと区別するため、引数を渡す
    call unite#start_script(['javaimport'], {'source__package': a:candidate.action__package})
endfunction

let s:kind.action_table.expand= s:action_expand

続いて、unite-javaimportのgather_candidates()から返却するcandidateのkindを、expandableにします。
また、クラス名の列挙の際には、kindは今まで通りjavatypeとします。

autoload/unite/sources/javaimport.vim
let s:source= {
\   'name': 'javaimport',
\}

function! unite#sources#javaimport#define()
    return s:source
endfunction

function! s:source.gather_candidates(args, context)
    " 説明のため、file_include的な処理と関係ない箇所は省略しています
    if has_key(a:context, 'source__package')
        let l:package= a:context.source__package

        " l:package以下に存在するクラス一覧を候補として返却
        return [
        \   {
        \       'word': 'hoge.fuga.Piyo',
        \       'kind': 'javatype',
        \   },
        \]
    else
        " パッケージ名の列挙
        return [
        \   {
        \       'word': 'hoge.fuga',
        \       'kind': 'expandable',
        \       'action__package': 'hoge.fuga',
        \   },
        \]
    endif
endfunction

まとめ

以上、説明のための簡易的な実装でしたが、これでunite.vimでfile_include的な挙動が実装できました。
候補数が巨大になってしまうようなunite-sourceも、これで便利できますね。

以上でVAC2013の34日目の記事を終わります。
明日は @cocopon さんです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした