DEV Community

Ellison Leão
Ellison Leão

Posted on

Criando plugins Lua para Neovim - Parte 3

Migrando do init.vim para o init.lua


Para fechar nossa série de criação de plugins em Lua para o Neovim, vamos mudar um pouco o foco na criação de plugins para um tutorial de como podemos migrar toda nossa configuração do Neovim usando o tradicional Vimscript, para Lua.

Mas por que Lua?

A decisão tomada pelo core team de desenvolvimento do Neovim tem várias respostas diversas, que vão desde velocidade a facilidade de escrita, pelo fato de Lua ser uma linguagem bem extensível. Para entender as principais motivações, sugiro dar uma pausa no post e apreciar a talk “We can have nice things”, do Justin M. Keyes, um dos atuais líderes de desenvolvimento do Neovim.

A migração

Caso você ainda não conheça a estrutura de diretórios do Neovim, toda configuração vai em:

Unix: ~/.config/nvim/init.vim
Windows: ~/AppData/Local/nvim/init.vim
ou, caso você queria um path de config customizado, onde $XDG_CONFIG_HOME é definido, fica assim:

$XDG_CONFIG_HOME/nvim/init.vim

Esse arquivo é o ponto de entrada para as configurações customizadas do editor, desde cores, tamanhos de fonte, a instalação de plugins.

No nosso caso, começaremos renomeando o arquivo init.vim para init.old e criando um novo arquivo chamado init.lua.

Por quê precisamos renomear o init.vim?

No neovim não é possível existir 2 pontos de entrada para as configurações, então você só poderá usar o init.vim ou o mais recente, init.lua

Para o nosso tutorial não ficar bastante extenso, iremos usar um init.vim relativamente pequeno, explicando as principais configurações e como portá-las para a versão em lua.

Mas primeiro precisamos relembrar como a API em Lua se comporta.

O namespace vim

Na implementação da API Lua para o Neovim, existe uma variável chamada vim, que é o ponto de entrada para todas as funções da chamada “biblioteca padrão” Lua para o Neovim. Essas funções servem diferentes propósitos, mas o principal sendo a ponte entre o código lua e o editor. Algumas APIs incluem:

  • vim.inspect - uma função de debug para inspecionar resultados. Geralmente em lua, os resultados vem em tables, e esses são difíceis de ser expostos em formato de “print”. Essa função “disseca” a váriável que você gostaria de expor de uma forma mais amigável.

  • vim.regex - Módulo que possibilita o uso de regexes diretamente no código Lua. Para mais info :h vim.regex

  • vim.api - Provavelmente o módulo mais importante dessa API. Aqui encontramos todas a funções para trabalhar com buffers, windows, chamar comandos do neovim, etc.

  • vim.loop - Módulo que funciona como uma camada de abstração da LibUV, a camada de event-loop do Neovim. Pra quem quer usar concorrência, timers, etc, esse módulo é o caminho.

  • vim.lsp - Pra quem trabalhar diretamente na fantástica implementação nativa de um cliente LSP dentro do Neovim.

  • vim.treesitter - O módulo que expõe algumas funções para se trabalhar diretamente com o tree-sitter.

  • vim.keymap - Inclua ou remova keymaps do vim

Para este post, focaremos apenas na vim.api e vim.keymap

Nosso arquivo init.lua

Levando em consideração que nosso init.vim tinha a seguinte cara:

" Parte 1
set colorcolumn=80
set expandtab
set tabstop=4
set autoindent
set incsearch
set hlsearch
set termguicolors

" Parte 2
let g:minha_config = 'valor'
let g:algumnamespace#teste#variavel = "valor"

" Parte 3
nmap <leader>, <Cmd>:noh<CR>
nnoremap <leader>h <Cmd>split<CR>
vnoremap < <gv

" Parte 4
autocmd BufEnter * echo "hello nvim"

" Parte 5
function! SayHello()
  echohl WarningMsg
  echo "Hello World"
  echohl
