diff options
-rw-r--r-- | src/nvim/edit.c | 3 | ||||
-rw-r--r-- | src/nvim/normal.c | 7 | ||||
-rw-r--r-- | src/nvim/ops.c | 6 | ||||
-rw-r--r-- | test/functional/autocmd/textchanged_spec.lua | 39 | ||||
-rw-r--r-- | test/old/testdir/test_autocmd.vim | 32 |
5 files changed, 68 insertions, 19 deletions
diff --git a/src/nvim/edit.c b/src/nvim/edit.c index a7a98b9557..ef0317b212 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -143,9 +143,6 @@ static void insert_enter(InsertState *s) update_Insstart_orig = true; ins_compl_clear(); // clear stuff for CTRL-X mode - // Reset Changedtick_i, so that TextChangedI will only be triggered for stuff - // from insert mode - curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf); // Trigger InsertEnter autocommands. Do not do this for "r<CR>" or "grx". if (s->cmdchar != 'r' && s->cmdchar != 'v') { diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 1f2403450f..93bbdd3be9 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -5724,6 +5724,8 @@ static void n_opencmd(cmdarg_T *cap) (void)hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum); } + // trigger TextChangedI for the 'o/O' command + curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf); if (u_save(curwin->w_cursor.lnum - (cap->cmdchar == 'O' ? 1 : 0), curwin->w_cursor.lnum + (cap->cmdchar == 'o' ? 1 : 0)) && open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD, @@ -6265,6 +6267,11 @@ static void invoke_edit(cmdarg_T *cap, int repl, int cmd, int startln) // Always reset "restart_edit", this is not a restarted edit. restart_edit = 0; + // Reset Changedtick_i, so that TextChangedI will only be triggered for stuff + // from insert mode, for 'o/O' this has already been done in n_opencmd + if (cap->cmdchar != 'O' && cap->cmdchar != 'o') { + curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf); + } if (edit(cmd, startln, cap->count1)) { cap->retval |= CA_COMMAND_BUSY; } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 260019da33..f325eceaaf 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -6226,6 +6226,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) // Restore linebreak, so that when the user edits it looks as before. restore_lbr(lbr_saved); + // trigger TextChangedI + curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf); + if (op_change(oap)) { // will call edit() cap->retval |= CA_COMMAND_BUSY; } @@ -6324,6 +6327,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) // Restore linebreak, so that when the user edits it looks as before. restore_lbr(lbr_saved); + // trigger TextChangedI + curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf); + op_insert(oap, cap->count1); // Reset linebreak, so that formatting works correctly. diff --git a/test/functional/autocmd/textchanged_spec.lua b/test/functional/autocmd/textchanged_spec.lua index b90a0fd020..b621eb36bf 100644 --- a/test/functional/autocmd/textchanged_spec.lua +++ b/test/functional/autocmd/textchanged_spec.lua @@ -4,6 +4,7 @@ local exec = helpers.exec local command = helpers.command local feed = helpers.feed local eq = helpers.eq +local neq = helpers.neq local eval = helpers.eval local poke_eventloop = helpers.poke_eventloop @@ -26,14 +27,14 @@ it('TextChangedI and TextChangedP autocommands', function() poke_eventloop() feed('<esc>') -- TextChangedI triggers only if text is actually changed in Insert mode - eq('', eval('g:autocmd')) + eq('I', eval('g:autocmd')) command([[let g:autocmd = '']]) feed('S') poke_eventloop() feed('f') poke_eventloop() - eq('I', eval('g:autocmd')) + eq('II', eval('g:autocmd')) feed('<esc>') command([[let g:autocmd = '']]) @@ -43,7 +44,7 @@ it('TextChangedI and TextChangedP autocommands', function() poke_eventloop() feed('<C-N>') poke_eventloop() - eq('IP', eval('g:autocmd')) + eq('IIP', eval('g:autocmd')) feed('<esc>') command([[let g:autocmd = '']]) @@ -55,7 +56,7 @@ it('TextChangedI and TextChangedP autocommands', function() poke_eventloop() feed('<C-N>') poke_eventloop() - eq('IPP', eval('g:autocmd')) + eq('IIPP', eval('g:autocmd')) feed('<esc>') command([[let g:autocmd = '']]) @@ -69,7 +70,7 @@ it('TextChangedI and TextChangedP autocommands', function() poke_eventloop() feed('<C-N>') poke_eventloop() - eq('IPPP', eval('g:autocmd')) + eq('IIPPP', eval('g:autocmd')) feed('<esc>') command([[let g:autocmd = '']]) @@ -84,7 +85,7 @@ it('TextChangedI and TextChangedP autocommands', function() feed('<C-N>') poke_eventloop() feed('<C-N>') - eq('IPPPP', eval('g:autocmd')) + eq('IIPPPP', eval('g:autocmd')) feed('<esc>') eq({'foo', 'bar', 'foobar', 'foo'}, eval('getline(1, "$")')) @@ -145,17 +146,37 @@ it('TextChangedI and TextChanged', function() eq('', eval('g:autocmd_n')) eq('I5', eval('g:autocmd_i')) - command([[call feedkeys("yyp", 'tnix')]]) + feed('yyp') eq('N6', eval('g:autocmd_n')) eq('I5', eval('g:autocmd_i')) -- TextChangedI should only trigger if change was done in Insert mode command([[let g:autocmd_i = '']]) - command([[call feedkeys("yypi\<esc>", 'tnix')]]) + feed('yypi<esc>') eq('', eval('g:autocmd_i')) -- TextChanged should only trigger if change was done in Normal mode command([[let g:autocmd_n = '']]) - command([[call feedkeys("ibar\<esc>", 'tnix')]]) + feed('ibar<esc>') eq('', eval('g:autocmd_n')) + + local function validate_mixed_textchangedi(keys) + feed('ifoo<esc>') + command([[let g:autocmd_i = '']]) + command([[let g:autocmd_n = '']]) + for _, s in ipairs(keys) do + feed(s) + poke_eventloop() + end + neq('', eval('g:autocmd_i')) + eq('', eval('g:autocmd_n')) + end + + validate_mixed_textchangedi({'o', '<esc>'}) + validate_mixed_textchangedi({'O', '<esc>'}) + validate_mixed_textchangedi({'ciw', '<esc>'}) + validate_mixed_textchangedi({'cc', '<esc>'}) + validate_mixed_textchangedi({'C', '<esc>'}) + validate_mixed_textchangedi({'s', '<esc>'}) + validate_mixed_textchangedi({'S', '<esc>'}) end) diff --git a/test/old/testdir/test_autocmd.vim b/test/old/testdir/test_autocmd.vim index 447a716331..a2b46daff3 100644 --- a/test/old/testdir/test_autocmd.vim +++ b/test/old/testdir/test_autocmd.vim @@ -2476,28 +2476,27 @@ func Test_ChangedP() call cursor(3, 1) let g:autocmd = '' call feedkeys("o\<esc>", 'tnix') - " `TextChangedI` triggers only if text is actually changed in Insert mode - call assert_equal('', g:autocmd) + call assert_equal('I', g:autocmd) let g:autocmd = '' call feedkeys("Sf", 'tnix') - call assert_equal('I', g:autocmd) + call assert_equal('II', g:autocmd) let g:autocmd = '' call feedkeys("Sf\<C-N>", 'tnix') - call assert_equal('IP', g:autocmd) + call assert_equal('IIP', g:autocmd) let g:autocmd = '' call feedkeys("Sf\<C-N>\<C-N>", 'tnix') - call assert_equal('IPP', g:autocmd) + call assert_equal('IIPP', g:autocmd) let g:autocmd = '' call feedkeys("Sf\<C-N>\<C-N>\<C-N>", 'tnix') - call assert_equal('IPPP', g:autocmd) + call assert_equal('IIPPP', g:autocmd) let g:autocmd = '' call feedkeys("Sf\<C-N>\<C-N>\<C-N>\<C-N>", 'tnix') - call assert_equal('IPPPP', g:autocmd) + call assert_equal('IIPPPP', g:autocmd) call assert_equal(['foo', 'bar', 'foobar', 'foo'], getline(1, '$')) " TODO: how should it handle completeopt=noinsert,noselect? @@ -3489,6 +3488,25 @@ func Test_Changed_ChangedI() call feedkeys("ibar\<esc>", 'tnix') call assert_equal('', g:autocmd_n) + " If change is a mix of Normal and Insert modes, TextChangedI should trigger + func s:validate_mixed_textchangedi(keys) + call feedkeys("ifoo\<esc>", 'tnix') + let g:autocmd_i = '' + let g:autocmd_n = '' + call feedkeys(a:keys, 'tnix') + call assert_notequal('', g:autocmd_i) + call assert_equal('', g:autocmd_n) + endfunc + + call s:validate_mixed_textchangedi("o\<esc>") + call s:validate_mixed_textchangedi("O\<esc>") + call s:validate_mixed_textchangedi("ciw\<esc>") + call s:validate_mixed_textchangedi("cc\<esc>") + call s:validate_mixed_textchangedi("C\<esc>") + call s:validate_mixed_textchangedi("s\<esc>") + call s:validate_mixed_textchangedi("S\<esc>") + + " CleanUp call test_override("char_avail", 0) au! TextChanged <buffer> |