Vimでのカーソル位置から開始してラップアラウンドするグローバル検索と置換


112

/ ノーマルモードコマンドで検索すると:

/\vSEARCHTERM

Vimはカーソル位置から検索を開始し、下方向に進み、先頭に戻ります。ただし、:substitute次のコマンドを使用して検索して置換すると、

:%s/\vBEFORE/AFTER/gc

代わりに、Vimはファイルの先頭から始まります。

Vimは検索し、カーソル位置から置き換える行うようにする方法がありますし、それが終わりに達した時点で先頭に折り返しますか?


2
\vpattern-「非常に魔法の」パターン:英数字以外の文字は特別な正規表現記号として解釈されます(エスケープは不要)
Carlos Araya

現在の位置から開始し、単に使用する代わりにファイルをラップするポイントは何%ですか?とにかくファイル全体を調べているので、私には合理的な使用法がありません。
ティミク2018

@tymik目的は、カーソルから検索を開始し、各結果を段階的に実行することでした。しかし、私は何年もVimを使用していません。
basteln 2018

回答:


50

1.  2ステップの置換を使用して動作を達成することは難しくありません。

:,$s/BEFORE/AFTER/gc|1,''-&&

まず、現在の行からファイルの終わりまで、各行に対して置換コマンドが実行されます。

,$s/BEFORE/AFTER/gc

次に、:substitute次のコマンドを使用して、同じコマンドを同じ検索パターン、置換文字列、フラグで繰り返します:& (を参照:help :&)。

1,''-&&

