mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	fix(input): only disable mapped CTRL-C interrupts when getting input (#18310)
This commit is contained in:
		| @@ -4661,7 +4661,6 @@ void global_exe(char_u *cmd) | |||||||
|   linenr_T old_lcount;      // b_ml.ml_line_count before the command |   linenr_T old_lcount;      // b_ml.ml_line_count before the command | ||||||
|   buf_T *old_buf = curbuf;  // remember what buffer we started in |   buf_T *old_buf = curbuf;  // remember what buffer we started in | ||||||
|   linenr_T lnum;            // line number according to old situation |   linenr_T lnum;            // line number according to old situation | ||||||
|   int save_mapped_ctrl_c = mapped_ctrl_c; |  | ||||||
|  |  | ||||||
|   // Set current position only once for a global command. |   // Set current position only once for a global command. | ||||||
|   // If global_busy is set, setpcmark() will not do anything. |   // If global_busy is set, setpcmark() will not do anything. | ||||||
| @@ -4670,8 +4669,6 @@ void global_exe(char_u *cmd) | |||||||
|  |  | ||||||
|   // When the command writes a message, don't overwrite the command. |   // When the command writes a message, don't overwrite the command. | ||||||
|   msg_didout = true; |   msg_didout = true; | ||||||
|   // Disable CTRL-C mapping, let it interrupt (potentially long output). |  | ||||||
|   mapped_ctrl_c = 0; |  | ||||||
|  |  | ||||||
|   sub_nsubs = 0; |   sub_nsubs = 0; | ||||||
|   sub_nlines = 0; |   sub_nlines = 0; | ||||||
| @@ -4684,7 +4681,6 @@ void global_exe(char_u *cmd) | |||||||
|     os_breakcheck(); |     os_breakcheck(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   mapped_ctrl_c = save_mapped_ctrl_c; |  | ||||||
|   global_busy = 0; |   global_busy = 0; | ||||||
|   if (global_need_beginline) { |   if (global_need_beginline) { | ||||||
|     beginline(BL_WHITE | BL_FIX); |     beginline(BL_WHITE | BL_FIX); | ||||||
|   | |||||||
| @@ -2317,6 +2317,10 @@ static int vgetorpeek(bool advance) | |||||||
|       // try re-mapping. |       // try re-mapping. | ||||||
|       for (;;) { |       for (;;) { | ||||||
|         check_end_reg_executing(advance); |         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 |         // os_breakcheck() is slow, don't use it too often when | ||||||
|         // inside a mapping.  But call it each time for typed |         // inside a mapping.  But call it each time for typed | ||||||
|         // characters. |         // characters. | ||||||
| @@ -2325,6 +2329,7 @@ static int vgetorpeek(bool advance) | |||||||
|         } else { |         } else { | ||||||
|           os_breakcheck();  // check for CTRL-C |           os_breakcheck();  // check for CTRL-C | ||||||
|         } |         } | ||||||
|  |         ctrl_c_interrupts = true; | ||||||
|         int keylen = 0; |         int keylen = 0; | ||||||
|         if (got_int) { |         if (got_int) { | ||||||
|           // flush all input |           // flush all input | ||||||
|   | |||||||
| @@ -670,6 +670,7 @@ EXTERN bool ins_at_eol INIT(= false);   // put cursor after eol when | |||||||
| EXTERN bool no_abbr INIT(= true);       // true when no abbreviations loaded | EXTERN bool no_abbr INIT(= true);       // true when no abbreviations loaded | ||||||
|  |  | ||||||
| EXTERN int mapped_ctrl_c INIT(= 0);  // Modes where CTRL-C is mapped. | EXTERN int mapped_ctrl_c INIT(= 0);  // Modes where CTRL-C is mapped. | ||||||
|  | EXTERN bool ctrl_c_interrupts INIT(= true);  // CTRL-C sets got_int | ||||||
|  |  | ||||||
| EXTERN cmdmod_T cmdmod;                 // Ex command modifiers | EXTERN cmdmod_T cmdmod;                 // Ex command modifiers | ||||||
|  |  | ||||||
|   | |||||||
| @@ -98,7 +98,7 @@ static void create_cursorhold_event(bool events_enabled) | |||||||
|  |  | ||||||
| /// Low level input function | /// Low level input function | ||||||
| /// | /// | ||||||
| /// wait until either the input buffer is non-empty or , if `events` is not NULL | /// wait until either the input buffer is non-empty or, if `events` is not NULL | ||||||
| /// until `events` is non-empty. | /// until `events` is non-empty. | ||||||
| int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *events) | int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *events) | ||||||
| { | { | ||||||
| @@ -106,6 +106,11 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e | |||||||
|     return (int)rbuffer_read(input_buffer, (char *)buf, (size_t)maxlen); |     return (int)rbuffer_read(input_buffer, (char *)buf, (size_t)maxlen); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   // No risk of a UI flood, so disable CTRL-C "interrupt" behavior if it's mapped. | ||||||
|  |   if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state()) { | ||||||
|  |     ctrl_c_interrupts = false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   InbufPollResult result; |   InbufPollResult result; | ||||||
|   if (ms >= 0) { |   if (ms >= 0) { | ||||||
|     if ((result = inbuf_poll(ms, events)) == kInputNone) { |     if ((result = inbuf_poll(ms, events)) == kInputNone) { | ||||||
| @@ -127,6 +132,8 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   ctrl_c_interrupts = true; | ||||||
|  |  | ||||||
|   // If input was put directly in typeahead buffer bail out here. |   // If input was put directly in typeahead buffer bail out here. | ||||||
|   if (typebuf_changed(tb_change_cnt)) { |   if (typebuf_changed(tb_change_cnt)) { | ||||||
|     return 0; |     return 0; | ||||||
| @@ -275,7 +282,7 @@ size_t input_enqueue(String keys) | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   size_t rv = (size_t)(ptr - keys.data); |   size_t rv = (size_t)(ptr - keys.data); | ||||||
|   process_interrupts(); |   process_ctrl_c(); | ||||||
|   return rv; |   return rv; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -480,9 +487,9 @@ static void input_read_cb(Stream *stream, RBuffer *buf, size_t c, void *data, bo | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void process_interrupts(void) | static void process_ctrl_c(void) | ||||||
| { | { | ||||||
|   if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state()) { |   if (!ctrl_c_interrupts) { | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,10 +2,15 @@ local helpers = require('test.functional.helpers')(after_each) | |||||||
| local Screen = require('test.functional.ui.screen') | local Screen = require('test.functional.ui.screen') | ||||||
| local clear, feed, source = helpers.clear, helpers.feed, helpers.source | local clear, feed, source = helpers.clear, helpers.feed, helpers.source | ||||||
| local command = helpers.command | local command = helpers.command | ||||||
|  | local sleep = helpers.sleep | ||||||
|  |  | ||||||
| describe("CTRL-C (mapped)", function() | describe("CTRL-C (mapped)", function() | ||||||
|  |   local screen | ||||||
|  |  | ||||||
|   before_each(function() |   before_each(function() | ||||||
|     clear() |     clear() | ||||||
|  |     screen = Screen.new(52, 6) | ||||||
|  |     screen:attach() | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|   it("interrupts :global", function() |   it("interrupts :global", function() | ||||||
| @@ -20,14 +25,6 @@ describe("CTRL-C (mapped)", function() | |||||||
|     ]]) |     ]]) | ||||||
|  |  | ||||||
|     command("silent edit! test/functional/fixtures/bigfile.txt") |     command("silent edit! test/functional/fixtures/bigfile.txt") | ||||||
|     local screen = Screen.new(52, 6) |  | ||||||
|     screen:attach() |  | ||||||
|     screen:set_default_attr_ids({ |  | ||||||
|       [0] = {foreground = Screen.colors.White, |  | ||||||
|              background = Screen.colors.Red}, |  | ||||||
|       [1] = {bold = true, |  | ||||||
|              foreground = Screen.colors.SeaGreen} |  | ||||||
|     }) |  | ||||||
|  |  | ||||||
|     screen:expect([[ |     screen:expect([[ | ||||||
|       ^0000;<control>;Cc;0;BN;;;;;N;NULL;;;;               | |       ^0000;<control>;Cc;0;BN;;;;;N;NULL;;;;               | | ||||||
| @@ -56,4 +53,23 @@ describe("CTRL-C (mapped)", function() | |||||||
|       end |       end | ||||||
|     end |     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) | end) | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
|  |  | ||||||
| local helpers = require('test.functional.helpers')(after_each) | local helpers = require('test.functional.helpers')(after_each) | ||||||
| local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert | local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert | ||||||
| local feed_command, expect, poke_eventloop = helpers.feed_command, helpers.expect, helpers.poke_eventloop | local expect, poke_eventloop = helpers.expect, helpers.poke_eventloop | ||||||
| local command, eq, eval, meths = helpers.command, helpers.eq, helpers.eval, helpers.meths | local command, eq, eval, meths = helpers.command, helpers.eq, helpers.eval, helpers.meths | ||||||
| local sleep = helpers.sleep | local sleep = helpers.sleep | ||||||
|  |  | ||||||
| @@ -15,7 +15,7 @@ describe('mapping', function() | |||||||
|       ]]) |       ]]) | ||||||
|  |  | ||||||
|     -- Abbreviations with р (0x80) should work. |     -- Abbreviations with р (0x80) should work. | ||||||
|     feed_command('inoreab чкпр   vim') |     command('inoreab чкпр   vim') | ||||||
|     feed('GAчкпр <esc>') |     feed('GAчкпр <esc>') | ||||||
|  |  | ||||||
|     expect([[ |     expect([[ | ||||||
| @@ -25,17 +25,15 @@ describe('mapping', function() | |||||||
|  |  | ||||||
|   it('Ctrl-c works in Insert mode', function() |   it('Ctrl-c works in Insert mode', function() | ||||||
|     -- Mapping of ctrl-c in insert mode |     -- Mapping of ctrl-c in insert mode | ||||||
|     feed_command('set cpo-=< cpo-=k') |     command('set cpo-=< cpo-=k') | ||||||
|     feed_command('inoremap <c-c> <ctrl-c>') |     command('inoremap <c-c> <ctrl-c>') | ||||||
|     feed_command('cnoremap <c-c> dummy') |     command('cnoremap <c-c> dummy') | ||||||
|     feed_command('cunmap <c-c>') |     command('cunmap <c-c>') | ||||||
|     feed('GA<cr>') |     feed('GA<cr>') | ||||||
|     feed('TEST2: CTRL-C |') |     -- XXX: editor must be in Insert mode before <C-C> is put into input buffer | ||||||
|     poke_eventloop() |     poke_eventloop() | ||||||
|     feed('<c-c>A|<cr><esc>') |     feed('TEST2: CTRL-C |<c-c>A|<cr><esc>') | ||||||
|     poke_eventloop() |     command('unmap! <c-c>') | ||||||
|     feed_command('unmap <c-c>') |  | ||||||
|     feed_command('unmap! <c-c>') |  | ||||||
|  |  | ||||||
|     expect([[ |     expect([[ | ||||||
|  |  | ||||||
| @@ -44,13 +42,12 @@ describe('mapping', function() | |||||||
|   end) |   end) | ||||||
|  |  | ||||||
|   it('Ctrl-c works in Visual mode', function() |   it('Ctrl-c works in Visual mode', function() | ||||||
|     feed_command([[vnoremap <c-c> :<C-u>$put ='vmap works'<cr>]]) |     command([[vnoremap <c-c> :<C-u>$put ='vmap works'<cr>]]) | ||||||
|     feed('GV') |     feed('GV') | ||||||
|     -- XXX: For some reason the mapping is only triggered |     -- XXX: editor must be in Visual mode before <C-C> is put into input buffer | ||||||
|     -- when <C-c> is in a separate feed command. |  | ||||||
|     poke_eventloop() |     poke_eventloop() | ||||||
|     feed('<c-c>') |     feed('vV<c-c>') | ||||||
|     feed_command('vunmap <c-c>') |     command('vunmap <c-c>') | ||||||
|  |  | ||||||
|     expect([[ |     expect([[ | ||||||
|  |  | ||||||
| @@ -59,23 +56,23 @@ describe('mapping', function() | |||||||
|  |  | ||||||
|   it('langmap', function() |   it('langmap', function() | ||||||
|     -- langmap should not get remapped in insert mode. |     -- langmap should not get remapped in insert mode. | ||||||
|     feed_command('inoremap { FAIL_ilangmap') |     command('inoremap { FAIL_ilangmap') | ||||||
|     feed_command('set langmap=+{ langnoremap') |     command('set langmap=+{ langnoremap') | ||||||
|     feed('o+<esc>') |     feed('o+<esc>') | ||||||
|  |  | ||||||
|     -- Insert mode expr mapping with langmap. |     -- Insert mode expr mapping with langmap. | ||||||
|     feed_command('inoremap <expr> { "FAIL_iexplangmap"') |     command('inoremap <expr> { "FAIL_iexplangmap"') | ||||||
|     feed('o+<esc>') |     feed('o+<esc>') | ||||||
|  |  | ||||||
|     -- langmap should not get remapped in cmdline mode. |     -- langmap should not get remapped in cmdline mode. | ||||||
|     feed_command('cnoremap { FAIL_clangmap') |     command('cnoremap { FAIL_clangmap') | ||||||
|     feed('o+<esc>') |     feed('o+<esc>') | ||||||
|     feed_command('cunmap {') |     command('cunmap {') | ||||||
|  |  | ||||||
|     -- cmdline mode expr mapping with langmap. |     -- cmdline mode expr mapping with langmap. | ||||||
|     feed_command('cnoremap <expr> { "FAIL_cexplangmap"') |     command('cnoremap <expr> { "FAIL_cexplangmap"') | ||||||
|     feed('o+<esc>') |     feed('o+<esc>') | ||||||
|     feed_command('cunmap {') |     command('cunmap {') | ||||||
|  |  | ||||||
|     -- Assert buffer contents. |     -- Assert buffer contents. | ||||||
|     expect([[ |     expect([[ | ||||||
| @@ -93,10 +90,10 @@ describe('mapping', function() | |||||||
|       ]]) |       ]]) | ||||||
|  |  | ||||||
|     -- Vim's issue #212 (feedkeys insert mapping at current position) |     -- Vim's issue #212 (feedkeys insert mapping at current position) | ||||||
|     feed_command('nnoremap . :call feedkeys(".", "in")<cr>') |     command('nnoremap . :call feedkeys(".", "in")<cr>') | ||||||
|     feed('/^a b<cr>') |     feed('/^a b<cr>') | ||||||
|     feed('0qqdw.ifoo<esc>qj0@q<esc>') |     feed('0qqdw.ifoo<esc>qj0@q<esc>') | ||||||
|     feed_command('unmap .') |     command('unmap .') | ||||||
|     expect([[ |     expect([[ | ||||||
|       fooc d |       fooc d | ||||||
|       fooc d |       fooc d | ||||||
| @@ -105,15 +102,15 @@ describe('mapping', function() | |||||||
|  |  | ||||||
|   it('i_CTRL-G_U', function() |   it('i_CTRL-G_U', function() | ||||||
|     -- <c-g>U<cursor> works only within a single line |     -- <c-g>U<cursor> works only within a single line | ||||||
|     feed_command('imapclear') |     command('imapclear') | ||||||
|     feed_command('imap ( ()<c-g>U<left>') |     command('imap ( ()<c-g>U<left>') | ||||||
|     feed('G2o<esc>ki<cr>Test1: text with a (here some more text<esc>k.') |     feed('G2o<esc>ki<cr>Test1: text with a (here some more text<esc>k.') | ||||||
|     -- test undo |     -- test undo | ||||||
|     feed('G2o<esc>ki<cr>Test2: text wit a (here some more text [und undo]<c-g>u<esc>k.u') |     feed('G2o<esc>ki<cr>Test2: text wit a (here some more text [und undo]<c-g>u<esc>k.u') | ||||||
|     feed_command('imapclear') |     command('imapclear') | ||||||
|     feed_command('set whichwrap=<,>,[,]') |     command('set whichwrap=<,>,[,]') | ||||||
|     feed('G3o<esc>2k') |     feed('G3o<esc>2k') | ||||||
|     feed_command([[:exe ":norm! iTest3: text with a (parenthesis here\<C-G>U\<Right>new line here\<esc>\<up>\<up>."]]) |     command([[:exe ":norm! iTest3: text with a (parenthesis here\<C-G>U\<Right>new line here\<esc>\<up>\<up>."]]) | ||||||
|  |  | ||||||
|     expect([[ |     expect([[ | ||||||
|  |  | ||||||
|   | |||||||
| @@ -268,7 +268,7 @@ describe('system()', function() | |||||||
|         :call system("for /L %I in (1,0,2) do @echo y")      |]] |         :call system("for /L %I in (1,0,2) do @echo y")      |]] | ||||||
|         or  [[ |         or  [[ | ||||||
|         :call system("yes")                                  |]])) |         :call system("yes")                                  |]])) | ||||||
|       feed('<c-c>') |       feed('foo<c-c>') | ||||||
|       screen:expect([[ |       screen:expect([[ | ||||||
|         ^                                                     | |         ^                                                     | | ||||||
|         ~                                                    | |         ~                                                    | | ||||||
| @@ -286,6 +286,49 @@ describe('system()', function() | |||||||
|         Type  :qa  and press <Enter> to exit Nvim            | |         Type  :qa  and press <Enter> to exit Nvim            | | ||||||
|       ]]) |       ]]) | ||||||
|     end) |     end) | ||||||
|  |  | ||||||
|  |     it('`yes` interrupted with mapped CTRL-C', function() | ||||||
|  |       command('nnoremap <C-C> i') | ||||||
|  |       feed(':call system("' .. (iswin() | ||||||
|  |         and 'for /L %I in (1,0,2) do @echo y' | ||||||
|  |         or  'yes') .. '")<cr>') | ||||||
|  |       screen:expect([[ | ||||||
|  |                                                              | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  | ]] .. (iswin() | ||||||
|  |         and [[ | ||||||
|  |         :call system("for /L %I in (1,0,2) do @echo y")      |]] | ||||||
|  |         or  [[ | ||||||
|  |         :call system("yes")                                  |]])) | ||||||
|  |       feed('foo<c-c>') | ||||||
|  |       screen:expect([[ | ||||||
|  |         ^                                                     | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         ~                                                    | | ||||||
|  |         -- INSERT --                                         | | ||||||
|  |       ]]) | ||||||
|  |     end) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|   describe('passing no input', function() |   describe('passing no input', function() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 zeertzjq
					zeertzjq