Import upstream version 0~20200414+git20220223.1.18d1298
Debian Janitor
2 years ago
0 | 0 | ## vim-gitgutter |
1 | 1 | |
2 | A Vim plugin which shows a git diff in the 'gutter' (sign column). It shows which lines have been added, modified, or removed. You can also preview, stage, and undo individual hunks; and stage partial hunks. The plugin also provides a hunk text object. | |
2 | A Vim plugin which shows a git diff in the sign column. It shows which lines have been added, modified, or removed. You can also preview, stage, and undo individual hunks; and stage partial hunks. The plugin also provides a hunk text object. | |
3 | 3 | |
4 | 4 | The signs are always up to date and the plugin never saves your buffer. |
5 | ||
6 | The name "gitgutter" comes from the Sublime Text 3 plugin which inspired this in 2013. | |
5 | 7 | |
6 | 8 | Features: |
7 | 9 | |
32 | 34 | * Supports git only. If you work with other version control systems, I recommend [vim-signify](https://github.com/mhinz/vim-signify). |
33 | 35 | * Relies on the `FocusGained` event. If your terminal doesn't report focus events, either use something like [Terminus][] or set `let g:gitgutter_terminal_reports_focus=0`. For tmux, `set -g focus-events on` in your tmux.conf. |
34 | 36 | |
37 | Compatibility: | |
38 | ||
39 | Compatible back to Vim 7.4, and probably 7.3. | |
40 | ||
35 | 41 | |
36 | 42 | ### Screenshot |
37 | 43 | |
70 | 76 | |
71 | 77 | ### Windows |
72 | 78 | |
73 | I recommend configuring vim-gitgutter with the full path to your git executable. For example: | |
74 | ||
75 | ```viml | |
79 | There is a potential risk on Windows due to `cmd.exe` prioritising the current folder over folders in `PATH`. If you have a file named `git.*` (i.e. with any extension in `PATHEXT`) in your current folder, it will be executed instead of git whenever the plugin calls git. | |
80 | ||
81 | You can avoid this risk by configuring the full path to your git executable. For example: | |
82 | ||
83 | ```viml | |
84 | " This path probably won't work | |
76 | 85 | let g:gitgutter_git_executable = 'C:\Program Files\Git\bin\git.exe' |
77 | 86 | ``` |
78 | 87 | |
79 | This is to avoid a problem which occurs if you have file named `git.*` (i.e. with any extension in `PATHEXT`) in your current folder. `cmd.exe` prioritises the current folder over folders in `PATH` and will try to execute your file instead of the `git` binary. | |
88 | Unfortunately I don't know the correct escaping for the path - if you do, please let me know! | |
80 | 89 | |
81 | 90 | |
82 | 91 | ### Getting started |
86 | 95 | You can jump between hunks with `[c` and `]c`. You can preview, stage, and undo hunks with `<leader>hp`, `<leader>hs`, and `<leader>hu` respectively. |
87 | 96 | |
88 | 97 | You cannot unstage a staged hunk. |
98 | ||
99 | After updating the signs, the plugin fires the `GitGutter` User autocommand. | |
100 | ||
101 | After staging a hunk or part of a hunk, the plugin fires the `GitGutterStage` User autocommand. | |
89 | 102 | |
90 | 103 | |
91 | 104 | #### Activation |
126 | 139 | |
127 | 140 | If you switch off both line highlighting and signs, you won't see the sign column. |
128 | 141 | |
129 | To keep your Vim snappy, vim-gitgutter will suppress the signs when a file has more than 500 changes. As soon as the number of changes falls below the limit vim-gitgutter will show the signs again. You can configure the threshold with: | |
130 | ||
131 | ```viml | |
132 | let g:gitgutter_max_signs = 500 " default value | |
133 | ``` | |
142 | In older Vims (pre 8.1.0614 / Neovim 0.4.0) vim-gitgutter will suppress the signs when a file has more than 500 changes, to avoid slowing down the UI. As soon as the number of changes falls below the limit vim-gitgutter will show the signs again. You can configure the threshold with: | |
143 | ||
144 | ```viml | |
145 | let g:gitgutter_max_signs = 500 " default value (Vim < 8.1.0614, Neovim < 0.4.0) | |
146 | let g:gitgutter_max_signs = -1 " default value (otherwise) | |
147 | ``` | |
148 | ||
149 | You can also remove the limit by setting `g:gitgutter_max_signs = -1`. | |
134 | 150 | |
135 | 151 | #### Hunks |
136 | 152 | |
148 | 164 | nmap [h <Plug>(GitGutterPrevHunk) |
149 | 165 | ``` |
150 | 166 | |
151 | You can load all your hunks into the quickfix list with `:GitGutterQuickFix`. Note this ignores any unsaved changes in your buffers. If the option `g:gitgutter_use_location_list` is set, this command will load hunks into the current window's location list instead. | |
167 | When you jump between hunks, a message like `Hunk 4 of 11` is shown on the command line. If you want to turn the message off, you can use: | |
168 | ||
169 | ```viml | |
170 | let g:gitgutter_show_msg_on_hunk_jumping = 0 | |
171 | ``` | |
172 | ||
173 | You can load all your hunks into the quickfix list with `:GitGutterQuickFix`. Note this ignores any unsaved changes in your buffers. If the option `g:gitgutter_use_location_list` is set, this command will load hunks into the current window's location list instead. Use `:copen` (or `:lopen`) to open the quickfix / location list or add a custom command like this: | |
174 | ||
175 | ```viml | |
176 | command! Gqf GitGutterQuickFix | copen | |
177 | ``` | |
152 | 178 | |
153 | 179 | You can stage or undo an individual hunk when your cursor is in it: |
154 | 180 | |
256 | 282 | * How to handle non-gitgutter signs |
257 | 283 | * The signs' colours and symbols |
258 | 284 | * Line highlights |
285 | * Line number highlights (only in Neovim 0.3.2 or higher) | |
286 | * The diff syntax colours used in the preview window | |
287 | * The intra-line diff highlights used in the preview window | |
259 | 288 | * Whether the diff is relative to the index (default) or working tree. |
260 | 289 | * The base of the diff |
261 | 290 | * Extra arguments for `git` when running `git diff` |
269 | 298 | * Whether to clobber or preserve non-gitgutter signs |
270 | 299 | * The priority of gitgutter's signs. |
271 | 300 | * Whether to use a floating/popup window for hunk previews |
301 | * The appearance of a floating/popup window for hunk previews | |
272 | 302 | * Whether to populate the quickfix list or a location list with all hunks |
273 | 303 | |
274 | 304 | Please note that vim-gitgutter won't override any colours or highlights you've set in your colorscheme. |
276 | 306 | |
277 | 307 | #### Sign column |
278 | 308 | |
279 | By default vim-gitgutter will make the sign column look like the line number column. | |
280 | ||
281 | To customise your sign column's background color, first tell vim-gitgutter to leave it alone: | |
282 | ||
283 | ```viml | |
284 | let g:gitgutter_override_sign_column_highlight = 0 | |
285 | ``` | |
286 | ||
287 | And then either update your colorscheme's `SignColumn` highlight group or set it in your vimrc: | |
288 | ||
289 | ```viml | |
290 | highlight SignColumn ctermbg=whatever " terminal Vim | |
291 | highlight SignColumn guibg=whatever " gVim/MacVim | |
309 | Set the `SignColumn` highlight group to change the sign column's colour. For example: | |
310 | ||
311 | ```viml | |
312 | " vim-gitgutter used to do this by default: | |
313 | highlight! link SignColumn LineNr | |
314 | ||
315 | " or you could do this: | |
316 | highlight SignColumn guibg=whatever ctermbg=whatever | |
292 | 317 | ``` |
293 | 318 | |
294 | 319 | By default the sign column will appear when there are signs to show and disappear when there aren't. To always have the sign column, add to your vimrc: |
307 | 332 | |
308 | 333 | #### Signs' colours and symbols |
309 | 334 | |
310 | By default vim-gitgutter uses your colourscheme's `Diff*` highlight groups' foreground colours for the signs' foreground colours. For example, your `DiffAdd` foreground colour will be used for the `+` sign's foreground colour. | |
311 | ||
312 | The signs' background colours will all be set to the sign column's background colour. | |
313 | ||
314 | If you don't like the default colours, you can either fix your colourscheme's `Diff*` highlights or configure your own `GitGutter*` highlight groups. These groups are: | |
315 | ||
316 | ```viml | |
317 | GitGutterAdd " an added line (default: links to DiffAdd) | |
318 | GitGutterChange " a changed line (default: links to DiffChange) | |
319 | GitGutterDelete " at least one removed line (default: links to DiffDelete) | |
320 | GitGutterChangeDelete " a changed line followed by at least one removed line (default: links to GitGutterChange) | |
321 | ``` | |
322 | ||
323 | You can either set these with `highlight GitGutterAdd {key}={arg}...` or link them to existing highlight groups with, say, `highlight link GitGutterAdd MyDiffAdd`. | |
324 | ||
325 | To get vim-gitgutter's original colours (based on git-diff's colours in my terminal): | |
326 | ||
327 | ```viml | |
328 | highlight GitGutterAdd guifg=#009900 guibg=<X> ctermfg=2 ctermbg=<Y> | |
329 | highlight GitGutterChange guifg=#bbbb00 guibg=<X> ctermfg=3 ctermbg=<Y> | |
330 | highlight GitGutterDelete guifg=#ff2222 guibg=<X> ctermfg=1 ctermbg=<Y> | |
331 | ``` | |
332 | ||
333 | – where you would replace `<X>` and `<Y>` with the background colour of your `SignColumn` in the gui and the terminal respectively. For example, with the solarized colorscheme and a dark background, `guibg=#073642` and `ctermbg=0`. | |
335 | If you or your colourscheme has defined `GitGutter*` highlight groups, the plugin will use them for the signs' colours. | |
336 | ||
337 | If you want the background colours to match the sign column, but don't want to update the `GitGutter*` groups yourself, you can get the plugin to do it: | |
338 | ||
339 | ```viml | |
340 | let g:gitgutter_set_sign_backgrounds = 1 | |
341 | ``` | |
342 | ||
343 | If no `GitGutter*` highlight groups exist, the plugin will check the `Diff*` highlight groups. If their foreground colours differ the plugin will use them; if not, these colours will be used: | |
344 | ||
345 | ```viml | |
346 | highlight GitGutterAdd guifg=#009900 ctermfg=2 | |
347 | highlight GitGutterChange guifg=#bbbb00 ctermfg=3 | |
348 | highlight GitGutterDelete guifg=#ff2222 ctermfg=1 | |
349 | ``` | |
334 | 350 | |
335 | 351 | To customise the symbols, add the following to your `~/.vimrc`: |
336 | 352 | |
339 | 355 | let g:gitgutter_sign_modified = 'yy' |
340 | 356 | let g:gitgutter_sign_removed = 'zz' |
341 | 357 | let g:gitgutter_sign_removed_first_line = '^^' |
358 | let g:gitgutter_sign_removed_above_and_below = '{' | |
342 | 359 | let g:gitgutter_sign_modified_removed = 'ww' |
343 | 360 | ``` |
344 | 361 | |
381 | 398 | ``` |
382 | 399 | |
383 | 400 | |
401 | #### The diff syntax colours used in the preview window | |
402 | ||
403 | To change the diff syntax colours used in the preview window, set up the `diff*` highlight groups in your colorscheme or `~/.vimrc`: | |
404 | ||
405 | ```viml | |
406 | diffAdded " if not set: use GitGutterAdd's foreground colour | |
407 | diffChanged " if not set: use GitGutterChange's foreground colour | |
408 | diffRemoved " if not set: use GitGutterDelete's foreground colour | |
409 | ``` | |
410 | ||
411 | Note the `diff*` highlight groups are used in any buffer whose `'syntax'` is `diff`. | |
412 | ||
413 | ||
414 | #### The intra-line diff highlights used in the preview window | |
415 | ||
416 | To change the intra-line diff highlights used in the preview window, set up the following highlight groups in your colorscheme or `~/.vimrc`: | |
417 | ||
418 | ```viml | |
419 | GitGutterAddIntraLine " default: gui=reverse cterm=reverse | |
420 | GitGutterDeleteIntraLine " default: gui=reverse cterm=reverse | |
421 | ``` | |
422 | ||
423 | For example, to use `DiffAdd` for intra-line added regions: | |
424 | ||
425 | ```viml | |
426 | highlight link GitGutterAddIntraLine DiffAdd | |
427 | ``` | |
428 | ||
429 | ||
384 | 430 | #### Whether the diff is relative to the index or working tree |
385 | 431 | |
386 | 432 | By default diffs are relative to the index. How you can make them relative to the working tree: |
471 | 517 | #### To use floating/popup windows for hunk previews |
472 | 518 | |
473 | 519 | Add `let g:gitgutter_preview_win_floating = 1` to your `~/.vimrc`. Note that on Vim this prevents you staging (partial) hunks via the preview window. |
520 | ||
521 | ||
522 | #### The appearance of a floating/popup window for hunk previews | |
523 | ||
524 | Set `g:gitgutter_floating_window_options` to a dictionary of the options you want. This dictionary is passed directly to `popup_create()` (Vim) / `nvim_open_win()` (Neovim). | |
474 | 525 | |
475 | 526 | |
476 | 527 | #### To load all hunks into the current window's location list instead of the quickfix list |
633 | 684 | |
634 | 685 | Your colorscheme is configuring the `SignColumn` highlight group weirdly. Please see the section above on customising the sign column. |
635 | 686 | |
636 | > Why are the colours in the preview window weird? | |
637 | ||
638 | Probably because your colourscheme doesn't configure the `diff{Added,Changed,Removed}` highlight groups. Try this in `after/syntax/diff.vim`: | |
639 | ||
640 | ```viml | |
641 | highlight link diffAdded DiffAdd | |
642 | highlight link diffChanged DiffChange | |
643 | highlight link diffRemoved DiffDelete | |
644 | ``` | |
645 | ||
646 | 687 | > What happens if I also use another plugin which uses signs (e.g. Syntastic)? |
647 | 688 | |
648 | 689 | You can configure whether GitGutter preserves or clobbers other signs using `g:gitgutter_sign_allow_clobber`. Set to `1` to clobber other signs (default on Vim >= 8.1.0614 and NeoVim >= 0.4.0) or `0` to preserve them. |
658 | 699 | * Verify `:echo system("git --version")` succeeds. |
659 | 700 | * Verify your git config is compatible with the version of git returned by the command above. |
660 | 701 | * Verify your Vim supports signs (`:echo has('signs')` should give `1`). |
661 | * Verify your file is being tracked by git and has unstaged changes. | |
702 | * Verify your file is being tracked by git and has unstaged changes. Check whether the plugin thinks git knows about your file: `:echo b:gitgutter.path` should show the path to the file in the repo. | |
703 | * Execute `:sign place group=gitgutter`; you should see a list of signs. | |
704 | - If the signs are listed: this is a colorscheme / highlight problem. Compare `:highlight GitGutterAdd` with `:highlight SignColumn`. | |
705 | - If no signs are listed: the call to git-diff is probably failing. Add `let g:gitgutter_log=1` to your vimrc, restart, reproduce the problem, and look at the `gitgutter.log` file in the plugin's directory. | |
662 | 706 | |
663 | 707 | #### When the whole file is marked as added |
664 | 708 | |
680 | 724 | * [Smash Into Vim][siv] |
681 | 725 | |
682 | 726 | This was one of PeepCode's all-time top three bestsellers and is now available at Pluralsight. |
683 | ||
684 | You can read reviews on my [website][airblade]. | |
685 | 727 | |
686 | 728 | |
687 | 729 | ### Intellectual Property |
4 | 4 | \ (has('patch-7-4-1832') && has('gui_macvim')) |
5 | 5 | \ ) |
6 | 6 | \ ) |
7 | ||
8 | let s:jobs = {} | |
7 | 9 | |
8 | 10 | function! gitgutter#async#available() |
9 | 11 | return s:available |
27 | 29 | \ 'on_exit': function('s:on_exit_nvim') |
28 | 30 | \ })) |
29 | 31 | else |
30 | call job_start(command, { | |
32 | let job = job_start(command, { | |
31 | 33 | \ 'out_cb': function('s:on_stdout_vim', options), |
32 | 34 | \ 'err_cb': function('s:on_stderr_vim', options), |
33 | 35 | \ 'close_cb': function('s:on_exit_vim', options) |
34 | 36 | \ }) |
37 | let s:jobs[s:job_id(job)] = 1 | |
35 | 38 | endif |
36 | 39 | endfunction |
37 | 40 | |
82 | 85 | |
83 | 86 | function! s:on_exit_vim(channel) dict abort |
84 | 87 | let job = ch_getjob(a:channel) |
88 | let jobid = s:job_id(job) | |
89 | if has_key(s:jobs, jobid) | unlet s:jobs[jobid] | endif | |
85 | 90 | while 1 |
86 | 91 | if job_status(job) == 'dead' |
87 | 92 | let exit_code = job_info(job).exitval |
94 | 99 | call self.handler.out(self.buffer, join(self.stdoutbuffer, "\n")) |
95 | 100 | endif |
96 | 101 | endfunction |
102 | ||
103 | function! s:job_id(job) | |
104 | " Vim | |
105 | return job_info(a:job).process | |
106 | endfunction |
21 | 21 | call s:separator() |
22 | 22 | |
23 | 23 | call s:option('updatetime') |
24 | call s:option('shell') | |
25 | call s:option('shellcmdflag') | |
26 | call s:option('shellpipe') | |
27 | call s:option('shellquote') | |
28 | call s:option('shellredir') | |
29 | call s:option('shellslash') | |
30 | call s:option('shelltemp') | |
31 | call s:option('shelltype') | |
32 | call s:option('shellxescape') | |
33 | call s:option('shellxquote') | |
34 | 24 | endfunction |
35 | 25 | |
36 | 26 | |
51 | 41 | endfunction |
52 | 42 | |
53 | 43 | function! s:grep_version() |
54 | let v = system('grep --version') | |
44 | let v = system(g:gitgutter_grep.' --version') | |
55 | 45 | call s:output( substitute(v, '\n$', '', '') ) |
56 | 46 | |
57 | let v = system('grep --help') | |
47 | let v = system(g:gitgutter_grep.' --help') | |
58 | 48 | call s:output( substitute(v, '\%x00', '', 'g') ) |
59 | 49 | endfunction |
60 | 50 |
10 | 10 | endfunction |
11 | 11 | |
12 | 12 | let s:c_flag = s:git_supports_command_line_config_override() |
13 | ||
14 | 13 | |
15 | 14 | let s:temp_from = tempname() |
16 | 15 | let s:temp_buffer = tempname() |
70 | 69 | " grep is available. |
71 | 70 | function! gitgutter#diff#run_diff(bufnr, from, preserve_full_diff) abort |
72 | 71 | if gitgutter#utility#repo_path(a:bufnr, 0) == -1 |
73 | throw 'gitgutter author fail' | |
72 | throw 'gitgutter path not set' | |
74 | 73 | endif |
75 | 74 | |
76 | 75 | if gitgutter#utility#repo_path(a:bufnr, 0) == -2 |
119 | 118 | endif |
120 | 119 | |
121 | 120 | " Write file from index to temporary file. |
122 | let index_name = g:gitgutter_diff_base.':'.gitgutter#utility#repo_path(a:bufnr, 1) | |
121 | let index_name = gitgutter#utility#get_diff_base(a:bufnr).':'.gitgutter#utility#repo_path(a:bufnr, 1) | |
123 | 122 | let cmd .= g:gitgutter_git_executable.' '.g:gitgutter_git_args.' --no-pager show '.index_name.' > '.from_file.' && ' |
124 | 123 | |
125 | 124 | elseif a:from ==# 'working_tree' |
127 | 126 | endif |
128 | 127 | |
129 | 128 | " Call git-diff. |
130 | let cmd .= g:gitgutter_git_executable.' '.g:gitgutter_git_args.' --no-pager '.g:gitgutter_git_args | |
129 | let cmd .= g:gitgutter_git_executable.' '.g:gitgutter_git_args.' --no-pager' | |
131 | 130 | if s:c_flag |
132 | 131 | let cmd .= ' -c "diff.autorefreshindex=0"' |
133 | 132 | let cmd .= ' -c "diff.noprefix=false"' |
181 | 180 | let modified_lines = gitgutter#diff#process_hunks(a:bufnr, gitgutter#hunk#hunks(a:bufnr)) |
182 | 181 | |
183 | 182 | let signs_count = len(modified_lines) |
184 | if signs_count > g:gitgutter_max_signs | |
183 | if g:gitgutter_max_signs != -1 && signs_count > g:gitgutter_max_signs | |
185 | 184 | call gitgutter#utility#warn_once(a:bufnr, printf( |
186 | 185 | \ 'exceeded maximum number of signs (%d > %d, configured by g:gitgutter_max_signs).', |
187 | 186 | \ signs_count, g:gitgutter_max_signs), 'max_signs') |
399 | 398 | let bufcontents[0]=''.bufcontents[0] |
400 | 399 | endif |
401 | 400 | |
402 | call writefile(bufcontents, a:file, 'b') | |
401 | " The file we are writing to is a temporary file. Sometimes the parent | |
402 | " directory is deleted outside Vim but, because Vim caches the directory | |
403 | " name at startup and does not check for its existence subsequently, Vim | |
404 | " does not realise. This causes E482 errors. | |
405 | try | |
406 | call writefile(bufcontents, a:file, 'b') | |
407 | catch /E482/ | |
408 | call mkdir(fnamemodify(a:file, ':h'), '', '0700') | |
409 | call writefile(bufcontents, a:file, 'b') | |
410 | endtry | |
403 | 411 | endfunction |
404 | 412 | |
405 | 413 |
62 | 62 | endif |
63 | 63 | endfunction |
64 | 64 | |
65 | ||
66 | function! gitgutter#highlight#define_sign_column_highlight() abort | |
67 | if g:gitgutter_override_sign_column_highlight | |
68 | highlight! link SignColumn LineNr | |
69 | else | |
70 | highlight default link SignColumn LineNr | |
71 | endif | |
72 | endfunction | |
73 | 65 | |
74 | 66 | function! gitgutter#highlight#define_highlights() abort |
75 | 67 | let [guibg, ctermbg] = s:get_background_colors('SignColumn') |
83 | 75 | highlight default link GitGutterChangeDeleteInvisible GitGutterChangeInvisible |
84 | 76 | |
85 | 77 | " When they are visible. |
86 | " By default use Diff* foreground colors with SignColumn's background. | |
87 | for type in ['Add', 'Change', 'Delete'] | |
88 | let [guifg, ctermfg] = s:get_foreground_colors('Diff'.type) | |
89 | execute "highlight GitGutter".type."Default guifg=".guifg." guibg=".guibg." ctermfg=".ctermfg." ctermbg=".ctermbg | |
90 | execute "highlight default link GitGutter".type." GitGutter".type."Default" | |
78 | for type in ["Add", "Change", "Delete"] | |
79 | if hlexists("GitGutter".type) && s:get_foreground_colors("GitGutter".type) != ['NONE', 'NONE'] | |
80 | if g:gitgutter_set_sign_backgrounds | |
81 | execute "highlight GitGutter".type." guibg=".guibg." ctermbg=".ctermbg | |
82 | endif | |
83 | continue | |
84 | elseif s:useful_diff_colours() | |
85 | let [guifg, ctermfg] = s:get_foreground_colors('Diff'.type) | |
86 | else | |
87 | let [guifg, ctermfg] = s:get_foreground_fallback_colors(type) | |
88 | endif | |
89 | execute "highlight GitGutter".type." guifg=".guifg." guibg=".guibg." ctermfg=".ctermfg." ctermbg=".ctermbg | |
91 | 90 | endfor |
91 | ||
92 | if hlexists("GitGutterChangeDelete") && g:gitgutter_set_sign_backgrounds | |
93 | execute "highlight GitGutterChangeDelete guibg=".guibg." ctermbg=".ctermbg | |
94 | endif | |
95 | ||
92 | 96 | highlight default link GitGutterChangeDelete GitGutterChange |
93 | 97 | |
94 | 98 | " Highlights used for the whole line. |
104 | 108 | highlight default link GitGutterChangeDeleteLineNr CursorLineNr |
105 | 109 | |
106 | 110 | " Highlights used intra line. |
107 | highlight GitGutterAddIntraLine gui=reverse cterm=reverse | |
108 | highlight GitGutterDeleteIntraLine gui=reverse cterm=reverse | |
111 | highlight default GitGutterAddIntraLine gui=reverse cterm=reverse | |
112 | highlight default GitGutterDeleteIntraLine gui=reverse cterm=reverse | |
113 | " Set diff syntax colours (used in the preview window) - diffAdded,diffChanged,diffRemoved - | |
114 | " to match the signs, if not set aleady. | |
115 | for [dtype,type] in [['Added','Add'], ['Changed','Change'], ['Removed','Delete']] | |
116 | if !hlexists('diff'.dtype) | |
117 | let [guifg, ctermfg] = s:get_foreground_colors('GitGutter'.type) | |
118 | execute "highlight diff".dtype." guifg=".guifg." ctermfg=".ctermfg." guibg=NONE ctermbg=NONE" | |
119 | endif | |
120 | endfor | |
109 | 121 | endfunction |
110 | 122 | |
111 | 123 | function! gitgutter#highlight#define_signs() abort |
162 | 174 | sign define GitGutterLineRemovedAboveAndBelow linehl=GitGutterDeleteLine |
163 | 175 | sign define GitGutterLineModifiedRemoved linehl=GitGutterChangeDeleteLine |
164 | 176 | else |
165 | sign define GitGutterLineAdded linehl= | |
166 | sign define GitGutterLineModified linehl= | |
167 | sign define GitGutterLineRemoved linehl= | |
168 | sign define GitGutterLineRemovedFirstLine linehl= | |
169 | sign define GitGutterLineRemovedAboveAndBelow linehl= | |
170 | sign define GitGutterLineModifiedRemoved linehl= | |
177 | sign define GitGutterLineAdded linehl=NONE | |
178 | sign define GitGutterLineModified linehl=NONE | |
179 | sign define GitGutterLineRemoved linehl=NONE | |
180 | sign define GitGutterLineRemovedFirstLine linehl=NONE | |
181 | sign define GitGutterLineRemovedAboveAndBelow linehl=NONE | |
182 | sign define GitGutterLineModifiedRemoved linehl=NONE | |
171 | 183 | endif |
172 | 184 | endfunction |
173 | 185 | |
182 | 194 | sign define GitGutterLineRemovedAboveAndBelow numhl=GitGutterDeleteLineNr |
183 | 195 | sign define GitGutterLineModifiedRemoved numhl=GitGutterChangeDeleteLineNr |
184 | 196 | else |
185 | sign define GitGutterLineAdded numhl= | |
186 | sign define GitGutterLineModified numhl= | |
187 | sign define GitGutterLineRemoved numhl= | |
188 | sign define GitGutterLineRemovedFirstLine numhl= | |
189 | sign define GitGutterLineRemovedAboveAndBelow numhl= | |
190 | sign define GitGutterLineModifiedRemoved numhl= | |
197 | sign define GitGutterLineAdded numhl=NONE | |
198 | sign define GitGutterLineModified numhl=NONE | |
199 | sign define GitGutterLineRemoved numhl=NONE | |
200 | sign define GitGutterLineRemovedFirstLine numhl=NONE | |
201 | sign define GitGutterLineRemovedAboveAndBelow numhl=NONE | |
202 | sign define GitGutterLineModifiedRemoved numhl=NONE | |
191 | 203 | endif |
192 | 204 | catch /E475/ |
193 | 205 | endtry |
213 | 225 | let guibg = s:get_hl(a:group, 'bg', 'gui') |
214 | 226 | return [guibg, ctermbg] |
215 | 227 | endfunction |
228 | ||
229 | function! s:useful_diff_colours() | |
230 | let [guifg_add, ctermfg_add] = s:get_foreground_colors('DiffAdd') | |
231 | let [guifg_del, ctermfg_del] = s:get_foreground_colors('DiffDelete') | |
232 | ||
233 | return guifg_add != guifg_del && ctermfg_add != ctermfg_del | |
234 | endfunction | |
235 | ||
236 | function! s:get_foreground_fallback_colors(type) | |
237 | if a:type == 'Add' | |
238 | return ['#009900', '2'] | |
239 | elseif a:type == 'Change' | |
240 | return ['#bbbb00', '3'] | |
241 | elseif a:type == 'Delete' | |
242 | return ['#ff2222', '1'] | |
243 | endif | |
244 | endfunction |
0 | 0 | let s:winid = 0 |
1 | let s:preview_bufnr = 0 | |
2 | let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : '' | |
1 | 3 | |
2 | 4 | function! gitgutter#hunk#set_hunks(bufnr, hunks) abort |
3 | 5 | call gitgutter#utility#setbufvar(a:bufnr, 'hunks', a:hunks) |
43 | 45 | |
44 | 46 | function! gitgutter#hunk#next_hunk(count) abort |
45 | 47 | let bufnr = bufnr('') |
46 | if gitgutter#utility#is_active(bufnr) | |
47 | let current_line = line('.') | |
48 | let hunk_count = 0 | |
49 | for hunk in gitgutter#hunk#hunks(bufnr) | |
50 | if hunk[2] > current_line | |
51 | let hunk_count += 1 | |
52 | if hunk_count == a:count | |
53 | execute 'normal!' hunk[2] . 'Gzv' | |
54 | return | |
48 | if !gitgutter#utility#is_active(bufnr) | return | endif | |
49 | ||
50 | let hunks = gitgutter#hunk#hunks(bufnr) | |
51 | if empty(hunks) | |
52 | call gitgutter#utility#warn('No hunks in file') | |
53 | return | |
54 | endif | |
55 | ||
56 | let current_line = line('.') | |
57 | let hunk_count = 0 | |
58 | for hunk in hunks | |
59 | if hunk[2] > current_line | |
60 | let hunk_count += 1 | |
61 | if hunk_count == a:count | |
62 | execute 'normal!' hunk[2] . 'Gzv' | |
63 | if g:gitgutter_show_msg_on_hunk_jumping | |
64 | redraw | echo printf('Hunk %d of %d', index(hunks, hunk) + 1, len(hunks)) | |
55 | 65 | endif |
66 | if gitgutter#hunk#is_preview_window_open() | |
67 | call gitgutter#hunk#preview() | |
68 | endif | |
69 | return | |
56 | 70 | endif |
57 | endfor | |
58 | call gitgutter#utility#warn('No more hunks') | |
59 | endif | |
71 | endif | |
72 | endfor | |
73 | call gitgutter#utility#warn('No more hunks') | |
60 | 74 | endfunction |
61 | 75 | |
62 | 76 | function! gitgutter#hunk#prev_hunk(count) abort |
63 | 77 | let bufnr = bufnr('') |
64 | if gitgutter#utility#is_active(bufnr) | |
65 | let current_line = line('.') | |
66 | let hunk_count = 0 | |
67 | for hunk in reverse(copy(gitgutter#hunk#hunks(bufnr))) | |
68 | if hunk[2] < current_line | |
69 | let hunk_count += 1 | |
70 | if hunk_count == a:count | |
71 | let target = hunk[2] == 0 ? 1 : hunk[2] | |
72 | execute 'normal!' target . 'Gzv' | |
73 | return | |
78 | if !gitgutter#utility#is_active(bufnr) | return | endif | |
79 | ||
80 | let hunks = gitgutter#hunk#hunks(bufnr) | |
81 | if empty(hunks) | |
82 | call gitgutter#utility#warn('No hunks in file') | |
83 | return | |
84 | endif | |
85 | ||
86 | let current_line = line('.') | |
87 | let hunk_count = 0 | |
88 | for hunk in reverse(copy(hunks)) | |
89 | if hunk[2] < current_line | |
90 | let hunk_count += 1 | |
91 | if hunk_count == a:count | |
92 | let target = hunk[2] == 0 ? 1 : hunk[2] | |
93 | execute 'normal!' target . 'Gzv' | |
94 | if g:gitgutter_show_msg_on_hunk_jumping | |
95 | redraw | echo printf('Hunk %d of %d', index(hunks, hunk) + 1, len(hunks)) | |
74 | 96 | endif |
97 | if gitgutter#hunk#is_preview_window_open() | |
98 | call gitgutter#hunk#preview() | |
99 | endif | |
100 | return | |
75 | 101 | endif |
76 | endfor | |
77 | call gitgutter#utility#warn('No previous hunks') | |
78 | endif | |
102 | endif | |
103 | endfor | |
104 | call gitgutter#utility#warn('No previous hunks') | |
79 | 105 | endfunction |
80 | 106 | |
81 | 107 | " Returns the hunk the cursor is currently in or an empty list if the cursor |
222 | 248 | let hunk_diff = join(hunk_header + hunk_body, "\n")."\n" |
223 | 249 | |
224 | 250 | call s:goto_original_window() |
225 | call s:close_hunk_preview_window() | |
251 | call gitgutter#hunk#close_hunk_preview_window() | |
226 | 252 | call s:stage(hunk_diff) |
227 | 253 | endif |
228 | 254 | |
236 | 262 | let g:gitgutter_async = async |
237 | 263 | |
238 | 264 | call gitgutter#hunk#set_hunks(bufnr, gitgutter#diff#parse_diff(diff)) |
265 | call gitgutter#diff#process_hunks(bufnr, gitgutter#hunk#hunks(bufnr)) " so the hunk summary is updated | |
239 | 266 | |
240 | 267 | if empty(s:current_hunk()) |
241 | call gitgutter#utility#warn('cursor is not in a hunk') | |
268 | call gitgutter#utility#warn('Cursor is not in a hunk') | |
242 | 269 | elseif s:cursor_in_two_hunks() |
243 | 270 | let choice = input('Choose hunk: upper or lower (u/l)? ') |
244 | 271 | " Clear input |
248 | 275 | elseif choice =~ 'l' |
249 | 276 | call a:op(gitgutter#diff#hunk_diff(bufnr, diff, 1)) |
250 | 277 | else |
251 | call gitgutter#utility#warn('did not recognise your choice') | |
278 | call gitgutter#utility#warn('Did not recognise your choice') | |
252 | 279 | endif |
253 | 280 | else |
254 | 281 | let hunk_diff = gitgutter#diff#hunk_diff(bufnr, diff) |
272 | 299 | \ gitgutter#utility#cd_cmd(bufnr, g:gitgutter_git_executable.' '.g:gitgutter_git_args.' apply --cached --unidiff-zero - '), |
273 | 300 | \ diff) |
274 | 301 | if v:shell_error |
275 | call gitgutter#utility#warn('patch does not apply') | |
302 | call gitgutter#utility#warn('Patch does not apply') | |
303 | else | |
304 | if exists('#User#GitGutterStage') | |
305 | execute 'doautocmd' s:nomodeline 'User GitGutterStage' | |
306 | endif | |
276 | 307 | endif |
277 | 308 | |
278 | 309 | " Refresh gitgutter's view of buffer. |
395 | 426 | function! s:open_hunk_preview_window() |
396 | 427 | if g:gitgutter_preview_win_floating |
397 | 428 | if exists('*nvim_open_win') |
398 | call s:close_hunk_preview_window() | |
429 | call gitgutter#hunk#close_hunk_preview_window() | |
399 | 430 | |
400 | 431 | let buf = nvim_create_buf(v:false, v:false) |
401 | 432 | " Set default width and height for now. |
402 | let s:winid = nvim_open_win(buf, v:false, { | |
403 | \ 'relative': 'cursor', | |
404 | \ 'row': 1, | |
405 | \ 'col': 0, | |
406 | \ 'width': 42, | |
407 | \ 'height': &previewheight, | |
408 | \ 'style': 'minimal' | |
409 | \ }) | |
433 | let s:winid = nvim_open_win(buf, v:false, g:gitgutter_floating_window_options) | |
410 | 434 | call nvim_buf_set_option(buf, 'filetype', 'diff') |
411 | 435 | call nvim_buf_set_option(buf, 'buftype', 'acwrite') |
412 | 436 | call nvim_buf_set_option(buf, 'bufhidden', 'delete') |
414 | 438 | call nvim_buf_set_name(buf, 'gitgutter://hunk-preview') |
415 | 439 | |
416 | 440 | " Assumes cursor is in original window. |
417 | autocmd CursorMoved <buffer> ++once call s:close_hunk_preview_window() | |
441 | autocmd CursorMoved <buffer> ++once call gitgutter#hunk#close_hunk_preview_window() | |
442 | ||
443 | if g:gitgutter_close_preview_on_escape | |
444 | " Map <Esc> to close the floating preview. | |
445 | nnoremap <buffer> <silent> <Esc> :<C-U>call gitgutter#hunk#close_hunk_preview_window()<CR> | |
446 | " Ensure that when the preview window is closed, the map is removed. | |
447 | autocmd User GitGutterPreviewClosed silent! nunmap <buffer> <Esc> | |
448 | autocmd CursorMoved <buffer> ++once silent! nunmap <buffer> <Esc> | |
449 | execute "autocmd WinClosed <buffer=".winbufnr(s:winid)."> doautocmd" s:nomodeline "User GitGutterPreviewClosed" | |
450 | endif | |
418 | 451 | |
419 | 452 | return |
420 | 453 | endif |
421 | 454 | |
422 | 455 | if exists('*popup_create') |
423 | let s:winid = popup_create('', { | |
424 | \ 'line': 'cursor+1', | |
425 | \ 'col': 'cursor', | |
426 | \ 'moved': 'any', | |
427 | \ }) | |
456 | if g:gitgutter_close_preview_on_escape | |
457 | let g:gitgutter_floating_window_options.filter = function('s:close_popup_on_escape') | |
458 | endif | |
459 | ||
460 | let s:winid = popup_create('', g:gitgutter_floating_window_options) | |
428 | 461 | |
429 | 462 | call setbufvar(winbufnr(s:winid), '&filetype', 'diff') |
430 | 463 | |
432 | 465 | endif |
433 | 466 | endif |
434 | 467 | |
468 | if exists('&previewpopup') | |
469 | let [previewpopup, &previewpopup] = [&previewpopup, ''] | |
470 | endif | |
471 | ||
472 | " Specifying where to open the preview window can lead to the cursor going | |
473 | " to an unexpected window when the preview window is closed (#769). | |
474 | silent! noautocmd execute g:gitgutter_preview_win_location 'pedit gitgutter://hunk-preview' | |
435 | 475 | silent! wincmd P |
436 | if !&previewwindow | |
437 | noautocmd execute g:gitgutter_preview_win_location &previewheight 'new gitgutter://hunk-preview' | |
438 | doautocmd WinEnter | |
476 | setlocal statusline=%{''} | |
477 | doautocmd WinEnter | |
478 | if exists('*win_getid') | |
439 | 479 | let s:winid = win_getid() |
440 | set previewwindow | |
441 | setlocal filetype=diff buftype=acwrite bufhidden=delete | |
442 | " Reset some defaults in case someone else has changed them. | |
443 | setlocal noreadonly modifiable noswapfile | |
444 | endif | |
480 | else | |
481 | let s:preview_bufnr = bufnr('') | |
482 | endif | |
483 | setlocal filetype=diff buftype=acwrite bufhidden=delete | |
484 | " Reset some defaults in case someone else has changed them. | |
485 | setlocal noreadonly modifiable noswapfile | |
486 | if g:gitgutter_close_preview_on_escape | |
487 | " Ensure cursor goes to the expected window. | |
488 | nnoremap <buffer> <silent> <Esc> :<C-U>wincmd p<Bar>pclose<CR> | |
489 | endif | |
490 | ||
491 | if exists('&previewpopup') | |
492 | let &previewpopup=previewpopup | |
493 | endif | |
494 | endfunction | |
495 | ||
496 | ||
497 | function! s:close_popup_on_escape(winid, key) | |
498 | if a:key == "\<Esc>" | |
499 | call popup_close(a:winid) | |
500 | return 1 | |
501 | endif | |
502 | return 0 | |
445 | 503 | endfunction |
446 | 504 | |
447 | 505 | |
449 | 507 | " Preview window: assumes cursor is in preview window. |
450 | 508 | function! s:populate_hunk_preview_window(header, body) |
451 | 509 | let body_length = len(a:body) |
452 | let height = min([body_length, &previewheight]) | |
453 | 510 | |
454 | 511 | if g:gitgutter_preview_win_floating |
455 | 512 | if exists('*nvim_open_win') |
513 | let height = min([body_length, g:gitgutter_floating_window_options.height]) | |
514 | ||
456 | 515 | " Assumes cursor is not in previewing window. |
457 | 516 | call nvim_buf_set_var(winbufnr(s:winid), 'hunk_header', a:header) |
517 | ||
518 | let [_scrolloff, &scrolloff] = [&scrolloff, 0] | |
458 | 519 | |
459 | 520 | let width = max(map(copy(a:body), 'strdisplaywidth(v:val)')) |
460 | 521 | call nvim_win_set_width(s:winid, width) |
461 | 522 | call nvim_win_set_height(s:winid, height) |
523 | ||
524 | let &scrolloff=_scrolloff | |
462 | 525 | |
463 | 526 | call nvim_buf_set_lines(winbufnr(s:winid), 0, -1, v:false, []) |
464 | 527 | call nvim_buf_set_lines(winbufnr(s:winid), 0, -1, v:false, a:body) |
485 | 548 | |
486 | 549 | else |
487 | 550 | let b:hunk_header = a:header |
488 | execute 'resize' height | |
489 | 551 | |
490 | 552 | %delete _ |
491 | 553 | call setline(1, a:body) |
492 | 554 | setlocal nomodified |
555 | ||
556 | normal! G$ | |
557 | let hunk_height = max([body_length, winline()]) | |
558 | let height = min([hunk_height, &previewheight]) | |
559 | execute 'resize' height | |
560 | 1 | |
493 | 561 | |
494 | 562 | call clearmatches() |
495 | 563 | for region in gitgutter#diff_highlight#process(a:body) |
505 | 573 | function! s:enable_staging_from_hunk_preview_window() |
506 | 574 | augroup gitgutter_hunk_preview |
507 | 575 | autocmd! |
508 | execute 'autocmd BufWriteCmd <buffer='.winbufnr(s:winid).'> GitGutterStageHunk' | |
576 | let bufnr = s:winid != 0 ? winbufnr(s:winid) : s:preview_bufnr | |
577 | execute 'autocmd BufWriteCmd <buffer='.bufnr.'> GitGutterStageHunk' | |
509 | 578 | augroup END |
510 | 579 | endfunction |
511 | 580 | |
516 | 585 | endfunction |
517 | 586 | |
518 | 587 | |
519 | function! s:close_hunk_preview_window() | |
520 | call setbufvar(winbufnr(s:winid), '&modified', 0) | |
588 | function! gitgutter#hunk#close_hunk_preview_window() | |
589 | let bufnr = s:winid != 0 ? winbufnr(s:winid) : s:preview_bufnr | |
590 | call setbufvar(bufnr, '&modified', 0) | |
521 | 591 | |
522 | 592 | if g:gitgutter_preview_win_floating |
523 | 593 | if win_id2win(s:winid) > 0 |
528 | 598 | endif |
529 | 599 | |
530 | 600 | let s:winid = 0 |
531 | endfunction | |
601 | let s:preview_bufnr = 0 | |
602 | endfunction | |
603 | ||
604 | ||
605 | function gitgutter#hunk#is_preview_window_open() | |
606 | if g:gitgutter_preview_win_floating | |
607 | if win_id2win(s:winid) > 0 | |
608 | execute win_id2win(s:winid).'wincmd c' | |
609 | endif | |
610 | else | |
611 | for i in range(1, winnr('$')) | |
612 | if getwinvar(i, '&previewwindow') | |
613 | return 1 | |
614 | endif | |
615 | endfor | |
616 | endif | |
617 | return 0 | |
618 | endfunction |
29 | 29 | |
30 | 30 | function! gitgutter#utility#warn(message) abort |
31 | 31 | echohl WarningMsg |
32 | echo 'vim-gitgutter: ' . a:message | |
32 | echo a:message | |
33 | 33 | echohl None |
34 | 34 | let v:warningmsg = a:message |
35 | 35 | endfunction |
38 | 38 | if empty(gitgutter#utility#getbufvar(a:bufnr, a:key)) |
39 | 39 | call gitgutter#utility#setbufvar(a:bufnr, a:key, '1') |
40 | 40 | echohl WarningMsg |
41 | redraw | echom 'vim-gitgutter: ' . a:message | |
41 | redraw | echom a:message | |
42 | 42 | echohl None |
43 | 43 | let v:warningmsg = a:message |
44 | 44 | endif |
47 | 47 | " Returns truthy when the buffer's file should be processed; and falsey when it shouldn't. |
48 | 48 | " This function does not and should not make any system calls. |
49 | 49 | function! gitgutter#utility#is_active(bufnr) abort |
50 | return g:gitgutter_enabled && | |
51 | \ gitgutter#utility#getbufvar(a:bufnr, 'enabled', 1) && | |
50 | return gitgutter#utility#getbufvar(a:bufnr, 'enabled') && | |
52 | 51 | \ !pumvisible() && |
53 | 52 | \ s:is_file_buffer(a:bufnr) && |
54 | 53 | \ s:exists_file(a:bufnr) && |
161 | 160 | |
162 | 161 | |
163 | 162 | function! gitgutter#utility#cd_cmd(bufnr, cmd) abort |
164 | let cd = s:unc_path(a:bufnr) ? 'pushd' : (gitgutter#utility#windows() ? 'cd /d' : 'cd') | |
163 | let cd = s:unc_path(a:bufnr) ? 'pushd' : (gitgutter#utility#windows() && s:dos_shell() ? 'cd /d' : 'cd') | |
165 | 164 | return cd.' '.s:dir(a:bufnr).' && '.a:cmd |
166 | 165 | endfunction |
167 | 166 | |
169 | 168 | return s:abs_path(a:bufnr, 0) =~ '^\\\\' |
170 | 169 | endfunction |
171 | 170 | |
171 | function! s:dos_shell() | |
172 | return &shell == 'cmd.exe' || &shell == 'command.com' | |
173 | endfunction | |
174 | ||
172 | 175 | function! s:use_known_shell() abort |
173 | 176 | if has('unix') && &shell !=# 'sh' |
174 | let [s:shell, s:shellcmdflag, s:shellredir] = [&shell, &shellcmdflag, &shellredir] | |
177 | let [s:shell, s:shellcmdflag, s:shellredir, s:shellpipe, s:shellquote, s:shellxquote] = [&shell, &shellcmdflag, &shellredir, &shellpipe, &shellquote, &shellxquote] | |
175 | 178 | let &shell = 'sh' |
176 | 179 | set shellcmdflag=-c shellredir=>%s\ 2>&1 |
177 | 180 | endif |
181 | if has('win32') && (&shell =~# 'pwsh' || &shell =~# 'powershell') | |
182 | let [s:shell, s:shellcmdflag, s:shellredir, s:shellpipe, s:shellquote, s:shellxquote] = [&shell, &shellcmdflag, &shellredir, &shellpipe, &shellquote, &shellxquote] | |
183 | let &shell = 'cmd.exe' | |
184 | set shellcmdflag=/s\ /c shellredir=>%s\ 2>&1 shellpipe=>%s\ 2>&1 shellquote= shellxquote=" | |
185 | endif | |
178 | 186 | endfunction |
179 | 187 | |
180 | 188 | function! s:restore_shell() abort |
181 | if has('unix') && exists('s:shell') | |
182 | let [&shell, &shellcmdflag, &shellredir] = [s:shell, s:shellcmdflag, s:shellredir] | |
183 | endif | |
184 | endfunction | |
185 | ||
186 | function! gitgutter#utility#set_diff_base_if_fugitive(bufnr) | |
189 | if (has('unix') || has('win32')) && exists('s:shell') | |
190 | let [&shell, &shellcmdflag, &shellredir, &shellpipe, &shellquote, &shellxquote] = [s:shell, s:shellcmdflag, s:shellredir, s:shellpipe, s:shellquote, s:shellxquote] | |
191 | endif | |
192 | endfunction | |
193 | ||
194 | function! gitgutter#utility#get_diff_base(bufnr) | |
187 | 195 | let p = resolve(expand('#'.a:bufnr.':p')) |
188 | 196 | let ml = matchlist(p, '\v^fugitive:/.*/(\x{40,})/') |
189 | 197 | if !empty(ml) && !empty(ml[1]) |
190 | let g:gitgutter_diff_base = ml[1].'^' | |
191 | endif | |
198 | return ml[1].'^' | |
199 | endif | |
200 | return g:gitgutter_diff_base | |
192 | 201 | endfunction |
193 | 202 | |
194 | 203 | function! s:abs_path(bufnr, shellesc) |
20 | 20 | function! gitgutter#process_buffer(bufnr, force) abort |
21 | 21 | " NOTE a:bufnr is not necessarily the current buffer. |
22 | 22 | |
23 | if gitgutter#utility#getbufvar(a:bufnr, 'enabled', -1) == -1 | |
24 | call gitgutter#utility#setbufvar(a:bufnr, 'enabled', g:gitgutter_enabled) | |
25 | endif | |
26 | ||
23 | 27 | if gitgutter#utility#is_active(a:bufnr) |
24 | 28 | |
25 | 29 | if has('patch-7.4.1559') |
31 | 35 | if [how] == ['async'] " avoid string-to-number conversion if how is a number |
32 | 36 | return |
33 | 37 | endif |
34 | ||
35 | call gitgutter#utility#set_diff_base_if_fugitive(a:bufnr) | |
36 | 38 | |
37 | 39 | if a:force || s:has_fresh_changes(a:bufnr) |
38 | 40 | |
56 | 58 | |
57 | 59 | |
58 | 60 | function! gitgutter#disable() abort |
59 | " get list of all buffers (across all tabs) | |
61 | call s:toggle_each_buffer(0) | |
62 | let g:gitgutter_enabled = 0 | |
63 | endfunction | |
64 | ||
65 | function! gitgutter#enable() abort | |
66 | call s:toggle_each_buffer(1) | |
67 | let g:gitgutter_enabled = 1 | |
68 | endfunction | |
69 | ||
70 | function s:toggle_each_buffer(enable) | |
60 | 71 | for bufnr in range(1, bufnr('$') + 1) |
61 | 72 | if buflisted(bufnr) |
62 | 73 | let file = expand('#'.bufnr.':p') |
63 | 74 | if !empty(file) |
64 | call s:clear(bufnr) | |
75 | if a:enable | |
76 | call gitgutter#buffer_enable(bufnr) | |
77 | else | |
78 | call gitgutter#buffer_disable(bufnr) | |
79 | end | |
65 | 80 | endif |
66 | 81 | endif |
67 | 82 | endfor |
68 | ||
69 | let g:gitgutter_enabled = 0 | |
70 | endfunction | |
71 | ||
72 | function! gitgutter#enable() abort | |
73 | let g:gitgutter_enabled = 1 | |
74 | call gitgutter#all(1) | |
75 | 83 | endfunction |
76 | 84 | |
77 | 85 | function! gitgutter#toggle() abort |
83 | 91 | endfunction |
84 | 92 | |
85 | 93 | |
86 | function! gitgutter#buffer_disable() abort | |
87 | let bufnr = bufnr('') | |
94 | function! gitgutter#buffer_disable(...) abort | |
95 | let bufnr = a:0 ? a:1 : bufnr('') | |
88 | 96 | call gitgutter#utility#setbufvar(bufnr, 'enabled', 0) |
89 | 97 | call s:clear(bufnr) |
90 | 98 | endfunction |
91 | 99 | |
92 | function! gitgutter#buffer_enable() abort | |
93 | let bufnr = bufnr('') | |
100 | function! gitgutter#buffer_enable(...) abort | |
101 | let bufnr = a:0 ? a:1 : bufnr('') | |
94 | 102 | call gitgutter#utility#setbufvar(bufnr, 'enabled', 1) |
95 | 103 | call gitgutter#process_buffer(bufnr, 1) |
96 | 104 | endfunction |
97 | 105 | |
98 | function! gitgutter#buffer_toggle() abort | |
99 | if gitgutter#utility#getbufvar(bufnr(''), 'enabled', 1) | |
100 | call gitgutter#buffer_disable() | |
106 | function! gitgutter#buffer_toggle(...) abort | |
107 | let bufnr = a:0 ? a:1 : bufnr('') | |
108 | if gitgutter#utility#getbufvar(bufnr, 'enabled', 1) | |
109 | call gitgutter#buffer_disable(bufnr) | |
101 | 110 | else |
102 | call gitgutter#buffer_enable() | |
111 | call gitgutter#buffer_enable(bufnr) | |
103 | 112 | endif |
104 | 113 | endfunction |
105 | 114 | |
180 | 189 | " - this runs synchronously |
181 | 190 | " - it ignores unsaved changes in buffers |
182 | 191 | " - it does not change to the repo root |
183 | function! gitgutter#quickfix() | |
192 | function! gitgutter#quickfix(current_file) | |
193 | let cmd = g:gitgutter_git_executable.' '.g:gitgutter_git_args.' rev-parse --show-cdup' | |
194 | let path_to_repo = get(systemlist(cmd), 0, '') | |
195 | if !empty(path_to_repo) && path_to_repo[-1:] != '/' | |
196 | let path_to_repo .= '/' | |
197 | endif | |
198 | ||
184 | 199 | let locations = [] |
185 | let cmd = g:gitgutter_git_executable.' '.g:gitgutter_git_args.' --no-pager '.g:gitgutter_git_args. | |
186 | \ ' diff --no-ext-diff --no-color -U0 '.g:gitgutter_diff_args. ' '. g:gitgutter_diff_base | |
200 | let cmd = g:gitgutter_git_executable.' '.g:gitgutter_git_args.' --no-pager'. | |
201 | \ ' diff --no-ext-diff --no-color -U0'. | |
202 | \ ' --src-prefix=a/'.path_to_repo.' --dst-prefix=b/'.path_to_repo.' '. | |
203 | \ g:gitgutter_diff_args. ' '. g:gitgutter_diff_base | |
204 | if a:current_file | |
205 | let cmd = cmd.' -- '.expand('%:p') | |
206 | endif | |
187 | 207 | let diff = systemlist(cmd) |
188 | 208 | let lnum = 0 |
189 | 209 | for line in diff |
195 | 215 | elseif line =~ '^diff --git "' |
196 | 216 | let [_, fnamel, _, fnamer] = split(line, '"') |
197 | 217 | let fname = fnamel ==# fnamer ? fnamel : fnamel[2:] |
218 | elseif line =~ '^diff --cc [^"]' | |
219 | let fname = line[10:] | |
220 | elseif line =~ '^diff --cc "' | |
221 | let [_, fname] = split(line, '"') | |
198 | 222 | elseif line =~ '^@@' |
199 | 223 | let lnum = matchlist(line, '+\(\d\+\)')[1] |
200 | 224 | elseif lnum > 0 |
0 | 0 | *gitgutter.txt* A Vim plugin which shows a git diff in the gutter. |
1 | 1 | |
2 | 2 | |
3 | Vim Git Gutter | |
3 | Vim GitGutter | |
4 | 4 | |
5 | 5 | |
6 | 6 | Author: Andy Stewart <https://airbladesoftware.com/> |
26 | 26 | =============================================================================== |
27 | 27 | INTRODUCTION *gitgutter-introduction* |
28 | 28 | |
29 | GitGutter is a Vim plugin which shows a git diff in the 'gutter' (sign column). | |
29 | GitGutter is a Vim plugin which shows a git diff in the sign column. | |
30 | 30 | It shows which lines have been added, modified, or removed. You can also |
31 | 31 | preview, stage, and undo individual hunks. The plugin also provides a hunk |
32 | 32 | text object. |
33 | 33 | |
34 | 34 | The signs are always up to date and the plugin never saves your buffer. |
35 | ||
36 | The name "gitgutter" comes from the Sublime Text 3 plugin which inspired this | |
37 | one in 2013. | |
35 | 38 | |
36 | 39 | |
37 | 40 | =============================================================================== |
59 | 62 | =============================================================================== |
60 | 63 | WINDOWS *gitgutter-windows* |
61 | 64 | |
62 | I recommend configuring vim-gitgutter with the full path to your git executable. | |
65 | There is a potential risk on Windows due to `cmd.exe` prioritising the current | |
66 | folder over folders in `PATH`. If you have a file named `git.*` (i.e. with | |
67 | any extension in `PATHEXT`) in your current folder, it will be executed | |
68 | instead of git whenever the plugin calls git. | |
69 | ||
70 | You can avoid this risk by configuring the full path to your git executable. | |
63 | 71 | For example: |
64 | 72 | > |
73 | " This path probably won't work | |
65 | 74 | let g:gitgutter_git_executable = 'C:\Program Files\Git\bin\git.exe' |
66 | 75 | < |
67 | This is to avoid a problem which occurs if you have file named "git.*" (i.e. | |
68 | with any extension in "PATHEXT") in your current folder. "cmd.exe" prioritises | |
69 | the current folder over folders in 'PATH' and will try to execute your file | |
70 | instead of the "git" binary. | |
76 | ||
77 | Unfortunately I don't know the correct escaping for the path - if you do, | |
78 | please let me know! | |
71 | 79 | |
72 | 80 | |
73 | 81 | =============================================================================== |
83 | 91 | |
84 | 92 | *gitgutter-:GitGutterToggle* |
85 | 93 | :GitGutterToggle Toggle vim-gitgutter on or off for all buffers. |
94 | ||
95 | *gitgutter-:GitGutterBufferDisable* | |
96 | :GitGutterBufferDisable Turn vim-gitgutter off for current buffer. | |
97 | ||
98 | *gitgutter-:GitGutterBufferEnable* | |
99 | :GitGutterBufferEnable Turn vim-gitgutter on for current buffer. | |
100 | ||
101 | *gitgutter-:GitGutterBufferToggle* | |
102 | :GitGutterBufferToggle Toggle vim-gitgutter on or off for current buffer. | |
86 | 103 | |
87 | 104 | *gitgutter-:GitGutter* |
88 | 105 | :GitGutter Update signs for the current buffer. You shouldn't |
142 | 159 | :GitGutterQuickFix Load all hunks into the |quickfix| list. Note this |
143 | 160 | ignores any unsaved changes in your buffers. The |
144 | 161 | |g:gitgutter_use_location_list| option can be set to |
145 | populate the location list of the current window instead | |
162 | populate the location list of the current window | |
163 | instead. Use |:copen| (or |:lopen|) to open a buffer | |
164 | containing the search results in linked form; or add a | |
165 | custom command like this: | |
166 | > | |
167 | command! Gqf GitGutterQuickFix | copen | |
168 | < | |
169 | *gitgutter-:GitGutterQuickFixCurrentFile* | |
170 | :GitGutterQuickFixCurrentFile Same as :GitGutterQuickFix, but only load hunks for | |
171 | the file in the focused buffer. This has the same | |
172 | functionality as :GitGutterQuickFix when the focused | |
173 | buffer is empty. | |
146 | 174 | |
147 | 175 | |
148 | 176 | Commands for operating on a hunk:~ |
162 | 190 | |
163 | 191 | *gitgutter-:GitGutterPreviewHunk* |
164 | 192 | :GitGutterPreviewHunk Preview the hunk the cursor is in. |
165 | Use |:pclose| or |CTRL-W_CTRL-Z| to close the preview | |
166 | window. | |
167 | 193 | |
168 | 194 | To stage part of the hunk, move to the preview window, |
169 | 195 | delete any lines you do not want to stage, and |
170 | 196 | |GitGutterStageHunk|. |
171 | 197 | |
198 | To close a non-floating preview window use |:pclose| | |
199 | or |CTRL-W_z| or |CTRL-W_CTRL-Z|; or normal window- | |
200 | closing (|:quit| or |:close| or |CTRL-W_c|) if your cursor | |
201 | is in the preview window. | |
202 | ||
203 | To close a floating window when the cursor is in the | |
204 | original buffer, move the cursor. | |
205 | ||
206 | To close a floating window when the cursor is in the | |
207 | floating window use normal window-closing, or move to | |
208 | the original window with |CTRL-W_p|. Alternatively set | |
209 | |g:gitgutter_close_preview_on_escape| and use <Esc>. | |
210 | ||
211 | Two functions are available for your own logic: | |
212 | > | |
213 | gitgutter#hunk#is_preview_window_open() | |
214 | gitgutter#hunk#close_hunk_preview_window() | |
215 | < | |
216 | ||
172 | 217 | Commands for folds:~ |
173 | 218 | |
174 | 219 | *gitgutter-:GitGutterFold* |
176 | 221 | |
177 | 222 | |
178 | 223 | =============================================================================== |
179 | AUTOCOMMAND *gitgutter-autocommand* | |
224 | AUTOCOMMANDS *gitgutter-autocommands* | |
180 | 225 | |
181 | 226 | User GitGutter~ |
182 | 227 | |
188 | 233 | A dictionary `g:gitgutter_hook_context` is made available during its execution, |
189 | 234 | which contains an entry `bufnr` that contains the buffer number being updated. |
190 | 235 | |
236 | User GitGutterStage~ | |
237 | ||
238 | After staging a hunk or part of a hunk vim-gitgutter fires a |User| |autocmd| | |
239 | with the event GitGutterStage. Staging always happens in the current buffer. | |
191 | 240 | |
192 | 241 | =============================================================================== |
193 | 242 | MAPPINGS *gitgutter-mappings* |
298 | 347 | |g:gitgutter_sign_removed| |
299 | 348 | |g:gitgutter_sign_removed_first_line| |
300 | 349 | |g:gitgutter_sign_modified_removed| |
301 | |g:gitgutter_override_sign_column_highlight| | |
350 | |g:gitgutter_set_sign_backgrounds| | |
351 | ||
352 | Hunk jumping:~ | |
353 | ||
354 | |g:gitgutter_show_msg_on_hunk_jumping| | |
302 | 355 | |
303 | 356 | Hunk previews:~ |
304 | 357 | |
305 | 358 | |g:gitgutter_preview_win_floating| |
359 | |g:gitgutter_floating_window_options| | |
360 | |g:gitgutter_close_preview_on_escape| | |
306 | 361 | |
307 | 362 | Terminal:~ |
308 | 363 | |
404 | 459 | Determines whether or not to show line number highlights. |
405 | 460 | |
406 | 461 | *g:gitgutter_max_signs* |
407 | Default: 500 | |
462 | Default: 500 (Vim < 8.1.0614, Neovim < 0.4.0) | |
463 | -1 (otherwise) | |
408 | 464 | |
409 | 465 | Sets the maximum number of signs to show in a buffer. Vim is slow at updating |
410 | 466 | signs, so to avoid slowing down the GUI the number of signs is capped. When |
411 | 467 | the number of changed lines exceeds this value, the plugin removes all signs |
412 | 468 | and displays a warning message. |
413 | 469 | |
470 | When set to -1 the limit is not applied. | |
471 | ||
414 | 472 | *g:gitgutter_sign_priority* |
415 | 473 | Default: 10 |
416 | 474 | |
427 | 485 | *g:gitgutter_sign_modified* |
428 | 486 | *g:gitgutter_sign_removed* |
429 | 487 | *g:gitgutter_sign_removed_first_line* |
488 | *g:gitgutter_sign_removed_above_and_below* | |
430 | 489 | *g:gitgutter_sign_modified_removed* |
431 | 490 | Defaults: |
432 | 491 | > |
434 | 493 | let g:gitgutter_sign_modified = '~' |
435 | 494 | let g:gitgutter_sign_removed = '_' |
436 | 495 | let g:gitgutter_sign_removed_first_line = '‾' |
496 | let g:gitgutter_sign_removed_above_and_below = '_¯' | |
437 | 497 | let g:gitgutter_sign_modified_removed = '~_' |
438 | 498 | < |
439 | 499 | You can use unicode characters but not images. Signs must not take up more than |
440 | 500 | 2 columns. |
441 | 501 | |
442 | *g:gitgutter_override_sign_column_highlight* | |
443 | Default: 1 | |
444 | ||
445 | Controls whether to make the sign column look like the line-number column (i.e. | |
446 | the |hl-LineNr| highlight group). | |
447 | ||
448 | To customise your sign column's background color, first tell vim-gitgutter to | |
449 | leave it alone: | |
450 | > | |
451 | let g:gitgutter_override_sign_column_highlight = 0 | |
452 | < | |
453 | ||
454 | And then either update your colorscheme's |hlSignColumn| highlight group or set | |
455 | it in your |vimrc|: | |
456 | ||
457 | Desired appearance Command ~ | |
458 | Same as line-number column highlight clear SignColumn | |
459 | User-defined (terminal Vim) highlight SignColumn ctermbg={whatever} | |
460 | User-defined (graphical Vim) highlight SignColumn guibg={whatever} | |
461 | ||
502 | *g:gitgutter_set_sign_backgrounds* | |
503 | Default: 0 | |
504 | ||
505 | Only applies to existing GitGutter* highlight groups. See | |
506 | |gitgutter-highlights|. | |
507 | ||
508 | Controls whether to override the signs' background colours to match the | |
509 | |hl-SignColumn|. | |
462 | 510 | |
463 | 511 | *g:gitgutter_preview_win_floating* |
464 | 512 | Default: 0 (Vim) |
469 | 517 | popup windows on Vim you will not be able to stage partial hunks via the |
470 | 518 | preview window. |
471 | 519 | |
520 | *g:gitgutter_floating_window_options* | |
521 | Default: | |
522 | > | |
523 | " Vim | |
524 | { | |
525 | \ 'line': 'cursor+1', | |
526 | \ 'col': 'cursor', | |
527 | \ 'moved': 'any' | |
528 | } | |
529 | ||
530 | " Neovim | |
531 | { | |
532 | \ 'relative': 'cursor', | |
533 | \ 'row': 1, | |
534 | \ 'col': 0, | |
535 | \ 'width': 42, | |
536 | \ 'height': &previewheight, | |
537 | \ 'style': 'minimal' | |
538 | } | |
539 | < | |
540 | This dictionary is passed directly to |popup_create()| (Vim) or | |
541 | |nvim_open_win()| (Neovim). | |
542 | ||
543 | *g:gitgutter_close_preview_on_escape* | |
544 | Default: 0 | |
545 | ||
546 | Whether pressing <Esc> in a preview window closes it. | |
547 | ||
472 | 548 | *g:gitgutter_terminal_reports_focus* |
473 | 549 | Default: 1 |
474 | 550 | |
517 | 593 | When switched on, the :GitGutterQuickFix command populates the location list |
518 | 594 | of the current window instead of the global quickfix list. |
519 | 595 | |
596 | *g:gitgutter_show_msg_on_hunk_jumping* | |
597 | Default: 1 | |
598 | ||
599 | When switched on, a message like "Hunk 4 of 11" is shown on hunk jumping. | |
600 | ||
520 | 601 | |
521 | 602 | =============================================================================== |
522 | 603 | HIGHLIGHTS *gitgutter-highlights* |
523 | 604 | |
524 | To change the signs' colours, set up the following highlight groups in your | |
525 | colorscheme or |vimrc|: | |
526 | > | |
527 | GitGutterAdd " an added line | |
528 | GitGutterChange " a changed line | |
529 | GitGutterDelete " at least one removed line | |
530 | GitGutterChangeDelete " a changed line followed by at least one removed line | |
531 | < | |
532 | ||
533 | You can either set these with `highlight GitGutterAdd {key}={arg}...` or link | |
534 | them to existing highlight groups with, say: | |
535 | > | |
536 | highlight link GitGutterAdd MyDiffAdd | |
537 | < | |
605 | To change the signs' colours, specify these highlight groups in your |vimrc|: | |
606 | > | |
607 | highlight GitGutterAdd guifg=#009900 ctermfg=2 | |
608 | highlight GitGutterChange guifg=#bbbb00 ctermfg=3 | |
609 | highlight GitGutterDelete guifg=#ff2222 ctermfg=1 | |
610 | < | |
611 | ||
612 | See |highlight-guifg| and |highlight-ctermfg| for the values you can use. | |
613 | ||
614 | If you do not like the signs' background colours and you do not want to update | |
615 | the GitGutter* highlight groups yourself, you can get the plugin to do it | |
616 | |g:gitgutter_set_sign_backgrounds|. | |
538 | 617 | |
539 | 618 | To change the line highlights, set up the following highlight groups in your |
540 | 619 | colorscheme or |vimrc|: |
561 | 640 | > |
562 | 641 | highlight link GitGutterChangeLineNr Underlined |
563 | 642 | < |
643 | To change the diff syntax colours used in the preview window, set up the diff* | |
644 | highlight groups in your colorscheme or |vimrc|: | |
645 | > | |
646 | diffAdded " if not set: use GitGutterAdd's foreground colour | |
647 | diffChanged " if not set: use GitGutterChange's foreground colour | |
648 | diffRemoved " if not set: use GitGutterDelete's foreground colour | |
649 | < | |
650 | Note the diff* highlight groups are used in any buffer whose 'syntax' is | |
651 | "diff". | |
652 | ||
653 | To change the intra-line diff highlights used in the preview window, set up | |
654 | the following highlight groups in your colorscheme or |vimrc|: | |
655 | > | |
656 | GitGutterAddIntraLine " default: gui=reverse cterm=reverse | |
657 | GitGutterDeleteIntraLine " default: gui=reverse cterm=reverse | |
658 | < | |
659 | For example, to use |hl-DiffAdd| for intra-line added regions: | |
660 | > | |
661 | highlight link GitGutterAddIntraLine DiffAdd | |
662 | < | |
564 | 663 | |
565 | 664 | |
566 | 665 | =============================================================================== |
591 | 690 | d. Why are the colours in the sign column weird? |
592 | 691 | |
593 | 692 | Your colorscheme is configuring the |hl-SignColumn| highlight group weirdly. |
594 | Please see |g:gitgutter_override_sign_column_highlight| on customising the | |
595 | sign column. | |
693 | Here are two ways you could change the colours: | |
694 | > | |
695 | highlight! link SignColumn LineNr | |
696 | highlight SignColumn guibg=whatever ctermbg=whatever | |
697 | < | |
596 | 698 | |
597 | 699 | e. What happens if I also use another plugin which uses signs (e.g. Syntastic)? |
598 | 700 | |
631 | 733 | < |
632 | 734 | If the result is -2, the plugin thinks your file is not tracked by git. |
633 | 735 | |
736 | 6. Check whether the signs have been placed: | |
737 | > | |
738 | :sign place group=gitgutter | |
739 | < | |
740 | If you see a list of signs, this is a colorscheme / highlight problem. | |
741 | Compare these two highlight values: | |
742 | > | |
743 | :highlight GitGutterAdd | |
744 | :highlight SignColumn | |
745 | < | |
746 | If no signs are listed, the call to git-diff is probably failing. Turn on | |
747 | logging by adding the following to your vimrc, restart, reproduce the problem, | |
748 | and examing the gitgutter.log file in the plugin's directory. | |
749 | > | |
750 | let g:gitgutter_log = 1 | |
751 | < | |
634 | 752 | |
635 | 753 | When the whole file is marked as added:~ |
636 | 754 | |
656 | 774 | let g:gitgutter_terminal_reports_focus = 0 |
657 | 775 | < |
658 | 776 | |
777 | vim:tw=78:et:ft=help:norl: |
7 | 7 | " Initialisation {{{ |
8 | 8 | |
9 | 9 | if v:version < 703 || (v:version == 703 && !has("patch105")) |
10 | call gitgutter#utility#warn('requires Vim 7.3.105') | |
10 | call gitgutter#utility#warn('Requires Vim 7.3.105') | |
11 | 11 | finish |
12 | 12 | endif |
13 | 13 | |
14 | function! s:set(var, default) abort | |
15 | if !exists(a:var) | |
16 | if type(a:default) | |
17 | execute 'let' a:var '=' string(a:default) | |
18 | else | |
19 | execute 'let' a:var '=' a:default | |
20 | endif | |
21 | endif | |
22 | endfunction | |
23 | ||
24 | call s:set('g:gitgutter_preview_win_location', 'bo') | |
14 | let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : '' | |
15 | ||
16 | function! s:obsolete(var) | |
17 | if exists(a:var) | |
18 | call gitgutter#utility#warn(a:var.' is obsolete and has no effect.') | |
19 | endif | |
20 | endfunction | |
21 | ||
22 | ||
23 | let g:gitgutter_preview_win_location = get(g:, 'gitgutter_preview_win_location', 'bo') | |
25 | 24 | if exists('*nvim_open_win') |
26 | call s:set('g:gitgutter_preview_win_floating', 1) | |
25 | let g:gitgutter_preview_win_floating = get(g:, 'gitgutter_preview_win_floating', 1) | |
26 | let g:gitgutter_floating_window_options = get(g:, 'gitgutter_floating_window_options', { | |
27 | \ 'relative': 'cursor', | |
28 | \ 'row': 1, | |
29 | \ 'col': 0, | |
30 | \ 'width': 42, | |
31 | \ 'height': &previewheight, | |
32 | \ 'style': 'minimal' | |
33 | \ }) | |
27 | 34 | else |
28 | call s:set('g:gitgutter_preview_win_floating', 0) | |
29 | endif | |
30 | call s:set('g:gitgutter_enabled', 1) | |
31 | call s:set('g:gitgutter_max_signs', 500) | |
32 | call s:set('g:gitgutter_signs', 1) | |
33 | call s:set('g:gitgutter_highlight_lines', 0) | |
34 | call s:set('g:gitgutter_highlight_linenrs', 0) | |
35 | call s:set('g:gitgutter_sign_priority', 10) | |
35 | let default = exists('&previewpopup') ? !empty(&previewpopup) : 0 | |
36 | let g:gitgutter_preview_win_floating = get(g:, 'gitgutter_preview_win_floating', default) | |
37 | let g:gitgutter_floating_window_options = get(g:, 'gitgutter_floating_window_options', { | |
38 | \ 'line': 'cursor+1', | |
39 | \ 'col': 'cursor', | |
40 | \ 'moved': 'any' | |
41 | \ }) | |
42 | endif | |
43 | let g:gitgutter_enabled = get(g:, 'gitgutter_enabled', 1) | |
44 | if exists('*sign_unplace') | |
45 | let g:gitgutter_max_signs = get(g:, 'gitgutter_max_signs', -1) | |
46 | else | |
47 | let g:gitgutter_max_signs = get(g:, 'gitgutter_max_signs', 500) | |
48 | endif | |
49 | let g:gitgutter_signs = get(g:, 'gitgutter_signs', 1) | |
50 | let g:gitgutter_highlight_lines = get(g:, 'gitgutter_highlight_lines', 0) | |
51 | let g:gitgutter_highlight_linenrs = get(g:, 'gitgutter_highlight_linenrs', 0) | |
52 | let g:gitgutter_sign_priority = get(g:, 'gitgutter_sign_priority', 10) | |
36 | 53 | " Nvim 0.4.0 has an expanding sign column |
37 | 54 | " The sign_place() function supports sign priority. |
38 | 55 | if (has('nvim-0.4.0') || exists('*sign_place')) && !exists('g:gitgutter_sign_allow_clobber') |
39 | 56 | let g:gitgutter_sign_allow_clobber = 1 |
40 | 57 | endif |
41 | call s:set('g:gitgutter_sign_allow_clobber', 0) | |
42 | call s:set('g:gitgutter_override_sign_column_highlight', 1) | |
43 | call s:set('g:gitgutter_sign_added', '+') | |
44 | call s:set('g:gitgutter_sign_modified', '~') | |
45 | call s:set('g:gitgutter_sign_removed', '_') | |
58 | let g:gitgutter_sign_allow_clobber = get(g:, 'gitgutter_sign_allow_clobber', 0) | |
59 | let g:gitgutter_set_sign_backgrounds = get(g:, 'gitgutter_set_sign_backgrounds', 0) | |
60 | let g:gitgutter_sign_added = get(g:, 'gitgutter_sign_added', '+') | |
61 | let g:gitgutter_sign_modified = get(g:, 'gitgutter_sign_modified', '~') | |
62 | let g:gitgutter_sign_removed = get(g:, 'gitgutter_sign_removed', '_') | |
46 | 63 | |
47 | 64 | if gitgutter#utility#supports_overscore_sign() |
48 | call s:set('g:gitgutter_sign_removed_first_line', '‾') | |
65 | let g:gitgutter_sign_removed_first_line = get(g:, 'gitgutter_sign_removed_first_line', '‾') | |
49 | 66 | else |
50 | call s:set('g:gitgutter_sign_removed_first_line', '_^') | |
51 | endif | |
52 | ||
53 | call s:set('g:gitgutter_sign_removed_above_and_below', '[') | |
54 | call s:set('g:gitgutter_sign_modified_removed', '~_') | |
55 | call s:set('g:gitgutter_git_args', '') | |
56 | call s:set('g:gitgutter_diff_relative_to', 'index') | |
57 | call s:set('g:gitgutter_diff_args', '') | |
58 | call s:set('g:gitgutter_diff_base', '') | |
59 | call s:set('g:gitgutter_map_keys', 1) | |
60 | call s:set('g:gitgutter_terminal_reports_focus', 1) | |
61 | call s:set('g:gitgutter_async', 1) | |
62 | call s:set('g:gitgutter_log', 0) | |
63 | call s:set('g:gitgutter_use_location_list', 0) | |
64 | ||
65 | call s:set('g:gitgutter_git_executable', 'git') | |
67 | let g:gitgutter_sign_removed_first_line = get(g:, 'gitgutter_sign_removed_first_line', '_^') | |
68 | endif | |
69 | ||
70 | let g:gitgutter_sign_removed_above_and_below = get(g:, 'gitgutter_sign_removed_above_and_below', '_¯') | |
71 | let g:gitgutter_sign_modified_removed = get(g:, 'gitgutter_sign_modified_removed', '~_') | |
72 | let g:gitgutter_git_args = get(g:, 'gitgutter_git_args', '') | |
73 | let g:gitgutter_diff_relative_to = get(g:, 'gitgutter_diff_relative_to', 'index') | |
74 | let g:gitgutter_diff_args = get(g:, 'gitgutter_diff_args', '') | |
75 | let g:gitgutter_diff_base = get(g:, 'gitgutter_diff_base', '') | |
76 | let g:gitgutter_map_keys = get(g:, 'gitgutter_map_keys', 1) | |
77 | let g:gitgutter_terminal_reports_focus = get(g:, 'gitgutter_terminal_reports_focus', 1) | |
78 | let g:gitgutter_async = get(g:, 'gitgutter_async', 1) | |
79 | let g:gitgutter_log = get(g:, 'gitgutter_log', 0) | |
80 | let g:gitgutter_use_location_list = get(g:, 'gitgutter_use_location_list', 0) | |
81 | let g:gitgutter_close_preview_on_escape = get(g:, 'gitgutter_close_preview_on_escape', 0) | |
82 | let g:gitgutter_show_msg_on_hunk_jumping = get(g:, 'gitgutter_show_msg_on_hunk_jumping', 1) | |
83 | ||
84 | let g:gitgutter_git_executable = get(g:, 'gitgutter_git_executable', 'git') | |
66 | 85 | if !executable(g:gitgutter_git_executable) |
67 | call gitgutter#utility#warn('cannot find git. Please set g:gitgutter_git_executable.') | |
86 | if g:gitgutter_enabled | |
87 | call gitgutter#utility#warn('Cannot find git. Please set g:gitgutter_git_executable.') | |
88 | endif | |
89 | finish | |
68 | 90 | endif |
69 | 91 | |
70 | 92 | let default_grep = 'grep' |
71 | call s:set('g:gitgutter_grep', default_grep) | |
93 | let g:gitgutter_grep = get(g:, 'gitgutter_grep', default_grep) | |
72 | 94 | if !empty(g:gitgutter_grep) |
73 | 95 | if executable(split(g:gitgutter_grep)[0]) |
74 | 96 | if $GREP_OPTIONS =~# '--color=always' |
76 | 98 | endif |
77 | 99 | else |
78 | 100 | if g:gitgutter_grep !=# default_grep |
79 | call gitgutter#utility#warn('cannot find '.g:gitgutter_grep.'. Please check g:gitgutter_grep.') | |
101 | call gitgutter#utility#warn('Cannot find '.g:gitgutter_grep.'. Please check g:gitgutter_grep.') | |
80 | 102 | endif |
81 | 103 | let g:gitgutter_grep = '' |
82 | 104 | endif |
83 | 105 | endif |
84 | 106 | |
85 | call gitgutter#highlight#define_sign_column_highlight() | |
86 | 107 | call gitgutter#highlight#define_highlights() |
87 | 108 | call gitgutter#highlight#define_signs() |
88 | 109 | |
109 | 130 | command! -bar GitGutterBufferEnable call gitgutter#buffer_enable() |
110 | 131 | command! -bar GitGutterBufferToggle call gitgutter#buffer_toggle() |
111 | 132 | |
112 | command! -bar GitGutterQuickFix call gitgutter#quickfix() | |
133 | command! -bar GitGutterQuickFix call gitgutter#quickfix(0) | |
134 | command! -bar GitGutterQuickFixCurrentFile call gitgutter#quickfix(1) | |
113 | 135 | |
114 | 136 | " }}} |
115 | 137 | |
193 | 215 | " Maps {{{ |
194 | 216 | |
195 | 217 | nnoremap <silent> <expr> <Plug>(GitGutterNextHunk) &diff ? ']c' : ":\<C-U>execute v:count1 . 'GitGutterNextHunk'\<CR>" |
196 | 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>" | |
218 | 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>" | |
197 | 219 | nnoremap <silent> <expr> <Plug>(GitGutterPrevHunk) &diff ? '[c' : ":\<C-U>execute v:count1 . 'GitGutterPrevHunk'\<CR>" |
198 | 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>" | |
220 | 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>" | |
199 | 221 | |
200 | 222 | xnoremap <silent> <Plug>(GitGutterStageHunk) :GitGutterStageHunk<CR> |
201 | xnoremap <silent> <Plug>GitGutterStageHunk :call gitgutter#utility#warn('please change your map <lt>Plug>GitGutterStageHunk to <lt>Plug>(GitGutterStageHunk)')<CR> | |
223 | xnoremap <silent> <Plug>GitGutterStageHunk :call gitgutter#utility#warn('Please change your map <lt>Plug>GitGutterStageHunk to <lt>Plug>(GitGutterStageHunk)')<CR> | |
202 | 224 | nnoremap <silent> <Plug>(GitGutterStageHunk) :GitGutterStageHunk<CR> |
203 | nnoremap <silent> <Plug>GitGutterStageHunk :call gitgutter#utility#warn('please change your map <lt>Plug>GitGutterStageHunk to <lt>Plug>(GitGutterStageHunk)')<CR> | |
225 | nnoremap <silent> <Plug>GitGutterStageHunk :call gitgutter#utility#warn('Please change your map <lt>Plug>GitGutterStageHunk to <lt>Plug>(GitGutterStageHunk)')<CR> | |
204 | 226 | nnoremap <silent> <Plug>(GitGutterUndoHunk) :GitGutterUndoHunk<CR> |
205 | nnoremap <silent> <Plug>GitGutterUndoHunk :call gitgutter#utility#warn('please change your map <lt>Plug>GitGutterUndoHunk to <lt>Plug>(GitGutterUndoHunk)')<CR> | |
227 | nnoremap <silent> <Plug>GitGutterUndoHunk :call gitgutter#utility#warn('Please change your map <lt>Plug>GitGutterUndoHunk to <lt>Plug>(GitGutterUndoHunk)')<CR> | |
206 | 228 | nnoremap <silent> <Plug>(GitGutterPreviewHunk) :GitGutterPreviewHunk<CR> |
207 | nnoremap <silent> <Plug>GitGutterPreviewHunk :call gitgutter#utility#warn('please change your map <lt>Plug>GitGutterPreviewHunk to <lt>Plug>(GitGutterPreviewHunk)')<CR> | |
229 | nnoremap <silent> <Plug>GitGutterPreviewHunk :call gitgutter#utility#warn('Please change your map <lt>Plug>GitGutterPreviewHunk to <lt>Plug>(GitGutterPreviewHunk)')<CR> | |
208 | 230 | |
209 | 231 | " }}} |
210 | 232 | |
211 | 233 | function! s:on_bufenter() |
212 | 234 | call gitgutter#setup_maps() |
235 | ||
236 | " To keep vim's start-up fast, do not process the buffer when vim is starting. | |
237 | " Instead process it a short time later. Normally we would rely on our | |
238 | " CursorHold autocommand to handle this but it turns out CursorHold is not | |
239 | " guaranteed to fire if the user has not typed anything yet; so set up a | |
240 | " timer instead. The disadvantage is that if CursorHold does fire, the | |
241 | " plugin will do a round of unnecessary work; but since there will not have | |
242 | " been any changes to the buffer since the first round, the second round | |
243 | " will be cheap. | |
244 | if has('vim_starting') && !$VIM_GITGUTTER_TEST | |
245 | if exists('*timer_start') | |
246 | call timer_start(&updatetime, 'GitGutterCursorHold') | |
247 | endif | |
248 | return | |
249 | endif | |
213 | 250 | |
214 | 251 | if exists('t:gitgutter_didtabenter') && t:gitgutter_didtabenter |
215 | 252 | let t:gitgutter_didtabenter = 0 |
219 | 256 | endif |
220 | 257 | endfunction |
221 | 258 | |
259 | function! GitGutterCursorHold(timer) | |
260 | execute 'doautocmd' s:nomodeline 'gitgutter CursorHold' | |
261 | endfunction | |
262 | ||
263 | function! s:next_tick(cmd) | |
264 | call timer_start(1, {-> execute(a:cmd)}) | |
265 | endfunction | |
266 | ||
222 | 267 | " Autocommands {{{ |
223 | 268 | |
224 | 269 | augroup gitgutter |
228 | 273 | |
229 | 274 | autocmd BufEnter * call s:on_bufenter() |
230 | 275 | |
276 | " Ensure Vim is always checking for CursorMoved to avoid CursorMoved | |
277 | " being fired at the wrong time in floating preview window on Neovim. | |
278 | " See vim/vim#2053. | |
279 | autocmd CursorMoved * execute '' | |
280 | ||
231 | 281 | autocmd CursorHold,CursorHoldI * call gitgutter#process_buffer(bufnr(''), 0) |
232 | 282 | if exists('*timer_start') && has('lambda') |
233 | autocmd FileChangedShellPost * call timer_start(1, {-> gitgutter#process_buffer(bufnr(''), 1)}) | |
283 | autocmd FileChangedShellPost * call s:next_tick("call gitgutter#process_buffer(+".expand('<abuf>').", 1)") | |
234 | 284 | else |
235 | autocmd FileChangedShellPost * call gitgutter#process_buffer(bufnr(''), 1) | |
285 | autocmd FileChangedShellPost * call gitgutter#process_buffer(+expand('<abuf>'), 1) | |
236 | 286 | endif |
237 | 287 | |
238 | 288 | " Ensure that all buffers are processed when opening vim with multiple files, e.g.: |
252 | 302 | " FocusGained gets triggered on startup with Neovim at least already. |
253 | 303 | " Therefore this tracks also if it was lost before. |
254 | 304 | let s:focus_was_lost = 0 |
255 | autocmd FocusGained * if s:focus_was_lost | let focus_was_lost = 0 | call gitgutter#all(1) | endif | |
305 | autocmd FocusGained * if s:focus_was_lost | let s:focus_was_lost = 0 | call gitgutter#all(1) | endif | |
256 | 306 | autocmd FocusLost * let s:focus_was_lost = 1 |
257 | 307 | |
258 | 308 | if exists('##VimResume') |
259 | 309 | autocmd VimResume * call gitgutter#all(1) |
260 | 310 | endif |
261 | 311 | |
262 | autocmd ColorScheme * call gitgutter#highlight#define_sign_column_highlight() | call gitgutter#highlight#define_highlights() | |
312 | autocmd ColorScheme * call gitgutter#highlight#define_highlights() | |
263 | 313 | |
264 | 314 | " Disable during :vimgrep |
265 | autocmd QuickFixCmdPre *vimgrep* let g:gitgutter_enabled = 0 | |
266 | autocmd QuickFixCmdPost *vimgrep* let g:gitgutter_enabled = 1 | |
315 | autocmd QuickFixCmdPre *vimgrep* let [g:gitgutter_was_enabled, g:gitgutter_enabled] = [g:gitgutter_enabled, 0] | |
316 | autocmd QuickFixCmdPost *vimgrep* let g:gitgutter_enabled = g:gitgutter_was_enabled | unlet g:gitgutter_was_enabled | |
267 | 317 | augroup END |
268 | 318 | |
269 | 319 | " }}} |
946 | 946 | call setline(5, ['A', 'B']) |
947 | 947 | call setline(9, ['C', 'D']) |
948 | 948 | write |
949 | let bufnr1 = bufnr('') | |
950 | ||
951 | edit fixture_dos.txt | |
952 | call setline(2, ['A', 'B']) | |
953 | write | |
954 | let bufnr2 = bufnr('') | |
949 | 955 | |
950 | 956 | GitGutterQuickFix |
951 | 957 | |
952 | 958 | let expected = [ |
953 | \ {'lnum': 5, 'bufnr': bufnr(''), 'text': '-e'}, | |
954 | \ {'lnum': 9, 'bufnr': bufnr(''), 'text': '-i'} | |
959 | \ {'lnum': 5, 'bufnr': bufnr1, 'text': '-e'}, | |
960 | \ {'lnum': 9, 'bufnr': bufnr1, 'text': '-i'}, | |
961 | \ {'lnum': 2, 'bufnr': bufnr2, 'text': "-b\r"} | |
962 | \ ] | |
963 | ||
964 | call s:assert_list_of_dicts(expected, getqflist()) | |
965 | ||
966 | GitGutterQuickFixCurrentFile | |
967 | ||
968 | let expected = [ | |
969 | \ {'lnum': 2, 'bufnr': bufnr(''), 'text': "-b\r"}, | |
955 | 970 | \ ] |
956 | 971 | |
957 | 972 | call s:assert_list_of_dicts(expected, getqflist()) |
0 | " Measure how long it takes to unplace signs. | |
1 | " | |
2 | " Source this file with `:source %` or `vim -S unplace.vim` | |
3 | ||
4 | ||
5 | let num = 500 | |
6 | sign define Foo text=* | |
7 | ||
8 | new | |
9 | ||
10 | call append(0, range(1, num)) | |
11 | ||
12 | for i in range(1, num) | |
13 | execute "sign place ".i." line=".i." name=Foo buffer=".bufnr('') | |
14 | endfor | |
15 | ||
16 | let start = reltime() | |
17 | for i in range(1, num) | |
18 | execute "sign unplace ".i | |
19 | endfor | |
20 | let elapsed = reltime(start) | |
21 | ||
22 | bdelete! | |
23 | ||
24 | echom split(reltimestr(elapsed))[0]."s to remove ".num." signs" | |
25 | echom string(reltimefloat(elapsed) * 1000 / num).' ms/sign' | |
26 | echom string(float2nr(num / reltimefloat(elapsed))).' sign/s' |