vim-jp / vimdoc-ja / usr_41

usr_41 - Vim日本語ドキュメント

メインヘルプファイルに戻る English | 日本語
usr_41.txt    For Vim バージョン 8.0.  Last change: 2017 Mar 01

                     VIM USER MANUAL - by Bram Moolenaar

                              Vim script 書法


Vim script 言語は vimrc ファイルや構文ファイルなど、さまざまな目的に使われま
す。この章では Vim script の書き方を説明します。説明することがたくさんあるので
大きな章になってます。

41.1  はじめに
41.2  変数
41.3  式
41.4  条件式
41.5  式を実行する
41.6  関数を使う
41.7  関数を定義する
41.8  リストと辞書
41.9  例外
41.10 注意事項
41.11 プラグインを書く
41.12 ファイルタイププラグインを書く
41.13 コンパイラプラグインを書く
41.14 プラグインを書く (高速ロード版)
41.15 ライブラリスクリプトを書く
41.16 Vim script を配布する

次章: usr_42.txt  新しいメニューを追加する
前章: usr_40.txt  新しいコマンドを作る
目次: usr_toc.txt

==============================================================================
41.1  はじめに                                vim-script-intro script

誰もが最初に触れる Vim script は vimrc ファイルです。Vim が起動するときに読
み込まれ、書かれているコマンドが実行されます。それにより好きなように設定を変更
できます。vimrc の中ではすべてのコロンコマンドが使えます (":" で始まるコマンド
のこと。Ex コマンドやコマンドラインコマンドと呼ばれることもある)。
シンタックスファイルも Vim script です。シンタックスファイルは、ファイルタ
イプ別にオプションを設定するファイルの一種です。複雑なマクロ定義を別ファイルに
分けて保存しておくこともできます。このように、いろいろな使用方法が考えられま
す。

簡単な例から始めましょう:

        :let i = 1
        :while i < 5
        :  echo "count is" i
        :  let i += 1
        :endwhile

        Note:
        実際には ":" を書く必要はありません。":" が必要なのはコマンドラインで
        入力するときだけです。Vim script ファイルを書くときは省略できます。
        このヘルプでは、コロンコマンドであることを強調し、ノーマルモードと区別
        するためにコロンを表記しています。
        Note:
        例文をヤンクして :@" コマンドで実際に実行できます。

出力は次のようになります:

        count is 1
        count is 2
        count is 3
        count is 4

一行目では ":let" コマンドで変数に値を代入しています。書式は次のとおりです:

        :let {変数名} = {式}

例では、変数名が "i"、式が 1 です。
":while" コマンドでループを開始します。書式は次のとおりです:

        :while {条件式}
        :  {ステートメント}
        :endwhile

条件式が真である間、ステートメントが実行されます。例では、条件式は "i < 5" で
す。これは、i が 5 より小さい場合に真になります。
        Note:
        何かのミスで while ループが止まらなかった場合は、CTRL-C を押せば中断で
        きます (MS-Windows では CTRL-Break)。

":echo" コマンドは引数を出力します。例では、"count is" という文字列と、変数 i
の値を出力しています。i が 1 なら、次のように表示されます:

        count is 1

":let i += 1" は ":let i = i + 1" と同じ意味です。変数 i に 1 を加算し、新しい
値を同じ変数に代入します。

上述の例は、実際にはもっと簡潔に書くことができます:

        :for i in range(1, 4)
        :  echo "count is" i
        :endfor

:for と range() の説明はもっと先です。すぐに知りたい人はリンク先にジャンプ
してください。


三種類の数値
------------

数値は10進数、16進数、8進数のいずれかで表記します。16進数は "0x" か "0X" で開
始します。例えば "0x1f" は10進数の 31 です。8進数は "0" で開始します。例えば
"017" は10進数の 15 です。注意: 10進数で書くときは先頭に "0" を付けないでくだ
さい。8進数として扱われてしまいます。
":echo" コマンドは常に10進数で出力します。例:

        :echo 0x7f 036
        127 30

数値にマイナス記号を付けると負数になります。8進数や16進数も負数にできます。マ
イナス記号は減算記号としても使われます。次の例を上の例と比べてみてください:

        :echo 0x7f -036
        97

式の途中にある空白は無視されますが、可読性を高めるために、適切に空白で区切るこ
とをお勧めします。例えば上記の数値が負数であると勘違いしてしまわないように、マ
イナス記号と数値の間に空白をいれましょう:

        :echo 0x7f - 036

==============================================================================
41.2  変数

変数名にはアルファベット、数字、アンダースコアが使えます。変数名を数字で開始す
ることはできません。次のような変数名が使えます:

        counter
        _aap3
        very_long_variable_name_with_underscores
        FuncLength
        LENGTH

"foo+var" や "6var" のような名前は使えません。
例に挙げた変数はグローバル変数です。定義されている変数の一覧を見るのは次のコマ
ンドを使います:

        :let

グローバル変数はどこでも使えます。そのため、あるスクリプトファイルで "count"
という変数を使ったとき、その変数は他のスクリプトでも使われている可能性がありま
す。これは混乱を招きますし、トラブルの元です。それを避けるには "s:" を付けてス
クリプトローカル変数を使います。例えば、次のように使います:

        :let s:count = 1
        :while s:count < 5
        :  source other.vim
        :  let s:count += 1
        :endwhile

"s:count" はスクリプトローカル変数なので、他のスクリプトファイルによって変更さ
れる心配はありません。他のスクリプトファイルで "s:count" 変数が使われていたと
しても、それは別の変数です。スクリプトローカル変数についての詳細は
script-variable を参照してください。

変数の種類は他にもあります。internal-variables 参照。次の変数がよく使われま
す:

        b:name          バッファローカル変数
        w:name          ウィンドウローカル変数
        g:name          グローバル変数 (関数内では g: 必須)
        v:name          Vim が定義する変数


変数の削除
----------

変数はメモリを消費します。":let" コマンドの出力にも表示されます。変数を削除す
るには ":unlet" コマンドを使います。例:

        :unlet s:count

スクリプトローカル変数の "s:count" が削除され、使用されていたメモリが開放され
ます。変数が存在しない場合でもエラーを起こしたくない場合は ! を付けてください:

        :unlet! s:count

スクリプトの実行が終了したとき、ローカル変数は自動的には削除されません。次に同
じスクリプトを実行したときにその変数を使うことができます。例:

        :if !exists("s:call_count")
        :  let s:call_count = 0
        :endif
        :let s:call_count = s:call_count + 1
        :echo "called" s:call_count "times"

"exists()" 関数は変数が定義されているかどうかをチェックします。引数に調べたい
変数の名前を指定します。変数自体を指定するのではありません。例えば:

        :if !exists(s:call_count)

これは、s:call_count の値を変数名として exists() 関数を呼び出しているので、意
味が違ってしまいます。
感嘆符 (! 記号) は値を反転します。値が真なら偽になり、偽なら真になります。この
記号は "not" と読むことができます。つまり、"if !exists()" は "if not exists()"
と読むことができます。
Vim では、0 以外の値はすべて真です。0 は偽です。
        Note:
        数値が必要なところで文字列を使ったとき、文字列は自動的に数値に変換され
        ます。文字列の先頭が数字ではなかった場合は 0 に変換されます。つまり:
                :if "true"
        "true" は 0 に変換されるので偽になります。


文字列変数と定数
----------------

ここまでは変数の値に数値だけを使っていましたが、文字列を使うこともできます。
Vim は数値と文字列を基本型としてサポートしています。変数は動的に型付けされま
す。型は ":let" コマンドで変数に値を代入するたびに変化します。詳しくは41.8
を参照してください。
変数に文字列を代入するには文字列定数を使う必要があります。文字列定数には二つの
種類があります。一つはダブルクォート文字列です:

        :let name = "peter"
        :echo name
        peter

文字列の中でダブルクォートを使いたい場合は、バックスラッシュを前置してくださ
い:

        :let name = "\"peter\""
        :echo name
        "peter"

バックスラッシュを使いたくない場合はシングルクォート文字列を使ってください:

        :let name = '"peter"'
        :echo name
        "peter"

シングルクォート文字列の中ではすべての文字がそのまま使われます。ただし、シング
ルクォートだけは特別で、一つのシングルクォートを表すためには二つのシングル
クォートを書く必要があります。バックスラッシュはそのまま使われるので、特殊文字
は使えません。
ダブルクォート文字列の中では特殊文字が使えます。次のようなものがあります:

        \t              <Tab>
        \n              <NL>, 改行
        \r              <CR><Enter>
        \e              <Esc>
        \b              <BS>, バックスペース
        \"              "
        \\              \, バックスラッシュ
        \<Esc>          <Esc>
        \<C-W>          CTRL-W

最後の二つはただの一例です。"\<name>" 形式で "name" という特殊キーを使うことが
できます。
文字列で使える特殊表記については expr-quote を参照してください。

==============================================================================
41.3  式

Vim の式は高機能でシンプルです。式の定義については expression-syntax を参照
してください。ここでは基本的なことだけを説明します。
数値と文字列と変数はそれ自体が式です。つまり、式が必要なところでは数値でも文字
列でも変数でも使えます。他にも次のようなものが使えます:

        $NAME           環境変数
        &name           オプション
        @r              レジスタ

例:

        :echo "The value of 'tabstop' is" &ts
        :echo "Your home directory is" $HOME
        :if @a > 5

&name 形式を使うと、オプションを保存し、別の値に設定し、何かを実行して、オプ
ションを元に戻す、というようなことができます。例:

        :let save_ic = &ic
        :set noic
        :/The Start/,$delete
        :let &ic = save_ic

'ignorecase' オプションをオフにしてから "The Start" パターンを検索しています。
しかし設定は変更されません。(パターンに "\C" を加える方法でも同じことができま
す。/\C 参照。)


数値計算
--------

基本的な要素を組み合わせると面白くなってきます。まずは数値計算です:

        a + b           加算
        a - b           減算
        a * b           乗算
        a / b           除算
        a % b           剰余演算(余りを得る)

演算子の優先順位は一般的な規則と同じです:

        :echo 10 + 5 * 2
        20

カッコを使って優先順位を変更できます:

        :echo (10 + 5) * 2
        30

文字列は "." で連結できます:

        :echo "foo" . "bar"
        foobar

":echo" コマンドに複数の引数を指定すると、スペースで区切られて表示されます。こ
れらの例では一つの式しか使われていないので、スペースは挿入されていません。

C 言語と同じ条件演算子も使えます:

        a ? b : c

"a" が真なら "b" が使われ、そうでなければ "c" が使われます。例:

        :let i = 4
        :echo i > 5 ? "i is big" : "i is small"
        i is small

被演算子の部分は優先的に評価されるので、次のように見なすことができます:

        (a) ? (b) : (c)

==============================================================================
41.4  条件式

":if" コマンドは条件が真の場合に ":endif" までのステートメントを実行します。書
式は次のとおり:

        :if {condition}
           {statements}
        :endif

{condition} を評価した結果が真 (0以外) であれば、{statements} の内容が実行され
ます。{statements} は正しく記述されている必要があります。不正な記述があると
":endif" までたどり着けません。
":else" を使うこともできます。書式は次のとおり:

        :if {condition}
           {statements}
        :else
           {statements}
        :endif

二つ目の {statements} は条件が偽の場合にだけ実行されます。
":elseif" を使うこともできます:

        :if {condition}
           {statements}
        :elseif {condition}
           {statements}
        :endif

これは ":else" に続けて "if" 文を使うのと同じ動作ですが、余計な ":endif" を使
わなくて済みます。
vimrc ファイルで便利に使える例を示しましょう。'term' オプションの値を調べ、そ
の値に応じて処理を分けます:

        :if &term == "xterm"
        :  " xterm 用の設定
        :elseif &term == "vt100"
        :  " vt100 端末用の設定
        :else
        :  " その他の端末用の設定
        :endif


