vim automatically show left tab after closing tab - vim

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...

Related

vimscript for Ultisnips+Deoplete compatibility

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.

Vim cursor position after expanding html tag

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

Making two windows with separate files(?) in gvim

I'm a beginner vi user. I don't know the terminology, but I want to split my gvim terminal(screen?) into 2 windows which each have 5 different files(buffers?). I can open the first 5 files in one window, then split to a second window, but I don't know how to open 5 more different files in the second window. I haven't been able to find this information. Normally I switch between files with :n and :prev.
To say it again: I want files 1-5 on a left window and files 6-10 on a right window. Is this possible?
You can indeed have window-local argument lists:
:arglocal
:args file1 file2 file3 file4 file5
:vsplit
:arglocal
:args file6 file7 file8 file9 file10
This way, you can have one argument list (with files 1-5) for the left window, and another (with files 6-10) on a split right window. Commands like :next and :first in the windows are then independent of each other.
Buffers are global. It means that you can't have, say two vertical windows, housing two exclusive sets of buffers. The same applies to tabs, of course.
So, just use two instances: one on the left with files 1-5 and the other on the left with files 6-10.
Because the two instances are separated, you can safely use :n et :prev without "overflowing".
Tabs are viewports for windows, windows are viewports for buffers. You can view any buffer in any window. I would not call it impossible to create some workaround though: e.g. you can create commands :NEXT and :PREV via :command and make them iterate only over buffers that were opened in this window via :EDIT: like in the code below. But I would highly suggest use some plugin that aids in buffer switching like Command-T (I have nnoremap ,b :CommandTBuffer<CR> for buffer switching) and forget about highly inefficient :next/:previous commands.
function s:Edit(args)
let w:winbuflist=get(w:, 'winbuflist', [bufnr('%')])
execute 'edit' a:args
let buf=bufnr('%')
if index(w:winbuflist, buf) == -1
call add(w:winbuflist, bufnr('%'))
endif
endfunction
function s:Switch(direction)
let buf=bufnr('%')
let w:winbuflist=get(w:, 'winbuflist', [buf])
let idx=index(w:winbuflist, buf)
if idx==-1 || w:winbuflist ==# [buf]
if idx == -1
echohl ErrorMsg
echomsg 'Current buffer was not opened using :E or was opened in another window'
echohl None
endif
execute a:direction
return
elseif a:direction is# 'next'
let idx += 1
if idx == len(w:winbuflist)
let idx=0
endif
elseif a:direction is# 'previous'
let idx -= 1
if idx == -1
let idx=len(w:winbuflist)-1
endif
endif
execute 'buffer' w:winbuflist[idx]
endfunction
function s:RemoveBuf(buf)
for tab in range(1, tabpagenr('$'))
for win in range(1, tabpagewinnr(tab, '$'))
call filter(getwinvar(win, 'winbuflist', []), 'v:val isnot '.a:buf)
endfor
endfor
endfunction
augroup BufWinList
autocmd! BufWipeout * :call s:RemoveBuf(+expand('<abuf>'))
augroup END
" \/\/\/\/\/\/\/ Warning: this is not a completion option. It also
" \/\/\/\/\/\/\/ makes command do the expansion of its arguments.
command -complete=file -nargs=? -bar EDIT :call s:Edit(<q-args>)
command -nargs=0 -bar NEXT :call s:Switch('next')
command -nargs=0 -bar PREV :call s:Switch('previous')
It looks that you just need to perform several splits.
Split to 2 vertical windows: Ctrl-w v
In each window: Ctrl-w s (repeat it 4 times, to get 5 buffers)
You can move between windows with Ctrl-w j / Ctrl-w k
The :split command takes a file name, and opens that in a new, horizontally split window. (But you can also first just :split / <C-W>s / <C-W>v, and then :edit / :next another file.) Prepend :vertical (or shorter :vsplit) for vertical splitting. With this, you can create your desired layout.
To focus a different window, there are many mappings that start with Ctrl + W, e.g. <C-w>j to go to the window below. See :help CTRL-W for the full list.

Is it possible to configure behaviour of NERDTreeToggle to use NERDTreeFind when opening?

Is it possible to configure NERDTree so that :NERDTreeToggle acts like :NERDTreeFind if a buffer is not open (instead of the default :NERDTree) ?
Failing that, would it be possible to create a mapping/small script that could check the visibility of the NERDTree window and if open close it, if closed invoke NERDTreeFind ?
I looked at the NERDTree documentation to find if the visibility of the NERDTree window was open, but could not find it.
You can set let NERDTreeQuitOnOpen=1 to close the tree when you select a file, and create a mapping for find:
nmap <leader>p :NERDTreeFind<CR>
there is a function, which may help you to distinguish if the NERDTree is opened.
nerdtree#isTreeOpen()
you could test a little bit with :echom nerdtree#isTreeOpen() when you opened/closed the Nerdtree.
I'm now learning to use Vim too and had the same desire to make NERDTreeToggle to use NERDTreeFind when opening. After some digging/googling, I had a go at writing a simple Vim script below, and it seems to work for me! :]
function! ToggleNERDTreeFind()
if g:NERDTree.IsOpen()
execute ':NERDTreeClose'
else
execute ':NERDTreeFind'
endif
endfunction
And I just bound the above function to a shortcut key to use for both finding/closing NERDTree. Hope this helps.
nnoremap <leader>f :call ToggleNERDTreeFind()<CR>
small improvment of the function provided by thomaswhyyou which also works
if current buffer is empty:
function! ToggleNERDTreeFind()
if g:NERDTree.IsOpen()
execute ':NERDTreeClose'
else
if bufname('%') == ''
execute ':NERDTree'
else
execute ':NERDTreeFind'
endif
endif
endfunction

In VimScript, How to test if a window is the last window

I'v made a mapping:
nmap: <ESC><ESC> :close<CR>
But this mapping can't close the last openned window
So I plan to write a function to test if the current window is the last openned window
Thanks in advance
I use the following to tell if the quickfix windows is the only remaining if so exiting without prompt.
if winbufnr(2) == -1
quit!
endif
Here is my entire code from my .vimrc:
autocmd BufEnter * call MyLastWindow()
function! MyLastWindow()
if &buftype=="quickfix"
" if this window is last on screen quit without warning
if winbufnr(2) == -1
quit!
endif
endif
endfunction
You could modify it to simply do a close if its not the last window and do a quit! if it is. Obviously calling it from your mapping instead of from an autocmd.
Why not just use:
nmap: <ESC><ESC> :quit<CR>
I see it's been mentioned in the comments, but this deserves to be presented as an answer: winnr('$'). From :help winnr()
The result is a Number, which is the number of the current window. The top window has number 1. When the optional argument is "$", the number of the last window is returned (the window count).
So you could do,
if winnr('$') == 1
" only one window
endif
It has always surprised me that there isn't a native way of finding such information. I have a function that returns the number of open windows by crudely counting them using the windo command:
fun! NumWindows()
let num_wins = 0
windo let num_wins += 1
return num_wins
endfun
So you have reached the last window when NumWindows() == 1.
(I think I may have stolen the windo idea from another thread, but I'm afraid I can't remember which.)

Resources