Introdução
Eu gosto de estruturas minimalistas, e esse é um dos motivos pelos quais eu utilizo Vim como meu editor de texto. Desde a versão 5.5, o Vim vem de fábrica com um plugin que permite navegar no terminal através de arquivos e pastas de maneira visual e sem precisar ficar dando cd e ls toda hora. Trata-se do Netrw.
Muita gente não o utiliza, sendo o Nerdtree seu principal substituto. Uma das desvantagens do Netrw é que, para que funcione de maneira aceitável, um punhado de configurações são necessárias. No entanto, seguindo a filosofia do minimalismo, não vejo nisso argumento forte o suficiente para instalar um plugin a mais.
Em momento oportuno farei uma publicação sobre minhas configurações para o Netrw. Hoje, no entanto, gostaria de trazer a solução de um problema que me \ ,incomodava há algum tempo.
O Problema
Suponhamos que você abriu seu Netrw e começou a navegar por diretórios dentro do Vim. Rapidamente você sai de um diretório A e chega em um diretório distante B. Suponhamos, agora, que você quer executar algum comando de Bash (digamos cmd_Bash) estando no diretório B. Utilizando apenas de um ferramental builtin, tem-se algumas opções:
- executar
:shellnoVimpara abrir um prompt, o qual iniciará no diretórioB; - executar
:! cd path/to/B | cmd_BashnoVim.
Suponha, ainda, que você já não mais precisa do Vim e quer fazer coisas no terminal no entorno do diretório B. Nesse caso, você sairá do Vim. Seria muito interessante se o seu working directory do terminal estivesse sincronizado com o working directory do Netrw. Pois, assim, ao fechar o Netrw no diretório B seu terminal estaria já posicionado ali.
O problema é que, no entanto, tal sincronização não existe: alterações em buffers do Vim só se aplicam ao Vim.
A Solução Errada
A estratégia imediata de solução ao problema seria a seguinte:
- definir uma variável
vim_cwdinterna aoVimque contém o working directory doNetrw; - atualizar tal variável quando o buffer do
Netrwé fechado; - associar
vim_cwduma variávelbash_cwdemBash; - configurar o
Vimpara que, ao ser fechado, seja executado o comandocd $bash_cwd.
As etapas 1. e 2. são tranquilas, como segue.
Etapa 1
No Netrw o símbolo % representa o arquivo em uso, p representa "tomar o full path de", ao passo que h representa "pegar o diretório de". Assim, pode-se obter o diretório de um arquivo aberto através de expand('%:p:h'). Em um buffer do Netrw isso se refere exatamente ao working directory. Para salvá-lo em uma variável global vim_cwd, basta um
let g:vim_cwd = expand('%:p:h').
Etapa 2
No Vim pode-se executar comandos internos ao se fechar um buffer adicionando-os como
autocmd BufDelete * your_command
Para se restringir a um dado buffer, concatena-se com um if &ft ==# 'your_buffer'. No nosso caso, o buffer é netrw, de modo que ficamos com
autocmd BufDelete * if &ft ==# 'netrw' | your_command | endif
Aplicando à situação particular da etapa anterior, temos:
autocmd BufDelete * if &ft ==# 'netrw' | let g:vim_cwd = expand('%:p:h') | endif
Etapa 4
Assim como existe uma maneira de executar comandos internos ao Vim quando buffers são fechados, pode-se executá-los quando o próprio Vim é fechado. A sintaxe é a seguinte:
autocmd VimLeave * your_command
Por outro lado, pode-se executar comandos de Bash como comandos de Vim através de execute '!your_bash_command'. Portanto, o seguinte código executa um comando em Bash ao se fechar o Vim:
autocmd VimLeave * execute '!your_bash_command'
Etapa 3
É no ato de transformar uma variável interna ao Vim em uma variável de Bash (etapa 3) que a estratégia anterior falha. Até onde vai meu conhecimento, não existe uma maneira de se fazer isso diretamente dentro do Vim sem se recorrer a um script de Bash.
A solução Correta
Como contornar a situação? Ao invés de transformar diretamente uma variável vim_cwd do Vim em uma variável bash_cwd do Bash, podemos fazer isso em passos:
- escrever o valor da variável
vim_cwdem um arquivo; - ler tal arquivo em uma variável de
Bash.
Isso sim funciona, pois o Vim admite uma função chamada writefile() que permite exatamente a escrita de uma variável em um arquivo. Em nosso caso, temos:
writefile([g:vim_cwd], '/tmp/some_file').
Por sua vez, poderíamos obter bash_cwd através de
bash_cwd=$(cat /tmp/some_file)
Juntando tudo, a solução ao nosso problema seria, então, dada pelo seguinte código:
autocmd BufDelete * if &ft ==# 'netrw' | let g:vim_cwd = expand('%:p:h') | call writefile([g:vim_cwd], '/tmp/some_file') | endif
autocmd VimLeave * execute '!bash_cwd=$(cat /tmp/some_file) | cd $bash_cwd'
Temos, no entanto, mais um problema:
- o operador de substituição
$()não funciona muito bem ao ser executado como um comando dentro doVim!
A alternativa é pedir que o comando cd $(cat /tmp/some_file) seja executado não dentro do Vim, mas diretamente em Bash. Isso pode ser feito redefinindo o comando vim substituindo-o por uma função vim():
function vim(){
command vim "$@"
if [[ -f /tmp/some_file ]]; then
bash_cwd=$(cat /tmp/some_file)
cd $bash_cwd
rm /tmp/some_file
else
return
fi
}
Conclusão
Se você quer que ao fechar o Vim seu terminal esteja no último diretório acessado pelo Netrw, faça o seguinte:
- em seu
.vim/vimrcadicione
autocmd BufDelete * if &ft ==# 'netrw' | let g:vim_cwd = expand('%:p:h') | call writefile([g:vim_cwd], '/tmp/some_file') | endif
- em seu
.bashrcadicione
function vim(){
command vim "$@"
if [[ -f /tmp/some_file ]]; then
bash_cwd=$(cat /tmp/some_file)
cd $bash_cwd
rm /tmp/some_file
else
return
fi
}
Fim
Até mais,
Yuri.
Top comments (0)