Vim pesquisa e substitui o texto selecionado

109

Digamos que temos um texto e eu entro no modo visual e seleciono algum texto. Como faço rapidamente uma pesquisa pelo texto destacado e o substituo por outro?

Gabriel Solomon
fonte

Respostas:

135

Tente executar o seguinte ou colocar em seu .vimrc

vnoremap <C-r> "hy:%s/<C-r>h//gc<left><left><left>

Ao pressionar ctrl+ rno modo visual, você será solicitado a inserir o texto para substituir. Pressione entere confirme cada alteração com a qual você concorda you recusa n.

Este comando irá sobrescrever o seu registro hpara que você possa escolher outro (mudando hno comando acima para outra letra minúscula) que você não usa.

Mykola Golubyev
fonte
13
Se você omitir o cem gc, Replace é feito de uma só vez em todo o buffer. Se você cometeu um erro e deseja revertê-lo, basta digitar uno modo de comando. Eu gosto mais dessa abordagem do que confirmar cada uma das substituições (isso acontece com o cmodificador).
Niloct
3
vmap * y:let @/ = @"<CR>selecionado é realçado.
kev
8
Qual é o objetivo <left><left><left>?
aymericbeaumet
7
@aymericbeaumet para colocar o cursor entre as duas barras, dessa forma a palavra de substituição pode ser facilmente digitada.
mcabrams
5
Observe que se você seguir a sugestão de @Niloct sobre a remoção de cde gc, você também terá que remover um dos <left>do final da linha.
Nathan Amigo
36

Este mapeamento rápido e simples busca texto destacado visualmente (sem sobrescrever o registro h) e sem usar o registro dependente + do terminal:

" search for visually hightlighted text
vnoremap <c-f> y<ESC>/<c-r>"<CR>   

Se você não gosta do ctrl-f mude-o de acordo com sua preferência

Assim que o texto for destacado, você sempre pode fazer uma ssubstituição simplesmente digitando:

%s//<your-replacement-string>

... porque uma pesquisa em branco no scomando usa a última string pesquisada.

ErichBSchulz
fonte
Quando eu uso isso na minha configuração do vim, control+fsempre seleciona o próximo caractere, bem como o que eu selecionei visualmente. Alguma ideia?
Robert Mark Bram
%s//<your-replacement-string>não parece funcionar para destacar algo como /tmp/file. torna<your-replacement-string>/tmp/file
naisanza
28

Este também funciona (pelo menos para seleções em uma única linha / seleções que não contêm caracteres especiais)

  • selecione o texto no modo visual
  • arrancar o texto com y
  • :s/<C-r>0/

0 é o registro de puxão.

Como outras pessoas mencionaram, existem maneiras mais rápidas de fazer isso, mas se você acabou de começar a aprender Vim (como eu), esta é uma das maneiras 'genéricas'.

// editar: acabei de perceber que a abordagem de 'registro' falhará se houver caracteres como tabulações ou novas linhas ou /caracteres na seleção (eu teria que processar manualmente esses caracteres de alguma forma para que o :scomando funcione):

insira a descrição da imagem aqui

Colemik
fonte
2
É muito simples. Oh Deus, por que não está coberto na wiki. Vamos ... Obrigado pela resposta!
Benyamin Limanto
2
Esta é a resposta ideal para aqueles de nós ainda no início da curva de aprendizado do Vim. Eu sei que a personalização é um ponto forte, mas não quero copiar e colar um script complexo em meu vimrc e descobrir como ele se encaixa para um problema tão simples como este. Essa resposta me ensinou que ctrl-R descarta um registrador, o que será útil lembrar em outros contextos.
nivek
26

A resposta aceita funciona muito bem, a menos que você tenha caracteres especiais em sua seleção visual. Eu hackeei dois scripts ( Jeremy Cantrell postou aqui e Peter Odding ) para fazer um comando que permitirá a você selecionar visualmente uma string que deseja encontrar, mesmo que contenha caracteres regex especiais.

