Skip to content

Create vimdoc from readme #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 134 additions & 1 deletion autoload/mergetool.vim
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ endfunction
let g:mergetool_layout = get(g:, 'mergetool_layout', 'mr')
let g:mergetool_prefer_revision = get(g:, 'mergetool_prefer_revision', 'local')
let g:MergetoolSetLayoutCallback = get(g:, 'MergetoolSetLayoutCallback', function('s:noop'))
let g:mergetool_args_order = get(g:, 'mergetool_args_order', '')

" {{{ Public exports

Expand Down Expand Up @@ -38,6 +39,16 @@ function! mergetool#start() "{{{
let s:mergedfile_fileformat = &fileformat
let s:mergedfile_filetype = &filetype

if !empty(g:mergetool_args_order)
let success = s:apply_args_order(s:mergedfile_bufnr, g:mergetool_args_order)
if !success
echohl WarningMsg
echo "g:mergetool_args_order didn't use the current file as MERGED. Ensure you're using the order as seen in :args."
echohl None
return
endif
endif

" Detect if we're run as 'git mergetool' by presence of BASE|LOCAL|REMOTE buf names
let s:run_as_git_mergetool = bufnr('BASE') != -1 &&
\ bufnr('LOCAL') != -1 &&
Expand All @@ -52,8 +63,34 @@ function! mergetool#start() "{{{

call mergetool#prefer_revision(g:mergetool_prefer_revision)
call mergetool#set_layout(g:mergetool_layout)
call mergetool#bind_commands()
endfunction "}}}

function! mergetool#bind_commands()
command! -nargs=0 MergetoolStop call mergetool#stop()
command! -nargs=1 MergetoolSetLayout call mergetool#set_layout(<f-args>)
command! -nargs=1 MergetoolToggleLayout call mergetool#toggle_layout(<f-args>)
command! -nargs=0 MergetoolPreferLocal call mergetool#prefer_revision('local')
command! -nargs=0 MergetoolPreferRemote call mergetool#prefer_revision('remote')
doautocmd User MergetoolStart
endf

function! mergetool#unbind_commands()
delcommand MergetoolStop
delcommand MergetoolSetLayout
delcommand MergetoolToggleLayout
delcommand MergetoolPreferLocal
delcommand MergetoolPreferRemote
doautocmd User MergetoolStop
endf

" Dummy autocmds to prevent errors.
augroup mergetool_dummy
au!
autocmd User MergetoolStart let s:mergetool_dummy = 1
autocmd User MergetoolStop let s:mergetool_dummy = 0
augroup END

" Stop mergetool effect depends on:
" - when run as 'git mergetool'
" - when run from Vim directly on file with conflict markers
Expand Down Expand Up @@ -104,6 +141,7 @@ function! mergetool#stop() " {{{
endif

let g:mergetool_in_merge_mode = 0
call mergetool#unbind_commands()
tabclose
endif
endfunction " }}}
Expand All @@ -117,6 +155,38 @@ function! mergetool#toggle() " {{{
endif
endfunction " }}}

" Create hidden buffers that use git's special buffer names to support any
" scm. We never create a MERGED buffer. Instead, return it so we can validate
" it's as expected.
function! s:apply_args_order(merged_bufnr, arg_order) " {{{
let abbrevs = {
\ 'M': 'MERGED',
\ 'B': 'BASE',
\ 'R': 'REMOTE',
\ 'L': 'LOCAL' }

let i = 1
for labbr in split(a:arg_order, '\zs')
if labbr ==# 'M'
let current_arg_bufnr = bufnr(argv(i - 1))
if a:merged_bufnr != current_arg_bufnr
" Fail -- input merged buffer number doesn't match arg order.
return 0
endif
else
execute 'silent' i 'argument'
execute 'silent file' abbrevs[labbr]
setlocal buftype=nofile
setlocal bufhidden=hide
endif
let i += 1
endfor

execute "buffer " . a:merged_bufnr
" Success
return 1
endfunction " }}}

" Opens set of windows with merged file and various file revisions
" Supported layout options:
" - w, 'MERGED' revision as passed by Git, or working tree version of merged file
Expand Down Expand Up @@ -149,6 +219,11 @@ function! mergetool#set_layout(layout) " {{{
let l:_winstate = winsaveview()
endif

" Ensure merged file (which likely has unsaved conflict removal changes) can
" be hidden without error.
let bufhidden_bak = getbufvar(s:mergedfile_bufnr, '&bufhidden')
call setbufvar(s:mergedfile_bufnr, '&bufhidden', 'hide')

" Before changing layout, turn off diff mode in all visible windows
windo diffoff

Expand Down Expand Up @@ -198,6 +273,7 @@ function! mergetool#set_layout(layout) " {{{
if s:goto_win_with_merged_file() && exists('l:_winstate')
call winrestview(l:_winstate)
endif
call setbufvar(s:mergedfile_bufnr, '&bufhidden', bufhidden_bak)
endfunction " }}}

" Toggles between given and default layout
Expand All @@ -223,6 +299,57 @@ endfunction " }}}

" }}}

" Diff exchange {{{

" Do either diffget or diffput, depending on given direction
" and whether the window has adjacent window in a given direction
" h|<left> + window on right = diffget from right win
" h|<left> + no window on right = diffput to left win
" l|<right> + window on left = diffget from left win
" l|<right> + no window on left = diffput to right win
" Same logic applies for vertical directions: 'j' and 'k'

let s:directions = {
\ 'h': 'l',
\ 'l': 'h',
\ 'j': 'k',
\ 'k': 'j' }

function mergetool#DiffExchange(dir)
let oppdir = s:directions[a:dir]

let winoppdir = s:FindWindowOnDir(oppdir)
if (winoppdir != -1)
execute "diffget " . winbufnr(winoppdir)
else
let windir = s:FindWindowOnDir(a:dir)
if (windir != -1)
execute "diffput " . winbufnr(windir)
else
echohl WarningMsg
echo 'Cannot exchange diff. Found only single window'
echohl None
endif
endif
endfunction

" Finds window in given direction and returns it win number
" If no window found, returns -1
function s:FindWindowOnDir(dir)
let oldwin = winnr()

execute "noautocmd wincmd " . a:dir
let curwin = winnr()
if (oldwin != curwin)
noautocmd wincmd p
return curwin
else
return -1
endif
endfunction

" }}}

" Private functions{{{

let s:markers = {
Expand All @@ -243,7 +370,13 @@ function! s:load_revision(revision)
call s:remove_conflict_markers(a:revision)
setlocal nomodifiable readonly buftype=nofile bufhidden=delete nobuflisted
execute "setlocal filetype=" . s:mergedfile_filetype
execute "file " . a:revision
let bufname = a:revision
if s:run_as_git_mergetool && has('win32')
" Cannot create a buffer called 'remote' if there's already one called
" 'REMOTE' because win32 is not case-sensitive.
let bufname .= '_derived'
endif
execute "file " . bufname
elseif a:revision ==# 'BASE' || a:revision ==# 'REMOTE' || a:revision ==# 'LOCAL'

" First, if run as 'git mergetool', try find buffer by name: 'BASE|REMOTE|LOCAL'
Expand Down
Loading