Using placeholders in vim - vim

Given a vim document with multiple occurrences of a specific placeholder, say <%%>, I want to be able to jump to the next placeholder from the beginning of the document: More explicitly, if the document is given by
$\frac{<%%>}{<%%>}$
I want to press a key such that the first placeholder gets removed, i.e. we have
$\frac{}{<%%>}$
where the cursor is at the position of the placeholder and vim is in insert mode.
I'm aware of the vim-latex plugin which implements such a behaviour but only need this one feature. I tried to use the /-search of vim but didnt get the cursor position right.
Thanks in advance for any advice.

lh-brackets provides this feature -- actually vim-latex placeholder system has been inspired by lh-brackets one.
The idea to implement this feature, is:
to look for the pattern of the placeholder -- prefer search() to know whether something has been found: no selection shall be done otherwise
Actually doing it correctly may require a couple of calls to searchpair() to handle the case where the cursor is in the middle of the placeholder, see lh-brackets code as search(..., 'c') is not enough;
select this pattern -- v + movement 3<right> for instance
and finally either go into SELECT-mode (gh <c-g>) or remove the placeholder and go into insert mode (s)
If your placeholder pattern is exactly <%%>, it'll be quite simple to implement.
" I factorize common code without introducing the exact keybinding
" NB: we have to use the ancestor of map-<expr> as the later doesn't
" permit to move the cursor -> we execute the expression register: :h #=
" NB: As said earlier a correct implementation would require to call searchpair()
" twice in case the cursor is within a placeholder, see lh-brackets code
nnoremap <silent> <Plug>(jump-next) #=(search('<%%>') > 0 ? "v3l<c-g>" : '')<cr>
vmap <silent> <Plug>(jump-next) <c-\><c-n><Plug>(jump-next)
imap <silent> <Plug>(jump-next) <c-\><c-n><Plug>(jump-next)
" Tests shall be done in a real plugin before binding to the chosen shortcut: µ, <f3>, <c-j>, <tab>...
nmap <silent> µ <Plug>(jump-next)
vmap <silent> µ <Plug>(jump-next)
imap <silent> µ <Plug>(jump-next)
If sometimes it could become <%somestring%>, then I would definitively recommend using lh-brackets or any snippet engine that already takes care of this -- for instance, mu-template would permit to use your exact snippets/templates by changing locally the default placeholder characters with VimL: let s:marker_open = '<%' +
VimL: let s:marker_close = '%>' (I'm also maintaining mu-template which depends on lh-brackets).
NB: lh-brackets also provides surrounding (non intrusive), and bracket pairs insertion (can be deactivated: add :let g:cb_no_default_brackets = 1 in your .vimrc)

Using a macro might help.
In your example, use /<%%> to search for your placeholder. Then gg will take you at the beginning of the document.
Then start the macro with qa for instance. Go to the next occurrence of your placeholder with n. Then, ca< will remove the placeholder. C-o q will stop recording, while keeping you in insertion mode.
To go to and replace the next placeholder, just do #a (execute the macro stored in register a)

Does this mapping help?
:nmap %% /<%%><cr>ni
It executes a search (/<%%><cr>), repeats the search with n to skip the 1st placeholder and goes to the second. Then it switches (i) to Insert Mode.

Related

VimScript execute search does not work anymore

I don't know since when, but my visual selection search function is not working anymore. I broke the problem down to this minimal example.
Assume the following buffer:
word
word
word
When I run /word, I find all results and can jump between them.
When I run :execute '/word' this works the same as before.
When I write a short autoload function just doing the same it does not work the same:
~/.config/nvim/autoload/utils/search.vim:
function! utils#search#visual_selection() abort
execute '/word'
endfunction
Executing :call utils#search#visual_selection() makes the cursor land on the first result, but no results are highlighted. Moreover it is using the old search pattern instead of the new one. So if I search first for something non existing like foo and then execute this function, pressing n give me the error message Pattern not found: foo.
What has changed. What is the difference here?
This is actually documented behavior of Vim and NeoVim. It's not really related to the use of :execute (you can reproduce it with a direct use of /word), but with how search (and redo) work in a function.
See :help function-search-undo, which states:
The last used search pattern and the redo command "." will not be changed by the function. This also implies that the effect of :nohlsearch is undone when the function returns.
You can work around that by explicitly setting the search pattern register, which you can do with a let command.
function! utils#search#visual_selection()
let #/ = 'word'
execute "normal /\<cr>"
endfunction
The second command executes a simple / from normal mode, that is enough to search for word, since it will look for the last search pattern which is now set to what you wanted.
After the function is finished, the search pattern will keep its value, which means highlighting through 'hlsearch' will work, and so will the n command to find the next match.
A restriction from the approach above is that you can't really set search direction for repeats with n. Even though there is v:searchforward, which can be set, that variable is also reset after a function as part of the :help function-search-undo effects. There doesn't seem to be anything you can do about that one...
If the purpose of this function is for use in a key mapping, you might consider a completely different approach, using nnoremap <expr> and having the function return the normal mode command for the search as a string, that way the actual search happens outside of the function and the restrictions from function-search-undo won't apply.
For example:
function! utils#search#visual_selection(searchforward)
let pattern = 'word'
if a:searchforward
let dir = '?'
else
let dir = '/'
endif
return dir.pattern."\<cr>"
endfunction
And then:
" Mappings for next-word and previous-word
nnoremap <expr> <leader>nw utils#search#visual_selection(1)
nnoremap <expr> <leader>pw utils#search#visual_selection(0)
This avoids the issue with :help function-search-undo altogether, so consider something like this approach, if possible in your case.

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 can I delete empty lines in motion in Vim

I know how to delete blank lines with commands.
We may visually select the text block first, and then run commands like
s/^$/d to delete all blank lines or %s/^\(\s*\n\)\+/\r to keep only one blank lines.
Can I perform the above using motion, so that I can just press some keys to perform the "delete-motion" without having to enter command-line mode?
Creating a new operator for this is a good idea, but it can be tough to get right.
The operator-user plugin makes that task easy.
Once you have installed operator-user, all you need to do is add two lines to your vimrc, one to define the operator, and one to define your personal mapping to it:
call operator#user#define_ex_command('delete-blanks', 'g/^$/d')
map _ <Plug>(operator-delete-blanks)
This creates a new operator _. Change it to whatever you like best.
Now you can do _3} or _G or _23k to delete the blank lines contained in the motion. Text objects _a}, doubling of the operator 4__, and Visual mode V7j_ are all also supported, as befits a proper operator.
You could use operatorfunc. For example:
Define a function like this in your .vimrc:
function! DeleteEmptyLines(type)
if a:type == 'line'
silent execute ".,'\"g/^$/d"
endif
endfunction
And a mapping:
nnoremap <silent> ,d :set operatorfunc=DeleteEmptyLines<CR>m"g#
,d performs now just like an operator and accepts a (line-based) motion. You can, for example, insert ,d5j or ,dG in normal mode to delete empty lines in the next 5 lines or to the end of file.
You can find more information on how to extend this functionality here:
http://learnvimscriptthehardway.stevelosh.com/chapters/33.html
and of course::h operatorfunc and :h map-operator.
From :h map-operator:
"An operator is used before a {motion} command. To define your own operator
you must create mapping that first sets the operatorfunc option and then
invoke the g# operator. After the user types the {motion} command the
specified function will be called."
well, using motions I don't think you can only delete blank lines.
But you can do it using a mapping:
:nmap <Leader>db :g/^$/d<CR>
The motions help you move one word, one paragraph away... And before the motion you use an operator (d, c...).
So what you'd want is to create a new operator that deletes blank lines within the given motion (or selection). What I gave you is close to that, but you'd have to invent a new operator (and I don't think there's many unbound keys left).
Other vimmers may correct me, but I think the easiest way to create such operators would be to define a map for each motion and bind it to a function.
There isn't a motion that can combine with a delete such that it only deletes blank lines. A motion denotes all the text between the initial and final position, without any gaps. That includes motions which search using regular expressions.

