Avançar para a área de conteúdo

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

A primeira vez que acessar o developerWorks, um perfil será criado para você. Informações do seu perfil (tais como: nome, país / região, e empresa) estarão disponíveis ao público, que poderá acompanhar qualquer conteúdo que você publicar. Seu perfil no developerWorks pode ser atualizado a qualquer momento.

Todas as informações enviadas são seguras.

  • Fechar [x]

Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

Todas as informações enviadas são seguras.

  • Fechar [x]

Criando Scripts do Editor Vim, Parte 5: Criação de scripts e automação movidos a eventos

Automatize o seu fluxo de trabalho com os comandos automáticos do Vim

Damian Conway, Dr., CEO and Chief Trainer, Thoughtstream
author photo - damian conway
Damian Conway é um Professor Associado Adjunto de Ciência da Computação na Monash University, Austrália, e CEO da Thoughtstream, uma empresa de treinamento de TI internacional. Ele é um usuário diário do vi há mais de vinte e cinco anos e quase não tem esperanças de acabar com essa compulsão.

Resumo:  Por que se repetir? É possível configurar o modelo de eventos abrangente do Vim para executar scripts que poupam tempo sempre que determinados eventos de edição — como carregar um arquivo ou alternar entre modos de editor — ocorrem. Este artigo, o quinto de uma série, descreve como os eventos funcionam no Vim, explora uma seleção de tipos de evento úteis e, em seguida, mostra como começar a anexar scripts específicos a determinados eventos. O resultado final é um fluxo de trabalho mais automatizado, configurado de forma precisa para as suas necessidades.

Visualizar mais conteúdo nesta série

Data:  14/Abr/2011
Nível:  Introdutório Também disponível em :   Inglês
Atividade:  2098 visualizações
Comentários:  


Modelo de eventos do Vim

As funções de edição do Vim se comportam como se fossem movidas a eventos. Por motivo de desempenho, a implementação real é mais complexa do que isso, com uma boa parte do tratamento de eventos otimizada à distância ou tratada várias camadas abaixo do loop de eventos em si, mas mesmo assim é possível considerar o editor como um loop while simples que responde a uma série de eventos de edição.

Sempre que você inicia uma sessão do Vim, abre um arquivo, edita um buffer, altera o modo de edição, alterna janelas ou interage com o sistema de arquivos circundante, você está colocando na fila, com eficiência, um evento que o Vim recebe e trata imediatamente.

Por exemplo: se você inicia o Vim, edita um arquivo chamado demo.txt, alterna para o modo Insert, digita um texto, salva o arquivo e, em seguida, sai, a sessão do Vim recebe uma série de eventos como a que é mostrada na Listagem 1.


Listagem 1. Sequência de eventos em uma sessão de edição simples do Vim

> vim
    BufWinEnter     (create a default window)
    BufEnter        (create a default buffer)
    VimEnter        (start the Vim session)
