2
2

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のpopup_menu()のcallbackに追加で引数を渡したい

Last updated at Posted at 2019-11-27

はじめに

popup_menu()のコールバックに(globalに置くような無茶な渡し方などをせずに)上手く追加で値を受け渡す方法を探していて、一つ方法が見つかったのでそのシェアになります。

追記: コールバックの引数にwindowのidが渡されるので本来はwindowスコープの変数で上手く渡すのがいいみたいです。今回の記事はそれとは別でJSの無名関数チックなアプローチをしてみます。

自分が困ったケース

Vim scriptで選択肢のユーザーインターフェースといえばinputlist()があったと思いますが、最近の機能追加でpopup_menu()が追加され、ポップアップメニューの中からユーザーに選ばせるという選択も増えました。

↓↓popup_menu()の例
スクリーンショット 2019-11-28 1.57.48.png

自分のプラグイン(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()の呼び出し元のスコープにある変数の受け渡しができました🙌

2
2
0

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
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?