Instructions: Replace the template text and remove irrelevant text (including this line)
Describe the bug
After patch 8.2.1654, insert mode is exited unexpectedly using prompt-buffers and ctrl-x mode. Perhaps related, similar to #7013. Initially I noticed that the pum was visible but I was unexpectedly in normal mode. when the completion menu is shown in a prompt buffer. But since that was fixed, now the behaviour is just that you end up in normal mode without pressing escape.
I have a very solid reproduction case, but it's full of Vimspector and YouCompleteMe which are heavyweight vim plugins. Both make use of channels and timers, both for completion.
I used the repro case to bisect this to 8.2.1654 patch. Prior to that patch completion works well in prompt buffers, but since then user is unexpectedly bumped out of insert mode.
To Reproduce
Detailed steps to reproduce the behavior:
I'm working on a minimal vim-only repro, but it's a bit tricky. I can't expect you to install YCM and Vimspector to test this, but for what it's worth this can repro reliably:
mkdir insert-mode-testcd insert-mode-testgit clone https://github.com/ycm-core/YouCompleteMecd YouCompleteMe ; git submodule update --init --recursive; ./install.py; cd ..git clone https://github.com/puremourning/vimspectorcd vimspector && ./install_gadget.py --enable-python; cd ..ycm_plus_vimspector:let &rtp = getcwd() .. '/YouCompleteMe,' .. getcwd() .. 'vimspector'
filetype plugin indent on
let g:vimspector_enable_mappings = 'HUMAN'
let g:ycm_semantic_triggers = { 'VimspectorPrompt': [ '.' ] }
vim -Nu ycm_plus_vimspector vimspector/support/test/python/simple_python/main.py<F5>Run - default<F10> to step over the class TestClass lineWatches window (a prompt buffer), enter insert mode and type TestClass.You will be bumped unexpectedly out of insert mode into normal mode as you're typing.
Expected behavior
You should see the completion menu with various elements of TestClass. <Tab> or <C-n> should cycle through them.
This repeating the last few steps on patches prior to v8.2.1654 works.
Environment (please complete the following information):
Additional context
As mentioned, I'll try and write a test for this, as I realise entirely that installing 2 huge plugins is not in any way "minimal".
YCM is using the TextChangedI and TextChangeP auto commands and the InsertCharPre auto command. It's calling the omnifunc provided by Vimspector when you press . in insert mode, which is doing a ch_read() blocking on a response from a server. Then YCM is calling complete() to populate the completion menu.
Again apologies for lack of minimal repro. I'll try and work on it tonight.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.![]()
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 ?
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.
:set timeout? timeoutlen? ttimeout? ttimeoutlen?
(including the question marks)?
Best regards,
Tony.
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 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?
timeouttimeoutlen=1000ttimeoutttimeoutlen=100Does 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 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?
timeouttimeoutlen=1000ttimeoutttimeoutlen=100
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.
—
You are receiving this because you commented.
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.vimi and type some characters - you're bumped out of insert mode—
You are receiving this because you commented.
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'
—
You are receiving this because you commented.
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.
—
You are receiving this because you commented.
I can't reproduce because "uuencode: Command not found". Perhaps something more common (and not keeping the CPU busy)?
—
You are receiving this because you commented.
Oh, and there is no aucmd_prepwin(), you probably mean aucmd_prepbuf(). But it doesn't call leaving_window() directly.
—
You are receiving this because you commented.
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 %
—
You are receiving this because you commented.
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()
—
You are receiving this because you commented.
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; 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
—
You are receiving this because you commented.
I was wondering if this patch (that I just made myself when I first encountered the bug) is useful:
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.
—
You are receiving this because you commented.
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.
—
You are receiving this because you commented.
@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.
—
You are receiving this because you commented.
Thanks for figuring this out and writing a test. I'll clean it up a bit.
—
You are receiving this because you commented.
Thanks Bram. That's working nice now.
—
You are receiving this because you commented.