mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00:00 
			
		
		
		
	fix(input): only disable mapped CTRL-C interrupts when getting input
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
 | 
			
		||||
  buf_T *old_buf = curbuf;  // remember what buffer we started in
 | 
			
		||||
  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.
 | 
			
		||||
  // 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.
 | 
			
		||||
  msg_didout = true;
 | 
			
		||||
  // Disable CTRL-C mapping, let it interrupt (potentially long output).
 | 
			
		||||
  mapped_ctrl_c = 0;
 | 
			
		||||
 | 
			
		||||
  sub_nsubs = 0;
 | 
			
		||||
  sub_nlines = 0;
 | 
			
		||||
@@ -4684,7 +4681,6 @@ void global_exe(char_u *cmd)
 | 
			
		||||
    os_breakcheck();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mapped_ctrl_c = save_mapped_ctrl_c;
 | 
			
		||||
  global_busy = 0;
 | 
			
		||||
  if (global_need_beginline) {
 | 
			
		||||
    beginline(BL_WHITE | BL_FIX);
 | 
			
		||||
 
 | 
			
		||||
@@ -2317,6 +2317,10 @@ 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.
 | 
			
		||||
@@ -2325,6 +2329,7 @@ static int vgetorpeek(bool advance)
 | 
			
		||||
        } else {
 | 
			
		||||
          os_breakcheck();  // check for CTRL-C
 | 
			
		||||
        }
 | 
			
		||||
        ctrl_c_interrupts = true;
 | 
			
		||||
        int keylen = 0;
 | 
			
		||||
        if (got_int) {
 | 
			
		||||
          // 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 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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 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;
 | 
			
		||||
  if (ms >= 0) {
 | 
			
		||||
    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 (typebuf_changed(tb_change_cnt)) {
 | 
			
		||||
    return 0;
 | 
			
		||||
@@ -275,7 +282,7 @@ size_t input_enqueue(String keys)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  size_t rv = (size_t)(ptr - keys.data);
 | 
			
		||||
  process_interrupts();
 | 
			
		||||
  process_ctrl_c();
 | 
			
		||||
  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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,15 @@ 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()
 | 
			
		||||
@@ -20,14 +25,6 @@ describe("CTRL-C (mapped)", function()
 | 
			
		||||
    ]])
 | 
			
		||||
 | 
			
		||||
    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([[
 | 
			
		||||
      ^0000;<control>;Cc;0;BN;;;;;N;NULL;;;;               |
 | 
			
		||||
@@ -56,4 +53,23 @@ describe("CTRL-C (mapped)", function()
 | 
			
		||||
      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)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
local helpers = require('test.functional.helpers')(after_each)
 | 
			
		||||
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 sleep = helpers.sleep
 | 
			
		||||
 | 
			
		||||
@@ -15,7 +15,7 @@ describe('mapping', function()
 | 
			
		||||
      ]])
 | 
			
		||||
 | 
			
		||||
    -- Abbreviations with р (0x80) should work.
 | 
			
		||||
    feed_command('inoreab чкпр   vim')
 | 
			
		||||
    command('inoreab чкпр   vim')
 | 
			
		||||
    feed('GAчкпр <esc>')
 | 
			
		||||
 | 
			
		||||
    expect([[
 | 
			
		||||
@@ -25,17 +25,15 @@ describe('mapping', function()
 | 
			
		||||
 | 
			
		||||
  it('Ctrl-c works in Insert mode', function()
 | 
			
		||||
    -- Mapping of ctrl-c in insert mode
 | 
			
		||||
    feed_command('set cpo-=< cpo-=k')
 | 
			
		||||
    feed_command('inoremap <c-c> <ctrl-c>')
 | 
			
		||||
    feed_command('cnoremap <c-c> dummy')
 | 
			
		||||
    feed_command('cunmap <c-c>')
 | 
			
		||||
    command('set cpo-=< cpo-=k')
 | 
			
		||||
    command('inoremap <c-c> <ctrl-c>')
 | 
			
		||||
    command('cnoremap <c-c> dummy')
 | 
			
		||||
    command('cunmap <c-c>')
 | 
			
		||||
    feed('GA<cr>')
 | 
			
		||||
    feed('TEST2: CTRL-C |')
 | 
			
		||||
    -- XXX: editor must be in Insert mode before <C-C> is put into input buffer
 | 
			
		||||
    poke_eventloop()
 | 
			
		||||
    feed('<c-c>A|<cr><esc>')
 | 
			
		||||
    poke_eventloop()
 | 
			
		||||
    feed_command('unmap <c-c>')
 | 
			
		||||
    feed_command('unmap! <c-c>')
 | 
			
		||||
    feed('TEST2: CTRL-C |<c-c>A|<cr><esc>')
 | 
			
		||||
    command('unmap! <c-c>')
 | 
			
		||||
 | 
			
		||||
    expect([[
 | 
			
		||||
 | 
			
		||||
@@ -44,13 +42,12 @@ describe('mapping', function()
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  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')
 | 
			
		||||
    -- XXX: For some reason the mapping is only triggered
 | 
			
		||||
    -- when <C-c> is in a separate feed command.
 | 
			
		||||
    -- XXX: editor must be in Visual mode before <C-C> is put into input buffer
 | 
			
		||||
    poke_eventloop()
 | 
			
		||||
    feed('<c-c>')
 | 
			
		||||
    feed_command('vunmap <c-c>')
 | 
			
		||||
    feed('vV<c-c>')
 | 
			
		||||
    command('vunmap <c-c>')
 | 
			
		||||
 | 
			
		||||
    expect([[
 | 
			
		||||
 | 
			
		||||
@@ -59,23 +56,23 @@ describe('mapping', function()
 | 
			
		||||
 | 
			
		||||
  it('langmap', function()
 | 
			
		||||
    -- langmap should not get remapped in insert mode.
 | 
			
		||||
    feed_command('inoremap { FAIL_ilangmap')
 | 
			
		||||
    feed_command('set langmap=+{ langnoremap')
 | 
			
		||||
    command('inoremap { FAIL_ilangmap')
 | 
			
		||||
    command('set langmap=+{ langnoremap')
 | 
			
		||||
    feed('o+<esc>')
 | 
			
		||||
 | 
			
		||||
    -- Insert mode expr mapping with langmap.
 | 
			
		||||
    feed_command('inoremap <expr> { "FAIL_iexplangmap"')
 | 
			
		||||
    command('inoremap <expr> { "FAIL_iexplangmap"')
 | 
			
		||||
    feed('o+<esc>')
 | 
			
		||||
 | 
			
		||||
    -- langmap should not get remapped in cmdline mode.
 | 
			
		||||
    feed_command('cnoremap { FAIL_clangmap')
 | 
			
		||||
    command('cnoremap { FAIL_clangmap')
 | 
			
		||||
    feed('o+<esc>')
 | 
			
		||||
    feed_command('cunmap {')
 | 
			
		||||
    command('cunmap {')
 | 
			
		||||
 | 
			
		||||
    -- cmdline mode expr mapping with langmap.
 | 
			
		||||
    feed_command('cnoremap <expr> { "FAIL_cexplangmap"')
 | 
			
		||||
    command('cnoremap <expr> { "FAIL_cexplangmap"')
 | 
			
		||||
    feed('o+<esc>')
 | 
			
		||||
    feed_command('cunmap {')
 | 
			
		||||
    command('cunmap {')
 | 
			
		||||
 | 
			
		||||
    -- Assert buffer contents.
 | 
			
		||||
    expect([[
 | 
			
		||||
@@ -93,10 +90,10 @@ describe('mapping', function()
 | 
			
		||||
      ]])
 | 
			
		||||
 | 
			
		||||
    -- 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('0qqdw.ifoo<esc>qj0@q<esc>')
 | 
			
		||||
    feed_command('unmap .')
 | 
			
		||||
    command('unmap .')
 | 
			
		||||
    expect([[
 | 
			
		||||
      fooc d
 | 
			
		||||
      fooc d
 | 
			
		||||
@@ -105,15 +102,15 @@ describe('mapping', function()
 | 
			
		||||
 | 
			
		||||
  it('i_CTRL-G_U', function()
 | 
			
		||||
    -- <c-g>U<cursor> works only within a single line
 | 
			
		||||
    feed_command('imapclear')
 | 
			
		||||
    feed_command('imap ( ()<c-g>U<left>')
 | 
			
		||||
    command('imapclear')
 | 
			
		||||
    command('imap ( ()<c-g>U<left>')
 | 
			
		||||
    feed('G2o<esc>ki<cr>Test1: text with a (here some more text<esc>k.')
 | 
			
		||||
    -- test undo
 | 
			
		||||
    feed('G2o<esc>ki<cr>Test2: text wit a (here some more text [und undo]<c-g>u<esc>k.u')
 | 
			
		||||
    feed_command('imapclear')
 | 
			
		||||
    feed_command('set whichwrap=<,>,[,]')
 | 
			
		||||
    command('imapclear')
 | 
			
		||||
    command('set whichwrap=<,>,[,]')
 | 
			
		||||
    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([[
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -268,7 +268,7 @@ describe('system()', function()
 | 
			
		||||
        :call system("for /L %I in (1,0,2) do @echo y")      |]]
 | 
			
		||||
        or  [[
 | 
			
		||||
        :call system("yes")                                  |]]))
 | 
			
		||||
      feed('<c-c>')
 | 
			
		||||
      feed('foo<c-c>')
 | 
			
		||||
      screen:expect([[
 | 
			
		||||
        ^                                                     |
 | 
			
		||||
        ~                                                    |
 | 
			
		||||
@@ -286,6 +286,49 @@ describe('system()', function()
 | 
			
		||||
        Type  :qa  and press <Enter> to exit Nvim            |
 | 
			
		||||
      ]])
 | 
			
		||||
    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)
 | 
			
		||||
 | 
			
		||||
  describe('passing no input', function()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user