Skip to content

Commit 407908a

Browse files
committed
Merge pull request #786 from fatih/t-yuki-vim-go-coverlay
Coverage overlay integration (vim-go-coverlay)
2 parents 5c282de + 0eff026 commit 407908a

File tree

19 files changed

+571
-46
lines changed

19 files changed

+571
-46
lines changed

Rakefile

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env rake
2+
3+
task :ci => [:dump, :test]
4+
5+
task :dump do
6+
sh 'vim --version'
7+
end
8+
9+
# Firstly, `bundle install; bundle install --deployment`
10+
# Then, `rake test`
11+
task :test do
12+
sh 'bundle exec vim-flavor test'
13+
end

VimFlavor

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
flavor 'fatih/vim-go', '~> 1.5'

autoload/go/cmd.vim

-41
Original file line numberDiff line numberDiff line change
@@ -290,47 +290,6 @@ function! go#cmd#TestFunc(bang, ...)
290290
call call('go#cmd#Test', args)
291291
endfunction
292292

293-
let s:coverage_handler_id = ''
294-
let s:coverage_handler_jobs = {}
295-
296-
function! s:coverage_handler(job, exit_status, data)
297-
if !has_key(s:coverage_handler_jobs, a:job.id)
298-
return
299-
endif
300-
let l:tmpname = s:coverage_handler_jobs[a:job.id]
301-
if a:exit_status == 0
302-
let openHTML = 'go tool cover -html='.l:tmpname
303-
call go#tool#ExecuteInDir(openHTML)
304-
endif
305-
call delete(l:tmpname)
306-
unlet s:coverage_handler_jobs[a:job.id]
307-
endfunction
308-
309-
" Coverage creates a new cover profile with 'go test -coverprofile' and opens
310-
" a new HTML coverage page from that profile.
311-
function! go#cmd#Coverage(bang, ...)
312-
let l:tmpname=tempname()
313-
let args = [a:bang, 0, "-coverprofile", l:tmpname]
314-
315-
if a:0
316-
call extend(args, a:000)
317-
endif
318-
let id = call('go#cmd#Test', args)
319-
if has('nvim')
320-
if s:coverage_handler_id == ''
321-
let s:coverage_handler_id = go#jobcontrol#AddHandler(function('s:coverage_handler'))
322-
endif
323-
let s:coverage_handler_jobs[id] = l:tmpname
324-
return
325-
endif
326-
if !v:shell_error
327-
let openHTML = 'go tool cover -html='.l:tmpname
328-
call go#tool#ExecuteInDir(openHTML)
329-
endif
330-
331-
call delete(l:tmpname)
332-
endfunction
333-
334293
" Generate runs 'go generate' in similar fashion to go#cmd#Build()
335294
function! go#cmd#Generate(bang, ...)
336295
let default_makeprg = &makeprg

autoload/go/coverage.vim

