Não se repita (DRY): use macros no Vim

@ 2011-12-28 by João Paulo Pizani Flor

Meu último post aqui no blog foi há MUITO tempo atrás, eu sei, mas eu ainda estou vivo, acreditem! ☺ E hoje o assunto do post vai ser um pouco mais técnico: vou falar sobre as poderosas macros do Vim! Quem me conhece sabe muito bem que eu admiro – e uso bastante – o editor de texto Vim.

Então… estava eu programando umas coisas em Java há uns dias, usando vim, e tive a necessidade de substituir vários blocos de código similares mas não idênticos por um nome de variável que eu tinha recém-fatorado. Eram blocos de código bem grandes que tinham que ser substituídos, com variações sutis, então um simples find/replace não seria suficiente…

Eu comecei tentando usar o tradicional truque da tecla “.”: Eu substituí o primeiro bloco pela variável, e então prossegui selecionando cada bloco subsequente e apertando “.”. Era eficiente, mas não tanto quanto eu precisava. Ia demorar demais para que eu andasse por todo o arquivo até substituir os mais de 200 blocos de código. Então percebi que havia chegado a hora de usar um recurso do vim que eu tinha conhecido só recentemente: macros!

As macros do vim são um mecanismo bastante genérico: você começa a gravar uma macro e então tudo que você faz – cada movimento, cada edição tanto em modo normal quanto em modo insert, visual, etc. – é capturado. Então você pode dar replay na macro e os comandos são repetidos exatamente como você os fez. Isso pode ser muito útil, muita gente tende a subestimar a utilidade gigante desse recurso.

Eu espero ilustrar um pouquinho dessa utilidade com o meu exemplo da “vida real”. Aqui vai um dos blocos de código que eu estava mexendo, antes e depois da transformação que eu precisava fazer:

// <antes>
put(bnome_en, new ValidationRule() {
    protected ValidationResult check(String userInput) {
        if(isEmpty(userInput)) return ValidationError.empty;
        else return intact(userInput);
    }
});
// <depois>
put(bnome_en, BaseRules.nonEmptyRule);

Depois de experimentar um pouco, eu descobri que o que eu precisava fazer era mudar tudo que estava entre new e }) (o parênteses que fechava a classe) para BaseRules.emptyRule. Então meus comandos foram:

Para começar a gravar uma macro no vim, você digita q<letra-minuscula>, e a macro vai ser armazenada no registro de nome <letra-minuscula>. Você vai precisar se lembrar dessa letra quando quiser dar replay na macro mais tarde. A minha sequência exata de teclas pressionadas para gravar a macro foi a seguinte:

qn/new&lt;CR&gt;v/})&lt;CR&gt;cBaseRules.nonEmpty&lt;ESC&gt;jddq

onde <CR> significa Enter e <ESC> significa Escape. Note que eu armazenei a macro no registro n, e que no final da minha sequência de comandos eu pressionei novamente a tecla q para terminar a gravação. Agora tudo o que eu precisava era clicar em cima de um bloco de código e digitar:

@n

Ou seja, a tecla arroba @, seguida do nome do registro onde a macro está armazenada. E BOOM! Funcionou muito bem. Então eu ganhei confiança e digitei:

50@n

Isso significa que eu executei a macro repetidamente 50 vezes. Funcionou e foi muito bonito ☺ Conclusão: macros no vim são úteis e fáceis de usar; elas até fazem sentido! Então, caro futuro usuário, novato ou avançado do vim, sempre que você perceber que está repetindo uma tarefa e ela é mais complexa que um find/replace, aperte q e comece a gravar uma macro :)

Dica importante: uma macro é completamente insensível a contexto, ou seja, exatamente os mesmos comandos (inclusive os mesmos deslocamentos) vão ser realizados no replay, não importa a região onde você executar a macro. O segredo então é você utilizar de recursos como busca (“/”) durante a gravação para ter certeza que está no “lugar certo” onde fazer a edição…