Is there a way to rebind :q in vim to a more complex command to prevent accidentally exiting vim?
What you want is :close. It acts like :q but won't let you close the last window:
http://vimdoc.sourceforge.net/htmldoc/windows.html#:close
You can set an alias for the q command to map to close:
cabbrev q <c-r>=(getcmdtype()==':' && getcmdpos()==1 ? 'close' : 'q')<CR>
Thanks #Paradoxial for this :cabbrev trick.
I know, I know, it is a very old question, but I had the same question today and I found this post first. I developed a short script to put in .vimrc
function! ConfirmQuit(writeFile)
if (a:writeFile)
if (expand('%:t')=="")
echo "Can't save a file with no name."
return
endif
:write
endif
if (winnr('$')==1 && tabpagenr('$')==1)
if (confirm("Do you really want to quit?", "&Yes\n&No", 2)==1)
:quit
endif
else
:quit
endif
endfu
cnoremap <silent> q<CR> :call ConfirmQuit(0)<CR>
cnoremap <silent> x<CR> :call ConfirmQuit(1)<CR>
I hope this helps someone.
What are you afraid of? Vim won't let you quit (without a ! command modifier, anyway) when you still have unsaved changes, so the only thing you'll potentially lose is the window position, size, and maybe taskbar position of GVIM.
Anyway, to override built-in commands like :q, you can use the cmdalias plugin, like this:
:Alias q if\ winnr('$')>1||tabpagenr('$')>1||confirm('Really\ quit?',\ "&OK\\n&Cancel")==1|quit|endif
This checks for the last window (:q does not necessarily exit Vim), and inserts a confirmation.
You can use something like this to remove the :q command:
:cabbrev q <c-r>=(getcmdtype()==':' && getcmdpos()==1 ? 'echo' : 'q')<CR>
This abbreviates q to echo in command mode, but doesn't allow the abbreviation to trigger if the q isn't in the first column. This way, edit q won't abbreviate to edit echo.
ConfirmQuit.vim : Provides a confirm dialog when you try to quit vim
http://www.vim.org/scripts/script.php?script_id=1072
I adapted this by using
autocmd bufenter c:/intranet/notes.txt cnoremap <silent> wq<cr> call ConfirmQuit(1)<cr>
As I only wanted this on this for a particular file
Using the coot/cmdalias_vim plugin, I effectively disabled the short, impulsive quit commands :q, :q!, and :wq. Hopefully this will slow me down and make me think about whether I really want to use :quit or, say, :bdelete. Here's a condensed version of the "autocmd section" of my .vimrc file:
if has("autocmd")
augroup VIMRC_aliases
au!
au VimEnter * CmdAlias wqu\%[it] write|quit
au VimEnter * CmdAlias q echo\ "Use\ :qu[it]\ instead\ of\ :q"
au VimEnter * CmdAlias q! echo\ "Use\ :qu[it]!\ instead\ of\ :q!"
au VimEnter * CmdAlias wq echo\ "Use\ :wqu[it]\ instead\ of\ :wq"
augroup END
endif
Related
folks!
I'm using vim as an IDE for writing code in bash, Python and C. Besides, I have a key map to execute my current buffer depending on the file type. Here is a responsible parts of my .vimrc:
...
autocmd FileType python call Python_source()
autocmd FileType sh call Bash_source()
...
" Read lw (lispwords) modelise from current buffer and pass it as command arguments
func! LWargs()
set lw=''
doautocmd BufRead
if len(&lw) > 0 && len(&lw) < 512
return ' ' . &lw
endif
return ''
endfunc
func! Python_source()
setlocal number cursorline
setlocal shiftwidth=2
setlocal foldmethod=indent
map <F9> :w \| :exe '!python' '%:p' . LWargs()<CR>
imap <F9> <Esc> :w \| :exe '!python' '%:p' . LWargs()<CR>
" Comments on Ctrl-C
map <C-C> :call ToggleComment('#')<CR>
imap <C-C> <Esc>:call ToggleComment('#')<CR>li
endfunc
func! Bash_source()
setlocal number cursorline
setlocal shiftwidth=4
map <F9> :w \| :!./%<CR>
imap <F9> <Esc> :w \| :!./%<CR>
map <C-C> :call ToggleComment('#')<CR>
imap <C-C> <Esc>:call ToggleComment('#')<CR>li
endfunc
...
So, when I press F9, the magic happen and my code is executed with arguments passed by the LWargs. The only problem is after the program exits, vim will jump to the beginning of the file forcing me to position cursor back the line I was working and making my life harder. Is there is any way to prevent vim from jumping around?
The issue is with the doautocmd BufRead command in your LWargs(), which resets the cursor position to the top of the file.
(At least in my case, the command used to restore cursor position upon opening Vim is the one causing the cursor to move. You can inspect the list from :autocmd BufEnter * to see if you can spot a similar command or another one that might be causing the cursor to move. Looking again, it turns out I was also getting that same rule from my Linux distribution...)
A good way to prevent this is to use winsaveview() to save the cursor location and the window view in general (which line is at the top, whether your window is scrolled right to a column when word wrap is off) and winrestview() to restore it.
Here's an updated LWargs() that won't move your cursor:
function! LWargs()
set lw=
let saved_view = winsaveview()
doautocmd BufRead
call winrestview(saved_view)
if len(&lw) > 0 && len(&lw) < 512
return ' ' . &lw
endif
return ''
endfunction
Note also that the correct syntax is set lw=, if you use set lw='' you'll be setting a two-character string with two single quotes.
" ----------------------------------------------------------------------------
" Functions
" ----------------------------------------------------------------------------
function! g:UltiSnips_Complete()
call UltiSnips#ExpandSnippet()
if g:ulti_expand_res == 0
if pumvisible()
return "\<c-n>"
else
call UltiSnips#JumpForwards()
if g:ulti_jump_forwards_res == 0
return "\<tab>"
endif
endif
endif
return ""
endfunction
"
" ----------------------------------------------------------------------------
" Autocmds
" ----------------------------------------------------------------------------
augroup relativenumber
autocmd InsertEnter,focusLost * :set norelativenumber
autocmd InsertLeave,focusGained * :set relativenumber
augroup END
autocmd BufEnter * exec "inoremap <silent> " . g:UltiSnipsExpandTrigger . " <c-r>=g:UltiSnips_Complete()<cr>"
The above code is what comprises the end of my .vimrc file. This function use to work but after some updates (either YouCompleteMe or UltiSnips, unsure) the only time these work is when I first open vim in a directory and then open a file from there. I think it has something to do with the autocmd, but to be honest I don't know where to even start. I've tried changing the autocmd event to BufRead but, unfortunately, that made no difference. Any help is appreciated, thanks!
EDIT: If you think there is a better place I could post this question or you need more details, please tell me! I'm happy to help.
It seems BufEnter runs before VimEnter. This is a problem since SnipMate puts up its mappings in VimEnter. So the very first buffer has the SnipMate mapping instead of your custom one. And when you switch to a new buffer the autocmd run again putting the mapping in place for the new buffer.
To fix this just make the BufEnter mapping a buffer local mapping. This means that the mapping won't get over written by the SnipMate mapping. And the buffer local mapping has precedence when vim looks to run different mappings.
autocmd BufEnter * exec "inoremap <buffer> <silent> " . g:UltiSnipsExpandTrigger . " <cr>=g:UltiSnips_Complete()<cr>"
There are a few corrections that I can suggest :
You don't need to put the mapping in an autocmd BufEnter *, if you want this mapping to be available everywhere, just add the
exec "inoremap <silent>" g:UltiSnipsExpandTrigger "<c-r>=g:UltiSnips_Complete()<cr>"`
From the looks of it, what you really need is an <expr> mapping, something like this should work better :
exec "inoremap <expr> <silent>" g:UltiSnipsExpandTrigger g:UltiSnips_Complete()
NOTE: exec takes in multiple args separated by spaces, if you want to just add a space between strings just add the space, you should use concatenation only when you want to avoid the space that exec would add automatically to it's space separated args.
Edit:
Updated the expression mapping so that <expr> must be the first argument.
Since g:UltiSnipsExpandTrigger is defined by UltiSnips plugin, it is not yet available / defined within your vimrc, it being sourced before all plugins. You should hence put this snippet of code in after/plugin/ultisnips_complete.vim. Then you shouldn't get the error.
I’ve installed the winmanager, NERDTree and BufExplorer plugins. Now, I have <F8> set to toggle the winmanager display, using the following code in my .vimrc:
" mapping for triggering winmanager plugin
nnoremap <silent> <F8> :if IsWinManagerVisible() <BAR>WMToggle<CR><BAR> else<BAR> WMToggle<CR>:q<CR> endif <CR><CR>
This works fine.
What I want to do is to have winmanager show up automatically if the filetype is .c or .cpp. I added this to my .vimrc:
autocmd FileType c,cpp nested "\<F8>"
but it does not work.
Any help? Thanks in advance!
<F8> is a normal-mode mapping, but :autocmd expects an Ex command on its right-hand side. You need to use :normal (without a ! here, to allow mappings to take effect), and :execute to interpret the special key code:
:autocmd FileType c,cpp nested execute "normal \<F8>"
But I think it is cleaner to avoid the additional redirection and duplicate the mapping's commands instead:
:autocmd FileType c,cpp nested if IsWinManagerVisible() |exe 'WMToggle'| else| exe 'WMToggle' | quit | endif
I have the following in my .vimrc:
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Open NERDTree by default
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
autocmd VimEnter * NERDTree
autocmd VimEnter * wincmd p
So,
% vim file.txt
opens NERDTree and focuses the cursor in the file.txt buffer. I make my edits, and hit :q on the buffer, and I'm left with . . . NERDTree. This is annoying.
I could use :qa to close all buffers, and exit vim, but I'm used to the :q trope. So I'm wondering if there's a way to detect that the only remaining buffer is NERDTree, and "unify" the two buffers, for purposes of :q
Edit
Ask and ye shall receive: https://github.com/scrooloose/nerdtree/issues#issue/21
A script to do exactly this has been posted on the NERDTree issue list. Checkout issue-21 on GitHub for nerdtree.
This leads to the single line command for your vimrc here:
autocmd bufenter * if (winnr("$") == 1 && exists("b:NERDTree") && b:NERDTree.isTabTree()) | q | endif
function! s:CloseIfOnlyControlWinLeft()
if winnr("$") != 1
return
endif
if (exists("t:NERDTreeBufName") && bufwinnr(t:NERDTreeBufName) != -1)
\ || &buftype == 'quickfix'
q
endif
endfunction
augroup CloseIfOnlyControlWinLeft
au!
au BufEnter * call s:CloseIfOnlyControlWinLeft()
augroup END
From my vimrc, based on a version from janus repo.
Enhancements: also close if only a quickfix window is left.
It uses the BufEnter autocommand instead, which is required for &bt to work properly.
An idea in need of implementation:
You could write a function which, when called, checks if the only buffer remaining (or perhaps the only non-help buffer, if you prefer) is a NERDTree buffer and, if so, deletes it (or just quits).
Then have an autocmd run it whenever a buffer is deleted / hidden / whatever actually happens when you :q (it shames me to admit I'm not entirely sure!).
You could :cabbrv q qa but I'd advise against that because you'll forget about it when you actually want q.
I like to do this: cmap bq :bufdo q<CR> to close all buffers with two keystrokes in command mode.
Is it possible to open NERDTree in every tab with pressing t or T in NERDTree, if yes, How?
autocmd VimEnter * NERDTree
autocmd BufEnter * NERDTreeMirror
edit: The above command seems to open the new tab in NERDTree's buffer. Instead use this as mentioned by wejrowski in the comment below :
autocmd BufWinEnter * NERDTreeMirror
I wrote a vim plugin that does this and also adds some goodies on top (i.e. keeps all trees in sync, ensures meaningful tab captions - not captions like 'NERD_tree_1' etc.).
It's here on Github: https://github.com/jistr/vim-nerdtree-tabs
autocmd VimEnter * NERDTree
autocmd BufEnter * NERDTreeMirror
autocmd VimEnter * wincmd w
This one is a little better than Dustin's one because it places the cursor directly on the file you are intending to edit for quick edits. Thanks dustin for the original example ^^
A better solution is to open NERDTree only if there are no command line arguments set.
" Open NERDTree in new tabs and windows if no command line args set
autocmd VimEnter * if !argc() | NERDTree | endif
autocmd BufEnter * if !argc() | NERDTreeMirror | endif
NERDTree is e.g. not helpful if you do a git commit or something similiar.
This is probably not the best way, but if you edit plugin/NERDTree.vim and change this:
exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTab ." :call <SID>openInNewTab(0)<cr>"
to this:
exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTab ." :call <SID>openInNewTab(0)<cr>:NERDTree<cr>"
it will alter the binding of 't' in the NERDTree view to first open the file and then open NERDTree. Note, that the NERDTree views will not keep in sync.
How about toggling it.
" in .vimrc
" NERDTree, Use F3 for toggle NERDTree
nmap <silent> <F3> :NERDTreeToggle<CR>
In OSX, you just need to fn-F3 to toggle NERDTree.
This problem was actually mentioned in the official Repository's Readme file including three situations related to opening NERDTree automatically:
How can I open a NERDTree automatically when vim starts up?
Stick this in your vimrc: autocmd vimenter * NERDTree
How can I open a NERDTree automatically when vim starts up if no files were specified?
Stick this in your vimrc:
autocmd StdinReadPre * let s:std_in=1
autocmd VimEnter * if argc() == 0 && !exists("s:std_in") | NERDTree | endif
Note: Now start vim with plain vim, not vim .
How can I open NERDTree automatically when vim starts up on opening a directory?
autocmd StdinReadPre * let s:std_in=1
autocmd VimEnter * if argc() == 1 && isdirectory(argv()[0]) && !exists("s:std_in") | exe 'NERDTree' argv()[0] | wincmd p | ene | endif
This window is tab-specific, meaning it's used by all windows in the tab. This trick also prevents NERDTree from hiding when first selecting a file.