Vim のユーザー定義補完について調べてみた


はじめに

最近 Vim 標準の補完機能について調べています。

以前は Vim の辞書ファイルからの補完について調べました。
Vim の辞書ファイルからのキーワード補完について調べてみた - Qiita

今回は、ユーザー定義関数による補完について使い方を調べてみました。

ユーザー定義補完とは

Vim のマニュアル から引用します。

ユーザー定義補完は、オプション 'completefunc' で設定した関数(ユーザー定義関数
でもよい)によって補完する方法である。

自前で指定した関数を使って、補完候補を準備できるようです。

使い方

ユーザー定義の関数の指定方法は、Vim マニュアルの complete-functions という項目に書かれています。

Vim では挿入モードで CTRL-X CTRL-U でユーザー定義の補完を呼び出すことができます。 ユーザー定義の補完は変数 completefunc に指定された関数を使って行われます。completefunc に指定の形式に沿った補完用の関数を作って指定することで、Vim で独自の補完を利用することができるようになります。

completefunc に指定された関数は、補完開始時に2回呼び出されます。

  1. 最初に補完するテキストの始点を見つける
  2. 実際に候補を検索する

completefunc の関数は findstartbase の2つの引数を取ります。それぞれの呼び出しの際に渡される値は以下のとおりです。

  1. findstartが1、base がempty
  2. findstartが0、base が補完対象のテキスト

上記の動作に従って関数を準備する必要があります。

サンプル関数

最初に、Vim マニュアルを参考にしたサンプル関数を載せます。こちらをもとに説明します。

前回の記事と同様の例として、Git のコミットメッセージのプレフィックスを候補にする関数にしました。(参考:angular.js/DEVELOPERS.md at master · angular/angular.js · GitHub)

fun! CompleteGitCommitPrefixes(findstart, base)
  if a:findstart
    let line = getline('.')
    let start = col('.') - 1
    while start > 0 && line[start - 1] =~ '\a'
      let start -= 1
    endwhile
    return start
  else
    let res = []
    for m in split("feat: fix: docs: style: refactor: perf: test: chore:")
      if m =~ '^' . a:base
        call add(res, m)
      endif
    endfor
    return res
  endif
endfun
set completefunc=CompleteGitCommitPrefixes

挿入モードで CTRL-X CTRL-U と入力すると、以下のような補完候補が表示されます。

1回目呼び出し:最初に補完するテキストの始点を見つける

最初の呼び出しにおいて、if a:findstart1 のブロックが呼ばれます。ここでは補完テキストの始点を探すための処理を実施しています。

getline('.') でカーソルのある行文字列を取得し、col('.') - 1 でカーソル位置の直前を最初に取得します。

その後、while ループで line[start - 1] が英字を表す文字クラス \a の場合に start の番号を前に1ずつずらし、最初の単語位置を特定しています。

2回目呼び出し: 実際に候補を検索する

2回目の呼び出しにおいて、if a:findstart0 のブロックが呼ばれます。ここでは補完候補を表示する処理が実施されます。

変数 a:base には、1回目呼び出しで抽出した補完対象テキスト文字列が入っています。その文字列から始まる候補を for 文でループして検索し、マッチした候補のみ res の配列に格納し、配列を返しています。

この配列が補完候補として表示されます。

配列の要素の形式は Vim マニュアル complete-itemsの項目にもある通り、文字列か辞書形式であれば OK です。

おわりに

マニュアルから動作を仕組みを汲み取って関数を準備するというスタイルが面白いですね。もっと読み込んでいこうと思います。

参考