aboutsummaryrefslogtreecommitdiff
path: root/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/hunk.vim
diff options
context:
space:
mode:
Diffstat (limited to 'vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/hunk.vim')
-rw-r--r--vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/hunk.vim332
1 files changed, 332 insertions, 0 deletions
diff --git a/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/hunk.vim b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/hunk.vim
new file mode 100644
index 0000000..50ea5f4
--- /dev/null
+++ b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/hunk.vim
@@ -0,0 +1,332 @@
+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)
+ let current_line = line('.')
+ let hunk_count = 0
+ for hunk in gitgutter#hunk#hunks(bufnr)
+ if hunk[2] > current_line
+ let hunk_count += 1
+ if hunk_count == a:count
+ execute 'normal!' hunk[2] . 'Gzv'
+ return
+ endif
+ endif
+ endfor
+ call gitgutter#utility#warn('No more hunks')
+ endif
+endfunction
+
+function! gitgutter#hunk#prev_hunk(count) abort
+ let bufnr = bufnr('')
+ if gitgutter#utility#is_active(bufnr)
+ let current_line = line('.')
+ let hunk_count = 0
+ for hunk in reverse(copy(gitgutter#hunk#hunks(bufnr)))
+ 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'
+ return
+ endif
+ endif
+ endfor
+ call gitgutter#utility#warn('No previous hunks')
+ endif
+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
+ call s:hunk_op(function('s:stage'))
+ silent! call repeat#set("\<Plug>GitGutterStageHunk", -1)<CR>
+endfunction
+
+function! gitgutter#hunk#undo() abort
+ call s:hunk_op(function('s:undo'))
+ silent! call repeat#set("\<Plug>GitGutterUndoHunk", -1)<CR>
+endfunction
+
+function! gitgutter#hunk#preview() abort
+ call s:hunk_op(function('s:preview'))
+ silent! call repeat#set("\<Plug>GitGutterPreviewHunk", -1)<CR>
+endfunction
+
+
+function! s:hunk_op(op)
+ let bufnr = bufnr('')
+
+ 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, 'index', 1)
+ let g:gitgutter_async = async
+
+ call gitgutter#hunk#set_hunks(bufnr, gitgutter#diff#parse_diff(diff))
+
+ 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
+ call a:op(gitgutter#diff#hunk_diff(bufnr, diff))
+ endif
+ endif
+endfunction
+
+
+function! s:stage(hunk_diff)
+ let bufnr = bufnr('')
+ 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.' apply --cached --unidiff-zero - '),
+ \ diff)
+
+ " 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, '\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
+endfunction
+
+
+function! s:preview(hunk_diff)
+ let hunk_lines = split(s:discard_header(a:hunk_diff), "\n")
+ let hunk_lines_length = len(hunk_lines)
+ let previewheight = min([hunk_lines_length, &previewheight])
+
+ silent! wincmd P
+ if !&previewwindow
+ noautocmd execute 'bo' previewheight 'new'
+ set previewwindow
+ else
+ execute 'resize' previewheight
+ endif
+
+ setlocal noreadonly modifiable filetype=diff buftype=nofile bufhidden=delete noswapfile
+ execute "%delete_"
+ call append(0, hunk_lines)
+ normal! gg
+ setlocal readonly nomodifiable
+
+ noautocmd wincmd p
+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
+
+if $VIM_GITGUTTER_TEST
+ function! gitgutter#hunk#fix_file_references(filepath, hunk_diff)
+ return s:fix_file_references(a:filepath, a:hunk_diff)
+ endfunction
+endif
+
+
+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], '+\@<=\(\d\+\)', '\=submatch(1)+line_adjustment', '')
+ return join(diff, "\n")
+endfunction
+
+
+function! s:discard_header(hunk_diff)
+ return join(split(a:hunk_diff, '\n', 1)[5:], "\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
+