Vim script入門 - コマンドでの引数の受け取り方について

# 最初に

今年密かに立てた目標の一つがVim scriptを書いてみること。Vimで自作コマンドを作ってみること。
Vimを使っていながらこれらのことを敷居の高いものと勝手に感じていた自分を見直すため、Vimで基本的な部分を調べながらサンプルを書いてみました。
(今回は主に、関数における引数の受け取り方について調べました)

なお、これは個人的な備忘録(個人的学習ノート)となります。

ここである程度、基本的な事柄に触れたら、vim-jpのドキュメントを一通り読んでみて理解を深めていこうと思います！

[help - Vim日本語ドキュメント](http://vim-jp.org/vimdoc-ja/)



# コマンドラインコマンドを定義してみる

作業ディレクトリ内で``sample.vim``という名前で作成し、下記のように記述してみます。

```vim
command! -nargs=0 Test echo "This is Test!"
```

同じディレクトリ名でもう一つVimを開き、``:source sample.vim``と打ってファイルを読み込んでから、``:Test``とVim内で打つと、``This is Test``と表示されます。

とやっても良いのですが、
``sample.vim``を開いているエディター内で``:source sample.vim``を打てば、自分を読み込んでそのまま``:Test``と打てるようです。楽ちん！


ちなみに``command!``とやると、コマンドを再定義することになり、既存のコマンドの内容が上書きされることになります。
上の処理を実行したあとで、Testコマンドをcommandに書き換えて、再度``:source sample.vim``を打つと、既にコマンドがあります、というエラーが表示されます。


# 基本となる関数の作成

処理する内容にもよりますが、処理内容はだいたい関数化するようです。
基本的な関数の呼び方は下記の通り。

```vim
command! -nargs=0 Hello call Hello()
function! Hello()
  echo "Hello!!"
endfunction
```

ちなみに関数名を``s:Hello``とした場合はローカル関数になるようです。


# 関数の引数について


## 引数を一つ取りたい場合

引数を取る関数を作成する場合、下記のように引数部分に``<f-args>``を使うようです。
また、関数内で引数として取得した変数については``a:``が付くようなので、こちらも忘れないように気をつけます。

```vim
command! -nargs=1 Hi call Hi(<f-args>)
function! Hi(name)
  echo "Hi " . a:name
endfunction
```

以上の関数を記述した後、vim内で自身を読み込み(``:source sample.vim``)、コマンドを打ってみると下記のようになります。

```vim
:Hi Merry
" => Hi Merry
```

## 引数を0、もしくは1つ、取りたい場合

引数を0 or 1つで取りたい場合は``-nargs=?``とするようです。
この場合、関数での引数は可変長引数である``...``に変更する必要が出てきます。
(名前付き引数では、渡されないとエラーとなります。)

なお、取得した引数については、
``a:0``で引数の数、
``a:1``で引数の中身を取得できるようです。


```vim
command! -nargs=? Hi call Hi(<f-args>)
function! Hi(...)
  if a:0 >= 1
    echo "Hi " . a:1
    echo a:0
    echo a:1
  else
    echo "Hi..."
  end
endfunction
```

実際に実行してみた結果は下記。

```vim
:Hi Merry
" => Hi Merry
" => 1
" => Merry
```

## 引数を一つだけ取りたい場合

``-nargs=1``とするようです。

この場合、引数を付けないと"引数が必要です"とエラーが表示されます。

```vim
command! -nargs=1 Hi call Hi(<f-args>)
function! Hi(name)
  echo "Hi " . a:name
endfunction
```

関数の引数を可変長引数にしても、同じように書けるようです。
(もちろん、引数は付けないとエラー)

```vim
command! -nargs=1 Hi call Hi(<f-args>)
function! Hi(...)
  echo a:0
  echo "Hi " . a:1
endfunction
```


## もっとたくさんの引数を取りたい！

``-=nargs=*``で取れるようです。
関数の引数は可変長引数とし、
a:0で引数の数、
a:nでn番目の引数、
a:000ですべての引数の配列を取得できるようです。

```vim
command! -nargs=* Hey call Hey(<f-args>)
function! Hey(...)
  if a:0 >= 1
    echo "name count : " . a:0
    let res = ["Hey! "]
    let nlist = a:000
    for n in nlist
      call add(res, n)
    endfor
    echo join(res)
  else
    echo "Hey..."
  end
endfunction
```

for文を使いたくて上のように書いてしまいましたが、extend関数を使えば、リストにリストを連結することができるようなので、下記のような書き方がベターですかね。


```vim
command! -nargs=* Hey call Hey(<f-args>)
function! Hey(...)
  if a:0 >= 1
    echo "name count : " . a:0
    let res = ["Hey! "]
    call extend(res,a:000)
    echo join(res)
  else
    echo "Hey..."
  end
endfunction
```

実行結果は、下記のようになります。

```vim
:Hey　
" ⇒ Hey...

:Hey Merry Atom Bob
" ⇒  Hey!  Merry Atom Bob
```

## 引数を1個以上パターン

``-nargs=+``を使うよう。
引数なしだとエラーになります。


```vim
command! -nargs=+ HeyHey call HeyHey(<f-args>)
function! HeyHey(...)
  if a:0 >= 1
    echo "name count : " . a:0
    let res = ["Hey! "]
    call extend(res,a:000)
    echo join(res)
  else
    " 引数なしはエラーになるため、ここには到達しない
    echo "Hey..."
  end
endfunction
```

可変長引数じゃないパターン


```vim
command! -nargs=+ HeyHey2 call HeyHey2(<f-args>)
function! HeyHey2(n1, n2, n3)
  let res = ["HeyHey! ", a:n1, a:n2, a:n3]
  call extend(res,a:000)
  echo join(res)
endfunction
```


ひとまず今回はここまで。
実際に触れてみることで、一気に身近に思えてきました！
今まではVimの自作関連は遠目に見ているだけでしたが、今年は自分からも積極的に作っていけたらと思います。


# 参照

[usr_40 - Vim日本語ドキュメント](http://vim-jp.org/vimdoc-ja/usr_40.html#40.2)

[usr_41 - Vim日本語ドキュメント](http://vim-jp.org/vimdoc-ja/usr_41.html)

[vim-jp >> Hack #158: ユーザコマンドを定義する](http://vim-jp.org/vim-users-jp/2010/06/29/Hack-158.html)

[eval - Vim日本語ドキュメント](http://vim-jp.org/vimdoc-ja/eval.html)

[引数が 0 or 1 個の関数を呼ぶExコマンドを定義する方法](https://qiita.com/kasei-san/items/060bbd267ddc0cca5068)

[はじめてのvimscriptとvimプラグインの作成](http://qs.nndo.jp/2017/08/12/643/)

[VimScriptざっくりチュートリアル(関数編)](http://d.hatena.ne.jp/kasei_san/20130711/p1)