論理演算子
----------

今までの説明で既に論理演算子を使いました。次の演算子がよく使われます:

        a == b          等しい
        a != b          等しくない
        a >  b          より大きい
        a >= b          より大きいか等しい
        a <  b          より小さい
        a <= b          より小さいか等しい

条件が成立するなら 1、そうでなければ 0 が返ります。例:

        :if v:version >= 700
        :  echo "おめでとう"
        :else
        :  echo "古いバージョンを使っています。更新してね!"
        :endif

"v:version" は Vim によって定義されている変数で、Vim のバージョンが入っていま
す。バージョン 6.0 なら 600、バージョン 6.1 なら 601 です。これは複数のバー
ジョンに対応するスクリプトを書くときに便利です。v:version

論理演算子は数値でも文字列でも扱えます。文字列どうしを比較するときは数学的な差
が比較されます。文字のバイト値を比較するので、一部の言語では正しい結果にならな
いかもしれません。
文字列と数値を比較するときは、文字列を数値に変換します。文字列が数字ではなかっ
たときは 0 になるので注意してください。例:

        :if 0 == "one"
        :  echo "yes"
        :endif

これは "yes" と表示されます。"one" は数字ではないので 0 に変換されるのです。

文字列にはさらに二つの論理演算子があります:

        a =~ b          パターンにマッチする
        a !~ b          パターンにマッチしない

左辺の "a" は文字列として扱われます。右辺の "b" は検索パターンとして扱われま
す。例:

        :if str =~ " "
        :  echo "str にはスペースが含まれている"
        :endif
        :if str !~ '\.$'
        :  echo "str の末尾はピリオドではない"
        :endif

パターンを指定するのにシングルクォート文字列を使うのがコツです。ダブルクォート
文字列ではバックスラッシュを二重に書く必要があり、そして、検索パターンではバッ
クスラッシュをよく使うので、バックスラッシュだらけになってしまいます。

文字列を比較するときは 'ignorecase' オプションが使われます。大文字小文字の区別
を明示的に指定したい場合は比較演算子に "#" (区別する) または "?" (区別しない)
をつけます。大文字小文字を区別せずに等しいかどうかを比較したい場合は "==?" を
使います。"!~#" ならパターンにマッチしないことを、大文字と小文字を区別して確認
できます。演算子の一覧は expr-== を参照してください。


他のループコマンド
------------------

":while" コマンドは既に説明しました。":while" ループの中では二つのステートメン
トが使えます:

        :continue               ループの先頭にジャンプしてループを継続する。
        :break                  ":endwhile" までジャンプしてループを脱ける。

例:

        :while counter < 40
        :  call do_something()
        :  if skip_flag
        :    continue
        :  endif
        :  if finished_flag
        :    break
        :  endif
        :  sleep 50m
        :endwhile

":sleep" コマンドは Vim を一定時間停止します。"50m" は 50 ミリ秒です。
":sleep 4" なら 4 秒間スリープします。

":for" コマンドを使ってループすることもできます。41.8を参照。

==============================================================================
41.5  式を実行する

今まではコマンドを直接書いてきました。":execute" コマンドを使うと、式の評価結
果をコマンドとして実行できます。これによってコマンドを動的に生成することができ
ます。
例えば、変数に格納された文字列を使ってタグジャンプするには次のようにします:

        :execute "tag " . tag_name

文字列 "tag " と変数 "tag_name" の値を "." で連結しています。仮に "tag_name"
の値が "get_cmd" だった場合、次のコマンドが実行されることになります:

        :tag get_cmd

":execute" コマンドはコロンコマンドのみ実行できます。":normal" コマンドでノー
マルモードコマンドを実行できますが、このコマンドの引数は文字がそのまま使われ、
式としては評価されません。例:

        :normal gg=G

このコマンドは一行目にジャンプしてから "=" オペレータですべての行を整形しま
す。
":normal" コマンドで式の値を使いたい場合は ":execute" と組み合わせてください。
例:

        :execute "normal " . normal_commands

変数 "normal_commands" にはノーマルモードコマンドを入れておく必要があります。
":normal" には完結したコマンドを指定するようにしてください。引数が最後まで実行
された段階でコマンドは中断されます。例えば、挿入モードを開始した場合は挿入モー
ドを終了しなくてはなりません。次のコマンドは正しく動作します:

        :execute "normal Inew text \<Esc>"

これは現在行に "new text" を挿入します。特殊キー "\<ESC>" を使っていることに注
目してください。これによりスクリプトの中で本物の <Esc> 文字を使わないですみま
す。

文字列を実行するのではなく、その式の値を得たい場合は、eval() 関数を使います:

        :let optname = "path"
        :let optval = eval('&' . optname)

文字 "&" と "path" を連結しているので eval() の引数は "&path" になります。戻り
値は 'path' オプションの値です。
次のようにすることもできます:
        :exe 'let optval = &' . optname

==============================================================================
41.6  関数を使う

たくさんの関数があらかじめ定義され、豊富な機能が提供されています。このセクショ
ンの説明にもいくつか登場します。関数の一覧は functions を参照してください。

関数は ":call" コマンドで呼び出します。引数はカッコで囲み、それぞれをコンマで
区切ります。例:

        :call search("Date: ", "W")

これは "Date: " と "W" を引数にして search() 関数を呼び出しています。search()
関数は一つ目の引数を検索パターンとして使い、二つ目の引数をフラグとして使いま
す。"W" フラグを指定するとファイル末尾で検索が終了します (折り返さない)。

関数は式の中で使うこともできます。例:

        :let line = getline(".")
        :let repl = substitute(line, '\a', "*", "g")
        :call setline(".", repl)

