現在の行の特定の文字で始まる次の単語に移動する


7

タイトルに記載されているコマンドが欲しいです。組み込みのVimコマンドで探しましたが見つかりませんでした。私はvimの関数についていくつかのチュートリアルをオンラインで実行しました、アイデアは私のようなものを持つことです.vimrc

map <command> <argument>: call nextWordWithLetter(argument)

function nextWordWithLetter(letter)
    " for word i in line
    " if word i's first letter = letter
        " col = column of word i
        " break
    col | "The command to jump to column number 'col'
end function

回答:


11

を使用getchar()search()て目標を達成できます。

nnoremap <key> :call search('\<' . nr2char(getchar()), 'W', line('.'))<cr>

これはnr2char(getchar())、文字が入力されるのを待ち、を使用しsearch()て単語の先頭にある文字の次のインスタンスを検索するために使用します\<。現在行をline('.')3番目のパラメーターとしてに指定することにより、これを現在行に制限しますsearch()

個人的には、/やを使用するほうがいいと思いfますが、同様の機能を提供するプラグインが他にもあります。

詳細については、以下を参照してください。

:h search(
:h getchar(
:h /\<

7

編集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にそのようなモーションがすでに組み込まれているかどうかはわかりませんが、組み込まれていないとしましょう。

私たちは、新しいモーション(のように定義する必要がありwbeなど)。通常、新しいモーションまたはオブジェクトを定義するには、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}opfuncNextWordWithLetter()

コマンドモードで入力することで同じことができます:

:set operatorfunc=NextWordWithLetter

次に、通常モードで:g@
次に、常に通常モードで:{motion}

しかし、もちろん面倒なので<leader>goperatorfuncオプションを自動的に目的の関数名に設定し、ヒットするマッピングを作成しますg@

あなたのリーダーキーがで,あり、あなたがその文字で始まる次の単語を探しているとしましょうp。マッピングを使用すると、と入力できます,gfp
その結果、vimはカーソルと次の文字の間のマーク`[`]テキストの周囲をpに設定opfuncNextWordWithLetter、このオプションの値のために関数を実行しますNextWordWithLetter()

次に、関数の機能を見てみましょう。


変数の設定

最初に、探している文字を関数に通知する必要があります。

normal! `]vy

normalあなたが通常モードであったかのように、コマンドの種類は、任意の文字列あなたはそれに渡します。ここでは、文字のシーケンスはです`]vy

バッファにいるときは、通常のコマンドでどこにでもマークを設定できますm{letter}

aカーソルがある場所にマークを付けたい場合は、と入力しmaます。次に、移動aして、コマンドでマークと同じ行に戻る'aa、コマンドでマークを設定した正確な文字に戻ることができます`a
コマンドですべてのマークを見ることができます:marks。大文字のマークはグローバルであり、バッファー間をジャンプするために使用でき、小文字のマークはバッファーに対してローカルです。

vimが自動的に設定する2つの特別なマークが`[あり`]ます。テキストを変更またはヤンクすると、vimはそれらのマークをテキストの周りに配置します(:h '[)。

例に戻りましょう。文字pで始まる次の単語に行きたいので、を押します,gfpfpカーソルを次の文字に移動するコマンドですp

この時点で、vimは、(この例では)タイプしただけで移動したはずのマーク`[`]テキストの周囲を自動的に設定します。 それはヘルプ()に書かれています:{motion}fp
:h g@

g @ {motion} 'operatorfunc'オプションで設定された関数を呼び出します。'[マークは、{motion}によって移動されたテキストの先頭に配置され、']マークはテキストの最後の文字に配置されます。

まだ何も変更していないので、なぜvimはすでにこれらのマークを付けているのですか?それは、関数がテキストに対して何かを実行したいと考えているため、関数は操作対象のテキストを識別するためにこれらのマークが必要になるためです。

したがって、normalコマンドが入力されると、次の`]vyように読み取ることができます。
マーク`](テキストの最後の文字)に移動し、ビジュアルモードに切り替え(v)、ビジュアル選択をコピーします(y

これで、検索した文字(文字p)がコピーされました。コピーしたものはすべてレジスターにあります。vimにはさまざまなレジスターがあり、それぞれが異なる目的で使用されています。それらを見るには:reg、と入力し、どのレジスタが何を格納しているかを確認し:h registersます。

コピーした最後のテキストはレジスタに保存されます0。あなたがそれにアクセスすることができます@0echo @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ます。

最初の文字比較するwordletter、我々が使用して==#演算子を。大文字と小文字が区別される演算子です(ignorecaseオプションの値に関係なく)。また、使用することができ==?、ケース小文字を区別しない、または裸である==(大文字と小文字を区別かに依存しないでignorecase、これを使用することを推奨しません)。

最初の文字があれば、wordであるletter、ここで関数が何をするかです。

let col = match(line, word) + 1

word変数内にプラス1の最初の文字のインデックスを格納しますcolmatch()組み込み関数です。その基本構文はmatch(string, pattern)でありstringpattern一致する場所のインデックスを返します。vimが0からインデックス作成を開始するため、1を追加します。

最後にこれを実行します:

execute "normal! " . col . "|"

execute渡した文字列の内容を実行するコマンドです。ここに文字列があります:"normal! " . col . "|"

ドット演算子は、3つの文字列連結し"normal !"col(それは数だが、Vimの強制は、自動的に文字列に変換しますが)、と"|"

検索した文字が行の10列目にあるとしましょう。この場合、前の文字列は次のように評価されます"normal! 10|"。したがって、をexecute実行する"normal! 10|"と、normalコマンドが入力さ10|れます。


ありがとう、それがまさに私が探していたものです。あなたが使用したコマンドの半分がわかりません:/
solalito 2015年

2
コードを説明しなくて申し訳ありません。投稿を編集して説明し、必要に応じて好きなように調整できるようにします。少し時間がかかります。
saginaw 2015年

どうぞよろしくお願いいたします。私は常に自分が使用しているツールを正確に知ることに興味があります。
solalito 2015年

私はコードを単純化しようとしました、そして、それを詳細に説明するために最善を尽くしました。それを読んだ後、コードがもっと意味をなすことを願っています。
saginaw 2015年

私はそのような完全な答えを得ることを望んでいませんでした。私はそれを読みましたが、慎重に検討する必要があります。+2できれば!
solalito 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.