Criando plugins Lua para Neovim - Parte 2
Tags: neovim, vim, plugins, lua
Published: 2022-05-09 17:19:46

> 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
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9nz7gum7mq8jj6o3wmq0.png)

## Estrutura do módulo

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

```lua
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
```

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:

```lua
vim.api.nvim_create_user_command("Weather", M.create_window(), {nargs=0})
```

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:

```lua
buf = vim.api.nvim_create_buf(false, true)
```

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:
```lua
  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",
  }
```

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:

```lua
win = vim.api.nvim_open_win(buf, true, win_opts)
```

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:

```lua
vim.keymap.set("n", "q", M.close_window(), {noremap = true, silent = true, buffer = buf})
```

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:

```lua
local command = "curl https://wttr.in/?0"
vim.fn.termopen(command)
```

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`:

```lua
vim.api.nvim_win_close(win, true)
```

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](https://github.com/ellisonleao/criando-plugins-lua-neovim/tree/master/parte-2).

Para ver a versão completa do plugin, com mais customizações, clique [aqui](https://github.com/ellisonleao/weather.nvim) 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

{% embed https://youtu.be/oKz-YAs6ZsA %}