Executando verificação de segurança...
5

Aprenda a configurar o LanguageServer no NeoVim

O que é Language Server?

Essa é a definição em tradução livre do próprio site provido pela Microsoft.

O Language Server Protocol (LSP) define o protocolo usado entre um editor ou IDE e um servidor de linguagem que fornece recursos de linguagem como autocompletar, ir para definição, encontrar todas as referências etc. O objetivo do Language Server Index Format (LSIF, pronunciado como "else if") é para dar suporte à navegação de código avançado em ferramentas de desenvolvimento ou uma interface do usuário da Web sem precisar de uma cópia local do código-fonte.

Em termos simples, ela é parte do que está debaixo do capô do VSCode.

vscode intelissense

Algumas considerações

Nesse artigo, eu vou compartilhar como é a minha atual configuração, utilizando a linguagem lua com o lsp do próprio NeoVim. Existem outras maneiras também, como por exemplo o famoso coc.nvim. Não seria muito bom falar sobre os dois métodos em um único post pois ficaria confuso, mas caso você se interesse, pode conferir minha configuração antiga aqui, onde eu usava essa segunda forma.

Instalação dos plugins

Esses são os plugins que eu uso para toda a questão de language server e auto complete, usando o packer.nvim como gerenciador.

-- Parsers e language servers
  use 'neovim/nvim-lspconfig' -- Collection of configurations for built-in LSP client
  use 'hrsh7th/nvim-cmp' -- Autocompletion plugin
  use 'hrsh7th/cmp-nvim-lsp' -- LSP source for nvim-cmp
  use 'saadparwaiz1/cmp_luasnip' -- Snippets source for nvim-cmp
  use 'L3MON4D3/LuaSnip' -- Snippets plugin
  use 'hrsh7th/cmp-buffer' -- Suporte ao Buffer no autocomplete

Instalação do servidor de linguagem

Aqui eu vou focar no servidor de linguagem do TypeScript, mas existem vários outros, que você pode conferir no repositório oficial.

tsserver

https://github.com/theia-ide/typescript-language-server

typescript-language-server depende de typescript. Ambos os pacotes podem ser instalados via npm:

npm install -g typescript typescript-language-server

Para configurar o servidor de linguagens, adicione um tsconfig.json ou jsconfig.json para a raiz do seu projeto.

Aqui está um exemplo que desativa a verificação de tipo em arquivos JavaScript.

{
  "compilerOptions": {
    "module": "commonjs",
    "alvo": "es6",
    "checkJs": false
  },
  "excluir": [
    "node_modules"
  ]
}

Snippet para ativar o servidor de linguagens:

require'lspconfig'.tsserver.setup{}

Valores padrão:

  • cmd:
{ "typescript-language-server", "--stdio" }
  • tipos de arquivo:
{ "javascript", "javascriptreact", "javascript.jsx", "typescript", "typescriptreact", "typescript.tsx" }
  • init_options:
{
  hostInfo = "neovim"
}
  • dir_raiz:
root_pattern("package.json", "tsconfig.json", "jsconfig.json", ".git")

Configuração

-- Mappings.
-- See `:help vim.diagnostic.*` for documentation on any of the below functions
local opts = { noremap=true, silent=true }
vim.keymap.set('n', '<space>e', vim.diagnostic.open_float, opts)
vim.keymap.set('n', '<space>q', vim.diagnostic.setloclist, opts)
vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, opts)
vim.keymap.set('n', ']d', vim.diagnostic.goto_next, opts)

-- Use an on_attach function to only map the following keys
-- after the language server attaches to the current buffer
local on_attach = function(client, bufnr)
  -- Enable completion triggered by <c-x><c-o>
  vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc')

  -- Mappings.
  -- See `:help vim.lsp.*` for documentation on any of the below functions
  local bufopts = { noremap=true, silent=true, buffer=bufnr }
  vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, bufopts)
  vim.keymap.set('n', 'gd', vim.lsp.buf.definition, bufopts)
  vim.keymap.set('n', 'gtd', vim.lsp.buf.type_definition, bufopts)
  vim.keymap.set('n', 'gr', vim.lsp.buf.references, bufopts)
  vim.keymap.set('n', 'K', vim.lsp.buf.hover, bufopts)
  -- vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, bufopts)
  vim.keymap.set('n', '<space>rn', vim.lsp.buf.rename, bufopts)
  vim.keymap.set('n', '<space>fd', vim.lsp.buf.formatting, bufopts)
end

local lspconfig = require('lspconfig')
local util = require "lspconfig/util"

-- Add additional capabilities supported by nvim-cmp
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities = require('cmp_nvim_lsp').update_capabilities(capabilities)
-- Language servers
lspconfig.tsserver.setup {
  on_attach = on_attach,
  capabilities = capabilities,
  root_dir = util.root_pattern("package.json"),
}

lspconfig.denols.setup {
  on_attach = on_attach,
  root_dir = util.root_pattern("deno.json", "deno.jsonc"),
}

lspconfig.cssls.setup {
  capabilities = capabilities,
  capabilities = capabilities,
  root_dir = util.root_pattern("package.json"),
}

-- nvim-cmp setup
local cmp = require 'cmp'
local luasnip = require 'luasnip'
cmp.setup {
  snippet = {
    expand = function(args)
      luasnip.lsp_expand(args.body)
    end,
  },
  mapping = cmp.mapping.preset.insert({
    ['<C-d>'] = cmp.mapping.scroll_docs(-4),
    ['<C-f>'] = cmp.mapping.scroll_docs(4),
    ['<C-Space>'] = cmp.mapping.complete(),
    ['<CR>'] = cmp.mapping.confirm {
      behavior = cmp.ConfirmBehavior.Replace,
      select = true,
    },
    ['<Tab>'] = cmp.mapping(function(fallback)
      if cmp.visible() then
        cmp.select_next_item()
      elseif luasnip.expand_or_jumpable() then
        luasnip.expand_or_jump()
      else
        fallback()
      end
    end, { 'i', 's' }),
    ['<S-Tab>'] = cmp.mapping(function(fallback)
      if cmp.visible() then
        cmp.select_prev_item()
      elseif luasnip.jumpable(-1) then
        luasnip.jump(-1)
      else
        fallback()
      end
    end, { 'i', 's' }),
  }),
  sources = {
    { name = 'nvim_lsp' },
    { name = 'buffer' },
    { name = 'luasnip' }
  },
}

vim.diagnostic.config({
  virtual_text = false,
  signs = true,
  underline = true,
  update_in_insert = true,
  severity_sort = false,
})

local signs = { Error = "", Warn = " ", Hint = " ", Info = " " }
for type, icon in pairs(signs) do
  local hl = "DiagnosticSign" .. type
  vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = hl })
end

Atalhos

Esses são os atalhos que eu mais uso e que estão configurados no trecho acima.

  • <space>e - Mostra o resumo do erro em uma janela flutuante;
  • ]d - Vai para o proximo diagnóstico de erro;
  • [d - Vai para o diagnóstico de erro anterior;
  • gd - Vai para a definição (como ctrl + click no VSCode);
  • gr - Mostra a lista de referências da variável sob o cursor;
  • K - Mostra detalhes de algo (como ao hover do VSCode);
  • <space>rn - Renomeia uma variável;
  • ctrl + <space> - Mostra a lista de opções do autocomplete.

Resultado

Marcação dos erros de sintaxe

tsseerver errors

Autocomplete

autocomplete no neovim

Carregando publicação patrocinada...