How to change Vim insert mode mappings behavior? - vim

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.

Related

Insert mode default keys in vim

The following items are useful to me in editing text, and I was wondering if vim had something for this built out of the box (though I didn't see it on the https://vimhelp.org/index.txt.html#index.txt page), or I had to create mappings for it:
Forward-delete a character. This is X in normal mode.
Forward-delete all text to the right of the cursor on the line. This is the inverse of ctrl-u.
Are either of these mappings available? And if not, are there 'standard' mappings for this that are common (for example, how it might be done in another unix program).
Note that this is the keyboard I have -- there is only one delete key (which acts like a normal backspace key) and there is no backspace key:
Note: for forward-delete, I am currently mapping ctrl-d as:
"Ctrl-d to forward-delete when in insert or command mode
noremap! <C-d> <Delete>
However, this interferes with the tab in insert mode (which I don't use) and the help-options in command mode (which I do use!) so I may have to modify this later, or hopefully someone suggests a better solution.
though I didn't see it on the https://vimhelp.org/index.txt.html#index.txt page
If you can't find it in the documentation, then it doesn't exist.
You can use fn+delete for "Forward-delete a character".
"Forward-delete all text to the right of the cursor on the line" is ctrl+k in MacOS, but Vim has its own use for that combo, :help i_ctrl-k so it is up to you to create a mapping for it.
Something like:
inoremap <key> <C-o>ld$

Using placeholders in 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.

How do I use Vim's escape() with <C-R> as its first argument?

My goal is to select several words in Vim's visual mode (well, neovim in my case), press leader+L and let fzf show search results for the selected string through :Rg. I came up with this:
vnoremap <expr> <leader>l 'y:<C-U>Rg '. shellescape(escape('<C-R>"', '()[]><')) .'<CR>'
Which does work, but when I select the text options(:modifier) and trigger a search, the escape() command doesn't escape the parentheses and Rg fails to return results.
In short, I'm expecting this command to fire:
:Rg 'options\(:modifier\)'
And I'm getting this instead:
:Rg 'options(:modifier)'
I'm guessing I can't use <C-R> in this context, but I can't seem to figure out why?
UPDATE: Thanks to a helpful reply from user D. Ben Knoble indicating I could drop and construct the mapping differently, I ended up with this, solving my problem:
vnoremap <leader>l "ky:exec 'Rg '. shellescape(escape(#k, '()[]{}?.'))<CR>
You don’t need to—all registers are available as variable’s prefixed with # (all are readable except #_, most are writable, I think).
So instead of <C-R>", use #"

How to imap a character in Vim based on previous character

I have created the following mapping to simulate behaviour in some IDEs where when you insert { after a function declaration like foo() a closing } and empty row is inserted automatically and cursor is set to the empty row on tabbed position.
:imap { {<CR><CR>} <up><Tab>
This of course does this behavior when I insert { in any context. How do I do it based on the previously inserted character? Must be a vim script function involved?
Note: I do not want to use external vim plugins.
IDEs usually do this expansion after typing {<CR>, which is easy to do in vimscript:
:imap {<CR> {<CR><CR>} <up><Tab>
This will not expand if you keep on typing other things on the same line.
The caveat is that there's a small delay when typing a { with this mapping. See the 'timeout' and 'timeoutlen' options for details.
These code snippets give you the character just before and just after the cursor when in insert mode:
let previous_character = getline(".")[col(".")-2]
let next_character = getline(".")[col(".")-1]
You can use them in an <expr> mapping:
:inoremap <expr> { getline(".")[col(".")-2] == " " ? "{^M}^OO" : "{"
The pointless mapping above checks if the character before the cursor is a space before deciding if it inserts a { or an expanded {}.
If you want a "smart" mapping you won't be able to avoid writing one or more functions. The one I use, for example, is 69 lines long.
You'll find multiple approaches and a list of plugins on the Automatically append closing characters Vim Tips Wiki page. Note that though there are simplistic solutions, they usually have some downsides, and the whole approach is unfortunately broken in Vim 7.4 with regards to undo.

VIM: Mappable (unused) shortcut letters?

I'm trying to create two mappings which are efficient for myself:
map X ddp
Which I'd use to delete and paste in one go.
map X "_dw
Which would delete a word without yanking into a register.
However I don't want to break any existing, useful shortcuts so I'm wondering what keys I could use - any suggestions? Am I being too uptidy?
vim help has a section :he map-which-keys
1.7 WHAT KEYS TO MAP *map-which-keys*
If you are going to map something, you will need to choose which key(s) to use
for the {lhs}. You will have to avoid keys that are used for Vim commands,
otherwise you would not be able to use those commands anymore. Here are a few
suggestions:
- Function keys <F2>, <F3>, etc.. Also the shifted function keys <S-F1>,
<S-F2>, etc. Note that <F1> is already used for the help command.
- Meta-keys (with the ALT key pressed). |:map-alt-keys|
- Use the '_' or ',' character and then any other character. The "_" and ","
commands do exist in Vim (see |_| and |,|), but you probably never use them.
- Use a key that is a synonym for another command. For example: CTRL-P and
CTRL-N. Use an extra character to allow more mappings.
See the file "index" for keys that are not used and thus can be mapped without
losing any builtin function. You can also use ":help {key}^D" to find out if
a key is used for some command. ({key} is the specific key you want to find
out about, ^D is CTRL-D).
Many Vim plugins use an initial <Leader> to start their key sequences; this is an (otherwise normally) unused key that is configurable by the user.
*<Leader>* *mapleader*
To define a mapping which uses the "mapleader" variable, the special string
"<Leader>" can be used. It is replaced with the string value of "mapleader".
If "mapleader" is not set or empty, a backslash is used instead. Example:
:map <Leader>A oanother line<Esc>
Works like:
:map \A oanother line<Esc>
But after:
:let mapleader = ","
It works like:
:map ,A oanother line<Esc>
Note that the value of "mapleader" is used at the moment the mapping is
defined. Changing "mapleader" after that has no effect for already defined
mappings.
Every single ASCII character, upper and lower case, is used for something in Vim. So you're going to wind up overwriting something--just pick something that you don't use. It may help to use a common idiom for your own extensions. I use a leading comma, for example:
map ,w :w!<CR>
map ,e :e #<CR>
imap ,, <ESC>
(The last is particularly useful for me, since I pretty much never need to write two consecutive commas in insert mode, and it's nice not to have to go all the way to the Esc key.)
Typically I use control + [letter] or alt + [letter] for most mappings and it's safe, but watch out for 'w' since that's needed for window commands. You might also be interested in arpeggio.vim which lets you create mappings to simultaneously pressed groups of keys - it will massively expand the possibilities for your mappings with no danger of over-mapping something. For example, you could map "dp" (pressed simultaneously) to execute "ddp" to delete and paste in one command.
Uhmm, no, don't. When creating your mappings try not to overwrite anything ... not so much because you don't use the command you're overmapping, but because some plugin which you have/or will have maybe using it.
And then you overmap it, and then you have to worry.
Personally, for commands such as you gave as an example, I like Ctrl+some key combinations. There are a lot of free ones in vim, and the letters on the left side near Ctrl make a nice pair then.
Btw, what are you trying to do with those mappings ... I understand the second one (delete word by word), but the first doesn't make sense to me. What is it supposed to do ? Transpose lines ?

Resources