Site do Guilherme

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:

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:

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!

#cli #português #python #vim