Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get the current line in visual mode from a function

Tags:

vim

I have a simple vim script that takes a visual block of text and stores it as a list. The problem with the function VtoList() is that it executes after the cursor returns to the start of the visual block, not before it. Because of this, I have no way of getting the line where the visual block ends.

nn <F2> :call VtoList()<CR>

func! VtoList()
    firstline = line('v')  " Gets the line where the visual block begins
    lastline = line('.')   " Gets the current line, but not the one I want.
    mylist = getline(firstline, lastline)
    echo mylist
endfunc

The problem is with line('.'). It should return the current line of the cursor, but before the function is called, the cursor has already returned to the start of the visual block. Thus, I'm only getting a single line instead of a range of lines.

I put together a solution that sets a mark everytime the user hits V and sets another mark before the function is called.

nnoremap V mV
nnoremap <F2> mZ:call VtoList()<CR>

The function works fine if I substitute line('v') and line('.') with line("'V") and line("'Z"), but I want to avoid this solution if I can because it could conflict with a user's mappings.

Is there a way I can get current line of a visual block within a function before the cursor has returned to the start of the visual block?

like image 305
camel_space Avatar asked Mar 17 '26 10:03

camel_space


1 Answers

Don't use :, use <expr>:

function! s:NumSort(a, b)
    return a:a>a:b ? 1 : a:a==a:b ? 0 : -1
endfunction
func! VtoList()
    let [firstline, lastline]=sort([line('v'), line('.')], 's:NumSort')
    let mylist = getline(firstline, lastline)
    echo mylist
    return ""
endfunc
vnoremap <expr> <F2> VtoList()

Note other changes: let (you forgot it), sort (line where selection starts may be after the line where selection ends), vnoremap (line("v") works only in visual mode), return (expr mappings return value is executed, but you don't need it, you need only side effects). You can replace the second line with

    if mode()=~#"^[vV\<C-v>]"
        let [firstline, lastline]=sort([line('v'), line('.')], 's:NumSort')
    else
        let [firstline, lastline]=sort([line("'<"), line("'>")], 's:NumSort')
    endif

The reason why your solution is not working is that when : occurs in the mapping, you immediately exit visual mode and enter command mode. line("v") works only in visual mode.

Other note: vnoremap {lhs} : will produce command line already filled with '<,'>. You may have added range to the function definition and use let [firstline, lastline]=sort([a:firstline, a:lastline], 's:NumSort'). But you nevertheless will exit visual mode with :.

like image 146
ZyX Avatar answered Mar 20 '26 05:03

ZyX