diff --git a/codesettings.json b/codesettings.json index ba51c0e..6c9ff1a 100644 --- a/codesettings.json +++ b/codesettings.json @@ -3,13 +3,14 @@ "runtime": { "version": "LuaJIT", "path": [ + "?.lua", "lua/?.lua", "lua/?/init.lua", "lua/?/?.lua", "plugin/?.lua", "ftplugin/?.lua", "spec/?.lua" - ] + ], }, "workspace": { "library": [ @@ -23,5 +24,5 @@ "vim" ] }, - "codesettings.live_reload": true + "codesettings.live_reload": true, } diff --git a/lua/codesettings/setup/jsonls/init.lua b/lua/codesettings/setup/jsonls/init.lua index 1cb80b1..c611b6f 100644 --- a/lua/codesettings/setup/jsonls/init.lua +++ b/lua/codesettings/setup/jsonls/init.lua @@ -57,27 +57,21 @@ function M.get_json_schemas() }, } - -- make sure we don't clobber any already configured schemas - local configured_schemas = vim.tbl_get(vim.lsp.config, 'jsonls', 'settings', 'json', 'schemas') or {} - - _cache = vim.list_extend(vim.deepcopy(configured_schemas), json_schemas) + _cache = json_schemas return _cache end function M.setup() - vim.lsp.config('jsonls', { + local config_update = { settings = { json = { schemas = M.get_json_schemas(), validate = { enable = true }, }, }, - }) + } - -- lazy loading; if jsonls is already active, restart it - vim.defer_fn(function() - Util.did_change_configuration('jsonls', vim.lsp.config.jsonls, true) - end, 500) + Util.ensure_lsp_settings('jsonls', config_update) end return M diff --git a/lua/codesettings/setup/live-reload.lua b/lua/codesettings/setup/live-reload.lua index e6e6bc0..c71959c 100644 --- a/lua/codesettings/setup/live-reload.lua +++ b/lua/codesettings/setup/live-reload.lua @@ -5,7 +5,7 @@ local M = {} ---@type number|nil local augroup = nil ----@type {[string]: uv_timer_t} +---@type {[string]: uv.uv_timer_t} local debounce_timers = {} ---Reload settings for all active LSP clients diff --git a/lua/codesettings/setup/lua_ls.lua b/lua/codesettings/setup/lua_ls.lua index da57798..3c0d92c 100644 --- a/lua/codesettings/setup/lua_ls.lua +++ b/lua/codesettings/setup/lua_ls.lua @@ -4,23 +4,17 @@ local M = {} function M.setup() ---@type lsp.lua_ls - local lua_ls_settings = (vim.lsp.config.lua_ls or {}).settings or {} - local library = vim.tbl_get(lua_ls_settings, 'Lua', 'workspace', 'library') or {} - vim.list_extend(library, { Util.path('lua/codesettings/generated') }) - vim.lsp.config('lua_ls', { + local config_update = { settings = { Lua = { workspace = { - library = library, + library = { Util.path('lua/codesettings/generated') }, }, }, }, - }) + } - -- lazy loading; if lua_ls is already active, restart it - vim.defer_fn(function() - Util.did_change_configuration('lua_ls', vim.lsp.config.lua_ls, true) - end, 500) + Util.ensure_lsp_settings('lua_ls', config_update) end return M diff --git a/lua/codesettings/util.lua b/lua/codesettings/util.lua index c6a9006..721d16f 100644 --- a/lua/codesettings/util.lua +++ b/lua/codesettings/util.lua @@ -389,4 +389,43 @@ function M.did_change_configuration(client_or_name, config, silent) end) end +---Ensure these LSP settings are always applied, forcing `merge_lists = 'append'`. +---This is useful for the built-in jsonls and lua_ls integrations, which rely on +---adding schemas and library paths, respectively. +---@param lsp_name string name of the LSP server +---@param config { settings: table } settings to ensure +function M.ensure_lsp_settings(lsp_name, config) + local group = vim.api.nvim_create_augroup('codesettings_' .. lsp_name, { clear = true }) + + -- Handler function to update a client's config + local function update_client(client) + if client.name ~= lsp_name then + return false + end + + -- Deep merge the config update into the client's existing config + client.config.settings = M.merge(client.config.settings or {}, config.settings or {}, { merge_lists = 'append' }) + + -- Notify the server of the configuration change + M.did_change_configuration(lsp_name, client.config, true) + return true + end + + -- Update any already-running clients + for _, client in ipairs(vim.lsp.get_clients({ name = lsp_name })) do + update_client(client) + end + + -- Set up autocmd for future clients + vim.api.nvim_create_autocmd('LspAttach', { + group = group, + callback = function(args) + local client = vim.lsp.get_client_by_id(args.data.client_id) + if client then + update_client(client) + end + end, + }) +end + return M diff --git a/spec/jsonls_spec.lua b/spec/jsonls_spec.lua index 72a05d0..f6a48ea 100644 --- a/spec/jsonls_spec.lua +++ b/spec/jsonls_spec.lua @@ -27,11 +27,51 @@ describe('jsonls integration', function() }, }) - local schemas = Jsonls.get_json_schemas() + Jsonls.setup() + vim.lsp.enable('jsonls') + -- Create a mock jsonls client + local mock_client = { + id = 999, + name = 'jsonls', + config = { + settings = { + json = { + schemas = { existing_schema }, + }, + }, + }, + } + -- Temporarily override vim.lsp.get_client_by_id to return our mock + local original_get_client = vim.lsp.get_client_by_id + vim.lsp.get_client_by_id = function(id) ---@diagnostic disable-line: duplicate-set-field + if id == 999 then + return mock_client + end + return original_get_client(id) + end + + -- Fire the LspAttach autocmd + vim.api.nvim_exec_autocmds('LspAttach', { + data = { client_id = 999 }, + }) + + -- Restore original function + vim.lsp.get_client_by_id = original_get_client + -- Fire the LspAttach autocmd + vim.api.nvim_exec_autocmds('LspAttach', { + data = { client_id = 999 }, + }) + + -- Restore original function + vim.lsp.get_client_by_id = original_get_client + + -- Check that existing schema is still present + local schemas = mock_client.config.settings.json.schemas + assert.truthy(vim.tbl_contains(schemas, existing_schema)) - local codesettings_schemas = require('codesettings.build.schemas').get_schemas() - assert.True(#schemas > #codesettings_schemas) - assert.same(existing_schema, schemas[1]) + -- Check that codesettings schemas were added + assert.True(#schemas > 1) + assert.same(schemas[#schemas], Jsonls.get_json_schemas()[1]) end) end) end)