回答:
おそらくもっと簡単な方法がありますが、次の方法を試すことができます。
register qを使用して再帰マクロを記録するとします。
記録の最初に、次のように入力します。
:let a = line('.')
次に、記録の最後で、ヒット@qしてマクロを再帰的にするのではなく、次のコマンドを入力します。
:if line('.') == a | exe 'norm @q' | endif
最後に、マクロの記録をで終了しqます。
最後に入力したコマンドはマクロq(exe 'norm @q')を再生しますが、現在の行番号(line('.'))が変数に最初に保存された番号と同じ場合のみですa。
この:normalコマンドを使用すると@q、Exモードから通常のコマンド(など)を入力できます。
そして、コマンドが文字列にラップされてコマンドによって実行される理由は、コマンドの残りを消費(入力):executeしないようにするためです:normal(|endif)。
使用例。
次のバッファがあるとします。
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
そして、再帰的なマクロを使用して、任意の行からすべての数値を増やしたいと考えています。
次のように入力0して、カーソルを行の先頭に移動し、マクロの記録を開始できます。
qqq
qq
:let a=line('.')
<C-a>
w
:if line('.')==a|exe 'norm @q'|endif
q
qqqレジスタの内容をクリアしてq、マクロの定義中に最初に呼び出すときに、それが干渉しないようにしますqq 録音を開始します:let a=line('.') 現在の行番号を変数内に保存します aw カーソルを次の番号に移動します:if line('.')==a|exe 'norm @q'|endif マクロを呼び出しますが、行番号が変更されなかった場合のみq 記録を停止しますマクロを定義したら、カーソルを3行目に配置し、ヒット0して行の先頭に移動し、ヒットし@qてマクロを再生するqと、現在の行にのみ影響し、他の行には影響しません。
1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
記録後にマクロを再帰的にする
必要に応じて、記録後にマクロを再帰的にすることができます。マクロはレジスタ内の文字列に格納され、2つの文字列をドット.演算子で連結できるという事実を使用します。
これにより、いくつかの利点が得られます。
@qこれは、マクロが定義された後、古い内容が上書きされた後にマクロに追加されるためです。マクロを通常どおり(非再帰的に)記録する場合は、次のコマンドを使用して、後で再帰的にマクロを作成できます。
let @q = @q . "@q"
またはさらに短く:let @q .= "@q"
.=文字列を別の文字列に追加できる演算子です。
これ@qにより、register内に格納されたキーストロークのシーケンスの最後に2文字が追加されますq。カスタムコマンドを定義することもできます。
command! -register RecursiveMacro let @<reg> .= "@<reg>"
:RecursiveMacro引数としてレジスタの名前を待つコマンドを定義します(-register属性がに渡されるため:command)。
これは、唯一の違いは、あなたがすべての出現置き換えるで、前と同じコマンドだqとします<reg>。コマンドが実行されると、Vimは指定され<reg>たレジスタ名ですべての出現を自動的に展開します。
さて、あなたがしなければならないことは、マクロを通常通りに(非再帰的に)記録し、それから入力:RecursiveMacro qしてマクロをレジスター内に保存することqです。
同じことを行って、マクロが現在の行に留まるという条件でマクロを再帰的にすることもできます。
let @q = ":let a=line('.')\r" . @q . ":if line('.')==a|exe 'norm @q'|endif\r"
投稿の最初に説明したものとまったく同じですが、今回は録音後に行います。qレジスタに現在含まれているキーストロークの前と後の2つの文字列を連結するだけです。
let @q = レジスタの内容を再定義します q":let a=line('.')\r"VimにEnterキーを押してコマンドを実行するように指示するためにaマクロが動作する前に、変数内に現在の行番号を保存します。同様の特殊文字のリストを\r参照:help expr-quoteしてください。 . @q .qレジスタの現在の内容を前の文字列と次の文字列で連結し、":if line('.')==a|exe 'norm @q'|endif\r"q行が変更されなかったという条件でマクロを呼び出します繰り返しますが、いくつかのキーストロークを保存するには、次のカスタムコマンドを定義してプロセスを自動化できます。
command! -register RecursiveMacroOnLine let @<reg> = ":let a=line('.')\r" . @<reg> . ":if line('.')==a|exe 'norm @<reg>'|endif\r"
繰り返しますが、あなたがしなければならないことは、マクロを通常通り(非再帰的に)記録し、それから入力:RecursiveMacroOnLine qしqて、現在の行に留まる条件でレジスタ内に保存されたマクロを再帰的にすることです。
2つのコマンドをマージする
次:RecursiveMacroの2つのケースをカバーするように調整することもできます。
これを行うには、2番目の引数をに渡すことができます:RecursiveMacro。後者は、単にその値をテストし、値に応じて、前の2つのコマンドのいずれかを実行します。次のようなものになります。
command! -register -nargs=1 RecursiveMacro if <args> | let @<reg> .= "@<reg>" | else | let @<reg> = ":let a=line('.')\r" . @<reg> . ":if line('.')==a|exe 'norm @<reg>'|endif\r" | endif
または(行の継続/バックスラッシュを使用して、読みやすくします):
command! -register -nargs=1 RecursiveMacro
\ if <args> |
\ let @<reg> .= "@<reg>" |
\ else |
\ let @<reg> = ":let a = line('.')\r" .
\ @<reg> .
\ ":if line('.')==a | exe 'norm @<reg>' | endif\r" |
\ endif
前と同じですが、今回は:RecursiveMacro(-nargs=1属性のために)2番目の引数を指定する必要があります。
この新しいコマンドが実行されると、Vimは指定さ<args>れた値で自動的に展開します。
この2番目の引数が非ゼロ/ true(if <args>)の場合、コマンドの最初のバージョン(マクロを無条件に再帰的にするもの)が実行されます。そうでない場合、ゼロ/ falseの場合、2番目のバージョン(現在の行に留まるという条件で再帰的なマクロ)。
したがって、前の例に戻ると、次のことがわかります。
qq
<C-a>
w
q
:RecursiveMacro q 0
3G
0@q
qq レジスタ内のマクロの記録を開始します q<C-a> カーソルの下の数字をインクリメントしますw カーソルを次の番号に移動しますq 記録を終了します:RecursiveMacro q 0レジスタ内に保存されたマクロをq
再帰的にしますが、行の終わりまでのみです(2番目の引数のため0)3G カーソルを任意の行(たとえば3)に移動します0@q 行の先頭から再帰的なマクロを再生します前と同じ結果になるはずです。
1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
ただし、今回はマクロの記録中に気を散らすコマンドを入力する必要はなかったので、作業中のコマンドの作成に集中できます。
また、ステップ5で、コマンドにゼロ以外の引数を渡した場合、つまりの:RecursiveMacro q 1代わりに入力した場合:RecursiveMacro q 0、マクロqは無条件に再帰的になり、次のバッファーが与えられます。
1 2 3 4
1 2 3 4
2 3 4 5
2 3 4 5
今回は、マクロは3行目の終わりではなく、バッファーの終わりで停止します。
詳細については、以下を参照してください。
:help line()
:help :normal
:help :execute
:help :command-nargs
:help :command-register
1 2 3 4 5 6 7 8 9 10、私が手2 3 4 5 6 7 8 9 10 12の代わりに2 3 4 5 6 7 8 9 10 11。なぜか分からない、多分何かをタイプミスした。とにかく、それは私の単純なアプローチよりも洗練されているようで、マクロがカーソルを移動する場所を記述する正規表現と、この方法で使用したことがない場所リストを含みます。それは大好きです!
\d\+複数の数字を記述する必要がありました。
:lv ...コマンドの後、コマンドを:lla使用して最後の一致にジャンプし、:lpコマンドを使用して一致を逆の順序で進めることができます。
再帰的なマクロは、失敗したコマンドに遭遇するとすぐに停止します。したがって、行の終わりで停止するには、行の終わりで失敗するコマンドが必要です。
デフォルトでは、lコマンドはそのようなコマンドであるため、再帰マクロを停止するために使用できます。カーソルが行の最後にない場合は、コマンドを使用してカーソルを後ろに戻すだけですh。
したがって、saginawと同じサンプルマクロを使用します。
qqqqq<c-a>lhw@qq
分解:
qqq:qレジスタをクリアし、qq:qレジスター内のマクロの記録を開始します。<c-a>:カーソルの下の数字を増やし、lh:行の最後にいる場合は、マクロを中止します。それ以外の場合は、何もしません。w:行の次の単語に進みます。@q:再帰q:記録を停止します。その後0@q、saginawで説明されているのと同じコマンドでマクロを実行できます。
*この'whichwrap'オプションを使用すると、行の先頭または末尾にいるときに次の行にラップする移動キーを定義できます(を参照:help 'whichwrap')。lこのオプションを設定した場合、上記の解決策が破られます。
しかし、それはあなたが唯一の単一の文字を進めるための3つのデフォルトノーマルモードのいずれかのコマンドを使用します(可能性が高い<Space>、lと<Right>あなたがしているのであれば、)l自分の中に含ま'whichwrap'設定、あなたがいることを1削除することができませんから、使用を'whichwrap'オプション、例えば<Space>:
:set whichwrap-=s
その後l、マクロのステップ4のコマンドをコマンドに置き換えることができます<Space>。
virtualedit=onemoreはのlように厳しくはありませんが、行末を検出するための使用を妨げることに注意してくださいwhichwrap=l。
've'
:lv /\%3l\d/g %<CR>qqqqq<C-a>:lne<CR>@qq@q3行目のすべての数字をインクリメントする限り、ロケーションリストを使用して、マクロ内の検索一致を進めることができます。