Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ 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)
codesettings.with_local_settings(config.name, config)
end,
})
```
Expand Down Expand Up @@ -208,6 +208,7 @@ vim.lsp.config('lua_ls', {

- `require('codesettings').with_local_settings(lsp_name: string, config: table, opts: CodesettingsConfigOverrides?): 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.
- This **mutates the input `config` table.** This is necessary for some workflows to ensure the `vim.lsp` module sees the updated settings.

- `require('codesettings').local_settings(opts: CodesettingsConfigOverrides?): Settings`
- Loads and parses the settings file(s) for the current project. Returns a `Settings` object.
Expand Down Expand Up @@ -439,4 +440,3 @@ This project would not exist without the hard work of some other open source pro
- [x] [yamlls](https://github.com/redhat-developer/vscode-yaml/tree/master/package.json)
- [x] [zeta_note](https://github.com/artempyanykh/zeta-note-vscode/tree/main/package.json)
- [x] [zls](https://github.com/zigtools/zls-vscode/tree/master/package.json)

4 changes: 2 additions & 2 deletions lua/codesettings/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ function M.local_settings(opts)
return Settings.load_all(opts)
end

---Load settings from VS Code settings.json file
---Load settings from VS Code settings.json file. This mutates the given LSP config.
---@param lsp_name string the name of the LSP, like 'rust-analyzer' or 'tsserver'
---@param config table the LSP config to merge the vscode settings into
---@param config vim.lsp.Config|vim.lsp.ClientConfig the LSP config to merge the vscode settings into
---@param opts CodesettingsConfigOverrides? optional config overrides for this load
---@return table config the merged config
function M.with_local_settings(lsp_name, config, opts)
Expand Down
13 changes: 11 additions & 2 deletions lua/codesettings/settings.lua
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,17 @@ function Settings:merge(settings, key, config)
settings = M.new(settings)
end
if key then
local value = Util.merge(Util.merge({}, self:get(key) or {}, config), settings._settings, config)
self:set(key, value)
local existing = self:get(key)
if existing then
-- Mutate the existing table in place to preserve references
local value = Util.merge(existing, settings._settings, config)
-- technically `Util.merge` will mutate the value,
-- but set here to be explicit
self:set(key, value)
else
-- No existing value, safe to set directly
self:set(key, settings._settings)
end
else
self._settings = Util.merge(self._settings, settings._settings, config)
end
Expand Down
5 changes: 4 additions & 1 deletion lua/codesettings/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,10 @@ function M.merge(a, b, config)
if type(v) ~= 'table' then
return false
end
if vim.islist(v) then
-- NB: vim.islist({}) returns true for empty tables, but we want to treat
-- empty tables as mergeable maps, not lists so that an empty settings table
-- gets mutated rather than replaced.
if vim.islist(v) and #v > 0 then
return false
end
return true
Expand Down
18 changes: 18 additions & 0 deletions spec/settings_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,24 @@ describe('Settings:merge()', function()
A:merge(B)
assert.equal(2, A:get('opt.value'))
end)

it('works with an empty settings = {} table', function()
local config = {
settings = {},
}

local A = Settings.new(config)
local B = Settings.new()
B:set('settings.gopls.buildFlags', { '-tags=bsd' })
A:merge(B)
assert.same({
settings = {
gopls = {
buildFlags = { '-tags=bsd' },
},
},
}, config)
end)
end)

describe('Settings:clear()', function()
Expand Down
8 changes: 4 additions & 4 deletions spec/util_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ describe('util.merge - scalar merge', function()
assert.equal(2, merged)
end)

it('nil right side keeps left if right nil (expected: becomes nil because b takes precedence)', function()
it('empty table on right side preserves left values', function()
local a = { value = 5 }
local b = { value = nil }
local b = {} -- { value = nil } is essentially equivalent to {}
local merged = Util.merge(a, b)
-- Current contract: b takes precedence even if nil
assert.is_nil(merged.value)
-- Empty tables are now treated as maps and merged, so left side is preserved
assert.equal(5, merged.value)
end)
end)

Expand Down