"
" Python filetype plugin for marking code coverage.
" Language: Vim (ft=python)
" Maintainer: Peter Sagerson <psagers at ignorare dot net>
" Version: 0.1.1
" URL: https://bitbucket.org/psagers/vim-py-coverage
"
if exists("b:loaded_py_coverage_ftplugin")
finish
endif
let b:loaded_py_coverage_ftplugin=1
let g:py_coverage_bin = ! exists('g:py_coverage_bin') ? 'coverage' : g:py_coverage_bin
highlight default link PyCoverageMissed Error
"
" Populate the quickfix list with the missed line numbers.
"
function! s:PyCoverageSetQuickfix()
let newlist = []
for line in PyCoverageMissedLines('')
call add(newlist, {'bufnr': bufnr(''), 'lnum': line, 'text': 'Line not covered'})
endfor
call setqflist(newlist)
endfunction
command! PyCoverageSetQuickfix :call s:PyCoverageSetQuickfix()
"
" Populate the current window's location list with the missed line numbers.
"
function! s:PyCoverageSetLoclist()
let newlist = []
for line in PyCoverageMissedLines('')
call add(newlist, {'bufnr': bufnr(''), 'lnum': line, 'text': 'Line not covered'})
endfor
call setloclist(winnr(), newlist)
endfunction
command! PyCoverageSetLoclist :call s:PyCoverageSetLoclist()
"
" Highlight the missed line numbers.
"
function! s:PyCoverageHighlight()
call s:PyCoverageClear()
for line in PyCoverageMissedLines('')
call matchadd('PyCoverageMissed', '\%'.line.'l')
endfor
endfunction
command! PyCoverageHighlight :call s:PyCoverageHighlight()
"
" Clear highlighting.
"
function! s:PyCoverageClear()
for m in getmatches()
if m.group == 'PyCoverageMissed'
call matchdelete(m.id)
endif
endfor
endfunction
command! PyCoverageClear :call s:PyCoverageClear()
" Returns an array of line numbers representing all of the lines missed by the
" last coverage run in a given source file.
function! PyCoverageMissedLines(buffer)
let linenos = []
let sourcefile = fnamemodify(bufname(a:buffer), ':p')
let report = s:CoverageReport(sourcefile)
let lines = split(report, '[\r\n]\+')
if len(lines) == 3
let headers = lines[0]
let fields = lines[2]
else
return []
endif
let offset = match(headers, 'Missing$')
if offset >= 0
let missing = fields[offset :]
else
return []
endif
for range in split(missing, ', ')
let bounds = map(split(range, '-'), 'str2nr(v:val)')
if len(bounds) == 1
call extend(linenos, bounds)
elseif len(bounds) == 2
let lineno = bounds[0]
while lineno <= bounds[1]
call add(linenos, lineno)
let lineno += 1
endwhile
endif
endfor
return linenos
endfunction
" Find the nearest .coverage file and generate a report for the given target
" source file. The report looks like:
"
" Name Stmts Miss Cover Missing
" --------------------------------------------------
" python/module/path 169 30 82% 26-28, 64, 73-74, ...
function! s:CoverageReport(sourcefile)
let coverage_dir = ''
let report = ''
if exists('g:py_coverage_dir')
let coverage_dir = g:py_coverage_dir
elseif filereadable(a:sourcefile)
let suffixesadd_save = &suffixesadd
let &suffixesadd = ''
let coverage_db = findfile('.coverage', fnamemodify(a:sourcefile, ':h') . ';')
let &suffixesadd = suffixesadd_save
if coverage_db != ''
let coverage_dir = fnamemodify(coverage_db, ':h')
endif
endif
if coverage_dir != ''
exec printf('cd! %s', fnameescape(coverage_dir))
let report = system(printf('%s report -m --include=%s', shellescape(g:py_coverage_bin), shellescape(a:sourcefile)))
if v:shell_error != 0
echo report
let report = ''
endif
cd! -
endif
return report
endfunction