aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2023-09-27 18:51:40 +0800
committerGitHub <noreply@github.com>2023-09-27 18:51:40 +0800
commitb65f4151d9e52a8521c0682a817c4dab9690e1e7 (patch)
treee76592f65135cf3b18e9eca05dfe71cd249b20ab
parentdfa8b582a64aa22d3c57261bfcdc970b26cb58f3 (diff)
downloadrneovim-b65f4151d9e52a8521c0682a817c4dab9690e1e7.tar.gz
rneovim-b65f4151d9e52a8521c0682a817c4dab9690e1e7.tar.bz2
rneovim-b65f4151d9e52a8521c0682a817c4dab9690e1e7.zip
vim-patch:8.2.3517: TextChanged does not trigger after TextChangedI (#25384)
Problem: TextChanged does not trigger after TextChangedI. Solution: Store the tick separately for TextChangedI. (Christian Brabandt, closes vim/vim#8968, closes vim/vim#8932) https://github.com/vim/vim/commit/db3b44640d69ab27270691a3cab8d83cc93a0861 Co-authored-by: Christian Brabandt <cb@256bit.org>
-rw-r--r--src/nvim/buffer.c1
-rw-r--r--src/nvim/buffer_defs.h6
-rw-r--r--src/nvim/bufwrite.c4
-rw-r--r--src/nvim/edit.c12
-rw-r--r--test/functional/autocmd/textchanged_spec.lua150
-rw-r--r--test/functional/editor/completion_spec.lua87
-rw-r--r--test/old/testdir/test_autocmd.vim44
7 files changed, 204 insertions, 100 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 7a3e65e10e..15fbf7df7b 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -354,6 +354,7 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags_arg)
// Set last_changedtick to avoid triggering a TextChanged autocommand right
// after it was added.
curbuf->b_last_changedtick = buf_get_changedtick(curbuf);
+ curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf);
curbuf->b_last_changedtick_pum = buf_get_changedtick(curbuf);
// require "!" to overwrite the file, because it wasn't read completely
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index ff0fca1a56..f267dbb2f1 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -418,10 +418,10 @@ struct file_buffer {
/// This is a dictionary item used to store b:changedtick.
ChangedtickDictItem changedtick_di;
- 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
+ varnumber_T b_last_changedtick; // b:changedtick when TextChanged was
// last triggered.
+ varnumber_T b_last_changedtick_i; // b:changedtick for TextChangedI
+ varnumber_T b_last_changedtick_pum; // b:changedtick for TextChangedP
bool b_saving; // Set to true if we are in the middle of
// saving the buffer.
diff --git a/src/nvim/bufwrite.c b/src/nvim/bufwrite.c
index 0c95314c52..2f13586dcc 100644
--- a/src/nvim/bufwrite.c
+++ b/src/nvim/bufwrite.c
@@ -1797,8 +1797,8 @@ restore_backup:
unchanged(buf, true, false);
const varnumber_T changedtick = buf_get_changedtick(buf);
if (buf->b_last_changedtick + 1 == changedtick) {
- // b:changedtick may be incremented in unchanged() but that
- // should not trigger a TextChanged event.
+ // b:changedtick may be incremented in unchanged() but that should not
+ // trigger a TextChanged event.
buf->b_last_changedtick = changedtick;
}
u_unchanged(buf);
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 520d3bc2b3..9169cbcd9c 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -1306,9 +1306,9 @@ void ins_redraw(bool ready)
last_cursormoved = curwin->w_cursor;
}
- // Trigger TextChangedI if changedtick differs.
+ // Trigger TextChangedI if changedtick_i differs.
if (ready && has_event(EVENT_TEXTCHANGEDI)
- && curbuf->b_last_changedtick != buf_get_changedtick(curbuf)
+ && curbuf->b_last_changedtick_i != buf_get_changedtick(curbuf)
&& !pum_visible()) {
aco_save_T aco;
varnumber_T tick = buf_get_changedtick(curbuf);
@@ -1317,16 +1317,16 @@ void ins_redraw(bool ready)
aucmd_prepbuf(&aco, curbuf);
apply_autocmds(EVENT_TEXTCHANGEDI, NULL, NULL, false, curbuf);
aucmd_restbuf(&aco);
- curbuf->b_last_changedtick = buf_get_changedtick(curbuf);
+ curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf);
if (tick != buf_get_changedtick(curbuf)) { // see ins_apply_autocmds()
u_save(curwin->w_cursor.lnum,
(linenr_T)(curwin->w_cursor.lnum + 1));
}
}
- // Trigger TextChangedP if changedtick differs. When the popupmenu closes
- // TextChangedI will need to trigger for backwards compatibility, thus use
- // different b_last_changedtick* variables.
+ // Trigger TextChangedP if changedtick_pum 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 != buf_get_changedtick(curbuf)
&& pum_visible()) {
diff --git a/test/functional/autocmd/textchanged_spec.lua b/test/functional/autocmd/textchanged_spec.lua
new file mode 100644
index 0000000000..ffeb76835d
--- /dev/null
+++ b/test/functional/autocmd/textchanged_spec.lua
@@ -0,0 +1,150 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local exec = helpers.exec
+local command = helpers.command
+local feed = helpers.feed
+local eq = helpers.eq
+local eval = helpers.eval
+local poke_eventloop = helpers.poke_eventloop
+
+before_each(clear)
+
+-- oldtest: Test_ChangedP()
+it('TextChangedI and TextChangedP autocommands', function()
+ -- The oldtest uses feedkeys() with 'x' flag, which never triggers TextChanged.
+ -- So don't add TextChanged autocommand here.
+ exec([[
+ call setline(1, ['foo', 'bar', 'foobar'])
+ set complete=. completeopt=menuone
+ au! TextChangedI <buffer> let g:autocmd ..= 'I'
+ au! TextChangedP <buffer> let g:autocmd ..= 'P'
+ call cursor(3, 1)
+ ]])
+
+ command([[let g:autocmd = '']])
+ feed('o')
+ poke_eventloop()
+ feed('<esc>')
+ eq('I', eval('g:autocmd'))
+
+ command([[let g:autocmd = '']])
+ feed('S')
+ poke_eventloop()
+ feed('f')
+ poke_eventloop()
+ eq('II', eval('g:autocmd'))
+ feed('<esc>')
+
+ command([[let g:autocmd = '']])
+ feed('S')
+ poke_eventloop()
+ feed('f')
+ poke_eventloop()
+ feed('<C-N>')
+ poke_eventloop()
+ eq('IIP', eval('g:autocmd'))
+ feed('<esc>')
+
+ command([[let g:autocmd = '']])
+ feed('S')
+ poke_eventloop()
+ feed('f')
+ poke_eventloop()
+ feed('<C-N>')
+ poke_eventloop()
+ feed('<C-N>')
+ poke_eventloop()
+ eq('IIPP', eval('g:autocmd'))
+ feed('<esc>')
+
+ command([[let g:autocmd = '']])
+ feed('S')
+ poke_eventloop()
+ feed('f')
+ poke_eventloop()
+ feed('<C-N>')
+ poke_eventloop()
+ feed('<C-N>')
+ poke_eventloop()
+ feed('<C-N>')
+ poke_eventloop()
+ eq('IIPPP', eval('g:autocmd'))
+ feed('<esc>')
+
+ command([[let g:autocmd = '']])
+ feed('S')
+ poke_eventloop()
+ feed('f')
+ poke_eventloop()
+ feed('<C-N>')
+ poke_eventloop()
+ feed('<C-N>')
+ poke_eventloop()
+ feed('<C-N>')
+ poke_eventloop()
+ feed('<C-N>')
+ eq('IIPPPP', eval('g:autocmd'))
+ feed('<esc>')
+
+ eq({'foo', 'bar', 'foobar', 'foo'}, eval('getline(1, "$")'))
+end)
+
+-- oldtest: Test_TextChangedI_with_setline()
+it('TextChangedI with setline()', function()
+ exec([[
+ let g:setline_handled = v:false
+ func SetLineOne()
+ if !g:setline_handled
+ call setline(1, "(x)")
+ let g:setline_handled = v:true
+ endif
+ endfunc
+ autocmd TextChangedI <buffer> call SetLineOne()
+ ]])
+
+ feed('i')
+ poke_eventloop()
+ feed('(')
+ poke_eventloop()
+ feed('<CR>')
+ poke_eventloop()
+ feed('<Esc>')
+ eq('(', eval('getline(1)'))
+ eq('x)', eval('getline(2)'))
+ command('undo')
+ eq('', eval('getline(1)'))
+ eq('', eval('getline(2)'))
+end)
+
+-- oldtest: Test_Changed_ChangedI()
+it('TextChanged is triggerd after TextChangedI', function()
+ exec([[
+ let [g:autocmd_i, g:autocmd_n] = ['','']
+
+ func! TextChangedAutocmdI(char)
+ let g:autocmd_{tolower(a:char)} = a:char .. b:changedtick
+ endfunc
+
+ augroup Test_TextChanged
+ au!
+ au TextChanged <buffer> :call TextChangedAutocmdI('N')
+ au TextChangedI <buffer> :call TextChangedAutocmdI('I')
+ augroup END
+ ]])
+
+ feed('i')
+ poke_eventloop()
+ feed('f')
+ poke_eventloop()
+ feed('o')
+ poke_eventloop()
+ feed('o')
+ poke_eventloop()
+ feed('<esc>')
+ eq('N5', eval('g:autocmd_n'))
+ eq('I5', eval('g:autocmd_i'))
+
+ command([[call feedkeys("yyp", 'tnix')]])
+ eq('N6', eval('g:autocmd_n'))
+ eq('I5', eval('g:autocmd_i'))
+end)
diff --git a/test/functional/editor/completion_spec.lua b/test/functional/editor/completion_spec.lua
index dcd7ef720f..8c299636cc 100644
--- a/test/functional/editor/completion_spec.lua
+++ b/test/functional/editor/completion_spec.lua
@@ -1032,93 +1032,6 @@ describe('completion', function()
]])
end)
- -- oldtest: Test_ChangedP()
- it('TextChangedI and TextChangedP autocommands', 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')
- poke_eventloop()
- feed('<esc>')
- eq({'I'}, eval('g:foo'))
-
- command('let g:foo = []')
- feed('S')
- poke_eventloop()
- feed('f')
- poke_eventloop()
- eq({'I', 'I'}, eval('g:foo'))
- feed('<esc>')
-
- command('let g:foo = []')
- feed('S')
- poke_eventloop()
- feed('f')
- poke_eventloop()
- feed('<C-N>')
- poke_eventloop()
- eq({'I', 'I', 'P'}, eval('g:foo'))
- feed('<esc>')
-
- command('let g:foo = []')
- feed('S')
- poke_eventloop()
- feed('f')
- poke_eventloop()
- feed('<C-N>')
- poke_eventloop()
- feed('<C-N>')
- poke_eventloop()
- eq({'I', 'I', 'P', 'P'}, eval('g:foo'))
- feed('<esc>')
-
- command('let g:foo = []')
- feed('S')
- poke_eventloop()
- feed('f')
- poke_eventloop()
- feed('<C-N>')
- poke_eventloop()
- feed('<C-N>')
- poke_eventloop()
- feed('<C-N>')
- poke_eventloop()
- eq({'I', 'I', 'P', 'P', 'P'}, eval('g:foo'))
- feed('<esc>')
-
- command('let g:foo = []')
- feed('S')
- poke_eventloop()
- feed('f')
- poke_eventloop()
- feed('<C-N>')
- poke_eventloop()
- feed('<C-N>')
- poke_eventloop()
- feed('<C-N>')
- poke_eventloop()
- 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)
-
it('CompleteChanged autocommand', function()
curbufmeths.set_lines(0, 1, false, { 'foo', 'bar', 'foobar', ''})
source([[
diff --git a/test/old/testdir/test_autocmd.vim b/test/old/testdir/test_autocmd.vim
index b3238e8214..406de695a3 100644
--- a/test/old/testdir/test_autocmd.vim
+++ b/test/old/testdir/test_autocmd.vim
@@ -2442,7 +2442,7 @@ endfunc
" Test TextChangedI and TextChangedP
func Test_ChangedP()
- throw 'Skipped: use test/functional/editor/completion_spec.lua'
+ throw 'Skipped: use test/functional/autocmd/textchanged_spec.lua'
new
call setline(1, ['foo', 'bar', 'foobar'])
call test_override("char_avail", 1)
@@ -2452,6 +2452,7 @@ func Test_ChangedP()
let g:autocmd .= a:char
endfunc
+ " TextChanged will not be triggered, only check that it isn't.
au! TextChanged <buffer> :call TextChangedAutocmd('N')
au! TextChangedI <buffer> :call TextChangedAutocmd('I')
au! TextChangedP <buffer> :call TextChangedAutocmd('P')
@@ -2505,7 +2506,7 @@ func SetLineOne()
endfunc
func Test_TextChangedI_with_setline()
- CheckFunction test_override
+ throw 'Skipped: use test/functional/autocmd/textchanged_spec.lua'
new
call test_override('char_avail', 1)
autocmd TextChangedI <buffer> call SetLineOne()
@@ -3433,6 +3434,45 @@ func Test_autocmd_vimgrep()
augroup END
endfunc
+" Test TextChangedI and TextChanged
+func Test_Changed_ChangedI()
+ throw 'Skipped: use test/functional/autocmd/textchanged_spec.lua'
+ new
+ call test_override("char_avail", 1)
+ let [g:autocmd_i, g:autocmd_n] = ['','']
+
+ func! TextChangedAutocmdI(char)
+ let g:autocmd_{tolower(a:char)} = a:char .. b:changedtick
+ endfunc
+
+ augroup Test_TextChanged
+ au!
+ au TextChanged <buffer> :call TextChangedAutocmdI('N')
+ au TextChangedI <buffer> :call TextChangedAutocmdI('I')
+ augroup END
+
+ call feedkeys("ifoo\<esc>", 'tnix')
+ " TODO: Test test does not seem to trigger TextChanged autocommand, this
+ " requires running Vim in a terminal window.
+ " call assert_equal('N3', g:autocmd_n)
+ call assert_equal('I3', g:autocmd_i)
+
+ call feedkeys("yyp", 'tnix')
+ " TODO: Test test does not seem to trigger TextChanged autocommand.
+ " call assert_equal('N4', g:autocmd_n)
+ call assert_equal('I3', g:autocmd_i)
+
+ " CleanUp
+ call test_override("char_avail", 0)
+ au! TextChanged <buffer>
+ au! TextChangedI <buffer>
+ augroup! Test_TextChanged
+ delfu TextChangedAutocmdI
+ unlet! g:autocmd_i g:autocmd_n
+
+ bw!
+endfunc
+
func Test_closing_autocmd_window()
let lines =<< trim END
edit Xa.txt