Como migrar o init.vim para a linguagem lua – Parte 1

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)
Ver mais

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:

Imagem de versão do neovim

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

Imagem do comando checkhealth
Imagem do resultado do 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:

Provider opcional no neovim

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

Imagem da criação da estrutura de pastas

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.

Deixe um comentário