Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpectedly leaving insert mode when job writes to hidden buffer using prompt buffers (regression by patch 8.2.1654) #7035

Closed
puremourning opened this issue Sep 28, 2020 · 19 comments

Comments

@puremourning
Copy link
Contributor

puremourning commented Sep 28, 2020

Describe the bug

After patch 8.2.1654, insert mode is exited unexpectedly using prompt-buffers when a job writes to a hidden buffer.

I used the repro case to bisect this to 8.2.1654 patch.

To Reproduce
Detailed steps to reproduce the behavior:

test.vim

set buftype=prompt

call prompt_setprompt( bufnr(), 'Enter some text: ' )

call job_start( [ 'uuencode', '/dev/urandom', '/dev/stdout' ], #{
      \ out_io: 'buffer',
      \ out_name: ''
      \ } )
  • vim -Nu NONE -S test.vim
  • i
  • type something

You will be bumped unexpectedly out of insert mode into normal mode as you're typing.

Expected behavior

Remain in insert mode until pressing <CR> in the prompt buffer.

Environment (please complete the following information):

  • Vim version 8.6.1654 and later
  • OS: Linux RHEL 7, macOS
  • Terminal: tmux, PuTTY, iTerm
@brammool
Copy link
Contributor

I'm surprised patch 8.6.1654 would cause this, and only now gets reported.
Are you sure it's not related to 8.2.1762 ?

@puremourning
Copy link
Contributor Author

I can check but I bisected and it indicated the above commit. I can confirm that with 8.2.1762 we no longer have the pum left open, but it still exits insert mode unexpectedly. With 8.1.1654, you end up with both - exit insert mode unexpectedly and the pum is left open.

@tonymec
Copy link

tonymec commented Sep 28, 2020

  • Does it happen also in gvim?
  • What are the answers to
:set timeout? timeoutlen? ttimeout? ttimeoutlen?

(including the question marks)?

Best regards,
Tony.

@puremourning
Copy link
Contributor Author

puremourning commented Sep 28, 2020

Does it happen also in gvim?

Will check motif GUI at work tomorrow, sorry don't have a gvim here right now.

For the record, it does repro in MacVim (GUI) with:

ben@BenMBP vim-clean % gvim --version
VIM - Vi IMproved 8.2 (2019 Dec 12, compiled Sep 28 2020 22:02:11)
macOS version
Included patches: 1-1768
Compiled by Homebrew
Huge version with MacVim GUI.  Features included (+) or not (-):

What are the answers to :set timeout? timeoutlen? ttimeout? ttimeoutlen?

Screenshot 2020-09-28 at 22 21 56

  • timeout
  • timeoutlen=1000
  • ttimeout
  • ttimeoutlen=100

@vim-ml
Copy link

vim-ml commented Sep 28, 2020 via email

@puremourning
Copy link
Contributor Author

Yes Tony I’m fairly sure that this is not a mapping issue. It’s as if something is calling stopinsert. I have a couple of theories of how to minimise the repro case. Will try after work hopefully.

Re the point about being not reported before, it seems that it’s only prompt buffers that are affected, which are not frequently used.

@puremourning
Copy link
Contributor Author

OK, this is easy to repro. Here's a trivial case.

set buftype=prompt

call prompt_setprompt( bufnr(), 'Enter some text: ' )

call job_start( [ 'uuencode', '/dev/urandom', '/dev/stdout' ], #{
      \ out_io: 'buffer',
      \ out_name: ''
      \ } )
  • vim -Nu NONE -S test.vim
  • enter insert mode i and type some characters - you're bumped out of insert mode

@puremourning
Copy link
Contributor Author

Here's a test that repros it:


