How do you exit vimdiff mode in vim, specifically, for Fugitive? - vim

I am using vim with the fugitive extension. It has a :Gdiff command which brings you into vimdiff mode, but what is the right/quick way to close/quit vimdiff mode?
I.e., let's say I am editing the file FooBar.txt under Git repository. I fire up :Gdiff, review my changes in vimdiff, and then I want to get back and continue editing FooBar.txt or any other file :)
UPDATE1: I'm going to give these quick combos a try next working day :)
"vimdiff current vs git head (fugitive extension)
nnoremap <Leader>gd :Gdiff<cr>
"switch back to current file and closes fugitive buffer
nnoremap <Leader>gD :diffoff!<cr><c-w>h:bd<cr>
UPDATE2: My current mappings (closes diff window only!)
"vimdiff current vs git head (fugitive extension)
nnoremap <Leader>gd :Gdiff<cr>
"switch back to current file and closes fugitive buffer
nnoremap <Leader>gD <c-w>h<c-w>c
Also, please help me decide if the following should be an anwser: https://stackoverflow.com/a/15975201/275980

You can execute windo set nodiff noscrollbind and then close the second window.
Update: there is a diffoff command. Use windo diffoff, not what I wrote in previous line.

According to: https://github.com/tpope/vim-fugitive/issues/36
Close the other window. The easiest way to do this if you haven't shifted focus to it is <C-W><C-O>, which means "make this Window the Only window."

I had no luck with diffoff, but I just learned that :Gedit with no argument will bring you back to the working-directory version of the file, as opposed to some earlier version you were reviewing.
And as q (no need for :q) will close the diff sidebar, you can do q followed by :Gedit to get rid of the sidebar and then go back to the current version of the file.

None of the above solutions worked for me. Ended up doing this instead:
nnoremap <Leader>D :Gedit<CR><C-w>h :q<CR><C-w>k

This works fine for me, combining some of the existing ideas here:
function! MyCloseDiff()
if (&diff == 0 || getbufvar('#', '&diff') == 0)
\ && (bufname('%') !~ '^fugitive:' && bufname('#') !~ '^fugitive:')
echom "Not in diff view."
return
endif
" close current buffer if alternate is not fugitive but current one is
if bufname('#') !~ '^fugitive:' && bufname('%') =~ '^fugitive:'
if bufwinnr("#") == -1
b #
bd #
else
bd
endif
else
bd #
endif
endfunction
nnoremap <Leader>gD :call MyCloseDiff()<cr>

An alternative to <C-W><C-O>, if you have multiple windows, would be move to the other diff window and do <C-W>c, which close only one window.
If you close the wrong diff window do a :Gedit
Be careful and don't confuse <C-W>c with <C-W><C-C>

I've found a simple solution for this. You can check it here: https://gist.github.com/radmen/5048080
" Simple way to turn off Gdiff splitscreen
" works only when diff buffer is focused
if !exists(":Gdiffoff")
command Gdiffoff diffoff | q | Gedit
endif

Check the vimdiff toggling between diffthis and diffoff here
at this page.
The code:
nnoremap <silent> <Leader>df :call DiffToggle()<CR>
function! DiffToggle()
if &diff
diffoff
else
diffthis
endif
:endfunction

Method 1:
open a compare by:
:windo diffthis
close it by:
:windo diffoff
Method 2:
I recommend just using the most simple command: :q<CR>
when you want to do it quickly, add the mapping:
" Set mapleader
let mapleader = ","
let g:mapleader = ","
and
" Quickly close the current window
nnoremap <leader>q :q<CR>
It works well for me. Exit vimdiff just by ,q, because normally your cursor in the old file.

this is what I have to leave the vimdiff windows after using :Gdiff
nnoremap gD :q!<CR> :Gedit!<CR>

noremap <leader>do :diffoff \| windo if &diff \| hide \| endif<cr>
Quite diff mode and close other diff windows. (Note: fugitive will auto delete its hidden buffers.)

My function will work both from diff window and file window. But probably won't handle itself with multiple diffs opened. For that you'll need to use fugitive#buffer(n).path() to scan and match.
command! Gdiffoff call Gdiffoff()
function! Gdiffoff()
let diffbufnr = bufnr('^fugitive:')
if diffbufnr > -1 && &diff
diffoff | q
if bufnr('%') == diffbufnr | Gedit | endif
setlocal nocursorbind
else
echo 'Error: Not in diff or file'
endif
endfunction
Add a key binding:
nnoremap <silent> <leader>gD :Gdiffoff<CR>

