aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorhalfcrazy2026-01-25 15:25:25 +0800
committerGitHub2026-01-25 16:25:25 +0900
commitc0dd8167a6fc469105e2cd8ef8a4e73cdf04e4d8 (patch)
tree4ad5c2b0f6aa26413adae1ad5ae0ba606e4921bc
parent8eb4803da99a575bc827a6c814e63b1053b7002f (diff)
downloadale-c0dd8167a6fc469105e2cd8ef8a4e73cdf04e4d8.tar.gz

fix gopls without setting alegogoplsinitoptions (#5059)

-rw-r--r--lua/ale/lsp.lua8
-rw-r--r--test/linter/test_gopls.vader22
-rw-r--r--test/lua/ale_lsp_spec.lua48
3 files changed, 78 insertions, 0 deletions
diff --git a/lua/ale/lsp.lua b/lua/ale/lsp.lua
index 6c54db126..5df3097ab 100644
--- a/lua/ale/lsp.lua
+++ b/lua/ale/lsp.lua
@@ -8,6 +8,14 @@ module.start = function(config)
config.init_options[true] = nil
end
+ -- ensure init_options uses empty_dict if empty
+ if type(config.init_options) == "table"
+ and next(config.init_options) == nil
+ and getmetatable(config.init_options) == nil
+ then
+ config.init_options = vim.empty_dict()
+ end
+
-- If configuring LSP via a socket connection, then generate the cmd
-- using vim.lsp.rpc.connect(), as defined in Neovim documentation.
if config.host then
diff --git a/test/linter/test_gopls.vader b/test/linter/test_gopls.vader
index 1c91fa103..26fcf9d93 100644
--- a/test/linter/test_gopls.vader
+++ b/test/linter/test_gopls.vader
@@ -94,3 +94,25 @@ Execute('go.mod' should be ignored if modules are off):
call delete(b:git_dir, 'd')
unlet! b:parent_dir
unlet! b:git_dir
+
+Execute(Init options should handle empty and non-empty values correctly):
+ " Regression test for: Invalid settings: invalid options type []interface {}
+ " This ensures empty init_options {} is passed as an object, not array
+ call ale#test#SetFilename('../test-files/go/go1/prj1/file.go')
+
+ " Test 1: Default empty dict
+ AssertLSPOptions {}
+
+ " Test 2: Explicitly set to empty
+ let b:ale_go_gopls_init_options = {}
+ AssertLSPOptions {}
+
+ " Test 3: Non-empty options should be preserved
+ let b:ale_go_gopls_init_options = {
+ \ 'ui.diagnostic.analyses': {'composites': v:false},
+ \ 'completeUnimported': v:true
+ \}
+ AssertLSPOptions {
+ \ 'ui.diagnostic.analyses': {'composites': v:false},
+ \ 'completeUnimported': v:true
+ \}
diff --git a/test/lua/ale_lsp_spec.lua b/test/lua/ale_lsp_spec.lua
index b37fbe287..9a74cea68 100644
--- a/test/lua/ale_lsp_spec.lua
+++ b/test/lua/ale_lsp_spec.lua
@@ -13,6 +13,10 @@ describe("ale.lsp.start", function()
defer_fn = function(func, delay)
table.insert(defer_calls, {func, delay})
end,
+ empty_dict = function()
+ -- Returns a table with a metatable to distinguish it from arrays
+ return setmetatable({}, {__empty_dict = true})
+ end,
fn = setmetatable({}, {
__index = function(_, key)
return function(...)
@@ -105,6 +109,50 @@ describe("ale.lsp.start", function()
eq({}, vim_fn_calls)
end)
+ it("should convert empty init_options to vim.empty_dict", function()
+ -- Mock vim.empty_dict
+ local empty_dict_called = false
+ _G.vim.empty_dict = function()
+ empty_dict_called = true
+ return setmetatable({}, {__empty_dict = true})
+ end
+
+ lsp.start({
+ name = "gopls:/code",
+ cmd = "gopls",
+ root_dir = "/code",
+ -- Empty table without metatable (like from VimScript {})
+ init_options = {},
+ })
+
+ -- Verify that empty_dict was called
+ eq(true, empty_dict_called)
+
+ -- Verify init_options has metatable now
+ eq(1, #start_calls)
+ local init_opts = start_calls[1][1].init_options
+ eq(true, getmetatable(init_opts) ~= nil)
+ end)
+
+ it("should preserve non-empty init_options", function()
+ lsp.start({
+ name = "gopls:/code",
+ cmd = "gopls",
+ root_dir = "/code",
+ init_options = {foo = "bar", nested = {baz = 123}},
+ })
+
+ -- Remove functions we can't compare
+ for _, args in pairs(start_calls) do
+ args[1].handlers = nil
+ args[1].on_init = nil
+ args[1].get_language_id = nil
+ end
+
+ eq(1, #start_calls)
+ eq({foo = "bar", nested = {baz = 123}}, start_calls[1][1].init_options)
+ end)
+
it("should start lsp socket connections with the correct arguments", function()
lsp.start({
name = "localhost:1234:/code",