行の終わりで再帰マクロを停止するにはどうすればよいですか?


13

行の最後までしか実行されないように再帰マクロを作成するにはどうすればよいですか?

または、行末までのみ再帰マクロを実行する方法は?

回答:


11

おそらくもっと簡単な方法がありますが、次の方法を試すことができます。

register qを使用して再帰マクロを記録するとします。

記録の最初に、次のように入力します。

:let a = line('.')

次に、記録の最後で、ヒット@qしてマクロを再帰的にするのではなく、次のコマンドを入力します。

:if line('.') == a | exe 'norm @q' | endif

最後に、マクロの記録をで終了しqます。

最後に入力したコマンドはマクロqexe '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
  1. qqqレジスタの内容をクリアしてq、マクロの定義中に最初に呼び出すときに、それが干渉しないようにします
  2. qq 録音を開始します
  3. :let a=line('.') 現在の行番号を変数内に保存します a
  4. Ctrl+ aカーソルの下の数を増やします
  5. w カーソルを次の番号に移動します
  6. :if line('.')==a|exe 'norm @q'|endif マクロを呼び出しますが、行番号が変更されなかった場合のみ
  7. 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つの文字列を連結するだけです。

  1. let @q = レジスタの内容を再定義します q
  2. ":let a=line('.')\r"VimにEnterキーを押してコマンドを実行するように指示するためにaマクロが動作する前に、変数内に現在の行番号を保存します。同様の特殊文字のリストを
    \r参照:help expr-quoteしてください。
  3. . @q .qレジスタの現在の内容を前の文字列と次の文字列で連結し、
  4. ":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 qqて、現在の行に留まる条件でレジスタ内に保存されたマクロを再帰的にすることです。


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
  1. qq レジスタ内のマクロの記録を開始します q
  2. <C-a> カーソルの下の数字をインクリメントします
  3. w カーソルを次の番号に移動します
  4. q 記録を終了します
  5. :RecursiveMacro q 0レジスタ内に保存されたマクロをq 再帰的にしますが、行の終わりまでのみです(2番目の引数のため0
  6. 3G カーソルを任意の行(たとえば3)に移動します
  7. 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

2
マクロが一致の位置を変更しない限り、たとえば、:lv /\%3l\d/g %<CR>qqqqq<C-a>:lne<CR>@qq@q3行目のすべての数字をインクリメントする限り、ロケーションリストを使用して、マクロ内の検索一致を進めることができます。
djjcast

@djjcast回答として投稿することもできます。私は試してみましたが、本当にうまくいきます。私は理解していないだけで1ケース、私は次の行にマクロを実行する場合があり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。なぜか分からない、多分何かをタイプミスした。とにかく、それは私の単純なアプローチよりも洗練されているようで、マクロがカーソルを移動する場所を記述する正規表現と、この方法で使用したことがない場所リストを含みます。それは大好きです!
-saginaw

@djjcast申し訳ありませんが、問題は正規表現に由来するもので、\d\+複数の数字を記述する必要がありました。
-saginaw

@djjcastああ、マクロがマッチの位置を変えてはいけないと言ったとき、あなたが何を意味したか理解できました。しかし、私はこの問題を解決する方法を知りません。私が持っている唯一のアイデアは、マクロ内から場所リストを更新することですが、私は場所リストに慣れていません。それは私にとって複雑すぎます、本当に申し訳ありません。
サギノー

1
@saginaw逆の順序で一致を反復処理すると、マクロが以前の一致の位置を変更する可能性が低くなるため、ほとんどの場合、問題を解決するようです。そのため、:lv ...コマンドの後、コマンドを:lla使用して最後の一致にジャンプし、:lpコマンドを使用して一致を逆の順序で進めることができます。
djjcast

9

再帰的なマクロは、失敗したコマンドに遭遇するとすぐに停止します。したがって、行の終わりで停止するには、行の終わりで失敗するコマンドが必要です。

デフォルトでは、lコマンドはそのようなコマンドであるため、再帰マクロを停止するために使用できます。カーソルが行の最後にない場合は、コマンドを使用してカーソルを後ろに戻すだけですh

したがって、saginawと同じサンプルマクロを使用します

qqqqq<c-a>lhw@qq

分解:

  1. qqq:qレジスタをクリアし、
  2. qqqレジスター内のマクロの記録を開始します。
  3. <c-a>:カーソルの下の数字を増やし、
  4. lh:行の最後にいる場合は、マクロを中止します。それ以外の場合は、何もしません。
  5. w:行の次の単語に進みます。
  6. @q:再帰
  7. q:記録を停止します。

その後0@q、saginawで説明されているのと同じコマンドでマクロを実行できます。


*この'whichwrap'オプションを使用すると、行の先頭または末尾にいるときに次の行にラップする移動キーを定義できます(を参照:help 'whichwrap')。lこのオプションを設定した場合、上記の解決策が破られます。

しかし、それはあなたが唯一の単一の文字を進めるための3つのデフォルトノーマルモードのいずれかのコマンドを使用します(可能性が高い<Space>l<Right>あなたがしているのであれば、)l自分の中に含ま'whichwrap'設定、あなたがいることを1削除することができませんから、使用を'whichwrap'オプション、例えば<Space>

:set whichwrap-=s

その後l、マクロのステップ4のコマンドをコマンドに置き換えることができます<Space>


1
また、設定virtualedit=onemoreはのlように厳しくはありませんが、行末を検出するための使用を妨げることに注意してくださいwhichwrap=l
ケビン

@ケビン良い点!言及するために答えを更新します've'
リッチ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.