Como mudar para o cwd do Netrw ao sair do Vim?

yxm

Yuri Ximenes Martins

Posted on September 21, 2023

Como mudar para o cwd do Netrw ao sair do Vim?

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:

  1. executar :shell no Vim para abrir um prompt, o qual iniciará no diretório B;
  2. executar :! cd path/to/B | cmd_Bash no Vim.

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:

  1. definir uma variável vim_cwd interna ao Vim que contém o working directory do Netrw;
  2. atualizar tal variável quando o buffer do Netrw é fechado;
  3. associar vim_cwd uma variável bash_cwd em Bash;
  4. configurar o Vim para que, ao ser fechado, seja executado o comando cd $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').
Enter fullscreen mode Exit fullscreen mode

Etapa 2

No Vim pode-se executar comandos internos ao se fechar um buffer adicionando-os como

autocmd BufDelete * your_command
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Aplicando à situação particular da etapa anterior, temos:

autocmd BufDelete * if &ft ==# 'netrw' | let g:vim_cwd = expand('%:p:h') | endif
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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:

  1. escrever o valor da variável vim_cwd em um arquivo;
  2. 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').
Enter fullscreen mode Exit fullscreen mode

Por sua vez, poderíamos obter bash_cwd através de

bash_cwd=$(cat /tmp/some_file)
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

Temos, no entanto, mais um problema:

  • o operador de substituição $() não funciona muito bem ao ser executado como um comando dentro do Vim!

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
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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
}
Enter fullscreen mode Exit fullscreen mode

Fim

Até mais,

Yuri.

💖 💪 🙅 🚩
yxm
Yuri Ximenes Martins

Posted on September 21, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related