diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 29aa6d0cad..ef7d7428a1 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -315,6 +315,16 @@ size_t input_enqueue(uint64_t chan_id, String keys) { current_ui = chan_id; + // If the buffer has been fully drained, rewind the read/write cursors to the + // start to reclaim tail space. Otherwise a drained-but-not-rewound buffer left + // pinned within <19 bytes of the top (input_get() advances input_read_pos but + // never rewinds it) would fail the `input_space() >= 19` gate below forever, + // silently dropping all further input and freezing the editor. + if (input_read_pos == input_write_pos) { + input_read_pos = input_buffer; + input_write_pos = input_buffer; + } + if (keys.size > 0) { set_vim_var_nr(VV_USERACTIVE, (varnumber_T)os_realtime()); } diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 817684846f..3846cfd3ae 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -820,6 +820,23 @@ describe('API', function() command("call nvim_input('')") eq(1, eval('1')) end) + + it('keeps accepting input after the input buffer fills and drains', function() + -- Regression test: the fixed input buffer (INPUT_BUFFER_SIZE == 16386) is + -- compacted left, not a ring. Filling it to within <19 bytes of the top + -- and then draining it used to leave the read/write cursors pinned, so + -- input_enqueue() could never reclaim space and silently dropped all + -- further input, freezing the editor. One oversized burst of a Normal-mode + -- no-op (CTRL-L) saturates the buffer in a single nvim_input() call; once it + -- has drained, input must still register. poke_eventloop() waits for the + -- burst to be fully consumed before the probe: the fix only reclaims space + -- once the buffer is empty, so the probe must arrive after the drain. + api.nvim_input(('\12'):rep(20000)) + n.poke_eventloop() + api.nvim_input('ix') + n.poke_eventloop() + eq('x', api.nvim_get_current_line()) + end) end) describe('nvim_paste', function()