mirror of
https://github.com/neovim/neovim.git
synced 2025-09-15 07:48:18 +00:00
vim-patch:8.0.1494: no autocmd triggered in Insert mode with visible popup menu
Problem: No autocmd triggered in Insert mode with visible popup menu.
Solution: Add TextChangedP. (Prabir Shrestha, Christian Brabandt,
closes vim/vim#2372, closes vim/vim#1691)
Fix that the TextChanged autocommands are not always triggered
when sourcing a script.
5a09343719
This commit is contained in:

committed by
chemzqm

parent
36b2e3f743
commit
021c5875c1
@@ -325,6 +325,9 @@ Name triggered by ~
|
|||||||
|
|
||||||
|TextChanged| after a change was made to the text in Normal mode
|
|TextChanged| after a change was made to the text in Normal mode
|
||||||
|TextChangedI| after a change was made to the text in Insert 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
|
|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.
|
current buffer in Insert mode.
|
||||||
Not triggered when the popup menu is visible.
|
Not triggered when the popup menu is visible.
|
||||||
Otherwise the same as TextChanged.
|
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*
|
||||||
User Never executed automatically. To be used for
|
User Never executed automatically. To be used for
|
||||||
autocommands that are only executed with
|
autocommands that are only executed with
|
||||||
|
@@ -85,7 +85,8 @@ return {
|
|||||||
'TermOpen', -- after opening a terminal buffer
|
'TermOpen', -- after opening a terminal buffer
|
||||||
'TermResponse', -- after setting "v:termresponse"
|
'TermResponse', -- after setting "v:termresponse"
|
||||||
'TextChanged', -- text was modified
|
'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)
|
'TextYankPost', -- after a yank or delete was done (y, d, c)
|
||||||
'User', -- user defined autocommand
|
'User', -- user defined autocommand
|
||||||
'VimEnter', -- after starting Vim
|
'VimEnter', -- after starting Vim
|
||||||
|
@@ -484,6 +484,11 @@ struct file_buffer {
|
|||||||
#define b_changedtick changedtick_di.di_tv.vval.v_number
|
#define b_changedtick changedtick_di.di_tv.vval.v_number
|
||||||
ChangedtickDictItem changedtick_di; // b:changedtick dictionary item.
|
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
|
bool b_saving; /* Set to true if we are in the middle of
|
||||||
saving the buffer. */
|
saving the buffer. */
|
||||||
|
|
||||||
|
@@ -1387,13 +1387,20 @@ ins_redraw (
|
|||||||
|
|
||||||
// Trigger TextChangedI if b_changedtick differs.
|
// Trigger TextChangedI if b_changedtick differs.
|
||||||
if (ready && has_event(EVENT_TEXTCHANGEDI)
|
if (ready && has_event(EVENT_TEXTCHANGEDI)
|
||||||
&& last_changedtick != curbuf->b_changedtick
|
&& curbuf->b_last_changedtick != curbuf->b_changedtick
|
||||||
&& !pum_visible()) {
|
&& !pum_visible()) {
|
||||||
if (last_changedtick_buf == curbuf) {
|
|
||||||
apply_autocmds(EVENT_TEXTCHANGEDI, NULL, NULL, false, curbuf);
|
apply_autocmds(EVENT_TEXTCHANGEDI, NULL, NULL, false, curbuf);
|
||||||
}
|
curbuf->b_last_changedtick = curbuf->b_changedtick;
|
||||||
last_changedtick_buf = curbuf;
|
}
|
||||||
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)
|
if (must_redraw)
|
||||||
@@ -1415,6 +1422,7 @@ ins_redraw (
|
|||||||
emsg_on_display = FALSE; /* may remove error message now */
|
emsg_on_display = FALSE; /* may remove error message now */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle a CTRL-V or CTRL-Q typed in Insert mode.
|
* Handle a CTRL-V or CTRL-Q typed in Insert mode.
|
||||||
*/
|
*/
|
||||||
|
@@ -3571,9 +3571,8 @@ restore_backup:
|
|||||||
unchanged(buf, TRUE);
|
unchanged(buf, TRUE);
|
||||||
/* buf->b_changedtick is always incremented in unchanged() but that
|
/* buf->b_changedtick is always incremented in unchanged() but that
|
||||||
* should not trigger a TextChanged event. */
|
* should not trigger a TextChanged event. */
|
||||||
if (last_changedtick + 1 == buf->b_changedtick
|
if (buf->b_last_changedtick + 1 == buf->b_changedtick) {
|
||||||
&& last_changedtick_buf == buf) {
|
buf->b_last_changedtick = buf->b_changedtick;
|
||||||
last_changedtick = buf->b_changedtick;
|
|
||||||
}
|
}
|
||||||
u_unchanged(buf);
|
u_unchanged(buf);
|
||||||
u_update_save_nr(buf);
|
u_update_save_nr(buf);
|
||||||
|
@@ -868,9 +868,6 @@ EXTERN int did_cursorhold INIT(= false); // set when CursorHold t'gerd
|
|||||||
// for CursorMoved event
|
// for CursorMoved event
|
||||||
EXTERN pos_T last_cursormoved INIT(= INIT_POS_T(0, 0, 0));
|
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 INIT(= 0); /* for CTRL-W CTRL-] command */
|
||||||
EXTERN int postponed_split_flags INIT(= 0); /* args for win_split() */
|
EXTERN int postponed_split_flags INIT(= 0); /* args for win_split() */
|
||||||
EXTERN int postponed_split_tab INIT(= 0); /* cmdmod.tab */
|
EXTERN int postponed_split_tab INIT(= 0); /* cmdmod.tab */
|
||||||
|
@@ -1215,13 +1215,9 @@ static void normal_check_text_changed(NormalState *s)
|
|||||||
{
|
{
|
||||||
// Trigger TextChanged if b_changedtick differs.
|
// Trigger TextChanged if b_changedtick differs.
|
||||||
if (!finish_op && has_event(EVENT_TEXTCHANGED)
|
if (!finish_op && has_event(EVENT_TEXTCHANGED)
|
||||||
&& last_changedtick != curbuf->b_changedtick) {
|
&& curbuf->b_last_changedtick != curbuf->b_changedtick) {
|
||||||
if (last_changedtick_buf == curbuf) {
|
|
||||||
apply_autocmds(EVENT_TEXTCHANGED, NULL, NULL, false, curbuf);
|
apply_autocmds(EVENT_TEXTCHANGED, NULL, NULL, false, curbuf);
|
||||||
}
|
curbuf->b_last_changedtick = curbuf->b_changedtick;
|
||||||
|
|
||||||
last_changedtick_buf = curbuf;
|
|
||||||
last_changedtick = curbuf->b_changedtick;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1165,3 +1165,59 @@ func Test_nocatch_wipe_dummy_buffer()
|
|||||||
call assert_fails('lv½ /x', 'E480')
|
call assert_fails('lv½ /x', 'E480')
|
||||||
au!
|
au!
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" Test TextChangedI and TextChangedP
|
||||||
|
func Test_ChangedP() abort
|
||||||
|
throw 'skipped: Nvim does not support test_override()'
|
||||||
|
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
|
||||||
|
@@ -3,7 +3,10 @@ local Screen = require('test.functional.ui.screen')
|
|||||||
local clear, feed = helpers.clear, helpers.feed
|
local clear, feed = helpers.clear, helpers.feed
|
||||||
local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq
|
local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq
|
||||||
local feed_command, source, expect = helpers.feed_command, helpers.source, helpers.expect
|
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 meths = helpers.meths
|
||||||
|
local wait = helpers.wait
|
||||||
|
|
||||||
describe('completion', function()
|
describe('completion', function()
|
||||||
local screen
|
local screen
|
||||||
@@ -971,4 +974,91 @@ describe('ui/ext_popupmenu', function()
|
|||||||
eq(nil, items) -- popupmenu was hidden
|
eq(nil, items) -- popupmenu was hidden
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('TextChangeP autocommand', function()
|
||||||
|
it('can trigger TextChangedP autocommand as expected',
|
||||||
|
function()
|
||||||
|
curbufmeths.set_lines(0, 1, false, { 'foo', 'bar', 'foobar'})
|
||||||
|
command('set complete=. completeopt=menuone')
|
||||||
|
command('let g:foo = []')
|
||||||
|
command('autocmd! TextChanged * :call add(g:foo, "N")')
|
||||||
|
command('autocmd! TextChangedI * :call add(g:foo, "I")')
|
||||||
|
command('autocmd! TextChangedP * :call add(g:foo, "P")')
|
||||||
|
command('call cursor(3, 1)')
|
||||||
|
|
||||||
|
command('let g:foo = []')
|
||||||
|
feed('o')
|
||||||
|
wait()
|
||||||
|
feed('<esc>')
|
||||||
|
assert.same({'I'}, eval('g:foo'))
|
||||||
|
|
||||||
|
command('let g:foo = []')
|
||||||
|
feed('S')
|
||||||
|
wait()
|
||||||
|
feed('f')
|
||||||
|
wait()
|
||||||
|
assert.same({'I', 'I'}, eval('g:foo'))
|
||||||
|
feed('<esc>')
|
||||||
|
|
||||||
|
command('let g:foo = []')
|
||||||
|
feed('S')
|
||||||
|
wait()
|
||||||
|
feed('f')
|
||||||
|
wait()
|
||||||
|
feed('<C-n>')
|
||||||
|
wait()
|
||||||
|
assert.same({'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()
|
||||||
|
assert.same({'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()
|
||||||
|
assert.same({'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>')
|
||||||
|
assert.same({'I', 'I', 'P', 'P', 'P', 'P'}, eval('g:foo'))
|
||||||
|
feed('<esc>')
|
||||||
|
|
||||||
|
assert.same({'foo', 'bar', 'foobar', 'foo'}, eval('getline(1, "$")'))
|
||||||
|
|
||||||
|
source([[
|
||||||
|
au! TextChanged
|
||||||
|
au! TextChangedI
|
||||||
|
au! TextChangedP
|
||||||
|
set complete&vim completeopt&vim
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
Reference in New Issue
Block a user