mirror of
1
0
Fork 0
This commit is contained in:
amix 2017-03-14 16:16:10 +01:00
commit 590d4b554c
29 changed files with 5119 additions and 4 deletions

@ -1 +0,0 @@
Subproject commit 705e68d90d6596c621093a28bf480a572a41ad02

View File

@ -0,0 +1,117 @@
# Change Log
## [Unreleased](https://github.com/morhetz/gruvbox/tree/HEAD)
[Full Changelog](https://github.com/morhetz/gruvbox/compare/v1.3.5...HEAD)
**Fixed bugs:**
- Lighter background on terminal [\#8](https://github.com/morhetz/gruvbox/issues/8)
**Closed issues:**
- Installation issue. [\#54](https://github.com/morhetz/gruvbox/issues/54)
- Italic font in terminal\(urxvt\) [\#49](https://github.com/morhetz/gruvbox/issues/49)
- Unable to log in when sourcing the palette shellscript [\#48](https://github.com/morhetz/gruvbox/issues/48)
- How can i modify multiple comment scheme [\#46](https://github.com/morhetz/gruvbox/issues/46)
- Remove comment highlight in iterm [\#44](https://github.com/morhetz/gruvbox/issues/44)
- Comments looking strange withing tmux [\#43](https://github.com/morhetz/gruvbox/issues/43)
- comments are reverse-video in xterm [\#41](https://github.com/morhetz/gruvbox/issues/41)
- What font are you using in the screenshots? [\#39](https://github.com/morhetz/gruvbox/issues/39)
- vim-signature crashes when I use gruvbox [\#38](https://github.com/morhetz/gruvbox/issues/38)
- Color of statusbar in inactive windows [\#37](https://github.com/morhetz/gruvbox/issues/37)
- Go method and struct highlighting missing [\#36](https://github.com/morhetz/gruvbox/issues/36)
- gruvbox\_256palette.sh doesn't work for Konsole [\#35](https://github.com/morhetz/gruvbox/issues/35)
- Contrast in jekyll markdown files [\#33](https://github.com/morhetz/gruvbox/issues/33)
- Pentadactyl Gruvbox Theme [\#32](https://github.com/morhetz/gruvbox/issues/32)
- make vertsplit better [\#31](https://github.com/morhetz/gruvbox/issues/31)
- Console support. [\#30](https://github.com/morhetz/gruvbox/issues/30)
- How can I change the background color? [\#29](https://github.com/morhetz/gruvbox/issues/29)
- Some words are not bold [\#28](https://github.com/morhetz/gruvbox/issues/28)
- Terminal theme on base gruvbox [\#25](https://github.com/morhetz/gruvbox/issues/25)
- Markdown has inverted colors when using \* [\#24](https://github.com/morhetz/gruvbox/issues/24)
- how install it on mac osx [\#23](https://github.com/morhetz/gruvbox/issues/23)
- Comments color for Terminal Vim [\#22](https://github.com/morhetz/gruvbox/issues/22)
- Move palette files to gruvbox-generalized [\#20](https://github.com/morhetz/gruvbox/issues/20)
- Maybe add Gruvbox Airline theme? [\#19](https://github.com/morhetz/gruvbox/issues/19)
- For Sublime text [\#18](https://github.com/morhetz/gruvbox/issues/18)
**Merged pull requests:**
- Fix the 256 palette script failed login issue [\#53](https://github.com/morhetz/gruvbox/pull/53) ([jonasmalacofilho](https://github.com/jonasmalacofilho))
- add minimal coloring for gitcommit highlighting [\#52](https://github.com/morhetz/gruvbox/pull/52) ([daniely](https://github.com/daniely))
- For terminals, turn off italics by default. [\#47](https://github.com/morhetz/gruvbox/pull/47) ([ryanmjacobs](https://github.com/ryanmjacobs))
- Change color of vertical/horizontal seperators between split windows [\#45](https://github.com/morhetz/gruvbox/pull/45) ([deshtop](https://github.com/deshtop))
- Improve gruvbox with C code [\#34](https://github.com/morhetz/gruvbox/pull/34) ([gladiac](https://github.com/gladiac))
- Fix for linux console [\#27](https://github.com/morhetz/gruvbox/pull/27) ([vyp](https://github.com/vyp))
- Colors for plugin vimshell.vim [\#21](https://github.com/morhetz/gruvbox/pull/21) ([joelmo](https://github.com/joelmo))
## [v1.3.5](https://github.com/morhetz/gruvbox/tree/v1.3.5) (2014-03-19)
[Full Changelog](https://github.com/morhetz/gruvbox/compare/v0.0.8...v1.3.5)
**Implemented enhancements:**
- Better selection colors [\#15](https://github.com/morhetz/gruvbox/issues/15)
- When hlsearch is on, the cursor inverts the search color and not visible [\#2](https://github.com/morhetz/gruvbox/issues/2)
**Fixed bugs:**
- Problem with changing between dark and light on 256 color terminal [\#7](https://github.com/morhetz/gruvbox/issues/7)
- IndentGuides coloring doesn't show up [\#1](https://github.com/morhetz/gruvbox/issues/1)
**Closed issues:**
- Requesting rxvt-unicode theme [\#17](https://github.com/morhetz/gruvbox/issues/17)
- gruvbox\_256palette.sh gets reset \(gnome-terminal on Ubuntu\) [\#13](https://github.com/morhetz/gruvbox/issues/13)
- Powerline colors [\#12](https://github.com/morhetz/gruvbox/issues/12)
- Info necessary for making a port of this colorscheme [\#10](https://github.com/morhetz/gruvbox/issues/10)
**Merged pull requests:**
- Fix GNU screen detection for \*-bce [\#16](https://github.com/morhetz/gruvbox/pull/16) ([blueyed](https://github.com/blueyed))
- Added iTerm2 dark theme [\#11](https://github.com/morhetz/gruvbox/pull/11) ([Greduan](https://github.com/Greduan))
- Fix typo in Readme [\#5](https://github.com/morhetz/gruvbox/pull/5) ([ViViDboarder](https://github.com/ViViDboarder))
## [v0.0.8](https://github.com/morhetz/gruvbox/tree/v0.0.8) (2012-12-08)
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*

View File

@ -0,0 +1,110 @@
<p align="center"><img src="http://imgh.us/gruvbox_logo.svg"></p>
gruvbox is heavily inspired by [badwolf][], [jellybeans][] and [solarized][].
Designed as a bright theme with pastel 'retro groove' colors and light/dark mode switching in the way of [solarized][]. The main focus when developing gruvbox is to keep colors easily distinguishable, contrast enough and still pleasant for the eyes.
[badwolf]: https://github.com/sjl/badwolf
[jellybeans]: https://github.com/nanotech/jellybeans.vim
[solarized]: http://ethanschoonover.com/solarized
Attention
---------
1. [Read this first](https://github.com/morhetz/gruvbox/wiki/Terminal-specific)
2. Typeface from gallery is [Fantasque Sans Mono](https://github.com/belluzj/fantasque-sans)
3. Typeface from screenshots below is [Fira Mono](http://www.carrois.com/fira-4-1/)
Screenshots
-----------
Refer [Gallery][] for more syntax-specific screenshots.
[Gallery]: https://github.com/morhetz/gruvbox/wiki/Gallery
### Dark mode
![Screenshot Dark](http://i.imgur.com/GkIl8Fn.png)
### Light mode
![Screenshot Light](http://i.imgur.com/X75niEa.png)
### Airline theme
![Screenshot Airline](http://i.imgur.com/wRQceUR.png)
Palette
-------
### Dark mode
![Palette Dark](http://i.imgur.com/wa666xg.png)
### Light mode
![Palette Light](http://i.imgur.com/49qKyYW.png)
Contrast options
----------------
Refer [wiki section][] for contrast configuration and other options.
[wiki section]: https://github.com/morhetz/gruvbox/wiki/Configuration#ggruvbox_contrast_dark
![Contrast Options](http://i.imgur.com/5MSbe6T.png)
Documentation
-------------
Please check [wiki][] for installation details, terminal-specific setup, troubleshooting, configuration options and others.
[wiki]: https://github.com/morhetz/gruvbox/wiki
Features
--------
* Lots of style-customization options (contrast, color invertion, italics usage etc.)
* Extended filetype highlighting: Html, Xml, Vim (and ES6 with [yajs.vim](https://github.com/othree/yajs.vim)), Clojure, C, Python, JavaScript, CoffeeScript, Ruby, Objective-C, Go, Lua, MoonScript, Java, Markdown, Haskell
* Supported plugins: [EasyMotion][], [vim-sneak][], [Indent Guides][], [indentLine][], [Rainbow Parentheses][], [Airline][], [Lightline][], [GitGutter][], [Signify][], [ShowMarks][], [Signature][], [Syntastic][], [CtrlP][], [Startify][]
[EasyMotion]: https://github.com/Lokaltog/vim-easymotion
[vim-sneak]: https://github.com/justinmk/vim-sneak
[Indent Guides]: https://github.com/nathanaelkane/vim-indent-guides
[indentLine]: https://github.com/Yggdroot/indentLine
[Rainbow Parentheses]: https://github.com/kien/rainbow_parentheses.vim
[Airline]: https://github.com/bling/vim-airline
[Lightline]: https://github.com/itchyny/lightline.vim
[GitGutter]: https://github.com/airblade/vim-gitgutter
[Signify]: https://github.com/mhinz/vim-signify
[ShowMarks]: http://www.vim.org/scripts/script.php?script_id=152
[Signature]: https://github.com/kshenoy/vim-signature
[Syntastic]: https://github.com/scrooloose/syntastic
[CtrlP]: https://github.com/kien/ctrlp.vim
[Startify]: https://github.com/mhinz/vim-startify
Contributions
-------------
See [gruvbox-contrib][] repo for contributions, ports and extras.
[gruvbox-contrib]: https://github.com/morhetz/gruvbox-contrib
ToDo
----
* Filetype syntax highlighting (R, TeX, Swift, Erlang, Purescript and I'm still dissatisfied with CSS)
* Plugin support (MiniBufExplorer, Tagbar, Netrw, VimPLug)
Self-Promotion
--------------
If you like gruvbox follow the repository on
[GitHub](https://github.com/morhetz/gruvbox) and vote for it on
[vim.org](http://www.vim.org/scripts/script.php?script_id=4349).
License
-------
[MIT/X11][]
[MIT/X11]: https://en.wikipedia.org/wiki/MIT_License

View File

@ -0,0 +1,79 @@
" -----------------------------------------------------------------------------
" File: gruvbox.vim
" Description: Retro groove color scheme for Airline
" Author: morhetz <morhetz@gmail.com>
" Source: https://github.com/morhetz/gruvbox
" Last Modified: 22 Aug 2014
" -----------------------------------------------------------------------------
let g:airline#themes#gruvbox#palette = {}
function! airline#themes#gruvbox#refresh()
let M0 = airline#themes#get_highlight('Identifier')
let accents_group = airline#themes#get_highlight('Special')
let modified_group = [M0[0], '', M0[2], '', '']
let warning_group = airline#themes#get_highlight2(['Normal', 'bg'], ['Question', 'fg'])
let error_group = airline#themes#get_highlight2(['Normal', 'bg'], ['WarningMsg', 'fg'])
let s:N1 = airline#themes#get_highlight2(['Normal', 'bg'], ['StatusLineNC', 'bg'])
let s:N2 = airline#themes#get_highlight2(['StatusLineNC', 'bg'], ['StatusLineNC', 'fg'])
let s:N3 = airline#themes#get_highlight2(['StatusLineNC', 'bg'], ['CursorLine', 'bg'])
let g:airline#themes#gruvbox#palette.normal = airline#themes#generate_color_map(s:N1, s:N2, s:N3)
let g:airline#themes#gruvbox#palette.normal_modified = { 'airline_c': modified_group }
let g:airline#themes#gruvbox#palette.normal.airline_warning = warning_group
let g:airline#themes#gruvbox#palette.normal_modified.airline_warning = warning_group
let g:airline#themes#gruvbox#palette.normal.airline_error = error_group
let g:airline#themes#gruvbox#palette.normal_modified.airline_error = error_group
let s:I1 = airline#themes#get_highlight2(['Normal', 'bg'], ['Identifier', 'fg'])
let s:I2 = s:N2
let s:I3 = airline#themes#get_highlight2(['Normal', 'fg'], ['StatusLineNC', 'fg'])
let g:airline#themes#gruvbox#palette.insert = airline#themes#generate_color_map(s:I1, s:I2, s:I3)
let g:airline#themes#gruvbox#palette.insert_modified = g:airline#themes#gruvbox#palette.normal_modified
let g:airline#themes#gruvbox#palette.insert.airline_warning = g:airline#themes#gruvbox#palette.normal.airline_warning
let g:airline#themes#gruvbox#palette.insert_modified.airline_warning = g:airline#themes#gruvbox#palette.normal_modified.airline_warning
let g:airline#themes#gruvbox#palette.insert.airline_error = g:airline#themes#gruvbox#palette.normal.airline_error
let g:airline#themes#gruvbox#palette.insert_modified.airline_error = g:airline#themes#gruvbox#palette.normal_modified.airline_error
let s:R1 = airline#themes#get_highlight2(['Normal', 'bg'], ['Structure', 'fg'])
let s:R2 = s:I2
let s:R3 = s:I3
let g:airline#themes#gruvbox#palette.replace = airline#themes#generate_color_map(s:R1, s:R2, s:R3)
let g:airline#themes#gruvbox#palette.replace_modified = g:airline#themes#gruvbox#palette.normal_modified
let g:airline#themes#gruvbox#palette.replace.airline_warning = g:airline#themes#gruvbox#palette.normal.airline_warning
let g:airline#themes#gruvbox#palette.replace_modified.airline_warning = g:airline#themes#gruvbox#palette.normal_modified.airline_warning
let g:airline#themes#gruvbox#palette.replace.airline_error = g:airline#themes#gruvbox#palette.normal.airline_error
let g:airline#themes#gruvbox#palette.replace_modified.airline_error = g:airline#themes#gruvbox#palette.normal_modified.airline_error
let s:V1 = airline#themes#get_highlight2(['Normal', 'bg'], ['ModeMsg', 'fg'])
let s:V2 = s:N2
let s:V3 = airline#themes#get_highlight2(['Normal', 'bg'], ['TabLine', 'fg'])
let g:airline#themes#gruvbox#palette.visual = airline#themes#generate_color_map(s:V1, s:V2, s:V3)
let g:airline#themes#gruvbox#palette.visual_modified = { 'airline_c': [ s:V3[0], '', s:V3[2], '', '' ] }
let g:airline#themes#gruvbox#palette.visual.airline_warning = g:airline#themes#gruvbox#palette.normal.airline_warning
let g:airline#themes#gruvbox#palette.visual_modified.airline_warning = g:airline#themes#gruvbox#palette.normal_modified.airline_warning
let g:airline#themes#gruvbox#palette.visual.airline_error = g:airline#themes#gruvbox#palette.normal.airline_error
let g:airline#themes#gruvbox#palette.visual_modified.airline_error = g:airline#themes#gruvbox#palette.normal_modified.airline_error
let s:IA = airline#themes#get_highlight2(['TabLine', 'fg'], ['CursorLine', 'bg'])
let g:airline#themes#gruvbox#palette.inactive = airline#themes#generate_color_map(s:IA, s:IA, s:IA)
let g:airline#themes#gruvbox#palette.inactive_modified = { 'airline_c': modified_group }
let g:airline#themes#gruvbox#palette.accents = { 'red': accents_group }
let s:TF = airline#themes#get_highlight2(['Normal', 'bg'], ['Normal', 'bg'])
let g:airline#themes#gruvbox#palette.tabline = {
\ 'airline_tab': s:N2,
\ 'airline_tabsel': s:N1,
\ 'airline_tabtype': s:V1,
\ 'airline_tabfill': s:TF,
\ 'airline_tabhid': s:IA,
\ 'airline_tabmod': s:I1
\ }
endfunction
call airline#themes#gruvbox#refresh()
" vim: set sw=2 ts=2 sts=2 et tw=80 ft=vim fdm=marker:

View File

@ -0,0 +1,41 @@
" -----------------------------------------------------------------------------
" File: gruvbox.vim
" Description: Retro groove color scheme for Vim
" Author: morhetz <morhetz@gmail.com>
" Source: https://github.com/morhetz/gruvbox
" Last Modified: 09 Apr 2014
" -----------------------------------------------------------------------------
function! gruvbox#invert_signs_toggle()
if g:gruvbox_invert_signs == 0
let g:gruvbox_invert_signs=1
else
let g:gruvbox_invert_signs=0
endif
colorscheme gruvbox
endfunction
" Search Highlighting {{{
function! gruvbox#hls_show()
set hlsearch
call GruvboxHlsShowCursor()
endfunction
function! gruvbox#hls_hide()
set nohlsearch
call GruvboxHlsHideCursor()
endfunction
function! gruvbox#hls_toggle()
if &hlsearch
call gruvbox#hls_hide()
else
call gruvbox#hls_show()
endif
endfunction
" }}}
" vim: set sw=2 ts=2 sts=2 et tw=80 ft=vim fdm=marker:

View File

@ -0,0 +1,53 @@
" -----------------------------------------------------------------------------
" File: gruvbox.vim
" Description: Gruvbox colorscheme for Lightline (itchyny/lightline.vim)
" Author: gmoe <me@griffinmoe.com>
" Source: https://github.com/morhetz/gruvbox
" Last Modified: 31 Oct 2015
" -----------------------------------------------------------------------------
function! s:getGruvColor(group)
let guiColor = synIDattr(hlID(a:group), "fg", "gui")
let termColor = synIDattr(hlID(a:group), "fg", "cterm")
return [ guiColor, termColor ]
endfunction
if exists('g:lightline')
let s:bg0 = s:getGruvColor('GruvboxBg0')
let s:bg1 = s:getGruvColor('GruvboxBg1')
let s:bg2 = s:getGruvColor('GruvboxBg2')
let s:bg4 = s:getGruvColor('GruvboxBg4')
let s:fg1 = s:getGruvColor('GruvboxFg1')
let s:fg4 = s:getGruvColor('GruvboxFg4')
let s:yellow = s:getGruvColor('GruvboxYellow')
let s:blue = s:getGruvColor('GruvboxBlue')
let s:aqua = s:getGruvColor('GruvboxAqua')
let s:orange = s:getGruvColor('GruvboxOrange')
let s:p = {'normal':{}, 'inactive':{}, 'insert':{}, 'replace':{}, 'visual':{}, 'tabline':{}}
let s:p.normal.left = [ [ s:bg0, s:fg4 ], [ s:fg4, s:bg2 ] ]
let s:p.normal.right = [ [ s:bg0, s:fg4 ], [ s:fg4, s:bg2 ] ]
let s:p.normal.middle = [ [ s:fg4, s:bg1 ] ]
let s:p.inactive.right = [ [ s:bg4, s:bg1 ], [ s:bg4, s:bg1 ] ]
let s:p.inactive.left = [ [ s:bg4, s:bg1 ], [ s:bg4, s:bg1 ] ]
let s:p.inactive.middle = [ [ s:bg4, s:bg1 ] ]
let s:p.insert.left = [ [ s:bg0, s:blue ], [ s:fg1, s:bg2 ] ]
let s:p.insert.right = [ [ s:bg0, s:blue ], [ s:fg1, s:bg2 ] ]
let s:p.insert.middle = [ [ s:fg4, s:bg2 ] ]
let s:p.replace.left = [ [ s:bg0, s:aqua ], [ s:fg1, s:bg2 ] ]
let s:p.replace.right = [ [ s:bg0, s:aqua ], [ s:fg1, s:bg2 ] ]
let s:p.replace.middle = [ [ s:fg4, s:bg2 ] ]
let s:p.visual.left = [ [ s:bg0, s:orange ], [ s:bg0, s:bg4 ] ]
let s:p.visual.right = [ [ s:bg0, s:orange ], [ s:bg0, s:bg4 ] ]
let s:p.visual.middle = [ [ s:fg4, s:bg1 ] ]
let s:p.tabline.left = [ [ s:fg4, s:bg2 ] ]
let s:p.tabline.tabsel = [ [ s:bg0, s:fg4 ] ]
let s:p.tabline.middle = [ [ s:bg0, s:bg0 ] ]
let s:p.tabline.right = [ [ s:bg0, s:orange ] ]
let s:p.normal.error = [ [ s:bg0, s:orange ] ]
let s:p.normal.warning = [ [ s:bg2, s:yellow ] ]
let g:lightline#colorscheme#gruvbox#palette = lightline#colorscheme#flatten(s:p)
endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,118 @@
#!/bin/sh
if [ "${TERM%%-*}" = "screen" ]; then
if [ -n "$TMUX" ]; then
printf "\033Ptmux;\033\033]4;236;rgb:32/30/2f\007\033\\"
printf "\033Ptmux;\033\033]4;234;rgb:1d/20/21\007\033\\"
printf "\033Ptmux;\033\033]4;235;rgb:28/28/28\007\033\\"
printf "\033Ptmux;\033\033]4;237;rgb:3c/38/36\007\033\\"
printf "\033Ptmux;\033\033]4;239;rgb:50/49/45\007\033\\"
printf "\033Ptmux;\033\033]4;241;rgb:66/5c/54\007\033\\"
printf "\033Ptmux;\033\033]4;243;rgb:7c/6f/64\007\033\\"
printf "\033Ptmux;\033\033]4;244;rgb:92/83/74\007\033\\"
printf "\033Ptmux;\033\033]4;245;rgb:92/83/74\007\033\\"
printf "\033Ptmux;\033\033]4;228;rgb:f2/e5/bc\007\033\\"
printf "\033Ptmux;\033\033]4;230;rgb:f9/f5/d7\007\033\\"
printf "\033Ptmux;\033\033]4;229;rgb:fb/f1/c7\007\033\\"
printf "\033Ptmux;\033\033]4;223;rgb:eb/db/b2\007\033\\"
printf "\033Ptmux;\033\033]4;250;rgb:d5/c4/a1\007\033\\"
printf "\033Ptmux;\033\033]4;248;rgb:bd/ae/93\007\033\\"
printf "\033Ptmux;\033\033]4;246;rgb:a8/99/84\007\033\\"
printf "\033Ptmux;\033\033]4;167;rgb:fb/49/34\007\033\\"
printf "\033Ptmux;\033\033]4;142;rgb:b8/bb/26\007\033\\"
printf "\033Ptmux;\033\033]4;214;rgb:fa/bd/2f\007\033\\"
printf "\033Ptmux;\033\033]4;109;rgb:83/a5/98\007\033\\"
printf "\033Ptmux;\033\033]4;175;rgb:d3/86/9b\007\033\\"
printf "\033Ptmux;\033\033]4;108;rgb:8e/c0/7c\007\033\\"
printf "\033Ptmux;\033\033]4;208;rgb:fe/80/19\007\033\\"
printf "\033Ptmux;\033\033]4;88;rgb:9d/00/06\007\033\\"
printf "\033Ptmux;\033\033]4;100;rgb:79/74/0e\007\033\\"
printf "\033Ptmux;\033\033]4;136;rgb:b5/76/14\007\033\\"
printf "\033Ptmux;\033\033]4;24;rgb:07/66/78\007\033\\"
printf "\033Ptmux;\033\033]4;96;rgb:8f/3f/71\007\033\\"
printf "\033Ptmux;\033\033]4;66;rgb:42/7b/58\007\033\\"
printf "\033Ptmux;\033\033]4;130;rgb:af/3a/03\007\033\\"
else
printf "\033P\033]4;236;rgb:32/30/2f\007\033\\"
printf "\033P\033]4;234;rgb:1d/20/21\007\033\\"
printf "\033P\033]4;235;rgb:28/28/28\007\033\\"
printf "\033P\033]4;237;rgb:3c/38/36\007\033\\"
printf "\033P\033]4;239;rgb:50/49/45\007\033\\"
printf "\033P\033]4;241;rgb:66/5c/54\007\033\\"
printf "\033P\033]4;243;rgb:7c/6f/64\007\033\\"
printf "\033P\033]4;244;rgb:92/83/74\007\033\\"
printf "\033P\033]4;245;rgb:92/83/74\007\033\\"
printf "\033P\033]4;228;rgb:f2/e5/bc\007\033\\"
printf "\033P\033]4;230;rgb:f9/f5/d7\007\033\\"
printf "\033P\033]4;229;rgb:fb/f1/c7\007\033\\"
printf "\033P\033]4;223;rgb:eb/db/b2\007\033\\"
printf "\033P\033]4;250;rgb:d5/c4/a1\007\033\\"
printf "\033P\033]4;248;rgb:bd/ae/93\007\033\\"
printf "\033P\033]4;246;rgb:a8/99/84\007\033\\"
printf "\033P\033]4;167;rgb:fb/49/34\007\033\\"
printf "\033P\033]4;142;rgb:b8/bb/26\007\033\\"
printf "\033P\033]4;214;rgb:fa/bd/2f\007\033\\"
printf "\033P\033]4;109;rgb:83/a5/98\007\033\\"
printf "\033P\033]4;175;rgb:d3/86/9b\007\033\\"
printf "\033P\033]4;108;rgb:8e/c0/7c\007\033\\"
printf "\033P\033]4;208;rgb:fe/80/19\007\033\\"
printf "\033P\033]4;88;rgb:9d/00/06\007\033\\"
printf "\033P\033]4;100;rgb:79/74/0e\007\033\\"
printf "\033P\033]4;136;rgb:b5/76/14\007\033\\"
printf "\033P\033]4;24;rgb:07/66/78\007\033\\"
printf "\033P\033]4;96;rgb:8f/3f/71\007\033\\"
printf "\033P\033]4;66;rgb:42/7b/58\007\033\\"
printf "\033P\033]4;130;rgb:af/3a/03\007\033\\"
fi
elif [ "$TERM" != "linux" ] && [ "$TERM" != "vt100" ] && [ "$TERM" != "vt220" ]; then
printf "\033]4;236;rgb:32/30/2f\033\\"
printf "\033]4;234;rgb:1d/20/21\033\\"
printf "\033]4;235;rgb:28/28/28\033\\"
printf "\033]4;237;rgb:3c/38/36\033\\"
printf "\033]4;239;rgb:50/49/45\033\\"
printf "\033]4;241;rgb:66/5c/54\033\\"
printf "\033]4;243;rgb:7c/6f/64\033\\"
printf "\033]4;244;rgb:92/83/74\033\\"
printf "\033]4;245;rgb:92/83/74\033\\"
printf "\033]4;228;rgb:f2/e5/bc\033\\"
printf "\033]4;230;rgb:f9/f5/d7\033\\"
printf "\033]4;229;rgb:fb/f1/c7\033\\"
printf "\033]4;223;rgb:eb/db/b2\033\\"
printf "\033]4;250;rgb:d5/c4/a1\033\\"
printf "\033]4;248;rgb:bd/ae/93\033\\"
printf "\033]4;246;rgb:a8/99/84\033\\"
printf "\033]4;167;rgb:fb/49/34\033\\"
printf "\033]4;142;rgb:b8/bb/26\033\\"
printf "\033]4;214;rgb:fa/bd/2f\033\\"
printf "\033]4;109;rgb:83/a5/98\033\\"
printf "\033]4;175;rgb:d3/86/9b\033\\"
printf "\033]4;108;rgb:8e/c0/7c\033\\"
printf "\033]4;208;rgb:fe/80/19\033\\"
printf "\033]4;88;rgb:9d/00/06\033\\"
printf "\033]4;100;rgb:79/74/0e\033\\"
printf "\033]4;136;rgb:b5/76/14\033\\"
printf "\033]4;24;rgb:07/66/78\033\\"
printf "\033]4;96;rgb:8f/3f/71\033\\"
printf "\033]4;66;rgb:42/7b/58\033\\"
printf "\033]4;130;rgb:af/3a/03\033\\"
fi

View File

@ -0,0 +1,116 @@
#!/bin/sh
if [ "${TERM%%-*}" = "screen" ]; then
if [ -n "$TMUX" ]; then
printf "\033Ptmux;\033\033]4;236;rgb:26/24/23\007\033\\"
printf "\033Ptmux;\033\033]4;234;rgb:16/18/19\007\033\\"
printf "\033Ptmux;\033\033]4;235;rgb:1e/1e/1e\007\033\\"
printf "\033Ptmux;\033\033]4;237;rgb:2e/2a/29\007\033\\"
printf "\033Ptmux;\033\033]4;239;rgb:3f/39/35\007\033\\"
printf "\033Ptmux;\033\033]4;241;rgb:53/4a/42\007\033\\"
printf "\033Ptmux;\033\033]4;243;rgb:68/5c/51\007\033\\"
printf "\033Ptmux;\033\033]4;244;rgb:7f/70/61\007\033\\"
printf "\033Ptmux;\033\033]4;245;rgb:7f/70/61\007\033\\"
printf "\033Ptmux;\033\033]4;228;rgb:ef/df/ae\007\033\\"
printf "\033Ptmux;\033\033]4;230;rgb:f8/f4/cd\007\033\\"
printf "\033Ptmux;\033\033]4;229;rgb:fa/ee/bb\007\033\\"
printf "\033Ptmux;\033\033]4;223;rgb:e6/d4/a3\007\033\\"
printf "\033Ptmux;\033\033]4;250;rgb:cb/b8/90\007\033\\"
printf "\033Ptmux;\033\033]4;248;rgb:af/9f/81\007\033\\"
printf "\033Ptmux;\033\033]4;246;rgb:97/87/71\007\033\\"
printf "\033Ptmux;\033\033]4;167;rgb:f7/30/28\007\033\\"
printf "\033Ptmux;\033\033]4;142;rgb:aa/b0/1e\007\033\\"
printf "\033Ptmux;\033\033]4;214;rgb:f7/b1/25\007\033\\"
printf "\033Ptmux;\033\033]4;109;rgb:71/95/86\007\033\\"
printf "\033Ptmux;\033\033]4;175;rgb:c7/70/89\007\033\\"
printf "\033Ptmux;\033\033]4;108;rgb:7d/b6/69\007\033\\"
printf "\033Ptmux;\033\033]4;208;rgb:fb/6a/16\007\033\\"
printf "\033Ptmux;\033\033]4;88;rgb:89/00/09\007\033\\"
printf "\033Ptmux;\033\033]4;100;rgb:66/62/0d\007\033\\"
printf "\033Ptmux;\033\033]4;136;rgb:a5/63/11\007\033\\"
printf "\033Ptmux;\033\033]4;24;rgb:0e/53/65\007\033\\"
printf "\033Ptmux;\033\033]4;96;rgb:7b/2b/5e\007\033\\"
printf "\033Ptmux;\033\033]4;66;rgb:35/6a/46\007\033\\"
printf "\033Ptmux;\033\033]4;130;rgb:9d/28/07\007\033\\"
else
printf "\033P\033]4;236;rgb:26/24/23\007\033\\"
printf "\033P\033]4;234;rgb:16/18/19\007\033\\"
printf "\033P\033]4;235;rgb:1e/1e/1e\007\033\\"
printf "\033P\033]4;237;rgb:2e/2a/29\007\033\\"
printf "\033P\033]4;239;rgb:3f/39/35\007\033\\"
printf "\033P\033]4;241;rgb:53/4a/42\007\033\\"
printf "\033P\033]4;243;rgb:68/5c/51\007\033\\"
printf "\033P\033]4;244;rgb:7f/70/61\007\033\\"
printf "\033P\033]4;245;rgb:7f/70/61\007\033\\"
printf "\033P\033]4;228;rgb:ef/df/ae\007\033\\"
printf "\033P\033]4;230;rgb:f8/f4/cd\007\033\\"
printf "\033P\033]4;229;rgb:fa/ee/bb\007\033\\"
printf "\033P\033]4;223;rgb:e6/d4/a3\007\033\\"
printf "\033P\033]4;250;rgb:cb/b8/90\007\033\\"
printf "\033P\033]4;248;rgb:af/9f/81\007\033\\"
printf "\033P\033]4;246;rgb:97/87/71\007\033\\"
printf "\033P\033]4;167;rgb:f7/30/28\007\033\\"
printf "\033P\033]4;142;rgb:aa/b0/1e\007\033\\"
printf "\033P\033]4;214;rgb:f7/b1/25\007\033\\"
printf "\033P\033]4;109;rgb:71/95/86\007\033\\"
printf "\033P\033]4;175;rgb:c7/70/89\007\033\\"
printf "\033P\033]4;108;rgb:7d/b6/69\007\033\\"
printf "\033P\033]4;208;rgb:fb/6a/16\007\033\\"
printf "\033P\033]4;88;rgb:89/00/09\007\033\\"
printf "\033P\033]4;100;rgb:66/62/0d\007\033\\"
printf "\033P\033]4;136;rgb:a5/63/11\007\033\\"
printf "\033P\033]4;24;rgb:0e/53/65\007\033\\"
printf "\033P\033]4;96;rgb:7b/2b/5e\007\033\\"
printf "\033P\033]4;66;rgb:35/6a/46\007\033\\"
printf "\033P\033]4;130;rgb:9d/28/07\007\033\\"
fi
else
printf "\033]4;236;rgb:26/24/23\033\\"
printf "\033]4;234;rgb:16/18/19\033\\"
printf "\033]4;235;rgb:1e/1e/1e\033\\"
printf "\033]4;237;rgb:2e/2a/29\033\\"
printf "\033]4;239;rgb:3f/39/35\033\\"
printf "\033]4;241;rgb:53/4a/42\033\\"
printf "\033]4;243;rgb:68/5c/51\033\\"
printf "\033]4;244;rgb:7f/70/61\033\\"
printf "\033]4;245;rgb:7f/70/61\033\\"
printf "\033]4;228;rgb:ef/df/ae\033\\"
printf "\033]4;230;rgb:f8/f4/cd\033\\"
printf "\033]4;229;rgb:fa/ee/bb\033\\"
printf "\033]4;223;rgb:e6/d4/a3\033\\"
printf "\033]4;250;rgb:cb/b8/90\033\\"
printf "\033]4;248;rgb:af/9f/81\033\\"
printf "\033]4;246;rgb:97/87/71\033\\"
printf "\033]4;167;rgb:f7/30/28\033\\"
printf "\033]4;142;rgb:aa/b0/1e\033\\"
printf "\033]4;214;rgb:f7/b1/25\033\\"
printf "\033]4;109;rgb:71/95/86\033\\"
printf "\033]4;175;rgb:c7/70/89\033\\"
printf "\033]4;108;rgb:7d/b6/69\033\\"
printf "\033]4;208;rgb:fb/6a/16\033\\"
printf "\033]4;88;rgb:89/00/09\033\\"
printf "\033]4;100;rgb:66/62/0d\033\\"
printf "\033]4;136;rgb:a5/63/11\033\\"
printf "\033]4;24;rgb:0e/53/65\033\\"
printf "\033]4;96;rgb:7b/2b/5e\033\\"
printf "\033]4;66;rgb:35/6a/46\033\\"
printf "\033]4;130;rgb:9d/28/07\033\\"
fi

@ -1 +0,0 @@
Subproject commit 28a989b28457e38df620e4c7ab23e224aff70efe

@ -1 +0,0 @@
Subproject commit 339f8ba079ed7d465ca442c9032b36bc56c21f61

View File

@ -0,0 +1,5 @@
/doc/tags
/misc
/test/*.actual
*.log

View File

@ -0,0 +1,499 @@
## vim-gitgutter
A Vim plugin which shows a git diff in the 'gutter' (sign column). It shows whether each line has been added, modified, and where lines have been removed. You can also stage and undo individual hunks.
Features:
* Shows signs for added, modified, and removed lines.
* Runs the diffs asynchronously in terminal Vim/MacVim (7.4.1826+), gVim (7.4.1850+), MacVim GUI (7.4.1832+), and NeoVim.
* Ensures signs are always as up to date as possible (but without running more than necessary).
* Quick jumping between blocks of changed lines ("hunks").
* Stage/undo/preview individual hunks.
* Provides a hunk text object.
* Diffs against index (default) or any commit.
* Handles line endings correctly, even with repos that do CRLF conversion.
* Optional line highlighting.
* Fully customisable (signs, sign column, line highlights, mappings, extra git-diff arguments, etc).
* Can be toggled on/off.
* Preserves signs from other plugins.
* 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).
### Screenshot
![screenshot](https://raw.github.com/airblade/vim-gitgutter/master/screenshot.png)
In the screenshot above you can see:
* Line 15 has been modified.
* Lines 21-24 are new.
* A line or lines were removed between lines 25 and 26.
### Installation
Before installation, please check your Vim supports signs by running `:echo has('signs')`. `1` means you're all set; `0` means you need to install a Vim with signs support. If you're compiling Vim yourself you need the 'big' or 'huge' feature set. [MacVim][] supports signs.
You install vim-gitgutter like any other vim plugin.
##### Pathogen
```
cd ~/.vim/bundle
git clone git://github.com/airblade/vim-gitgutter.git
```
##### Voom
Edit your plugin manifest (`voom edit`) and add:
```
airblade/vim-gitgutter
```
##### VimPlug
Place this in your .vimrc:
```viml
Plug 'airblade/vim-gitgutter'
```
Then run the following in Vim:
```
:source %
:PlugInstall
```
##### NeoBundle
Place this in your .vimrc:
```viml
NeoBundle 'airblade/vim-gitgutter'
```
Then run the following in Vim:
```
:source %
:NeoBundleInstall
```
##### No plugin manager
Copy vim-gitgutter's subdirectories into your vim configuration directory:
```
cd /tmp && git clone git://github.com/airblade/vim-gitgutter.git
cp -r vim-gitgutter/* ~/.vim/
```
See `:help add-global-plugin`.
If you are on Windows you may find the command prompt pops up briefly every time vim-gitgutter runs. You can avoid this by installing both [vim-misc](https://github.com/xolox/vim-misc) and [vim-shell](https://github.com/xolox/vim-shell). If you have those two plugins but don't want vim-gitgutter to use them, you can opt out with `let g:gitgutter_avoid_cmd_prompt_on_windows = 0` in your `~/.vimrc`.
### Getting started
When you make a change to a file tracked by git, the diff markers should appear automatically. The delay is governed by vim's `updatetime` option; the default value is 4 seconds but I suggest reducing it to around 250ms (add `set updatetime=250` to your vimrc).
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 currently unstage a staged hunk.
#### Activation
You can explicitly turn vim-gitgutter off and on (defaults to on):
* turn off with `:GitGutterDisable`
* turn on with `:GitGutterEnable`
* toggle with `:GitGutterToggle`.
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 always shows the sign column even if the signs are invisible.
If you switch off both line highlighting and signs, you won't see the sign column. That is unless you have set `let g:gitgutter_sign_column_always = 1` so it's always there.
To keep your Vim snappy, vim-gitgutter will suppress itself when a file has more than 500 changes. As soon as the number of changes falls below the limit vim-gitgutter will show the signs again. You can configure the threshold with:
```viml
let g:gitgutter_max_signs = 500 " default value
```
#### 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
```
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`.
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 the mnemonics hunk-add and hunk-revert:
```viml
nmap <Leader>ha <Plug>GitGutterStageHunk
nmap <Leader>hr <Plug>GitGutterUndoHunk
```
And you can preview a hunk's changes with `<Leader>hp`. You can of course change this mapping, e.g:
```viml
nmap <Leader>hv <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.
### When are the signs updated?
By default the signs are updated as follows:
| Event | Reason for update | Configuration |
|---------------------------|--------------------------------------|------------------------|
| Stop typing | So the signs are real time | `g:gitgutter_realtime` |
| Switch buffer | To notice change to git index | `g:gitgutter_eager` |
| Switch tab | To notice change to git index | `g:gitgutter_eager` |
| Focus the GUI | To notice change to git index | `g:gitgutter_eager` (not gVim on Windows) |
| Read a file into a buffer | To display initial signs | [always] |
| Save a buffer | So non-realtime signs are up to date | [always] |
| Change a file outside Vim | To notice `git stash` | [always] |
The length of time Vim waits after you stop typing before it triggers the plugin is governed by the setting `updatetime`. This defaults to `4000` milliseconds which is rather too long. I recommend around `250` milliseconds but it depends on your system and your preferences. Note that in terminal Vim pre-7.4.427 an `updatetime` of less than approximately `1000` milliseconds can lead to random highlighting glitches; the lower the `updatetime`, the more glitches.
If you experience a lag, you can trade speed for accuracy:
```viml
let g:gitgutter_realtime = 0
let g:gitgutter_eager = 0
```
Note the realtime updating requires Vim 7.3.105 or higher.
### 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)
* The signs' colours and symbols
* Line highlights
* The base of the diff
* Extra arguments for `git diff`
* Key mappings
* Whether or not vim-gitgutter is on initially (defaults to on)
* Whether or not signs are shown (defaults to yes)
* Whether or not line highlighting is on initially (defaults to off)
* Whether or not vim-gitgutter runs in "realtime" (defaults to yes)
* Whether or not vim-gitgutter runs eagerly (defaults to yes)
* Whether or not vim-gitgutter runs asynchronously (defaults to yes)
Please note that vim-gitgutter won't override any colours or highlights you've set in your colorscheme.
#### Sign column
By default vim-gitgutter will make the sign column look like the line number column.
To customise your sign column's background color, first tell vim-gitgutter to leave it alone:
```viml
let g:gitgutter_override_sign_column_highlight = 0
```
And then either update your colorscheme's `SignColumn` highlight group or set it in your vimrc:
```viml
highlight SignColumn ctermbg=whatever " terminal Vim
highlight SignColumn guibg=whatever " gVim/MacVim
```
By default the sign column will appear when there are signs to show and disappear when there aren't. If you would always like the sign column to be there, add `let g:gitgutter_sign_column_always = 1` to your `~/.vimrc`.
#### Signs' colours and symbols
To customise the colours, set up the following highlight groups in your colorscheme or `~/.vimrc`:
```viml
GitGutterAdd " an added line
GitGutterChange " a changed line
GitGutterDelete " at least one removed line
GitGutterChangeDelete " a changed line followed by at least one removed line
```
You can either set these with `highlight GitGutterAdd {key}={arg}...` or link them to existing highlight groups with, say, `highlight link GitGutterAdd DiffAdd`.
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_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 GitGutterChangeLineDefault, i.e. DiffChange
```
#### 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>'
```
#### 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.
#### Use a custom `grep` command
If you use an alternative to grep, or your grep does not support the `color` flag, you can tell vim-gitgutter to use it here. It only needs to support extended POSIX regex.
```viml
" Default:
let g:gitgutter_grep_command = 'grep --color=never -e'
```
#### 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 off asynchronous updates
By default diffs are run asynchronously. To run diffs synchronously instead:
```viml
let g:gitgutter_async = 0
```
### 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\+$//
```
### FAQ
> Why can't I unstage staged changes?
Unstaging staged hunks is feasible but not quite as easy as it sounds. There are three relevant versions of a file at any one time:
1. The version at HEAD in the repo.
2. The version staged in the index.
3. The version in the working tree, in your vim buffer.
`git-diff` without arguments shows you how 3 and 2 differ; this is what vim-gitgutter shows too.
`git-diff --staged` shows you how 2 and 1 differ.
Let's say you are looking at a file in vim which has some unstaged changes. Now you stage a hunk, either via vim-gitgutter or another means. The hunk is no longer marked in vim-gitgutter because it is the same in 3 and 2.
Now you want to unstage that hunk. To see it, you need the difference between 2 and 1. For vim-gitgutter to show those differences, it would need to show you 2 instead of 3 in your vim buffer. But 2 is virtual so vim-gitgutter would need to handle it without touching 3.
I intend to implement this but I can't commit to any deadline.
> 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.
> There's a noticeable lag when vim-gitter runs; how can I avoid it?
By default vim-gitgutter runs often so the signs are as accurate as possible. The delay is governed by `updatetime`; see [above](#when-are-the-signs-updated) for more information.
If you don't want realtime updates and would like to trade a little accuracy for speed, add this to your `~/.vimrc`:
```viml
let g:gitgutter_realtime = 0
let g:gitgutter_eager = 0
```
> What happens if I also use another plugin which uses signs (e.g. Syntastic)?
Vim only allows one sign per line. Before adding a sign to a line, vim-gitgutter checks whether a sign has already been added by somebody else. If so it doesn't do anything. In other words vim-gitgutter won't overwrite another plugin's signs. It also won't remove another plugin's signs.
> Why aren't any signs showing at all?
Here are some things you can check:
* `:echo system("git --version")` succeeds.
* Your git config is compatible with the version of git returned by the command above.
* Your Vim supports signs (`:echo has('signs')` should give `1`).
* Your file is being tracked by git and has unstaged changes.
* If your grep does not support the `color` flag, add `let g:gitgutter_grep_command = 'grep -e'` to your `~/.vimrc`.
### 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.
You can read reviews on my [website][airblade].
### 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
[airblade]: http://airbladesoftware.com/peepcode-vim
[macvim]: http://code.google.com/p/macvim/

View File

@ -0,0 +1,253 @@
let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : ''
" Primary functions {{{
function! gitgutter#all() abort
for buffer_id in gitgutter#utility#dedup(tabpagebuflist())
let file = expand('#' . buffer_id . ':p')
if !empty(file)
call gitgutter#process_buffer(buffer_id, 0)
endif
endfor
endfunction
" bufnr: (integer) the buffer to process.
" realtime: (boolean) when truthy, do a realtime diff; otherwise do a disk-based diff.
function! gitgutter#process_buffer(bufnr, realtime) abort
call gitgutter#utility#use_known_shell()
call gitgutter#utility#set_buffer(a:bufnr)
if gitgutter#utility#is_active()
if g:gitgutter_sign_column_always
call gitgutter#sign#add_dummy_sign()
endif
try
if !a:realtime || gitgutter#utility#has_fresh_changes()
let diff = gitgutter#diff#run_diff(a:realtime || gitgutter#utility#has_unsaved_changes(), 0)
if diff != 'async'
call gitgutter#handle_diff(diff)
endif
endif
catch /diff failed/
call gitgutter#debug#log('diff failed')
call gitgutter#hunk#reset()
endtry
execute "silent doautocmd" s:nomodeline "User GitGutter"
else
call gitgutter#hunk#reset()
endif
call gitgutter#utility#restore_shell()
endfunction
function! gitgutter#handle_diff(diff) abort
call gitgutter#debug#log(a:diff)
call setbufvar(gitgutter#utility#bufnr(), 'gitgutter_tracked', 1)
call gitgutter#hunk#set_hunks(gitgutter#diff#parse_diff(a:diff))
let modified_lines = gitgutter#diff#process_hunks(gitgutter#hunk#hunks())
if len(modified_lines) > g:gitgutter_max_signs
call gitgutter#utility#warn_once('exceeded maximum number of signs (configured by g:gitgutter_max_signs).', 'max_signs')
call gitgutter#sign#clear_signs()
return
endif
if g:gitgutter_signs || g:gitgutter_highlight_lines
call gitgutter#sign#update_signs(modified_lines)
endif
call gitgutter#utility#save_last_seen_change()
endfunction
function! gitgutter#disable() abort
" get list of all buffers (across all tabs)
let buflist = []
for i in range(tabpagenr('$'))
call extend(buflist, tabpagebuflist(i + 1))
endfor
for buffer_id in gitgutter#utility#dedup(buflist)
let file = expand('#' . buffer_id . ':p')
if !empty(file)
call gitgutter#utility#set_buffer(buffer_id)
call gitgutter#sign#clear_signs()
call gitgutter#sign#remove_dummy_sign(1)
call gitgutter#hunk#reset()
endif
endfor
let g:gitgutter_enabled = 0
endfunction
function! gitgutter#enable() abort
let g:gitgutter_enabled = 1
call gitgutter#all()
endfunction
function! gitgutter#toggle() abort
if g:gitgutter_enabled
call gitgutter#disable()
else
call gitgutter#enable()
endif
endfunction
" }}}
" Line highlights {{{
function! gitgutter#line_highlights_disable() abort
let g:gitgutter_highlight_lines = 0
call gitgutter#highlight#define_sign_line_highlights()
if !g:gitgutter_signs
call gitgutter#sign#clear_signs()
call gitgutter#sign#remove_dummy_sign(0)
endif
redraw!
endfunction
function! gitgutter#line_highlights_enable() abort
let old_highlight_lines = g:gitgutter_highlight_lines
let g:gitgutter_highlight_lines = 1
call gitgutter#highlight#define_sign_line_highlights()
if !old_highlight_lines && !g:gitgutter_signs
call gitgutter#all()
endif
redraw!
endfunction
function! gitgutter#line_highlights_toggle() abort
if g:gitgutter_highlight_lines
call gitgutter#line_highlights_disable()
else
call gitgutter#line_highlights_enable()
endif
endfunction
" }}}
" Signs {{{
function! gitgutter#signs_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
call gitgutter#all()
endif
endfunction
function! gitgutter#signs_disable() abort
let g:gitgutter_signs = 0
call gitgutter#highlight#define_sign_text_highlights()
if !g:gitgutter_highlight_lines
call gitgutter#sign#clear_signs()
call gitgutter#sign#remove_dummy_sign(0)
endif
endfunction
function! gitgutter#signs_toggle() abort
if g:gitgutter_signs
call gitgutter#signs_disable()
else
call gitgutter#signs_enable()
endif
endfunction
" }}}
" Hunks {{{
function! gitgutter#stage_hunk() abort
call gitgutter#utility#use_known_shell()
if gitgutter#utility#is_active()
" Ensure the working copy of the file is up to date.
" It doesn't make sense to stage a hunk otherwise.
noautocmd silent write
let diff = gitgutter#diff#run_diff(0, 1)
call gitgutter#handle_diff(diff)
if empty(gitgutter#hunk#current_hunk())
call gitgutter#utility#warn('cursor is not in a hunk')
else
let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(diff, 'stage')
call gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file(g:gitgutter_git_executable.' apply --cached --unidiff-zero - '), diff_for_hunk)
" refresh gitgutter's view of buffer
silent execute "GitGutter"
endif
silent! call repeat#set("\<Plug>GitGutterStageHunk", -1)<CR>
endif
call gitgutter#utility#restore_shell()
endfunction
function! gitgutter#undo_hunk() abort
call gitgutter#utility#use_known_shell()
if gitgutter#utility#is_active()
" Ensure the working copy of the file is up to date.
" It doesn't make sense to stage a hunk otherwise.
noautocmd silent write
let diff = gitgutter#diff#run_diff(0, 1)
call gitgutter#handle_diff(diff)
if empty(gitgutter#hunk#current_hunk())
call gitgutter#utility#warn('cursor is not in a hunk')
else
let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(diff, 'undo')
call gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file(g:gitgutter_git_executable.' apply --reverse --unidiff-zero - '), diff_for_hunk)
" reload file preserving screen line position
let wl = winline()
silent edit
let offset = wl - winline()
execute "normal! ".offset."\<C-Y>"
endif
silent! call repeat#set("\<Plug>GitGutterUndoHunk", -1)<CR>
endif
call gitgutter#utility#restore_shell()
endfunction
function! gitgutter#preview_hunk() abort
call gitgutter#utility#use_known_shell()
if gitgutter#utility#is_active()
" Ensure the working copy of the file is up to date.
" It doesn't make sense to stage a hunk otherwise.
noautocmd silent write
let diff = gitgutter#diff#run_diff(0, 1)
call gitgutter#handle_diff(diff)
if empty(gitgutter#hunk#current_hunk())
call gitgutter#utility#warn('cursor is not in a hunk')
else
let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(diff, 'preview')
silent! wincmd P
if !&previewwindow
noautocmd execute 'bo' &previewheight 'new'
set previewwindow
endif
setlocal noro modifiable filetype=diff buftype=nofile bufhidden=delete noswapfile
execute "%delete_"
call append(0, split(diff_for_hunk, "\n"))
noautocmd wincmd p
endif
endif
call gitgutter#utility#restore_shell()
endfunction
" }}}

View File

@ -0,0 +1,211 @@
let s:jobs = {}
" Nvim has always supported async commands.
"
" Vim introduced async in 7.4.1826.
"
" gVim didn't support aync until 7.4.1850 (though I haven't been able to
" verify this myself).
"
" MacVim-GUI didn't support async until 7.4.1832 (actually commit
" 88f4fe0 but 7.4.1832 was the first subsequent patch release).
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'))
\ )
\ )
function! gitgutter#async#available()
return s:available
endfunction
function! gitgutter#async#execute(cmd) abort
let bufnr = gitgutter#utility#bufnr()
if has('nvim')
if has('unix')
let command = ["sh", "-c", a:cmd]
elseif has('win32')
let command = ["cmd.exe", "/c", a:cmd]
else
throw 'unknown os'
endif
" Make the job use a shell while avoiding (un)quoting problems.
let job_id = jobstart(command, {
\ 'buffer': bufnr,
\ 'on_stdout': function('gitgutter#async#handle_diff_job_nvim'),
\ 'on_stderr': function('gitgutter#async#handle_diff_job_nvim'),
\ 'on_exit': function('gitgutter#async#handle_diff_job_nvim')
\ })
call gitgutter#debug#log('[nvim job: '.job_id.', buffer: '.bufnr.'] '.a:cmd)
if job_id < 1
throw 'diff failed'
endif
" Note that when `cmd` doesn't produce any output, i.e. the diff is empty,
" the `stdout` event is not fired on the job handler. Therefore we keep
" track of the jobs ourselves so we can spot empty diffs.
call s:job_started(job_id)
else
" Make the job use a shell.
"
" Pass a handler for stdout but not for stderr so that errors are
" ignored (and thus signs are not updated; this assumes that an error
" only occurs when a file is not tracked by git).
if has('unix')
let command = ["sh", "-c", a:cmd]
elseif has('win32')
" Help docs recommend {command} be a string on Windows. But I think
" they also say that will run the command directly, which I believe would
" mean the redirection and pipe stuff wouldn't work.
" let command = "cmd.exe /c ".a:cmd
let command = ["cmd.exe", "/c", a:cmd]
else
throw 'unknown os'
endif
let job = job_start(command, {
\ 'out_cb': 'gitgutter#async#handle_diff_job_vim',
\ 'close_cb': 'gitgutter#async#handle_diff_job_vim_close'
\ })
call gitgutter#debug#log('[vim job: '.string(job_info(job)).', buffer: '.bufnr.'] '.a:cmd)
call s:job_started(s:channel_id(job_getchannel(job)), bufnr)
endif
endfunction
function! gitgutter#async#handle_diff_job_nvim(job_id, data, event) abort
call gitgutter#debug#log('job_id: '.a:job_id.', event: '.a:event.', buffer: '.self.buffer)
let job_bufnr = self.buffer
if bufexists(job_bufnr)
let current_buffer = gitgutter#utility#bufnr()
call gitgutter#utility#set_buffer(job_bufnr)
if a:event == 'stdout'
" a:data is a list
call s:job_finished(a:job_id)
if gitgutter#utility#is_active()
call gitgutter#handle_diff(gitgutter#utility#stringify(a:data))
endif
elseif a:event == 'exit'
" If the exit event is triggered without a preceding stdout event,
" the diff was empty.
if s:is_job_started(a:job_id)
if gitgutter#utility#is_active()
call gitgutter#handle_diff("")
endif
call s:job_finished(a:job_id)
endif
else " a:event is stderr
call gitgutter#hunk#reset()
call s:job_finished(a:job_id)
endif
call gitgutter#utility#set_buffer(current_buffer)
else
call s:job_finished(a:job_id)
endif
endfunction
" Channel is in NL mode.
function! gitgutter#async#handle_diff_job_vim(channel, line) abort
call gitgutter#debug#log('channel: '.a:channel.', line: '.a:line)
call s:accumulate_job_output(s:channel_id(a:channel), a:line)
endfunction
function! gitgutter#async#handle_diff_job_vim_close(channel) abort
call gitgutter#debug#log('channel: '.a:channel)
let channel_id = s:channel_id(a:channel)
let job_bufnr = s:job_buffer(channel_id)
if bufexists(job_bufnr)
let current_buffer = gitgutter#utility#bufnr()
call gitgutter#utility#set_buffer(job_bufnr)
if gitgutter#utility#is_active()
call gitgutter#handle_diff(s:job_output(channel_id))
endif
call gitgutter#utility#set_buffer(current_buffer)
endif
call s:job_finished(channel_id)
endfunction
function! s:channel_id(channel) abort
return ch_info(a:channel)['id']
endfunction
"
" Keep track of jobs.
"
" nvim: receives all the job's output at once so we don't need to accumulate
" it ourselves. We can pass the buffer number into the job so we don't need
" to track that either.
"
" s:jobs {} -> key: job's id, value: anything truthy
"
" vim: receives the job's output line by line so we need to accumulate it.
" We also need to keep track of the buffer the job is running for.
" Vim job's don't have an id. Instead we could use the external process's id
" or the channel's id (there seems to be 1 channel per job). Arbitrarily
" choose the channel's id.
"
" s:jobs {} -> key: channel's id, value: {} key: output, value: [] job's output
" key: buffer: value: buffer number
" nvim:
" id: job's id
"
" vim:
" id: channel's id
" arg: buffer number
function! s:job_started(id, ...) abort
if a:0 " vim
let s:jobs[a:id] = {'output': [], 'buffer': a:1}
else " nvim
let s:jobs[a:id] = 1
endif
endfunction
function! s:is_job_started(id) abort
return has_key(s:jobs, a:id)
endfunction
function! s:accumulate_job_output(id, line) abort
call add(s:jobs[a:id].output, a:line)
endfunction
" Returns a string
function! s:job_output(id) abort
if has_key(s:jobs, a:id)
return gitgutter#utility#stringify(s:jobs[a:id].output)
else
return ""
endif
endfunction
function! s:job_buffer(id) abort
return s:jobs[a:id].buffer
endfunction
function! s:job_finished(id) abort
if has_key(s:jobs, a:id)
unlet s:jobs[a:id]
endif
endfunction

View File

@ -0,0 +1,119 @@
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 gitgutter#debug#vim_version()
call gitgutter#debug#separator()
call gitgutter#debug#git_version()
call gitgutter#debug#separator()
call gitgutter#debug#grep_version()
call gitgutter#debug#separator()
call gitgutter#debug#option('updatetime')
call gitgutter#debug#option('shell')
call gitgutter#debug#option('shellcmdflag')
call gitgutter#debug#option('shellpipe')
call gitgutter#debug#option('shellquote')
call gitgutter#debug#option('shellredir')
call gitgutter#debug#option('shellslash')
call gitgutter#debug#option('shelltemp')
call gitgutter#debug#option('shelltype')
call gitgutter#debug#option('shellxescape')
call gitgutter#debug#option('shellxquote')
endfunction
function! gitgutter#debug#separator()
call gitgutter#debug#output('')
endfunction
function! gitgutter#debug#vim_version()
redir => version_info
silent execute 'version'
redir END
call gitgutter#debug#output(split(version_info, '\n')[0:2])
endfunction
function! gitgutter#debug#git_version()
let v = system(g:gitgutter_git_executable.' --version')
call gitgutter#debug#output( substitute(v, '\n$', '', '') )
endfunction
function! gitgutter#debug#grep_version()
let v = system('grep --version')
call gitgutter#debug#output( substitute(v, '\n$', '', '') )
let v = system('grep --help')
call gitgutter#debug#output( substitute(v, '\%x00', '', 'g') )
endfunction
function! gitgutter#debug#option(name)
if exists('+' . a:name)
let v = eval('&' . a:name)
call gitgutter#debug#output(a:name . '=' . v)
" redir => output
" silent execute "verbose set " . a:name . "?"
" redir END
" call gitgutter#debug#output(a:name . '=' . output)
else
call gitgutter#debug#output(a:name . ' [n/a]')
end
endfunction
function! gitgutter#debug#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
execute 'redir >> '.s:log_file
if s:new_log_session
let s:start = reltime()
silent echo "\n==== start log session ===="
endif
let elapsed = reltimestr(reltime(s:start)).' '
silent echo ''
" callers excluding this function
silent echo elapsed.expand('<sfile>')[:-22].':'
silent echo elapsed.s:format_for_log(a:message)
if a:0 && !empty(a:1)
for msg in a:000
silent echo elapsed.s:format_for_log(msg)
endfor
endif
redir END
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

View File

@ -0,0 +1,342 @@
if exists('g:gitgutter_grep_command')
let s:grep_available = 1
let s:grep_command = g:gitgutter_grep_command
else
let s:grep_available = executable('grep')
if s:grep_available
let s:grep_command = 'grep --color=never -e'
endif
endif
let s:hunk_re = '^@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@'
let s:c_flag = gitgutter#utility#git_supports_command_line_config_override()
let s:temp_index = tempname()
let s:temp_buffer = tempname()
" Returns a diff of the buffer.
"
" The way to get the diff depends on whether the buffer is saved or unsaved.
"
" * Saved: the buffer contents is the same as the file on disk in the working
" tree so we simply do:
"
" git diff myfile
"
" * Unsaved: 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
"
" The first instance is the file in the index which we obtain with:
"
" git show :myfile > myfileA
"
" The second instance is the buffer contents. Ideally we would pass this to
" git-diff on stdin via the second argument to vim's system() function.
" Unfortunately git-diff does not do CRLF conversion for input received on
" stdin, and git-show never performs CRLF conversion, so repos with CRLF
" conversion report that every line is modified due to mismatching EOLs.
"
" Instead, we write the buffer contents to a temporary file - myfileB in this
" example. Note the file extension must be preserved for the CRLF
" conversion to work.
"
" Before diffing a buffer for the first time, we check whether git knows about
" the file:
"
" git ls-files --error-unmatch myfile
"
" After running the diff we pass it through grep where available to reduce
" subsequent processing by the plugin. If grep is not available the plugin
" does the filtering instead.
function! gitgutter#diff#run_diff(realtime, preserve_full_diff) abort
" Wrap compound commands in parentheses to make Windows happy.
" bash doesn't mind the parentheses.
let cmd = '('
let bufnr = gitgutter#utility#bufnr()
let tracked = getbufvar(bufnr, 'gitgutter_tracked') " i.e. tracked by git
if !tracked
" Don't bother trying to realtime-diff an untracked file.
" NOTE: perhaps we should pull this guard up to the caller?
if a:realtime
throw 'diff failed'
else
let cmd .= g:gitgutter_git_executable.' ls-files --error-unmatch '.gitgutter#utility#shellescape(gitgutter#utility#filename()).' && ('
endif
endif
if a:realtime
let blob_name = g:gitgutter_diff_base.':'.gitgutter#utility#shellescape(gitgutter#utility#file_relative_to_repo_root())
let blob_file = s:temp_index
let buff_file = s:temp_buffer
let extension = gitgutter#utility#extension()
if !empty(extension)
let blob_file .= '.'.extension
let buff_file .= '.'.extension
endif
let cmd .= g:gitgutter_git_executable.' show '.blob_name.' > '.blob_file.' && '
" Writing the whole buffer resets the '[ and '] marks and also the
" 'modified' flag (if &cpoptions includes '+'). These are unwanted
" side-effects so we save and restore the values ourselves.
let modified = getbufvar(bufnr, "&mod")
let op_mark_start = getpos("'[")
let op_mark_end = getpos("']")
execute 'keepalt noautocmd silent write!' buff_file
call setbufvar(bufnr, "&mod", modified)
call setpos("'[", op_mark_start)
call setpos("']", op_mark_end)
endif
let cmd .= g:gitgutter_git_executable
if s:c_flag
let cmd .= ' -c "diff.autorefreshindex=0"'
endif
let cmd .= ' diff --no-ext-diff --no-color -U0 '.g:gitgutter_diff_args.' '
if a:realtime
let cmd .= ' -- '.blob_file.' '.buff_file
else
let cmd .= g:gitgutter_diff_base.' -- '.gitgutter#utility#shellescape(gitgutter#utility#filename())
endif
if !a:preserve_full_diff && s:grep_available
let cmd .= ' | '.s:grep_command.' '.gitgutter#utility#shellescape('^@@ ')
endif
if (!a:preserve_full_diff && s:grep_available) || a:realtime
" grep exits with 1 when no matches are found; diff exits with 1 when
" differences are found. However we want to treat non-matches and
" differences as non-erroneous behaviour; so we OR the command with one
" which always exits with success (0).
let cmd .= ' || exit 0'
endif
let cmd .= ')'
if !tracked
let cmd .= ')'
endif
let cmd = gitgutter#utility#command_in_directory_of_file(cmd)
if g:gitgutter_async && gitgutter#async#available() && !a:preserve_full_diff
call gitgutter#async#execute(cmd)
return 'async'
else
let diff = gitgutter#utility#system(cmd)
if gitgutter#utility#shell_error()
" A shell error indicates the file is not tracked by git (unless something bizarre is going on).
throw 'diff failed'
endif
return diff
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
function! gitgutter#diff#process_hunks(hunks) abort
call gitgutter#hunk#reset()
let modified_lines = []
for hunk in a:hunks
call extend(modified_lines, gitgutter#diff#process_hunk(hunk))
endfor
return modified_lines
endfunction
" Returns [ [<line_number (number)>, <name (string)>], ...]
function! gitgutter#diff#process_hunk(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 gitgutter#diff#is_added(from_count, to_count)
call gitgutter#diff#process_added(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_added(to_count)
elseif gitgutter#diff#is_removed(from_count, to_count)
call gitgutter#diff#process_removed(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_removed(from_count)
elseif gitgutter#diff#is_modified(from_count, to_count)
call gitgutter#diff#process_modified(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_modified(to_count)
elseif gitgutter#diff#is_modified_and_added(from_count, to_count)
call gitgutter#diff#process_modified_and_added(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_added(to_count - from_count)
call gitgutter#hunk#increment_lines_modified(from_count)
elseif gitgutter#diff#is_modified_and_removed(from_count, to_count)
call gitgutter#diff#process_modified_and_removed(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_modified(to_count)
call gitgutter#hunk#increment_lines_removed(from_count - to_count)
endif
return modifications
endfunction
function! gitgutter#diff#is_added(from_count, to_count) abort
return a:from_count == 0 && a:to_count > 0
endfunction
function! gitgutter#diff#is_removed(from_count, to_count) abort
return a:from_count > 0 && a:to_count == 0
endfunction
function! gitgutter#diff#is_modified(from_count, to_count) abort
return a:from_count > 0 && a:to_count > 0 && a:from_count == a:to_count
endfunction
function! gitgutter#diff#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! gitgutter#diff#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! gitgutter#diff#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! gitgutter#diff#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! gitgutter#diff#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! gitgutter#diff#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! gitgutter#diff#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
" Generates a zero-context diff for the current hunk.
"
" diff - the full diff for the buffer
" type - stage | undo | preview
function! gitgutter#diff#generate_diff_for_hunk(diff, type) abort
let diff_for_hunk = gitgutter#diff#discard_hunks(a:diff, a:type == 'stage' || a:type == 'undo')
if a:type == 'stage' || a:type == 'undo'
let diff_for_hunk = gitgutter#diff#adjust_hunk_summary(diff_for_hunk, a:type == 'stage')
endif
return diff_for_hunk
endfunction
" Returns the diff with all hunks discarded except the current.
"
" diff - the diff to process
" keep_header - truthy to keep the diff header and hunk summary, falsy to discard it
function! gitgutter#diff#discard_hunks(diff, keep_header) abort
let modified_diff = []
let keep_line = a:keep_header
for line in split(a: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)
endif
if keep_line
call add(modified_diff, line)
endif
endfor
if a:keep_header
return gitgutter#utility#stringify(modified_diff)
else
" Discard hunk summary too.
return gitgutter#utility#stringify(modified_diff[1:])
endif
endfunction
" Adjust hunk summary (from's / to's line number) to ignore changes above/before this one.
"
" diff_for_hunk - a diff containing only the hunk of interest
" staging - truthy if the hunk is to be staged, falsy if it is to be undone
"
" TODO: push this down to #discard_hunks?
function! gitgutter#diff#adjust_hunk_summary(diff_for_hunk, staging) abort
let line_adjustment = gitgutter#hunk#line_adjustment_for_current_hunk()
let adj_diff = []
for line in split(a:diff_for_hunk, '\n')
if match(line, s:hunk_re) != -1
if a:staging
" increment 'to' line number
let line = substitute(line, '+\@<=\(\d\+\)', '\=submatch(1)+line_adjustment', '')
else
" decrement 'from' line number
let line = substitute(line, '-\@<=\(\d\+\)', '\=submatch(1)-line_adjustment', '')
endif
endif
call add(adj_diff, line)
endfor
return gitgutter#utility#stringify(adj_diff)
endfunction

View File

@ -0,0 +1,115 @@
function! gitgutter#highlight#define_sign_column_highlight() abort
if g:gitgutter_override_sign_column_highlight
highlight! link SignColumn LineNr
else
highlight default link SignColumn LineNr
endif
endfunction
function! gitgutter#highlight#define_highlights() abort
let [guibg, ctermbg] = gitgutter#highlight#get_background_colors('SignColumn')
" Highlights used by the signs.
execute "highlight GitGutterAddDefault guifg=#009900 guibg=" . guibg . " ctermfg=2 ctermbg=" . ctermbg
execute "highlight GitGutterChangeDefault guifg=#bbbb00 guibg=" . guibg . " ctermfg=3 ctermbg=" . ctermbg
execute "highlight GitGutterDeleteDefault guifg=#ff2222 guibg=" . guibg . " ctermfg=1 ctermbg=" . ctermbg
highlight default link GitGutterChangeDeleteDefault GitGutterChangeDefault
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 GitGutterChangeInvisble
highlight default link GitGutterAdd GitGutterAddDefault
highlight default link GitGutterChange GitGutterChangeDefault
highlight default link GitGutterDelete GitGutterDeleteDefault
highlight default link GitGutterChangeDelete GitGutterChangeDeleteDefault
" 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
endfunction
function! gitgutter#highlight#define_signs() abort
sign define GitGutterLineAdded
sign define GitGutterLineModified
sign define GitGutterLineRemoved
sign define GitGutterLineRemovedFirstLine
sign define GitGutterLineModifiedRemoved
sign define GitGutterDummy
call gitgutter#highlight#define_sign_text()
call gitgutter#highlight#define_sign_text_highlights()
call gitgutter#highlight#define_sign_line_highlights()
endfunction
function! gitgutter#highlight#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 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 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 GitGutterLineModifiedRemoved texthl=GitGutterChangeDeleteInvisible
endif
endfunction
function! gitgutter#highlight#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 GitGutterLineModifiedRemoved linehl=GitGutterChangeDeleteLine
else
sign define GitGutterLineAdded linehl=
sign define GitGutterLineModified linehl=
sign define GitGutterLineRemoved linehl=
sign define GitGutterLineRemovedFirstLine linehl=
sign define GitGutterLineModifiedRemoved linehl=
endif
endfunction
function! gitgutter#highlight#get_background_colors(group) abort
redir => highlight
silent execute 'silent highlight ' . a:group
redir END
let link_matches = matchlist(highlight, 'links to \(\S\+\)')
if len(link_matches) > 0 " follow the link
return gitgutter#highlight#get_background_colors(link_matches[1])
endif
let ctermbg = gitgutter#highlight#match_highlight(highlight, 'ctermbg=\([0-9A-Za-z]\+\)')
let guibg = gitgutter#highlight#match_highlight(highlight, 'guibg=\([#0-9A-Za-z]\+\)')
return [guibg, ctermbg]
endfunction
function! gitgutter#highlight#match_highlight(highlight, pattern) abort
let matches = matchlist(a:highlight, a:pattern)
if len(matches) == 0
return 'NONE'
endif
return matches[1]
endfunction

View File

@ -0,0 +1,137 @@
let s:hunks = []
function! gitgutter#hunk#set_hunks(hunks) abort
let s:hunks = a:hunks
endfunction
function! gitgutter#hunk#hunks() abort
return s:hunks
endfunction
function! gitgutter#hunk#summary(bufnr) abort
return get(getbufvar(a:bufnr,''), 'gitgutter_summary', [0,0,0])
endfunction
function! gitgutter#hunk#reset() abort
call setbufvar(gitgutter#utility#bufnr(), 'gitgutter_summary', [0,0,0])
endfunction
function! gitgutter#hunk#increment_lines_added(count) abort
let bufnr = gitgutter#utility#bufnr()
let summary = gitgutter#hunk#summary(bufnr)
let summary[0] += a:count
call setbufvar(bufnr, 'gitgutter_summary', summary)
endfunction
function! gitgutter#hunk#increment_lines_modified(count) abort
let bufnr = gitgutter#utility#bufnr()
let summary = gitgutter#hunk#summary(bufnr)
let summary[1] += a:count
call setbufvar(bufnr, 'gitgutter_summary', summary)
endfunction
function! gitgutter#hunk#increment_lines_removed(count) abort
let bufnr = gitgutter#utility#bufnr()
let summary = gitgutter#hunk#summary(bufnr)
let summary[2] += a:count
call setbufvar(bufnr, 'gitgutter_summary', summary)
endfunction
function! gitgutter#hunk#next_hunk(count) abort
if gitgutter#utility#is_active()
let current_line = line('.')
let hunk_count = 0
for hunk in s:hunks
if hunk[2] > current_line
let hunk_count += 1
if hunk_count == a:count
execute 'normal!' hunk[2] . 'G'
return
endif
endif
endfor
call gitgutter#utility#warn('No more hunks')
endif
endfunction
function! gitgutter#hunk#prev_hunk(count) abort
if gitgutter#utility#is_active()
let current_line = line('.')
let hunk_count = 0
for hunk in reverse(copy(s:hunks))
if hunk[2] < current_line
let hunk_count += 1
if hunk_count == a:count
let target = hunk[2] == 0 ? 1 : hunk[2]
execute 'normal!' target . 'G'
return
endif
endif
endfor
call gitgutter#utility#warn('No previous hunks')
endif
endfunction
" Returns the hunk the cursor is currently in or an empty list if the cursor
" isn't in a hunk.
function! gitgutter#hunk#current_hunk() abort
let current_hunk = []
for hunk in s:hunks
if gitgutter#hunk#cursor_in_hunk(hunk)
let current_hunk = hunk
break
endif
endfor
return current_hunk
endfunction
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
" 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! gitgutter#hunk#line_adjustment_for_current_hunk() abort
let adj = 0
for hunk in s:hunks
if gitgutter#hunk#cursor_in_hunk(hunk)
break
else
let adj += hunk[1] - hunk[3]
endif
endfor
return adj
endfunction
function! gitgutter#hunk#text_object(inner) abort
let hunk = gitgutter#hunk#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

View File

@ -0,0 +1,179 @@
" Vim doesn't namespace sign ids so every plugin shares the same
" namespace. Sign ids are simply integers so to avoid clashes with other
" signs we guess at a clear run.
"
" Note also we currently never reset s:next_sign_id.
let s:first_sign_id = 3000
let s:next_sign_id = s:first_sign_id
let s:dummy_sign_id = s:first_sign_id - 1
" Remove-all-signs optimisation requires Vim 7.3.596+.
let s:supports_star = v:version > 703 || (v:version == 703 && has("patch596"))
" Removes gitgutter's signs (excluding dummy sign) from the buffer being processed.
function! gitgutter#sign#clear_signs() abort
let bufnr = gitgutter#utility#bufnr()
call gitgutter#sign#find_current_signs()
let sign_ids = map(values(getbufvar(bufnr, 'gitgutter_gitgutter_signs')), 'v:val.id')
call gitgutter#sign#remove_signs(sign_ids, 1)
call setbufvar(bufnr, 'gitgutter_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(modified_lines) abort
call gitgutter#sign#find_current_signs()
let new_gitgutter_signs_line_numbers = map(copy(a:modified_lines), 'v:val[0]')
let obsolete_signs = gitgutter#sign#obsolete_gitgutter_signs_to_remove(new_gitgutter_signs_line_numbers)
let flicker_possible = s:remove_all_old_signs && !empty(a:modified_lines)
if flicker_possible
call gitgutter#sign#add_dummy_sign()
endif
call gitgutter#sign#remove_signs(obsolete_signs, s:remove_all_old_signs)
call gitgutter#sign#upsert_new_gitgutter_signs(a:modified_lines)
if flicker_possible
call gitgutter#sign#remove_dummy_sign(0)
endif
endfunction
function! gitgutter#sign#add_dummy_sign() abort
let bufnr = gitgutter#utility#bufnr()
if !getbufvar(bufnr, 'gitgutter_dummy_sign')
execute "sign place" s:dummy_sign_id "line=" . 9999 "name=GitGutterDummy buffer=" . bufnr
call setbufvar(bufnr, 'gitgutter_dummy_sign', 1)
endif
endfunction
function! gitgutter#sign#remove_dummy_sign(force) abort
let bufnr = gitgutter#utility#bufnr()
if getbufvar(bufnr, 'gitgutter_dummy_sign') && (a:force || !g:gitgutter_sign_column_always)
execute "sign unplace" s:dummy_sign_id "buffer=" . bufnr
call setbufvar(bufnr, 'gitgutter_dummy_sign', 0)
endif
endfunction
"
" Internal functions
"
function! gitgutter#sign#find_current_signs() abort
let bufnr = gitgutter#utility#bufnr()
let gitgutter_signs = {} " <line_number (string)>: {'id': <id (number)>, 'name': <name (string)>}
let other_signs = [] " [<line_number (number),...]
let dummy_sign_placed = 0
redir => signs
silent execute "sign place buffer=" . bufnr
redir END
for sign_line in filter(split(signs, '\n')[2:], 'v:val =~# "="')
" Typical sign line: line=88 id=1234 name=GitGutterLineAdded
" We assume splitting is faster than a regexp.
let components = split(sign_line)
let name = split(components[2], '=')[1]
if name =~# 'GitGutterDummy'
let dummy_sign_placed = 1
else
let line_number = str2nr(split(components[0], '=')[1])
if name =~# 'GitGutter'
let id = str2nr(split(components[1], '=')[1])
" 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, line_number)
execute "sign unplace" gitgutter_signs[line_number].id
endif
let gitgutter_signs[line_number] = {'id': id, 'name': name}
else
call add(other_signs, line_number)
endif
end
endfor
call setbufvar(bufnr, 'gitgutter_dummy_sign', dummy_sign_placed)
call setbufvar(bufnr, 'gitgutter_gitgutter_signs', gitgutter_signs)
call setbufvar(bufnr, 'gitgutter_other_signs', other_signs)
endfunction
" Returns a list of [<id (number)>, ...]
" Sets `s:remove_all_old_signs` as a side-effect.
function! gitgutter#sign#obsolete_gitgutter_signs_to_remove(new_gitgutter_signs_line_numbers) abort
let bufnr = gitgutter#utility#bufnr()
let signs_to_remove = [] " list of [<id (number)>, ...]
let remove_all_signs = 1
let old_gitgutter_signs = getbufvar(bufnr, 'gitgutter_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! gitgutter#sign#remove_signs(sign_ids, all_signs) abort
let bufnr = gitgutter#utility#bufnr()
if a:all_signs && s:supports_star && empty(getbufvar(bufnr, 'gitgutter_other_signs'))
let dummy_sign_present = getbufvar(bufnr, 'gitgutter_dummy_sign')
execute "sign unplace * buffer=" . bufnr
if dummy_sign_present
execute "sign place" s:dummy_sign_id "line=" . 9999 "name=GitGutterDummy buffer=" . bufnr
endif
else
for id in a:sign_ids
execute "sign unplace" id
endfor
endif
endfunction
function! gitgutter#sign#upsert_new_gitgutter_signs(modified_lines) abort
let bufnr = gitgutter#utility#bufnr()
let other_signs = getbufvar(bufnr, 'gitgutter_other_signs')
let old_gitgutter_signs = getbufvar(bufnr, 'gitgutter_gitgutter_signs')
for line in a:modified_lines
let line_number = line[0] " <number>
if index(other_signs, line_number) == -1 " don't clobber others' signs
let name = gitgutter#utility#highlight_name_for_change(line[1])
if !has_key(old_gitgutter_signs, line_number) " insert
let id = gitgutter#sign#next_sign_id()
execute "sign place" id "line=" . line_number "name=" . name "buffer=" . 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=" . bufnr
end
endif
endif
endfor
" At this point b:gitgutter_gitgutter_signs is out of date.
endfunction
function! gitgutter#sign#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

View File

@ -0,0 +1,212 @@
let s:file = ''
let s:using_xolox_shell = -1
let s:exit_code = 0
function! gitgutter#utility#warn(message) abort
echohl WarningMsg
echo 'vim-gitgutter: ' . a:message
echohl None
let v:warningmsg = a:message
endfunction
function! gitgutter#utility#warn_once(message, key) abort
if empty(getbufvar(s:bufnr, a:key))
call setbufvar(s:bufnr, a:key, '1')
echohl WarningMsg
redraw | echo 'vim-gitgutter: ' . 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() abort
return g:gitgutter_enabled &&
\ !pumvisible() &&
\ gitgutter#utility#is_file_buffer() &&
\ gitgutter#utility#exists_file() &&
\ gitgutter#utility#not_git_dir()
endfunction
function! gitgutter#utility#not_git_dir() abort
return gitgutter#utility#full_path_to_directory_of_file() !~ '[/\\]\.git\($\|[/\\]\)'
endfunction
function! gitgutter#utility#is_file_buffer() abort
return empty(getbufvar(s:bufnr, '&buftype'))
endfunction
" A replacement for the built-in `shellescape(arg)`.
"
" Recent versions of Vim handle shell escaping pretty well. However older
" versions aren't as good. This attempts to do the right thing.
"
" See:
" https://github.com/tpope/vim-fugitive/blob/8f0b8edfbd246c0026b7a2388e1d883d579ac7f6/plugin/fugitive.vim#L29-L37
function! gitgutter#utility#shellescape(arg) abort
if a:arg =~ '^[A-Za-z0-9_/.-]\+$'
return a:arg
elseif &shell =~# 'cmd' || gitgutter#utility#using_xolox_shell()
return '"' . substitute(substitute(a:arg, '"', '""', 'g'), '%', '"%"', 'g') . '"'
else
return shellescape(a:arg)
endif
endfunction
function! gitgutter#utility#set_buffer(bufnr) abort
let s:bufnr = a:bufnr
let s:file = resolve(bufname(a:bufnr))
endfunction
function! gitgutter#utility#bufnr()
return s:bufnr
endfunction
function! gitgutter#utility#file()
return s:file
endfunction
function! gitgutter#utility#filename() abort
return fnamemodify(s:file, ':t')
endfunction
function! gitgutter#utility#extension() abort
return fnamemodify(s:file, ':e')
endfunction
function! gitgutter#utility#full_path_to_directory_of_file() abort
return fnamemodify(s:file, ':p:h')
endfunction
function! gitgutter#utility#directory_of_file() abort
return fnamemodify(s:file, ':h')
endfunction
function! gitgutter#utility#exists_file() abort
return filereadable(s:file)
endfunction
function! gitgutter#utility#has_unsaved_changes() abort
return getbufvar(s:bufnr, "&mod")
endfunction
function! gitgutter#utility#has_fresh_changes() abort
return getbufvar(s:bufnr, 'changedtick') != getbufvar(s:bufnr, 'gitgutter_last_tick')
endfunction
function! gitgutter#utility#save_last_seen_change() abort
call setbufvar(s:bufnr, 'gitgutter_last_tick', getbufvar(s:bufnr, 'changedtick'))
endfunction
function! gitgutter#utility#shell_error() abort
return gitgutter#utility#using_xolox_shell() ? s:exit_code : v:shell_error
endfunction
function! gitgutter#utility#using_xolox_shell() abort
if s:using_xolox_shell == -1
if !g:gitgutter_avoid_cmd_prompt_on_windows
let s:using_xolox_shell = 0
" Although xolox/vim-shell works on both windows and unix we only want to use
" it on windows.
elseif has('win32') || has('win64') || has('win32unix')
let s:using_xolox_shell = exists('g:xolox#misc#version') && exists('g:xolox#shell#version')
else
let s:using_xolox_shell = 0
endif
endif
return s:using_xolox_shell
endfunction
function! gitgutter#utility#system(cmd, ...) abort
call gitgutter#debug#log(a:cmd, a:000)
if gitgutter#utility#using_xolox_shell()
let options = {'command': a:cmd, 'check': 0}
if a:0 > 0
let options['stdin'] = a:1
endif
let ret = xolox#misc#os#exec(options)
let output = join(ret.stdout, "\n")
let s:exit_code = ret.exit_code
else
silent let output = (a:0 == 0) ? system(a:cmd) : system(a:cmd, a:1)
endif
return output
endfunction
function! gitgutter#utility#file_relative_to_repo_root() abort
let file_path_relative_to_repo_root = getbufvar(s:bufnr, 'gitgutter_repo_relative_path')
if empty(file_path_relative_to_repo_root)
let dir_path_relative_to_repo_root = gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file(g:gitgutter_git_executable.' rev-parse --show-prefix'))
let dir_path_relative_to_repo_root = gitgutter#utility#strip_trailing_new_line(dir_path_relative_to_repo_root)
let file_path_relative_to_repo_root = dir_path_relative_to_repo_root . gitgutter#utility#filename()
call setbufvar(s:bufnr, 'gitgutter_repo_relative_path', file_path_relative_to_repo_root)
endif
return file_path_relative_to_repo_root
endfunction
function! gitgutter#utility#command_in_directory_of_file(cmd) abort
return 'cd '.gitgutter#utility#shellescape(gitgutter#utility#directory_of_file()).' && '.a:cmd
endfunction
function! gitgutter#utility#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'
endif
endfunction
" Dedups list in-place.
" Assumes list has no empty entries.
function! gitgutter#utility#dedup(list)
return filter(sort(a:list), 'index(a:list, v:val, v:key + 1) == -1')
endfunction
function! gitgutter#utility#strip_trailing_new_line(line) abort
return substitute(a:line, '\n$', '', '')
endfunction
function! gitgutter#utility#git_version() abort
return matchstr(system(g:gitgutter_git_executable.' --version'), '[0-9.]\+')
endfunction
" True for git v1.7.2+.
function! gitgutter#utility#git_supports_command_line_config_override() abort
let [major, minor, patch; _] = split(gitgutter#utility#git_version(), '\.')
return major > 1 || (major == 1 && minor > 7) || (minor == 7 && patch > 1)
endfunction
function! gitgutter#utility#stringify(list) abort
return join(a:list, "\n")."\n"
endfunction
function! gitgutter#utility#use_known_shell() abort
if has('unix')
if &shell !=# 'sh'
let s:shell = &shell
let s:shellcmdflag = &shellcmdflag
let s:shellredir = &shellredir
let &shell = 'sh'
set shellcmdflag=-c
set shellredir=>%s\ 2>&1
endif
endif
endfunction
function! gitgutter#utility#restore_shell() abort
if has('unix')
if exists('s:shell')
let &shell = s:shell
let &shellcmdflag = s:shellcmdflag
let &shellredir = s:shellredir
endif
endif
endfunction

View File

@ -0,0 +1,340 @@
*gitgutter.txt* A Vim plugin which shows a git diff in the gutter.
Vim Git Gutter
Author: Andy Stewart <http://airbladesoftware.com/>
Plugin Homepage: <https://github.com/airblade/vim-gitgutter>
===============================================================================
CONTENTS *GitGutterContents*
1. Introduction ................. |GitGutterIntroduction|
2. Installation ................. |GitGutterInstallation|
3. Usage ........................ |GitGutterUsage|
4. Commands ..................... |GitGutterCommands|
5. Autocommand .................. |GitGutterAutocmd|
6. CUSTOMISATION................. |GitGutterCustomisation|
7. FAQ .......................... |GitGutterFAQ|
===============================================================================
1. INTRODUCTION *GitGutterIntroduction*
*GitGutter*
Vim Git Gutter is a Vim plugin which shows a git diff in the 'gutter' (sign
column). It shows whether each line has been added, modified, and where lines
have been removed.
This is a port of the Git Gutter plugin for Sublime Text 2.
===============================================================================
2. INSTALLATION *GitGutterInstallation*
* Pathogen:
>
cd ~/.vim/bundle
git clone git://github.com/airblade/vim-gitgutter.git
<
* Voom:
Edit your plugin manifest (`voom edit`) and add:
>
airblade/vim-gitgutter
<
* VimPlug:
Place this in your .vimrc:
>
Plug 'airblade/vim-gitgutter'
<
Then run the following in Vim:
>
:source %
:PlugInstall
<
* NeoBundle:
Place this in your .vimrc:
>
NeoBundle 'airblade/vim-gitgutter'
<
Then run the following in Vim:
>
:source %
:NeoBundleInstall
<
* No plugin manager:
Copy vim-gitgutter's subdirectories into your vim configuration directory:
>
cd tmp && git clone git://github.com/airblade/vim-gitgutter.git
cp vim-gitgutter/* ~/.vim/
<
See |add-global-plugin|.
===============================================================================
3. USAGE *GitGutterUsage*
You don't have to do anything: it just works.
===============================================================================
4. COMMANDS *GitGutterCommands*
Commands for turning Git Gutter on and off:
:GitGutterDisable *:GitGutterDisable*
Explicitly turn Git Gutter off.
:GitGutterEnable *:GitGutterEnable*
Explicitly turn Git Gutter on.
:GitGutterToggle *:GitGutterToggle*
Explicitly turn Git Gutter on if it was off and vice versa.
:GitGutter *:GitGutter*
Update signs for the current buffer.
:GitGutterAll *:GitGutterAll*
Update signs across all buffers.
Commands for turning signs on and off (defaults to on):
:GitGutterSignsEnable *:GitGutterSignsEnable*
Explicitly turn line signs on.
:GitGutterSignsDisable *:GitGutterSignsDisable*
Explicitly turn line signs off.
:GitGutterSignsToggle *:GitGutterSignsToggle*
Explicitly turn line signs on if it was off and vice versa.
Commands for turning line highlighting on and off (defaults to off):
:GitGutterLineHighlightsEnable *:GitGutterLineHighlightsEnable*
Explicitly turn line highlighting on.
:GitGutterLineHighlightsDisable *:GitGutterLineHighlightsDisable*
Explicitly turn line highlighting off.
:GitGutterLineHighlightsToggle *:GitGutterLineHighlightsToggle*
Explicitly turn line highlighting on if it was off and vice versa.
Commands for jumping between marked hunks:
:GitGutterNextHunk *:GitGutterNextHunk*
Jump to the next marked hunk. Takes a count.
:GitGutterPrevHunk *:GitGutterPrevHunk*
Jump to the previous marked hunk. Takes a count.
Commands for staging or undoing individual hunks:
:GitGutterStageHunk *:GitGutterStageHunk*
Stage the hunk the cursor is in.
:GitGutterUndoHunk *:GitGutterUndoHunk*
Undo the hunk the cursor is in.
:GitGutterPreviewHunk *:GitGutterPreviewHunk*
Preview the hunk the cursor is in.
===============================================================================
5. AUTOCOMMAND *GitGutterAutocmd*
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()
<
===============================================================================
6. CUSTOMISATION *GitGutterCustomisation*
You can customise:
- The sign column's colours
- The signs' colours and symbols
- Line highlights
- The base of the diff
- Extra arguments for git-diff
- Key mappings
- The grep executable used
- Whether or not vim-gitgutter is on initially (defaults to on)
- Whether or not signs are shown (defaults to yes)
- Whether or not line highlighting is on initially (defaults to off)
- Whether or not vim-gitgutter runs in realtime (defaults to yes)
- Whether or not vim-gitgutter runs eagerly (defaults to yes)
- Whether or not vim-gitgutter runs asynchronously (defaults to yes)
Please note that vim-gitgutter won't override any colours or highlights you've
set in your colorscheme.
SIGN COLUMN
By default vim-gitgutter will make the sign column look like the line number
column (i.e. the |hl-LineNr| highlight group).
To customise your sign column's background color, first tell vim-gitgutter to
leave it alone:
>
let g:gitgutter_override_sign_column_highlight = 0
<
And then either update your colorscheme's |hlSignColumn| highlight group or set
it in your |vimrc|:
Desired appearance Command ~
Same as line number column highlight clear SignColumn
User-defined (terminal Vim) highlight SignColumn ctermbg={whatever}
User-defined (graphical Vim) highlight SignColumn guibg={whatever}
SIGNS' COLOURS AND SYMBOLS
To customise the colours, set up the following highlight groups in your
colorscheme or |vimrc|:
>
GitGutterAdd " an added line
GitGutterChange " a changed line
GitGutterDelete " at least one removed line
GitGutterChangeDelete " a changed line followed by at least one removed line
<
You can either set these with `highlight GitGutterAdd {key}={arg}...` or link
them to existing highlight groups with, say:
>
highlight link GitGutterAdd DiffAdd
<
To customise the symbols, add the following to your |vimrc|:
>
let g:gitgutter_sign_added = 'xx'
let g:gitgutter_sign_modified = 'yy'
let g:gitgutter_sign_removed = 'zz'
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|:
>
GitGutterAddLine " default: links to DiffAdd
GitGutterChangeLine " default: links to DiffChange
GitGutterDeleteLine " default: links to DiffDelete
GitGutterChangeDeleteLine " default: links to GitGutterChangeLineDefault
<
THE BASE OF THE DIFF
By default buffers are diffed against the index. To diff against a commit
instead:
>
let g:gitgutter_diff_base = '<commit SHA>'
<
EXTRA ARGUMENTS FOR GIT-DIFF
To pass extra arguments to git-diff, add this to your |vimrc|:
>
let g:gitgutter_diff_args = '-w'
<
KEY MAPPINGS
To disable all key maps:
>
let g:gitgutter_map_keys = 0
<
To change the hunk-jumping maps (defaults shown):
>
nmap [c <Plug>GitGutterPrevHunk
nmap ]c <Plug>GitGutterNextHunk
<
To change the hunk-staging/undoing/previewing maps (defaults shown):
>
nmap <Leader>hs <Plug>GitGutterStageHunk
nmap <Leader>hu <Plug>GitGutterUndoHunk
nmap <Leader>hp <Plug>GitGutterPreviewHunk
<
To change the hunk text object maps (defaults shown):
>
omap ic <Plug>GitGutterTextObjectInnerPending
omap ac <Plug>GitGutterTextObjectOuterPending
xmap ic <Plug>GitGutterTextObjectInnerVisual
xmap ac <Plug>GitGutterTextObjectOuterVisual
<
TO USE A CUSTOM GREP COMMAND
To use a custom invocation for grep, use this:
>
let g:gitgutter_grep_command = 'grep --color=never -e'
<
TO TURN OFF VIM-GITGUTTER BY DEFAULT
Add to your |vimrc|
>
let g:gitgutter_enabled = 0
<
TO TURN OFF SIGNS BY DEFAULT
Add to your |vimrc|
>
let g:gitgutter_signs = 0
<
Note that the sign column will still be present if you have line highlighting
switched on.
TO TURN ON LINE HIGHLIGHTING BY DEFAULT
Add to your |vimrc|
>
let g:gitgutter_highlight_lines = 1
<
TO STOP VIM-GITGUTTER RUNNING IN REALTIME
Add to your |vimrc|
>
let g:gitgutter_realtime = 0
<
TO STOP VIM-GITGUTTER RUNNING EAGERLY
Add to your |vimrc|
>
let g:gitgutter_eager = 0
<
TO TURN OFF ASYNCHRONOUS UPDATES
By default diffs are run asynchronously. To run diffs synchronously
instead:
Add to your |vimrc|
>
let g:gitgutter_async = 0
<
===============================================================================
7. FAQ *GitGutterFAQ*
a. Why are the colours in the sign column weird?
Your colorscheme is configuring the |hl-SignColumn| highlight group weirdly.
Please see |GitGutterCustomisation| on customising the sign column.
b. What happens if I also use another plugin which uses signs (e.g. Syntastic)?
Vim only allows one sign per line. Before adding a sign to a line,
vim-gitgutter checks whether a sign has already been added by somebody else.
If so it doesn't do anything. In other words vim-gitgutter won't overwrite
another plugin's signs. It also won't remove another plugin's signs.

View File

@ -0,0 +1,241 @@
scriptencoding utf-8
if exists('g:loaded_gitgutter') || !has('signs') || &cp
finish
endif
let g:loaded_gitgutter = 1
" Initialisation {{{
" Realtime sign updates require Vim 7.3.105+.
if v:version < 703 || (v:version == 703 && !has("patch105"))
let g:gitgutter_realtime = 0
endif
" Eager updates require gettabvar()/settabvar().
if !exists("*gettabvar")
let g:gitgutter_eager = 0
endif
function! s:set(var, default) abort
if !exists(a:var)
if type(a:default)
execute 'let' a:var '=' string(a:default)
else
execute 'let' a:var '=' a:default
endif
endif
endfunction
call s:set('g:gitgutter_enabled', 1)
call s:set('g:gitgutter_max_signs', 500)
call s:set('g:gitgutter_signs', 1)
call s:set('g:gitgutter_highlight_lines', 0)
call s:set('g:gitgutter_sign_column_always', 0)
call s:set('g:gitgutter_override_sign_column_highlight', 1)
call s:set('g:gitgutter_realtime', 1)
call s:set('g:gitgutter_eager', 1)
call s:set('g:gitgutter_sign_added', '+')
call s:set('g:gitgutter_sign_modified', '~')
call s:set('g:gitgutter_sign_removed', '_')
try
call s:set('g:gitgutter_sign_removed_first_line', '‾')
catch /E239/
let g:gitgutter_sign_removed_first_line = '_^'
endtry
call s:set('g:gitgutter_sign_modified_removed', '~_')
call s:set('g:gitgutter_diff_args', '')
call s:set('g:gitgutter_diff_base', '')
call s:set('g:gitgutter_map_keys', 1)
call s:set('g:gitgutter_avoid_cmd_prompt_on_windows', 1)
call s:set('g:gitgutter_async', 1)
call s:set('g:gitgutter_log', 0)
call s:set('g:gitgutter_git_executable', 'git')
if !executable(g:gitgutter_git_executable)
call gitgutter#utility#warn('cannot find git. Please set g:gitgutter_git_executable.')
endif
call gitgutter#highlight#define_sign_column_highlight()
call gitgutter#highlight#define_highlights()
call gitgutter#highlight#define_signs()
" }}}
" Primary functions {{{
command -bar GitGutterAll call gitgutter#all()
command -bar GitGutter call gitgutter#process_buffer(bufnr(''), 0)
command -bar GitGutterDisable call gitgutter#disable()
command -bar GitGutterEnable call gitgutter#enable()
command -bar GitGutterToggle call gitgutter#toggle()
" }}}
" Line highlights {{{
command -bar GitGutterLineHighlightsDisable call gitgutter#line_highlights_disable()
command -bar GitGutterLineHighlightsEnable call gitgutter#line_highlights_enable()
command -bar GitGutterLineHighlightsToggle call gitgutter#line_highlights_toggle()
" }}}
" Signs {{{
command -bar GitGutterSignsEnable call gitgutter#signs_enable()
command -bar GitGutterSignsDisable call gitgutter#signs_disable()
command -bar GitGutterSignsToggle call gitgutter#signs_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 GitGutterStageHunk call gitgutter#stage_hunk()
command -bar GitGutterUndoHunk call gitgutter#undo_hunk()
command -bar GitGutterRevertHunk echomsg 'GitGutterRevertHunk is deprecated. Use GitGutterUndoHunk'<Bar>call gitgutter#undo_hunk()
command -bar GitGutterPreviewHunk call gitgutter#preview_hunk()
" 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()
return gitgutter#utility#is_active() ? gitgutter#hunk#hunks() : []
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
" }}}
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>GitGutterPrevHunk &diff ? '[c' : ":\<C-U>execute v:count1 . 'GitGutterPrevHunk'\<CR>"
if g:gitgutter_map_keys
if !hasmapto('<Plug>GitGutterPrevHunk') && maparg('[c', 'n') ==# ''
nmap [c <Plug>GitGutterPrevHunk
endif
if !hasmapto('<Plug>GitGutterNextHunk') && maparg(']c', 'n') ==# ''
nmap ]c <Plug>GitGutterNextHunk
endif
endif
nnoremap <silent> <Plug>GitGutterStageHunk :GitGutterStageHunk<CR>
nnoremap <silent> <Plug>GitGutterUndoHunk :GitGutterUndoHunk<CR>
nnoremap <silent> <Plug>GitGutterPreviewHunk :GitGutterPreviewHunk<CR>
if g:gitgutter_map_keys
if !hasmapto('<Plug>GitGutterStageHunk') && maparg('<Leader>hs', 'n') ==# ''
nmap <Leader>hs <Plug>GitGutterStageHunk
endif
if !hasmapto('<Plug>GitGutterUndoHunk') && maparg('<Leader>hu', 'n') ==# ''
nmap <Leader>hu <Plug>GitGutterUndoHunk
nmap <Leader>hr <Plug>GitGutterUndoHunk:echomsg '<Leader>hr is deprecated. Use <Leader>hu'<CR>
endif
if !hasmapto('<Plug>GitGutterPreviewHunk') && maparg('<Leader>hp', 'n') ==# ''
nmap <Leader>hp <Plug>GitGutterPreviewHunk
endif
if !hasmapto('<Plug>GitGutterTextObjectInnerPending') && maparg('ic', 'o') ==# ''
omap ic <Plug>GitGutterTextObjectInnerPending
endif
if !hasmapto('<Plug>GitGutterTextObjectOuterPending') && maparg('ac', 'o') ==# ''
omap ac <Plug>GitGutterTextObjectOuterPending
endif
if !hasmapto('<Plug>GitGutterTextObjectInnerVisual') && maparg('ic', 'x') ==# ''
xmap ic <Plug>GitGutterTextObjectInnerVisual
endif
if !hasmapto('<Plug>GitGutterTextObjectOuterVisual') && maparg('ac', 'x') ==# ''
xmap ac <Plug>GitGutterTextObjectOuterVisual
endif
endif
" }}}
" Autocommands {{{
augroup gitgutter
autocmd!
if g:gitgutter_realtime
autocmd CursorHold,CursorHoldI * call gitgutter#process_buffer(bufnr(''), 1)
endif
if g:gitgutter_eager
autocmd BufWritePost,FileChangedShellPost * call gitgutter#process_buffer(bufnr(''), 0)
" When you enter a new tab, BufEnter is only fired if the buffer you enter
" is not the one you came from.
"
" For example:
"
" `:tab split` fires TabEnter but not BufEnter.
" `:tab new` fires TabEnter and BufEnter.
"
" As and when both TabEnter and BufEnter are fired, we do not want to
" process the entered buffer twice. We avoid this by setting and clearing
" a flag.
autocmd BufEnter *
\ if gettabvar(tabpagenr(), 'gitgutter_didtabenter') |
\ call settabvar(tabpagenr(), 'gitgutter_didtabenter', 0) |
\ else |
\ call gitgutter#process_buffer(bufnr(''), 0) |
\ endif
autocmd TabEnter *
\ call settabvar(tabpagenr(), 'gitgutter_didtabenter', 1) |
\ call gitgutter#all()
if !has('gui_win32')
autocmd FocusGained * call gitgutter#all()
endif
else
autocmd BufRead,BufWritePost,FileChangedShellPost * call gitgutter#process_buffer(bufnr(''), 0)
endif
autocmd ColorScheme * call gitgutter#highlight#define_sign_column_highlight() | call gitgutter#highlight#define_highlights()
" Disable during :vimgrep
autocmd QuickFixCmdPre *vimgrep* let g:gitgutter_enabled = 0
autocmd QuickFixCmdPost *vimgrep* let g:gitgutter_enabled = 1
augroup END
" }}}
" vim:set et sw=2 fdm=marker:

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,11 @@
a
b
c
d
e
f
g
h
i
j

View File

@ -0,0 +1,162 @@
"
" 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!

View File

@ -0,0 +1,19 @@
#!/usr/bin/env bash
VIM="/Applications/MacVim.app/Contents/MacOS/Vim -v"
$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

View File

@ -0,0 +1,400 @@
let s:current_dir = expand('%:p:h')
let s:test_repo = s:current_dir.'/test-repo'
let s:bufnr = bufnr('')
"
" Helpers
"
function s:signs(filename)
redir => signs
silent execute 'sign place'
redir END
let signs = split(signs, '\n')
" filter out signs for this test file
" assumes a:filename's signs are last set listed
let i = index(signs, 'Signs for '.a:filename.':')
let signs = (i > -1 ? signs[i+1:] : [])
call map(signs, {_, v -> substitute(v, ' ', '', '')})
return signs
endfunction
function s:git_diff()
return split(system('git diff -U0 fixture.txt'), '\n')
endfunction
function s:git_diff_staged()
return split(system('git diff -U0 --staged fixture.txt'), '\n')
endfunction
"
" SetUp / TearDown
"
function SetUp()
call system("git init ".s:test_repo.
\ " && cd ".s:test_repo.
\ " && cp ../fixture.txt .".
\ " && git add . && git commit -m 'initial'")
execute ':cd' s:test_repo
edit! fixture.txt
call gitgutter#sign#reset()
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*
write
let expected = ["line=2 id=3000 name=GitGutterLineAdded"]
call assert_equal(expected, s:signs('fixture.txt'))
endfunction
function Test_add_lines_fish()
let _shell = &shell
set shell=/usr/local/bin/fish
normal ggo*
write
let expected = ["line=2 id=3000 name=GitGutterLineAdded"]
call assert_equal(expected, s:signs('fixture.txt'))
let &shell = _shell
endfunction
function Test_modify_lines()
normal ggi*
write
let expected = ["line=1 id=3000 name=GitGutterLineModified"]
call assert_equal(expected, s:signs('fixture.txt'))
endfunction
function Test_remove_lines()
execute '5d'
write
let expected = ["line=4 id=3000 name=GitGutterLineRemoved"]
call assert_equal(expected, s:signs('fixture.txt'))
endfunction
function Test_remove_first_lines()
execute '1d'
write
let expected = ["line=1 id=3000 name=GitGutterLineRemovedFirstLine"]
call assert_equal(expected, s:signs('fixture.txt'))
endfunction
function Test_edit_file_with_same_name_as_a_branch()
normal 5Gi*
call system('git checkout -b fixture.txt')
write
let expected = ["line=5 id=3000 name=GitGutterLineModified"]
call assert_equal(expected, s:signs('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
write
let expected = ["line=1 id=3000 name=GitGutterLineAdded"]
call assert_equal(expected, s:signs('fileAddedToGit.tmp'))
endfunction
function Test_filename_with_equals()
call system('touch =fixture=.txt && git add =fixture=.txt')
edit =fixture=.txt
normal ggo*
write
let expected = [
\ 'line=1 id=3000 name=GitGutterLineAdded',
\ 'line=2 id=3001 name=GitGutterLineAdded'
\ ]
call assert_equal(expected, s:signs('=fixture=.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*
write
let expected = [
\ 'line=1 id=3000 name=GitGutterLineAdded',
\ 'line=2 id=3001 name=GitGutterLineAdded'
\ ]
call assert_equal(expected, s:signs('fix[tu]re.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
write
let expected = ['line=5 id=3000 name=GitGutterLineRemoved']
call assert_equal(expected, s:signs('symlink'))
endfunction
function Test_keep_alt()
enew
execute "normal! \<C-^>"
call assert_equal('fixture.txt', bufname(''))
call assert_equal('', bufname('#'))
normal ggx
doautocmd CursorHold
call assert_equal('', bufname('#'))
endfunction
function Test_keep_modified()
normal 5Go*
call assert_equal(1, getbufvar('', '&modified'))
doautocmd CursorHold
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("']"))
doautocmd CursorHold
call assert_equal([0,6,1,0], getpos("'["))
call assert_equal([0,6,2,0], getpos("']"))
endfunction
function Test_no_modifications()
call assert_equal([], s:signs('fixture.txt'))
endfunction
function Test_orphaned_signs()
execute "normal 5GoX\<CR>Y"
write
6d
write
let expected = ['line=6 id=3001 name=GitGutterLineAdded']
call assert_equal(expected, s:signs('fixture.txt'))
endfunction
function Test_sign_column_always()
let g:gitgutter_sign_column_always=1
write
let expected = ['line=9999 id=2999 name=GitGutterDummy']
call assert_equal(expected, s:signs('fixture.txt'))
let g:gitgutter_sign_column_always=0
endfunction
function Test_untracked_file_outside_repo()
let tmp = tempname()
call system('touch '.tmp)
execute 'edit '.tmp
call assert_equal([], s:signs(tmp))
endfunction
function Test_untracked_file_within_repo()
let tmp = 'untrackedFileWithinRepo.tmp'
call system('touch '.tmp)
execute 'edit '.tmp
normal ggo*
doautocmd CursorHold
call assert_equal([], s:signs(tmp))
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*
doautocmd CursorHold
call assert_equal([], s:signs(tmp))
call system('rm '.tmp)
endfunction
function Test_hunk_outside_noop()
normal 5G
GitGutterStageHunk
call assert_equal([], s:signs('fixture.txt'))
call assert_equal([], s:git_diff())
call assert_equal([], s:git_diff_staged())
GitGutterUndoHunk
call assert_equal([], s:signs('fixture.txt'))
call assert_equal([], s:git_diff())
call assert_equal([], s:git_diff_staged())
endfunction
function Test_hunk_stage()
let _shell = &shell
set shell=foo
normal 5Gi*
GitGutterStageHunk
call assert_equal('foo', &shell)
let &shell = _shell
call assert_equal([], s:signs('fixture.txt'))
call assert_equal([], s:git_diff())
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())
endfunction
function Test_hunk_stage_nearby_hunk()
execute "normal! 2Gox\<CR>y\<CR>z"
normal 2jdd
normal k
GitGutterStageHunk
let expected = [
\ 'line=3 id=3000 name=GitGutterLineAdded',
\ 'line=4 id=3001 name=GitGutterLineAdded',
\ 'line=5 id=3002 name=GitGutterLineAdded'
\ ]
call assert_equal(expected, s:signs('fixture.txt'))
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())
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())
endfunction
function Test_hunk_undo()
let _shell = &shell
set shell=foo
normal 5Gi*
GitGutterUndoHunk
write " write file so we can verify git diff (--staged)
call assert_equal('foo', &shell)
let &shell = _shell
call assert_equal([], s:signs('fixture.txt'))
call assert_equal([], s:git_diff())
call assert_equal([], s:git_diff_staged())
endfunction
function Test_undo_nearby_hunk()
execute "normal! 2Gox\<CR>y\<CR>z"
normal 2jdd
normal k
GitGutterUndoHunk
write " write file so we can verify git diff (--staged)
let expected = [
\ 'line=3 id=3000 name=GitGutterLineAdded',
\ 'line=4 id=3001 name=GitGutterLineAdded',
\ 'line=5 id=3002 name=GitGutterLineAdded'
\ ]
call assert_equal(expected, s:signs('fixture.txt'))
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())
call assert_equal([], s:git_diff_staged())
endfunction

View File

@ -78,7 +78,7 @@ let NERDTreeShowHidden=0
let NERDTreeIgnore = ['\.pyc$', '__pycache__']
let g:NERDTreeWinSize=35
map <leader>nn :NERDTreeToggle<cr>
map <leader>nb :NERDTreeFromBookmark
map <leader>nb :NERDTreeFromBookmark<Space>
map <leader>nf :NERDTreeFind<cr>