Plugin para o Vim

A pouco tempo comecei a utilizar e tentar aprender a trabalhar com o Vi e conforme vou descobrindo suas funcionalidades uma coisa fica evidente, uma das melhores características dele é o suporte a plugins. E existem muitos, para diversas funções. Alguns testei por um tempo, outros já não deixo de usar de maneira nenhuma.
E vendo essa diversidade de plugins e com tanta gente fazendo e utilizando acabei me interessando bastante, e quis aprender um pouco mais. E pra começar a entender nada melhor que fazer um plugin simples que me ajudaria com uma atividade corriqueira do trabalho.

A motivação

No dia a dia, muitas vezes me deparo com mensagens do protocolo FIX, que possui um formato de pares chave e valor, separados por um ‘=’ e esses pares separados por um caractere ASCII 01. Normalmente preciso destes valores transformados em um formato mais fácil de ler e com os códigos numéricos dos campos traduzidos para os nomes, pois com frequência preciso visualizar essas mensagens extraídas de logs de aplicação. Até aí eu poderia desenvolver algum aplicativo pra quebrar a mensagem e ver os valores, mas por motivos de aprendizado preferi fazer um plugin pro vi, que é onde costumo abrir esses arquivos de log.

O plugin

O primeiro passo foi procurar material sobre desenvolvimento de plugin pro vi, o que acreditava não ser tão difícil pois o Vi e a grande maioria desses plugins são de código aberto. E foi mais fácil que imaginava, o próprio vi e tem no seu sistema de ajuda, que é ótimo por sinal, uma seção dedicada a criação de plugins. Mas aqui não vou traduzir a página de ajuda, mas sim comentar sobre o que mais precisei e o que achei de diferença para as linguagens que eu conheço.

O código

Sintaxe

A sintaxe da VimL é bem familiar para qualquer desenvolvedor que já tenha trabalhado com bash, C, javascript. Com a diferença sendo a interação com os buffers, janelas e barra de comandos do Vi. Como o código fica parecido com essas linguagens já elimina um choque cultural logo de início.

1
2
3
if a == 'a'
let b = 0
endif

Definindo escopos

Já com o escopo das variáveis e das funções, isso muda.
Para variáveis:

  • Para definir uma variável global é necessário prefixar com g: 
  • para uma com escopo apenas do script é necessário prefixar com s: 
  • uma variável que seja passada como parâmetro para uma função tem que ser utilizado o prefixo a: mas nesse ultimo caso somente no momento do acesso a variável.
    E para funções é possível separara-las em diversos arquivos e subdiretórios. Para identificar isso basta concaternar o nome do arquivo e caminho com # nas chamadas
    1
    call fix44dict#loadFixDictionary()

Comandos e Funções

Uma coisa que é necessário diferenciar ao programar em VimL é que existe uma diferença entre comando do editor e funções da VimL e geralmente um plugin acaba utilizando os dois. Isso quer dizer, que algumas coisas que um usuário de vi faz no editor não necessariamente é feito da mesma maneira no plugin.
Esses foram os pontos de atenção que eu encontrei, isso fazendo um script bem simples. Com certeza fazendo coisas mais interessantes mais peculiaridades apareçam. E para ilustrar o que falei acima vamos a alguns trechos de código.
Para manter um padrão que vi nos fontes de outros Plugins, um pequeno cabeçalho:

1
2
3
" fixprotocol.vim - converts FIX to xml
" Maintainer: Vitor Appolinario
" Version: 0.1

logo de cara uma guarda no estilo #ifdef para evitar carregar o plugin mais de uma vez:

1
2
3
4
5
"checks if plugin is already loaded or is in compatibility mode
if exists("g:vim_fixprotocol_loaded") || &cp
finish
endif
let g:vim_fixprotocol_loaded = 1

Algumas variáveis globais para que o usuário possa ajustar alguns parâmetros do plugin para a necessidade dele:

1
2
3
4
5
6
7
8
" checks if user has configured the protocol version
if !exists('g:fixprotocol_fixversion')
let g:fixprotocol_fixversion = '42'
endif
" checks if user has configured the xml root tag
let g:fixprotocol_root_tag = 'FIXMESSAGE'
if !exists('g:fixprotocol_root_tag')
endif

Uma função que carrega o dicionario FIX de acordo com a versão escolhida pelo usuário, notando que os dicionários ficam em outros dois arquivos separados e precisei usar o nome do arquivo na chamada da função:

1
2
3
4
5
6
7
8
" loads a map with the fix dictionary according with version chosen
function! fixprotocol#initDictionary()
if g:fixprotocol_fixversion == '44'
call fix44dict#loadFixDictionary()
else
call fix42dict#loadFixDictionary()
endif
endfunction

Aqui uma função que usa o dicionario para traduzir o código do campo para o nome do campo reparando que os parâmetros são definidos apenas com os nomes das variáveis, mas o seu uso é prefixado o a:

1
2
3
4
5
" get a xml tag with field name and value
function! fixprotocol#getXmlString(field, value)
let name = fixprotocol#getFieldName(a:field)
return '<' . name . '>' . a:value . '</' . name . '>'
endfunction

E no fim a função que realmente faz a transformação dos campos em XML. Sendo a parte de tratamento da mensagem apenas uma expressão regular. Aqui podemos ver uma combinação de comando do editor :substitute com funções da VimL submatch) e uma função do script getFieldName.

1
2
3
4
5
6
7
8
9
" converts the current line fom fixmessage to Xml
function! fixprotocol#toXml()
" add the opening xml tag
call append(line(".")-1, '<' . g:fixprotocol_root_tag . '>')
" transform the msg into xml
:substitute /\([^=]\+\)=\([^=]\+\)\(\%x01\)/\=fixprotocol#getXmlString(submatch(1), submatch(2))/g
" adds the closing xml tag
call append(line("."), '</' . g:fixprotocol_root_tag . '>')
endfunction

E ao final criamos um novo comando para o editor

1
2
" add the command for command bar
command! -bar FixToXml call fixprotocol#toXml()

No fim de tudo temos um plugin bem simples, utilizando alguns recursos do vim e VimL.

Conclusão

Criar um plugin simples para o Vi não é tão complicado, apenas precisa se familiarizar com os comandos e um sintaxe um pouco diferente do habitual. Obviamente para tarefas mais complexas é necessário entender melhor e conhecer mais comandos do editor, mas o sistema de ajuda está ai para isso.Não abordei aqui o uso de outras linguagens como Python e Ruby que são bastante utilizadas para o desenvolvimento de scripts para o Vi pois queria focar apenas no editor e suas funcionalidades. 
O código fonte para este plugin esta disponível neste repositório: vim-fixprotocol