VimScriptざっくりチュートリアル(関数編)

関数宣言

:function Func(flg) " グローバルスコープの場合、関数名はアルファベットの大文字で始まる
:  if a:flg         " 引数の参照は、a:引数名
:    echo "true"
:  else
:    return "false" |" 戻り値を返したい場合、return コマンドを使う
:  endif
:endfunction

関数の呼び出し

:call Func(0)       " call コマンドは、渡された関数を実行するコマンド。この場合、false が返る
:let test = Func(1) " 戻り値を変数に格納したい場合
:echo Func(1)       " 戻り値をそのまま echo したい場合

関数の再定義


.vimrc に関数を定義した後で、:source .vimrc とかして、一度定義した関数が再定義されるとエラーになる
function! で、関数の再定義ができるので、.vimrc に関数を書く場合は、何度も呼ばれることを想定して function! で関数を定義したほうが良い

:function! Func(flg)
  ...
:endfunction

ローカル関数


関数は、スクリプトローカルのみ指定できる

:function! s:Func(flg)
  ...
:endfunction

FuncRef


関数ポインタ的な感じに関数への参照を変数に格納できる

:let FuncRef = function('Func') " グローバルスコープの場合、FuncRef 変数も大文字で始まる必要がある
:call FuncRef(0)

例外処理


VimScriptは、エラーが発生しても処理を止めない
なので、エラーが発生した時、処理を飛ばしたい場合は、try, catch 構文を使うか、abort 識別が必要


:function! ExceptionTest()
:  call aaaa() " 未定義の関数
:  return "???"
:endfunction
:
:echo ExceptionTest() |" エラーメッセージの後に、"???" が表示される
try, catch 構文
:function! TryCatchTest()
:  try
:    call aaaa() " 未定義の関数
:    return "???"
:  catch
:    " 例外処理
:  finally
:    return "finally"
:  endtry
:endfunction
:
:echo TryCatchTest() |" "finally" が表示される
abort 識別


関数宣言に abort 識別を付けることで、エラーが発生した時に、即時に関数を抜けるようにできる

:function! AboutTest() abort
:  call aaaa() " 未定義の関数
:  return "???"
:endfunction
:
:echo AboutTest() |" エラーメッセージが表示されて、-1 が返る

関数の範囲指定呼び出しについて


VimScriptでは行を加工する関数、コマンドは、範囲指定をして呼び出すことが出来る
範囲指定をした場合は、範囲の行を1行ずつカレントとして関数が呼び出される


例えばこんなテキストがあった場合

1
2
3
4
5


こんな関数を用意する

:function! Increment() |" カレント行を数値と見なして、+1 する関数
:  let line = str2nr(getline('.'), 10)   |" カレント行の取得(後述)
:  let line += 1
:  call setline('.', printf("%d", line)) |" カレント行の更新(後述)
:endfunction


で、以下のコマンドを実行する

:%call Increment() " 1行目〜最後行に対して、Increment を実行

すると、各行に対して、Increment が実行されて、以下のようになる


2
3
4
5
6
行指定子


行や範囲を指定する場合、以下の文字が使える

  • 数値 : 1から始まる行番号(0の場合大抵のコマンドは行頭と解釈する)
  • . : カレント行
  • $ : 行末
  • % : 行頭〜行末


:" delete は指定した行 or 範囲を削除するコマンド
:3delete    " 3行目を削除
:4,10delete " 4〜10行目を削除
:.,$delete  " カレント行〜行末まで削除
:%delete    " 全部削除

関数の range 識別について


range 識別をつけることで、範囲指定されることを前提とした関数を用意することができる

:function! Increment2() range
:  for n in range(a:firstline, a:lastline) " a:firstline, a:lastline に範囲指定時の行番号が格納される
:    let line = str2nr(getline(n), 10)
:    let line += 1
:    call setline(n, printf("%d", line))
:  endfor
:endfunction


上の例の場合、挙動は Increment とおんなじだけど、関数呼び出しが1回になるので、速度面や、特定の行を保持して、他の行に何かしたい時とかに便利