Background
vim 7.4
VimL scripting problem
Question
Does VimL have a means of polling the current Visual selection?
Goal
Trevor wishes to create a function in VimL script that does one thing only.
return TRUE if there is currently a visual selection containing one or characters in the currently-active buffer.
return FALSE if there is not currently a visual selection containing one or more characters in the currently-active buffer.
The function will be an addon to other functions that need to exhibit different behavior, depending on whether or not there is a currently-non-empty Visual selection (either line-wise or character-wise) in the currently-active buffer.
Failed attempts
For some reason this is not coming through in the docs, no existing solution seems readily apparent, and it seems like a pretty basic thing.
I believe the mode() function is what you are looking for. (From :h mode())
mode()
mode([expr]) Return a string that indicates the current mode.
If [expr] is supplied and it evaluates to a non-zero Number or
a non-empty String (non-zero-arg), then the full mode is
returned, otherwise only the first letter is returned. Note
that " " and "0" are also non-empty strings.
n Normal
no Operator-pending
v Visual by character
V Visual by line
CTRL-V Visual blockwise
s Select by character
S Select by line
CTRL-S Select blockwise
i Insert
R Replace R
Rv Virtual Replace gR
c Command-line
cv Vim Ex mode gQ
ce Normal Ex mode Q
r Hit-enter prompt
rm The -- more -- prompt
r? A :confirm query of some sort
! Shell or external command is executing
This is useful in the 'statusline' option or when used
with remote_expr() In most other places it always returns
"c" or "n".
Also see visualmode().
If will return v, V or CTRL-V if you are in a visual mode.
However this function almost always returns c or n since a visual selection ends immediately when an ex command is run. You can determine where the visual selection was by using the marks '< and '>. You can also determine if you were in a visual function by using xnoremap commands that pass a flag to the function to say you were in visual mode.
If you put your function in a mapping mode() seems to work properly.
As your question is about polling, the strict answer is mode(). But polling only happens during status line evaluation, or in triggered :autocmd event handlers.
If the other function that you're vaguely referring to is invoked by a custom mapping or command, mode() doesn't help you, as visual mode has already been left by the time your function is invoked. The right way™ to handle visual selections for this is as follows:
Custom commands should work on a supplied range, and that range can be generated from a visual selection; Vim will automatically prepend the :'<,'> range for you. In rare cases, you may want to create a special command to work on the visual selection, e.g. :Frobnize vs. :FrobnizeVisual, and then just use :normal! gv to get and work on the selection.
Separate custom mappings can be defined for normal and visual mode, and pass information as a flag to the invoked function:
:nnoremap <Leader>x :<C-u>call Frobnize(0)<CR>
:xnoremap <Leader>x :<C-u>call Frobnize(1)<CR>
function! Frobnize( isVisualMode )
...
TL/DR: There's a reason you didn't find such convenient function; rethink your approach.
Related
Sometimes I want to replace some text in multiple lines but not in the whole line, so I toggle visual mode on and highlight the text I want to replace. But then, I need to add this annoying \%V that I always forget for it to replace the text just in the selection and not in the whole line
:'<, '>s/\%Vold/new/g
Is there a way to map the normal replace command
:'<, '>s/old/new/g
To the one shown above so that I don't need to remember that V every time?
Thank you
I tried using cmap command in the vimrc file in the following way:
cmap '<, '>s/* '<, '>s/\%V*
But this of course doesn't work, because instead of the asterisk I should input the text I want to search and replace to end the search and replace command.
There are three basic parts to a mapping:
the mapping command,
the "left-hand side" (LHS),
and the "right-hand side" (RHS).
The LHS is the key sequence/combination that you want to press and the RHS is a different key sequence that you want Vim to "press" instead of those in the LHS. You "map" one key sequence to another.
The RHS is, fundamentally, a macro: a sequence of keystrokes, "just" like if you pressed them yourself but without any delay between them. So, the RHS is really just a bunch of keystrokes you are tired of making over and over again.
The main use of mappings is to reduce repetitiveness. You figure that you often press the imaginary sequence sdlusdrosydtsodsd and you map it all to <F5> to be more efficient.
In this case…
The current mode is visual mode so the mapping has to be a visual mode mapping.
This makes :xmap or :xnoremap the proper mapping command. Let's make it :xnoremap.
The repetitive key sequence you want to abstract away is:
:s/\%V
That's your RHS.
(Note that the range '<,'> is automatically inserted for you so it can be ignored.)
The key sequence you want to press instead is, let's say… <F5>.
That's your LHS.
Now that you have your mapping command, your LHS, and your RHS, building the mapping is extremely easy:
:xnoremap <F5> :s/\%V
I want to replicate the VS code feature of 'highlight and comment out code' (usually bound to keys SHIFT + /) in vim.
I can run :g//s/^/\/\/ / in normal mode to prepend // at the start of every line. I just want to put a constraint on this so it only applies the substitution to lines highlighted in visual mode.
Visually select all the lines (using V), then hit :.
This will give you :'<,'> which is the range of your visual selection.
Then you can add your vim command to it.
I would recommend the following method if you wish to not use plugins.
:'<,'>normal I//
Which is not a substitution.
If you want a really nice vim plugin that does this task in a vim manner, check out tpope's vim-commentary which is an essential in my opinion.
I can run :g//s/^/\/\/ / in normal mode to prepend // at the start of every line.
Well, that would be an unnecessarily complicated way to do it and your command wouldn't really work anyway.
:g// either matches nothing or it matches the previous search. What you want, here, is probably something like :g/^/ or :g/$/.
A simple substitution on the whole buffer would be much simpler and much faster:
:%s/^/\/\/ /
Using :help :global in this context provides no value (you want to operate on every line anyway) and is considerably slower (we are talking thousands of times slower)
You can use alternative separators to avoid all that backslashing:
:%s#^#// #
The last separator is not even needed:
:%s#^#// <-- there is a space, here
And the kicker: you can enter command-line mode from visual mode like you would do from normal mode, by pressing :. So you can simply make your visual selection, press :, and run your substitution:
v " enter visual mode
<motion> " expand the selection
: " press :
:'<,'> " Vim inserts the range covering the visual selection for you
:'<,'>s#^#// <CR> " perform your substitution
Inside a function I can use:
s/foo/bar/ge
but it only substitutes the current line.
I'd like to substitute in the current selection. I tried
'<,'>s/foo/bar/ge
with no success.
Any help is appreciated.
This is perfectly fine:
fun! Foo()
'<,'>s/foo/bar/ge
endfun
You may get E20: Mark not set when no visual selection has yet been established, though. For the '<,'> marks to be defined, visual mode must have been left already; but this is also accomplished by the : command that is used to invoke the function, so it shouldn't be a problem (except for special cases like :help :map-<expr>). If you establish the visual selection only within the function, you need to leave it. Instead of
:normal! Vjj
append a <Esc> to leave visual mode (and set the marks):
:execute normal! "Vjj\<Esc>"
Note that hard-coding the selection often is bad style; you usually want a mapping to work either on the selection, or [count] lines, or the current line / entire buffer. For that, it's advisable to define the function with the range attribute; see :help function-range-example for details.
Currently I have this mapping in my ~/.vimrc
noremap <Leader>a :Ack <cword><cr>
which enables me to search for a word under the cursor.
I would like to search for a current visual selection instead, because sometimes words are not enough.
Is there a way I can send visual selection to ack.vim?
You can write a visual-mode map that yanks the highlighted text and then pastes it verbatim (properly escaped) onto the vim command-line:
vnoremap <Leader>a y:Ack <C-r>=fnameescape(#")<CR><CR>
This solution uses the <C-r>= trick that allows you to enter a kind of second-level command-line, which allows you to enter any vimscript expression, which is then evaluated, and the result is stringified and pasted onto the (original, first-level) command-line where the cursor is.
A slight disadvantage of this approach is that it commandeers the unnamed register, which you may not want.
While bgoldst's answer should work just fine, you could also consider my fork of ack.vim: https://github.com/AndrewRadev/ack.vim
It comes with a working :Ack command in visual mode, and a few other extras that I've summarized at the top of the README.
At the time of this writing this is the default behaviour of Ack.
Just do the following:
move your cursor on any word in normal mode (for instance, hit Esc button to enter in normal mode, you know...)
type :Ack with no argument
it will search for the word under the cursor
Usually I select text during a search in a file (for instance put cursor inside word and type * repeateadly) the type :Ack to look for that word in other files of the project.
Is there any way to yank a linewise visual selection without the newline at the end of the last line of the selection? More specifically, I'd like to be able to copy a line or lines from vim to the system clipboard and paste it elsewhere without the last command being executed without a chance to edit it.
I can get the desired effect by executing this command on the register in question after yanking:
:let #*=substitute(#*,'\n$','','g')
but is there any way to execute that command automatically? I am using MacVim at the moment and there doesn't appear to be a way to map an extra command to ⌘-C, so I'd have to have another mapping to remember to execute after copying if I can't find another solution.
My original answer to myself has been working well enough that I pretty much forgot about it, but now I've decided to set clipboard=unnamed to always yank to the system clipboard. A side effect is that yanking to the clipboard also affects the unnamed register, from which I don't want the trailing newline to be removed.
The new (and more Vim-ish) answer is a mapping to convert the linewise visual selection to characterwise before yanking it:
vnoremap <C-c> <Esc>'<0v'>g_y
Now I copy with <C-c> if I intend to paste in a command line or just y any other time. Pretty much what user Explosion Pills suggested but some added stuff to include the first character of the first line and last of the last line.
I came up with a solution that works for me, inspired by one of the answers here: Run command when vim enters visual mode
There is no real equivalent to a hypothetical VisualEnter or VisualLeave, but the CursorHold event along with changing updatetime will trigger after exiting visual mode. I also added the gv map to make it work when copying the previous selection.
" Remove last newline after copying visual selection to clipboard
function! RemoveClipboardNewline()
if &updatetime==1
let #*=substitute(#*,'\n$','','g')
set updatetime=4000
endif
endfunction
function! VisualEnter()
set updatetime=1
endfunction
vnoremap <expr> <SID>VisualEnter VisualEnter()
nnoremap <script> V V<SID>VisualEnter
nnoremap <script> gv gv<SID>VisualEnter
autocmd CursorHold * call RemoveClipboardNewline()
I don't like the idea of writing a swap file every 0 milliseconds, but I tested this in both Windows and OS X, and it works and causes no noticeable performance problems. It doesn't appear to write the swap file unless there are changes.
Also, I'm a little confused as to why this works at all, since it should trigger the function and set updatetime back to 4000 while still in visual mode, but apparently the value of updatetime doesn't change until after exiting visual mode, which works out nicely here.