New upstream snapshot.
Debian Janitor
1 year, 6 months 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 | |
15 | 17 | * Stage partial hunks. |
16 | 18 | * Provides a hunk text object. |
17 | 19 | * Diffs against index (default) or any commit. |
20 | * Heeds git's "assume unchanged" bit. | |
18 | 21 | * Allows folding all unchanged text. |
19 | 22 | * Provides fold text showing whether folded lines have been changed. |
20 | 23 | * Can load all hunk locations into quickfix list or the current window's location list. |
32 | 35 | * Supports git only. If you work with other version control systems, I recommend [vim-signify](https://github.com/mhinz/vim-signify). |
33 | 36 | * 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 | 37 | |
38 | Compatibility: | |
39 | ||
40 | Compatible back to Vim 7.4, and probably 7.3. | |
41 | ||
35 | 42 | |
36 | 43 | ### Screenshot |
37 | 44 | |
70 | 77 | |
71 | 78 | ### Windows |
72 | 79 | |
73 | I recommend configuring vim-gitgutter with the full path to your git executable. For example: | |
74 | ||
75 | ```viml | |
80 | 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. | |
81 | ||
82 | You can avoid this risk by configuring the full path to your git executable. For example: | |
83 | ||
84 | ```viml | |
85 | " This path probably won't work | |
76 | 86 | let g:gitgutter_git_executable = 'C:\Program Files\Git\bin\git.exe' |
77 | 87 | ``` |
78 | 88 | |
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. | |
89 | Unfortunately I don't know the correct escaping for the path - if you do, please let me know! | |
80 | 90 | |
81 | 91 | |
82 | 92 | ### Getting started |
86 | 96 | 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 | 97 | |
88 | 98 | You cannot unstage a staged hunk. |
99 | ||
100 | After updating the signs, the plugin fires the `GitGutter` User autocommand. | |
101 | ||
102 | After staging a hunk or part of a hunk, the plugin fires the `GitGutterStage` User autocommand. | |
89 | 103 | |
90 | 104 | |
91 | 105 | #### Activation |
126 | 140 | |
127 | 141 | If you switch off both line highlighting and signs, you won't see the sign column. |
128 | 142 | |
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 | ``` | |
143 | 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: | |
144 | ||
145 | ```viml | |
146 | let g:gitgutter_max_signs = 500 " default value (Vim < 8.1.0614, Neovim < 0.4.0) | |
147 | let g:gitgutter_max_signs = -1 " default value (otherwise) | |
148 | ``` | |
149 | ||
150 | You can also remove the limit by setting `g:gitgutter_max_signs = -1`. | |
134 | 151 | |
135 | 152 | #### Hunks |
136 | 153 | |
148 | 165 | nmap [h <Plug>(GitGutterPrevHunk) |
149 | 166 | ``` |
150 | 167 | |
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. | |
168 | 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: | |
169 | ||
170 | ```viml | |
171 | let g:gitgutter_show_msg_on_hunk_jumping = 0 | |
172 | ``` | |
173 | ||
174 | 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: | |
175 | ||
176 | ```viml | |
177 | command! Gqf GitGutterQuickFix | copen | |
178 | ``` | |
152 | 179 | |
153 | 180 | You can stage or undo an individual hunk when your cursor is in it: |
154 | 181 | |
209 | 236 | Finally, you can force vim-gitgutter to update its signs across all visible buffers with `:GitGutterAll`. |
210 | 237 | |
211 | 238 | See the customisation section below for how to change the defaults. |
239 | ||
240 | ||
241 | ### Vimdiff | |
242 | ||
243 | Use the `GitGutterDiffOrig` command to open a vimdiff view of the current buffer, respecting `g:gitgutter_diff_relative_to` and `:gitgutter_diff_base`. | |
212 | 244 | |
213 | 245 | |
214 | 246 | ### Folding |
256 | 288 | * How to handle non-gitgutter signs |
257 | 289 | * The signs' colours and symbols |
258 | 290 | * Line highlights |
291 | * Line number highlights (only in Neovim 0.3.2 or higher) | |
292 | * The diff syntax colours used in the preview window | |
293 | * The intra-line diff highlights used in the preview window | |
259 | 294 | * Whether the diff is relative to the index (default) or working tree. |
260 | 295 | * The base of the diff |
261 | 296 | * Extra arguments for `git` when running `git diff` |
269 | 304 | * Whether to clobber or preserve non-gitgutter signs |
270 | 305 | * The priority of gitgutter's signs. |
271 | 306 | * Whether to use a floating/popup window for hunk previews |
307 | * The appearance of a floating/popup window for hunk previews | |
272 | 308 | * Whether to populate the quickfix list or a location list with all hunks |
273 | 309 | |
274 | 310 | Please note that vim-gitgutter won't override any colours or highlights you've set in your colorscheme. |
276 | 312 | |
277 | 313 | #### Sign column |
278 | 314 | |
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 | |
315 | Set the `SignColumn` highlight group to change the sign column's colour. For example: | |
316 | ||
317 | ```viml | |
318 | " vim-gitgutter used to do this by default: | |
319 | highlight! link SignColumn LineNr | |
320 | ||
321 | " or you could do this: | |
322 | highlight SignColumn guibg=whatever ctermbg=whatever | |
292 | 323 | ``` |
293 | 324 | |
294 | 325 | 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 | 338 | |
308 | 339 | #### Signs' colours and symbols |
309 | 340 | |
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`. | |
341 | If you or your colourscheme has defined `GitGutter*` highlight groups, the plugin will use them for the signs' colours. | |
342 | ||
343 | 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: | |
344 | ||
345 | ```viml | |
346 | let g:gitgutter_set_sign_backgrounds = 1 | |
347 | ``` | |
348 | ||
349 | 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: | |
350 | ||
351 | ```viml | |
352 | highlight GitGutterAdd guifg=#009900 ctermfg=2 | |
353 | highlight GitGutterChange guifg=#bbbb00 ctermfg=3 | |
354 | highlight GitGutterDelete guifg=#ff2222 ctermfg=1 | |
355 | ``` | |
334 | 356 | |
335 | 357 | To customise the symbols, add the following to your `~/.vimrc`: |
336 | 358 | |
339 | 361 | let g:gitgutter_sign_modified = 'yy' |
340 | 362 | let g:gitgutter_sign_removed = 'zz' |
341 | 363 | let g:gitgutter_sign_removed_first_line = '^^' |
364 | let g:gitgutter_sign_removed_above_and_below = '{' | |
342 | 365 | let g:gitgutter_sign_modified_removed = 'ww' |
343 | 366 | ``` |
344 | 367 | |
381 | 404 | ``` |
382 | 405 | |
383 | 406 | |
407 | #### The diff syntax colours used in the preview window | |
408 | ||
409 | To change the diff syntax colours used in the preview window, set up the `diff*` highlight groups in your colorscheme or `~/.vimrc`: | |
410 | ||
411 | ```viml | |
412 | diffAdded " if not set: use GitGutterAdd's foreground colour | |
413 | diffChanged " if not set: use GitGutterChange's foreground colour | |
414 | diffRemoved " if not set: use GitGutterDelete's foreground colour | |
415 | ``` | |
416 | ||
417 | Note the `diff*` highlight groups are used in any buffer whose `'syntax'` is `diff`. | |
418 | ||
419 | ||
420 | #### The intra-line diff highlights used in the preview window | |
421 | ||
422 | To change the intra-line diff highlights used in the preview window, set up the following highlight groups in your colorscheme or `~/.vimrc`: | |
423 | ||
424 | ```viml | |
425 | GitGutterAddIntraLine " default: gui=reverse cterm=reverse | |
426 | GitGutterDeleteIntraLine " default: gui=reverse cterm=reverse | |
427 | ``` | |
428 | ||
429 | For example, to use `DiffAdd` for intra-line added regions: | |
430 | ||
431 | ```viml | |
432 | highlight link GitGutterAddIntraLine DiffAdd | |
433 | ``` | |
434 | ||
435 | ||
384 | 436 | #### Whether the diff is relative to the index or working tree |
385 | 437 | |
386 | 438 | By default diffs are relative to the index. How you can make them relative to the working tree: |
471 | 523 | #### To use floating/popup windows for hunk previews |
472 | 524 | |
473 | 525 | 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. |
526 | ||
527 | ||
528 | #### The appearance of a floating/popup window for hunk previews | |
529 | ||
530 | 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 | 531 | |
475 | 532 | |
476 | 533 | #### To load all hunks into the current window's location list instead of the quickfix list |
633 | 690 | |
634 | 691 | Your colorscheme is configuring the `SignColumn` highlight group weirdly. Please see the section above on customising the sign column. |
635 | 692 | |
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 | 693 | > What happens if I also use another plugin which uses signs (e.g. Syntastic)? |
647 | 694 | |
648 | 695 | 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 | 705 | * Verify `:echo system("git --version")` succeeds. |
659 | 706 | * Verify your git config is compatible with the version of git returned by the command above. |
660 | 707 | * Verify your Vim supports signs (`:echo has('signs')` should give `1`). |
661 | * Verify your file is being tracked by git and has unstaged changes. | |
708 | * 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. | |
709 | * Execute `:sign place group=gitgutter`; you should see a list of signs. | |
710 | - If the signs are listed: this is a colorscheme / highlight problem. Compare `:highlight GitGutterAdd` with `:highlight SignColumn`. | |
711 | - 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 | 712 | |
663 | 713 | #### When the whole file is marked as added |
664 | 714 | |
680 | 730 | * [Smash Into Vim][siv] |
681 | 731 | |
682 | 732 | 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 | 733 | |
686 | 734 | |
687 | 735 | ### Intellectual Property |
0 | 0 | let s:available = has('nvim') || ( |
1 | 1 | \ has('job') && ( |
2 | \ (has('patch-7-4-1826') && !has('gui_running')) || | |
3 | \ (has('patch-7-4-1850') && has('gui_running')) || | |
4 | \ (has('patch-7-4-1832') && has('gui_macvim')) | |
2 | \ (has('patch-7.4.1826') && !has('gui_running')) || | |
3 | \ (has('patch-7.4.1850') && has('gui_running')) || | |
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 |
77 | 76 | throw 'gitgutter not tracked' |
77 | endif | |
78 | ||
79 | if gitgutter#utility#repo_path(a:bufnr, 0) == -3 | |
80 | throw 'gitgutter assume unchanged' | |
78 | 81 | endif |
79 | 82 | |
80 | 83 | " Wrap compound commands in parentheses to make Windows happy. |
119 | 122 | endif |
120 | 123 | |
121 | 124 | " Write file from index to temporary file. |
122 | let index_name = g:gitgutter_diff_base.':'.gitgutter#utility#repo_path(a:bufnr, 1) | |
125 | let index_name = gitgutter#utility#get_diff_base(a:bufnr).':'.gitgutter#utility#repo_path(a:bufnr, 1) | |
123 | 126 | let cmd .= g:gitgutter_git_executable.' '.g:gitgutter_git_args.' --no-pager show '.index_name.' > '.from_file.' && ' |
124 | 127 | |
125 | 128 | elseif a:from ==# 'working_tree' |
127 | 130 | endif |
128 | 131 | |
129 | 132 | " Call git-diff. |
130 | let cmd .= g:gitgutter_git_executable.' '.g:gitgutter_git_args.' --no-pager '.g:gitgutter_git_args | |
133 | let cmd .= g:gitgutter_git_executable.' '.g:gitgutter_git_args.' --no-pager' | |
131 | 134 | if s:c_flag |
132 | 135 | let cmd .= ' -c "diff.autorefreshindex=0"' |
133 | 136 | let cmd .= ' -c "diff.noprefix=false"' |
181 | 184 | let modified_lines = gitgutter#diff#process_hunks(a:bufnr, gitgutter#hunk#hunks(a:bufnr)) |
182 | 185 | |
183 | 186 | let signs_count = len(modified_lines) |
184 | if signs_count > g:gitgutter_max_signs | |
187 | if g:gitgutter_max_signs != -1 && signs_count > g:gitgutter_max_signs | |
185 | 188 | call gitgutter#utility#warn_once(a:bufnr, printf( |
186 | 189 | \ 'exceeded maximum number of signs (%d > %d, configured by g:gitgutter_max_signs).', |
187 | 190 | \ signs_count, g:gitgutter_max_signs), 'max_signs') |
399 | 402 | let bufcontents[0]=''.bufcontents[0] |
400 | 403 | endif |
401 | 404 | |
402 | call writefile(bufcontents, a:file, 'b') | |
405 | " The file we are writing to is a temporary file. Sometimes the parent | |
406 | " directory is deleted outside Vim but, because Vim caches the directory | |
407 | " name at startup and does not check for its existence subsequently, Vim | |
408 | " does not realise. This causes E482 errors. | |
409 | try | |
410 | call writefile(bufcontents, a:file, 'b') | |
411 | catch /E482/ | |
412 | call mkdir(fnamemodify(a:file, ':h'), '', '0700') | |
413 | call writefile(bufcontents, a:file, 'b') | |
414 | endtry | |
403 | 415 | endfunction |
404 | 416 | |
405 | 417 |
149 | 149 | return a:s1[endindex - maxlength + 1 : endindex] |
150 | 150 | endfunction |
151 | 151 | |
152 | if $VIM_GITGUTTER_TEST | |
153 | function! gitgutter#diff_highlight#lcs(s1, s2) | |
154 | return s:lcs(a:s1, a:s2) | |
155 | endfunction | |
156 | endif | |
157 | ||
158 | 152 | |
159 | 153 | " Returns 0-based index of last character of common prefix |
160 | 154 | " If there is no common prefix, returns -1. |
167 | 161 | return -1 |
168 | 162 | endif |
169 | 163 | for i in range(len) |
170 | if a:a[i:i] != a:b[i:i] | |
164 | if a:a[i:i] !=# a:b[i:i] | |
171 | 165 | return i - 1 |
172 | 166 | endif |
173 | 167 | endfor |
174 | 168 | return i |
175 | 169 | endfunction |
176 | ||
177 | if $VIM_GITGUTTER_TEST | |
178 | function! gitgutter#diff_highlight#common_prefix(a, b) | |
179 | return s:common_prefix(a:a, a:b) | |
180 | endfunction | |
181 | endif | |
182 | 170 | |
183 | 171 | |
184 | 172 | " Returns 0-based indices of start of common suffix |
198 | 186 | return [sa+1, sb+1] |
199 | 187 | endfunction |
200 | 188 | |
201 | if $VIM_GITGUTTER_TEST | |
202 | function! gitgutter#diff_highlight#common_suffix(a, b, start) | |
203 | return s:common_suffix(a:a, a:b, a:start) | |
204 | endfunction | |
205 | endif | |
206 | ||
207 | 189 | |
208 | 190 | " Split a string on another string. |
209 | 191 | " Assumes 1 occurrence of the delimiter. |
216 | 198 | |
217 | 199 | return [a:str[:i-1], a:str[i+len(a:delimiter):]] |
218 | 200 | endfunction |
219 | ||
220 | if $VIM_GITGUTTER_TEST | |
221 | function! gitgutter#diff_highlight#split(str, delimiter) | |
222 | return s:split(a:str, a:delimiter) | |
223 | endfunction | |
224 | endif |
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. |
349 | 380 | |
350 | 381 | return join(lines, "\n")."\n" |
351 | 382 | endfunction |
352 | ||
353 | if $VIM_GITGUTTER_TEST | |
354 | function! gitgutter#hunk#fix_file_references(filepath, hunk_diff) | |
355 | return s:fix_file_references(a:filepath, a:hunk_diff) | |
356 | endfunction | |
357 | endif | |
358 | 383 | |
359 | 384 | |
360 | 385 | function! s:adjust_hunk_summary(hunk_diff) abort |
395 | 420 | function! s:open_hunk_preview_window() |
396 | 421 | if g:gitgutter_preview_win_floating |
397 | 422 | if exists('*nvim_open_win') |
398 | call s:close_hunk_preview_window() | |
423 | call gitgutter#hunk#close_hunk_preview_window() | |
399 | 424 | |
400 | 425 | let buf = nvim_create_buf(v:false, v:false) |
401 | 426 | " 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 | \ }) | |
427 | let s:winid = nvim_open_win(buf, v:false, g:gitgutter_floating_window_options) | |
410 | 428 | call nvim_buf_set_option(buf, 'filetype', 'diff') |
411 | 429 | call nvim_buf_set_option(buf, 'buftype', 'acwrite') |
412 | 430 | call nvim_buf_set_option(buf, 'bufhidden', 'delete') |
414 | 432 | call nvim_buf_set_name(buf, 'gitgutter://hunk-preview') |
415 | 433 | |
416 | 434 | " Assumes cursor is in original window. |
417 | autocmd CursorMoved <buffer> ++once call s:close_hunk_preview_window() | |
435 | autocmd CursorMoved <buffer> ++once call gitgutter#hunk#close_hunk_preview_window() | |
436 | ||
437 | if g:gitgutter_close_preview_on_escape | |
438 | " Map <Esc> to close the floating preview. | |
439 | nnoremap <buffer> <silent> <Esc> :<C-U>call gitgutter#hunk#close_hunk_preview_window()<CR> | |
440 | " Ensure that when the preview window is closed, the map is removed. | |
441 | autocmd User GitGutterPreviewClosed silent! nunmap <buffer> <Esc> | |
442 | autocmd CursorMoved <buffer> ++once silent! nunmap <buffer> <Esc> | |
443 | execute "autocmd WinClosed <buffer=".winbufnr(s:winid)."> doautocmd" s:nomodeline "User GitGutterPreviewClosed" | |
444 | endif | |
418 | 445 | |
419 | 446 | return |
420 | 447 | endif |
421 | 448 | |
422 | 449 | if exists('*popup_create') |
423 | let s:winid = popup_create('', { | |
424 | \ 'line': 'cursor+1', | |
425 | \ 'col': 'cursor', | |
426 | \ 'moved': 'any', | |
427 | \ }) | |
450 | if g:gitgutter_close_preview_on_escape | |
451 | let g:gitgutter_floating_window_options.filter = function('s:close_popup_on_escape') | |
452 | endif | |
453 | ||
454 | let s:winid = popup_create('', g:gitgutter_floating_window_options) | |
428 | 455 | |
429 | 456 | call setbufvar(winbufnr(s:winid), '&filetype', 'diff') |
430 | 457 | |
432 | 459 | endif |
433 | 460 | endif |
434 | 461 | |
462 | if exists('&previewpopup') | |
463 | let [previewpopup, &previewpopup] = [&previewpopup, ''] | |
464 | endif | |
465 | ||
466 | " Specifying where to open the preview window can lead to the cursor going | |
467 | " to an unexpected window when the preview window is closed (#769). | |
468 | silent! noautocmd execute g:gitgutter_preview_win_location 'pedit gitgutter://hunk-preview' | |
435 | 469 | silent! wincmd P |
436 | if !&previewwindow | |
437 | noautocmd execute g:gitgutter_preview_win_location &previewheight 'new gitgutter://hunk-preview' | |
438 | doautocmd WinEnter | |
470 | setlocal statusline=%{''} | |
471 | doautocmd WinEnter | |
472 | if exists('*win_getid') | |
439 | 473 | 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 | |
474 | else | |
475 | let s:preview_bufnr = bufnr('') | |
476 | endif | |
477 | setlocal filetype=diff buftype=acwrite bufhidden=delete | |
478 | " Reset some defaults in case someone else has changed them. | |
479 | setlocal noreadonly modifiable noswapfile | |
480 | if g:gitgutter_close_preview_on_escape | |
481 | " Ensure cursor goes to the expected window. | |
482 | nnoremap <buffer> <silent> <Esc> :<C-U>wincmd p<Bar>pclose<CR> | |
483 | endif | |
484 | ||
485 | if exists('&previewpopup') | |
486 | let &previewpopup=previewpopup | |
487 | endif | |
488 | endfunction | |
489 | ||
490 | ||
491 | function! s:close_popup_on_escape(winid, key) | |
492 | if a:key == "\<Esc>" | |
493 | call popup_close(a:winid) | |
494 | return 1 | |
495 | endif | |
496 | return 0 | |
445 | 497 | endfunction |
446 | 498 | |
447 | 499 | |
449 | 501 | " Preview window: assumes cursor is in preview window. |
450 | 502 | function! s:populate_hunk_preview_window(header, body) |
451 | 503 | let body_length = len(a:body) |
452 | let height = min([body_length, &previewheight]) | |
453 | 504 | |
454 | 505 | if g:gitgutter_preview_win_floating |
455 | 506 | if exists('*nvim_open_win') |
507 | let height = min([body_length, g:gitgutter_floating_window_options.height]) | |
508 | ||
456 | 509 | " Assumes cursor is not in previewing window. |
457 | 510 | call nvim_buf_set_var(winbufnr(s:winid), 'hunk_header', a:header) |
511 | ||
512 | let [_scrolloff, &scrolloff] = [&scrolloff, 0] | |
458 | 513 | |
459 | 514 | let width = max(map(copy(a:body), 'strdisplaywidth(v:val)')) |
460 | 515 | call nvim_win_set_width(s:winid, width) |
461 | 516 | call nvim_win_set_height(s:winid, height) |
517 | ||
518 | let &scrolloff=_scrolloff | |
462 | 519 | |
463 | 520 | call nvim_buf_set_lines(winbufnr(s:winid), 0, -1, v:false, []) |
464 | 521 | call nvim_buf_set_lines(winbufnr(s:winid), 0, -1, v:false, a:body) |
485 | 542 | |
486 | 543 | else |
487 | 544 | let b:hunk_header = a:header |
488 | execute 'resize' height | |
489 | 545 | |
490 | 546 | %delete _ |
491 | 547 | call setline(1, a:body) |
492 | 548 | setlocal nomodified |
549 | ||
550 | normal! G$ | |
551 | let hunk_height = max([body_length, winline()]) | |
552 | let height = min([hunk_height, &previewheight]) | |
553 | execute 'resize' height | |
554 | 1 | |
493 | 555 | |
494 | 556 | call clearmatches() |
495 | 557 | for region in gitgutter#diff_highlight#process(a:body) |
505 | 567 | function! s:enable_staging_from_hunk_preview_window() |
506 | 568 | augroup gitgutter_hunk_preview |
507 | 569 | autocmd! |
508 | execute 'autocmd BufWriteCmd <buffer='.winbufnr(s:winid).'> GitGutterStageHunk' | |
570 | let bufnr = s:winid != 0 ? winbufnr(s:winid) : s:preview_bufnr | |
571 | execute 'autocmd BufWriteCmd <buffer='.bufnr.'> GitGutterStageHunk' | |
509 | 572 | augroup END |
510 | 573 | endfunction |
511 | 574 | |
516 | 579 | endfunction |
517 | 580 | |
518 | 581 | |
519 | function! s:close_hunk_preview_window() | |
520 | call setbufvar(winbufnr(s:winid), '&modified', 0) | |
582 | function! gitgutter#hunk#close_hunk_preview_window() | |
583 | let bufnr = s:winid != 0 ? winbufnr(s:winid) : s:preview_bufnr | |
584 | call setbufvar(bufnr, '&modified', 0) | |
521 | 585 | |
522 | 586 | if g:gitgutter_preview_win_floating |
523 | 587 | if win_id2win(s:winid) > 0 |
528 | 592 | endif |
529 | 593 | |
530 | 594 | let s:winid = 0 |
531 | endfunction | |
595 | let s:preview_bufnr = 0 | |
596 | endfunction | |
597 | ||
598 | ||
599 | function gitgutter#hunk#is_preview_window_open() | |
600 | if g:gitgutter_preview_win_floating | |
601 | if win_id2win(s:winid) > 0 | |
602 | execute win_id2win(s:winid).'wincmd c' | |
603 | endif | |
604 | else | |
605 | for i in range(1, winnr('$')) | |
606 | if getwinvar(i, '&previewwindow') | |
607 | return 1 | |
608 | endif | |
609 | endfor | |
610 | endif | |
611 | return 0 | |
612 | 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) && |
108 | 107 | " * non-empty string - path |
109 | 108 | " * -1 - pending |
110 | 109 | " * -2 - not tracked by git |
110 | " * -3 - assume unchanged | |
111 | 111 | function! gitgutter#utility#repo_path(bufnr, shellesc) abort |
112 | 112 | let p = gitgutter#utility#getbufvar(a:bufnr, 'path', '') |
113 | 113 | return a:shellesc ? gitgutter#utility#shellescape(p) : p |
116 | 116 | |
117 | 117 | let s:set_path_handler = {} |
118 | 118 | |
119 | function! s:set_path_handler.out(buffer, path) abort | |
120 | let path = s:strip_trailing_new_line(a:path) | |
121 | call gitgutter#utility#setbufvar(a:buffer, 'path', path) | |
119 | function! s:set_path_handler.out(buffer, listing) abort | |
120 | let listing = s:strip_trailing_new_line(a:listing) | |
121 | let [status, path] = [listing[0], listing[2:]] | |
122 | if status =~# '[a-z]' | |
123 | call gitgutter#utility#setbufvar(a:buffer, 'path', -3) | |
124 | else | |
125 | call gitgutter#utility#setbufvar(a:buffer, 'path', path) | |
126 | endif | |
122 | 127 | |
123 | 128 | if type(self.continuation) == type(function('tr')) |
124 | 129 | call self.continuation() |
140 | 145 | " * non-empty string - path |
141 | 146 | " * -1 - pending |
142 | 147 | " * -2 - not tracked by git |
148 | " * -3 - assume unchanged | |
143 | 149 | |
144 | 150 | call gitgutter#utility#setbufvar(a:bufnr, 'path', -1) |
145 | let cmd = gitgutter#utility#cd_cmd(a:bufnr, g:gitgutter_git_executable.' '.g:gitgutter_git_args.' ls-files --error-unmatch --full-name -z -- '.gitgutter#utility#shellescape(s:filename(a:bufnr))) | |
151 | let cmd = gitgutter#utility#cd_cmd(a:bufnr, | |
152 | \ g:gitgutter_git_executable.' '.g:gitgutter_git_args. | |
153 | \ ' ls-files -v --error-unmatch --full-name -z -- '. | |
154 | \ gitgutter#utility#shellescape(s:filename(a:bufnr))) | |
146 | 155 | |
147 | 156 | if g:gitgutter_async && gitgutter#async#available() && !has('vim_starting') |
148 | 157 | let handler = copy(s:set_path_handler) |
151 | 160 | return 'async' |
152 | 161 | endif |
153 | 162 | |
154 | let path = gitgutter#utility#system(cmd) | |
163 | let listing = gitgutter#utility#system(cmd) | |
164 | ||
155 | 165 | if v:shell_error |
156 | 166 | call gitgutter#utility#setbufvar(a:bufnr, 'path', -2) |
157 | else | |
158 | call gitgutter#utility#setbufvar(a:bufnr, 'path', s:strip_trailing_new_line(path)) | |
167 | return | |
168 | endif | |
169 | ||
170 | let listing = s:strip_trailing_new_line(listing) | |
171 | let [status, path] = [listing[0], listing[2:]] | |
172 | if status =~# '[a-z]' | |
173 | call gitgutter#utility#setbufvar(a:bufnr, 'path', -3) | |
174 | else | |
175 | call gitgutter#utility#setbufvar(a:bufnr, 'path', path) | |
159 | 176 | endif |
160 | 177 | endfunction |
161 | 178 | |
162 | 179 | |
163 | 180 | function! gitgutter#utility#cd_cmd(bufnr, cmd) abort |
164 | let cd = s:unc_path(a:bufnr) ? 'pushd' : (gitgutter#utility#windows() ? 'cd /d' : 'cd') | |
181 | let cd = s:unc_path(a:bufnr) ? 'pushd' : (gitgutter#utility#windows() && s:dos_shell() ? 'cd /d' : 'cd') | |
165 | 182 | return cd.' '.s:dir(a:bufnr).' && '.a:cmd |
166 | 183 | endfunction |
167 | 184 | |
169 | 186 | return s:abs_path(a:bufnr, 0) =~ '^\\\\' |
170 | 187 | endfunction |
171 | 188 | |
189 | function! s:dos_shell() | |
190 | return &shell == 'cmd.exe' || &shell == 'command.com' | |
191 | endfunction | |
192 | ||
172 | 193 | function! s:use_known_shell() abort |
173 | 194 | if has('unix') && &shell !=# 'sh' |
174 | let [s:shell, s:shellcmdflag, s:shellredir] = [&shell, &shellcmdflag, &shellredir] | |
195 | let [s:shell, s:shellcmdflag, s:shellredir, s:shellpipe, s:shellquote, s:shellxquote] = [&shell, &shellcmdflag, &shellredir, &shellpipe, &shellquote, &shellxquote] | |
175 | 196 | let &shell = 'sh' |
176 | 197 | set shellcmdflag=-c shellredir=>%s\ 2>&1 |
177 | 198 | endif |
199 | if has('win32') && (&shell =~# 'pwsh' || &shell =~# 'powershell') | |
200 | let [s:shell, s:shellcmdflag, s:shellredir, s:shellpipe, s:shellquote, s:shellxquote] = [&shell, &shellcmdflag, &shellredir, &shellpipe, &shellquote, &shellxquote] | |
201 | let &shell = 'cmd.exe' | |
202 | set shellcmdflag=/s\ /c shellredir=>%s\ 2>&1 shellpipe=>%s\ 2>&1 shellquote= shellxquote=" | |
203 | endif | |
178 | 204 | endfunction |
179 | 205 | |
180 | 206 | 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) | |
207 | if (has('unix') || has('win32')) && exists('s:shell') | |
208 | let [&shell, &shellcmdflag, &shellredir, &shellpipe, &shellquote, &shellxquote] = [s:shell, s:shellcmdflag, s:shellredir, s:shellpipe, s:shellquote, s:shellxquote] | |
209 | endif | |
210 | endfunction | |
211 | ||
212 | function! gitgutter#utility#get_diff_base(bufnr) | |
187 | 213 | let p = resolve(expand('#'.a:bufnr.':p')) |
188 | 214 | let ml = matchlist(p, '\v^fugitive:/.*/(\x{40,})/') |
189 | 215 | if !empty(ml) && !empty(ml[1]) |
190 | let g:gitgutter_diff_base = ml[1].'^' | |
191 | endif | |
216 | return ml[1].'^' | |
217 | endif | |
218 | return g:gitgutter_diff_base | |
192 | 219 | endfunction |
193 | 220 | |
194 | 221 | 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 | |
41 | 43 | let diff = gitgutter#diff#run_diff(a:bufnr, g:gitgutter_diff_relative_to, 0) |
42 | 44 | catch /gitgutter not tracked/ |
43 | 45 | call gitgutter#debug#log('Not tracked: '.gitgutter#utility#file(a:bufnr)) |
46 | catch /gitgutter assume unchanged/ | |
47 | call gitgutter#debug#log('Assume unchanged: '.gitgutter#utility#file(a:bufnr)) | |
44 | 48 | catch /gitgutter diff failed/ |
45 | 49 | call gitgutter#debug#log('Diff failed: '.gitgutter#utility#file(a:bufnr)) |
46 | 50 | call gitgutter#hunk#reset(a:bufnr) |
56 | 60 | |
57 | 61 | |
58 | 62 | function! gitgutter#disable() abort |
59 | " get list of all buffers (across all tabs) | |
63 | call s:toggle_each_buffer(0) | |
64 | let g:gitgutter_enabled = 0 | |
65 | endfunction | |
66 | ||
67 | function! gitgutter#enable() abort | |
68 | call s:toggle_each_buffer(1) | |
69 | let g:gitgutter_enabled = 1 | |
70 | endfunction | |
71 | ||
72 | function s:toggle_each_buffer(enable) | |
60 | 73 | for bufnr in range(1, bufnr('$') + 1) |
61 | 74 | if buflisted(bufnr) |
62 | 75 | let file = expand('#'.bufnr.':p') |
63 | 76 | if !empty(file) |
64 | call s:clear(bufnr) | |
77 | if a:enable | |
78 | call gitgutter#buffer_enable(bufnr) | |
79 | else | |
80 | call gitgutter#buffer_disable(bufnr) | |
81 | end | |
65 | 82 | endif |
66 | 83 | endif |
67 | 84 | 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 | 85 | endfunction |
76 | 86 | |
77 | 87 | function! gitgutter#toggle() abort |
83 | 93 | endfunction |
84 | 94 | |
85 | 95 | |
86 | function! gitgutter#buffer_disable() abort | |
87 | let bufnr = bufnr('') | |
96 | function! gitgutter#buffer_disable(...) abort | |
97 | let bufnr = a:0 ? a:1 : bufnr('') | |
88 | 98 | call gitgutter#utility#setbufvar(bufnr, 'enabled', 0) |
89 | 99 | call s:clear(bufnr) |
90 | 100 | endfunction |
91 | 101 | |
92 | function! gitgutter#buffer_enable() abort | |
93 | let bufnr = bufnr('') | |
102 | function! gitgutter#buffer_enable(...) abort | |
103 | let bufnr = a:0 ? a:1 : bufnr('') | |
94 | 104 | call gitgutter#utility#setbufvar(bufnr, 'enabled', 1) |
95 | 105 | call gitgutter#process_buffer(bufnr, 1) |
96 | 106 | endfunction |
97 | 107 | |
98 | function! gitgutter#buffer_toggle() abort | |
99 | if gitgutter#utility#getbufvar(bufnr(''), 'enabled', 1) | |
100 | call gitgutter#buffer_disable() | |
101 | else | |
102 | call gitgutter#buffer_enable() | |
108 | function! gitgutter#buffer_toggle(...) abort | |
109 | let bufnr = a:0 ? a:1 : bufnr('') | |
110 | if gitgutter#utility#getbufvar(bufnr, 'enabled', 1) | |
111 | call gitgutter#buffer_disable(bufnr) | |
112 | else | |
113 | call gitgutter#buffer_enable(bufnr) | |
103 | 114 | endif |
104 | 115 | endfunction |
105 | 116 | |
180 | 191 | " - this runs synchronously |
181 | 192 | " - it ignores unsaved changes in buffers |
182 | 193 | " - it does not change to the repo root |
183 | function! gitgutter#quickfix() | |
194 | function! gitgutter#quickfix(current_file) | |
195 | let cmd = g:gitgutter_git_executable.' '.g:gitgutter_git_args.' rev-parse --show-cdup' | |
196 | let path_to_repo = get(systemlist(cmd), 0, '') | |
197 | if !empty(path_to_repo) && path_to_repo[-1:] != '/' | |
198 | let path_to_repo .= '/' | |
199 | endif | |
200 | ||
184 | 201 | 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 | |
202 | let cmd = g:gitgutter_git_executable.' '.g:gitgutter_git_args.' --no-pager'. | |
203 | \ ' diff --no-ext-diff --no-color -U0'. | |
204 | \ ' --src-prefix=a/'.path_to_repo.' --dst-prefix=b/'.path_to_repo.' '. | |
205 | \ g:gitgutter_diff_args. ' '. g:gitgutter_diff_base | |
206 | if a:current_file | |
207 | let cmd = cmd.' -- '.expand('%:p') | |
208 | endif | |
187 | 209 | let diff = systemlist(cmd) |
188 | 210 | let lnum = 0 |
189 | 211 | for line in diff |
195 | 217 | elseif line =~ '^diff --git "' |
196 | 218 | let [_, fnamel, _, fnamer] = split(line, '"') |
197 | 219 | let fname = fnamel ==# fnamer ? fnamel : fnamel[2:] |
220 | elseif line =~ '^diff --cc [^"]' | |
221 | let fname = line[10:] | |
222 | elseif line =~ '^diff --cc "' | |
223 | let [_, fname] = split(line, '"') | |
198 | 224 | elseif line =~ '^@@' |
199 | 225 | let lnum = matchlist(line, '+\(\d\+\)')[1] |
200 | 226 | elseif lnum > 0 |
208 | 234 | call setloclist(0, locations) |
209 | 235 | endif |
210 | 236 | endfunction |
237 | ||
238 | ||
239 | function! gitgutter#difforig() | |
240 | let bufnr = bufnr('') | |
241 | let path = gitgutter#utility#repo_path(bufnr, 1) | |
242 | let filetype = &filetype | |
243 | ||
244 | vertical new | |
245 | set buftype=nofile | |
246 | let &filetype = filetype | |
247 | ||
248 | if g:gitgutter_diff_relative_to ==# 'index' | |
249 | let index_name = gitgutter#utility#get_diff_base(bufnr).':'.path | |
250 | let cmd = gitgutter#utility#cd_cmd(bufnr, | |
251 | \ g:gitgutter_git_executable.' '.g:gitgutter_git_args.' --no-pager show '.index_name | |
252 | \ ) | |
253 | " NOTE: this uses &shell to execute cmd. Perhaps we should use instead | |
254 | " gitgutter#utility's use_known_shell() / restore_shell() functions. | |
255 | silent! execute "read ++edit !" cmd | |
256 | else | |
257 | silent! execute "read ++edit" path | |
258 | endif | |
259 | ||
260 | 0d_ | |
261 | diffthis | |
262 | wincmd p | |
263 | diffthis | |
264 | endfunction |
0 | vim-gitgutter (0~20200414+git20220824.1.f19b620-1) UNRELEASED; urgency=low | |
1 | ||
2 | * New upstream snapshot. | |
3 | ||
4 | -- Debian Janitor <janitor@jelmer.uk> Fri, 21 Oct 2022 21:43:59 -0000 | |
5 | ||
0 | 6 | vim-gitgutter (0~20200414-2) unstable; urgency=medium |
1 | 7 | |
2 | 8 | * Team upload. |
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* |
175 | 220 | :GitGutterFold Fold all unchanged lines. Execute again to undo. |
176 | 221 | |
177 | 222 | |
178 | =============================================================================== | |
179 | AUTOCOMMAND *gitgutter-autocommand* | |
223 | Other commands:~ | |
224 | ||
225 | *gitgutter-:GitGutterDiffOrig* | |
226 | :GitGutterDiffOrig Similar to |:DiffOrig| but shows gitgutter's diff. | |
227 | ||
228 | ||
229 | =============================================================================== | |
230 | AUTOCOMMANDS *gitgutter-autocommands* | |
180 | 231 | |
181 | 232 | User GitGutter~ |
182 | 233 | |
188 | 239 | A dictionary `g:gitgutter_hook_context` is made available during its execution, |
189 | 240 | which contains an entry `bufnr` that contains the buffer number being updated. |
190 | 241 | |
242 | User GitGutterStage~ | |
243 | ||
244 | After staging a hunk or part of a hunk vim-gitgutter fires a |User| |autocmd| | |
245 | with the event GitGutterStage. Staging always happens in the current buffer. | |
191 | 246 | |
192 | 247 | =============================================================================== |
193 | 248 | MAPPINGS *gitgutter-mappings* |
298 | 353 | |g:gitgutter_sign_removed| |
299 | 354 | |g:gitgutter_sign_removed_first_line| |
300 | 355 | |g:gitgutter_sign_modified_removed| |
301 | |g:gitgutter_override_sign_column_highlight| | |
356 | |g:gitgutter_set_sign_backgrounds| | |
357 | ||
358 | Hunk jumping:~ | |
359 | ||
360 | |g:gitgutter_show_msg_on_hunk_jumping| | |
302 | 361 | |
303 | 362 | Hunk previews:~ |
304 | 363 | |
305 | 364 | |g:gitgutter_preview_win_floating| |
365 | |g:gitgutter_floating_window_options| | |
366 | |g:gitgutter_close_preview_on_escape| | |
306 | 367 | |
307 | 368 | Terminal:~ |
308 | 369 | |
404 | 465 | Determines whether or not to show line number highlights. |
405 | 466 | |
406 | 467 | *g:gitgutter_max_signs* |
407 | Default: 500 | |
468 | Default: 500 (Vim < 8.1.0614, Neovim < 0.4.0) | |
469 | -1 (otherwise) | |
408 | 470 | |
409 | 471 | Sets the maximum number of signs to show in a buffer. Vim is slow at updating |
410 | 472 | signs, so to avoid slowing down the GUI the number of signs is capped. When |
411 | 473 | the number of changed lines exceeds this value, the plugin removes all signs |
412 | 474 | and displays a warning message. |
413 | 475 | |
476 | When set to -1 the limit is not applied. | |
477 | ||
414 | 478 | *g:gitgutter_sign_priority* |
415 | 479 | Default: 10 |
416 | 480 | |
427 | 491 | *g:gitgutter_sign_modified* |
428 | 492 | *g:gitgutter_sign_removed* |
429 | 493 | *g:gitgutter_sign_removed_first_line* |
494 | *g:gitgutter_sign_removed_above_and_below* | |
430 | 495 | *g:gitgutter_sign_modified_removed* |
431 | 496 | Defaults: |
432 | 497 | > |
434 | 499 | let g:gitgutter_sign_modified = '~' |
435 | 500 | let g:gitgutter_sign_removed = '_' |
436 | 501 | let g:gitgutter_sign_removed_first_line = '‾' |
502 | let g:gitgutter_sign_removed_above_and_below = '_¯' | |
437 | 503 | let g:gitgutter_sign_modified_removed = '~_' |
438 | 504 | < |
439 | 505 | You can use unicode characters but not images. Signs must not take up more than |
440 | 506 | 2 columns. |
441 | 507 | |
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 | ||
508 | *g:gitgutter_set_sign_backgrounds* | |
509 | Default: 0 | |
510 | ||
511 | Only applies to existing GitGutter* highlight groups. See | |
512 | |gitgutter-highlights|. | |
513 | ||
514 | Controls whether to override the signs' background colours to match the | |
515 | |hl-SignColumn|. | |
462 | 516 | |
463 | 517 | *g:gitgutter_preview_win_floating* |
464 | 518 | Default: 0 (Vim) |
469 | 523 | popup windows on Vim you will not be able to stage partial hunks via the |
470 | 524 | preview window. |
471 | 525 | |
526 | *g:gitgutter_floating_window_options* | |
527 | Default: | |
528 | > | |
529 | " Vim | |
530 | { | |
531 | \ 'line': 'cursor+1', | |
532 | \ 'col': 'cursor', | |
533 | \ 'moved': 'any' | |
534 | } | |
535 | ||
536 | " Neovim | |
537 | { | |
538 | \ 'relative': 'cursor', | |
539 | \ 'row': 1, | |
540 | \ 'col': 0, | |
541 | \ 'width': 42, | |
542 | \ 'height': &previewheight, | |
543 | \ 'style': 'minimal' | |
544 | } | |
545 | < | |
546 | This dictionary is passed directly to |popup_create()| (Vim) or | |
547 | |nvim_open_win()| (Neovim). | |
548 | ||
549 | *g:gitgutter_close_preview_on_escape* | |
550 | Default: 0 | |
551 | ||
552 | Whether pressing <Esc> in a preview window closes it. | |
553 | ||
472 | 554 | *g:gitgutter_terminal_reports_focus* |
473 | 555 | Default: 1 |
474 | 556 | |
517 | 599 | When switched on, the :GitGutterQuickFix command populates the location list |
518 | 600 | of the current window instead of the global quickfix list. |
519 | 601 | |
602 | *g:gitgutter_show_msg_on_hunk_jumping* | |
603 | Default: 1 | |
604 | ||
605 | When switched on, a message like "Hunk 4 of 11" is shown on hunk jumping. | |
606 | ||
520 | 607 | |
521 | 608 | =============================================================================== |
522 | 609 | HIGHLIGHTS *gitgutter-highlights* |
523 | 610 | |
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 | < | |
611 | To change the signs' colours, specify these highlight groups in your |vimrc|: | |
612 | > | |
613 | highlight GitGutterAdd guifg=#009900 ctermfg=2 | |
614 | highlight GitGutterChange guifg=#bbbb00 ctermfg=3 | |
615 | highlight GitGutterDelete guifg=#ff2222 ctermfg=1 | |
616 | < | |
617 | ||
618 | See |highlight-guifg| and |highlight-ctermfg| for the values you can use. | |
619 | ||
620 | If you do not like the signs' background colours and you do not want to update | |
621 | the GitGutter* highlight groups yourself, you can get the plugin to do it | |
622 | |g:gitgutter_set_sign_backgrounds|. | |
538 | 623 | |
539 | 624 | To change the line highlights, set up the following highlight groups in your |
540 | 625 | colorscheme or |vimrc|: |
561 | 646 | > |
562 | 647 | highlight link GitGutterChangeLineNr Underlined |
563 | 648 | < |
649 | To change the diff syntax colours used in the preview window, set up the diff* | |
650 | highlight groups in your colorscheme or |vimrc|: | |
651 | > | |
652 | diffAdded " if not set: use GitGutterAdd's foreground colour | |
653 | diffChanged " if not set: use GitGutterChange's foreground colour | |
654 | diffRemoved " if not set: use GitGutterDelete's foreground colour | |
655 | < | |
656 | Note the diff* highlight groups are used in any buffer whose 'syntax' is | |
657 | "diff". | |
658 | ||
659 | To change the intra-line diff highlights used in the preview window, set up | |
660 | the following highlight groups in your colorscheme or |vimrc|: | |
661 | > | |
662 | GitGutterAddIntraLine " default: gui=reverse cterm=reverse | |
663 | GitGutterDeleteIntraLine " default: gui=reverse cterm=reverse | |
664 | < | |
665 | For example, to use |hl-DiffAdd| for intra-line added regions: | |
666 | > | |
667 | highlight link GitGutterAddIntraLine DiffAdd | |
668 | < | |
564 | 669 | |
565 | 670 | |
566 | 671 | =============================================================================== |
591 | 696 | d. Why are the colours in the sign column weird? |
592 | 697 | |
593 | 698 | 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. | |
699 | Here are two ways you could change the colours: | |
700 | > | |
701 | highlight! link SignColumn LineNr | |
702 | highlight SignColumn guibg=whatever ctermbg=whatever | |
703 | < | |
596 | 704 | |
597 | 705 | e. What happens if I also use another plugin which uses signs (e.g. Syntastic)? |
598 | 706 | |
631 | 739 | < |
632 | 740 | If the result is -2, the plugin thinks your file is not tracked by git. |
633 | 741 | |
742 | 6. Check whether the signs have been placed: | |
743 | > | |
744 | :sign place group=gitgutter | |
745 | < | |
746 | If you see a list of signs, this is a colorscheme / highlight problem. | |
747 | Compare these two highlight values: | |
748 | > | |
749 | :highlight GitGutterAdd | |
750 | :highlight SignColumn | |
751 | < | |
752 | If no signs are listed, the call to git-diff is probably failing. Turn on | |
753 | logging by adding the following to your vimrc, restart, reproduce the problem, | |
754 | and examing the gitgutter.log file in the plugin's directory. | |
755 | > | |
756 | let g:gitgutter_log = 1 | |
757 | < | |
634 | 758 | |
635 | 759 | When the whole file is marked as added:~ |
636 | 760 | |
656 | 780 | let g:gitgutter_terminal_reports_focus = 0 |
657 | 781 | < |
658 | 782 | |
783 | 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) | |
135 | ||
136 | command! -bar GitGutterDiffOrig call gitgutter#difforig() | |
113 | 137 | |
114 | 138 | " }}} |
115 | 139 | |
193 | 217 | " Maps {{{ |
194 | 218 | |
195 | 219 | 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>" | |
220 | 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 | 221 | 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>" | |
222 | 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 | 223 | |
200 | 224 | 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> | |
225 | xnoremap <silent> <Plug>GitGutterStageHunk :call gitgutter#utility#warn('Please change your map <lt>Plug>GitGutterStageHunk to <lt>Plug>(GitGutterStageHunk)')<CR> | |
202 | 226 | 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> | |
227 | nnoremap <silent> <Plug>GitGutterStageHunk :call gitgutter#utility#warn('Please change your map <lt>Plug>GitGutterStageHunk to <lt>Plug>(GitGutterStageHunk)')<CR> | |
204 | 228 | 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> | |
229 | nnoremap <silent> <Plug>GitGutterUndoHunk :call gitgutter#utility#warn('Please change your map <lt>Plug>GitGutterUndoHunk to <lt>Plug>(GitGutterUndoHunk)')<CR> | |
206 | 230 | 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> | |
231 | nnoremap <silent> <Plug>GitGutterPreviewHunk :call gitgutter#utility#warn('Please change your map <lt>Plug>GitGutterPreviewHunk to <lt>Plug>(GitGutterPreviewHunk)')<CR> | |
208 | 232 | |
209 | 233 | " }}} |
210 | 234 | |
211 | 235 | function! s:on_bufenter() |
212 | 236 | call gitgutter#setup_maps() |
237 | ||
238 | " To keep vim's start-up fast, do not process the buffer when vim is starting. | |
239 | " Instead process it a short time later. Normally we would rely on our | |
240 | " CursorHold autocommand to handle this but it turns out CursorHold is not | |
241 | " guaranteed to fire if the user has not typed anything yet; so set up a | |
242 | " timer instead. The disadvantage is that if CursorHold does fire, the | |
243 | " plugin will do a round of unnecessary work; but since there will not have | |
244 | " been any changes to the buffer since the first round, the second round | |
245 | " will be cheap. | |
246 | if has('vim_starting') && !$VIM_GITGUTTER_TEST | |
247 | if exists('*timer_start') && has('lambda') | |
248 | call s:next_tick("call gitgutter#process_buffer(+".bufnr('').", 0)") | |
249 | else | |
250 | call gitgutter#process_buffer(bufnr(''), 0) | |
251 | endif | |
252 | return | |
253 | endif | |
213 | 254 | |
214 | 255 | if exists('t:gitgutter_didtabenter') && t:gitgutter_didtabenter |
215 | 256 | let t:gitgutter_didtabenter = 0 |
219 | 260 | endif |
220 | 261 | endfunction |
221 | 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 | " }}} |
204 | 204 | endfunction |
205 | 205 | |
206 | 206 | |
207 | function Test_filename_with_space() | |
208 | call system('touch fix\ ture.txt && git add fix\ ture.txt') | |
209 | edit fix\ ture.txt | |
210 | normal ggo* | |
211 | call s:trigger_gitgutter() | |
212 | ||
213 | let expected = [ | |
214 | \ {'lnum': 1, 'name': 'GitGutterLineAdded'}, | |
215 | \ {'lnum': 2, 'name': 'GitGutterLineAdded'} | |
216 | \ ] | |
217 | call s:assert_signs(expected, 'fix\ ture.txt') | |
218 | endfunction | |
219 | ||
220 | ||
207 | 221 | function Test_filename_leading_dash() |
208 | 222 | call system('touch -- -fixture.txt && git add -- -fixture.txt') |
209 | 223 | edit -fixture.txt |
834 | 848 | |
835 | 849 | |
836 | 850 | function Test_fix_file_references() |
851 | let sid = matchstr(execute('filter autoload/gitgutter/hunk.vim scriptnames'), '\d\+') | |
852 | let FixFileReferences = function("<SNR>".sid."_fix_file_references") | |
853 | ||
837 | 854 | " No special characters |
838 | 855 | let hunk_diff = join([ |
839 | 856 | \ 'diff --git a/fixture.txt b/fixture.txt', |
854 | 871 | \ '+x' |
855 | 872 | \ ], "\n")."\n" |
856 | 873 | |
857 | call assert_equal(expected, gitgutter#hunk#fix_file_references(filepath, hunk_diff)) | |
874 | call assert_equal(expected, FixFileReferences(filepath, hunk_diff)) | |
858 | 875 | |
859 | 876 | " diff.mnemonicPrefix; spaces in filename |
860 | 877 | let hunk_diff = join([ |
876 | 893 | \ '+x' |
877 | 894 | \ ], "\n")."\n" |
878 | 895 | |
879 | call assert_equal(expected, gitgutter#hunk#fix_file_references(filepath, hunk_diff)) | |
896 | call assert_equal(expected, FixFileReferences(filepath, hunk_diff)) | |
880 | 897 | |
881 | 898 | " Backslashes in filename; quotation marks |
882 | 899 | let hunk_diff = join([ |
898 | 915 | \ '+x' |
899 | 916 | \ ], "\n")."\n" |
900 | 917 | |
901 | call assert_equal(expected, gitgutter#hunk#fix_file_references(filepath, hunk_diff)) | |
918 | call assert_equal(expected, FixFileReferences(filepath, hunk_diff)) | |
902 | 919 | endfunction |
903 | 920 | |
904 | 921 | |
946 | 963 | call setline(5, ['A', 'B']) |
947 | 964 | call setline(9, ['C', 'D']) |
948 | 965 | write |
966 | let bufnr1 = bufnr('') | |
967 | ||
968 | edit fixture_dos.txt | |
969 | call setline(2, ['A', 'B']) | |
970 | write | |
971 | let bufnr2 = bufnr('') | |
949 | 972 | |
950 | 973 | GitGutterQuickFix |
951 | 974 | |
952 | 975 | let expected = [ |
953 | \ {'lnum': 5, 'bufnr': bufnr(''), 'text': '-e'}, | |
954 | \ {'lnum': 9, 'bufnr': bufnr(''), 'text': '-i'} | |
976 | \ {'lnum': 5, 'bufnr': bufnr1, 'text': '-e'}, | |
977 | \ {'lnum': 9, 'bufnr': bufnr1, 'text': '-i'}, | |
978 | \ {'lnum': 2, 'bufnr': bufnr2, 'text': "-b\r"} | |
955 | 979 | \ ] |
956 | 980 | |
957 | 981 | call s:assert_list_of_dicts(expected, getqflist()) |
982 | ||
983 | GitGutterQuickFixCurrentFile | |
984 | ||
985 | let expected = [ | |
986 | \ {'lnum': 2, 'bufnr': bufnr(''), 'text': "-b\r"}, | |
987 | \ ] | |
988 | ||
989 | call s:assert_list_of_dicts(expected, getqflist()) | |
958 | 990 | endfunction |
959 | 991 | |
960 | 992 | |
961 | 993 | function Test_common_prefix() |
994 | let sid = matchstr(execute('filter autoload/gitgutter/diff_highlight.vim scriptnames'), '\d\+') | |
995 | let CommonPrefix = function("<SNR>".sid."_common_prefix") | |
996 | ||
962 | 997 | " zero length |
963 | call assert_equal(-1, gitgutter#diff_highlight#common_prefix('', 'foo')) | |
964 | call assert_equal(-1, gitgutter#diff_highlight#common_prefix('foo', '')) | |
998 | call assert_equal(-1, CommonPrefix('', 'foo')) | |
999 | call assert_equal(-1, CommonPrefix('foo', '')) | |
965 | 1000 | " nothing in common |
966 | call assert_equal(-1, gitgutter#diff_highlight#common_prefix('-abcde', '+pqrst')) | |
967 | call assert_equal(-1, gitgutter#diff_highlight#common_prefix('abcde', 'pqrst')) | |
1001 | call assert_equal(-1, CommonPrefix('-abcde', '+pqrst')) | |
1002 | call assert_equal(-1, CommonPrefix('abcde', 'pqrst')) | |
968 | 1003 | " something in common |
969 | call assert_equal(-1, gitgutter#diff_highlight#common_prefix('-abcde', '+abcpq')) | |
970 | call assert_equal(2, gitgutter#diff_highlight#common_prefix('abcde', 'abcpq')) | |
971 | call assert_equal(0, gitgutter#diff_highlight#common_prefix('abc', 'apq')) | |
1004 | call assert_equal(-1, CommonPrefix('-abcde', '+abcpq')) | |
1005 | call assert_equal(2, CommonPrefix('abcde', 'abcpq')) | |
1006 | call assert_equal(0, CommonPrefix('abc', 'apq')) | |
972 | 1007 | " everything in common |
973 | call assert_equal(-1, gitgutter#diff_highlight#common_prefix('-abcde', '+abcde')) | |
974 | call assert_equal(4, gitgutter#diff_highlight#common_prefix('abcde', 'abcde')) | |
1008 | call assert_equal(-1, CommonPrefix('-abcde', '+abcde')) | |
1009 | call assert_equal(4, CommonPrefix('abcde', 'abcde')) | |
975 | 1010 | " different lengths |
976 | call assert_equal(-1, gitgutter#diff_highlight#common_prefix('-abcde', '+abx')) | |
977 | call assert_equal(1, gitgutter#diff_highlight#common_prefix('abcde', 'abx')) | |
978 | call assert_equal(-1, gitgutter#diff_highlight#common_prefix('-abx', '+abcde')) | |
979 | call assert_equal(1, gitgutter#diff_highlight#common_prefix('abx', 'abcde')) | |
980 | call assert_equal(-1, gitgutter#diff_highlight#common_prefix('-abcde', '+abc')) | |
981 | call assert_equal(2, gitgutter#diff_highlight#common_prefix('abcde', 'abc')) | |
1011 | call assert_equal(-1, CommonPrefix('-abcde', '+abx')) | |
1012 | call assert_equal(1, CommonPrefix('abcde', 'abx')) | |
1013 | call assert_equal(-1, CommonPrefix('-abx', '+abcde')) | |
1014 | call assert_equal(1, CommonPrefix('abx', 'abcde')) | |
1015 | call assert_equal(-1, CommonPrefix('-abcde', '+abc')) | |
1016 | call assert_equal(2, CommonPrefix('abcde', 'abc')) | |
982 | 1017 | endfunction |
983 | 1018 | |
984 | 1019 | |
985 | 1020 | function Test_common_suffix() |
1021 | let sid = matchstr(execute('filter autoload/gitgutter/diff_highlight.vim scriptnames'), '\d\+') | |
1022 | let CommonSuffix = function("<SNR>".sid."_common_suffix") | |
1023 | ||
986 | 1024 | " nothing in common |
987 | call assert_equal([6,6], gitgutter#diff_highlight#common_suffix('-abcde', '+pqrst', 0)) | |
1025 | call assert_equal([6,6], CommonSuffix('-abcde', '+pqrst', 0)) | |
988 | 1026 | " something in common |
989 | call assert_equal([3,3], gitgutter#diff_highlight#common_suffix('-abcde', '+pqcde', 0)) | |
1027 | call assert_equal([3,3], CommonSuffix('-abcde', '+pqcde', 0)) | |
990 | 1028 | " everything in common |
991 | call assert_equal([5,5], gitgutter#diff_highlight#common_suffix('-abcde', '+abcde', 5)) | |
1029 | call assert_equal([5,5], CommonSuffix('-abcde', '+abcde', 5)) | |
992 | 1030 | " different lengths |
993 | call assert_equal([4,2], gitgutter#diff_highlight#common_suffix('-abcde', '+xde', 0)) | |
994 | call assert_equal([2,4], gitgutter#diff_highlight#common_suffix('-xde', '+abcde', 0)) | |
1031 | call assert_equal([4,2], CommonSuffix('-abcde', '+xde', 0)) | |
1032 | call assert_equal([2,4], CommonSuffix('-xde', '+abcde', 0)) | |
995 | 1033 | endfunction |
996 | 1034 | |
997 | 1035 | |
1080 | 1118 | |
1081 | 1119 | |
1082 | 1120 | function Test_lcs() |
1083 | call assert_equal('', gitgutter#diff_highlight#lcs('', 'foo')) | |
1084 | call assert_equal('', gitgutter#diff_highlight#lcs('foo', '')) | |
1085 | call assert_equal('bar', gitgutter#diff_highlight#lcs('foobarbaz', 'bbart')) | |
1086 | call assert_equal('transaction', gitgutter#diff_highlight#lcs('transaction.unexplained_amount', 'amount(transaction)')) | |
1121 | let sid = matchstr(execute('filter autoload/gitgutter/diff_highlight.vim scriptnames'), '\d\+') | |
1122 | let Lcs = function("<SNR>".sid."_lcs") | |
1123 | ||
1124 | call assert_equal('', Lcs('', 'foo')) | |
1125 | call assert_equal('', Lcs('foo', '')) | |
1126 | call assert_equal('bar', Lcs('foobarbaz', 'bbart')) | |
1127 | call assert_equal('transaction', Lcs('transaction.unexplained_amount', 'amount(transaction)')) | |
1087 | 1128 | endfunction |
1088 | 1129 | |
1089 | 1130 | |
1090 | 1131 | function Test_split() |
1091 | call assert_equal(['foo', 'baz'], gitgutter#diff_highlight#split('foobarbaz', 'bar')) | |
1092 | call assert_equal(['', 'barbaz'], gitgutter#diff_highlight#split('foobarbaz', 'foo')) | |
1093 | call assert_equal(['foobar', ''], gitgutter#diff_highlight#split('foobarbaz', 'baz')) | |
1094 | call assert_equal(['1', '2'], gitgutter#diff_highlight#split('1~2', '~')) | |
1132 | let sid = matchstr(execute('filter autoload/gitgutter/diff_highlight.vim scriptnames'), '\d\+') | |
1133 | let Split = function("<SNR>".sid."_split") | |
1134 | ||
1135 | call assert_equal(['foo', 'baz'], Split('foobarbaz', 'bar')) | |
1136 | call assert_equal(['', 'barbaz'], Split('foobarbaz', 'foo')) | |
1137 | call assert_equal(['foobar', ''], Split('foobarbaz', 'baz')) | |
1138 | call assert_equal(['1', '2'], Split('1~2', '~')) | |
1095 | 1139 | endfunction |
1096 | 1140 | |
1097 | 1141 | |
1110 | 1154 | call assert_equal(0, gitgutter#fold#is_changed()) |
1111 | 1155 | call assert_equal('+- 3 lines: a', gitgutter#fold#foldtext()) |
1112 | 1156 | endfunction |
1157 | ||
1158 | ||
1159 | function Test_assume_unchanged() | |
1160 | call system("git update-index --assume-unchanged fixture.txt") | |
1161 | unlet b:gitgutter.path " it was already set when fixture.txt was loaded in SetUp() | |
1162 | normal ggo* | |
1163 | call s:trigger_gitgutter() | |
1164 | call s:assert_signs([], 'fixture.txt') | |
1165 | endfunction |
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' |