function! Test_Prompt_While_Writing_To_Hidden_Buffer()
  call CanTestPromptBuffer()
  let scriptName = 'XpromptscriptHiddenBuf'
  let script =<< trim END
    set buftype=prompt
    call prompt_setprompt( bufnr(), 'cmd:' )
    let job = job_start( [ 'uuencode', '/dev/urandom', '/dev/stdout' ], #{
      \ out_io: 'buffer',
      \ out_name: '' } )
    startinsert
  END
  exec script->writefile( scriptName )

  let buf = RunVimInTerminal('-S ' . scriptName, {})
  call WaitForAssert({-> assert_equal('cmd:', term_getline(buf, 1))})

  call term_sendkeys( buf, 'test' )
  call WaitForAssert( {-> assert_equal('cmd:test', term_getline(buf, 1)) } )

  call StopVimInTerminal(buf)
  call delete(scriptName)
endfunc

|| command line..script /Users/ben/Development/vim/src/testdir/runtest.vim[459]..function RunTheTest[39]..Test_Prompt_While_Writing_To_Hidden_Buffer[17]..WaitForAssert[2]..6_WaitForCommon[11]..3 line 1: Expected 'cmd:test' but got 'cmd:t'

@puremourning puremourning changed the title Unexpectedly leaving insert mode when completion is triggered in prompt buffer (regression by patch 8.2.1654) Unexpectedly leaving insert mode when job writes to hidden buffer using prompt buffers (regression by patch 8.2.1654) Sep 29, 2020
@puremourning
Copy link
Contributor Author

Been debugging this. I'm thinking that it's related to this:

    static void
leaving_window(win_T *win)
{
    // Only matters for a prompt window.
    if (!bt_prompt(win->w_buffer))
	return;

    // When leaving a prompt window stop Insert mode and perhaps restart
    // it when entering that window again.
    win->w_buffer->b_prompt_insert = restart_edit;
    if (restart_edit != 0 && mode_displayed)
	clear_cmdline = TRUE;		// unshow mode later
    restart_edit = NUL;

    // When leaving the window (or closing the window) was done from a
    // callback we need to break out of the Insert mode loop and restart Insert
    // mode when entering the window again.
    if (State & INSERT)
    {
	stop_insert_mode = TRUE;
	if (win->w_buffer->b_prompt_insert == NUL)
	    win->w_buffer->b_prompt_insert = 'A';
    }
}

    static void
entering_window(win_T *win)
{
    // Only matters for a prompt window.
    if (!bt_prompt(win->w_buffer))
	return;

    // When switching to a prompt buffer that was in Insert mode, don't stop
    // Insert mode, it may have been set in leaving_window().
    if (win->w_buffer->b_prompt_insert != NUL)
	stop_insert_mode = FALSE;

    // When entering the prompt window restart Insert mode if we were in Insert
    // mode when we left it.
    restart_edit = win->w_buffer->b_prompt_insert;
}

When using aucmd_prepwin we actually leave the window in order to write to the hidden buffer, and we're calling leaving_window(). . But it seems like we're not calling entering_window() for the prompt buffer when reverting this temporary window.

For certain when we do call entering_window(), bt_prompt(win->w_buffer) is false. I'ma summing that's actually entering the temporary autocmd window.

Are we missing a call to win_enter in aucmd_restbuf() ? That seems intrusive, but I haven't fully groked this code yet.

@brammool
Copy link
Contributor

I can't reproduce because "uuencode: Command not found". Perhaps something more common (and not keeping the CPU busy)?

@brammool
Copy link
Contributor

Oh, and there is no aucmd_prepwin(), you probably mean aucmd_prepbuf(). But it doesn't call leaving_window() directly.

@puremourning
Copy link
Contributor Author

Just need a job to write to a hidden buffer while in insert mode.

This repro's it too

set buftype=prompt

call prompt_setprompt( bufnr(), 'Enter some text: ' )

augroup TEST
  au InsertEnter call job_start( [ 'ls' ], #{
      \ out_io: 'buffer',
      \ out_name: ''
      \ } )
augroup END

this seems to resolve it:

