Neovim plugin for lspcontainers - developed weekly live at The Alt-F4 Stream on Twitch.
IMPORTANT: everything below is a work-in-progress and subject to change at any time
Provide a simple method for running language servers in Docker containers using neovim/nvim-lspconfig
. This plugin expects the same language server names from neovim/nvim-lspconfig
. See neovim/nvim-lspconfig/doc/server_configurations.md for a complete list of servers.
-
Install latest Docker Engine for your operating system
-
Install
lspconfig
andlspcontainers
via package manager
-
via
packer
manageruse 'neovim/nvim-lspconfig' use 'lspcontainers/lspcontainers.nvim'
-
via
plug
managerPlug 'neovim/nvim-lspconfig' Plug 'lspcontainers/lspcontainers.nvim'
- Setup the language of your choice from Supported LSPs
You can add configurations to more languages by providing an image and the command to start the container by adding the following options to lspcontainers commands:
NOTE: LspContainers makes no attempt to modify LspConfig. It is up to the end user to correctly configure LspConfig.
lspconfig.html.setup {
on_attach = on_attach,
capabilities = capabilities,
cmd = lspcontainers.command('html', {
image = "lspcontainers/html-language-server:1.4.0",
cmd = function (runtime, volume, image)
return {
runtime,
"container",
"run",
"--interactive",
"--rm",
"--volume",
volume,
image
}
end,
}),
root_dir = require'lspconfig/util'.root_pattern(".git", vim.fn.getcwd()),
}
The cmd
option of the lspcontainers config also allows modification of the command that is used to start the container for possibly mounting additional volumes or similar things.
In some circumstances a language server may need the root_dir
path synced with the Docker container. To sync up the volume mount with the lspconfig's root_dir
, use on_new_config
:
local server = "lua_ls"
require'lspconfig'[server].setup{
on_new_config = function(new_config, new_root_dir)
new_config.cmd = require'lspcontainers'.command(server, { root_dir = new_root_dir })
end
}
You can either mount a path on host or a docker volume
You can create a volume (docker_volume) and mount it at path (workdir).
docker create volume persistent_volume_projects
require'lspconfig'.omnisharp.setup {
capabilities = capabilities,
before_init = before_init_process_id_nil,
cmd = require'lspcontainers'.command(
'omnisharp',
{
workdir = /projects
docker_volume = 'persistent_volume_projects',
}
),
on_new_config = on_new_config,
on_attach = on_attach,
root_dir = require'lspconfig/util'.root_pattern("*.sln", vim.fn.getcwd()),
}
You can mount a volume from your host by just assigning workdir to match the host path.
require'lspconfig'.omnisharp.setup {
capabilities = capabilities,
before_init = before_init_process_id_nil,
cmd = require'lspcontainers'.command(
'omnisharp',
{
workdir = /home/[UserName]/projects
}
),
on_new_config = on_new_config,
on_attach = on_attach,
root_dir = require'lspconfig/util'.root_pattern("*.sln", vim.fn.getcwd()),
}
If you are using podman instead of docker it is sufficient to just specify "podman" as container_runtime
:
lspconfig.gopls.setup {
on_attach = on_attach,
capabilities = capabilities,
cmd = lspcontainers.command('gopls', {
container_runtime = "podman",
}),
root_dir = require'lspconfig/util'.root_pattern(".git", vim.fn.getcwd()),
}
By default, LSPs that don't require network access run with network=none
.
This means the container has no network access. If you have a special case
where an LSP needs network access, you can specify this explicitly:
cmd = lspcontainers.command('mylsp', {
-- ...
network = "bridge",
}),
If you find that an LSP that commonly requires network access doesn't have this
by default, please open a PR updating its default (see init.lua
).
The LSP spec allows a client to send its process id to a language server, so that the server can exit immediately when it detects that the client is no longer running.
This feature fails to work properly no a containerized language server because the host and the container do not share the container namespace by default.
A container can share a process namespace with the host by passing the
--pid=host
flag to docker/podman, although it should be noted that this
somewhat reduces isolation.
It is also possible to simply disable the process id detection. This can be
done with the following before_init
function:
require'lspconfig'.bashls.setup {
before_init = function(params)
params.processId = vim.NIL
end,
cmd = require'lspcontainers'.command('bashls'),
}
This is required for several LSPs, and they will exit immediately if this is not specified.
Below is a list of supported language servers for configuration with nvim-lspconfig
. Follow a link to find documentation for that config.
- bashls
- clangd
- denols
- dockerls
- gopls
- graphql
- html
- intelephense
- jsonls
- omnisharp
- powershell_es
- prismals
- pylsp
- pyright
- rust_analyzer
- solargraph
- lemminx
- lua_ls
- svelte
- tailwindcss
- terraformls
- tsserver
- vuels
- yamlls
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#bashls
require'lspconfig'.bashls.setup {
before_init = function(params)
params.processId = vim.NIL
end,
cmd = require'lspcontainers'.command('bashls'),
root_dir = require'lspconfig/util'.root_pattern(".git", vim.fn.getcwd()),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#clangd
require'lspconfig'.clangd.setup {
cmd = require'lspcontainers'.command('clangd'),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#denols
require'lspconfig'.denols.setup {
before_init = function(params)
params.processId = vim.NIL
end,
cmd = require'lspcontainers'.command('denols'),
root_dir = require'lspconfig/util'.root_pattern("deno.json", vim.fn.getcwd()),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#dockerls
require'lspconfig'.dockerls.setup {
before_init = function(params)
params.processId = vim.NIL
end,
cmd = require'lspcontainers'.command('dockerls'),
root_dir = require'lspconfig/util'.root_pattern(".git", vim.fn.getcwd()),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#gopls
require'lspconfig'.gopls.setup {
cmd = require'lspcontainers'.command('gopls'),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#graphql
require'lspconfig'.graphql.setup {
before_init = function(params)
params.processId = vim.NIL
end,
cmd = require'lspcontainers'.command('graphql'),
root_dir = require'lspconfig/util'.root_pattern(".git", vim.fn.getcwd()),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#html
require'lspconfig'.html.setup {
before_init = function(params)
params.processId = vim.NIL
end,
cmd = require'lspcontainers'.command('html'),
root_dir = require'lspconfig/util'.root_pattern(".git", vim.fn.getcwd()),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#intelephense
require'lspconfig'.intelephense.setup {
before_init = function(params)
params.processId = vim.NIL
end,
cmd = require'lspcontainers'.command('intelephense'),
root_dir = require'lspconfig/util'.root_pattern("composer.json", ".git", vim.fn.getcwd()),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#jsonls
require'lspconfig'.jsonls.setup {
before_init = function(params)
params.processId = vim.NIL
end,
cmd = require'lspcontainers'.command('jsonls'),
root_dir = require'lspconfig/util'.root_pattern(".git", vim.fn.getcwd()),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#omnisharp
require'lspconfig'.omnisharp.setup {
before_init = function(params)
params.processId = vim.NIL
end,
cmd = require'lspcontainers'.command('omnisharp'),
root_dir = require'lspconfig/util'.root_pattern("*.sln", "*.csproj", vim.fn.getcwd()),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#powershell_es
require'lspconfig'.powershell_es.setup {
before_init = function(params)
params.processId = vim.NIL
end,
on_new_config = function(new_config, new_root_dir)
new_config.cmd = require'lspcontainers'.command(server, {root_dir = new_root_dir})
end,
cmd = require'lspcontainers'.command(server),
filetypes = {"ps1", "psm1", "psd1"},
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#prismals
require'lspconfig'.prismals.setup {
before_init = function(params)
params.processId = vim.NIL
end,
cmd = require'lspcontainers'.command('prismals'),
root_dir = require'lspconfig/util'.root_pattern(".git", vim.fn.getcwd()),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#pylsp
require'lspconfig'.pylsp.setup {
cmd = require'lspcontainers'.command('pylsp'),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#pyright
require'lspconfig'.pyright.setup {
before_init = function(params)
params.processId = vim.NIL
end,
cmd = require'lspcontainers'.command('pyright'),
root_dir = require'lspconfig/util'.root_pattern(".git", vim.fn.getcwd()),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#rust_analyzer
require'lspconfig'.rust_analyzer.setup {
cmd = require'lspcontainers'.command('rust_analyzer'),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#solargraph
require'lspconfig'.solargraph.setup {
cmd = require'lspcontainers'.command('solargraph'),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#lemminx
require'lspconfig'.lemminx.setup {
cmd = require'lspcontainers'.command('lemminx'),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#lua_ls
require'lspconfig'.lua_ls.setup {
cmd = require'lspcontainers'.command('lua_ls'),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#svelte
require'lspconfig'.svelte.setup {
before_init = function(params)
params.processId = vim.NIL
end,
cmd = require'lspcontainers'.command('svelte'),
root_dir = require'lspconfig/util'.root_pattern(".git", vim.fn.getcwd()),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#tailwindcss
require'lspconfig'.tailwindcss.setup {
before_init = function(params)
params.processId = vim.NIL
end,
cmd = require'lspcontainers'.command('tailwindcss'),
filetypes = { "django-html", "htmldjango", "gohtml", "html", "markdown", "php", "css", "postcss", "sass", "scss", "stylus", "javascript", "javascriptreact", "rescript", "typescript", "typescriptreact", "vue", "svelte" },
root_dir = require'lspconfig/util'.root_pattern("tailwind.config.js", "tailwind.config.ts", "postcss.config.js", "postcss.config.ts", "package.json", "node_modules", ".git", vim.fn.getcwd()),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#terraformls
require'lspconfig'.terraformls.setup {
cmd = require'lspcontainers'.command('terraformls'),
filetypes = { "hcl", "tf", "terraform", "tfvars" },
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#tsserver
require'lspconfig'.tsserver.setup {
before_init = function(params)
params.processId = vim.NIL
end,
cmd = require'lspcontainers'.command('tsserver'),
root_dir = require'lspconfig/util'.root_pattern(".git", vim.fn.getcwd()),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#yamlls
require'lspconfig'.yamlls.setup {
before_init = function(params)
params.processId = vim.NIL
end,
cmd = require'lspcontainers'.command('yamlls'),
root_dir = require'lspconfig/util'.root_pattern(".git", vim.fn.getcwd()),
...
}
https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#vuels
require'lspconfig'.vuels.setup {
before_init = function(params)
params.processId = vim.NIL
end,
cmd = require'lspcontainers'.command('vuels'),
root_dir = require'lspconfig/util'.root_pattern(".git", vim.fn.getcwd()),
...
}
To contribute to LSPs, please see the lspcontainers/dockerfiles repository.