O próximo passo rumo a uma IDE completa é tornar o Neovim capaz de trabalhar com Language Servers (LSP) e recursos de análise e formatação de código.
Até aqui, iniciamos a transformação do Neovim em uma IDE, implementando funcionalidades básicas como indentação, plugins, temas, explorador de arquivos e afins.
Portanto, se você está chegando agora, recomendo fortemente que leia o primeiro artigo dessa série intitulado: Neovim IDE: começando a transformação.
Uma IDE não faria sentido algum se não fosse capaz de compreender a sintaxe da linguagem que estamos codificando – afinal, nesse caso, usaríamos somente o Neovim.
É nesse momento que entra o LSP (Language Server Protocol), uma forma padronizada de editores como VSCode ou Neovim se comunicarem com servidores de linguagem.
Os servidores de linguagem, por sua vez, entendem profundamente a sintaxe e semântica de uma linguagem (como Python, C++, Lua) e oferecem recursos como:
- Navegação de código (ir para definição, etc.)
- Autocompletar inteligente
- Verificação de erros
- Refatoração
- Informações de tipo
Além disso, precisamos de outros recursos que ajudem a manter qualidade, legibilidade e consistência do código, mas que não fazem parte dos servidores de linguagem.
Aqui entram em campo os linters (analisadores estáticos de código) e os formatadores (organizadores de código) – ferramentas externas fundamentais em um ambiente de desenvolvimento.
Integramos essas ferramentas diretamente à arquitetura LSP. Desta forma, tudo funciona como se fosse uma única coisa.
Dessa maneira, veremos nesse artigo como adicionar LSPs através da instalação de plugins que atuam em conjunto, como por exemplo o trio mason, mason-lspconfig e nvim-lspconfig.
Veremos também como integrar sugestões de correção de código (code actions) ao já conhecido Telescope, tornando-as mais visíveis e acessíveis.
Por fim, aprenderemos a instalar linters e formatadores e a usar o none-ls, que os conecta ao sistema LSP do Neovim.
Fique ligado e bons estudos!
Instalando Servidores de Linguagem
A instalação de servidores de linguagem no Neovim é bastante simples graças a um plugin chamado Mason.
Assim como o Lazy.nvim gerencia plugins, o Mason gerencia servidores de linguagem. E mais: para nossa sorte ele também lida com linters e formatadores que usaremos mais adiante.
Assim sendo, para instalá-lo vamos ao nosso famoso arquivo de spec do Lazy. Vamos chamá-lo de lsp-config.lua pois ele também terá outros plugins conforme veremos em instantes.
Não se esqueça de colocá-lo em ~/.config/nvim/lua/plugins
return {
{
"mason-org/mason.nvim",
opts = {},
},
-- aqui vamos colocar mais coisas!
}Como de costume, salve, saia e entre novamente no Neovim. Como resultado você verá a já conhecida tela de instalação do Lazy. Pressione q para retornar o editor.
Em seguida, digite :Mason e veja a tela principal do plugin:

O Mason é um plugin bastante intuitivo, basta digitar os números de 1 à 5 para selecionar a categoria e então percorrer a lista com as setas do teclado até o item desejado. Por fim, a tecla i o instala.
No entanto, como queremos tudo automatizado, faremos a instalação dos servidores de linguagem via código dentro do próprio lsp-config.lua, mas na spec de outro plugin que veremos em instantes.
Portanto, não se preocupe em entender como o Mason funciona agora. Posteriormente, quando chegarmos nos linters e formatadores, os instalaremos através dessa interface. (Você verá o porquê)
Além disso é importante entender que o Mason apenas instala os recursos, não os integra com o Neovim nem os configura.
Assim sendo, vamos precisar de outros plugins que façam esse trabalho: mason-lspconfig (integração com o cliente LSP) e nvim-lspconfig (inicialização e configuração).
Integrando Servidores de Linguagem com o Neovim
Conforme veremos em breve, o Neovim possui por padrão um cliente LSP embutido que trabalha diretamente com outro plugin: nvim-lspconfig.
No entanto, esse cliente LSP não é capaz de interagir com o servidor de linguagem sem um intermediário que faça a comunicação entre ambos.
É aqui que entra o mason-lspconfig, que faz a ponte entre o servidor de linguagem e o cliente LSP.
Vamos então adicionar outra tabela Lua para instalar o mason-lspconfig. Veja como fica agora o nosso arquivo lsp-config.lua:
return {
{
"mason-org/mason.nvim",
opts = {},
},
{
"mason-org/mason-lspconfig.nvim",
opts = {
ensure_installed = { "lua_ls" },
},
},
}O segredo aqui está na tabela ensure_installed. Repare que nesse exemplo temos apenas o valor lua_ls, abreviação de Lua Language Server.
Isso quer dizer que estamos pedindo que Mason que instale o servidor de linguagem do Lua e faça a comunicação deste servidor com o cliente LSP do Neovim.
Podemos adicionar quantos servidores de linguagem quisermos. Suponha que você trabalhe com Python, Java e Linguagem C.
Seu ensure_installed poderia ficar assim: ensure_installed = { “pyright”, “jdtls”, “clangd”}. Saiba que geralmente, existe mais de um servidor de linguagem para uma determinada linguagem.
Para saber os nomes exatos dos servidores disponíveis, faça o seguinte: abra um arquivo vazio com a extensão desejada através do comando :e no seu Neovim.
Por exemplo, vamos supor que queremos saber o nome dos servidores de linguagem Java. Digite :e temp.java. Em seguida, digite :Lspinstall e veja o resultado:

