Neovim: Migrando do coc.vim para nvim-lsp
Tags: neovim, lsp
Published: 2021-05-16 18:02:34

A versão 0.5 do Neovim (nightly build) traz algumas mudanças como a integração do Lua como uma linguagem de primeira classe e o suporte a LSP embutido. Adeus CoC!

Você pode obter a versão 0.5 do Neovim [aqui](https://github.com/neovim/neovim/releases/nightly) ou via homebrew:
```sh
$ brew unlink neovim
$ brew install neovim --HEAD
$ nvim --version
```


# Migrando suas configurações para Lua
Vale dar uma passada pelo guia de [primeiros passos usando Lua no Neovim](https://github.com/npxbr/nvim-lua-guide/blob/master/README.pt-br.md) e começar a migrar aos poucos suas configurações.

Exemplo do meu `init.lua`:
```lua
vim.g.mapleader = ','
vim.api.nvim_set_keymap("n", ";", ":", { noremap = true })

vim.cmd [[
  source ~/.config/nvim/config.vim
  source ~/.config/nvim/plugins.vim
  source ~/.config/nvim/settings.vim
  source ~/.config/nvim/keymappings.vim
]]

require'config.lsp'
-- ... configs
```
Escolhi mudar o `init.vim` para `init.lua` e aos poucos ir migrando cada `.vim`. Também mantenho por enquanto o mesmo gerenciador de plugins (vim-plug), então vamos instalar os novos plugins também por ele.

# Configurando o LSP

A primeira coisa que precisamos fazer é instalar os plugins:
* [neovim/nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) e [kabouzeid/nvim-lspinstall](https://github.com/kabouzeid/nvim-lspinstall) para configurar e instalar os servers de cada linguagem;
* [hrsh7th/nvim-compe](https://github.com/hrsh7th/nvim-compe) para o auto completion
* [glepnir/lspsaga.nvim](https://github.com/glepnir/lspsaga.nvim) para exibir melhor as mensagens e popups

## Instalação, Inicialização e Comandos

Para que algum servidor comece a funcionar é necessário que ele seja instalado e inicializado no seu `init.lua`. Com o `nvim-lspinstall` podemos instalar com o comando `:LspInstall <linguagem>`.

Utilizando no exemplo o `tsserver`, vamos instalar com o comando `:LspInstall typescript` ([lista ref](https://github.com/kabouzeid/nvim-lspinstall#bundled-installers)) e para iniciar os servidores que instalamos o seguinte trecho (em lua) é necessário:

```lua
require'lspsaga'.init_lsp_saga()
require'lspinstall'.setup()
local servers = require'lspinstall'.installed_servers()
for _, server in pairs(servers) do
  require'lspconfig'[server].setup{}
end
```

Com isso você já terá o LSP rodando e pode testar abrindo um arquivo `.ts` ou `.tsx` e executar o comando `:LspInfo`. O `lspconfig` também vai aparecer no comando `:checkhealth` então vale a pena executa-lo para verificar se está tudo ok.

Para configurar os atalhos você precisará passar uma função `on_attach` no setup do `lspconfig`, exemplo:

```lua
local on_attach = function(client, bufnr)
-- ...mappings
end

require'lspinstall'.setup()
local servers = require'lspinstall'.installed_servers()
for _, server in pairs(servers) do
  require'lspconfig'[server].setup{ on_attach = on_attach }
end
```

Com os mappings:

```lua
local on_attach = function(_, bufnr)
  local function buf_set_keymap(...) vim.api.nvim_buf_set_keymap(bufnr, ...) end
  local function buf_set_option(...) vim.api.nvim_buf_set_option(bufnr, ...) end
  local opts = { noremap = true, silent = true }

  buf_set_option('omnifunc', 'v:lua.vim.lsp.omnifunc')
  buf_set_keymap('n', '<leader>ca', ':Lspsaga code_action<CR>', opts)
  buf_set_keymap('v', '<leader>ca', ':Lspsaga range_code_action<CR>', opts)
  buf_set_keymap('n', '<leader>rn', ':Lspsaga rename<CR>', opts)
  buf_set_keymap('n', 'K', ':Lspsaga hover_doc<CR>', opts)
  buf_set_keymap('n', 'dn', ':Lspsaga diagnostic_jump_next<CR>', opts)
  buf_set_keymap('n', 'dp', ':Lspsaga diagnostic_jump_prev<CR>', opts)
  buf_set_keymap('n', 'gf', ':lua vim.lsp.buf.definition()<CR>', opts)
end

require'lspinstall'.setup()
local servers = require'lspinstall'.installed_servers()
for _, server in pairs(servers) do
  require'lspconfig'[server].setup{ on_attach = on_attach }
end
```

### Comandos: 
* `<leader>ca`: Abre pop-up com as Code Actions
* `<leader>rn`: Para renomear símbolo
* `K`: Exibe a referência/documentação da seleção ou posição do cursor
* `dn` e `dp`: Navega entre os diagnósticos
* `gf`: Navegação de código

Mais comandos: [aqui](https://github.com/neovim/nvim-lspconfig#keybindings-and-completion)

Se você precisar configurar algo especifico para algum servidor, lembre-se de adicionar a chamada `on_attach` para manter os comandos, exemplo (para linguagem lua):

```lua
require'lspconfig'.lua.setup {
  on_attach = on_attach,
  settings = {
    Lua = {
      diagnostics = {
        globals = { 'vim' }
      }
    }
  }
}
```

Para as sugestões e o auto completion, certifique-se de que você tenha os plugins [SirVer/ultisnips](https://github.com/SirVer/ultisnips) e [hrsh7th/vim-vsnip](https://github.com/hrsh7th/vim-vsnip) para sugestões de snippets. E então adicione o seguinte trecho:

```lua
require'compe'.setup {
  enabled = true;
  preselect = 'disable';
  source = {
    path = true;
    buffer = true;
    calc = true;
    nvim_lsp = true;
    nvim_lua = true;
    vsnip = true;
    ultisnips = true;
    -- treesitter = true;
  };
}

local t = function(str)
  return vim.api.nvim_replace_termcodes(str, true, true, true)
end

local check_back_space = function()
  local col = vim.fn.col('.') - 1
  if col == 0 or vim.fn.getline('.'):sub(col, col):match('%s') then
    return true
  else
    return false
  end
end

vim.g.vsnip_filetypes = {
  typescriptreact = {"typescript"}
}

_G.tab_complete_next = function()
  if vim.fn.pumvisible() == 1 then
    return t "<C-n>"
  elseif vim.fn.call("vsnip#available", {1}) == 1 then
    return t "<Plug>(vsnip-expand-or-jump)"
  elseif check_back_space() then
    return t "<Tab>"
  else
    return vim.fn['compe#complete']()
  end
end
_G.tab_complete_prev = function()
  if vim.fn.pumvisible() == 1 then
    return t "<C-p>"
  elseif vim.fn.call("vsnip#jumpable", {-1}) == 1 then
    return t "<Plug>(vsnip-jump-prev)"
  else
    return t "<S-Tab>"
  end
end

local opt = { expr = true, silent = true}
vim.api.nvim_set_keymap("i", "<Tab>", "v:lua.tab_complete_next()", opt)
vim.api.nvim_set_keymap("s", "<Tab>", "v:lua.tab_complete_next()", opt)
vim.api.nvim_set_keymap("i", "<S-Tab>", "v:lua.tab_complete_prev()", opt)
vim.api.nvim_set_keymap("s", "<S-Tab>", "v:lua.tab_complete_prev()", opt)
vim.api.nvim_set_keymap("i", "<CR>", 'compe#confirm("<CR>")', opt)
```

Com isso você tem o autocomplete funcionando e a navegação mapeada com Tab/Shift+Tab e o Esc para fechar o pop-up.

Obs: Caso você utilize o treesitter, remova o comentário das opções do setup.

<!--Você pode ver minhas configs atuais no repositório:
{% github https://github.com/enriquesaid/nvim-config %}-->

É isso ☺️. Me diz ai o que achou e manda nos comentários como ficou seu nvim. 

Abraços!