I would like to do something like this
if exists(expand('<cword>'))
inoremap ( (<esc>g_i)<left>
else
inoremap ( ()<left>
endif
The logic behind this is that if there is something under my curser, surround that with the parenthesis, otherwise just create a closed parenthesis.
The if does not currently recognize the element under the cursor.
Is there a solution?
To have a mapping react to the current conditions when it is invoked, you cannot redefine the mapping itself. Instead you have to make the right-hand side of the mapping react to the conditions. This is done easiest with a :help :map-expr, like this:
:inoremap <expr> ( (empty(expand('<cword>')) ? '()<Left>' : '(<Esc>g_i)<Left>')
Note that I just translated your example, I didn't check that it makes sense or works as expected.
Related
I'm using the CoC plugin for which works great.
This command remaps the enter key so we can use enter to confirm selection of the suggestion popover.
This mapping works great, except that it stays in insert mode after accepting the selection:
inoremap <expr> <cr> pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>"`
From https://github.com/neoclide/coc.nvim/wiki/Completion-with-sources#use-cr-to-confirm-completion
How can I modify this to return back to normal mode after I hit enter?
Any ideas? Thanks!
First, let's deconstruct the mapping you got from that page:
inoremap <expr> <cr> pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>"
inoremap makes it a non-recursive insert mode mapping. i means "insert mode" and nore means "non-recursive". I know the remap at the end makes it tempting to call those things "remaps" or "remappings" but they are not. They are just "mappings".
<expr> makes it an expression mapping. It means that the right-hand side is an expression that is going to be evaluated at runtime to produce the actual RHS.
pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>" is what Vim calls a "trinary operator" (AKA ternary operator): :help trinary. In plain english, it reads like this: "if the popup menu is visible, return <C-y>, if not, return <C-g>u<CR>".
:help complete_ctrl-y accepts the current suggestion in the popup menu.
:help ctrl-g_u prevents Vim from inserting an undo point before <CR>. It is not strictly necessary but good practice.
You want to adjust the mapping's behaviour when you accept a suggestion so it is the "\<C-y>" part that must be changed.
Now…
you already know what key to press to leave insert mode,
you can take a look at :help key-notation if you are unsure how to represent that key,
you know what part of the mapping you need to change,
you should get an idea of how to proceed just by looking at the two last parts of the "trinary" operator.
I am trying to replicate the behavior of beg abbreviation shown on the page https://castel.dev/post/lecture-notes-1/ with vimscript. That means I want to make it so that when I type "beg" in insert mode at the beginning of the line, it calls a function that i choose.
I tried to make it with iabbrev, but it doesn't expand until i hit space, and space is typed after my abbreviation. It also doesn't recognize if it is the beginning of a line.
Another approach I tried is with an auto command. I added to my init.vim the following
function SayHello ()
if (getline('.') =~ "\s*beg$")
s/^\(\s*\)beg/\1\\begin{}\r\r\\end{}/
endif
endfunction
autocmd TextChangedI *.tex call SayHello ()
This almost works, but for one problem. It doesn't work if the autocomplete popup is active, which is always because of a plugin I use. So the effect of this code is that as i first type beg, there is no effect, but if i backspace one letter and add it again, it works as intended. I tried to remedy it by adding the command
autocmd CompleteChanged *.tex call SayHello ()
so that it works anyway if there is autocomplete. Unfortunately it doesn't work, because it tries to edit the buffer of the popup. I tried making the function exit the popup to no effect.
How can I make it work like on the site?
Especially useful would be knowing how to use the TextChangedI command regardless of autocomplete because that would make other useful things possible.
Create a harmless insert mode mapping:
inoremap beg beg
It inserts beg when you type beg. Cool.
Turn it into an equally harmless "expression mapping":
inoremap <expr> beg 'beg'
In an expression mapping, the right-hand side is an expression that is evaluated when you press the left-hand side in order to produce the macro that is going to be executed for you. Here, the expression is the string beg so the mapping works just like the first one.
Experiment with logic in the RHS:
inoremap <expr> beg &filetype == 'foo' ? 'beg' : 'geb'
Here, you either get a beg or a geb, depending on the current filetype. Cool, it's getting interesting!
Make it insert a crude snippet only when at the beginning of a line:
inoremap <expr> beg col('.') == 1 ? 'begin{}<CR>end{}<C-o>O' : 'beg'
Make it possible to name the environment "dynamically":
inoremap <expr> beg col('.') == 1 ? substitute('\begin{+++}<CR>\end{+++}','+++',input('environment: '),'g').'<C-o>O' : 'beg'
OK. It works, but it feels clunky.
That's where one usually starts to wonder whether it is actually worth it to roll one's own when existing plugins already handle all that much more elegantly.
I have two remaps in my .vimrc for the insert mode:
inoremap jk <ESC>
inoremap {<CR> {<CR>}
The first remap should have a short timeoutlen, whereas the latter should have no timeout at all. Reason: I need a timeout for the first remap, as jk might be used as regular characters. The latter on the other hand is code completion, where a timeout makes no sense.
Is it possible to assign different timeoutlen's for different remaps? Thanks & BR
One way to extend the timeout of your second mapping is to actually only map <CR> and then use an expression to check that it's being typed after a { character.
The behavior is somewhat different from a two-character mapping in that the mapping will also work if you're typing a <CR> after a { that was already there, which might be acceptable to you (or might even be exactly what you wanted.)
Since you're using an expression to do checks, you can do additional checks such as only applying the mapping if you're typing the <CR> at the end of a line (so you avoid it if you're using it to split an existing long line.)
A possible implementation of that would be:
inoremap <expr> <CR>
\ col('.') ==# col('$')
\ && getline('.')[col('.')-2] ==# '{'
\ ? "\<CR>}\<C-O>O"
\ : "\<CR>"
We're using a ternary operator here to produce a different output based on the condition holding or not. If it doesn't, we simply map back to the keystroke itself, so it keeps working as it usually does in all other contexts.
For the part that inserts a closing curly brace, I also added a CTRL-O O to the end, so after inserting the brace, it will add a new line before it and leave the cursor in an indented column inside the block. I hope you'll find this helpful, but feel free to disregard that part if it's not really what you had in mind.
Note: The bar symbol (|) represents the editor caret throughout this question
I've made an UltiSnips snippet like this:
snippet "(\w+)" "HTML tag" r
<`!p snip.rv = match.group(1)`>$0</`!p snip.rv = match.group(1)`>
endsnippet
This lets me expand any word into a HTML tag, for example typing "body" and pressing tab expands to <body>|</body>.
The caret is placed between the tags. When I now press return, I would like to end up with:
<body>
|
</body>
This could be done with a keybind like this:
:ino <buffer> <CR> <CR><Esc>O
But I don't want to permanently rebind my return key. I only want this specific keybind to be active when my caret is placed between an opening and a closing HTML tag.
How can that be done most simply?
Another example is when I have my caret placed between two curly brackets, like so:
function() {|}
And press enter, I would like the result to be:
function() {
|
}
Again this can be done with the above key mapping, but in this case I would only want it to be active when my caret is placed between two curly brackets.
You could use map-expression (see :h map-expression) to decide whether what <CR> get mapped to when you hit it:
for example the following insert mode map:
inoremap <expr> <CR> strpart(getline('.'), col('.')-2, 1) =~ '[>{]' ? '<CR><ESC>O' : '<CR>'
checks the charachter before cursor and if its > or { it returns <CR><ESC>O in other situations it simply do <CR>
In place of conditional ternary expression you could define a full functional function to respond in any situatuion with any pairs; But there is already great plugins that intend to do such tasks gracefully:
demilitMate and auto-pairs are two famous one.
give them a try.
I've implemented this in lh-brackets, and I remember giving an answer to a very close question here: How to move opening curly braces to a new line in Vim?
I define it with:
call lh#brackets#enrich_imap('<cr>',
\ {'condition': 'getline(".")[col(".")-2:col(".")-1]=="{}"',
\ 'action': 'lh#brackets#_add_newline_between_brackets()'},
\ 0,
\ '\<cr\>'
\ )
If you want to distinguish language situations, you'll have to have ftplugins for c (and all other bracket based languages), xml (and other tag based languages) where you have a inoremap <buffer> <expr> that tests the context (see the test above) to return either "<cr>" or "<cr><esc>O".
" to be put in c, js, java, c#... ftplugins
inoremap <buffer> <expr> <cr> getline(".")[col(".")-2:col(".")-1]=="{}" ? '<cr> : '<cr><esc>O'
" to be put in a xml and HTML ftplugin
inoremap <buffer> <expr> <cr> getline(".")[col(".")-2:col(".")-1]=="><" ? '<cr> : '<cr><esc>O'
You can also define them globally in your vimrc, but then, it will be triggered all the time -- the tests will need to be merged in that case.
I'm trying to write a vimscript function that will expand do this:
if (condition) { /*Press enter here*/ }
// Turns into this
if (condition)
{
//Cursor gets placed here
}
The comments are not included, they just say what happens.
I started by mapping the enter key in insert mode.
inoremap <expr> <cr> OpenBrackets()
Next, I started writing the function. However I'm not great at vimscript, so I don't know how to. Here is the pseudocode:
function! OpenBrackets()
if "check if cursor is surrounded by brackets. Ideally check if cursor is inside of a regex pattern.
normal "expand brackets
else
normal <cr>
endif
endfunction
How would I do this with real code?
Rather than reinvent the wheel, you probably want to take a look at some of the snippet plugins. Some popular ones that would seem to accomplish what you’re after are vim-snippets, vim-snipmate, and Ultisnips (feels a little heavy).