Alterar nomes de variáveis ​​no Vim

96

Estou tentando ler muito código C / Perl por meio do Vim que contém muitos nomes de variáveis ​​de uma única letra.

Seria bom ter algum comando que pudesse me ajudar a mudar o nome de uma variável para algo mais significativo enquanto estou no processo de leitura do código para que eu pudesse ler o resto mais rápido.

Existe algum comando no Vim que poderia me permitir fazer isso rapidamente?

Não acho que regexes funcionariam porque:

  1. o mesmo nome de uma única letra pode ter diferentes finalidades em diferentes blocos de escopo

  2. a mesma combinação de letras pode ser parte de outro nome var maior, ou em uma string ou comentário ... não gostaria de modificá-los

Alguma solução conhecida?

Aditya Mukherji
fonte

Respostas:

198

Veja a seguir como renomear uma variável definida no escopo atual {}.

Mova o cursor para o uso variável. Pressione gd. O que significa - mova o cursor para a definição. Agora pressione [{- isso o levará ao início do osciloscópio. Pressione V- para ativar a seleção de Linha Visual. Pressione %- irá pular para o oposto, }portanto, irá selecionar todo o escopo. Pressione :s/- início do comando substituto. <C-R>/- irá inserir o padrão que corresponde ao nome da variável (aquele nome em que você estava antes de pressionar gd). /newname/gc<CR>- iniciará a pesquisa e substituirá pela confirmação em cada correspondência.

Agora você tem que gravar macros ou ainda melhor - mapear uma chave.

Aqui estão os mapeamentos finais:

" For local replace
nnoremap gr gd[{V%::s/<C-R>///gc<left><left><left>

" For global replace
nnoremap gR gD:%s/<C-R>///gc<left><left><left>

Coloque isso em seu .vimrcou apenas execute. Depois disso, pressionar gra variável local o levará ao :scomando, onde você simplesmente deve inserir new_variable_namee pressionar Enter.

Mykola Golubyev
fonte
40
+1 para aprender mais de 5 novos truques do Vim que eu deveria saber antes. Obrigado
Kenny Meyer
6
Nota: gd não funciona para localizar a variável no escopo imediato de definição no modo Javascript no macvim. Isso me leva ao primeiro uso do nome da variável em alguma função no arquivo :( Se você estiver dentro de algum escopo e quiser selecionar esse escopo, "va {" - "seleção visual ao redor {" - é o caminho a percorrer Eu acho.
Srikumar
3
Uma versão ligeiramente melhorada: gist.github.com/048616a2e3f5d1b5a9ad avisa o usuário, mostra o nome antigo, restaura a posição do cursor após a substituição
Andy Ray
7
+1 vimgineering muito bom (acho que acabei de cunhar um novo termo). Nota: Na verdade, eu precisava pressionar dois pontos ( :) duas vezes. Na primeira vez que apertei, vi :'<,'>. Se eu simplesmente digitar a s/partir daí, não funcionou; Tive que digitar outro dois pontos antes de s/.
Kelvin de
6
Observe que você não precisa <C-R>do comando substitute, simplesmente omitir a pesquisa fará com que o vim use o último padrão pesquisado, que neste caso foi criado a partir do gdcomando. Portanto, basta fazer :s//<newname>/gc.
shangxiao
10

AFAIK, não há suporte real de refatoração no VIM. Ao renomear com a intenção de refatorar, geralmente tomo as seguintes precauções:

  1. Limite o escopo da mudança ao usar marcas.
  2. Ao inserir a regex, coloque o nome entre colchetes \ <e>. Isso fará com que corresponda a uma palavra inteira, o que reduz os tipos de renomeações incorretas que ocorrerão.
  3. Não faça uma substituição de várias linhas para reduzir as chances de uma substituição incorreta
  4. Examine a diferença de código cuidadosamente se for algo diferente de uma pequena mudança.

Minha mudança final se parece com isso

:'a,'bs/\<foo\>/bar

Eu adoraria estar errado sobre não haver uma ferramenta de refatoração para o VIM, mas não vi isso.

JaredPar
fonte
Em Perl, você também pode adicionar o tipo ao padrão de pesquisa. ou seja, $ para escalares, @ para matrizes,% para hashes
Nathan Fellman
@Nathan: infelizmente, é um pouco mais difícil em Perl (5.X), já que o sigilo de matrizes e hashes muda com o uso:% hash -> hash inteiro, $ hash {key} -> valor único, @hash { fatia hash qw / ab /}.
usuário55400
9

Eu sei que é uma pergunta antiga, e o método de @ mykola-golubyev obviamente É a melhor resposta para o caso específico da pergunta de OP (que, presumo, está passando por um código ofuscado, em que é provável que você tenha vários blocos com os mesmos nomes de var) ; mas com o nome da pergunta como aquele, muitas pessoas que vêm aqui das pesquisas do google provavelmente procuram maneiras menos específicas da situação para renomear variáveis ​​no VIM - e essas podem ser mais concisas

Estou surpreso que ninguém sugeriu desta forma:

* :s// NOVO NOME /gc

O *é o mesmo que gn- ele procura a próxima ocorrência da palavra sob o cursor E se torna o último padrão pesquisado, portanto, quando você omite o padrão de pesquisa no comando substituto, o VIM assume que este é o padrão a ser pesquisado.

Para pequenas quantidades de cópias var, uma ainda mais rápida:

* cw NEWNAME <esc> então repita n.para outras ocorrências

Pesquisa por ocorrência, cwé o comando para alterar palavra , nvai para a próxima ocorrência do último termo pesquisado e .repete o último comando (que é alterar palavra para NEWNAME )

(Os créditos por saber de tudo isso vão para @doomedbunnies no Reddit )

Outro truque legal é este: ( créditos para @ nobe4 )

* cgn NEWNAME <esc> então repita .para outras ocorrências

cgné "alterar o resultado de (localizar a próxima ocorrência)". Agora que este é o último comando , você não precisa nir para a próxima ocorrência, portanto, menos golpes novamente e, mais importante, não há necessidade de alternar ne .. Mas, obviamente, este tem a desvantagem de não ter uma maneira de pular uma ocorrência.

Aqui estão alguns benefícios:

  • sem mapeamento, sem .vimrc (ou init.vim), então você pode usá-lo em qualquer cópia do VIM que encontrar (por exemplo, uma tarefa rápida em algum VPS ou na máquina de um amigo onde configurar o VIM do seu jeito iria contra o propósito de 'rápido' )
  • usar *ou gnpara a seleção de palavras é muito rápido - apenas um toque de tecla (bem, digamos 1,5)
  • usando *ou gngarante que você não obtenha nenhuma correspondência dentro de outras palavras, assim como :%s/<C-R>//gcfaz. Melhor do que digitar à :%s/\<OLDNAME\>/NEWNAME/gcmão: pessoalmente, tendo a esquecer de usar as \<coisas para limitar as correspondências apenas a palavras inteiras.
  • Não usar o escopo resultará apenas em alguns toques extras de npara pular combinações indesejadas - provavelmente ainda menos do que os toques extras necessários para limitar o escopo. Em circunstâncias normais, de qualquer maneira suas variáveis ​​provavelmente estão localizadas em um determinado bloco de código.
Ivan Bartsov
fonte
8

Coloque isso no seu .vimrc

" Function to rename the variable under the cursor
function! Rnvar()
  let word_to_replace = expand("<cword>")
  let replacement = input("new name: ")
  execute '%s/\(\W\)' . word_to_replace . '\(\W\)/\1' . replacement . '\2/gc'
endfunction

Ligue com :call Rnvar()

expand("<cword>")coloca a palavra sob o cursor. A string de pesquisa usa %para escopo de arquivo, e os \(\W\)padrões procuram por caracteres não-word no limite da palavra para substituir e salvá-los em variáveis \1e \2para serem reinseridos no padrão de substituição.

Mike
fonte
3

Você poderia usar o modificador 'c' na pesquisa global e substituir que solicitaria a confirmação de cada substituição. Levaria mais tempo, mas pode funcionar para um arquivo de código não enorme:

%s/\$var/\$foo/gc

O c significa confirmar.

Adnan
fonte
O i significa ignore-case. Você quer c, confirme todas as alterações.
Michael Kristofik
oops .. desculpe por isso .. Corrigida a resposta
Adnan
A resposta ainda não foi completamente corrigida;)
user55400
você quis dizer o $ escapado? Isso é algo que é uma preferência pessoal minha. $ funciona sem escape e também com e, uma vez que no mundo regex e em vi $ tem um significado especial, gosto de escapar dele apenas para maior clareza.
Adnan,
0

Em c, você pode fazer algum progresso usando cscope. Faz uma tentativa de entender a sintaxe, então teria uma chance de saber quando a letra era uma variável.

Kim Reece
fonte
0

Se isso ocorrer em vários arquivos, você pode considerar dar uma olhada no sed. Use find para pegar seus arquivos e xargs mais sed para uma substituição. Digamos que você queira substituir a por a_better_name em todos os arquivos correspondentes a * .c, você pode fazer

encontrar . -name "* .c" | xargs sed -i -e 's / a / a_better_name / g'

Lembre-se de que isso substituirá TODAS as ocorrências de a, então você pode querer uma regex mais robusta.

Drew Olson
fonte