Como encontrar e substituir no Vim sem precisar digitar a palavra original?

53

Eu gostaria de otimizar meu fluxo de trabalho "localizar e substituir" no Vim. É algo que faço frequentemente, como tenho certeza que a maioria de vocês também. Geralmente algo como - copie um bloco e mude o nome de uma variável em alguns lugares. Eu sei, eu sei, que provavelmente dispara o reflexo "por que você está copiando e colando código", mas não vamos seguir esse caminho ... Existem muitos casos de uso válidos :)

Conheço bem os comandos de pesquisa e substituição: :sou :%smas não gosto deles. Obriga-me a digitar o nome completo da variável que estou procurando e para o que estou mudando. Talvez exista uma maneira melhor de corrigir a quantidade de digitação :%s? Costumo usar nomes de variáveis ​​descritivos longos, de modo que isso é realmente um diferencial para mim. Eu também não gosto de como digitar um nome de variável a partir do zero é propenso a erros de digitação e pode consumir tempo e inteligência para caçar erros de digitação. Eu prefiro digitá-lo uma vez e depois copiar e colar para evitar isso inteiramente, se possível.

Meu fluxo de trabalho atual usa alguma combinação de movimento / puxar / selecionar / pesquisar / colocar para mover o arquivo e substituir um por um. Não é ótimo, mas tem o benefício de evitar a digitação de nomes de variáveis ​​completos. Talvez eu precise digitar as primeiras letras com /ou usar outro comando de movimento (por exemplo fx), dependendo do que estiver ao redor, e pressione vea tecla para selecionar a palavra inteira. Também não me importo de ter que repetir para cada instância. Eu nunca faço uma busca completa substituir sem confirmar cada alteração. Mas seria muito preferível se eu pudesse repetir a ação de substituição com um único pressionamento de tecla (o que não posso fazer com esse método). cada substituição é geralmente algo como n, em seguida, veem seguida, p(ou pior "0p)

Existe uma maneira mais rápida?

Joel
fonte
11
.repete o último "bloco de comando" (não tem certeza do termo exato. por exemplo: mover + ação + texto. por exemplo: cwtoto<Esc>(mude do cursor para o final da palavra com "toto")) c/foo<enter>bar<Esc>(mude do cursor para pouco antes " foo "e substitua por" bar "). Você pode mover (com cursor, hjkl ou um número + hjkl (faz isso n vezes), G (ir para o final do arquivo) / algo (ir logo antes" algo "), etc) e pressione .para refazer o mesmo 'bloco de comando'
Olivier Dulac
Eu uso isso bastante! No entanto, não funciona se você deseja puxar uma palavra e substituí-la por vários locais. Esse é o principal caso de uso com o qual tenho um problema e é mais ou menos assim n ve "0p. Que, infelizmente, não pode ser replicado com apenas um.
Joel
5
n ce ctrl-r 0 <esc> n.n.n.n.n.n.
Rich
@ Joel, você também pode gravar uma macro (qa, em seguida, tudo o que quiser, depois q: cria a macro a) e, em seguida, use: @a para reproduzi-la (e, como de costume, 134 @ a para fazê-lo 134 vezes). a macro pode ser tão elaborada quanto necessária (exemplo: encontre algo, vá para o lugar certo, depois mude a palavra para ele e depois vá para o lugar apropriado para poder ser chamado novamente em uma posição melhor)
Olivier Dulac
11
Aprendi truques mais úteis do VIM com esta pergunta do que com qualquer outra pergunta no site. Obrigado!
dotancohen

Respostas:

81

Na verdade, eu tenho um fluxo de trabalho bastante semelhante ao seu (copiar e colar blocos que são semelhantes e depois usar :spara alterar nomes de variáveis), especialmente quando estou escrevendo muitas linhas semelhantes, exceto pela variável que elas usam. Mas há algumas coisas que faço que você pode achar úteis.

Coisas gerais do vim

  1. A primeira coisa que o ajudará é perceber que :s//foo/gpreencherá o termo de pesquisa que você usou por último e o substituirá por foo . Só isso pode economizar muito tempo, mas quando você o combina com o comando asterisco (procure a palavra sob o cursor atual), ele economiza muito tempo. Por exemplo, para mudar tudo foopara spam, em vez de fazer

    :%s/foo/spam/g
    

    O que exige que você digite foo e spam (que podem ser variáveis ​​longas), você pode simplesmente navegar para foo e fazer:

    *:%s//spam/g
    

    Dessa forma, você pode obter rapidamente o nome da variável que está alterando pesquisando-a e não precisará digitá-la. Isso também funcionará bem com o seu fluxo de trabalho atual de usar nmuito. Também é útil ter hlsearch, porque então você pode dizer visualmente onde estão as variáveis ​​que você está substituindo.

    (nota: O comando asterisco irá adicionar limites de palavra, que é \<e \>em torno da palavra sob o cursor Se você não quer isso, use. g*em vez disso.)

  2. seria muito preferível se eu pudesse repetir a ação de substituição com um único pressionamento de tecla

    Você pode usar @:para refazer o último comando substituto. Isso é bom se você deseja executar um :scomando em várias linhas, mas nem todas elas :%snão funcionarão. Outra opção é a bandeira de confirmação, ou seja :%s///gc. Isso permitirá que você digite you nem cada ocorrência para decidir se deseja substituí-lo ou não.

    Como Desty apontou , você também pode &executar novamente o último comando substituto, mas observe que isso não manterá seus sinalizadores (como globalou confirm). Se isso é bom ou ruim, depende da sua opinião pessoal. Se você gostaria de usar isso, mas manter suas bandeiras também, você pode fazer

    nnoremap & :&&<cr>
    
  3. Se você deseja alterar todas as ocorrências em um bloco, mas não todas as correspondências no arquivo, você sempre pode usar uma seleção visual do bloco, que será preenchida

    :'<,'>
    

    ao seu substituto, que o restringe às linhas que você selecionou. Quando combinado com a abordagem de asterisco, acho isso extremamente útil. Além disso, se você estiver criando vários comandos substitutos no mesmo bloco de linhas, poderá usar gvpara selecionar novamente as mesmas linhas para executar outro comando. (seja :substituteum comando normal ou)

    Se você executar outro comando substituto, não precisará selecionar novamente as mesmas linhas, porque :'<,'>ainda operará nas mesmas linhas. No entanto, é mais conveniente digitar

    gv:s
    

    ao invés de

    :'<,'>s
    

Plugins / Mapeamentos que eu gosto

  1. Eu tenho o seguinte mapeamento na minha .vimrcque estende a abordagem asterisco para que eu só precisa digitar o novo nome desejado:

    "(R)eplace all
    nnoremap <leader>r yiw:%s/\<<C-r>"\>//g<left><left>
    

    Para usar isso, basta colocar o cursor na palavra que deseja alterar e digitar <leader>rNewVarName<cr>e pronto. Observe que isso substituirá todas as correspondências sem oferecer a opção de confirmá-las; portanto, você pode não gostar muito. Claro, você pode simplesmente mudar para

    nnoremap <leader>r yiw:%s/\<<C-r>"\>//gc<left><left><left>
    

    Para permitir a opção de confirmação.

    editar : Após a resposta da missa e o comentário da rcorre , você também pode escrever isso como

    nnoremap <leader>r :%s/\<<C-r><C-w>\>//g<left><left>
    

    Qual tem a vantagem de manter o seu registro sem nome o mesmo.

  2. Se você estiver renomeando grupos de variáveis ​​semelhantes ao mesmo tempo, por exemplo, alterando

    fooSuffix
    PrefixFoo
    foo1
    SHOUTINGFOO
    

    para

    barSuffix
    PrefixBar
    bar1
    SHOUTINGBAR
    

    pode ser difícil substituir todos eles individualmente. É aqui que o tpope / vim-abolish é útil. Você pode substituir tudo isso em um comando por:

    :%S/foo/bar/g
    

    e cuida da capitalização para você.

Isso deve ajudá-lo muito, mas deixe-me saber se há algo sobre essas abordagens que você não gosta ou sente que está faltando. Fico feliz em falar sobre mais abordagens. :)