Como queremos apenas saber os nomes, não se preocupe em instalar por esse comando. O mason-lspconfig se encarregará disso. Tecle q para sair.
Então basta escolher um servidor (no exemplo escolhi o jdtls) e adicioná-lo no ensure_installed. Na próxima vez que entrar no Neovim, você verá o seguinte na barra de status:

Agora basta configurarmos e inicializarmos os servidores com o nvim-lspconfig e ver como tudo funciona!
Configurando e Inicializando Servidores de Linguagem
O último passo para termos um servidor de linguagem minimamente funcional é instalar o nvim-lspconfig – que se encarrega de inicializá-lo e configurá-lo.
Honestamente, o que mais nos interessa nesse momento é a inicialização dos servidores, uma vez que a configuração padrão já é suficiente para nossos testes iniciais.
Mais adiante nesse artigo, com a finalidade de melhorar sua experiência no Neovim, adicionaremos algumas configurações extras.
Assim sendo, apenas adicione a tabela Lua referente à instalação do nvim-lspconfig ao final do lsp-config.lua, conforme o arquivo (completo) abaixo:
return {
{
"mason-org/mason.nvim",
opts = {},
},
{
"mason-org/mason-lspconfig.nvim",
opts = {
ensure_installed = { "lua_ls", }
},
},
{
"neovim/nvim-lspconfig",
},
}Para nos certificarmos de que tudo está funcionando, salve, saia do Neovim e entre novamente no arquivo lsp-config.lua.
Em seguida, digite :LspInfo e veja se algo parecido com a imagem abaixo aparece para você:

A linha vim.lsp: Active Clients mostra que existe um cliente ativo no servidor de linguagem Lua (lua_ls), indicando dessa maneira que nosso LSP no Neovim está funcionando.
Importante reforçar que o Active Clients só mostra que estamos conectados ao servidor de linguagem porque temos um arquivo .lua (lsp-config.lua) aberto no Neovim.
Testando o Neovim com LSP
Agora, veja na prática o servidor de linguagem Lua atuando diretamente no editor quando removemos a vírgula que separa os elementos de uma tabela Lua:

Repare que o Neovim aponta o erro exatamente onde ele ocorre. Repare também que até o Neotree aponta erro no arquivo em questão!
Adicionando mais informações de erros e alertas
Seria bem mais interessante se tivéssemos uma descrição completa do erro e não somente um mero apontamento com a letra E.
Nesse sentido, existe um recurso chamado Virtual Text, que exibe a descrição do erro ao lado do local onde ele ocorre.
Podemos configurar o Virtual Text em qualquer script Lua, entretanto, como se trata de algo relacionado ao LSP, prefiro colocá-lo com a função config do neovim/nvim-lspconfig:
return {
{
"mason-org/mason.nvim",
opts = {},
},
{
"mason-org/mason-lspconfig.nvim",
opts = {
ensure_installed = { "lua_ls", }
},
},
{
"neovim/nvim-lspconfig",
config = function()
-- Definição do Virtual Text
vim.diagnostic.config({
virtual_text = {
spacing = 4, -- Espaço entre o código e a mensagem
},
signs = true, -- Mostra ícones na lateral esquerda
underline = true, -- Sublinha o texto com problema
update_in_insert = false, -- Atualiza apenas fora do modo insert (evita distração)
severity_sort = true, -- Ordena por severidade
})
end,
},
}Caso queira, você pode adicionar o vim.diagnostic.config({…}) dentro do seu init.lua, por exemplo. Trata-se apenas de uma questão de preferência.
Veja agora como fica a indicação de erro com o Virtual Text:

