編集:Peter Rinckerの答えは短く、説明が簡単で、何度でも繰り返すことができます。私の解決策は長すぎて、数語で繰り返すことはできません。私は答えを削除すべきですが、おそらく誰かがそれに興味を持っているので、ここでそれを許可します。
これがあなたの望むものかどうかはわかりませんが、次のコードを試してください:
function! NextWordWithLetter(type)
normal! `]vy
let letter = @0
let line = getline('.')
let list = split(line)
for word in list
if matchstr(word, '\%1c.') ==# letter
let col = match(line, word) + 1
execute "normal! " . col . "|"
break
endif
endfor
endfunction
nnoremap <silent> <leader>g :set operatorfunc=NextWordWithLetter<cr>g@
関数を呼び出すためのマッピングは<leader>g<motion>
です。
カーソルを文字p
で始まる次の単語に移動したい場合,
、それがリーダーキーであれば、と入力できます,gfp
。
ゴール
特定の文字で始まる次の単語に移動したい。vimにそのようなモーションがすでに組み込まれているかどうかはわかりませんが、組み込まれていないとしましょう。
私たちは、新しいモーション(のように定義する必要がありw
、b
、e
など)。通常、新しいモーションまたはオブジェクトを定義するには、onoremap
コマンドを使用します。ここで詳しく説明します。
この方法であなたの目標を達成する方法はわかりませんが、間接的にあなたが望むことをする新しい演算子を定義する方法は知っています。新しい演算子の目的は通常、どこかに移動することではなく、テキストの一部に対して何かを行うことなので、間接的
に言います。
Vimはヘルプで新しい演算子を作成する方法を説明しています:h map-operator
。ここ、ここ、ここでも説明されています。
構文は奇妙に思われるかもしれませんが、vimscriptで動作するように見えるのは次のとおりです。
g@
独自の演算子を作成できる特別な演算子(c
変更、d
削除、y
コピーなど)です。
g@{motion}
vimと入力するとopfunc
、値が関数の名前であるはずのオプションが検索されます。入力した直後に移動したはずのマーク`[
と`]
テキストの周囲を設定し{motion}
(これについては後で詳しく説明します)、次のことができる関数を実行します。このテキストに必要なことは何でもしてください。
異なる関数を記述opfunc
し、使用する関数の名前にオプションを設定することで、異なる演算子を作成できます。
しかし、関数が何をするのかを掘り下げる前に、それをトリガーするために必要なマッピングを見てみましょう。
マッピング
コードの最後には、次の行があります。
nnoremap <silent> <leader>g :set operatorfunc=NextWordWithLetter<cr>g@
これはvimに、ヒットするたびに<leader>g
、黙って(<silent>
)operatorfunc
オプションの値をに設定し、NextWordWithLetter
通常モードでヒットする必要があることを伝えますg@
。
オペレータg@
は、に保存されている関数を実行する前に、移動する予定のマーク`[
と`]
テキストの周囲の動きを待ちます。ここに関数があります。{motion}
opfunc
NextWordWithLetter()
コマンドモードで入力することで同じことができます:
:set operatorfunc=NextWordWithLetter
次に、通常モードで:g@
次に、常に通常モードで:{motion}
しかし、もちろん面倒なので<leader>g
、operatorfunc
オプションを自動的に目的の関数名に設定し、ヒットするマッピングを作成しますg@
。
あなたのリーダーキーがで,
あり、あなたがその文字で始まる次の単語を探しているとしましょうp
。マッピングを使用すると、と入力できます,gfp
。
その結果、vimはカーソルと次の文字の間のマーク`[
と`]
テキストの周囲をp
に設定opfunc
しNextWordWithLetter
、このオプションの値のために関数を実行しますNextWordWithLetter()
。
次に、関数の機能を見てみましょう。
変数の設定
最初に、探している文字を関数に通知する必要があります。
normal! `]vy
normal
あなたが通常モードであったかのように、コマンドの種類は、任意の文字列あなたはそれに渡します。ここでは、文字のシーケンスはです`]vy
。
バッファにいるときは、通常のコマンドでどこにでもマークを設定できますm{letter}
。
a
カーソルがある場所にマークを付けたい場合は、と入力しma
ます。次に、移動a
して、コマンドでマークと同じ行に戻る'a
かa
、コマンドでマークを設定した正確な文字に戻ることができます`a
。
コマンドですべてのマークを見ることができます:marks
。大文字のマークはグローバルであり、バッファー間をジャンプするために使用でき、小文字のマークはバッファーに対してローカルです。
vimが自動的に設定する2つの特別なマークが`[
あり`]
ます。テキストを変更またはヤンクすると、vimはそれらのマークをテキストの周りに配置します(:h '[
)。
例に戻りましょう。文字p
で始まる次の単語に行きたいので、を押します,gfp
。fp
カーソルを次の文字に移動するコマンドですp
。
この時点で、vimは、(この例では)タイプしただけで移動したはずのマーク`[
と`]
テキストの周囲を自動的に設定します。
それはヘルプ()に書かれています:{motion}
fp
:h g@
g @ {motion} 'operatorfunc'オプションで設定された関数を呼び出します。'[マークは、{motion}によって移動されたテキストの先頭に配置され、']マークはテキストの最後の文字に配置されます。
まだ何も変更していないので、なぜvimはすでにこれらのマークを付けているのですか?それは、関数がテキストに対して何かを実行したいと考えているため、関数は操作対象のテキストを識別するためにこれらのマークが必要になるためです。
したがって、normal
コマンドが入力されると、次の`]vy
ように読み取ることができます。
マーク`]
(テキストの最後の文字)に移動し、ビジュアルモードに切り替え(v
)、ビジュアル選択をコピーします(y
)。
これで、検索した文字(文字p
)がコピーされました。コピーしたものはすべてレジスターにあります。vimにはさまざまなレジスターがあり、それぞれが異なる目的で使用されています。それらを見るには:reg
、と入力し、どのレジスタが何を格納しているかを確認し:h registers
ます。
コピーした最後のテキストはレジスタに保存されます0
。あなたがそれにアクセスすることができます@0
:echo @0
。
検索した文字p
はこのレジスターにあります。これを変数に割り当てますletter
。
let letter = @0
次に、現在の行を変数に割り当てますline
。
let line = getline('.')
getline()
引数として渡された番号の行を返し'.'
、現在の行の番号として展開される組み込み関数です。
次にlist
、行の各単語を含むリストを変数に割り当てます。
let list = split(line)
split()
空白(スペース、タブ文字...)が見つかるたびに文字列を分割する組み込み関数です。2番目の引数を与えることで、他の場所で分割するように指示できます。たとえば、コロン文字があるときはいつでも分割したい場合は、を使用しますsplit(line, ':')
。
forループ
for
ループ変数を反復処理list
、格納変数内部その項目の各々word
:
for word in list
...
endfor
次に、の最初の文字がword
目的の文字かどうかを確認します。
if matchstr(word, '\%1c.') ==# letter
...
endif
matchstr()
文字列の中にパターンが見つかった場合にパターンを返す組み込み関数です。
ここでは、'\%1c.'
内部のパターンを探していますword
。vim正規表現で\%1c
は、幅ゼロのアトムです。これはパターンに何も追加しません。正規表現を解析する正規表現エンジンに、パターンがテキストの最初の列から始まることを通知するだけです(詳細については:h /\%c
)。正規表現の最後にあるドットは、任意の文字を意味します。
つまり'\%1c.'
、テキストの最初の列にある任意の文字を意味しmatchstr(word, '\%1c.')
、の最初の文字を返しword
ます。
最初の文字比較するword
とletter
、我々が使用して==#
演算子を。大文字と小文字が区別される演算子です(ignorecase
オプションの値に関係なく)。また、使用することができ==?
、ケース小文字を区別しない、または裸である==
(大文字と小文字を区別かに依存しないでignorecase
、これを使用することを推奨しません)。
最初の文字があれば、word
であるletter
、ここで関数が何をするかです。
let col = match(line, word) + 1
word
変数内にプラス1の最初の文字のインデックスを格納しますcol
。match()
組み込み関数です。その基本構文はmatch(string, pattern)
でありstring
、pattern
一致する場所のインデックスを返します。vimが0からインデックス作成を開始するため、1を追加します。
最後にこれを実行します:
execute "normal! " . col . "|"
execute
渡した文字列の内容を実行するコマンドです。ここに文字列があります:"normal! " . col . "|"
ドット演算子は、3つの文字列連結し"normal !"
、col
(それは数だが、Vimの強制は、自動的に文字列に変換しますが)、と"|"
。
検索した文字が行の10列目にあるとしましょう。この場合、前の文字列は次のように評価されます"normal! 10|"
。したがって、をexecute
実行する"normal! 10|"
と、normal
コマンドが入力さ10|
れます。