diff options
| -rw-r--r-- | runtime/doc/autocmd.txt | 8 | ||||
| -rw-r--r-- | src/nvim/auevents.lua | 3 | ||||
| -rw-r--r-- | src/nvim/buffer_defs.h | 5 | ||||
| -rw-r--r-- | src/nvim/edit.c | 19 | ||||
| -rw-r--r-- | src/nvim/fileio.c | 5 | ||||
| -rw-r--r-- | src/nvim/globals.h | 3 | ||||
| -rw-r--r-- | src/nvim/normal.c | 10 | ||||
| -rw-r--r-- | src/nvim/testdir/test_autocmd.vim | 57 | ||||
| -rw-r--r-- | test/functional/viml/completion_spec.lua | 89 | 
9 files changed, 179 insertions, 20 deletions
| diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 06a016eddb..0b7cd5b2cd 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -325,6 +325,9 @@ Name			triggered by ~  |TextChanged|		after a change was made to the text in Normal mode  |TextChangedI|		after a change was made to the text in Insert mode +			when popup menu is not visible +|TextChangedP|		after a change was made to the text in Insert mode +			when popup menu visible  |ColorScheme|		after loading a color scheme @@ -969,6 +972,11 @@ TextChangedI			After a change was made to the text in the  				current buffer in Insert mode.  				Not triggered when the popup menu is visible.  				Otherwise the same as TextChanged. +							*TextChangedP* +TextChangedP			After a change was made to the text in the +				current buffer in Insert mode, only when the +				popup menu is visible.  Otherwise the same as +				TextChanged.  							*User*  User				Never executed automatically.  To be used for  				autocommands that are only executed with diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index 1153314e76..f07a92ab87 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -85,7 +85,8 @@ return {      'TermOpen',               -- after opening a terminal buffer      'TermResponse',           -- after setting "v:termresponse"      'TextChanged',            -- text was modified -    'TextChangedI',           -- text was modified in Insert mode +    'TextChangedI',           -- text was modified in Insert mode(no popup) +    'TextChangedP',           -- text was modified in Insert mode(popup)      'TextYankPost',           -- after a yank or delete was done (y, d, c)      'User',                   -- user defined autocommand      'VimEnter',               -- after starting Vim diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 53edae58a5..807baf02c1 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -484,6 +484,11 @@ struct file_buffer {  #define b_changedtick changedtick_di.di_tv.vval.v_number    ChangedtickDictItem changedtick_di;  // b:changedtick dictionary item. +  varnumber_T b_last_changedtick;       // b:changedtick when TextChanged or +                                        // TextChangedI was last triggered. +  varnumber_T b_last_changedtick_pum;   // b:changedtick when TextChangedP was +                                        // last triggered. +    bool b_saving;                /* Set to true if we are in the middle of                                     saving the buffer. */ diff --git a/src/nvim/edit.c b/src/nvim/edit.c index a1987cf2d5..462762aea0 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1387,13 +1387,20 @@ ins_redraw (    // Trigger TextChangedI if b_changedtick differs.    if (ready && has_event(EVENT_TEXTCHANGEDI) -      && last_changedtick != curbuf->b_changedtick +      && curbuf->b_last_changedtick != curbuf->b_changedtick        && !pum_visible()) { -    if (last_changedtick_buf == curbuf) { -      apply_autocmds(EVENT_TEXTCHANGEDI, NULL, NULL, false, curbuf); -    } -    last_changedtick_buf = curbuf; -    last_changedtick = curbuf->b_changedtick; +    apply_autocmds(EVENT_TEXTCHANGEDI, NULL, NULL, false, curbuf); +    curbuf->b_last_changedtick = curbuf->b_changedtick; +  } + +  // Trigger TextChangedP if b_changedtick differs. When the popupmenu closes +  // TextChangedI will need to trigger for backwards compatibility, thus use +  // different b_last_changedtick* variables. +  if (ready && has_event(EVENT_TEXTCHANGEDP) +      && curbuf->b_last_changedtick_pum != curbuf->b_changedtick +      && pum_visible()) { +      apply_autocmds(EVENT_TEXTCHANGEDP, NULL, NULL, false, curbuf); +      curbuf->b_last_changedtick_pum = curbuf->b_changedtick;    }    if (must_redraw) diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index efeee1ba2b..520aedaac7 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -3571,9 +3571,8 @@ restore_backup:      unchanged(buf, TRUE);      /* buf->b_changedtick is always incremented in unchanged() but that       * should not trigger a TextChanged event. */ -    if (last_changedtick + 1 == buf->b_changedtick -        && last_changedtick_buf == buf) { -      last_changedtick = buf->b_changedtick; +    if (buf->b_last_changedtick + 1 == buf->b_changedtick) { +      buf->b_last_changedtick = buf->b_changedtick;      }      u_unchanged(buf);      u_update_save_nr(buf); diff --git a/src/nvim/globals.h b/src/nvim/globals.h index ddc58c2425..4aa0ef7def 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -868,9 +868,6 @@ EXTERN int did_cursorhold INIT(= false);       // set when CursorHold t'gerd  // for CursorMoved event  EXTERN pos_T last_cursormoved INIT(= INIT_POS_T(0, 0, 0)); -EXTERN varnumber_T last_changedtick INIT(= 0);  // for TextChanged event -EXTERN buf_T    *last_changedtick_buf INIT(= NULL); -  EXTERN int postponed_split INIT(= 0);       /* for CTRL-W CTRL-] command */  EXTERN int postponed_split_flags INIT(= 0);       /* args for win_split() */  EXTERN int postponed_split_tab INIT(= 0);       /* cmdmod.tab */ diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 43e8b6fb3d..a2aaf8f9af 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1215,13 +1215,9 @@ static void normal_check_text_changed(NormalState *s)  {    // Trigger TextChanged if b_changedtick differs.    if (!finish_op && has_event(EVENT_TEXTCHANGED) -      && last_changedtick != curbuf->b_changedtick) { -    if (last_changedtick_buf == curbuf) { -      apply_autocmds(EVENT_TEXTCHANGED, NULL, NULL, false, curbuf); -    } - -    last_changedtick_buf = curbuf; -    last_changedtick = curbuf->b_changedtick; +      && curbuf->b_last_changedtick != curbuf->b_changedtick) { +    apply_autocmds(EVENT_TEXTCHANGED, NULL, NULL, false, curbuf); +    curbuf->b_last_changedtick = curbuf->b_changedtick;    }  } diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 24651b75e1..238de5a87d 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1165,3 +1165,60 @@ func Test_nocatch_wipe_dummy_buffer()    call assert_fails('lv½ /x', 'E480')    au!  endfunc + +" Test TextChangedI and TextChangedP +func Test_ChangedP() abort +  " Nvim does not support test_override(). +  throw 'skipped: see test/functional/viml/completion_spec.lua' +  new +  call setline(1, ['foo', 'bar', 'foobar']) +  call test_override("char_avail", 1) +  set complete=. completeopt=menuone + +  func! TextChangedAutocmd(char) +    let g:autocmd .= a:char +  endfunc + +  au! TextChanged <buffer> :call TextChangedAutocmd('N') +  au! TextChangedI <buffer> :call TextChangedAutocmd('I') +  au! TextChangedP <buffer> :call TextChangedAutocmd('P') + +  call cursor(3, 1) +  let g:autocmd = '' +  call feedkeys("o\<esc>", 'tnix') +  call assert_equal('I', g:autocmd) + +  let g:autocmd = '' +  call feedkeys("Sf", 'tnix') +  call assert_equal('II', g:autocmd) + +  let g:autocmd = '' +  call feedkeys("Sf\<C-N>", 'tnix') +  call assert_equal('IIP', g:autocmd) + +  let g:autocmd = '' +  call feedkeys("Sf\<C-N>\<C-N>", 'tnix') +  call assert_equal('IIPP', g:autocmd) + +  let g:autocmd = '' +  call feedkeys("Sf\<C-N>\<C-N>\<C-N>", 'tnix') +  call assert_equal('IIPPP', g:autocmd) + +  let g:autocmd = '' +  call feedkeys("Sf\<C-N>\<C-N>\<C-N>\<C-N>", 'tnix') +  call assert_equal('IIPPPP', g:autocmd) + +  call assert_equal(['foo', 'bar', 'foobar', 'foo'], getline(1, '$')) +  " TODO: how should it handle completeopt=noinsert,noselect? + +  " CleanUp +  call test_override("char_avail", 0) +  au! TextChanged +  au! TextChangedI +  au! TextChangedP +  delfu TextChangedAutocmd +  unlet! g:autocmd +  set complete&vim completeopt&vim + +  bw! +endfunc diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua index 216ccb3744..c14f7fc1a6 100644 --- a/test/functional/viml/completion_spec.lua +++ b/test/functional/viml/completion_spec.lua @@ -3,7 +3,10 @@ local Screen = require('test.functional.ui.screen')  local clear, feed = helpers.clear, helpers.feed  local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq  local feed_command, source, expect = helpers.feed_command, helpers.source, helpers.expect +local curbufmeths = helpers.curbufmeths +local command = helpers.command  local meths = helpers.meths +local wait = helpers.wait  describe('completion', function()    local screen @@ -971,4 +974,90 @@ describe('ui/ext_popupmenu', function()        eq(nil, items) -- popupmenu was hidden      end)    end) + +  it('TextChangedP autocommand', function() +    curbufmeths.set_lines(0, 1, false, { 'foo', 'bar', 'foobar'}) +    source([[ +      set complete=. completeopt=menuone +      let g:foo = [] +      autocmd! TextChanged * :call add(g:foo, "N") +      autocmd! TextChangedI * :call add(g:foo, "I") +      autocmd! TextChangedP * :call add(g:foo, "P") +      call cursor(3, 1) +    ]]) + +    command('let g:foo = []') +    feed('o') +    wait() +    feed('<esc>') +    eq({'I'}, eval('g:foo')) + +    command('let g:foo = []') +    feed('S') +    wait() +    feed('f') +    wait() +    eq({'I', 'I'}, eval('g:foo')) +    feed('<esc>') + +    command('let g:foo = []') +    feed('S') +    wait() +    feed('f') +    wait() +    feed('<C-N>') +    wait() +    eq({'I', 'I', 'P'}, eval('g:foo')) +    feed('<esc>') + +    command('let g:foo = []') +    feed('S') +    wait() +    feed('f') +    wait() +    feed('<C-N>') +    wait() +    feed('<C-N>') +    wait() +    eq({'I', 'I', 'P', 'P'}, eval('g:foo')) +    feed('<esc>') + +    command('let g:foo = []') +    feed('S') +    wait() +    feed('f') +    wait() +    feed('<C-N>') +    wait() +    feed('<C-N>') +    wait() +    feed('<C-N>') +    wait() +    eq({'I', 'I', 'P', 'P', 'P'}, eval('g:foo')) +    feed('<esc>') + +    command('let g:foo = []') +    feed('S') +    wait() +    feed('f') +    wait() +    feed('<C-N>') +    wait() +    feed('<C-N>') +    wait() +    feed('<C-N>') +    wait() +    feed('<C-N>') +    eq({'I', 'I', 'P', 'P', 'P', 'P'}, eval('g:foo')) +    feed('<esc>') + +    eq({'foo', 'bar', 'foobar', 'foo'}, eval('getline(1, "$")')) + +    source([[ +      au! TextChanged +      au! TextChangedI +      au! TextChangedP +      set complete&vim completeopt&vim +    ]]) +  end)  end) | 