How to change Vim insert mode mappings behavior?

I have insert-mapped in a script the pattern date<Tab> to insert the current date in the format YYYY-MM-DD.
inoremap <script> <silent> <buffer> date<Tab> <C-R>=strftime("%Y-%m-%d")<CR>
When I start typing the pattern in insert mode, instead of displaying the full pattern before replacing it by the date string only when I press Tab, Vim displays only the last character typed and it is pretty anoying when I don't want to use it. If I'm typing 'date', for instance, this is what Vim displays ('|' is the cursor representation):
|d
|a
|t
data|
Also, I have installed the Snipmate vim plugin which makes use of the Tab key to replace snippets by code templates and when I'm typing a snippet, it doesn't behave like I described before. What Snipmate does is to map only the Tab key and when the key is pressed, it gets the previous word and check if it matches one of its snippets.
That said, I will leave two questions and the answer to one of them solves my problem:
Is there a way to configure Vim no display the full pattern before changing it to its mappings?
Can I have two plugins using the same mapping? Like if I map the Tab key too and whenever the word before the cursor is 'date' my plugins acts and Snipmate acts in the other cases.
First answer is no.
Second is also no, but it can be emulated:
Generic way is the following (requires frawor):
" plugin/tab.vim
execute frawor#Setup('0.0', {'#/mappings': '0.0'})
" Make sure that mappings were set up
runtime! after/plugin/snipMate.vim
" Get information about already existing mapping
" (it was defined by snipmate)
let s:snipmap=s:_r.map.maparg('<Tab>', 'i', 0)
" Create a new mapping with unique lhs
let s:snipmap.lhs='<SNR>'.s:_sid.'_OldSnipMap'
call s:_r.map.map(s:snipmap)
function s:F.insdate()
if getline('.')[:(col('.')-1)][-4:] is# 'date'
return repeat("\<BS>", 4).strftime("%Y-%m-%d")
else
" Here is the magic: I have a choice to either use remappable mapping
" or <C-\><C-o>:call feedkeys()<CR> workaround for nore mapping
return "\<C-\>\<C-o>:call feedkeys(\"\\<SNR>".s:_sid."_OldSnipMap\")\n"
endif
endfunction
call s:_f.mapgroup.add('Tab', {'tab': {'lhs': '<Tab>', 'rhs': s:F.insdate, 'mode': 'i'}})
Note that in your example you don't map <Tab>, you map date<Tab> so it does not interfer with snipmate mapping. Above code uses the same approach as IMAP plugin: when last key of {lhs} is pressed, check whether previous keys are in the buffer. If they are remove them and insert {rhs} instead. Thus you can type date<Tab> no matter how slow and it will work.
Note 2: it is the generic way. You can drop frawor dependency and most of the code by simply looking at <Tab> {rhs}:
function s:Insdate()
if getline('.')[:(col('.')-1)][-4:] is# 'date'
return repeat("\<BS>", 4).strftime("%Y-%m-%d")
else
return "\<C-g>u\<C-r>=TriggerSnippet()\n"
endif
endfunction
inoremap <Tab> <C-r>=<SID>Insdate()<CR>
As I know, there seems no such setting to "turn off" the "searching map" status, which vim will eat all the char you type if it is part of some map in keymap matching.
Vim only can bind one key to a specific action, so there is no way to make a key do two thing as you may wish. On the other hand, you can configure "snipmate" to use other key to do the "expand" action. And that's should be a usually way when you meet the key conflict problem. Alternatively, you can use "abbreviate" to do something :
:abbreviate <expr> :date: strftime("%Y-%m-%d")
But I am sorry for that, the "eating matching" also exists here.

