mirror of
https://github.com/airblade/vim-gitgutter.git
synced 2025-11-08 11:33:48 -05:00
Since we change directory to the file's directory we can use its name rather than its path in the repo.
659 lines
20 KiB
VimL
659 lines
20 KiB
VimL
let s:winid = 0
|
|
let s:preview_bufnr = 0
|
|
let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : ''
|
|
|
|
function! gitgutter#hunk#set_hunks(bufnr, hunks) abort
|
|
call gitgutter#utility#setbufvar(a:bufnr, 'hunks', a:hunks)
|
|
call s:reset_summary(a:bufnr)
|
|
endfunction
|
|
|
|
function! gitgutter#hunk#hunks(bufnr) abort
|
|
return gitgutter#utility#getbufvar(a:bufnr, 'hunks', [])
|
|
endfunction
|
|
|
|
function! gitgutter#hunk#reset(bufnr) abort
|
|
call gitgutter#utility#setbufvar(a:bufnr, 'hunks', [])
|
|
call s:reset_summary(a:bufnr)
|
|
endfunction
|
|
|
|
|
|
function! gitgutter#hunk#summary(bufnr) abort
|
|
return gitgutter#utility#getbufvar(a:bufnr, 'summary', [0,0,0])
|
|
endfunction
|
|
|
|
function! s:reset_summary(bufnr) abort
|
|
call gitgutter#utility#setbufvar(a:bufnr, 'summary', [0,0,0])
|
|
endfunction
|
|
|
|
function! gitgutter#hunk#increment_lines_added(bufnr, count) abort
|
|
let summary = gitgutter#hunk#summary(a:bufnr)
|
|
let summary[0] += a:count
|
|
call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary)
|
|
endfunction
|
|
|
|
function! gitgutter#hunk#increment_lines_modified(bufnr, count) abort
|
|
let summary = gitgutter#hunk#summary(a:bufnr)
|
|
let summary[1] += a:count
|
|
call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary)
|
|
endfunction
|
|
|
|
function! gitgutter#hunk#increment_lines_removed(bufnr, count) abort
|
|
let summary = gitgutter#hunk#summary(a:bufnr)
|
|
let summary[2] += a:count
|
|
call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary)
|
|
endfunction
|
|
|
|
|
|
function! gitgutter#hunk#next_hunk(count) abort
|
|
let bufnr = bufnr('')
|
|
if !gitgutter#utility#is_active(bufnr) | return | endif
|
|
|
|
let hunks = gitgutter#hunk#hunks(bufnr)
|
|
if empty(hunks)
|
|
call gitgutter#utility#warn('No hunks in file')
|
|
return
|
|
endif
|
|
|
|
let current_line = line('.')
|
|
let hunk_count = 0
|
|
for hunk in hunks
|
|
if hunk[2] > current_line
|
|
let hunk_count += 1
|
|
if hunk_count == a:count
|
|
execute 'normal!' hunk[2] . 'Gzv'
|
|
if g:gitgutter_show_msg_on_hunk_jumping
|
|
redraw | echo printf('Hunk %d of %d', index(hunks, hunk) + 1, len(hunks))
|
|
endif
|
|
if gitgutter#hunk#is_preview_window_open()
|
|
call gitgutter#hunk#preview()
|
|
endif
|
|
return
|
|
endif
|
|
endif
|
|
endfor
|
|
call gitgutter#utility#warn('No more hunks')
|
|
endfunction
|
|
|
|
function! gitgutter#hunk#prev_hunk(count) abort
|
|
let bufnr = bufnr('')
|
|
if !gitgutter#utility#is_active(bufnr) | return | endif
|
|
|
|
let hunks = gitgutter#hunk#hunks(bufnr)
|
|
if empty(hunks)
|
|
call gitgutter#utility#warn('No hunks in file')
|
|
return
|
|
endif
|
|
|
|
let current_line = line('.')
|
|
let hunk_count = 0
|
|
for hunk in reverse(copy(hunks))
|
|
if hunk[2] < current_line
|
|
let hunk_count += 1
|
|
if hunk_count == a:count
|
|
let target = hunk[2] == 0 ? 1 : hunk[2]
|
|
execute 'normal!' target . 'Gzv'
|
|
if g:gitgutter_show_msg_on_hunk_jumping
|
|
redraw | echo printf('Hunk %d of %d', index(hunks, hunk) + 1, len(hunks))
|
|
endif
|
|
if gitgutter#hunk#is_preview_window_open()
|
|
call gitgutter#hunk#preview()
|
|
endif
|
|
return
|
|
endif
|
|
endif
|
|
endfor
|
|
call gitgutter#utility#warn('No previous hunks')
|
|
endfunction
|
|
|
|
" Returns the hunk the cursor is currently in or an empty list if the cursor
|
|
" isn't in a hunk.
|
|
function! s:current_hunk() abort
|
|
let bufnr = bufnr('')
|
|
let current_hunk = []
|
|
|
|
for hunk in gitgutter#hunk#hunks(bufnr)
|
|
if gitgutter#hunk#cursor_in_hunk(hunk)
|
|
let current_hunk = hunk
|
|
break
|
|
endif
|
|
endfor
|
|
|
|
return current_hunk
|
|
endfunction
|
|
|
|
" Returns truthy if the cursor is in two hunks (which can only happen if the
|
|
" cursor is on the first line and lines above have been deleted and lines
|
|
" immediately below have been deleted) or falsey otherwise.
|
|
function! s:cursor_in_two_hunks()
|
|
let hunks = gitgutter#hunk#hunks(bufnr(''))
|
|
|
|
if line('.') == 1 && len(hunks) > 1 && hunks[0][2:3] == [0, 0] && hunks[1][2:3] == [1, 0]
|
|
return 1
|
|
endif
|
|
|
|
return 0
|
|
endfunction
|
|
|
|
" A line can be in 0 or 1 hunks, with the following exception: when the first
|
|
" line(s) of a file has been deleted, and the new second line (and
|
|
" optionally below) has been deleted, the new first line is in two hunks.
|
|
function! gitgutter#hunk#cursor_in_hunk(hunk) abort
|
|
let current_line = line('.')
|
|
|
|
if current_line == 1 && a:hunk[2] == 0
|
|
return 1
|
|
endif
|
|
|
|
if current_line >= a:hunk[2] && current_line < a:hunk[2] + (a:hunk[3] == 0 ? 1 : a:hunk[3])
|
|
return 1
|
|
endif
|
|
|
|
return 0
|
|
endfunction
|
|
|
|
|
|
function! gitgutter#hunk#in_hunk(lnum)
|
|
" Hunks are sorted in the order they appear in the buffer.
|
|
for hunk in gitgutter#hunk#hunks(bufnr(''))
|
|
" if in a hunk on first line of buffer
|
|
if a:lnum == 1 && hunk[2] == 0
|
|
return 1
|
|
endif
|
|
|
|
" if in a hunk generally
|
|
if a:lnum >= hunk[2] && a:lnum < hunk[2] + (hunk[3] == 0 ? 1 : hunk[3])
|
|
return 1
|
|
endif
|
|
|
|
" if hunk starts after the given line
|
|
if a:lnum < hunk[2]
|
|
return 0
|
|
endif
|
|
endfor
|
|
|
|
return 0
|
|
endfunction
|
|
|
|
|
|
function! gitgutter#hunk#text_object(inner) abort
|
|
let hunk = s:current_hunk()
|
|
|
|
if empty(hunk)
|
|
return
|
|
endif
|
|
|
|
let [first_line, last_line] = [hunk[2], hunk[2] + hunk[3] - 1]
|
|
|
|
if ! a:inner
|
|
let lnum = last_line
|
|
let eof = line('$')
|
|
while lnum < eof && empty(getline(lnum + 1))
|
|
let lnum +=1
|
|
endwhile
|
|
let last_line = lnum
|
|
endif
|
|
|
|
execute 'normal! 'first_line.'GV'.last_line.'G'
|
|
endfunction
|
|
|
|
|
|
function! gitgutter#hunk#stage(...) abort
|
|
if !s:in_hunk_preview_window() && !gitgutter#utility#has_repo_path(bufnr('')) | return | endif
|
|
|
|
if a:0 && (a:1 != 1 || a:2 != line('$'))
|
|
call s:hunk_op(function('s:stage'), a:1, a:2)
|
|
else
|
|
call s:hunk_op(function('s:stage'))
|
|
endif
|
|
silent! call repeat#set("\<Plug>(GitGutterStageHunk)", -1)
|
|
endfunction
|
|
|
|
function! gitgutter#hunk#undo() abort
|
|
if !gitgutter#utility#has_repo_path(bufnr('')) | return | endif
|
|
|
|
call s:hunk_op(function('s:undo'))
|
|
silent! call repeat#set("\<Plug>(GitGutterUndoHunk)", -1)
|
|
endfunction
|
|
|
|
function! gitgutter#hunk#preview() abort
|
|
if !gitgutter#utility#has_repo_path(bufnr('')) | return | endif
|
|
|
|
call s:hunk_op(function('s:preview'))
|
|
silent! call repeat#set("\<Plug>(GitGutterPreviewHunk)", -1)
|
|
endfunction
|
|
|
|
|
|
function! s:hunk_op(op, ...)
|
|
let bufnr = bufnr('')
|
|
|
|
if s:in_hunk_preview_window()
|
|
if string(a:op) =~ '_stage'
|
|
" combine hunk-body in preview window with updated hunk-header
|
|
let hunk_body = getline(1, '$')
|
|
|
|
let [removed, added] = [0, 0]
|
|
for line in hunk_body
|
|
if line[0] == '-'
|
|
let removed += 1
|
|
elseif line[0] == '+'
|
|
let added += 1
|
|
endif
|
|
endfor
|
|
|
|
let hunk_header = b:hunk_header
|
|
" from count
|
|
let hunk_header[4] = substitute(hunk_header[4], '\(-\d\+\)\(,\d\+\)\?', '\=submatch(1).",".removed', '')
|
|
" to count
|
|
let hunk_header[4] = substitute(hunk_header[4], '\(+\d\+\)\(,\d\+\)\?', '\=submatch(1).",".added', '')
|
|
|
|
let hunk_diff = join(hunk_header + hunk_body, "\n")."\n"
|
|
|
|
call s:goto_original_window()
|
|
call gitgutter#hunk#close_hunk_preview_window()
|
|
call s:stage(hunk_diff)
|
|
endif
|
|
|
|
return
|
|
endif
|
|
|
|
if gitgutter#utility#is_active(bufnr)
|
|
" Get a (synchronous) diff.
|
|
let [async, g:gitgutter_async] = [g:gitgutter_async, 0]
|
|
let diff = gitgutter#diff#run_diff(bufnr, g:gitgutter_diff_relative_to, 1)
|
|
let g:gitgutter_async = async
|
|
|
|
call gitgutter#hunk#set_hunks(bufnr, gitgutter#diff#parse_diff(diff))
|
|
call gitgutter#diff#process_hunks(bufnr, gitgutter#hunk#hunks(bufnr)) " so the hunk summary is updated
|
|
|
|
if empty(s:current_hunk())
|
|
call gitgutter#utility#warn('Cursor is not in a hunk')
|
|
elseif s:cursor_in_two_hunks()
|
|
let choice = input('Choose hunk: upper or lower (u/l)? ')
|
|
" Clear input
|
|
normal! :<ESC>
|
|
if choice =~ 'u'
|
|
call a:op(gitgutter#diff#hunk_diff(bufnr, diff, 0))
|
|
elseif choice =~ 'l'
|
|
call a:op(gitgutter#diff#hunk_diff(bufnr, diff, 1))
|
|
else
|
|
call gitgutter#utility#warn('Did not recognise your choice')
|
|
endif
|
|
else
|
|
let hunk_diff = gitgutter#diff#hunk_diff(bufnr, diff)
|
|
|
|
if a:0
|
|
let hunk_first_line = s:current_hunk()[2]
|
|
let hunk_diff = s:part_of_diff(hunk_diff, a:1-hunk_first_line, a:2-hunk_first_line)
|
|
endif
|
|
|
|
call a:op(hunk_diff)
|
|
endif
|
|
endif
|
|
endfunction
|
|
|
|
|
|
function! s:stage(hunk_diff)
|
|
let bufnr = bufnr('')
|
|
|
|
if gitgutter#utility#clean_smudge_filter_applies(bufnr)
|
|
let choice = input('File uses clean/smudge filter. Stage entire file (y/n)? ')
|
|
normal! :<ESC>
|
|
if choice =~ 'y'
|
|
let path = gitgutter#utility#repo_path(bufnr, 1)
|
|
" Add file to index.
|
|
let cmd = gitgutter#utility#cd_cmd(bufnr,
|
|
\ g:gitgutter_git_executable.' '.g:gitgutter_git_args.
|
|
\ ' add '.
|
|
\ gitgutter#utility#shellescape(gitgutter#utility#filename(bufnr)))
|
|
call gitgutter#utility#system(cmd)
|
|
else
|
|
return
|
|
endif
|
|
|
|
else
|
|
let diff = s:adjust_header(bufnr, a:hunk_diff)
|
|
" Apply patch to index.
|
|
call gitgutter#utility#system(
|
|
\ gitgutter#utility#cd_cmd(bufnr, g:gitgutter_git_executable.' '.g:gitgutter_git_args.' apply --cached --unidiff-zero - '),
|
|
\ diff)
|
|
endif
|
|
|
|
if v:shell_error
|
|
call gitgutter#utility#warn('Patch does not apply')
|
|
else
|
|
if exists('#User#GitGutterStage')
|
|
execute 'doautocmd' s:nomodeline 'User GitGutterStage'
|
|
endif
|
|
endif
|
|
|
|
" Refresh gitgutter's view of buffer.
|
|
call gitgutter#process_buffer(bufnr, 1)
|
|
endfunction
|
|
|
|
|
|
function! s:undo(hunk_diff)
|
|
" Apply reverse patch to buffer.
|
|
let hunk = gitgutter#diff#parse_hunk(split(a:hunk_diff, '\n')[4])
|
|
let lines = map(split(a:hunk_diff, '\r\?\n')[5:], 'v:val[1:]')
|
|
let lnum = hunk[2]
|
|
let added_only = hunk[1] == 0 && hunk[3] > 0
|
|
let removed_only = hunk[1] > 0 && hunk[3] == 0
|
|
|
|
if removed_only
|
|
call append(lnum, lines)
|
|
elseif added_only
|
|
execute lnum .','. (lnum+len(lines)-1) .'d _'
|
|
else
|
|
call append(lnum-1, lines[0:hunk[1]])
|
|
execute (lnum+hunk[1]) .','. (lnum+hunk[1]+hunk[3]) .'d _'
|
|
endif
|
|
|
|
" Refresh gitgutter's view of buffer.
|
|
call gitgutter#process_buffer(bufnr(''), 1)
|
|
endfunction
|
|
|
|
|
|
function! s:preview(hunk_diff)
|
|
let lines = split(a:hunk_diff, '\r\?\n')
|
|
let header = lines[0:4]
|
|
let body = lines[5:]
|
|
|
|
call s:open_hunk_preview_window()
|
|
call s:populate_hunk_preview_window(header, body)
|
|
call s:enable_staging_from_hunk_preview_window()
|
|
if &previewwindow
|
|
call s:goto_original_window()
|
|
endif
|
|
endfunction
|
|
|
|
|
|
" Returns a new hunk diff using the specified lines from the given one.
|
|
" Assumes all lines are additions.
|
|
" a:first, a:last - 0-based indexes into the body of the hunk.
|
|
function! s:part_of_diff(hunk_diff, first, last)
|
|
let diff_lines = split(a:hunk_diff, '\n', 1)
|
|
|
|
" adjust 'to' line count in header
|
|
let diff_lines[4] = substitute(diff_lines[4], '\(+\d\+\)\(,\d\+\)\?', '\=submatch(1).",".(a:last-a:first+1)', '')
|
|
|
|
return join(diff_lines[0:4] + diff_lines[5+a:first:5+a:last], "\n")."\n"
|
|
endfunction
|
|
|
|
|
|
function! s:adjust_header(bufnr, hunk_diff)
|
|
let filepath = gitgutter#utility#repo_path(a:bufnr, 0)
|
|
return s:adjust_hunk_summary(s:fix_file_references(filepath, a:hunk_diff))
|
|
endfunction
|
|
|
|
|
|
" Replaces references to temp files with the actual file.
|
|
function! s:fix_file_references(filepath, hunk_diff)
|
|
let lines = split(a:hunk_diff, '\n')
|
|
|
|
let left_prefix = matchstr(lines[2], '[abciow12]').'/'
|
|
let right_prefix = matchstr(lines[3], '[abciow12]').'/'
|
|
let quote = lines[0][11] == '"' ? '"' : ''
|
|
|
|
let left_file = quote.left_prefix.a:filepath.quote
|
|
let right_file = quote.right_prefix.a:filepath.quote
|
|
|
|
let lines[0] = 'diff --git '.left_file.' '.right_file
|
|
let lines[2] = '--- '.left_file
|
|
let lines[3] = '+++ '.right_file
|
|
|
|
return join(lines, "\n")."\n"
|
|
endfunction
|
|
|
|
|
|
function! s:adjust_hunk_summary(hunk_diff) abort
|
|
let line_adjustment = s:line_adjustment_for_current_hunk()
|
|
let diff = split(a:hunk_diff, '\n', 1)
|
|
let diff[4] = substitute(diff[4], '+\zs\(\d\+\)', '\=submatch(1)+line_adjustment', '')
|
|
return join(diff, "\n")
|
|
endfunction
|
|
|
|
|
|
" Returns the number of lines the current hunk is offset from where it would
|
|
" be if any changes above it in the file didn't exist.
|
|
function! s:line_adjustment_for_current_hunk() abort
|
|
let bufnr = bufnr('')
|
|
let adj = 0
|
|
for hunk in gitgutter#hunk#hunks(bufnr)
|
|
if gitgutter#hunk#cursor_in_hunk(hunk)
|
|
break
|
|
else
|
|
let adj += hunk[1] - hunk[3]
|
|
endif
|
|
endfor
|
|
return adj
|
|
endfunction
|
|
|
|
|
|
function! s:in_hunk_preview_window()
|
|
if g:gitgutter_preview_win_floating
|
|
return win_id2win(s:winid) == winnr()
|
|
else
|
|
return &previewwindow
|
|
endif
|
|
endfunction
|
|
|
|
|
|
" Floating window: does not move cursor to floating window.
|
|
" Preview window: moves cursor to preview window.
|
|
function! s:open_hunk_preview_window()
|
|
let source_wrap = &wrap
|
|
let source_window = winnr()
|
|
|
|
if g:gitgutter_preview_win_floating
|
|
if exists('*nvim_open_win')
|
|
call gitgutter#hunk#close_hunk_preview_window()
|
|
|
|
let buf = nvim_create_buf(v:false, v:false)
|
|
" Set default width and height for now.
|
|
let s:winid = nvim_open_win(buf, v:false, g:gitgutter_floating_window_options)
|
|
call nvim_win_set_option(s:winid, 'wrap', source_wrap ? v:true : v:false)
|
|
call nvim_buf_set_option(buf, 'filetype', 'diff')
|
|
call nvim_buf_set_option(buf, 'buftype', 'acwrite')
|
|
call nvim_buf_set_option(buf, 'bufhidden', 'delete')
|
|
call nvim_buf_set_option(buf, 'swapfile', v:false)
|
|
call nvim_buf_set_name(buf, 'gitgutter://hunk-preview')
|
|
|
|
" Assumes cursor is in original window.
|
|
autocmd CursorMoved,TabLeave <buffer> ++once call gitgutter#hunk#close_hunk_preview_window()
|
|
|
|
if g:gitgutter_close_preview_on_escape
|
|
" Map <Esc> to close the floating preview.
|
|
nnoremap <buffer> <silent> <Esc> :<C-U>call gitgutter#hunk#close_hunk_preview_window()<CR>
|
|
" Ensure that when the preview window is closed, the map is removed.
|
|
autocmd User GitGutterPreviewClosed silent! nunmap <buffer> <Esc>
|
|
autocmd CursorMoved <buffer> ++once silent! nunmap <buffer> <Esc>
|
|
execute "autocmd WinClosed <buffer=".winbufnr(s:winid)."> doautocmd" s:nomodeline "User GitGutterPreviewClosed"
|
|
endif
|
|
|
|
return
|
|
endif
|
|
|
|
if exists('*popup_create')
|
|
if g:gitgutter_close_preview_on_escape
|
|
let g:gitgutter_floating_window_options.filter = function('s:close_popup_on_escape')
|
|
endif
|
|
|
|
let s:winid = popup_create('', g:gitgutter_floating_window_options)
|
|
|
|
call setbufvar(winbufnr(s:winid), '&filetype', 'diff')
|
|
call setwinvar(s:winid, '&wrap', source_wrap)
|
|
|
|
return
|
|
endif
|
|
endif
|
|
|
|
if exists('&previewpopup')
|
|
let [previewpopup, &previewpopup] = [&previewpopup, '']
|
|
endif
|
|
|
|
" Specifying where to open the preview window can lead to the cursor going
|
|
" to an unexpected window when the preview window is closed (#769).
|
|
silent! noautocmd execute g:gitgutter_preview_win_location 'pedit gitgutter://hunk-preview'
|
|
silent! wincmd P
|
|
setlocal statusline=%{''}
|
|
doautocmd WinEnter
|
|
if exists('*win_getid')
|
|
let s:winid = win_getid()
|
|
else
|
|
let s:preview_bufnr = bufnr('')
|
|
endif
|
|
setlocal filetype=diff buftype=acwrite bufhidden=delete
|
|
let &l:wrap = source_wrap
|
|
let b:source_window = source_window
|
|
" Reset some defaults in case someone else has changed them.
|
|
setlocal noreadonly modifiable noswapfile
|
|
if g:gitgutter_close_preview_on_escape
|
|
" Ensure cursor goes to the expected window.
|
|
nnoremap <buffer> <silent> <Esc> :<C-U>execute b:source_window . "wincmd w"<Bar>pclose<CR>
|
|
endif
|
|
|
|
if exists('&previewpopup')
|
|
let &previewpopup=previewpopup
|
|
endif
|
|
endfunction
|
|
|
|
|
|
function! s:close_popup_on_escape(winid, key)
|
|
if a:key == "\<Esc>"
|
|
call popup_close(a:winid)
|
|
return 1
|
|
endif
|
|
return 0
|
|
endfunction
|
|
|
|
|
|
" Floating window: does not care where cursor is.
|
|
" Preview window: assumes cursor is in preview window.
|
|
function! s:populate_hunk_preview_window(header, body)
|
|
if g:gitgutter_preview_win_floating
|
|
if exists('*nvim_open_win')
|
|
" Assumes cursor is not in previewing window.
|
|
call nvim_buf_set_var(winbufnr(s:winid), 'hunk_header', a:header)
|
|
|
|
let [_scrolloff, &scrolloff] = [&scrolloff, 0]
|
|
|
|
let [width, height] = s:screen_lines(a:body)
|
|
let height = min([height, g:gitgutter_floating_window_options.height])
|
|
call nvim_win_set_width(s:winid, width)
|
|
call nvim_win_set_height(s:winid, height)
|
|
|
|
let &scrolloff=_scrolloff
|
|
|
|
call nvim_buf_set_lines(winbufnr(s:winid), 0, -1, v:false, [])
|
|
call nvim_buf_set_lines(winbufnr(s:winid), 0, -1, v:false, a:body)
|
|
call nvim_buf_set_option(winbufnr(s:winid), 'modified', v:false)
|
|
|
|
let ns_id = nvim_create_namespace('GitGutter')
|
|
call nvim_buf_clear_namespace(winbufnr(s:winid), ns_id, 0, -1)
|
|
for region in gitgutter#diff_highlight#process(a:body)
|
|
let group = region[1] == '+' ? 'GitGutterAddIntraLine' : 'GitGutterDeleteIntraLine'
|
|
call nvim_buf_add_highlight(winbufnr(s:winid), ns_id, group, region[0]-1, region[2]-1, region[3])
|
|
endfor
|
|
|
|
call nvim_win_set_cursor(s:winid, [1,0])
|
|
endif
|
|
|
|
if exists('*popup_create')
|
|
call popup_settext(s:winid, a:body)
|
|
|
|
for region in gitgutter#diff_highlight#process(a:body)
|
|
let group = region[1] == '+' ? 'GitGutterAddIntraLine' : 'GitGutterDeleteIntraLine'
|
|
call win_execute(s:winid, "call matchaddpos('".group."', [[".region[0].", ".region[2].", ".(region[3]-region[2]+1)."]])")
|
|
endfor
|
|
endif
|
|
|
|
else
|
|
let b:hunk_header = a:header
|
|
|
|
%delete _
|
|
call setline(1, a:body)
|
|
setlocal nomodified
|
|
|
|
let [_, height] = s:screen_lines(a:body)
|
|
execute 'resize' height
|
|
1
|
|
|
|
call clearmatches()
|
|
for region in gitgutter#diff_highlight#process(a:body)
|
|
let group = region[1] == '+' ? 'GitGutterAddIntraLine' : 'GitGutterDeleteIntraLine'
|
|
call matchaddpos(group, [[region[0], region[2], region[3]-region[2]+1]])
|
|
endfor
|
|
|
|
1
|
|
endif
|
|
endfunction
|
|
|
|
|
|
" Calculates the number of columns and the number of screen lines the given
|
|
" array of lines will take up, taking account of wrapping.
|
|
function! s:screen_lines(lines)
|
|
let [_virtualedit, &virtualedit]=[&virtualedit, 'all']
|
|
let cursor = getcurpos()
|
|
normal! 0g$
|
|
let available_width = virtcol('.')
|
|
call setpos('.', cursor)
|
|
let &virtualedit=_virtualedit
|
|
let width = min([max(map(copy(a:lines), 'strdisplaywidth(v:val)')), available_width])
|
|
|
|
if exists('*reduce')
|
|
let height = reduce(a:lines, { acc, val -> acc + strdisplaywidth(val) / width + (strdisplaywidth(val) % width == 0 ? 0 : 1) }, 0)
|
|
else
|
|
let height = eval(join(map(copy(a:lines), 'strdisplaywidth(v:val) / width + (strdisplaywidth(v:val) % width == 0 ? 0 : 1)'), '+'))
|
|
endif
|
|
|
|
return [width, height]
|
|
endfunction
|
|
|
|
|
|
function! s:enable_staging_from_hunk_preview_window()
|
|
augroup gitgutter_hunk_preview
|
|
autocmd!
|
|
let bufnr = s:winid != 0 ? winbufnr(s:winid) : s:preview_bufnr
|
|
execute 'autocmd BufWriteCmd <buffer='.bufnr.'> GitGutterStageHunk'
|
|
augroup END
|
|
endfunction
|
|
|
|
|
|
function! s:goto_original_window()
|
|
noautocmd execute b:source_window . "wincmd w"
|
|
doautocmd WinEnter
|
|
endfunction
|
|
|
|
|
|
function! gitgutter#hunk#close_hunk_preview_window()
|
|
let bufnr = s:winid != 0 ? winbufnr(s:winid) : s:preview_bufnr
|
|
call setbufvar(bufnr, '&modified', 0)
|
|
|
|
if g:gitgutter_preview_win_floating
|
|
if win_id2win(s:winid) > 0
|
|
execute win_id2win(s:winid).'wincmd c'
|
|
endif
|
|
else
|
|
pclose
|
|
endif
|
|
|
|
let s:winid = 0
|
|
let s:preview_bufnr = 0
|
|
endfunction
|
|
|
|
|
|
function gitgutter#hunk#is_preview_window_open()
|
|
if g:gitgutter_preview_win_floating
|
|
if win_id2win(s:winid) > 0
|
|
execute win_id2win(s:winid).'wincmd c'
|
|
endif
|
|
else
|
|
for i in range(1, winnr('$'))
|
|
if getwinvar(i, '&previewwindow')
|
|
return 1
|
|
endif
|
|
endfor
|
|
endif
|
|
return 0
|
|
endfunction
|