-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Coverage overlay in buffers #785
Changes from all commits
12c0ca5
5b990f0
38bfa6b
768ca02
32b7a06
319539d
aabd713
1262cfa
dac0c65
f3cc980
7685a2b
70c8cf8
4d9e43c
fabeb9d
674268f
0ba4cf2
bd79496
9cc31c8
5518f61
67cbd56
2934945
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
source 'https://rubygems.org' | ||
|
||
gem 'vim-flavor', '~> 2.2.1' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#!/usr/bin/env rake | ||
|
||
task :ci => [:dump, :test] | ||
|
||
task :dump do | ||
sh 'vim --version' | ||
end | ||
|
||
# Firstly, `bundle install; bundle install --deployment` | ||
# Then, `rake test` | ||
task :test do | ||
sh 'bundle exec vim-flavor test' | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
if !exists("g:go_gopath") | ||
let g:go_gopath = $GOPATH | ||
endif | ||
|
||
augroup plugin-go-coverlay | ||
autocmd! | ||
autocmd BufEnter,BufWinEnter,BufFilePost * call go#coverlay#draw() | ||
autocmd BufWinLeave * call go#coverlay#clear() | ||
augroup END | ||
|
||
function! go#coverlay#draw() | ||
call go#coverlay#hook() | ||
call go#coverlay#clear() | ||
for m in b:go_coverlay_matches | ||
let id = matchadd(m.group, m.pattern, m.priority) | ||
call add(b:go_coverlay_match_ids, id) | ||
endfor | ||
endfunction | ||
|
||
function! go#coverlay#hook() | ||
"TODO: can we initialize buf local vars more smartly? | ||
if !exists("b:go_coverlay_matches") | ||
let b:go_coverlay_matches = [] | ||
endif | ||
if !exists("b:go_coverlay_match_ids") | ||
let b:go_coverlay_match_ids = [] | ||
endif | ||
endfunction | ||
|
||
"findbufnr look for the number of buffer that opens `file`, | ||
" as it is displayed by the ":ls" command. | ||
"If the buffer doesn't exist, -1 is returned. | ||
function! go#coverlay#findbufnr(file) | ||
if a:file[0] == "_" | ||
return bufnr(a:file[1:]) | ||
endif | ||
for path in split(g:go_gopath, ':') | ||
let nr = bufnr(path . '/src/' . a:file) | ||
if nr != -1 | ||
return nr | ||
endif | ||
endfor | ||
return -1 | ||
endfunction | ||
|
||
function! go#coverlay#isopenedon(file, bufnr) | ||
if a:file[0] == "_" | ||
if bufnr(a:file[1:]) == a:bufnr | ||
return 1 | ||
endif | ||
return 0 | ||
endif | ||
for path in split(g:go_gopath, ':') | ||
if bufnr(path . '/src/' . a:file) == a:bufnr | ||
return 1 | ||
endif | ||
endfor | ||
return 0 | ||
endfunction | ||
|
||
function! go#coverlay#parsegocoverline(line) | ||
" file:startline.col,endline.col numstmt count | ||
let mx = '\([^:]\+\):\(\d\+\)\.\(\d\+\),\(\d\+\)\.\(\d\+\)\s\(\d\+\)\s\(\d\+\)' | ||
let l = matchstr(a:line, mx) | ||
let ret = {} | ||
let ret.file = substitute(l, mx, '\1', '') | ||
let ret.startline = substitute(l, mx, '\2', '') | ||
let ret.startcol = substitute(l, mx, '\3', '') | ||
let ret.endline = substitute(l, mx, '\4', '') | ||
let ret.endcol = substitute(l, mx, '\5', '') | ||
let ret.numstmt = substitute(l, mx, '\6', '') | ||
let ret.cnt = substitute(l, mx, '\7', '') | ||
return ret | ||
endfunction | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can simplify it like:
|
||
|
||
function! go#coverlay#genmatch(cov) | ||
let pat1 = '\%>' . a:cov.startline . 'l' | ||
let pat2 = '\%<' . a:cov.endline . 'l' | ||
let pat3 = '\|\%' . a:cov.startline . 'l\_^\s\+\|\%' . a:cov.endline . 'l\_^\s\+\(\}$\)\@!' | ||
let color = 'covered' | ||
let prio = 6 | ||
if a:cov.cnt == 0 | ||
let color = 'uncover' | ||
let prio = 5 | ||
endif | ||
return {'group': color, 'pattern': pat1 . '\_^\s\+' . pat2 . pat3, 'priority': prio} | ||
endfunction | ||
|
||
function! go#coverlay#overlay(file) | ||
call go#coverlay#hook() | ||
|
||
highlight covered term=bold ctermbg=green guibg=green | ||
highlight uncover term=bold ctermbg=red guibg=red | ||
|
||
if !filereadable(a:file) | ||
return | ||
endif | ||
let lines = readfile(a:file) | ||
let mode = lines[0] | ||
for line in lines[1:] | ||
let c = go#coverlay#parsegocoverline(line) | ||
let nr = go#coverlay#findbufnr(c.file) | ||
if nr == -1 | ||
"should we records cov data | ||
" even if it is not opened currently? | ||
continue | ||
endif | ||
let m = go#coverlay#genmatch(c) | ||
let matches = get(getbufvar(nr, ""), "go_coverlay_matches", []) | ||
call add(matches, m) | ||
call setbufvar(nr, "go_coverlay_matches", matches) | ||
endfor | ||
"TODO: can we draw other window for split windows mode? | ||
call go#coverlay#draw() | ||
endfunction | ||
|
||
let s:coverlay_handler_id = '' | ||
let s:coverlay_handler_jobs = {} | ||
|
||
function! s:coverlay_handler(job, exit_status, data) | ||
if !has_key(s:coverlay_handler_jobs, a:job.id) | ||
return | ||
endif | ||
let l:tmpname = s:coverlay_handler_jobs[a:job.id] | ||
if a:exit_status == 0 | ||
call go#coverlay#overlay(l:tmpname) | ||
endif | ||
|
||
call delete(l:tmpname) | ||
unlet s:coverlay_handler_jobs[a:job.id] | ||
endfunction | ||
|
||
function! go#coverlay#Coverlay(bang, ...) | ||
call go#coverlay#Clearlay() | ||
let l:tmpname=tempname() | ||
let args = [a:bang, 0, "-coverprofile", l:tmpname] | ||
|
||
if a:0 | ||
call extend(args, a:000) | ||
endif | ||
"TODO: add -coverpkg options based on current buf list | ||
let id = call('go#cmd#Test', args) | ||
if has('nvim') | ||
if s:coverlay_handler_id == '' | ||
let s:coverlay_handler_id = go#jobcontrol#AddHandler(function('s:coverlay_handler')) | ||
endif | ||
let s:coverlay_handler_jobs[id] = l:tmpname | ||
return | ||
endif | ||
if !v:shell_error | ||
call go#coverlay#overlay(l:tmpname) | ||
endif | ||
call delete(l:tmpname) | ||
endfunction | ||
|
||
function! go#coverlay#Clearlay() | ||
call go#coverlay#hook() | ||
call go#coverlay#clear() | ||
let b:go_coverlay_matches = [] | ||
endfunction | ||
|
||
function! go#coverlay#clear(...) | ||
for id in b:go_coverlay_match_ids | ||
call matchdelete(id) | ||
endfor | ||
let b:go_coverlay_match_ids = [] | ||
endfunction | ||
|
||
function! go#coverlay#matches() | ||
call go#coverlay#hook() | ||
return b:go_coverlay_matches | ||
endfunction | ||
|
||
" vim:ts=4:sw=4:et |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
if exists("b:did_ftplugin_go_coverlay") | ||
finish | ||
endif | ||
let b:did_ftplugin_go_coverlay = 1 | ||
|
||
" Some handy plug mappings | ||
nnoremap <silent> <Plug>(go-coverlay) :<C-u>call go#coverlay#Coverlay(!g:go_jump_to_error)<CR> | ||
nnoremap <silent> <Plug>(go-clearlay) :<C-u>call go#coverlay#Clearlay()<CR> | ||
|
||
" coverlay | ||
command! -nargs=* -bang GoCoverlay call go#coverlay#Coverlay(<bang>0, <f-args>) | ||
command! -nargs=* GoClearlay call go#coverlay#Clearlay(<f-args>) | ||
|
||
" vim:ts=4:sw=4:et | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some notes:
Our current command will not open the browser anymore, instead it will use this current implementation. However if people want they can still access them via |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
" to execute, `rake test` on parent dir | ||
|
||
describe 'go#coverlay#Coverlay' | ||
before | ||
new | ||
let g:curdir = expand('<sfile>:p:h') . '/' | ||
let g:srcpath = 't/fixtures/src/' | ||
let g:sample = 'pkg1/sample.go' | ||
let g:sampleabs = g:curdir . g:srcpath . 'pkg1/sample.go' | ||
let g:samplecover = g:curdir . g:srcpath . 'pkg1/sample.out' | ||
let g:go_gopath = g:curdir . 't/fixtures' | ||
execute "badd " . g:srcpath . g:sample | ||
execute "buffer " . bufnr("$") | ||
end | ||
after | ||
execute "bprev" | ||
execute "bdelete " . g:srcpath . g:sample | ||
close! | ||
end | ||
|
||
it 'puts match to the list' | ||
call go#coverlay#Coverlay(0) | ||
Expect len(go#coverlay#matches()) == 5 | ||
call go#coverlay#Clearlay() | ||
Expect len(go#coverlay#matches()) == 0 | ||
|
||
call go#coverlay#Coverlay(0) | ||
Expect len(go#coverlay#matches()) == 5 | ||
call go#coverlay#Clearlay() | ||
Expect len(go#coverlay#matches()) == 0 | ||
|
||
call go#coverlay#Coverlay(0) | ||
Expect len(go#coverlay#matches()) == 5 | ||
call go#coverlay#Coverlay(0) | ||
Expect len(go#coverlay#matches()) == 5 | ||
call go#coverlay#Clearlay() | ||
Expect len(go#coverlay#matches()) == 0 | ||
end | ||
end | ||
|
||
describe 'go#coverlay#Coverlay fail' | ||
before | ||
new | ||
let g:curdir = expand('<sfile>:p:h') . '/' | ||
let g:srcpath = 't/fixtures/src/' | ||
let g:sample = 'failtest/sample.go' | ||
let g:sampletest = 'failtest/sample_test.go' | ||
let g:sampleabs = g:curdir . g:srcpath . 'failtest/sample.go' | ||
let g:go_gopath = g:curdir . 't/fixtures' | ||
execute "badd " . g:srcpath . g:sample | ||
execute "buffer " . bufnr("$") | ||
end | ||
after | ||
execute "bprev" | ||
execute "bdelete " . g:srcpath . g:sampletest | ||
execute "bdelete " . g:srcpath . g:sample | ||
end | ||
|
||
it 'does nothing if test fail' | ||
call go#coverlay#Coverlay(0) | ||
Expect len(go#coverlay#matches()) == 0 | ||
Expect len(getqflist()) == 1 | ||
end | ||
end | ||
|
||
describe 'go#coverlay#Coverlay build fail' | ||
before | ||
new | ||
let g:curdir = expand('<sfile>:p:h') . '/' | ||
let g:srcpath = 't/fixtures/src/' | ||
let g:sample = 'buildfail/sample.go' | ||
let g:sampleabs = g:curdir . g:srcpath . 'buildfail/sample.go' | ||
let g:go_gopath = g:curdir . 't/fixtures' | ||
execute "badd " . g:srcpath . g:sample | ||
execute "buffer " . bufnr("$") | ||
end | ||
after | ||
execute "bprev" | ||
execute "bdelete " . g:srcpath . g:sample | ||
end | ||
|
||
it 'does nothing if test fail' | ||
call go#coverlay#Coverlay(0) | ||
Expect len(go#coverlay#matches()) == 0 | ||
end | ||
end | ||
|
||
describe 'go#coverlay#Coverlay build test fail' | ||
before | ||
new | ||
let g:curdir = expand('<sfile>:p:h') . '/' | ||
let g:srcpath = 't/fixtures/src/' | ||
let g:sample = 'buildtestfail/sample.go' | ||
let g:sampleabs = g:curdir . g:srcpath . 'buildtestfail/sample.go' | ||
let g:go_gopath = g:curdir . 't/fixtures' | ||
execute "badd " . g:srcpath . g:sample | ||
execute "buffer " . bufnr("$") | ||
end | ||
after | ||
execute "bprev" | ||
execute "bdelete " . g:srcpath . g:sample | ||
end | ||
|
||
it 'does nothing if test fail' | ||
call go#coverlay#Coverlay(0) | ||
Expect len(go#coverlay#matches()) == 0 | ||
end | ||
end | ||
|
||
describe 'go#coverlay#findbufnr' | ||
before | ||
new | ||
let g:curdir = expand('<sfile>:p:h') . '/' | ||
let g:srcpath = 't/fixtures/src/' | ||
let g:sample = 'pkg1/sample.go' | ||
let g:sample2 = 'pkg2/sample.go' | ||
let g:sampleabs = g:curdir . g:srcpath . 'pkg1/sample.go' | ||
let g:sampleabs2 = g:curdir . g:srcpath . 'pkg2/sample.go' | ||
let g:go_gopath = g:curdir . 't/fixtures' | ||
execute "badd " . g:srcpath . g:sample | ||
execute "badd " . g:srcpath . g:sample2 | ||
end | ||
after | ||
execute "bdelete " . g:srcpath . g:sample2 | ||
execute "bdelete " . g:srcpath . g:sample | ||
close! | ||
end | ||
|
||
it 'returns BUFNR if FILE is opened at BUFNR' | ||
Expect go#coverlay#findbufnr('_' . g:sampleabs) == bufnr(g:sampleabs) | ||
Expect go#coverlay#findbufnr(g:sample) == bufnr(g:sampleabs) | ||
|
||
Expect go#coverlay#findbufnr('_' . g:sampleabs2) == bufnr(g:sampleabs2) | ||
Expect go#coverlay#findbufnr(g:sample2) == bufnr(g:sampleabs2) | ||
end | ||
|
||
it 'returns -1 if FILE is not exists' | ||
Expect go#coverlay#findbufnr('pkg1/NOTEXISTS.go') == -1 | ||
Expect go#coverlay#findbufnr('_' . g:curdir . g:srcpath . 'pkg1/NOTEXISTS.go') == -1 | ||
end | ||
end | ||
|
||
describe 'go#coverlay#isopenedon' | ||
before | ||
new | ||
let g:curdir = expand('<sfile>:p:h') . '/' | ||
let g:srcpath = 't/fixtures/src/' | ||
let g:sample = 'pkg1/sample.go' | ||
let g:sampleabs = g:curdir . g:srcpath . 'pkg1/sample.go' | ||
let g:go_gopath = g:curdir . 't/fixtures' | ||
execute "badd " . g:srcpath . g:sample | ||
end | ||
after | ||
execute "bdelete " . g:srcpath . g:sample | ||
close! | ||
end | ||
|
||
it 'returns 1 if FILE is opened at BUFNR' | ||
Expect go#coverlay#isopenedon('_' . g:sampleabs, bufnr(g:sampleabs)) == 1 | ||
Expect go#coverlay#isopenedon(g:sample, bufnr(g:sampleabs)) == 1 | ||
end | ||
|
||
it 'returns 0 if FILE is not opened at BUFNR' | ||
Expect go#coverlay#isopenedon('_' . g:sampleabs, 42) == 0 | ||
Expect go#coverlay#isopenedon(g:sample, 42) == 0 | ||
end | ||
|
||
it 'returns 0 if FILE is not exists' | ||
Expect go#coverlay#isopenedon('_' . g:curdir . g:srcpath . 'pkg1/NOTEXISTS', bufnr(g:sampleabs)) == 0 | ||
Expect go#coverlay#isopenedon('pkg1/NOTEXISTS.go', bufnr(g:sampleabs)) == 0 | ||
end | ||
end | ||
|
||
|
||
|
||
describe 'go#coverlay#parsegocoverline' | ||
it 'parses a go cover output line and returns as dict' | ||
let d = {'file': 'f',"startline": "1", "startcol": "2", "endline": "3", "endcol": "4", "numstmt": "5", "cnt": "6"} | ||
" file:startline.col,endline.col numstmt count | ||
Expect go#coverlay#parsegocoverline("f:1.2,3.4 5 6") == d | ||
end | ||
end | ||
|
||
describe 'go#coverlay#genmatch' | ||
it 'generate mark pattern from cover data' | ||
let d = {'file': 'f',"startline": "1", "startcol": "2", "endline": "3", "endcol": "4", "numstmt": "5", "cnt": "6"} | ||
Expect go#coverlay#genmatch(d) == {'group': 'covered', "pattern": '\%>1l\_^\s\+\%<3l\|\%1l\_^\s\+\|\%3l\_^\s\+\(\}$\)\@!', "priority": 6} | ||
let d = {'file': 'f',"startline": "1", "startcol": "2", "endline": "3", "endcol": "4", "numstmt": "5", "cnt": "0"} | ||
Expect go#coverlay#genmatch(d) == {'group': 'uncover', "pattern": '\%>1l\_^\s\+\%<3l\|\%1l\_^\s\+\|\%3l\_^\s\+\(\}$\)\@!', "priority": 5} | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be moved to our autogroup section in file
plugin/go.vim