/
gitgutter.vim
178 lines (139 loc) · 4.5 KB
/
gitgutter.vim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
" get sign ctrembg color
function! s:get_sign_ctermbg()
redir => sign_col
silent hi SignColumn
redir END
let index = match(sign_col, 'ctermbg')
let ctermbg = strpart(sign_col, index + 8, 3)
return ctermbg
endfunction
" get sign guibg color
function! s:get_sign_guibg()
redir => sign_col
silent hi SignColumn
redir END
let index = match(sign_col, 'guibg')
let guibg = strpart(sign_col, index + 6, 7)
return guibg
endfunction
" initialization
function! s:init()
let ctermbg = s:get_sign_ctermbg()
let guibg = s:get_sign_guibg()
exec 'hi gitGutterChange ctermfg=darkyellow guifg=#ffcc00 ctermbg=' . ctermbg . ' guibg=' . guibg . ' cterm=bold gui=bold'
exec 'hi gitGutterAdd ctermfg=darkgreen guifg=#00ee00 ctermbg=' . ctermbg . ' guibg=' . guibg . ' cterm=bold gui=bold'
exec 'hi gitGutterDelete ctermfg=darkred guifg=#dd0000 ctermbg=' . ctermbg . ' guibg=' . guibg . ' cterm=bold gui=bold'
sign define change text=! texthl=gitGutterChange
sign define add text=+ texthl=gitGutterAdd
sign define delete_top text=^ texthl=gitGutterDelete
sign define delete_bottom text=_ texthl=gitGutterDelete
" init window variables
if !exists('w:marked_lines')
let w:marked_lines = {}
let w:prev_marked_lines = {}
endif
endfunction
" mark a gutter
" name: sign name
" begin: mark begin line number
" end: mark end line number
function! s:mark(name, begin, end)
let i = str2nr(a:begin)
let end = str2nr(a:end)
while (i <= end)
" reset
call s:reset_mark(i)
" set
exe ":sign place " . i . " line=" . i . " name=" . a:name . " file=" . expand("%:p")
let w:marked_lines[i] = a:name
let i += 1
endwhile
endfunction
" reset mark
function! s:reset_mark(i)
if has_key(w:prev_marked_lines, a:i)
exe ":sign unplace " . a:i . " file=" . expand("%:p")
call remove(w:prev_marked_lines, a:i)
endif
endfunction
" get a path of temporary file for current file
function! s:get_current_file_path()
return substitute(tempname(), '\', '/', 'g')
endfunction
" check for a git repository
function! s:is_git_repos()
let path = expand("%:r")
let ret = system('git status ' . path . ' 2> /dev/null; echo $?')
if ret
return 0
endif
return 1
endfunction
" check for a file status
function! s:is_modified()
redir => ret
silent se modified?
redir END
let ret = ret[1: ] " 改行を削除
if (ret == 'nomodified')
return 0
endif
return 1
endfunction
" get different between HEAD and current
" current: current file path
function! s:get_diff(current)
let filename = expand("%")
let prefix = system('git rev-parse --show-prefix')[ :-2]
echo 'git show HEAD:' . prefix . filename . ' | diff - ' . a:current
let diff = system('git show HEAD:' . prefix . filename . ' | diff - ' . a:current)
return split(diff, '\n')
endfunction
" git gutter
function! gitgutter#git_gutter()
" check for a git repo
if (!s:is_git_repos())
return
endif
" check for a file stat
if (!s:is_modified())
" return
endif
" get diff
let current = s:get_current_file_path()
silent execute 'write! ' . escape(current, ' ')
let diff = s:get_diff(current)
" init marked lines
let w:prev_marked_lines = w:marked_lines
let w:marked_lines = {}
" parse diff
for line in diff
" pattern match (e.g. '12c24,25')
let head_pattern = '^\([0-9]\+\),\?\([0-9]*\)\([acd]\)\([0-9]\+\),\?\([0-9]*\)$'
if (match(line, head_pattern) >= 0)
let [origin,before_begin,before_end,ope,after_begin,after_end;_] = matchlist(line, head_pattern)
" mark only one line when end is empty
if (after_end == '')
let after_end = after_begin
endif
" when added
if (ope == 'a')
call s:mark('add', after_begin, after_end)
" when changed
elseif (ope == 'c')
call s:mark('change', after_begin, after_end)
" when deleted
elseif (ope == 'd')
if after_begin > 0
call s:mark('delete_bottom', after_begin, after_end)
endif
call s:mark('delete_top', after_begin + 1, after_end + 1)
endif
endif
endfor
for i in keys(w:prev_marked_lines)
call s:reset_mark(i)
endfor
endfunction
" main
call s:init()