How to emulate Emacs’ transpose-words in Vim?

Emacs has a useful transpose-words command which lets one exchange the word before the cursor with the word after the cursor, preserving punctuation.
For example, ‘stack |overflow’ + M-t = ‘overflow stack|’ (‘|’ is the cursor position).
<a>|<p> becomes <p><a|>.
Is it possible to emulate it in Vim? I know I can use dwwP, but it doesn’t work well with punctuation.
Update: No, dwwP is really not a solution. Imagine:
SOME_BOOST_PP_BLACK_MAGIC( (a)(b)(c) )
// with cursor here ^
Emacs’ M-t would have exchanged b and c, resulting in (a)(c)(b).
What works is /\w
yiwNviwpnviwgp. But it spoils "" and "/. Is there a cleaner solution?
Update²:
Solved
:nmap gn :s,\v(\w+)(\W*%#\W*)(\w+),\3\2\1\r,<CR>kgJ:nohl<CR>
Imperfect, but works.
Thanks Camflan for bringing the %# item to my attention. Of course, it’s all on the wiki, but I didn’t realize it could solve the problem of exact (Emacs got it completely right) duplication of the transpose-words feature.
These are from my .vimrc and work well for me.
" swap two words
:vnoremap <C-X> <Esc>`.``gvP``P
" Swap word with next word
nmap <silent> gw "_yiw:s/\(\%#\w\+\)\(\_W\+\)\(\w\+\)/\3\2\1/<cr><c-o><c-l> *N*
Depending on the situation, you can use the W or B commands, as in dWwP. The "capital" versions skip to the next/previous space, including punctuation. The f and t commands can help, as well, for specifying the end of the deleted range.
There's also a discussion on the Vim Tips Wiki about various swapping techniques.
In the middle of a line, go to the first letter of the first word, then do
dw wP
At the end of a line (ie the last two words of the line), go to the space between the words and do
2dw bhP
From the handy Equivalence of VIM & Emacs commands
You could add shortcut keys for those by adding something like the following to your vimrc file:
map L dwwP
map M 2dwbhP
In that case, SHIFT-L (in command-mode) would switch words in the middle of the line and SHIFT-M would do it at the end.
NB: This works best with space-separated words and doesn't handle the OP's specific case very well.
There's a tip on http://vim.wikia.com/wiki/VimTip10. But I choose to roll my own.
My snippet has two obvious advantages over the method mentioned in the tip: 1) it works when the cursor isn't in a word. 2) it won't high-light the entire screen.
It works almost like emacs 'transpose-words', except that when transposition is impossible, it does nothing. (emacs 'transpose-words' would blink and change cursor position to the beginning of current word.)
"transpose words (like emacs `transpose-words')
function! TransposeWords()
if search('\w\+\%#\w*\W\+\w\+')
elseif search('\w\+\W\+\%#\W*\w\+')
endif
let l:pos = getpos('.')
exec 'silent! :s/\(\%#\w\+\)\(\W\+\)\(\w\+\)/\3\2\1/'
call setpos('.', l:pos)
let l:_ = search('\(\%#\w\+\W\+\)\#<=\w\+')
normal el
endfunction
nmap <silent> <M-right> :call TransposeWords()<CR>
imap <silent> <M-right> <C-O>:call TransposeWords()<CR>
You can use dwwP or dWwP as Mark and CapnNefarious have said, but I have a few notes of my own:
If the cursor is on the first letter of the second word, as in the example you gave, you can use dwbP (or dWbP to handle punctuation);
If the cursor is in the middle of the word, you can use dawbP/daWbP.
There's a transpose-words script on vim.org that works beautifully.

Resources