diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 1dde2c5101..bb22c76cae 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -408,7 +408,7 @@ struct file_buffer { 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_i; // b:changedtick for TextChangedI/T varnumber_T b_last_changedtick_pum; // b:changedtick for TextChangedP bool b_saving; // Set to true if we are in the middle of diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 8640b977df..74efd51cfd 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -780,6 +780,8 @@ bool terminal_enter(void) // Tell the terminal it has focus terminal_focus(s->term, true); + // Don't fire TextChangedT from changes in Normal mode. + curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf); apply_autocmds(EVENT_TERMENTER, NULL, NULL, false, curbuf); may_trigger_modechanged(); @@ -805,6 +807,8 @@ bool terminal_enter(void) // Tell the terminal it lost focus terminal_focus(s->term, false); + // Don't fire TextChanged from changes in terminal mode. + curbuf->b_last_changedtick = buf_get_changedtick(curbuf); if (curbuf->terminal == s->term && !s->close) { terminal_check_cursor(); @@ -892,9 +896,10 @@ static int terminal_check(VimState *state) // Don't let autocommands free the terminal from under our fingers. s->term->refcount++; - if (must_redraw) { - // TODO(seandewar): above changes will maybe change the behaviour of this more; untrollify this + if (has_event(EVENT_TEXTCHANGEDT) + && curbuf->b_last_changedtick_i != buf_get_changedtick(curbuf)) { apply_autocmds(EVENT_TEXTCHANGEDT, NULL, NULL, false, curbuf); + curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf); } may_trigger_win_scrolled_resized(); s->term->refcount--; diff --git a/test/functional/autocmd/termxx_spec.lua b/test/functional/autocmd/termxx_spec.lua index 0ec1a54d32..c5bc5d928e 100644 --- a/test/functional/autocmd/termxx_spec.lua +++ b/test/functional/autocmd/termxx_spec.lua @@ -1,6 +1,6 @@ local t = require('test.testutil') local n = require('test.functional.testnvim')() -local tt = require('test.functional.testterm') +local Screen = require('test.functional.ui.screen') local uv = vim.uv local clear, command, testprg = n.clear, n.command, n.testprg @@ -202,12 +202,81 @@ describe('autocmd TextChangedT,WinResized', function() before_each(clear) it('TextChangedT works', function() - command('autocmd TextChangedT * ++once let g:called = 1') - tt.setup_screen() - tt.feed_data('a') - retry(nil, nil, function() - eq(1, api.nvim_get_var('called')) + local screen = Screen.new(50, 7) + screen:set_default_attr_ids({ + [1] = { bold = true }, + [31] = { foreground = Screen.colors.Gray100, background = Screen.colors.DarkGreen }, + [32] = { + foreground = Screen.colors.Gray100, + bold = true, + background = Screen.colors.DarkGreen, + }, + }) + + local term, term_unfocused = exec_lua(function() + -- Split windows before opening terminals so TextChangedT doesn't fire an additional time due + -- to the inner terminal being resized (which is usually deferred too). + vim.cmd.vnew() + local term_unfocused = vim.api.nvim_open_term(0, {}) + vim.cmd.wincmd 'p' + local term = vim.api.nvim_open_term(0, {}) + vim.cmd.startinsert() + return term, term_unfocused end) + eq('t', eval('mode()')) + + exec_lua(function() + _G.n_triggered = 0 + vim.api.nvim_create_autocmd('TextChanged', { + callback = function() + _G.n_triggered = _G.n_triggered + 1 + end, + }) + _G.t_triggered = 0 + vim.api.nvim_create_autocmd('TextChangedT', { + callback = function() + _G.t_triggered = _G.t_triggered + 1 + end, + }) + end) + + api.nvim_chan_send(term, 'a') + retry(nil, nil, function() + eq(1, exec_lua('return _G.t_triggered')) + end) + api.nvim_chan_send(term, 'b') + retry(nil, nil, function() + eq(2, exec_lua('return _G.t_triggered')) + end) + + -- Not triggered by changes in a non-current terminal. + api.nvim_chan_send(term_unfocused, 'hello') + screen:expect([[ + hello │ab^ | + │ |*4 + {31:[Scratch] [-] }{32:[Scratch] [-] }| + {1:-- TERMINAL --} | + ]]) + eq(2, exec_lua('return _G.t_triggered')) + + -- Not triggered by unflushed redraws. + api.nvim__redraw({ valid = false, flush = false }) + eq(2, exec_lua('return _G.t_triggered')) + + -- Not triggered when not in terminal mode. + command('stopinsert') + eq('n', eval('mode()')) + eq(2, exec_lua('return _G.t_triggered')) + eq(0, exec_lua('return _G.n_triggered')) -- Nothing we did was in Normal mode yet. + + api.nvim_chan_send(term, 'c') + screen:expect([[ + hello │a^bc | + │ |*4 + {31:[Scratch] [-] }{32:[Scratch] [-] }| + | + ]]) + eq(1, exec_lua('return _G.n_triggered')) -- Happened in Normal mode. end) it('no crash when deleting terminal buffer', function()