function! s:enhance_syntax() abort syntax case match syntax keyword healthError ERROR \ containedin=markdownCodeBlock,mkdListItemLine highlight link healthError Error syntax keyword healthWarning WARNING \ containedin=markdownCodeBlock,mkdListItemLine highlight link healthWarning WarningMsg syntax keyword healthSuccess SUCCESS \ containedin=markdownCodeBlock,mkdListItemLine highlight healthSuccess guibg=#5fff00 guifg=#080808 ctermbg=82 ctermfg=232 syntax match healthHelp "|.\{-}|" contains=healthBar \ containedin=markdownCodeBlock,mkdListItemLine syntax match healthBar "|" contained conceal highlight link healthHelp Identifier " We do not care about markdown syntax errors in :CheckHealth output. highlight! link markdownError Normal endfunction " Runs the specified healthchecks. " Runs all discovered healthchecks if a:plugin_names is empty. function! health#check(plugin_names) abort let healthchecks = empty(a:plugin_names) \ ? s:discover_health_checks() \ : s:to_fn_names(a:plugin_names) tabnew setlocal wrap breakindent setlocal filetype=markdown setlocal conceallevel=2 concealcursor=nc setlocal keywordprg=:help iskeyword=@,48-57,_,192-255,-,# call s:enhance_syntax() if empty(healthchecks) call setline(1, 'ERROR: No healthchecks found.') else redraw|echo 'Running healthchecks...' for c in healthchecks let output = '' call append('$', split(printf("\n%s\n%s", c, repeat('=',72)), "\n")) try let output = "\n\n".execute('call '.c.'()') catch if v:exception =~# '^Vim\%((\a\+)\)\=:E117.*\V'.c let output = execute( \ 'call health#report_error(''No healthcheck found for "' \ .s:to_plugin_name(c) \ .'" plugin.'')') else let output = execute( \ 'call health#report_error(''Failed to run healthcheck for "' \ .s:to_plugin_name(c) \ .'" plugin. Exception:''."\n".v:throwpoint."\n".v:exception)') endif endtry call append('$', split(output, "\n") + ['']) redraw endfor endif " needed for plasticboy/vim-markdown, because it uses fdm=expr normal! zR setlocal nomodified redraw|echo '' endfunction " Starts a new report. function! health#report_start(name) abort echo "\n## " . a:name endfunction " Indents lines *except* line 1 of a string if it contains newlines. function! s:indent_after_line1(s, columns) abort let lines = split(a:s, "\n", 0) if len(lines) < 2 " We do not indent line 1, so nothing to do. return a:s endif for i in range(1, len(lines)-1) " Indent lines after the first. let lines[i] = substitute(lines[i], '^\s*', repeat(' ', a:columns), 'g') endfor return join(lines, "\n") endfunction " Changes ':h clipboard' to ':help |clipboard|'. function! s:help_to_link(s) abort return substitute(a:s, '\v:h%[elp] ([^|][^"\r\n]+)', ':help |\1|', 'g') endfunction " Format a message for a specific report item function! s:format_report_message(status, msg, ...) abort " {{{ let output = ' - ' . a:status . ': ' . s:indent_after_line1(a:msg, 4) let suggestions = [] " Optional parameters if a:0 > 0 let suggestions = type(a:1) == type("") ? [a:1] : a:1 if type(suggestions) != type([]) echoerr "Expected String or List" endif endif " Report each suggestion if len(suggestions) > 0 let output .= "\n - SUGGESTIONS:" endif for suggestion in suggestions let output .= "\n - " . s:indent_after_line1(suggestion, 10) endfor return s:help_to_link(output) endfunction " }}} " Use {msg} to report information in the current section function! health#report_info(msg) abort " {{{ echo s:format_report_message('INFO', a:msg) endfunction " }}} " Reports a successful healthcheck. function! health#report_ok(msg) abort " {{{ echo s:format_report_message('SUCCESS', a:msg) endfunction " }}} " Reports a health warning. function! health#report_warn(msg, ...) abort " {{{ if a:0 > 0 echo s:format_report_message('WARNING', a:msg, a:1) else echo s:format_report_message('WARNING', a:msg) endif endfunction " }}} " Reports a failed healthcheck. function! health#report_error(msg, ...) abort " {{{ if a:0 > 0 echo s:format_report_message('ERROR', a:msg, a:1) else echo s:format_report_message('ERROR', a:msg) endif endfunction " }}} function! s:filepath_to_function(name) abort return substitute(substitute(substitute(a:name, '.*autoload[\/]', '', ''), \ '\.vim', '#check', ''), '[\/]', '#', 'g') endfunction function! s:discover_health_checks() abort let healthchecks = globpath(&runtimepath, 'autoload/health/*.vim', 1, 1) let healthchecks = map(healthchecks, 'filepath_to_function(v:val)') return healthchecks endfunction " Translates a list of plugin names to healthcheck function names. function! s:to_fn_names(plugin_names) abort let healthchecks = [] for p in a:plugin_names call add(healthchecks, 'health#'.p.'#check') endfor return healthchecks endfunction " Extracts 'foo' from 'health#foo#check'. function! s:to_plugin_name(fn_name) abort return substitute(a:fn_name, \ '\v.*health\#(.+)\#check.*', '\1', '') endfunction