Perceba que o lua_ls também comunica os alertas no código (em amarelo). Além disso, a barra de status também tem suas mensagens.
E não para por aí, os servidores de linguagem tem ainda mais recursos. Veja logo abaixo!
Adicionando mais recursos
Um servidor LSP tem funcionalidades que vão além da análise sintática do código, como por exemplo:
- ir para definições
- realizar ações de correção (code actions)
- exibir tipo ou documentação
Eu particularmente considero essas três as mais importantes. Portanto, mostrarei a seguir como configurá-las.
No entanto, caso precise de mais recursos, veja a lista completa aqui: https://neovim.io/doc/user/lsp.html#_lua-module:-vim.lsp.buf
Vamos adicionar três atalhos ao nosso arquivo lsp-config.lua, responsáveis por executar as funções acima. Veja como fica a versão completa (o código é autoexplicativo):
return {
{
"mason-org/mason.nvim",
opts = {},
},
{
"mason-org/mason-lspconfig.nvim",
opts = {
ensure_installed = { "lua_ls", }
},
},
{
"neovim/nvim-lspconfig",
config = function()
-- Exibe tipo/documentação sobre item (shift+k)
vim.keymap.set('n', 'K', vim.lsp.buf.hover, {})
-- Ir para a definição (g,d)
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, {})
-- Exibe lista de ações de correção (espaço,c,a)
vim.keymap.set('n', '<leader>ca', vim.lsp.buf.code_action, {})
-- Definição do Virtual Text
vim.diagnostic.config({
virtual_text = {
spacing = 4, -- Espaço entre o código e a mensagem
},
signs = true, -- Mostra ícones na lateral esquerda
underline = true, -- Sublinha o texto com problema
update_in_insert = false, -- Atualiza apenas fora do modo insert (evita distração)
severity_sort = true, -- Ordena por severidade
})
end,
},
}Obs.: Você também pode definir esses atalhos em outro lugar, como por exemplo dentro do init.lua. Coloque-os onde melhor lhe servir!
Como de costume salve, saia e entre novamente neste mesmo arquivo. Posicione o cursor sobre o campo opts.
Agora, ao digitarmos a tecla de atalho recém-definida K (shift+k) veja que temos as informações sobre o tipo da variável:

O mesmo acontece no código Python logo abaixo. Ao posicionarmos o cursor sobre a chamada da função saudacao() e pressionarmos shift+k:

O Neovim, através do LSP, exibe toda a descrição da função:

De forma semelhante, ao pressionar a tecla g e em seguida, a tecla d (abreviação de go to definition) com o cursor ainda sobre a chamada da função saudação(), você irá diretamente para ela:

Para finalizar, também já temos o conveniente “code actions” à nossa disposição através do atalho espaço, c, a.
Como exemplo, vamos retornar ao arquivo lsp-config.lua. Note que o lua_ls faz alguns alertas sobre o objeto vim quando retornamos ao modo normal do editor:

Quando posicionamos o cursor sobre ele e pressionamos a sequência de teclas espaço, c, a, o Neovim abre uma lista de sugestões de solução para o alerta:

Se você digitar a opção 1, os alertas serão “ignorados” e não mais aparecerão. O mesmo procedimento também funciona para tentar solucionar erros!
Code Actions mais bonito
Temos que concordar que a lista de sugestões do code actions não é nem um pouco atrativa. Pensando nisso, a comunidade criou um plugin que se chama telescope-ui-select.nvim.
Esse plugin pega a lista de sugestões e direciona seu conteúdo para o telescope. Assim, podemos usar a interface e o mecanismo dele para aplicar as alterações que desejamos.
Então vamos instalá-lo adicionando outra tabela Lua, mas desta vez no arquivo de spec do telescope (telescope.lua):
nvim ~/.config/nvim/lua/plugins/telescope.luaO conteúdo do arquivo completo fica desta maneira:
return {
{
'nvim-telescope/telescope.nvim', tag = '0.1.8',
dependencies = { 'nvim-lua/plenary.nvim' },
config = function()
local builtin = require('telescope.builtin')
vim.keymap.set('n', '<leader>ff', builtin.find_files, { desc = 'Telescope find files' })
vim.keymap.set('n', '<leader>fg', builtin.live_grep, { desc = 'Telescope live grep' })
vim.keymap.set('n', '<leader>fb', builtin.buffers, { desc = 'Telescope buffers' })
vim.keymap.set('n', '<leader>fh', builtin.help_tags, { desc = 'Telescope help tags' })
end
},
{
'nvim-telescope/telescope-ui-select.nvim',
config = function()
require("telescope").setup({
extensions = {
["ui-select"] = {
require("telescope.themes").get_dropdown {
}
}
}
})
require("telescope").load_extension("ui-select")
end
}
}Salve, saia e retorne ao lsp-config.lua. Repita o procedimento para gerar a lista de sugestões do code actions e veja como fica agora:

