Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if there is a current active visual selection in Vim from a function invoked by a command

Tags:

vim

Situation #1: I have just selected a block of text. I then type ":Command", which invokes some function.

Situation #2: There is no current visual selection (though I may have made such a selection previously in the edit session). I type ":Command", which invokes (the same) function.

Is there a (robust) way to distinguish the above two situations from within the function? I tried mode(), but the problem is that in both cases I am in command mode, though in the first case I got into command mode from visual mode while in the second it was from normal mode. Maybe through inspection of a:firstline/a:lastline/v:count?


Update -- Use Case example: ":Sum". If there is a current visual selection, e.g., a column of numbers (block selection) or a range of lines containing just numbers, this command will echo the sum of the numbers. Otherwise, it expects a list of space-separated numbers as arguments, and will echo the sum of those numbers. Basic framework:

command! -nargs="*" -range Sum :call CalcSum(<f-args>)
function! CalcSum(...) range
   " 1. collect numbers from visual selection if there is a current active selection
   " 2. otherwise, if len(args) > 0, collect numbers from args
   " 3. other cases (i.e., no selection and no args or both selection and args) handled reasonably
   " 4. sum collection of numbers
   " 5. return/echo result
endfunction 

Steps (2)-(5) are straightforward. I'm having trouble with (1). I use the "<"/">" markers to recreate the collect the numbers from a visual selection. But I only want to do this if there is a visual selection currently highlighted/active.

Maybe my entire logic is wrong and there is a better way to design this functionality?

like image 729
Jeet Avatar asked Nov 30 '10 16:11

Jeet


2 Answers

If you need to use command, the only way I see is to inspect a:firstline/a:lastline:

" Assuming that you have passed -range=% when defining command
if a:firstline==1 && a:lastline==line('$')
    " Do something
endif

but this does not hold the case when you selected the whole buffer. I suggest you use expression mappings:

function DoCommand()
    if mode()!~#"^[vV\<C-v>]"
        " Do something. For example, set global variable (and unset it in :Command)
    endif
    return ':Command'
endfunction
noremap <expr> {lhs} DoCommand()

Update: Visual mode is never active in command mode. Never. Just because the command mode is not the visual mode. Using mappings is the only way to achieve what you want and here are two approaches: you use exactly the same expr mapping for all modes and check mode() somewhere in this expression or you define different mappings for different modes and use these differences to tell function from what mode it is called.

like image 161
ZyX Avatar answered Nov 07 '22 06:11

ZyX


Old question.
Came here via DDG search. There is a new function visualmode that's meant to be used in functions. It returns the last used visual mode.

like image 23
pellucide Avatar answered Nov 07 '22 08:11

pellucide