diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 2da7d777dd..2de4be9822 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -650,7 +650,7 @@ static void do_autocmd_completedone(int c, int mode, char *word) tv_dict_add_str(v_event, S_LEN("complete_type"), mode_str != NULL ? mode_str : ""); tv_dict_add_str(v_event, S_LEN("reason"), - (compl_used_match ? "accept" : (c == Ctrl_E ? "cancel" : "discard"))); + (c == Ctrl_Y || word != NULL ? "accept" : (c == Ctrl_E ? "cancel" : "discard"))); tv_dict_set_keys_readonly(v_event); ins_apply_autocmds(EVENT_COMPLETEDONE); @@ -2646,6 +2646,16 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval) redrawWinline(curwin, curwin->w_cursor.lnum); } + // When a match was inserted but the pum was never displayed + // (eg: only one match with 'completeopt' "menu" without "menuone"), + // the user had no opportunity to explicitly accept or dismiss it, + // so treat this as an implicit accept (#38160). + if (word == NULL && c != Ctrl_E && compl_used_match && compl_match_array == NULL + && compl_curr_match != NULL + && compl_curr_match->cp_str.data != NULL) { + word = xstrdup(compl_curr_match->cp_str.data); + } + // CTRL-E means completion is Ended, go back to the typed text. // but only do this, if the Popup is still visible if (c == Ctrl_E) { diff --git a/test/functional/autocmd/completedone_spec.lua b/test/functional/autocmd/completedone_spec.lua index bd482c6a8a..97c156dba4 100644 --- a/test/functional/autocmd/completedone_spec.lua +++ b/test/functional/autocmd/completedone_spec.lua @@ -13,7 +13,6 @@ describe('CompleteDone', function() describe('sets v:event.reason', function() before_each(function() - command('set completeopt+=noinsert') command('autocmd CompleteDone * let g:donereason = v:event.reason') feed('i') call('complete', call('col', '.'), { 'foo', 'bar' }) @@ -25,16 +24,20 @@ describe('CompleteDone', function() end) it('accept when candidate is inserted without noinsert #38160', function() - command('set completeopt=menu,menuone') -- Omit "noinsert". + command('set completeopt=menu') -- Omit "noinsert". feed('Stest') eq('accept', eval('g:donereason')) + eq('test', eval('v:completed_item').word) eq('test', n.api.nvim_get_current_line()) - feed('Stipt') - eq('accept', eval('g:donereason')) - eq('tip', n.api.nvim_get_current_line()) - feed('Stryt') - eq('accept', eval('g:donereason')) - eq('try ', n.api.nvim_get_current_line()) + + -- discard when pum was shown and dismissed without accepting + command('set completeopt=menuone') + feed('Stesttes') + eq('discard', eval('g:donereason')) + + feed('Stesttes') + eq('discard', eval('g:donereason')) + eq('test ', n.api.nvim_get_current_line()) end) it('cancel', function() diff --git a/test/functional/plugin/lsp/completion_spec.lua b/test/functional/plugin/lsp/completion_spec.lua index ac650f58e2..3c94098f03 100644 --- a/test/functional/plugin/lsp/completion_spec.lua +++ b/test/functional/plugin/lsp/completion_spec.lua @@ -1001,9 +1001,6 @@ describe('vim.lsp.completion: protocol', function() }, }, } - exec_lua(function() - vim.o.completeopt = 'menuone,noselect' - end) local client_id = create_server('dummy', completion_list) exec_lua(function() @@ -1042,9 +1039,6 @@ describe('vim.lsp.completion: protocol', function() isIncomplete = false, items = { { label = 'hello' } }, } - exec_lua(function() - vim.o.completeopt = 'menuone,noselect' - end) local client_id = create_server('dummy', completion_list, { resolve_result = { label = 'hello',