:edit example.txt
    BufNew          (create a new buffer to contain demo.txt)
    BufAdd          (add that new buffer to the session’s buffer list)
    BufLeave        (exit the default buffer)
    BufWinLeave     (exit the default window)
    BufUnload       (remove the default buffer from the buffer list)
    BufDelete       (deallocate the default buffer)
    BufReadCmd      (read the contexts of demo.txt into the new buffer)
    BufEnter        (activate the new buffer)
    BufWinEnter     (activate the new buffer's window)
i
    InsertEnter     (swap into Insert mode)
Hello
    CursorMovedI    (insert a character)
    CursorMovedI    (insert a character)
    CursorMovedI    (insert a character)
    CursorMovedI    (insert a character)
    CursorMovedI    (insert a character)
<ESC>
    InsertLeave     (swap back to Normal mode)
:wq
    BufWriteCmd     (save the buffer contents back to disk)
    BufWinLeave     (exit the buffer's window)
    BufUnload       (remove the buffer from the buffer list)
    VimLeavePre     (get ready to quit Vim)
    VimLeave        (quit Vim)

O mais interessante é que o Vim fornece "ganchos" que permitem interceptar qualquer um desses eventos de edição. Portanto, é possível fazer com que um determinado comando ou função do Vimscript seja executado sempre que um evento específico ocorrer: sempre que o Vim inicia, um arquivo é carregado, você sai do modo Insert… ou até mesmo sempre que você move o cursor. Isso possibilita incluir comportamentos automáticos praticamente em qualquer ponto do editor.

O Vim fornece notificações referentes a 78 eventos de edição diferentes, que se enquadram em oito categorias amplas: eventos de inicialização e limpeza de sessão, eventos de leitura de arquivos, gravação de arquivos, mudança no buffer, definição de opções, eventos relacionados a janelas, eventos de interação com o usuário e notificações assíncronas.

Para ver a lista completa de eventos, digite :help autocmd-events na linha de comando do Vim. Para ver descrições detalhadas de cada evento, consulte :help autocmd-events-abc.

Este artigo explica como os eventos funcionam no Vim e, em seguida, apresenta uma série de scripts para automatizar comportamentos e eventos de edição.


Tratamento de eventos com comandos automáticos

O mecanismo que o Vim fornece para interceptar eventos é conhecido como comando automático. Cada comando automático especifica o tipo de evento a ser interceptado, o nome do arquivo editado no qual esses eventos devem ser interceptados e a ação do modo de linha de comando a ser realizada quando o evento é detectado. A palavra-chave referente a tudo isso é autocmd (frequentemente abreviado como au). A sintaxe usual é:

autocmd  EventName  filename_pattern   :command

O nome do evento é um dos 78 nomes de evento válidos do Vim (listados em :help autocmd-events). A sintaxe padrão do nome do arquivo é semelhante — mas não idêntica — a um padrão de shell normal (consulte :help autocmd-patterns para ver os detalhes). O comando é qualquer comando válido do Vim, inclusive chamadas a funções Vimscript. O ponto e vírgula no início do comando é opcional, mas é recomendável incluí-lo; ao fazer isso, fica mais fácil localizar o comando na lista de argumentos (geralmente complexa) de um autocmd.

Por exemplo: você poderia abrir mão do que resta da sua dignidade e especificar um manipulador de eventos para o evento FocusGained acrescentando o seguinte ao seu arquivo .vimrc :

autocmd  FocusGained  *.txt   :echo 'Welcome back, ' . $USER . '! You look great!'

Os eventos FocusGained são colocados na fila sempre que uma janela do Vim se torna o foco de entrada do sistema window — portanto, sempre que você alterna de volta para a sessão do Vim, se você está editando qualquer arquivo cujo nome corresponde ao padrão de nome do arquivo *.txt, o Vim executa automaticamente o comando echo especificado.

É possível configurar quantos manipuladores você quiser para o mesmo evento, e todos eles serão executados na sequência em que foram especificados originalmente. Por exemplo: uma automação muito mais útil para os eventos FocusGained pode ser uma breve ênfase da linha do cursor sempre que você alterne de volta para a sessão de edição, como mostra a Listagem 2.


Listagem 2. Uma automação útil para eventos FocusGained

autocmd  FocusGained  *.txt   :set cursorline
autocmd  FocusGained  *.txt   :redraw
autocmd  FocusGained  *.txt   :sleep 1
autocmd  FocusGained  *.txt   :set nocursorline

Esses quatro comandos automáticos fazem com que o Vim destaque automaticamente a linha que contém o cursor (set cursorline), mostre esse destaque (redraw), espere um segundo (sleep 1) e, em seguida, desative o realce (set nocursorline).

É possível usar qualquer série de comando dessa forma; é possível até mesmo dividir uma única estrutura de controle ao longo de diversos comandos automáticos. Por exemplo: é possível definir uma variável global (g:autosave_on_focus_change) para controlar um mecanismo de "salvamento automático" que grava automaticamente qualquer arquivo .txt modificado sempre que o usuário vai da janela do Vim para outra janela (fazendo com que seja colocado na fila o evento FocusLost ):


Listagem 3. Comando automático para salvamento automático ao sair de uma janela do editor

autocmd  FocusLost  *.txt   :    if &modified && g:autosave_on_focus_change
autocmd  FocusLost  *.txt   :    write
autocmd  FocusLost  *.txt   :    echo "Autosaved file while you were absent" 
autocmd  FocusLost  *.txt   :    endif

Os comandos automáticos multilinhas como esse requerem que você repita a especificação essencial do seletor de eventos (ou seja, FocusLost *.txt) diversas vezes. Por isso, a manutenção deles, de forma geral, é desagradável e esses comandos estão mais propensos a erros. É muito mais claro e seguro fatorar qualquer estrutura de controle, ou outras sequências de comandos, em uma função separada e, em seguida, fazer com que um único comando automático chame essa função. Por exemplo:


Listagem 4. Uma forma mais limpa de tratar comandos automáticos multilinhas

function! Highlight_cursor ()
    set cursorline
    redraw
    sleep 1
    set nocursorline
endfunction
function! Autosave ()
   if &modified && g:autosave_on_focus_change
       write
       echo "Autosaved file while you were absent" 
   endif
endfunction

autocmd  FocusGained  *.txt   :call Highlight_cursor()
autocmd  FocusLost    *.txt   :call Autosave() 

Comandos automáticos universais e de arquivo único

Até agora, todos os exemplos mostrados restringiram o tratamento de arquivos aos arquivos que correspondem ao padrão *.txt. Obviamente, isso significa que é possível usar qualquer padrão de mascaramento de arquivos para especificar os arquivos aos quais um comando automático específico se aplica. Por exemplo: é possível fazer com que o comando automático anterior de destaque do cursor, FocusGained , se aplique a qualquer arquivo usando simplesmente o padrão universal de correspondência de arquivos * como o filtro de nome do arquivo:

" Cursor-highlight any file when context-switching ...
autocmd  FocusGained  *          :call Highlight_cursor()

Como alternativa, é possível restringir os comandos a um único arquivo:

" Only cursor-highlight for my .vimrc ...
autocmd  FocusGained  ~/.vimrc   :call Highlight_cursor()

Observe que isso também significa que é possível especificar comportamentos diferentes para o mesmo evento, dependendo do arquivo que está sendo editado. Por exemplo: quando o usuário volta a atenção para outro lugar, é possível optar pelo salvamento automático dos arquivos de texto ou por fazer com que os scripts de Perl ou Python passem por um ponto de verificação, ao passo que um arquivo de documentação pode ser instruído a reformatar o parágrafo atual, como mostra a Listagem 5.


Listagem 5. O que fazer quando a atenção do usuário está voltada para outro lugar

autocmd  FocusLost  *.txt   :call Autosave()
autocmd  FocusLost  *.p[ly] :call Checkpoint_sourcecode()
autocmd  FocusLost  *.doc  :call Reformat_current_para()

Grupos de comandos automáticos

Os comandos automáticos têm um mecanismo associado de namespace que lhes permite ser agrupados em grupos de comandos automáticos, de onde podem ser manipulados coletivamente.

Para especificar um grupo de comandos automáticos, é possível usar o comando augroup . A sintaxe geral do comando é:

augroup GROUPNAME
    " autocommand specifications here ...
augroup END

O nome do grupo pode ser qualquer série de caracteres que não sejam espaços em branco, com exceção de "end" ou "END”, que são reservados para especificar o fim de um grupo.

Dentro de um grupo de comandos automáticos, é possível colocar qualquer número de comandos automáticos. Tipicamente, os comandos são agrupados de acordo com o evento ao qual respondem, como mostra a Listagem 6.


Listagem 6. Definindo um grupo para comandos automáticos que respondem a eventos de FocusLost

augroup Defocus
    autocmd  FocusLost  *.txt   :call Autosave()
    autocmd  FocusLost  *.p[ly] :call Checkpoint_sourcecode()
    autocmd  FocusLost  *.doc   :call Reformat_current_para()
augroup END

Também é possível agrupar uma série de comandos automáticos relacionados a um único tipo de arquivo, tal como:


Listagem 7. Definindo um grupo de comandos automáticos para tratar de arquivos de texto

augroup TextEvents 
    autocmd  FocusGained  *.txt   :call Highlight_cursor()
    autocmd  FocusLost    *.txt   :call Autosave()
augroup END

Desativando comandos automáticos

É possível remover manipuladores de eventos específicos usando o comando autocmd! (ou seja, com um ponto de exclamação). A sintaxe geral desse comando é:

autocmd!  [group]  [EventName [filename_pattern]]

Para remover um único manipulador de eventos, especifique todos os três argumentos. Por exemplo: para remover o manipulador para eventos deFocusLost em arquivos .txt a partir do grupo Unfocussed , use:

autocmd!  Unfocussed  FocusLost  *.txt

Em vez de um nome de evento específico, é possível usar um asterisco para indicar que todos os tipos de eventos referentes ao grupo e padrão de nome de arquivo devem ser removidos. Se você quisesse remover todos os eventos referentes a arquivos .txt dentro do grupo Unfocussed , usaria:

autocmd!  Unfocussed      *      *.txt

Se você não inclui o padrão de nome do arquivo, todo manipulador referente ao tipo de evento especificado é removido. É possível remover todos os manipuladores de FocusLost do grupo Unfocussed desta forma:

autocmd!  Unfocussed  FocusLost

Se você também deixa de fora o nome do evento, todos os manipuladores de eventos do grupo especificado são removidos. Portanto, para desativar toda a manipulação de eventos especificada no grupo Unfocussed :

autocmd!  Unfocussed

Finalmente, se você omite o nome do grupo, a remoção de comandos automáticos se aplica ao grupo ativo no momento. Tipicamente, essa opção é usada como "preparação para a ação" dentro de um grupo antes de configurar uma série de comandos automáticos. Por exemplo: o grupo Unfocussed é mais bem especificado desta forma:


Listagem 8. Certificando-se de que um grupo esteja vazio antes de incluir novos comandos automáticos

augroup Unfocussed
    autocmd!

    autocmd  FocusLost  *.txt   :call Autosave()
    autocmd  FocusLost  *.p[ly] :call Checkpoint_sourcecode()
    autocmd  FocusLost  *.doc   :call Reformat_current_para()
augroup END

A inclusão de um autocmd! ao início de cada grupo é importante porque os comandos automáticos não declaram estaticamente os manipuladores de eventos; esses comandos os criam dinamicamente. Se você executa o mesmo autocmd duas vezes, você obtém dois manipuladores de eventos. Ambos serão chamados separadamente pela mesma combinação de evento e nome do arquivo daquele ponto em diante. Iniciando cada grupo de comandos automáticos com autocmd!, você limpa todos os manipuladores já existentes no grupo para que as instruções subsequentes de autocmd substituam os manipuladores já existentes, em vez de aumentá-los. Isso, por sua vez, significa que o seu script pode ser executado tantas vezes quantas forem necessárias (do contrário, o .vimrc pode sofrer source repetidamente) sem multiplicar desnecessariamente as entidades de manipulação de eventos.

Alguns exemplos práticos

O uso adequado dos comandos automáticos pode facilitar muito a edição. Vamos ver algumas formas de usar comandos automáticos para otimizar o processo de edição e remover as frustrações já existentes.

Gerenciando edições simultâneas

Um dos recursos mais úteis do Vim é detectar automaticamente as tentativas de editar um arquivo que está sendo editado por outra instância do Vim. Isso acontece frequentemente em ambientes com várias janelas, nos quais você já está editando um arquivo em outro terminal, ou em configurações multiusuário, nas quais outra pessoa já está trabalhando em um arquivo compartilhado. Quando o Vim detecta uma segunda tentativa de editar um arquivo específico, você obtém a seguinte solicitação:

Swap file ".filename.swp" already exists!
[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: _

Dependendo do ambiente no qual você está trabalhando, é provável que escolha automaticamente uma dessas opções todas as vezes, sem muito pensamento consciente da sua parte. Por exemplo: se você raramente trabalha em arquivos compartilhados, é provável que selecione q para terminar a sessão e, em seguida, procure a janela do terminal na qual você já está editando o arquivo. Por outro lado, se você costuma editar recursos compartilhados, talvez você esteja condicionado a pressionar imediatamente <ENTER> para selecionar a opção padrão e abrir o arquivo como somente leitura.

Entretanto, com os comandos automáticos, é possível eliminar totalmente a necessidade de ver, reconhecer e responder à mensagem, automatizando a resposta ao evento SwapExists que a aciona. Por exemplo: se você não quer editar nunca os arquivos que já estão sendo editados em outro lugar, é possível incluir o seguinte ao seu .vimrc:


Listagem 9. Encerrando automaticamente em caso de edições simultâneas

augroup NoSimultaneousEdits
    autocmd!
    autocmd  SwapExists  *  :let v:swapchoice = 'q'
augroup END

Essa ação configura um grupo de comandos automáticos e remove os manipuladores anteriores (por meio do comando autocmd! ). Em seguida, instala um manipulador para o evento SwapExists em qualquer arquivo (usando o padrão do arquivo universal: *). Esse manipulador simplesmente designa a resposta "q" para a variável especial v:swapchoice . O Vim consulta essa variável antes de exibir a mensagem "swapfile exists". Se a variável tiver sido configurada, ela usa o valor como resposta automática e não se dá ao trabalho de mostrar a mensagem. Agora, você não verá nunca a mensagem swapfile ; a sessão do Vim encerra automaticamente se você tenta editar um arquivo que está sendo editado em outro lugar.

Como alternativa, se você prefere abrir sempre os arquivos já editados no modo somente leitura, é possível alterar simplesmente o grupo NoSimultaneousEdits para:


Listagem 10. Automatizando o acesso somente leitura a arquivos já existentes

augroup NoSimultaneousEdits
    autocmd!
    autocmd  SwapExists  *  :let v:swapchoice = 'o'
augroup END

O mais interessante é que é possível fazer um arranjo para selecionar entre essas duas alternativas (ou quaisquer outras) com base no local do arquivo em questão. Por exemplo: você pode preferir encerrar automaticamente os arquivos nos seus próprios subdiretórios, mas abrir os arquivos compartilhados em /dev/shared/ como somente leitura. É possível fazer isso da seguinte forma:


Listagem 11. Automatizando uma resposta sensível ao contexto

augroup NoSimultaneousEdits
    autocmd!
    autocmd  SwapExists  ~/*            :let v:swapchoice = 'q'
    autocmd  SwapExists  /dev/shared/*  :let v:swapchoice = 'o'
augroup END

Ou seja, se o nome do arquivo integral começa com o diretório inicial, sem mais nada depois (~/*), selecione previamente o comportamento de "encerrar", mas se o nome do arquivo integral começa com o diretório compartilhado (/dev/shared/*), selecione previamente o comportamento "somente leitura".

Formatando automaticamente o código de forma consistente

O Vim tem um bom suporte para o layout automático do código no tempo de edição (consulte :help indent.txt e :help filter). Por exemplo: é possível ativar as opções 'autoindent' e do 'smartindent' e fazer com que o Vim recue novamente os blocos de códigos automaticamente conforme você digita. Também é possível vincular o seu reformatador de código específico da linguagem ao comando = padrão configurando a opção 'equalprg' .

Infelizmente, o Vim não tem um comando ou opção para lidar com uma das situações mais comuns de formatação de código: ser obrigado a ler o código terrivelmente mal formatado de outra pessoa. Especificamente, não há nenhuma opção integrada para instruir o Vim a arrumar automaticamente qualquer arquivo de código que você abrir.

Não há problema, porque é muito fácil configurar um comando automático para fazer isso.

Por exemplo: é possível incluir o seguinte comando automático ao seu .vimrc, para que os arquivos de C, Python, Perl e XML sejam executados automaticamente por meio do formatador de código adequado sempre que você abrir um arquivo do tipo correspondente, como mostra a Listagem 12.


Listagem 12. Código bonito, em um comando automático

augroup CodeFormatters
    autocmd!

    autocmd  BufReadPost,FileReadPost   *.py    :silent %!PythonTidy.py
    autocmd  BufReadPost,FileReadPost   *.p[lm] :silent %!perltidy -q
    autocmd  BufReadPost,FileReadPost   *.xml   :silent %!xmlpp –t –c –n
    autocmd  BufReadPost,FileReadPost   *.[ch]  :silent %!indent
augroup END

Todos os comandos automáticos do grupo são idênticos em termos de estrutura — a diferença está apenas nas extensões de nome do arquivo às quais eles se aplicam e na impressora de "impressão elegante" correspondente que chamam.

Observem que os comandos automáticos não nomeiam um único evento a ser tratado. Em vez disso, cada um especifica uma lista de eventos. Qualquer autocmd pode ser especificado com uma lista separada por vírgula de tipos de evento; nesse caso, o manipulador será chamado para todos os eventos listados.

Nesse caso, os eventos listados para cada manipulador são BufReadPost (que é colocado na fila sempre que um arquivo já existente é carregado em um novo buffer) e FileReadPost (que é colocado na fila imediatamente depois da execução de qualquer comando :read ). Frequentemente, esses dois eventos são especificados juntos porque, combinados, eles cobrem as formas mais comuns de carregar o conteúdo de um arquivo já existente em um buffer.

Depois da lista de eventos, cada comando automático especifica o(s) sufixo(s) de arquivo a que ele se aplica: .py de Python, .pl de Perl e .pm, .xmlde XML ou os arquivos .c e .h de C. Observe que, assim como acontece com os eventos, esses padrões de nome do arquivo também podem ter sido especificados como uma lista separada por vírgula, e não como um padrão único. Por exemplo: o manipulador de Perl poderia ter sido escrito:

autocmd  BufReadPost,FileReadPost   *.pl,*.pm   :silent %!perltidy -q

ou o manipulador de C poderia ser estendido para tratar também de variantes comuns de C++ (.C, .cc, .cxx, etc.) como:

autocmd  BufReadPost,FileReadPost   *.[chCH],*.cc,*.hh,*.[ch]xx  :silent %!indent

Como sempre, o componente final de cada comando automático é o comando a ser executado. Em cada caso, é um comando de filtro global (%!filter_program), que toma todo o conteúdo do arquivo (%) e o canaliza (!) para o programa externo especificado (um destes: PythonTidy.py, perltidy, xmlpp(indent). Em seguida, a saída de cada programa é colada de volta no buffer, substituindo o conteúdo original.

Normalmente, quando são usados comandos de filtro como esse, o Vim exibe automaticamente uma notificação após a conclusão do comando, desta forma:

42 lines filtered
Press ENTER or type command to continue_

Para evitar esse aborrecimento, cada comando automático prefixa a sua ação com um :silent, que neutraliza as mensagens de erro sem importância, mas permite a exibição de mensagens de erro.

Formatação automática de código oportunista

Vim tem um suporte excelente para a formatação automática do código de C conforme você o digita, mas oferece menos suporte para outras linguagens. Essa falha não pode ser totalmente atribuída ao Vim; a formatação dinâmica correta de algumas linguagens — como o Perl — pode ser extremamente difícil.

Se o Vim não oferece suporte adequado para a formatação automática do código de origem na sua linguagem favorita, é fácil fazer com que o seu editor chame um utilitário externo para fazer isso para você.

A abordagem mais simples é usar o evento InsertLeave . Esse evento é colocado na fila sempre que você sai do modo Insert (mais frequentemente, logo depois de pressionar<ESC>). É fácil configurar um manipulador que reformata o seu código sempre que você termina de incluir elementos nele, desta forma:


Listagem 13. Invocando o PerlTidy depois de cada edição

function! TidyAndResetCursor ()
    let cursor_pos = getpos('.')
    %!perltidy -q
    call setpos('.', cursor_pos)
endfunction

augroup PerlTidy
    autocmd!
    autocmd InsertLeave *.p[lm]  :call TidyAndResetCursor()
augroup END

A função TidyAndResetCursor() primeiro registra a posição do cursor no momento armazenando as informações de cursor retornadas pelo getpos() integrado à variável cursor_pos. Em seguida, executa o utilitário externo perltidy em todo o arquivo (%!perltidy -q) e, para terminar, restaura a posição original do cursor, passando as informações salvas do cursor para a função integrada setpos() .

Em seguida, dentro do grupo PerlTidy , você só configura um comando automático que chama o TidyAndResetCursor() sempre que o usuário sai do modo Insert dentro de qualquer arquivo Perl.

Esse mesmo padrão de código pode ser adaptado para realizar qualquer ação adequada cada vez que você insere texto. Por exemplo: se você estivesse trabalhando em um sistema pouco confiável e quisesse maximizar a capacidade de recuperar arquivos (consulte :help usr_11.txt) em caso de erro, seria possível configurar o Vim para atualizar o seu arquivo de troca sempre que você sair do modo Insert , desta forma:

augroup UpdateSwap
    autocmd!
    autocmd  InsertLeave  *  :preserve
augroup END

Colocando registro de data e hora nos arquivos

O grupo de eventos BufWritePre, FileWritePre e FileAppendPre. também é muito útil. Esses eventos "Pre" são colocados na fila imediatamente antes de a sessão do Vim gravar um buffer no disco (como resultado de um comando como :write, :update(:saveas). O evento BufWritePre ocorre imediatamente antes da gravação do buffer inteiro; o FileWritePre ocorre imediatamente antes da gravação de uma parte do buffer (ou seja, quando você especifica um intervalo de linhas para ser gravado: :1,10write). O FileAppendPre ocorre imediatamente antes que um comando :write seja usado para incluir, em vez de substituir; por exemplo:

:write >> logfile.log). 

Em todos os três tipos de eventos, o Vim configura os aliases especiais de número da linha '[ e '] para o intervalo de linhas que está sendo gravado. Em seguida, esses aliases podem ser usados no especificador de intervalo de qualquer comando subsequente, para garantir que as ações de comando automático sejam aplicadas somente às linhas relevantes.

Tipicamente, você configuraria um único manipulador que cobre todos os três tipos de evento pré-gravação. Por exemplo: você pode fazer com que o Vim atualize automaticamente um registro de data e hora interno sempre que um arquivo seja gravado (ou incluído) no disco, como mostra a Listagem 14.


Listagem 14. Atualizando automaticamente um registro de data e hora interno sempre que um arquivo é salvo

function! UpdateTimestamp ()
    '[,']s/^This file last updated: \zs.*/\= strftime("%c") /
endfunction

augroup TimeStamping
    autocmd!

    autocmd BufWritePre,FileWritePre,FileAppendPre  *  :call UpdateTimestamp()
augroup END

A função UpdateTimestamp() faz uma substituição (s/.../.../) em todas as linhas que estão sendo gravadas, limitando especificamente o intervalo da substituição ao que fica entre '[ e '] desta forma: '[,']s/.../.../. A substituição procura linhas que começam com "This file last updated:”, seguido por qualquer coisa (.*). O \zs antes do .* faz com que a substituição finja que a correspondência só começou depois do ponto e vírgula, para que somente o registro de data e hora seja substituído.

Para atualizar o registro de data e hora, a substituição usa a sequência especial \= escape no texto de substituição. Essa sequência de escape instrui o Vim a tratar o texto de substituição como uma expressão do Vimscript, avaliando-a para obter a cadeia de caracteres real de substituição. Nesse caso, essa expressão é uma chamada à função integrada strftime() , que retorna uma cadeia de caracteres padrão de registro de data e hora com este formato: "Fri Oct 23 14:51:01 2009". Em seguida, essa cadeia de caracteres é gravada na linha de registro de data e hora pelo comando de substituição.

Só falta configurar um manipulador de eventos autocmd) para todos os três tipos de evento (BufWritePre,FileWritePre,FileAppendPre) em qualquer arquivo (*) e fazer com que ele chame a função adequada de registro de data e hora (:call UpdateTimestamp()). Agora, sempre que um arquivo é gravado, qualquer registro de data e hora presente nas linhas que estão sendo salvas será atualizado para o horário atual.

