2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Vim script で「あとで処理を実行する」ための定型処理を vital の外部モジュール化した

Last updated at Posted at 2018-03-08

Vim のプラグインを書くときに、後でこの処理を実行したい、ということがしばしばあります。この場合二つの選択肢があり、自動コマンド (:help :autocmd) かタイマー (:help timer) が使えます。ところがこの二つは全く異なるインターフェースをもっており、似たような目的であっても全く違った記述をしなければなりません。また組み合わせて使う場合にはコードが複雑になりがちです。

また、Vim script あるあるの一つだと思うのですが、一回だけ自動コマンドで処理したい、ということがよくあります。この場合自動コマンドイベントに関数をセットし、その関数自身が自動コマンドを解除することで実現します。

function! s:main() abort
  " set the autocmd event for s:run_once()
  augroup my-group
    autocmd!
    autocmd WinLeave * call s:run_once()
  augroup END
endfunction

function! s:run_once() abort
  call s:do_something()

  " unset the autocmd events
  augroup my-group
    autocmd! WinLeave
  augroup END
endfunction

が、正直毎回これを書くのは面倒くさいです。そもそも実行したいのは s:do_something() なのに、s:run_once() のような関数でラップする手間がかかります。このような定型作業は繰り返したくありません。

vital-Schedule をつかう

vital-Schedule

vital-Schedule は上のような不満を解決するためのモジュールです。上の例を vital-Schedule を使って書き直してみます。

let s:Schedule = vital#{pluginname}#new().import('Schedule')

function! s:main() abort
  " set task
  let task = s:Schedule.Task()
  call task.call(function('s:do_something'), [])
  call task.waitfor(['WinLeave'])
endfunction

task.call() メソッドは組み込みの call() 関数と同じインターフェースを持っています。このメソッドに渡された関数が後で実行されます。これに直接 s:do_something() を渡すことにより、 s:run_once() が必要なくなりました。自動コマンド周りの諸々の手続きは vital-Schedule が代替します。

もっと具体的な例を考えてみましょう。例えば何らかの編集を行い、その行を一定時間ハイライトしたい場合を考えてみましょう。ハイライトはタイマーをつかい時間経過で消すのですが、ユーザーがそれ以前に次の編集を開始した場合にも消したいとします。つまり、タイマーと自動コマンドの組み合わせです。

let s:Schedule = vital#{pluginname}#new().import('Schedule')

function! s:highlight(lnum, duration) abort
  let id = matchaddpos('MyPluginHigroup', [a:lnum])

  " set task
  let task = s:Schedule.Task()
  call task.call('matchdelete', [id])
  call task.waitfor([a:duration, 'TextChanged', 'TextChangedI'])
endfunction

s:highlight() 関数に行番号とハイライトの持続時間を渡すと一時ハイライトをしてくれます。(実際にはバッファやウィンドウを移動した場合なども考える必要があるかもしれません)

タイマーと自動コマンドを組み合わせて、「どれか最初に発火したもの」という条件で登録した関数を実行しています。


コンストラクタ (のようなもの) に文字列を渡すことで augroup を指定することができます。これは省略可能で、省略した場合は vital-Schedule が使われます。 augroup なしではありません。人類は augroup なしの自動コマンドを定義すべきではありません。

let Schedule = vital#{pluginname}#new().import('Schedule')
let task = Schedule.Task('my-group')

あるいは augroup() 関数を使うと augroup の固定されたモジュールのコピーが使えます。下の例は上の例と同じ意味になります。

let Schedule = vital#{pluginname}#new().import('Schedule').augroup('my-group')
let task = Schedule.Task()

あまり vital.vim のことをよくわからずに書いているので、おかしいところがあったら教えていただけるとありがたいです。 :help vital.vim すると、 How to make a vital moduleExternal vital modules へのリンクがありましたが前者の情報は古いっぽい? :help から読めるとうれしいんだけど翻訳するのは大変そう。

あと今回、辞書関数を積極的に使ってみたらスタックトレースに関数名が出ず、デバッグ作業が地獄と化しました…なにかいい方法はないものか…

2
3
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?