Yuri Ximenes Martins
Posted on September 21, 2023
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
:shell
noVim
para abrir um prompt, o qual iniciará no diretórioB
; - executar
:! cd path/to/B | cmd_Bash
noVim
.
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_cwd
interna aoVim
que contém o working directory doNetrw
; - atualizar tal variável quando o buffer do
Netrw
é fechado; - associar
vim_cwd
uma variávelbash_cwd
emBash
; - configurar o
Vim
para 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_cwd
em 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/vimrc
adicione
autocmd BufDelete * if &ft ==# 'netrw' | let g:vim_cwd = expand('%:p:h') | call writefile([g:vim_cwd], '/tmp/some_file') | endif
- em seu
.bashrc
adicione
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.
Posted on September 21, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.