Observe que o Vim oferece outros dois conjuntos de eventos que podem ser usados para modificar o comportamento das operações de gravação. Para automatizar algumas ações que devem acontecer depois de uma gravação, é possível usar BufWritePost, FileWritePost e FileAppendPost. Para substituir completamente o comportamento padrão de gravação pelo seu script, é possível usar BufWriteCmd, FileWriteCmde FileAppendCmd (mas consulte :help Cmd-event primeiro para ver algumas advertências importantes).

Registros de data e hora movidos a tabelas

Logicamente, é possível criar mecanismos muito mais elaborados para tratar de arquivos com convenções diferentes de registro de data e hora. Por exemplo: é possível preferir especificar as várias assinaturas de registro de data e hora e as suas substituições em um dicionário do Vim (consulte o artigo anterior desta série) e, em seguida, fazer um loop em cada par para determinar como o registro de data e hora deve ser atualizado. Essa abordagem é mostrada na Listagem 15.


Listagem 15. Registros de data e hora automáticos movidos por tabelas

let s:timestamps = {
\  'This file last updated: \zs.*'             :  'strftime("%c")',
\  'Last modification: \zs.*'                  :  'strftime("%Y%m%d.%H%M%S")',
\  'Copyright (c) .\{-}, \d\d\d\d-\zs\d\d\d\d' :  'strftime("%Y")',
\}