ben@BenMBP vim-clean % git diff
diff --git a/src/autocmd.c b/src/autocmd.c
index d5c61ca6a..1789d0477 100644
--- a/src/autocmd.c
+++ b/src/autocmd.c
@@ -1530,6 +1530,7 @@ win_found:
 
        restore_snapshot(SNAP_AUCMD_IDX, FALSE);
        (void)win_comp_pos();   // recompute window positions
+       win_enter(aco->save_curwin, TRUE);
        unblock_autocmds();
 
        if (win_valid(aco->save_curwin))
ben@BenMBP vim-clean % 

@puremourning
Copy link
Contributor Author

Oh, and there is no aucmd_prepwin(), you probably mean aucmd_prepbuf(). But it doesn't call leaving_window() directly.

Yeah I meant aucmd_prepbuf() - this calls win_split_ins(), which calls win_enter_ext(wp, FALSE, FALSE, TRUE, TRUE, TRUE); ... and that calls leaving_window()

@puremourning
Copy link
Contributor Author

puremourning commented Sep 29, 2020

OK here's a test which repros it too; had to introduce some sleeps though :/

function! Test_Prompt_While_Writing_To_Hidden_Buffer()
  call CanTestPromptBuffer()
  let scriptName = 'XpromptscriptHiddenBuf'
  let script =<< trim END
    set buftype=prompt
    call prompt_setprompt( bufnr(), 'cmd:' )
    let job = job_start( [ '/bin/sh', '-c', 'while true; do echo line; sleep 0.2; done' ], #{
      \ out_io: 'buffer',
      \ out_name: '' } )
    startinsert
  END
  exec script->writefile( scriptName )

  let buf = RunVimInTerminal('-S ' . scriptName, {})
  call WaitForAssert({-> assert_equal('cmd:', term_getline(buf, 1))})

  call term_sendkeys( buf, 'test' )
  call WaitForAssert( {-> assert_equal('cmd:test', term_getline(buf, 1)) } )
  sleep 10m
  call term_sendkeys( buf, 'test' )
  call WaitForAssert( {-> assert_equal('cmd:testtest', term_getline(buf, 1)) } )
  sleep 10m
  call term_sendkeys( buf, 'test' )
  call WaitForAssert( {-> assert_equal('cmd:testtesttest', term_getline(buf, 1)) } )

  call StopVimInTerminal(buf)
  call delete(scriptName)
endfunc

@tjdevries
Copy link

I was wondering if this patch (that I just made myself when I first encountered the bug) is useful:

neovim/neovim#12977

I have no idea if this is the "right" solution or not, just figured I'd show what I was thinking. I just expected :stopinsert to just completely shutdown everything that could be happening in insert mode.

@happy-dude
Copy link

happy-dude commented Oct 1, 2020

Just so no one is confused about @puremourning 's test script, WaitForAssert is defined in https://github.com/puremourning/vimspector/blob/4ed915828285c067b7f4a35fecc7e0ed56fc9be8/tests/lib/plugin/shared.vim.

I believe you are calling helper functions from the vimspector plugin, puremourning?

EDIT: if you can upload your full script (and dependencies), it could help us run your test case 👍
As it is now, CanTestPromptBuffer, RunVimInTerminal and other functions/vars are undefined.

@puremourning
Copy link
Contributor Author

@happy-dude my test runs in Vim’s test suite. Paste it into test_promt_buffer.vim

Those functions are defined in vim’s shared.vim and friends.

@brammool
Copy link
Contributor

brammool commented Oct 1, 2020

Thanks for figuring this out and writing a test. I'll clean it up a bit.

@puremourning
Copy link
Contributor Author

Thanks Bram. That's working nice now.

