Vim: глобальный поиск и замена, начиная с позиции курсора

Когда я выполняю поиск с помощью

/\vSEARCHTERM

Vim начинает поиск с позиции курсора вниз, и он будет перемещаться вверх. Однако при поиске и замене с помощью

:%s/\vBEFORE/AFTER/gc

Vim начинается в верхней части файла.

Есть ли способ сделать поиск и замену Vim, начиная с позиции курсора И обертывая его вверх, как только он достигнет конца?

Ответ 1

Нетрудно добиться такого поведения, используя двухэтапную замену:

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

Сначала выполняется команда подстановки для каждой строки, начиная с текущий до конца файла. Затем команда :substitute повторяется с тем же шаблоном поиска, заменой строки и флагов, используя команду :&. Последнее, однако, выполняет замену в диапазоне строк от первой строки файла до строки, где предыдущая метка контекста была установлена, минус одна. Начиная с первого Команда :substitute сохраняет позицию курсора перед началом фактического замены, строка, адресованная '', - это строка, которая была текущий до того, как была выполнена эта команда подстановки.

Обратите внимание, что вторая команда (после символа |) не требует изменение при изменении шаблона или флагов первого.

Ответ 2

Вы уже используете диапазон, %, который является короткой рукой для 1,$, что означает весь файл. Чтобы перейти от текущей строки до конца, вы используете .,$. Период означает текущую строку, а $ означает последнюю строку. Таким образом, команда будет:

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

Но можно предположить, что . или текущая строка могут быть удалены:

:,$s/\vBEFORE/AFTER/gc

Подробнее см.

:h range

Ответ 3

% является ярлыком для 1,$
(Vim help = > :help :%, равный 1, $(весь файл).)

. - позиция курсора, поэтому вы можете сделать

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

Для замены с начала документа до курсора

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

и т.д.

Я настоятельно рекомендую вам прочитать руководство о диапазоне :help range, так как почти все команды работают с диапазоном.

Ответ 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)

Ответ 5

Здесь что-то очень грубое, что касается проблем вокруг обхода поиска с помощью двухэтапного подхода (:,$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".
  • Если вы уже записываете макрос при использовании команды, все ставки отключены.
  • Пытается делать правильные действия с прерываниями.
  • Предотвращает предупреждения "назад диапазона" с явным контролем.