This is the follow-up patch to trigger ModeChanged in more cases, as discussed in #8856.
I added a few more tests (e.g. SELECT mode, normal operator pending mode, ctrl_x mode).
I hope this covers all cases now. If you find anything where ModeChanged still does not fire, please tell me how to reproduce.
One more thing: I wanted to fixup the doc for the usecase example:
au ModeChanged [vV<C-V]*:* let &l:relativenumber = mode() =~# '^[vV�]' au ModeChanged *:[vV<C-V]* let &l:relativenumber = mode() =~# '^[vV�]' au WinEnter * let &l:relativenumber = mode() =~# '^[vV�]' au WinLeave * let &l:relativenumber = mode() =~# '^[vV�]'
But obviously the <C-V inside the regex does not match the ^V mode. I also tried ^V and \^V, but both don't match. What do I need to use to match properly? Or perhaps we should come up with another usecase example, as this one is getting quite large.
https://github.com/vim/vim/pull/8999
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub.
Triage notifications on the go with GitHub Mobile for iOS or Android.
![]()
Merging #8999 (e361ea0) into master (0a7984a) will decrease coverage by
87.61%.
The diff coverage is0.00%.
@@ Coverage Diff @@ ## master #8999 +/- ## =========================================== - Coverage 90.06% 2.45% -87.62% =========================================== Files 151 149 -2 Lines 168353 165975 -2378 =========================================== - Hits 151631 4077 -147554 - Misses 16722 161898 +145176
| Flag | Coverage Δ | |
|---|---|---|
| huge-clang-none | ? |
|
| huge-gcc-none | ? |
|
| huge-gcc-testgui | ? |
|
| huge-gcc-unittests | 2.45% <0.00%> (-0.01%) |
⬇️ |
Flags with carried forward coverage won't be shown. Click here to find out more.
| Impacted Files | Coverage Δ | |
|---|---|---|
| src/insexpand.c | 0.00% <0.00%> (-92.45%) |
⬇️ |
| src/misc1.c | 13.40% <0.00%> (-75.62%) |
⬇️ |
| src/normal.c | 0.50% <0.00%> (-95.28%) |
⬇️ |
| src/terminal.c | 0.00% <0.00%> (-91.23%) |
⬇️ |
| src/float.c | 0.00% <0.00%> (-99.22%) |
⬇️ |
| src/gui_gtk_f.c | 0.00% <0.00%> (-97.43%) |
⬇️ |
| src/crypt_zip.c | 0.00% <0.00%> (-97.06%) |
⬇️ |
| src/sound.c | 0.00% <0.00%> (-97.03%) |
⬇️ |
| src/cmdhist.c | 0.00% <0.00%> (-97.00%) |
⬇️ |
| src/sha256.c | 0.00% <0.00%> (-96.94%) |
⬇️ |
| ... and 141 more |
Continue to review full report at Codecov.
Legend - Click here to learn more
Δ = absolute <relative> (impact),ø = not affected,? = missing data
Powered by Codecov. Last update 0a7984a...e361ea0. Read the comment docs.
But obviously the
<C-Vinside the regex does not match the^Vmode. I also tried^Vand\^V, but both don't match. What do I need to use to match properly? Or perhaps we should come up with another usecase example, as this one is getting quite large.
Try pressing CTRL-V twice in Insert mode.
@zeertzjq commented on this pull request.
> |InsertLeave|. The following values of |v:event| are set: old_mode The mode before it changed. new_mode The new mode as also returned - by |mode()|. + by |mode(1)|.
Pressing K on this fails. Maybe you can change it to |mode()| called with a non-zero argument.
> @@ -930,18 +930,20 @@ MenuPopup Just before showing the popup menu (under the *ModeChanged* ModeChanged After changing the mode. The pattern is matched against `'old_mode:new_mode'`, for - example match against `i:*` to simulate + example match against `i*:*` to simulate
InsertLeave is also triggered when leaving Replace mode and Virtual Replace mode. I think this should be [iR]*:[^iR]*.
@vimpostor commented on this pull request.
> |InsertLeave|. The following values of |v:event| are set: old_mode The mode before it changed. new_mode The new mode as also returned - by |mode()|. + by |mode(1)|.
Good catch, done.
The change from i to ic may be fired too late. Try this:
:set noshowmode:set shortmess+=c:autocmd ModeChanged * echomsg v:event mode(1):inoremap <F2> <Cmd>echomsg mode(1)<CR>i<C-X><C-F><F2><F2><F2><F2><F2><C-N>.<F2>s print ic, but ModeChanged from i to ic is only triggered on <C-N>.> @@ -930,18 +930,20 @@ MenuPopup Just before showing the popup menu (under the *ModeChanged* ModeChanged After changing the mode. The pattern is matched against `'old_mode:new_mode'`, for - example match against `i:*` to simulate + example match against `i*:*` to simulate
I only compared it to InsertLeave because it was a simple way to present and understand the wildcard mechanism, I don't like this becoming complicated due to the intrinsics of InsertLeave. Therefore I have now replaced this comparision with CmdlineEnter and *:c*, I think it is better to keep the Regex simple here and CmdlineEnter should be just c in this case.
@vimpostor pushed 1 commit.
—
You are receiving this because you are subscribed to this thread.
@vimpostor pushed 1 commit.
—
You are receiving this because you are subscribed to this thread.
The change from
itoicmay be fired too late.
Good catch again! It should work now.
Try pressing
CTRL-Vtwice in Insert mode.
This inserts ^V, but I already tried matching against that and it did not work.
You may also need to trigger ModeChanged in set_completion().
Not sure about ins_compl_clear() and ins_compl_restart().
@zeertzjq commented on this pull request.
> let g:index = 0 - let g:mode_seq = ['n', 'i', 'n', 'v', 'V', 'n', 'V', 'v', 'n'] + let g:mode_seq = ['n', 'i', 'n', 'v', 'V', 'i', 'ix', 'i', 'n', 'no', 'n', 'V', 'v', 's', 'n']
I think tests are failing because you didn't add ic here.
The change from
itoicmay be fired too late.Good catch again! It should work now.
Try pressing
CTRL-Vtwice in Insert mode.This inserts
^V, but I already tried matching against that and it did not work.
It inserts a control character that is displayed as ^V, but it is a control character.
For that example, paste the following into Vim and run :%!xxd -r:
00000000: 6175 204d 6f64 6543 6861 6e67 6564 205b au ModeChanged [
00000010: 7656 165d 2a3a 2a20 6c65 7420 266c 3a72 vV.]*:* let &l:r
00000020: 656c 6174 6976 656e 756d 6265 7220 3d20 elativenumber =
00000030: 6d6f 6465 2829 203d 7e23 2027 5e5b 7656 mode() =~# '^[vV
00000040: 165d 270a 6175 204d 6f64 6543 6861 6e67 .]'.au ModeChang
00000050: 6564 202a 3a5b 7656 165d 2a20 6c65 7420 ed *:[vV.]* let
00000060: 266c 3a72 656c 6174 6976 656e 756d 6265 &l:relativenumbe
00000070: 7220 3d20 6d6f 6465 2829 203d 7e23 2027 r = mode() =~# '
00000080: 5e5b 7656 165d 270a 6175 2057 696e 456e ^[vV.]'.au WinEn
00000090: 7465 7220 2a20 6c65 7420 266c 3a72 656c ter * let &l:rel
000000a0: 6174 6976 656e 756d 6265 7220 3d20 6d6f ativenumber = mo
000000b0: 6465 2829 203d 7e23 2027 5e5b 7656 165d de() =~# '^[vV.]
000000c0: 270a 6175 2057 696e 4c65 6176 6520 2a20 '.au WinLeave *
000000d0: 6c65 7420 266c 3a72 656c 6174 6976 656e let &l:relativen
000000e0: 756d 6265 7220 3d20 6d6f 6465 2829 203d umber = mode() =
000000f0: 7e23 2027 5e5b 7656 165d 270a ~# '^[vV.]'.
But obviously the <C-V inside the regex does not match the ^V mode. I also tried ^V and ^V, but both don't match. What do I need to use to match properly? Or perhaps we should come up with another usecase example, as this one is getting quite large.
Use double quoted strings and key-notation, e.g. something like this: "\<c-v>"
But obviously the <C-V inside the regex does not match the ^V mode. I also tried ^V and ^V, but both don't match. What do I need to use to match properly? Or perhaps we should come up with another usecase example, as this one is getting quite large.
Use double quoted strings and key-notation, e.g. something like this:
"\<c-v>"
I think this requires :execute to be used in autocmd pattern.
This seems to work for me:
vim9script autocmd ModeChanged [vV\x16]*:*,*:[vV\x16]* &l:relativenumber = mode() =~ '^[vV\x16]' autocmd WinEnter,WinLeave * &l:relativenumber = mode() =~ '^[vV\x16]'
Oh, \x16 works without vim9script:
:au ModeChanged [vV\x16]*:*,*:[vV\x16]* let &l:rnu = mode() =~# '^[vV\x16]' :au WinEnter,WinLeave * let &l:rnu = mode() =~# '^[vV\x16]'
@vimpostor pushed 1 commit.
—
You are receiving this because you are subscribed to this thread.
Thanks guys, I have updated the doc now.
You may also need to trigger
ModeChangedinset_completion().
Why? What minor mode change is not yet covered by the other trigger_modechanged() in that file?
Thanks guys, I have updated the doc now.
You may also need to trigger
ModeChangedinset_completion().Why? What minor mode change is not yet covered by the other
trigger_modechanged()in that file?
complete() completion.
Thanks guys, I have updated the doc now.
You may also need to trigger
ModeChangedinset_completion().Why? What minor mode change is not yet covered by the other
trigger_modechanged()in that file?
Try this:
:set noshowmode:set shortmess+=c:autocmd ModeChanged * echomsg v:event mode(1):inoremap <F2> <Cmd>echomsg mode(1)<CR>:inoremap <F3> <Cmd>call complete(col('.'), ['a', 'b', 'c'])<CR>i<F3><F2><F2><F2><F2><F2><C-N>.<F2>s print ic, but ModeChanged from i to ic is only triggered on <C-N>.—
@zeertzjq commented on this pull request.
In src/misc1.c:
> tv[0].v_type = VAR_NUMBER;
tv[0].vval.v_number = 1; // get full mode
tv[1].v_type = VAR_UNKNOWN;
f_mode(tv, &rettv);
+ if (!STRCMP(rettv.vval.v_string, last_mode))
+ return;
You didn't free rettv.vval.v_string.
@vimpostor pushed 1 commit.
—
You are receiving this because you are subscribed to this thread.
Try this (
complete()function):
:set noshowmode:set shortmess+=c:autocmd ModeChanged * echomsg v:event mode(1):inoremap <F2> <Cmd>echomsg mode(1)<CR>:inoremap <F3> <Cmd>call complete(col('.'), ['a', 'b', 'c'])<CR>- Press
i<F3><F2><F2><F2><F2><F2><C-N>.- The
<F2>s printic, butModeChangedfromitoicis only triggered on<C-N>.
Thanks for the clear example, fixed now.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub.
@zeertzjq commented on this pull request.
In src/normal.c:
> @@ -521,6 +521,7 @@ normal_cmd(
#ifdef CURSOR_SHAPE
if (finish_op != c)
{
+ trigger_modechanged();
This line is not executed if CURSOR_SHAPE is not defined.
Some edge cases not covered:
From Terminal-Job to another buffer:
:set noshowmode:autocmd ModeChanged * echomsg v:event:edit foo:terminal<C-\><C-N>:set bufhidden=hide:tnoremap <F2> <Cmd>bnext<CR>i<F2>v<F2> didn't trigger ModeChanged, and the v shows {'old_mode': 't', 'new_mode': 'v'} in messages.From Terminal-Job to another window:
:set noshowmode:autocmd ModeChanged * echomsg v:event:tnoremap <F2> <Cmd>wincmd w<CR>:terminal<F2>v<F2> didn't trigger ModeChanged, and the v shows {'old_mode': 't', 'new_mode': 'v'} in messages.Another problem I found:
:set noshowmode:xnoremap <F2> <Cmd>autocmd ModeChanged * echomsg v:event<CR>v<F2><F2> shows {'old_mode': 'n', 'new_mode': 'v'}This is because last_mode is initialized to "n". My suggestion:
has_modechanged returns FALSE, set last_mode to an empty string (I think this just needs last_mode[0] = NUL;).has_modechanged returns TRUE but last_mode is an empty string (which means a ModeChanged event has just been added and there were none previously), copy new mode to last_mode, but do not trigger ModeChanged event.@vimpostor pushed 1 commit.
—
You are receiving this because you are subscribed to this thread.
Some edge cases not covered:
From Terminal-Job to another buffer:
:set noshowmode:set hidden:autocmd ModeChanged * echomsg v:event
:tnoremap <F2> <Cmd>bnext<CR>
:edit foo
:terminal- Press
<F2>v- The
<F2>didn't triggerModeChanged, and thevshows{'old_mode': 't', 'new_mode': 'v'}in messages.
From Terminal-Job to another window:
:set noshowmode:autocmd ModeChanged * echomsg v:event:tnoremap <F2> <Cmd>wincmd w<CR>:terminal- Press
<F2>v- The
<F2>didn't triggerModeChanged, and thevshows{'old_mode': 't', 'new_mode': 'v'}in messages.
I can't reproduce both of these. Are you sure you had the latest version of my branch checked out? For me the <F2> does trigger in both cases the correct modechange.
Another problem I found:
:set noshowmode:xnoremap <F2> <Cmd>autocmd ModeChanged * echomsg v:event<CR>- Press
v<F2>- The
<F2>shows{'old_mode': 'n', 'new_mode': 'v'}This is because
last_modeis initialized to"n". My suggestion:
- If
has_modechangedreturnsFALSE, setlast_modeto an empty string (I think this just needslast_mode[0] = NUL;).- If
has_modechangedreturnsTRUEbutlast_modeis an empty string (which means aModeChangedevent has just been added and there were none previously), copy new mode tolast_mode, but do not triggerModeChangedevent.
Yes I actually thought about this edge case, but didn't realize that the first ModeChanged could be created somewhere else than leaving CMD-mode to Normal mode (which would have been just right).
The problem with your proposed solution is that the first ModeChanged would not trigger at all, which I consider just as broken. I think it would be better to initialize last_mode a second time when creating the first ModeChanged autocmd. Maybe somewhere at the end of do_autocmd(). That way we don't miss the first ModeChanged. Do you agree?
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub.
Maybe I forgot to git fetch. I'll check again later.
Indeed I can no longer reproduce these two.
Yes I actually thought about this edge case, but didn't realize that the first ModeChanged autocmd could be created somewhere else than leaving CMD-mode to Normal mode (which would have been just right). The problem with your proposed solution is that the first ModeChanged would not trigger at all, which I consider just as broken. I think it would be better to initialize
last_modea second time when creating the firstModeChangedautocmd. Maybe somewhere at the end ofdo_autocmd(). That way we don't miss the firstModeChanged. Do you agree?
Oh, I also didn't think about ++once. You solution indeed seem better.
A script can create an autocommand in any mode.
Fixed the problem by initializing last_mode inside do_autocmd_event() for EVENT_MODECHANGED.
Perhaps it is time to add an internal get_mode() command, since we are now assigning to last_mode with duplicated f_mode() boilerplate code in two different places...
@vimpostor pushed 1 commit.
—
You are receiving this because you are subscribed to this thread.
Do we anticipate any more changes, or should the be ready to include now?
I do expect some fine tuning later, when users start to use it, that should be OK.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub.
@vimpostor pushed 1 commit.
—
You are receiving this because you are subscribed to this thread.
Do we anticipate any more changes, or should the be ready to include now? I do expect some fine tuning later, when users start to use it, that should be OK.
I just added another simple test case for terminal-normal mode. If @zeertzjq doesn't have anything to add, I think this should be good to go now.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub.
@vimpostor pushed 1 commit.
—
You are receiving this because you are subscribed to this thread.
Vim can be compiled without the Terminal feature, so I think it is better to wrap tests for Terminal inside a has('terminal') block.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub.
Vim can be compiled without the Terminal feature, so I think it is better to wrap tests for Terminal inside a
has('terminal')block.
I am already using CheckFeature terminal, is that not enough?
Vim can be compiled without the Terminal feature, so I think it is better to wrap tests for Terminal inside a
has('terminal')block.I am already using
CheckFeature terminal, is that not enough?
That skips the whole function. I'm thinking about how to skip less.
Try this diff:
diff --git a/src/testdir/test_edit.vim b/src/testdir/test_edit.vim
index 22b7b222c..17ef31bcc 100644
--- a/src/testdir/test_edit.vim
+++ b/src/testdir/test_edit.vim
@@ -1996,8 +1996,8 @@ func Test_mode_changes()
let g:i_to_any = 0
au ModeChanged i:* let g:i_to_any += 1
let g:index = 0
- let g:mode_seq = ['n', 'i', 'niI', 'i', 'n', 'c', 'n', 't', 'nt', 'c', 'nt', 'n']
- call feedkeys("a\<C-O>l\<esc>:term\<CR>\<C-W>N:bd!\<CR>", 'tnix')
+ let g:mode_seq = ['n', 'i', 'niI', 'i', 'n']
+ call feedkeys("a\<C-O>l\<esc>", 'tnix')
call assert_equal(len(g:mode_seq) - 1, g:index)
call assert_equal(1, g:n_to_i)
call assert_equal(1, g:n_to_niI)
@@ -2005,7 +2005,20 @@ func Test_mode_changes()
call assert_equal(2, g:nany_to_i)
call assert_equal(1, g:i_to_n)
call assert_equal(2, g:i_to_any)
- call assert_equal(5, g:nori_to_any)
+ call assert_equal(3, g:nori_to_any)
+
+ if has('terminal')
+ let g:mode_seq += ['c', 'n', 't', 'nt', 'c', 'nt', 'n']
+ call feedkeys(":term\<CR>\<C-W>N:bd!\<CR>", 'tnix')
+ call assert_equal(len(g:mode_seq) - 1, g:index)
+ call assert_equal(1, g:n_to_i)
+ call assert_equal(1, g:n_to_niI)
+ call assert_equal(1, g:niI_to_i)
+ call assert_equal(2, g:nany_to_i)
+ call assert_equal(1, g:i_to_n)
+ call assert_equal(2, g:i_to_any)
+ call assert_equal(5, g:nori_to_any)
+ endif
au! ModeChanged
delfunc TestMode
I haven't tested it, so it may fail.
Try this diff:
diff --git a/src/testdir/test_edit.vim b/src/testdir/test_edit.vim index 22b7b222c..9627140ee 100644 --- a/src/testdir/test_edit.vim +++ b/src/testdir/test_edit.vim @@ -1959,7 +1959,6 @@ endfunc " Test for ModeChanged pattern func Test_mode_changes() - CheckFeature terminal let g:index = 0 let g:mode_seq = ['n', 'i', 'n', 'v', 'V', 'i', 'ix', 'i', 'ic', 'i', 'n', 'no', 'n', 'V', 'v', 's', 'n'] func! TestMode() @@ -1996,8 +1995,8 @@ func Test_mode_changes() let g:i_to_any = 0 au ModeChanged i:* let g:i_to_any += 1 let g:index = 0
- let g:mode_seq = ['n', 'i', 'niI', 'i', 'n', 'c', 'n', 't', 'nt', 'c', 'nt', 'n'] - call feedkeys("a\<C-O>l\<esc>:term\<CR>\<C-W>N:bd!\<CR>", 'tnix') + let g:mode_seq = ['n', 'i', 'niI', 'i', 'n'] + call feedkeys("a\<C-O>l\<esc>", 'tnix') call assert_equal(len(g:mode_seq) - 1, g:index) call assert_equal(1, g:n_to_i) call assert_equal(1, g:n_to_niI)
@@ -2005,7 +2004,20 @@ func Test_mode_changes()
call assert_equal(2, g:nany_to_i)
call assert_equal(1, g:i_to_n)
call assert_equal(2, g:i_to_any)
- call assert_equal(5, g:nori_to_any) + call assert_equal(3, g:nori_to_any) + + if has('terminal') + let g:mode_seq += ['c', 'n', 't', 'nt', 'c', 'nt', 'n'] + call feedkeys(":term\<CR>\<C-W>N:bd!\<CR>", 'tnix') + call assert_equal(len(g:mode_seq) - 1, g:index) + call assert_equal(1, g:n_to_i) + call assert_equal(1, g:n_to_niI) + call assert_equal(1, g:niI_to_i) + call assert_equal(2, g:nany_to_i) + call assert_equal(1, g:i_to_n) + call assert_equal(2, g:i_to_any) + call assert_equal(5, g:nori_to_any) + endif au! ModeChanged delfunc TestMode
I haven't tested it yet, so it may fail.
Try this diff:
diff --git a/src/testdir/test_edit.vim b/src/testdir/test_edit.vim
index 22b7b222c..f0f04f049 100644
--- a/src/testdir/test_edit.vim +++ b/src/testdir/test_edit.vim @@ -1959,7 +1959,6 @@ endfunc " Test for ModeChanged pattern func Test_mode_changes() - CheckFeature terminal let g:index = 0 let g:mode_seq = ['n', 'i', 'n', 'v', 'V', 'i', 'ix', 'i', 'ic', 'i', 'n', 'no', 'n', 'V', 'v', 's', 'n'] func! TestMode() @@ -1996,8 +1995,8 @@ func Test_mode_changes() let g:i_to_any = 0 au ModeChanged i:* let g:i_to_any += 1 let g:index = 0 - let g:mode_seq = ['n', 'i', 'niI', 'i', 'n', 'c', 'n', 't', 'nt', 'c', 'nt', 'n'] - call feedkeys("a\<C-O>l\<esc>:term\<CR>\<C-W>N:bd!\<CR>", 'tnix') + let g:mode_seq = ['n', 'i', 'niI', 'i', 'n'] + call feedkeys("a\<C-O>l\<esc>", 'tnix') call assert_equal(len(g:mode_seq) - 1, g:index) call assert_equal(1, g:n_to_i) call assert_equal(1, g:n_to_niI)
@@ -2005,12 +2004,34 @@ func Test_mode_changes()
call assert_equal(2, g:nany_to_i)
call assert_equal(1, g:i_to_n)
call assert_equal(2, g:i_to_any)
- call assert_equal(5, g:nori_to_any) + call assert_equal(3, g:nori_to_any) + + if has('terminal') + let g:mode_seq += ['c', 'n', 't', 'nt', 'c', 'nt', 'n'] + call feedkeys(":term\<CR>\<C-W>N:bd!\<CR>", 'tnix') + call assert_equal(len(g:mode_seq) - 1, g:index) + call assert_equal(1, g:n_to_i) + call assert_equal(1, g:n_to_niI) + call assert_equal(1, g:niI_to_i) + call assert_equal(2, g:nany_to_i) + call assert_equal(1, g:i_to_n) + call assert_equal(2, g:i_to_any) + call assert_equal(5, g:nori_to_any) + endif
au! ModeChanged delfunc TestMode unlet! g:mode_seq unlet! g:index + unlet! g:n_to_any + unlet! g:V_to_v + unlet! g:n_to_i + unlet! g:n_to_niI + unlet! g:niI_to_i + unlet! g:nany_to_i + unlet! g:i_to_n + unlet! g:nori_to_any + unlet! g:i_to_any endfunc " vim: shiftwidth=2 sts=2 expandtab
@vimpostor pushed 1 commit.
—
You are receiving this because you are subscribed to this thread.
Try this diff: (...)
Applied, I think this should be good to go then now.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub.
When Ctrl-C is used to exit insert mode to normal mode, got_int is set which means that the autocmd is not received. But I guess this is intentional?
@vimpostor What is [vV\x16] trying to match?
The docs says:
The pattern is matched against 'old_mode:new_mode'
So I'd expect a mode name. :help mode() lists a bunch of characters used as mode names, but doesn't mention \x16.
From the conversation above about ^V, maybe it means that, but how is a user supposed to guess? Could the match regex accept ^V (inserted with C-v C-v), but convert it under the hood to \x16? Same for Ctrl-S.
CTRL-V is returned by mode() for Visual block mode. Not the easiest to work with, but we can't change that now.
Using \x16 inside square brackets in a pattern works to match the CTRL-V. See :help /] (slash backslash right square bracket)