Merge changes using vimdiff - vim

In my case, I have two files file1 and file2. Using vimdiff, I want to merge the changes as follows:
In first difference, place line from file1 above line from file2. It means difference such as Listing 2 in file2 and List 2 should be List 2 followed by Listing 2 in the merged file.
Reverse case in another change.
Snapshot is shown below.
How can we achieve this using vimdiff?

You can use the following basic commands to merge:
do - Get changes from other window into the current window.
dp - Put the changes from current window into the other window.
]c - Jump to the next change.
[c - Jump to the previous change.
zo - Open folded lines.
zc - Close folded lines.
zr - Unfold both files completely.
zm - Fold both files completely.
Ctrlww - change window.
:only | wq - quit other windows, write and quit.
Quirks to watch for
Both do and dp work if you are on a block of change (or just one line under a single line of change) in Normal mode, but not in Visual mode.
The undo command will only work in the buffer that was changed, so if you use dp and change your mind, you need to switch to the other buffer to undo.
:diffupdate will re-scan the files for changes (Vim can get confused, and show bogus stuff).
Visual mode and finer grained control
When selecting lines of text in Visual mode, you must use the normal commands:
:'<,'>diffget and
:'<,'>diffput.
For example:
Enter Visual mode and mark some text/lines.
Then type :diffput to push the selected lines to the other file or :diffget to get the selected lines from the other file.
To belabor the point: This means that if there is a block of changes consisting of multiple lines, then selecting a subset of lines and issueing :diffput will only apply those changes in the other buffer.
(:diffget and :diffput also accept ranges, see :h copy-diffs for more.)
Compare two buffers inside Vim
If you load up two files in splits (:vs or :sp), you can do :diffthis on each window and achieve a diff of files that were already loaded in buffers.
:diffoff can be used to turn off the diff mode.
This Vimcasts post and video show this in practice.
How to apply all changes between buffers
Make sure that all participating buffers are in diff mode (see :h start-vimdiff)
a. Get changes from a buffer to the current one:
:%diffget <buffer-number>
b. Put all changes from current buffer into another:
:%diffput <buffer-number>
(:% is a range to select the entire file; see :h :%. :ls will show currently opened buffers.)

You can switch back and forth between the two windows with Ctrlww. You can copy from one window do a Ctrlww, and then paste into the other. As you resolve differences, the highlights will change, and disappear.
Take a look at this video.

You can just switch between the windows and copy and paste to resolve the differences, as #David W. suggests in his answer, but Vim also has dedicated :diffput and :diffget commands to simplify this. With these (or the corresponding normal mode do and dp commands), you don't have to switch between windows, and the range defaults to the current change.
If you need to add instead of overwrite with the other buffer's differences (which is a rather unusual case in a classic two-way diff), you still have to yank the original lines and put them after the :diffget.
After you're done in one place, you can use the ]c, [c commands to jump to the next difference.

I am using the following mappings to deal with three-way merges (when conflictstyle=diff3)
nnoremap g1 :<C-U>call MergeKeepLeft()<CR>
nnoremap g2 :<C-U>call MergeKeepBoth()<CR>
nnoremap g3 :<C-U>call MergeKeepRight()<CR>
function! MergeKeepLeft()
let lastsearch = #/
let #/ = '<<<<<<<'
execute "normal! ?\<cr>dd"
let #/ = '|||||||'
execute "normal! /\<cr>V"
let #/ = '>>>>>>>'
execute "normal! /\<cr>d"
let #/ = lastsearch
endfunction
function! MergeKeepBoth()
let lastsearch = #/
let #/ = '<<<<<<<'
execute "normal! ?\<cr>dd"
let #/ = '|||||||'
execute "normal! /\<cr>V"
let #/ = '======='
execute "normal! /\<cr>d"
let #/ = '>>>>>>>'
execute "normal! /\<cr>dd"
let #/ = lastsearch
endfunction
function! MergeKeepRight()
let lastsearch = #/
let #/ = '<<<<<<<'
execute "normal! ?\<cr>V"
let #/ = '======='
execute "normal! /\<cr>d"
let #/ = '>>>>>>>'
execute "normal! /\<cr>dd"
let #/ = lastsearch
endfunction

Related

How to have multiple views of the same buffer in vim?

I have been trying to implement a function in vimscript to switch between two views of the same buffer. The functionality I want to have is like this:
I press a key and the screen position and cursor are moved to my previously saved location. If there is not yet another saved location, it creates one at the current view and cursor location. I have a vimscript function that does exactly this functionality i described:
fun! SwitchFileMarker(reset)
if a:reset == 1
if exists("b:switch_file_window")
unlet b:switch_file_window
endif
endif
if exists("b:switch_file_window")
let cursor_location = getpos("'0")
let top_location = getpos("'9")
:execute "normal! m0Hm9`0"
call setpos('.', top_location)
:execute "normal! zt"
call setpos('.', cursor_location)
:execute "normal! zv"
else
" save the location
:execute "normal! m0Hm9`0"
endif
let b:switch_file_window = 1
endfun
nmap <leader>b :call SwitchFileMarker(0)<CR>
nmap <leader>B :call SwitchFileMarker(1)<CR>
The only problem with this function is I want it to save the folds of the current view and load the folds of the saved view when the function is called. I can achieve this by using :mkview and :loadview, but the problem with that is if the number of lines in the file changes, the folds are lost. The :mkview function seems to remember the folds at a specific line number, and If ive added several lines above that fold location while editing the file, when i use :loadview, the fold is lost. using the marks, as done in the function i show works to save my cursor position (but not folds) because the marks keep track of the changing line number. The funcionality that I am trying to get is essentially like having two views of a buffer but in the same window, rather than two windows. If i have a two windows editing the same buffer, I can add lines in one window and the folds are not lost in the other, so this is exactly the functionality I want, just in one window instead of two. Any suggestions how this can be done?
Use marks!
To set a mark, you can use m<letter> or :mark <letter>.
To go to a marked line, use '<letter>. To go to the exact position, prefer `<letter>.
If the letter is lowercase, it is buffer-local. If it is uppercase, it is global.
Edit: OP wants to save folds, which this can’t do. But it’s helpful to know anyway, so I’m leaving the answer.

Set active register persistently in Vim

Is there a way to change the active register persistently in Vim?
The " register is used by default, if I want to use the a register I have to prepend "a to my normal mode command. After giving the command, the active register gets switched back to ".
Is there a way to tell Vim to keep a specific register active until explicitly told not to do so?
There is no such option to achieve your goal.
You can read and change the vim source code for your requirement for sure.
It is also not easy to implement it with vimscript, what I can think of:
declare a var, indicate the target "unnamed reg", e.g. a
create mappings for d D s S c C y Y p P ... commands, let them call your function
in your function, you check the variable to get the target, and finally fire a "[targetReg]command or call set/getreg()
However I don't think this is a good idea. You have to think about many corner cases to make your script work for all situations. For example, user pressed "_dd or "bx, your script should know that user has explicitly declared which register he wanted to use, and you should bypass your target var.
So just use the default unnamed reg.
If the goal is just to append to ", these functions and mappings should work for visual and operator pending (e.g. $, w, fx, etc...).
function! AppOp(type, ...)
call App(a:type, a:0)
endfunction
function! App(type, visual)
let r = #"
if a:visual
execute "normal! `<" . a:type . "`>y"
else
execute "normal! `[v`]y"
endif
let #" = r . #"
endfunction
xnoremap <silent> <leader>a :call App(visualmode(), 1)<cr>
nnoremap <silent> <leader>a :set opfunc=AppOp<cr>g#
Swap y for d to delete instead of yank. Define mappings to taste.

concatenate exe mode commands in command assignment

I've a few files I want to apply folds to in a specific format incorporating two fixed elements and the contents of the " register. I was using a macro to do it but lately the files aren't in a consistent enough format.
I have 4 registers set by a function
#v='========= BEGIN'
#b='========== END'
#n=' =========={{{'
#m=' ==========}}}'
I find where the fold has to go yank the descriptor and then set the following and paste at the begiing and end
let #z=#v.#".#n
let #x=#b.#".#m
I tried every combination I could think of to concatenate the register assignment into a single map such as variations of
nnoremap <leader>X :'let #z=#v.#".#n | let #x=#b.#".#m'
Couldn't get it to work so added it to the function and mapped the function call to the X key.
Is there a way to chain command assignments in a key mapping? The functin works but the concept would be useful eslewhere.
Use <bar> or \|.
nnoremap <leader>X :'let #z=#v.#".#n <bar> let #x=#b.#".#m'
nnoremap <leader>X :'let #z=#v.#".#n \| let #x=#b.#".#m'
Take a look at :help map_bar

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.

Vim: Close All Buffers But This One

How can I close all buffers in Vim except the one I am currently editing?
I was able to do this pretty easily like this:
:%bd|e#
Try this
bufdo bd
bufdo runs command for all buffers
http://vim.wikia.com/wiki/Run_a_command_in_multiple_buffers
You could use this script from vim.org:
http://www.vim.org/scripts/script.php?script_id=1071
Just put it to your .vim/plugin directory and then use :BufOnly command to close all buffers but the active one. You could also map it elsewhere you like in your .vimrc.
Source on Github (via vim-scripts mirror): https://github.com/vim-scripts/BufOnly.vim/blob/master/plugin/BufOnly.vim
If you don´t care the current one, is more simple to do something like (no script needing):
1,100bd
I do this
:w | %bd | e#
My favorite if I just want my current buffer open and close all others.
How it works: first write current buffer's changes, then close all open buffers, then reopen the buffer I was currently on. In Vim, the | chains the execution of commands together. If your buffer is up to date the above can be shortened to :%bd | e#
Building on juananruiz's answer.
Make a small change in the buffer you want to keep, then
:1,1000bd
The command bd (buffer delete) will not delete any buffers with unsaved changes. This way you can keep the current (changed) file in the buffer list.
Edit: Please notice that this will also delete your NERDTreeBuffer. You can get it back with :NERDTree
Note: As mentioned in the comments, this closes windows and not buffers.
By using
:on[ly][!]
and
:h only
I put this in my .vimrc file
nnoremap <leader>ca :w <bar> %bd <bar> e# <bar> bd# <CR>
then your leader + ca (close all) close all the buffers except the current one.
What it does is
:w - save current buffer
%bd - close all the buffers
e# - open last edited file
bd# - close the unnamed buffer
Here's what I do. So I like to keep my cursor position after removing all buffers and most of the solutions above just ignores this fact. I also think remapping the command is better than typing it so Here I use <leader>bd to remove all buffers and jump back to my original cursor position.
noremap <leader>bd :%bd\|e#\|bd#<cr>\|'"
%bd = delete all buffers.
e# = open the last buffer for editing (Which Is the buffer I'm working on).
bd# to delete the [No Name] buffer that gets created when you use %bd.
The pipe in between just does one command after another. You've gotta escape it though using \|
'" = keep my cursor position.
Closing all open buffers:
silent! execute "1,".bufnr("$")."bd"
Closing all open buffers except for the current one:
function! CloseAllBuffersButCurrent()
let curr = bufnr("%")
let last = bufnr("$")
if curr > 1 | silent! execute "1,".(curr-1)."bd" | endif
if curr < last | silent! execute (curr+1).",".last."bd" | endif
endfunction
Add this function to .vimrc and call it using :call CloseAllBuffersButCurrent().
Convenience map:
nmap <Leader>\c :call CloseAllBuffersButCurrent()<CR>
There's a plugin that does exactly this and a bit more!
Check out close-buffers.vim
so this is an old question but it helped me get some ideas for my project. in order to close all buffers but the one you are currently using, use;
map <leader>o :execute "%bd\|e#"<CR>
The answer with highest votes will reopen the buffer, it will lose the current line we are working on.
Close then reopen will induce a flush on screen
function! CloseOtherBuffer()
let l:bufnr = bufnr()
execute "only"
for buffer in getbufinfo()
if !buffer.listed
continue
endif
if buffer.bufnr == l:bufnr
continue
else
if buffer.changed
echo buffer.name . " has changed, save first"
continue
endif
let l:cmd = "bdelete " . buffer.bufnr
execute l:cmd
endif
endfor
endfunction
let mapleader = ','
nnoremap <leader>o :call CloseOtherBuffer()<CR>
Previous code will take effect when you are on the target buffer and press , + o. It will close all other buffers except current one.
It iterates all the buffers and close all the buffer number which is not equal to current buffer number.
:%bd then Ctrl-o
:%bd to delete all buffers
Ctrl-o to jump back to the last buffer, which would be the "This One" you were referring to.
I like 1,100bd (suggested by juananruiz) which seems to work for me.
I added a quit! to my mapping to give me
nnoremap <leader>bd :1,100bd<CR>
nnoremap <leader>bdq :1,100bd<CR>:q!<CR>
This kills all the buffers and shuts down Vim, which is what I was looking for mostly.
How about just:
ctrl-w o
(thanks to https://thoughtbot.com/blog/vim-splits-move-faster-and-more-naturally)
I combined Alejandro's comment with badteeth's comment:
command! Bonly silent execute "%bd|norm <C-O>"
The norm <C-O> jumps to the last position in the jump list, which means where the cursor was before the %bd.
I used silent instead of silent!. That way, if any open buffers are modified, Vim prints an error message so I know what happened. The modified buffers stay open in my tests.
Unrelated: this is my 500th answer!
nnoremap <leader>x :execute '%bdelete\|edit #\|normal `"'\|bdelete#<CR>
Close all buffers (side-effect creates new empty buffer)
Open last buffer
Jump to last edit position in buffer
Delete empty buffer

Resources