はじめに
popup_menu()
のコールバックに(globalに置くような無茶な渡し方などをせずに)上手く追加で値を受け渡す方法を探していて、一つ方法が見つかったのでそのシェアになります。
追記: コールバックの引数にwindowのidが渡されるので本来はwindowスコープの変数で上手く渡すのがいいみたいです。今回の記事はそれとは別でJSの無名関数チックなアプローチをしてみます。
自分が困ったケース
Vim scriptで選択肢のユーザーインターフェースといえばinputlist()
があったと思いますが、最近の機能追加でpopup_menu()
が追加され、ポップアップメニューの中からユーザーに選ばせるという選択も増えました。
自分のプラグイン(spelunker.vim)もこれまでinputlist()
を使っていたのですが、今回新たにpopup_menu()
も利用できるように修正を行おうと思ったのですが、下記のような問題があり、これまでのinputlist()
をそのまま置き換えるような修正はできなさそうでした。
これまでのinputlist()の実装例
" ユーザーが選ぶ
function select(arg)
let l:list = ['111', '222', '333']
let l:selected = inputlist(l:list)
" このあとl:selectedを使ってなんかする
endfunction
popup_menu()を使う場合の実装例
function select(arg)
" ユーザーが選ぶとコールバックが呼ばれる
let l:list = ['111', '222', '333']
call popup_menu(l:list, #{
\ callback: 'callbackFunction',
\ pos: 'topleft',
\ line: 'cursor+1',
\ col: 'cursor'
\ })
endfunction
function calbackFunction(id, selected)
" a:selectedを使ってなんかする
endfunction
ポップアップメニュー選択後、コールバックが呼び出されるようになります。
こうなってくるとselect()
の関数内で持っている変数で(a:arg
など)、このあとの処理でも使いたい変数はどうやってcalbackFunction()
に受け渡せばいいのか...などなど、これは設計にも影響してきそうです。
コールバックに追加で引数を渡す
こちらの記事のクロージャの項目の内容的なアプローチを取ってみます。
https://vim-jp.org/vim-users-jp/2010/08/13/Hack-167.html
" argをコールバックに渡したい!
function s:select(arg)
" 記事中のクロージャの項目の内容的なアプローチ
" https://vim-jp.org/vim-users-jp/2010/08/13/Hack-167.html
let l:callback = {'arg': a:arg}
function l:callback.funcall(id, selected) dict
call Popup_callback(a:id, a:selected, self.arg)
endfunction
let l:list = ['111', '222', '333']
call popup_menu(l:list, #{
\ callback: l:callback.funcall,
\ pos: 'topleft',
\ line: 'cursor+1',
\ col: 'cursor'
\ })
endfunction
function Popup_callback(id, selected, arg)
" ここでもs:select()のarg(a:arg)を使える!
endfunction
引数が上手く追加できましたね。
また、これは簡単にするとこうにもなります。
function s:select(arg)
let l:callback = {'arg': a:arg}
function l:callback.funcall(id, selected) dict
" callbackとしてやりたかった内容をここに移動します
" self.argもa:id, a:selectedも使えます
endfunction
let l:list = ['111', '222', '333']
call popup_menu(l:list, #{
\ callback: l:callback.funcall,
\ pos: 'topleft',
\ line: 'cursor+1',
\ col: 'cursor'
\ })
endfunction
最後の2つの書き方は好みや目的によって使い分け程度の話だと思います。共通化やコールバック関数のテストをしたければ前者ですし、簡潔さを求めるなら後者、のような。
これで一応、popup_menu()
の呼び出し元のスコープにある変数の受け渡しができました🙌