iTranslated by AI
The Shortest LSP Setup for Neovim Beginners (v0.11, as of 2025-10-04)
There are many articles out there summarizing Neovim's LSP settings, and this is one of them.
However, existing articles are often outdated or are migration guides intended for users who have already configured LSP.
This article is a guide for those starting Neovim now, in October 2025.
While this article will eventually become outdated as well, I will explain the steps to set up LSP in the shortest way possible at this point in time.
For those already using older versions of Neovim, here are some migration articles:
- From Neovim 0.11, LSP can be handled almost entirely with native APIs
- LSP settings for Neovim 0.11
- 📜2025-04-27 Configuring optimal LSP settings for Neovim 0.11 with nvim-lspconfig v2 - Minerva
- nvim-lspconfig support for nvim v0.11 for busy people
- Using Neovim v0.11 native completion API with lazy.nvim+Mason 2.0 - Watasuke's Room
Prior Knowledge
- LSP (Language Server Protocol) is a specification (protocol) for providing language features (completion, code navigation, etc.) to code editors.
- Official page for Language Server Protocol
- Basic knowledge for configuring Neovim's LSP
- You might occasionally see people referring to a Language Server as "LSP," but please keep in mind that LSP is the protocol itself, so avoid confusing it with the language server.
- In Neovim, unlike IDEs like VSCode, all LSP-related settings are opt-in.
- This means you need to configure them yourself.
- Built-in standard APIs for LSP have been enhanced since Neovim 0.11.
- Lsp - Neovim docs
- Note that the settings have changed significantly from 0.10 and earlier versions.
- Common terms related to LSP:
-
lazy.nvim: folke/lazy.nvim: 💤 A modern plugin manager for Neovim- Plugin manager.
- While you can use other plugin managers, I will use the
lazy.nvimformat here. - Also, I am assuming that a plugin manager is already installed.
-
nvim-lspconfig: neovim/nvim-lspconfig: Quickstart configs for Nvim LSP- A plugin that provides a collection of presets for language servers.
- As mentioned, all settings are opt-in, so you need to configure them yourself. However, thanks to the presets provided by nvim-lspconfig, you can usually get things working just by loading and enabling them.
- Of course, you can also override and customize these settings.
-
mason.nvim: mason-org/mason.nvim: Portable package manager for Neovim that runs everywhere Neovim runs. Easily install and manage LSP servers, DAP servers, linters, and formatters.- A package manager plugin that allows you to install and manage language servers within Neovim.
- In addition to language servers, it can manage Debug Adapters, Linters, Formatters, etc.
-
mason-lspconfig: mason-org/mason-lspconfig.nvim: Extension to mason.nvim that makes it easier to use lspconfig with mason.nvim.- A plugin that helps automatically enable language servers installed via
mason.nvim.
- A plugin that helps automatically enable language servers installed via
-
LSP-related Configuration
Now, let's start the configuration.
Here, we will configure the Lua language server (lua-language-server) necessary for writing Neovim settings. Please install lua-language-server on your machine in advance.
Also, to keep the explanation as simple as possible and reduce dependencies, I will explain the method using only Neovim's standard APIs and nvim-lspconfig (and a plugin manager).
For instructions on using mason.nvim or mason-lspconfig, please refer to other articles.
Directory Structure
Let's follow the standard Neovim directory structure. It starts from ~/.config/nvim/init.lua.
$ tree ~/.config/nvim/
.
├── after # Automatically loaded after init.lua loading is complete
│ └── lsp # Language server overrides are automatically loaded
│ └── lua_ls.lua
├── init.lua # Entry point
└── lua # Loaded from init.lua
├── config
│ ├── lazy.lua
│ └── lsp.lua
└── plugins
└── nvim-lspconfig.nvim
Reference: Lua-guide - Neovim docs
Description of Each File
The following explains the contents of each file.
In init.lua, we load lazy.nvim and LSP-related configuration files.
require("config.lazy")
require("config.lsp")
In lua/config/lazy.lua, configure lazy.nvim and define where plugin settings will be located. If you have already configured it in a different location, please adjust as necessary.
-- lazy.nvim configuration
-- Ref: https://lazy.folke.io/installation
-- ...
-- Setup lazy.nvim
require("lazy").setup({
spec = {
-- Automatically load lua files under lua/plugins/ as lazy.nvim Plugin Specs
{ import = "plugins" },
},
})
For now, let's skip the LSP-related settings and write the nvim-lspconfig configuration first. By simply writing the Plugin Spec under the lua/plugins directory configured earlier, lazy.nvim will automatically handle installation and setup.
---@type LazyPluginSpec
return {
"neovim/nvim-lspconfig",
-- Lazy load when a buffer is read or a new file is created
event = { "BufReadPre", "BufNewFile" },
}
Now that we've configured nvim-lspconfig, let's set up the LSP-related configuration we skipped earlier.
vim.lsp.enable({
-- The preset configured in nvim-lspconfig as "lua_ls" will be loaded
-- https://github.com/neovim/nvim-lspconfig/blob/master/lsp/lua_ls.lua
"lua_ls",
-- Other language server settings
-- "gopls",
})
-- Called when a language server is attached
vim.api.nvim_create_autocmd("LspAttach", {
group = vim.api.nvim_create_augroup("my.lsp", {}),
callback = function(args)
local client = assert(vim.lsp.get_client_by_id(args.data.client_id))
local buf = args.buf
-- Add settings to the default language server keybindings
-- See https://neovim.io/doc/user/lsp.html#lsp-defaults
-- The logic is to add settings if the language server client implements the functions defined in LSP
if client:supports_method("textDocument/definition") then
vim.keymap.set("n", "gd", vim.lsp.buf.definition, { buffer = buf, desc = "Go to definition" })
end
if client:supports_method("textDocument/hover") then
vim.keymap.set("n", "<leader>k",
function() vim.lsp.buf.hover({ border = "single" }) end,
{ buffer = buf, desc = "Show hover documentation" })
end
if client:supports_method("textDocument/completion") then
vim.lsp.completion.enable(true, client.id, args.buf, { autotrigger = true })
end
-- Auto-format ("lint") on save.
-- Usually not needed if server supports "textDocument/willSaveWaitUntil".
if not client:supports_method("textDocument/willSaveWaitUntil")
and client:supports_method("textDocument/formatting") then
vim.api.nvim_create_autocmd("BufWritePre", {
group = vim.api.nvim_create_augroup("my.lsp", { clear = false }),
buffer = args.buf,
callback = function()
vim.lsp.buf.format({ bufnr = args.buf, id = client.id, timeout_ms = 1000 })
end,
})
end
if client:supports_method("textDocument/inlineCompletion") then
vim.lsp.inline_completion.enable(true, { bufnr = buf })
vim.keymap.set("i", "<Tab>", function()
if not vim.lsp.inline_completion.get() then
return "<Tab>"
end
-- close the completion popup if it's open
if vim.fn.pumvisible() == 1 then
return "<C-e>"
end
end, {
expr = true,
buffer = buf,
desc = "Accept the current inline completion",
})
end
end,
})
Up to this point, the configuration for the Lua language server is complete.
In this state, if you start Neovim, place the cursor on a symbol in a Lua file, and press <leader>k (<leader> is the space key by default), you should be able to see information provided by the language server.
This is also possible because nvim-lspconfig provides a preset for the Lua language server.
However, since the Lua language server is not specialized for Vim/Neovim, it will likely issue a warning because it lacks information about the vim keyword used in vim.lsp.enable. Finally, let's set up a configuration to clear this warning.
The after/ directory is a special directory that is loaded after the normal loading process is finished. By returning the language server settings under the after/lsp/ directory, Neovim will automatically call these settings. nvim-lspconfig is essentially a collection of pre-written settings for each language server that should originally be written here.
The global vim keyword is defined in Lua files bundled with Neovim. You can access the directory containing these Lua files via vim.env.VIMRUNTIME .. "/lua". For example, if you installed Neovim via Homebrew, this should point to something like /opt/homebrew/share/nvim/runtime/lua.
By creating after/lsp/lua_ls.lua and adding vim.env.VIMRUNTIME .. "/lua" to the settings.Lua.workspace.library array, you can load the definition of the vim keyword and clear the warning.
---@type vim.lsp.Config
return {
settings = {
Lua = {
workspace = {
library = {
vim.env.VIMRUNTIME .. "/lua",
},
},
},
},
}
With this, you have successfully cleared the warning.
Conclusion
Enjoy your Neovim life!
Discussion
globalsの設定で警告を消すのも良いですが、Neovimに同梱されている型定義を読み込ませるとより正確な情報が得られてハッピーかもしれません100%定義されているわけではないので、一部警告は出てしまいますが……
確かに、"/opt/homebrew/share/nvim/runtime/lua" (これは
vim.env.VIMRUNTIMEでbrewの依存を剥がせそうです) でグローバルのvimが型定義されているのでそっちを使うのが良さそうですね。実は自分の手元ではそれも併せて設定しているんですが、ここでは最小限のカスタムを行うために
settings.Lua.diagnostics.globalsだけを書いていました。修正しておきます、ご指摘ありがとうございます!