function! UpdateTimestamp ()
    for [signature, replacement] in items(s:timestamps)
        silent! execute "'[,']s/" . signature . '/\= ' . replacement . '/'
    endfor
endfunction

Aqui, o loop for faz uma iteração em cada par de assinatura/substituição de registro de data e hora no dicionário s:timestamps , desta forma:

for [signature, replacement] in items(s:timestamps)

Em seguida, ele gera uma cadeia de caracteres que contém o comando de substituição correspondente. O comando de substituição a seguir é idêntico, em termos de estrutura, ao comando do exemplo anterior, mas aqui foi construído interpolando o par de assinaturas/substituição em uma cadeia de caracteres:

"'[,']s/" . signature . '/\= ' . replacement . '/'

Finalmente, ele executa o comando gerado silenciosamente:

silent! execute "'[,']s/" . signature . '/\= ' . replacement . '/'

O uso de silent! é importante porque garante que as substituições que não correspondem não terão como resultado a mensagem de erro irritante Pattern not found .

Observe que a última entrada em s:timestamps é um exemplo particularmente útil: ele atualiza automaticamente o intervalo de anos de qualquer aviso de copyright integrado sempre que um arquivo que os contém é gravado.

Registros de data e hora movidos a nome de arquivo

Em vez de listar todos os possíveis formatos de registro de data e hora em uma única tabela, é possível que você prefira parametrizar a função UpdateTimestamp() e, em seguida, criar uma série de autocmds distintos para tipos de arquivos diferentes, como mostra a Listagem 16.


