Full Code of airblade/vim-gitgutter for AI

main 55b368d4c685 cached
26 files
186.0 KB
56.1k tokens
1 requests
Download .txt
Repository: airblade/vim-gitgutter
Branch: main
Commit: 55b368d4c685
Files: 26
Total size: 186.0 KB

Directory structure:
gitextract_g6rggijz/

├── .github/
│   └── issue_template.md
├── .gitignore
├── LICENCE
├── README.mkd
├── autoload/
│   ├── gitgutter/
│   │   ├── async.vim
│   │   ├── debug.vim
│   │   ├── diff.vim
│   │   ├── diff_highlight.vim
│   │   ├── fold.vim
│   │   ├── highlight.vim
│   │   ├── hunk.vim
│   │   ├── sign.vim
│   │   └── utility.vim
│   └── gitgutter.vim
├── doc/
│   └── gitgutter.txt
├── plugin/
│   └── gitgutter.vim
└── test/
    ├── .gitattributes
    ├── .gitconfig
    ├── cp932.txt
    ├── fixture.foo
    ├── fixture.txt
    ├── fixture_dos.txt
    ├── fixture_dos_noeol.txt
    ├── runner.vim
    ├── test
    └── test_gitgutter.vim

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/issue_template.md
================================================
> What is the latest commit SHA in your installed vim-gitgutter?

> What vim/nvim version are you on?



