Open9

VimでEasyMotionみたいなものを作ってみる

kuukuu

coc-smartfというcoc.nvimの拡張機能がEasyMotionみたいな動きをしてくれるので中身を覗いてみたら案外シンプルだったので真似しながら要所を書き出していこうと思う

kuukuu

EasyMotionは大体こんな感じの動きをする

  • 画面に映っている範囲から条件にマッチする場所を抽出
  • 抽出した範囲にラベルを付ける
  • キーを取得してラベルとマッチしたらその場所へジャンプ
kuukuu

画面に表示されている範囲はVim scriptのline()関数を使うことで

let start = line('w0')
let end = line('w$')

のように取得できる(line()のヘルプに書いてある)

kuukuu

マッチ場所を取得する方法は色々あるが(coc-smartfはNode.js側で自力で全走査している)面倒なのでVimの機能を使って収集する

kuukuu

Vim scriptにはsearch()という関数があり、これを使うと指定した正規表現にマッチする場所へのジャンプを範囲を限定して行える。

kuukuu

その前に、search()を使うとカーソルの位置等が動いてしまうので保存しておく。

Vim scriptにはwinsaveview()でウィンドウの状態をオブジェクト化し、winrestview()で元の状態に戻せる機能があるので事前に保存しておく

let view = winsaveview()
kuukuu

まず、先ほど取得した先頭位置を利用し、次のコマンドを実行する

execute start

少し見た目からは分かりづらいが、executeは引数をVim scriptの式として解釈し、更にその結果をExコマンドとして解釈、実行する。
Exコマンドで行番号を実行すると指定した位置に飛べるので、これを実行するとカーソルがウィンドウの開始地点に移動する。

これだめなので後で書き直す

kuukuu

そしてひたすらsearch()を実行して集めていく。この関数はマッチした場合は行番号、しない場合は0を返すのでwhileで実行できる。
line()及びcol()の結果を配列にしておく。(この形にしておくと後述するマッチ位置の表示関数でそのまま利用できる)
ついでに、現在位置を起点としたソートを行うためにwordcount()により位置のバイト数情報を取得しておく。(別にlineとcolの情報でもできなくはないがソート動作が複雑になるのでしない)

let re = input('> ')
let found = []
while search(re, '', end)
  call add(found, [wordcount().cursor_bytes, [line('.'), col('.')]])
endwhile
kuukuu

最後にウィンドウの状態を元に戻す

call winrestview(view)