Listagem 16. Registros de data e hora sensíveis ao contexto para tipos de arquivo diferentes

function! UpdateTimestamp (signature, replacement)
    silent! execute "'[,']s/" . a:signature . '/\= ' . a:replacement . '/'
endfunction

augroup Timestamping
    autocmd!

    " C header files use one timestamp format ...
    autocmd BufWritePre,FileWritePre,FileAppendPre  *.h
        \ :call UpdateTimestamp('This file last updated: \zs.*', 'strftime("%c")')

    " C code files use another ...
    autocmd BufWritePre,FileWritePre,FileAppendPre  *.c
        \ :call UpdateTimestamp('Last update: \zs.*', 'strftime("%Y%m%d.%H%M%S")')
augroup END

Nesta versão, os componentes de assinatura e substituição são passados explicitamente para UpdateTimestamp()o qual, em seguida, gera uma cadeia de caracteres que contém o único comando de substituição correspondente e o executa. Dentro do grupo Timestamping , você configura comandos automáticos individuais para cada tipo de arquivo necessário, passando a assinatura de registro de data e hora adequada e o texto de substituição para cada uma.

Invocando diretórios

Os comandos automáticos podem ser úteis até mesmo antes de você começar a editar. Por exemplo: ao começar a editar um novo arquivo, você ocasionalmente vê uma mensagem como esta:

