Is it wrong to have two statements inside an if on vimscript? - vim

I recently decided to revamp my .vimrc.
As per instructions here, I added a small snippet in a file called ultisnips_tab_hack.vim, which is sourced in my .vimrc.
The last line of that snippet is this:
inoremap <expr> <CR> pumvisible() ? "\<C-R>=ExpandSnippetOrCarriageReturn()\<CR>" : "\<CR>"
Which from what I could tell was mapping Enter to an expression that would test if the pop-up menu was visible. If it was, the inserted text would be decided by the ExpandSnippetOrCarriageReturn() function that is defined in the snippet. Else a simple carriage return would be inserted.
This snippet is supposed to make YouCompleteMe and Ultisnips behave, and, as per this other comment, the last line should be modified to the following to make it also behave with vim-endwise.
let g:endwise_no_mappings = 1
inoremap <expr> <CR> pumvisible() ? "\<C-R>=ExpandSnippetOrCarriageReturn()\<CR>" : "\<CR>\<C-R>=EndwiseDiscretionary()\<CR>"
Which is the same except that if the pop-up menu is not visible, it will also call the vim-endwise function to decide if something else should be inserted. And also, the endwise mapping is disabled, so that it won't conflict with our custom mapping.
So, I was trying to make a solution that would work whether vim-endwise was loaded or not (because I wanted to load it only on ruby files). And what I tried was:
if exists("*EndwiseDiscretionary")
let g:endwise_no_mappings = 1
inoremap <expr> <CR> pumvisible() ? "\<C-R>=ExpandSnippetOrCarriageReturn()\<CR>" : "\<CR>\<C-R>=EndwiseDiscretionary()\<CR>"
else
inoremap <expr> <CR> pumvisible() ? "\<C-R>=ExpandSnippetOrCarriageReturn()\<CR>" : "\<CR>"
endif
To my surprise, that gave me the following weird error on vim when I pressed Enter (and vim-endwise was loaded).
E15: Invalid Expression: ExpandSnippetOrCarriageReturn()\
And also, the following text was inserted: pumvisible() ? " instead of a carriage return.
To my even-more-surprise, though, the following caused no errors at all, whether vim-endwise was loaded or not.
let g:endwise_no_mappings = 1
if exists("*EndwiseDiscretionary")
inoremap <expr> <CR> pumvisible() ? "\<C-R>=ExpandSnippetOrCarriageReturn()\<CR>" : "\<CR>\<C-R>=EndwiseDiscretionary()\<CR>"
else
inoremap <expr> <CR> pumvisible() ? "\<C-R>=ExpandSnippetOrCarriageReturn()\<CR>" : "\<CR>"
endif
I've been bitten by some weird vimscript stuff before, so I'm now wondering if two statements inside an if causes this kind of problems and I'm supposed to always extract them to a function.
Or maybe I need a special separator that I don't know of, or something like that. Maybe it's a problem with a variable assignment right above a mapping?
Can any vim guru please elucidate me?
TL;DR
Causes weird E15: Invalid Expression: ExpandSnippetOrCarriageReturn()\ error when I press Enter on insert mode:
if exists("*EndwiseDiscretionary")
let g:endwise_no_mappings = 1
inoremap <expr> <CR> pumvisible() ? "\<C-R>=ExpandSnippetOrCarriageReturn()\<CR>" : "\<CR>\<C-R>=EndwiseDiscretionary()\<CR>"
else
inoremap <expr> <CR> pumvisible() ? "\<C-R>=ExpandSnippetOrCarriageReturn()\<CR>" : "\<CR>"
endif
Work as intended:
let g:endwise_no_mappings = 1
if exists("*EndwiseDiscretionary")
inoremap <expr> <CR> pumvisible() ? "\<C-R>=ExpandSnippetOrCarriageReturn()\<CR>" : "\<CR>\<C-R>=EndwiseDiscretionary()\<CR>"
else
inoremap <expr> <CR> pumvisible() ? "\<C-R>=ExpandSnippetOrCarriageReturn()\<CR>" : "\<CR>"
endif

Related

Binding a key to execute multiple expression maps in vim