endfunction`
Enter fullscreen mode Exit fullscreen mode

separamos a migração em 4 partes:

  • options
  • vars
  • mappings
  • autocmds
  • functions

Para a parte 1, temos as seguintes funções da vim.api:

  • vim.api.nvim_set_option
  • vim.api.nvim_buf_set_option
  • vim.api.nvim_win_set_option

Para facilitar a escrita, temos o atalho vim.opt:

vim.opt.termguicolors = true   -- set termguicolors
vim.opt.colorcolumn = 80       -- set colorcolumn=80
vim.opt.expandtab              -- set expandtab
vim.opt.tabstop=4              -- set tabstop=4
vim.opt.autoindent = true      -- set autoindent
vim.opt.incsearch = true       -- set incsearch
vim.opt.hlsearch = true        -- set hlsearch
Enter fullscreen mode Exit fullscreen mode

Continuando com a parte 2, temos uma declaração de uma variável global (escopo g:). Na API, podemos utilizar as seguintes funções:

vim.api.nvim_set_var e suas variações (vim.api.nvim_get_var e vim.api.nvim_del_var)
Aqui também temos uma versão “minimalista” com seus respectivo meta-acessor (ver :h lua-vim-variables para mais detalhes):

vim.g - Também funciona como um getter/setter

Temos então nosso código lua, com a parte 2:

-- parte 1
vim.opt.termguicolors = true   -- set termguicolors
vim.opt.colorcolumn = 80       -- set colorcolumn=80
vim.opt.expandtab = true       -- set expandtab
vim.opt.tabstop = 4            -- set tabstop=4
vim.opt.autoindent = true      -- set autoindent
vim.opt.incsearch = true       -- set incsearch
vim.opt.hlsearch = true        -- set hlsearch

-- parte 2
vim.g.minha_config = "valor"
 -- o `meta-acessor` é uma table, então podemos atribuir valores usando também os colchetes
vim.g["algumnamespace#teste#variavel"] = "valor"
Enter fullscreen mode Exit fullscreen mode

Para a parte 3 temos os mappings e para isso temos 2 funções que podem ser utilizadas:

  • vim.api.nvim_set_keymap - mappings que serão atribuídos para todos os buffers
  • vim.api.nvim_buf_set_keymap - mappings que serão exclusivos de algum buffer em particular

Para facilitar ainda mais, na versão 0.7 foi introduzida a API vim.keymap (:h lua-keymap), que deixa as coisas bem mais fáceis quando precisamos criar os mappings.

Temos então, no nosso arquivo lua:

-- parte 1
vim.opt.termguicolors = true   -- set termguicolors
vim.opt.colorcolumn = 80       -- set colorcolumn=80
vim.opt.expandtab = true       -- set expandtab
vim.opt.tabstop = 4            -- set tabstop=4
vim.opt.autoindent = true      -- set autoindent
vim.opt.incsearch = true       -- set incsearch
vim.opt.hlsearch = true        -- set hlsearch

-- parte 2
vim.g.minha_config = "valor"
 -- o `meta-acessor` é uma table, então podemos atribuir valores usando também os colchetes
vim.g["algumnamespace#teste#variavel"] = "valor"

-- parte 3
vim.keymap.set("n", "<leader>,", "<Cmd>:noh<CR>", nil) -- nmap <leader>, <Cmd>:noh<CR>
vim.keymap.set("n", "<leader>h", "<Cmd>split<CR>", {noremap = true}) -- nnoremap <leader>h <Cmd>split<CR>
vim.keymap.set("v", "<", "<gv", {noremap = true}) -- vnoremap < <gv
Enter fullscreen mode Exit fullscreen mode

Já para a parte 4, vamos adicionar os autocmd e augroups. Para isso, iremos usar a função vim.api.nvim_create_autocmd, introduzida também na versão 0.7. Adicionando a parte 4 na nossa configuração, temos:

-- parte 1
vim.opt.termguicolors = true   -- set termguicolors
vim.opt.colorcolumn = 80       -- set colorcolumn=80
vim.opt.expandtab = true       -- set expandtab
vim.opt.tabstop = 4            -- set tabstop=4
vim.opt.autoindent = true      -- set autoindent
vim.opt.incsearch = true       -- set incsearch
vim.opt.hlsearch = true        -- set hlsearch

-- parte 2
vim.g.minha_config = "valor"
 -- o `meta-acessor` é uma table, então podemos atribuir valores usando também os colchetes
vim.g["algumnamespace#teste#variavel"] = "valor"

-- parte 3
vim.keymap.set("n", "<leader>,", "<Cmd>:noh<CR>", nil) -- nmap <leader>, <Cmd>:noh<CR>
vim.keymap.set("n", "<leader>h", "<Cmd>split<CR>", {noremap = true}) -- nnoremap <leader>h <Cmd>split<CR>
vim.keymap.set("v", "<", "<gv", {noremap = true}) -- vnoremap < <gv

-- parte 4
vim.api.nvim_create_autocmd("BufEnter", {
  callback = function()
    print("hello nvim")
  end
})
Enter fullscreen mode Exit fullscreen mode

Para a última parte, temos uma função vimscript que poderá ser portada utilizando a função vim.api.nvim_exec, que avalia um código vimscript contendo várias linhas. A assinatura da função é da seguinte forma:

vim.api.nvim_exec(bloco_de_codigo, retorno)
Enter fullscreen mode Exit fullscreen mode

Onde:

  • bloco_de_codigo: A string multiline ou não do bloco de código vimscript que será executado
  • retorno - true caso deseje retornar o resultado string da execução do bloco, false para retornar uma string vazia

Temos então a versão final da config lua:

-- parte 1
vim.opt.termguicolors = true   -- set termguicolors
vim.opt.colorcolumn = 80       -- set colorcolumn=80
vim.opt.expandtab = true       -- set expandtab
vim.opt.tabstop = 4            -- set tabstop=4
vim.opt.autoindent = true      -- set autoindent
vim.opt.incsearch = true       -- set incsearch
vim.opt.hlsearch = true        -- set hlsearch

-- parte 2
vim.g.minha_config = "valor"
 -- o `meta-acessor` é uma table, então podemos atribuir valores usando também os colchetes
vim.g["algumnamespace#teste#variavel"] = "valor"

-- parte 3
vim.keymap.set("n", "<leader>,", "<Cmd>:noh<CR>", nil) -- nmap <leader>, <Cmd>:noh<CR>
vim.keymap.set("n", "<leader>h", "<Cmd>split<CR>", {noremap = true}) -- nnoremap <leader>h <Cmd>split<CR>
vim.keymap.set("v", "<", "<gv", {noremap = true}) -- vnoremap < <gv
Enter fullscreen mode Exit fullscreen mode

Já para a parte 4, vamos adicionar os autocmd e augroups. Para isso, iremos usar a função vim.api.nvim_create_autocmd, introduzida também na versão 0.7. Adicionando a parte 4 na nossa configuração, temos:

-- parte 1
vim.opt.termguicolors = true   -- set termguicolors
vim.opt.colorcolumn = 80       -- set colorcolumn=80
vim.opt.expandtab = true       -- set expandtab
vim.opt.tabstop = 4            -- set tabstop=4
vim.opt.autoindent = true      -- set autoindent
vim.opt.incsearch = true       -- set incsearch
vim.opt.hlsearch = true        -- set hlsearch

-- parte 2
vim.g.minha_config = "valor"
 -- o `meta-acessor` é uma table, então podemos atribuir valores usando também os colchetes
vim.g["algumnamespace#teste#variavel"] = "valor"

-- parte 3
vim.keymap.set("n", "<leader>,", "<Cmd>:noh<CR>", nil) -- nmap <leader>, <Cmd>:noh<CR>
vim.keymap.set("n", "<leader>h", "<Cmd>split<CR>", {noremap = true}) -- nnoremap <leader>h <Cmd>split<CR>
vim.keymap.set("v", "<", "<gv", {noremap = true}) -- vnoremap < <gv

-- parte 4
vim.api.nvim_create_autocmd("BufEnter", {
  callback = function()
    print("hello nvim")
  end
})

-- parte 5
vim.api.nvim_exec([[
function! SayHello()
  echohl WarningMsg
  echo "Hello World"
  echohl
endfunction
]], false)
Enter fullscreen mode Exit fullscreen mode

Conclusão

Mostramos que, apesar de algumas limitações, portar uma configuração em vimscript para Lua está cada dia mais fácil com a API vigente. Para um exemplo de configuração mais extensa usando mais elementos da API Lua, visite a minha configuração do neovim, deixa seu star por lá também e caso tenha alguma dúvida, manda mensagem por aqui

Lembrando que faço lives esporádicas na Twitch! Caso deseje ver esse tipo de conteúdo ao vivo, não deixe de me seguir por lá e também acompanhar meu Twitter

Para ouvir

Top comments (0)