Vim で Go 言語を書いている時、Neosnippet でいい感じに if する
今回は、Neosnippet
x Go
x if
にフォーカスを当てた話をしようと思います。
Neosnippet とは?
みなさん、Neosnippet を知っていますか? && 使っていますか?
Neosnippet はいい感じに snippet を展開してくれる Vim プラグインです。
Neosnippet 自体については、GitHub や下記ブログ記事などを参考にしていただければと思います。
- Shougo/neosnippet.vim
- Vimプラグイン初心者がスニペット機能を導入して、独自スニペットを追加できるまでの流れ - Qiita
- neosnippet.vim を活用しよう - C++でゲームプログラミング
Go言語とは
Go言語とは、プログラミング言語です。
Go言語の特徴の一つに、例外機構が存在しないことがあります。
また、関数が多値を返すことができます。
そのため、Go言語ではエラー情報を関数の呼び出し元に伝える時に戻り値にerror
を含めて返すことが一般的です。
func Foo() (string, error) { err := DoSomething() if err != nil { return "", err } return "foo", nil }
このように、err
をチェックしてnil
でなければそのまま返す、といったコードを書くことがとても多いです。
Neosnippet を使用した開発上のサポート
Neosnippet 標準のスニペット定義では、以下の様なif
スニペットが Go言語用に定義されています。
snippet if options head if ${1:err != nil} { ${0:TARGET} }
neosnippet-snippets/go.snip · Shougo/neosnippet-snippets
しかし前述した通りGo言語ではif文を使用する際にエラーチェックをしてエラーを返すことが多いため、私は以下の様にsnippetを上書きして定義していました。
snippet if options head if ${1:err != nil} { ${0:return err} }
これでエラーチェックをはさみたい場合にreturn err
までしてくれるようになりました。便利。
上記手法の問題点
しかし、上記の手法には問題点があります。
それは、多値を返す関数に対応していないことです。
func() (string, error)
の様なシグネチャの関数内で先ほどのスニペットを使用しても、return err
しか補完されません。
この関数の戻り値はstring, error
なので、return "", err
のようなものが展開されることを期待したいです。
解決
そこで、上記の問題を解決したスニペットを作成しました。
Installation
2015/12/23 追記
Shougo/neosnippet-snippets 本体に以下のコードが取り込まれました。
@p_ck_ 実装が終わり、関数を取り込みました。スニペットはifeへと変更されています。ご確認ください
— 暗黒美夢王(deoplete dev) (@ShougoMatsu) 2015, 12月 23
そのため、neosnippet-snippets さえインストールしていれば、インストールのために特別な操作は必要ありません。
また、下記使用例でif<C-k>
としていますが、neosnippetにはife
というスニペット名で取り込まれたため、ife<C-k>
と入力することでスニペットを展開することが出来ます。
追記ここまで
Neosnippet はインストールされている前提です。
下記の Vim Script を .vimrc
に貼り付けてください。
function! GoIfSnip() abort let re_func = '\vfunc' let re_type = '%(%([.A-Za-z0-9*]|\[|\]|%(%(struct)|%(interface)\{\}))+)' let re_rcvr = '%(\s*\(\w+\s+' . re_type . '\))?' let re_name = '%(\s*\w+)?' let re_arg = '\(%(\w+%(\s+%(\.\.\.)?' . re_type . ')?\s*,?\s*)*\)' let re_ret_v = '%(\w+)' let re_ret = '%(\s*\(?(\s*\*?[a-zA-Z0-9_. ,]+)\)?\s*)?' let re_ret_body = '%(' . re_ret_v . '|%(' . re_ret_v . '\s*' . re_type . ')|' . re_type . '\s*,?\s*)*' let re_ret = '%(\s*\(?\s*(' . re_ret_body . ')\)?\s*)?' let re = re_func . re_rcvr . re_name . re_arg . re_ret . '\{' let lnum = line('.') let ret = "" while lnum > 0 let lnum -= 1 let ma = matchlist(getline(lnum), re) if len(ma) == 0 continue endif let ret = ma[1] break endwhile if ret =~ '\v^\s*$' return '${0:return}' endif let rets = [] for t in split(ret, ',') if t =~# '\v^\s*error\s*$' let v = 'err' elseif t =~# '\v^\s*string\s*$' let v = '""' elseif t =~# '\v^\s*int\d*\s*$' let v = '0' elseif t =~# '\v^\s*bool\s*$' let v = 'false' else let v = 'nil' endif call add(rets, v) endfor return '${0:return ' . join(rets, ", ") . '}' endfunction
また、下記の内容を go.snip
として適当に Neosnippet のパスが通った場所に配置して下さい。
snippet if options head if ${1:err != nil} { `GoIfSnip()` }
以上です。
そのうちプラグインとして切り出したいと思っています。
しかし、どうしてもneosnippet標準のスニペットに上書きされてしまうので諦めました、誰か解決方法知っている方がいたら教えて下さい…
Usage
使い方は簡単です。
適当な関数の中で、if<C-k>
を押します(<C-k>
はNeosnippetの設定によって変わりますが、snippetを展開するためのキーです)。以上です!
言葉では説明しづらいので、以下のGif動画を参考にしてください。
3つの関数内でif<C-k>
を入力していますが、全て関数のシグネチャに沿った戻り値が挿入されていることに気がつくと思います。
ほか
ブログ書いている途中にこんな記事が流れてきました、便利そう。