aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoribhagwan <59988195+ibhagwan@users.noreply.github.com>2024-08-19 15:23:56 -0700
committerGitHub <noreply@github.com>2024-08-20 06:23:56 +0800
commit1d11808bfd2879bf278cd05a7095a6634fa5afec (patch)
treef8a2f1b58cf9d4a372811b6f185f985ce2e23318
parent169f37b94f5227fd3bb98aef3e1f592ab22f96b7 (diff)
downloadrneovim-1d11808bfd2879bf278cd05a7095a6634fa5afec.tar.gz
rneovim-1d11808bfd2879bf278cd05a7095a6634fa5afec.tar.bz2
rneovim-1d11808bfd2879bf278cd05a7095a6634fa5afec.zip
fix(terminal): interrupt/got_int hangs terminal (#30056)
Upon `terminal_enter`, `mapped_ctrl_c` is set in order to avoid `CTRL-C` interrupts (which is proxied to the terminal process instead), `os_inchar` will then test `mapped_ctrl_c` against `State` and set `ctrl_c_interrupts=false` which prevents `process_ctrl_c` from setting `got_int=true` in a terminal state. However, if `got_int` is set outside of `process_ctrl_c`, e.g. via `interrupt()`, this will hang the neovim process as `terminal_execute` will enter an endless loop as `got_int` will never be cleared causing `safe_vgetc` to always return `Ctrl_C`. A minimal example reproducing this bug: ```vim :autocmd TermEnter * call timer_start(500, {-> interrupt()}) :terminal :startinsert ``` To fix, we make sure `got_int` is cleared inside `terminal_execute` when it detects `Ctrl_C`. Closes #20726 Co-authored-by: zeertzjq <zeertzjq@outlook.com>
-rw-r--r--src/nvim/terminal.c6
-rw-r--r--test/functional/terminal/buffer_spec.lua10
2 files changed, 16 insertions, 0 deletions
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 43f68f7321..ea3617098b 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -782,6 +782,12 @@ static int terminal_execute(VimState *state, int key)
FALLTHROUGH;
default:
+ if (key == Ctrl_C) {
+ // terminal_enter() always sets `mapped_ctrl_c` to avoid `got_int`. 8eeda7169aa4
+ // But `got_int` may be set elsewhere, e.g. by interrupt() or an autocommand,
+ // so ensure that it is cleared.
+ got_int = false;
+ }
if (key == Ctrl_BSL && !s->got_bsl) {
s->got_bsl = true;
break;
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
index 365a4f8035..767a3dc205 100644
--- a/test/functional/terminal/buffer_spec.lua
+++ b/test/functional/terminal/buffer_spec.lua
@@ -312,6 +312,16 @@ describe(':terminal buffer', function()
pcall_err(command, 'write test/functional/fixtures/tty-test.c')
)
end)
+
+ it('external interrupt (got_int) does not hang #20726', function()
+ eq({ mode = 't', blocking = false }, api.nvim_get_mode())
+ command('call timer_start(0, {-> interrupt()})')
+ feed('<Ignore>') -- Add input to separate two RPC requests
+ eq({ mode = 't', blocking = false }, api.nvim_get_mode())
+ feed([[<C-\><C-N>]])
+ eq({ mode = 'nt', blocking = false }, api.nvim_get_mode())
+ command('bd!')
+ end)
end)
describe(':terminal buffer', function()