diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000000..d562d4327e --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gem 'vim-flavor', '~> 2.2.1' diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000000..64346ff0aa --- /dev/null +++ b/Rakefile @@ -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 diff --git a/VimFlavor b/VimFlavor new file mode 100644 index 0000000000..e69de29bb2 diff --git a/autoload/go/coverlay.vim b/autoload/go/coverlay.vim new file mode 100644 index 0000000000..17e5ab4121 --- /dev/null +++ b/autoload/go/coverlay.vim @@ -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 + +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 diff --git a/ftplugin/go/coverlay.vim b/ftplugin/go/coverlay.vim new file mode 100644 index 0000000000..d6507802b5 --- /dev/null +++ b/ftplugin/go/coverlay.vim @@ -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 (go-coverlay) :call go#coverlay#Coverlay(!g:go_jump_to_error) +nnoremap (go-clearlay) :call go#coverlay#Clearlay() + +" coverlay +command! -nargs=* -bang GoCoverlay call go#coverlay#Coverlay(0, ) +command! -nargs=* GoClearlay call go#coverlay#Clearlay() + +" vim:ts=4:sw=4:et diff --git a/t/coverlay.vim b/t/coverlay.vim new file mode 100644 index 0000000000..a8732a0dca --- /dev/null +++ b/t/coverlay.vim @@ -0,0 +1,191 @@ +" to execute, `rake test` on parent dir + +describe 'go#coverlay#Coverlay' + before + new + let g:curdir = expand(':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(':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(':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(':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(':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(':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 diff --git a/t/fixtures/src/buildfail/sample.go b/t/fixtures/src/buildfail/sample.go new file mode 100644 index 0000000000..45fc006afd --- /dev/null +++ b/t/fixtures/src/buildfail/sample.go @@ -0,0 +1,13 @@ +// set gopath before +//go:generate go test --coverprofile=sample.out +// go1.5.3 example output: +// GOPATH=`pwd`/fixtures go test --coverprofile=log.out buildfail +// # buildfail +// /tmp/go-build264733986/buildfail/_test/_obj_test/sample.go:7: undefined: IT_SHOULD_BE_BUILD_FAILED +// /tmp/go-build264733986/buildfail/_test/_obj_test/sample.go:8: missing return at end of function +// FAIL buildfail [build failed] +package pkg + +func Sample() int { + IT_SHOULD_BE_BUILD_FAILED +} diff --git a/t/fixtures/src/buildfail/sample_test.go b/t/fixtures/src/buildfail/sample_test.go new file mode 100644 index 0000000000..2356d3ed09 --- /dev/null +++ b/t/fixtures/src/buildfail/sample_test.go @@ -0,0 +1,7 @@ +package pkg + +import "testing" + +func TestSample(t *testing.T) { + Sample() +} diff --git a/t/fixtures/src/buildtestfail/sample.go b/t/fixtures/src/buildtestfail/sample.go new file mode 100644 index 0000000000..792f12349b --- /dev/null +++ b/t/fixtures/src/buildtestfail/sample.go @@ -0,0 +1,7 @@ +// set gopath before +//go:generate go test --coverprofile=sample.out +package pkg + +func Sample() int { + return 1 +} diff --git a/t/fixtures/src/buildtestfail/sample_test.go b/t/fixtures/src/buildtestfail/sample_test.go new file mode 100644 index 0000000000..17c4d4636e --- /dev/null +++ b/t/fixtures/src/buildtestfail/sample_test.go @@ -0,0 +1,15 @@ +// go1.5.3 example output: +// GOPATH=`pwd`/fixtures go test --coverprofile=log.out buildtestfail +// # buildtestfail +// fixtures/src/buildtestfail/sample_test.go:14: undefined: IT_SHOULD_BE_BUILD_FAILED +// FAIL buildtestfail [build failed] +// echo $? +// 2 + +package pkg + +import "testing" + +func TestSample(t *testing.T) { + IT_SHOULD_BE_BUILD_FAILED +} diff --git a/t/fixtures/src/failtest/sample.go b/t/fixtures/src/failtest/sample.go new file mode 100644 index 0000000000..5859e92624 --- /dev/null +++ b/t/fixtures/src/failtest/sample.go @@ -0,0 +1,12 @@ +// set gopath before +//go:generate go test --coverprofile=sample.out +package pkg + +func Sample() int { + if false { + return 0 + } else if false { + return 0 + } + return 1 +} diff --git a/t/fixtures/src/failtest/sample_test.go b/t/fixtures/src/failtest/sample_test.go new file mode 100644 index 0000000000..7f1ade0d73 --- /dev/null +++ b/t/fixtures/src/failtest/sample_test.go @@ -0,0 +1,8 @@ +package pkg + +import "testing" + +func TestSample(t *testing.T) { + Sample() + t.Fatal("itwillfail") +} diff --git a/t/fixtures/src/parsefail/sample.go b/t/fixtures/src/parsefail/sample.go new file mode 100644 index 0000000000..e9f3faa953 --- /dev/null +++ b/t/fixtures/src/parsefail/sample.go @@ -0,0 +1,14 @@ +// set gopath before +//go:generate go test --coverprofile=sample.out +// go1.5.3 example output: +// GOPATH=`pwd`/fixtures go test --coverprofile=log.out parsefail +// # cover parsefail +// 2016/01/17 23:59:08 cover: /home/sey/vimfiles/_vim/bundle/vim-go-coverlay/t/fixtures/src/parsefail/sample.go: /home/sey/vimfiles/_vim/bundle/vim-go-coverlay/t/fixtures/src/parsefail/sample.go:10:1: expected declaration, found 'IDENT' PARSEFAIL +// FAIL parsefail [build failed] +// echo $? +// 2 +package pkg + +PARSEFAIL Sample() int { + return 0 +} diff --git a/t/fixtures/src/parsefail/sample_test.go b/t/fixtures/src/parsefail/sample_test.go new file mode 100644 index 0000000000..2356d3ed09 --- /dev/null +++ b/t/fixtures/src/parsefail/sample_test.go @@ -0,0 +1,7 @@ +package pkg + +import "testing" + +func TestSample(t *testing.T) { + Sample() +} diff --git a/t/fixtures/src/pkg1/sample.go b/t/fixtures/src/pkg1/sample.go new file mode 100644 index 0000000000..6ce94b394b --- /dev/null +++ b/t/fixtures/src/pkg1/sample.go @@ -0,0 +1,12 @@ +// set gopath before +//go:generate go test --coverprofile=sample.out +package pkg1 + +func Sample() int { + if false { + return 0 + } else if false { + return 0 + } + return 1 +} diff --git a/t/fixtures/src/pkg1/sample.out b/t/fixtures/src/pkg1/sample.out new file mode 100644 index 0000000000..909a63f142 --- /dev/null +++ b/t/fixtures/src/pkg1/sample.out @@ -0,0 +1,6 @@ +mode: set +pkg1/sample.go:5.19,6.11 1 1 +pkg1/sample.go:11.2,11.10 1 1 +pkg1/sample.go:6.11,8.3 1 0 +pkg1/sample.go:8.3,8.18 1 1 +pkg1/sample.go:8.18,10.3 1 0 diff --git a/t/fixtures/src/pkg1/sample_test.go b/t/fixtures/src/pkg1/sample_test.go new file mode 100644 index 0000000000..e39d7ba835 --- /dev/null +++ b/t/fixtures/src/pkg1/sample_test.go @@ -0,0 +1,7 @@ +package pkg1 + +import "testing" + +func TestSample(t *testing.T) { + Sample() +}