neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 9e442c17eeffca934c1de3674b910458e04c989b
parent ab1f96e1d5ba6d6664eb472c2eaade4f91982734
Author: zeertzjq <zeertzjq@outlook.com>
Date:   Tue,  7 Jun 2022 12:41:18 +0800

fix(input): allow Ctrl-C to interrupt a recursive mapping even if mapped (#18885)


Diffstat:
Msrc/nvim/getchar.c | 10+++++-----
Atest/functional/editor/ctrl_c_spec.lua | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dtest/functional/ex_cmds/ctrl_c_spec.lua | 75---------------------------------------------------------------------------
3 files changed, 99 insertions(+), 80 deletions(-)

diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c @@ -2328,19 +2328,19 @@ static int vgetorpeek(bool advance) // try re-mapping. for (;;) { check_end_reg_executing(advance); - // os_breakcheck() can call input_enqueue() - if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state()) { - ctrl_c_interrupts = false; - } // os_breakcheck() is slow, don't use it too often when // inside a mapping. But call it each time for typed // characters. if (typebuf.tb_maplen) { line_breakcheck(); } else { + // os_breakcheck() can call input_enqueue() + if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state()) { + ctrl_c_interrupts = false; + } os_breakcheck(); // check for CTRL-C + ctrl_c_interrupts = true; } - ctrl_c_interrupts = true; int keylen = 0; if (got_int) { // flush all input diff --git a/test/functional/editor/ctrl_c_spec.lua b/test/functional/editor/ctrl_c_spec.lua @@ -0,0 +1,94 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear, feed, source = helpers.clear, helpers.feed, helpers.source +local command = helpers.command +local sleep = helpers.sleep + +describe("CTRL-C (mapped)", function() + local screen + + before_each(function() + clear() + screen = Screen.new(52, 6) + screen:attach() + end) + + it("interrupts :global", function() + -- Crashes luajit. + if helpers.skip_fragile(pending) then + return + end + + source([[ + set nomore nohlsearch undolevels=-1 + nnoremap <C-C> <NOP> + ]]) + + command("silent edit! test/functional/fixtures/bigfile.txt") + + screen:expect([[ + ^0000;<control>;Cc;0;BN;;;;;N;NULL;;;; | + 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; | + 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; | + 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; | + 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;;| + | + ]]) + + local function test_ctrl_c(ms) + feed(":global/^/p<CR>") + screen:sleep(ms) + feed("<C-C>") + screen:expect{any="Interrupt"} + end + + -- The test is time-sensitive. Try different sleep values. + local ms_values = {100, 1000, 10000} + for i, ms in ipairs(ms_values) do + if i < #ms_values then + local status, _ = pcall(test_ctrl_c, ms) + if status then break end + else -- Call the last attempt directly. + test_ctrl_c(ms) + end + end + end) + + it('interrupts :sleep', function() + command('nnoremap <C-C> <Nop>') + feed(':sleep 100<CR>') + -- wait for :sleep to start + sleep(10) + feed('foo<C-C>') + -- wait for input buffer to be flushed + sleep(10) + feed('i') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + ~ | + -- INSERT -- | + ]]) + end) + + it('interrupts recursive mapping', function() + command('nnoremap <C-C> <Nop>') + command('nmap <F2> <Ignore><F2>') + feed('<F2>') + sleep(10) + feed('foo<C-C>') + -- wait for input buffer to be flushed + sleep(10) + feed('i') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + ~ | + -- INSERT -- | + ]]) + end) +end) diff --git a/test/functional/ex_cmds/ctrl_c_spec.lua b/test/functional/ex_cmds/ctrl_c_spec.lua @@ -1,75 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local Screen = require('test.functional.ui.screen') -local clear, feed, source = helpers.clear, helpers.feed, helpers.source -local command = helpers.command -local sleep = helpers.sleep - -describe("CTRL-C (mapped)", function() - local screen - - before_each(function() - clear() - screen = Screen.new(52, 6) - screen:attach() - end) - - it("interrupts :global", function() - -- Crashes luajit. - if helpers.skip_fragile(pending) then - return - end - - source([[ - set nomore nohlsearch undolevels=-1 - nnoremap <C-C> <NOP> - ]]) - - command("silent edit! test/functional/fixtures/bigfile.txt") - - screen:expect([[ - ^0000;<control>;Cc;0;BN;;;;;N;NULL;;;; | - 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; | - 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; | - 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; | - 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;;| - | - ]]) - - local function test_ctrl_c(ms) - feed(":global/^/p<CR>") - screen:sleep(ms) - feed("<C-C>") - screen:expect{any="Interrupt"} - end - - -- The test is time-sensitive. Try different sleep values. - local ms_values = {100, 1000, 10000} - for i, ms in ipairs(ms_values) do - if i < #ms_values then - local status, _ = pcall(test_ctrl_c, ms) - if status then break end - else -- Call the last attempt directly. - test_ctrl_c(ms) - end - end - end) - - it('interrupts :sleep', function() - command('nnoremap <C-C> <Nop>') - feed(':sleep 100<CR>') - -- wait for :sleep to start - sleep(10) - feed('foo<C-C>') - -- wait for input buffer to be flushed - sleep(10) - feed('i') - screen:expect([[ - ^ | - ~ | - ~ | - ~ | - ~ | - -- INSERT -- | - ]]) - end) -end)