" Escape special characters in a string for exact matching.
" This is useful to copying strings from the file to the search tool
" Based on this - http://peterodding.com/code/vim/profile/autoload/xolox/escape.vim
function! EscapeString (string)
  let string=a:string
  " Escape regex characters
  let string = escape(string, '^$.*\/~[]')
  " Escape the line endings
  let string = substitute(string, '\n', '\\n', 'g')
  return string
endfunction

" Get the current visual block for search and replaces
" This function passed the visual block through a string escape function
" Based on this - /programming/676600/vim-replace-selected-text/677918#677918
function! GetVisual() range
  " Save the current register and clipboard
  let reg_save = getreg('"')
  let regtype_save = getregtype('"')
  let cb_save = &clipboard
  set clipboard&

  " Put the current visual selection in the " register
  normal! ""gvy
  let selection = getreg('"')

  " Put the saved registers and clipboards back
  call setreg('"', reg_save, regtype_save)
  let &clipboard = cb_save

  "Escape any special characters in the selection
  let escaped_selection = EscapeString(selection)

  return escaped_selection
endfunction

" Start the find and replace command across the entire file
vmap <leader>z <Esc>:%s/<c-r>=GetVisual()<cr>/

Eu incluí isso no meu vimrc se for mais útil para alguém.

bryan kennedy
fonte
2
obrigado pela solução, combinando várias propostas, acabei com o seguinte: vmap <C-r> <Esc>:%s/<c-r>=GetVisual()<cr>//g<left><left>(que apenas substitui todas no arquivo)
Peter Butkovic
13

A partir do Vim 7.4, você pode usar o gnmovimento que seleciona regiões de texto que correspondem ao padrão de pesquisa atual.

Depois de usar cgnpara alterar o texto na correspondência atualmente selecionada (ou dgnexcluir), no modo normal, você pode usar .para repetir o comando e operar na próxima correspondência.

um episódio do Vimcast mostrando esse recurso.

Dapeng Li
fonte
3
Esta é uma ótima resposta!
mauriciojost
Isso também é incrível. além da resposta colemik. Muito bom!
Benyamin Limanto
uau, é como um daqueles segredos que você descobre quando navega fundo para encontrar uma boa solução, obrigado!
vdegenne
8

Mykola Golubyev, Obrigado pela dica! A seguir usa o registro "+" que (dependendo do seu terminal) já contém o texto destacado, salvando de usar o registro "h".

vnoremap <C-r> <Esc>:%s/<C-r>+//gc<left><left><left>
dmd
fonte
Meu VIM 7.0 no CentOS 5.x não parece ter o registro +. Este é um recurso padrão?
dotancohen
3
Noto que acesso a máquina CentOS através do Putty, e ao pesquisar no Google vejo que + é o registro da área de transferência. O Putty não parece suportar isso! Portanto, essa resposta é na verdade dependente do terminal.
dotancohen
Olá, tentei seu comando, mas ele está me pedindo para substituir as ocorrências do texto especificado em todo o arquivo, e não apenas no texto selecionado
solalito
7

De http://vim.wikia.com/wiki/Search_and_replace_in_a_visual_selection :

Quando o texto for selecionado visualmente, pressione: para inserir um comando. A linha de comando irá inserir automaticamente o intervalo:

:'<,'>

Você pode inserir um comando como s / red / green / g para substituir cada vermelho por verde em todas as linhas da última seleção visual. O comando aparecerá como:

:'<,'>s/red/green/g

Para repetir um comando Ex sobre um bloco previamente selecionado, use o: history. Ou seja, pressione: e então edite um comando anterior.

syvex
fonte
4
sim, isso substitui vermelho por verde na área selecionada - muito útil, mas não é o que o op pediu.
ErichBSchulz,
@ErichBSchulz Ele realmente faz isso em todas as linhas selecionadas. Portanto, mesmo que apenas parte de uma linha seja selecionada, ele fará isso em toda a linha.
Cully
1
Talvez não responda à pergunta do OP, mas isso responde exatamente à minha pesquisa "vim search and replace with visual selection", onde eu quero limitar a pesquisa e substituir apenas a seleção visual, o que traz esta questão
icc97
5

Eu tenho isso no meu vimrc:

function! GetVisual() range
    let reg_save = getreg('"')
    let regtype_save = getregtype('"')
    let cb_save = &clipboard
    set clipboard&
    normal! ""gvy
    let selection = getreg('"')
    call setreg('"', reg_save, regtype_save)
    let &clipboard = cb_save
    return selection
endfunction

vmap <leader>z :%s/<c-r>=GetVisual()<cr>/

Isso pegará a seleção visual e iniciará um comando de substituição com ela.

EDIT: Devo salientar que isso não funciona com seleções visuais multiline. Embora GetVisual () não tenha problemas para devolvê-lo, não tenho certeza de como colocá-lo corretamente na linha de comando. Se alguém tiver alguma dica de como posso fazer isso, comente.

Jeremy Cantrell
fonte
2

Eu não acho que você pode fazer isso fora da caixa. Mas muitas pessoas gostam desse recurso, então há toneladas de macros e tal (eu mesmo adicionei). Aqui está um que você pode adicionar, por exemplo; apenas pressione *para pesquisar a próxima correspondência no texto selecionado visualmente.

John Feminella
fonte
2

Outras respostas são boas, mas não escapam a caracteres especiais. A resposta de @brian kennedy mostra um método para escapar, mas requer muito código. No caso de uma URL, por exemplo, o escape é obrigatório ou a pesquisa será interrompida em “:”.

Conforme escrito em vim.fandom.com/wiki/Search_for_visually_selected_text , você pode fazer um oneliner para pesquisar e escapar alguns caracteres. Você pode escapar dos personagens que quiser, eu escolhi escapar /\: :

vnoremap // y/\V<C-R>=escape(@",'/\:')<CR><CR>

Com este mapa, devo pressionar //no modo visual para pesquisar o texto atualmente selecionado em todo o buffer.

RomainTT
fonte
1

Minha versão curta favorita disso é

vnoremap s / y: s / "/

Então você pode destacar uma seleção e apenas apertar s / para iniciar um comando de substituição para ela.

alex
fonte
Não funcionou para mim, tratado "como um literal. <c-r>"funcionou
Josh Bodah de
1

Não tenho representante para comentar, então estou adicionando mais uma resposta ...

vnoremap <C-r> "0y<Esc>:%s/<C-r>0//g<left><left>

@dmd e @dotancohen: funcionou para mim sem esgotar "h(usa explicitamente 0, que é a última posição de arrancada de qualquer maneira) e funciona com putty ssh -> mint (lmde); não sei sobre cent. Também usa comentários de @Niloct e @Nathan Friend para remover a confirmação.

10.004
fonte
1

Exemplo 1. Cite a seleção com estrelas, mantenha o cursor no início da seleção.

:%s,\%#\%V\_.*\%V/,**&**,c 

Exemplo 2. Tubo de texto selecionado com base64:

:%s!\%#\%V\_.*\%V!\=system("base64 -w 0",@")!c

Explicação LHS:

\%# matches cursor 
\%V matches inside the selection only
\_. matches any character.
\_.* matches beginning to end of selection.

Explicação RHS:

\=expr()  Replace match with result of expr() for each substitution.
mosh
fonte
0

Eu esperava que houvesse uma maneira embutida no Vim para substituir uma palavra, sem ter que redigitar a palavra em primeiro lugar. Existe, embora funcione apenas para texto até um caractere de limite de palavra.

Depois de passar o mouse sobre a palavra, pressione *ou #para realçar a palavra e pule para o local anterior / seguinte dela (se você não quiser alterar os locais no arquivo, pressione a tecla oposta para voltar ao local você estava apenas em).

Depois é só digitar:

%s//<your-replacement-string>

Conforme mencionado na resposta de outra pessoa, se %sreceber um item em branco entre as barras, então usa a palavra pesquisada anteriormente. Ao pressionar *ou #, você está procurando a palavra sob o cursor, o que a torna a palavra pesquisada mais recentemente.

No final, são apenas seis ou sete pressionamentos de tecla + comprimento da palavra de substituição para fazer isso e não requer macro ou edição do seu .vimrc.

Cameron Gagnon
fonte