1

I'm writing what may become a plugin that needs to know what is currently visually selected.

To do it, I'm using the autocmd CursorMoved event to detect the creation of visual mode selections on the fly. So far, it works well enough, but for some reason if I make a visual selection using vip or vap this isn't picked up by the autocmd even though the cursor moves position.

Edit!

I think this is because in my code below I only check for mode() == 'v' (i.e. is visual mode character-wise) and vip could be a line-wise visual selection.

...continued OP

Other text objects (like vi], viw and the like) work fine as do regular motions (like v$ v}).

I wonder if perhaps it's a bug in Vim? If not, what is the correct way to detect that vip has been executed in visual mode? I can't find any other autocmd that does the job.

Here are the relevant sections of code if it helps:


function! Update_visual_mark_list()
    if mode() ==# 'v'
        " exit visual mode
        normal! v

        " get end points of last visual selection
        let [_, l1, c1, _] = getpos("'<")
        let [_, l2, c2, _] = getpos("'>")

        " do some stuff

        " go back to last visual selection
        normal! gv
    endif
endfunction

augroup visual_walk
    autocmd!
    autocmd CursorMoved * call Update_visual_mark_list()
augroup END
2
  • it used to be that TextYankPost captured the action of visual selections, but I'm not sure that is still in vim
    – Mass
    Feb 2, 2022 at 0:27
  • @Mass ah ok I'll looking into that - from the name of that autocmd it didn't seem like it would apply to non-yanked visual selections. But if it does that could be useful.
    – mattb
    Feb 2, 2022 at 8:14

1 Answer 1

2

The "less-and-greater" marks are only guaranteed to be up-to-date after the visual selection finished and the mode has been switched.

If still in Visual mode then getpos('v') and getpos('.') must be used instead. Also to note that the selection could be expanded towards any direction, so it's not guaranteed which mark is the "left" and which is the "right" one. This is told under :h line() (that is also linked by :h getpos()).

UPD.

I think this is because in my code below I only check for mode() == 'v' (i.e. is visual mode character-wise) and vip could be a line-wise visual selection.

Yes, that's right. v_ip is always linewise.

I wonder if perhaps it's a bug in Vim? If not, what is the correct way to detect that vip has been executed in visual mode? I can't find any other autocmd that does the job.

In recent Vim there's :h ModeChanged event that allows to track all mode changes (including v to V). Perhaps, you can make use of this instead of CursorMoved?

3
  • Thanks for the info - I didn't know about the 'v' flag for getpos() (+1). This is why I was explicitly leaving visual mode, then querying the 'less-and-greater" marks, and then re-selecting the previous visual selection. I updated the OP to make that clearer. However I suspect that the problem I'm having here is that I'm only checking mode() == 'v' and this of course only applies to character-wise visual selection not line-wise or block-wise. I'll test that assumption later today.
    – mattb
    Feb 2, 2022 at 8:13
  • @mattb Yes, some commands (including ip) can change the current (sub)mode. You can watch this by set showmode or any status line plugin able to display it. So, yes, you've got the bug here.
    – Matt
    Feb 2, 2022 at 8:44
  • Thanks for confirming it for me - even though the original question was based on my ignorance I'll leave it up in case it helps others.
    – mattb
    Feb 2, 2022 at 9:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.