Taking a quick look at DiffOrig, then switching back - vim

I've discovered this great command from the documentation:
command DiffOrig vert new | set bt=nofile | r # | 0d_ | diffthis
\ | wincmd p | diffthis
So I've come up with this:
command DiffOrig vert new | set bt=nofile | r # | 0d_ | diffthis | wincmd p | diffthis | wincmd p
map <Leader>do :DiffOrig<cr>
map <leader>dc :q<cr>:diffoff!<cr>
The problem is, when I se \dc, it will jump back to the beginning of the file, not where I left it prior to issuing \do. How to fix this?
Please try this yourself, see what the problems are and how to fix them. And tell me how to fix them too :-)

You could try:
command DiffOrig let g:diffline = line('.') | vert new | set bt=nofile | r # | 0d_ | diffthis | :exe "norm! ".g:diffline."G" | wincmd p | diffthis | wincmd p
nnoremap <Leader>do :DiffOrig<cr>
nnoremap <leader>dc :q<cr>:diffoff<cr>:exe "norm! ".g:diffline."G"<cr>

You can :q in the window you want to close, and then :diffoff to turn off the diff formatting in the remaining window. Not sure if it can be done in one command.

To close the diff windows and go back to the file you were editing, you could try adding this to your .vimrc file:
if !exists(":DiffOff")
command DiffOff diffoff <bar> only
endif
Then enter :DiffOff to close the diff windows.

Related

Vim Error E20 'Mark not set' when running BuffWrite Command

I set up an auto run script in my vimrc to condense any block of 3 or more empty newlines down to 3 newlines. I set a mark so after the script executes, I retain my cursor position but I'm getting an E20 Mark not set error when the cursor is within an area that is being removed.
How can I fix this issue/silence the error when this happens?
" .vimrc file:
autocmd BufWrite * mark ' | silent! %s/\n\{3,}/\r\r\r/e | norm''
You could replace your marks with winsaveview() and winrestview().
autocmd BufWrite * let w:winview = winsaveview() | ... | if exists('w:winview') | call winrestview(w:winview) | endif
Also silence the normal command:
autocmd BufWrite * mark ' | silent! %s/\n\{3,}/\r\r\r/e | silent! exe "norm! ''"

Use of spaces and \ characters in a conditional vim expression

I tried to implement a version of Ingo's answer from here
In my _vimrcfile I have the following:
:autocmd BufEnter,FileType * if &ft ==# 'sql' colorscheme SummerFruit256 | elseif &ft ==? 'python' | colorscheme IntelliJ | else | colorscheme pyte | endif
I've taken the \ characters out of Ingo's answer and tried to do it on one line but I still get an E15.
How are spaces treated in these vim scripts? and why doesn't this script work?
Ingo's Answer is copied below.
:autocmd BufEnter,FileType *
\ if &ft ==# 'c' || &ft ==# 'cpp' | colorscheme darkblue |
\ elseif &ft ==? 'r' | colorscheme desert |
\ else | colorscheme default |
\ endif
The \ characters tell vim that the command should be on the same line as the previous one (if its at the beginning of the line.) This allows long commands to be more readable.
Spaces are treated as separators between inputs to the commands so it doesn't matter how many there are.
In your command I believe you are missing a pipe | after the first if.
... if &ft ==# 'sql' colorscheme SummerFruit256 | ...
Should be
... if &ft ==# 'sql' | colorscheme SummerFruit256 | ...
So the whole command would be
:autocmd BufEnter,FileType * if &ft ==# 'sql' | colorscheme SummerFruit256 | elseif &ft ==? 'python' | colorscheme IntelliJ | else | colorscheme pyte | endif
Note: Since these commands are in your vimrc file, You don't need to the leading :

Setting the cursor to a vertical thin line in vim

