Skip to content

Commit a991d6e

Browse files
committed
test(lsp-completion): simplify CompleteDonePre tests using fire_autocmd
1 parent 591f85a commit a991d6e

1 file changed

Lines changed: 33 additions & 90 deletions

File tree

tests/unit/completion_lsp_spec.lua

Lines changed: 33 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,6 @@ describe('opencode LSP completion', function()
664664

665665
describe('CompleteDonePre autocmd', function()
666666
local completion
667-
local augroup_id
668667

669668
before_each(function()
670669
package.loaded['opencode.ui.completion'] = nil
@@ -674,69 +673,38 @@ describe('opencode LSP completion', function()
674673
package.loaded['opencode.lsp.opencode_ls'] = nil
675674
ls = require('opencode.lsp.opencode_ls')
676675

677-
-- Use a scratch buffer so CompleteDonePre autocmds don't accumulate on buffer 0
678-
augroup_id = vim.api.nvim_create_augroup('test_completedone', { clear = true })
676+
ls.start(0)
679677
end)
680678

681679
after_each(function()
682-
-- Clear the autocmds registered by ls.start to avoid cross-test pollution
683680
vim.api.nvim_clear_autocmds({ event = 'CompleteDonePre', buffer = 0 })
684-
vim.api.nvim_del_augroup_by_id(augroup_id)
685681
end)
686682

687-
-- Fire CompleteDonePre with a nested user_data table.
688-
-- vim.v.completed_item only accepts a dict value, so we build it via nvim_input
689-
-- simulation: set via a temporary vimscript assignment.
690-
local function fire_complete_done_pre_with_data(user_data)
691-
-- We can't directly assign a nested Lua table to vim.v.completed_item because
692-
-- Neovim validates the type. Instead we invoke the autocmd callback directly
693-
-- by temporarily patching vim.v.completed_item through nvim_exec.
694-
vim.api.nvim_exec2('let v:completed_item = {}', { output = false })
695-
-- Trigger the autocmd so the registered callback reads vim.v.completed_item.
696-
-- To pass user_data we call the callback stored in the module directly.
697-
local autocmds = vim.api.nvim_get_autocmds({ event = 'CompleteDonePre', buffer = 0 })
698-
for _, au in ipairs(autocmds) do
699-
if au.callback then
700-
-- Temporarily make completed_item look like it has user_data by monkeypatching
701-
local orig = vim.v.completed_item
702-
-- We simulate the callback environment
703-
local saved = vim.v
704-
-- Instead of fighting vim.v, call the callback with a fake environment:
705-
-- patch the module's read of vim.v.completed_item
706-
local real_completed_item = vim.v.completed_item
707-
-- We patch getters at the module level isn't possible, so we call the
708-
-- callback directly after storing data into a local and overriding access.
709-
au.callback()
710-
end
711-
end
712-
end
713-
714-
-- Invoke the CompleteDonePre callback directly, bypassing vim.v assignment restrictions.
715-
-- We reach into the registered autocmd and call its Lua callback after patching
716-
-- vim.v.completed_item to the simplest valid value (empty dict), then override the
717-
-- module's behavior by injecting the item through the module's internal state.
718-
local function invoke_autocmd_callback()
719-
local autocmds = vim.api.nvim_get_autocmds({ event = 'CompleteDonePre', buffer = 0 })
720-
for _, au in ipairs(autocmds) do
721-
if au.callback then
722-
au.callback()
723-
end
724-
end
683+
local function fire_autocmd(user_data)
684+
vim.v.completed_item = { user_data = user_data }
685+
vim.api.nvim_exec_autocmds('CompleteDonePre', { buffer = 0 })
725686
end
726687

727688
it('sets _completion_done_handled to true when fired', function()
728-
ls.start(0)
729689
ls._completion_done_handled = false
730690

731-
-- Fire the autocmd with an empty completed_item (no user_data)
732-
vim.api.nvim_exec_autocmds('CompleteDonePre', { buffer = 0 })
691+
fire_autocmd({
692+
nvim = {
693+
lsp = {
694+
completion_item = {
695+
data = { _opencode_item = { source_name = 'test', label = 'test', data = {} } },
696+
},
697+
},
698+
},
699+
})
733700

734701
assert.is_true(ls._completion_done_handled)
735702
end)
736703

737704
it('calls on_completion_done via nvim lsp user_data path', function()
738705
local on_complete_called = false
739706
local received_item = nil
707+
local original_item = { label = 'AutoItem', source_name = 'autocmd_source', data = {} }
740708

741709
completion.register_source({
742710
name = 'autocmd_source',
@@ -748,28 +716,15 @@ describe('opencode LSP completion', function()
748716
end,
749717
})
750718

751-
-- Directly invoke the CompleteDonePre logic by calling the module's internal
752-
-- handler with simulated completed_item data, since vim.v.completed_item
753-
-- cannot hold a nested Lua table. We test the handler function directly.
754-
local original_item = { label = 'AutoItem', source_name = 'autocmd_source', data = {} }
755-
756-
-- Simulate what the autocmd callback does when user_data is present:
757-
-- (mirrors the logic in opencode_ls.lua lines 232-238)
758-
local fake_user_data = {
719+
fire_autocmd({
759720
nvim = {
760721
lsp = {
761722
completion_item = {
762723
data = { _opencode_item = original_item },
763724
},
764725
},
765726
},
766-
}
767-
local data = vim.tbl_get(fake_user_data, 'nvim', 'lsp', 'completion_item', 'data')
768-
or vim.tbl_get(fake_user_data, 'lsp', 'item', 'data')
769-
local item = data and data._opencode_item
770-
if item then
771-
completion.on_completion_done(item)
772-
end
727+
})
773728

774729
assert.is_true(on_complete_called)
775730
assert.are.same(original_item, received_item)
@@ -778,6 +733,7 @@ describe('opencode LSP completion', function()
778733
it('calls on_completion_done via lsp.item user_data path', function()
779734
local on_complete_called = false
780735
local received_item = nil
736+
local original_item = { label = 'AutoItem2', source_name = 'autocmd_source2', data = {} }
781737

782738
completion.register_source({
783739
name = 'autocmd_source2',
@@ -789,50 +745,33 @@ describe('opencode LSP completion', function()
789745
end,
790746
})
791747

792-
local original_item = { label = 'AutoItem2', source_name = 'autocmd_source2', data = {} }
793-
794-
-- Simulate the autocmd handler using the lsp.item path
795-
local fake_user_data = {
748+
fire_autocmd({
796749
lsp = {
797750
item = {
798751
data = { _opencode_item = original_item },
799752
},
800753
},
801-
}
802-
local data = vim.tbl_get(fake_user_data, 'nvim', 'lsp', 'completion_item', 'data')
803-
or vim.tbl_get(fake_user_data, 'lsp', 'item', 'data')
804-
local item = data and data._opencode_item
805-
if item then
806-
completion.on_completion_done(item)
807-
end
754+
})
808755

809756
assert.is_true(on_complete_called)
810757
assert.are.same(original_item, received_item)
811758
end)
812759

813760
it('does not error when user_data has no _opencode_item', function()
814761
assert.has_no.errors(function()
815-
local fake_user_data = { nvim = { lsp = { completion_item = { data = {} } } } }
816-
local data = vim.tbl_get(fake_user_data, 'nvim', 'lsp', 'completion_item', 'data')
817-
or vim.tbl_get(fake_user_data, 'lsp', 'item', 'data')
818-
local item = data and data._opencode_item
819-
if item then
820-
completion.on_completion_done(item)
821-
end
762+
fire_autocmd({ nvim = { lsp = { completion_item = { data = {} } } } })
822763
end)
823764
end)
824765

825766
it('does not error when completed_item has no user_data', function()
826-
ls.start(0)
827-
828767
assert.has_no.errors(function()
829-
-- Fire the autocmd; vim.v.completed_item defaults to {} with no user_data key
830768
vim.api.nvim_exec_autocmds('CompleteDonePre', { buffer = 0 })
831769
end)
832770
end)
833771

834772
it('prevents executeCommand from firing on_completion_done a second time', function()
835773
local call_count = 0
774+
local original_item = { label = 'DedupItem', source_name = 'dedup_autocmd', data = {} }
836775

837776
completion.register_source({
838777
name = 'dedup_autocmd',
@@ -843,17 +782,21 @@ describe('opencode LSP completion', function()
843782
end,
844783
})
845784

846-
-- Simulate CompleteDonePre path: set the flag and call on_completion_done once
847-
local original_item = { label = 'DedupItem', source_name = 'dedup_autocmd', data = {} }
848-
ls._completion_done_handled = false
849-
ls._completion_done_handled = true
850-
completion.on_completion_done(original_item)
785+
-- Fire CompleteDonePre; this sets _completion_done_handled and calls on_complete once
786+
fire_autocmd({
787+
nvim = {
788+
lsp = {
789+
completion_item = {
790+
data = { _opencode_item = original_item },
791+
},
792+
},
793+
},
794+
})
851795

852796
assert.are.equal(1, call_count)
853797

854-
-- Simulate the completion engine then sending workspace/executeCommand
855-
-- Because _completion_done_handled is true, it should skip the second call
856-
ls.start(0)
798+
-- Simulate the completion engine then sending workspace/executeCommand.
799+
-- Because _completion_done_handled is true, it should skip the second call.
857800
local config_obj = ls.create_config()
858801
local server = config_obj.cmd({}, {})
859802
server.request('workspace/executeCommand', {

0 commit comments

Comments
 (0)