Yet another way. What I have in fugitive.vim - first save some info (s:gitbufname) when diff starts:
function! s:Diff(vert,...) abort
call sy#toggle()
let s:startcol = winwidth(0)
let &columns=(winwidth(0) * 2 - 20)
...
if getwinvar('#', '&diff')
let s:gitbufname = bufname("%")
wincmd p
call feedkeys(winnr."\<C-W>w", 'n')
endif
...
endfunction
and later when leaving the buffer switch window to the saved buffer and restore:
augroup fugitive_diff
autocmd!
autocmd BufWinLeave *
\ if s:can_diffoff(+expand('<abuf>')) && s:diff_window_count() == 2 |
\ if exists('s:gitbufname') && winnr() != bufwinnr(s:gitbufname) |
\ let nr = bufnr("%") | exe bufwinnr(s:gitbufname).'wincmd w' | exe 'buf'.nr |
\ endif |
\ call s:diffoff_all(getbufvar(+expand('<abuf>'), 'git_dir')) |
\ call sy#toggle() |
\ call airline#load_theme() | call airline#update_statusline() |
\ let &columns=s:startcol |
\ endif
...

Was using the code below based on https://stackoverflow.com/a/15113951/10999673 :
if !exists(":Gdiffoff")
command Gdiffoff bw! fugitive://*
endif
but it gave me an error "E93: more than one match for ..." in a 3 way diff, so i instead used the answer from https://stackoverflow.com/a/4867969/10999673 and finally have this:
function! GetBufferList()
return filter(range(1,bufnr('$')), 'buflisted(v:val)')
endfunction
function! GetMatchingBuffers(pattern)
return filter(GetBufferList(), 'bufname(v:val) =~ a:pattern')
endfunction
function! WipeMatchingBuffers(pattern)
let l:matchList = GetMatchingBuffers(a:pattern)
let l:count = len(l:matchList)
if l:count < 1
echo 'No buffers found matching pattern ' . a:pattern
return
endif
if l:count == 1
let l:suffix = ''
else
let l:suffix = 's'
endif
exec 'bw ' . join(l:matchList, ' ')
echo 'Wiped ' . l:count . ' buffer' . l:suffix . '.'
endfunction
command! -nargs=1 Gdiffoff call WipeMatchingBuffers('fugitive://')
I just tweaked, copied and pasted the code into my .vimrc

Running :Gwrite after merging to your satisfaction will close the other two diff panes in addition to updating the git cache to mark the file as merged.

Related

Automatically Quit Vim if NERDTree and TagList are the only 2 Buffers Left

