I am searching for how to get visual selection in vimscript. And I found this: https://stackoverflow.com/a/61486601/4438921, it is great, but the only issue is that, in block-wise visual mode, I always get the previous visual selection, not the current one.

Is there a way to do it correctly?

Code from the link:

xnoremap <leader>a :<C-U> call GetVisualSelection(visualmode())<Cr>

function! GetVisualSelection(mode)
    " call with visualmode() as the argument
    let [line_start, column_start] = getpos("'<")[1:2]
    let [line_end, column_end]     = getpos("'>")[1:2]
    let lines = getline(line_start, line_end)
    if a:mode ==# 'v'
        " Must trim the end before the start, the beginning will shift left.
        let lines[-1] = lines[-1][: column_end - (&selection == 'inclusive' ? 1 : 2)]
        let lines[0] = lines[0][column_start - 1:]
    elseif  a:mode ==# 'V'
        " Line mode no need to trim start or end
    elseif  a:mode == "\<c-v>"
        " Block mode, trim every line
        let new_lines = []
        let i = 0
        for line in lines
            let lines[i] = line[column_start - 1: column_end - (&selection == 'inclusive' ? 1 : 2)]
            let i = i + 1
        return ''
    for line in lines
        echom line
    return join(lines, "\n")
  • The solution seems to work fine for me. Could you elaborate slightly more about the problem you have. A concrete example would help I suppose. Commented Jun 19, 2023 at 8:09
  • Please edit to add detail from the linked answer such as code or other context. As written, your question is hard to understand on it’s own.
    – D. Ben Knoble
    Commented Jun 19, 2023 at 11:32
  • It would be good if you could adapt the question with your usage that made the original solution failing. Commented Jun 20, 2023 at 5:23

2 Answers 2


See :h col()

The result is a Number, which is the byte index of the column
position given with {expr}.  The accepted positions are:
    .   the cursor position
    $   the end of the cursor line (the result is the
        number of bytes in the cursor line plus one)
    'x  position of mark x (if the mark is not set, 0 is
    v   In Visual mode: the start of the Visual area (the
        cursor is the end).  When not in Visual mode
        returns the cursor position.  Differs from '< in
        that it's updated right away.

In short you need to check col('v') and col('.') to get the cursor position and start of visual selection.


I finally fixed it.

From https://github.com/neovim/neovim/discussions/24055#discussioncomment-6213580

In that case, it seems I will miss the <C-U>, thus I cannot get the correct block-wise visual selection?

In this context, those keys only delete '<,'> because it gets automatically inserted in the command line when you press : on visual mode. If you check :h c_ctrl-u you will see that this is what <c-u> does in command mode.

mayby I can try adding a normal! before calling the function: vim.cmd('normal! call getVisualSelection(visualmode())'). because based on another answer: https://stackoverflow.com/a/47051271/4438921, it says '< and '> don't get updated when the user is still in visual mode. That is the reason, that I cannot get the correct visual selection in getVisualSelection function. But if now I try to add normal!, maybe it will first exit visual mode, then I can get the correct (e.g. the previous visual mode) visual selection

Yes, those marks are not set until you leave visual mode. But, \<c-u> has nothing to do with it, the thing that makes you leave visual mode in the vimscript mapping is : (because you enter command mode). So, if you wan't to force neovim to leave visual mode so the marks are set, you can use vim.cmd.normal('') (where is what neoivm sees when you press <esc>). The full function would be:

vim.keymap.set({ "x" }, "<leader>f", function()
    vim.cmd.normal ""
  • 1
    It would be good if, instead of a mere link, you could give the solution you selected. Commented Jun 20, 2023 at 5:24
  • You are not mentioning what the solution is. Commented Jun 20, 2023 at 9:31
  • Now I am confused. May be I did not understand the problem but copy-pasting this discussion here verbatim does not help with what problem you are trying to solve :/ Commented Jun 20, 2023 at 17:52

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.