Lua API
setup()
This is the function doing all the work. The LSP servers and autocompletion are not configured until this function is called. It should be the last function you call.
local lsp = require('lsp-zero').preset({})
lsp.on_attach(function(client, bufnr)
lsp.default_keymaps({buffer = bufnr})
end)
lsp.setup()
preset(opts)
Here is where you can add settings specific to lsp-zero. The default configuration is described in the minimal preset.
You can override any value of the preset by using a lua table, like this.
local lsp = require('lsp-zero').preset({
float_border = 'none',
})
In order to remain compatible with the v1.x
branch {opts}
can be a string with the name of a preset.
local lsp = require('lsp-zero').preset('minimal')
It supports the following presets:
When using a lua table as argument you can pass the property name
to specify which preset you wish to use. And of course, you can still override the configuration in the preset.
local lsp = require('lsp-zero').preset({
name = 'recommended',
call_servers = 'global',
})
If you don't specify a preset then minimal will be the default.
Preset settings
Options supported by presets.
set_lsp_keymaps
It can be a boolean or a lua table.
Supported properties:
preserve_mappings
. Boolean. When set totrue
lsp-zero will not override any shortcut that is already "taken". When set tofalse
lsp-zero the LSP shortcuts will be created no matter what.omit
. List of strings. List of shorcuts you don't want lsp-zero to override.
When set_lsp_keymaps is set to true then preserve_mappings
is assumed to be false and omit
is set to an empty list. When set_lsp_keymaps is false then the keybindings will not be created.
manage_nvim_cmp
It can be a boolean or a lua table. When is set to a boolean every supported property will have that value.
Supported properties:
set_basic_mappings
. Boolean. When set to true it will create keybindings that emulate Neovim's default completion.set_extra_mappings
. Boolean. When set to true it will setup tab completion, scrolling through documentation window, and navigation between snippets.set_sources
. String or Boolean. When set to'lsp'
it will only setup cmp-nvim-lsp as a source. When set to'recommended'
it will try to setup a few recommended sources for nvim-cmp. When set to the Booleanfalse
it won't setup any sources.use_luasnip
. Boolean. When set to true it will setup luasnip to expand snippets. This option does not include a collection of snippets.set_format
. Boolean. When set to true it'll modify the "format" of the completion items.documentation_window
. Boolean. When set to true enables the documentation window.
setup_servers_on_start
Boolean. When set to true all servers installed with mason.nvim will be initialized on startup. If the value is false
servers will be initialized when you call .configure() or .setup_servers().
call_servers
String. When set to local
it will use mason.nvim whenever possible. When set to global
any feature or support that dependes on mason.nvim will be disabled.
configure_diagnostics
Boolean. When set to true adds borders and sorts "severity" of diagnostics.
float_border
String. Shape of borders in floating windows. It can be one of the following: 'none'
, 'single'
, 'double'
, 'rounded'
, 'solid'
or 'shadow'
.
Available presets
minimal
Enables the support for mason.nvim if it is installed. Configures the diagnostics. Adds a basic setup to nvim-cmp. It doesn't add keybindings for LSP or autocompletion. Doesn't setup the sources for nvim-cmp.
These are the settings it uses.
{
float_border = 'rounded',
call_servers = 'local',
configure_diagnostics = true,
setup_servers_on_start = true,
set_lsp_keymaps = false,
manage_nvim_cmp = {
set_sources = 'lsp',
set_basic_mappings = true,
set_extra_mappings = false,
use_luasnip = true,
set_format = true,
documentation_window = true,
},
}
recommended
Creates keybindings bound to LSP actions. Configures diagnostics. Adds a complete configuration to nvim-cmp. And enables support for mason.nvim.
These are the settings it uses.
{
float_border = 'rounded',
call_servers = 'local',
configure_diagnostics = true,
setup_servers_on_start = true,
set_lsp_keymaps = {
preserve_mappings = false,
omit = {},
},
manage_nvim_cmp = {
set_sources = 'recommended',
set_basic_mappings = true,
set_extra_mappings = false,
use_luasnip = true,
set_format = true,
documentation_window = true,
},
}
lsp-only
Is base on recommended but it disables the support for nvim-cmp.
manage_nvim_cmp = false
manual-setup
Is based on recommended but it disables the automatic setup of language servers.
setup_servers_on_start = false
system-lsp
Is based on recommended but it disables all the features that depends on mason.nvim.
setup_servers_on_start = false
call_servers = 'global'
default_keymaps(opts)
Create the keybindings bound to built-in LSP functions.
The {opts}
table supports the same properties as set_lsp_keymaps and adds the following:
- buffer: Number. The "id" of an open buffer. If the number
0
is provided then the keymaps will be effective in the current buffer.
LSP Actions
K
: Displays hover information about the symbol under the cursor in a floating window. See :help vim.lsp.buf.hover().gd
: Jumps to the definition of the symbol under the cursor. See :help vim.lsp.buf.definition().gD
: Jumps to the declaration of the symbol under the cursor. Some servers don't implement this feature. See :help vim.lsp.buf.declaration().gi
: Lists all the implementations for the symbol under the cursor in the quickfix window. See :help vim.lsp.buf.implementation().go
: Jumps to the definition of the type of the symbol under the cursor. See :help vim.lsp.buf.type_definition().gr
: Lists all the references to the symbol under the cursor in the quickfix window. See :help vim.lsp.buf.references().gs
: Displays signature information about the symbol under the cursor in a floating window. See :help vim.lsp.buf.signature_help(). If a mapping already exists for this key this function is not bound.<F2>
: Renames all references to the symbol under the cursor. See :help vim.lsp.buf.rename().<F3>
: Format code in current buffer. See :help vim.lsp.buf.format().<F4>
: Selects a code action available at the current cursor position. See :help vim.lsp.buf.code_action().gl
: Show diagnostics in a floating window. See :help vim.diagnostic.open_float().[d
: Move to the previous diagnostic in the current buffer. See :help vim.diagnostic.goto_prev().]d
: Move to the next diagnostic. See :help vim.diagnostic.goto_next().
on_attach(callback)
Executes the {callback}
function every time a server is attached to a buffer.
This is where you can declare your own keymaps and commands.
local lsp = require('lsp-zero').preset({})
lsp.on_attach(function(client, bufnr)
lsp.default_keymaps({buffer = bufnr}) -- add lsp-zero defaults
local opts = {buffer = bufnr}
local bind = vim.keymap.set
bind('n', '<leader>r', '<cmd>lua vim.lsp.buf.rename()<cr>', opts)
-- more code ...
end)
lsp.setup()
ensure_installed(list)
If you have support for mason.nvim enabled it will install all the servers in {list}
. The names of the servers should match the ones listed here: server_configurations.
local lsp = require('lsp-zero').preset({})
lsp.on_attach(function(client, bufnr)
lsp.default_keymaps({buffer = bufnr})
end)
lsp.ensure_installed({
'tsserver',
'eslint',
'rust_analyzer',
})
lsp.setup()
skip_server_setup(list)
All the language servers in {list}
will be ignored during setup.
local lsp = require('lsp-zero').preset({})
lsp.on_attach(function(client, bufnr)
lsp.default_keymaps({buffer = bufnr})
end)
lsp.skip_server_setup({'eslint', 'rust_analyzer'})
lsp.setup()
setup_servers(list)
Will configure all the language servers you have on {list}
.
This is useful when you disable the automatic setup of language servers.
local lsp = require('lsp-zero').preset({
setup_servers_on_start = false,
})
lsp.on_attach(function(client, bufnr)
lsp.default_keymaps({buffer = bufnr})
end)
lsp.setup_servers({'tsserver', 'rust_analyzer'})
lsp.setup()
configure(name, opts)
Gathers the arguments for a particular language server. {name}
must be a string with the name of language server in this list: server_configurations. And {opts}
is a lua table with the options for that server. These options are the same nvim-lspconfig uses in their setup function, see :help lspconfig-setup for more details.
local lsp = require('lsp-zero').preset({})
lsp.on_attach(function(client, bufnr)
lsp.default_keymaps({buffer = bufnr})
end)
lsp.configure('tsserver', {
single_file_support = false,
on_attach = function(client, bufnr)
print('hello tsserver')
end
})
lsp.setup()
store_config(name, opts)
Saves the configuration options for a language server, so you can use it at a later time in a local config file.
use(name, opts)
For when you want you want to add more settings to a particular language server in a particular project. It is meant to be called in project local config (but you can still use it in your init.lua).
Ideally, you would setup some default values for your servers in your neovim config using .configure(), or maybe .store_config() if you don't use any presets.
-- init.lua
local lsp = require('lsp-zero')
lsp.on_attach(function(client, bufnr)
lsp.default_keymaps({buffer = bufnr})
end)
lsp.configure('pyright', {
single_file_support = false,
})
lsp.setup()
And then in your local config you can tweak the server options even more.
-- local config
local lsp = require('lsp-zero')
lsp.use('pyright', {
settings = {
python = {
analysis = {
extraPaths = {'/path/to/my/dependencies'},
}
}
}
})
Options from .configure() will be merged with the ones on .use()
and the server will restart with the new config.
lsp-zero does not execute files. It only provides utility functions. So to execute your "local config" you'll have to use another plugin.
set_server_config(opts)
It will share the configuration options with all the language servers lsp-zero initializes. These options are the same nvim-lspconfig uses in their setup function, see :help lspconfig-setup.
Here is an example that enables the folding capabilities and disable single file support.
lsp.set_server_config({
single_file_support = false,
capabilities = {
textDocument = {
foldingRange = {
dynamicRegistration = false,
lineFoldingOnly = true
}
}
}
})
build_options(name, opts)
Returns all the parameters lsp-zero uses to initialize a language server. This includes default capabilities and settings that were added using the .set_server_config() function.
nvim_lua_ls(opts)
Returns settings specific to Neovim for the lua language server, lua_ls.
local lsp = require('lsp-zero').preset({})
lsp.on_attach(function(client, bufnr)
lsp.default_keymaps({buffer = bufnr})
end)
require('lspconfig').lua_ls.setup(lsp.nvim_lua_ls())
lsp.setup()
If you provide the {opts} table it'll merge it with the defaults, this way you can extend or change the values easily.
require('lspconfig').lua_ls.setup(
lsp.nvim_lua_ls({
single_file_support = false,
on_attach = function(client, bufnr)
print('hello there')
end,
})
)
If you provide the {opts}
table it'll merge it with the defaults, this way you can extend or change the values easily.
format_on_save(opts)
Setup autoformat on save. This will to allow you to associate a language server with a list of filetypes.
Keep in mind it's only meant to allow one LSP server per filetype, this is so the formatting is consistent.
{opts}
supports the following properties:
servers: (Table) Key/value pair list. On the left hand side you must specify the name of a language server. On the right hand side you must provide a list of filetypes, this can be any pattern supported by the
FileType
autocommand.format_opts: (Table). These are the options you can pass to vim.lsp.buf.format().
When you enable async formatting the only argument in format_opts
that will have any effect are formatting_options
and timeout_ms
, the rest will be ignored.
local lsp = require('lsp-zero').preset({})
lsp.on_attach(function(client, bufnr)
lsp.default_keymaps({buffer = bufnr})
end)
lsp.format_on_save({
format_opts = {
async = false,
timeout_ms = 10000,
},
servers = {
['lua_ls'] = {'lua'},
['rust_analyzer'] = {'rust'},
}
})
lsp.setup()
buffer_autoformat(client, bufnr, opts)
Format the current buffer using the active language servers.
If {client} argument is provided it will only use the LSP server associated with that client.
client: (Table, Optional) if provided it must be a lua table with a
name
property or an instance of vim.lsp.client.bufnr: (Number, Optional) if provided it must be the id of an open buffer.
opts: (Table). These are the same options you can pass to vim.lsp.buf.format().
local lsp = require('lsp-zero').preset({})
lsp.on_attach(function(client, bufnr)
lsp.default_keymaps({buffer = bufnr})
lsp.buffer_autoformat()
end)
lsp.setup()
async_autoformat(client, bufnr, opts)
Send a formatting request to {client}
. After the getting the response from the client it will save the file (again).
Here is how it works: when you save the file Neovim will write your changes without formatting. Then, lsp-zero will send a request to {client}
, when it gets the response it will apply the formatting and save the file again.
client: (Table) It must be an instance of vim.lsp.client.
bufnr: (Number, Optional) if provided it must be the id of an open buffer.
opts: (Table, Optional) Supports the following properties:
formatting_options: (Table, Optional) Settings send to the language server. These are the same settings as the
formatting_options
argument in vim.lsp.buf.format().timeout_ms: (Number, Optional) Defaults to 10000. Time in milliseconds to ignore the current format request.
Do not use this in the global on_attach
, call this function with the specific language server you want to format with.
local lsp = require('lsp-zero').preset({})
lsp.on_attach(function(client, bufnr)
lsp.default_keymaps({buffer = bufnr})
end)
require('lspconfig').tsserver.setup({
on_attach = function(client, bufnr)
lsp.async_autoformat(client, bufnr)
end
})
lsp.setup()
format_mapping(key, opts)
Configure {key}
to format the current buffer.
The idea here is that you associate a language server with a list of filetypes, so {key}
can format the buffer using only one LSP server.
{opts}
supports the following properties:
servers: (Table) Key/value pair list. On the left hand side you must specify the name of a language server. On the right hand side you must provide a list of filetypes, this can be any pattern supported by the
FileType
autocommand.format_opts: (Table). These are the options you can pass to vim.lsp.buf.format().
mode: (Table). The list of modes where the keybinding will be active. By default is set to
{'n', 'x'}
, which means normal mode and visual mode.
local lsp = require('lsp-zero').preset({})
lsp.on_attach(function(client, bufnr)
lsp.default_keymaps({buffer = bufnr})
end)
lsp.format_mapping('gq', {
format_opts = {
async = false,
timeout_ms = 10000,
},
servers = {
['lua_ls'] = {'lua'},
['rust_analyzer'] = {'rust'},
}
})
lsp.setup()
new_server(opts)
lsp-zero will execute a user provided function to detect the root directory of the project when Neovim assigns the file type for a buffer. If the root directory is detected the LSP server will be attached to the file.
This function does not depend on lspconfig
, it's a thin wrapper around a Neovim function called vim.lsp.start().
{opts}
supports every property vim.lsp.start
supports with a few changes:
filestypes
: Can be list filetype names. This can be any pattern theFileType
autocommand accepts.root_dir
: Can be a function, it'll be executed after Neovim assigns the file type for a buffer. If it returns a string that will be considered the root directory for the project.
Other important properties are:
cmd
: (Table) A lua table with the arguments necessary to start the language server.name
: (String) This is the name Neovim will assign to the client object.on_attach
: (Function) A function that will be executed after the language server gets attached to a buffer.
Here is an example that starts the typescript language server on javascript and typescript, but only in a project that package.json in the current directory or any of its parent folders.
local lsp = require('lsp-zero')
lsp.on_attach(function()
lsp.default_keymaps({buffer = bufnr})
end)
lsp.new_server({
name = 'tsserver',
cmd = {'typescript-language-server', '--stdio'},
filetypes = {'javascript', 'typescript'},
root_dir = function()
return lsp.dir.find_first({'package.json'})
end
})
set_sign_icons(opts)
Defines the sign icons that appear in the gutter.
local lsp = require('lsp-zero').preset({})
lsp.on_attach(function(client, bufnr)
lsp.default_keymaps({buffer = bufnr})
end)
lsp.set_sign_icons({
error = '✘',
warn = '▲',
hint = '⚑',
info = '»'
})
lsp.setup()
highlight_symbol(client, bufnr)
Uses the CursorHold
event to trigger a document highlight request. In other words, it will highlight the symbol under the cursor.
For this to work properly your colorscheme needs to set these highlight groups: LspReferenceRead
, LspReferenceText
and LspReferenceWrite
.
Keep in mind the event CursorHold
depends on the updatetime
option. If you want the highlight to happen fast, you will need to set this option to a "low" value.
vim.opt.updatetime = 350
local lsp = require('lsp-zero').preset({})
lsp.on_attach(function(client, bufnr)
lsp.default_keymaps({buffer = bufnr})
lsp.highlight_symbol(client, bufnr)
end)
lsp.setup()
dir.find_first(list)
Checks the parent directories and returns the path to the first folder that has a file in {list}
. This is useful to detect the root directory.
Note: search will stop once it gets to your "HOME" folder.
{list}
supports the following properties:
path: (String) The path from where it should start looking for the files in
{list}
.buffer: (Boolean) When set to
true
use the path of the current buffer.
local lsp = require('lsp-zero')
lsp.on_attach(function(client, bufnr)
lsp.default_keymaps({buffer = bufnr})
end)
require('lspconfig').lua_ls.setup({
root_dir = function()
--- project root will be the first directory that has
--- either .luarc.json or .stylua.toml
return lsp.dir.find_first({'.luarc.json', '.stylua.toml'})
end
})
lsp.setup()
dir.find_all(list)
Checks the parent directories and returns the path to the first folder that has all the files in {list}
.
Note: search will stop once it gets to your "HOME" folder.
{list}
supports the following properties:
path: (String) The path from where it should start looking for the files in
{list}
.buffer: (Boolean) When set to
true
use the path of the current buffer.
local lsp = require('lsp-zero')
lsp.on_attach(function(client, bufnr)
lsp.default_keymaps({buffer = bufnr})
end)
require('lspconfig').vuels.setup({
root_dir = function()
--- project root will be the directory that has
--- package.json + vetur config
return lsp.dir.find_all({'package.json', 'vetur.config.js'})
end
})
lsp.setup()
extend_lspconfig(opts)
The purpose of this function is to allow you to interact with lspconfig
directly and still get some features lsp-zero offers.
It "extends" the default configuration in lspconfig
by adding the capabilities
provided by cmp_nvim_lsp. And, it creates an autocommand that executes a function everytime a language server is attached to a buffer.
Note: don't use it along side .setup(). It is meant to be independent of any settings provided by presets.
This is the intended usage:
require('mason').setup()
require('mason-lspconfig').setup()
require('lsp-zero').extend_lspconfig()
require('lspconfig').tsserver.setup({})
Notice here it can coexists with other plugins. Allowing you to have full control of your configuration.
{opts}
table supports the following properties:
set_lsp_keymaps
: it supports the same properties as the preset counter part.capabilities
: These are the "client capabilities" a language server expects. This argument will be merge nvim-cmp's default capabilities if you have it installed.on_attach
: This must be a function. Think of it as "global"on_attach
so you don't have to keep passing a function to each server's setup function.
Here's an example that showcase each option.
-- There is no need to copy any of this
require('lsp-zero').extend_lspconfig({
set_lsp_keymaps = {omit = {'gs', 'gl'}},
on_attach = function(client, bufnr)
print('hello there')
end,
capabilities = {
textDocument = {
foldingRange = {
dynamicRegistration = false,
lineFoldingOnly = true
}
}
}
})
cmp_action()
Is a function that returns methods meant to be used as mappings for nvim-cmp.
These are the supported methods:
tab_complete
: Enables completion when the cursor is inside a word. If the completion menu is visible it will navigate to the next item in the list. If the line is empty it uses the fallback.select_prev_or_fallback
: If the completion menu is visible navigate to the previous item in the list. Else, uses the fallback.toggle_completion
: If the completion menu is visible it cancels the process. Else, it triggers the completion menu. You can use the propertymodes
in the first argument to specify where this mapping should active (the default is{modes = {'i'}}
).luasnip_jump_forward
: Go to the next placeholder in the snippet.luasnip_jump_backward
: Go to the previous placeholder in the snippet.luasnip_next
: If completion menu is visible it will navigate to the item in the list. If the cursor can jump to a snippet placeholder, it moves to it. Else, it uses the fallback.luasnip_next_or_expand
: If completion menu is visible it will navigate to the item in the list. If cursor is on top of the trigger of a snippet it'll expand it. If the cursor can jump to a snippet placeholder, it moves to it. Else, it uses the fallback.luasnip_supertab
: If the completion menu is visible it will navigate to the next item in the list. If cursor is on top of the trigger of a snippet it'll expand it. If the cursor can jump to a snippet placeholder, it moves to it. If the cursor is in the middle of a word that doesn't trigger a snippet it displays the completion menu. Else, it uses the fallback.luasnip_shift_supertab
: If the completion menu is visible it will navigate to previous item in the list. If the cursor can navigate to a previous snippet placeholder, it moves to it. Else, it uses the fallback.
Quick note: "the fallback" is the default behavior of the key you assign to a method.
extend_cmp(opts)
In case you don't want to use lsp-zero to actually setup any LSP servers, or want to lazy load nvim-cmp, you can use .extend_cmp
to setup nvim-cmp.
When you invoke this function it is assumed you want a "minimal" configuration. Meaning that if you call it without {opts}
it will use the same config the minimal preset uses in the setting manage_nvim_cmp.
This is completely valid.
require('lsp-zero').extend_cmp()
local cmp = require('cmp')
local cmp_action = require('lsp-zero').cmp_action()
cmp.setup({
sources = {
{name = 'nvim_lsp'},
},
window = {
completion = cmp.config.window.bordered(),
documentation = cmp.config.window.bordered(),
},
mapping = {
['<C-Space>'] = cmp.mapping.complete(),
['<C-y>'] = cmp.mapping.confirm({select = true}),
['<C-u>'] = cmp.mapping.scroll_docs(-4),
['<C-d>'] = cmp.mapping.scroll_docs(4),
['<C-f>'] = cmp_action.luasnip_jump_forward(),
['<C-b>'] = cmp_action.luasnip_jump_backward(),
}
})
{opts}
supports the same properties manage_nvim_cmp has.
omnifunc.setup(opts)
Configure the behavior of Neovim's completion mechanism. If for some reason you refuse to install nvim-cmp you can use this function to make the built-in completions more user friendly.
{opts}
supports the following properties:
autocomplete
: Boolean. Default value isfalse
. When enabled it triggers the completion menu if the character under the cursor matchesopts.keyword_pattern
. Completions will be disabled when you are recording a macro. Do note, the implementation here is extremely simple, there isn't any kind of optimizations in place. Is literally like pressing<Ctrl-x><Ctrl-o>
after you insert a character in a word.tabcomplete
: Boolean. Default value isfalse
. When enabled<Tab>
will trigger the completion menu if the cursor is in the middle of a word. When the completion menu is visible it will navigate to the next item in the menu. If there is a blank character under the cursor it inserts aTab
character.<Shift-Tab>
will navigate to the previous item in the menu, and if the menu is not visible it'll insert aTab
character.trigger
: String. It must be a valid keyboard shortcut. This will be used as a keybinding to trigger the completion menu manually. Actually, it will be able to toggle the completion menu. You'll be able to show and hide the menu with the same keybinding.use_fallback
: Boolean. Default value isfalse
. When enabled lsp-zero will try to complete using the words in the current buffer. And when an LSP server is attached to the buffer, it will replace the fallback completion with the LSP completions.keyword_pattern
: String. Regex pattern used by the autocomplete implementation. Default value is"[[:keyword:]]"
.update_on_delete
: Boolean. Default value isfalse
. Turns out Neovim will hide the completion menu when you delete a character, so when you enable this option lsp-zero will trigger the menu again after you press<backspace>
. This will only happen with LSP completions, the fallback completion updates automatically (again, this is Neovim's default behavior). This option is disabled by default because it requires lsp-zero to bind the backspace key, which may cause conflicts with other plugins.select_behavior
: String. Default value is"select"
. Configures what happens when you select an item in the completion menu. When the value is"insert"
Neovim will insert the text of the item in the buffer. When the value is"select"
nothing happens, Neovim will only highlight the item in the menu, the text in the buffer will not change.preselect
: Boolean. Default value istrue
. When enabled the first item in the completion menu will be selected automatically.verbose
: Boolean. Default value isfalse
. When enabled Neovim will show the state of the completion in message area.mapping
: Table. Defaults to an empty table. With this you can configure the keybinding for common actions.confirm
: Accept the selected completion item.abort
: Cancel current completion.next_item
: Navigate to next item in the completion menu.prev_item
: Navigate to previous item in the completion menu.
You can configure a basic "tab completion" behavior using these settings.
local lsp = require('lsp-zero')
lsp.omnifunc.setup({
tabcomplete = true,
use_fallback = true,
update_on_delete = true,
})
And here is an example for autocomplete.
local lsp = require('lsp-zero')
lsp.omnifunc.setup({
autocomplete = true,
use_fallback = true,
update_on_delete = true,
trigger = '<C-Space>',
})