I have this code taken from another StackOverflow user Conner, from this question Automatically quit Vim if NERDTree and TagList are the last and only buffers
(There wasn't an option for me to comment on that question, so my only option was to ask a new one).
The question is: How do I close Vim editor (in Linux Mint) if only NERDTree and TagList are the only two buffers left?
The answer provided was:
" If only 2 windows left, NERDTree and Tag_List, close vim or current tab
fun! NoExcitingBuffersLeft()
if winnr("$") == 3
let w1 = bufname(winbufnr(1))
let w2 = bufname(winbufnr(2))
let w3 = bufname(winbufnr(3))
if (exists(":NERDTree")) && (w1 == "__Tag_List__" || w2 == "__Tag_List__" || w3 == "__Tag_List__")
if tabpagenr("$") == 1
exec 'qa'
else
exec 'tabclose'
endif
endif
endif
endfun
autocmd BufWinLeave * call NoExcitingBuffersLeft()
But that does not work properly. Is closes whenever I close the last "exciting" buffer (one that is non-NERDTree or non-TagList), but it also closes whenever I try to open a new file from NERDTree (by double clicking on a filename in the "explorer").
Honestly, I do not understand this code too well. I have tried to mess around with it but I couldn't get the results I would like.
How do I alter this code to not close when I open a new file from NERDTree explorer?
Thank you, Conner, and the rest of the community!
From the Taglist manual, put on your .vimrc file
let Tlist_Exit_OnlyWindow=1
I don't use NERDTree, but you may succeed with the following
autocmd bufenter * if (winnr("$") == 1 && exists("b:NERDTreeType") && b:NERDTreeType == "primary") | q | endif

Automatically quit Vim if NERDTree and TagList are the last and only buffers

Basically, my .vimrc starts TagList and NERDTree when Vim is launched, as splits on the left and on the right of the normal file buffer.
I want to close Vim when, closing the last buffer/tab, TagList and NERDTree splits are the only remained. I'm already using vim-nerdtree-tabs and it works great when NERDTree is the only and last buffer open.
I'm aware that such topic has been discussed here on StackOverflow but I cannot find anything related to both NERDTree and TagList.
Thanks
let Tlist_Exit_OnlyWindow = 1
will close Tag_list window if it's the last window, look at http://vim-taglist.sourceforge.net/manual.html for more infomation about Tlist_Exit_OnlyWindow, I'm not sure if you are looking for this, if not, please delete my answer.
Something like... (untested)
fun! NoExcitingBuffersLeft()
if tabpagenr("$") == 1 && winnr("$") == 2
let window1 = bufname(winbufnr(1))
let window2 = bufname(winbufnr(2))
if (window1 == t:NERDTreeBufName || window1 == "__Tag_List__") &&
(window2 == t:NERDTreeBufName || window2 == "__Tag_List__")
quit
endif
endif
endfun
then tie that function to an autocommand...
au WinEnter * call NoExcitingBuffersLeft()<cr>
I don't use either of those plugins, so you may need to adjust the t:NERDTreeBufName and __Tag_List__.
Improving on Conner's idea, I've made a functional solution here.
" If only 2 windows left, NERDTree and Tag_List, close vim or current tab
fun! NoExcitingBuffersLeft()
if winnr("$") == 3
let w1 = bufname(winbufnr(1))
let w2 = bufname(winbufnr(2))
let w3 = bufname(winbufnr(3))
if (exists(":NERDTree")) && (w1 == "__Tag_List__" || w2 == "__Tag_List__" || w3 == "__Tag_List__")
if tabpagenr("$") == 1
exec 'qa'
else
exec 'tabclose'
endif
endif
endif
endfun
autocmd BufWinLeave * call NoExcitingBuffersLeft()
Need vim 7.0+ for the BufWinLeave event.
Closes the tab if more than one tab is open, otherwise quits vim.
This way, the auto-command is tied to when you close the last window that's not NERDTree or Tag_List, rather than upon entering one of the two windows.
This is nice extendable solution. To validate against other plugins/window types just add them to the regex check.
function! s:CloseAddons()
for w in range(1, winnr('$'))
if bufname(winbufnr(w)) !~# '__Tagbar\|NERD_tree_\|coc-explorer'
\ && getbufvar(winbufnr(w), "&buftype") !=? "quickfix"
return
endif
endfor
if tabpagenr('$') ==? 1
execute 'quitall'
else
execute 'tabclose'
endif
endfunction

Vim - show diff on commit in mercurial;

In my .hgrc I can provide an editor or a command to launch an editor with options on commit.
I want to write a method or alias that launches $ hg ci, it would not only open up message in Vim, but also would split window and there print out $ hg diff.
I know that I can give parameters to vim by using +{command} option. So launching $ vim "+vsplit" does the split but any other options goes to first opened window. So I assume i need a specific function, yet I have no experience in writing my own Vim scripts.
The script should:
Open new vertical split with empty buffer (with vnew possibly)
In empty buffer launch :.!hg diff
Set empty buffer file type as diff :set ft=diff
I've written such function:
function! HgCiDiff()
vnew
:.!hg diff
set ft=diff
endfunction
And in .hgrc I've added option: editor = vim "+HgCiDiff()"
It kind of works, but I would like that splited window would be in right side (now it opens up in left) and mercurial message would be focused window. Also :wq could be setted as temporary shortcut to :wq<CR>:q! (having an assumption that mercurial message is is focused).
Any suggestions to make this a bit more useful and less chunky?
UPDATE: I found vim split guide so changing vnew with rightbelow vnew opens up diff on the right side.
So I expanded my own code:
function! HgCiDiff()
"In .hgrc editor option I call vim "+HgCiDiff()"
"It opens new split with diff inside
rightbelow vnew
:.!hg diff
set ft=diff
saveas! /tmp/hgdiff.txt
execute "normal \<c-w>w"
endfunction
Yet It missed :wq mapping as :wqa, yet using :wqa is not that hard.
Sources of my vimrc is located here: http://hg.jackleo.info/vim-configs/src/08df5cb9d143/vimrc
Sources of my hgrc is located here: http://hg.jackleo.info/home-configs/src/22f5fb47a7d2/.hgrc
Update: as suggested by Randy Morris I updated my code and now it works just as I wanted. Thanks! Also added few extra features as the time went by.
function! HgCiDiff()
"In .hgrc editor option I call vim "+HgCiDiff()"
"It opens new split with diff inside
rightbelow vnew
setlocal buftype=nofile
:.!hg diff
setlocal ft=diff
wincmd p
setlocal spell spelllang=en_us
cnoremap wq wqa
cnoremap q qa
start
endfunction
Edit
Hmm I think this might not be what you are after on second reading. I understand you want a multi-file (unified) diff. I'd really use a hg-aware UI tool and a separate vim editor for the commit message. Sorry about that.
I'll leave the 'original' response stand in case you didn't know VCSCommand + Hg + Vim yet:
My weapon of choice is to abstract it all away with
vcscommand.vim : CVS/SVN/SVK/git/hg/bzr integration plugin
You would
:VCSVimDiff
to diffsplit against the repo version (also with Leadercv)
:VCSVimDiff <revid>
to compare against a specific revision.
My solution consists of three vim files. It doesn't require hg configuration changes, and only shows the diff for the files you're committing, if you've used hg commit file1 file2:
~/.vim/ftdetect/hg.vim
au BufRead,BufNewFile /tmp/hg-editor-*.txt set filetype=hg
~/.vim/syntax/hg.vim
" Vim syntax file
" Language: hg commit file
" Maintainer: Marius Gedminas <marius#gedmin.as>
" Filenames: /tmp/hg-editor-*.txt
" Last Change: 2012 July 8
" Based on gitcommit.vim by Tim Pope
if exists("b:current_syntax")
finish
endif
syn case match
syn sync minlines=50
if has("spell")
syn spell toplevel
endif
syn match hgComment "^HG: .*"
hi def link hgComment Comment
let b:current_syntax = "hg"
~/.vim/ftplugin/hg.vim
" Show diff while editing a Mercurial commit message
" Inspired by http://stackoverflow.com/questions/8009333/vim-show-diff-on-commit-in-mercurial
" and Michael Scherer' svn.vim
function! HgCiDiff()
let i = 0
let list_of_files = ''
while i <= line('$') && getline(i) != 'HG: --'
let i = i + 1
endwhile
while i <= line('$')
let line = getline(i)
if line =~ '^HG: \(added\|changed\)'
let file = substitute(line, '^HG: \(added\|changed\) ', '', '')
let file = "'".substitute(file, "'", "'\''", '')."'"
let list_of_files = list_of_files . ' '.file
endif
let i = i + 1
endwhile
if list_of_files == ""
return
endif
pclose
new
setlocal ft=diff previewwindow bufhidden=delete nobackup noswf nobuflisted nowrap buftype=nofile
silent exec ':0r!hg diff ' . list_of_files
setlocal nomodifiable
goto 1
redraw!
" nooo idea why I have to do this
syn enable
endfunction
call HgCiDiff()
Here's my variation based on Marius Gedminas and JackLeo's versions:
function! HgCiDiff()
" find files that were changed (not interested in added or deleted)
let changed_files = []
let pattern = '\vHG: changed \zs(.+)\ze'
while search("HG: changed", "W") > 0
let line_text = getline(line("."))
call add(changed_files, matchstr(line_text, pattern))
endwhile
let diff_cmd = "hg diff " . join(changed_files, " ")
" Reset cursor to beginning of the buffer
call cursor(1, 1)
rightbelow vnew
setlocal buftype=nofile
let diff_output = system(diff_cmd)
call append(0, split(diff_output, "\n"))
" Reset cursor to beginning of the buffer
call cursor(1, 1)
setlocal ft=diff
wincmd p
setlocal spell spelllang=en_us
cnoremap wq wqa
cnoremap q qa!
startinsert
endfunction

Vim autocommand trigger on opening "nothing"

I want vim to open up the :Explorer when no file is opened or created. Eg. when I call vim without any options.
calling vim newfile.txt should still behave the normal way though.
How would I go about doing this? I can't seem to find the correct autocmd for it.
If you want to do this for vim invocation only, the best way is to use argc():
autocmd VimEnter * :if argc() is 0 | Explore | endif
argc() function returns a number of filenames specified on command-line when vim was invoked unless something modified arguments list, more information at :h argc().
Found the answer myself:
"open to Explorer when no file is opened
function! TabIsEmpty()
" Remember which window we're in at the moment
let initial_win_num = winnr()
let win_count = 0
" Add the length of the file name on to count:
" this will be 0 if there is no file name
windo let win_count += len(expand('%'))
" Go back to the initial window
exe initial_win_num . "wincmd w"
" Check count
if win_count == 0
" Tab page is empty
return 1
else
return 0
endif
endfunction
" Test it like this:
" echo TabIsEmpty()
function! OpenExplorer()
if (TabIsEmpty())
:Explore
end
endfunction
The greatest part of this code was taken from this question.

How to delete multiple buffers in Vim?

Assuming I have multiple files opened as buffers in Vim. The files have *.cpp, *.h and some are *.xml. I want to close all the XML files with :bd *.xml. However, Vim does not allow this (E93: More than one match...).
Is there any way to do this?
P.S. I know that :bd file1 file2 file3 works. So can I somehow evaluate *.xml to file1.xml file2.xml file3.xml?
You can use <C-a> to complete all matches. So if you type :bd *.xml and then hit <C-a>, vim will complete the command to :bd file1.xml file2.xml file3.xml.
:3,5bd[elete]
Will delete buffer range from 3 to 5 .
You also can use alternatively use:
:.,$-bd[elete] " to delete buffers from the current one to last but one
:%bd[elete] " to delete all buffers
You can use this.
:exe 'bd '. join(filter(map(copy(range(1, bufnr('$'))), 'bufname(v:val)'), 'v:val =~ "\.xml$"'), ' ')
It should be quite easy to add it to a command.
function! s:BDExt(ext)
let buffers = filter(range(1, bufnr('$')), 'buflisted(v:val) && bufname(v:val) =~ "\.'.a:ext.'$"')
if empty(buffers) |throw "no *.".a:ext." buffer" | endif
exe 'bd '.join(buffers, ' ')
endfunction
command! -nargs=1 BDExt :call s:BDExt(<f-args>)
Try the script below. The example is for "txt", change it as needed, e.g. to "xml".
Modified buffers are not deleted. Press \bd to delete the buffers.
map <Leader>bd :bufdo call <SID>DeleteBufferByExtension("txt")
function! <SID>DeleteBufferByExtension(strExt)
if (matchstr(bufname("%"), ".".a:strExt."$") == ".".a:strExt )
if (! &modified)
bd
endif
endif
endfunction
[Edit]
Same without :bufdo (as requested by Luc Hermitte, see comment below)
map <Leader>bd :call <SID>DeleteBufferByExtension("txt")
function! <SID>DeleteBufferByExtension(strExt)
let s:bufNr = bufnr("$")
while s:bufNr > 0
if buflisted(s:bufNr)
if (matchstr(bufname(s:bufNr), ".".a:strExt."$") == ".".a:strExt )
if getbufvar(s:bufNr, '&modified') == 0
execute "bd ".s:bufNr
endif
endif
endif
let s:bufNr = s:bufNr-1
endwhile
endfunction
I too had a need for this functionality all the time. This is the solution I have in my vimrc.
function! GetBufferList()
return filter(range(1,bufnr('$')), 'buflisted(v:val)')
endfunction
function! GetMatchingBuffers(pattern)
return filter(GetBufferList(), 'bufname(v:val) =~ a:pattern')
endfunction
function! WipeMatchingBuffers(pattern)
let l:matchList = GetMatchingBuffers(a:pattern)
let l:count = len(l:matchList)
if l:count < 1
echo 'No buffers found matching pattern ' . a:pattern
return
endif
if l:count == 1
let l:suffix = ''
else
let l:suffix = 's'
endif
exec 'bw ' . join(l:matchList, ' ')
echo 'Wiped ' . l:count . ' buffer' . l:suffix . '.'
endfunction
command! -nargs=1 BW call WipeMatchingBuffers('<args>')
Now, I can just do :BW regex (e.g. :BW \.cpp$ and wipe all matching buffers that have match that pattern in their pathname.
If you want to delete rather than wipe, you can of course replace exec 'bw ' . join(l:matchList, ' ') with exec 'bd ' . join(l:matchList, ' ')
TAB will only autocomplete one file for you as of Vim 7.4.282
use <c-a> to autocomplete all files.
You can just use:
bd filetype
then just use <c-a> to facilitate the completion of all open files of specified filetype.
for example, you have 1.xml, 2.xml, 3.xml, and 4.xml,
you can do:
bd xml
then press <c-a>
vim will autocomplete for you as follow:
bd 1.xml 2.xml 3.xml 4.xml
you can just press enter to complete the command.
if you have made changes in one of the files mentioned above, do remember to do:
bd! xml
Very simply: use the :bd[elete] command. For example, :bd[elete] buf#1 buf#5 buf#3 will delete the buffers 1, 3, and 5.

Resources