diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index bb84966228..ca816851dd 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -466,6 +466,16 @@ CompleteDone After Insert mode completion is done. Either CompleteDonePre if you need it. |v:completed_item| gives the completed item. + Sets these |v:event| keys: + reason Reason for completion being + done. Can be one of: + - "accept": completion was + accepted using |complete_CTRL-Y|. + - "cancel": completion was cancelled + using |complete_CTRL-E|, pressing + a non-keyword character, or + triggering a new completion. + *CursorHold* CursorHold When the user doesn't press a key for the time specified with 'updatetime'. Not triggered diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index f8bc983441..03f6f7f8fd 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -130,6 +130,9 @@ UI • TODO +• |CompleteDone| now sets the `reason` key in `v:event` which specifies the reason + for completion being done. + ============================================================================== CHANGED FEATURES *news-changed* diff --git a/runtime/doc/vvars.txt b/runtime/doc/vvars.txt index de7f23a34d..15d836a83d 100644 --- a/runtime/doc/vvars.txt +++ b/runtime/doc/vvars.txt @@ -187,6 +187,7 @@ v:event changed_window Is |v:true| if the event fired while changing window (or tab) on |DirChanged|. status Job status or exit code, -1 means "unknown". |TermClose| + reason Reason for completion being done. |CompleteDone| *v:exception* *exception-variable* v:exception diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua index 1660d1dd6c..e00402ab3f 100644 --- a/runtime/lua/vim/_meta/vvars.lua +++ b/runtime/lua/vim/_meta/vvars.lua @@ -195,6 +195,7 @@ vim.v.errors = ... --- changed_window Is `v:true` if the event fired while --- changing window (or tab) on `DirChanged`. --- status Job status or exit code, -1 means "unknown". `TermClose` +--- reason Reason for completion being done. `CompleteDone` --- @type any vim.v.event = ... diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index cec1e4a9e3..b557b9802e 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -568,6 +568,18 @@ static bool is_first_match(const compl_T *const match) return match == compl_first_match; } +static void do_autocmd_completedone(int c) +{ + save_v_event_T save_v_event; + dict_T *v_event = get_v_event(&save_v_event); + + tv_dict_add_str(v_event, S_LEN("reason"), (c == Ctrl_Y ? "accept" : "cancel")); + tv_dict_set_keys_readonly(v_event); + + ins_apply_autocmds(EVENT_COMPLETEDONE); + restore_v_event(v_event, &save_v_event); +} + /// Check that character "c" is part of the item currently being /// completed. Used to decide whether to abandon complete mode when the menu /// is visible. @@ -2110,7 +2122,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval) } // Trigger the CompleteDone event to give scripts a chance to act // upon the end of completion. - ins_apply_autocmds(EVENT_COMPLETEDONE); + do_autocmd_completedone(c); return retval; } @@ -2199,7 +2211,7 @@ bool ins_compl_prep(int c) } else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) { // Trigger the CompleteDone event to give scripts a chance to act // upon the (possibly failed) completion. - ins_apply_autocmds(EVENT_COMPLETEDONE); + do_autocmd_completedone(c); } may_trigger_modechanged(); diff --git a/src/nvim/vvars.lua b/src/nvim/vvars.lua index 224edfaf6e..2f43f8b32b 100644 --- a/src/nvim/vvars.lua +++ b/src/nvim/vvars.lua @@ -215,6 +215,7 @@ M.vars = { changed_window Is |v:true| if the event fired while changing window (or tab) on |DirChanged|. status Job status or exit code, -1 means "unknown". |TermClose| + reason Reason for completion being done. |CompleteDone| ]=], }, exception = { diff --git a/test/functional/autocmd/completedone_spec.lua b/test/functional/autocmd/completedone_spec.lua new file mode 100644 index 0000000000..33beb16db2 --- /dev/null +++ b/test/functional/autocmd/completedone_spec.lua @@ -0,0 +1,41 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local call = n.call +local feed = n.feed +local eval = n.eval +local eq = t.eq + +describe('CompleteDone', function() + before_each(clear) + + describe('sets v:event.reason', function() + before_each(function() + clear() + command('autocmd CompleteDone * let g:donereason = v:event.reason') + feed('i') + call('complete', call('col', '.'), { 'foo', 'bar' }) + end) + + it('accept', function() + feed('') + eq('accept', eval('g:donereason')) + end) + describe('cancel', function() + it('on ', function() + feed('') + eq('cancel', eval('g:donereason')) + end) + it('on non-keyword character', function() + feed('') + eq('cancel', eval('g:donereason')) + end) + it('when overriden by another complete()', function() + call('complete', call('col', '.'), { 'bar', 'baz' }) + eq('cancel', eval('g:donereason')) + end) + end) + end) +end)