Vim Advent Calendar 2013 126日目:Vim で一時的に変更したオプションの値を安全に戻す

この記事は Vim Advent Calendar 2013 126日目の記事になります。
さて、プラグインなどを作っている場合、オプションなどの値を一時的に変更したい事があると思います。

function! Func()
    " 一時的に値を変更する
    let old_selection = &selection
    let &selection = 'inclusive'

    " ... なにか処理 ...

    " 値を戻す
    let &selection = old_selection
endfunction


しかし、上記のようなコードでは途中で例外が発生した場合、そこで処理が中断されてしまう可能性がある為、元の値に正しく戻さない可能性があります。

function! Func()
    " 一時的に値を変更する
    let old_selection = &selection
    let &selection = 'inclusive'

    " throw した場合、以降の処理が呼ばれない
    throw "homu"

    " 値が戻らない
    let &selection = old_selection
endfunction


こういう場合は finally を使用することで回避する事ができます。

function! Func()
    " 一時的に値を変更する
    let old_selection = &selection
    let &selection = 'inclusive'

    try
        " 例外が発生しても finally の処理は呼ばれる
        throw "homu"
    finally
        " 例外が発生しても呼ばれる
        let &selection = old_selection
    endtry
endfunction


また、 finally は例外以外にも途中で return した場合にも呼ばれます。

function! Func()
    " 一時的に値を変更する
    let old_selection = &selection
    let &selection = 'inclusive'

    try
        " 途中で関数を抜ける
        if g:flag
            return
        endif
    finally
        " 途中で return しても呼ばれる
        let &selection = old_selection
    endtry
endfunction


このようにオプションなどを一時的に変更する場合は finally を使用するとより安全に戻す事ができます。

[おまけ]

最近気づいたのですが、以下のように return した値を finally で書き換えるとどうなるのでしょうか。

function! Func()
    let old_selection = &selection
    let &selection = 'inclusive'

    try
        return &selection
    finally
        " return する値を書き換える
        let &selection = old_selection
    endtry
endfunction

let &selection = 'exclusive'
echo Func()
echo &selection

[答え]

inclusive
exclusive


どうやら return を呼び出した時点での値を返すようですね。

[おまけ2]

では、辞書の場合はどうだろうか。

function! Func()
    let result = { "name" : "homu" }
    try
        return result
    finally
        " 辞書の値に代入する
        let result.name = "mami"
    endtry
endfunction
echo Func().name

[答え]

mami


finally のタイミングで辞書の値を書き換えた場合は反映されるようですね。
ちなみにこれは copy() なりで防ぐことは可能です。

" copy() を返す
return copy(result)


面白そうなので何かに利用できないかなー。