vimscript: How to substitute in current selection? - vim

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.

Related

Vim function to find pattern, replace, and visual mode

So I use Vim to write reports at work, reports are basically a bunch of "common issues" that we write over and over, so they are templated. These templates have placeholder blocks {==BLOCK==} to ensure people modify things as/when needed, so this is an example:
The test revealed that it was possible to access {==sensitive data==},
as shown in the examples below...
That block may need to be modified, or not. So the idea is, I am editing the common issue, and I see there are 3 or 4 blocks like the one in the example, I'd like to press let's say [leader]b and then end up having the template text for the first block selected in visual mode without the {== and ==} that are around it.
I have tried a few things but I didn't get too far, any suggestions?
Thanks!
You could define the following function:
function! VisualSelect()
call search("==}")
norm hvT=
endfunction
nnoremap <leader>b :call VisualSelect()<cr>
vnoremap <leader>b Ol<esc>:call VisualSelect()<cr>
This will visually select the contents between {== and ==}. Typing <leader>b repeatedly will select the next occurrence.
Most template/snippet expand plugins support this.
With my lh-brackets plugin, you can execute
:SetMarkers {== ==}
and then jump from one placeholder to the next with CTRL+J with vim, or META-DEL with gvim. lh-brackets doesn't take care of loading/expanding templates. mu-template will add this layer.
If instead you choose to use one of the more popular snippet plugin, there will certainly be an option to change the syntax of the placeholders, but I don't know it.
The poor man's solution would look like:
nnoremap <c-j> <c-\><c-n>/{==.*==}<cr>v/==}/e<cr><c-g>
snoremap <c-j> <c-\><c-n>/{==.*==}<cr>v/==}/e<cr><c-g>
but it won't take care of restoring the search pattern, of the cases where the cursor is already within a placeholder, and so on...
EDIT: the version that automatically deletes the placeholder marks is
nnoremap <c-j> <c-\><c-n>/{==.*==}<cr>v/==}/e<cr>s<c-r>=matchstr(#", '{==\zs.*\ze==}')<cr><esc>gv6h<c-g>
the same in snoremap
In short:
nnoremap <leader>b /{==.*==}<cr>xxxvt=<esc>/==}<cr>xxxgv
What it does:
1.) find the pattern
/{==.*==}<cr>
2.) Remove the first "{=="
xxx
3.) Visual select your text until the first = (maybe this could be also optimized for using a regex instead of simple searching for the next =)
vt=
4.) Go to the end sequence
/==}<cr>
5.) Remove it
xxx
6.) Select again your last selection
gv
I have figured out a way based on what #snap said, I ended up adding the code to a Python plugin to run it through it, as that fits better with what I am trying to do, snippet below:
#neovim.command('VimisEditTemplateBlock')
def urlify(self):
"""Search next codify block and prepare for editing"""
[...]
self.nvim.command('call search("{==")') # Find beginning of codify block
self.nvim.command('norm xxx') # Delete {==
self.nvim.command('norm vf=') # Select the inner text
self.nvim.command('norm v')
self.nvim.command('norm xxxgvh') #Delete ==} and leave the inner text selected in Visual Mode

How to detect existence of Visual selection in VimL script

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.

Reference selection in Vim command mode to pass on to terminal

Is there something like % (that represents the whole buffer in Vim command line) for the current selection, so that I can do something like: :# sort (Imagine # represents the selection).
EDIT:
Sorry, I missed to mention that I am requesting for a way to operate on block selections not on ordinary selections that can be operated using ranges '<,'>.
Yes. Example:
:'<,'>!sort
The range :'<,'> represents the visually selected lines.
:* is shorthand for :'<,'>
If you hit : while in visual mode it will start the command with '<,'>
For more help see:
:h '<
:h v_:
:h range
You are probably looking for the marks '< and '>:
:'<,'>sort
If you just select a few lines and hit : to enter the command line these marks should appear automatically.
As others have already remarked, the visual selection is represented by the '<,'> range (there's also the :* short form). However, as all ranges, this always covers entire lines, even if the selection is only characters or a block.
Most Ex commands (like :sort) only operate on full lines, for :substitute, you can limit the effects to the visual selection by including the special \%V atom in the search pattern, cp. :help /\%V.
The idea to make Ex commands handle blockwise selections is old, but unlikely to be tackled any time soon.

vim: end comments and retain indentation

I'd like to be able to end a run of comments but retain the current indentation. Is this possible either through an existing command or with a function?
I have formatoptions -=o and autoindent so I normally get by using o from normal mode - the cursor is on the next line, the indentation is correct and I'm in insert mode. I'm only worrying about single line comments (eg '#'s - as for shell, python etc).
I'm interested in how I can make this more general (particularly not dependent on my formatoptions). I'd like to have an imap for ;; but I can't seem to find a straight forward way if I want to call a function (for example, to check whether I'm currently on a comment line).
I've played around with <expr> mappings and the expression register but either I lose the indentation (cursor ends up in the first column) or the comment continues. It seems like there might be a better alternative to either reimplementing the autoindent logic or trying to delete the extra comment characters. I've also tried saving/restoring formatoptions while using normal o but leaving insert mode when there's no other content on the line deletes the indentation.
I appreciate suggestions about how I should approach this.

How to automatically remove newline at end of yanked linewise visual selection?

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.

Resources