================================================
FILE: .gitignore
================================================
/doc/tags
/misc
/test/*.actual
*.log



================================================
FILE: LICENCE
================================================
MIT License

Copyright (c) Andrew Stewart

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



================================================
FILE: README.mkd
================================================
## vim-gitgutter

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.

The signs are always up to date and the plugin never saves your buffer.

The name "gitgutter" comes from the Sublime Text 3 plugin which inspired this in 2013.

Features:

* Shows signs for added, modified, and removed lines.
* Runs the diffs asynchronously where possible.
* Ensures signs are always up to date.
* Never saves the buffer.
* Quick jumping between blocks of changed lines ("hunks").
* Stage/undo/preview individual hunks.
* Previews highlight intra-line changes.
* Stage partial hunks.
* Provides a hunk text object.
* Diffs against index (default) or any commit.
* Handles file moves / renames.
* Heeds git's "assume unchanged" bit.
* Allows folding all unchanged text.
* Provides fold text showing whether folded lines have been changed.
* Can load all hunk locations into quickfix list or the current window's location list.
* Handles line endings correctly, even with repos that do CRLF conversion.
* Handles clean/smudge filters.
* Optional line highlighting.
* Optional line number highlighting. (Only available in Neovim 0.3.2 or higher)
* Fully customisable (signs, sign column, line (number) highlights, mappings, extra git-diff arguments, etc).
* Can be toggled on/off, globally or per buffer.
* Preserves signs from other plugins.
* Does the right thing when viewing revisions with [fugitive](https://github.com/tpope/vim-fugitive)'s `:0Gclog`.
* Easy to integrate diff stats into status line; built-in integration with [vim-airline](https://github.com/bling/vim-airline/).
* Works with fish shell (in addition to the usual shells).

Constraints:

* Supports git only.  If you work with other version control systems, I recommend [vim-signify](https://github.com/mhinz/vim-signify).
* 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.

Compatibility:

Compatible back to Vim 7.4, and probably 7.3.


### Screenshot

![screenshot](./screenshot.png?raw=true)

In the screenshot above you can see:

* Lines 183-184 are new.
* Lines 186-187 have been modified.
* The preview for the modified lines highlights changed regions within the line.


### Installation

First, install using your favourite package manager, or use Vim's built-in package support.

Vim:

```
mkdir -p ~/.vim/pack/airblade/start
cd ~/.vim/pack/airblade/start
git clone https://github.com/airblade/vim-gitgutter.git
vim -u NONE -c "helptags vim-gitgutter/doc" -c q
```

Neovim:

```
mkdir -p ~/.config/nvim/pack/airblade/start
cd ~/.config/nvim/pack/airblade/start
git clone https://github.com/airblade/vim-gitgutter.git
nvim -u NONE -c "helptags vim-gitgutter/doc" -c q
```

Second, ensure your `updatetime` and `signcolumn` options are set appropriately.

When you make a change to a file tracked by git, the diff markers should appear automatically after a short delay.  The delay is governed by vim's `updatetime` option; the default value is `4000`, i.e. 4 seconds, but I suggest reducing it to around 100ms (add `set updatetime=100` to your vimrc).  Note `updatetime` also controls the delay before vim writes its swap file (see `:help updatetime`).

The `signcolumn` option can have any value except `'no'`.


### Windows

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.

You can avoid this risk by configuring the full path to your git executable.  For example:

```viml
" This path probably won't work
let g:gitgutter_git_executable = 'C:\Program Files\Git\bin\git.exe'
```

Unfortunately I don't know the correct escaping for the path - if you do, please let me know!


### Getting started

When you make a change to a file tracked by git, the diff markers should appear automatically after a short delay.

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.

You cannot unstage a staged hunk.

After updating the signs, the plugin fires the `GitGutter` User autocommand.

After staging a hunk or part of a hunk, the plugin fires the `GitGutterStage` User autocommand.


#### Activation

You can explicitly turn vim-gitgutter off and on (defaults to on):

* turn off with `:GitGutterDisable`
* turn on with `:GitGutterEnable`
* toggle with `:GitGutterToggle`.

To toggle vim-gitgutter per buffer:

* turn off with `:GitGutterBufferDisable`
* turn on with `:GitGutterBufferEnable`
* toggle with `:GitGutterBufferToggle`

You can turn the signs on and off (defaults to on):

* turn on with `:GitGutterSignsEnable`
* turn off with `:GitGutterSignsDisable`
* toggle with `:GitGutterSignsToggle`.

And you can turn line highlighting on and off (defaults to off):

* turn on with `:GitGutterLineHighlightsEnable`
* turn off with `:GitGutterLineHighlightsDisable`
* toggle with `:GitGutterLineHighlightsToggle`.

Note that if you have line highlighting on and signs off, you will have an empty sign column – more accurately, a sign column with invisible signs.  This is because line highlighting requires signs and Vim/NeoVim always shows the sign column when there are signs even if the signs are invisible.

With Neovim 0.3.2 or higher, you can turn line number highlighting on and off (defaults to off):

* turn on with `:GitGutterLineNrHighlightsEnable`
* turn off with `:GitGutterLineNrHighlightsDisable`
* toggle with `:GitGutterLineNrHighlightsToggle`.

The same caveat applies to line number highlighting as to line highlighting just above.

If you switch off both line highlighting and signs, you won't see the sign column.

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:

```viml
let g:gitgutter_max_signs = 500  " default value (Vim < 8.1.0614, Neovim < 0.4.0)
let g:gitgutter_max_signs = -1   " default value (otherwise)
```

You can also remove the limit by setting `g:gitgutter_max_signs = -1`.

#### Hunks

You can jump between hunks:

* jump to next hunk (change): `]c`
* jump to previous hunk (change): `[c`.

Both of those take a preceding count.

To set your own mappings for these, for example `]h` and `[h`:

```viml
nmap ]h <Plug>(GitGutterNextHunk)
nmap [h <Plug>(GitGutterPrevHunk)
```

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:

```viml
let g:gitgutter_show_msg_on_hunk_jumping = 0
```

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:

```viml
command! Gqf GitGutterQuickFix | copen
```

You can stage or undo an individual hunk when your cursor is in it:

* stage the hunk with `<Leader>hs` or
* undo it with `<Leader>hu`.

To stage part of an additions-only hunk by:

* either visually selecting the part you want and staging with your mapping, e.g. `<Leader>hs`;
* or using a range with the `GitGutterStageHunk` command, e.g. `:42,45GitGutterStageHunk`.

To stage part of any hunk:

* preview the hunk, e.g. `<Leader>hp`;
* move to the preview window, e.g. `:wincmd P`;
* delete the lines you do not want to stage;
* stage the remaining lines: either write (`:w`) the window or stage via `<Leader>hs` or `:GitGutterStageHunk`.

Note the above workflow is not possible if you have opted in to preview hunks with Vim's popup windows.

See the FAQ if you want to unstage staged changes.

The `.` command will work with both these if you install [repeat.vim](https://github.com/tpope/vim-repeat).

To set your own mappings for these, for example if you prefer `g`-based maps:

```viml
nmap ghs <Plug>(GitGutterStageHunk)
nmap ghu <Plug>(GitGutterUndoHunk)
```

And you can preview a hunk's changes with `<Leader>hp`.  The location of the preview window is configured with `g:gitgutter_preview_win_location` (default `'bo'`).  You can of course change this mapping, e.g:

```viml
nmap ghp <Plug>(GitGutterPreviewHunk)
```

A hunk text object is provided which works in visual and operator-pending modes.

- `ic` operates on all lines in the current hunk.
- `ac` operates on all lines in the current hunk and any trailing empty lines.

To re-map these, for example to `ih` and `ah`:

```viml
omap ih <Plug>(GitGutterTextObjectInnerPending)
omap ah <Plug>(GitGutterTextObjectOuterPending)
xmap ih <Plug>(GitGutterTextObjectInnerVisual)
xmap ah <Plug>(GitGutterTextObjectOuterVisual)
```

If you don't want vim-gitgutter to set up any mappings at all, use this:

```viml
let g:gitgutter_map_keys = 0
```

Finally, you can force vim-gitgutter to update its signs across all visible buffers with `:GitGutterAll`.

See the customisation section below for how to change the defaults.


### Vimdiff

Use the `GitGutterDiffOrig` command to open a vimdiff view of the current buffer, respecting `g:gitgutter_diff_relative_to` and `:gitgutter_diff_base`.


### Folding

Use the `GitGutterFold` command to fold all unchanged lines, leaving just the hunks visible.  Use `zr` to unfold 3 lines of context above and below a hunk.

Execute `GitGutterFold` a second time to restore the previous view.

Use `gitgutter#fold#foldtext()` to augment the default `foldtext()` with an indicator of whether the folded lines have been changed.

```viml
set foldtext=gitgutter#fold#foldtext()
```

For a closed fold with changed lines:

```
Default foldtext():         +-- 45 lines: abcdef
gitgutter#fold#foldtext():  +-- 45 lines (*): abcdef
```

You can use `gitgutter#fold#is_changed()` in your own `foldtext` expression to find out whether the folded lines have been changed.


### Status line

Call the `GitGutterGetHunkSummary()` function from your status line to get a list of counts of added, modified, and removed lines in the current buffer.  For example:

```viml
" Your vimrc
function! GitStatus()
  let [a,m,r] = GitGutterGetHunkSummary()
  return printf('+%d ~%d -%d', a, m, r)
endfunction
set statusline+=%{GitStatus()}
```


### Customisation

You can customise:

* The sign column's colours
* Whether or not the sign column is shown when there aren't any signs (defaults to no)
* How to handle non-gitgutter signs
* The signs' colours and symbols
* Line highlights
* Line number highlights (only in Neovim 0.3.2 or higher)
* The diff syntax colours used in the preview window
* The intra-line diff highlights used in the preview window
* Whether the diff is relative to the index (default) or working tree.
* The base of the diff
* Extra arguments for `git` when running `git diff`
* Extra arguments for `git diff`
* Key mappings
* Whether vim-gitgutter is on initially (defaults to on)
* Whether signs are shown (defaults to yes)
* Whether line highlighting is on initially (defaults to off)
* Whether line number highlighting is on initially (defaults to off)
* Whether vim-gitgutter runs asynchronously (defaults to yes)
* Whether to clobber or preserve non-gitgutter signs
* The priority of gitgutter's signs.
* Whether to use a floating/popup window for hunk previews
* The appearance of a floating/popup window for hunk previews
* Whether to populate the quickfix list or a location list with all hunks

Please note that vim-gitgutter won't override any colours or highlights you've set in your colorscheme.


#### Sign column

Set the `SignColumn` highlight group to change the sign column's colour.  For example:

```viml
" vim-gitgutter used to do this by default:
highlight! link SignColumn LineNr

" or you could do this:
highlight SignColumn guibg=whatever ctermbg=whatever
```

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:

```viml
" Vim 7.4.2201
set signcolumn=yes
```

GitGutter can preserve or ignore non-gitgutter signs.  For Vim v8.1.0614 and later you can set gitgutter's signs' priorities with `g:gitgutter_sign_priority`, so gitgutter defaults to clobbering other signs.  For Neovim v0.4.0 and later you can set an expanding sign column so gitgutter again defaults to clobbering other signs.  Otherwise, gitgutter defaults to preserving other signs.  You can configure this with:

```viml
let g:gitgutter_sign_allow_clobber = 1
```


#### Signs' colours and symbols

If you or your colourscheme has defined `GitGutter*` highlight groups, the plugin will use them for the signs' colours.

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:

```viml
let g:gitgutter_set_sign_backgrounds = 1
```

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:

```viml
highlight GitGutterAdd    guifg=#009900 ctermfg=2
highlight GitGutterChange guifg=#bbbb00 ctermfg=3
highlight GitGutterDelete guifg=#ff2222 ctermfg=1
```

To customise the symbols, add the following to your `~/.vimrc`:

```viml
let g:gitgutter_sign_added = 'xx'
let g:gitgutter_sign_modified = 'yy'
let g:gitgutter_sign_removed = 'zz'
let g:gitgutter_sign_removed_first_line = '^^'
let g:gitgutter_sign_removed_above_and_below = '{'
let g:gitgutter_sign_modified_removed = 'ww'
```


#### Line highlights

Similarly to the signs' colours, set up the following highlight groups in your colorscheme or `~/.vimrc`:

```viml
GitGutterAddLine          " default: links to DiffAdd
GitGutterChangeLine       " default: links to DiffChange
GitGutterDeleteLine       " default: links to DiffDelete
GitGutterChangeDeleteLine " default: links to GitGutterChangeLine, i.e. DiffChange
```

For example, in some colorschemes the `DiffText` highlight group is easier to read than `DiffChange`.  You could use it like this:

```viml
highlight link GitGutterChangeLine DiffText
```


#### Line number highlights

NOTE: This feature requires Neovim 0.3.2 or higher.

Similarly to the signs' colours, set up the following highlight groups in your colorscheme or `~/.vimrc`:

```viml
GitGutterAddLineNr          " default: links to CursorLineNr
GitGutterChangeLineNr       " default: links to CursorLineNr
GitGutterDeleteLineNr       " default: links to CursorLineNr
GitGutterChangeDeleteLineNr " default: links to GitGutterChangeLineNr
```

Maybe you think `CursorLineNr` is a bit annoying.  For example, you could use `Underlined` for this:

```viml
highlight link GitGutterChangeLineNr Underlined
```


#### The diff syntax colours used in the preview window

To change the diff syntax colours used in the preview window, set up the `diff*` highlight groups in your colorscheme or `~/.vimrc`:

```viml
diffAdded   " if not set: use GitGutterAdd's foreground colour
diffChanged " if not set: use GitGutterChange's foreground colour
diffRemoved " if not set: use GitGutterDelete's foreground colour
```

Note the `diff*` highlight groups are used in any buffer whose `'syntax'` is `diff`.


#### The intra-line diff highlights used in the preview window

To change the intra-line diff highlights used in the preview window, set up the following highlight groups in your colorscheme or `~/.vimrc`:

```viml
GitGutterAddIntraLine    " default: gui=reverse cterm=reverse
GitGutterDeleteIntraLine " default: gui=reverse cterm=reverse
```

For example, to use `DiffAdd` for intra-line added regions:

```viml
highlight link GitGutterAddIntraLine DiffAdd
```


#### Whether the diff is relative to the index or working tree

By default diffs are relative to the index.  How you can make them relative to the working tree:

```viml
let g:gitgutter_diff_relative_to = 'working_tree'
```


#### The base of the diff

By default buffers are diffed against the index.  However you can diff against any commit by setting:

```viml
let g:gitgutter_diff_base = '<commit SHA>'
```

If you are looking at a previous version of a file with Fugitive (e.g. via `:0Gclog`), gitgutter sets the diff base to the parent of the current revision.

This setting is ignored when the diffs are relative to the working tree.


#### Extra arguments for `git` when running `git diff`

If you want to pass extra arguments to `git` when running `git diff`, do so like this:

```viml
let g:gitgutter_git_args = '--git-dir-""'
```

#### Extra arguments for `git diff`

If you want to pass extra arguments to `git diff`, for example to ignore whitespace, do so like this:

```viml
let g:gitgutter_diff_args = '-w'
```

#### Key mappings

To disable all key mappings:

```viml
let g:gitgutter_map_keys = 0
```

See above for configuring maps for hunk-jumping and staging/undoing.

#### To turn off vim-gitgutter by default

Add `let g:gitgutter_enabled = 0` to your `~/.vimrc`.


#### To turn off signs by default

Add `let g:gitgutter_signs = 0` to your `~/.vimrc`.


#### To turn on line highlighting by default

Add `let g:gitgutter_highlight_lines = 1` to your `~/.vimrc`.


#### To turn on line number highlighting by default

Add `let g:gitgutter_highlight_linenrs = 1` to your `~/.vimrc`.


#### To turn off asynchronous updates

By default diffs are run asynchronously.  To run diffs synchronously instead:

```viml
let g:gitgutter_async = 0
```


#### To use floating/popup windows for hunk previews

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.

On Neovim, the preview hunk command will move the cursor into the floating window if it is already open.


#### The appearance of a floating/popup window for hunk previews

Either 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).

Or if you just want to override one or two of the defaults, you can do that with a file in an `after/` directory.  For example:

```viml
" ~/.vim/after/vim-gitgutter/overrides.vim
let g:gitgutter_floating_window_options['border'] = 'single'
```


#### To load all hunks into the current window's location list instead of the quickfix list

Add `let g:gitgutter_use_location_list = 1` to your `~/.vimrc`.


### Extensions

#### Operate on every line in a hunk

You can map an operator to do whatever you want to every line in a hunk.

Let's say, for example, you want to remove trailing whitespace.

```viml
function! CleanUp(...)
  if a:0  " opfunc
    let [first, last] = [line("'["), line("']")]
  else
    let [first, last] = [line("'<"), line("'>")]
  endif
  for lnum in range(first, last)
    let line = getline(lnum)

    " clean up the text, e.g.:
    let line = substitute(line, '\s\+$', '', '')

    call setline(lnum, line)
  endfor
endfunction

nmap <silent> <Leader>x :set opfunc=CleanUp<CR>g@
```

Then place your cursor in a hunk and type `\xic` (assuming a leader of `\`).

Alternatively you could place your cursor in a hunk, type `vic` to select it, then `:call CleanUp()`.


#### Operate on every changed line in a file

You can write a command to do whatever you want to every changed line in a file.

```viml
function! GlobalChangedLines(ex_cmd)
  for hunk in GitGutterGetHunks()
    for lnum in range(hunk[2], hunk[2]+hunk[3]-1)
      let cursor = getcurpos()
      silent! execute lnum.a:ex_cmd
      call setpos('.', cursor)
    endfor
  endfor
endfunction

command -nargs=1 Glines call GlobalChangedLines(<q-args>)
```

Let's say, for example, you want to remove trailing whitespace from all changed lines:

```viml
:Glines s/\s\+$//
```


#### Cycle through hunks in current buffer

This is like `:GitGutterNextHunk` but when it gets to the last hunk in the buffer it cycles around to the first.

```viml
function! GitGutterNextHunkCycle()
  let line = line('.')
  silent! GitGutterNextHunk
  if line('.') == line
    1
    GitGutterNextHunk
  endif
endfunction
```


#### Cycle through hunks in all buffers

You can use `:GitGutterQuickFix` to load all hunks into the quickfix list or the current window's location list.

Alternatively, given that`]c` and `[c` jump from one hunk to the next in the current buffer, you can use this code to jump to the next hunk no matter which buffer it's in.

```viml
function! NextHunkAllBuffers()
  let line = line('.')
  GitGutterNextHunk
  if line('.') != line
    return
  endif

  let bufnr = bufnr('')
  while 1
    bnext
    if bufnr('') == bufnr
      return
    endif
    if !empty(GitGutterGetHunks())
      1
      GitGutterNextHunk
      return
    endif
  endwhile
endfunction

function! PrevHunkAllBuffers()
  let line = line('.')
  GitGutterPrevHunk
  if line('.') != line
    return
  endif

  let bufnr = bufnr('')
  while 1
    bprevious
    if bufnr('') == bufnr
      return
    endif
    if !empty(GitGutterGetHunks())
      normal! G
      GitGutterPrevHunk
      return
    endif
  endwhile
endfunction

nmap <silent> ]c :call NextHunkAllBuffers()<CR>
nmap <silent> [c :call PrevHunkAllBuffers()<CR>
```


### FAQ

> How can I turn off realtime updates?

Add this to your vim configuration (in an `/after/plugin` directory):

```viml
" .vim/after/plugin/gitgutter.vim
autocmd! gitgutter CursorHold,CursorHoldI
```

> I turned off realtime updates, how can I have signs updated when I save a file?

If you really want to update the signs when you save a file, add this to your vimrc:

```viml
autocmd BufWritePost * GitGutter
```

> Why can't I unstage staged changes?

This plugin is for showing changes between the buffer and the index (and staging/undoing those changes).  Unstaging a staged hunk would require showing changes between the index and HEAD, which is out of scope.

> Why are the colours in the sign column weird?

Your colorscheme is configuring the `SignColumn` highlight group weirdly.  Please see the section above on customising the sign column.

> What happens if I also use another plugin which uses signs (e.g. Syntastic)?

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.


### Troubleshooting

#### When no signs are showing at all

Here are some things you can check:

* Verify `:echo system("git --version")` succeeds.
* Verify your git config is compatible with the version of git returned by the command above.
* Verify your Vim supports signs (`:echo has('signs')` should give `1`).
* 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.
* Execute `:sign place group=gitgutter`; you should see a list of signs.
  - If the signs are listed: this is a colorscheme / highlight problem.  Compare `:highlight GitGutterAdd` with `:highlight SignColumn`.
  - 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.

#### When the whole file is marked as added

* If you use zsh, and you set `CDPATH`, make sure `CDPATH` doesn't include the current directory.

#### When signs take a few seconds to appear

* Try reducing `updatetime`, e.g. `set updatetime=100`.  Note this also controls the delay before vim writes its swap file.

#### When signs don't update after focusing Vim

* Your terminal probably isn't reporting focus events.  Either try installing [Terminus][] or set `let g:gitgutter_terminal_reports_focus=0`.  For tmux, try `set -g focus-events on` in your tmux.conf.


### Shameless Plug

If this plugin has helped you, or you'd like to learn more about Vim, why not check out this screencast I wrote for PeepCode:

* [Smash Into Vim][siv]

This was one of PeepCode's all-time top three bestsellers and is now available at Pluralsight.


### Intellectual Property

Copyright Andrew Stewart, AirBlade Software Ltd.  Released under the MIT licence.


  [pathogen]: https://github.com/tpope/vim-pathogen
  [siv]: http://pluralsight.com/training/Courses/TableOfContents/smash-into-vim
  [terminus]: https://github.com/wincent/terminus


================================================
FILE: autoload/gitgutter/async.vim
================================================
let s:available = has('nvim') || (
      \   has('job') && (
      \     (has('patch-7.4.1826') && !has('gui_running')) ||
      \     (has('patch-7.4.1850') &&  has('gui_running')) ||
      \     (has('patch-7.4.1832') &&  has('gui_macvim'))
      \   )
      \ )

let s:jobs = {}

function! gitgutter#async#available()
  return s:available
endfunction


function! gitgutter#async#execute(cmd, bufnr, handler) abort
  call gitgutter#debug#log('[async] '.a:cmd)

  let options = {
        \   'stdoutbuffer': [],
        \   'buffer': a:bufnr,
        \   'handler': a:handler
        \ }
  let command = s:build_command(a:cmd)

  if has('nvim')
    call jobstart(command, extend(options, {
          \   'on_stdout': function('s:on_stdout_nvim'),
          \   'on_stderr': function('s:on_stderr_nvim'),
          \   'on_exit':   function('s:on_exit_nvim')
          \ }))
  else
    let job = job_start(command, {
          \   'out_cb':   function('s:on_stdout_vim', options),
          \   'err_cb':   function('s:on_stderr_vim', options),
          \   'close_cb': function('s:on_exit_vim', options)
          \ })
    let s:jobs[s:job_id(job)] = 1
  endif
endfunction


function! s:build_command(cmd)
  if has('unix')
    return ['sh', '-c', a:cmd]
  endif

  if has('win32')
    return has('nvim') ? a:cmd : 'cmd.exe /c '.a:cmd
  endif

  throw 'unknown os'
endfunction


function! s:on_stdout_nvim(_job_id, data, _event) dict abort
  if empty(self.stdoutbuffer)
    let self.stdoutbuffer = a:data
  else
    let self.stdoutbuffer = self.stdoutbuffer[:-2] +
          \ [self.stdoutbuffer[-1] . a:data[0]] +
          \ a:data[1:]
  endif
endfunction

function! s:on_stderr_nvim(_job_id, data, _event) dict abort
  if a:data != ['']  " With Neovim there is always [''] reported on stderr.
    call self.handler.err(self.buffer)
  endif
endfunction

function! s:on_exit_nvim(_job_id, exit_code, _event) dict abort
  if !a:exit_code
    call self.handler.out(self.buffer, join(self.stdoutbuffer, "\n"))
  endif
endfunction


function! s:on_stdout_vim(_channel, data) dict abort
  call add(self.stdoutbuffer, a:data)
endfunction

function! s:on_stderr_vim(channel, _data) dict abort
  call self.handler.err(self.buffer)
endfunction

function! s:on_exit_vim(channel) dict abort
  let job = ch_getjob(a:channel)
  let jobid = s:job_id(job)
  if has_key(s:jobs, jobid) | unlet s:jobs[jobid] | endif
  while 1
    if job_status(job) == 'dead'
      let exit_code = job_info(job).exitval
      break
    endif
    sleep 5m
  endwhile

  if !exit_code
    call self.handler.out(self.buffer, join(self.stdoutbuffer, "\n"))
  endif
endfunction

function! s:job_id(job)
  " Vim
  return job_info(a:job).process
endfunction


================================================
FILE: autoload/gitgutter/debug.vim
================================================
let s:plugin_dir  = expand('<sfile>:p:h:h:h').'/'
let s:log_file    = s:plugin_dir.'gitgutter.log'
let s:channel_log = s:plugin_dir.'channel.log'
let s:new_log_session = 1


function! gitgutter#debug#debug()
  " Open a scratch buffer
  vsplit __GitGutter_Debug__
  normal! ggdG
  setlocal buftype=nofile
  setlocal bufhidden=delete
  setlocal noswapfile

  call s:vim_version()
  call s:separator()

  call s:git_version()
  call s:separator()

  call s:separator()

  call s:option('updatetime')
endfunction


function! s:separator()
  call s:output('')
endfunction

function! s:vim_version()
  redir => version_info
    silent execute 'version'
  redir END
  call s:output(split(version_info, '\n')[0:2])
endfunction

function! s:git_version()
  let v = system(g:gitgutter_git_executable.' --version')
  call s:output( substitute(v, '\n$', '', '') )
endfunction

function! s:option(name)
  if exists('+' . a:name)
    let v = eval('&' . a:name)
    call s:output(a:name . '=' . v)
    " redir => output
    "   silent execute "verbose set " . a:name . "?"
    " redir END
    " call s:output(a:name . '=' . output)
  else
    call s:output(a:name . ' [n/a]')
  end
endfunction

function! s:output(text)
  call append(line('$'), a:text)
endfunction

" assumes optional args are calling function's optional args
function! gitgutter#debug#log(message, ...) abort
  if g:gitgutter_log
    if s:new_log_session && gitgutter#async#available()
      if exists('*ch_logfile')
        call ch_logfile(s:channel_log, 'w')
      endif
    endif

    if s:new_log_session
      let s:start = reltime()
      call writefile(['', '========== start log session '.strftime('%d.%m.%Y %H:%M:%S').' =========='], s:log_file, 'a')
    endif

    let elapsed = reltimestr(reltime(s:start)).' '
    call writefile([''], s:log_file, 'a')
    " callers excluding this function
    call writefile([elapsed.expand('<sfile>')[:-22].':'], s:log_file, 'a')
    call writefile([elapsed.s:format_for_log(a:message)], s:log_file, 'a')
    if a:0 && !empty(a:1)
      for msg in a:000
        call writefile([elapsed.s:format_for_log(msg)], s:log_file, 'a')
      endfor
    endif

    let s:new_log_session = 0
  endif
endfunction

function! s:format_for_log(data) abort
  if type(a:data) == 1
    return join(split(a:data,'\n'),"\n")
  elseif type(a:data) == 3
    return '['.join(a:data,"\n").']'
  else
    return a:data
  endif
endfunction



================================================
FILE: autoload/gitgutter/diff.vim
================================================
scriptencoding utf8

let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : ''

let s:hunk_re = '^@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@'

let s:temp_from = tempname()
let s:temp_buffer = tempname()
let s:counter = 0

" Returns a diff of the buffer against the index or the working tree.
"
" When diffing against the index:
"
" The buffer contents is not the same as the file on disk so we need to pass
" two instances of the file to git-diff:
"
"     git diff myfileA myfileB
"
" where myfileA comes from
"
"     git show :myfile > myfileA
"
" and myfileB is the buffer contents.
"
" Regarding line endings:
"
" git-show does not convert line endings.
" git-diff FILE FILE does convert line endings for the given files.
"
" If a file has CRLF line endings and git's core.autocrlf is true,
" the file in git's object store will have LF line endings.  Writing
" it out via git-show will produce a file with LF line endings.
"
" If this last file is one of the files passed to git-diff, git-diff will
" convert its line endings to CRLF before diffing -- which is what we want --
" but also by default output a warning on stderr.
"
"   warning: LF will be replace by CRLF in <temp file>.
"   The file will have its original line endings in your working directory.
"
" When running the diff asynchronously, the warning message triggers the stderr
" callbacks which assume the overall command has failed and reset all the
" signs.  As this is not what we want, and we can safely ignore the warning,
" we turn it off by passing the '-c "core.safecrlf=false"' argument to
" git-diff.
"
" When writing the temporary files we preserve the original file's extension
" so that repos using .gitattributes to control EOL conversion continue to
" convert correctly.
"
" Arguments:
"
" bufnr - the number of the buffer to be diffed
" from  - 'index' or 'working_tree'; what the buffer is diffed against
function! gitgutter#diff#run_diff(bufnr, from) abort
  if gitgutter#utility#repo_path(a:bufnr, 0) == -1
    throw 'gitgutter path not set'
  endif

  if gitgutter#utility#repo_path(a:bufnr, 0) == -2
    throw 'gitgutter not tracked'
  endif

  if gitgutter#utility#repo_path(a:bufnr, 0) == -3
    throw 'gitgutter assume unchanged'
  endif

  " Wrap compound commands in parentheses to make Windows happy.
  " bash doesn't mind the parentheses.
  let cmd = '('

  " Append buffer number to temp filenames to avoid race conditions between
  " writing and reading the files when asynchronously processing multiple
  " buffers.

  " Without the buffer number, buff_file would have a race between the
  " second gitgutter#process_buffer() writing the file (synchronously, below)
  " and the first gitgutter#process_buffer()'s async job reading it (with
  " git-diff).
  let buff_file = s:temp_buffer.'.'.a:bufnr

  " Add a counter to avoid a similar race with two quick writes of the same buffer.
  " Use a modulus greater than a maximum reasonable number of visible buffers.
  let s:counter = (s:counter + 1) % 20
  let buff_file .= '.'.s:counter

  let extension = gitgutter#utility#extension(a:bufnr)
  if !empty(extension)
    let buff_file .= '.'.extension
  endif

  " Write buffer to temporary file.
  " Note: this is synchronous.
  call s:write_buffer(a:bufnr, buff_file)

  if a:from ==# 'index'
    " Without the buffer number, from_file would have a race in the shell
    " between the second process writing it (with git-show) and the first
    " reading it (with git-diff).
    let from_file = s:temp_from.'.'.a:bufnr

    " Add a counter to avoid a similar race with two quick writes of the same buffer.
    let from_file .= '.'.s:counter

    if !empty(extension)
      let from_file .= '.'.extension
    endif

    " Write file from index to temporary file.
    let index_name = gitgutter#utility#get_diff_base(a:bufnr).':'.gitgutter#utility#base_path(a:bufnr)
    let cmd .= gitgutter#git(a:bufnr).' --no-pager show --textconv '.index_name
    let cmd .= ' > '.gitgutter#utility#shellescape(from_file).' || exit 0) && ('

  elseif a:from ==# 'working_tree'
    let from_file = gitgutter#utility#repo_path(a:bufnr, 1)
  endif

  " Call git-diff.
  let cmd .= gitgutter#git(a:bufnr).' --no-pager'
  if gitgutter#utility#git_supports_command_line_config_override()
    let cmd .= ' -c "diff.autorefreshindex=0"'
    let cmd .= ' -c "diff.noprefix=false"'
    let cmd .= ' -c "core.safecrlf=false"'
  endif
  let cmd .= ' diff --no-ext-diff --no-color -U0 '.g:gitgutter_diff_args
  let cmd .= ' -- '.gitgutter#utility#shellescape(from_file).' '.gitgutter#utility#shellescape(buff_file)

  " git-diff exits with 1 when differences are found but we want to treat
  " differences as non-erroneous behaviour.  So we OR the command with one
  " which always exits with success (0).
  let cmd .= ' || exit 0'

  let cmd .= ')'

  if g:gitgutter_async && gitgutter#async#available()
    call gitgutter#async#execute(cmd, a:bufnr, {
          \   'out': function('gitgutter#diff#handler'),
          \   'err': function('gitgutter#hunk#reset'),
          \ })
    return 'async'

  else
    let [diff, error_code] = gitgutter#utility#system(cmd)

    if error_code
      call gitgutter#debug#log(diff)
      throw 'gitgutter diff failed'
    endif

    return diff
  endif
endfunction


function! gitgutter#diff#handler(bufnr, diff) abort
  call gitgutter#debug#log(a:diff)

  if !bufexists(a:bufnr)
    return
  endif

  call gitgutter#hunk#set_hunks(a:bufnr, gitgutter#diff#parse_diff(a:diff))
  let modified_lines = gitgutter#diff#process_hunks(a:bufnr, gitgutter#hunk#hunks(a:bufnr))

  let signs_count = len(modified_lines)
  if g:gitgutter_max_signs != -1 && signs_count > g:gitgutter_max_signs
    call gitgutter#utility#warn_once(a:bufnr, printf(
          \ 'exceeded maximum number of signs (%d > %d, configured by g:gitgutter_max_signs).',
          \ signs_count, g:gitgutter_max_signs), 'max_signs')
    call gitgutter#sign#clear_signs(a:bufnr)

  else
    if g:gitgutter_signs || g:gitgutter_highlight_lines || g:gitgutter_highlight_linenrs
      call gitgutter#sign#update_signs(a:bufnr, modified_lines)
    endif
  endif

  call s:save_last_seen_change(a:bufnr)
  if exists('#User#GitGutter')
    let g:gitgutter_hook_context = {'bufnr': a:bufnr}
    execute 'doautocmd' s:nomodeline 'User GitGutter'
    unlet g:gitgutter_hook_context
  endif
endfunction


function! gitgutter#diff#parse_diff(diff) abort
  let hunks = []
  for line in split(a:diff, '\n')
    let hunk_info = gitgutter#diff#parse_hunk(line)
    if len(hunk_info) == 4
      call add(hunks, hunk_info)
    endif
  endfor
  return hunks
endfunction

function! gitgutter#diff#parse_hunk(line) abort
  let matches = matchlist(a:line, s:hunk_re)
  if len(matches) > 0
    let from_line  = str2nr(matches[1])
    let from_count = (matches[2] == '') ? 1 : str2nr(matches[2])
    let to_line    = str2nr(matches[3])
    let to_count   = (matches[4] == '') ? 1 : str2nr(matches[4])
    return [from_line, from_count, to_line, to_count]
  else
    return []
  end
endfunction

" This function is public so it may be used by other plugins
" e.g. vim-signature.
function! gitgutter#diff#process_hunks(bufnr, hunks) abort
  let modified_lines = []
  for hunk in a:hunks
    call extend(modified_lines, s:process_hunk(a:bufnr, hunk))
  endfor
  return modified_lines
endfunction

" Returns [ [<line_number (number)>, <name (string)>], ...]
function! s:process_hunk(bufnr, hunk) abort
  let modifications = []
  let from_line  = a:hunk[0]
  let from_count = a:hunk[1]
  let to_line    = a:hunk[2]
  let to_count   = a:hunk[3]

  if s:is_added(from_count, to_count)
    call s:process_added(modifications, from_count, to_count, to_line)
    call gitgutter#hunk#increment_lines_added(a:bufnr, to_count)

  elseif s:is_removed(from_count, to_count)
    call s:process_removed(modifications, from_count, to_count, to_line)
    call gitgutter#hunk#increment_lines_removed(a:bufnr, from_count)

  elseif s:is_modified(from_count, to_count)
    call s:process_modified(modifications, from_count, to_count, to_line)
    call gitgutter#hunk#increment_lines_modified(a:bufnr, to_count)

  elseif s:is_modified_and_added(from_count, to_count)
    call s:process_modified_and_added(modifications, from_count, to_count, to_line)
    call gitgutter#hunk#increment_lines_added(a:bufnr, to_count - from_count)
    call gitgutter#hunk#increment_lines_modified(a:bufnr, from_count)

  elseif s:is_modified_and_removed(from_count, to_count)
    call s:process_modified_and_removed(modifications, from_count, to_count, to_line)
    call gitgutter#hunk#increment_lines_modified(a:bufnr, to_count)
    call gitgutter#hunk#increment_lines_removed(a:bufnr, from_count - to_count)

  endif
  return modifications
endfunction

function! s:is_added(from_count, to_count) abort
  return a:from_count == 0 && a:to_count > 0
endfunction

function! s:is_removed(from_count, to_count) abort
  return a:from_count > 0 && a:to_count == 0
endfunction

function! s:is_modified(from_count, to_count) abort
  return a:from_count > 0 && a:to_count > 0 && a:from_count == a:to_count
endfunction

function! s:is_modified_and_added(from_count, to_count) abort
  return a:from_count > 0 && a:to_count > 0 && a:from_count < a:to_count
endfunction

function! s:is_modified_and_removed(from_count, to_count) abort
  return a:from_count > 0 && a:to_count > 0 && a:from_count > a:to_count
endfunction

function! s:process_added(modifications, from_count, to_count, to_line) abort
  let offset = 0
  while offset < a:to_count
    let line_number = a:to_line + offset
    call add(a:modifications, [line_number, 'added'])
    let offset += 1
  endwhile
endfunction

function! s:process_removed(modifications, from_count, to_count, to_line) abort
  if a:to_line == 0
    call add(a:modifications, [1, 'removed_first_line'])
  else
    call add(a:modifications, [a:to_line, 'removed'])
  endif
endfunction

function! s:process_modified(modifications, from_count, to_count, to_line) abort
  let offset = 0
  while offset < a:to_count
    let line_number = a:to_line + offset
    call add(a:modifications, [line_number, 'modified'])
    let offset += 1
  endwhile
endfunction

function! s:process_modified_and_added(modifications, from_count, to_count, to_line) abort
  let offset = 0
  while offset < a:from_count
    let line_number = a:to_line + offset
    call add(a:modifications, [line_number, 'modified'])
    let offset += 1
  endwhile
  while offset < a:to_count
    let line_number = a:to_line + offset
    call add(a:modifications, [line_number, 'added'])
    let offset += 1
  endwhile
endfunction

function! s:process_modified_and_removed(modifications, from_count, to_count, to_line) abort
  let offset = 0
  while offset < a:to_count
    let line_number = a:to_line + offset
    call add(a:modifications, [line_number, 'modified'])
    let offset += 1
  endwhile
  let a:modifications[-1] = [a:to_line + offset - 1, 'modified_removed']
endfunction


" Returns a diff for the current hunk.
" Assumes there is only 1 current hunk unless the optional argument is given,
" in which case the cursor is in two hunks and the argument specifies the one
" to choose.
"
" Optional argument: 0 (to use the first hunk) or 1 (to use the second).
function! gitgutter#diff#hunk_diff(bufnr, full_diff, ...)
  let modified_diff = []
  let hunk_index = 0
  let keep_line = 1
  " Don't keepempty when splitting because the diff we want may not be the
  " final one.  Instead add trailing NL at end of function.
  for line in split(a:full_diff, '\n')
    let hunk_info = gitgutter#diff#parse_hunk(line)
    if len(hunk_info) == 4  " start of new hunk
      let keep_line = gitgutter#hunk#cursor_in_hunk(hunk_info)

      if a:0 && hunk_index != a:1
        let keep_line = 0
      endif

      let hunk_index += 1
    endif
    if keep_line
      call add(modified_diff, line)
    endif
  endfor
  return join(modified_diff, "\n")."\n"
endfunction


function! s:write_buffer(bufnr, file)
  let bufcontents = getbufline(a:bufnr, 1, '$')

  if bufcontents == [''] && line2byte(1) == -1
    " Special case: completely empty buffer.
    " A nearly empty buffer of only a newline has line2byte(1) == 1.
    call writefile([], a:file)
    return
  endif

  if getbufvar(a:bufnr, '&fileformat') ==# 'dos'
    if getbufvar(a:bufnr, '&endofline')
      call map(bufcontents, 'v:val."\r"')
    else
      for i in range(len(bufcontents) - 1)
        let bufcontents[i] = bufcontents[i] . "\r"
      endfor
    endif
  endif

  if getbufvar(a:bufnr, '&endofline')
    call add(bufcontents, '')
  endif

  let fenc = getbufvar(a:bufnr, '&fileencoding')
  if fenc !=# &encoding
    call map(bufcontents, 'iconv(v:val, &encoding, "'.fenc.'")')
  endif

  if getbufvar(a:bufnr, '&bomb')
    let bufcontents[0]=''.bufcontents[0]
  endif

  " The file we are writing to is a temporary file.  Sometimes the parent
  " directory is deleted outside Vim but, because Vim caches the directory
  " name at startup and does not check for its existence subsequently, Vim
  " does not realise.  This causes E482 errors.
  try
    call writefile(bufcontents, a:file, 'b')
  catch /E482/
    call mkdir(fnamemodify(a:file, ':h'), '', '0700')
    call writefile(bufcontents, a:file, 'b')
  endtry
endfunction


function! s:save_last_seen_change(bufnr) abort
  call gitgutter#utility#setbufvar(a:bufnr, 'tick', getbufvar(a:bufnr, 'changedtick'))
endfunction


================================================
FILE: autoload/gitgutter/diff_highlight.vim
================================================
" This is the minimum number of characters required between regions of change
" in a line.  It's somewhat arbitrary: higher values mean less visual busyness;
" lower values mean more detail.
let s:gap_between_regions = 5


" Calculates the changed portions of lines.
"
" Based on:
"
" - diff-highlight (included with git)
"   https://github.com/git/git/blob/master/contrib/diff-highlight/DiffHighlight.pm
"
" - Diff Strategies, Neil Fraser
"   https://neil.fraser.name/writing/diff/


" Returns a list of intra-line changed regions.
" Each element is a list:
"
"   [
"     line number (1-based),
"     type ('+' or '-'),
"     start column (1-based, inclusive),
"     stop column (1-based, inclusive),
"   ]
"
" Args:
"   hunk_body - list of lines
function! gitgutter#diff_highlight#process(hunk_body)
  " Check whether we have the same number of lines added as removed.
  let [removed, added] = [0, 0]
  for line in a:hunk_body
    if line[0] == '-'
      let removed += 1
    elseif line[0] == '+'
      let added += 1
    endif
  endfor
  if removed != added
    return []
  endif

  let regions = []

  for i in range(removed)
    " pair lines by position
    let rline = a:hunk_body[i]
    let aline = a:hunk_body[i + removed]

    call s:diff(rline, aline, i, i+removed, 0, 0, regions, 1)
  endfor

  return regions
endfunction


function! s:diff(rline, aline, rlinenr, alinenr, rprefix, aprefix, regions, whole_line)
  " diff marker does not count as a difference in prefix
  let start = a:whole_line ? 1 : 0
  let prefix = s:common_prefix(a:rline[start:], a:aline[start:])
  if a:whole_line
    let prefix += 1
  endif
  let [rsuffix, asuffix] = s:common_suffix(a:rline, a:aline, prefix+1)

  " region of change (common prefix and suffix removed)
  let rtext = a:rline[prefix+1:rsuffix-1]
  let atext = a:aline[prefix+1:asuffix-1]

  " singular insertion
  if empty(rtext)
    if !a:whole_line || len(atext) != len(a:aline)  " not whole line
      call add(a:regions, [a:alinenr+1, '+', a:aprefix+prefix+1+1, a:aprefix+asuffix+1-1])
    endif
    return
  endif

  " singular deletion
  if empty(atext)
    if !a:whole_line || len(rtext) != len(a:rline)  " not whole line
      call add(a:regions, [a:rlinenr+1, '-', a:rprefix+prefix+1+1, a:rprefix+rsuffix+1-1])
    endif
    return
  endif

  " two insertions
  let j = stridx(atext, rtext)
  if j != -1
    call add(a:regions, [a:alinenr+1, '+', a:aprefix+prefix+1+1, a:aprefix+prefix+j+1])
    call add(a:regions, [a:alinenr+1, '+', a:aprefix+prefix+1+1+j+len(rtext), a:aprefix+asuffix+1-1])
    return
  endif

  " two deletions
  let j = stridx(rtext, atext)
  if j != -1
    call add(a:regions, [a:rlinenr+1, '-', a:rprefix+prefix+1+1, a:rprefix+prefix+j+1])
    call add(a:regions, [a:rlinenr+1, '-', a:rprefix+prefix+1+1+j+len(atext), a:rprefix+rsuffix+1-1])
    return
  endif

  " two edits
  let lcs = s:lcs(rtext, atext)
  " TODO do we need to ensure we don't get more than 2 elements when splitting?
  if len(lcs) > s:gap_between_regions
    let redits = s:split(rtext, lcs)
    let aedits = s:split(atext, lcs)
    call s:diff(redits[0], aedits[0], a:rlinenr, a:alinenr, a:rprefix+prefix+1,                         a:aprefix+prefix+1,                         a:regions, 0)
    call s:diff(redits[1], aedits[1], a:rlinenr, a:alinenr, a:rprefix+prefix+1+len(redits[0])+len(lcs), a:aprefix+prefix+1+len(aedits[0])+len(lcs), a:regions, 0)
    return
  endif

  " fall back to highlighting entire changed area

  " if a change (but not the whole line)
  if !a:whole_line || ((prefix != 0 || rsuffix != len(a:rline)) && prefix+1 < rsuffix)
    call add(a:regions, [a:rlinenr+1, '-', a:rprefix+prefix+1+1, a:rprefix+rsuffix+1-1])
  endif

  " if a change (but not the whole line)
  if !a:whole_line || ((prefix != 0 || asuffix != len(a:aline)) && prefix+1 < asuffix)
    call add(a:regions, [a:alinenr+1, '+', a:aprefix+prefix+1+1, a:aprefix+asuffix+1-1])
  endif
endfunction


function! s:lcs(s1, s2)
  if empty(a:s1) || empty(a:s2)
    return ''
  endif

  let matrix = map(repeat([repeat([0], len(a:s2)+1)], len(a:s1)+1), 'copy(v:val)')

  let maxlength = 0
  let endindex = len(a:s1)

  for i in range(1, len(a:s1))
    for j in range(1, len(a:s2))
      if a:s1[i-1] ==# a:s2[j-1]
        let matrix[i][j] = 1 + matrix[i-1][j-1]
        if matrix[i][j] > maxlength
          let maxlength = matrix[i][j]
          let endindex = i - 1
        endif
      endif
    endfor
  endfor

  return a:s1[endindex - maxlength + 1 : endindex]
endfunction


" Returns 0-based index of last character of common prefix
" If there is no common prefix, returns -1.
"
" a, b - strings
"
function! s:common_prefix(a, b)
  let len = min([len(a:a), len(a:b)])
  if len == 0
    return -1
  endif
  for i in range(len)
    if a:a[i:i] !=# a:b[i:i]
      return i - 1
    endif
  endfor
  return i
endfunction


" Returns 0-based indices of start of common suffix
"
" a, b - strings
" start - 0-based index to start from
function! s:common_suffix(a, b, start)
  let [sa, sb] = [len(a:a), len(a:b)]
  while sa >= a:start && sb >= a:start
    if a:a[sa] ==# a:b[sb]
      let sa -= 1
      let sb -= 1
    else
      break
    endif
  endwhile
  return [sa+1, sb+1]
endfunction


" Split a string on another string.
" Assumes 1 occurrence of the delimiter.
function! s:split(str, delimiter)
  let i = stridx(a:str, a:delimiter)

  if i == 0
    return ['', a:str[len(a:delimiter):]]
  endif

  return [a:str[:i-1], a:str[i+len(a:delimiter):]]
endfunction


================================================
FILE: autoload/gitgutter/fold.vim
================================================
function! gitgutter#fold#enable()
  call s:save_fold_state()

  call s:set_fold_levels()
  setlocal foldexpr=gitgutter#fold#level(v:lnum)
  setlocal foldmethod=expr
  setlocal foldlevel=0
  setlocal foldenable

  call gitgutter#utility#setbufvar(bufnr(''), 'folded', 1)
endfunction


function! gitgutter#fold#disable()
  call s:restore_fold_state()
  call gitgutter#utility#setbufvar(bufnr(''), 'folded', 0)
endfunction


function! gitgutter#fold#toggle()
  if s:folded()
    call gitgutter#fold#disable()
  else
    call gitgutter#fold#enable()
  endif
endfunction


function! gitgutter#fold#level(lnum)
  return gitgutter#utility#getbufvar(bufnr(''), 'fold_levels')[a:lnum]
endfunction


function! gitgutter#fold#foldtext()
  if !gitgutter#fold#is_changed()
    return foldtext()
  endif

  return substitute(foldtext(), ':', ' (*):', '')
endfunction


" Returns 1 if any of the folded lines have been changed
" (added, removed, or modified), 0 otherwise.
function! gitgutter#fold#is_changed()
  for hunk in gitgutter#hunk#hunks(bufnr(''))
    let hunk_begin = hunk[2]
    let hunk_end   = hunk[2] + (hunk[3] == 0 ? 1 : hunk[3])

    if hunk_end < v:foldstart
      continue
    endif

    if hunk_begin > v:foldend
      break
    endif

    return 1
  endfor

  return 0
endfunction


" A line in a hunk has a fold level of 0.
" A line within 3 lines of a hunk has a fold level of 1.
" All other lines have a fold level of 2.
function! s:set_fold_levels()
  let fold_levels = ['']

  for lnum in range(1, line('$'))
    let in_hunk = gitgutter#hunk#in_hunk(lnum)
    call add(fold_levels, (in_hunk ? 0 : 2))
  endfor

  let lines_of_context = 3

  for lnum in range(1, line('$'))
    if fold_levels[lnum] == 2
      let pre = lnum >= 3 ? lnum - lines_of_context : 0
      let post = lnum + lines_of_context
      if index(fold_levels[pre:post], 0) != -1
        let fold_levels[lnum] = 1
      endif
    endif
  endfor

  call gitgutter#utility#setbufvar(bufnr(''), 'fold_levels', fold_levels)
endfunction


function! s:save_fold_state()
  let bufnr = bufnr('')
  call gitgutter#utility#setbufvar(bufnr, 'foldlevel', &foldlevel)
  call gitgutter#utility#setbufvar(bufnr, 'foldmethod', &foldmethod)
  if &foldmethod ==# 'manual'
    mkview
  endif
endfunction

function! s:restore_fold_state()
  let bufnr = bufnr('')
  let &foldlevel = gitgutter#utility#getbufvar(bufnr, 'foldlevel')
  let &foldmethod = gitgutter#utility#getbufvar(bufnr, 'foldmethod')
  if &foldmethod ==# 'manual'
    loadview
  else
    normal! zx
  endif
endfunction

function! s:folded()
  return gitgutter#utility#getbufvar(bufnr(''), 'folded')
endfunction



================================================
FILE: autoload/gitgutter/highlight.vim
================================================
function! gitgutter#highlight#line_disable() abort
  let g:gitgutter_highlight_lines = 0
  call s:define_sign_line_highlights()

  if !g:gitgutter_signs
    call gitgutter#sign#clear_signs(bufnr(''))
  endif

  redraw!
endfunction

function! gitgutter#highlight#line_enable() abort
  let old_highlight_lines = g:gitgutter_highlight_lines

  let g:gitgutter_highlight_lines = 1
  call s:define_sign_line_highlights()

  if !old_highlight_lines && !g:gitgutter_signs
    call gitgutter#all(1)
  endif

  redraw!
endfunction

function! gitgutter#highlight#line_toggle() abort
  if g:gitgutter_highlight_lines
    call gitgutter#highlight#line_disable()
  else
    call gitgutter#highlight#line_enable()
  endif
endfunction


function! gitgutter#highlight#linenr_disable() abort
  let g:gitgutter_highlight_linenrs = 0
  call s:define_sign_linenr_highlights()

  if !g:gitgutter_signs
    call gitgutter#sign#clear_signs(bufnr(''))
  endif

  redraw!
endfunction

function! gitgutter#highlight#linenr_enable() abort
  let old_highlight_linenrs = g:gitgutter_highlight_linenrs

  let g:gitgutter_highlight_linenrs = 1
  call s:define_sign_linenr_highlights()

  if !old_highlight_linenrs && !g:gitgutter_signs
    call gitgutter#all(1)
  endif

  redraw!
endfunction

function! gitgutter#highlight#linenr_toggle() abort
  if g:gitgutter_highlight_linenrs
    call gitgutter#highlight#linenr_disable()
  else
    call gitgutter#highlight#linenr_enable()
  endif
endfunction


function! gitgutter#highlight#define_highlights() abort
  let [guibg, ctermbg] = s:get_background_colors('SignColumn')

  " Highlights used by the signs.

  " When they are invisible.
  execute "highlight GitGutterAddInvisible    guifg=bg guibg=" . guibg . " ctermfg=" . ctermbg . " ctermbg=" . ctermbg
  execute "highlight GitGutterChangeInvisible guifg=bg guibg=" . guibg . " ctermfg=" . ctermbg . " ctermbg=" . ctermbg
  execute "highlight GitGutterDeleteInvisible guifg=bg guibg=" . guibg . " ctermfg=" . ctermbg . " ctermbg=" . ctermbg
  highlight default link GitGutterChangeDeleteInvisible GitGutterChangeInvisible

  " When they are visible.
  for type in ["Add", "Change", "Delete"]
    if hlexists("GitGutter".type) && s:get_foreground_colors("GitGutter".type) != ['NONE', 'NONE']
      if g:gitgutter_set_sign_backgrounds
        execute "highlight GitGutter".type." guibg=".guibg." ctermbg=".ctermbg
      endif
      continue
    elseif s:useful_diff_colours()
      let [guifg, ctermfg] = s:get_foreground_colors('Diff'.type)
    else
      let [guifg, ctermfg] = s:get_foreground_fallback_colors(type)
    endif
    execute "highlight GitGutter".type." guifg=".guifg." guibg=".guibg." ctermfg=".ctermfg." ctermbg=".ctermbg
  endfor

  if hlexists("GitGutterChangeDelete") && g:gitgutter_set_sign_backgrounds
    execute "highlight GitGutterChangeDelete guibg=".guibg." ctermbg=".ctermbg
  endif

  highlight default link GitGutterChangeDelete GitGutterChange

  " Highlights used for the whole line.

  highlight default link GitGutterAddLine          DiffAdd
  highlight default link GitGutterChangeLine       DiffChange
  highlight default link GitGutterDeleteLine       DiffDelete
  highlight default link GitGutterChangeDeleteLine GitGutterChangeLine

  highlight default link GitGutterAddLineNr          CursorLineNr
  highlight default link GitGutterChangeLineNr       CursorLineNr
  highlight default link GitGutterDeleteLineNr       CursorLineNr
  highlight default link GitGutterChangeDeleteLineNr GitGutterChangeLineNr

  " Highlights used intra line.
  highlight default GitGutterAddIntraLine    gui=reverse cterm=reverse
  highlight default GitGutterDeleteIntraLine gui=reverse cterm=reverse
  " Set diff syntax colours (used in the preview window) - diffAdded,diffChanged,diffRemoved -
  " to match the signs, if not set aleady.
  for [dtype,type] in [['Added','Add'], ['Changed','Change'], ['Removed','Delete']]
    if !hlexists('diff'.dtype)
      let [guifg, ctermfg] = s:get_foreground_colors('GitGutter'.type)
      execute "highlight diff".dtype." guifg=".guifg." ctermfg=".ctermfg." guibg=NONE ctermbg=NONE"
    endif
  endfor
endfunction

function! gitgutter#highlight#define_signs() abort
  sign define GitGutterLineAdded
  sign define GitGutterLineModified
  sign define GitGutterLineRemoved
  sign define GitGutterLineRemovedFirstLine
  sign define GitGutterLineRemovedAboveAndBelow
  sign define GitGutterLineModifiedRemoved

  call s:define_sign_text()
  call gitgutter#highlight#define_sign_text_highlights()
  call s:define_sign_line_highlights()
  call s:define_sign_linenr_highlights()
endfunction

function! s:define_sign_text() abort
  execute "sign define GitGutterLineAdded                 text=" . g:gitgutter_sign_added
  execute "sign define GitGutterLineModified              text=" . g:gitgutter_sign_modified
  execute "sign define GitGutterLineRemoved               text=" . g:gitgutter_sign_removed
  execute "sign define GitGutterLineRemovedFirstLine      text=" . g:gitgutter_sign_removed_first_line
  execute "sign define GitGutterLineRemovedAboveAndBelow  text=" . g:gitgutter_sign_removed_above_and_below
  execute "sign define GitGutterLineModifiedRemoved       text=" . g:gitgutter_sign_modified_removed
endfunction

function! gitgutter#highlight#define_sign_text_highlights() abort
  " Once a sign's text attribute has been defined, it cannot be undefined or
  " set to an empty value.  So to make signs' text disappear (when toggling
  " off or disabling) we make them invisible by setting their foreground colours
  " to the background's.
  if g:gitgutter_signs
    sign define GitGutterLineAdded                 texthl=GitGutterAdd
    sign define GitGutterLineModified              texthl=GitGutterChange
    sign define GitGutterLineRemoved               texthl=GitGutterDelete
    sign define GitGutterLineRemovedFirstLine      texthl=GitGutterDelete
    sign define GitGutterLineRemovedAboveAndBelow  texthl=GitGutterDelete
    sign define GitGutterLineModifiedRemoved       texthl=GitGutterChangeDelete
  else
    sign define GitGutterLineAdded                 texthl=GitGutterAddInvisible
    sign define GitGutterLineModified              texthl=GitGutterChangeInvisible
    sign define GitGutterLineRemoved               texthl=GitGutterDeleteInvisible
    sign define GitGutterLineRemovedFirstLine      texthl=GitGutterDeleteInvisible
    sign define GitGutterLineRemovedAboveAndBelow  texthl=GitGutterDeleteInvisible
    sign define GitGutterLineModifiedRemoved       texthl=GitGutterChangeDeleteInvisible
  endif
endfunction

function! s:define_sign_line_highlights() abort
  if g:gitgutter_highlight_lines
    sign define GitGutterLineAdded                 linehl=GitGutterAddLine
    sign define GitGutterLineModified              linehl=GitGutterChangeLine
    sign define GitGutterLineRemoved               linehl=GitGutterDeleteLine
    sign define GitGutterLineRemovedFirstLine      linehl=GitGutterDeleteLine
    sign define GitGutterLineRemovedAboveAndBelow  linehl=GitGutterDeleteLine
    sign define GitGutterLineModifiedRemoved       linehl=GitGutterChangeDeleteLine
  else
    sign define GitGutterLineAdded                 linehl=NONE
    sign define GitGutterLineModified              linehl=NONE
    sign define GitGutterLineRemoved               linehl=NONE
    sign define GitGutterLineRemovedFirstLine      linehl=NONE
    sign define GitGutterLineRemovedAboveAndBelow  linehl=NONE
    sign define GitGutterLineModifiedRemoved       linehl=NONE
  endif
endfunction

function! s:define_sign_linenr_highlights() abort
  if has('nvim-0.3.2')
    try
      if g:gitgutter_highlight_linenrs
        sign define GitGutterLineAdded                 numhl=GitGutterAddLineNr
        sign define GitGutterLineModified              numhl=GitGutterChangeLineNr
        sign define GitGutterLineRemoved               numhl=GitGutterDeleteLineNr
        sign define GitGutterLineRemovedFirstLine      numhl=GitGutterDeleteLineNr
        sign define GitGutterLineRemovedAboveAndBelow  numhl=GitGutterDeleteLineNr
        sign define GitGutterLineModifiedRemoved       numhl=GitGutterChangeDeleteLineNr
      else
        sign define GitGutterLineAdded                 numhl=NONE
        sign define GitGutterLineModified              numhl=NONE
        sign define GitGutterLineRemoved               numhl=NONE
        sign define GitGutterLineRemovedFirstLine      numhl=NONE
        sign define GitGutterLineRemovedAboveAndBelow  numhl=NONE
        sign define GitGutterLineModifiedRemoved       numhl=NONE
      endif
    catch /E475/
    endtry
  endif
endfunction

function! s:get_hl(group, what, mode) abort
  let r = synIDattr(synIDtrans(hlID(a:group)), a:what, a:mode)
  if empty(r) || r == -1
    return 'NONE'
  endif
  return r
endfunction

function! s:get_foreground_colors(group) abort
  let ctermfg = s:get_hl(a:group, 'fg', 'cterm')
  let guifg = s:get_hl(a:group, 'fg', 'gui')
  return [guifg, ctermfg]
endfunction

function! s:get_background_colors(group) abort
  let ctermbg = s:get_hl(a:group, 'bg', 'cterm')
  let guibg = s:get_hl(a:group, 'bg', 'gui')
  return [guibg, ctermbg]
endfunction

function! s:useful_diff_colours()
  let [guifg_add, ctermfg_add] = s:get_foreground_colors('DiffAdd')
  let [guifg_del, ctermfg_del] = s:get_foreground_colors('DiffDelete')

  return guifg_add != guifg_del && ctermfg_add != ctermfg_del
endfunction

function! s:get_foreground_fallback_colors(type)
  if a:type == 'Add'
    return ['#009900', '2']
  elseif a:type == 'Change'
    return ['#bbbb00', '3']
  elseif a:type == 'Delete'
    return ['#ff2222', '1']
  endif
endfunction


================================================
FILE: autoload/gitgutter/hunk.vim
================================================
let s:winid = 0
let s:preview_bufnr = 0
let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : ''

function! gitgutter#hunk#set_hunks(bufnr, hunks) abort
  call gitgutter#utility#setbufvar(a:bufnr, 'hunks', a:hunks)
  call s:reset_summary(a:bufnr)
endfunction

function! gitgutter#hunk#hunks(bufnr) abort
  return gitgutter#utility#getbufvar(a:bufnr, 'hunks', [])
endfunction

function! gitgutter#hunk#reset(bufnr) abort
  call gitgutter#utility#setbufvar(a:bufnr, 'hunks', [])
  call s:reset_summary(a:bufnr)
endfunction


function! gitgutter#hunk#summary(bufnr) abort
  return gitgutter#utility#getbufvar(a:bufnr, 'summary', [0,0,0])
endfunction

function! s:reset_summary(bufnr) abort
  call gitgutter#utility#setbufvar(a:bufnr, 'summary', [0,0,0])
endfunction

function! gitgutter#hunk#increment_lines_added(bufnr, count) abort
  let summary = gitgutter#hunk#summary(a:bufnr)
  let summary[0] += a:count
  call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary)
endfunction

function! gitgutter#hunk#increment_lines_modified(bufnr, count) abort
  let summary = gitgutter#hunk#summary(a:bufnr)
  let summary[1] += a:count
  call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary)
endfunction

function! gitgutter#hunk#increment_lines_removed(bufnr, count) abort
  let summary = gitgutter#hunk#summary(a:bufnr)
  let summary[2] += a:count
  call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary)
endfunction


function! gitgutter#hunk#next_hunk(count) abort
  let bufnr = bufnr('')
  if !gitgutter#utility#is_active(bufnr) | return | endif

  let hunks = gitgutter#hunk#hunks(bufnr)
  if empty(hunks)
    call gitgutter#utility#warn('No hunks in file')
    return
  endif

  let current_line = line('.')
  let hunk_count = 0
  for hunk in hunks
    if hunk[2] > current_line
      let hunk_count += 1
      if hunk_count == a:count
        let keys = &foldopen =~# '\<block\>' ? 'zv' : ''
        execute 'normal!' hunk[2] . 'G' . keys
        if g:gitgutter_show_msg_on_hunk_jumping
          redraw | echo printf('Hunk %d of %d', index(hunks, hunk) + 1, len(hunks))
        endif
        if gitgutter#hunk#is_preview_window_open()
          call gitgutter#hunk#preview()
        endif
        return
      endif
    endif
  endfor
  call gitgutter#utility#warn('No more hunks')
endfunction

function! gitgutter#hunk#prev_hunk(count) abort
  let bufnr = bufnr('')
  if !gitgutter#utility#is_active(bufnr) | return | endif

  let hunks = gitgutter#hunk#hunks(bufnr)
  if empty(hunks)
    call gitgutter#utility#warn('No hunks in file')
    return
  endif

  let current_line = line('.')
  let hunk_count = 0
  for hunk in reverse(copy(hunks))
    if hunk[2] < current_line
      let hunk_count += 1
      if hunk_count == a:count
        let keys = &foldopen =~# '\<block\>' ? 'zv' : ''
        let target = hunk[2] == 0 ? 1 : hunk[2]
        execute 'normal!' target . 'G' . keys
        if g:gitgutter_show_msg_on_hunk_jumping
          redraw | echo printf('Hunk %d of %d', index(hunks, hunk) + 1, len(hunks))
        endif
        if gitgutter#hunk#is_preview_window_open()
          call gitgutter#hunk#preview()
        endif
        return
      endif
    endif
  endfor
  call gitgutter#utility#warn('No previous hunks')
endfunction

" Returns the hunk the cursor is currently in or an empty list if the cursor
" isn't in a hunk.
function! s:current_hunk() abort
  let bufnr = bufnr('')
  let current_hunk = []

  for hunk in gitgutter#hunk#hunks(bufnr)
    if gitgutter#hunk#cursor_in_hunk(hunk)
      let current_hunk = hunk
      break
    endif
  endfor

  return current_hunk
endfunction

" Returns truthy if the cursor is in two hunks (which can only happen if the
" cursor is on the first line and lines above have been deleted and lines
" immediately below have been deleted) or falsey otherwise.
function! s:cursor_in_two_hunks()
  let hunks = gitgutter#hunk#hunks(bufnr(''))

  if line('.') == 1 && len(hunks) > 1 && hunks[0][2:3] == [0, 0] && hunks[1][2:3] == [1, 0]
    return 1
  endif

  return 0
endfunction

" A line can be in 0 or 1 hunks, with the following exception: when the first
" line(s) of a file has been deleted, and the new second line (and
" optionally below) has been deleted, the new first line is in two hunks.
function! gitgutter#hunk#cursor_in_hunk(hunk) abort
  let current_line = line('.')

  if current_line == 1 && a:hunk[2] == 0
    return 1
  endif

  if current_line >= a:hunk[2] && current_line < a:hunk[2] + (a:hunk[3] == 0 ? 1 : a:hunk[3])
    return 1
  endif

  return 0
endfunction


function! gitgutter#hunk#in_hunk(lnum)
  " Hunks are sorted in the order they appear in the buffer.
  for hunk in gitgutter#hunk#hunks(bufnr(''))
    " if in a hunk on first line of buffer
    if a:lnum == 1 && hunk[2] == 0
      return 1
    endif

    " if in a hunk generally
    if a:lnum >= hunk[2] && a:lnum < hunk[2] + (hunk[3] == 0 ? 1 : hunk[3])
      return 1
    endif

    " if hunk starts after the given line
    if a:lnum < hunk[2]
      return 0
    endif
  endfor

  return 0
endfunction


function! gitgutter#hunk#text_object(inner) abort
  let hunk = s:current_hunk()

  if empty(hunk)
    return
  endif

  let [first_line, last_line] = [hunk[2], hunk[2] + hunk[3] - 1]

  if ! a:inner
    let lnum = last_line
    let eof = line('$')
    while lnum < eof && empty(getline(lnum + 1))
      let lnum +=1
    endwhile
    let last_line = lnum
  endif

  execute 'normal! 'first_line.'GV'.last_line.'G'
endfunction


function! gitgutter#hunk#stage(...) abort
  if !s:in_hunk_preview_window() && !gitgutter#utility#has_repo_path(bufnr('')) | return | endif

  if a:0 && (a:1 != 1 || a:2 != line('$'))
    call s:hunk_op(function('s:stage'), a:1, a:2)
  else
    call s:hunk_op(function('s:stage'))
  endif
  silent! call repeat#set("\<Plug>(GitGutterStageHunk)", -1)
endfunction

function! gitgutter#hunk#undo() abort
  if !gitgutter#utility#has_repo_path(bufnr('')) | return | endif

  call s:hunk_op(function('s:undo'))
  silent! call repeat#set("\<Plug>(GitGutterUndoHunk)", -1)
endfunction

function! gitgutter#hunk#preview() abort
  if !gitgutter#utility#has_repo_path(bufnr('')) | return | endif

  call s:hunk_op(function('s:preview'))
  silent! call repeat#set("\<Plug>(GitGutterPreviewHunk)", -1)
endfunction


function! s:hunk_op(op, ...)
  let bufnr = bufnr('')

  if s:in_hunk_preview_window()
    if string(a:op) =~ '_stage'
      " combine hunk-body in preview window with updated hunk-header
      let hunk_body = getline(1, '$')

      let [removed, added] = [0, 0]
      for line in hunk_body
        if line[0] == '-'
          let removed += 1
        elseif line[0] == '+'
          let added += 1
        endif
      endfor

      let hunk_header = b:hunk_header
      " from count
      let hunk_header[4] = substitute(hunk_header[4], '\(-\d\+\)\(,\d\+\)\?', '\=submatch(1).",".removed', '')
      " to count
      let hunk_header[4] = substitute(hunk_header[4], '\(+\d\+\)\(,\d\+\)\?', '\=submatch(1).",".added', '')

      let hunk_diff = join(hunk_header + hunk_body, "\n")."\n"

      if &previewwindow
        call s:goto_original_window()
      endif
      call gitgutter#hunk#close_hunk_preview_window()
      call s:stage(hunk_diff)
    endif

    return
  endif

  if gitgutter#utility#is_active(bufnr)
    " Get a (synchronous) diff.
    let [async, g:gitgutter_async] = [g:gitgutter_async, 0]
    let diff = gitgutter#diff#run_diff(bufnr, g:gitgutter_diff_relative_to)
    let g:gitgutter_async = async

    call gitgutter#hunk#set_hunks(bufnr, gitgutter#diff#parse_diff(diff))
    call gitgutter#diff#process_hunks(bufnr, gitgutter#hunk#hunks(bufnr))  " so the hunk summary is updated

    if empty(s:current_hunk())
      call gitgutter#utility#warn('Cursor is not in a hunk')
    elseif s:cursor_in_two_hunks()
      let choice = input('Choose hunk: upper or lower (u/l)? ')
      " Clear input
      normal! :<ESC>
      if choice =~ 'u'
        call a:op(gitgutter#diff#hunk_diff(bufnr, diff, 0))
      elseif choice =~ 'l'
        call a:op(gitgutter#diff#hunk_diff(bufnr, diff, 1))
      else
        call gitgutter#utility#warn('Did not recognise your choice')
      endif
    else
      let hunk_diff = gitgutter#diff#hunk_diff(bufnr, diff)

      if a:0
        let hunk_first_line = s:current_hunk()[2]
        let hunk_diff = s:part_of_diff(hunk_diff, a:1-hunk_first_line, a:2-hunk_first_line)
      endif

      call a:op(hunk_diff)
    endif
  endif
endfunction


function! s:stage(hunk_diff)
  let bufnr = bufnr('')

  if gitgutter#utility#clean_smudge_filter_applies(bufnr)
    let choice = input('File uses clean/smudge filter. Stage entire file (y/n)? ')
    normal! :<ESC>
    if choice =~ 'y'
      " We are about to add the file to the index so write the buffer to
      " ensure the file on disk matches it (the buffer).
      write
      let path = gitgutter#utility#repo_path(bufnr, 1)
      " Add file to index.
      let cmd = gitgutter#git(bufnr).' add '.
            \ gitgutter#utility#shellescape(gitgutter#utility#filename(bufnr))
      let [_, error_code] = gitgutter#utility#system(cmd)
    else
      return
    endif

  else
    let diff = s:adjust_header(bufnr, a:hunk_diff)
    " Apply patch to index.
    let [_, error_code] = gitgutter#utility#system(
          \ gitgutter#git(bufnr).' apply --cached --unidiff-zero - ',
          \ diff)
  endif

  if error_code
    call gitgutter#utility#warn('Patch does not apply')
  else
    if exists('#User#GitGutterStage')
      execute 'doautocmd' s:nomodeline 'User GitGutterStage'
    endif
  endif

  " Refresh gitgutter's view of buffer.
  call gitgutter#process_buffer(bufnr, 1)
endfunction


function! s:undo(hunk_diff)
  " Apply reverse patch to buffer.
  let hunk  = gitgutter#diff#parse_hunk(split(a:hunk_diff, '\n')[4])
  let lines = map(split(a:hunk_diff, '\r\?\n')[5:], 'v:val[1:]')
  let lnum  = hunk[2]
  let added_only   = hunk[1] == 0 && hunk[3]  > 0
  let removed_only = hunk[1]  > 0 && hunk[3] == 0

  if removed_only
    call append(lnum, lines)
  elseif added_only
    execute lnum .','. (lnum+len(lines)-1) .'d _'
  else
    call append(lnum-1, lines[0:hunk[1]])
    execute (lnum+hunk[1]) .','. (lnum+hunk[1]+hunk[3]) .'d _'
  endif

  " Refresh gitgutter's view of buffer.
  call gitgutter#process_buffer(bufnr(''), 1)
endfunction


function! s:preview(hunk_diff)
  if g:gitgutter_preview_win_floating && exists('*nvim_set_current_win') && s:winid != 0
    call nvim_set_current_win(s:winid)
    return
  endif

  let lines = split(a:hunk_diff, '\r\?\n')
  let header = lines[0:4]
  let body = lines[5:]

  call s:open_hunk_preview_window()
  call s:populate_hunk_preview_window(header, body)
  call s:enable_staging_from_hunk_preview_window()
  if &previewwindow
    call s:goto_original_window()
  endif
endfunction


" Returns a new hunk diff using the specified lines from the given one.
" Assumes all lines are additions.
" a:first, a:last - 0-based indexes into the body of the hunk.
function! s:part_of_diff(hunk_diff, first, last)
  let diff_lines = split(a:hunk_diff, '\n', 1)

  " adjust 'to' line count in header
  let diff_lines[4] = substitute(diff_lines[4], '\(+\d\+\)\(,\d\+\)\?', '\=submatch(1).",".(a:last-a:first+1)', '')

  return join(diff_lines[0:4] + diff_lines[5+a:first:5+a:last], "\n")."\n"
endfunction


function! s:adjust_header(bufnr, hunk_diff)
  let filepath = gitgutter#utility#repo_path(a:bufnr, 0)
  return s:adjust_hunk_summary(s:fix_file_references(filepath, a:hunk_diff))
endfunction


" Replaces references to temp files with the actual file.
function! s:fix_file_references(filepath, hunk_diff)
  let lines = split(a:hunk_diff, '\n')

  let left_prefix  = matchstr(lines[2], '[abciow12]').'/'
  let right_prefix = matchstr(lines[3], '[abciow12]').'/'
  let quote        = lines[0][11] == '"' ? '"' : ''

  let left_file  = quote.left_prefix.a:filepath.quote
  let right_file = quote.right_prefix.a:filepath.quote

  let lines[0] = 'diff --git '.left_file.' '.right_file
  let lines[2] = '--- '.left_file
  let lines[3] = '+++ '.right_file

  return join(lines, "\n")."\n"
endfunction


function! s:adjust_hunk_summary(hunk_diff) abort
  let line_adjustment = s:line_adjustment_for_current_hunk()
  let diff = split(a:hunk_diff, '\n', 1)
  let diff[4] = substitute(diff[4], '+\zs\(\d\+\)', '\=submatch(1)+line_adjustment', '')
  return join(diff, "\n")
endfunction


" Returns the number of lines the current hunk is offset from where it would
" be if any changes above it in the file didn't exist.
function! s:line_adjustment_for_current_hunk() abort
  let bufnr = bufnr('')
  let adj = 0
  for hunk in gitgutter#hunk#hunks(bufnr)
    if gitgutter#hunk#cursor_in_hunk(hunk)
      break
    else
      let adj += hunk[1] - hunk[3]
    endif
  endfor
  return adj
endfunction


function! s:in_hunk_preview_window()
  if g:gitgutter_preview_win_floating
    return win_id2win(s:winid) == winnr()
  else
    return &previewwindow
  endif
endfunction


" Floating window: does not move cursor to floating window.
" Preview window: moves cursor to preview window.
"
" Note the "diff" file type treats a line starting "--- " as a file header
" instead of a removed line, thanks to the syntax group "diffNewFile".  We
" want it to be treated as a removed line.  Since we never show headers in
" the preview window it is safe to remove the offending syntax group.
function! s:open_hunk_preview_window()
  let source_wrap = &wrap
  let source_window = winnr()

  if g:gitgutter_preview_win_floating
    if exists('*nvim_open_win')
      call gitgutter#hunk#close_hunk_preview_window()

      let buf = nvim_create_buf(v:false, v:false)
      " Set default width and height for now.
      let s:winid = nvim_open_win(buf, v:false, g:gitgutter_floating_window_options)
      call nvim_win_set_option(s:winid, 'wrap', source_wrap ? v:true : v:false)
      call nvim_buf_set_option(buf, 'filetype',  'diff')
      if exists("*win_execute")
        try
          call win_execute(s:winid, "syntax clear diffNewFile", 1)
        catch /E28/
          " noop
        endtry
      endif
      call nvim_buf_set_option(buf, 'buftype',   'acwrite')
      call nvim_buf_set_option(buf, 'bufhidden', 'delete')
      call nvim_buf_set_option(buf, 'swapfile',  v:false)
      call nvim_buf_set_name(buf, 'gitgutter://hunk-preview')

      if g:gitgutter_close_preview_on_escape
        let winnr = nvim_win_get_number(s:winid)
        execute winnr.'wincmd w'
        nnoremap <buffer> <silent> <Esc> :<C-U>call gitgutter#hunk#close_hunk_preview_window()<CR>
        wincmd w
      endif

      " Assumes cursor is in original window.
      autocmd CursorMoved,TabLeave <buffer> ++once call gitgutter#hunk#close_hunk_preview_window()

      return
    endif

    if exists('*popup_create')
      if g:gitgutter_close_preview_on_escape
        let g:gitgutter_floating_window_options.filter = function('s:close_popup_on_escape')
      endif

      let s:winid = popup_create('', g:gitgutter_floating_window_options)

      call setbufvar(winbufnr(s:winid), '&filetype', 'diff')
      if exists("*win_execute")
        try
          call win_execute(s:winid, "syntax clear diffNewFile", 1)
        catch /E28/
          " noop
        endtry
      endif
      call setwinvar(s:winid, '&wrap', source_wrap)

      return
    endif
  endif

  if exists('&previewpopup')
    let [previewpopup, &previewpopup] = [&previewpopup, '']
  endif

  " Specifying where to open the preview window can lead to the cursor going
  " to an unexpected window when the preview window is closed (#769).
  silent! noautocmd execute g:gitgutter_preview_win_location 'pedit gitgutter://hunk-preview'
  silent! wincmd P
  setlocal statusline=%{''}
  doautocmd WinEnter
  if exists('*win_getid')
    let s:winid = win_getid()
  else
    let s:preview_bufnr = bufnr('')
  endif
  setlocal filetype=diff buftype=acwrite bufhidden=delete
  try
    syntax clear diffNewFile
  catch /E28/
    " noop
  endtry
  let &l:wrap = source_wrap
  let b:source_window = source_window
  " Reset some defaults in case someone else has changed them.
  setlocal noreadonly modifiable noswapfile
  if g:gitgutter_close_preview_on_escape
    " Ensure cursor goes to the expected window.
    nnoremap <buffer> <silent> <Esc> :<C-U>execute b:source_window . "wincmd w"<Bar>pclose<CR>
  endif

  if exists('&previewpopup')
    let &previewpopup=previewpopup
  endif
endfunction


function! s:close_popup_on_escape(winid, key)
  if a:key == "\<Esc>"
    call popup_close(a:winid)
    return 1
  endif
  return 0
endfunction


" Floating window: does not care where cursor is.
" Preview window: assumes cursor is in preview window.
function! s:populate_hunk_preview_window(header, body)
  if g:gitgutter_preview_win_floating
    if exists('*nvim_open_win')
      " Assumes cursor is not in previewing window.
      call nvim_buf_set_var(winbufnr(s:winid), 'hunk_header', a:header)

      let [_scrolloff, &scrolloff] = [&scrolloff, 0]

      let [width, height] = s:screen_lines(a:body)
      let height = min([height, g:gitgutter_floating_window_options.height])
      call nvim_win_set_width(s:winid, width)
      call nvim_win_set_height(s:winid, height)

      let &scrolloff=_scrolloff

      call nvim_buf_set_lines(winbufnr(s:winid), 0, -1, v:false, [])
      call nvim_buf_set_lines(winbufnr(s:winid), 0, -1, v:false, a:body)
      call nvim_buf_set_option(winbufnr(s:winid), 'modified', v:false)

      let ns_id = nvim_create_namespace('GitGutter')
      call nvim_buf_clear_namespace(winbufnr(s:winid), ns_id, 0, -1)
      for region in gitgutter#diff_highlight#process(a:body)
        let group = region[1] == '+' ? 'GitGutterAddIntraLine' : 'GitGutterDeleteIntraLine'
        call nvim_buf_add_highlight(winbufnr(s:winid), ns_id, group, region[0]-1, region[2]-1, region[3])
      endfor

      call nvim_win_set_cursor(s:winid, [1,0])
    endif

    if exists('*popup_create')
      call popup_settext(s:winid, a:body)

      for region in gitgutter#diff_highlight#process(a:body)
        let group = region[1] == '+' ? 'GitGutterAddIntraLine' : 'GitGutterDeleteIntraLine'
        call win_execute(s:winid, "call matchaddpos('".group."', [[".region[0].", ".region[2].", ".(region[3]-region[2]+1)."]])")
      endfor
    endif

  else
    let b:hunk_header = a:header

    %delete _
    call setline(1, a:body)
    setlocal nomodified

    let [_, height] = s:screen_lines(a:body)
    execute 'resize' height
    1

    call clearmatches()
    for region in gitgutter#diff_highlight#process(a:body)
      let group = region[1] == '+' ? 'GitGutterAddIntraLine' : 'GitGutterDeleteIntraLine'
      call matchaddpos(group, [[region[0], region[2], region[3]-region[2]+1]])
    endfor

    1
  endif
endfunction


" Calculates the number of columns and the number of screen lines the given
" array of lines will take up, taking account of wrapping.
function! s:screen_lines(lines)
  let [_virtualedit, &virtualedit]=[&virtualedit, 'all']
  let cursor = getcurpos()
  normal! 0g$
  let available_width = virtcol('.')
  call setpos('.', cursor)
  let &virtualedit=_virtualedit
  let width = min([max(map(copy(a:lines), 'strdisplaywidth(v:val)')), available_width])

  if exists('*reduce')
    let height = reduce(a:lines, { acc, val -> acc + strdisplaywidth(val) / width + (strdisplaywidth(val) % width == 0 ? 0 : 1) }, 0)
  else
    let height = eval(join(map(copy(a:lines), 'strdisplaywidth(v:val) / width + (strdisplaywidth(v:val) % width == 0 ? 0 : 1)'), '+'))
  endif

  return [width, height]
endfunction


function! s:enable_staging_from_hunk_preview_window()
  augroup gitgutter_hunk_preview
    autocmd!
    let bufnr = s:winid != 0 ? winbufnr(s:winid) : s:preview_bufnr
    execute 'autocmd BufWriteCmd <buffer='.bufnr.'> GitGutterStageHunk'
  augroup END
endfunction


function! s:goto_original_window()
  noautocmd execute b:source_window . "wincmd w"
  doautocmd WinEnter
endfunction


function! gitgutter#hunk#close_hunk_preview_window()
  let bufnr = s:winid != 0 ? winbufnr(s:winid) : s:preview_bufnr
  call setbufvar(bufnr, '&modified', 0)

  if g:gitgutter_preview_win_floating
    if win_id2win(s:winid) > 0
      execute win_id2win(s:winid).'wincmd c'
    endif
  else
    pclose
  endif

  let s:winid = 0
  let s:preview_bufnr = 0
endfunction


function gitgutter#hunk#is_preview_window_open()
  if g:gitgutter_preview_win_floating
    if win_id2win(s:winid) > 0
      execute win_id2win(s:winid).'wincmd c'
    endif
  else
    for i in range(1, winnr('$'))
      if getwinvar(i, '&previewwindow')
        return 1
      endif
    endfor
  endif
  return 0
endfunction


================================================
FILE: autoload/gitgutter/sign.vim
================================================
" For older Vims without sign_place() the plugin has to manaage the sign ids.
let s:first_sign_id = 3000
let s:next_sign_id  = s:first_sign_id
" Remove-all-signs optimisation requires Vim 7.3.596+.
let s:supports_star = v:version > 703 || (v:version == 703 && has("patch596"))


function! gitgutter#sign#enable() abort
  let old_signs = g:gitgutter_signs

  let g:gitgutter_signs = 1
  call gitgutter#highlight#define_sign_text_highlights()

  if !old_signs && !g:gitgutter_highlight_lines && !g:gitgutter_highlight_linenrs
    call gitgutter#all(1)
  endif
endfunction

function! gitgutter#sign#disable() abort
  let g:gitgutter_signs = 0
  call gitgutter#highlight#define_sign_text_highlights()

  if !g:gitgutter_highlight_lines && !g:gitgutter_highlight_linenrs
    call gitgutter#sign#clear_signs(bufnr(''))
  endif
endfunction

function! gitgutter#sign#toggle() abort
  if g:gitgutter_signs
    call gitgutter#sign#disable()
  else
    call gitgutter#sign#enable()
  endif
endfunction


" Removes gitgutter's signs from the buffer being processed.
function! gitgutter#sign#clear_signs(bufnr) abort
  if exists('*sign_unplace')
    call sign_unplace('gitgutter', {'buffer': a:bufnr})
    return
  endif


  call s:find_current_signs(a:bufnr)

  let sign_ids = map(values(gitgutter#utility#getbufvar(a:bufnr, 'gitgutter_signs')), 'v:val.id')
  call s:remove_signs(a:bufnr, sign_ids, 1)
  call gitgutter#utility#setbufvar(a:bufnr, 'gitgutter_signs', {})
endfunction


" Updates gitgutter's signs in the buffer being processed.
"
" modified_lines: list of [<line_number (number)>, <name (string)>]
" where name = 'added|removed|modified|modified_removed'
function! gitgutter#sign#update_signs(bufnr, modified_lines) abort
  if exists('*sign_unplace')
    " Vim is (hopefully) now quick enough to remove all signs then place new ones.
    call sign_unplace('gitgutter', {'buffer': a:bufnr})

    let modified_lines = s:handle_double_hunk(a:modified_lines)
    let signs = map(copy(modified_lines), '{'.
          \ '"buffer":   a:bufnr,'.
          \ '"group":    "gitgutter",'.
          \ '"name":     s:highlight_name_for_change(v:val[1]),'.
          \ '"lnum":     v:val[0],'.
          \ '"priority": g:gitgutter_sign_priority'.
          \ '}')

    if exists('*sign_placelist')
      call sign_placelist(signs)
      return
    endif

    for sign in signs
      call sign_place(0, sign.group, sign.name, sign.buffer, {'lnum': sign.lnum, 'priority': sign.priority})
    endfor
    return
  endif


  " Derive a delta between the current signs and the ones we want.
  " Remove signs from lines that no longer need a sign.
  " Upsert the remaining signs.

  call s:find_current_signs(a:bufnr)

  let new_gitgutter_signs_line_numbers = map(copy(a:modified_lines), 'v:val[0]')
  let obsolete_signs = s:obsolete_gitgutter_signs_to_remove(a:bufnr, new_gitgutter_signs_line_numbers)

  call s:remove_signs(a:bufnr, obsolete_signs, s:remove_all_old_signs)
  call s:upsert_new_gitgutter_signs(a:bufnr, a:modified_lines)
endfunction


"
" Internal functions
"


function! s:find_current_signs(bufnr) abort
  let gitgutter_signs = {}  " <line_number (string)>: {'id': <id (number)>, 'name': <name (string)>}
  if !g:gitgutter_sign_allow_clobber
    let other_signs = []      " [<line_number (number),...]
  endif

  if exists('*getbufinfo')
    let bufinfo = getbufinfo(a:bufnr)[0]
    let signs = has_key(bufinfo, 'signs') ? bufinfo.signs : []
  else
    let signs = []

    redir => signlines
      silent execute "sign place buffer=" . a:bufnr
    redir END

    for signline in filter(split(signlines, '\n')[2:], 'v:val =~# "="')
      " Typical sign line before v8.1.0614:  line=88 id=1234 name=GitGutterLineAdded
      " We assume splitting is faster than a regexp.
      let components = split(signline)
      call add(signs, {
            \ 'lnum': str2nr(split(components[0], '=')[1]),
            \ 'id':   str2nr(split(components[1], '=')[1]),
            \ 'name':        split(components[2], '=')[1]
            \ })
    endfor
  endif

  for sign in signs
    if sign.name =~# 'GitGutter'
      " Remove orphaned signs (signs placed on lines which have been deleted).
      " (When a line is deleted its sign lingers.  Subsequent lines' signs'
      " line numbers are decremented appropriately.)
      if has_key(gitgutter_signs, sign.lnum)
        execute "sign unplace" gitgutter_signs[sign.lnum].id
      endif
      let gitgutter_signs[sign.lnum] = {'id': sign.id, 'name': sign.name}
    else
      if !g:gitgutter_sign_allow_clobber
        call add(other_signs, sign.lnum)
      endif
    endif
  endfor

  call gitgutter#utility#setbufvar(a:bufnr, 'gitgutter_signs', gitgutter_signs)
  if !g:gitgutter_sign_allow_clobber
    call gitgutter#utility#setbufvar(a:bufnr, 'other_signs', other_signs)
  endif
endfunction


" Returns a list of [<id (number)>, ...]
" Sets `s:remove_all_old_signs` as a side-effect.
function! s:obsolete_gitgutter_signs_to_remove(bufnr, new_gitgutter_signs_line_numbers) abort
  let signs_to_remove = []  " list of [<id (number)>, ...]
  let remove_all_signs = 1
  let old_gitgutter_signs = gitgutter#utility#getbufvar(a:bufnr, 'gitgutter_signs')
  for line_number in keys(old_gitgutter_signs)
    if index(a:new_gitgutter_signs_line_numbers, str2nr(line_number)) == -1
      call add(signs_to_remove, old_gitgutter_signs[line_number].id)
    else
      let remove_all_signs = 0
    endif
  endfor
  let s:remove_all_old_signs = remove_all_signs
  return signs_to_remove
endfunction


function! s:remove_signs(bufnr, sign_ids, all_signs) abort
  if a:all_signs && s:supports_star && (g:gitgutter_sign_allow_clobber || empty(gitgutter#utility#getbufvar(a:bufnr, 'other_signs')))
    execute "sign unplace * buffer=" . a:bufnr
  else
    for id in a:sign_ids
      execute "sign unplace" id
    endfor
  endif
endfunction


function! s:upsert_new_gitgutter_signs(bufnr, modified_lines) abort
  if !g:gitgutter_sign_allow_clobber
    let other_signs = gitgutter#utility#getbufvar(a:bufnr, 'other_signs')
  endif
  let old_gitgutter_signs = gitgutter#utility#getbufvar(a:bufnr, 'gitgutter_signs')

  let modified_lines = s:handle_double_hunk(a:modified_lines)

  for line in modified_lines
    let line_number = line[0]  " <number>
    if g:gitgutter_sign_allow_clobber || index(other_signs, line_number) == -1  " don't clobber others' signs
      let name = s:highlight_name_for_change(line[1])
      if !has_key(old_gitgutter_signs, line_number)  " insert
        let id = s:next_sign_id()
        execute "sign place" id "line=" . line_number "name=" . name "buffer=" . a:bufnr
      else  " update if sign has changed
        let old_sign = old_gitgutter_signs[line_number]
        if old_sign.name !=# name
          execute "sign place" old_sign.id "name=" . name "buffer=" . a:bufnr
        end
      endif
    endif
  endfor
  " At this point b:gitgutter_gitgutter_signs is out of date.
endfunction


" Handle special case where the first line is the site of two hunks:
" lines deleted above at the start of the file, and lines deleted
" immediately below.
function! s:handle_double_hunk(modified_lines)
  if a:modified_lines[0:1] == [[1, 'removed_first_line'], [1, 'removed']]
    return [[1, 'removed_above_and_below']] + a:modified_lines[2:]
  endif

  return a:modified_lines
endfunction


function! s:next_sign_id() abort
  let next_id = s:next_sign_id
  let s:next_sign_id += 1
  return next_id
endfunction


" Only for testing.
function! gitgutter#sign#reset()
  let s:next_sign_id  = s:first_sign_id
endfunction


function! s:highlight_name_for_change(text) abort
  if a:text ==# 'added'
    return 'GitGutterLineAdded'
  elseif a:text ==# 'removed'
    return 'GitGutterLineRemoved'
  elseif a:text ==# 'removed_first_line'
    return 'GitGutterLineRemovedFirstLine'
  elseif a:text ==# 'modified'
    return 'GitGutterLineModified'
  elseif a:text ==# 'modified_removed'
    return 'GitGutterLineModifiedRemoved'
  elseif a:text ==# 'removed_above_and_below'
    return 'GitGutterLineRemovedAboveAndBelow'
  endif
endfunction




================================================
FILE: autoload/gitgutter/utility.vim
================================================
function! gitgutter#utility#supports_overscore_sign()
  if gitgutter#utility#windows()
    return &encoding ==? 'utf-8'
  else
    return &termencoding ==? &encoding || &termencoding == ''
  endif
endfunction

" True for git v1.7.2+.
function! gitgutter#utility#git_supports_command_line_config_override() abort
  if !exists('s:c_flag')
    let [_, error_code] = gitgutter#utility#system(gitgutter#git().' -c foo.bar=baz --version')
    let s:c_flag = !error_code
  endif
  return s:c_flag
endfunction

function! gitgutter#utility#setbufvar(buffer, varname, val)
  let buffer = +a:buffer
  " Default value for getbufvar() was introduced in Vim 7.3.831.
  let ggvars = getbufvar(buffer, 'gitgutter')
  if type(ggvars) == type('')
    unlet ggvars
    let ggvars = {}
    call setbufvar(buffer, 'gitgutter', ggvars)
  endif
  let ggvars[a:varname] = a:val
endfunction

function! gitgutter#utility#getbufvar(buffer, varname, ...)
  let buffer = +a:buffer
  let ggvars = getbufvar(buffer, 'gitgutter')
  if type(ggvars) == type({}) && has_key(ggvars, a:varname)
    return ggvars[a:varname]
  endif
  if a:0
    return a:1
  endif
endfunction

function! gitgutter#utility#warn(message) abort
  echohl WarningMsg
  echo a:message
  echohl None
  let v:warningmsg = a:message
endfunction

function! gitgutter#utility#warn_once(bufnr, message, key) abort
  if empty(gitgutter#utility#getbufvar(a:bufnr, a:key))
    call gitgutter#utility#setbufvar(a:bufnr, a:key, '1')
    echohl WarningMsg
    redraw | echom a:message
    echohl None
    let v:warningmsg = a:message
  endif
endfunction

" Returns truthy when the buffer's file should be processed; and falsey when it shouldn't.
" This function does not and should not make any system calls.
function! gitgutter#utility#is_active(bufnr) abort
  return gitgutter#utility#getbufvar(a:bufnr, 'enabled') &&
        \ !pumvisible() &&
        \ s:is_file_buffer(a:bufnr) &&
        \ s:exists_file(a:bufnr) &&
        \ s:not_git_dir(a:bufnr)
endfunction

function! s:not_git_dir(bufnr) abort
  return gitgutter#utility#dir(a:bufnr) !~ '[/\\]\.git\($\|[/\\]\)'
endfunction

function! s:is_file_buffer(bufnr) abort
  return empty(getbufvar(a:bufnr, '&buftype'))
endfunction

" From tpope/vim-fugitive
function! s:winshell()
  return &shell =~? 'cmd' || exists('+shellslash') && !&shellslash
endfunction

" From tpope/vim-fugitive
function! gitgutter#utility#shellescape(arg) abort
  if a:arg =~ '^[A-Za-z0-9_/.-]\+$'
    return a:arg
  elseif s:winshell()
    return '"' . substitute(substitute(a:arg, '"', '""', 'g'), '%', '"%"', 'g') . '"'
  else
    return shellescape(a:arg)
  endif
endfunction

function! gitgutter#utility#file(bufnr)
  return s:abs_path(a:bufnr, 1)
endfunction

" Not shellescaped
function! gitgutter#utility#extension(bufnr) abort
  return fnamemodify(s:abs_path(a:bufnr, 0), ':e')
endfunction

function! gitgutter#utility#system(cmd, ...) abort
  call gitgutter#debug#log(a:cmd, a:000)

  call s:use_known_shell()
  let prev_error_code = v:shell_error
  silent let output = (a:0 == 0) ? system(a:cmd) : system(a:cmd, a:1)
  let error_code = v:shell_error
  silent call system('exit ' . prev_error_code)
  call s:restore_shell()

  return [output, error_code]
endfunction

function! gitgutter#utility#has_repo_path(bufnr)
  return index(['', -1, -2], gitgutter#utility#repo_path(a:bufnr, 0)) == -1
endfunction

" Path of file relative to repo root.
"
" *     empty string - not set
" * non-empty string - path
" *               -1 - pending
" *               -2 - not tracked by git
" *               -3 - assume unchanged
function! gitgutter#utility#repo_path(bufnr, shellesc) abort
  let p = gitgutter#utility#getbufvar(a:bufnr, 'path', '')
  return a:shellesc ? gitgutter#utility#shellescape(p) : p
endfunction


let s:set_path_handler = {}

function! s:set_path_handler.out(buffer, listing) abort
  let listing = s:strip_trailing_new_line(a:listing)
  let [status, path] = [listing[0], listing[2:]]
  if status =~# '[a-z]'
    call gitgutter#utility#setbufvar(a:buffer, 'path', -3)
  else
    call gitgutter#utility#setbufvar(a:buffer, 'path', path)
  endif

  if type(self.continuation) == type(function('tr'))
    call self.continuation()
  else
    call call(self.continuation.function, self.continuation.arguments)
  endif
endfunction

function! s:set_path_handler.err(buffer) abort
  call gitgutter#utility#setbufvar(a:buffer, 'path', -2)
endfunction


" continuation - a funcref or hash to call after setting the repo path asynchronously.
"
" Returns 'async' if the the path is set asynchronously, 0 otherwise.
function! gitgutter#utility#set_repo_path(bufnr, continuation) abort
  " Values of path:
  " * non-empty string - path
  " *               -1 - pending
  " *               -2 - not tracked by git
  " *               -3 - assume unchanged

  call gitgutter#utility#setbufvar(a:bufnr, 'path', -1)
  let cmd = gitgutter#git(a:bufnr).' ls-files -v --error-unmatch --full-name -z -- '.
        \ gitgutter#utility#shellescape(gitgutter#utility#filename(a:bufnr))

  if g:gitgutter_async && gitgutter#async#available() && !has('vim_starting')
    let handler = copy(s:set_path_handler)
    let handler.continuation = a:continuation
    call gitgutter#async#execute(cmd, a:bufnr, handler)
    return 'async'
  endif

  let [listing, error_code] = gitgutter#utility#system(cmd)

  if error_code
    call gitgutter#utility#setbufvar(a:bufnr, 'path', -2)
    return
  endif

  let listing = s:strip_trailing_new_line(listing)
  let [status, path] = [listing[0], listing[2:]]
  if status =~# '[a-z]'
    call gitgutter#utility#setbufvar(a:bufnr, 'path', -3)
  else
    call gitgutter#utility#setbufvar(a:bufnr, 'path', path)
  endif
endfunction


function! gitgutter#utility#clean_smudge_filter_applies(bufnr)
  let filtered = gitgutter#utility#getbufvar(a:bufnr, 'filter', -1)
  if filtered == -1
    let cmd = gitgutter#git(a:bufnr).' check-attr filter -- '.
          \ gitgutter#utility#shellescape(gitgutter#utility#filename(a:bufnr))
    let [out, _] = gitgutter#utility#system(cmd)
    let filtered = out !~ 'unspecified'
    call gitgutter#utility#setbufvar(a:bufnr, 'filter', filtered)
  endif
  return filtered
endfunction


function! s:use_known_shell() abort
  if has('unix') && &shell !=# 'sh'
    let [s:shell, s:shellcmdflag, s:shellredir, s:shellpipe, s:shellquote, s:shellxquote] = [&shell, &shellcmdflag, &shellredir, &shellpipe, &shellquote, &shellxquote]
    let &shell = 'sh'
    set shellcmdflag=-c shellredir=>%s\ 2>&1
  endif
  if has('win32') && (&shell =~# 'pwsh' || &shell =~# 'powershell')
    let [s:shell, s:shellcmdflag, s:shellredir, s:shellpipe, s:shellquote, s:shellxquote] = [&shell, &shellcmdflag, &shellredir, &shellpipe, &shellquote, &shellxquote]
    let &shell = 'cmd.exe'
    set shellcmdflag=/s\ /c shellredir=>%s\ 2>&1 shellpipe=>%s\ 2>&1 shellquote= shellxquote="
  endif
endfunction

function! s:restore_shell() abort
  if (has('unix') || has('win32')) && exists('s:shell')
    let [&shell, &shellcmdflag, &shellredir, &shellpipe, &shellquote, &shellxquote] = [s:shell, s:shellcmdflag, s:shellredir, s:shellpipe, s:shellquote, s:shellxquote]
  endif
endfunction

function! gitgutter#utility#get_diff_base(bufnr)
  let p = resolve(expand('#'.a:bufnr.':p'))
  let ml = matchlist(p, '\v^fugitive:/.*/(\x{40,})/')
  if !empty(ml) && !empty(ml[1])
    return ml[1].'^'
  endif
  return g:gitgutter_diff_base
endfunction

" Returns the original path (shellescaped) at the buffer's diff base.
function! gitgutter#utility#base_path(bufnr)
  let diffbase = gitgutter#utility#get_diff_base(a:bufnr)

  " If we already know the original path at this diff base, return it.
  let basepath = gitgutter#utility#getbufvar(a:bufnr, 'basepath', '')
  if !empty(basepath)
    " basepath is diffbase:path
    " Note that path can also contain colons.
    " List destructuring / unpacking where the remaining items are assigned
    " to a single variable (:help let-unpack) is only available in v8.2.0540.
    let parts = split(basepath, ':', 1)
    let base = parts[0]
    let bpath = join(parts[1:], ':')

    if base == diffbase
      return gitgutter#utility#shellescape(bpath)
    endif
  endif

  " Obtain buffers' paths.
  let current_paths = {}
  for bufnr in range(1, bufnr('$') + 1)
    if gitgutter#utility#has_repo_path(bufnr)
      let current_paths[gitgutter#utility#repo_path(bufnr, 0)] = bufnr
    endif
  endfor

  " Get a list of file renames at the buffer's diff base.
  " Store the original paths on any corresponding buffers.
  " If the buffer's file was one of them, return its original path.
  let op = ''
  let renames = s:obtain_file_renames(a:bufnr, diffbase)
  for [current, original] in items(renames)
    if has_key(current_paths, current)
      let bufnr = current_paths[current]
      let basepath = diffbase.':'.original
      call gitgutter#utility#setbufvar(bufnr, 'basepath', basepath)

      if bufnr == a:bufnr
        let op = original
      endif
    endif
  endfor
  if !empty(op)
    return gitgutter#utility#shellescape(op)
  endif

  " Buffer's file was not renamed, so store current path and return it.
  let current_path = gitgutter#utility#repo_path(a:bufnr, 0)
  let basepath = diffbase.':'.current_path
  call gitgutter#utility#setbufvar(a:bufnr, 'basepath', basepath)
  return gitgutter#utility#shellescape(current_path)
endfunction

" Returns a dict of current path to original path at the given base.
function! s:obtain_file_renames(bufnr, base)
  let renames = {}
  let cmd = gitgutter#git(a:bufnr)
  if gitgutter#utility#git_supports_command_line_config_override()
    let cmd .= ' -c "core.safecrlf=false"'
  endif
  let cmd .= ' diff --diff-filter=R --name-status '.a:base
  let [out, error_code] = gitgutter#utility#system(cmd)
  if error_code
    " Assume the problem is the diff base.
    call gitgutter#utility#warn('g:gitgutter_diff_base ('.a:base.') is invalid')
    return {}
  endif
  for line in split(out, '\n')
    let fields = split(line)
    if len(fields) != 3
      call gitgutter#utility#warn('gitgutter: unable to list renamed files: '.line)
      return {}
    endif
    let [original, current] = fields[1:]
    let renames[current] = original
  endfor
  return renames
endfunction

function! s:abs_path(bufnr, shellesc)
  let p = expand('#'.a:bufnr.':p')
  if p =~ '\v^fugitive:/.*/(\x{40,})/'
    let p = FugitiveReal(expand('#'.a:bufnr.':p'))
  else
    let p = resolve(p)
  endif
  return a:shellesc ? gitgutter#utility#shellescape(p) : p
endfunction

" Shellescaped
function! gitgutter#utility#dir(bufnr) abort
  return gitgutter#utility#shellescape(fnamemodify(s:abs_path(a:bufnr, 0), ':h'))
endfunction

" Not shellescaped.
function! gitgutter#utility#filename(bufnr) abort
  return fnamemodify(s:abs_path(a:bufnr, 0), ':t')
endfunction

function! s:exists_file(bufnr) abort
  return filereadable(s:abs_path(a:bufnr, 0))
endfunction

" Get rid of any trailing new line or SOH character.
"
" git ls-files -z produces output with null line termination.
" Vim's system() replaces any null characters in the output
" with SOH (start of header), i.e. ^A.
function! s:strip_trailing_new_line(line) abort
  return substitute(a:line, '[[:cntrl:]]$', '', '')
endfunction

function! gitgutter#utility#windows()
  return has('win64') || has('win32') || has('win16')
endfunction


================================================
FILE: autoload/gitgutter.vim
================================================
" Primary functions {{{

function! gitgutter#all(force) abort
  let visible = tabpagebuflist()

  for bufnr in range(1, bufnr('$') + 1)
    if buflisted(bufnr)
      let file = expand('#'.bufnr.':p')
      if !empty(file)
        if index(visible, bufnr) != -1
          call gitgutter#process_buffer(bufnr, a:force)
        elseif a:force
          call s:reset_tick(bufnr)
        endif
      endif
    endif
  endfor
endfunction


function! gitgutter#process_buffer(bufnr, force) abort
  " NOTE a:bufnr is not necessarily the current buffer.

  if gitgutter#utility#getbufvar(a:bufnr, 'enabled', -1) == -1
    call gitgutter#utility#setbufvar(a:bufnr, 'enabled', g:gitgutter_enabled)
  endif

  if gitgutter#utility#is_active(a:bufnr)

    if has('patch-7.4.1559')
      let l:Callback = function('gitgutter#process_buffer', [a:bufnr, a:force])
    else
      let l:Callback = {'function': 'gitgutter#process_buffer', 'arguments': [a:bufnr, a:force]}
    endif
    let how = s:setup_path(a:bufnr, l:Callback)
    if [how] == ['async']  " avoid string-to-number conversion if how is a number
      return
    endif

    if a:force || s:has_fresh_changes(a:bufnr)

      let diff = 'NOT SET'
      try
        let diff = gitgutter#diff#run_diff(a:bufnr, g:gitgutter_diff_relative_to)
      catch /gitgutter not tracked/
        call gitgutter#debug#log('Not tracked: '.gitgutter#utility#file(a:bufnr))
      catch /gitgutter assume unchanged/
        call gitgutter#debug#log('Assume unchanged: '.gitgutter#utility#file(a:bufnr))
      catch /gitgutter diff failed/
        call gitgutter#debug#log('Diff failed: '.gitgutter#utility#file(a:bufnr))
        call gitgutter#hunk#reset(a:bufnr)
      endtry

      if diff != 'async' && diff != 'NOT SET'
        call gitgutter#diff#handler(a:bufnr, diff)
      endif

    endif
  endif
endfunction


function! gitgutter#disable() abort
  call s:toggle_each_buffer(0)
  let g:gitgutter_enabled = 0
endfunction

function! gitgutter#enable() abort
  call s:toggle_each_buffer(1)
  let g:gitgutter_enabled = 1
endfunction

function s:toggle_each_buffer(enable)
  for bufnr in range(1, bufnr('$') + 1)
    if buflisted(bufnr)
      let file = expand('#'.bufnr.':p')
      if !empty(file)
        if a:enable
          call gitgutter#buffer_enable(bufnr)
        else
          call gitgutter#buffer_disable(bufnr)
        end
      endif
    endif
  endfor
endfunction

function! gitgutter#toggle() abort
  if g:gitgutter_enabled
    call gitgutter#disable()
  else
    call gitgutter#enable()
  endif
endfunction


function! gitgutter#buffer_disable(...) abort
  let bufnr = a:0 ? a:1 : bufnr('')
  call gitgutter#utility#setbufvar(bufnr, 'enabled', 0)
  call s:clear(bufnr)
endfunction

function! gitgutter#buffer_enable(...) abort
  let bufnr = a:0 ? a:1 : bufnr('')
  call gitgutter#utility#setbufvar(bufnr, 'enabled', 1)
  call gitgutter#process_buffer(bufnr, 1)
endfunction

function! gitgutter#buffer_toggle(...) abort
  let bufnr = a:0 ? a:1 : bufnr('')
  if gitgutter#utility#getbufvar(bufnr, 'enabled', 1)
    call gitgutter#buffer_disable(bufnr)
  else
    call gitgutter#buffer_enable(bufnr)
  endif
endfunction

" }}}


" Optional argument is buffer number
function! gitgutter#git(...)
  let git = g:gitgutter_git_executable
  if a:0
    let git .= ' -C '.gitgutter#utility#dir(a:1)
  endif
  if empty(g:gitgutter_git_args)
    return git
  else
    return git.' '.g:gitgutter_git_args
  endif
endfunction


function! gitgutter#setup_maps()
  if !g:gitgutter_map_keys
    return
  endif

  " Note hasmapto() and maparg() operate on the current buffer.

  let bufnr = bufnr('')

  if gitgutter#utility#getbufvar(bufnr, 'mapped', 0)
    return
  endif

  if !hasmapto('<Plug>(GitGutterPrevHunk)') && maparg('[c', 'n') ==# ''
    nmap <buffer> [c <Plug>(GitGutterPrevHunk)
  endif
  if !hasmapto('<Plug>(GitGutterNextHunk)') && maparg(']c', 'n') ==# ''
    nmap <buffer> ]c <Plug>(GitGutterNextHunk)
  endif

  if !hasmapto('<Plug>(GitGutterStageHunk)', 'v') && maparg('<Leader>hs', 'x') ==# ''
    xmap <buffer> <Leader>hs <Plug>(GitGutterStageHunk)
  endif
  if !hasmapto('<Plug>(GitGutterStageHunk)', 'n') && maparg('<Leader>hs', 'n') ==# ''
    nmap <buffer> <Leader>hs <Plug>(GitGutterStageHunk)
  endif
  if !hasmapto('<Plug>(GitGutterUndoHunk)') && maparg('<Leader>hu', 'n') ==# ''
    nmap <buffer> <Leader>hu <Plug>(GitGutterUndoHunk)
  endif
  if !hasmapto('<Plug>(GitGutterPreviewHunk)') && maparg('<Leader>hp', 'n') ==# ''
    nmap <buffer> <Leader>hp <Plug>(GitGutterPreviewHunk)
  endif

  if !hasmapto('<Plug>(GitGutterTextObjectInnerPending)') && maparg('ic', 'o') ==# ''
    omap <buffer> ic <Plug>(GitGutterTextObjectInnerPending)
  endif
  if !hasmapto('<Plug>(GitGutterTextObjectOuterPending)') && maparg('ac', 'o') ==# ''
    omap <buffer> ac <Plug>(GitGutterTextObjectOuterPending)
  endif
  if !hasmapto('<Plug>(GitGutterTextObjectInnerVisual)') && maparg('ic', 'x') ==# ''
    xmap <buffer> ic <Plug>(GitGutterTextObjectInnerVisual)
  endif
  if !hasmapto('<Plug>(GitGutterTextObjectOuterVisual)') && maparg('ac', 'x') ==# ''
    xmap <buffer> ac <Plug>(GitGutterTextObjectOuterVisual)
  endif

  call gitgutter#utility#setbufvar(bufnr, 'mapped', 1)
endfunction

function! s:setup_path(bufnr, continuation)
  if gitgutter#utility#has_repo_path(a:bufnr) | return | endif

  return gitgutter#utility#set_repo_path(a:bufnr, a:continuation)
endfunction

function! s:has_fresh_changes(bufnr) abort
  return getbufvar(a:bufnr, 'changedtick') != gitgutter#utility#getbufvar(a:bufnr, 'tick')
endfunction

function! s:reset_tick(bufnr) abort
  call gitgutter#utility#setbufvar(a:bufnr, 'tick', 0)
endfunction

function! s:clear(bufnr)
  call gitgutter#sign#clear_signs(a:bufnr)
  call gitgutter#hunk#reset(a:bufnr)
  call s:reset_tick(a:bufnr)
  call gitgutter#utility#setbufvar(a:bufnr, 'path', '')
  call gitgutter#utility#setbufvar(a:bufnr, 'basepath', '')
endfunction


" Note:
" - this runs synchronously
" - it ignores unsaved changes in buffers
" - it does not change to the repo root
function! gitgutter#quickfix(current_file)
  let cmd = gitgutter#git().' rev-parse --show-cdup'
  let path_to_repo = get(systemlist(cmd), 0, '')
  if !empty(path_to_repo) && path_to_repo[-1:] != '/'
    let path_to_repo .= '/'
  endif

  let locations = []
  let cmd = gitgutter#git().' --no-pager'.
        \ ' diff --no-ext-diff --no-color -U0'.
        \ ' --src-prefix=a/'.path_to_repo.' --dst-prefix=b/'.path_to_repo.' '.
        \ g:gitgutter_diff_args. ' '. g:gitgutter_diff_base
  if a:current_file
    let cmd = cmd.' -- '.expand('%:p')
  endif
  let diff = systemlist(cmd)
  let lnum = 0
  for line in diff
    if line =~ '^diff --git [^"]'
      " No quotation mark therefore no spaces in filenames
      let [fnamel, fnamer] = split(line)[2:3]
      let fname = fnamel ==# fnamer ? fnamer : fnamer[2:]
    elseif line =~ '^diff --git "'
      " Quotation mark therefore do not split on space
      let [_, fnamel, _, fnamer] = split(line, '"')
      let fname = fnamel ==# fnamer ? fnamer : fnamer[2:]
    elseif line =~ '^diff --cc [^"]'
      let fname = line[10:]
    elseif line =~ '^diff --cc "'
      let [_, fname] = split(line, '"')
    elseif line =~ '^@@'
      let lnum = matchlist(line, '+\(\d\+\)')[1]
    elseif lnum > 0
      call add(locations, {'filename': fname, 'lnum': lnum, 'text': line})
      let lnum = 0
    endif
  endfor
  if !g:gitgutter_use_location_list
    call setqflist(locations)
  else
    call setloclist(0, locations)
  endif
endfunction


function! gitgutter#difforig()
  let bufnr = bufnr('')
  let filetype = &filetype

  vertical new
  set buftype=nofile
  if v:version >= 800
     setlocal bufhidden=wipe
  endif
  setlocal noswapfile
  let &filetype = filetype

  if g:gitgutter_diff_relative_to ==# 'index'
    let index_name = gitgutter#utility#get_diff_base(bufnr).':'.gitgutter#utility#base_path(bufnr)
    let cmd = gitgutter#git(bufnr).' --no-pager show '.index_name
    " NOTE: this uses &shell to execute cmd.  Perhaps we should use instead
    " gitgutter#utility's use_known_shell() / restore_shell() functions.
    silent! execute "read ++edit !" cmd
  else
    silent! execute "read ++edit" gitgutter#utility#repo_path(bufnr, 1)
  endif

  0d_
  diffthis
  setlocal nomodifiable
  wincmd p
  diffthis
endfunction


================================================
FILE: doc/gitgutter.txt
================================================
*gitgutter.txt*              A Vim plugin which shows a git diff in the gutter.


                           Vim GitGutter


Author:            Andy Stewart <https://airbladesoftware.com/>
Plugin Homepage:   <https://github.com/airblade/vim-gitgutter>


===============================================================================
CONTENTS                                                            *gitgutter*

  Introduction ................. |gitgutter-introduction|
  Installation ................. |gitgutter-installation|
  Windows      ................. |gitgutter-windows|
  Commands ..................... |gitgutter-commands|
  Mappings ..................... |gitgutter-mappings|
  Autocommand .................. |gitgutter-autocommand|
  Status line .................. |gitgutter-statusline|
  Options ...................... |gitgutter-options|
  Highlights ................... |gitgutter-highlights|
  FAQ .......................... |gitgutter-faq|
  TROUBLESHOOTING .............. |gitgutter-troubleshooting|


===============================================================================
INTRODUCTION                                           *gitgutter-introduction*

GitGutter is 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.  The plugin also provides a hunk
text object.

The signs are always up to date and the plugin never saves your buffer.

The name "gitgutter" comes from the Sublime Text 3 plugin which inspired this
one in 2013.


===============================================================================
INSTALLATION                                           *gitgutter-installation*

First, use your favourite package manager, or use Vim's built-in package
support.

Vim:~
>
  mkdir -p ~/.vim/pack/airblade/start
  cd ~/.vim/pack/airblade/start
  git clone https://github.com/airblade/vim-gitgutter.git
  vim -u NONE -c "helptags vim-gitgutter/doc" -c q
<

Neovim:~
>
  mkdir -p ~/.config/nvim/pack/airblade/start
  cd ~/.config/nvim/pack/airblade/start
  git clone https://github.com/airblade/vim-gitgutter.git
  nvim -u NONE -c "helptags vim-gitgutter/doc" -c q
<

Second, ensure your 'updatetime' and 'signcolumn' options are set appropriately.

When you make a change to a file tracked by git, the diff markers should
appear automatically after a short delay.  The delay is governed by vim's
'updatetime' option; the default value is `4000`, i.e. 4 seconds, but I
suggest reducing it to around 100ms (add `set updatetime=100` to your vimrc).
Note 'updatetime' also controls the delay before vim writes its swap file.

The 'signcolumn' option can have any value except "off".


===============================================================================
WINDOWS                                                     *gitgutter-windows*

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.

You can avoid this risk by configuring the full path to your git executable.
For example:
>
    " This path probably won't work
    let g:gitgutter_git_executable = 'C:\Program Files\Git\bin\git.exe'
<

Unfortunately I don't know the correct escaping for the path - if you do,
please let me know!


===============================================================================
COMMANDS                                                   *gitgutter-commands*

Commands for turning vim-gitgutter on and off:~

                                                  *gitgutter-:GitGutterDisable*
:GitGutterDisable       Turn vim-gitgutter off for all buffers.

                                                   *gitgutter-:GitGutterEnable*
:GitGutterEnable        Turn vim-gitgutter on for all buffers.

                                                   *gitgutter-:GitGutterToggle*
:GitGutterToggle        Toggle vim-gitgutter on or off for all buffers.

                                            *gitgutter-:GitGutterBufferDisable*
:GitGutterBufferDisable Turn vim-gitgutter off for current buffer.

                                             *gitgutter-:GitGutterBufferEnable*
:GitGutterBufferEnable  Turn vim-gitgutter on for current buffer.

                                             *gitgutter-:GitGutterBufferToggle*
:GitGutterBufferToggle  Toggle vim-gitgutter on or off for current buffer.

                                                         *gitgutter-:GitGutter*
:GitGutter              Update signs for the current buffer.  You shouldn't
                        need to run this.

                                                      *gitgutter-:GitGutterAll*
:GitGutterAll           Update signs for all buffers.  You shouldn't need to
                        run this.


Commands for turning signs on and off (defaults to on):~

                                              *gitgutter-:GitGutterSignsEnable*
:GitGutterSignsEnable   Show signs for the diff.

                                             *gitgutter-:GitGutterSignsDisable*
:GitGutterSignsDisable  Do not show signs for the diff.

                                              *gitgutter-:GitGutterSignsToggle*
:GitGutterSignsToggle   Toggle signs on or off.


Commands for turning line highlighting on and off (defaults to off):~

                                     *gitgutter-:GitGutterLineHighlightsEnable*
:GitGutterLineHighlightsEnable  Turn on line highlighting.

                                    *gitgutter-:GitGutterLineHighlightsDisable*
:GitGutterLineHighlightsDisable Turn off line highlighting.

                                     *gitgutter-:GitGutterLineHighlightsToggle*
:GitGutterLineHighlightsToggle  Turn line highlighting on or off.


Commands for turning line number highlighting on and off (defaults to off):~
NOTE: This feature requires Neovim 0.3.2 or higher.

                                   *gitgutter-:GitGutterLineNrHighlightsEnable*
:GitGutterLineNrHighlightsEnable  Turn on line highlighting.

                                  *gitgutter-:GitGutterLineNrHighlightsDisable*
:GitGutterLineNrHighlightsDisable Turn off line highlighting.

                                   *gitgutter-:GitGutterLineNrHighlightsToggle*
:GitGutterLineNrHighlightsToggle  Turn line highlighting on or off.


Commands for jumping between hunks:~

                                                 *gitgutter-:GitGutterNextHunk*
:GitGutterNextHunk      Jump to the next [count] hunk.

                                                 *gitgutter-:GitGutterPrevHunk*
:GitGutterPrevHunk      Jump to the previous [count] hunk.

                                                 *gitgutter-:GitGutterQuickFix*
:GitGutterQuickFix      Load all hunks into the |quickfix| list.  Note this
                        ignores any unsaved changes in your buffers. The
                        |g:gitgutter_use_location_list| option can be set to
                        populate the location list of the current window
                        instead.  Use |:copen| (or |:lopen|) to open a buffer
                        containing the search results in linked form; or add a
                        custom command like this:
>
                          command! Gqf GitGutterQuickFix | copen
<
                                                 *gitgutter-:GitGutterQuickFixCurrentFile*
:GitGutterQuickFixCurrentFile     Same as :GitGutterQuickFix, but only load hunks for
                                  the file in the focused buffer. This has the same
                                  functionality as :GitGutterQuickFix when the focused
                                  buffer is empty.


Commands for operating on a hunk:~

                                                *gitgutter-:GitGutterStageHunk*
:GitGutterStageHunk     Stage the hunk the cursor is in.  Use a visual selection
                        to stage part of an (additions-only) hunk; or use a
                        range.

                        To stage part of any hunk, first |GitGutterPreviewHunk|
                        it, then move to the preview window, delete the lines
                        you do not want to stage, and |write| or
                        |GitGutterStageHunk|.

                                                 *gitgutter-:GitGutterUndoHunk*
:GitGutterUndoHunk      Undo the hunk the cursor is in.

                                              *gitgutter-:GitGutterPreviewHunk*
:GitGutterPreviewHunk   Preview the hunk the cursor is in or, if you are using
                        floating preview windows in Neovim and the window is
                        already open, move the cursor into the window.

                        To stage part of the hunk, move to the preview window,
                        delete any lines you do not want to stage, and |write|
                        or |GitGutterStageHunk|.

                        To close a non-floating preview window use |:pclose|
                        or |CTRL-W_z| or |CTRL-W_CTRL-Z|; or normal window-
                        closing (|:quit| or |:close| or |CTRL-W_c|) if your cursor
                        is in the preview window.

                        To close a floating window when the cursor is in the
                        original buffer, move the cursor.

                        To close a floating window when the cursor is in the
                        floating window use normal window-closing, or move to
                        the original window with |CTRL-W_p|.  Alternatively set
                        |g:gitgutter_close_preview_on_escape| and use <Esc>.

                        Two functions are available for your own logic:
>
                          gitgutter#hunk#is_preview_window_open()
                          gitgutter#hunk#close_hunk_preview_window()
<

Commands for folds:~

                                                     *gitgutter-:GitGutterFold*
:GitGutterFold          Fold all unchanged lines.  Execute again to undo.


Other commands:~

                                                 *gitgutter-:GitGutterDiffOrig*
:GitGutterDiffOrig      Similar to |:DiffOrig| but shows gitgutter's diff.


===============================================================================
AUTOCOMMANDS                                           *gitgutter-autocommands*

User GitGutter~

After updating a buffer's signs vim-gitgutter fires a |User| |autocmd| with the
event GitGutter.  You can listen for this event, for example:
>
  autocmd User GitGutter call updateMyStatusLine()
<
A dictionary `g:gitgutter_hook_context` is made available during its execution,
which contains an entry `bufnr` that contains the buffer number being updated.

User GitGutterStage~

After staging a hunk or part of a hunk vim-gitgutter fires a |User| |autocmd|
with the event GitGutterStage.  Staging always happens in the current buffer.

===============================================================================
MAPPINGS                                                   *gitgutter-mappings*

You can disable all these mappings with:
>
    let g:gitgutter_map_keys = 0
<

Hunk operations:~

These can be repeated with `.` if you have vim-repeat installed.

                                                         *gitgutter-<Leader>hp*
<Leader>hp              Preview the hunk under the cursor.

                                                         *gitgutter-<Leader>hs*
<Leader>hs              Stage the hunk under the cursor.

                                                         *gitgutter-<Leader>hu*
<Leader>hu              Undo the hunk under the cursor.

You can change these mappings like this:
>
    nmap ghp <Plug>(GitGutterPreviewHunk)
    nmap ghs <Plug>(GitGutterStageHunk)
    nmap ghu <Plug>(GitGutterUndoHunk)
<

Hunk jumping:~

                                                                 *gitgutter-]c*
]c                      Jump to the next [count] hunk.

                                                                 *gitgutter-[c*
[c                      Jump to the previous [count] hunk.

You can change these mappings like this:
>
    nmap [c <Plug>(GitGutterPrevHunk)
    nmap ]c <Plug>(GitGutterNextHunk)
<

Hunk text object:~

                          *gitgutter-ic* *gitgutter-ac* *gitgutter-text-object*
"ic" operates on the current hunk's lines.  "ac" does the same but also includes
trailing empty lines.
>
    omap ic <Plug>(GitGutterTextObjectInnerPending)
    omap ac <Plug>(GitGutterTextObjectOuterPending)
    xmap ic <Plug>(GitGutterTextObjectInnerVisual)
    xmap ac <Plug>(GitGutterTextObjectOuterVisual)
<


===============================================================================
STATUS LINE                                              *gitgutter-statusline*


Call the `GitGutterGetHunkSummary()` function from your status line to get a
list of counts of added, modified, and removed lines in the current buffer.
For example:
>
    " Your vimrc
    function! GitStatus()
      let [a,m,r] = GitGutterGetHunkSummary()
      return printf('+%d ~%d -%d', a, m, r)
    endfunction
    set statusline+=%{GitStatus()}
<


===============================================================================
OPTIONS                                                     *gitgutter-options*

The most important option is 'updatetime' which determines how long (in
milliseconds) the plugin will wait after you stop typing before it updates the
signs.  Vim's default is 4000.  I recommend 100.  Note this also controls how
long vim waits before writing its swap file.

Most important option:~

    'updatetime'

Git:~

    |g:gitgutter_git_executable|
    |g:gitgutter_git_args|
    |g:gitgutter_diff_args|
    |g:gitgutter_diff_relative_to|
    |g:gitgutter_diff_base|

Signs:~

    |g:gitgutter_signs|
    |g:gitgutter_highlight_lines|
    |g:gitgutter_highlight_linenrs|
    |g:gitgutter_max_signs|
    |g:gitgutter_sign_priority|
    |g:gitgutter_sign_allow_clobber|
    |g:gitgutter_sign_added|
    |g:gitgutter_sign_modified|
    |g:gitgutter_sign_removed|
    |g:gitgutter_sign_removed_first_line|
    |g:gitgutter_sign_modified_removed|
    |g:gitgutter_set_sign_backgrounds|

Hunk jumping:~

    |g:gitgutter_show_msg_on_hunk_jumping|

Hunk previews:~

    |g:gitgutter_preview_win_floating|
    |g:gitgutter_floating_window_options|
    |g:gitgutter_close_preview_on_escape|

Terminal:~

    |g:gitgutter_terminal_reports_focus|

General:~

    |g:gitgutter_enabled|
    |g:gitgutter_map_keys|
    |g:gitgutter_async|
    |g:gitgutter_log|
    |g:gitgutter_use_location_list|


                                             *g:gitgutter_preview_win_location*
Default: 'bo'

This option determines where the preview window pops up as a result of the
:GitGutterPreviewHunk command. Other plausible values are 'to', 'bel', 'abo'.
See the end of the |opening-window| docs.

                                                   *g:gitgutter_git_executable*
Default: 'git'

This option determines what git binary to use.  Set this if git is not on your
path.

                                                         *g:gitgutter_git_args*
Default: empty

Use this option to pass any extra arguments to git when running git-diff.
For example:
>
    let g:gitgutter_git_args = '--git-dir=""'
<

                                                        *g:gitgutter_diff_args*
Default: empty

Use this option to pass any extra arguments to git-diff.  For example:
>
    let g:gitgutter_diff_args = '-w'
<

                                                 *g:gitgutter_diff_relative_to*
Default: empty

By default buffers are diffed against the index.  Use this option to diff against
the working tree.  For example:
>
    let g:gitgutter_diff_relative_to = 'working_tree'
<

                                                        *g:gitgutter_diff_base*
Default: empty

By default buffers are diffed against the index.  Use this option to diff against
a revision instead.  For example:
>
    let g:gitgutter_diff_base = '<some commit SHA>'
<

If you are looking at a previous version of a file with Fugitive (e.g.
via :0Gclog), gitgutter sets the diff base to the parent of the current revision.

This setting is ignore when the diff is relative to the working tree
(|g:gitgutter_diff_relative_to|).

                                                            *g:gitgutter_signs*
Default: 1

Determines whether or not to show signs.

                                                  *g:gitgutter_highlight_lines*
Default: 0

Determines whether or not to show line highlights.

                                                *g:gitgutter_highlight_linenrs*
Default: 0

Determines whether or not to show line number highlights.

                                                        *g:gitgutter_max_signs*
Default: 500 (Vim < 8.1.0614, Neovim < 0.4.0)
          -1 (otherwise)

Sets the maximum number of signs to show in a buffer.  Vim is slow at updating
signs, so to avoid slowing down the GUI the number of signs is capped.  When
the number of changed lines exceeds this value, the plugin removes all signs
and displays a warning message.

When set to -1 the limit is not applied.

                                                   *g:gitgutter_sign_priority*
Default: 10

Sets the |sign-priority| gitgutter assigns to its signs.

                                               *g:gitgutter_sign_allow_clobber*
Default: 0 (Vim < 8.1.0614, Neovim < 0.4.0)
         1 (otherwise)

Determines whether gitgutter preserves non-gitgutter signs. When 1, gitgutter
will not preserve non-gitgutter signs.

                                          *g:gitgutter_sign_added*
                                          *g:gitgutter_sign_modified*
                                          *g:gitgutter_sign_removed*
                                          *g:gitgutter_sign_removed_first_line*
                                          *g:gitgutter_sign_removed_above_and_below*
                                          *g:gitgutter_sign_modified_removed*
Defaults:
>
    let g:gitgutter_sign_added              = '+'
    let g:gitgutter_sign_modified           = '~'
    let g:gitgutter_sign_removed            = '_'
    let g:gitgutter_sign_removed_first_line = '‾'
    let g:gitgutter_sign_removed_above_and_below = '_¯'
    let g:gitgutter_sign_modified_removed   = '~_'
<
You can use unicode characters but not images.  Signs must not take up more than
2 columns.

                                              *g:gitgutter_set_sign_backgrounds*
Default: 0

Only applies to existing GitGutter* highlight groups.  See
|gitgutter-highlights|.

Controls whether to override the signs' background colours to match the
|hl-SignColumn|.

                                             *g:gitgutter_preview_win_floating*
Default: 0 (Vim)
         0 (NeoVim which does not support floating windows)
         1 (NeoVim which does support floating windows)

Whether to use floating/popup windows for hunk previews.  Note that if you use
popup windows on Vim you will not be able to stage partial hunks via the
preview window.

                                          *g:gitgutter_floating_window_options*
Default:
>
    " Vim
    {
        \ 'line': 'cursor+1',
        \ 'col': 'cursor',
        \ 'moved': 'any'
    }

    " Neovim
    {
        \ 'relative': 'cursor',
        \ 'row': 1,
        \ 'col': 0,
        \ 'width': 42,
        \ 'height': &previewheight,
        \ 'style': 'minimal'
    }
<
This dictionary is passed directly to |popup_create()| (Vim) or
|nvim_open_win()| (Neovim).

If you simply want to override one or two of the default values, create a file
in an after/ directory.  For example:
>
    " ~/.vim/after/vim-gitgutter/overrides.vim
    let g:gitgutter_floating_window_options['border'] = 'single'
<

                                          *g:gitgutter_close_preview_on_escape*
Default: 0

Whether pressing <Esc> in a preview window closes it.

                                           *g:gitgutter_terminal_reports_focus*
Default: 1

Normally the plugin uses |FocusGained| to force-update all buffers when Vim
receives focus.  However some terminals do not report focus events and so the
|FocusGained| autocommand never fires.

If this applies to you, either install something like Terminus
(https://github.com/wincent/terminus) to make |FocusGained| work or set this
option to 0.

If you use tmux, try this in your tmux.conf:
>
    set -g focus-events on
<

When this option is 0, the plugin force-updates the buffer on |BufEnter|
(instead of only updating if the buffer's contents has changed since the last
update).

                                                          *g:gitgutter_enabled*
Default: 1

Controls whether or not the plugin is on at startup.

                                                         *g:gitgutter_map_keys*
Default: 1

Controls whether or not the plugin provides mappings.  See |gitgutter-mappings|.

                                                            *g:gitgutter_async*
Default: 1

Controls whether or not diffs are run in the background.  This has no effect if
your Vim does not support background jobs.

                                                              *g:gitgutter_log*
Default: 0

When switched on, the plugin logs to gitgutter.log in the directory where it is
installed.  Additionally it logs channel activity to channel.log.

                                                *g:gitgutter_use_location_list*
Default: 0

When switched on, the :GitGutterQuickFix command populates the location list
of the current window instead of the global quickfix list.

                                         *g:gitgutter_show_msg_on_hunk_jumping*
Default: 1

When switched on, a message like "Hunk 4 of 11" is shown on hunk jumping.


===============================================================================
HIGHLIGHTS                                               *gitgutter-highlights*

To change the signs' colours, specify these highlight groups in your |vimrc|:
>
    highlight GitGutterAdd    guifg=#009900 ctermfg=2
    highlight GitGutterChange guifg=#bbbb00 ctermfg=3
    highlight GitGutterDelete guifg=#ff2222 ctermfg=1
<

See |highlight-guifg| and |highlight-ctermfg| for the values you can use.

If you do not like the signs' background colours and you do not want to update
the GitGutter* highlight groups yourself, you can get the plugin to do it
|g:gitgutter_set_sign_backgrounds|.

To change the line highlights, set up the following highlight groups in your
colorscheme or |vimrc|:
>
    GitGutterAddLine          " default: links to DiffAdd
    GitGutterChangeLine       " default: links to DiffChange
    GitGutterDeleteLine       " default: links to DiffDelete
    GitGutterChangeDeleteLine " default: links to GitGutterChangeLine
<

For example, to use |hl-DiffText| instead of |hl-DiffChange|:
>
    highlight link GitGutterChangeLine DiffText
<
To change the line number highlights, set up the following highlight groups in
your colorscheme or |vimrc|:
>
    GitGutterAddLineNr          " default: links to CursorLineNr
    GitGutterChangeLineNr       " default: links to CursorLineNr
    GitGutterDeleteLineNr       " default: links to CursorLineNr
    GitGutterChangeDeleteLineNr " default: links to GitGutterChangeLineNr
<
For example, to use |hl-Underlined| instead of |hl-CursorLineNr|:
>
    highlight link GitGutterChangeLineNr Underlined
<
To change the diff syntax colours used in the preview window, set up the diff*
highlight groups in your colorscheme or |vimrc|:
>
    diffAdded   " if not set: use GitGutterAdd's foreground colour
    diffChanged " if not set: use GitGutterChange's foreground colour
    diffRemoved " if not set: use GitGutterDelete's foreground colour
<
Note the diff* highlight groups are used in any buffer whose 'syntax' is
"diff".

To change the intra-line diff highlights used in the preview window, set up
the following highlight groups in your colorscheme or |vimrc|:
>
    GitGutterAddIntraLine    " default: gui=reverse cterm=reverse
    GitGutterDeleteIntraLine " default: gui=reverse cterm=reverse
<
For example, to use |hl-DiffAdd| for intra-line added regions:
>
    highlight link GitGutterAddIntraLine DiffAdd
<


===============================================================================
FAQ                                                             *gitgutter-faq*

a. How do I turn off realtime updates?

  Add this to your vim configuration in an |after-directory|:
>
    autocmd! gitgutter CursorHold,CursorHoldI
<

b. I turned off realtime updates, how can I have signs updated when I save a
   file?

  If you really want to update the signs when you save a file, add this to your
  |vimrc|:
>
    autocmd BufWritePost * GitGutter
<

c. Why can't I unstage staged changes?

  This plugin is for showing changes between the working tree and the index
  (and staging/undoing those changes). Unstaging a staged hunk would require
  showing changes between the index and HEAD, which is out of scope.

d. Why are the colours in the sign column weird?

  Your colorscheme is configuring the |hl-SignColumn| highlight group weirdly.
  Here are two ways you could change the colours:
>
    highlight! link SignColumn LineNr
    highlight SignColumn guibg=whatever ctermbg=whatever
<

e. What happens if I also use another plugin which uses signs (e.g. Syntastic)?

  Vim only allows one sign per line.  Vim-gitgutter will not interfere with
  signs it did not add.


===============================================================================
TROUBLESHOOTING                                     *gitgutter-troubleshooting*

When no signs are showing at all:~

1. Verify git is on your path:
>
    :echo system('git --version')
<

2. Verify your git config is compatible with the version of git return by the
   command above.

3. Verify your Vim supports signs.  The following should give 1:
>
    :echo has('signs')
<

4. Check whether the plugin thinks git knows about your file:
>
    :echo b:gitgutter.path
<
  If the result is -2, the plugin thinks your file is not tracked by git.

5. Check whether the signs have been placed:
>
    :sign place group=gitgutter
<
  If you see a list of signs, this is a colorscheme / highlight problem.
  Compare these two highlight values:
>
    :highlight GitGutterAdd
    :highlight SignColumn
<
  If no signs are listed, the call to git-diff is probably failing.  Turn on
  logging by adding the following to your vimrc, restart, reproduce the problem,
  and examing the gitgutter.log file in the plugin's directory.
>
    let g:gitgutter_log = 1
<

When the whole file is marked as added:~

If you use zsh, and you set "CDPATH", make sure "CDPATH" does not include the
current directory.


When signs take a few seconds to appear:~

Try reducing 'updatetime':
>
    set updatetime=100
<

Note this also controls how long vim waits before writing its swap file.


When signs don't update after focusing Vim:~

Your terminal probably isn't reporting focus events.  Either try installing
Terminus (https://github.com/wincent/terminus) or set:
>
    let g:gitgutter_terminal_reports_focus = 0
<

  vim:tw=78:et:ft=help:norl:


================================================
FILE: plugin/gitgutter.vim
================================================
scriptencoding utf-8

if exists('g:loaded_gitgutter') || !has('signs') || &cp
  finish
endif
let g:loaded_gitgutter = 1

" Initialisation {{{

if v:version < 703 || (v:version == 703 && !has("patch105"))
  call gitgutter#utility#warn('Requires Vim 7.3.105')
  finish
endif

let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : ''

function! s:obsolete(var)
  if exists(a:var)
    call gitgutter#utility#warn(a:var.' is obsolete and has no effect.')
  endif
endfunction


let g:gitgutter_preview_win_location = get(g:, 'gitgutter_preview_win_location', 'bo')
if exists('*nvim_open_win')
  let g:gitgutter_preview_win_floating = get(g:, 'gitgutter_preview_win_floating', 1)
  let g:gitgutter_floating_window_options = get(g:, 'gitgutter_floating_window_options', {
        \ 'relative': 'cursor',
        \ 'row': 1,
        \ 'col': 0,
        \ 'width': 42,
        \ 'height': &previewheight,
        \ 'style': 'minimal'
        \ })
else
  let default = exists('&previewpopup') ? !empty(&previewpopup) : 0
  let g:gitgutter_preview_win_floating = get(g:, 'gitgutter_preview_win_floating', default)
  let g:gitgutter_floating_window_options = get(g:, 'gitgutter_floating_window_options', {
        \ 'line': 'cursor+1',
        \ 'col': 'cursor',
        \ 'moved': 'any'
        \ })
endif
let g:gitgutter_enabled = get(g:, 'gitgutter_enabled', 1)
if exists('*sign_unplace')
  let g:gitgutter_max_signs = get(g:, 'gitgutter_max_signs', -1)
else
  let g:gitgutter_max_signs = get(g:, 'gitgutter_max_signs', 500)
endif
let g:gitgutter_signs             = get(g:, 'gitgutter_signs', 1)
let g:gitgutter_highlight_lines   = get(g:, 'gitgutter_highlight_lines', 0)
let g:gitgutter_highlight_linenrs = get(g:, 'gitgutter_highlight_linenrs', 0)
let g:gitgutter_sign_priority     = get(g:, 'gitgutter_sign_priority', 10)
" Nvim 0.4.0 has an expanding sign column
" The sign_place() function supports sign priority.
if (has('nvim-0.4.0') || exists('*sign_place')) && !exists('g:gitgutter_sign_allow_clobber')
  let g:gitgutter_sign_allow_clobber = 1
endif
let g:gitgutter_sign_allow_clobber   = get(g:, 'gitgutter_sign_allow_clobber', 0)
let g:gitgutter_set_sign_backgrounds = get(g:, 'gitgutter_set_sign_backgrounds', 0)
let g:gitgutter_sign_added           = get(g:, 'gitgutter_sign_added', '+')
let g:gitgutter_sign_modified        = get(g:, 'gitgutter_sign_modified', '~')
let g:gitgutter_sign_removed         = get(g:, 'gitgutter_sign_removed', '_')

if gitgutter#utility#supports_overscore_sign()
  let g:gitgutter_sign_removed_first_line = get(g:, 'gitgutter_sign_removed_first_line', '‾')
else
  let g:gitgutter_sign_removed_first_line = get(g:, 'gitgutter_sign_removed_first_line', '_^')
endif

let g:gitgutter_sign_removed_above_and_below = get(g:, 'gitgutter_sign_removed_above_and_below', '_¯')
let g:gitgutter_sign_modified_removed        = get(g:, 'gitgutter_sign_modified_removed', '~_')
let g:gitgutter_git_args                     = get(g:, 'gitgutter_git_args', '')
let g:gitgutter_diff_relative_to             = get(g:, 'gitgutter_diff_relative_to', 'index')
let g:gitgutter_diff_args                    = get(g:, 'gitgutter_diff_args', '')
let g:gitgutter_diff_base                    = get(g:, 'gitgutter_diff_base', '')
let g:gitgutter_map_keys                     = get(g:, 'gitgutter_map_keys', 1)
let g:gitgutter_terminal_reports_focus       = get(g:, 'gitgutter_terminal_reports_focus', 1)
let g:gitgutter_async                        = get(g:, 'gitgutter_async', 1)
let g:gitgutter_log                          = get(g:, 'gitgutter_log', 0)
let g:gitgutter_use_location_list            = get(g:, 'gitgutter_use_location_list', 0)
let g:gitgutter_close_preview_on_escape      = get(g:, 'gitgutter_close_preview_on_escape', 0)
let g:gitgutter_show_msg_on_hunk_jumping     = get(g:, 'gitgutter_show_msg_on_hunk_jumping', 1)

let g:gitgutter_git_executable = get(g:, 'gitgutter_git_executable', 'git')
if !executable(g:gitgutter_git_executable)
  if g:gitgutter_enabled
    call gitgutter#utility#warn('Cannot find git. Please set g:gitgutter_git_executable.')
  endif
  finish
endif

if exists('g:gitgutter_grep')
  call gitgutter#utility#warn('g:gitgutter_grep is obsolete')
endif

call gitgutter#highlight#define_highlights()
call gitgutter#highlight#define_signs()

" Prevent infinite loop where:
" - executing a job in the foreground launches a new window which takes the focus;
" - when the job finishes, focus returns to gvim;
" - the FocusGained event triggers a new job (see below).
if gitgutter#utility#windows() && !(g:gitgutter_async && gitgutter#async#available())
  set noshelltemp
endif

" }}}

" Primary functions {{{

command! -bar GitGutterAll call gitgutter#all(1)
command! -bar GitGutter    call gitgutter#process_buffer(bufnr(''), 1)

command! -bar GitGutterDisable call gitgutter#disable()
command! -bar GitGutterEnable  call gitgutter#enable()
command! -bar GitGutterToggle  call gitgutter#toggle()

command! -bar GitGutterBufferDisable call gitgutter#buffer_disable()
command! -bar GitGutterBufferEnable  call gitgutter#buffer_enable()
command! -bar GitGutterBufferToggle  call gitgutter#buffer_toggle()

command! -bar GitGutterQuickFix call gitgutter#quickfix(0)
command! -bar GitGutterQuickFixCurrentFile call gitgutter#quickfix(1)

command! -bar GitGutterDiffOrig call gitgutter#difforig()

" }}}

" Line highlights {{{

command! -bar GitGutterLineHighlightsDisable call gitgutter#highlight#line_disable()
command! -bar GitGutterLineHighlightsEnable  call gitgutter#highlight#line_enable()
command! -bar GitGutterLineHighlightsToggle  call gitgutter#highlight#line_toggle()

" }}}

" 'number' column highlights {{{
command! -bar GitGutterLineNrHighlightsDisable call gitgutter#highlight#linenr_disable()
command! -bar GitGutterLineNrHighlightsEnable  call gitgutter#highlight#linenr_enable()
command! -bar GitGutterLineNrHighlightsToggle  call gitgutter#highlight#linenr_toggle()
" }}}

" Signs {{{

command! -bar GitGutterSignsEnable  call gitgutter#sign#enable()
command! -bar GitGutterSignsDisable call gitgutter#sign#disable()
command! -bar GitGutterSignsToggle  call gitgutter#sign#toggle()

" }}}

" Hunks {{{

command! -bar -count=1 GitGutterNextHunk call gitgutter#hunk#next_hunk(<count>)
command! -bar -count=1 GitGutterPrevHunk call gitgutter#hunk#prev_hunk(<count>)

command! -bar -range=% GitGutterStageHunk call gitgutter#hunk#stage(<line1>,<line2>)
command! -bar GitGutterUndoHunk    call gitgutter#hunk#undo()
command! -bar GitGutterPreviewHunk call gitgutter#hunk#preview()

" Hunk text object
onoremap <silent> <Plug>(GitGutterTextObjectInnerPending) :<C-U>call gitgutter#hunk#text_object(1)<CR>
onoremap <silent> <Plug>(GitGutterTextObjectOuterPending) :<C-U>call gitgutter#hunk#text_object(0)<CR>
xnoremap <silent> <Plug>(GitGutterTextObjectInnerVisual)  :<C-U>call gitgutter#hunk#text_object(1)<CR>
xnoremap <silent> <Plug>(GitGutterTextObjectOuterVisual)  :<C-U>call gitgutter#hunk#text_object(0)<CR>


" Returns the git-diff hunks for the file or an empty list if there
" aren't any hunks.
"
" The return value is a list of lists.  There is one inner list per hunk.
"
"   [
"     [from_line, from_count, to_line, to_count],
"     [from_line, from_count, to_line, to_count],
"     ...
"   ]
"
" where:
"
" `from`  - refers to the staged file
" `to`    - refers to the working tree's file
" `line`  - refers to the line number where the change starts
" `count` - refers to the number of lines the change covers
function! GitGutterGetHunks()
  let bufnr = bufnr('')
  return gitgutter#utility#is_active(bufnr) ? gitgutter#hunk#hunks(bufnr) : []
endfunction

" Returns an array that contains a summary of the hunk status for the current
" window.  The format is [ added, modified, removed ], where each value
" represents the number of lines added/modified/removed respectively.
function! GitGutterGetHunkSummary()
  return gitgutter#hunk#summary(winbufnr(0))
endfunction

" }}}

" Folds {{{

command! -bar GitGutterFold call gitgutter#fold#toggle()

" }}}

command! -bar GitGutterDebug call gitgutter#debug#debug()

" Maps {{{

nnoremap <silent> <expr> <Plug>(GitGutterNextHunk) &diff ? ']c' : ":\<C-U>execute v:count1 . 'GitGutterNextHunk'\<CR>"
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>"
nnoremap <silent> <expr> <Plug>(GitGutterPrevHunk) &diff ? '[c' : ":\<C-U>execute v:count1 . 'GitGutterPrevHunk'\<CR>"
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>"

xnoremap <silent> <Plug>(GitGutterStageHunk)   :GitGutterStageHunk<CR>
xnoremap <silent> <Plug>GitGutterStageHunk     :call gitgutter#utility#warn('Please change your map <lt>Plug>GitGutterStageHunk to <lt>Plug>(GitGutterStageHunk)')<CR>
nnoremap <silent> <Plug>(GitGutterStageHunk)   :GitGutterStageHunk<CR>
nnoremap <silent> <Plug>GitGutterStageHunk     :call gitgutter#utility#warn('Please change your map <lt>Plug>GitGutterStageHunk to <lt>Plug>(GitGutterStageHunk)')<CR>
nnoremap <silent> <Plug>(GitGutterUndoHunk)    :GitGutterUndoHunk<CR>
nnoremap <silent> <Plug>GitGutterUndoHunk      :call gitgutter#utility#warn('Please change your map <lt>Plug>GitGutterUndoHunk to <lt>Plug>(GitGutterUndoHunk)')<CR>
nnoremap <silent> <Plug>(GitGutterPreviewHunk) :GitGutterPreviewHunk<CR>
nnoremap <silent> <Plug>GitGutterPreviewHunk   :call gitgutter#utility#warn('Please change your map <lt>Plug>GitGutterPreviewHunk to <lt>Plug>(GitGutterPreviewHunk)')<CR>

" }}}

function! s:on_bufenter()
  call gitgutter#setup_maps()

  " To keep vim's start-up fast, do not process the buffer when vim is starting.
  " Instead process it a short time later.  Normally we would rely on our
  " CursorHold autocommand to handle this but it turns out CursorHold is not
  " guaranteed to fire if the user has not typed anything yet; so set up a
  " timer instead.  The disadvantage is that if CursorHold does fire, the
  " plugin will do a round of unnecessary work; but since there will not have
  " been any changes to the buffer since the first round, the second round
  " will be cheap.
  if has('vim_starting') && !$VIM_GITGUTTER_TEST
    if exists('*timer_start') && has('lambda')
      call s:next_tick("call gitgutter#process_buffer(+".bufnr('').", 0)")
    else
      call gitgutter#process_buffer(bufnr(''), 0)
    endif
    return
  endif

  if exists('t:gitgutter_didtabenter') && t:gitgutter_didtabenter
    let t:gitgutter_didtabenter = 0
    call gitgutter#all(!g:gitgutter_terminal_reports_focus)
  else
    call gitgutter#process_buffer(bufnr(''), !g:gitgutter_terminal_reports_focus)
  endif
endfunction

function! s:next_tick(cmd)
  call timer_start(1, {-> execute(a:cmd)})
endfunction

function! s:on_buffilepre(bufnr)
  if !exists('s:renaming')
    let s:renaming = []
    let s:gitgutter_was_enabled = gitgutter#utility#getbufvar(a:bufnr, 'enabled')
  endif

  let s:renaming += [a:bufnr]
endfunction

function! s:on_buffilepost(bufnr)
  if len(s:renaming) > 1
    if s:renaming[0] != a:bufnr
      throw 'gitgutter rename error' s:renaming[0] a:bufnr
    endif
    unlet s:renaming[0]
    return
  endif

  " reset cached values
  GitGutterBufferDisable

  if s:gitgutter_was_enabled
    GitGutterBufferEnable
  endif

  unlet s:renaming
  unlet s:gitgutter_was_enabled
endfunction

" Autocommands {{{

augroup gitgutter
  autocmd!

  autocmd TabEnter * let t:gitgutter_didtabenter = 1

  autocmd BufEnter * call s:on_bufenter()

  " Ensure Vim is always checking for CursorMoved to avoid CursorMoved
  " being fired at the wrong time in floating preview window on Neovim.
  " See vim/vim#2053.
  autocmd CursorMoved * execute ''

  autocmd CursorHold,CursorHoldI * call gitgutter#process_buffer(bufnr(''), 0)
  if exists('*timer_start') && has('lambda')
    autocmd FileChangedShellPost * call s:next_tick("call gitgutter#process_buffer(+".expand('<abuf>').", 1)")
  else
    autocmd FileChangedShellPost * call gitgutter#process_buffer(+expand('<abuf>'), 1)
  endif

  " Ensure that all buffers are processed when opening vim with multiple files, e.g.:
  "
  "   vim -o file1 file2
  autocmd VimEnter * if winnr() != winnr('$') | call gitgutter#all(0) | endif

  autocmd ShellCmdPost * call gitgutter#all(1)
  autocmd BufLeave term://* call gitgutter#all(1)

  autocmd User FugitiveChanged call gitgutter#all(1)

  " Handle all buffers when focus is gained, but only after it was lost.
  " FocusGained gets triggered on startup with Neovim at least already.
  " Therefore this tracks also if it was lost before.
  let s:focus_was_lost = 0
  autocmd FocusGained * if s:focus_was_lost | let s:focus_was_lost = 0 | call gitgutter#all(1) | endif
  autocmd FocusLost * let s:focus_was_lost = 1

  if exists('##VimResume')
    autocmd VimResume * call gitgutter#all(1)
  endif

  autocmd ColorScheme * call gitgutter#highlight#define_highlights()

  autocmd BufFilePre  * call s:on_buffilepre(expand('<abuf>'))
  autocmd BufFilePost * call s:on_buffilepost(expand('<abuf>'))

  autocmd QuickFixCmdPre *vimgrep*
        \ if gitgutter#utility#getbufvar(expand('<abuf>'), 'enabled') |
        \   let s:gitgutter_was_enabled = expand('<abuf>') |
        \ else |
        \   let s:gitgutter_was_enabled = 0 |
        \ endif |
        \ GitGutterBufferDisable
  autocmd QuickFixCmdPost *vimgrep*
        \ if s:gitgutter_was_enabled |
        \   call gitgutter#buffer_enable(s:gitgutter_was_enabled) |
        \ endif |
        \ unlet s:gitgutter_was_enabled
augroup END

" }}}

" vim:set et sw=2 fdm=marker:


================================================
FILE: test/.gitattributes
================================================
*.foo filter=reverse diff=reverse


================================================
FILE: test/.gitconfig
================================================
[filter "reverse"]
  clean = "rev"
  smudge = "rev"

[diff "reverse"]
  textconv = "cat"


================================================
FILE: test/cp932.txt
================================================
The quick brown fox jumps
over the lazy dog

͂ɂقւƂʂ
킩悽ꂻ‚˂Ȃ
̂܂ӂ
߂݂Ђ



================================================
FILE: test/fixture.foo
================================================
one
two
three
four


================================================
FILE: test/fixture.txt
================================================
a
b
c
d
e
f
g
h
i
j



================================================
FILE: test/fixture_dos.txt
================================================
a
b
c
d
e
f
g
h
i
j



================================================
FILE: test/fixture_dos_noeol.txt
================================================
a
b
c
d
e
f
g

================================================
FILE: test/runner.vim
================================================
"
" Adapted from https://github.com/vim/vim/blob/master/src/testdir/runtest.vim
"
" When debugging tests it can help to write debug output:
"    call Log('oh noes')
"

function RunTest(test)
  if exists("*SetUp")
    call SetUp()
  endif

  try
    execute 'call '.a:test
  catch
    call Exception()
    let s:errored = 1
  endtry

  if exists("*TearDown")
    call TearDown()
  endif
endfunction

function Log(msg)
  if type(a:msg) == type('')
    call add(s:messages, a:msg)
  elseif type(a:msg) == type([])
    call extend(s:messages, a:msg)
  else
    call add(v:errors, 'Exception: unsupported type: '.type(a:msg))
  endif
endfunction

function Exception()
  call add(v:errors, v:throwpoint.'..'.'Exception: '.v:exception)
endfunction

" Shuffles list in place.
function Shuffle(list)
  " Fisher-Yates-Durstenfeld-Knuth
  let n = len(a:list)
  if n < 2
    return a:list
  endif
  for i in range(0, n-2)
    let j = Random(0, n-i-1)
    let e = a:list[i]
    let a:list[i] = a:list[i+j]
    let a:list[i+j] = e
  endfor
  return a:list
endfunction

" Returns a pseudorandom integer i such that 0 <= i <= max
function Random(min, max)
  if has('unix')
    let i = system('echo $RANDOM')  " 0 <= i <= 32767
  else
    let i = system('echo %RANDOM%')  " 0 <= i <= 32767
  endif
  return i * (a:max - a:min + 1) / 32768 + a:min
endfunction

function FriendlyName(test_name)
  return substitute(a:test_name[5:-3], '_', ' ', 'g')
endfunction

function Align(left, right)
  if type(a:right) == type([])
    let result = []
    for s in a:right
      if empty(result)
        call add(result, printf('%-'.s:indent.'S', a:left).s)
      else
        call add(result, printf('%-'.s:indent.'S',     '').s)
      endif
    endfor
    return result
  endif

  return printf('%-'.s:indent.'S', a:left).a:right
endfunction

let g:testname = expand('%')
let s:errored = 0
let s:done = 0
let s:fail = 0
let s:errors = 0
let s:messages = []
let s:indent = ''

call Log(g:testname.':')

" Source the test script.
try
  source %
catch
  let s:errors += 1
  call Exception()
endtry

" Locate the test functions.
set nomore
redir @q
silent function /^Test_
redir END
let s:tests = split(substitute(@q, 'function \(\k*()\)', '\1', 'g'))

" If there is another argument, filter test-functions' names against it.
if argc() > 1
  let s:tests = filter(s:tests, 'v:val =~ argv(1)')
endif

let s:indent = max(map(copy(s:tests), {_, val -> len(FriendlyName(val))}))

" Run the tests in random order.
for test in Shuffle(s:tests)
  call RunTest(test)
  let s:done += 1

  let friendly_name = FriendlyName(test)
  if len(v:errors) == 0
    call Log(Align(friendly_name, ' - ok'))
  else
    if s:errored
      let s:errors += 1
      let s:errored = 0
    else
      let s:fail += 1
    endif
    call Log(Align(friendly_name, ' - not ok'))

    let i = 0
    for error in v:errors
      if i != 0
        call Log(Align('','   ! ----'))
      endif
      for trace in reverse(split(error, '\.\.'))
        call Log(Align('', '   ! '.trace))
      endfor
      let i += 1
    endfor

    let v:errors = []
  endif
endfor

let summary = [
      \ s:done.(  s:done   == 1 ? ' test'    : ' tests'),
      \ s:errors.(s:errors == 1 ? ' error'   : ' errors'),
      \ s:fail.(  s:fail   == 1 ? ' failure' : ' failures'),
      \ ]
call Log('')
call Log(join(summary, ', '))

split messages.log
call append(line('$'), s:messages)
write

qall!



================================================
FILE: test/test
================================================
#!/usr/bin/env bash

VIM="/Applications/MacVim.app/Contents/MacOS/Vim -v"

export VIM_GITGUTTER_TEST=1

$VIM -u NONE -U NONE -N                      \
  --cmd 'set rtp+=../'                       \
  --cmd 'let g:gitgutter_async=0'            \
  --cmd 'source ../plugin/gitgutter.vim'     \
  -S runner.vim                              \
  test_*.vim                                 \
  "$@"

cat messages.log

grep -q "0 errors, 0 failures" messages.log
status=$?
rm messages.log
exit $status



================================================
FILE: test/test_gitgutter.vim
================================================
let s:current_dir = expand('%:p:h')
let s:test_repo   = s:current_dir.'/test-repo'
let s:bufnr       = bufnr('')

"
" Helpers
"

" Ignores unexpected keys in actual.
function s:assert_list_of_dicts(expected, actual)
  if empty(a:expected)
    call assert_equal([], a:actual)
    return
  endif

  let expected_keys = keys(a:expected[0])

  for dict in a:actual
    for k in keys(dict)
      if index(expected_keys, k) == -1
        call remove(dict, k)
      endif
    endfor
  endfor

  call assert_equal(a:expected, a:actual)
endfunction

" Ignores unexpected keys.
"
" expected - list of signs
function s:assert_signs(expected, filename)
  let actual = sign_getplaced(a:filename, {'group': 'gitgutter'})[0].signs
  call s:assert_list_of_dicts(a:expected, actual)
endfunction

function s:git_diff(...)
  return split(system('git diff -U0 '.(a:0 ? a:1 : 'fixture.txt')), '\n')
endfunction

function s:git_diff_staged(...)
  return split(system('git diff -U0 --staged '.(a:0 ? a:1 : 'fixture.txt')), '\n')
endfunction

function s:trigger_gitgutter()
  doautocmd CursorHold
endfunction


"
" SetUp / TearDown
"

function SetUp()
  let g:gitgutter_diff_base = ''
  call system("git init ".s:test_repo.
        \ " && cd ".s:test_repo.
        \ " && cp ../.gitconfig .".
        \ " && cp ../.gitattributes .".
        \ " && cp ../fixture.foo .".
        \ " && cp ../fixture.txt .".
        \ " && cp ../fixture_dos.txt .".
        \ " && cp ../fixture_dos_noeol.txt .".
        \ " && git add . && git commit -m 'initial'".
        \ " && git config diff.mnemonicPrefix false")
  execute ':cd' s:test_repo
  edit! fixture.txt
  call gitgutter#sign#reset()

  " FIXME why won't vim autoload the file?
  execute 'source' '../../autoload/gitgutter/diff_highlight.vim'
  execute 'source' '../../autoload/gitgutter/fold.vim'
endfunction

function TearDown()
  " delete all buffers except this one
  " TODO: move to runner.vim, accounting for multiple test files
  if s:bufnr > 1
    silent! execute '1,'.s:bufnr-1.'bdelete!'
  endif
  silent! execute s:bufnr+1.',$bdelete!'

  execute ':cd' s:current_dir
  call system("rm -rf ".s:test_repo)
endfunction

"
" The tests
"

function Test_add_lines()
  normal ggo*
  call s:trigger_gitgutter()

  let expected = [{'lnum': 2, 'name': 'GitGutterLineAdded', 'group': 'gitgutter', 'priority': 10}]
  call s:assert_signs(expected, 'fixture.txt')
endfunction


function Test_add_lines_fish()
  let _shell = &shell
  set shell=/usr/local/bin/fish

  normal ggo*
  call s:trigger_gitgutter()

  let expected = [{'lnum': 2, 'name': 'GitGutterLineAdded'}]
  call s:assert_signs(expected, 'fixture.txt')

  let &shell = _shell
endfunction


function Test_modify_lines()
  normal ggi*
  call s:trigger_gitgutter()

  let expected = [{'lnum': 1, 'name': 'GitGutterLineModified'}]
  call s:assert_signs(expected, 'fixture.txt')
endfunction


function Test_remove_lines()
  execute '5d'
  call s:trigger_gitgutter()

  let expected = [{'lnum': 4, 'name': 'GitGutterLineRemoved'}]
  call s:assert_signs(expected, 'fixture.txt')
endfunction


function Test_remove_first_lines()
  execute '1d'
  call s:trigger_gitgutter()

  let expected = [{'lnum': 1, 'name': 'GitGutterLineRemovedFirstLine'}]
  call s:assert_signs(expected, 'fixture.txt')
endfunction


function Test_priority()
  let g:gitgutter_sign_priority = 5

  execute '1d'
  call s:trigger_gitgutter()

  call s:assert_signs([{'priority': 5}], 'fixture.txt')

  let g:gitgutter_sign_priority = 10
endfunction


function Test_overlapping_hunks()
  execute '3d'
  execute '1d'
  call s:trigger_gitgutter()

  let expected = [{'lnum': 1, 'name': 'GitGutterLineRemovedAboveAndBelow'}]
  call s:assert_signs(expected, 'fixture.txt')
endfunction


function Test_edit_file_with_same_name_as_a_branch()
  normal 5Gi*
  call system('git checkout -b fixture.txt')
  call s:trigger_gitgutter()

  let expected = [{'lnum': 5, 'name': 'GitGutterLineModified'}]
  call s:assert_signs(expected, 'fixture.txt')
endfunction


function Test_file_added_to_git()
  let tmpfile = 'fileAddedToGit.tmp'
  call system('touch '.tmpfile.' && git add '.tmpfile)
  execute 'edit '.tmpfile
  normal ihello
  call s:trigger_gitgutter()

  let expected = [{'lnum': 1, 'name': 'GitGutterLineAdded'}]
  call s:assert_signs(expected, 'fileAddedToGit.tmp')
endfunction


function Test_filename_with_equals()
  call system('touch =fixture=.txt && git add =fixture=.txt')
  edit =fixture=.txt
  normal ggo*
  call s:trigger_gitgutter()

  let expected = [
        \ {'lnum': 1, 'name': 'GitGutterLineAdded'},
        \ {'lnum': 2, 'name': 'GitGutterLineAdded'}
        \ ]
  call s:assert_signs(expected, '=fixture=.txt')
endfunction


function Test_filename_with_colon()
  call system('touch fix:ture.txt && git add fix:ture.txt')
  edit fix:ture.txt
  normal ggo*
  call s:trigger_gitgutter()

  let expected = [
        \ {'lnum': 1, 'name': 'GitGutterLineAdded'},
        \ {'lnum': 2, 'name': 'GitGutterLineAdded'}
        \ ]
  call s:assert_signs(expected, 'fix:ture.txt')
endfunction


function Test_filename_with_square_brackets()
  call system('touch fix[tu]re.txt && git add fix[tu]re.txt')
  edit fix[tu]re.txt
  normal ggo*
  call s:trigger_gitgutter()

  let expected = [
        \ {'lnum': 1, 'name': 'GitGutterLineAdded'},
        \ {'lnum': 2, 'name': 'GitGutterLineAdded'}
        \ ]
  call s:assert_signs(expected, 'fix[tu]re.txt')
endfunction


function Test_filename_with_space()
  call system('touch fix\ ture.txt && git add fix\ ture.txt')
  edit fix\ ture.txt
  normal ggo*
  call s:trigger_gitgutter()

  let expected = [
        \ {'lnum': 1, 'name': 'GitGutterLineAdded'},
        \ {'lnum': 2, 'name': 'GitGutterLineAdded'}
        \ ]
  call s:assert_signs(expected, 'fix\ ture.txt')
endfunction


function Test_filename_leading_dash()
  call system('touch -- -fixture.txt && git add -- -fixture.txt')
  edit -fixture.txt
  normal ggo*
  call s:trigger_gitgutter()

  let expected = [
        \ {'lnum': 1, 'name': 'GitGutterLineAdded'},
        \ {'lnum': 2, 'name': 'GitGutterLineAdded'}
        \ ]
  call s:assert_signs(expected, '-fixture.txt')
endfunction


function Test_filename_umlaut()
  call system('touch -- fixtüre.txt && git add -- fixtüre.txt')
  edit fixtüre.txt
  normal ggo*
  call s:trigger_gitgutter()

  let expected = [
        \ {'lnum': 1, 'name': 'GitGutterLineAdded'},
        \ {'lnum': 2, 'name': 'GitGutterLineAdded'}
        \ ]
  call s:assert_signs(expected, 'fixtüre.txt')
endfunction


function Test_file_cmd()
  normal ggo*

  file other.txt

  call s:trigger_gitgutter()
  call assert_equal(1, b:gitgutter.enabled)
  call assert_equal('', b:gitgutter.path)
  call s:assert_signs([], 'other.txt')

  write

  call s:trigger_gitgutter()
  call assert_equal(-2, b:gitgutter.path)
endfunction


function Test_saveas()
  normal ggo*

  saveas other.txt

  call s:trigger_gitgutter()
  call assert_equal(1, b:gitgutter.enabled)
  call assert_equal(-2, b:gitgutter.path)
  call s:assert_signs([], 'other.txt')
endfunction


function Test_file_mv()
  call system('git mv fixture.txt fixture_moved.txt')
  edit fixture_moved.txt
  normal ggo*
  call s:trigger_gitgutter()
  let expected = [{'lnum': 2, 'name': 'GitGutterLineAdded'}]
  call s:assert_signs(expected, 'fixture_moved.txt')

  write
  call system('git add fixture_moved.txt && git commit -m "moved and edited"')
  GitGutterDisable
  GitGutterEnable
  let expected = []
  call s:assert_signs(expected, 'fixture_moved.txt')

  GitGutterDisable
  let g:gitgutter_diff_base = 'HEAD^'
  GitGutterEnable
  let expected = [{'lnum': 2, 'name': 'GitGutterLineAdded'}]
  call s:assert_signs(expected, 'fixture_moved.txt')
endfunction


" FIXME: this test fails when it is the first (or only) test to be run
function Test_follow_symlink()
  let tmp = 'symlink'
  call system('ln -nfs fixture.txt '.tmp)
  execute 'edit '.tmp
  6d
  call s:trigger_gitgutter()

  let expected = [{'lnum': 5, 'name': 'GitGutterLineRemoved'}]
  call s:assert_signs(expected, 'symlink')
endfunction


function Test_keep_alt()
  enew
  execute "normal! \<C-^>"

  call assert_equal('fixture.txt', bufname(''))
  call assert_equal('',            bufname('#'))

  normal ggx
  call s:trigger_gitgutter()

  call assert_equal('', bufname('#'))
endfunction


function Test_keep_modified()
  normal 5Go*
  call assert_equal(1, getbufvar('', '&modified'))

  call s:trigger_gitgutter()

  call assert_equal(1, getbufvar('', '&modified'))
endfunction


function Test_keep_op_marks()
  normal 5Go*
  call assert_equal([0,6,1,0], getpos("'["))
  call assert_equal([0,6,2,0], getpos("']"))

  call s:trigger_gitgutter()

  call assert_equal([0,6,1,0], getpos("'["))
  call assert_equal([0,6,2,0], getpos("']"))
endfunction


function Test_no_modifications()
  call s:assert_signs([], 'fixture.txt')
endfunction


function Test_orphaned_signs()
  execute "normal 5GoX\<CR>Y"
  call s:trigger_gitgutter()
  6d
  call s:trigger_gitgutter()

  let expected = [{'lnum': 6, 'name': 'GitGutterLineAdded'}]
  call s:assert_signs(expected, 'fixture.txt')
endfunction


function Test_untracked_file_outside_repo()
  let tmp = tempname()
  call system('touch '.tmp)
  execute 'edit '.tmp

  call s:assert_signs([], tmp)
endfunction


function Test_untracked_file_within_repo()
  let tmp = 'untrackedFileWithinRepo.tmp'
  call system('touch '.tmp)
  execute 'edit '.tmp
  normal ggo*
  call s:trigger_gitgutter()

  call s:assert_signs([], tmp)
  call assert_equal(-2, b:gitgutter.path)

  call system('rm '.tmp)
endfunction


function Test_untracked_file_square_brackets_within_repo()
  let tmp = '[un]trackedFileWithinRepo.tmp'
  call system('touch '.tmp)
  execute 'edit '.tmp
  normal ggo*
  call s:trigger_gitgutter()

  call s:assert_signs([], tmp)

  call system('rm '.tmp)
endfunction


function Test_file_unknown_in_base()
  let starting_branch = split(system('git branch --show-current'))[0]
  call system('git checkout -b some-feature')
  let tmp = 'file-on-this-branch-only.tmp'
  call system('echo "hi" > '.tmp.' && git add '.tmp)
  execute 'edit '.tmp
  let g:gitgutter_diff_base = starting_branch
  GitGutter
  let expected = [{'lnum': 1, 'name': 'GitGutterLineAdded', 'group': 'gitgutter', 'priority': 10}]
  call s:assert_signs(expected, tmp)
  let g:gitgutter_diff_base = ''
endfunction


function Test_v_shell_error_not_clobbered()
  " set gitgutter up to generate a shell error
  let starting_branch = split(system('git branch --show-current'))[0]
  call system('git checkout -b some-feature')
  let tmp = 'file-on-this-branch-only.tmp'
  call system('echo "hi" > '.tmp.' && git add '.tmp)
  execute 'edit '.tmp
  let g:gitgutter_diff_base = starting_branch

  " run a successful shell command
  silent !echo foobar >/dev/null

  " run gitgutter
  GitGutter

  call assert_equal(0, v:shell_error)

  let g:gitgutter_diff_base = ''
endfunction


function Test_hunk_outside_noop()
  5
  GitGutterStageHunk

  call s:assert_signs([], 'fixture.txt')
  call assert_equal([], s:git_diff())
  call assert_equal([], s:git_diff_staged())

  GitGutterUndoHunk

  call s:assert_signs([], 'fixture.txt')
  call assert_equal([], s:git_diff())
  call assert_equal([], s:git_diff_staged())
endfunction


function Test_preview()
  normal 5Gi*
  GitGutterPreviewHunk

  wincmd P
  call assert_equal(2, line('$'))
  call assert_equal('-e', getline(1))
  call assert_equal('+*e', getline(2))
  wincmd p
endfunction


function Test_preview_dos()
  edit! fixture_dos.txt

  normal 5Gi*
  GitGutterPreviewHunk

  wincmd P
  call assert_equal(2, line('$'))
  call assert_equal('-e', getline(1))
  call assert_equal('+*e', getline(2))
  wincmd p
endfunction


function Test_dos_noeol()
  edit! fixture_dos_noeol.txt
  GitGutter

  call s:assert_signs([], 'fixture_dos_noeol.txt')
endfunction


function Test_hunk_stage()
  let _shell = &shell
  set shell=foo

  normal 5Gi*
  GitGutterStageHunk

  call assert_equal('foo', &shell)
  let &shell = _shell

  call s:assert_signs([], 'fixture.txt')

  " Buffer is unsaved
  let expected = [
        \ 'diff --git a/fixture.txt b/fixture.txt',
        \ 'index ae8e546..f5c6aff 100644',
        \ '--- a/fixture.txt',
        \ '+++ b/fixture.txt',
        \ '@@ -5 +5 @@ d',
        \ '-*e',
        \ '+e'
        \ ]
  call assert_equal(expected, s:git_diff())

  " Index has been updated
  let expected = [
        \ 'diff --git a/fixture.txt b/fixture.txt',
        \ 'index f5c6aff..ae8e546 100644',
        \ '--- a/fixture.txt',
        \ '+++ b/fixture.txt',
        \ '@@ -5 +5 @@ d',
        \ '-e',
        \ '+*e'
        \ ]
  call assert_equal(expected, s:git_diff_staged())

  " Save the buffer
  write

  call assert_equal([], s:git_diff())
endfunction


function Test_hunk_stage_nearby_hunk()
  execute "normal! 2Gox\<CR>y\<CR>z"
  normal 2jdd
  normal k
  GitGutterStageHunk

  let expected = [
        \ {'lnum': 3, 'name': 'GitGutterLineAdded'},
        \ {'lnum': 4, 'name': 'GitGutterLineAdded'},
        \ {'lnum': 5, 'name': 'GitGutterLineAdded'}
        \ ]
  call s:assert_signs(expected, 'fixture.txt')

  " Buffer is unsaved
  let expected = [
        \ 'diff --git a/fixture.txt b/fixture.txt',
        \ 'index 53b13df..f5c6aff 100644',
        \ '--- a/fixture.txt',
        \ '+++ b/fixture.txt',
        \ '@@ -3,0 +4 @@ c',
        \ '+d',
        \ ]
  call assert_equal(expected, s:git_diff())

  " Index has been updated
  let expected = [
        \ 'diff --git a/fixture.txt b/fixture.txt',
        \ 'index f5c6aff..53b13df 100644',
        \ '--- a/fixture.txt',
        \ '+++ b/fixture.txt',
        \ '@@ -4 +3,0 @@ c',
        \ '-d',
        \ ]
  call assert_equal(expected, s:git_diff_staged())

  " Save the buffer
  write

  let expected = [
        \ 'diff --git a/fixture.txt b/fixture.txt',
        \ 'index 53b13df..8fdfda7 100644',
        \ '--- a/fixture.txt',
        \ '+++ b/fixture.txt',
        \ '@@ -2,0 +3,3 @@ b',
        \ '+x',
        \ '+y',
        \ '+z',
        \ ]
  call assert_equal(expected, s:git_diff())
endfunction


function Test_hunk_stage_partial_visual_added()
  call append(5, ['A','B','C','D'])
  execute "normal 7GVj:GitGutterStageHunk\<CR>"

  let expected = [
        \ {'lnum': 6, 'name': 'GitGutterLineAdded'},
        \ {'lnum': 9, 'name': 'GitGutterLineAdded'},
        \ ]
  call s:assert_signs(expected, 'fixture.txt')

  let expected = [
        \ 'diff --git a/fixture.txt b/fixture.txt',
        \ 'index 8a7026e..f5c6aff 100644',
        \ '--- a/fixture.txt',
        \ '+++ b/fixture.txt',
        \ '@@ -6,2 +5,0 @@ e',
        \ '-B',
        \ '-C',
        \ ]
  call assert_equal(expected, s:git_diff())

  let expected = [
        \ 'diff --git a/fixture.txt b/fixture.txt',
        \ 'index f5c6aff..8a7026e 100644',
        \ '--- a/fixture.txt',
        \ '+++ b/fixture.txt',
        \ '@@ -5,0 +6,2 @@ e',
        \ '+B',
        \ '+C',
        \ ]
  call assert_equal(expected, s:git_diff_staged())
endfunction


function Test_hunk_stage_partial_cmd_added()
  call append(5, ['A','B','C','D'])
  6
  7,8GitGutterStageHunk

  let expected = [
        \ {'lnum': 6, 'name': 'GitGutterLineAdded'},
        \ {'lnum': 9, 'name': 'GitGutterLineAdded'},
        \ ]
  call s:assert_signs(expected, 'fixture.txt')

  let expected = [
        \ 'diff --git a/fixture.txt b/fixture.txt',
        \ 'index 8a7026e..f5c6aff 100644',
        \ '--- a/fixture.txt',
        \ '+++ b/fixture.txt',
        \ '@@ -6,2 +5,0 @@ e',
        \ '-B',
        \ '-C',
        \ ]
  call assert_equal(expected, s:git_diff())

  let expected = [
        \ 'diff --git a/fixture.txt b/fixture.txt',
        \ 'index f5c6aff..8a7026e 100644',
        \ '--- a/fixture.txt',
        \ '+++ b/fixture.txt',
        \ '@@ -5,0 +6,2 @@ e',
        \ '+B',
        \ '+C',
        \ ]
  call assert_equal(expected, s:git_diff_staged())
endfunction


function Test_hunk_stage_partial_preview_added()
  call append(5, ['A','B','C','D'])
  6
  GitGutterPreviewHunk
  wincmd P

  " remove C and A so we stage B and D
  3delete
  1delete

  GitGutterStageHunk
  write

  let expected = [
        \ {'lnum': 6, 'name': 'GitGutterLineAdded'},
        \ {'lnum': 8, 'name': 'GitGutterLineAdded'},
        \ ]
  call s:assert_signs(expected, 'fixture.txt')

  let expected = [
        \ 'diff --git a/fixture.txt b/fixture.txt',
        \ 'index 975852f..3dd23a3 100644',
        \ '--- a/fixture.txt',
        \ '+++ b/fixture.txt',
        \ '@@ -5,0 +6 @@ e',
        \ '+A',
        \ '@@ -6,0 +8 @@ B',
        \ '+C',
        \ ]
  call assert_equal(expected, s:git_diff())

  let expected = [
        \ 'diff --git a/fixture.txt b/fixture.txt',
        \ 'index f5c6aff..975852f 100644',
        \ '--- a/fixture.txt',
        \ '+++ b/fixture.txt',
        \ '@@ -5,0 +6,2 @@ e',
        \ '+B',
        \ '+D',
        \ ]
  call assert_equal(expected, s:git_diff_staged())
endfunction


function Test_hunk_stage_preview_write()
  call append(5, ['A','B','C','D'])
  6
  GitGutterPreviewHunk
  wincmd P

  " preview window
  call feedkeys(":w\<CR>", 'tx')
  " original window
  write

  call s:assert_signs([], 'fixture.txt')

  call assert_equal([], s:git_diff())

  let expected = [
        \ 'diff --git a/fixture.txt b/fixture.txt',
        \ 'index f5c6aff..3dd23a3 100644',
        \ '--- a/fixture.txt',
        \ '+++ b/fixture.txt',
        \ '@@ -5,0 +6,4 @@ e',
        \ '+A',
        \ '+B',
        \ '+C',
        \ '+D',
        \ ]
  call assert_equal(expected, s:git_diff_staged())
endfunction


function Test_hunk_stage_partial_preview_added_removed()
  4,5delete
  call append(3, ['A','B','C','D'])
  4
  GitGutterPreviewHunk
  wincmd P

  " -d
  " -e
  " +A
  " +B
  " +C
  " +D

  " remove D and d so they do not get staged
  6delete
  1delete

  GitGutterStageHunk
  write

  let expected = [
        \ {'lnum': 3, 'name': 'GitGutterLineRemoved'},
        \ {'lnum': 7, 'name': 'GitGutterLineAdded'},
        \ ]
  call s:assert_signs(expected, 'fixture.txt')

  let expected = [
        \ 'diff --git a/fixture.txt b/fixture.txt',
        \ 'index 9a19589..e63fb0a 100644',
        \ '--- a/fixture.txt',
        \ '+++ b/fixture.txt',
        \ '@@ -4 +3,0 @@ c',
        \ '-d',
        \ '@@ -7,0 +7 @@ C',
        \ '+D',
        \ ]
  call assert_equal(expected, s:git_diff())

  let expected = [
        \ 'diff --git a/fixture.txt b/fixture.txt',
        \ 'index f5c6aff..9a19589 100644',
        \ '--- a/fixture.txt',
        \ '+++ b/fixture.txt',
        \ '@@ -5 +5,3 @@ d',
        \ '-e',
        \ '+A',
        \ '+B',
        \ '+C',
        \ ]
  call assert_equal(expected, s:git_diff_staged())
endfunction


function Test_hunk_undo()
  let _shell = &shell
  set shell=foo

  normal 5Gi*
  GitGutterUndoHunk

  call assert_equal('foo', &shell)
  let &shell = _shell

  call s:assert_signs([], 'fixture.txt')
  call assert_equal([], s:git_diff())
  call assert_equal([], s:git_diff_staged())
  call assert_equal('e', getline(5))
endfunction


function Test_hunk_undo_dos()
  edit! fixture_dos.txt

  normal 5Gi*
  GitGutterUndoHunk

  call s:assert_signs([], 'fixture_dos.txt')
  call assert_equal([], s:git_diff('fixture_dos.txt'))
  call assert_equal([], s:git_diff_staged('fixture_dos.txt'))
  call assert_equal('e', getline(5))
endfunction


function Test_undo_nearby_hunk()
  execute "normal! 2Gox\<CR>y\<CR>z"
  normal 2jdd
  normal k
  call s:trigger_gitgutter()
  GitGutterUndoHunk
  call s:trigger_gitgutter()

  let expected = [
        \ {'lnum': 3, 'name': 'GitGutterLineAdded'},
        \ {'lnum': 4, 'name': 'GitGutterLineAdded'},
        \ {'lnum': 5, 'name': 'GitGutterLineAdded'}
        \ ]
  call s:assert_signs(expected, 'fixture.txt')

  call assert_equal([], s:git_diff())

  call assert_equal([], s:git_diff_staged())

  " Save the buffer
  write

  let expected = [
        \ 'diff --git a/fixture.txt b/fixture.txt',
        \ 'index f5c6aff..3fbde56 100644',
        \ '--- a/fixture.txt',
        \ '+++ b/fixture.txt',
        \ '@@ -2,0 +3,3 @@ b',
        \ '+x',
        \ '+y',
        \ '+z',
        \ ]
  call assert_equal(expected, s:git_diff())

endfunction


function Test_overlapping_hunk_op()
  func! Answer(char)
    call feedkeys(a:char."\<CR>")
  endfunc

  " Undo upper

  execute '3d'
  execute '1d'
  call s:trigger_gitgutter()
  normal gg
  call timer_start(100, {-> Answer('u')} )
  GitGutterUndoHunk
  call s:trigger_gitgutter()

  let expected = [{'lnum': 2, 'name': 'GitGutterLineRemoved'}]
  call s:assert_signs(expected, 'fixture.txt')

  " Undo lower

  execute '1d'
  call s:trigger_gitgutter()
  normal gg
  call timer_start(100, {-> Answer('l')} )
  GitGutterUndoHunk
  call s:trigger_gitgutter()

  let expected = [{'lnum': 1, 'name': 'GitGutterLineRemovedFirstLine'}]
  call s:assert_signs(expected, 'fixture.txt')
endfunction


function Test_write_option()
  set nowrite

  normal ggo*
  call s:trigger_gitgutter()

  let expected = [{'lnum': 2, 'name': 'GitGutterLineAdded'}]
  call s:assert_signs(expected, 'fixture.txt')

  set write
endfunction


function Test_inner_text_object()
  execute "normal! 2Gox\<CR>y\<CR>z\<CR>\<CR>"
  call s:trigger_gitgutter()
  normal dic
  call s:trigger_gitgutter()

  call s:assert_signs([], 'fixture.txt')
  call assert_equal(readfile('fixture.txt'), getline(1,'$'))

  " Excludes trailing lines
  normal 9Gi*
  normal 10Gi*
  call s:trigger_gitgutter()
  execute "normal vic\<Esc>"
  call assert_equal([9, 10], [line("'<"), line("'>")])
endfunction


function Test_around_text_object()
  execute "normal! 2Gox\<CR>y\<CR>z\<CR>\<CR>"
  call s:trigger_gitgutter()
  normal dac
  call s:trigger_gitgutter()

  call s:assert_signs([], 'fixture.txt')
  call assert_equal(readfile('fixture.txt'), getline(1,'$'))

  " Includes trailing lines
  normal 9Gi*
  normal 10Gi*
  call s:trigger_gitgutter()
  execute "normal vac\<Esc>"
  call assert_equal([9, 11], [line("'<"), line("'>")])
endfunction


function Test_user_autocmd()
  autocmd User GitGutter let s:autocmd_user = g:gitgutter_hook_context.bufnr

  " Verify not fired when nothing changed.
  let s:autocmd_user = 0
  call s:trigger_gitgutter()
  call assert_equal(0, s:autocmd_user)

  " Verify fired when there was a change.
  normal ggo*
  let bufnr = bufnr('')
  call s:trigger_gitgutter()
  call assert_equal(bufnr, s:autocmd_user)
endfunction


function Test_fix_file_references()
  let sid = matchstr(execute('filter autoload/gitgutter/hunk.vim scriptnames'), '\d\+')
  let FixFileReferences = function("<SNR>".sid."_fix_file_references")

  " No special characters
  let hunk_diff = join([
        \ 'diff --git a/fixture.txt b/fixture.txt',
        \ 'index f5c6aff..3fbde56 100644',
        \ '--- a/fixture.txt',
        \ '+++ b/fixture.txt',
        \ '@@ -2,0 +3,1 @@ b',
        \ '+x'
        \ ], "\n")."\n"
  let filepath = 'blah.txt'

  let expected = join([
        \ 'diff --git a/blah.txt b/blah.txt',
        \ 'index f5c6aff..3fbde56 100644',
        \ '--- a/blah.txt',
        \ '+++ b/blah.txt',
        \ '@@ -2,0 +3,1 @@ b',
        \ '+x'
        \ ], "\n")."\n"

  call assert_equal(expected, FixFileReferences(filepath, hunk_diff))

  " diff.mnemonicPrefix; spaces in filename
  let hunk_diff = join([
        \ 'diff --git i/x/cat dog w/x/cat dog',
        \ 'index f5c6aff..3fbde56 100644',
        \ '--- i/x/cat dog',
        \ '+++ w/x/cat dog',
        \ '@@ -2,0 +3,1 @@ b',
        \ '+x'
        \ ], "\n")."\n"
  let filepath = 'blah.txt'

  let expected = join([
        \ 'diff --git i/blah.txt w/blah.txt',
        \ 'index f5c6aff..3fbde56 100644',
        \ '--- i/blah.txt',
        \ '+++ w/blah.txt',
        \ '@@ -2,0 +3,1 @@ b',
        \ '+x'
        \ ], "\n")."\n"

  call assert_equal(expected, FixFileReferences(filepath, hunk_diff))

  " Backslashes in filename; quotation marks
  let hunk_diff = join([
        \ 'diff --git "a/C:\\Users\\FOO~1.PAR\\AppData\\Local\\Temp\\nvimJcmSv9\\11.1.vim" "b/C:\\Users\\FOO~1.PAR\\AppData\\Local\\Temp\\nvimJcmSv9\\12.1.vim"',
        \ 'index f42aeb0..4930403 100644',
        \ '--- "a/C:\\Users\\FOO~1.PAR\\AppData\\Local\\Temp\\nvimJcmSv9\\11.1.vim"',
        \ '+++ "b/C:\\Users\\FOO~1.PAR\\AppData\\Local\\Temp\\nvimJcmSv9\\12.1.vim"',
        \ '@@ -172,0 +173 @@ stuff',
        \ '+x'
        \ ], "\n")."\n"
  let filepath = 'init.vim'

  let expected = join([
        \ 'diff --git "a/init.vim" "b/init.vim"',
        \ 'index f42aeb0..4930403 100644',
        \ '--- "a/init.vim"',
        \ '+++ "b/init.vim"',
        \ '@@ -172,0 +173 @@ stuff',
        \ '+x'
        \ ], "\n")."\n"

  call assert_equal(expected, FixFileReferences(filepath, hunk_diff))
endfunction


function Test_encoding()
  call system('cp ../cp932.txt . && git add cp932.txt')
  edit ++enc=cp932 cp932.txt

  call s:trigger_gitgutter()

  call s:assert_signs([], 'cp932.txt')
endfunction


function Test_empty_file()
  " 0-byte file
  call system('touch empty.txt && git add empty.txt')
  edit empty.txt

  call s:trigger_gitgutter()
  call s:assert_signs([], 'empty.txt')


  " File consisting only of a newline
  call system('echo "" > newline.txt && git add newline.txt')
  edit newline.txt

  call s:trigger_gitgutter()
  call s:assert_signs([], 'newline.txt')


  " 1 line file without newline
  " Vim will force a newline unless we tell it not to.
  call system('echo -n a > oneline.txt && git add oneline.txt')
  set noeol nofixeol
  edit! oneline.txt

  call s:trigger_gitgutter()
  call s:assert_signs([], 'oneline.txt')

  set eol fixeol
endfunction


function Test_quickfix()
  call setline(5, ['A', 'B'])
  call setline(9, ['C', 'D'])
  write
  let bufnr1 = bufnr('')

  edit fixture_dos.txt
  call setline(2, ['A', 'B'])
  write
  let bufnr2 = bufnr('')

  GitGutterQuickFix

  let expected = [
        \ {'lnum': 5, 'bufnr': bufnr1, 'text': '-e'},
        \ {'lnum': 9, 'bufnr': bufnr1, 'text': '-i'},
        \ {'lnum': 2, 'bufnr': bufnr2, 'text': "-b\r"}
        \ ]

  call s:assert_list_of_dicts(expected, getqflist())

  GitGutterQuickFixCurrentFile

  let expected = [
        \ {'lnum': 2, 'bufnr': bufnr(''), 'text': "-b\r"},
        \ ]

  call s:assert_list_of_dicts(expected, getqflist())
endfunction


function Test_common_prefix()
  let sid = matchstr(execute('filter autoload/gitgutter/diff_highlight.vim scriptnames'), '\d\+')
  let CommonPrefix = function("<SNR>".sid."_common_prefix")

  " zero length
  call assert_equal(-1, CommonPrefix('', 'foo'))
  call assert_equal(-1, CommonPrefix('foo', ''))
  " nothing in common
  call assert_equal(-1, CommonPrefix('-abcde', '+pqrst'))
  call assert_equal(-1, CommonPrefix('abcde', 'pqrst'))
  " something in common
  call assert_equal(-1, CommonPrefix('-abcde', '+abcpq'))
  call assert_equal(2, CommonPrefix('abcde', 'abcpq'))
  call assert_equal(0, CommonPrefix('abc', 'apq'))
  " everything in common
  call assert_equal(-1, CommonPrefix('-abcde', '+abcde'))
  call assert_equal(4, CommonPrefix('abcde', 'abcde'))
  " different lengths
  call assert_equal(-1, CommonPrefix('-abcde', '+abx'))
  call assert_equal(1, CommonPrefix('abcde', 'abx'))
  call assert_equal(-1, CommonPrefix('-abx',   '+abcde'))
  call assert_equal(1, CommonPrefix('abx',   'abcde'))
  call assert_equal(-1, CommonPrefix('-abcde', '+abc'))
  call assert_equal(2, CommonPrefix('abcde', 'abc'))
endfunction


function Test_common_suffix()
  let sid = matchstr(execute('filter autoload/gitgutter/diff_highlight.vim scriptnames'), '\d\+')
  let CommonSuffix = function("<SNR>".sid."_common_suffix")

  " nothing in common
  call assert_equal([6,6], CommonSuffix('-abcde', '+pqrst', 0))
  " something in common
  call assert_equal([3,3], CommonSuffix('-abcde', '+pqcde', 0))
  " everything in common
  call assert_equal([5,5], CommonSuffix('-abcde', '+abcde', 5))
  " different lengths
  call assert_equal([4,2], CommonSuffix('-abcde', '+xde', 0))
  call assert_equal([2,4], CommonSuffix('-xde',   '+abcde', 0))
endfunction


" Note the order of lists within the overall returned list does not matter.
function Test_diff_highlight()
  " Ignores mismatched number of added and removed lines.
  call assert_equal([], gitgutter#diff_highlight#process(['-foo']))
  call assert_equal([], gitgutter#diff_highlight#process(['+foo']))
  call assert_equal([], gitgutter#diff_highlight#process(['-foo','-bar','+baz']))

  " everything changed
  let hunk = ['-foo', '+cat']
  let expected = []
  call assert_equal(expected, gitgutter#diff_highlight#process(hunk))

  " change in middle
  let hunk = ['-foo bar baz', '+foo zip baz']
  let expected = [[1, '-', 6, 8], [2, '+', 6, 8]]
  call assert_equal(expected, gitgutter#diff_highlight#process(hunk))

  " change at start
  let hunk = ['-foo bar baz', '+zip bar baz']
  let expected = [[1, '-', 2, 4], [2, '+', 2, 4]]
  call assert_equal(expected, gitgutter#diff_highlight#process(hunk))

  " change at end
  let hunk = ['-foo bar baz', '+foo bar zip']
  let expected = [[1, '-', 10, 12], [2, '+', 10, 12]]
  call assert_equal(expected, gitgutter#diff_highlight#process(hunk))

  " removed in middle
  let hunk = ['-foo bar baz', '+foo baz']
  let expected = [[1, '-', 8, 11]]
  call assert_equal(expected, gitgutter#diff_highlight#process(hunk))

  " added in middle
  let hunk = ['-foo baz', '+foo bar baz']
  let expected = [[2, '+', 8, 11]]
  call assert_equal(expected, gitgutter#diff_highlight#process(hunk))

  " two insertions at start
  let hunk = ['-foo bar baz', '+(foo) bar baz']
  let expected = [[2, '+', 2, 2], [2, '+', 6, 6]]
  call assert_equal(expected, gitgutter#diff_highlight#process(hunk))

  " two insertions in middle
  let hunk = ['-foo bar baz', '+foo (bar) baz']
  let expected = [[2, '+', 6, 6], [2, '+', 10, 10]]
  call assert_equal(expected, gitgutter#diff_highlight#process(hunk))

  " two insertions at end
  let hunk = ['-foo bar baz', '+foo bar (baz)']
  let expected = [[2, '+', 10, 10], [2, '+', 14, 14]]
  call assert_equal(expected, gitgutter#diff_highlight#process(hunk))

  " singular insertion
  let hunk = ['-The cat in the hat.', '+The furry cat in the hat.']
  call assert_equal([[2, '+', 6, 11]], gitgutter#diff_highlight#process(hunk))

  " singular deletion
  let hunk = ['-The cat in the hat.', '+The cat.']
  call assert_equal([[1, '-', 9, 19]], gitgutter#diff_highlight#process(hunk))

  " two insertions
  let hunk = ['-The cat in the hat.', '+The furry cat in the teal hat.']
  call assert_equal([[2, '+', 6, 11], [2, '+', 22, 26]], gitgutter#diff_highlight#process(hunk))

  " two deletions
  let hunk = ['-The furry cat in the teal hat.', '+The cat in the hat.']
  call assert_equal([[1, '-', 6, 11], [1, '-', 22, 26]], gitgutter#diff_highlight#process(hunk))

  " two edits
  let hunk = ['-The cat in the hat.', '+The ox in the box.']
  call assert_equal([[1, '-', 6, 8], [2, '+', 6, 7], [1, '-', 17, 19], [2, '+', 16, 18]], gitgutter#diff_highlight#process(hunk))

  " Requires s:gap_between_regions = 2 to pass.
  " let hunk = ['-foo: bar.zap', '+foo: quux(bar)']
  " call assert_equal([[2, '+', 7, 11], [1, '-', 10, 13], [2, '+', 15, 15]], gitgutter#diff_highlight#process(hunk))

  let hunk = ['-gross_value: transaction.unexplained_amount', '+gross_value: amount(transaction)']
  call assert_equal([[2, '+', 15, 21], [1, '-', 26, 44], [2, '+', 33, 33]], gitgutter#diff_highlight#process(hunk))

  let hunk = ['-gem "contact_sport", "~> 1.0.2"', '+gem ("contact_sport"), "~> 1.2"']
  call assert_equal([[2, '+', 6, 6], [2, '+', 22, 22], [1, '-', 28, 29]], gitgutter#diff_highlight#process(hunk))
endfunction


function Test_lcs()
  let sid = matchstr(execute('filter autoload/gitgutter/diff_highlight.vim scriptnames'), '\d\+')
  let Lcs = function("<SNR>".sid."_lcs")

  call assert_equal('', Lcs('', 'foo'))
  call assert_equal('', Lcs('foo', ''))
  call assert_equal('bar', Lcs('foobarbaz', 'bbart'))
  call assert_equal('transaction', Lcs('transaction.unexplained_amount', 'amount(transaction)'))
endfunction


function Test_split()
  let sid = matchstr(execute('filter autoload/gitgutter/diff_highlight.vim scriptnames'), '\d\+')
  let Split = function("<SNR>".sid."_split")

  call assert_equal(['foo', 'baz'], Split('foobarbaz', 'bar'))
  call assert_equal(['', 'barbaz'], Split('foobarbaz', 'foo'))
  call assert_equal(['foobar', ''], Split('foobarbaz', 'baz'))
  call assert_equal(['1', '2'], Split('1~2', '~'))
endfunction


function Test_foldtext()
  8d
  call s:trigger_gitgutter()
  call assert_equal(0, gitgutter#fold#is_changed())

  let v:foldstart = 5
  let v:foldend = 9
  call assert_equal(1, gitgutter#fold#is_changed())
  call assert_equal('+-  5 lines (*): e', gitgutter#fold#foldtext())

  let v:foldstart = 1
  let v:foldend = 3
  call assert_equal(0, gitgutter#fold#is_changed())
  call assert_equal('+-  3 lines: a', gitgutter#fold#foldtext())
endfunction


function Test_assume_unchanged()
  call system("git update-index --assume-unchanged fixture.txt")
  unlet b:gitgutter.path  " it was already set when fixture.txt was loaded in SetUp()
  normal ggo*
  call s:trigger_gitgutter()
  call s:assert_signs([], 'fixture.txt')
endfunction


function Test_clean_smudge_filter()
  call system("git config --local include.path ../.gitconfig")
  call system("rm fixture.foo && git checkout fixture.foo")

  func! Answer(char)
    call feedkeys(a:char."\<CR>")
  endfunc

  edit fixture.foo
  call setline(2, ['A'])
  call setline(4, ['B'])
  call s:trigger_gitgutter()
  normal! 2G
  call timer_start(100, {-> Answer('y')} )
  GitGutterStageHunk
  call s:trigger_gitgutter()

  let expected = [
        \ {'lnum': 2, 'id': 23, 'name': 'GitGutterLineModified', 'priority': 10, 'group': 'gitgutter'},
        \ {'lnum': 4, 'id': 24, 'name': 'GitGutterLineModified', 'priority': 10, 'group': 'gitgutter'}
        \ ]
  " call s:assert_signs(expected, 'fixture.foo')
  call s:assert_signs([], 'fixture.foo')
endfunction
Download .txt
gitextract_g6rggijz/

├── .github/
│   └── issue_template.md
├── .gitignore
├── LICENCE
├── README.mkd
├── autoload/
│   ├── gitgutter/
│   │   ├── async.vim
│   │   ├── debug.vim
│   │   ├── diff.vim
│   │   ├── diff_highlight.vim
│   │   ├── fold.vim
│   │   ├── highlight.vim
│   │   ├── hunk.vim
│   │   ├── sign.vim
│   │   └── utility.vim
│   └── gitgutter.vim
├── doc/
│   └── gitgutter.txt
├── plugin/
│   └── gitgutter.vim
└── test/
    ├── .gitattributes
    ├── .gitconfig
    ├── cp932.txt
    ├── fixture.foo
    ├── fixture.txt
    ├── fixture_dos.txt
    ├── fixture_dos_noeol.txt
    ├── runner.vim
    ├── test
    └── test_gitgutter.vim
Condensed preview — 26 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (199K chars).
[
  {
    "path": ".github/issue_template.md",
    "chars": 103,
    "preview": "> What is the latest commit SHA in your installed vim-gitgutter?\n\n> What vim/nvim version are you on?\n\n"
  },
  {
    "path": ".gitignore",
    "chars": 38,
    "preview": "/doc/tags\n/misc\n/test/*.actual\n*.log\n\n"
  },
  {
    "path": "LICENCE",
    "chars": 1067,
    "preview": "MIT License\n\nCopyright (c) Andrew Stewart\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n"
  },
  {
    "path": "README.mkd",
    "chars": 25037,
    "preview": "## vim-gitgutter\n\nA Vim plugin which shows a git diff in the sign column.  It shows which lines have been added, modifie"
  },
  {
    "path": "autoload/gitgutter/async.vim",
    "chars": 2719,
    "preview": "let s:available = has('nvim') || (\n      \\   has('job') && (\n      \\     (has('patch-7.4.1826') && !has('gui_running')) "
  },
  {
    "path": "autoload/gitgutter/debug.vim",
    "chars": 2415,
    "preview": "let s:plugin_dir  = expand('<sfile>:p:h:h:h').'/'\nlet s:log_file    = s:plugin_dir.'gitgutter.log'\nlet s:channel_log = s"
  },
  {
    "path": "autoload/gitgutter/diff.vim",
    "chars": 13477,
    "preview": "scriptencoding utf8\n\nlet s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : ''"
  },
  {
    "path": "autoload/gitgutter/diff_highlight.vim",
    "chars": 5503,
    "preview": "\" This is the minimum number of characters required between regions of change\n\" in a line.  It's somewhat arbitrary: hig"
  },
  {
    "path": "autoload/gitgutter/fold.vim",
    "chars": 2635,
    "preview": "function! gitgutter#fold#enable()\n  call s:save_fold_state()\n\n  call s:set_fold_levels()\n  setlocal foldexpr=gitgutter#f"
  },
  {
    "path": "autoload/gitgutter/highlight.vim",
    "chars": 9643,
    "preview": "function! gitgutter#highlight#line_disable() abort\n  let g:gitgutter_highlight_lines = 0\n  call s:define_sign_line_highl"
  },
  {
    "path": "autoload/gitgutter/hunk.vim",
    "chars": 20716,
    "preview": "let s:winid = 0\nlet s:preview_bufnr = 0\nlet s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? "
  },
  {
    "path": "autoload/gitgutter/sign.vim",
    "chars": 8087,
    "preview": "\" For older Vims without sign_place() the plugin has to manaage the sign ids.\nlet s:first_sign_id = 3000\nlet s:next_sign"
  },
  {
    "path": "autoload/gitgutter/utility.vim",
    "chars": 11344,
    "preview": "function! gitgutter#utility#supports_overscore_sign()\n  if gitgutter#utility#windows()\n    return &encoding ==? 'utf-8'\n"
  },
  {
    "path": "autoload/gitgutter.vim",
    "chars": 8340,
    "preview": "\" Primary functions {{{\n\nfunction! gitgutter#all(force) abort\n  let visible = tabpagebuflist()\n\n  for bufnr in range(1, "
  },
  {
    "path": "doc/gitgutter.txt",
    "chars": 27471,
    "preview": "*gitgutter.txt*              A Vim plugin which shows a git diff in the gutter.\n\n\n                           Vim GitGutt"
  },
  {
    "path": "plugin/gitgutter.vim",
    "chars": 13773,
    "preview": "scriptencoding utf-8\n\nif exists('g:loaded_gitgutter') || !has('signs') || &cp\n  finish\nendif\nlet g:loaded_gitgutter = 1\n"
  },
  {
    "path": "test/.gitattributes",
    "chars": 34,
    "preview": "*.foo filter=reverse diff=reverse\n"
  },
  {
    "path": "test/.gitconfig",
    "chars": 89,
    "preview": "[filter \"reverse\"]\n  clean = \"rev\"\n  smudge = \"rev\"\n\n[diff \"reverse\"]\n  textconv = \"cat\"\n"
  },
  {
    "path": "test/cp932.txt",
    "chars": 68,
    "preview": "The quick brown fox jumps\nover the lazy dog\n\n͂ɂقւƂʂ\n킩悽ꂻ‚˂Ȃ\n̂܂ӂ\n߂݂Ђ\n\n"
  },
  {
    "path": "test/fixture.foo",
    "chars": 19,
    "preview": "one\ntwo\nthree\nfour\n"
  },
  {
    "path": "test/fixture.txt",
    "chars": 21,
    "preview": "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\n\n"
  },
  {
    "path": "test/fixture_dos.txt",
    "chars": 32,
    "preview": "a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng\r\nh\r\ni\r\nj\r\n\r\n"
  },
  {
    "path": "test/fixture_dos_noeol.txt",
    "chars": 19,
    "preview": "a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng"
  },
  {
    "path": "test/runner.vim",
    "chars": 3412,
    "preview": "\"\n\" Adapted from https://github.com/vim/vim/blob/master/src/testdir/runtest.vim\n\"\n\" When debugging tests it can help to "
  },
  {
    "path": "test/test",
    "chars": 496,
    "preview": "#!/usr/bin/env bash\n\nVIM=\"/Applications/MacVim.app/Contents/MacOS/Vim -v\"\n\nexport VIM_GITGUTTER_TEST=1\n\n$VIM -u NONE -U "
  },
  {
    "path": "test/test_gitgutter.vim",
    "chars": 33904,
    "preview": "let s:current_dir = expand('%:p:h')\nlet s:test_repo   = s:current_dir.'/test-repo'\nlet s:bufnr       = bufnr('')\n\n\"\n\" He"
  }
]

About this extraction

This page contains the full source code of the airblade/vim-gitgutter GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 26 files (186.0 KB), approximately 56.1k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!