nnoremap <expr> <C-b> ':set bg='.(&bg=='dark' ? "light" : "dark")."<cr>"
nnoremap <expr> <C-b> ':!'.(&bg=='dark' ? "dark" : "light")."<cr>"
Both work indivisually, but when I set the keybinding to
nnoremap <expr> <C-b> ':set bg='.(&bg=='dark' ? "light" : "dark")."<cr>" \| ':!'.(&bg=='dark' ? "dark" : "light")."<cr>"
Only the first one gets executed. How do I make the keybind execute both of these commands?
By adding the <expr> modifier you tell Vim that the whole right hand side of the mapping is an expression but the <bar> in the middle breaks the expression.
This means that your second expression, that conditionally executes an external command, must be fused with the first one in one way or another. There would be a few ways to do it in this specific case but the simplest is to concatenate the two instead of separating them with a <bar>:
nnoremap <expr> <C-b> ':set bg='.(&bg=='dark' ? "light" : "dark")."<cr>".':!'.(&bg=='dark' ? "dark" : "light")."<cr>"
You could also have a single ternary condition.
But that one-liner is getting dangerously long. That's usually where turning your unmaintainable logic into a readable function starts to make sense:
function! ToggleBackground()
let toggles = { "dark": "light", "light": "dark" }
let &background = toggles[&background]
call system(toggles[&background])
endfunction
nnoremap <C-b> <Cmd>call ToggleBackground()<CR>

How to remap CoC VIM autocomplete key?

