I'm going to assume that you use the latest matchit
plugin version 1.13.3 which I could find at the moment on Vim's github repository.
[%
most of the time jumps to the function!
, occasionally
(inconsistently depending on cursor position) jumps to the if
, and
totally ignores (
s.
When you hit [%
, the s:MultiMatch()
function is invoked. At the end of the latter, there's a while
loop whose number of iterations is v:count1
. This variable stores the count for the last normal command. Here, it should be 1
, because you didn't hit any number before [%
. But in the middle of the function, some :normal
commands are executed to save the screen and cursor position. These normal commands alter the value of v:count1
.
You can check this like so:
nno cd :<c-u>call Func()<cr>
fu! Func()
echom v:count1
endfu
Source this code, and press cd
, read your messages (:messages
): you should see 1
, because you didn't hit any number before cd
.
Now, source this code:
nno cd :<c-u>call Func()<cr>
fu! Func()
norm! 3G
echom v:count1
endfu
And do the same experiment, press cd
in a file with more than 3 lines. This time, in the messages, you should see 3
, not 1
.
Because of this, the searchpair()
function inside the loop is invoked too many times, which, I suspect, explains the behavior you described. At least, it does on my machine (Linux, Vim 8.0
patches 1-134
).
To fix this, you could delete the line 729:
let level = v:count1
And move it at the beginning of the function (before any other commands, including :normal
commands, has the chance to alter v:count1
):
fun! s:MultiMatch(spflag, mode)
if !exists("b:match_words") || b:match_words == ""
return ""
end
let level = v:count1
I just tried neovim's matchit in vim (surprisingly is different than
vim's) and it seems to work.. weird
This is probably because of the pull request #5124 that neovim merged a year ago. According to the commit message, its purpose was to prevent matchit
to add an undesired entry in the jumplist. To solve this, the commit changed how the screen and cursor positions were saved. It doesn't use :normal
commands anymore, but invokes the function winsaveview()
and winrestview()
. By getting rid of :normal
, for a different purpose, they've also fixed the issue you described earlier, because v:count1
isn't modified anymore. Although it could be in the future if some commands are added in the middle of the function.
]%
jumps to )
s and nothing else.
Towards the end of the s:MultiMatch()
function, line 722, the pattern describing the end part of a group of tokens is defined like this:
let closepat = substitute(closepat, ',', '\\|', 'g')
The purpose of the substitution is to replace each comma, which separates 2 consecutive groups of tokens, with a double escaped bar which will be interpreted by Vim's regex engine as an alternation (separation between 2 branches). I think that the substitution should also replace the colons :
which separate the tokens inside each group. So, you could rewrite the substitution like this:
let closepat = substitute(closepat, '[,:]', '\\|', 'g')
With this change, ]%
should move the cursor on the various tokens described in b:match_words
and &l:matchpairs
, not just )
. At least, it does on my machine.
va%
behaves identically to [%
in this example and does not select
anything.
With the previous changes, in particular the 2nd one, va%
should select the text between the 2 surrounding tokens. Although, the plugin seems to treat a middle part of a group of tokens (like else
in if|else|endif
) as an end part (it's passed to searchpair()
through its 3rd argument, not its 2nd). So, what it considers to be the surrounding tokens may sometimes surprise you.
However, neovim's va%
((( )))
does not "expand" after repeated a%
. I'm
not sure if vim's ever did, but imo it should. just like a(
.
Currently, the visual mapping a%
is defined on line 71 like this:
vmap a% <Esc>[%v]%
When I add o
at the beginning of the {rhs}
, to move the cursor to the beginning of the selection (:h v_o
), I get the behavior that you want:
vmap a% o<Esc>[%v]%
Here's a version of the matchit
plugin, with the 3 small changes described so far.
And here's another one where I tried to also include the PR #5124 from Neovim.
If you want to test the code on your machine, but you don't have the rights to change the default matchit
plugin (or you don't want to), you can create the file ~/.vim/plugin/matchit.vim
, and write your experimental matchit
plugin there.
Since in the runtimepath, ~/.vim
comes before $VIMRUNTIME
, Vim should source your custom version before the default one. And since, the default plugin has the guard:
if exists("loaded_matchit") || &cp
finish
endif
let loaded_matchit = 1
... only your version should be fully sourced.
]%
does almost nothing. I'm going to have to take a look at the code.va%
((( )))
does not "expand" after repeateda%
. I'm not sure if vim's ever did, but imo it should.just likea(
.