Vim - capture in between slashes? - vim

Is there a motion for capturing text in between / or \? I know there is motions for other symbols ---
ci" - Capture 'text' inside "text"
vi( - Visual capture int var inside foo(int var)
di[ - Delete word [word] into []
The only workaround I can find is by using Vim Surround, and using it to change surrounding \ into " (cs\"), and then working from there. However, not only is it that kind of tedious, but the plugin only supports backslashes, not forward.

You could write your own text-object for this pretty easily.
onoremap <silent> i/ :<C-U>normal! T/vt/<CR> " inside /
onoremap <silent> a/ :<C-U>normal! F/vf/<CR> " around /
For it to work with visual mode :
xnoremap <silent> i/ :<C-U>normal! T/vt/<CR> " inside /
xnoremap <silent> a/ :<C-U>normal! F/vf/<CR> " around /
Similarly you could also do for \
Edit: Added info comments.

Here's one way to do it with a mapping:
nnoremap H mal??e+1<Enter>mb//<Enter>y`b`a
This mapping will yank everything between two occurrences of the last search pattern.
Place it in your .vimrc file to make it permanent.
Usage for your question:
search for forward slash: /\/ (note the backslash escape character)
position cursor between two slashes (or on second slash)
press H
Everything between the last / and the next / will get yanked.
Explanation:
nnoremap H mal??e+1<Enter>mb//<Enter>y`b`a
- nnoremap H map H in normal mode, ignoring other mappings
- ma place a mark (named a) at the current cursor location
- l move the cursor one char to the right
- ??e+1<Enter> move to 1 character after END of prev occurrence of last search pattern
- mb place a mark (named b) at the current cursor location
- //<Enter> go to the beginning of the next occurrence of the last search pattern
- y`b yank to the location of the mark named x (note: ` = "back tick")
- `a go to the mark named `a`
Example input:
This is a *funky* string
search for *
position cursor between two asterisks (or on 2nd asterisk)
press H
The word funky will be in the yank buffer.
You can use words as delimeters!
Example input:
<br>
Capture all this text.
<br>
search for <br>
press H in normal mode when between <br>s (or on 2nd <br>)
You can use regexes, too!
Example input:
<p>
Capture this paragraph.
</p>
search for <.\?p> (or <\/\{,1}p> to be more correct)
press H in normal mode when inside the paragraph (or on closing <p> tag)
...
A better approach might be to use a register to remember a delimiter, so you can use this mapping quickly and/or repeatedly. In other words, you could store / or \ or <\?p> in a register and use that to quickly capture text between your stored delimiter.

there is no built-in text object with slash. However there are plugins support customized text-object, like:
targets

A recently proposed Vim patch adds a text object for arbitrary matched pairs:
https://groups.google.com/d/topic/vim_dev/pZxLAAXxk0M/discussion

Related

Dot-repeatable mapping to insert a space from normal mode and move the cursor along

I have a mapping nnoremap <leader>l i<space><esc> that inserts a space while staying in a normal mode, and the mapping is dot-repeatable. However, the cursor stays where it was and the space "expands" to the right of the cursor.
I want to have a mapping that does the same but moves the cursor along, e.g.
nnoremap <leader>h i<space><esc>l
I love breaking sequences of characters with a space from normal mode and I want to decide at will if the cursor should stay or move (using either h to move the cursor along or l to move the text to the left while leaving the cursor where it is).
None of the tricks with i or a or <C-o> seems to work and this is an expected behavior.
Is there any clever hack to accomplish dot-repeatability using nnoremap only? It should also work if I am at the end of the line.
I can probably re-phrase it like this: I want to have functionality similar to X and x that deletes a single character before or at the cursor, but instead of deleting a character I want to insert a <space> before or at the cursor (not after the cursor as with a). And it should be dot-repeatable and take counts. Preferably a mapping that does not use more than a single line in the .vimrc and does not require any plugins. And I don't want to change the behaviour of <esc> (or i, whichever is responsible for moving the cursor when one leaves the insert mode) that moves a cursor one character to the left.
Edit 1:
Dot-repeatable nnoremap <leader>l i<space><esc> does this:
<leader>l...
AAAAAAAAAA[B]BBBBBBBB
AAAAAAAAAA[ ]BBBBBBBBB
AAAAAAAAAA[ ] BBBBBBBBB
AAAAAAAAAA[ ] BBBBBBBBBB
And I want a dot-repeatable <leader>h that would do this:
<leader>h...
AAAAAAAAAA[B]BBBBBBBB
AAAAAAAAAA [B]BBBBBBBB
AAAAAAAAAA [B]BBBBBBBB
AAAAAAAAAA [B]BBBBBBBBB
Edit 2:
The workaround mentioned in reply by #romainl does the trick:
function! s:insspace(...)
if a:0
" perform operation
execute 'normal' v:count1.'i '."\<esc>".'l'
else
" set up
let &operatorfunc = matchstr(expand('<sfile>'), '[^. ]*$')
return "g#\<space>"
endif
endfunction
nnoremap <silent><expr> <leader>h <sid>insspace()
Can someone explain how it works? I am a beginner in vim...
Given the string below and assuming the cursor is on the -:
lorem-ipsum
^
There are various easy and repeatable ways to insert a space after the - and leave the cursor on the space:
a <Esc>
s<C-r>" <Esc>
lorem- ipsum
^
The two macros aboveā€¦
are "dot-repeatable", because there is no motion involved,
leave the cursor on the space because, after an insertion, Vim places the cursor on the last inserted character.
But this is precisely the latter behaviour that prevents us from doing the same in the other direction without a motion either before or after the insertion.
Without motion, the operation is "dot-repeatable" but the cursor is left on the -:
i <Esc>
a <Esc>
s <C-r>"<Esc>
lorem -ipsum
^
With motion, the cursor is left on the space but the operation is not "dot-repeatable":
i <Esc><Left>
<Left>a <Esc>
s <C-r>"<Esc><Left>
lorem -ipsum
^
The workaround is a bit contrived but nifty.
Maybe I did not understand your question well enough, but for me this does the trick:
:nnoremap <leader>h i<space><esc>w
Rationale: Insert space, after <ESC> go one left, so the cursor stays on the space, use w to get to the next word boundary.
Is that what you want? I can now do 5\h and it stays at the current position while inserting spaces to the left.

How can I pre-fill a substitute command with the result of the previous `*` search in the position where the {string} goes?

I currently use the following mapping to substitute the result of the last search pattern with a new word in the entire buffer:
nnoremap <Leader>sa :%s///g<left><left>
What I would like is to put the result of the last search pattern where the new word goes.
Example:
suppose my cursor is standing on the word "hello"
Then I press *
Then I invoke the mapping with <Leader>sa
At this point, my command line is filled with :%s///g. What I would like to have is :%s//hello/g.
I tried the mapping below, but it adds the whole word delimeters (\< and \>) which I don't want.
nnoremap <Leader>sa :%s///g<left><left><C-r>/
--- EDIT ---
After some much needed clarification, I think that the simplest approach is to use :help s/\& in the replacement part of your substitution:
nnoremap <key> :%s//&/g<left><left>
If you really need a more visually explicit method, you can trim the \< and the \>, like this:
nnoremap <key> :%s//<C-r>=substitute(#/,'\\<\\|\\>','','g')<CR>/g<left><left>
which should get you something like this:
:%s//hello/g
with the cursor after hello, ready for further editing.
--- ENDEDIT ---
In command-line mode, :help c_ctrl-r_ctrl-w inserts the word under the cursor:
nnoremap <key> :%s///g<left><left><C-r><C-w>
<cword> gets the word under the cursor, but you have to call expand and send a carriage return
nnoremap <Leader>sa :%s///g<left><left><C-r>=expand("<cword>")<CR>
One way to support visual selections and multiple words is to yank the text you want to replace then search for it with <C-r>"
nnoremap <Leader>sa :%s///g<left><left><C-r>"
You would make a visual selection, press y, then <leader>sa to replace, or /<C-r>" to search.
Edit
After reading the clarifications above, here is a different way to do (almost) the same thing:
Make a visual selection
Press *
Type cgn<C-r>"2 (e.g. change "ThisIsAVeryLongVariable" to "ThisIsAVeryLongVariable2")
Each press of . will repeat the substitution

vim copy non linewise without leading or trailing spaces

I am trying to create a mapping that will allow me to select a line of text non linewise so I can paste at curosr(not before or after) without introducing spaces that may have preceeded the line where it was yanked from.
This is what I was trying to do
"copy non linewise
nmap <leader>yy 0y$
nnoremap <Leader>yy ^yg_
^ and g_ are similar to 0 and $, respectively, but they exclude blank characters.
My UnconditionalPaste plugin has a gcp / gcP mapping that not only flattens any number of yanked lines into a characterwise paste, but it also removes preceding and trailing whitespace.
The advantage of "casting the contents" only on paste is that you don't need to think about the future use while yanking, and as the original contents are preserved, you can paste the same register contents in various ways (linewise, characterwise, and any of the other flavors that my plugin supports).
Alternatively, you could remap Y to yank until the end of string (similar to C):
noremap Y y$
Now ^Y would do the job.

Search forward for the [count]'th occurrence of the text selected in visual mode

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.

How do I search for the selected text?

How do I search for the selected (with v and y) string? I usually search with /, but I cannot paste the selected string after /.
In insert mode you can use CTRL-R to insert the contents of Vim registers. By default, copied text gets put in the unnamed register ", so to insert that text you would type <C-R>" in insert mode. The search prompt uses Command-line mode which has its own
CTRL-R that works almost identically to the one in Insert mode.
So if I just yanked the text foo, typing /<C-R>" would search for the text foo once I press enter.
:set hls
:vmap * y:let #/ = #"<CR>
set hls (hight light search)
v => hjkl (select something)
press *, copy selected text to reg "
set content of reg / as "
press n/N to navigate
I have this mapping defined in my vimrc, it maps * to defining the search pattern as what is currently highlighted (escaping all potential dangerous characters, and converting a space in what is highlighted to any sequence of spaces)
xnoremap * :<C-U>let old_reg=getreg('"')|let old_regtype=getregtype('"')<CR>gvy/<C-R><C-R>=substitute(substitute(escape(#", '/\.*$^~['), '\s\+', '\\s\\+', 'g'), '\_s\+', '\\_s*', 'g')<CR><CR>gV:call setreg('"', old_reg, old_regtype)<CR>:let v:searchforward=1<CR>
In order to use it, start visual mode with v, and then highlight what you want to search and press * not y.
Of course you can map # to search backwards (exactly the same except that v:searchforward should be set to 0.
If you only care about searches, you can use the cheat method I use. Yank a word via v or y+w, then issue the command
:%s//XYZ/gc
This will search for the last searched word. Then when you find it, it will ask for confirmation to replace with XYZ, and all you have to do is hit q to quit.
First yank the highlighted text using y. Then
/
Ctrl + r
"
Above commands will paste what you have yanked after the /. Then press ENTER

Resources