开发者

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

开发者 https://www.devze.com 2023-01-27 21:01 出处:网络
Situation #1: I have just selected a block of text. I then type \":Command\", which invokes some function.

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. Bu开发者_如何学Ct 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?


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.


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.


Per the vim manual for :h mode(),

This is useful in the 'statusline' option or when used with remote_expr() In most other places it always returns "c" or "n".

Also per the manual :h mode-switching, visual mode will:

Go from Visual mode to Normal mode by giving a non-movement command, which causes the command to be executed, or by hitting "v", "V" or "CTRL-V"...

So you can't use mode() in a function to get the calling mode. As mentioned above, visualmode() will return the last visual mode or empty if none, but that is not guaranteed to come from the current invocation. If you are only looking for one way to call the function, it can be done with mapped leaders as they can be re-used for each unique mapping:

function! HowCalled( mymode = 'command', ... ) range
    if a:mymode == "visual"
        echo "visual " .. a:firstline
        echo "visual " .. a:lastline
    elseif a:mymode == "command"
        echo "command " .. a:firstline
        echo "command " .. a:lastline
    elseif a:mymode == "normal"
        echo "normal " .. a:firstline
        echo "normal " .. a:lastline
    else
        echo "don't know what mode we are in and can't continue"
        return 1
    endif
endfunction

command! -nargs=* -range HowCalledV <line1>,<line2>call HowCalled('visual',<f-args>)
command! -nargs=* -range HowCalledC <line1>,<line2>call HowCalled('command',<f-args>)
command! -nargs=* -range HowCalledN <line1>,<line2>call HowCalled('normal',<f-args>)

xnoremap <Leader>a  :HowCalledV<CR>
cnoremap <Leader>a  :HowCalledC<CR>
nnoremap <Leader>a  :HowCalledN<CR>

You can assume all calls come from the command line, eg :1,3call Howcalled(), and if visual make adjustments and know visualmode() is accurate, or if called in normal mode as %\a

0

精彩评论

暂无评论...
验证码 换一张
取 消