Bem melhor, não é mesmo?
Instalando Linters e Formatadores
Conforme mencionado na introdução, os linters são analisadores estáticos de código responsáveis, basicamente, por detectar erros de sintaxe e más práticas de programação.
Os formatadores de código, por sua vez, reescrevem o código de forma automática, aplicando regras de estilo predefinidas visando a padronização.
Como os servidores de linguagem não possuem essas funcionalidades, precisamos de ferramentas externas que executem tais funções.
Essas ferramentas são instaladas através do próprio Mason, no entanto, assim como os servidores de linguagem, elas precisam de um plugin intermediário para funcionarem com o editor.
Esse plugin chama-se none-ls e sua responsabilidade é integrar linters e formatadores ao protocolo LSP, de forma que o Neovim os utilize como se fossem funcionalidades do próprio servidor de linguagem.
Para exemplificar, vamos primeiramente instalar um linter (selene) e um formatador (stylua) para a linguagem Lua com o Mason.
Já que temos que fazer isso através do Neovim, vamos aproveitar para abri-lo com um arquivo .lua em branco que receberá a spec do none-ls:
nvim .config/nvim/lua/plugins/none-ls.luaUma vez dentro do Neovim, digite :Mason e em seguida digite o número quatro para selecionar a aba Linter. Após, digite /selene<enter> para descer a lista até ele. Por fim, digite i para instalá-lo. (certifique-se de ter o unzip instalado)

Após isso, instale o stylua digitando o número cinco (Formatter), em seguida procure (/stylua<enter>) e instale com i:

Tudo pronto! Agora só falta conectar tudo com o none-ls!
Instalando e configurando o none-ls
Como já estamos com o arquivo vazio none-ls.lua aberto no Neovim, simplesmente copie e cole o conteúdo abaixo:
return {
'nvimtools/none-ls.nvim',
dependencies = { 'nvim-lua/plenary.nvim' },
config = function()
local null_ls = require('null-ls')
null_ls.setup {
sources = {
null_ls.builtins.formatting.stylua,
null_ls.builtins.diagnostics.selene,
},
}
vim.keymap.set('n', '<leader>gf', vim.lsp.buf.format, {})
end,
}Então salve, saia e retorne ao mesmo arquivo. Veja agora que o selene aponta erros e alertas como se fosse um servidor de linguagem Lua:

O problema aqui é que como utilizamos Lua de uma forma um pouco “diferente” do que na programação convencional, o selene aponta erros e más práticas de programação inexistentes.
No exemplo acima, ele alerta que a tabela Lua possui tipos de valores diferentes. Além disso, entende que não existe o objeto vim, sendo que ele é fornecido automaticamente pelo Neovim.
Nesse caso o selene ajuda por um lado, mas acaba atrapalhando por outro. Contornamos esse problema adicionando comentários que ignoram o problema, conforme abaixo:

Veja que agora não temos mais os chatos falsos alertas poluindo nossa tela. Você encontrará mais informações sobre outros tipos de erros/alertas no Github do Selene.
Conclusão
Nesse momento temos um editor bastante robusto, leve e, desde que instaladas as devidas ferramentas, capaz de entender a sintaxe de praticamente qualquer linguagem.
Vamos recapitular o que adicionamos ao Neovim desde o artigo passado:
- Gerenciador de Plugins com Lazy.nvim
- Temas (Catppuccin)
- Explorador de arquivos com Neotree
- Buscador inteligente com Telescope
- Analisador de Sintaxe com Treesitter
- Barra de status com Lualine
- Servidores de linguagem com Mason
- Integração dos servidores com Mason-lspconfig
- Configuração e inicialização dos servidores com Neovim-lspconfig
- Linters e Formatadores com Mason
- Integração e configuração dos linters e formatadores com None-ls
No entanto ainda faltam alguns recursos muito importantes para atingirmos nosso objetivo: auto-complete de código, debugadores, terminal integrado e alguns ajustes finos.
Portanto fique ligado e acompanhe o terceiro e último artigo da jornada rumo ao Neovim IDE!
Espero ter ajudado!
Até o próximo artigo!
