Easily read your project's local settings files and merge them into your Neovim 0.11+ native LSP configuration.
This plugin makes it easy to reuse settings your team already committed to version control for VS Code by
providing an API to merge the relevant settings from VS Code's settings schema into the LSP settings table you pass
to vim.lsp.config() (or any way you configure LSP).
- Neovim 0.11+ (uses the new
vim.lsp.config()API) - A JSON(C) file in your project root with LSP settings (optional; if missing, your config is returned unchanged).
Paths are configurable, but by default, the plugin looks for any of:
.vscode/settings.jsoncodesettings.jsonlspsettings.json
For some features (namely, jsonls integration and jsonc filetype handling), you must call setup().
- lazy.nvim (recommended)
return {
'mrjones2014/codesettings.nvim',
-- these are the default settings just set `opts = {}` to use defaults
opts = {
---Look for these config files
config_file_paths = { '.vscode/settings.json', 'codesettings.json', 'lspsettings.json' },
---Integrate with jsonls to provide LSP completion for LSP settings based on schemas
jsonls_integration = true,
---Set filetype to jsonc when opening a file specified by `config_file_paths`,
---make sure you have the jsonc tree-sitter parser installed for highlighting
jsonc_filetype = true,
---Provide your own root dir; can be a string or function returning a string.
---It should be/return the full absolute path to the root directory.
---If not set, defaults to `require('codesettings.util').get_root()`
root_dir = nil,
---Choose the default merge behavior
default_merge_opts = {
--- How to merge lists; 'replace' (default), 'append', or 'prepend'
list_behavior = 'append',
},
},
-- I recommend loading on these filetype so that the
-- jsonls integration and jsonc filetype setup works
ft = { 'json', 'jsonc' },
}Recommended setup: If you don't use before_init for anything else, you can use it as a global hook
to look for local config files for all LSPs:
vim.lsp.config('*', {
before_init = function(_, config)
local codesettings = require('codesettings')
config = codesettings.with_local_settings(config.name, config)
end,
})Alternatively, you can configure it on a per-server basis.
-- you can also still use `before_init` here
-- if you want codesettings to be `require`d
-- lazily
local codesettings = require('codesettings')
vim.lsp.config(
'yamlls',
codesettings.with_local_settings('yamlls', {
settings = {
yaml = {
validate = true,
schemaStore = { enable = true },
},
},
}, {
-- you can also pass custom merge opts on a per-server basis
list_behavior = 'replace',
})
)
-- or from a config file under `/lsp/rust-analyzer.lua` in your config directory.
-- if you use rustaceanvim to configure rust-analyzer, see the `rustaceanvim` section below
return codesettings.with_local_settings('rust-analyzer', {
settings = {
-- ...
},
})The before_init global hook does not work if you use rustaceanvim
to configure rust-analyzer, however you can still use codesettings.nvim to merge local settings.
rustaceanvim loads VS Code settings by default, but your global settings override the local ones; codesettings.nvim
does the opposite. Here's how I configure rustaceanvim in my own setup:
return {
'mrcjkb/rustaceanvim',
ft = 'rust',
version = '^6',
dependencies = { 'mrjones2014/codesettings.nvim' },
init = function()
vim.g.rustaceanvim = {
-- the rest of your settings go here...
-- I want VS Code settings to override my settings,
-- not the other way around, so use codesettings.nvim
-- instead of rustaceanvim's built-in vscode settings loader
load_vscode_settings = false,
-- the global hook doesn't work when configuring rust-analyzer with rustaceanvim
settings = function(_, config)
return require('codesettings').with_local_settings('rust-analyzer', config)
end,
default_settings = {
['rust-analyzer'] = {
-- your global LSP settings go here
},
},
}
end,
}- Minimal API: one function you call per server setup, or with a global hook (see example below)
jsoncfiletype for local config filesjsonlsintegration for schema-based completion of LSP settings in JSON(C) configuration files
- Lua type annotations generated from schemas for autocomplete when writing LSP configs in Lua