ただし、後者は、ファイルの最初の行から、前のコンテキストマークが設定されている行から1を引いた行までの範囲の行で置換を実行します。最初の:substituteコマンドは実際の置換を開始する前にカーソル位置を保存するため、によってアドレス指定され  ''た行は、その置換コマンドが実行される前の現在の行でした。('' アドレスは ' 疑似マークを参照しています。詳細については:help :range、および:help ''を参照してください。)

2番目のコマンド(| コマンドセパレータの後-を参照:help :bar)は、最初のコマンドで パターンまたはフラグが変更されている場合、変更を必要としないことに注意してください。

2.  入力を減らすために、コマンドラインで上記の置換コマンドのスケルトンを表示するために、次のようにノーマルモードマッピングを定義できます。

:noremap <leader>cs :,$s///gc\|1,''-&&<c-b><right><right><right><right>

<c-b><right><right><right><right>カーソルをコマンドラインの先頭(<c-b>)に移動し、次に4文字を右(<right> ×4)に移動して、最初の2つのスラッシュ記号の間にカーソルを移動し、ユーザーが検索パターンの入力を開始できるようにするために、後続部分が必要です。。目的のパターンと置換の準備ができたら、を押して結果のコマンドを実行できます Enter

(上記のマッピングでは//なく///、パターンを入力したい場合は、右矢印を使用してカーソルを既存の分離スラッシュの先頭に移動するのではなく、分離スラッシュを入力し、その後に置換文字列を入力することを検討してください。交換部品)


1
このソリューションの唯一の問題は、オプションlast(l)とquit(q)が2番目の代替コマンドの実行を停止しないことです
Steve Vermeulen

@eventualEntropy別の「q」プレスのプロンプトについては、以下の私の解決策を参照してください。
q335r49 14年

2
これをキーマッピングに含める場合は、(私がしたように)パイプをエスケープすることを忘れないでください。
jazzabeanie

11
なんてこった、これらの恣意的なキャラクターすべてが何を意味するのか。どのようにしてそれを学びましたか?
岩だらけのアライグマ

2
@Tropilio:その後、次のようなことを試すことができます:noremap <leader>R :,$s///gc\|1,''-&&<c-b><right><right><right><right>:,$s///gc|1,''-&&最初の2つのスラッシュ記号の間にカーソルを置いてコマンドラインに配置します。目的のパターンと置換を入力したら、Enterキーを押して結果のコマンドを実行できます。
ib。

174

既に範囲を使用しています。%これは1,$、ファイル全体を意味する省略形です。現在の行から最後まで移動するには、を使用します.,$。ピリオドは現在の行を$意味し、最後の行を意味します。したがって、コマンドは次のようになります。

:.,$s/\vBEFORE/AFTER/gc

しかし、.または現在の行は削除できると想定できます。

:,$s/\vBEFORE/AFTER/gc

詳細については、

:h range

ありがとう、私はすでにそれについて知っていました。ドキュメントの最後に到達したら、検索と置換を折り返したいのですが。:。、$ sの場合、それは行われず、「パターンが見つかりません」とだけ表示されます。
basteln

7
:「次の10行」のために10:s/pattern/replace/gc
ここでは

10
OPが求めていたものではありませんが、これは私にとって非常に役に立ちました。
Tobias J

51

%1,$
(Vimのヘルプ=> :help :%1、$(ファイル全体)に等しい)のショートカットです。

. あなたができるようにカーソル位置です

:.,$s/\vBEFORE/AFTER/gc

文書の最初からカーソルまでを置き換えるには

:1,.s/\vBEFORE/AFTER/gc

:help rangeほとんどすべてのコマンドが範囲で機能するため、範囲に関するマニュアルを読むことを強くお勧めします。


ありがとう、私はすでにそれについて知っていました。ドキュメントの最後に到達したら、検索と置換を折り返したいのですが。:。、$ sの場合、それは行われず、「パターンが見つかりません」とだけ表示されます。
basteln

@basteln:投稿にタイプミスがありました//コピーして貼り付けましたか?
sehe

すべてを置き換えたいのですが、確認を求めたいので、現在の位置から始めたいと思います。あとは二回だけ。
mb14 2011

代わりにnと&を使用できます。
mb14、2009

うーん、私はnと&が最善の解決策だと思いますが、正確にそうである必要はありません(すべての一致でyes / noプロンプトが表示されます)。しかし、間違いなく十分です、ありがとう!:)
basteln '09 / 09/29

4

最後に、検索を終了すると、巨大な関数を作成せずにファイルの先頭に戻るという解決策が思い付きました...

私がこれを思いつくのにどれくらい時間がかかったのか信じられないでしょう。折り返すかどうかのプロンプトを追加するだけです。ユーザーがqもう一度押しても折り返さないでください。つまり、基本的に、qq代わりにをタップして検索を終了しますq。(ラップしたい場合は、と入力してくださいy。)

:,$s/BEFORE/AFTER/gce|echo 'Continue at beginning of file? (y/q)'|if getchar()!=113|1,''-&&|en

これを実際にホットキーにマッピングしました。したがって、たとえば、あなたがして、現在の位置から開始し、カーソルの下のすべての単語を検索し、交換したい場合はq*

exe 'nno q* :,$s/\<<c-r>=expand("<cword>")<cr>\>//gce\|echo "Continue at beginning of file? (y/q)"\|if getchar()==121\|1,''''-&&\|en'.repeat('<left>',77)

私の意見では、このアプローチ(私の回答で提案された2つのコマンドの間に追加のプロンプトを挿入する)は、使いやすさを向上させることなく、不必要な複雑さを追加します。で、元のバージョンあなたが(と第1の置換コマンドを終了する場合は、ql、またはEscキー)と、上から継続したくない、あなたはすでに押すことができますqまたはEscキーは再びすぐに2番目のコマンドを終了します。つまり、提案されているプロンプトの追加は、既存の機能を効果的に複製しているようです。
ib。

おい...あなたはあなたのアプローチを試しましたか?「let」の次の3つの出現箇所を「unlet」に置き換えたいとしましょう。次に、これがキーです。段落の編集を続けます。あなたのアプローチ "y、y、y、qを押してください。今度はqを押します。段落の最初の行から検索を開始します。もう一度qを押して終了します。前に戻って編集を続けます。OK、g ;.したがって、基本的に:yyyqq ...混乱する... g;(またはu ^ R)私の方法:yyyqq
q335r49

もちろんyyyq、理想的な方法は、方向転換のジャンプをせずに編集を続けることです。しかしyyyqq、使いやすさの点でダブルタップをほぼシングルタップと考えると、ほぼ同じです。
q335r49 14年

元のコマンドもプロンプト付きのバージョンも、そのシナリオでカーソルを元の位置に戻しません。前者は、最初に出現したパターン(q2回目を押した瞬間)からユーザーを離れます。後者では、置換された最後のオカレンス(最初qに押された直前)にカーソルを置きます。
ib。

1
元のバージョンが自動的に(ユーザ入力せずに、その前の位置にカーソルを戻すことが望ましい場合、Ctrlキー + O)と、1つだけ追加することができるnorm!``コマンドを::,$s/BEFORE/AFTER/gc|1,''-&&|norm!``
ib。

3

これは、2段階のアプローチ:,$s/BEFORE/AFTER/gc|1,''-&&)または中間の「ファイルの先頭で続行しますか?」で検索をラップすることに関する懸念に対処する非常に大まかなものです。アプローチ:

" Define a mapping that calls a command.
nnoremap <Leader>e :Substitute/\v<<C-R>=expand('<cword>')<CR>>//<Left>

" And that command calls a script-local function.
command! -nargs=1 Substitute call s:Substitute(<q-args>)

function! s:Substitute(patterns)
  if getregtype('s') != ''
    let l:register=getreg('s')
  endif
  normal! qs
  redir => l:replacements
  try
    execute ',$s' . a:patterns . 'gce#'
  catch /^Vim:Interrupt$/
    return
  finally
    normal! q
    let l:transcript=getreg('s')
    if exists('l:register')
      call setreg('s', l:register)
    endif
  endtry
  redir END
  if len(l:replacements) > 0
    " At least one instance of pattern was found.
    let l:last=strpart(l:transcript, len(l:transcript) - 1)
    " Note: type the literal <Esc> (^[) here with <C-v><Esc>:
    if l:last ==# 'l' || l:last ==# 'q' || l:last ==# '^['
      " User bailed.
      return
    endif
  endif

  " Loop around to top of file and continue.
  " Avoid unwanted "Backwards range given, OK to swap (y/n)?" messages.
  if line("''") > 1
    1,''-&&"
  endif
endfunction

この関数は、いくつかのハックを使用して、先頭に回り込む必要があるかどうかを確認します。

  • ユーザーが「l」、「q」、または「Esc」を押した場合、折り返しはありません。これらはいずれも中止したいことを示しています。
  • マクロを "s"レジスタに記録し、最後の文字を検査することにより、最後のキー押下を検出します。
  • 「s」レジスタを保存/復元して、既存のマクロを上書きしないようにします。
  • コマンドの使用時にすでにマクロを記録している場合、すべてのベットはオフになっています。
  • 割り込みで正しいことをしようとします。
  • 明示的なガードで「後方範囲」警告を回避します。

1
これを小さなプラグインに抽出し、ここのGitHubに置きました
wincent

プラグインをインストールするだけで、魅力的に動作します!! これを作ってくれてありがとう!
Tropilio

1

私はパーティーに遅れましたが、私はそのような遅いstackoverflow回答者に頼ることが多すぎて、それをしませんでした。redditとstackoverflowに関するヒントを収集しました。最良のオプションは\%>...c、検索でパターンを使用することです。これは、カーソルの後にのみ一致します。

とは言っても、次の置換ステップのパターンがめちゃくちゃになり、タイプするのが難しいです。これらの影響に対抗するために、カスタム関数は後で検索パターンをフィルタリングして、リセットする必要があります。下記参照。

私は、次の出来事を置き換え、その後にジャンプするマッピングで自分自身を主張しましたが、それ以上ではありません(とにかく私の目標でした)。これに基づいて、グローバルな代替を行うことができると確信しています。:%s/.../.../g以下のパターンがカーソル位置に残っているすべての行の一致をフィルターで除外するようなソリューションを作成するときに作業するときは注意してください。次の一致、したがってすべての一致を1つずつ実行することができます。

fun! g:CleanColFromPattern(prevPattern) return substitute(a:prevPattern, '\V\^\\%>\[0-9]\+c', '', '') endf nmap <F3>n m`:s/\%><C-r>=col(".")-1<CR>c<C-.r>=g:CleanColFromPattern(getreg("/"))<CR>/~/&<CR>:call setreg("/", g:CleanColFromPattern(getreg("/")))<CR>``n


おかげで、遅い回答が役立つことが多いことに同意します。私は個人的には(これなどの理由で)長い間Vimを使用していませんが、他の多くの人がこれを参考にすると思います。
basteln
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.