seandewar added a commit to seandewar/neovim that referenced this issue Nov 8, 2021
Problem:    Writing to prompt buffer interferes with insert mode.
Solution:   Use win_enter() instead of just setting "curwin". (Ben Jackson,
            closes vim/vim#7035)
vim/vim@4537bcc

Patch v8.2.1783 has fixes.
seandewar added a commit to seandewar/neovim that referenced this issue Nov 12, 2021
Problem:    Writing to prompt buffer interferes with insert mode.
Solution:   Use win_enter() instead of just setting "curwin". (Ben Jackson,
            closes vim/vim#7035)
vim/vim@4537bcc
seandewar added a commit to seandewar/neovim that referenced this issue Nov 24, 2021
Problem:    Writing to prompt buffer interferes with insert mode.
Solution:   Use win_enter() instead of just setting "curwin". (Ben Jackson,
            closes vim/vim#7035)
vim/vim@4537bcc
seandewar added a commit to seandewar/neovim that referenced this issue Nov 24, 2021
Problem:    Writing to prompt buffer interferes with insert mode.
Solution:   Use win_enter() instead of just setting "curwin". (Ben Jackson,
            closes vim/vim#7035)
vim/vim@4537bcc
seandewar added a commit to seandewar/neovim that referenced this issue Nov 24, 2021
Problem:    Writing to prompt buffer interferes with insert mode.
Solution:   Use win_enter() instead of just setting "curwin". (Ben Jackson,
            closes vim/vim#7035)
vim/vim@4537bcc
seandewar added a commit to seandewar/neovim that referenced this issue Nov 24, 2021
Problem:    Writing to prompt buffer interferes with insert mode.
Solution:   Use win_enter() instead of just setting "curwin". (Ben Jackson,
            closes vim/vim#7035)
vim/vim@4537bcc
seandewar added a commit to seandewar/neovim that referenced this issue Nov 24, 2021
Problem:    Writing to prompt buffer interferes with insert mode.
Solution:   Use win_enter() instead of just setting "curwin". (Ben Jackson,
            closes vim/vim#7035)
vim/vim@4537bcc
seandewar added a commit to seandewar/neovim that referenced this issue Nov 24, 2021
Problem:    Writing to prompt buffer interferes with insert mode.
Solution:   Use win_enter() instead of just setting "curwin". (Ben Jackson,
            closes vim/vim#7035)
vim/vim@4537bcc
seandewar added a commit to seandewar/neovim that referenced this issue Nov 26, 2021
Problem:    Writing to prompt buffer interferes with insert mode.
Solution:   Use win_enter() instead of just setting "curwin". (Ben Jackson,
            closes vim/vim#7035)
vim/vim@4537bcc

Vim test will be skipped, so add a Lua test.
The problem boils down to the use of aucmd_restbuf in a callback, so just test
that (via nvim_buf_set_lines).
seandewar added a commit to seandewar/neovim that referenced this issue Dec 7, 2021
Problem:    Writing to prompt buffer interferes with insert mode.
Solution:   Use win_enter() instead of just setting "curwin". (Ben Jackson,
            closes vim/vim#7035)
vim/vim@4537bcc

Vim test will be skipped, so add a Lua test.
The problem boils down to the use of aucmd_restbuf in a callback, so just test
that (via nvim_buf_set_lines).
seandewar added a commit to seandewar/neovim that referenced this issue Dec 7, 2021
Problem:    Writing to prompt buffer interferes with insert mode.
Solution:   Use win_enter() instead of just setting "curwin". (Ben Jackson,
            closes vim/vim#7035)
vim/vim@4537bcc

Vim test will be skipped, so add a Lua test.
The problem boils down to the use of aucmd_restbuf in a callback, so just test
that (via nvim_buf_set_lines).
dmitmel pushed a commit to dmitmel/neovim that referenced this issue Dec 26, 2021
Problem:    Writing to prompt buffer interferes with insert mode.
Solution:   Use win_enter() instead of just setting "curwin". (Ben Jackson,
            closes vim/vim#7035)
vim/vim@4537bcc

Vim test will be skipped, so add a Lua test.
The problem boils down to the use of aucmd_restbuf in a callback, so just test
that (via nvim_buf_set_lines).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants