gitgutter.vim 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. scriptencoding utf-8
  2. if exists('g:loaded_gitgutter') || !has('signs') || &cp
  3. finish
  4. endif
  5. let g:loaded_gitgutter = 1
  6. " Initialisation {{{
  7. if v:version < 703 || (v:version == 703 && !has("patch105"))
  8. call gitgutter#utility#warn('Requires Vim 7.3.105')
  9. finish
  10. endif
  11. let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : ''
  12. function! s:obsolete(var)
  13. if exists(a:var)
  14. call gitgutter#utility#warn(a:var.' is obsolete and has no effect.')
  15. endif
  16. endfunction
  17. let g:gitgutter_preview_win_location = get(g:, 'gitgutter_preview_win_location', 'bo')
  18. if exists('*nvim_open_win')
  19. let g:gitgutter_preview_win_floating = get(g:, 'gitgutter_preview_win_floating', 1)
  20. let g:gitgutter_floating_window_options = get(g:, 'gitgutter_floating_window_options', {
  21. \ 'relative': 'cursor',
  22. \ 'row': 1,
  23. \ 'col': 0,
  24. \ 'width': 42,
  25. \ 'height': &previewheight,
  26. \ 'style': 'minimal'
  27. \ })
  28. else
  29. let default = exists('&previewpopup') ? !empty(&previewpopup) : 0
  30. let g:gitgutter_preview_win_floating = get(g:, 'gitgutter_preview_win_floating', default)
  31. let g:gitgutter_floating_window_options = get(g:, 'gitgutter_floating_window_options', {
  32. \ 'line': 'cursor+1',
  33. \ 'col': 'cursor',
  34. \ 'moved': 'any'
  35. \ })
  36. endif
  37. let g:gitgutter_enabled = get(g:, 'gitgutter_enabled', 1)
  38. if exists('*sign_unplace')
  39. let g:gitgutter_max_signs = get(g:, 'gitgutter_max_signs', -1)
  40. else
  41. let g:gitgutter_max_signs = get(g:, 'gitgutter_max_signs', 500)
  42. endif
  43. let g:gitgutter_signs = get(g:, 'gitgutter_signs', 1)
  44. let g:gitgutter_highlight_lines = get(g:, 'gitgutter_highlight_lines', 0)
  45. let g:gitgutter_highlight_linenrs = get(g:, 'gitgutter_highlight_linenrs', 0)
  46. let g:gitgutter_sign_priority = get(g:, 'gitgutter_sign_priority', 10)
  47. " Nvim 0.4.0 has an expanding sign column
  48. " The sign_place() function supports sign priority.
  49. if (has('nvim-0.4.0') || exists('*sign_place')) && !exists('g:gitgutter_sign_allow_clobber')
  50. let g:gitgutter_sign_allow_clobber = 1
  51. endif
  52. let g:gitgutter_sign_allow_clobber = get(g:, 'gitgutter_sign_allow_clobber', 0)
  53. let g:gitgutter_set_sign_backgrounds = get(g:, 'gitgutter_set_sign_backgrounds', 0)
  54. let g:gitgutter_sign_added = get(g:, 'gitgutter_sign_added', '+')
  55. let g:gitgutter_sign_modified = get(g:, 'gitgutter_sign_modified', '~')
  56. let g:gitgutter_sign_removed = get(g:, 'gitgutter_sign_removed', '_')
  57. if gitgutter#utility#supports_overscore_sign()
  58. let g:gitgutter_sign_removed_first_line = get(g:, 'gitgutter_sign_removed_first_line', '‾')
  59. else
  60. let g:gitgutter_sign_removed_first_line = get(g:, 'gitgutter_sign_removed_first_line', '_^')
  61. endif
  62. let g:gitgutter_sign_removed_above_and_below = get(g:, 'gitgutter_sign_removed_above_and_below', '_¯')
  63. let g:gitgutter_sign_modified_removed = get(g:, 'gitgutter_sign_modified_removed', '~_')
  64. let g:gitgutter_git_args = get(g:, 'gitgutter_git_args', '')
  65. let g:gitgutter_diff_relative_to = get(g:, 'gitgutter_diff_relative_to', 'index')
  66. let g:gitgutter_diff_args = get(g:, 'gitgutter_diff_args', '')
  67. let g:gitgutter_diff_base = get(g:, 'gitgutter_diff_base', '')
  68. let g:gitgutter_map_keys = get(g:, 'gitgutter_map_keys', 1)
  69. let g:gitgutter_terminal_reports_focus = get(g:, 'gitgutter_terminal_reports_focus', 1)
  70. let g:gitgutter_async = get(g:, 'gitgutter_async', 1)
  71. let g:gitgutter_log = get(g:, 'gitgutter_log', 0)
  72. let g:gitgutter_use_location_list = get(g:, 'gitgutter_use_location_list', 0)
  73. let g:gitgutter_close_preview_on_escape = get(g:, 'gitgutter_close_preview_on_escape', 0)
  74. let g:gitgutter_show_msg_on_hunk_jumping = get(g:, 'gitgutter_show_msg_on_hunk_jumping', 1)
  75. let g:gitgutter_git_executable = get(g:, 'gitgutter_git_executable', 'git')
  76. if !executable(g:gitgutter_git_executable)
  77. if g:gitgutter_enabled
  78. call gitgutter#utility#warn('Cannot find git. Please set g:gitgutter_git_executable.')
  79. endif
  80. finish
  81. endif
  82. let default_grep = 'grep'
  83. let g:gitgutter_grep = get(g:, 'gitgutter_grep', default_grep)
  84. if !empty(g:gitgutter_grep)
  85. if executable(split(g:gitgutter_grep)[0])
  86. if $GREP_OPTIONS =~# '--color=always'
  87. let g:gitgutter_grep .= ' --color=never'
  88. endif
  89. else
  90. if g:gitgutter_grep !=# default_grep
  91. call gitgutter#utility#warn('Cannot find '.g:gitgutter_grep.'. Please check g:gitgutter_grep.')
  92. endif
  93. let g:gitgutter_grep = ''
  94. endif
  95. endif
  96. call gitgutter#highlight#define_highlights()
  97. call gitgutter#highlight#define_signs()
  98. " Prevent infinite loop where:
  99. " - executing a job in the foreground launches a new window which takes the focus;
  100. " - when the job finishes, focus returns to gvim;
  101. " - the FocusGained event triggers a new job (see below).
  102. if gitgutter#utility#windows() && !(g:gitgutter_async && gitgutter#async#available())
  103. set noshelltemp
  104. endif
  105. " }}}
  106. " Primary functions {{{
  107. command! -bar GitGutterAll call gitgutter#all(1)
  108. command! -bar GitGutter call gitgutter#process_buffer(bufnr(''), 1)
  109. command! -bar GitGutterDisable call gitgutter#disable()
  110. command! -bar GitGutterEnable call gitgutter#enable()
  111. command! -bar GitGutterToggle call gitgutter#toggle()
  112. command! -bar GitGutterBufferDisable call gitgutter#buffer_disable()
  113. command! -bar GitGutterBufferEnable call gitgutter#buffer_enable()
  114. command! -bar GitGutterBufferToggle call gitgutter#buffer_toggle()
  115. command! -bar GitGutterQuickFix call gitgutter#quickfix(0)
  116. command! -bar GitGutterQuickFixCurrentFile call gitgutter#quickfix(1)
  117. command! -bar GitGutterDiffOrig call gitgutter#difforig()
  118. " }}}
  119. " Line highlights {{{
  120. command! -bar GitGutterLineHighlightsDisable call gitgutter#highlight#line_disable()
  121. command! -bar GitGutterLineHighlightsEnable call gitgutter#highlight#line_enable()
  122. command! -bar GitGutterLineHighlightsToggle call gitgutter#highlight#line_toggle()
  123. " }}}
  124. " 'number' column highlights {{{
  125. command! -bar GitGutterLineNrHighlightsDisable call gitgutter#highlight#linenr_disable()
  126. command! -bar GitGutterLineNrHighlightsEnable call gitgutter#highlight#linenr_enable()
  127. command! -bar GitGutterLineNrHighlightsToggle call gitgutter#highlight#linenr_toggle()
  128. " }}}
  129. " Signs {{{
  130. command! -bar GitGutterSignsEnable call gitgutter#sign#enable()
  131. command! -bar GitGutterSignsDisable call gitgutter#sign#disable()
  132. command! -bar GitGutterSignsToggle call gitgutter#sign#toggle()
  133. " }}}
  134. " Hunks {{{
  135. command! -bar -count=1 GitGutterNextHunk call gitgutter#hunk#next_hunk(<count>)
  136. command! -bar -count=1 GitGutterPrevHunk call gitgutter#hunk#prev_hunk(<count>)
  137. command! -bar -range=% GitGutterStageHunk call gitgutter#hunk#stage(<line1>,<line2>)
  138. command! -bar GitGutterUndoHunk call gitgutter#hunk#undo()
  139. command! -bar GitGutterPreviewHunk call gitgutter#hunk#preview()
  140. " Hunk text object
  141. onoremap <silent> <Plug>(GitGutterTextObjectInnerPending) :<C-U>call gitgutter#hunk#text_object(1)<CR>
  142. onoremap <silent> <Plug>(GitGutterTextObjectOuterPending) :<C-U>call gitgutter#hunk#text_object(0)<CR>
  143. xnoremap <silent> <Plug>(GitGutterTextObjectInnerVisual) :<C-U>call gitgutter#hunk#text_object(1)<CR>
  144. xnoremap <silent> <Plug>(GitGutterTextObjectOuterVisual) :<C-U>call gitgutter#hunk#text_object(0)<CR>
  145. " Returns the git-diff hunks for the file or an empty list if there
  146. " aren't any hunks.
  147. "
  148. " The return value is a list of lists. There is one inner list per hunk.
  149. "
  150. " [
  151. " [from_line, from_count, to_line, to_count],
  152. " [from_line, from_count, to_line, to_count],
  153. " ...
  154. " ]
  155. "
  156. " where:
  157. "
  158. " `from` - refers to the staged file
  159. " `to` - refers to the working tree's file
  160. " `line` - refers to the line number where the change starts
  161. " `count` - refers to the number of lines the change covers
  162. function! GitGutterGetHunks()
  163. let bufnr = bufnr('')
  164. return gitgutter#utility#is_active(bufnr) ? gitgutter#hunk#hunks(bufnr) : []
  165. endfunction
  166. " Returns an array that contains a summary of the hunk status for the current
  167. " window. The format is [ added, modified, removed ], where each value
  168. " represents the number of lines added/modified/removed respectively.
  169. function! GitGutterGetHunkSummary()
  170. return gitgutter#hunk#summary(winbufnr(0))
  171. endfunction
  172. " }}}
  173. " Folds {{{
  174. command! -bar GitGutterFold call gitgutter#fold#toggle()
  175. " }}}
  176. command! -bar GitGutterDebug call gitgutter#debug#debug()
  177. " Maps {{{
  178. nnoremap <silent> <expr> <Plug>(GitGutterNextHunk) &diff ? ']c' : ":\<C-U>execute v:count1 . 'GitGutterNextHunk'\<CR>"
  179. nnoremap <silent> <expr> <Plug>GitGutterNextHunk &diff ? ']c' : ":\<C-U>call gitgutter#utility#warn('Please change your map \<lt>Plug>GitGutterNextHunk to \<lt>Plug>(GitGutterNextHunk)')\<CR>"
  180. nnoremap <silent> <expr> <Plug>(GitGutterPrevHunk) &diff ? '[c' : ":\<C-U>execute v:count1 . 'GitGutterPrevHunk'\<CR>"
  181. nnoremap <silent> <expr> <Plug>GitGutterPrevHunk &diff ? '[c' : ":\<C-U>call gitgutter#utility#warn('Please change your map \<lt>Plug>GitGutterPrevHunk to \<lt>Plug>(GitGutterPrevHunk)')\<CR>"
  182. xnoremap <silent> <Plug>(GitGutterStageHunk) :GitGutterStageHunk<CR>
  183. xnoremap <silent> <Plug>GitGutterStageHunk :call gitgutter#utility#warn('Please change your map <lt>Plug>GitGutterStageHunk to <lt>Plug>(GitGutterStageHunk)')<CR>
  184. nnoremap <silent> <Plug>(GitGutterStageHunk) :GitGutterStageHunk<CR>
  185. nnoremap <silent> <Plug>GitGutterStageHunk :call gitgutter#utility#warn('Please change your map <lt>Plug>GitGutterStageHunk to <lt>Plug>(GitGutterStageHunk)')<CR>
  186. nnoremap <silent> <Plug>(GitGutterUndoHunk) :GitGutterUndoHunk<CR>
  187. nnoremap <silent> <Plug>GitGutterUndoHunk :call gitgutter#utility#warn('Please change your map <lt>Plug>GitGutterUndoHunk to <lt>Plug>(GitGutterUndoHunk)')<CR>
  188. nnoremap <silent> <Plug>(GitGutterPreviewHunk) :GitGutterPreviewHunk<CR>
  189. nnoremap <silent> <Plug>GitGutterPreviewHunk :call gitgutter#utility#warn('Please change your map <lt>Plug>GitGutterPreviewHunk to <lt>Plug>(GitGutterPreviewHunk)')<CR>
  190. " }}}
  191. function! s:on_bufenter()
  192. call gitgutter#setup_maps()
  193. " To keep vim's start-up fast, do not process the buffer when vim is starting.
  194. " Instead process it a short time later. Normally we would rely on our
  195. " CursorHold autocommand to handle this but it turns out CursorHold is not
  196. " guaranteed to fire if the user has not typed anything yet; so set up a
  197. " timer instead. The disadvantage is that if CursorHold does fire, the
  198. " plugin will do a round of unnecessary work; but since there will not have
  199. " been any changes to the buffer since the first round, the second round
  200. " will be cheap.
  201. if has('vim_starting') && !$VIM_GITGUTTER_TEST
  202. if exists('*timer_start') && has('lambda')
  203. call s:next_tick("call gitgutter#process_buffer(+".bufnr('').", 0)")
  204. else
  205. call gitgutter#process_buffer(bufnr(''), 0)
  206. endif
  207. return
  208. endif
  209. if exists('t:gitgutter_didtabenter') && t:gitgutter_didtabenter
  210. let t:gitgutter_didtabenter = 0
  211. call gitgutter#all(!g:gitgutter_terminal_reports_focus)
  212. else
  213. call gitgutter#process_buffer(bufnr(''), !g:gitgutter_terminal_reports_focus)
  214. endif
  215. endfunction
  216. function! s:next_tick(cmd)
  217. call timer_start(1, {-> execute(a:cmd)})
  218. endfunction
  219. " Autocommands {{{
  220. augroup gitgutter
  221. autocmd!
  222. autocmd TabEnter * let t:gitgutter_didtabenter = 1
  223. autocmd BufEnter * call s:on_bufenter()
  224. " Ensure Vim is always checking for CursorMoved to avoid CursorMoved
  225. " being fired at the wrong time in floating preview window on Neovim.
  226. " See vim/vim#2053.
  227. autocmd CursorMoved * execute ''
  228. autocmd CursorHold,CursorHoldI * call gitgutter#process_buffer(bufnr(''), 0)
  229. if exists('*timer_start') && has('lambda')
  230. autocmd FileChangedShellPost * call s:next_tick("call gitgutter#process_buffer(+".expand('<abuf>').", 1)")
  231. else
  232. autocmd FileChangedShellPost * call gitgutter#process_buffer(+expand('<abuf>'), 1)
  233. endif
  234. " Ensure that all buffers are processed when opening vim with multiple files, e.g.:
  235. "
  236. " vim -o file1 file2
  237. autocmd VimEnter * if winnr() != winnr('$') | call gitgutter#all(0) | endif
  238. autocmd ShellCmdPost * call gitgutter#all(1)
  239. autocmd BufLeave term://* call gitgutter#all(1)
  240. autocmd User FugitiveChanged call gitgutter#all(1)
  241. autocmd BufFilePre * GitGutterBufferDisable
  242. autocmd BufFilePost * GitGutterBufferEnable
  243. " Handle all buffers when focus is gained, but only after it was lost.
  244. " FocusGained gets triggered on startup with Neovim at least already.
  245. " Therefore this tracks also if it was lost before.
  246. let s:focus_was_lost = 0
  247. autocmd FocusGained * if s:focus_was_lost | let s:focus_was_lost = 0 | call gitgutter#all(1) | endif
  248. autocmd FocusLost * let s:focus_was_lost = 1
  249. if exists('##VimResume')
  250. autocmd VimResume * call gitgutter#all(1)
  251. endif
  252. autocmd ColorScheme * call gitgutter#highlight#define_highlights()
  253. " Disable during :vimgrep
  254. autocmd QuickFixCmdPre *vimgrep* let [g:gitgutter_was_enabled, g:gitgutter_enabled] = [g:gitgutter_enabled, 0]
  255. autocmd QuickFixCmdPost *vimgrep* let g:gitgutter_enabled = g:gitgutter_was_enabled | unlet g:gitgutter_was_enabled
  256. augroup END
  257. " }}}
  258. " vim:set et sw=2 fdm=marker: