Desenvolvendo Python com Neovim
Last updated on
Resolvi tentar mais uma vez arrumar um ambiente pra desenvolvimento com vim.
Eu sempre acabo desistindo quando preciso usar funcionalidades mais chatinhas de configurar, como setup de depurador, linters/formaters, busca de definições (classes e funções), essas coisas.
Como mudei de computador, aproveitei pra começar do zero, e dessa vez instalei o neovim, usando o template kickstart nvim.
Ele já vem com várias configurações prontas, tipo uso do tree-sitter e do lazy vim pra configuração rápida de plugins.
Pra que eu não precise voltar pro vscode, três coisas são fundamentais:
Navegar na codebase por referências, incluindo código fonte das bibliotecas
Esse é o fluxo de, no vscode, ir dando ctrl+clique nas classes e funções até encontrar a implementação. Eu faço isso com muita frequência e não faz sentido ter que sair do meu editor pra ir buscar o código fonte na internet.
Depurar projetos e suítes de teste
Projetos que usam frameworks como Flask, Django ou FastAPI tem formas específica de inicialização (
manage.py runserverouflask run app) e podem ter mais de um ponto de entrada, aceitar diferentes flags, etc. Também tenho que conseguir adicionar breakpoints em testes, geralmente compytest.Integração com docker
A maioria dos ambientes devs são distribuidos assim, e eu gostaria de conseguir plugar as funcionalidades do editor em arquivos que estão dentro dos containers, pra não precisar ficar instalando tudo localmente. Seria um equivalente ao “attach to container” do vscode, que funciona super bem.
Navegando na codebase
Essa foi a parte mais fácil, graças ao kickstart.nvim.
O que eu fiz foi incrementar as configurações padrões com alguns detalhes.
Configurar LSP pra Python
Isso é o que vai te permitir navegar através da codebase, tanto do seu código quanto das bibliotecas importadas.
Lá no init.lua, busque pela variável local servers, que define quais
ferramentas vão ser instaladas pelo Mason.
-- See `:help lsp-config` for information about keys and how to configure
---@type table<string, vim.lsp.Config>
local servers = {
-- clangd = {},
-- gopls = {},
pyright = {},
bashls = {},
No exemplo acima eu adicionei a lsp de Python (pyright) e de bash (bashls).
Depois de instalar o pyright, você vai precisar definir qual ambiente virtual o pyright deve usar de base. Sem configurar o ambiente virtual, você não vai conseguir usar o LSP pra navegar nas bibliotecas importadas.
Pra selecionar o ambiente virtual facilmente, tem um plugin chamado linux-cultist/venv-selector.nvim.
No seu init.lua, busque a parte que inicia a instalação de plugins (abertura da chamada ao lazyvim) e adicione:
-- NOTE: Here is where you install your plugins.
require('lazy').setup({
-- NOTE: Plugins can be added via a link or github org/name. To run setup automatically, use `opts = {}`
-- Instala o plugin de selecionar a venv para python
{
'linux-cultist/venv-selector.nvim',
cmd = 'VenvSelect',
opts = {
options = {
notify_user_on_venv_activation = true,
override_notify = false,
},
},
-- Call config for Python files and load the cached venv automatically
ft = 'python',
keys = { { '<leader>bpv', '<cmd>:VenvSelect<cr>', desc = 'Select VirtualEnv', ft = 'python' } },
},
Ali em <leader>bpv é onde eu defino como vou abrir o seletor de ambiente virtual:
pra mim ficou espaço + b + p + v. Você pode escolher qualquer combinação que
achar melhor.
Daí em qualquer projeto que você tenha um .venv instalado, é só usar a combinação e escolher o executável do python na caixinha que abrir.
Básico de navegação
Os comandos básicos pra navegar usando as LSPs são os seguintes:
- espaço g r d: pular pra definição. Use esse como você usaria o ctrl+clique no vscode.
- espaço g r i: pular pra implementação. Alternativa à opção anterior para linguagens que definem interfaces tipo TypeScript.
- espaço g r t: pular pra definição do tipo. Quando você quer ver onde o tipo (e não a variável) foi definido.
- ctrl+o (ou também ctrl+t): voltar pra onde você estava. Use pra voltar dos pulos que você deu conforme ia indo de definição em definição.
- espaço g r r: abre uma lista com todos os lugares onde essa palavra é utilizada
- espaço g O: abre uma listagem de todos os símbolos do documento atual. Bom pra buscar pelo nome de alguma variável.
- espaço g W: igual ao anterior, mas no projeto inteiro.
shift+k: preview de documentação
Visualização rápida de argumentos de função, documentação de classes, etc.
Boa adição aos comandos anteriores pra lembrar das definições sem mudar o contexto.
Procure por vim.api.nvim_create_autocmd('LspAttach', { no seu init.lua e
adicione o seguinte bloco:
vim.api.nvim_create_autocmd('LspAttach', {
group = vim.api.nvim_create_augroup('telescope-lsp-attach', { clear = true }),
callback = function(event)
local buf = event.buf
-- o seu init.lua vai ter mais várias coisas aqui
-- adicione em qualquer lugar dentro desse bloco :)
map('K', vim.lsp.buf.hover, 'Hover Documentation')
(wip) Uso do depurador
TODO: Escrever sobre o setup do nvim-dap e como fiz pra criar entrypoints customizados. Fazer uma comparação com o launch.json do vscode!
Lembrar do :MasonInstall debugpy.
Instalar parser de tree sitter para python (permite chamada do depurador para a função atual ou classe atual)
diff --git a/init.lua b/init.lua
index 31a2a60..8573ae3 100644
--- a/init.lua
+++ b/init.lua
@@ -872,7 +872,7 @@ require('lazy').setup({
branch = 'main',
-- [[ Configure Treesitter ]] See `:help nvim-treesitter-intro`
config = function()
- local parsers = { 'bash', 'c', 'diff', 'html', 'lua', 'luadoc', 'markdown', 'markdown_inline', 'query', 'vim', 'vimdoc' }
+ local parsers = { 'bash', 'c', 'diff', 'html', 'lua', 'luadoc', 'markdown', 'markdown_inline', 'query', 'vim', 'vimdoc', 'python' }
require('nvim-treesitter').install(parsers)
vim.api.nvim_create_autocmd('FileType', {
callback = function(args)
Integração com Docker
A estratégia é instalar o nvim dentro do container! Como ele funciona todo
dentro do terminal, você pode usar um docker exec pra dar ssh pra dentro do
container e fazer tudo por ali, sem precisar instalar nada na sua máquina.
O script abaixo que automatiza a instalação de tudo que é necessário no container.
Eu fiz ele pensando em um container rodando Debian, mas esses passos devem funcionar pra Ubuntu também.
Primeiro verifique qual a distro do container na qual você quer desenvolver:
docker exec -it meu_app bash
cat /etc/os-release | grep ID
Se não for Debian ou Ubuntu, recomendo dar uma analisada no script e adaptar conforme a necessidade (acho que o mais importante é mudar apt-get pro gerenciador de pacotes da sua distro - use a sessão Linux Install do kickstart.nvim como referência).
Salve o script como nvim-setup.sh e altere a variável CONTAINER_NAME pro
nome que estiver utilizando.
#!/bin/bash
# setup-nvim.sh by guites
# more info: https://guilhermegarcia.dev/blog/desenvolvendo-python-com-neovim
# glhf
set -eu
CONTAINER_NAME="meu_app"
docker exec -i "$CONTAINER_NAME" bash -seu <<'EOF'
# talvez você precise de sudo aqui caso seu usuário não seja root
apt-get update
if ! node -v >/dev/null 2>&1; then
echo "node não encontrado. Instalando via nvm..."
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
nvm install --lts
fi
# dependências para os pacotes básicos (LSPs, busca, uso do clipboard)
apt-get install -y make gcc ripgrep fd-find tree-sitter-cli unzip git xclip curl
# instalação do nvim
if ! nvim -v >/dev/null 2>&1; then
echo "nvim não encontrado. Instalando..."
curl -LO https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.tar.gz
rm -rf /opt/nvim-linux-x86_64
mkdir -p /opt/nvim-linux-x86_64
chmod a+rX /opt/nvim-linux-x86_64
tar -C /opt -xzf nvim-linux-x86_64.tar.gz
ln -sf /opt/nvim-linux-x86_64/bin/nvim /usr/local/bin/
rm nvim-linux-x86_64.tar.gz
grep -qxF "alias vim='nvim'" "$HOME/.bashrc" || echo "alias vim='nvim'" >> "$HOME/.bashrc"
fi
# instalação do kickstart.nvim
mkdir -p "$HOME/.config"
# se você possui seu próprio fork do kickstart.nvim,
# substitua "guites" pelo seu usuário do github
# se você quer usar a versão limpa sem as minhas alterações,
# substitua "guites" por "nvim-lua"
NVIM_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/nvim"
if [ ! -d "$NVIM_DIR/.git" ]; then
echo "configuração do nvim não encontrada. Inicializando via kickstart.nvim..."
rm -rf "$NVIM_DIR"
git clone https://github.com/guites/kickstart.nvim.git "$NVIM_DIR"
fi
EOF
Rode com bash nvim-setup.sh.
Depois você pode acessar o container via docker exec e
rodar o nvim lá dentro!
docker exec -it meu_app bash
nvim
Permitindo uso do clipboard
Isso permite copiar linhas de código direto do nvim e colar em outro lugar (por ex. ctrl + shift + v + y e depois ctrl + v num outro programa).
Não é essêncial mas ajuda a dar aquela sensação de que você está desenvolvendo localmente :).
O método pra sincronizar o clipboard do nvim com a sua máquina é através do xclip.
O xclip precisa ter acesso ao ambiente gráfico ($DISPLAY) da sua máquina, e
pra isso você vai ter que compartilhar esse acesso com o container do docker.
A forma mais prática que eu achei é criar um arquivo docker-compose.nvim.yml e adicionar um volume extra no serviço onde você vai abrir o nvim.
# arquivo docker-compose.nvim.yml
services:
meu_app:
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix
environment:
- DISPLAY=${DISPLAY}
Você precisa também permitir acesso de processos locais ao seu display com
xhost +local:
E daí pra acessar seu container:
# na hora de criar o container, usar o .yaml adicional
docker compose -f docker-compose.yml -f docker-compose.nvim.yml up meu_app -d
docker exec -it meu_app bash
Considerações finais
O maior benefício de usar o vim/neovim (imo) é a proximidade com o terminal.
Navegar com agilidade pelo código, sabendo onde as coisas estão (após decorar alguns
atalhos ¯\_(ツ)_/¯) facilita digerir grandes volumes de código e
criar um modelo mental correto do que está sendo desenvolvido.
Espero que o nvim te ajude a encontrar o equilibrio entre acessar informação com agilidade sem sobrecarregar o cérebro com milhares de abas e arquivos.
Eu mantenho a minha configuração sempre atualizada em github.com/guites/kickstart.nvim, caso você queria usar como referência.
Abraço!