So I'm trying to have a binding that runs lvimgrep on the currently selected text.
fun! s:get_visual_selection()
let l=getline("'<")
let [line1,col1] = getpos("'<")[1:2]
let [line2,col2] = getpos("'>")[1:2]
return l[col1 - 1: col2 - 1]
endfun
vnoremap <expr> <script><leader>* ":lvimgrep /" . <SID>get_visual_selection() . "/j **/*." . expand("%:e") . " \|lopen"
The function's from the comment on the question: How to get visually selected text in VimScript
Thing is that it is behaving really weird: Most of the times the text returned by the function doesn't match the visual selection and more often than not, it is the text of the last visual selection - not the current one.
Have gone through tons of posts around getting visual selection text in vimscript but can't get it to work.
I have also tried https://stackoverflow.com/a/1534347/287085 without success (copying selection to register) - get an error when called from my binding.
The problem is that the '<,'> marks are not set until after the current selection has been left (either by executing a command on it, or through <Esc>). Here, your expression mapping makes it more complex to prepend an <Esc> to leave visual mode first, so it's easier to insert the expression with :help i_CTRL-R and the expression register =:
:vnoremap <script> <leader>* <Esc>:lvimgrep /<C-R><C-R>=<SID>get_visual_selection()<CR>/j **/*.<C-R><C-R>=expand("%:e")<CR>\|lopen
If you don't mind clobbering the default register, you could also just yank the selection:
:vnoremap <leader>* y:lvimgrep /<C-R><C-R>"/j **/*.<C-R><C-R>=expand("%:e")<CR>\|lopen
Related
I sometime dream of a mark-selection feature in Vim, just like when you type:
ma
in normal mode, you can then hit, from anywhere:
`a
to get back to this place a.
I wish there were something like:
ma
in visual mode. This would save your visually selected area. And you would just type then, from any place in normal mode:
<someHeader>a
to enter visual mode back with this a selection.
Is there such a feature in Vim? Or an extension that looks like? Or an easy way to implement it?
I'm fairly certain there isn't any way to name selections similar to how marks or registers can be referenced.
The closest feature that I can think of is the gv command which enters Visual mode with the last previously selected block already re-selected. I find this to be a lot more convenient than having to manually re-selecting the same block of text more than once.
It should be possible to write a function using Vimscript that saves the start and ends of a visual block as marks that can be re-used to reselect a Visual block. I had a look to see if it was possible to use Vimscript to save the < and > marks as other (less ephemeral) marks but I didn't see anything.
I have written this which I believe is what you are asking for:
function! VisualMark()
call inputsave()
let registers = input("m")
call inputrestore()
exec "normal! `<m" . registers[0]
exec "normal! `>m" . registers[1]
endfun
function! GetVisualMark()
call inputsave()
let registers = input("`")
call inputrestore()
exec "normal! `" . registers[0]
if strlen(registers) > 1
exec "normal! v`" . registers[1]
endif
endfun
vnoremap m <esc>:call VisualMark()<cr>
nnoremap ` :call GetVisualMark()<cr>
they should behave in the same way as a regular mark except only in visual mode, and they both need two arguments.
First, to get a visually selected area saved, in visual mode with the block selected, hit the m key like you normally would. It will then let you type in some more text... here the function will expect 2 characters which are the two registers that the beginning and end marks will be saved to... so for example entering mab in visual mode and then pressing enter (I could not figure out a way to make it work without an enter), would create a mark in register a for the beginning of the block, and b would be the end of the block.
To reselect the block visually, go to visual mode and do the ` which is the same as a regular mark... this too will expect two characters
EDIT:
I have made the implementation better in my opinion. Before I had it so that to bring back the visually selected block you had to be in visual mode then hit the backtick (same button that you would use for a normal mark) button and then the 2 registers. Now you do it in normal mode... This means that the default use of the backtick now uses this function, but the function now checks how many registers you provide... so if you only use one register, it will only take you back to that one. if you provide 2 registers, then it will visually select them... if you provide more than 2, it will only use the first 2.
The enter key is still required to enter in your selection though. It is probably better this way with the new implementation anyways.
For those that want to see, or use the old implementation this is it:
function! VisualMark()
call inputsave()
let registers = input("m")
call inputrestore()
exec "normal! `<m" . registers[0]
exec "normal! `>m" . registers[1]
endfun
function! GetVisualMark()
call inputsave()
let registers = input("`")
call inputrestore()
exec "normal! `" . registers[0]
exec "normal! v`" . registers[1]
endfun
vnoremap m <esc>:call VisualMark()<cr>
vnoremap ` <esc>:call GetVisualMark()<cr>
Here is the kind solution of Steven Hall, along with some modifications I needed for the visual marks not to overwrite regular registers.
https://github.com/iago-lito/vim-visualMarks
It is a small vimScript allowing one to mark a visually selected area by typing, in visual mode:
ma
(for mark a)
Then retrieve it later from any place by typing, in normal mode:
<a
It does still need some basic improvements, but now anyone can try, improve and share. Thank you Steven for having launched the process :)
I know this must be something simple.
Why does vnoremap <leader>rl di[<esc>pa] wrap selected text in brackets, but:
vnoremap <leader>rl :call VisAddRefLink()<CR>
function! VisAddRefLink()
execute "normal! di[\<esc>pa]"
endfunction
doesn't?!
Any help appreciated!
Try this:
vnoremap <leader>rl :<C-u>call VisAddRefLink()<CR>
function! VisAddRefLink()
execute "normal! gvdi[\<esc>pa]"
endfunction
The <C-u> in front of the call avoids that the mapping inserts the '<,'> visual range in front of it; we want the function called only one time, not once for every line in the range.
Inside the function, we need to re-establish the visual selection first before operating on it; the gv does that.
You need to make your function handle a range. One way of accomplishing this would be like this:
vnoremap <leader>rl :call VisAddRefLink()<CR>
function! VisAddRefLink() range
exe a:firstline . "normal! ^i["
exe a:lastline . "normal! $a]"
endfunction
The reason your example isn't working is because the exe doesn't operate on a visual selection per say. For example, try visually selection something and then doing :norm d. You'll notice it doesn't delete. If you add a range to your function like :help function-range-example it helps with visual selections by operating in a similar fashion (line by line). However, it still isn't a true visual selection. The range addition does allow you the a:firstline and a:lastline variables though, which can be used to accomplish this. You can also accomplish this with a single like in this manner:
vnoremap <leader>rl <esc>:norm! '<^x2Phr['>$x2pr]<cr>
This first uses <esc> to end the visual selection. Then it executes a normal command that will only run once. If the visual selection was left then it would run once for each line in the visual selection. Once it's run once it
'<^ jumps to the first line of the visual selection and to the first non-blank space on that line.
x2Phr[ deletes that character, pastes it twice in front, moves to the left so we're over the new character, and replaces it with the opening [
'>$ move to the last character on the last line of the visual selection
x2pr] same as before but in the opposite direction
As usual, there's more than one way to skin a cat, especially with vimscript. As you learn more you see a lot of possibilities for accomplishing things.
I want to search, like I do with the * command, for a pattern I have selected in visual mode.
I am aware of visual mode yanking, which fills the register 0 by default, and the possibility of just searching by / and then Ctrl-R (retrieving) the contents of register 0 (Ctrl-R, 0) to paste the pattern as a search.
Thing is, I do not want to YANK first, I already have something yanked, I just want to search for what's selected in visual mode now.
How can I do that, please? Can I do that without fiddling with different "yank to register N" tricks?
If you use gvim or console vim built with X support (check if 'guioption' is available) and a is present in your 'guioptions', then you can get current selection from * register. Otherwise, I'm afraid there is no easy way to do that without writing a VimL function, which will extract the selection based on values of < and > marks. That function then can be used with CTRL-R = in the search prompt.
Why don't you just combine all the steps you've outlined into a mapping? The only thing missing is saving and restoring the unnamed register, and a little bit of escaping.
" Atom \V sets following pattern to "very nomagic", i.e. only the backslash has special meaning.
" As a search pattern we insert an expression (= register) that
" calls the 'escape()' function on the unnamed register content '##',
" and escapes the backslash and the character that still has a special
" meaning in the search command (/|?, respectively).
" This works well even with <Tab> (no need to change ^I into \t),
" but not with a linebreak, which must be changed from ^M to \n.
" This is done with the substitute() function.
" gV avoids automatic reselection of the Visual area in select mode.
vnoremap <silent> * :<C-U>let save_unnamedregister=##<CR>gvy/\V<C-R><C-R>=substitute(escape(##,'/\'),"\n",'\\n','ge')<CR><CR>:let ##=save_unnamedregister<Bar>unlet save_unnamedregister<CR>gV
Here's the solution that works for me to make * work with [count] in visual mode:
vnoremap * :call <SID>VisualSearch()<cr>:set hls<cr>
fun! s:VisualSearch() range
let unnamed = #"
let repeat = v:count
exe 'norm gv"zy' | let #/ = #z
for x in range(repeat)
call search(#/, 'ws')
endfor
let #" = unnamed
endfun
You change the "z"s on line five to whatever registers you never use.
Is there a vim command to directly select a block of text which has just been pasted?
ps. I know about gv to reselect a block after exiting visual mode. It doesn't apply to this case.
If you want to select it just after paste (before you change anything else), use
nnoremap <expr> gV "`[".getregtype(v:register)[0]."`]"
. [ and ] marks point to start and end of the last change, v:register is set to the last register used (which is register used for the paste command unless you, for example, yank something), [0] selects only first byte of register type (it is required because for blockwise register it returns <C-v>{width}) and register type is one byte which is just the same as the keystroke you should use in normal mode to invoke visual mode.
I saw this solution somewhere on SO, you may want to search for it in order to get some alternatives.
In my case I have this map:
:nnoremap gp `[v`]
After more research I think the better solution is:
" https://vim.fandom.com/wiki/Selecting_your_pasted_text
nnoremap <expr> gp '`[' . strpart(getregtype(), 0, 1) . '`]'
I have had the following maps in my vimrc forever:
nnoremap <leader>p `[V`]
nnoremap <leader>[ `[V`]<
nnoremap <leader>] `[V`]>
They do the following:
visually select the recently pasted block
de-indent the recently pasted block
indent the recently pasted block
I probably use the indent ones even more than the selection one.
I find myself often repeating the following pattern of operations.
I usually go into visual mode, select some lines or block. Then I yank them using y, and paste them using p or P. The next step is to select the pasted text, to replace a variable or function name or change indentation.
I know that I can use gvto reselect the "origin" but what I would like is a similar command to select the "destination".
:help gv mentions :
After using "p" or "P" in Visual mode the text that was put will be selected.
but it is only useful when you are replacing a selection by the content of register, not when you are inserting a whole new block.
You are looking for
`[v`]
'[ and '] are marks automatically set by vim to the start and the end of the "previously changed or yanked text". v switches to visual mode in between.
I prefer the following simple mapping to Benoit's function
nnoremap <expr> g<c-v> '`[' . strpart(getregtype(), 0, 1) . '`]'
Learn more about expression maps:
:h :map-expression
As #ZyX pointed out the strpart is not needed and can be rewritten as:
nnoremap <expr> g<c-v> '`[' . getregtype()[0] . '`]'
One of your use cases is to change indentation after pasting.
I use the following maps to achieve this:
nnoremap <leader>[ `[V`]<
nnoremap <leader>] `[V`]>
They do the following:
de-indent the recently pasted block
indent the recently pasted block
I find these very useful and well used maps.