I am trying to set the cursor in insert mode to be a thin vertical line and I am unable to. I have tried this in my .vimrc file:
set guicursor+=i:ver100-iCursor
It does not set the cursor to a vertical bar on insert mode.
What am I missing and how do I do this?
This code in my /home/el/.vimrc worked for my console:
if $TERM_PROGRAM =~ "iTerm"
let &t_SI = "\<Esc>]50;CursorShape=1\x7" " Vertical bar in insert mode
let &t_EI = "\<Esc>]50;CursorShape=0\x7" " Block in normal mode
endif
Which does this for me:
Source:
https://hamberg.no/erlend/posts/2014-03-09-change-vim-cursor-in-iterm.html
For gnome terminal version>3.15
Add this to your ~/.vimrc.
if has("autocmd")
au VimEnter,InsertLeave * silent execute '!echo -ne "\e[2 q"' | redraw!
au InsertEnter,InsertChange *
\ if v:insertmode == 'i' |
\ silent execute '!echo -ne "\e[6 q"' | redraw! |
\ elseif v:insertmode == 'r' |
\ silent execute '!echo -ne "\e[4 q"' | redraw! |
\ endif
au VimLeave * silent execute '!echo -ne "\e[ q"' | redraw!
endif
You will get a block cursor in normal mode and a thin one in insert mode.
This did the trick:
set guicursor=i:ver25-iCursor
I had to reduce the 100 to 25
I use iTerm2 on mac and none of the above worked. Silly (vim and interface ain't right) but works. To switch between vertical bar or box. Profiles -> Open Profiles... -> Edit Profiles... -> Text

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

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.

How can I swap positions of two open files (in splits) in vim?

Assume I've got some arbitrary layout of splits in vim.
____________________
| one | two |
| | |
| |______|
| | three|
| | |
|___________|______|
Is there a way to swap one and two and maintain the same layout? It's simple in this example, but I'm looking for a solution that will help for more complex layouts.
UPDATE:
I guess I should be more clear. My previous example was a simplification of the actual use-case. With an actual instance:
How could I swap any two of those splits, maintaining the same layout?
Update! 3+ years later...
I put sgriffin's solution in a Vim plugin you can install with ease! Install it with your favorite plugin manager and give it a try: WindowSwap.vim
Starting with this:
____________________
| one | two |
| | |
| |______|
| | three|
| | |
|___________|______|
Make 'three' the active window, then issue the command ctrl+w J. This moves the current window to fill the bottom of the screen, leaving you with:
____________________
| one | two |
| | |
|___________|______|
| three |
| |
|__________________|
Now make either 'one' or 'two' the active window, then issue the command ctrl+w r. This 'rotates' the windows in the current row, leaving you with:
____________________
| two | one |
| | |
|___________|______|
| three |
| |
|__________________|
Now make 'two' the active window, and issue the command ctrl+w H. This moves the current window to fill the left of the screen, leaving you with:
____________________
| two | one |
| | |
| |______|
| | three|
| | |
|___________|______|
As you can see, the manouevre is a bit of a shuffle. With 3 windows, it's a bit like one of those 'tile game' puzzles. I don't recommand trying this if you have 4 or more windows - you'd be better off closing them then opening them again in the desired positions.
I made a screencast demonstrating how to work with split windows in Vim.
A bit late to the post, but came across this searching for something else. I wrote two functions awhile back to mark a window and then swap buffers between windows. This seems to be what you're asking for.
Just slap these in your .vimrc and map the functions how you see fit:
function! MarkWindowSwap()
let g:markedWinNum = winnr()
endfunction
function! DoWindowSwap()
"Mark destination
let curNum = winnr()
let curBuf = bufnr( "%" )
exe g:markedWinNum . "wincmd w"
"Switch to source and shuffle dest->source
let markedBuf = bufnr( "%" )
"Hide and open so that we aren't prompted and keep history
exe 'hide buf' curBuf
"Switch to dest and shuffle source->dest
exe curNum . "wincmd w"
"Hide and open so that we aren't prompted and keep history
exe 'hide buf' markedBuf
endfunction
nmap <silent> <leader>mw :call MarkWindowSwap()<CR>
nmap <silent> <leader>pw :call DoWindowSwap()<CR>
To use (assuming your mapleader is set to \) you would:
Move to the window to mark for the swap via
ctrl-w movement
Type \mw
Move to the window you want to swap
Type \pw
Voila! Swapped buffers without screwing up your window layout!
Take a look at :h ctrl-w_ctrl-x and/or :h ctrl-w_ctrl-r. These commands allow you to exchange or rotate windows in the current layout.
Edit: Actually, this will not work in this situation because it only will swap in the current column or row. You could instead go to each of the windows and select the target buffer, but that's pretty verbose.
Randy's correct in that CTRL-W x doesn't want to swap windows that aren't in the same column/row.
I've found that the CTRL-W HJKL keys are most useful when manipulating windows. They will force your current window out of its current location and tell it to occupy the entire edge indicated by the direction of the key you press. See :help window-moving for more details.
For your example above, if you start in window "one", this does what you want:
CTRL-W K # moves window "one" to be topmost,
# stacking "one", "two", "three" top to bottom
CTRL-W j # moves cursor to window "two"
CTRL-W H # moves window "two" to be leftmost,
# leaving "one" and "three" split at right
For convenience, you can assign the sequences you need to key mappings (see :help mapping).
I have a slightly enhanced version from sgriffin's solution, you can swap windows without using two commands, but with intuitive HJKL commands.
So here is how it goes:
function! MarkWindowSwap()
" marked window number
let g:markedWinNum = winnr()
let g:markedBufNum = bufnr("%")
endfunction
function! DoWindowSwap()
let curWinNum = winnr()
let curBufNum = bufnr("%")
" Switch focus to marked window
exe g:markedWinNum . "wincmd w"
" Load current buffer on marked window
exe 'hide buf' curBufNum
" Switch focus to current window
exe curWinNum . "wincmd w"
" Load marked buffer on current window
exe 'hide buf' g:markedBufNum
endfunction
nnoremap H :call MarkWindowSwap()<CR> <C-w>h :call DoWindowSwap()<CR>
nnoremap J :call MarkWindowSwap()<CR> <C-w>j :call DoWindowSwap()<CR>
nnoremap K :call MarkWindowSwap()<CR> <C-w>k :call DoWindowSwap()<CR>
nnoremap L :call MarkWindowSwap()<CR> <C-w>l :call DoWindowSwap()<CR>
Try to move your window by using capital HJKL in normal node, it is really cool :)
Building heavily on #sgriffin's answer, here's something even closer to what you're asking for:
function! MarkWindow()
let g:markedWinNum = winnr()
endfunction
function! SwapBufferWithMarkedWindow()
" Capture current window and buffer
let curWinNum = winnr()
let curBufNum = bufnr("%")
" Switch to marked window, mark buffer, and open current buffer
execute g:markedWinNum . "wincmd w"
let markedBufNum = bufnr("%")
execute "hide buf" curBufNum
" Switch back to current window and open marked buffer
execute curWinNum . "wincmd w"
execute "hide buf" markedBufNum
endfunction
function! CloseMarkedWindow()
" Capture current window
let curWinNum = winnr()
" Switch to marked window and close it, then switch back to current window
execute g:markedWinNum . "wincmd w"
execute "hide close"
execute "wincmd p"
endfunction
function! MoveWindowLeft()
call MarkWindow()
execute "wincmd h"
if winnr() == g:markedWinNum
execute "wincmd H"
else
let g:markedWinNum += 1
execute "wincmd s"
execute g:markedWinNum . "wincmd w"
execute "wincmd h"
call SwapBufferWithMarkedWindow()
call CloseMarkedWindow()
endif
endfunction
function! MoveWindowDown()
call MarkWindow()
execute "wincmd j"
if winnr() == g:markedWinNum
execute "wincmd J"
else
execute "wincmd v"
execute g:markedWinNum . "wincmd w"
execute "wincmd j"
call SwapBufferWithMarkedWindow()
call CloseMarkedWindow()
endif
endfunction
function! MoveWindowUp()
call MarkWindow()
execute "wincmd k"
if winnr() == g:markedWinNum
execute "wincmd K"
else
let g:markedWinNum += 1
execute "wincmd v"
execute g:markedWinNum . "wincmd w"
execute "wincmd k"
call SwapBufferWithMarkedWindow()
call CloseMarkedWindow()
endif
endfunction
function! MoveWindowRight()
call MarkWindow()
execute "wincmd l"
if winnr() == g:markedWinNum
execute "wincmd L"
else
execute "wincmd s"
execute g:markedWinNum . "wincmd w"
execute "wincmd l"
call SwapBufferWithMarkedWindow()
call CloseMarkedWindow()
endif
endfunction
nnoremap <silent> <Leader>wm :call MarkWindow()<CR>
nnoremap <silent> <Leader>ws :call SwapBufferWithMarkedWindow()<CR>
nnoremap <silent> <Leader>wh :call MoveWindowLeft()<CR>
nnoremap <silent> <Leader>wj :call MoveWindowDown()<CR>
nnoremap <silent> <Leader>wk :call MoveWindowUp()<CR>
nnoremap <silent> <Leader>wl :call MoveWindowRight()<CR>
Please let me know if the behavior doesn't match your expectations.
Also based on sgriffin's solution, go to the window you want to swap, press CTRL-w m, go to the window you want to swap with and press CTRL-w m again.
CTRL-w m is a poor mnemonic choice, so if anybody comes up with a better one, please edit this.
Also, I'd like to receive a feedback from the script aka "Window marked. Please repeat on target", however being a vimscript noob, I do not know how to do that.
All that said, the script works well as is
" <CTRL>-w m : mark first window
" <CTRL>-w m : swap with that window
let s:markedWinNum = -1
function! MarkWindowSwap()
let s:markedWinNum = winnr()
endfunction
function! DoWindowSwap()
"Mark destination
let curNum = winnr()
let curBuf = bufnr( "%" )
exe s:markedWinNum . "wincmd w"
"Switch to source and shuffle dest->source
let markedBuf = bufnr( "%" )
"Hide and open so that we aren't prompted and keep history
exe 'hide buf' curBuf
"Switch to dest and shuffle source->dest
exe curNum . "wincmd w"
"Hide and open so that we aren't prompted and keep history
exe 'hide buf' markedBuf
endfunction
function! WindowSwapping()
if s:markedWinNum == -1
call MarkWindowSwap()
else
call DoWindowSwap()
let s:markedWinNum = -1
endif
endfunction
nnoremap <C-w>m :call WindowSwapping()<CR>
The following approach may be convenient if functions are not available for some reason (f.e. it's not your vim).
Use :buffers command to find out id's of open buffers, navigate to desired window and use command like :b 5 to open a buffer (buffer number 5 in this case). Repeate two times and contents of windows are swapped.
I "invented" this method after several attempts to memorise ctrl-w-something sequences even for very simple layouts like one-two-three in original question.
Really cool, but my proposal for the mapping is to use ^W^J instead of J (because all of H J K L already have meanings), plus also I'd pull in the new buffer, because by the time you want to swap around you probably don't want to continue editing the buffer you are already on. Here goes:
function! MarkSwapAway()
" marked window number
let g:markedOldWinNum = winnr()
let g:markedOldBufNum = bufnr("%")
endfunction
function! DoWindowToss()
let newWinNum = winnr()
let newBufNum = bufnr("%")
" Switch focus to marked window
exe g:markedOldWinNum . "wincmd w"
" Load current buffer on marked window
exe 'hide buf' newBufNum
" Switch focus to current window
exe newWinNum . "wincmd w"
" Load marked buffer on current window
exe 'hide buf' g:markedOldBufNum
" …and come back to the new one
exe g:markedOldWinNum . "wincmd w"
endfunction
nnoremap <C-w><C-h> :call MarkSwapAway()<CR> <C-w>h :call DoWindowToss()<CR>
nnoremap <C-w><C-j> :call MarkSwapAway()<CR> <C-w>j :call DoWindowToss()<CR>
nnoremap <C-w><C-k> :call MarkSwapAway()<CR> <C-w>k :call DoWindowToss()<CR>
nnoremap <C-w><C-l> :call MarkSwapAway()<CR> <C-w>l :call DoWindowToss()<CR>
All of the above answers are great, unfortunately these solutions do not work well in combination with QuickFix or LocationList windows (I ran in this problem while trying to get the Ale error message buffer to work with this).
Solution
Therefore I added an extra line of code to close all these windows before doing the swap.
exe ':windo if &buftype == "quickfix" || &buftype == "locationlist" | lclose | endif'
The total code looking like;
" Making swapping windows easy
function! SwapWindowBuffers()
exe ':windo if &buftype == "quickfix" || &buftype == "locationlist" | lclose | endif'
if !exists("g:markedWinNum")
" set window marked for swap
let g:markedWinNum = winnr()
:echo "window marked for swap"
else
" mark destination
let curNum = winnr()
let curBuf = bufnr( "%" )
if g:markedWinNum == curNum
:echo "window unmarked for swap"
else
exe g:markedWinNum . "wincmd w"
" switch to source and shuffle dest->source
let markedBuf = bufnr( "%" )
" hide and open so that we aren't prompted and keep history
exe 'hide buf' curBuf
" switch to dest and shuffle source->dest
exe curNum . "wincmd w"
" hide and open so that we aren't prompted and keep history
exe 'hide buf' markedBuf
:echo "windows swapped"
endif
" unset window marked for swap
unlet g:markedWinNum
endif
endfunction
nmap <silent> <leader>mw :call SwapWindowBuffers()<CR>
Credits for the swap function to Brandon Orther
Why it is needed
The reason the swap functions don't work properly without removing all the QuickFix (QF) and LocationList(LL) windows first is because if the parent of the QF/LL buffer the get's hidden (and nowhere shown in a window), the QF/LL window coupled to it is removed. This isn't a problem in itself but when the window hides all the window numbers are reassigned and the swap is messed up since the saved number of the first marked window is does (potentially) not exist any more.
To put this inperspective:
First window mark
____________________
| one | -> winnr = 1 marked first g:markedWinNum=1
| | -> bufnr = 1
|__________________|
| two (QF window | -> winnr = 2
| coupled to one |
|__________________|
| three | -> winnr = 3
| | -> bufnr = 2
|__________________|
Second window mark
____________________
| one | -> winnr = 1 g:markedWinNum=1
| | -> bufnr = 1
|__________________|
| two (QF window | -> winnr = 2
| coupled to one) |
|__________________|
| three | -> winnr = 3 marked second curNum=3
| | -> bufnr = 2 curBuf=2
|__________________|
First buffer switch, window one is filled with the buffer of window three. Thus the QF window is removed since it has no parent window any more. This rearranges the windows numbers. Note that curNum (the number of the secondly selected window) is pointing to a window that does not exist any more.
____________________
| three | -> winnr = 1 g:markedWinNum=1
| | -> bufnr = 2
|__________________|
| three | -> winnr = 2 curNum=3
| | -> bufnr = 2 curBuf=2
|__________________|
So when switching the second buffer, it tries to select the curNum window, which does not exist any more. So it creates it and switches the buffer, resulting in one unwanted window to be open still.
____________________
| three | -> winnr = 1 g:markedWinNum=1
| | -> bufnr = 2
|__________________|
| three | -> winnr = 2
| | -> bufnr = 2
|__________________|
| one | -> winnr = 3 curNum=3
| | -> bufnr = 1 curBuf=2
|__________________|
Similar mark-window-then-swap-buffer approach, but also let you reuse last swapping.
function! MarkWindowSwap()
unlet! g:markedWin1
unlet! g:markedWin2
let g:markedWin1 = winnr()
endfunction
function! DoWindowSwap()
if exists('g:markedWin1')
if !exists('g:markedWin2')
let g:markedWin2 = winnr()
endif
let l:curWin = winnr()
let l:bufWin1 = winbufnr(g:markedWin1)
let l:bufWin2 = winbufnr(g:markedWin2)
exec g:markedWin2 . 'wincmd w'
exec ':b '.l:bufWin1
exec g:markedWin1 . 'wincmd w'
exec ':b '.l:bufWin2
exec l:curWin . 'wincmd w'
endif
endfunction
nnoremap ,v :call DoWindowSwap()<CR>
nnoremap ,z :call MarkWindowSwap()<CR>
You could also use a tiling window manager like X-monad

Resources