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:
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)