- Supports custom config file names/locations
- Supports mixed nested and dotted key paths, for example, this project's
codesettings.jsonlooks like:
To get autocomplete in Lua files:
-- for example, for lua_ls
vim.lsp.config('lua_ls', {
-- this '@module' annotation makes lua_ls import the library from codesettings,
-- where the annotations come from
---@module 'codesettings'
-- then you will have access to the generated type annotations
---@type lsp.lua_ls
settings = {},
}):Codesettings show- show the resolved LSP config for each active LSP client; note that this only shows active clients:Codesettings local- show the resolved local config found in local config files in your project:Codesettings files- show the config files found in your project:Codesettings edit- edit or create a local config file based on your configured config file paths:Codesettings health- check plugin health (alias for:checkhealth codesettings)
-
require('codesettings').setup(opts?: CodesettingsConfig)- Initialize the plugin. You only need to call this for
jsonls_integrationandjsonc_filetypeto work, or to customize the local filepaths to look for. It is not required for your local configs to take effect, unless you wish to use non-default plugin configuration.
- Initialize the plugin. You only need to call this for
-
require('codesettings').with_local_settings(lsp_name: string, config: table): table- Loads settings from the configured files, extracts relevant settings for the given LSP based on its schema, and deep-merges into
config.settings. Returns the merged config.
- Loads settings from the configured files, extracts relevant settings for the given LSP based on its schema, and deep-merges into
-
require('codesettings').local_settings(lsp_name: string|nil): Settings- Loads and parses the settings file(s) for the current project. Returns a
Settingsobject. - If
lsp_nameis specified, filters down to only the relevant properties according to the LSP's schema. Settingsobject provides some methods like:Settings:schema(lsp_name)- Filter the settings down to only the keys that match the relevant schema e.g.settings:schema('eslint')Settings:merge(settings, key, merge_opts)- merge anotherSettingsobject into this one, optionally specify a sub-key to merge, and control merge behavior with the 2nd and 3rd parameter, respectivelySettings:get(key)- returns the value at the specified key; supports dot-separated key paths likeSettings:get('some.sub.property')Settings:get_subtable(key)- likeSettings:get(key), but returns aSettingsobject if the path is a table, otherwisenilSettings:clear()- remove all valuesSettings:set(key, value)- supports dot-separated key paths likesome.sub.property
- Loads and parses the settings file(s) for the current project. Returns a
Example using local_settings() directly:
local codesettings = require('codesettings')
local eslint_settings = c.local_settings()
:schema('eslint')
:merge({
eslint = {
codeAction = {
disableRuleComment = {
enable = true,
location = 'sameLine',
},
},
},
})
:get('eslint.codeAction') -- get the codeAction subtable- Root discovery uses
vim.fs.rootto search upwards with markers based on your configured config file paths, as well as.gitand.jj(for Jujutsu repos) - The plugin checks each path in
config_file_pathsunder your project root and uses any that exist
Follows the semantics of vim.tbl_deep_extend('force', your_config, local_config), essentially:
- The plugin deep-merges plain tables (non-list tables)
- List/array values are appended by default; you can change this behavior in configuration or through the API
- Your provided
configis the base; values from the settings file override or extend it withinconfig.settings
codesettings.nvim |
neoconf.nvim |
|
|---|---|---|
| Minimum Neovim version | Neovim >= 0.11.0 | Neovim >= 0.7.2 |
Depends on nvim-lspconfig |
No (but will still work with it if you choose to use it) | Yes |
| Supports mixed nested and dotted key paths | Yes | No |
| Customizable list value merging behavior | Yes | No |
jsonls integration |
Yes, including mixed nested and dotted key paths | Yes |
jsonc filetype support |
Yes | Yes |
setup() required |
Only for some editor integration features | Yes |
| Loading settings | API call | Automatic through nvim-lspconfig hooks |
The tl;dr: is if you wish to use nvim-lspconfig, then neoconf.nvim is more automatic but provides fewer features, and seems to be unmaintained.
If you want to get rid of nvim-lspconfig and just use vim.lsp.config() APIs, then codesettings.nvim provides an API to load local project settings for you,
as well as better autocomplete in configuration files, and autocomplete in Lua files not using nvim-lspconfig based on Lua type annotations.
This project would not exist without the hard work of some other open source projects!
- Some parts of this plugin are based on folke's neoconf.nvim plugin
- This plugin bundles json.lua, a pure-Lua JSON library for parsing
jsoncfiles
- als
- astro
- awkls
- basedpyright
- bashls
- clangd
- cssls
- dartls
- denols
- elixirls
- elmls
- eslint
- flow
- fsautocomplete
- grammarly
- haxe_language_server
- hhvm
- hie
- html
- intelephense
- java_language_server
- jdtls
- jsonls
- julials
- kotlin_language_server
- ltex
- lua_ls
- luau_lsp
- nixd
- omnisharp
- perlls
- perlnavigator
- perlpls
- powershell_es
- psalm
- puppet
- purescriptls
- pylsp
- pyright
- r_language_server
- rescriptls
- rls
- rome
- ruff_lsp
- rust_analyzer
- solargraph
- solidity_ls
- sonarlint
- sorbet
- sourcekit
- spectral
- stylelint_lsp
- svelte
- svlangserver
- tailwindcss
- terraformls
- tinymist
- ts_ls
- typst_lsp
- volar
- vtsls
- vuels
- wgls_analyzer
- yamlls
- zeta_note
- zls
{ "Lua": { "runtime.version": "LuaJIT", "workspace": { "library": ["${3rd}/luassert/library", "${addons}/busted/library"], "checkThirdParty": false, }, "diagnostics.globals": ["vim", "setup", "teardown"], }, }