"dir/subdir/filename" [New DIRECTORY] 

Isso significa que o arquivo que você especificou (neste caso, filename) não existe e que o diretório onde ele deveria estar (neste caso dir/subdir) também não existe.

O Vim permite que você ignore esse aviso (muitos usuários nem reconhecem que se trata de um aviso) e continuam a editar o arquivo. No entanto, quando você tenta salvá-lo, obterá a seguinte mensagem de erro, que não ajuda muito:

"dir/subdir/filename" E212: Can't open file for writing.

Agora, para salvar o seu trabalho, é necessário criar explicitamente o diretório ausente antes de gravar o arquivo nele. É possível fazer isso de dentro do Vim, desta forma:

:write
"dir/subdir/filename" E212: Can't open file for writing.
:call mkdir(expand("%:h"),"p")
:write

Aqui, a chamada à função integrada expand() é aplicada a "%:h", onde % significa o caminho de arquivo atual (neste caso, dir/subdir/filename) e o :h toma somente a "cabeça" desse caminho, removendo o nome do arquivo para sair do caminho do diretório pretendido (dir/subdir). A camada da função integrada do Vim mkdir() , em seguida, toma esse caminho de diretório e cria todos os diretórios temporários ao longo do mesmo (conforme a solicitação do segundo argumento, "p").

Entretanto, de forma realista, é mais provável que a maioria dos usuários do Vim recorram ao shell para construir os diretórios necessários. Por exemplo:

:write
"dir/subdir/filename" E212: Can't open file for writing.
:! mkdir -p dir/subdir/
:write

De uma forma ou de outra, é uma chatice. Se, no fim das contas, você terá que criar o diretório ausente, porque não fazer com que o Vim perceba logo de início que ele não existe e o crie mesmo antes de você começar? Dessa forma, você nunca obterá a dica enigmática[New DIRECTORY] e o seu fluxo de trabalho não será interrompido mais tarde por um erro igualmente misterioso E212 .

Para fazer com que o Vim se encarregue de construir previamente os diretórios não existentes, é possível colocar um manipulador no evento BufNewFile , que é colocado na fila sempre que você começa a editar um arquivo que não existe. A Listagem 17 mostra o código que você incluiria no arquivo .vimrc para fazer isso funcionar.


Listagem 17. Criando automaticamente de forma incondicional diretórios não existentes

augroup AutoMkdir
    autocmd!
    autocmd  BufNewFile  *  :call EnsureDirExists()
augroup END
function! EnsureDirExists ()
    let required_dir = expand("%:h")
    if !isdirectory(required_dir)
        call mkdir(required_dir, 'p')
    endif
endfunction

O grupo AutoMkdir configura um comando automático único para eventos BufNewFile em qualquer tipo de arquivo, chamando a função EnsureDirExists() sempre que um novo arquivo é editado. EnsureDirExists() primeiro determina o diretório que está sendo solicitado, expandindo a "cabeça" do caminho de arquivo atual: expand("%:h"). Em seguida, ele usa a função integrada isdirectory() para verificar se o diretório solicitado existe. Se não existe, ele tenta criar o diretório usando o mkdir() integrado do Vim.

Observe que, se a chamada a mkdir() não conseguir criar o diretório solicitado por algum motivo, ela produzirá uma mensagem de erro ligeiramente mais precisa e informativa:

E739: Cannot create directory: dir/subdir

Invocando diretórios com mais cuidado

O único problema dessa solução é que, ocasionalmente, a criação automática de subdiretórios não existentes não é a coisa certa a fazer. Por exemplo: suponha que você tenha solicitado o seguinte:

> vim /share/sites/corporate/root/.htaccess

Você tinha a intenção de criar um novo arquivo de controle de acesso no subdiretório já existente /share/corporate/website/root/. Entretanto, obviamente, já que o caminho estava errado, você na verdade criou um novo arquivo de controle de acesso no subdiretório (que antes não existia) /share/website/corporate/root/. E, como isso aconteceu automaticamente, sem nenhum tipo de aviso, é possível que você nem perceba o erro. Pelo menos até que o controle de acesso mal aplicado provoque algum desastre online.

Para se proteger contra erros desse tipo, talvez você prefira que o Vim ajude um pouco menos na criação automática de diretórios ausentes. A Listagem 18 mostra uma versão mais elaborada de EnsureDirExists(), que continua detectando diretórios ausentes, mas agora pergunta ao usuário o que deve fazer com eles. Observe que a configuração do comando automático é exatamente igual à da Listagem 17; somente a função EnsureDirExists() mudou.


Listagem 18. Criando automaticamente de forma condicional diretórios não existentes

augroup AutoMkdir
    autocmd!
    autocmd  BufNewFile  *  :call EnsureDirExists()
augroup END
function! EnsureDirExists ()
    let required_dir = expand("%:h")
    if !isdirectory(required_dir)
        call AskQuit("Directory '" . required_dir . "' doesn't exist.", "&Create it?")

        try
            call mkdir( required_dir, 'p' )
        catch
            call AskQuit("Can't create '" . required_dir . "'", "&Continue anyway?")
        endtry
    endif
endfunction

function! AskQuit (msg, proposed_action)
    if confirm(a:msg, "&Quit?\n" . a:proposed_action) == 1
        exit
    endif
endfunction

Nesta versão da função, EnsureDirExists() localiza o diretório necessário e detecta se ele existe, exatamente como antes. Entretanto, se o diretório está ausente, agora o EnsureDirExists() chama uma função auxiliar: AskQuit(). Essa função usa a função integrada confirm() para perguntar se você quer sair da sessão ou criar o diretório automaticamente. "Quit?" é apresentado como a primeira opção, que se torna também o padrão caso você simplesmente pressione <ENTER>.

Se você seleciona a opção "Quit?", a função auxiliar termina a sessão do Vim imediatamente. Do contrário, a função auxiliar simplesmente retorna. Nesse caso, EnsureDirExists() continua executando e tenta chamar mkdir().