I am trying to remap the autocomplete key from the "Enter" key to "TAB" because I keep autocompleting when I intend to go to the next line. The code below is the default option for coc, and I think this is where I should be able to remap the key.
" make <CR> auto-select the first completion item and notify coc.nvim to
" format on enter, <cr> could be remapped by other vim plugin
inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm()
\: "\<c-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
I thought that changing the <cr> in the beginning to <TAB> would work. However, although it does allow me to autocomplete using TAB, it creates weird auto indentations in some cases. For example:
//normal behavior
someFunction(){
//cursor here appropriately indented
}
//behavior after I made the changes mentioned above
someFunction(){
//cursor here}
I assume I just fundamentally don't understand something about coc or remapping keys in VIM.
Why can't I simply change that <cr> to <TAB>? How can I go about remapping the autocomplete key from "Enter" to "TAB"?
I don't understand vimscript too well, but I managed to get something working by trial and error.
Default Setting:
inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm()
\: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
Autocompleting on Tab:
"This expression seems to be responsible for coc formatting on enter
inoremap <silent><expr> <cr> "\C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
"I this just says autocomplete with the first option if pop up menu is open.
"If it is not open, just do a regular tab.
inoremap <silent><expr> <TAB> pumvisible() ? coc#select_confirm() : "\<C-g>u\<TAB>"
Replace the following line from the example coc config
inoremap <silent><expr> <CR> coc#pum#visible() ? coc#pum#confirm()
\: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
with this:
inoremap <silent><expr> <TAB> coc#pum#visible() ? coc#pum#confirm() : "\<C-g>u\<TAB>"
This was based on #christofuy's answer but updated after this line changed.

In .vimrc, function when cursor between brackets/braces and enter is pressed

The question kind of describes what I'm trying to do. Right now I'm using
inoremap { {}<Left>
To place the cursor between the brackets. The next thing I'm trying to achieve is if the cursor is between and enter is pressed, the result should be:
{
| <-cursor
}
Seems like maybe it should be simple but after some Googling, I couldn't find a solution. (still very novice at vim scripts)
What I have in lh-brackets could be summarized as
inoremap <expr> <cr> getline(".")[col(".")-2:col(".")-1]=="{}" ? "<cr><esc>O" : "<cr>"
(But it's actually a little bit more complex as other conditions could be added)
Hate to answer my own question but finally figured it out:
inoremap <expr> <CR> pumvisible() ? "\<CR>" : "\<Esc>:call Checkcursor()\<CR>"
function! Checkcursor()
let b:letters = strcharpart(getline('.')[col('.') - 1:], 0, 2)
if b:letters == '{}'
call feedkeys("li\<CR>\<Esc>\ko", "n")
else
call feedkeys('o')
endif
endfunction
You can record a macro...
qai{<cr><cr>}<esc>ki<tab><esc>q
#a
To leave the macro in insert mode you can append to the macro
ii<esc>y"A$
You also might want to try something like ultisnips.

Vim script: run an execute when mapping?

So I'm not sure how to go about running some some code in my mappings, like:
nnoremap <Return> :execute "normal! if 1 echo('one') endif"<cr>
Also tried it without the 'normal' - tried different combinations with separating the commands via '\' and '|' but nothing worked - keep getting variable not defined errors.
Any idea how?
EDIT:
So here's what I'm actually doing:
" Quickly toggle between insert/normal modes
nnoremap <A-e> i
inoremap <silent><A-e> <esc>:call GoRightIfNotBOL()<cr>
" Returns 1 if the cursor is at the beginning of a line "
function! IsBOL()
return col('.') == 1
endfu
function! GoRightIfNotBOL()
if !IsBOL()
execute "normal l"
endif
endfu
So instead of calling GoRightIfNotBOL I thought I could inline its code cause really, I can't think of another location where I would be using this function, and it's pretty small.
you are looking for <expr> mapping
read :h <expr> there you'll find examples.
If your codes were a bit long, put them in a function, and call that function in your mapping. It is more readable if you later want to do some change on it.
An example with inoremap <expr>:
inoremap <expr> <YourKeys> "<esc>".(col('.')>1?'l':'')

Ctrl+Space for omni and keyword completion in vim

I Want to use Ctrl+Space for omni-completion (and keyword completion if there is no omni-completion) in vim. I've tried this which I found somewhere on the web:
inoremap <expr> <c-space> pumvisible() ? "\<C-n>" : "\<C-x>\<C-o>\<C-n>\<C-p>\<C-r>=pumvisible() ? \"\\<Down>\" : \"\\<CR>\""
however it's not working. Anyone who is using Ctrl+Space for this too who can show me the correct way (which works) to do it?
Worth noting is that it needs to work in the terminal version of vim NOT gvim.
Try this:
inoremap <expr> <C-Space> pumvisible() \|\| &omnifunc == '' ?
\ "\<lt>C-n>" :
\ "\<lt>C-x>\<lt>C-o><c-r>=pumvisible() ?" .
\ "\"\\<lt>c-n>\\<lt>c-p>\\<lt>c-n>\" :" .
\ "\" \\<lt>bs>\\<lt>C-n>\"\<CR>"
imap <C-#> <C-Space>
The above way is "kind of" working, but it's so unreadable that almost nobody could say what it actually does. The solution above is not good.
Short Answer - Use this:
function! Auto_complete_string()
if pumvisible()
return "\<C-n>"
else
return "\<C-x>\<C-o>\<C-r>=Auto_complete_opened()\<CR>"
end
endfunction
function! Auto_complete_opened()
if pumvisible()
return "\<Down>"
end
return ""
endfunction
inoremap <expr> <Nul> Auto_complete_string()
inoremap <expr> <C-Space> Auto_complete_string()
This answer also respects that there are two possible values (depending on terminal/gvim usage) for Ctrl+Space: <C-Space> and <Nul>.
I use a similar approach as the first one in jedi-vim, but more customizable.
Long Answer - What the above does:
The whole escaping of the above answer is so confusing, that I've split the above answer into a readable format:
function! Auto_complete_string()
if pumvisible()
return "\<C-n>"
else
return "\<C-x>\<C-o>\<C-r>=Auto_complete_opened()\<CR>"
end
endfunction
function! Auto_complete_opened()
if pumvisible()
return "\<c-n>\<c-p>\<c-n>"
else
return "\<bs>\<C-n>"
end
endfunction
inoremap <expr> <Nul> Auto_complete_string()
This clearly shows what it is doing. There's some weird stuff happening in Auto_complete_opened. It's not just doing completion, it's doing two additional things after trying to complete:
When trying to use the omnicompletion, it somehow does a <C-n><C-p><C-n>, which could IMHO just be abbreviated to <C-n>.
In case completion is unsuccessful, it uses a backspace and does a completion again, not <C-o><C-x> but <C-n>, which just doesn't make a lot of sense.
I'm not saying that this is not what some user might want, but it's probably not what most users want! My short answer takes that in credit and gives you a simple way to edit it. You can now just easily change things if you want to (for example <Down> to <C-n>, if you want the first entry to be written from the beginning on).
For iterm2 and vim these lines works for me, I got from jedi-vim
" Next three lines are to enable C-Space to autocomplete, omnicomplete
inoremap <C-Space> <C-x><C-o>
imap <buffer> <Nul> <C-Space>
smap <buffer> <Nul> <C-Space>

Resources