+239
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
let s:toggle = 0
2+
3+
" Buffer creates a new cover profile with 'go test -coverprofile' and changes
4+
" teh current buffers highlighting to show covered and uncovered sections of
5+
" the code. If run again it clears the annotation
6+
function! go#coverage#Buffer(bang, ...)
7+
if s:toggle
8+
call go#coverage#Clear()
9+
return
10+
endif
11+
12+
let s:toggle = 1
13+
let l:tmpname=tempname()
14+
let args = [a:bang, 0, "-coverprofile", l:tmpname]
15+
16+
if a:0
17+
call extend(args, a:000)
18+
endif
19+
"TODO: add -coverpkg options based on current buf list
20+
let id = call('go#cmd#Test', args)
21+
if has('nvim')
22+
if s:coverage_handler_id == ''
23+
let s:coverage_handler_id = go#jobcontrol#AddHandler(function('s:coverage_handler'))
24+
endif
25+
let s:coverage_handler_jobs[id] = l:tmpname
26+
return
27+
endif
28+
if !v:shell_error
29+
call go#coverage#overlay(l:tmpname)
30+
endif
31+
call delete(l:tmpname)
32+
endfunction
33+
34+
" Clear clears and resets the buffer annotation matches
35+
function! go#coverage#Clear()
36+
if exists("g:syntax_on") | syntax enable | endif
37+
38+
if exists("s:toggle") | let s:toggle = 0 | endif
39+
40+
" remove the autocmd we defined
41+
if exists("#BufWinLeave#<buffer>")
42+
autocmd! BufWinLeave <buffer>
43+
endif
44+
45+
call clearmatches()
46+
endfunction
47+
48+
" Browser creates a new cover profile with 'go test -coverprofile' and opens
49+
" a new HTML coverage page from that profile in a new browser
50+
function! go#coverage#Browser(bang, ...)
51+
let l:tmpname=tempname()
52+
let args = [a:bang, 0, "-coverprofile", l:tmpname]
53+
54+
if a:0
55+
call extend(args, a:000)
56+
endif
57+
let id = call('go#cmd#Test', args)
58+
if has('nvim')
59+
if s:coverage_browser_handler_id == ''
60+
let s:coverage_browser_handler_id = go#jobcontrol#AddHandler(function('s:coverage_browser_handler'))
61+
endif
62+
let s:coverage_browser_handler_jobs[id] = l:tmpname
63+
return
64+
endif
65+
if !v:shell_error
66+
let openHTML = 'go tool cover -html='.l:tmpname
67+
call go#tool#ExecuteInDir(openHTML)
68+
endif
69+
70+
call delete(l:tmpname)
71+
endfunction
72+
73+
" Parses a single line from the cover file generated via go test -coverprofile
74+
" and returns a single coverage profile block.
75+
function! go#coverage#parsegocoverline(line)
76+
" file:startline.col,endline.col numstmt count
77+
let mx = '\([^:]\+\):\(\d\+\)\.\(\d\+\),\(\d\+\)\.\(\d\+\)\s\(\d\+\)\s\(\d\+\)'
78+
let tokens = matchlist(a:line, mx)
79+
let ret = {}
80+
let ret.file = tokens[1]
81+
let ret.startline = str2nr(tokens[2])
82+
let ret.startcol = str2nr(tokens[3])
83+
let ret.endline = str2nr(tokens[4])
84+
let ret.endcol = str2nr(tokens[5])
85+
let ret.numstmt = tokens[6]
86+
let ret.cnt = tokens[7]
87+
return ret
88+
endfunction
89+
90+
" Generates matches to be added to matchaddpos for the given coverage profile
91+
" block
92+
function! go#coverage#genmatch(cov)
93+
let color = 'covered'
94+
if a:cov.cnt == 0
95+
let color = 'uncover'
96+
endif
97+
98+
let matches = []
99+
100+
" if start and end are the same, also specify the byte length
101+
" example: foo.go:92.2,92.65 1 0
102+
if a:cov.startline == a:cov.endline
103+
call add(matches, {
104+
\ 'group': color,
105+
\ 'pos': [[a:cov.startline, a:cov.startcol, a:cov.endcol - a:cov.startcol]],
106+
\ 'priority': 2,
107+
\ })
108+
return matches
109+
endif
110+
111+
" add start columns. Because we don't know the length of the of
112+
" the line, we assume it is at maximum 200 bytes. I know this is hacky,
113+
" but that's only way of fixing the issue
114+
call add(matches, {
115+
\ 'group': color,
116+
\ 'pos': [[a:cov.startline, a:cov.startcol, 200]],
117+
\ 'priority': 2,
118+
\ })
119+
120+
" and then the remaining lines
121+
let start_line = a:cov.startline
122+
while start_line < a:cov.endline
123+
let start_line += 1
124+
call add(matches, {
125+
\ 'group': color,
126+
\ 'pos': [[start_line]],
127+
\ 'priority': 2,
128+
\ })
129+
endwhile
130+
131+
" finally end columns
132+
call add(matches, {
133+
\ 'group': color,
134+
\ 'pos': [[a:cov.endline, a:cov.endcol-1]],
135+
\ 'priority': 2,
136+
\ })
137+
138+
return matches
139+
endfunction
140+
141+
" Reads the given coverprofile file and annotates the current buffer
142+
function! go#coverage#overlay(file)
143+
if !filereadable(a:file)
144+
return
145+
endif
146+
let lines = readfile(a:file)
147+
148+
" cover mode, by default it's 'set'. Just here for debugging purposes
149+
let mode = lines[0]
150+
151+
" contains matches for matchaddpos()
152+
let matches = []
153+
154+
" first mark all lines as normaltext. We use a custom group to not
155+
" interfere with other buffers highlightings. Because the priority is
156+
" lower than the cover and uncover matches, it'll be overriden.
157+
let cnt = 1
158+
while cnt <= line('$')
159+
call add(matches, {'group': 'normaltext', 'pos': [cnt], 'priority': 1})
160+
let cnt += 1
161+
endwhile
162+
163+
let fname = expand('%:t')
164+
165+
" when called for a _test.go file, run the coverage for the actuall file
166+
" file
167+
if fname =~# '^\f\+_test\.go$'
168+
let l:root = split(fname, '_test.go$')[0]
169+
let fname = l:root . ".go"
170+
171+
" open the alternate file to show the coverage
172+
exe ":edit ". fname
173+
endif
174+
175+
for line in lines[1:]
176+
let cov = go#coverage#parsegocoverline(line)
177+
178+
" TODO(arslan): for now only include the coverage for the current
179+
" buffer
180+
if fname != fnamemodify(cov.file, ':t')
181+
continue
182+
endif
183+
184+
call extend(matches, go#coverage#genmatch(cov))
185+
endfor
186+
187+
syntax manual
188+
highlight normaltext term=bold ctermfg=59 guifg=#75715E
189+
highlight covered term=bold ctermfg=118 guifg=#A6E22E
190+
highlight uncover term=bold ctermfg=197 guifg=#F92672
191+
192+
" clear the matches if we leave the buffer
193+
autocmd BufWinLeave <buffer> call go#coverage#Clear()
194+
195+
for m in matches
196+
call matchaddpos(m.group, m.pos)
197+
endfor
198+
endfunction
199+
200+
201+
" -----------------------
202+
" | Neovim job handlers |
203+
" -----------------------
204+
205+
let s:coverage_handler_id = ''
206+
let s:coverage_handler_jobs = {}
207+
208+
function! s:coverage_handler(job, exit_status, data)
209+
if !has_key(s:coverage_handler_jobs, a:job.id)
210+
return
211+
endif
212+
let l:tmpname = s:coverage_handler_jobs[a:job.id]
213+
if a:exit_status == 0
214+
call go#coverage#overlay(l:tmpname)
215+
endif
216+
217+
call delete(l:tmpname)
218+
unlet s:coverage_handler_jobs[a:job.id]
219+
endfunction
220+
221+
222+
let s:coverage_browser_handler_id = ''
223+
let s:coverage_browser_handler_jobs = {}
224+
225+
function! s:coverage_browser_handler(job, exit_status, data)
226+
if !has_key(s:coverage_browser_handler_jobs, a:job.id)
227+
return
228+
endif
229+
let l:tmpname = s:coverage_browser_handler_jobs[a:job.id]
230+
if a:exit_status == 0
231+
let openHTML = 'go tool cover -html='.l:tmpname
232+
call go#tool#ExecuteInDir(openHTML)
233+
endif
234+
call delete(l:tmpname)
235+
unlet s:coverage_browser_handler_jobs[a:job.id]
236+
endfunction
237+
238+
239+
" vim:ts=4:sw=4:et

doc/vim-go.txt

+12-3
Original file line numberDiff line numberDiff line change
@@ -323,13 +323,22 @@ COMMANDS *go-commands*
323323

324324
If [!] is not given the first error is jumped to.
325325

326-
If using neovim `:GoTestCompile` will run in a new terminal or run asynchronously
327-
in the background according to |g:go_term_enabled|. You can set the mode of
328-
the new terminal with |g:go_term_mode|.
326+
If using neovim `:GoTestCompile` will run in a new terminal or run
327+
asynchronously in the background according to |g:go_term_enabled|. You can
328+
set the mode of the new terminal with |g:go_term_mode|.
329329

330330
*:GoCoverage*
331331
:GoCoverage[!] [options]
332332

333+
Create a coverage profile and annotates the current file's source code. If
334+
called again clears the annotation (works as a toggle)
335+
336+
If [!] is not given the first error is jumped to.
337+
338+
339+
*:GoCoverageBrowser*
340+
:GoCoverageBrowser[!] [options]
341+
333342
Create a coverage profile and open a browser to display the annotated
334343
source code of the current package.
335344

ftplugin/go/commands.vim

+4-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ command! -nargs=* -bang GoInstall call go#cmd#Install(<bang>0, <f-args>)
2626
command! -nargs=* -bang GoTest call go#cmd#Test(<bang>0, 0, <f-args>)
2727
command! -nargs=* -bang GoTestFunc call go#cmd#TestFunc(<bang>0, <f-args>)
2828
command! -nargs=* -bang GoTestCompile call go#cmd#Test(<bang>0, 1, <f-args>)
29-
command! -nargs=* -bang GoCoverage call go#cmd#Coverage(<bang>0, <f-args>)
29+
30+
" -- cover
31+
command! -nargs=* -bang GoCoverage call go#coverage#Buffer(<bang>0, <f-args>)
32+
command! -nargs=* -bang GoCoverageBrowser call go#coverage#Browser(<bang>0, <f-args>)
3033

3134
" -- play
3235
command! -nargs=0 -range=% GoPlay call go#play#Share(<count>, <line1>, <line2>)

ftplugin/go/mappings.vim

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ nnoremap <silent> <Plug>(go-install) :<C-u>call go#cmd#Install(!g:go_jump_to_err
2323
nnoremap <silent> <Plug>(go-test) :<C-u>call go#cmd#Test(!g:go_jump_to_error, 0)<CR>
2424
nnoremap <silent> <Plug>(go-test-func) :<C-u>call go#cmd#TestFunc(!g:go_jump_to_error)<CR>
2525
nnoremap <silent> <Plug>(go-test-compile) :<C-u>call go#cmd#Test(!g:go_jump_to_error, 1)<CR>
26-
nnoremap <silent> <Plug>(go-coverage) :<C-u>call go#cmd#Coverage(!g:go_jump_to_error)<CR>
26+
27+
nnoremap <silent> <Plug>(go-coverage) :<C-u>call go#coverage#Buffer(!g:go_jump_to_error)<CR>
28+
nnoremap <silent> <Plug>(go-coverage-browser) :<C-u>call go#coverage#Browser(!g:go_jump_to_error)<CR>
2729
2830
nnoremap <silent> <Plug>(go-files) :<C-u>call go#tool#Files()<CR>
2931
nnoremap <silent> <Plug>(go-deps) :<C-u>call go#tool#Deps()<CR>

0 commit comments

Comments
 (0)