Olá! Você ainda usa vimscript para configurar seu neovim? Gostaria de aprender como como migrar o init.vim para a linguagem lua? No bit de hoje eu gostaria de mostrar como migrar o init.vim para a linguagem lua, de tal forma que você consiga modernizar sua configuração e facilitar a manutenção.
Este guia será dividido em várias partes para não ficar muito extenso.
⚠️ Atenção! Eu não sei se esta é a melhor forma, mas é a que funcionou para mim, e é a maneira que eu estruturei minha configuração e me atende até hoje.
Se você está sem tempo e quer ver o resultado final da configuração, pode acessar o meu repositório no GitHub.
Cenário
Quando eu comecei a usar o Neovim, toda a minha configuração ficava em um único arquivo, o famoso init.vim. Na época não haviam tutoriais com a finalidade de auxiliar na transição para a linguagem lua e, por consequência disso, foi necessária muita pesquisa e tentativa e erro para chegar na minha configuração atual.
Para exemplificar, abaixo está a última versão que eu usei do arquivo init.vim antes de migrar para a linguagem lua:
"
" Plugins
"
call plug#begin('~/.nvim/plugged')
Plug 'neoclide/coc.nvim', {'branch': 'release'}
Plug 'kyazdani42/nvim-web-devicons'
Plug 'kyazdani42/nvim-tree.lua'
Plug 'airblade/vim-gitgutter'
Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'}
Plug 'tpope/vim-fugitive'
Plug 'nvim-lua/plenary.nvim'
Plug 'nvim-telescope/telescope-fzf-native.nvim', { 'do': 'make' }
Plug 'nvim-telescope/telescope.nvim', { 'tag': '0.1.0' }
Plug 'nvim-lualine/lualine.nvim'
Plug 'sainnhe/gruvbox-material'
call plug#end()
"
" Felipe Gomes configs
"
set laststatus=2
set encoding=utf-8
set showmatch
set ignorecase
set incsearch
set mouse+=a
set clipboard^=unnamed,unnamedplus
set list listchars=tab:»\ ,space:·,trail:·,eol:¬
set number
set smarttab
set tabstop=2
set shiftwidth=2
set expandtab
set relativenumber
set nohlsearch
set nowrap
set guicursor=
set scrolloff=3
set updatetime=50
set hidden
set shortmess+=c
set signcolumn=yes
let mapleader=" "
syntax on
set t_Co=256
"
" Automatically remove all trailing whitespace
autocmd BufWritePre * %s/\s\+$//e
"
" Key mappings
"
" j/k will move virtual lines (lines that wrap)
noremap <silent> <expr> j (v:count == 0 ? 'gj' : 'j')
noremap <silent> <expr> k (v:count == 0 ? 'gk' : 'k')
" Paste replacing selected text
xnoremap <leader>p "_dP
"
" gruvbox-material theme
"
set termguicolors
set background=dark
let g:gruvbox_material_background = 'medium'
let g:gruvbox_material_foreground = 'material'
let g:gruvbox_material_better_performance = 1
let g:gruvbox_material_enable_italic = 1
let g:gruvbox_material_sign_column_background = 'grey'
let g:gruvbox_material_show_eob = 0
colorscheme gruvbox-material
"
" LuaLine
"
lua << EOF
require('lualine').setup {
options = {
theme = 'gruvbox-material',
icons_enabled = true,
globalstatus = true
},
tabline = {}
}
EOF
"
" treesitter
"
lua << EOF
require'nvim-treesitter.configs'.setup {
auto_install = true,
highlight = {
enable = true,
additional_vim_regex_highlighting = false
}
}
EOF
"
" Telescope
"
lua << EOF
require('telescope').setup({
defaults = {
sorting_strategy = 'ascending',
scroll_strategy = 'limit',
layout_config = {
prompt_position = 'top'
},
},
})
EOF
nnoremap <leader>ff <cmd>lua require('telescope.builtin').find_files({ hidden = true })<cr>
nnoremap <leader>fg <cmd>lua require('telescope.builtin').live_grep({grep_open_files = false})<cr>
nnoremap <leader>fb <cmd>lua require('telescope.builtin').buffers()<cr>
nnoremap <leader>fh <cmd>lua require('telescope.builtin').help_tags()<cr>
"
" nvim-tree
"
let g:loaded = 1
let g:loaded_netrwPluggin = 1
lua << EOF
require("nvim-tree").setup {
disable_netrw = true,
reload_on_bufenter = true,
respect_buf_cwd = true,
hijack_cursor = true,
hijack_netrw = true,
hijack_unnamed_buffer_when_opening = true,
renderer = {
indent_markers = {
enable = true
}
},
update_focused_file = {
enable = true
},
actions = {
open_file = {
quit_on_open = true
}
}
}
EOF
inoremap jk <ESC>
nmap <C-n> :NvimTreeToggle<CR>
"
" coc config
"
command! -nargs=0 Prettier :CocCommand prettier.formatFile
let g:coc_global_extensions = [
\ 'coc-snippets',
\ 'coc-pairs',
\ 'coc-tsserver',
\ 'coc-eslint',
\ 'coc-prettier',
\ 'coc-json'
\ ]
inoremap <silent><expr> <TAB>
\ coc#pum#visible() ? coc#pum#next(1) :
\ CheckBackspace() ? "\<Tab>" :
\ coc#refresh()
inoremap <expr><S-TAB> coc#pum#visible() ? coc#pum#prev(1) : "\<C-h>"
function! CheckBackspace() abort
let col = col('.') - 1
return !col || getline('.')[col - 1] =~# '\s'
endfunction
" Use <c-space> to trigger completion.
inoremap <silent><expr> <c-space> coc#refresh()
" Make <CR> to accept selected completion item or notify coc.nvim to format
" <C-g>u breaks current undo, please make your own choice.
inoremap <silent><expr> <CR> coc#pum#visible() ? coc#pum#confirm() : "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
" Use `[g` and `]g` to navigate diagnostics
nmap <silent> [g <Plug>(coc-diagnostic-prev)
nmap <silent> ]g <Plug>(coc-diagnostic-next)
" Remap keys for gotos
nmap <silent> gd <Plug>(coc-definition)
nmap <silent> gy <Plug>(coc-type-definition)
nmap <silent> gi <Plug>(coc-implementation)
nmap <silent> gr <Plug>(coc-references)
" Use K to show documentation in preview window.
nnoremap <silent> K :call ShowDocumentation()<CR>
function! ShowDocumentation()
if CocAction('hasProvider', 'hover')
call CocActionAsync('doHover')
else
call feedkeys('K', 'in')
endif
endfunction
" Highlight symbol under cursor on CursorHold
autocmd CursorHold * silent call CocActionAsync('highlight')
" Remap for rename current word
nmap <F2> <Plug>(coc-rename)
" Remap for format selected region
xmap <leader>f <Plug>(coc-format-selected)
nmap <leader>f <Plug>(coc-format-selected)
augroup mygroup
autocmd!
" Setup formatexpr specified filetype(s).
autocmd FileType typescript,json setl formatexpr=CocAction('formatSelected')
" Update signature help on jump placeholder
autocmd User CocJumpPlaceholder call CocActionAsync('showSignatureHelp')
augroup end
" Remap for do codeAction of selected region, ex: `<leader>aap` for current paragraph
xmap <leader>a <Plug>(coc-codeaction-selected)
nmap <leader>a <Plug>(coc-codeaction-selected)
" Remap for do codeAction of current line
nmap <leader>ac <Plug>(coc-codeaction)
" Fix autofix problem of current line
nmap <leader>qf <Plug>(coc-fix-current)
" Create mappings for function text object, requires document symbols feature of languageserver.
xmap if <Plug>(coc-funcobj-i)
xmap af <Plug>(coc-funcobj-a)
omap if <Plug>(coc-funcobj-i)
omap af <Plug>(coc-funcobj-a)
" Use `:Format` to format current buffer
command! -nargs=0 Format :call CocAction('format')
" Use `:Fold` to fold current buffer
command! -nargs=? Fold :call CocAction('fold', <f-args>)
" use `:OR` for organize import of current buffer
command! -nargs=0 OR :call CocAction('runCommand', 'editor.action.organizeImport')
" Add status line support, for integration with other plugin, checkout `:h coc-status`
set statusline^=%{coc#status()}%{get(b:,'coc_current_function','')}
" Using CocList
" Show all diagnostics
nnoremap <silent> <space>a :<C-u>CocList diagnostics<cr>
" Manage extensions
nnoremap <silent> <space>e :<C-u>CocList extensions<cr>
" Show commands
nnoremap <silent> <space>c :<C-u>CocList commands<cr>
" Find symbol of current document
nnoremap <silent> <space>o :<C-u>CocList outline<cr>
" Search workspace symbols
nnoremap <silent> <space>s :<C-u>CocList -I symbols<cr>
" Do default action for next item.
nnoremap <silent> <space>j :<C-u>CocNext<CR>
" Do default action for previous item.
nnoremap <silent> <space>k :<C-u>CocPrev<CR>
" Resume latest coc list
nnoremap <silent> <space>p :<C-u>CocListResume<CR>
" Use <C-l> for trigger snippet expand.
imap <C-l> <Plug>(coc-snippets-expand)
" Use <C-j> for select text for visual placeholder of snippet.
vmap <C-j> <Plug>(coc-snippets-select)
" Use <C-j> for jump to next placeholder, it's default of coc.nvim
let g:coc_snippet_next = '<c-j>'
" Use <C-k> for jump to previous placeholder, it's default of coc.nvim
let g:coc_snippet_prev = '<c-k>'
" Use <C-j> for both expand and jump (make expand higher priority.)
imap <C-j> <Plug>(coc-snippets-expand-jump)
" Use <leader>x for convert visual selected code to snippet
xmap <leader>x <Plug>(coc-convert-snippet)
Como podem notar, é um arquivo bastante extenso, que engloba os seguintes aspectos:
- Gerenciador de plugins
- Opções genéricas do neovim
- Mapeamento de atalhos de teclado
- Tema
- Status line: barra da parte inferior
- Treesitter: Cores corretas com base na sintaxe
- Telescope: Plugin para abrir arquivos rapidamente
- Árvore de navegação
- CoC: Gerenciador de servidor de linguagem, para ter sugestões de código, snippets, linter, code actions, entre outros.
A minha ideia é criar um conjunto de configurações na linguagem lua que repliquem as mesmas funcionalidades do init.vim com o propósito de simplificar o entendimento e a manutenção de configurações e plugins.
Motivação
Com o passar do tempo, especialmente se você utilizar muitos plugins, o arquivo init.vim acaba ficando enorme e difícil de manter. Foi aí que eu decidi que era hora de migrar esta configuração para a linguagem lua, pois é suportada pelo neovim e vai proporcionar uma facilidade maior de manutenção.
Minhas dificuldades
Eu tive bastante dificuldade de criar uma estrutura inicial, pois não existiam tutoriais ensinando a montar uma config do zero na linguagem lua. No meu caso, foi bastante tentativa e erro, mas com toda a certeza a organização final vale a pena.
Contudo, antes de prosseguir com a configuração, vale lembrar que, no momento que eu escrevi este post, a versão mais recente do neovim é a 0.10.3. Eu vou tentar manter as configurações atualizadas por aqui, mas se encontrarem algum erro devido às versões mais novas, basta entrar em contato.
Configuração
Preparação do ambiente
Antes de começar a migrar as configurações para a linguagem lua, vamos garantir que o neovim está instalado e configurado, e que não existe nenhuma dependência faltando para o correto funcionamento.
⚠️ Atenção! No guia a seguir eu estou considerando que você esteja usando uma distro Linux. Se você estiver usando o Windows, é provável que você tenha mais sorte com o WSL, entretanto, não recomendo.
Versão
A versão que eu estou usando para este exercício é a 0.10.2. Para verificar a versão, abra o terminal e, em seguida, rodar o comando nvim --version
:

É importante que você utilize uma versão mais recente, pois alguns plugins podem não funcionar corretamente por consequência disso.
Checkhealth
O neovim possui uma ferramenta que verifica se existe algum problema de configuração ou se falta instalar alguma dependência; em resumo, é basicamente um médico que trata do neovim. Para iniciar a verificação, abra o neovim e execute o comando :checkhealth
:


Salvo a mensagem que eu destaquei em vermelho, que indica a falta do arquivo de configuração, é importante corrigir qualquer mensagem de erro que porventura venha a ser informado no relatório.
Ocasionalmente, na parte das linguagens, caso ele reclame que não possui instalado algum provider, como por exemplo o Pearl provider, e você não tem intenção de desenvolver nesta linguagem, não é necessário instalar.
No meu caso, não tenho instalado os providers do Perl e do Ruby, uma vez que não estou utilizando estas linguagens no momento. Você pode notar ao lado da descrição do provider que ele é um pacote opcional:

As imagens acima servem para ilustrar alguns casos que podem ocorrer. Eu não tenho como cobrir todas as possibilidades de instalação, pois cada distribuição do Linux vem com alguns pacotes pré-instalados, noutras pode faltar algo, então fica complexo de contemplar todos os casos, porém, uma pesquisa rápida no Google deve ajudar a sanar qualquer requisito que esteja faltando.
Criação da estrutura de diretórios
Por padrão, o neovim espera que os arquivos de configuração estejam dentro da pasta .config/nvim
que fica localizada na pasta home do seu usuário. Então, vamos primeiramente criar a pasta necessária com o comando mkdir ~/.config/nvim
Caso a pasta já exista, vai ser necessário renomear ou remover a pasta antiga antes de criar a nova pasta. Pode renomear a pasta antiga usando o comando mv ~/.config/nvim ~/.config/old_nvim
Dentro da pasta ~/.config/nvim
vamos criar um arquivo que servirá como ponto inicial da configuração, chamado init.lua
com o comando touch ~/.config/nvim/init.lua
.
Ainda no mesmo diretório vamos criar uma pasta chamada lua
e ao mesmo tempo dentro dela, uma pasta com o nome que você desejar, que será o nome do seu pacote lua. No meu caso, vou criar uma pasta chamada fsg
, que são as iniciais do meu nome, mas fica a seu critério o nome que deseja usar, afinal, esta será a sua config: mkdir -p ~/.config/nvim/lua/fsg

Neste ponto, embora ainda reste um longo caminho pela frente, estamos com tudo preparado para começar a fazer as primeiras configurações…
Continue lendo na parte 2.