Observe, entretanto, que a chamada a mkdir() agora está dentro de uma construção try...endtry . Isso é — como você pode esperar — um manipulador de exceções, que agora capturará o erro E739 que ocorre se mkdir() não consegue criar o diretório solicitado.

Quando esse erro ocorre, o bloco catch o interceptará e chamará AskQuit() novamente, informando a você que não foi possível criar o diretório e perguntando se você ainda quer continuar. Para ver mais detalhes sobre os mecanismos amplos de manipulação de exceções do Vim, consulte: :help exception-handling.

O efeito geral dessa segunda versão de EnsureDirExists() é destacar o diretório não existente, mas ela requer que você solicite explicitamente que seja criado (digitando um único ‘c’ quando houver a solicitação para fazer isso). Se não é possível criar o diretório, você é avisado novamente e tem a opção de continuar com a sessão mesmo assim (novamente, digitando um único 'c' quando for pedido). Dessa forma, fica muito mais fácil escapar de uma edição equivocada (simplesmente pressionando <ENTER> para selecionar a opção padrão "Quit?" quando for solicitado).

Obviamente, você pode preferir que a opção padrão seja continuar; nesse caso, você só mudaria a primeira linha de AskQuit() para:

if confirm(a:msg, a:proposed_action . "\n&Quit?") == 2 

Nesse caso, a ação proposta seria a primeira alternativa e, portanto, o comportamento padrão. Note que "Quit?" agora é a segunda alternativa — portanto, agora a resposta deve ser comparada com o valor 2.

Visão de futuro

Os comandos automáticos podem poupar bastante esforço e evitar erros automatizando ações repetitivas que, de outra maneira, você teria que realizar. Uma forma produtiva de começar é dar um passo atrás (mentalmente) conforme você edita e ficar atento a padrões de uso repetitivos que podem ser automatizados sutilmente usando os mecanismos de manipulação de eventos do Vim. A criação de scripts para esses padrões em comandos automáticos pode dar um pouco mais de trabalho no começo, mas as ações automatizadas compensarão esse investimento todos os dias. Automatizando as ações do dia a dia, você poupa tempo e esforço, evita erros, otimiza o seu fluxo de trabalho, elimina pequenos aborrecimentos e, consequentemente, melhora a sua produtividade.

Embora os seus comandos automáticos provavelmente comecem como simples automações com uma única linha, é possível que em breve você possa reprojetá-las e deixá-las mais elaboradas, à medida que pensa em formas melhores de fazer com que o Vim faça uma parte maior do seu trabalho repetitivo. Dessa forma, os seus manipuladores de eventos podem ficar cada vez mais inteligentes, seguros e adaptados ao seu modo de trabalhar.

Entretanto, à medida que os scripts de Vim como esses se tornam mais complexos, você precisará de ferramentas melhores para gerenciá-los. O fato de acrescentar 10 ou 20 linhas ao seu .vimrc sempre que você um mapeamento de teclas ou comando automático novo e inteligente acabará produzindo um arquivo de configuração com milhares e milhares de linhas... cuja manutenção fica impossível.

Portanto, no próximo artigo desta série, abordaremos a arquitetura simples de plug-ins do Vim, que permite fatorar partes do .vimrc e isolá-las em módulos separados. Veremos como esse sistema de plug-ins funciona, desenvolvendo um módulo independente que ameniza alguns dos horrores do trabalho com XML.


Recursos

Aprender

Obter produtos e tecnologias

  • Comece pela página de downloads das distribuições do Vim a fim de atualizar para a versão mais recente do Vim para a sua plataforma.

  • Avalie os produtos IBM da maneira que for melhor para você: faça download da versão de teste de um produto, avalie um produto online, use-o em um ambiente de nuvem ou passe algumas horas na SOA Sandbox aprendendo a implementar Arquitetura Orientada a Serviços de modo eficiente.

Discutir

  • Participe do comunidade do My developerWorks. Entre em contato com outros usuários do developerWorks e explore os blogs, fóruns, grupos e wikis voltados para desenvolvedores.

Sobre o autor

author photo - damian conway

Damian Conway é um Professor Associado Adjunto de Ciência da Computação na Monash University, Austrália, e CEO da Thoughtstream, uma empresa de treinamento de TI internacional. Ele é um usuário diário do vi há mais de vinte e cinco anos e quase não tem esperanças de acabar com essa compulsão.

Ajuda para Relatar Abuso

Relatar abuso

Obrigado. Esta entrada foi sinalizada para atenção do moderador.


Ajuda para Relatar Abuso

Relatar abuso

Falha no envio do Relatório de abuso. Tente novamente mais tarde.


developerWorks: Registre-se


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos de uso do developerWorks.

 


Na primeira vez que você efetua sign in no developerWorks, um perfil é criado para você. Informações selecionadas do seu perfil developerWorks são exibidas ao público, mas você pode editá-las a qualquer momento. Seu primeiro nome, sobrenome (a menos que escolha ocultá-los), e seu nome de exibição acompanharão o conteúdo que postar.

Selecione seu nome de exibição

Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

(Deve possuir de 3 a 31 caracteres.)


Ao clicar em Enviar, você concorda com os termos de uso do developerWorks.

 


Classificar este artigo

Comentários

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Linux
ArticleID=647325
ArticleTitle=Criando Scripts do Editor Vim, Parte 5: Criação de scripts e automação movidos a eventos
publish-date=04142011
author1-email=damian@conway.org
author1-email-cc=