DEV Community

Ellison Leão
Ellison Leão

Posted on • Edited on

5 4

Criando plugins Lua para Neovim - Parte 2

Um plugin para o mundo real


Na segunda parte da nossa série vamos criar um plugin que poderá ser usado no dia-a-dia, onde iremos introduzir algumas funcionalidades legais do Neovim, como as floating windows e algumas funções da API lua.

Vamos começar relembrando a estrutura de um plugin. Nosso novo plugin vai possuir a seguinte estrutura:

weather.nvim
├── lua
│  └── weather.lua
└── plugin
   └── weather.lua
Enter fullscreen mode Exit fullscreen mode

A idéia do plugin é mostrar a previsão do tempo atual em uma floating window, sendo chamado a partir de um comando.

Desenhando, vamos ter algo como:

weather.nvim

Estrutura do módulo

Nosso módulo weather.lua vai conter a seguinte estrutura:

local M = {}

local function create_command()
-- vamos criar o comando :Weather aqui
end

M.create_window = function()
-- aqui vamos criar a janela, um mapping para fechá-la e mostrar o tempo
end

M.close_window = function()
-- uma função para fechar a janela atual, que sera usada em um mapping
end

return M
Enter fullscreen mode Exit fullscreen mode

Analisando uma por uma, temos:

create_command()

A função create_command() vai criar nosso comando customizado (command!). Aqui introduzimos o vim.cmd, que pode ser utilizada para chamar comandos Ex nativos do vim, mais conhecidos como os comandos :.

defininos nosso comando como:

vim.api.nvim_create_user_command("Weather", M.create_window(), {nargs=0})
Enter fullscreen mode Exit fullscreen mode

com isso, nosso commando, :Weather, vai chamar diretamente uma função do nosso módulo lua para esse plugin

create_window()

a função create_window() vai ser responsável por criar uma floating window no canto superior direito da tela e mostrar o conteúdo do tempo. Para criá-la, precisamos seguir os seguintes passos:

  1. criar um buffer “descartável”, que será usado para o conteúdo da janela
  2. criar as configurações da janela (tamanho de linhas, colunas, posicao x e y na tela, bordas)
  3. chamar o comando que “abre” a janela
  4. criar um mapping para poder fechar a janela
  5. criar o conteúdo do buffer, no nosso caso, o tempo atual.

Para o passo 1, temos:

buf = vim.api.nvim_create_buf(false, true)
Enter fullscreen mode Exit fullscreen mode

Aqui temos outra novidade também, o vim.api , um conjunto de métodos para o neovim dentro do módulo lua vim. nvim_create_buf aceita 2 parâmetros, se o buffer vai ser listado ou não (no nosso caso não, por isso o false) e se ele será descartável ou não (no nosso caso sim, por isso o true). Para saber mais sobre o método, chame :help nvim_create_buf .

O método retorna o id do novo buffer criado, e a informação desse id é importante porque ele será usado para fazer referência em outras funções mais pra frente. Note aqui também que não estamos criando a váriavel buf dentro desse método, mas uma variável “global” do módulo lua que também será usada por outros métodos dentro desse módulo.

Para o passo 2, temos:

  local columns = vim.api.nvim_get_option("columns")
  local lines = vim.api.nvim_get_option("lines")
  local win_width = math.ceil(columns * 0.3 - 10)
  local win_height = math.ceil(lines * 0.3 - 6)
  local x_pos = 1
  local y_pos = columns - win_width

  local win_opts = {
    style = "minimal",
    relative = "editor",
    width = win_width,
    height = win_height,
    row = x_pos,
    col = y_pos,
    border = "single",
  }
Enter fullscreen mode Exit fullscreen mode

Primeiro pegamos as variáveis de total de linhas e colunas do buffer atual para fazer um cálculo proporcional do tamanho da nossa floating window.

  • relative="editor" é a opção que vai dizer que iremos usar as coordenadas x e y globais, relativas ao editor, tendo tamanho inicial (0,0) até (linhas-1, colunas-1)

  • style = "minimal", vai deixar nossa janela com configurações mínimas, removendo a maioria das opções de UI. Isso é essencial para janelas temporárias, onde não iremos precisar fazer nenhuma alteração.

  • width e height é o calculo proporcional do tamanho da janela, adicionando um padding para que ela não fique simplesmente “grudada” no canto superior direito da tela. Cada unidade de medida corresponde a um caracter

  • row e col vai setar a posição x e y da nossa janela

  • border vai passar a config da nossa borda da floating window, no caso single representa linhas simples

Para o passo 3, temos:

win = vim.api.nvim_open_win(buf, true, win_opts)
Enter fullscreen mode Exit fullscreen mode

Aqui chamamos a vim.api.nvim_open_win, o método que vai abrir nossa janela, usando o buffer que criamos, com a configuração que passamos. Nos parâmetros, podemos também ver o true, que vai setar a janela como a atual. Vamos precisar disso para chamar o comando que gera o conteúdo da mesma. Note também que nossa variável win também é “global”, pois vamos precisar da informação dela para criar os mappings que vão poder fechá-la.

Para o passo 4, temos:

vim.keymap.set("n", "q", M.close_window(), {noremap = true, silent = true, buffer = buf})
Enter fullscreen mode Exit fullscreen mode

Usamos a nova api introduzida no neovim 0.7 vim.keymap para criar um nnoremap local (somente para o buffer criado), que basicamente irá chamar outro método do nosso módulo, para fechar a janela. Iremos explicar o método close_window() mais pra frente.

Como parâmetros, primeiros temos o modo, no caso o modo Normal, depois o comando que esse mapping irá chamar e finalmente temos uma table com possíveis opções para o mapping, como noremap, silent, etc. No nosso caso, só queremos que o comando não coloque nenhum output na tela e não utilize nenhum outro mapping com a letra q, caso haja algum.

Finalmente para o passo 5, temos:

local command = "curl https://wttr.in/?0"
vim.fn.termopen(command)
Enter fullscreen mode Exit fullscreen mode

Aqui fazemos uma ponte entre lua e vimscript, chamando a função termopen, que basicamente abre um terminal emulado dentro do neovim com o resultado do comando curl

close_window()

A função close window no nosso caso só vai ser uma chamada direta para o nvim_win_close:

vim.api.nvim_win_close(win, true)
Enter fullscreen mode Exit fullscreen mode

Aqui passamos a variável win, que é o id da nossa janela criada e true fala que queremos forçar o fechamento da janela.

Conclusão

Com isso, nosso primeiro plugin “para o mundo real”, está pronto. A versão do código para esse post pode ser vista clicando aqui.

Para ver a versão completa do plugin, com mais customizações, clique aqui e já manda o star!. Para a parte 3 da nossa série, vamos mostrar como portar nossa config do neovim em vimscript totalmente para Lua. Não deixem de acompanhar!

Para ouvir

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay