I'm wondering how to write a function that overloads <TAB>.
First it would do a check to see if there is a snippet that needs to be completed, and if there is a snippet, then expand it.
Otherwise, I would like the function to do a check to see if there is a space before the cursor (or we are on new line) before Tab is pressed. If so, then it should do regular <tab>.
Otherwise, I'd like for it to call
deoplete#manual_complete()
Unless there is already a menu open, in which case, I should be able to tab through it.
Here was my attempt (Which fails completely) and some settings for reference:
let g:ulti_expand_or_jump_res = 0 "default value, just set once
function! Ulti_ExpandOrJump_and_getRes()
call UltiSnips#ExpandSnippetOrJump()
return g:ulti_expand_or_jump_res
endfunction
inoremap <silent><expr> <tab>
\ (Ulti_ExpandOrJump_and_getRes() > 0) ? "\<C-y>"
\ : pumvisible() ? "\<C-n>" :
\ <SID>check_back_space() ? "\<TAB>" :
\ deoplete#manual_complete()
function! s:check_back_space() abort "{{{
let col = col('.') - 1
return !col || getline('.')[col - 1] =~ '\s'
endfunction "}}}
Oddly enough, when I press tab, the bottom right of vim reads that I have typed "^I", which is very strange behavior.
The reason I do not have the ultisnips expand trigger as "tab" is that it disables the use of tab for deoplete (for whatever reason.)
I believe the main issue you're running into is that UltiSnips#ExpandSnippetOrJump() will try to move the cursor and that's a problem from an <expr> mapping in insert mode. Using <C-R>=...<CR> instead should make it work. (At least that's what the example in UltiSnips documentation uses.)
I'd also recommend using a single function to handle all Tab cases instead of a rather long one-liner. That way you can use ifs and document each case specifically.
I also think for the case you expand or jump on UltiSnips, you want to expand to nothing rather than a <C-y>, right?
Putting it all together:
function! HandleTab() abort
" First, try to expand or jump on UltiSnips.
call UltiSnips#ExpandSnippetOrJump()
if g:ulti_expand_or_jump_res > 0
return ""
endif
" Then, check if we're in a completion menu
if pumvisible()
return "\<C-n>"
endif
" Then check if we're indenting.
let col = col('.') - 1
if !col || getline('.')[col - 1] =~ '\s'
return "\<Tab>"
endif
" Finally, trigger deoplete completion.
return deoplete#manual_complete()
endfunction
inoremap <silent> <Tab> <C-R>=HandleTab()<CR>
You might want to review the order of the checks. It seems to me that checking for pumvisible() should be first. Also, checking whether you're indenting is probably cheaper than checking for an UltiSnips expansion, though I guess you could be jumping through fields when you have a space... Anyways, it should be easy to tweak this to make it work.
Related
Just another vim source code comment question here. I have this mapping for my python source code files:
map <C-C> <Home>i#<Esc>
imap <C-C> <Home>#<Esc> i
On Ctrl-C it puts # in the beginning if the line to comment it out. This improves productivity a lot. But when I want to uncomment lines, I have to do this manually, meaning going to the first character of each commented line and remove it. This is very annoying. At the first glance, I can just bind Home-x to some key, but I can occasionally remove an innocent space or something else in case I misshit and do this on line that has no # character at the beginning of it. I first try to do some replacement with :%s// for a single line, but that has an unwanted affect - it triggers a search and highlights 'pattern' in other lines. In case of a single # character it is a fail.
Can anybody suggest how to remove a specified character in the beginning of current line in case it present and do nothing, if not, without using pattern replacement?
I have created a simple function to Toggle comment in the line:
function! ToggleComment()
let l:pos = col('.')
if getline('.') =~ '\v(\s+|\t+)?#'
exec 'normal! _"_x'
let l:pos -= 1
else
exec 'normal! I#'
let l:pos += 1
endif
call cursor(line("."), l:pos)
endfunction
nnoremap <Leader>t :call ToggleComment()<CR>
inoremap <Leader>t <C-o>:call ToggleComment()<CR>
I recommend Tim Pope's plugin vim-commentary because is way more complete. But of course our idea gives you guys a glimpse how far we can get with vimscript.
Another approach, which does not need to save windowview and toggles comments in other languages can be seen here
Can anybody suggest how to remove a specified character in the beginning of current line in case it present and do nothing, if not, without using pattern replacement?
A solution would be (assuming your cursor is anywhere to the right of # when using the map):
map <c-c> mmF#x`m
A more general solution would be to use a substitution and histdel() to delete the last search pattern:
function! DelComment()
s/^\( *\)#/\1/
call histdel("search", -1)
let #/ = histget("search", -1)
endfunction
After executing the function (by selecting it and typing :#") you can map it to <c-c>:
map <silent> <c-c> mm:silent! call DelComment()<cr>`m
I like using marks around functions to retain the cursor position after executing the map. Feel free to remove mm and `m in the above map.
In my .vimrc file, I would like to configure a minimalist function to perform a smart selection.
For example try to select inner (), if selection is empty try inner [], if still empty try inner {}, etc.
But I am stuck at the very beginning: to call / execute a command / expression to select text from a function.
function! SelectInnerBrackets():
" failed attempts
call visualmode()
execute "vi("
execute "visual! i("
endfunction
Fyi: I actually use neovim, but it probably does not make difference to this issue.
EDIT: based on solution proposed by #Ingo Karkat, I share my final piece of code. Note that it does not work perfectly with cross nested delimiters.
function! SelectInner(delimiter)
" We have to switch to normal mode to compare positions
execute "normal! vi".a:delimiter."\<C-\>\<C-n>"
return getpos("'<") != getpos("'>")
endfunction
function! TrySelectInner(delimiters)
for delimiter in a:delimiters
if SelectInner(delimiter)
normal! gv
break
endif
endfor
endfunction
" quickly select a word, expression or brackets content
nnoremap W viw
nnoremap E :call TrySelectInner(["'", '"'])<CR>
nnoremap R :call TrySelectInner(['(', '[', '{'])<CR>
If you read :help visualmode(), you'll notice that the (non-argument version of the) function is a query that has no side effects. Just :calling doesn't do any good, and you don't need the current / previous visual mode, as you build the selection yourself.
Commands like vi( are normal mode commands. To invoke them from a Vimscript function, you need the :normal! command. :execute is used on Ex commands, in order to interpolate variable values (this is called eval() in many other languages), or use special :help key-notation (we'll use that later).
In order to test whether a selection was made, Vim conveniently has two special marks ('< and '>) that specify the boundaries of the selection. Unfortunately, they are only set after visual mode has been left (by operating on it, or via <Esc>). Within a plugin, it's better to use <C-\><C-n> keys instead of <Esc>; it will return to normal mode, too, but doesn't beep if we're already in normal mode. I use a separate :normal! command for that (with :execute to use the special key notation) to ensure that it will also execute when the previous command sequence aborts because no such selection can be made.
Taken together, here's the corrected version of your attempt:
function! SelectInnerBrackets()
echomsg "trying (...)"
normal! vi(
execute "normal! \<C-\>\<C-n>"
if getpos("'<") != getpos("'>") | return 1 | endif
echomsg "trying [...]"
normal! vi[
execute "normal! \<C-\>\<C-n>"
if getpos("'<") != getpos("'>") | return 1 | endif
echomsg "trying {...}"
normal! vi{
execute "normal! \<C-\>\<C-n>"
if getpos("'<") != getpos("'>") | return 1 | endif
echomsg "nothing found"
return 0
endfunction
In order to re-select, you can use gv afterwards, e.g. via this mapping:
nnoremap <Leader>V :if SelectInnerBrackets() <Bar> execute "normal! gv" <Bar> endif<CR>
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':'')
I most IDEs and modern text editors (Sublime Text 3) the cursor is correctly indented after inserting a newline in between an html tag (aka 'expanding" the tag):
Before:
<div>|</div>
After pressing CR:
<div>
|
</div>
But in Vim, this is what I get:
<div>
|</div>
How can I get the same behaviour in Vim like in most other editors (see above)?
The only correct behavior of <CR> in insert mode is to break the line at the cursor.
What you want is an enhanced behavior and you need to add something to your config to get it: a mapping, a short function or a full fledged plugin.
When I started to use vim, that behavior was actually one of the first things I added to my vimrc. I've changed it many times in the past but this mapping has been quite stable for a while:
inoremap <leader><CR> <CR><C-o>==<C-o>O
I've used <leader><CR> to keep the normal behavior of <CR>.
Here is a small function that seems to do what you want:
function! Expander()
let line = getline(".")
let col = col(".")
let first = line[col-2]
let second = line[col-1]
let third = line[col]
if first ==# ">"
if second ==# "<" && third ==# "/"
return "\<CR>\<C-o>==\<C-o>O"
else
return "\<CR>"
endif
else
return "\<CR>"
endif
endfunction
inoremap <expr> <CR> Expander()
This little snippet will remap Enter in insert mode to test whether or not the cursor is between > and < and act accordingly if it is. Depending on your indent settings the \<Tab> may need to be removed.
It will not play nice with other plugins that might be also be mapping the Enter key so be aware that there is probably more work to do if you want that compatibility.
function EnterOrIndentTag()
let line = getline(".")
let col = getpos(".")[2]
let before = line[col-2]
let after = line[col-1]
if before == ">" && after == "<"
return "\<Enter>\<C-o>O\<Tab>"
endif
return "\<Enter>"
endfunction
inoremap <expr> <Enter> EnterOrIndentTag()
I have only tested the simple cases (beginning of the line, end of the line, inside and outside of ><), there are probably edge cases that this would not catch.
#RandyMorris and #romainl have posted good solutions for your exact problem.
There are some other possibilities you might be interested in if you are typing out these tags yourself: there's the ragtag.vim plugin for HTML/XML editing.
With ragtag.vim you type this to create your "before" situation (in insert mode):
div<C-X><Space>
To create your "after" situation you would instead type:
div<C-X><Enter>
So if you know beforehand that you are going to "expand" the tag, typing just the element name and the combo CtrlX followed by Enter is enough.
There are also other more advanced plugins to save keystrokes when editing HTML, such as ZenCoding.vim and Sparkup.
Since no one have mentioned it I will. There is excellent plugin that does exactly that
delemitmate
After closing a tab in vim, how do I make it so that the tab to the left is the one automatically shown?
The default when closing a tab seems to be showing the right tab, which is annoying because new tabs OPEN on the right of your current tab. So opening a new tab and closing it leaves you on a different tab.
There is one idea: though there is no TabClose event there is TabEnter event which could be used to achieve what you want: if on one of the events number of tabs is less then previously recorded number then obviously it was triggered due to closed tab:
let s:prevtabnum=tabpagenr('$')
augroup TabClosed
autocmd! TabEnter * :if tabpagenr('$')<s:prevtabnum && tabpagenr()>1
\ | tabprevious
\ |endif
\ |let s:prevtabnum=tabpagenr('$')
augroup END
A patch has been proposed to add a 'tabcloseleft' option; it is on the todo list to be integrated into Vim (some time in the future, once Bram has time to work on it).
Great question, struggled with it for a while here is my solution.
Add the following to your .vimrc file. Note that I have remapped for the commands :q and :Q
" declare function for moving left when closing a tab.
function! TabCloseLeft(cmd)
if winnr('$') == 1 && tabpagenr('$') > 1 && tabpagenr() > 1 && tabpagenr() < tabpagenr('$')
exec a:cmd | tabprevious
else
exec a:cmd
endif
endfunction
" define :Q command
command Q call TabCloseLeft('q!')
" override default quit command
cabbrev q <c-r>=(getcmdtype()==':' && getcmdpos()==1 ? 'Q' : 'q')<CR>
credit for TabCloseLeft function: https://github.com/convissor/vim-settings/blob/master/.vimrc
I doubt it; there isn't any way that I can see. There isn't even an autocommand event that you could latch on to (e.g. TabClose would be what you would want, but it doesn't exist).
The closest you'll get is probably having your own command or mapping which will, as well as closing the tab, execute the normal mode gT. (You might be able to get fancy in a function and detect the state of the windows and thus whether you wish to gT or not. That would require a little more thought and investigation.)
You can, of course, also investigate using another solution to tabs; here are a few:
Use split windows
Use the alternate file (see CTRL-6)
Use the tag stack for moving between files (CTRL-] to move, CTRL-T to get back)
This looks useful:
http://vim.wikia.com/wiki/Have_focus_on_left_tab_after_tabclose
I think I'm going to modify it slightly to do this:
function! CloseSomething()
if winnr("$") == 1 && tabpagenr("$") > 1 && tabpagenr() > 1 && tabpagenr() < tabpagenr("$")
q | tabprev
else
q
endif
endfunction
cnoremap q<CR> :call CloseSomething()<CR>
So now :q will do what I want...