Leitura recomendada:

DJMcMayhem
fonte
Oh, eu acabei de aprender várias coisas aqui. Agradável! Uma abordagem alternativa que você pode adotar é <leader>rc(ou cr) adicioná /c-la ao final. Além disso, eu set gdefaultconfigurei, porque quase nunca quero substituir um único resultado, então seria apenas yiw:%s/\<<C-r>"\>//<left><left>
Wayne Werner
2
Que resposta fantástica!
Scott Lundberg 26/09
2
Se você usar ctrl+r-ctrl+wno mapeamento (como sugerido por @Mass) ele deve funcionar o mesmo, mas não vai mexer com o seu registo puxão (que pode ou não ser desejável)
rcorre
11
Uma coisa a lembrar ao trabalhar com blocos de texto que você destaca (antes de cortar / puxar etc) é que você pode inserir gv no modo normal para realçar o último texto destacado.
11
Eu escrevi o plugin github.com/hauleth/sad.vim, que funciona de maneira semelhante ao método 2. hovewer permite que você use .para realizar substituições adicionais.
Hauleth
20

A c_CTRL-Rfamília de mapas pode melhorar bastante o seu fluxo de trabalho:

  1. Você não precisa digitar nomes de variáveis ​​ao usar :s/, basta usar CTRL-R CTRL-Wpara inserir a palavra sob o cursor na linha de comando (ressalva: isso não escapa caracteres especiais, como *faz).

  2. Depois de fazer uma pequena alteração usando ce, você pode procurar a palavra antiga usando / CTRL-R -. Em seguida, use .para repetir a alteração.

  3. Se você precisar fazer alterações no padrão de pesquisa CTRL-R /, colocará a pesquisa existente na linha de comando.

  4. Finalmente, você pode colocar o conteúdo de qualquer registro usando CTRL-R {register}

Massa
fonte
Bom ponto! Eu sempre esqueço o Ctrl R. Vou ver se consigo trabalhar nisso também.
Joel
2
Vale mencionar especificamente Ctrl-R /? Eu uso isso com bastante frequência em :scomandos se quiser usar um termo de pesquisa que seja quase mas não exatamente o mesmo que o anterior.
Rich
12

Existem mais alguns métodos que (para minha surpresa!) Ainda não foram mencionados.

Usando o gncomando

gnfunciona como o ncomando, exceto que, além de pular para a partida, entra no modo visual, com toda a partida selecionada.

Portanto, para alterar uma palavra (ou qualquer coisa que você possa corresponder a uma expressão regular!), Procure-a primeiro 1 e, em seguida, pressione cgnseguido do texto com o qual deseja substituir a correspondência e Escretornar ao modo normal.

(Você menciona que, às vezes, o texto de substituição que você deseja usar é armazenado no "0registro yank - observe que você pode inseri-lo rapidamente sem precisar redigitá-lo pressionando Ctrl+R0no modo de inserção.)

Você pode então pular para a próxima partida e repetir a alteração com um único .toque de tecla!

Se houver lugares onde você não deseja fazer a substituição, basta pressionar unpara desfazer a alteração e pular para a próxima partida.

1: Rapidamente, usando as outras sugestões sobre esta página, como *, Ctrl+RCtrl+W, etc. Veja também o comentário de Peter Rincker para mais sugestões sobre como preencher inicialmente a sua pesquisa.

Usando o Modo de Seleção

(Aprendi isso com romainl 's post sobre o subreddit vim . Eu não sei se ele inventou isso sozinho ou estava apenas passando-o.)

O modo de seleção é um pouco semelhante ao modo visual, exceto se você começar a digitar o texto selecionado e imediatamente substituído pelo que você digita (como o modo de seleção funciona na maioria dos outros editores).

Assim, você pode usar esse trio de mapeamentos para fazer uma localização / substituição muito rápida, que pode ser alterada no meio do arquivo, se necessário:

nnoremap § *``gn<C-g>
inoremap § <C-o>gn<C-g>
snoremap <expr> . @.

Mapeamento do modo normal : entra no modo de seleção com a palavra mais próxima do cursor selecionado.

Você pode digitar sua substituição para a palavra selecionada e ( sem pressionar Esc) usar o:

Inserir mapeamento do modo : Salta para a próxima correspondência e entra no modo de seleção.

Você pode digitar uma nova substituição ou pressionar .para usar o:

Mapeamento do modo de seleção : reinsere o último texto inserido - essencialmente, repete a última inserção.

Rico
fonte
Eu mencionei o gnmétodo no final da minha resposta ontem vi.stackexchange.com/a/13696/488 ontem pelo que vale a pena! Sua resposta tem uma formatação muito melhor.
TankorSmash
11
@TankorSmash Então você fez! Observe com certeza como eu perdi isso. Observe que o método real que estou usando é diferente do seu (porém, apenas invoco gnuma vez), e é o método específico (e não apenas o gncomando) que me surpreendeu ainda não ter sido mencionado.
Rich
Eu recomendaria ter algum tipo de plugin "visual star" para usar gn. Oferece mais opções de pesquisa. Pobre homem é mapeamento estrela visual xnoremap * :<c-u>let @/=@"<cr>gvy:let [@/,@"]=[@",@/]<cr>/\V<c-r>=substitute(escape(@/,'/\'),'\n','\\n','g')<cr><cr>. Episódio relacionado do Vimcasts: Operando em correspondências de pesquisa usando o gn
Peter Rincker 27/17
11
Eu criei o plugin sad.vim que ajuda com esse fluxo.
Hauleth
7

O plug-in https://github.com/terryma/vim-multiple-cursors cuida desse problema em sua maior parte.

Meu fluxo de trabalho geralmente é o seguinte:

  1. Mova o cursor para a palavra a substituir.
  2. Segure ctrl-naté que tudo esteja selecionado.
  3. Pressione ce comece a digitar a substituição.

O mais conveniente aqui é que você também pode executar operações mais complicadas do que apenas substituir a pesquisa, embora eu ache que, se você levar isso longe demais, o plug-in começará a mostrar algumas falhas.

Chiel ten Brinke
fonte
4

Outra maneira de fazer coisas que ainda não foram mencionadas: Você pode usar a janela da linha de comando (aberta q:no modo normal - veja :help q:para obter mais informações), na qual você tem acesso total aos recursos de preenchimento automático do Vim ( i_CTRL-N/ i_CTRL-Pe as várias i_CTRL-Xseqüências estar entre eles).

Na janela da linha de comando, você também pode usar comandos no modo normal para editar a linha de comando, o que, por exemplo, significa que você pode puxar e colar partes dos comandos anteriores no histórico da linha de comando.

Observe que o cursor será movido para a janela da linha de comando ao usá-lo, o que significa que CTRL-R CTRL-Wmétodos semelhantes que envolvem a inserção de texto sob o cursor não funcionarão lá, ao contrário da linha de comando normal.

Slade
fonte
11
Você também pode abrir a janela da linha de comando quando já estiver no meio da digitação do :substitutecomando pressionando<ctrl-f>
Rich
2

A solução para isso está entre as outras respostas para mim:

Digamos que você tenha 6 linhas idênticas e queira substituir 'apple' nas duas do meio:

I have apples for days
I have apples for days
I have apples for days
I have apples for days
I have apples for days
I have apples for days

O que eu faço é:

  1. Chego ao início do texto que quero substituir, 'maçã'
  2. Pressione vpara entrar no modo visual
  3. Desça até a última palavra que quero substituir, neste caso, algumas linhas para 2j
  4. :s/<C-R><C-W>/que usa a seleção visual como o intervalo para :se depois insere a palavra sob o cursor
  5. Eu acertei o <C-F>que coloca você em um modo em que você pode editar seu comando como se fosse um buffer. Assim, você pode usar qualquer conclusão ou plug-in que desejar escolher a palavra de substituição e pronto.

Tudo em uma linha, o fluxo para isso seria, /apples<CR>2nv/apples<CR>nn:s/<C-R><C-W/oranges<CR>mas parece pior do que é.

No seu caso, você pode anexar o /cao :scomando para ignorar os que você não quer, se o intervalo contém alguns que você quiser pular: :s/<C-R><C-W/orange/c.

Alternativamente (e talvez de forma mais simples), você poderia procurar por toda a palavra que você deseja substituir, change-lo, em seguida, gne .para repetir a pesquisa e aplicar a mesma alteração novamente. Parece /\<apple\><CR>viwcorange<ESC>gn.gn.gn..ngn.repeti-lo em três, pular uma instância e repeti-la uma última vez.

TankorSmash
fonte
O método que você descreve no final não funciona para mim (no Vim 7.4, invocado com vim -Nu NONE). Apenas apita quando pressiono o primeiro .. Alguma idéia do porquê disso? Em qualquer caso, seria mais simples de usar ciw, em vez de viwe nem vez de gnfazer alterações neste caminho.
Rich
2

Isso não está totalmente relacionado à questão, mas ainda deve ajudar a selecionar a palavra a ser substituída.

A pesquisa incremental ( set incsearch) permite digitar o prefixo da palavra que você precisa encontrar, e o vim salta automaticamente para a primeira ocorrência do prefixo à medida que você digita cada vez mais. A pesquisa será confirmada se você pressionar Enter e descartado em Esc (no último caso, o cursor voltará para onde estava antes da pesquisa começar).

No entanto, a pesquisa incremental fornece mais um recurso útil. Quando você já digitou o prefixo de uma palavra e o cursor está na posição correta, pressione <C-L>para adicionar caracteres da palavra sob o cursor à string de pesquisa. Isso permite que você insira a sequência substituída quase sem pressionar as teclas.

Então, se eu quiser substituir someOddVariableNamepor evenOdderVariableName, eu

  1. digite /someOddVe chegue à ocorrência de someOddVariableName;
  2. pressione e segure <C-L>até someOddVariableNameaparecer completamente na linha de pesquisa;
  3. pressione Enter para confirmar a pesquisa;
  4. :%s//evenOdderVariableName/gou qualquer comando de substituição necessário; você pode seguir outras respostas aqui.
Ivan Smirnov
fonte
2

Você pode colocar o nome da variável no buffer primeiro ( ywpara copiar uma palavra) e, em seguida, copie e cole-o :%spressionando <C-r>".

vdudouyt
fonte
2

Se você deseja substituir uma variável dentro do novo bloco (colado), tenho duas sugestões:

Substituição automática

  • Vá para a primeira linha do novo bloco, pressione ma. Isso está marcando um local e nomeando-o a.

  • Vá para a última linha do novo bloco, pressione mb. Agora você tem dois locais nomeados.

  • Agora substitua entre as duas linhas, assim: :'a,'bs/oldVarName/newVarName/g.

  • Ou seja, o escopo da substituição está entre suas duas marcas.

A maneira manual

  • Vá para a primeira linha do novo bloco.

  • Use /oldVarNamepara encontrar a primeira instância de oldVarName.

  • Altere da seguinte forma:, cwnewVarName<Esc>que mudará a palavra para newVarNamee pressionando escape.

  • Use npara ir para a próxima instância de oldVarName.

  • Use .para repetir a última alteração (ou seja, repete isso cw).

Hidden Guest
fonte
2

Muitas das outras respostas aqui já abordam as formas criadas para melhorar o fluxo de trabalho de localização e substituição, mas eu queria mencionar o abolish.vim , um plugin de Tim Pope. Faz substituições nas quais você deseja cobrir uma variedade maior de casos, normalmente causada por variações de caso ou porque precisa lidar com as formas singular e plural de uma palavra.

:S/child{,ren}/adult{,s}/gconverte corretamente childpara adult, Childrenpara Adultse CHILDpara ADULT.

Se você estiver lidando com palavras com camelo, precisará incluir as letras maiúsculas nas palavras original e de substituição.

%:S/wordWrap/textBreak/glida corretamente com ambos wordWrape wordwrap.

Haegin
fonte
0

Como outros já apontaram, se você tiver o cursor posicionado sobre sua palavra, poderá usar a família de comandos Ctrl + r, sendo Ctrl + r Ctrl + w provavelmente o mais adequado para uso.

Esse método, no entanto, funciona apenas para inserir uma palavra (a logo abaixo do cursor), mas e se houver várias partes do texto no buffer que queremos usar? Como você não pode mover o cursor enquanto estiver na linha de comando. A melhor maneira de fazer isso é copiar partes do texto necessárias em diferentes registros, o que pode ser feito com o prefixo "{reg_letter} (aspas e a letra do registro) no operador de cópia. Dessa forma, será possível insira várias palavras na linha de comando posteriormente com Ctrl + r {reg_letter} .Isso é um pouco estranho, mas funciona.

Mas voltando à idéia anterior, seria ótimo se movermos a posição do cursor sobre o buffer enquanto escrevíamos um padrão. Dessa forma, poderíamos "escolher" o texto de diferentes posições com Ctrl + r Ctrl + w, então criei um plug-in que faz exatamente isso: EXtend.vim O plug-in também possui muitos outros recursos para realizar operações de substituição.

Saul Axel Martinez Ortiz
fonte