diff options
author | Matthieu Coudron <teto@users.noreply.github.com> | 2021-10-05 11:27:07 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-05 11:27:07 +0200 |
commit | 93a7571faba529f7b3e527336e933731eb91bbec (patch) | |
tree | 804a61b2ff65eaef7c06a7390768ece5e890142f /runtime | |
parent | 655e489e90712cc3c2f06d84a5030c66e5c51d91 (diff) | |
parent | 8b43b07333bc8ae9ca347daffb09f2bdf50af3ed (diff) | |
download | rneovim-93a7571faba529f7b3e527336e933731eb91bbec.tar.gz rneovim-93a7571faba529f7b3e527336e933731eb91bbec.tar.bz2 rneovim-93a7571faba529f7b3e527336e933731eb91bbec.zip |
Merge pull request #15259 from muniter/muniter/checkhealth-from-lua
feat(checkhealth): support Lua healthchecks
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/autoload/health.vim | 103 | ||||
-rw-r--r-- | runtime/doc/pi_health.txt | 175 | ||||
-rw-r--r-- | runtime/lua/health.lua | 23 |
3 files changed, 208 insertions, 93 deletions
diff --git a/runtime/autoload/health.vim b/runtime/autoload/health.vim index 0f7983f175..17047707fb 100644 --- a/runtime/autoload/health.vim +++ b/runtime/autoload/health.vim @@ -26,8 +26,8 @@ endfunction " 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) + \ ? s:discover_healthchecks() + \ : s:get_healthcheck(a:plugin_names) tabnew setlocal wrap breakindent linebreak @@ -42,24 +42,28 @@ function! health#check(plugin_names) abort else redraw|echo 'Running healthchecks...' for c in healthchecks - let output = '' - call append('$', split(printf("\n%s\n%s", c, repeat('=',72)), "\n")) + let [name, func, type] = c + let s:output = [] try - let output = "\n\n".execute('call '.c.'()') + if func == '' + throw 'healthcheck_not_found' + endif + eval type == 'v' ? call(func, []) : luaeval(func) 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.'')') + let s:output = [] " Clear the output + if v:exception =~# 'healthcheck_not_found' + call health#report_error('No healthcheck found for "'.name.'" 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)') + call health#report_error(printf( + \ "Failed to run healthcheck for \"%s\" plugin. Exception:\n%s\n%s", + \ name, v:throwpoint, v:exception)) endif endtry - call append('$', split(output, "\n") + ['']) + let header = [name. ': ' . func, repeat('=', 72)] + " remove empty line after header from report_start + let s:output = s:output[0] == '' ? s:output[1:] : s:output + let s:output = header + s:output + [''] + call append('$', s:output) redraw endfor endif @@ -71,9 +75,13 @@ function! health#check(plugin_names) abort redraw|echo '' endfunction +function! s:collect_output(output) + let s:output += split(a:output, "\n", 1) +endfunction + " Starts a new report. function! health#report_start(name) abort - echo "\n## " . a:name + call s:collect_output("\n## " . a:name) endfunction " Indents lines *except* line 1 of a string if it contains newlines. @@ -119,21 +127,21 @@ endfunction " }}} " Use {msg} to report information in the current section function! health#report_info(msg) abort " {{{ - echo s:format_report_message('INFO', a:msg) + call s:collect_output(s:format_report_message('INFO', a:msg)) endfunction " }}} " Reports a successful healthcheck. function! health#report_ok(msg) abort " {{{ - echo s:format_report_message('OK', a:msg) + call s:collect_output(s:format_report_message('OK', a:msg)) endfunction " }}} " Reports a health warning. " a:1: Optional advice (string or list) function! health#report_warn(msg, ...) abort " {{{ if a:0 > 0 - echo s:format_report_message('WARNING', a:msg, a:1) + call s:collect_output(s:format_report_message('WARNING', a:msg, a:1)) else - echo s:format_report_message('WARNING', a:msg) + call s:collect_output(s:format_report_message('WARNING', a:msg)) endif endfunction " }}} @@ -141,37 +149,52 @@ endfunction " }}} " a:1: Optional advice (string or list) function! health#report_error(msg, ...) abort " {{{ if a:0 > 0 - echo s:format_report_message('ERROR', a:msg, a:1) + call s:collect_output(s:format_report_message('ERROR', a:msg, a:1)) else - echo s:format_report_message('ERROR', a:msg) + call s:collect_output(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') +" From a path return a list [{name}, {func}, {type}] representing a healthcheck +function! s:filepath_to_healthcheck(path) abort + if a:path =~# 'vim$' + let name = matchstr(a:path, '\zs[^\/]*\ze\.vim$') + let func = 'health#'.name.'#check' + let type = 'v' + else + let base_path = substitute(a:path, + \ '.*lua[\/]\(.\{-}\)[\/]health\([\/]init\)\?\.lua$', + \ '\1', '') + let name = substitute(base_path, '[\/]', '.', 'g') + let func = 'require("'.name.'.health").check()' + let type = 'l' + endif + return [name, func, type] endfunction -function! s:discover_health_checks() abort - let healthchecks = globpath(&runtimepath, 'autoload/health/*.vim', 1, 1) - let healthchecks = map(healthchecks, '<SID>filepath_to_function(v:val)') - return healthchecks +function! s:discover_healthchecks() abort + return s:get_healthcheck('*') endfunction -" Translates a list of plugin names to healthcheck function names. -function! s:to_fn_names(plugin_names) abort +" Returns list of lists [ [{name}, {func}, {type}] ] representing healthchecks +function! s:get_healthcheck(plugin_names) abort let healthchecks = [] - let plugin_names = type('') ==# type(a:plugin_names) - \ ? split(a:plugin_names, '', v:false) + let plugin_names = type('') == type(a:plugin_names) + \ ? split(a:plugin_names, ' ', v:false) \ : a:plugin_names for p in plugin_names - call add(healthchecks, 'health#'.p.'#check') + " support vim/lsp/health{/init/}.lua as :checkhealth vim.lsp + let p = substitute(p, '\.', '/', 'g') + let p = substitute(p, '*$', '**', 'g') " find all submodule e.g vim* + let paths = nvim_get_runtime_file('autoload/health/'.p.'.vim', v:true) + \ + nvim_get_runtime_file('lua/**/'.p.'/health/init.lua', v:true) + \ + nvim_get_runtime_file('lua/**/'.p.'/health.lua', v:true) + if len(paths) == 0 + let healthchecks += [[p, '', '']] " healthchek not found + else + let healthchecks += map(uniq(sort(paths)), + \'<SID>filepath_to_healthcheck(v:val)') + end 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 diff --git a/runtime/doc/pi_health.txt b/runtime/doc/pi_health.txt index bb688770fc..69655ba902 100644 --- a/runtime/doc/pi_health.txt +++ b/runtime/doc/pi_health.txt @@ -13,7 +13,7 @@ python support, ruby support, clipboard support, and more. To run the healthchecks, use this command: > - :checkhealth + :checkhealth < Plugin authors are encouraged to write new healthchecks. |health-dev| @@ -23,32 +23,107 @@ Commands *health-commands* *:checkhealth* *:CheckHealth* :checkhealth Run all healthchecks. *E5009* - Nvim depends on |$VIMRUNTIME| and 'runtimepath' to find - the standard "runtime files" for syntax highlighting, - filetype-specific behavior, and standard plugins - (including :checkhealth). If the runtime files cannot - be found then those features will not work. + Nvim depends on |$VIMRUNTIME|, 'runtimepath' and 'packpath' to + find the standard "runtime files" for syntax highlighting, + filetype-specific behavior, and standard plugins (including + :checkhealth). If the runtime files cannot be found then + those features will not work. :checkhealth {plugins} - Run healthcheck(s) for one or more plugins. E.g. to run - only the standard Nvim healthcheck: > - :checkhealth nvim -< To run the healthchecks for the "foo" and "bar" plugins - (assuming these plugins are on your 'runtimepath' and - they have implemented health#foo#check() and - health#bar#check(), respectively): > - :checkhealth foo bar + Run healthcheck(s) for one or more plugins. E.g. to run only + the standard Nvim healthcheck: > + :checkhealth nvim < + To run the healthchecks for the "foo" and "bar" plugins + (assuming these plugins are on 'runtimepath' or 'packpath' and + they have implemented the Lua or Vimscript interface + require("foo.health").check() and health#bar#check(), + respectively): > + :checkhealth foo bar +< + To run healthchecks for lua submodules, use dot notation or + "*" to refer to all submodules. For example nvim provides + `vim.lsp` and `vim.treesitter` > + :checkhealth vim.lsp vim.treesitter + :checkhealth vim* +< +============================================================================== +Lua Functions *health-functions-lua* *health-lua* + +The Lua "health" module can be used to create new healthchecks (see also +|health-functions-vim|). To get started, simply use: > + local health = require('health') +< +health.report_start({name}) *health.report_start()* + Starts a new report. Most plugins should call this only once, but if + you want different sections to appear in your report, call this once + per section. + +health.report_info({msg}) *health.report_info()* + Reports an informational message. + +health.report_ok({msg}) *health.report_ok()* + Reports a "success" message. + +health.report_warn({msg} [, {advice}]) *health.report_warn()* + Reports a warning. {advice} is an optional List of suggestions. + +health.report_error({msg} [, {advice}]) *health.report_error()* + Reports an error. {advice} is an optional List of suggestions. + +============================================================================== +Create a Lua healthcheck *health-dev-lua* + +Healthchecks are functions that check the user environment, configuration, +etc. Nvim has built-in healthchecks in $VIMRUNTIME/autoload/health/. + +To add a new healthcheck for your own plugin, simply define a Lua module in +your plugin that returns a table with a "check()" function. |:checkhealth| +will automatically find and invoke this function. + +If your plugin is named "foo", then its healthcheck module should be a file in +one of these locations on 'runtimepath' or 'packpath': + - `lua/foo/health/init.lua` + - `lua/foo/health.lua` + +If your plugin provides a submodule named "bar" for which you want a separate +healthcheck, define the healthcheck at one of these locations on 'runtimepath' +or 'packpath': + - `lua/foo/bar/health/init.lua` + - `lua/foo/bar/health.lua` + +All submodules should return a Lua table containing the method `check()`. + +Copy this sample code into `lua/foo/health/init.lua` or `lua/foo/health.lua`, +replacing "foo" in the path with your plugin name: > + + local M = {} + local health = require("health") + + M.check = function() + health.report_start("my_plugin report") + -- make sure setup function parameters are ok + if check_setup() then + health.report_ok("Setup function is correct") + else + health.report_error("Setup function is incorrect") + end + -- do some more checking + -- ... + end + + return M + ============================================================================== -Functions *health-functions* +Vimscript Functions *health-functions-vimscript* *health-vimscript* -health.vim functions are for creating new healthchecks. They mostly just do -some layout and formatting, to give users a consistent presentation. +health.vim functions are for creating new healthchecks. (See also +|health-functions-lua|) health#report_start({name}) *health#report_start* Starts a new report. Most plugins should call this only once, but if - you want different sections to appear in your report, call this once - per section. + you want different sections to appear in your report, call this once + per section. health#report_info({msg}) *health#report_info* Reports an informational message. @@ -56,27 +131,23 @@ health#report_info({msg}) *health#report_info* health#report_ok({msg}) *health#report_ok* Reports a "success" message. -health#report_warn({msg}, [{advice}]) *health#report_warn* - Reports a warning. {advice} is an optional List of suggestions. +health#report_warn({msg} [, {advice}]) *health#report_warn* + Reports a warning. {advice} is an optional List of suggestions. -health#report_error({msg}, [{advice}]) *health#report_error* - Reports an error. {advice} is an optional List of suggestions. +health#report_error({msg} [, {advice}]) *health#report_error* + Reports an error. {advice} is an optional List of suggestions. health#{plugin}#check() *health.user_checker* - Healthcheck function for {plugin}. Called by |:checkhealth| - automatically. Example: > + Healthcheck function for {plugin}. Called by |:checkhealth| + automatically. Example: > - function! health#my_plug#check() abort - silent call s:check_environment_vars() - silent call s:check_python_configuration() - endfunction + function! health#my_plug#check() abort + silent call s:check_environment_vars() + silent call s:check_python_configuration() + endfunction < - All output will be captured from the healthcheck. Use the - health#report_* functions so that your healthcheck has a format - consistent with the standard healthchecks. - ============================================================================== -Create a healthcheck *health-dev* +Create a healthcheck *health-dev-vim* Healthchecks are functions that check the user environment, configuration, etc. Nvim has built-in healthchecks in $VIMRUNTIME/autoload/health/. @@ -86,26 +157,24 @@ health#{plugin}#check() function in autoload/health/{plugin}.vim. |:checkhealth| automatically finds and invokes such functions. If your plugin is named "foo", then its healthcheck function must be > - health#foo#check() + health#foo#check() -defined in this file on 'runtimepath': > - autoload/health/foo.vim +defined in this file on 'runtimepath' or 'packpath': > + autoload/health/foo.vim Copy this sample code into autoload/health/foo.vim and replace "foo" with your plugin name: > - function! health#foo#check() abort - call health#report_start('sanity checks') - " perform arbitrary checks - " ... - - if looks_good - call health#report_ok('found required dependencies') - else - call health#report_error('cannot find foo', - \ ['npm install --save foo']) - endif - endfunction - -============================================================================== - -vim:tw=78:ts=8:ft=help:fdm=marker + function! health#foo#check() abort + call health#report_start('sanity checks') + " perform arbitrary checks + " ... + + if looks_good + call health#report_ok('found required dependencies') + else + call health#report_error('cannot find foo', + \ ['npm install --save foo']) + endif + endfunction + +vim:noet tw=78:ts=8:ft=help:fdm=marker diff --git a/runtime/lua/health.lua b/runtime/lua/health.lua new file mode 100644 index 0000000000..142a353bf2 --- /dev/null +++ b/runtime/lua/health.lua @@ -0,0 +1,23 @@ +local M = {} + +function M.report_start(msg) + vim.fn['health#report_start'](msg) +end + +function M.report_info(msg) + vim.fn['health#report_info'](msg) +end + +function M.report_ok(msg) + vim.fn['health#report_ok'](msg) +end + +function M.report_warn(msg, ...) + vim.fn['health#report_warn'](msg, ...) +end + +function M.report_error(msg, ...) + vim.fn['health#report_error'](msg, ...) +end + +return M |