getline() 関数はカレントバッファから行を取得する関数です。引数には行番号を指定
します。この例では "." ですが、これはカーソルのある行を示します。
substitute() 関数は ":substitute" コマンドとほぼ同じです。最初の引数は置換対象
の文字列、二つ目の引数はパターン、三つ目は置き換え文字列、最後はフラグです。
setline() 関数は行の内容を置き換えます。最初の引数は行番号、二つ目の引数は置き
換える文字列です。この例では、substitute() の結果で現在行を置き換えています。
上記の三行のコマンドは次のコマンドと同じことをしています:

        :substitute/\a/*/g

substitute() コマンドの前後にいろいろな処理を入れたりすると、もっと面白いこと
ができるようになります。


関数一覧                                                function-list
--------

たくさんの関数があります。ここでは機能別に分類して紹介します。アルファベット順
の一覧は functions を参照してください。関数の名前の上で CTRL-] を押すと、詳
細な説明にジャンプできます。

文字列繰作:                                             string-functions
        nr2char()               ASCII値から文字を得る
        char2nr()               文字のASCII値を得る
        str2nr()                文字列を数値に変換する
        str2float()             文字列を浮動小数点数に変換する
        printf()                書式付き文字列を整形する
        escape()                文字列の特定の文字を '\' でエスケープ
        shellescape()           シェルコマンドで使えるように文字列をエスケープ
        fnameescape()           Vim コマンド用にファイル名をエスケープ
        tr()                    ある文字の集合から別の文字の集合へ置換する
        strtrans()              文字列を印字可能な状態とする
        tolower()               文字列を小文字にする
        toupper()               文字列を大文字にする
        match()                 文字列の中でパターンにマッチした位置
        matchend()              文字列の中でパターンにマッチした末尾の位置
        matchstr()              文字列の中でパターンにマッチした文字列
        matchstrpos()           文字列の中でパターンにマッチした文字列と位置
        matchlist()             matchstr()と同様だが、部分マッチも返す
        stridx()                文字列の中で部分文字列が見つかった最初の位置
        strridx()               文字列の中で部分文字列が見つかった最後の位置
        strlen()                文字列のバイト単位での長さ
        strchars()              文字列の文字単位での長さ
        strwidth()              表示された文字列のサイズ
        strdisplaywidth()       表示された文字列のサイズ、タブを扱う
        substitute()            パターンにマッチする文字列を置換
        submatch()              ":s" と substitute() の中で部分マッチを得る
        strpart()               文字列の一部分を得る(バイト数指定)
        strcharpart()           文字のインデックスで指定された部分文字列を得る
        strgetchar()            文字のインデックスで指定された文字コードを得る
        expand()                特殊キーワードを展開する
        iconv()                 テキストのエンコーディングを変換する
        byteidx()               文字列中の文字のバイトインデックス
        byteidxcomp()           byteidx() と同様だが合成文字を数に入れる
        repeat()                文字列を複数回繰り返す
        eval()                  文字列を式として評価する
        execute()               Ex コマンドを実行し出力を得る

リスト操作:                                             list-functions
        get()                   要素を取得。存在しないインデックスでもエラーを
                                出さない
        len()                   リスト中の要素の個数
        empty()                 リストが空であるか判定する
        insert()                リストの任意の位置に要素を挿入する
        add()                   リストに要素を追加する
        extend()                リストにリストを連結する
        remove()                リストから1個以上の要素を取り除く
        copy()                  リストの浅いコピーを作成する
        deepcopy()              リストの完全なコピーを作成する
        filter()                リストから選択された要素を取り除く
        map()                   リストの各要素を変換する
        sort()                  リストをソートする
        reverse()               リストの並び順を反転させる
        uniq()                  隣接して繰り返される要素のコピーを削除する
        split()                 文字列を分割し、リストにする
        join()                  リストの要素を連結し、文字列にする
        range()                 数列リストを返す
        string()                リストの文字列表現
        call()                  リストを引数として関数を呼ぶ
        index()                 リスト中の要素のインデックス
        max()                   リスト中の最大値
        min()                   リスト中の最小値
        count()                 ある要素がリスト中に出現する回数を返す
        repeat()                リストを複数回繰り返す

辞書操作:                                               dict-functions
        get()                   辞書の要素を返す。存在しないキーでもエラーを出
                                さない
        len()                   辞書の要素の個数
        has_key()               あるキーが辞書に含まれているか判定する
        empty()                 辞書が空であるか判定する
        remove()                辞書から要素を取り除く
        extend()                ある辞書の要素をすべて別の辞書に追加する
        filter()                辞書から選択された要素を取り除く
        map()                   辞書の各要素を変換する
        keys()                  辞書の全キーのリストを取得する
        values()                辞書の全値のリストを取得する
        items()                 辞書の全キー・値のペアを取得する
        copy()                  辞書の浅いコピーを作成する
        deepcopy()              辞書の完全なコピーを作成する
        string()                辞書の文字列表現
        max()                   辞書中の最大値
        min()                   辞書中の最小値
        count()                 ある値が出現する回数を返す

浮動小数点数の計算:                                     float-functions
        float2nr()              Float を Number に変換
        abs()                   絶対値 (Numberも処理可能)
        round()                 丸め
        ceil()                  切り上げ
        floor()                 切り下げ
        trunc()                 小数点以下切り捨て
        fmod()                  除法の余り
        exp()                   指数
        log()                   自然対数 (eを底とする対数)
        log10()                 10 を底とする対数
        pow()                   x の y 乗
        sqrt()                  平方根
        sin()                   正弦 (サイン)
        cos()                   余弦 (コサイン)
        tan()                   正接 (タンジェント)
        asin()                  逆正弦 (アークサイン)
        acos()                  逆余弦 (アークコサイン)
        atan()                  逆正接 (アークタンジェント)
        atan2()                 逆正接 (アークタンジェント)
        sinh()                  双曲線正弦 (ハイパボリックサイン)
        cosh()                  双曲線余弦 (ハイパボリックコサイン)
        tanh()                  双曲線正接 (ハイパボリックタンジェント)
        isnan()                 数値でないかどうかのチェック

その他の計算:                                           bitwise-function
        and()                   ビットごとの論理積
        invert()                ビットごとの否定
        or()                    ビットごとの論理和
        xor()                   ビットごとの排他的論理和
        sha256()                SHA-256 ハッシュ

変数:                                                   var-functions
        type()                  変数の型
        islocked()              変数がロックされているか判定する
        funcref()               関数参照へのFuncrefを取得する
        function()              関数名からFuncrefを取得する
        getbufvar()             指定バッファの変数値を得る
        setbufvar()             指定バッファに変数を設定する
        getwinvar()             指定ウィンドウの変数値を得る
        gettabvar()             指定タブページから変数値を得る
        gettabwinvar()          指定ウィンドウ・タブページから変数値を取得する
        setwinvar()             指定ウィンドウに変数を設定する
        settabvar()             指定タブページに変数を設定する
        settabwinvar()          指定ウィンドウ・タブページに変数を設定する
        garbagecollect()        解放可能なメモリを解放する

カーソルとマークの位置:                 cursor-functions mark-functions
        col()                   カーソルやマークの列番号を取得する
        virtcol()               カーソルやマークの画面上の列番号を得る
        line()                  カーソルやマークの行番号を取得する
        wincol()                カーソルのウィンドウでの列番号
        winline()               カーソルのウィンドウでの行番号
        cursor()                カーソルを指定した位置に移動させる
        screencol()             カーソルのスクリーン列を取得する
        screenrow()             カーソルのスクリーン行を取得する
        getcurpos()             カーソルの位置を取得する
        getpos()                カーソルやマークなどの位置を取得する
        setpos()                カーソルやマークなどの位置を設定する
        byte2line()             指定のバイト位置の行番号を取得する
        line2byte()             指定の行のバイト位置を取得する
        diff_filler()           ある行より上の詰め行の数を取得する
        screenattr()            スクリーン列/行の属性を取得する
        screenchar()            スクリーン列/行の文字コードを取得する

カレントバッファで動作するもの:                         text-functions
        getline()               バッファから行を得る
        setline()               バッファの行を置き換える
        append()                行または行のリストをバッファに追加する
        indent()                行のインデントを得る
        cindent()               C 言語におけるインデントを得る
        lispindent()            Lisp 言語におけるインデントを得る
        nextnonblank()          次の非空行を探す
        prevnonblank()          前の非空行を探す
        search()                パターンにマッチする場所を探す
        searchpos()             パターンにマッチする場所を探す
        searchpair()            start/skip/end の対を探す
        searchpairpos()         start/skip/end の対を探す
        searchdecl()            名前が宣言されている場所を探す
        getcharsearch()         文字検索情報を返す
        setcharsearch()         文字検索情報を設定する

                                        system-functions file-functions
システム関数とファイル繰作:
        glob()                  ワイルドカードを展開する
        globpath()              複数のディレクトリを対象にワイルドカードを展開
        glob2regpat()           glob パターンを正規表現に変換する
        findfile()              複数のディレクトリからファイルを探す
        finddir()               複数のディレクトリからディレクトリを探す
        resolve()               ショートカットのリンク先を得る
        fnamemodify()           ファイル名を修飾する
        pathshorten()           パス中のディレクトリ名を短くする
        simplify()              パスの意味を変えずに簡略化する
        executable()            実行形式ファイルかどうかをチェックする
        exepath()               実行ファイルのフルパスを得る
        filereadable()          ファイルが読み込み可能かどうかをチェックする
        filewritable()          ファイルが書き込み可能かどうかをチェックする
        getfperm()              ファイルのパーミッションを得る
        setfperm()              ファイルのパーミッションを設定する
        getftype()              ファイルの種類を得る
        isdirectory()           ディレクトリの存在をチェックする
        getfsize()              ファイルのサイズを得る
        getcwd()                カレントディレクトリを得る
        haslocaldir()           カレントウィンドウが :lcd を使用したかどうか
                                をチェックする
        tempname()              一時ファイルの名前を得る
        mkdir()                 ディレクトリを作成する
        delete()                ファイルを削除する
        rename()                ファイルの名前を変更する
        system()                シェルコマンドを実行し、その結果を文字列で得る
        systemlist()            シェルコマンドを実行し、その結果をリストで得る
        hostname()              システムの名称を得る
        readfile()              ファイルを読み込み、行のリストを得る
        writefile()             行のリストをファイルに書き込む

日付と時刻:                             date-functions time-functions
        getftime()              ファイルの最終更新日時を得る
        localtime()             現在時刻を秒単位で得る
        strftime()              時刻を文字列に変換する
        reltime()               現在時刻または経過時間を正確に取得する
        reltimestr()            reltime()の結果を文字列に変換する
        reltimefloat()          reltime()の結果を浮動小数点に変換する

                        buffer-functions window-functions arg-functions
バッファ、ウィンドウ、引数リスト:
        argc()                  引数リストの大きさ
        argidx()                引数リスト中の現在の位置
        arglistid()             引数リストのIDを得る
        argv()                  引数リストの中身を得る
        bufexists()             バッファの存在をチェックする
        buflisted()             バッファが存在し、リストされているかどうか
        bufloaded()             バッファが存在し、ロードされているかどうか
        bufname()               バッファの名前を得る
        bufnr()                 バッファの番号を得る
        tabpagebuflist()        タブページ中のバッファのリストを返す
        tabpagenr()             タブページの番号を取得する
        tabpagewinnr()          タブページを対象にwinnr()と同様
        winnr()                 カレントウィンドウの番号を得る
        bufwinid()              バッファのウィンドウIDを得る
        bufwinnr()              バッファのウィンドウ番号を得る
        winbufnr()              ウィンドウのバッファ番号を得る
        getbufline()            バッファの行のリストを得る
        win_findbuf()           バッファが含まれるウィンドウを探す
        win_getid()             ウィンドウのウィンドウIDを得る
        win_gotoid()            IDで指定されたウィンドウへ移動する
        win_id2tabwin()         IDで指定されたタブとウィンドウの番号を得る
        win_id2win()            IDで指定されたウィンドウの番号を得る
        getbufinfo()            バッファの情報一覧を得る
        gettabinfo()            タブページの情報一覧を得る
        getwininfo()            ウィンドウの情報一覧を得る

コマンドライン:                                 command-line-functions
        getcmdline()            現在のコマンドラインを取得
        getcmdpos()             コマンドラインにおけるカーソル位置を取得
        setcmdpos()             コマンドラインにおけるカーソル位置を設定
        getcmdtype()            現在のコマンドラインの種類を返す
        getcmdwintype()         現在のコマンドラインウィンドウの種類を返す
        getcompletion()         マッチするコマンド補完リストを返す

Quickfixとロケーションリスト:                   quickfix-functions
        getqflist()             quickfixエラーのリスト
        setqflist()             quickfixを変更する
        getloclist()            ロケーションリストの項目のリスト
        setloclist()            ロケーションリストを変更する

挿入モード補完:                                 completion-functions
        complete()              補完候補を設定する
        complete_add()          補完候補を追加する
        complete_check()        補完処理を終えるべきかどうかをチェックする
        pumvisible()            ポップアップメニューが表示されているかチェック

折り畳み:                                       folding-functions
        foldclosed()            行が折り畳まれているかどうかをチェックする
        foldclosedend()         foldclosed()と同様。折り畳み末尾の行番号を返す
        foldlevel()             行の折り畳みレベルを得る
        foldtext()              閉じた折り畳みを代替表示するテキストを生成
        foldtextresult()        閉じた折り畳みを代替表示するテキストを得る

シンタックスハイライト:           syntax-functions highlighting-functions
        clearmatches()          matchadd():matchコマンドで定義されたマッ
                                チをクリアする
        getmatches()            matchadd():matchコマンドで定義されたすべ
                                てのマッチを得る
        hlexists()              ハイライトグループの存在をチェック
        hlID()                  ハイライトグループのIDを得る
        synID()                 指定位置のシンタックスIDを得る
        synIDattr()             シンタックスIDから指定の属性を得る
        synIDtrans()            変換したシンタックスIDを得る
        synstack()              指定位置のシンタックスIDのリストを得る
        synconcealed()          conceal の情報を得る
        diff_hlID()             差分モードの指定位置のシンタックスIDを得る
        matchadd()              強調表示するパターンを定義する
        matchaddpos()           強調表示する位置のリストを定義する
        matcharg()              :matchの引数の情報を得る
        matchdelete()           matchadd():matchコマンドで定義されたマッ
                                チを削除する
        setmatches()            getmatches()で得たマッチを使って復元する

スペリング:                                     spell-functions
        spellbadword()          カーソル位置以降のスペルミスを探す
        spellsuggest()          スペル訂正の候補を返す
        soundfold()             単語の同音等値(sound-a-like equivalent)を返す

履歴:                                           history-functions
        histadd()               履歴に項目を追加
        histdel()               履歴から項目を削除
        histget()               履歴の項目を得る
        histnr()                履歴リストの最大インデックスを得る

対話インターフェイス:                           interactive-functions
        browse()                ファイル選択ダイアログを開く
        browsedir()             ディレクトリ選択ダイアログを開く
        confirm()               ユーザーに選択をさせる
        getchar()               ユーザーが入力した文字を得る
        getcharmod()            最後に入力した文字の修飾子(modifier)を得る
        feedkeys()              先行入力キューに文字を入れる
        input()                 ユーザーが入力した行を得る
        inputlist()             ユーザーにリストから項目を選択させる
        inputsecret()           ユーザーが入力した行を得る。ただし表示はしない
        inputdialog()           ダイアログを使ってユーザーが入力した行を得る
        inputsave()             先行入力キューを保存して空にする
        inputrestore()          inputsave()で保存した状態に戻す

GUI:                                            gui-functions
        getfontname()           現在使われているフォントの名前を取得
        getwinposx()            GUIのVimウィンドウのX座標
        getwinposy()            GUIのVimウィンドウのY座標
        balloon_show()          バルーンの内容を設定する

Vimサーバー:                                    server-functions
        serverlist()            サーバー名のリストを返す
        remote_send()           Vimサーバーにコマンド文字を送る
        remote_expr()           Vimサーバーで式を評価する
        server2client()         Vimサーバーのクライアントに応答を返す
        remote_peek()           Vimサーバーから返信があったかどうかをチェック
        remote_read()           Vimサーバーからの返信を読む
        foreground()            Vimのウィンドウを前面に持ってくる
        remote_foreground()     Vimサーバーのウィンドウを前面に持ってくる

ウィンドウサイズと位置:                         window-size-functions
        winheight()             ウィンドウの高さを取得
        winwidth()              ウィンドウの幅を取得
        winrestcmd()            ウィンドウサイズを復元するコマンドを返す
        winsaveview()           カレントウィンドウのビューを取得
        winrestview()           カレントウィンドウのビューを復元

マッピング:                                     mapping-functions
        hasmapto()              マップの存在をチェック
        mapcheck()              マッチするマップの存在をチェック
        maparg()                マップのrhs(展開結果)を得る
        wildmenumode()          wildmodeが有効かどうかをチェック

テスト用:                                       test-functions
        assert_equal()          2つの式が等しい事をテストする
        assert_notequal()       2つの式が等しくない事をテストする
        assert_inrange()        式が範囲内にある事をテストする
        assert_match()          値がパターンにマッチする事をテストする
        assert_notmatch()       値がパターンにマッチしない事をテストする
        assert_false()          式がfalseかどうかテストする
        assert_true()           式がtrueかどうかテストする
        assert_exception()      コマンドが例外を投げる事をテストする
        assert_fails()          関数呼び出しが失敗する事をテストする
        test_alloc_fail()       メモリの確保を失敗させる
        test_autochdir()        起動中に 'autochdir' を有効にする
        test_disable_char_avail()       typeahead なしにテスト
        test_garbagecollect_now()       直ちにメモリを解放する
        test_ignore_error()     特定のエラーメッセージを無視する
        test_null_channel()     null のチャンネルを返す
        test_null_dict()        null の辞書を返す
        test_null_job()         null の Job を返す
        test_null_list()        null のリストを返す
        test_null_partial()     null の部分適用を返す
        test_null_string()      null の文字列を返す
        test_settime()          Vimが内部的に用いる時間を設定する

プロセス間通信:                                 channel-functions
        ch_canread()            何か読むものがあるかチェックする
        ch_open()               チャンネルを開く
        ch_close()              チャンネルを閉じる
        ch_close_in()           チャンネルの入力パートを閉じる
        ch_read()               チャンネルからメッセージを読み取る
        ch_readraw()            チャンネルからrawメッセージを読み取る
        ch_sendexpr()           チャンネルにJSONメッセージを送る
        ch_sendraw()            チャンネルにrawメッセージを送る
        ch_evalexpr()           チャンネル経由で式を評価する
        ch_evalraw()            チャンネル経由で raw 文字列を評価する
        ch_status()             チャンネルの状態を取得する
        ch_getbufnr()           チャンネルのバッファ番号を取得する
        ch_getjob()             チャンネルが割り当てられている Job を取得する
        ch_info()               チャンネルの情報を取得する
        ch_log()                チャンネルのログファイルにメッセージを出力する
        ch_logfile()            チャンネルのログファイルを設定する
        ch_setoptions()         チャンネルのオプションを設定する
        json_encode()           式をJSONの文字列にエンコードする
        json_decode()           JSONの文字列をVimの型にデコードする
        js_encode()             式をJSONの文字列にエンコードする
        js_decode()             JSONの文字列をVimの型にデコードする

Jobs:                                           job-functions
        job_start()             Job を開始する
        job_stop()              Job を停止する
        job_status()            Job のステータスを取得する
        job_getchannel()        Job が使用する channel を取得する
        job_info()              Job の情報を取得する
        job_setoptions()        Job のオプションを設定する

Timers:                                         timer-functions
        timer_start()           タイマーを作る
        timer_pause()           タイマーを一時停止もしくは再開する
        timer_stop()            タイマーを止める
        timer_stopall()         全てのタイマーを止める
        timer_info()            タイマーの情報を得る

その他:                                         various-functions
        mode()                  現在の編集モードを得る
        visualmode()            最後に使われたビジュアルモードの種類
        exists()                変数、関数の存在をチェック
        has()                   機能がサポートされているかをチェック
        changenr()              最近の変更番号を返す
        cscope_connection()     cscope接続をチェック
        did_filetype()          FileTypeオートコマンドが使用されたかどうか
        eventhandler()          イベントハンドラによって起動されたかどうか
        getpid()                Vim のプロセスIDを得る

        libcall()               外部ライブラリの関数を呼ぶ
        libcallnr()             同上、数値を返す

        undofile()              アンドゥファイルの名前を得る
        undotree()              アンドゥツリーの状態を返す

        getreg()                レジスタの値を得る
        getregtype()            レジスタのタイプを得る
        setreg()                レジスタの値を設定する

        shiftwidth()            'shiftwidth' の実際の値

        wordcount()             バッファ内のバイト数/単語数/文字数などを得る

        taglist()               マッチするタグのリストを取得
        tagfiles()              タグファイルのリストを取得

        luaeval()               Luaの式を評価する
        mzeval()                MzScheme の式を評価する
        perleval()              Perlの式を評価する (+perl)
        py3eval()               Pythonの式を評価する (+python3)
        pyeval()                Pythonの式を評価する (+python)
        pyxeval()               python_x の式を評価する

==============================================================================
41.7  関数を定義する

自分で関数を定義することができます。基本的な関数定義は次のとおり:

        :function {name}({var1}, {var2}, ...)
        :  {body}
        :endfunction

        Note:
        関数名は大文字で開始する必要があります。

小さな関数を定義してみましょう。二つの数値のうち小さい方を返す関数を作ります。
関数は次のような行で始まります:

        :function Min(num1, num2)

関数の名前が "Min" であり、二つの引数 ("num1" と "num2") を取る、ということを
表しています。
最初にしなければならないのは、どちらの数値が小さいかをチェックすることです:

        :  if a:num1 < a:num2

"a:" は特殊なプリフィックスで、この変数が関数の引数であることを示します。小さい
方の値を変数 "smaller" に代入しましょう:

        :  if a:num1 < a:num2
        :    let smaller = a:num1
        :  else
        :    let smaller = a:num2
        :  endif

変数 "smaller" はローカル変数です。関数の中で使われた変数はローカル変数になり
ます。ただし、"g:"、"a:"、"s:" などのプリフィックスを付けた場合は別です。

        Note:
        関数の内からグローバル変数にアクセスするには "g:" を付ける必要がありま
        す。つまり、関数内では "g:today" はグローバル変数 "today" を示し、
        "today" ならそれとは別の変数、すなわちローカル変数になります。

":return" ステートメントを使って、小さい方の値を呼び出し元に返しましょう。そし
て、関数を閉じます:

        :  return smaller
        :endfunction

関数定義の全体は次のようになります:

        :function Min(num1, num2)
        :  if a:num1 < a:num2
        :    let smaller = a:num1
        :  else
        :    let smaller = a:num2
        :  endif
        :  return smaller
        :endfunction

関数を短く書きたい場合は、次のようにもできます:

        :function Min(num1, num2)
        :  if a:num1 < a:num2
        :    return a:num1
        :  endif
        :  return a:num2
        :endfunction

ユーザー定義関数は組み込み関数とまったく同じ方法で呼び出すことができます。違う
のは名前だけです。Min 関数は次のように使用できます:

        :echo Min(5, 8)

関数が実行され、関数の中身が Vim によって解釈されます。未定義の変数や関数を使
うなどの間違いがあったときは、エラーメッセージが表示されます。関数定義の時点で
はそれらのエラーは検出されません。

関数が ":endfunction" まで実行されたとき、あるいは引数無しで ":return" を使っ
たときは 0 が返ります。

既存の関数を再定義したい場合は ":function" コマンドに "!" を付けてください:

        :function!  Min(num1, num2, num3)


範囲指定を使う
--------------

":call" コマンドは行範囲を受け取ることができます。範囲指定の使用方法は二つあり
ます。関数を定義するときに "range" キーワードを使った場合は、関数自身が範囲指
定を処理します。
関数には "a:firstline" と "a:lastline" という二つの変数が暗黙的に渡されます。
この二つの変数には範囲指定された行番号が代入されています。例:

        :function Count_words() range
        :  let lnum = a:firstline
        :  let n = 0
        :  while lnum <= a:lastline
        :    let n = n + len(split(getline(lnum)))
        :    let lnum = lnum + 1
        :  endwhile
        :  echo "found " . n . " words"
        :endfunction

この関数は次のように呼び出すことができます:

        :10,30call Count_words()

関数が一度だけ実行され、単語の数が表示されます。
関数を定義するときに "range" キーワードを使わなかった場合は、指定された範囲の
それぞれの行に対して関数が呼ばれます (カーソルはその行の上)。例:

        :function  Number()
        :  echo "line " . line(".") . " contains: " . getline(".")
        :endfunction

次のように実行すると:

        :10,15call Number()

関数は 6 回実行されます。


可変長引数
----------

可変個の引数を取る関数を定義できます。例えば、次の関数は、必ず 1 つの引数
(start) を取り、最大で 20 個までの引数を取ることができます:

        :function Show(start, ...)

変数 "a:1" に 1 つ目のオプション引数が代入されます。2 つ目が "a:2" で、3 つ目
が "a:3" です。"a:0" にはオプション引数の数が入ります。
例:

        :function Show(start, ...)
        :  echohl Title
        :  echo "start is " . a:start
        :  echohl None
        :  let index = 1
        :  while index <= a:0
        :    echon "  Arg " . index . " is " . a:{index}
        :    let index = index + 1
        :  endwhile
        :  echo ""
        :endfunction

この関数は ":echohl" を使って ":echo" の出力に色を付けています。":echohl None"
で色付けをやめます。":echon" コマンドは ":echo" と同じ機能ですが、改行を出力し
ません。

変数 a:000 を使うこともできます。これは "..." 引数がすべて入ったリストです。
a:000を参照。


関数の一覧
----------

":function" コマンドでユーザー定義関数の一覧を表示できます:

        :function
        function Show(start, ...)
        function GetVimIndent()
        function SetSyn(name)

関数の中身を見たいときは関数名を指定してください:

        :function SetSyn
        1     if &syntax == ''
        2       let &syntax = a:name
        3     endif
           endfunction


デバッグ
--------

エラーメッセージが表示されたとき、あるいはデバッグ中に、行番号が表示されると便
利です。デバッグモードについては debug-scripts を参照してください。
'verbose' オプションに 12 以上の値を設定すると、すべての関数呼び出しが表示され
ます。15 以上にすると、実行されたすべての行が表示されます。


関数の削除
----------

例えば Show() 関数を削除するのは次のようにします:

        :delfunction Show

関数が存在しない場合はエラーになります。


関数への参照
------------

変数に関数を代入できると便利なことがあります。それには function() 関数を使いま
す。function() は関数の名前を受け取り、関数への参照を返します:

        :let result = 0         " or 1
        :function! Right()
        :  return 'Right!'
        :endfunc
        :function! Wrong()
        :  return 'Wrong!'
        :endfunc
        :
        :if result == 1
        :  let Afunc = function('Right')
        :else
        :  let Afunc = function('Wrong')
        :endif
        :echo call(Afunc, [])
        Wrong!

Note 関数への参照を保持する変数の名前は大文字で始めなければなりません。そうで
ないと組み込み関数の名前と紛らわしくなります。
変数が参照している関数を呼び出すには call() 関数を使います。call() 関数の最初
の引数は関数への参照で、2 番目の引数は引数のリストです。

関数への参照は、次節で説明される辞書と組み合わせたときもっとも役に立ちます。

==============================================================================
41.8  リストと辞書

ここまでは基本型(文字列と数値)を扱ってきました。Vim は二つの複合型、リストと辞
書もサポートしています。

リストとは、要素を順番に並べたものです。要素はどのような型でも構いません。数値
のリスト、リストのリスト、あるいは複数の型が混在したリストでも作れます。例え
ば、3 個の文字列からなるリストを作るには次のようにします:

        :let alist = ['aap', 'mies', 'noot']

リストの要素は角括弧で囲み、コンマで区切ります。空のリストを作るには次のように
します:

        :let alist = []

関数add()を使うとリストに要素を追加することができます:

        :let alist = []
        :call add(alist, 'foo')
        :call add(alist, 'bar')
        :echo alist
        ['foo', 'bar']

リストの連結には + を使います:

        :echo alist + ['foo', 'bar']
        ['foo', 'bar', 'foo', 'bar']

直接リストを拡張するには次のようにします:

        :let alist = ['one']
        :call extend(alist, ['two', 'three'])
        :echo alist
        ['one', 'two', 'three']

add()とは効果が異なることに注意してください:

        :let alist = ['one']
        :call add(alist, ['two', 'three'])
        :echo alist
        ['one', ['two', 'three']]

add()の第二引数は1つの要素として追加されます。


FOR ループ
----------

リストを使ってできる素晴らしいことの1つが、リストに対する繰り返しです:

        :let alist = ['one', 'two', 'three']
        :for n in alist
        :  echo n
        :endfor
        one
        two
        three

上の例は、リスト "alist" の各要素に対して、その値を変数 "n" に代入しながらルー
プを行います。forループの書式は次の通りです:

        :for {varname} in {listexpression}
        :  {commands}
        :endfor

ある回数だけループするには、その長さのリストを使います。関数range()を使うと、
そのようなリストを作成できます:

        :for a in range(3)
        :  echo a
        :endfor
        0
        1
        2

range()が生成するリストの最初の要素は0であることに注意してください。そのため、
最後の要素はリストの長さより1小さい値になります。
最大値、ステップ幅を指定することもでき、逆方向に進むこともできます:

        :for a in range(8, 4, -2)
        :  echo a
        :endfor
        8
        6
        4

より有用な例として、バッファ中の行に対するループ:

        :for line in getline(1, 20)
        :  if line =~ "Date: "
        :    echo matchstr(line, 'Date: \zs.*')
        :  endif
        :endfor

1行目から20行目(両端を含む)を調べ、そこに含まれる日付を全て表示しています。


辞書
-----

辞書はキーと値のペアを保持します。キーを指定することで高速に値を検索できます。
辞書は波括弧で作ります:

        :let uk2nl = {'one': 'een', 'two': 'twee', 'three': 'drie'}

そして角括弧の中にキーを書くことで単語を検索します:

        :echo uk2nl['two']
        twee

辞書の定義の書式は次の通りです:

        {<key> : <value>, ...}

空の辞書とは、どんなキーも持たない辞書のことです:

        {}

辞書にはいろいろな使い道があります。辞書を扱う関数もたくさんあります。例えば、
キーのリストを取得してそれに対してループするには次のようにします:

        :for key in keys(uk2nl)
        :  echo key
        :endfor
        three
        one
        two

キーはソートされていません。特定の順序に並べるにはリストをソートします:

        :for key in sort(keys(uk2nl))
        :  echo key
        :endfor
        one
        three
        two

要素が定義された順序を得ることはできません。そのような目的にはリストを使ってく
ださい。リストは順序を保って要素を保持します。


辞書の関数
----------

辞書の要素は角括弧でインデックスを指定して取得します:

        :echo uk2nl['one']
        een

記号を使わない方法もあります:

        :echo uk2nl.one
        een

この方法はキーがアルファベット、数字、アンダースコアなどの ASCII 文字だけで構
成されている場合に使えます。この方法で値を代入することもできます:

        :let uk2nl.four = 'vier'
        :echo uk2nl
        {'three': 'drie', 'four': 'vier', 'one': 'een', 'two': 'twee'}

関数の定義と辞書への代入を同時に記述することができます:

        :function uk2nl.translate(line) dict
        :  return join(map(split(a:line), 'get(self, v:val, "???")'))
        :endfunction

これを実行してみましょう:

        :echo uk2nl.translate('three two five one')
        drie twee ??? een

":function" の行の末尾に "dict" と書かれています。これは、その関数が辞書から使
われることを示します。ローカル変数 "self" がその辞書を指すようになります。
次に、複雑なreturnコマンドを分解してみましょう:

        split(a:line)

関数split()は文字列を空白文字で区切り、リストにして返します。そのため、この例
での戻り値は次のようになります:

        :echo split('three two five one')
        ['three', 'two', 'five', 'one']

このリストがmap()関数の第一引数になります。map()はリストの各要素を "v:val" に
代入した状態で第二引数を評価します。これによりforループより短いコードが書けま
す。このコードは:

        :let alist = map(split(a:line), 'get(self, v:val, "???")')

次のコードと同じです:

        :let alist = split(a:line)
        :for idx in range(len(alist))
        :  let alist[idx] = get(self, alist[idx], "???")
        :endfor

関数get()はそのキーが辞書に入っているかをチェックします。入っていればその値を
返します。入っていなければデフォルト値(この例では '???')を返します。キーが入っ
ていなくてもエラーを起こしたくないような場合に便利です。

関数join()はsplit()の逆の処理をします。つまり単語のリストをスペースでつなげま
す。
split()、map()、join() を組み合わせると、単語からなる行を簡潔に処理することが
できます。


オブジェクト指向プログラミング
------------------------------

辞書には値と関数を入れることができるので、辞書をオブジェクトとして使うことがで
きます。
上述の例ではオランダ語から英語に翻訳するために辞書を使いました。同じことが他の
言語でもできると面白いかもしれませんね。まず翻訳関数を持ったオブジェクト (つま
り辞書) を作ります。翻訳する単語はまだ定義しません:

        :let transdict = {}
        :function transdict.translate(line) dict
        :  return join(map(split(a:line), 'get(self.words, v:val, "???")'))
        :endfunction

単語を翻訳するのに 'self.words' を使う点が上述の例と少し違います。しかし、
self.words はまだありません。よって、これは抽象クラスと呼ぶことができます。

オランダ語を翻訳するオブジェクトをインスタンス化してみましょう:

        :let uk2nl = copy(transdict)
        :let uk2nl.words = {'one': 'een', 'two': 'twee', 'three': 'drie'}
        :echo uk2nl.translate('three one')
        drie een

さらにドイツ語の翻訳機を作ります:

        :let uk2de = copy(transdict)
        :let uk2de.words = {'one': 'eins', 'two': 'zwei', 'three': 'drei'}
        :echo uk2de.translate('three one')
        drei eins

copy() 関数を使って "transdict" 辞書をコピーし、そのコピーに対して単語を追加し
ています。元の辞書はもちろん変更されません。

さらに一歩進んで、適切な言語を選択できるようにしてみます:

        :if $LANG =~ "de"
        :  let trans = uk2de
        :else
        :  let trans = uk2nl
        :endif
        :echo trans.translate('one two three')
        een twee drie

"trans" は2つのオブジェクト(辞書)のうちどちらか1つを参照します。コピーは作られ
ていません。リストと辞書の同一性についてのより詳しい情報はlist-identity
dict-identityにあります。

未対応の言語を使う場合は、translate() 関数を上書きして何もしないようにするとい
いかもしれません:

        :let uk2uk = copy(transdict)
        :function! uk2uk.translate(line)
        :  return a:line
        :endfunction
        :echo uk2uk.translate('three one wladiwostok')
        three one wladiwostok

! を使って既に存在している関数への参照を上書きしています。続いて、未対応の言語
に対して "uk2uk" を使うように変更します:

        :if $LANG =~ "de"
        :  let trans = uk2de
        :elseif $LANG =~ "nl"
        :  let trans = uk2nl
        :else
        :  let trans = uk2uk
        :endif
        :echo trans.translate('one two three')
        one two three

さらなる情報についてはListDictionariesを参照してください。

==============================================================================
41.9  例外

まずは例題を見てください:

        :try
        :   read ~/templates/pascal.tmpl
        :catch /E484:/
        :   echo "パスカル用のテンプレートファイルは見つかりませんでした。"
        :endtry

":read" コマンドはファイルがなければ失敗します。そのエラーをキャッチして、エ
ラーメッセージの代わりにより親切なメッセージを表示しています。

":try" と ":endtry" の間で起きたエラーは例外に変わります。例外は文字列です。エ
ラーが例外に変わったとき、文字列にはエラーメッセージが含まれます。また、全ての
エラーメッセージは番号を持っています。例題では "E484:" を含んだエラーをキャッ
チしています。この番号は変わらないことが保証されています (テキストは翻訳される
などして変わるかもしれません)。

":read" コマンドが他のエラーを起こした場合、"E484:" というパターンはマッチしな
いでしょう。したがって、その例外はキャッチされず、通常のエラーメッセージが表示
されます。

次のように書くこともできます:

        :try
        :   read ~/templates/pascal.tmpl
        :catch
        :   echo "パスカル用のテンプレートファイルは見つかりませんでした。"
        :endtry

全ての例外がキャッチされます。しかしこれでは "E21: Cannot make changes,
'modifiable' is off" のような有効なエラーに気づくことができません。

":finally" という便利なコマンドもあります:

        :let tmp = tempname()
        :try
        :   exe ".,$write " . tmp
        :   exe "!filter " . tmp
        :   .,$delete
        :   exe "$read " . tmp
        :finally
        :   call delete(tmp)
        :endtry

カーソル行からファイル末尾までを "filter" コマンド (ファイル名を引数に取るコマ
ンド) でフィルタ処理しています。":try" と ":finally" の間で問題が起きても、
ユーザーが CTRL-C を押して操作をキャンセルしても、"call delete(tmp)" は必ず呼
ばれます。一時ファイルが残ってしまう心配はありません。

例外についてさらに詳しい情報はリファレンスマニュアルのexception-handling
参照してください。

==============================================================================
41.10 注意事項

Vim script において注意すべきことの概要を簡単に説明します。他の場所にも同じよ
うな説明はありますが、手頃なチェックリストに使えるでしょう。

改行記号はシステムによって異なります。Unix では <NL> 文字が使われますが、
MS-DOS、Windows、OS/2 などでは <CR><LF> が使われます。末尾が <CR> になってい
るマップを使うときは注意してください。:source_crnl 参照。


空白
-----

空の行はあっても構いません。無視されます。

行頭の空白 (スペースとTAB) は常に無視されます。引数と引数の間には空白がいくつ
あっても構いません (例えば下記の "set" と "cpoptions" の間)。空白は一つのス
ペースにまとめられ、セパレータの役目をします。最後の文字より後ろにある空白文字
は状況によって無視されたりされなかったりします。下記参照。

":set" コマンドで "=" 記号を使うとき:

        :set cpoptions    =aABceFst

"=" の直前にある空白は無視されます。しかし、"=" の後ろに空白をはさむことはでき
ません。

オプション値に空白を含めるときは、バックスラッシュ ("\") でエスケープする必要
があります:

        :set tags=my\ nice\ file

次のように書くと:

        :set tags=my nice file

これはエラーになります。このコマンドは次のように解釈されてしまいます:

        :set tags=my
        :set nice
        :set file


コメント
--------

コメントは " (ダブルクォート) 記号で開始します。行末までのすべての文字がコメン
トとして解釈され、無視されます。ただし、コメントを書くことができないコマンドも
あります (以下に例を示します)。コメントは行のどこからでも開始できます。

コメントとして簡単な注釈を付けたとします。例:

        :abbrev dev development         " shorthand
        :map <F3> o#include             " insert include
        :execute cmd                    " do it
        :!ls *.c                        " list C files

短縮形 'dev' は 'development     " shorthand' に展開されます。<F3> には
'o#....' から '" insert include' までの全部がマップされます。"execute" コマン
ドはエラーを起こします。"!" コマンドはすべての文字をシェルに渡すので、" 記号が
閉じられていないことでエラーが起こります。
":map"、":abbreviate"、":execute"、"!" などのコマンドはその後ろにコメントを書
くことができません (そのようなコマンドは他にもあります)。ただし、無理やりコメ
ントを書く方法もあります:

        :abbrev dev development|" shorthand
        :map <F3> o#include|" insert include
        :execute cmd                    |" do it

'|' 文字でコマンドを区切り、次のコマンドを書くことができます。この例では二つ目
のコマンドはコメントのみです。"!" の場合は :execute と '|' を使わなければな
りません:
        :exe '!ls *.c'                  |" list C files

":map" と ":abbreviate" の場合は '|' の前に空白を置かないように注意してくださ
い。これらのコマンドは行末か '|' までのすべての文字を使います。そのため、意図
せずに末尾に空白を入れてしまうかもしれません:

        :map <F4> o#include  

vimrc を編集するときに 'list' オプションをオンに設定しておくと、この問題が発見
しやすくなります。

Unix では特別なコメント書式を使って Vim script を実行形式にすることができま
す:
        #!/usr/bin/env vim -S
        echo "this is a Vim script"
        quit

"#" コマンドは行を行番号付きで表示しますが、'!' をつけると何もしなくなります。
よってファイルを実行するためのシェルコマンドを記述することができます。
:#! -S


落とし穴
--------

次の例には大きな問題があります:

        :map ,ab o#include
        :unmap ,ab 

この unmap コマンドはうまく動きません。なぜなら ",ab " を unmap しようとしてい
るからです。そのようなマップは存在しません。エラーが表示されますが、スペースは
目に見えないので、エラーの原因を見つけるのは困難です。

":unmap" コマンドの後にコメントを書いた場合も同様です:

        :unmap ,ab     " comment

コメントは無視されますが、Vim は ',ab     ' を unmap しようとします。次のよう
に書いてください:

        :unmap ,ab|    " comment


ビューの復元
------------

何らかの変更を加えてから、カーソルのあった場所に戻りたい時があります。そのとき
に、画面に表示されていた行範囲も復元されるとすてきです。
次の例は、現在行をヤンクしてファイルの先頭にプットし、ビューを復元します:

        map ,p ma"aYHmbgg"aP`bzt`a

これは次のことをしています:
        ma"aYHmbgg"aP`bzt`a
        ma                      現在のカーソル位置にマーク a を設定
          "aY                   現在行をレジスタ a にヤンク
             Hmb                ウィンドウの一行目に移動してマーク b を設定
                gg              ファイルの一行目に移動
                  "aP           ヤンクした行をその上にプット
                     `b         ウィンドウの一行目に戻る
                       zt       ウィンドウの表示範囲を以前と同じにする
                         `a     保存しておいたカーソル位置に移動


パッケージング
--------------

関数の名前が他の人の関数とかぶらないように、次の方法を使ってください:
- ユニークな文字列を名前の前に付ける。私はよく略語を使います。例えば、オプショ
  ンウィンドウ (option window) のための関数なら "OW_" などです。
- 関数を一つのファイルにまとめて、関数がロードされているかどうかを示すグローバ
  ル変数を設定する。ファイルが二回目に読み込まれたとき、最初にそれらの関数をア
  ンロードする。
例:

        " This is the XXX package

        if exists("XXX_loaded")
          delfun XXX_one
          delfun XXX_two
        endif

        function XXX_one(a)
                ... body of function ...
        endfun

        function XXX_two(b)
                ... body of function ...
        endfun

        let XXX_loaded = 1

==============================================================================
41.11 プラグインを書く                                write-plugin

Vim script を書いて、それを多くの人に使ってもらうことができます。そのようなス
クリプトはプラグインと呼ばれます。Vim ユーザーはあなたのスクリプトをプラグイン
ディレクトリにコピーするだけで、すぐにその機能を使うことができます。
add-plugin 参照。

プラグインには二種類あります:

      グローバルプラグイン : すべてのファイルで共通
  ファイルタイププラグイン : ファイルの種類別

この節ではグローバルプラグインについて説明します。ほとんどの説明はファイルタイ
ププラグインに対してもあてはまります。ファイルタイププラグイン特有の説明は次節
にあります write-filetype-plugin


名前
-----

最初にプラグインの名前を決めなければなりません。プラグインが提供する機能が名前
から分かるようにしてください。また、他の人が作ったプラグインと名前がかぶらない
ようにしてください。古い Windows システムでの問題を避けるため、名前は 8 文字以
内にしてください。

例えばタイプミス (type mistake) を修正 (correct) するためのスクリプトなら
"typecorr.vim" という名前を付けたりします。ここではこれを例題として使います。

プラグインが誰でも使えるようにするため、いくつかのガイドラインに従ってくださ
い。ガイドラインは段階的に説明していきます。例題プラグインの完全なソースは最後
に示します。


ボディ
------

まずはプラグインの本体部分を見てみましょう。行番号は実際の番号です:

 14     iabbrev teh the
 15     iabbrev otehr other
 16     iabbrev wnat want
 17     iabbrev synchronisation
 18             \ synchronization
 19     let s:count = 4

もちろん、実際のスクリプトはもっと巨大です。

行番号は説明のために追加したものです。プラグインを書くときは行番号を付けないで
ください。


ヘッダー
------

新しい単語を追加していくと、プラグインには複数のバージョンが存在することになり
ます。ファイルを配布したとき、それを使った人は、誰がこの素晴らしいプラグインを
書いたのかを知りたいと思うでしょうし、感想を伝えたいと思うかもしれません。
というわけで、次のようなヘッダーをプラグインに書いてください:

  1     " Vim global plugin for correcting typing mistakes
  2     " Last Change:  2000 Oct 15
  3     " Maintainer:   Bram Moolenaar <Bram@vim.org>

著作権とライセンスについて: プラグインがとても便利で、そして再配布を制限するほ
どのものでない場合は、パブリックドメインか Vim ライセンス (license) の適用を
検討してみてください。次の短い宣言をプラグインの先頭付近に書いておくだけで十分
です:

  4     " License:      This file is placed in the public domain.


行連結、副作用の回避                                    use-cpo-save
--------------------

上の例の 18 行目では行連結 (line-continuation) が使われています。ユーザーの
環境で 'compatible' オプションがオンに設定されていると、この行でエラーが発生し
ます。'compatible' オプションの設定には副作用があるので、勝手に設定をオフにす
ることはできません。問題を避けるには、一時的に 'cpoptions' の値を Vim の初期値
に設定し、後で元に戻します。そうすれば、行連結を使うことができ、スクリプトはほ
とんどの環境で動作するようになります。設定の変更は次のようにします:

 11     let s:save_cpo = &cpo
 12     set cpo&vim
 ..
 42     let &cpo = s:save_cpo
 43     unlet s:save_cpo

最初に 'cpoptions' の値を s:save_cpo 変数に保存します。プラグインの最後でオプ
ションの値を元に戻します。

スクリプトローカル変数 (s:var) を使っていることに注目してください。グローバ
ル変数は他の場所で使われている可能性があります。スクリプトの中だけで使う場合は
スクリプトローカル変数を使ってください。


ロードしない
------------

ユーザーが常にプラグインをロードしたいと思うとは限りません。また、システム管理
者がシステムのプラグインディレクトリにプラグインを入れたが、ユーザーは自分で入
れたプラグインを使いたいということもあります。したがって、指定したプラグインだ
けを無効にできる必要があります。次のようにします:

  6     if exists("g:loaded_typecorr")
  7       finish
  8     endif
  9     let g:loaded_typecorr = 1

これはスクリプトの二重ロードを避ける効果もあります。スクリプトを二重にロードす
ると、関数の再定義エラーが発生したり、自動コマンドが二重に追加されることでトラ
ブルが起きたりします。

変数の名前は "loaded_" で始めてプラグインのファイル名をそのまま付けるようにし
てください。"g:" を付けることで関数の中で変数を使用したときに発生するミスを防
いでいます (関数の中では "g:" を付けない変数はローカル変数になります)。

"finish" を使ってファイルの残りの部分の読み込みを停止しています。この方法は
ファイル全体を if-endif で囲むよりも速いです。


マップ
------

さて、プラグインをもっと魅力あるものに仕上げましょう。マップを追加して、カーソ
ルの下の単語に対する修正を追加できるようにします。単純にキーを選んでマップを設
定することもできますが、そのキーは既にユーザーが使っているかもしれません。マッ
プに使用するキーをユーザーが選択できるようにするには、<Leader> を使います:

 22       map <unique> <Leader>a  <Plug>TypecorrAdd

"<Plug>TypecorrAdd" は目的の動作をします。詳しくは後で説明します。

使用したいキーを "mapleader" 変数に設定することで、マップの最初のキーを設定で
きます。例えば、次のように設定すると:

        let mapleader = "_"

マップは "_a" と定義されます。変数が設定されていない場合は初期設定 (バックス
ラッシュ) が使われます。つまり "\a" というマップが定義されます。

Note: 上記のコマンドでは <unique> が使われています。これは、同じマップが既に定
義されていた場合にエラーを表示します。:map-<unique>

マップするキーをユーザーが自分で定義できるようにするには、次のようにします:

 21     if !hasmapto('<Plug>TypecorrAdd')
 22       map <unique> <Leader>a  <Plug>TypecorrAdd
 23     endif

"<Plug>TypecorrAdd" に対するマップが既にあるかどうかを調べ、無い場合のみ
"<Leader>a" にマップを定義します。ユーザーは自分の vimrc ファイルの中でマップ
を定義することができます:

        map ,c  <Plug>TypecorrAdd

すると、マップのキーとして ",c" が使われます。"_a" や "\a" は使われません。


ピース
------

スクリプトが大きくなると、それを部品ごとに分けたくなります。それには関数やマッ
プを使います。しかし、そうすると関数やマップが他のスクリプトのものと衝突する可
能性があります。例えば、Add() という関数を追加したとき、他のスクリプトでも同じ
名前の関数が定義されているかもしれません。そのような場合は、名前の前に "s:" を
付けて、スクリプトの中だけで使える関数を定義します。

新しい修正を追加するための関数を定義します:

 30     function s:Add(from, correct)
 31       let to = input("type the correction for " . a:from . ": ")
 32       exe ":iabbrev " . a:from . " " . to
 ..
 36     endfunction

s:Add() 関数は同じスクリプトの中から呼び出すことができます。他のスクリプトが
s:Add() を定義していた場合、それはそのスクリプトにローカルであり、関数が定義さ
れたスクリプトの中からのみ呼び出すことができます。さらにグローバルの Add() 関
数 ("s:" 無し) を定義することもでき、それはまた別の関数になります。

マップ定義では <SID> が使えます。これは、現在のスクリプトを識別するためのスク
リプト ID を生成します。私たちの入力修正プラグインでは <SID> を次のように使い
ます:

 24     noremap <unique> <script> <Plug>TypecorrAdd  <SID>Add
 ..
 28     noremap <SID>Add  :call <SID>Add(expand("<cword>"), 1)<CR>

ユーザーが "\a" と入力すると、次の手順でキー入力が呼び出されます:

        \a  ->  <Plug>TypecorrAdd  ->  <SID>Add  ->  :call <SID>Add()

他のスクリプトで <SID>Add をマップすると、別のスクリプト ID が使われ、別のマッ
プが生成されます。

Note: s:Add() ではなく <SID>Add() と書いていることに注意してください。マップは
スクリプトの外側でユーザーが入力するものだからです。<SID> はスクリプト ID に変
換され、どのスクリプトの Add() 関数を呼べばいいのかわかるようになっています。

これは少し複雑ですが、複数のプラグインを同時に使用するためには必要なことです。
基本的なルールとしては、マップの中では <SID>Add() を使い、他の場所 (スクリプト
の中、自動コマンド、ユーザー定義コマンド) では s:Add() を使います。

マップと同じ方法で、メニューを追加することもできます:

 26     noremenu <script> Plugin.Add\ Correction      <SID>Add

プラグインのメニューを追加する場合は "Plugin" メニューの下に登録することが推奨
されています。この例ではメニューが一つだけですが、複数のメニューを追加する場合
は、サブメニューの使用が推奨されています。例えば、"Plugin.CVS" 以下に
"Plugin.CVS.checkin" や "Plugin.CVS.checkout" などの CVS の操作を登録します。

Note: 28 行目では ":noremap" を使って、他のマップでトラブルが起きないようにし
ています。例えば、誰かが ":call" をマップしているかもしれないからです。24 行目
でも ":noremap" を使っていますが、ここでは "<SID>Add" を再マップして欲しいの
で、"<script>" を使っています。これを使うとスクリプトローカルなマップだけが再
マップされます :map-<script>。26 行目でも ":noremenu" で同様のことをしていま
す :menu-<script>


<SID> と <Plug>                                         using-<Plug>
---------------

<SID> と <Plug> は、入力したキーに対するマップと、他のマップの中だけで使われる
マップが干渉しないようにするために使われます。<SID> と <Plug> の違いに注意して
ください:

<Plug>  これはスクリプトの外側からも見えます。ユーザーが自分でプラグインの機能
        をマップできるようにするような場合に使います。<Plug> は特殊なコード
        で、キーボードから入力されることはありません。
        キー列が他のプラグインとかぶらないように、<Plug> スクリプト名 マップ
        名、という形式で使ってください。
        我々の例では、スクリプト名が "Typecorr"、マップ名が "Add" なので、
        "<Plug>TypecorrAdd" というキー列になります。スクリプト名とマップ名の最
        初の文字だけを大文字にして、どこがマップ名なのかわかるようにします。

<SID>   これはスクリプト ID (スクリプト固有の識別子) です。
        Vim は内部で <SID> を "<SNR>123_" に変換します ("123" の部分はいろいろ
        な数字が入ります)。つまり、関数 "<SID>Add()" は、あるスクリプトでは
        "<SNR>11_Add()" という名前になり、別のスクリプトでは "<SNR>22_Add()"
        になります。これは ":function" コマンドで関数一覧を表示すると確認する
        ことができます。<SID> の変換はマップの中でも同様におこなわれるので、
        マップの中からスクリプトローカル関数を呼び出すことができます。


ユーザー定義コマンド
------------------

修正を追加するためのユーザー定義コマンドを追加します:

 38     if !exists(":Correct")
 39       command -nargs=1  Correct  :call s:Add(<q-args>, 0)
 40     endif

ユーザー定義コマンドは、同じ名前のコマンドがまだない場合のみ定義できます。既に
定義されている場合はエラーになります。":command!" を使ってユーザー定義関数を上
書きするのは良いアイデアとは言えません。ユーザーは、自分が定義したコマンドがな
ぜ動かないのか不思議に思うでしょう。:command


スクリプト変数
--------------

先頭に "s:" が付いた変数はスクリプト変数です。これはスクリプトの中だけで使えま
す。スクリプトの外からは見えません。同じ名前の変数を複数のスクリプトで使ってし
まうようなトラブルを避けることができます。Vim が実行されている間、変数は保持さ
れます。そして、同じスクリプトが再読み込みされると、再び同じ変数が使われます。
s:var

スクリプト変数は、同じスクリプトの中で定義された関数、自動コマンド、ユーザー定
義コマンドでも使えます。我々の例に、修正の数を数えるための数行のコードを追加し
ます:

 19     let s:count = 4
 ..
 30     function s:Add(from, correct)
 ..
 34       let s:count = s:count + 1
 35       echo s:count . " corrections now"
 36     endfunction

最初に s:count はスクリプトの中で 4 で初期化されます。その後、s:Add() 関数が呼
び出されると、s:count が増加します。関数がどこから呼ばれたかに関わらず、関数が
定義されたスクリプトのローカル変数が使われます。


まとめ
------

例題の完成形は以下のようになります:

  1     " Vim global plugin for correcting typing mistakes
  2     " Last Change:  2000 Oct 15
  3     " Maintainer:   Bram Moolenaar <Bram@vim.org>
  4     " License:      This file is placed in the public domain.
  5
  6     if exists("g:loaded_typecorr")
  7       finish
  8     endif
  9     let g:loaded_typecorr = 1
 10
 11     let s:save_cpo = &cpo
 12     set cpo&vim
 13
 14     iabbrev teh the
 15     iabbrev otehr other
 16     iabbrev wnat want
 17     iabbrev synchronisation
 18             \ synchronization
 19     let s:count = 4
 20
 21     if !hasmapto('<Plug>TypecorrAdd')
 22       map <unique> <Leader>a  <Plug>TypecorrAdd
 23     endif
 24     noremap <unique> <script> <Plug>TypecorrAdd  <SID>Add
 25
 26     noremenu <script> Plugin.Add\ Correction      <SID>Add
 27
 28     noremap <SID>Add  :call <SID>Add(expand("<cword>"), 1)<CR>
 29
 30     function s:Add(from, correct)
 31       let to = input("type the correction for " . a:from . ": ")
 32       exe ":iabbrev " . a:from . " " . to
 33       if a:correct | exe "normal viws\<C-R>\" \b\e" | endif
 34       let s:count = s:count + 1
 35       echo s:count . " corrections now"
 36     endfunction
 37
 38     if !exists(":Correct")
 39       command -nargs=1  Correct  :call s:Add(<q-args>, 0)
 40     endif
 41
 42     let &cpo = s:save_cpo
 43     unlet s:save_cpo

33 行目は説明がまだでした。これは、新しい修正をカーソルの下の単語に適用しま
す。:normal コマンドを使って新しい略語を適用しています。Note: マップと略語は
その場で展開されます。":noremap" で定義されたマップから関数が呼び出されたとし
ても動作は同じです。

'fileformat' オプションを "unix" に設定することが推奨されています。そうすれ
ば、Vim script はどこでも動作します。'fileformat' が "dos" に設定されたスクリ
プトは Unix では動作しません。:source_crnl も参照。設定が正しいことを確実に
するため、ファイルを保存する前に次のコマンドを実行してください:

        :set fileformat=unix


ドキュメント                                            write-local-help
------------

プラグインのドキュメントを書くのは良いアイデアです。ユーザーが動作を変更できる
ような場合には特に重要です。add-local-help ではどのようにしてドキュメントが
インストールされるか説明されています。

プラグインヘルプファイルの例を示します ("typecorr.txt"):

  1     *typecorr.txt*  Plugin for correcting typing mistakes
  2
  3     If you make typing mistakes, this plugin will have them corrected
  4     automatically.
  5
  6     There are currently only a few corrections.  Add your own if you like.
  7
  8     Mappings:
  9     <Leader>a   or   <Plug>TypecorrAdd
 10             Add a correction for the word under the cursor.
 11
 12     Commands:
 13     :Correct {word}
 14             Add a correction for {word}.
 15
 16                                                     *typecorr-settings*
 17     This plugin doesn't have any settings.

書式に気をつけなければならないのは一行目だけです。一行目はコピーされ、help.txt
の "LOCAL ADDITIONS:" の項に埋め込まれます local-additions。最初の "*" は一
行目の一桁目に書いてください。ヘルプを追加したら ":help" を実行して項目が追加
されたことを確認してください。

ヘルプの中で ** で文字を囲むとタグを追加することができます。ただし、既存のヘル
プタグと同じものを使わないでください。"typecorr-settings" のように、プラグイン
の名前を使ってタグを作るといいかもしれません。

ヘルプの他の部分を参照するときは || で囲みます。そうすれば、ユーザーは簡単にヘ
ルプの関連した部分を参照することができます。


ファイルタイプの認識                                    plugin-filetype
--------------------

ファイルタイプが Vim によって認識されない場合は、別ファイルにファイルタイプを
認識するためにコードを作成する必要があります。通常は、自動コマンドを使って、
ファイル名がパターンにマッチしたときにファイルタイプを設定します。例:

        au BufNewFile,BufRead *.foo                     set filetype=foofoo

この一行を 'runtimepath' の最初のディレクトリの "ftdetect/foofoo.vim" に書き
込みます。例えば、Unix なら "~/.vim/ftdetect/foofoo.vim" などです。ファイルタ
イプとスクリプトファイルの名前を同じにする決まりになっています。

必要ならより複雑な処理をすることもできます。例えば、ファイルの中身を見て言語を
判定したりできます。new-filetype も参照。


要約                                                    plugin-special
-----

プラグインで使用する特有事項の要約を示します:

s:name                  スクリプトローカル変数。

<SID>                   スクリプトID。マップや関数をスクリプトローカルにする
                        のに使う。

hasmapto()              スクリプトが提供している機能に対して、ユーザーが既に
                        マップを定義したかどうかをチェックする関数。

<Leader>                "mapleader" の値。ユーザーがその変数にキーを設定するこ
                        とで、プラグインのマップの開始キーを指定できる。

:map <unique>           マップが既に定義されているなら警告を発する。

:noremap <script>       スクリプトローカルマップだけを使う。グローバルマップは
                        使わない。

exists(":Cmd")          ユーザー定義コマンドが既にあるかどうかをチェックする。

==============================================================================
41.12 ファイルタイププラグインを書く  write-filetype-plugin ftplugin

ファイルタイププラグインはグローバルプラグインと似ていますが、カレントバッファ
のマップやオプションだけを設定します。ファイルタイププラグインの使用方法につい
ては add-filetype-plugin を参照してください。

先に 41.10 節のグローバルプラグインの項を読んでください。そこで説明されてい
ることはすべてファイルタイププラグインにもあてはまります。この節ではファイルタ
イププラグイン特有の事項だけを説明します。ファイルタイププラグインはカレント
バッファに対してのみ機能するということが最も大切です。


無効化
------

ファイルタイププラグインを書いて多くの人に使ってもらおうとするなら、プラグイン
を無効化できるようにしておく必要があります。プラグインの先頭に次のような記述を
追加してください:

        " このバッファに対してまだ実行されていない場合のみ処理を実行する
        if exists("b:did_ftplugin")
          finish
        endif
        let b:did_ftplugin = 1

これは同じプラグインが同じバッファで二重にロードされるのを防ぐためにも必要です
(":edit" コマンドを引数なしで実行したときに発生します)。

ユーザーは、次の一行だけを書いたファイルタイププラグインを作成することで、標準
プラグインのロードを無効化できます:

        let b:did_ftplugin = 1

ただし、そのファイルを保存したファイルタイププラグインディレクトリが、
'runtimepath' の中で $VIMRUNTIME よりも前にある必要があります。

標準プラグインを使いつつ、その設定を一つだけ変更したいという場合は、スクリプト
の中で設定を変更することができます:

        setlocal textwidth=70

このファイルを "after" ディレクトリに保存すると、(例えば filetype=vim なら) 標
準配布の "vim.vim" が読み込まれた後に、保存したファイルが読み込まれるようにな
ります after-directory。Unix ならファイルのパスは
"~/.vim/after/ftplugin/vim.vim" です。Note: 標準プラグインは "b:did_ftplugin"
を設定しますが、ここではそれを無視しています。


オプション
----------

ファイルタイププラグインでは、カレントバッファの設定だけを変更するため、次のコ
マンドを使ってオプションを設定してください:

        :setlocal

そして、バッファローカルなオプションだけを設定してください (どのオプションがそ
うなのかはヘルプで確認してください)。:setlocal コマンドでグローバルオプショ
ンやウィンドウローカルオプションを設定すると、たくさんのバッファの設定が変更さ
れます。ファイルタイププラグインはそのような動作をすべきではありません。

オプションの値がフラグや設定項目のリストなら、"+=" や "-=" を使うことで既存の
設定を維持することができます。ユーザーがそのオプションの設定を変更している可能
性もあるので注意してください。最初に初期設定に戻してから設定を変更するといいか
もしれません。例:

        :setlocal formatoptions& formatoptions+=ro


マップ
------

カレントバッファの中だけで機能するマップを作るには次のコマンドを使います:

        :map <buffer>

上述したように、マップは二段階に分けて作る必要があります。ファイルタイププラグ
インで機能を定義する例を示します:

        if !hasmapto('<Plug>JavaImport')
          map <buffer> <unique> <LocalLeader>i <Plug>JavaImport
        endif
        noremap <buffer> <unique> <Plug>JavaImport oimport ""<Left><Esc>

hasmapto() を使って、ユーザーが既に <Plug>JavaImport に対してマップを定義し
ているかどうかを調べます。未定義ならファイルタイププラグインの標準のマップを定
義します。マップは <LocalLeader> で開始します。そうすることで、ファイルタイプ
プラグインのマップを開始するキーをユーザーが選択できます。初期設定はバックス
ラッシュです。
"<unique>" を使って、マップが既に存在したとき、あるいは既存のマップと重複した
ときにエラーメッセージが表示されるようにします。
:noremap を使って、ユーザーが定義した他のマップの影響を受けないようにしま
す。":noremap <script>" を使うと、スクリプトの中で定義した <SID> で始まるマッ
プだけが再マップされます。

ユーザーがファイルタイププラグインのマップを無効化できる仕組みを提供しなければ
なりません。例えば、"mail" ファイルタイプのプラグインなら次のようにします:

        " マップを追加する。ユーザーが望まない場合は追加しない。
        if !exists("no_plugin_maps") && !exists("no_mail_maps")
          " "> " を挿入して引用する
          if !hasmapto('<Plug>MailQuote')
            vmap <buffer> <LocalLeader>q <Plug>MailQuote
            nmap <buffer> <LocalLeader>q <Plug>MailQuote
          endif
          vnoremap <buffer> <Plug>MailQuote :s/^/> /<CR>
          nnoremap <buffer> <Plug>MailQuote :.,$s/^/> /<CR>
        endif

ここでは二つのグローバル変数が使われています:
  no_plugin_maps        すべてのファイルタイププラグインのマップを無効化
  no_mail_maps          特定のファイルタイププラグインのマップを無効化


ユーザー定義コマンド
--------------------

ファイルタイプ用のユーザー定義コマンドを追加して、それを一つのバッファの中だけ
で使えるようにするには、:command の引数に "-buffer" を指定します。例:

        :command -buffer  Make  make %:r.s


変数
-----

ファイルタイププラグインは対応するすべてのバッファに対して実行されます。スクリ
プトローカル変数 s:var はすべての実行で共有されます。バッファごとの変数を使
いたい場合はバッファローカル変数 b:var を使ってください。


関数
-----

関数は一度だけ定義すれば十分です。しかし、ファイルタイププラグインは対応する
ファイルが開かれるたびに読み込まれます。次のようにすると関数が一度だけ定義され
るようになります:

        :if !exists("*s:Func")
        :  function s:Func(arg)
        :    ...
        :  endfunction
        :endif


アンドゥ                                        undo_indent undo_ftplugin
--------

ユーザーが ":setfiletype xyz" としたとき、それ以前のファイルタイプの効果は無効
になるべきです。b:undo_ftplugin 変数にコマンドを設定し、ファイルタイププラグイ
ンの設定をアンドゥするようにしてください。例:

        let b:undo_ftplugin = "setlocal fo< com< tw< commentstring<"
                \ . "| unlet b:match_ignorecase b:match_words b:match_skip"

":setlocal" でオプション名の後に "<" を付けると、そのオプションをグローバルな
値でリセットします。オプションをリセットするにはこの方法が一番です。

このように行継続を使うには 'cpoptions' から "C" フラグを取り除く必要がありま
す。上述の use-cpo-save を参照してください。

インデントスクリプトの効果をアンドゥする為には、それに応じた b:undo_indent 変
数を設定すべきです。


ファイル名
----------

ファイルタイププラグインのファイル名にはファイルタイプ名が含まれていなければな
りません ftplugin-name。次の三つのうちのどれかにしてください:

        .../ftplugin/stuff.vim
        .../ftplugin/stuff_foo.vim
        .../ftplugin/stuff/bar.vim

"stuff" はファイルタイプ名、"foo" と "bar" は任意の名前です。


要約                                                    ftplugin-special
-----

ファイルタイププラグインの特有事項を要約します:

<LocalLeader>           "maplocalleader" の値。ユーザーがその変数にキーを設定
                        することで、ファイルタイププラグインのマップの開始キー
                        を指定できる。

:map <buffer>           バッファローカルなマップを定義する。

:noremap <script>       同スクリプトで定義している <SID> で始まるマップだけを
                        再マップする。

:setlocal               カレントバッファのオプションのみ設定する。

:command -buffer        バッファローカルなユーザー定義コマンドを定義する。

exists("*s:Func")       関数が定義済かどうかをチェックする。

プラグイン全般に関する事項は plugin-special を参照してください。

==============================================================================
41.13 コンパイラプラグインを書く              write-compiler-plugin

コンパイラプラグインは特定のコンパイラを使うためのオプションを設定します。ユー
ザーは :compiler コマンドでその設定を読み込むことができます。設定されるオプ
ションは主に 'errorformat' と 'makeprg' です。

百聞は一見に如かず。次のコマンドですべての標準コンパイラプラグインを開くことが
できます:

        :next $VIMRUNTIME/compiler/*.vim

:next を使って次のプラグインファイルに移動してください。

これらのファイルには二つの特有事項があります。一つは、標準ファイルに対して設定
を追加したり上書きしたりできる仕組みです。標準ファイルの先頭は次のようになって
います:

        :if exists("current_compiler")
        :  finish
        :endif
        :let current_compiler = "mine"

コンパイラファイルを書いて、それを個人用のランタイムディレクトリ (例えば Unix
なら ~/.vim/compiler) に置いたとき、"current_compiler" 変数を設定することで標
準ファイルの設定をスキップすることができます。
                                                        :CompilerSet
二つ目は、":compiler!" が使われたときは ":set" を使い、":compiler" が使われた
ときは ":setlocal" を使う仕組みです。Vim はそのために ":CompilerSet" という
ユーザーコマンドを定義します。古い Vim はそれを定義しないので、プラグインの中
で定義してください。例:

  if exists(":CompilerSet") != 2
    command -nargs=* CompilerSet setlocal <args>
  endif
  CompilerSet errorformat&              " use the dfault 'errorformat'
  CompilerSet makeprg=nmake

コンパイラプラグインを書いて、それを Vim の配布物に含めたり、システムのランタ
イムディレクトリに入れたりする場合は、上記の方法を使ってください。
"current_compiler" がユーザープラグインで設定された場合は何も実行しないように
します。

コンパイラプラグインを書いて標準プラグインの設定を上書きする場合は
"current_compiler" をチェックしないようにします。そのプラグインは最後に読み込
まれないといけないので、'runtimepath' の最後にあるディレクトリに置きます。例え
ば、Unix なら ~/.vim/after/compiler などです。

==============================================================================
41.14 プラグインを書く (高速ロード版)         write-plugin-quickload

プラグインが成長し、とても大きくなることがあります。すると、起動速度は遅くなっ
てきます。例えそのプラグインをたまにしか使わないとしても遅くなります。そういう
ときはクイックロードプラグインの出番です。

基本的なアイデアはプラグインを二回に分けて読み込むということです。一回目はユー
ザー定義コマンドやマップを定義して機能を提供します。二回目は機能を実装する関数
を定義します。

スクリプトを二回読み込むことがクイックロードだというと驚かれるかもしれません。
この手法の意味は、一回目は高速に読み込み、スクリプトの重い部分は二回目に後回し
にするということです。二回目の読み込みは、ユーザーが実際にその機能を使用したと
きに発生します。あなたがその機能を常に使うなら、これは逆に遅くなってしまいま
す。

Note Vim 7 以降では代わりの方法があります。41.15 で説明されている autoload
機能を使う方法です。

次に例を示します:

        " クイックロードのデモ用のグローバルプラグイン
        " Last Change:  2005 Feb 25
        " Maintainer:   Bram Moolenaar <Bram@vim.org>
        " License:      This file is placed in the public domain.

        if !exists("s:did_load")
                command -nargs=* BNRead  call BufNetRead(<f-args>)
                map <F19> :call BufNetWrite('something')<CR>

                let s:did_load = 1
                exe 'au FuncUndefined BufNet* source ' . expand('<sfile>')
                finish
        endif

        function BufNetRead(...)
                echo 'BufNetRead(' . string(a:000) . ')'
                " read 機能をここに書く
        endfunction

        function BufNetWrite(...)
                echo 'BufNetWrite(' . string(a:000) . ')'
                " write 機能をここに書く
        endfunction

このスクリプトが最初に読み込まれたとき、"s:did_load" は設定されていません。
"if" と "endif" の間のコマンドが実行されます。:finish コマンドによって終了
し、スクリプトの残りの部分は実行されません。

二回目に読み込まれたときは "s:did_load" が存在するので、"endif" 以降のコマンド
が実行されます。この部分では (長くなる可能性のある) BufNetRead() 関数と
BufNetWrite() 関数を定義します。

このスクリプトをプラグインディレクトリに置くと、Vim の起動時に実行されます。処
理の流れは次のようになります:

1. 起動時にスクリプトが読み込まれる。"BNRead" コマンドが定義され、<F19> キーに
   マップが設定される。自動コマンドの FuncUndefined が定義される。":finish"
   コマンドによってスクリプトが終了する。

2. ユーザーが BNRead コマンド実行する、または <F19> キーを押す。BufNetRead()
   関数か BufNetWrite() 関数が呼び出される。

3. Vim はその関数を見つけることができず、自動コマンドの FuncUndefined イベン
   トを発行する。関数名が "BufNet*" というパターンにマッチするので、"source
   fname" コマンドが実行される。"fname" はスクリプトの名前になります。スクリプ
   トがどこに保存されていても、"<sfile>" が展開されてファイル名になります
   (expand()参照)。

4. スクリプトが再び読み込まれる。"s:did_load" 変数が存在するので関数が定義され
   る。

遅延ロードされる関数の名前が FuncUndefined 自動コマンドのパターンにマッチし
ていることに注意してください。他のプラグインがこのパターンにマッチする関数を定
義しているとうまく動きません。

==============================================================================
41.15 ライブラリスクリプトを書く              write-library-script

いろいろな場所で同じ機能が必要になることがあります。コードが二、三行以上になる
場合は、それを一つのスクリプトに入れて、他のスクリプトから使えるようにしたくな
ると思います。そのようなスクリプトをライブラリスクリプトと呼びます。

自分でライブラリスクリプトを読み込むことは可能ですが、同じスクリプトを二重に読
み込まないようにする必要があります。それには exists() 関数を使います。例:

        if !exists('*MyLibFunction')
           runtime library/mylibscript.vim
        endif
        call MyLibFunction(arg)

'runtimepath' に設定されたディレクトリの中の "library/mylibscript.vim" の中で
MyLibFunction() が定義されている必要があります。

これをより簡単にするために、Vim には autoload という仕組みがあります。同じこと
を次のように書くことができます:

        call mylib#myfunction(arg)

この方がずっと簡単でしょう? Vim は関数の名前を見て、それが未定義なら、
'runtimepath' の中から "autoload/mylib.vim" を探します。そのスクリプトは関数
"mylib#myfunction()" を定義していなければなりません。

mylib.vim には他の関数も入れられます。ライブラリスクリプトの中では自由に関数を
作ることができます。ただし、関数名の '#' より前の部分はスクリプトの名前と同じ
にする必要があります。そうしないと Vim はどのスクリプトを読み込めばいいのかわ
かりません。

ライブラリスクリプトをたくさん書く場合は、サブディレクトリを使うといいかもしれ
ません。例:

        call netlib#ftp#read('somefile')

Unix では、このライブラリスクリプトは次のような場所に置かれます:

        ~/.vim/autoload/netlib/ftp.vim

関数は次のように定義します:

        function netlib#ftp#read(fname)
                "  ftp を使ってファイルを読み込む
        endfunction

関数定義と関数呼び出しではまったく同じ名前が使われます。最後の '#' より前の部
分がサブディレクトリとスクリプトの名前に対応しています。

同じ方法で変数を扱うこともできます:

        let weekdays = dutch#weekdays

これによって "autoload/dutch.vim" が読み込まれます。そのスクリプトには例えば次
のようなコードが書かれています:

        let dutch#weekdays = ['zondag', 'maandag', 'dinsdag', 'woensdag',
                \ 'donderdag', 'vrijdag', 'zaterdag']

より詳しくは autoload を参照してください。

==============================================================================
41.16 Vim script を配布する                   distribute-script

Vim ユーザーは Vim のウェブサイト http://www.vim.org でスクリプトを探します。
便利なスクリプトを作ったら、ぜひ共有しましょう!

Vim script はどのシステムでも使えます。tar や gzip コマンドは存在しないことが
あります。ファイルをまとめたり圧縮したりするには "zip" ユーティリティが推奨さ
れています。

可搬性を最大限に高めるには、Vim 自身を使ってスクリプトをパッケージ化します。そ
れには Vimball ユーティリティを使います。vimball を参照。

自動更新するための行を書いておくと便利です。glvs-plugins を参照。

==============================================================================

次章: usr_42.txt  新しいメニューを追加する

Copyright: see manual-copyright  vim:tw=78:ts=8:ft=help:norl: