Usr_41

Nvim の :help ページは、生成されており、ソースtree-sitter-vimdoc パーサーで解析したものです。


VIM ユーザーマニュアル - Bram Moolenaar 著
Vim スクリプトを書く
Vim スクリプト言語は、起動時の vimrc ファイル、構文ファイル、その他多くのものに使用されます。この章では、Vim スクリプトで使用できる項目について説明します。多くの項目があるため、長い章になります。
41.1 はじめに 41.2 変数 41.341.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 スクリプトの配布
次の章: usr_42.txt 新しいメニューを追加する 前の章: usr_40.txt 新しいコマンドを作成する 目次: usr_toc.txt
Vim スクリプトとの最初の出会いは、vimrc ファイルでしょう。Vim は起動時にこのファイルを読み込み、コマンドを実行します。オプションを設定して、好みの値にすることができます。また、任意のコロンコマンド(":" で始まるコマンド。Ex コマンドまたはコマンドラインコマンドとも呼ばれます)を使用できます。構文ファイルも Vim スクリプトです。特定のファイルタイプのオプションを設定するファイルも同様です。複雑なマクロは、別の Vim スクリプトファイルで定義できます。他にも用途は考えられます。
Python に詳しい方は、こちらに Python と Vim スクリプトの比較と、他のドキュメントへのポインタがあります: https://gist.github.com/yegappan/16d964a37ead0979b05e655aa036cad0 また、JavaScript に詳しい方はこちら: https://w0rp.com/blog/post/vim-script-for-the-javascripter/
簡単な例から始めましょう
:let i = 1
:while i < 5
:  echo "count is" i
:  let i += 1
:endwhile
注: ここでは、":" 文字は実際には必要ありません。コマンドを入力するときにのみ使用する必要があります。Vim スクリプトファイルでは、省略できます。ここでは、これらがコロンコマンドであることを明確にし、ノーマルモードコマンドと区別するために使用します。 注: ここにあるテキストから行をヤンクして、:@" で実行することで、例を試すことができます。
この例のコードの出力は次のようになります
count is 1
count is 2
count is 3
count is 4
最初の行の ":let" コマンドは、変数に値を代入します。一般的な形式は次のとおりです
:let {variable} = {expression}
この場合、変数名は "i" で、式は単純な値である数値の 1 です。":while" コマンドはループを開始します。一般的な形式は次のとおりです
:while {condition}
:  {statements}
:endwhile
一致する ":endwhile" までのステートメントは、条件が真である間実行されます。ここで使用されている条件は、式 "i < 5" です。これは、変数 i が 5 より小さい場合に真になります。 注: 実行し続ける 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
:forrange() がどのように機能するかは後で説明します。待ちきれない場合は、リンクをたどってください。

4種類の数値

数値は、10進数、16進数、8進数、または2進数にすることができます。
16進数は "0x" または "0X" で始まります。たとえば、"0x1f" は10進数の31です。

31.

8進数は、"0o"、"0O"、または 0 と別の数字で始まります。"0o17" は10進数の15です。
2進数は "0b" または "0B" で始まります。たとえば、"0b101" は10進数の5です。
10進数は単なる数字です。注意: 10進数の前に 0 を付けないでください。8進数として解釈されます!
":echo" コマンドは常に10進数を出力します。例
:echo 0x7f 0o36
127 30
数値はマイナス記号で負にできます。これは、16進数、8進数、および2進数でも機能します。マイナス記号は減算にも使用されます。前の例と比較してください
:echo 0x7f -0o36
97
式内の空白は無視されます。ただし、項目を区切るために使用して、式を読みやすくすることをお勧めします。たとえば、上記の負の数との混同を避けるために、マイナス記号と次の数の間にスペースを入れます
:echo 0x7f - 0o36

41.2 変数

変数名は、ASCII 文字、数字、およびアンダースコアで構成されます。数字で始めることはできません。有効な変数名は次のとおりです
counter _aap3 very_long_variable_name_with_underscores FuncLength LENGTH
無効な名前は "foo.bar" および "6var" です。これらの変数はグローバルです。現在定義されている変数のリストを表示するには、次のコマンドを使用します
:let
グローバル変数はどこでも使用できます。つまり、変数 "count" が1つのスクリプトファイルで使用されている場合、別のファイルでも使用される可能性があります。これは少なくとも混乱を招き、最悪の場合には深刻な問題につながります。これを回避するために、スクリプトファイルに対してローカルな変数を "s:" を先頭に付けて使用できます。たとえば、あるスクリプトには次のコードが含まれています
:let s:count = 1
:while s:count < 5
:  source other.vim
:  let s:count += 1
:endwhile
"s:count" はこのスクリプトに対してローカルであるため、"other.vim" スクリプトをソースしてもこの変数が変更されないことを確信できます。"other.vim" も "s:count" 変数を使用している場合、それは別のコピーであり、そのスクリプトに対してローカルになります。スクリプトローカル変数の詳細については、こちらを参照してください: script-variable
他にも変数の種類があります。 internal-variables を参照してください。最もよく使用されるものは次のとおりです
b:name バッファーにローカルな変数 w:name ウィンドウにローカルな変数 g:name グローバル変数 (関数内でも) 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 が真と呼ぶのは、ゼロ以外のすべてです。ゼロは偽です。 注: Vim は、数値を探しているときに、文字列を自動的に数値に変換します。数字で始まらない文字列を使用すると、結果の数値はゼロになります。したがって、これに注意してください
:if "true"
"true" はゼロ、つまり偽として解釈されます!

文字列変数と定数

これまでは、変数の値には数値のみが使用されていました。文字列も使用できます。数値と文字列は、Vim がサポートする変数の基本型です。型は動的で、":let" で変数に値を代入するたびに設定されます。型の詳細については、41.8 を参照してください。変数に文字列値を代入するには、文字列定数を使用する必要があります。これには2つのタイプがあります。まず、二重引用符で囲まれた文字列です
:let name = "peter"
:echo name
peter
文字列内に二重引用符を含める場合は、その前にバックスラッシュを付けます
:let name = "\"peter\""
:echo name
"peter"
バックスラッシュの必要性を回避するために、単一引用符で囲まれた文字列を使用できます
:let name = '"peter"'
:echo name
"peter"
単一引用符の文字列内では、すべての文字はそのままです。単一引用符自体のみが特別です: 1つを取得するには、2つ使用する必要があります。バックスラッシュは文字どおりに解釈されるため、その後の文字の意味を変更するために使用することはできません。二重引用符文字列では、特殊文字を使用できます。いくつかの便利なものを以下に示します
\t <Tab> \n <NL>, 改行 \r <CR>, <Enter> \e <Esc> \b <BS>, バックスペース \" " \\ \, バックスラッシュ \<Esc> <Esc> \<C-W> CTRL-W
最後の2つは単なる例です。"\<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
これにより、「The Start」パターンが、'ignorecase'オプションがオフの状態で使用されることが保証されます。それでも、ユーザーが設定した値は保持されます。(これを行う別の方法は、パターンに"\C"を追加することです。 /\Cを参照してください。)

数学

これらの基本的な項目を組み合わせると、さらに面白くなります。まず、数値に関する数学から始めましょう。
a + b 加算 a - b 減算 a * b 乗算 a / b 除算 a % b 剰余
通常の優先順位が使用されます。例
:echo 10 + 5 * 2
20
グループ化は括弧で行われます。ここには驚きはありません。例
:echo (10 + 5) * 2
30
文字列は「..」で連結できます(expr6を参照)。例
:echo "foo" .. "bar"
foobar
":echo"コマンドが複数の引数を受け取ると、それらをスペースで区切ります。例では、引数は単一の式であるため、スペースは挿入されません。
C言語から借用した条件式
a ? b : c
"a"がtrueと評価された場合は"b"が使用され、それ以外の場合は"c"が使用されます。例
:let i = 4
:echo i > 5 ? "i is big" : "i is small"
i is small
構文の3つの部分は常に最初に評価されるため、次のように機能すると見なすことができます。
(a) ? (b) : (c)

41.4 条件文

":if"コマンドは、条件が満たされた場合にのみ、対応する":endif"までの後続のステートメントを実行します。一般的な形式は次のとおりです。
:if {condition} {statements} :endif
{condition}がtrue(ゼロ以外)と評価された場合にのみ、{statements}が実行されます。これらは有効なコマンドである必要があります。それらにガベージが含まれている場合、Vimは":endif"を見つけることができません。":else"も使用できます。これの一般的な形式は次のとおりです。
:if {condition} {statements} :else {statements} :endif
2番目の{statements}は、最初のものが実行されない場合にのみ実行されます。最後に、":elseif"があります。
:if {condition} {statements} :elseif {condition} {statements} :endif
これは":else"を使用してから"if"を使用するのと同様に機能しますが、追加の":endif"は必要ありません。vimrcファイルで役立つ例は、'term'オプションをチェックし、その値に応じて何かを行うことです。
:if &term == "xterm"
:  " Do stuff for xterm
:elseif &term == "vt100"
:  " Do stuff for a vt100 terminal
:else
:  " Do something for other terminals
:endif

論理演算

例でいくつか既に使用しました。これらは最もよく使用されるものです。
a == b 等しい a != b 等しくない a > b より大きい a >= b 以上 a < b より小さい a <= b 以下
結果は、条件が満たされた場合は1、それ以外の場合は0になります。例
:if v:version >= 700
:  echo "congratulations"
:else
:  echo "you are using an old version, upgrade!"
:endif
ここで、"v:version"はVimによって定義された変数であり、Vimのバージョン値を持っています。600はバージョン6.0用です。バージョン6.1の値は601です。これは、複数のバージョンのVimで動作するスクリプトを作成するのに非常に便利です。v:version
論理演算子は、数値と文字列の両方で機能します。2つの文字列を比較する場合、数学的な差が使用されます。これによりバイト値が比較されますが、一部の言語では正しくない場合があります。文字列を数値と比較する場合、文字列は最初に数値に変換されます。文字列が数値のように見えない場合、数値のゼロが使用されるため、これは少しトリッキーです。例
:if 0 == "one"
:  echo "yes"
:endif
"one"は数値のように見えないため、数値のゼロに変換されるため、これは「yes」とエコーされます。
文字列にはさらに2つの項目があります。
a =~ b 一致する a !~ b 一致しない
左側の項目"a"は文字列として使用されます。右側の項目"b"は、検索に使用されるようなパターンとして使用されます。例
:if str =~ " "
:  echo "str contains a space"
:endif
:if str !~ '\.$'
:  echo "str does not end in a full stop"
:endif
パターンに単一引用符の文字列を使用していることに注意してください。これは便利です。二重引用符の文字列ではバックスラッシュを2倍にする必要があり、パターンには多くのバックスラッシュが含まれる傾向があるためです。
文字列を比較する場合、'ignorecase'オプションが使用されます。それが必要ない場合は、大文字小文字を区別するために"#"を追加し、大文字小文字を区別しないように"?"を追加します。したがって、"==?"は大文字と小文字を区別せずに2つの文字列が等しいかどうかを比較します。そして、"!~#"は、パターンの大文字小文字もチェックして、パターンが一致しないかどうかを確認します。詳細な表については、expr-==を参照してください。

ループ処理の追加

":while"コマンドは既に説明しました。":while"と":endwhile"の間には、さらに2つのステートメントを使用できます。
:continue whileループの先頭に戻ります。ループは続行されます。 :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 式の実行

これまでのところ、スクリプト内のコマンドはVimによって直接実行されていました。":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"の引数が完全なコマンドであることを確認してください。そうしないと、Vimは引数の終わりに到達し、コマンドを中断します。たとえば、挿入モードを開始する場合、挿入モードを終了する必要があります。これは機能します。
: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 関数の使用

Vimは多くの関数を定義し、それによって大量の機能を提供します。このセクションでは、いくつかの例を示します。以下に、リスト全体を見つけることができます。function-list
関数は、":call"コマンドで呼び出されます。パラメータは、コンマで区切られた括弧の間で渡されます。例
:call search("Date: ", "W")
これは、search()関数を、引数"Date: "と"W"で呼び出します。search()関数は、最初の引数を検索パターンとして使用し、2番目の引数をフラグとして使用します。"W"フラグは、検索がファイルの末尾をラップしないことを意味します。
関数は、式の中で呼び出すことができます。例
:let line = getline(".")
:let repl = substitute(line, '\a', "*", "g")
:call setline(".", repl)
getline()関数は、現在のバッファから行を取得します。その引数は、行番号の指定です。この場合、"."が使用されます。これは、カーソルがある行を意味します。substitute()関数は、":substitute"コマンドと同様のことを行います。最初の引数は、置換を実行する文字列です。2番目の引数はパターン、3番目は置換文字列です。最後に、最後の引数はフラグです。setline()関数は、最初の引数で指定された行を新しい文字列(2番目の引数)に設定します。この例では、カーソルの下の行がsubstitute()の結果で置き換えられます。したがって、3つのステートメントの効果は次と同じです。
:substitute/\a/*/g
substitute()呼び出しの前後にさらに多くの作業を行う場合、関数の使用は面白くなります。
多くの関数があります。ここでは、それらが何に使用されるかによってグループ化して説明します。アルファベット順のリストは、こちらにあります。builtin-function-details。関数名でCTRL-]を使用して、詳細なヘルプにジャンプします。
文字列操作:string-functions
nr2char() その数値で文字を取得 list2str() 数値のリストから文字列を取得 char2nr() 文字の数値を取得 str2list() 文字列から数値のリストを取得 str2nr() 文字列を数値に変換 str2float() 文字列を浮動小数点数に変換 printf() %項目に従って文字列をフォーマット escape() 文字列内の文字を'\ 'でエスケープ shellescape() シェルコマンドで使用するために文字列をエスケープ fnameescape() Vimコマンドで使用するためにファイル名をエスケープ tr() 文字のセットを別のセットに変換 strtrans() 文字列を変換して印刷可能にする keytrans() 内部キーコードを:mapで使用できる形式に変換 tolower() 文字列を小文字にする toupper() 文字列を大文字にする charclass() 文字のクラス match() 文字列内のパターンが一致する位置 matchbufline() バッファ内のパターンのすべての一致 matchend() 文字列内でパターンのマッチが終了する位置 matchfuzzy() 文字列のリスト内の文字列にファジーマッチ matchfuzzypos() 文字列のリスト内の文字列にファジーマッチ matchstr() 文字列内のパターンのマッチ matchstrlist() 文字列リスト内のパターンのすべての一致 matchstrpos() 文字列内のパターンのマッチと位置 matchlist() matchstr()と同様に、サブマッチも返します stridx() 長い文字列内の短い文字列の最初のインデックス strridx() 長い文字列内の短い文字列の最後のインデックス strlen() バイト単位での文字列の長さ strcharlen() 文字単位での文字列の長さ strchars() 文字列内の文字数 strutf16len() 文字列内のUTF-16コードユニット数 strwidth() 表示時の文字列のサイズ strdisplaywidth() タブを処理し、表示時の文字列のサイズを返す setcellwidths() 文字セル幅のオーバーライドを設定 getcellwidths() 文字セル幅のオーバーライドを取得 reverse() 文字列内の文字の順序を逆にする substitute() 文字列でパターンに一致するものを別の文字列に置き換える submatch() ":s"とsubstitute()内の特定のサブマッチを取得 strpart() バイトインデックスを使用して文字列の一部を取得 strcharpart() 文字インデックスを使用して文字列の一部を取得 slice() Vim9スクリプトで文字インデックスを使用して文字列のスライスを取得 strgetchar() 文字インデックスを使用して文字列から文字を取得 expand() 特殊キーワードを展開 expandcmd() :editのようにコマンドを展開する iconv() テキストをあるエンコーディングから別のエンコーディングに変換 byteidx() 文字列内の文字のバイトインデックス byteidxcomp() byteidx()と同様だが、構成文字をカウントする charidx() 文字列内のバイトの文字インデックス utf16idx() 文字列内のバイトのUTF-16インデックス repeat() 文字列を複数回繰り返す eval() 文字列式を評価する execute() Exコマンドを実行し、出力を取得 win_execute() execute()と同様だが、指定されたウィンドウで実行 trim() 文字列から文字をトリムする gettext() メッセージ翻訳を検索
リスト操作:list-functions
get() リスト内のアイテムを、間違ったインデックスの場合でもエラーなしで取得 len() リスト内のアイテム数 empty() リストが空かどうかを確認 insert() リスト内の任意の位置にアイテムを挿入 add() リストの末尾にアイテムを追加 extend() リストを別のリストの末尾に追加 extendnew() 新しいリストを作成し、アイテムを追加 remove() リストから1つまたは複数のアイテムを削除 copy() リストの浅いコピーを作成 deepcopy() リストの完全なコピーを作成 filter() リストから選択したアイテムを削除 map() 各リストのアイテムを変更 mapnew() 変更されたアイテムを持つ新しいリストを作成 foreach() リストの各アイテムに関数を適用 reduce() リストを1つの値に集約 slice() リストから一部分を抽出 sort() リストをソート reverse() リスト内のアイテムの順序を反転 uniq() 隣接する重複したアイテムのコピーを削除 split() 文字列をリストに分割 join() リストのアイテムを文字列に結合 range() 数値のシーケンスを含むリストを返す string() リストの文字列表現 call() リストを引数として関数を呼び出す index() リストまたはBlob内の値のインデックス indexof() 式が真と評価されるリストまたはBlob内のインデックス max() リスト内の最大値 min() リスト内の最小値 count() リスト内で値が出現する回数をカウント repeat() リストを複数回繰り返す flatten() リストを平坦化 flattennew() リストのコピーを平坦化
辞書操作: dict-functions
get() 間違ったキーの場合でもエラーなしでエントリを取得 len() 辞書内のエントリ数 has_key() 辞書内にキーが存在するかどうかを確認 empty() 辞書が空かどうかを確認 remove() 辞書からエントリを削除 extend() ある辞書から別の辞書にエントリを追加 extendnew() 新しい辞書を作成し、アイテムを追加 filter() 辞書から選択したエントリを削除 map() 各辞書のエントリを変更 mapnew() 変更されたアイテムを持つ新しい辞書を作成 foreach() 辞書の各アイテムに関数を適用 keys() 辞書のキーのリストを取得 values() 辞書の値のリストを取得 items() 辞書のキーと値のペアのリストを取得 copy() 辞書の浅いコピーを作成 deepcopy() 辞書の完全なコピーを作成 string() 辞書の文字列表現 max() 辞書内の最大値 min() 辞書内の最小値 count() 値が出現する回数をカウント
浮動小数点演算: float-functions
float2nr() 浮動小数点数を数値に変換 abs() 絶対値 (数値にも使用可能) round() 四捨五入 ceil() 切り上げ floor() 切り捨て trunc() 小数点以下の値を削除 fmod() 除算の余り exp() 指数関数 log() 自然対数 (底eの対数) log10() 常用対数 (底10の対数) pow() xのy乗の値 sqrt() 平方根 sin() サイン cos() コサイン tan() タンジェント asin() アークサイン acos() アークコサイン atan() アークタンジェント atan2() アークタンジェント sinh() ハイパボリックサイン cosh() ハイパボリックコサイン tanh() ハイパボリックタンジェント isinf() 無限大かどうかを確認 isnan() 非数かどうかを確認
Blob操作: blob-functions
blob2list() Blobから数値のリストを取得 list2blob() 数値のリストからBlobを取得 reverse() Blob内の数値の順序を反転
その他の演算: bitwise-function
and() ビット単位のAND invert() ビット単位の反転 or() ビット単位のOR xor() ビット単位のXOR sha256() SHA-256ハッシュ rand() 擬似乱数を取得 srand() rand()で使用するシードを初期化
変数: 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() カーソルの画面行を取得 screenpos() テキスト文字の画面行と列 virtcol2col() 画面上のテキスト文字のバイトインデックス getcurpos() カーソルの位置を取得 getpos() カーソル、マークなどの位置を取得 setpos() カーソル、マークなどの位置を設定 getmarklist() グローバル/ローカルマークのリスト byte2line() 特定のバイトの行番号を取得 line2byte() 特定の行のバイト数を取得 diff_filler() 行の上のフィラー行の数を取得 screenattr() 画面行/列の属性を取得 screenchar() 画面行/列の文字コードを取得 screenchars() 画面行/列の文字コードを取得 screenstring() 画面行/列の文字列を取得 charcol() カーソルまたはマークの文字番号 getcharpos() カーソル、マークなどの文字位置を取得 setcharpos() カーソル、マークなどの文字位置を設定 getcursorcharpos() カーソルの文字位置を取得 setcursorcharpos() カーソルの文字位置を設定
現在のバッファ内のテキストの操作: text-functions
getline() バッファから1行または複数行のリストを取得 getregion() バッファからテキストの領域を取得 getregionpos() 領域の位置のリストを取得 setline() バッファ内の行を置き換え append() バッファに行または複数行のリストを追加 indent() 特定の行のインデント cindent() Cインデントに従ってインデント lispindent() Lispインデントに従ってインデント nextnonblank() 次の空白でない行を検索 prevnonblank() 前の空白でない行を検索 search() パターンのマッチを検索 searchpos() パターンのマッチを検索 searchcount() カーソルの前/後のマッチ数を取得 searchpair() 開始/スキップ/終了のもう一方の端を検索 searchpairpos() 開始/スキップ/終了のもう一方の端を検索 searchdecl() 名前の宣言を検索 getcharsearch() 文字検索情報を返す setcharsearch() 文字検索情報を設定
別のバッファ内のテキストの操作: getbufline() 指定されたバッファから複数行のリストを取得 getbufoneline() 指定されたバッファから1行を取得 setbufline() 指定されたバッファ内の行を置き換え appendbufline() 指定されたバッファに複数行のリストを追加 deletebufline() 指定されたバッファから行を削除
system-functions file-functions システム関数とファイルの操作: glob() ワイルドカードを展開 globpath() 複数のディレクトリでワイルドカードを展開 glob2regpat() グロブパターンを検索パターンに変換 findfile() ディレクトリのリストからファイルを検索 finddir() ディレクトリのリストからディレクトリを検索 resolve() ショートカットがどこを指しているかを調べる fnamemodify() ファイル名を変更 pathshorten() パス内のディレクトリ名を短縮 simplify() 意味を変えずにパスを簡略化 executable() 実行可能プログラムが存在するかどうかを確認 exepath() 実行可能プログラムのフルパス filereadable() ファイルが読み取り可能かどうかを確認 filewritable() ファイルが書き込み可能かどうかを確認 getfperm() ファイルの権限を取得 setfperm() ファイルの権限を設定 getftype() ファイルの種類を取得 isabsolutepath() パスが絶対パスかどうかを確認 isdirectory() ディレクトリが存在するかどうかを確認 getfsize() ファイルのサイズを取得 getcwd() 現在の作業ディレクトリを取得 haslocaldir() 現在のウィンドウで:lcdまたは:tcdが使用されているかどうかを確認 tempname() 一時ファイルの名前を取得 mkdir() 新しいディレクトリを作成 chdir() 現在の作業ディレクトリを変更 delete() ファイルを削除 rename() ファイルの名前を変更 system() シェルコマンドの結果を文字列として取得 systemlist() シェルコマンドの結果をリストとして取得 environ() すべての環境変数を取得 getenv() 1つの環境変数を取得 setenv() 環境変数を設定 hostname() システムの名前 readfile() ファイルを複数行のリストとして読み込む readblob() ファイルをBlobとして読み込む readdir() ディレクトリ内のファイル名のリストを取得 writefile() 複数行のリストまたはBlobをファイルに書き込む filecopy() ファイルを{from}から{to}にコピー
日付と時刻: date-functions time-functions getftime() ファイルの最終更新時刻を取得 localtime() 現在時刻を秒単位で取得 strftime() 時刻を文字列に変換 strptime() 日付/時刻文字列を時刻に変換 reltime() 現在時刻または経過時刻を正確に取得 reltimestr() reltime()の結果を文字列に変換 reltimefloat() reltime()の結果を浮動小数点数に変換
buffer-functions window-functions arg-functions バッファ、ウィンドウ、および引数リスト: argc() 引数リストのエントリ数 argidx() 引数リストの現在の位置 arglistid() 引数リストのIDを取得 argv() 引数リストから1つのエントリを取得 bufadd() バッファのリストにファイルを追加 bufexists() バッファが存在するかどうかを確認 buflisted() バッファが存在し、リストされているかどうかを確認 bufload() バッファがロードされていることを確認 bufloaded() バッファが存在し、ロードされているかどうかを確認 bufname() 特定のバッファの名前を取得 bufnr() 特定のバッファのバッファ番号を取得 tabpagebuflist() タブページ内のバッファのリストを返す tabpagenr() タブページの番号を取得 tabpagewinnr() 指定されたタブページのwinnr()と同様 winnr() 現在のウィンドウのウィンドウ番号を取得 bufwinid() 特定のバッファのウィンドウIDを取得 bufwinnr() 特定のバッファのウィンドウ番号を取得 winbufnr() 特定のウィンドウのバッファ番号を取得 win_findbuf() バッファを含むウィンドウを検索 win_getid() ウィンドウのウィンドウIDを取得 win_gettype() ウィンドウのタイプを取得 win_gotoid() IDを持つウィンドウに移動 win_id2tabwin() ウィンドウIDからタブとウィンドウ番号を取得 win_id2win() ウィンドウIDからウィンドウ番号を取得 win_move_separator() ウィンドウの垂直区切り線を移動 win_move_statusline() ウィンドウのステータスラインを移動 win_splitmove() ウィンドウを別のウィンドウの分割に移動 getbufinfo() バッファ情報を含むリストを取得 gettabinfo() タブページ情報を含むリストを取得 getwininfo() ウィンドウ情報を含むリストを取得 getchangelist() 変更リストのエントリのリストを取得 getjumplist() ジャンプリストのエントリのリストを取得 swapfilelist() 'directory'内の既存のスワップファイルのリスト swapinfo() スワップファイルに関する情報 swapname() バッファのスワップファイルパスを取得
コマンドライン: command-line-functions
getcmdcomplpat() 現在のコマンドラインの補完パターンを取得 getcmdcompltype() 現在のコマンドライン補完のタイプを取得 getcmdline() 現在のコマンドライン入力を取得 getcmdprompt() 現在のコマンドラインプロンプトを取得 getcmdpos() コマンドラインにおけるカーソルの位置を取得 getcmdscreenpos() コマンドラインにおけるカーソルのスクリーン位置を取得 setcmdline() 現在のコマンドラインを設定 setcmdpos() コマンドラインにおけるカーソルの位置を設定 getcmdtype() 現在のコマンドラインタイプを返す getcmdwintype() 現在のコマンドラインウィンドウタイプを返す getcompletion() コマンドライン補完候補のリスト fullcommand() フルコマンド名を取得
クイックフィックスとロケーションリスト: quickfix-functions
getqflist() クイックフィックスエラーのリスト setqflist() クイックフィックスリストを修正 getloclist() ロケーションリスト項目のリスト setloclist() ロケーションリストを修正
挿入モード補完: completion-functions
complete() 見つかった候補を設定 complete_add() 見つかった候補に追加 complete_check() 補完を中断すべきか確認 complete_info() 現在の補完情報を取得 pumvisible() ポップアップメニューが表示されているか確認 pum_getpos() 表示されている場合のポップアップメニューの位置とサイズ
折りたたみ: 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() (シンタックス)隠蔽に関する情報を取得 diff_hlID() 特定の位置での差分モードのハイライトIDを取得 matchadd() ハイライトするパターン ("マッチ") を定義 matchaddpos() ハイライトする位置のリストを定義 matcharg() :match 引数に関する情報を取得 matchdelete() matchadd() または :match コマンドで定義されたマッチを削除 setmatches() getmatches() で保存されたマッチのリストを復元
スペルチェック: spell-functions
spellbadword() カーソル位置以降のスペルミスの単語を見つける spellsuggest() スペル修正候補を返す soundfold() 単語のサウンドアライクな等価物を返す
履歴: history-functions
histadd() 履歴に項目を追加 histdel() 履歴から項目を削除 histget() 履歴から項目を取得 histnr() 履歴リストの最大インデックスを取得
インタラクティブ: interactive-functions
browse() ファイルリクエスタを表示 browsedir() ディレクトリリクエスタを表示 confirm() ユーザーに選択をさせる getchar() ユーザーから文字を取得 getcharmod() 最後にタイプされた文字の修飾キーを取得 getmousepos() 最後に認識されたマウスの位置を取得 feedkeys() タイプアヘッドキューに文字を入れる input() ユーザーから1行を取得 inputlist() ユーザーにリストからエントリを選択させる inputsecret() ユーザーから行を取得(表示せずに) inputdialog() ダイアログでユーザーから行を取得 inputsave() タイプアヘッドを保存してクリア inputrestore() タイプアヘッドを復元
GUI: gui-functions
getfontname() 現在使用中のフォント名を取得 getwinpos() Vimウィンドウの位置を取得 getwinposx() VimウィンドウのX座標を取得 getwinposy() VimウィンドウのY座標を取得 balloon_show() バルーンコンテンツを設定 balloon_split() バルーン用のメッセージを分割 balloon_gettext() バルーン内のテキストを取得
Vimサーバー: server-functions
serverlist() サーバー名のリストを返す remote_startserver() サーバーを実行 remote_send() Vimサーバーにコマンド文字を送信 remote_expr() Vimサーバーで式を評価 server2client() Vimサーバーのクライアントに返信を送信 remote_peek() Vimサーバーからの返信があるか確認 remote_read() Vimサーバーからの返信を読み込む foreground() Vimウィンドウをフォアグラウンドに移動 remote_foreground() Vimサーバーウィンドウをフォアグラウンドに移動
ウィンドウのサイズと位置: window-size-functions
winheight() 特定のウィンドウの高さを取得 winwidth() 特定のウィンドウの幅を取得 win_screenpos() ウィンドウのスクリーン位置を取得 winlayout() タブページにおけるウィンドウのレイアウトを取得 winrestcmd() ウィンドウサイズを復元するコマンドを返す winsaveview() 現在のウィンドウのビューを取得 winrestview() 保存された現在のウィンドウのビューを復元
マッピングとメニュー: mapping-functions
digraph_get() ダイグラフを取得 digraph_getlist() すべてのダイグラフを取得 digraph_set() ダイグラフを登録 digraph_setlist() 複数のダイグラフを登録 hasmapto() マッピングが存在するか確認 mapcheck() 一致するマッピングが存在するか確認 maparg() マッピングの右辺を取得 maplist() すべてのマッピングのリストを取得 mapset() マッピングを復元 menu_info() メニュー項目に関する情報を取得 wildmenumode() wildmodeがアクティブか確認
サイン: sign-functions
sign_define() サインを定義または更新 sign_getdefined() 定義済みのサインのリストを取得 sign_getplaced() 配置済みのサインのリストを取得 sign_jump() サインにジャンプ sign_place() サインを配置 sign_placelist() サインのリストを配置 sign_undefine() サインの定義を解除 sign_unplace() サインの配置を解除 sign_unplacelist() サインのリストの配置を解除
テスト: test-functions
assert_equal() 2つの式の値が等しいことをアサート assert_equalfile() 2つのファイルの内容が等しいことをアサート assert_notequal() 2つの式の値が等しくないことをアサート assert_inrange() 式が範囲内にあることをアサート assert_match() パターンが値に一致することをアサート assert_notmatch() パターンが値に一致しないことをアサート assert_false() 式が偽であることをアサート assert_true() 式が真であることをアサート assert_exception() コマンドが例外をスローすることをアサート assert_beeps() コマンドがビープ音を鳴らすことをアサート assert_nobeep() コマンドがビープ音を鳴らさないことをアサート assert_fails() コマンドが失敗することをアサート assert_report() テストの失敗を報告
タイマー: timer-functions
timer_start() タイマーを作成 timer_pause() タイマーを一時停止または一時停止解除 timer_stop() タイマーを停止 timer_stopall() すべてのタイマーを停止 timer_info() タイマーに関する情報を取得 wait() 条件が満たされるまで待機
タグ: tag-functions
taglist() 一致するタグのリストを取得 tagfiles() タグファイルのリストを取得 gettagstack() ウィンドウのタグスタックを取得 settagstack() ウィンドウのタグスタックを修正
プロンプトバッファ: promptbuffer-functions
prompt_getprompt() バッファの有効なプロンプトテキストを取得 prompt_setcallback() バッファのプロンプトコールバックを設定 prompt_setinterrupt() バッファの割り込みコールバックを設定 prompt_setprompt() バッファのプロンプトテキストを設定
レジスタ: register-functions
getreg() レジスタの内容を取得 getreginfo() レジスタに関する情報を取得 getregtype() レジスタのタイプを取得 setreg() レジスタの内容とタイプを設定 reg_executing() 実行中のレジスタの名前を返す reg_recording() 記録中のレジスタの名前を返す
コンテキストスタック: ctx-functions
ctxget() 上から指定されたインデックスのコンテキストを返す ctxpop() 最上位のコンテキストをポップして復元 ctxpush() 指定されたコンテキストをプッシュ ctxset() 上から指定されたインデックスのコンテキストを設定 ctxsize() コンテキストスタックのサイズを返す
その他: various-functions
mode() 現在の編集モードを取得 visualmode() 最後に使用したビジュアルモードを取得 exists() 変数、関数などが存在するか確認 has() Vimで機能がサポートされているか確認 changenr() 最新の変更の番号を返す did_filetype() FileType自動コマンドが使用されたか確認 eventhandler() イベントハンドラによって呼び出されたか確認 getpid() VimのプロセスIDを取得 getscriptinfo() ソースされたVimスクリプトのリストを取得
libcall() 外部ライブラリの関数を呼び出す libcallnr() 同上、数値を返す
undofile() アンドゥファイルの名前を取得 undotree() バッファのアンドゥツリーの状態を返す
shiftwidth() 'shiftwidth' の有効な値
wordcount() バッファのバイト数/単語数/文字数を取得
luaeval() Lua式を評価 py3eval() Python式を評価 pyeval() Python式を評価 pyxeval() python_x式を評価 rubyeval() Ruby式を評価
debugbreak() デバッグ中のプログラムを中断

41.7 関数を定義する

Vimでは、独自の関数を定義できます。基本的な関数宣言は次のように始まります
:function {name}({var1}, {var2}, ...)
:  {body}
:endfunction
注意: 関数名は必ず大文字で始まる必要があります。
2つの数のうち小さい方を返す短い関数を定義してみましょう。これは、次の行から始まります
:function Min(num1, num2)
これは、関数が "Min" という名前で、2つの引数 "num1" と "num2" を取ることをVimに伝えます。最初に必要なのは、どちらの数が小さいかを確認することです
:  if a:num1 < a:num2
特別な接頭辞 "a:" は、変数が関数引数であることをVimに伝えます。変数 "smaller" に最小の数の値を割り当ててみましょう
:  if a:num1 < a:num2
:    let smaller = a:num1
:  else
:    let smaller = a:num2
:  endif
変数 "smaller" はローカル変数です。関数内で使用される変数は、"g:", "a:", "s:" などの接頭辞が付いていない限りローカルです。
注意: 関数内からグローバル変数にアクセスするには、"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"が使用されると、関数はゼロを返します。
すでに存在する関数を再定義するには、":function"コマンドで!を使用します。
:function!  Min(num1, num2, num3)
範囲の使用
":call"コマンドには、行範囲を指定できます。これには2つの意味があります。"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()
これは一度実行され、単語の数をエコー表示します。行範囲を使用するもう1つの方法は、"range"キーワードなしで関数を定義することです。関数は、範囲内のすべての行に対して1回ずつ、カーソルがその行にある状態で呼び出されます。例
:function  Number()
:  echo "line " .. line(".") .. " contains: " .. getline(".")
:endfunction
この関数を以下のように呼び出した場合
:10,15call Number()
関数は6回呼び出されます。

可変数の引数

Vimでは、可変数の引数を持つ関数を定義できます。たとえば、次のコマンドは、1つの引数(start)を持ち、最大20個の追加引数を持つことができる関数を定義します。
:function Show(start, ...)
変数"a:1"には最初のオプション引数が、"a:2"には2番目のオプション引数が格納されます。変数"a:0"には追加引数の数が格納されます。例
:function Show(start, ...)
:  echohl Title
:  echo "start is " .. a:start
:  echohl None
:  let index = 1
:  while index <= a:0
:    echo "  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"の引数としてその名前を使用します。
:function SetSyn
1 if &syntax == ''
2 let &syntax = a:name
3 endif
endfunction

デバッグ

エラーメッセージが表示されたり、デバッグを行う場合に、行番号が役立ちます。デバッグモードについてはdebug-scriptsを参照してください。また、'verbose'オプションを12以上に設定すると、すべての関数呼び出しが表示されます。15以上に設定すると、実行されるすべての行が表示されます。
関数の削除
Show()関数を削除するには
:delfunction Show
関数が存在しない場合、エラーが発生します。

関数参照

場合によっては、変数が1つの関数または別の関数を指すようにすると便利な場合があります。これは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, [])
間違いです!
関数参照を保持する変数の名前は、大文字で始める必要があることに注意してください。そうしないと、組み込み関数の名前と混同される可能性があります。変数が参照する関数を呼び出す方法は、call()関数を使用することです。最初の引数は関数参照、2番目の引数は引数を含むリストです。
関数参照は、次のセクションで説明するように、辞書と組み合わせて使用​​すると最も便利です。
独自の関数の定義に関する詳細については、こちらを参照してください:user-function

41.8 リストと辞書

これまで、基本的な型であるStringとNumberを使用しました。Vimは、ListとDictionaryの2つの複合型もサポートしています。
リストは、順序付けられた一連のものです。これらの要素は、任意の種類の値にすることができ、数値のリスト、リストのリスト、さらには混合項目のリストを作成できます。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()の2番目の引数は、単一の項目として追加されます。

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()が生成するリストの最初の項目はゼロであるため、最後の項目はリストの長さより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()関数の最初の引数です。これにより、リストを処理し、"v:val"を各項目の値に設定して、2番目の引数を評価します。これは、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つのオブジェクト(辞書)のいずれかを参照します。コピーは作成されません。リストと辞書の識別に関する詳細については、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
詳細については、ListsDictionariesを参照してください。

41.9 例外

例から始めましょう。
:try
:   read ~/templates/pascal.tmpl
:catch /E484:/
:   echo "Sorry, the Pascal template file cannot be found."
:endtry
ファイルが存在しない場合、":read"コマンドは失敗します。エラーメッセージを生成する代わりに、このコードはエラーをキャッチし、ユーザーに適切なメッセージを表示します。
":try"と":endtry"の間のコマンドの場合、エラーは例外に変換されます。例外は文字列です。エラーの場合、文字列にはエラーメッセージが含まれます。そして、すべてのエラーメッセージには番号があります。この場合、キャッチするエラーには"E484:"が含まれています。この番号は同じ状態を維持することが保証されています(テキストは変更される可能性があります。たとえば、翻訳される可能性があります)。
":read"コマンドが別のエラーを引き起こした場合、パターン"E484:"は一致しません。したがって、この例外はキャッチされず、通常のエラーメッセージが表示され、実行が中止されます。
これを行うように誘惑されるかもしれません
:try
:   read ~/templates/pascal.tmpl
:catch
:   echo "Sorry, the Pascal template file cannot be found."
:endtry
これは、すべてのエラーがキャッチされることを意味します。しかし、そうすると、"'modifiable'がオフのため、E21:変更できません"などの便利なエラーが表示されなくなります。
もう1つの便利なメカニズムは、":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スクリプトに適用される項目の概要を以下に示します。それらは他の場所でも言及されていますが、良いチェックリストを形成します。
行末文字はシステムによって異なります。Vimスクリプトでは、常にUnixファイル形式を使用することをお勧めします。行は改行文字で区切られます。これは他のシステムでも機能します。そうすれば、VimスクリプトをMS-WindowsからUnixにコピーしても、引き続き機能します。:source_crnlを参照してください。正しく設定されていることを確認するには、ファイルを書き込む前にこれを実行します。
:setlocal fileformat=unix
"dos"ファイル形式を使用する場合、行はCR-NLの2文字で区切られます。CR文字はさまざまな問題を引き起こすため、避けた方がよいでしょう。

空白

スクリプトでは空白行は許可されており、無視されます。
先頭の空白文字(空白とタブ)は、"trim"なしで:let-heredocを使用する場合を除き、無視されます。
末尾の空白は多くの場合無視されますが、常にそうではありません。それを含むコマンドの1つはmapです。これには注意が必要です。理解しにくい間違いを引き起こす可能性があります。一般的な解決策は、本当に必要な場合を除き、末尾の空白を使用しないことです。
オプションの値に空白文字を含めるには、次の例のように、 "\"(バックスラッシュ)でエスケープする必要があります。
: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"、および「!」コマンドの後にはコメントを記述できません(この制限があるコマンドは他にもいくつかあります)。":map"、":abbreviate"、および":execute"コマンドには、以下のようなトリックがあります。
:abbrev dev development|" shorthand
:map <F3> o#include|" insert include
:execute cmd                        |" do it
「|」文字を使用すると、コマンドが次のコマンドから分離されます。そして、その次のコマンドは単なるコメントになります。最後のコマンドでは、2つのことを行う必要があります。:executeを使用して「|」を使用します。
:exe '!ls *.c'                        |" list C files
省略形とマッピングで、「|」の前に空白がないことに注意してください。これらのコマンドでは、行末または「|」までのすべての文字が含まれます。この動作の結果として、末尾の空白が含まれていることが常に目に見えるとは限りません。
:map <F4> o#include
これらの問題を特定するには、vimrcファイルを編集するときに'list'オプションを設定できます。
Unixには、Vimスクリプトを実行可能にするための特別なコメント方法が1つあります。
#!/usr/bin/env vim -S
echo "this is a Vim script"
quit
「#」コマンド自体は、行番号付きでリストを表示します。感嘆符を追加すると、何も実行しないように変更されるため、ファイルの残りを実行するためのシェルコマンドを追加できます。:#! -S

落とし穴

次の例では、さらに大きな問題が発生します。
:map ,ab o#include
:unmap ,ab
ここで、unmapコマンドは、「,ab 」のunmapを試みるため、機能しません。これはマップされたシーケンスとして存在しません。エラーが発生しますが、":unmap ,ab "の末尾にある空白文字は表示されないため、特定が非常に困難です。
そして、これは「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 保存されたカーソル位置に戻る

パッケージング

自分の関数名が他の人から取得した関数と干渉しないようにするには、次のスキームを使用します。
各関数名の前に一意の文字列を付加します。私は省略形をよく使います。たとえば、「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スクリプトを作成できます。これはプラグインと呼ばれます。Vimユーザーは、自分のプラグインディレクトリにスクリプトをドロップすると、すぐにその機能を使用できます。add-pluginを参照してください。
実際には、2種類のプラグインがあります。
グローバルプラグイン:すべてのタイプのファイル用。ファイルタイププラグイン:特定のタイプのファイルのみ用。
このセクションでは、最初のタイプについて説明します。ほとんどの項目は、ファイルタイププラグインの作成にも関連しています。ファイルタイププラグインの具体的な内容は、次のセクションwrite-filetype-pluginを参照してください。

名前

まず、プラグインの名前を選択する必要があります。プラグインが提供する機能は、その名前から明確にする必要があります。また、他の誰かが同じ名前で異なる機能を持つプラグインを作成する可能性は低いようにする必要があります。古いMS-Windowsシステムで問題が発生しないように、名前を8文字に制限してください。
タイプミスの修正を行うスクリプトは、「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 <[email protected]>
著作権とライセンスについて:プラグインは非常に便利であり、その配布を制限する価値はほとんどないため、プラグインをパブリックドメインにするか、Vimのライセンスを使用することを検討してください。プラグインの先頭近くにこれについての簡単なメモがあれば十分でしょう。例:
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
これにより、スクリプトが2回ロードされたときに、関数の再定義によるエラーメッセージが発生したり、2回追加された自動コマンドで問題が発生したりすることも回避できます。
名前は、「loaded_」で始まり、その後にプラグインのファイル名を文字通り続けることをお勧めします。「g:」は、関数で変数を使用するときに間違いを避けるためだけに追加されます(「g:」がないと、関数にローカルな変数になります)。
「finish」を使用すると、Vimはファイルの残りの部分を読み取らなくなります。これは、ファイル全体をif-endifで囲むよりもはるかに高速です。

マッピング

それでは、プラグインをより面白くしましょう。カーソル下の単語の修正を追加するマッピングを追加します。このマッピングにキーシーケンスを選択するだけで済みますが、ユーザーがすでに他のことに使用している可能性があります。ユーザーがプラグインのマッピングで使用するキーを定義できるように、<Leader>項目を使用できます。
22          map <unique> <Leader>a  <Plug>TypecorrAdd;
「<Plug>TypecorrAdd;」は、実際に機能しますが、詳細については後述します。
ユーザーは、「mapleader」変数をこのマッピングを開始するキーシーケンスに設定できます。したがって、ユーザーが次のように行った場合
let mapleader = "_"
マッピングは「_a」を定義します。ユーザーがこれを行わなかった場合、デフォルト値が使用され、これはバックスラッシュです。次に、「\a」のマッピングが定義されます。
<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;
すると、マップされたキーシーケンスは、「_a」または「\a」ではなく「,c」になります。

ピース

スクリプトが長くなると、作業をピースに分割したくなることがよくあります。このためには、関数またはマッピングを使用できます。ただし、これらの関数とマッピングが他のスクリプトの関数やマッピングと干渉することは望ましくありません。たとえば、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を生成します。タイピング修正プラグインでは、次のように使用します。
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を取得し、したがって別のマッピングを定義します。
s:Add()の代わりに、ここで<SID>Add()を使用していることに注意してください。これは、マッピングがユーザーによって入力されるため、スクリプトの外部に存在するからです。<SID>はスクリプトIDに変換されるため、VimはAdd()関数を探すスクリプトを認識します。
これは少し複雑ですが、プラグインが他のプラグインと連携して機能するためには必要です。基本ルールは、マッピングでは<SID>Add()を使用し、他の場所(スクリプト自体、自動コマンド、ユーザーコマンド)ではs:Add()を使用することです。
マッピングと同じことを行うメニューエントリを追加することもできます。
26        noremenu <script> Plugin.Add\ Correction      <SID>Add
プラグインのメニュー項目を追加するには、「Plugin」メニューを使用することをお勧めします。この場合は1つの項目のみが使用されます。複数の項目を追加する場合は、サブメニューを作成することをお勧めします。たとえば、「Plugin.CVS」はCVS操作を提供するプラグインに使用でき、「Plugin.CVS.checkin」、「Plugin.CVS.checkout」などです。
28行目では、他のマッピングが問題を引き起こすのを避けるために":noremap"が使用されていることに注意してください。例えば、誰かが":call"をリマップしている可能性があります。24行目でも":noremap"を使用していますが、"<SID>Add"はリマップしたいと考えています。そのため、ここでは"<script>"が使用されています。これにより、スクリプトローカルなマッピングのみが許可されます。:map-<script>。":noremenu"についても26行目で同じことが行われています。: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が実行されている限り保持されます。また、同じスクリプトを再度sourceした場合、同じ変数が使用されます。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 <[email protected]>
 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コマンドは、新しい省略形を使用するために使用されます。関数が":noremap"で定義されたマッピングから呼び出された場合でも、ここではマッピングと省略形が展開されることに注意してください。
'fileformat'オプションに "unix" を使用することをお勧めします。そうすれば、Vimスクリプトはどこでも動作します。'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.
最初の行は、実際には書式が重要な唯一の行です。最初の行の "LOCAL ADDITIONS:" セクションに入れるために、ヘルプファイルから抽出されます。ヘルプファイルを追加した後、":help" を実行して、エントリが適切に整列していることを確認してください。
ヘルプファイル内で ** の中にさらにタグを追加できます。ただし、既存のヘルプタグを使用しないように注意してください。おそらく、例の "typecorr-settings" のように、ほとんどのタグでプラグインの名前を使用することになります。
|| で他のヘルプ部分への参照を使用することをお勧めします。これにより、ユーザーは関連するヘルプを簡単に見つけることができます。

ファイルタイプ検出 plugin-filetype

ファイルタイプがVimによってまだ検出されていない場合は、別のファイルにファイルタイプ検出スニペットを作成する必要があります。通常、ファイル名がパターンに一致する場合にファイルタイプを設定する自動コマンドの形式になります。例
au BufNewFile,BufRead *.foo                        set filetype=foofoo
この1行のファイルを、'runtimepath'に表示される最初のディレクトリの "ftdetect/foofoo.vim" として書き込みます。Unixの場合、これは "~/.config/nvim/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.11をお読みください。そこで述べられていることはすべて、ファイルタイププラグインにも当てはまります。ここでは、いくつかの追加事項について説明します。重要なことは、ファイルタイププラグインは現在のバッファにのみ影響を与える必要があるということです。

無効化

多くの人が使用するファイルタイププラグインを作成する場合は、読み込みを無効にする機会が必要です。プラグインの先頭にこれを記述してください。
" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
  finish
endif
let b:did_ftplugin = 1
これは、同じバッファに対して同じプラグインが2回実行されるのを防ぐためにも使用する必要があります(引数なしで":edit"コマンドを使用した場合に発生します)。
これでユーザーは、次の1行のみを含むファイルタイププラグインを作成することで、デフォルトのプラグインの読み込みを完全に無効にすることができます。
let b:did_ftplugin = 1
そのためには、ファイルタイププラグインディレクトリが'runtimepath'の $VIMRUNTIME より前に来る必要があります。
デフォルトのプラグインを使用したいが、設定の1つを上書きしたい場合は、異なる設定をスクリプトに記述できます。
setlocal textwidth=70
これを "after" ディレクトリに書き込むことで、配布された "vim.vim" ftplugin の後にsourceされるようにします。after-directory。Unixの場合、これは "~/.config/nvim/after/ftplugin/vim.vim" になります。デフォルトのプラグインは "b:did_ftplugin" を設定しますが、ここでは無視されます。

オプション

ファイルタイププラグインが現在のバッファにのみ影響を与えるようにするには、次のコマンドを使用します。
:setlocal
オプションを設定するためのコマンド。バッファローカルなオプションのみを設定します(オプションのヘルプを参照して確認してください)。グローバルオプションまたはウィンドウローカルなオプションに:setlocalを使用すると、多くのバッファの値が変更されますが、それはファイルタイププラグインがすべきことではありません。
オプションの値がフラグまたは項目のリストである場合は、既存の値を保持するために "+=" と "-=" の使用を検討してください。ユーザーが既にオプションの値を変更している可能性があることに注意してください。最初にデフォルト値にリセットしてから変更するのが良い考えであることがよくあります。例
:setlocal formatoptions& formatoptions+=ro

マッピング

マッピングが現在のバッファでのみ機能するようにするには、次のコマンドを使用します。
:map <buffer>
コマンド。これは、上記で説明した2段階のマッピングと組み合わせる必要があります。ファイルタイププラグインで機能を定義する方法の例
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は、ユーザーが定義した他のマッピングが妨害しないようにするために使用されます。<SID>で始まるこのスクリプトで定義されたマッピングのリマップを許可するには、":noremap <script>"を使用する必要があります。
ユーザーは、すべてを無効にすることなく、ファイルタイププラグインのマッピングを無効にする機会が必要です。以下に、メールファイルタイプ用のプラグインでこれを行う方法の例を示します。
" Add mappings, unless the user didn't want this.
if !exists("no_plugin_maps") && !exists("no_mail_maps")
  " Quote text by inserting "> "
  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
2つのグローバル変数が使用されます。no_plugin_mapsは、すべてのファイルタイププラグインのマッピングを無効にします。no_mail_mapsは、"mail"ファイルタイプのマッピングを無効にします。

ユーザーコマンド

特定のファイルタイプのユーザーコマンドを追加して、1つのバッファでのみ使用できるようにするには、:commandに "-buffer" 引数を使用します。例
:command -buffer  Make  make %:r.s

変数

ファイルタイププラグインは、そのタイプの各バッファに対してsourceされます。ローカルスクリプト変数s:varは、すべての呼び出し間で共有されます。特定のバッファ専用の変数が必要な場合は、ローカルバッファ変数b:varを使用します。

関数

関数を定義する場合、これは1回だけ行う必要があります。ただし、ファイルタイププラグインは、このファイルタイプのファイルが開かれるたびにsourceされます。この構造により、関数が1回だけ定義されるようになります。
:if !exists("*s:Func")
:  function s:Func(arg)
:    ...
:  endfunction
:endif
ユーザーが":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に含める必要があります。以下の3つの形式のいずれかを使用してください。
.../ftplugin/stuff.vim .../ftplugin/stuff_foo.vim .../ftplugin/stuff/bar.vim
「stuff」はファイルタイプ、「foo」と「bar」は任意の名前です。
ファイルタイププラグインで使用する特別なものの概要
<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を使用して、次のプラグインファイルに進みます。
これらのファイルには2つの特別な項目があります。1つ目は、ユーザーがデフォルトファイルを上書きまたは追加できるメカニズムです。デフォルトファイルは以下で始まります。
:if exists("current_compiler")
:  finish
:endif
:let current_compiler = "mine"
コンパイラファイルを作成して、個人のランタイムディレクトリ(Unixの場合は〜/.config/nvim/compilerなど)に配置すると、デフォルトファイルが設定をスキップするように「current_compiler」変数を設定します。:CompilerSet
2番目のメカニズムは、":compiler!"の場合は":set"を、":compiler"の場合は":setlocal"を使用することです。Vimは、このために":CompilerSet"ユーザーコマンドを定義しています。以下に例を示します。
CompilerSet errorformat&                " use the default 'errorformat'
CompilerSet makeprg=nmake
Vimディストリビューションまたはシステム全体のランタイムディレクトリ用のコンパイラプラグインを作成する場合は、上記のメカニズムを使用してください。「current_compiler」がユーザープラグインによってすでに設定されている場合、何も行われません。
デフォルトプラグインの設定を上書きするコンパイラプラグインを作成する場合は、「current_compiler」を確認しないでください。このプラグインは最後にロードされることになっているため、'runtimepath'の末尾にあるディレクトリにある必要があります。Unixの場合、これは〜/.config/nvim/after/compilerになります。

41.14 高速にロードするプラグインの作成 write-plugin-quickload

プラグインが成長して非常に長くなることがあります。プラグインをほとんど使用しない場合でも、起動遅延が顕著になる可能性があります。その場合は、クイックロードプラグインの時間です。
基本的な考え方は、プラグインが2回ロードされることです。1回目は、機能を提供するユーザーコマンドとマッピングが定義されます。2回目は、機能を実装する関数が定義されます。
クイックロードがスクリプトを2回ロードすることを意味するのは驚くかもしれません。ここで意味するのは、最初のロードは高速に行われ、スクリプトの大部分は、実際に使用する場合にのみ発生する2回目のロードに延期されるということです。常に機能を使用する場合、実際には遅くなります。
Vim 7以降、代替手段があることに注意してください。autoload機能41.15を使用してください。
次の例は、その実行方法を示しています。
" Vim global plugin for demonstrating quick loading
" Last Change:        2005 Feb 25
" Maintainer:        Bram Moolenaar <[email protected]>
" 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 functionality here
endfunction
function BufNetWrite(...)
        echo 'BufNetWrite(' .. string(a:000) .. ')'
        " write functionality here
endfunction
スクリプトが最初にロードされるとき、「s:did_load」は設定されていません。「if」と「endif」の間のコマンドが実行されます。これは:finishコマンドで終わるため、スクリプトの残りの部分は実行されません。
スクリプトが2回目にロードされると、「s:did_load」が存在し、「endif」の後のコマンドが実行されます。これにより、(長い可能性のある)BufNetRead()関数とBufNetWrite()関数が定義されます。
このスクリプトをプラグインディレクトリにドロップすると、Vimは起動時にそれを実行します。これは発生するイベントのシーケンスです。
1. 起動時にスクリプトがソースされると、「BNRead」コマンドが定義され、<F19>キーがマッピングされます。FuncUndefined自動コマンドが定義されます。「:finish」コマンドにより、スクリプトは早期に終了します。
2. ユーザーがBNReadコマンドを入力するか、<F19>キーを押します。BufNetRead()またはBufNetWrite()関数が呼び出されます。
3. Vimは関数を見つけることができず、FuncUndefined自動コマンドイベントをトリガーします。パターン「BufNet*」が呼び出された関数に一致するため、コマンド「source fname」が実行されます。「fname」は、スクリプトがどこに配置されているかに関係なく、スクリプトの名前と等しくなります。これは、expand()(expand()を参照)から取得されるためです。
4. スクリプトが再度ソースされ、「s:did_load」変数が存在し、関数が定義されます。
後でロードされる関数がFuncUndefined自動コマンドのパターンに一致することに注意してください。他のプラグインがこのパターンに一致する関数を定義しないようにする必要があります。

41.15 ライブラリスクリプトの作成 write-library-script

一部の機能は、いくつかの場所で必要になります。これが数行以上になる場合は、1つのスクリプトに入れて、多くのスクリプトから使用することをお勧めします。その1つのスクリプトをライブラリスクリプトと呼びます。
ライブラリスクリプトを手動でロードすることは可能ですが、すでに完了している場合はロードしないようにしてください。exists()関数でこれを行うことができます。例
if !exists('*MyLibFunction')
   runtime library/mylibscript.vim
endif
call MyLibFunction(arg)
ここでは、MyLibFunction()が'runtimepath'のディレクトリの1つにある「library/mylibscript.vim」スクリプトで定義されていることを知っておく必要があります。
これを少し簡単にするために、Vimはオートロードメカニズムを提供します。その場合、例は次のようになります。
call mylib#myfunction(arg)
ずっとシンプルになりましたね。Vimは関数名を認識し、定義されていない場合は'runtimepath'でスクリプト「autoload/mylib.vim」を検索します。そのスクリプトは「mylib#myfunction()」関数を定義する必要があります。
mylib.vimスクリプトには他の多くの関数を配置できます。ライブラリスクリプトに関数を自由に整理できます。ただし、'#'の前の部分がスクリプト名と一致する関数名を使用する必要があります。そうしないと、Vimはどのスクリプトをロードすればよいかわかりません。
あなたが非常に熱心になり、多くのライブラリスクリプトを作成する場合は、サブディレクトリを使用したい場合があります。例
call netlib#ftp#read('somefile')
Unixの場合、これに使用されるライブラリスクリプトは次のようになります。
~/.config/nvim/autoload/netlib/ftp.vim
関数は次のように定義されます。
function netlib#ftp#read(fname)
        "  Read the file fname through ftp
endfunction
関数が定義されている名前が、関数を呼び出すために使用される名前とまったく同じであることに注意してください。そして、最後の'#'の前の部分が、サブディレクトリとスクリプト名と正確に一致します。
変数にも同じメカニズムを使用できます。
let weekdays = dutch#weekdays
これにより、スクリプト「autoload/dutch.vim」がロードされます。これには次のようなものが含まれている必要があります。
let dutch#weekdays = ['zondag', 'maandag', 'dinsdag', 'woensdag',
        \ 'donderdag', 'vrijdag', 'zaterdag']
参考資料:autoload

41.16 Vimスクリプトの配布 distribute-script

Vimユーザーは、Vim Webサイトhttps://www.vim.orgでスクリプトを探します。他の人に役立つものを作成した場合は、共有してください。
Vimスクリプトは、あらゆるシステムで使用できます。tarコマンドまたはgzipコマンドがない可能性があります。ファイルをまとめてパックしたり、圧縮したりする場合は、「zip」ユーティリティを使用することをお勧めします。
次の章:usr_42.txt 新しいメニューの追加
著作権:manual-copyrightを参照してください vim:tw=78:ts=8:noet:ft=help:norl
メイン
コマンドインデックス
クイックリファレンス