diff options
author | dundargoc <33953936+dundargoc@users.noreply.github.com> | 2023-04-15 23:40:48 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-15 23:40:48 +0200 |
commit | c08b03076167837cff9eb66c19440d727e6dad31 (patch) | |
tree | bd99b63144c76361c5cec45715397d15bfdc935f | |
parent | c8667c8756a211b8597e2e0a80200498b752915d (diff) | |
download | rneovim-c08b03076167837cff9eb66c19440d727e6dad31.tar.gz rneovim-c08b03076167837cff9eb66c19440d727e6dad31.tar.bz2 rneovim-c08b03076167837cff9eb66c19440d727e6dad31.zip |
refactor: deprecate checkhealth functions
The following functions are deprecated and will be removed in
Nvim v0.11:
- health#report_start()
- health#report_info()
- health#report_ok()
- health#report_warn()
- health#report_error()
- vim.health.report_start()
- vim.health.report_info()
- vim.health.report_ok()
- vim.health.report_warn()
- vim.health.report_error()
Users should instead use these:
- vim.health.start()
- vim.health.info()
- vim.health.ok()
- vim.health.warn()
- vim.health.error()
22 files changed, 411 insertions, 435 deletions
diff --git a/runtime/autoload/health.vim b/runtime/autoload/health.vim index 5fd4627b11..1c414377f6 100644 --- a/runtime/autoload/health.vim +++ b/runtime/autoload/health.vim @@ -1,207 +1,38 @@ -" 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_healthchecks() - \ : s:get_healthcheck(a:plugin_names) - - " Create buffer and open in a tab, unless this is the default buffer when Nvim starts. - let emptybuf = (bufnr('$') == 1 && empty(getline(1)) && 1 == line('$')) - execute (emptybuf ? 'buffer' : 'tab sbuffer') nvim_create_buf(v:true, v:true) - if bufexists('health://') - bwipe health:// - endif - file health:// - setfiletype checkhealth - - if empty(healthchecks) - call setline(1, 'ERROR: No healthchecks found.') - else - redraw|echo 'Running healthchecks...' - for name in sort(keys(healthchecks)) - let [func, type] = healthchecks[name] - let s:output = [] - try - if func == '' - throw 'healthcheck_not_found' - endif - eval type == 'v' ? call(func, []) : luaeval(func) - " in the event the healthcheck doesn't return anything - " (the plugin author should avoid this possibility) - if len(s:output) == 0 - throw 'healthcheck_no_return_value' - endif - catch - let s:output = [] " Clear the output - if v:exception =~# 'healthcheck_not_found' - call health#report_error('No healthcheck found for "'.name.'" plugin.') - elseif v:exception =~# 'healthcheck_no_return_value' - call health#report_error('The healthcheck report for "'.name.'" plugin is empty.') - else - call health#report_error(printf( - \ "Failed to run healthcheck for \"%s\" plugin. Exception:\n%s\n%s", - \ name, v:throwpoint, v:exception)) - endif - endtry - let header = [repeat('=', 78), name .. ': ' .. func, ''] - " 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 - - " Clear the 'Running healthchecks...' message. - redraw|echo '' +function! s:deprecate(type) abort + let deprecate = v:lua.vim.deprecate('health#report_' . a:type, 'vim.health.' . a:type, '0.11') + redraw | echo 'Running healthchecks...' + call v:lua.vim.health.warn(deprecate) 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 - call s:collect_output(printf("\n%s ~", a:name)) + call v:lua.vim.health.start(a:name) + call s:deprecate('start') 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") +function! health#report_info(msg) abort + call v:lua.vim.health.info(a:msg) + call s:deprecate('info') 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') +function! health#report_ok(msg) abort + call v:lua.vim.health.ok(a:msg) + call s:deprecate('ok') endfunction -" Format a message for a specific report item. -" a:1: Optional advice (string or list) -function! s:format_report_message(status, msg, ...) abort " {{{ - let output = '- ' .. a:status .. (empty(a:status) ? '' : ' ') .. s:indent_after_line1(a:msg, 2) - - " Optional parameters - if a:0 > 0 - let advice = type(a:1) == type('') ? [a:1] : a:1 - if type(advice) != type([]) - throw 'a:1: expected String or List' - endif - - " Report each suggestion - if !empty(advice) - let output .= "\n - ADVICE:" - for suggestion in advice - let output .= "\n - " . s:indent_after_line1(suggestion, 6) - endfor - endif - endif - - return s:help_to_link(output) -endfunction " }}} - -" Reports a message as a listitem in the current section. -function! health#report_info(msg) abort " {{{ - call s:collect_output(s:format_report_message('', a:msg)) -endfunction " }}} - -" Reports a successful healthcheck. -function! health#report_ok(msg) abort " {{{ - 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 " {{{ +function! health#report_warn(msg, ...) abort if a:0 > 0 - call s:collect_output(s:format_report_message('WARNING', a:msg, a:1)) + call v:lua.vim.health.warn(a:msg, a:1) else - call s:collect_output(s:format_report_message('WARNING', a:msg)) + call v:lua.vim.health.warn(a:msg) endif -endfunction " }}} + call s:deprecate('warn') +endfunction -" Reports a failed healthcheck. -" a:1: Optional advice (string or list) -function! health#report_error(msg, ...) abort " {{{ +function! health#report_error(msg, ...) abort if a:0 > 0 - call s:collect_output(s:format_report_message('ERROR', a:msg, a:1)) + call v:lua.vim.health.error(a:msg, a:1) else - call s:collect_output(s:format_report_message('ERROR', a:msg)) + call v:lua.vim.health.error(a:msg) endif -endfunction " }}} - -" 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_healthchecks() abort - return s:get_healthcheck('*') -endfunction - -" Returns Dictionary {name: [func, type], ..} representing healthchecks -function! s:get_healthcheck(plugin_names) abort - let health_list = s:get_healthcheck_list(a:plugin_names) - let healthchecks = {} - for c in health_list - let normalized_name = substitute(c[0], '-', '_', 'g') - let existent = get(healthchecks, normalized_name, []) - " Prefer Lua over vim entries - if existent != [] && existent[2] == 'l' - continue - else - let healthchecks[normalized_name] = c - endif - endfor - let output = {} - for v in values(healthchecks) - let output[v[0]] = v[1:] - endfor - try - " vim.health is not a healthcheck, skip it - call remove(output, 'vim') - catch - endtry - return output -endfunction - -" Returns list of lists [ [{name}, {func}, {type}] ] representing healthchecks -function! s:get_healthcheck_list(plugin_names) abort - let healthchecks = [] - let plugin_names = type('') == type(a:plugin_names) - \ ? split(a:plugin_names, ' ', v:false) - \ : a:plugin_names - for p in plugin_names - " 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, '', '']] " healthcheck not found - else - let healthchecks += map(uniq(sort(paths)), - \'<SID>filepath_to_healthcheck(v:val)') - end - endfor - return healthchecks + call s:deprecate('error') endfunction diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index 171d285950..3150190a8b 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -54,11 +54,11 @@ FUNCTIONS - *buffer_name()* Obsolete name for |bufname()|. - *buffer_number()* Obsolete name for |bufnr()|. - *file_readable()* Obsolete name for |filereadable()|. -- *health#report_error* Use Lua |vim.health.report_error()| instead. -- *health#report_info* Use Lua |vim.health.report_info()| instead. -- *health#report_ok* Use Lua |vim.health.report_ok()| instead. -- *health#report_start* Use Lua |vim.health.report_start()| instead. -- *health#report_warn* Use Lua |vim.health.report_warn()| instead. +- *health#report_error* *vim.health.report_error()* Use |vim.health.error()| instead. +- *health#report_info* *vim.health.report_info()* Use |vim.health.info()| instead. +- *health#report_ok* *vim.health.report_ok()* Use |vim.health.ok()| instead. +- *health#report_start* *vim.health.report_start()* Use |vim.health.start()| instead. +- *health#report_warn* *vim.health.report_warn()* Use |vim.health.warn()| instead. - *highlight_exists()* Obsolete name for |hlexists()|. - *highlightID()* Obsolete name for |hlID()|. - *inputdialog()* Use |input()| instead. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 6697b3018a..f196c3d445 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -62,7 +62,11 @@ DEPRECATIONS *news-deprecations* The following functions are now deprecated and will be removed in the next release. -• ... - +• Checkhealth functions: + - |health#report_error|, |vim.health.report_error()| Use Lua |vim.health.error()| instead. + - |health#report_info|, |vim.health.report_info()| Use Lua |vim.health.info()| instead. + - |health#report_ok|, |vim.health.report_ok()| Use Lua |vim.health.ok()| instead. + - |health#report_start|, |vim.health.report_start()| Use Lua |vim.health.start()| instead. + - |health#report_warn|, |vim.health.report_warn()| Use Lua |vim.health.warn()| instead. vim:tw=78:ts=8:sw=2:et:ft=help:norl: diff --git a/runtime/doc/pi_health.txt b/runtime/doc/pi_health.txt index a0e06a7d37..266c021ecc 100644 --- a/runtime/doc/pi_health.txt +++ b/runtime/doc/pi_health.txt @@ -52,22 +52,22 @@ Functions *health-functions* *vim.health* The Lua "health" module can be used to create new healthchecks. To get started see |health-dev|. -vim.health.report_start({name}) *vim.health.report_start()* +vim.health.start({name}) *vim.health.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. -vim.health.report_info({msg}) *vim.health.report_info()* +vim.health.info({msg}) *vim.health.info()* Reports an informational message. -vim.health.report_ok({msg}) *vim.health.report_ok()* +vim.health.ok({msg}) *vim.health.ok()* Reports a "success" message. -vim.health.report_warn({msg} [, {advice}]) *vim.health.report_warn()* +vim.health.warn({msg} [, {advice}]) *vim.health.warn()* Reports a warning. {advice} is an optional list of suggestions to present to the user. -vim.health.report_error({msg} [, {advice}]) *vim.health.report_error()* +vim.health.error({msg} [, {advice}]) *vim.health.error()* Reports an error. {advice} is an optional list of suggestions to present to the user. @@ -105,12 +105,12 @@ with your plugin name: > local M = {} M.check = function() - vim.health.report_start("foo report") + vim.health.start("foo report") -- make sure setup function parameters are ok if check_setup() then - vim.health.report_ok("Setup is correct") + vim.health.ok("Setup is correct") else - vim.health.report_error("Setup is incorrect") + vim.health.error("Setup is incorrect") end -- do some more checking -- ... diff --git a/runtime/lua/nvim/health.lua b/runtime/lua/nvim/health.lua index 6f544e9407..19303d34f9 100644 --- a/runtime/lua/nvim/health.lua +++ b/runtime/lua/nvim/health.lua @@ -20,7 +20,7 @@ end local suggest_faq = 'https://github.com/neovim/neovim/wiki/Building-Neovim#optimized-builds' local function check_runtime() - health.report_start('Runtime') + health.start('Runtime') -- Files from an old installation. local bad_files = { ['plugin/man.vim'] = false, @@ -37,10 +37,10 @@ local function check_runtime() end local ok = (bad_files_msg == '') - local info = ok and health.report_ok or health.report_info + local info = ok and health.ok or health.info info(string.format('$VIMRUNTIME: %s', vim.env.VIMRUNTIME)) if not ok then - health.report_error( + health.error( string.format( '$VIMRUNTIME has files from an old installation (this can cause weird behavior):\n%s', bad_files_msg @@ -51,7 +51,7 @@ local function check_runtime() end local function check_config() - health.report_start('Configuration') + health.start('Configuration') local ok = true local vimrc = ( @@ -60,7 +60,7 @@ local function check_config() if not filereadable(vimrc) then ok = false local has_vim = filereadable(vim.fn.expand('~/.vimrc')) - health.report_warn( + health.warn( (-1 == vim.fn.getfsize(vimrc) and 'Missing' or 'Unreadable') .. ' user config file: ' .. vimrc, { has_vim and ':help nvim-from-vim' or ':help init.vim' } ) @@ -69,12 +69,12 @@ local function check_config() -- If $VIM is empty we don't care. Else make sure it is valid. if not empty(vim.env.VIM) and not filereadable(vim.env.VIM .. '/runtime/doc/nvim.txt') then ok = false - health.report_error('$VIM is invalid: ' .. vim.env.VIM) + health.error('$VIM is invalid: ' .. vim.env.VIM) end if vim.env.NVIM_TUI_ENABLE_CURSOR_SHAPE then ok = false - health.report_warn('$NVIM_TUI_ENABLE_CURSOR_SHAPE is ignored in Nvim 0.2+', { + health.warn('$NVIM_TUI_ENABLE_CURSOR_SHAPE is ignored in Nvim 0.2+', { "Use the 'guicursor' option to configure cursor shape. :help 'guicursor'", 'https://github.com/neovim/neovim/wiki/Following-HEAD#20170402', }) @@ -82,7 +82,7 @@ local function check_config() if vim.v.ctype == 'C' then ok = false - health.report_error( + health.error( 'Locale does not support UTF-8. Unicode characters may not display correctly.' .. ('\n$LANG=%s $LC_ALL=%s $LC_CTYPE=%s'):format( vim.env.LANG, @@ -99,7 +99,7 @@ local function check_config() if vim.o.paste == 1 then ok = false - health.report_error( + health.error( "'paste' is enabled. This option is only for pasting text.\nIt should not be set in your config.", { 'Remove `set paste` from your init.vim, if applicable.', @@ -132,7 +132,7 @@ local function check_config() or (not empty(shadafile) and (not filereadable(shadafile) or not filewritable(shadafile))) then ok = false - health.report_error( + health.error( 'shada file is not ' .. ((not writeable or filereadable(shadafile)) and 'writeable' or 'readable') .. ':\n' @@ -141,22 +141,22 @@ local function check_config() end if ok then - health.report_ok('no issues found') + health.ok('no issues found') end end local function check_performance() - health.report_start('Performance') + health.start('Performance') -- Check buildtype local buildtype = vim.fn.matchstr(vim.fn.execute('version'), [[\v\cbuild type:?\s*[^\n\r\t ]+]]) if empty(buildtype) then - health.report_error('failed to get build type from :version') + health.error('failed to get build type from :version') elseif vim.regex([[\v(MinSizeRel|Release|RelWithDebInfo)]]):match_str(buildtype) then - health.report_ok(buildtype) + health.ok(buildtype) else - health.report_info(buildtype) - health.report_warn('Non-optimized debug build. Nvim will be slower.', { + health.info(buildtype) + health.warn('Non-optimized debug build. Nvim will be slower.', { 'Install a different Nvim package, or rebuild with `CMAKE_BUILD_TYPE=RelWithDebInfo`.', suggest_faq, }) @@ -168,7 +168,7 @@ local function check_performance() vim.fn.system('echo') local elapsed_time = vim.fn.reltimefloat(vim.fn.reltime(start_time)) if elapsed_time > slow_cmd_time then - health.report_warn( + health.warn( 'Slow shell invocation (took ' .. vim.fn.printf('%.2f', elapsed_time) .. ' seconds).' ) end @@ -176,7 +176,7 @@ end -- Load the remote plugin manifest file and check for unregistered plugins local function check_rplugin_manifest() - health.report_start('Remote Plugins') + health.start('Remote Plugins') local existing_rplugins = {} for _, item in ipairs(vim.fn['remote#host#PluginsForHost']('python')) do @@ -218,7 +218,7 @@ local function check_rplugin_manifest() require_update = true end - health.report_warn(msg) + health.warn(msg) end break @@ -231,9 +231,9 @@ local function check_rplugin_manifest() end if require_update then - health.report_warn('Out of date', { 'Run `:UpdateRemotePlugins`' }) + health.warn('Out of date', { 'Run `:UpdateRemotePlugins`' }) else - health.report_ok('Up to date') + health.ok('Up to date') end end @@ -247,21 +247,21 @@ local function check_tmux() local out = vim.fn.system(vim.fn.split(cmd)) local val = vim.fn.substitute(out, [[\v(\s|\r|\n)]], '', 'g') if shell_error() then - health.report_error('command failed: ' .. cmd .. '\n' .. out) + health.error('command failed: ' .. cmd .. '\n' .. out) return 'error' elseif empty(val) then cmd = 'tmux show-option -qvgs ' .. option -- try session scope out = vim.fn.system(vim.fn.split(cmd)) val = vim.fn.substitute(out, [[\v(\s|\r|\n)]], '', 'g') if shell_error() then - health.report_error('command failed: ' .. cmd .. '\n' .. out) + health.error('command failed: ' .. cmd .. '\n' .. out) return 'error' end end return val end - health.report_start('tmux') + health.start('tmux') -- check escape-time local suggestions = @@ -269,14 +269,11 @@ local function check_tmux() local tmux_esc_time = get_tmux_option('escape-time') if tmux_esc_time ~= 'error' then if empty(tmux_esc_time) then - health.report_error('`escape-time` is not set', suggestions) + health.error('`escape-time` is not set', suggestions) elseif tonumber(tmux_esc_time) > 300 then - health.report_error( - '`escape-time` (' .. tmux_esc_time .. ') is higher than 300ms', - suggestions - ) + health.error('`escape-time` (' .. tmux_esc_time .. ') is higher than 300ms', suggestions) else - health.report_ok('escape-time: ' .. tmux_esc_time) + health.ok('escape-time: ' .. tmux_esc_time) end end @@ -284,17 +281,17 @@ local function check_tmux() local tmux_focus_events = get_tmux_option('focus-events') if tmux_focus_events ~= 'error' then if empty(tmux_focus_events) or tmux_focus_events ~= 'on' then - health.report_warn( + health.warn( "`focus-events` is not enabled. |'autoread'| may not work.", { '(tmux 1.9+ only) Set `focus-events` in ~/.tmux.conf:\nset-option -g focus-events on' } ) else - health.report_ok('focus-events: ' .. tmux_focus_events) + health.ok('focus-events: ' .. tmux_focus_events) end end -- check default-terminal and $TERM - health.report_info('$TERM: ' .. vim.env.TERM) + health.info('$TERM: ' .. vim.env.TERM) local cmd = 'tmux show-option -qvg default-terminal' local out = vim.fn.system(vim.fn.split(cmd)) local tmux_default_term = vim.fn.substitute(out, [[\v(\s|\r|\n)]], '', 'g') @@ -305,15 +302,15 @@ local function check_tmux() end if shell_error() then - health.report_error('command failed: ' .. cmd .. '\n' .. out) + health.error('command failed: ' .. cmd .. '\n' .. out) elseif tmux_default_term ~= vim.env.TERM then - health.report_info('default-terminal: ' .. tmux_default_term) - health.report_error( + health.info('default-terminal: ' .. tmux_default_term) + health.error( '$TERM differs from the tmux `default-terminal` setting. Colors might look wrong.', { '$TERM may have been set by some rc (.bashrc, .zshrc, ...).' } ) elseif not vim.regex([[\v(tmux-256color|screen-256color)]]):match_str(vim.env.TERM) then - health.report_error( + health.error( '$TERM should be "screen-256color" or "tmux-256color" in tmux. Colors might look wrong.', { 'Set default-terminal in ~/.tmux.conf:\nset-option -g default-terminal "screen-256color"', @@ -333,7 +330,7 @@ local function check_tmux() has_rgb = info:find(' Tc: (flag) true', 1, true) or info:find(' RGB: (flag) true', 1, true) end if not has_rgb then - health.report_warn( + health.warn( "Neither Tc nor RGB capability set. True colors are disabled. |'termguicolors'| won't work properly.", { "Put this in your ~/.tmux.conf and replace XXX by your $TERM outside of tmux:\nset-option -sa terminal-features ',XXX:RGB'", @@ -349,7 +346,7 @@ local function check_terminal() return end - health.report_start('terminal') + health.start('terminal') local cmd = 'infocmp -L' local out = vim.fn.system(vim.fn.split(cmd)) local kbs_entry = vim.fn.matchstr(out, 'key_backspace=[^,[:space:]]*') @@ -367,16 +364,16 @@ local function check_terminal() ) ) then - health.report_error('command failed: ' .. cmd .. '\n' .. out) + health.error('command failed: ' .. cmd .. '\n' .. out) else - health.report_info( + health.info( vim.fn.printf( 'key_backspace (kbs) terminfo entry: `%s`', (empty(kbs_entry) and '? (not found)' or kbs_entry) ) ) - health.report_info( + health.info( vim.fn.printf( 'key_dc (kdch1) terminfo entry: `%s`', (empty(kbs_entry) and '? (not found)' or kdch1_entry) @@ -392,7 +389,7 @@ local function check_terminal() 'SSH_TTY', }) do if vim.env[env_var] then - health.report_info(vim.fn.printf('$%s="%s"', env_var, vim.env[env_var])) + health.info(vim.fn.printf('$%s="%s"', env_var, vim.env[env_var])) end end end diff --git a/runtime/lua/provider/health.lua b/runtime/lua/provider/health.lua index de0729ab6b..1c882de222 100644 --- a/runtime/lua/provider/health.lua +++ b/runtime/lua/provider/health.lua @@ -1,10 +1,10 @@ local M = {} -local start = vim.health.report_start -local ok = vim.health.report_ok -local info = vim.health.report_info -local warn = vim.health.report_warn -local error = vim.health.report_error +local start = vim.health.start +local ok = vim.health.ok +local info = vim.health.info +local warn = vim.health.warn +local error = vim.health.error local iswin = vim.loop.os_uname().sysname == 'Windows_NT' local shell_error_code = 0 diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua index 044880e076..04f8f9695b 100644 --- a/runtime/lua/vim/health.lua +++ b/runtime/lua/vim/health.lua @@ -1,23 +1,196 @@ local M = {} -function M.report_start(msg) - vim.fn['health#report_start'](msg) +local s_output = {} + +-- From a path return a list [{name}, {func}, {type}] representing a healthcheck +local function filepath_to_healthcheck(path) + path = vim.fs.normalize(path) + local name + local func + local filetype + if path:find('vim$') then + name = vim.fs.basename(path):gsub('%.vim$', '') + func = 'health#' .. name .. '#check' + filetype = 'v' + else + local subpath = path:gsub('.*lua/', '') + if vim.fs.basename(subpath) == 'health.lua' then + -- */health.lua + name = vim.fs.dirname(subpath) + else + -- */health/init.lua + name = vim.fs.dirname(vim.fs.dirname(subpath)) + end + name = name:gsub('/', '.') + + func = 'require("' .. name .. '.health").check()' + filetype = 'l' + end + return { name, func, filetype } end -function M.report_info(msg) - vim.fn['health#report_info'](msg) +-- Returns { {name, func, type}, ... } representing healthchecks +local function get_healthcheck_list(plugin_names) + local healthchecks = {} + plugin_names = vim.split(plugin_names, ' ') + for _, p in pairs(plugin_names) do + -- support vim/lsp/health{/init/}.lua as :checkhealth vim.lsp + + p = p:gsub('%.', '/') + p = p:gsub('*', '**') + + local paths = vim.api.nvim_get_runtime_file('autoload/health/' .. p .. '.vim', true) + vim.list_extend( + paths, + vim.api.nvim_get_runtime_file('lua/**/' .. p .. '/health/init.lua', true) + ) + vim.list_extend(paths, vim.api.nvim_get_runtime_file('lua/**/' .. p .. '/health.lua', true)) + + if vim.tbl_count(paths) == 0 then + healthchecks[#healthchecks + 1] = { p, '', '' } -- healthcheck not found + else + local unique_paths = {} + for _, v in pairs(paths) do + unique_paths[v] = true + end + paths = {} + for k, _ in pairs(unique_paths) do + paths[#paths + 1] = k + end + + for _, v in ipairs(paths) do + healthchecks[#healthchecks + 1] = filepath_to_healthcheck(v) + end + end + end + return healthchecks end -function M.report_ok(msg) - vim.fn['health#report_ok'](msg) +-- Returns {name: [func, type], ..} representing healthchecks +local function get_healthcheck(plugin_names) + local health_list = get_healthcheck_list(plugin_names) + local healthchecks = {} + for _, c in pairs(health_list) do + if c[1] ~= 'vim' then + healthchecks[c[1]] = { c[2], c[3] } + end + end + + return healthchecks end -function M.report_warn(msg, ...) - vim.fn['health#report_warn'](msg, ...) +-- Indents lines *except* line 1 of a string if it contains newlines. +local function indent_after_line1(s, columns) + local lines = vim.split(s, '\n') + local indent = string.rep(' ', columns) + for i = 2, #lines do + lines[i] = indent .. lines[i] + end + return table.concat(lines, '\n') +end + +-- Changes ':h clipboard' to ':help |clipboard|'. +local function help_to_link(s) + return vim.fn.substitute(s, [[\v:h%[elp] ([^|][^"\r\n ]+)]], [[:help |\1|]], [[g]]) +end + +-- Format a message for a specific report item. +-- Variable args: Optional advice (string or list) +local function format_report_message(status, msg, ...) + local output = '- ' .. status + if status ~= '' then + output = output .. ' ' + end + + output = output .. indent_after_line1(msg, 2) + + local varargs = ... + + -- Optional parameters + if varargs then + if type(varargs) == 'string' then + varargs = { varargs } + end + + output = output .. '\n - ADVICE:' + + -- Report each suggestion + for _, v in ipairs(varargs) do + if v then + output = output .. '\n - ' .. indent_after_line1(v, 6) + end + end + end + + return help_to_link(output) +end + +local function collect_output(output) + vim.list_extend(s_output, vim.split(output, '\n')) +end + +-- Starts a new report. +function M.start(name) + local input = string.format('\n%s ~', name) + collect_output(input) +end + +-- Reports a message in the current section. +function M.info(msg) + local input = format_report_message('', msg) + collect_output(input) +end + +-- Reports a successful healthcheck. +function M.ok(msg) + local input = format_report_message('OK', msg) + collect_output(input) +end + +-- Reports a health warning. +-- ...: Optional advice (string or table) +function M.warn(msg, ...) + local input = format_report_message('WARNING', msg, ...) + collect_output(input) end +-- Reports a failed healthcheck. +-- ...: Optional advice (string or table) +function M.error(msg, ...) + local input = format_report_message('ERROR', msg, ...) + collect_output(input) +end + +local function deprecate(type) + local before = string.format('vim.health.report_%s()', type) + local after = string.format('vim.health.%s()', type) + local message = vim.deprecate(before, after, '0.11') + if message then + M.warn(message) + end + vim.cmd.redraw() + vim.print('Running healthchecks...') +end + +function M.report_start(name) + deprecate('start') + M.start(name) +end +function M.report_info(msg) + deprecate('info') + M.info(msg) +end +function M.report_ok(msg) + deprecate('ok') + M.ok(msg) +end +function M.report_warn(msg, ...) + deprecate('warn') + M.warn(msg, ...) +end function M.report_error(msg, ...) - vim.fn['health#report_error'](msg, ...) + deprecate('error') + M.error(msg, ...) end local path2name = function(path) @@ -59,4 +232,77 @@ M._complete = function() return vim.tbl_keys(unique) end +-- Runs the specified healthchecks. +-- Runs all discovered healthchecks if plugin_names is empty. +function M._check(plugin_names) + local healthchecks = plugin_names == '' and get_healthcheck('*') or get_healthcheck(plugin_names) + + -- Create buffer and open in a tab, unless this is the default buffer when Nvim starts. + local emptybuf = vim.fn.bufnr('$') == 1 and vim.fn.getline(1) == '' and 1 == vim.fn.line('$') + local mod = emptybuf and 'buffer' or 'tab sbuffer' + local bufnr = vim.api.nvim_create_buf(true, true) + vim.cmd(mod .. ' ' .. bufnr) + + if vim.fn.bufexists('health://') == 1 then + vim.cmd.bwipe('health://') + end + vim.cmd.file('health://') + vim.cmd.setfiletype('checkhealth') + + if healthchecks == nil or next(healthchecks) == nil then + vim.fn.setline(1, 'ERROR: No healthchecks found.') + return + end + vim.cmd.redraw() + vim.print('Running healthchecks...') + + for name, value in vim.spairs(healthchecks) do + local func = value[1] + local type = value[2] + s_output = {} + + if func == '' then + s_output = {} + M.error('No healthcheck found for "' .. name .. '" plugin.') + end + if type == 'v' then + vim.cmd.call(func, {}) + else + local f = assert(loadstring(func)) + local ok, output = pcall(f) + if not ok then + M.error( + string.format('Failed to run healthcheck for "%s" plugin. Exception:\n%s\n', name, output) + ) + end + end + -- in the event the healthcheck doesn't return anything + -- (the plugin author should avoid this possibility) + if next(s_output) == nil then + s_output = {} + M.error('The healthcheck report for "' .. name .. '" plugin is empty.') + end + local header = { string.rep('=', 78), name .. ': ' .. func, '' } + -- remove empty line after header from report_start + if s_output[1] == '' then + local tmp = {} + for i = 2, #s_output do + tmp[#tmp + 1] = s_output[i] + end + s_output = {} + for _, v in ipairs(tmp) do + s_output[#s_output + 1] = v + end + end + s_output[#s_output + 1] = '' + s_output = vim.list_extend(header, s_output) + vim.fn.append('$', s_output) + vim.cmd.redraw() + end + + -- Clear the 'Running healthchecks...' message. + vim.cmd.redraw() + vim.print('') +end + return M diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua index 987707e661..6ca468393e 100644 --- a/runtime/lua/vim/lsp/health.lua +++ b/runtime/lua/vim/lsp/health.lua @@ -2,8 +2,8 @@ local M = {} --- Performs a healthcheck for LSP function M.check() - local report_info = vim.health.report_info - local report_warn = vim.health.report_warn + local report_info = vim.health.info + local report_warn = vim.health.warn local log = require('vim.lsp.log') local current_log_level = log.get_level() @@ -29,7 +29,7 @@ function M.check() report_fn(string.format('Log size: %d KB', log_size / 1000)) local clients = vim.lsp.get_active_clients() - vim.health.report_start('vim.lsp: Active Clients') + vim.health.start('vim.lsp: Active Clients') if next(clients) then for _, client in pairs(clients) do report_info( diff --git a/runtime/lua/vim/treesitter/health.lua b/runtime/lua/vim/treesitter/health.lua index dabf2cdf6c..ed1161e97f 100644 --- a/runtime/lua/vim/treesitter/health.lua +++ b/runtime/lua/vim/treesitter/health.lua @@ -6,14 +6,14 @@ local health = require('vim.health') function M.check() local parsers = vim.api.nvim_get_runtime_file('parser/*', true) - health.report_info(string.format('Nvim runtime ABI version: %d', ts.language_version)) + health.info(string.format('Nvim runtime ABI version: %d', ts.language_version)) for _, parser in pairs(parsers) do local parsername = vim.fn.fnamemodify(parser, ':t:r') local is_loadable, err_or_nil = pcall(ts.language.add, parsername) if not is_loadable then - health.report_error( + health.error( string.format( 'Parser "%s" failed to load (path: %s): %s', parsername, @@ -23,7 +23,7 @@ function M.check() ) else local lang = ts.language.inspect(parsername) - health.report_ok( + health.ok( string.format('Parser: %-10s ABI: %d, path: %s', parsername, lang._abi_version, parser) ) end diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a30f9146c5..97cf0c6364 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8597,33 +8597,27 @@ void eval_fmt_source_name_line(char *buf, size_t bufsize) /// ":checkhealth [plugins]" void ex_checkhealth(exarg_T *eap) { - bool found = !!find_func("health#check"); - if (!found - && script_autoload("health#check", sizeof("health#check") - 1, false)) { - found = !!find_func("health#check"); - } - if (!found) { - const char *vimruntime_env = os_getenv("VIMRUNTIME"); - if (vimruntime_env == NULL) { - emsg(_("E5009: $VIMRUNTIME is empty or unset")); - } else { - bool rtp_ok = NULL != strstr(p_rtp, vimruntime_env); - if (rtp_ok) { - semsg(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env); - } else { - emsg(_("E5009: Invalid 'runtimepath'")); - } - } + Error err = ERROR_INIT; + MAXSIZE_TEMP_ARRAY(args, 1); + ADD_C(args, STRING_OBJ(cstr_as_string(eap->arg))); + NLUA_EXEC_STATIC("return vim.health._check(...)", args, &err); + if (!ERROR_SET(&err)) { return; } - size_t bufsize = strlen(eap->arg) + sizeof("call health#check('')"); - char *buf = xmalloc(bufsize); - snprintf(buf, bufsize, "call health#check('%s')", eap->arg); - - do_cmdline_cmd(buf); - - xfree(buf); + const char *vimruntime_env = os_getenv("VIMRUNTIME"); + if (vimruntime_env == NULL) { + emsg(_("E5009: $VIMRUNTIME is empty or unset")); + } else { + bool rtp_ok = NULL != strstr(p_rtp, vimruntime_env); + if (rtp_ok) { + semsg(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env); + } else { + emsg(_("E5009: Invalid 'runtimepath'")); + } + } + semsg_multiline(err.msg); + api_clear_error(&err); } void invoke_prompt_callback(void) diff --git a/test/functional/fixtures/autoload/health/broken.vim b/test/functional/fixtures/autoload/health/broken.vim deleted file mode 100644 index a2a595b96f..0000000000 --- a/test/functional/fixtures/autoload/health/broken.vim +++ /dev/null @@ -1,3 +0,0 @@ -function! health#broken#check() - throw 'caused an error' -endfunction diff --git a/test/functional/fixtures/autoload/health/full_render.vim b/test/functional/fixtures/autoload/health/full_render.vim deleted file mode 100644 index 2064b8606e..0000000000 --- a/test/functional/fixtures/autoload/health/full_render.vim +++ /dev/null @@ -1,8 +0,0 @@ -function! health#full_render#check() - call health#report_start("report 1") - call health#report_ok("life is fine") - call health#report_warn("no what installed", ["pip what", "make what"]) - call health#report_start("report 2") - call health#report_info("stuff is stable") - call health#report_error("why no hardcopy", [":h :hardcopy", ":h :TOhtml"]) -endfunction diff --git a/test/functional/fixtures/autoload/health/success1.vim b/test/functional/fixtures/autoload/health/success1.vim deleted file mode 100644 index a360347455..0000000000 --- a/test/functional/fixtures/autoload/health/success1.vim +++ /dev/null @@ -1,6 +0,0 @@ -function! health#success1#check() - call health#report_start("report 1") - call health#report_ok("everything is fine") - call health#report_start("report 2") - call health#report_ok("nothing to see here") -endfunction diff --git a/test/functional/fixtures/autoload/health/success2.vim b/test/functional/fixtures/autoload/health/success2.vim deleted file mode 100644 index b742b4879d..0000000000 --- a/test/functional/fixtures/autoload/health/success2.vim +++ /dev/null @@ -1,4 +0,0 @@ -function! health#success2#check() - call health#report_start("another 1") - call health#report_ok("ok") -endfunction diff --git a/test/functional/fixtures/lua/test_plug/autoload/health/test_plug.vim b/test/functional/fixtures/lua/test_plug/autoload/health/test_plug.vim deleted file mode 100644 index de05f56e9e..0000000000 --- a/test/functional/fixtures/lua/test_plug/autoload/health/test_plug.vim +++ /dev/null @@ -1,3 +0,0 @@ -function! health#success1#check() - call health#report_start("If you see this I'm broken") -endfunction diff --git a/test/functional/fixtures/lua/test_plug/full_render/health.lua b/test/functional/fixtures/lua/test_plug/full_render/health.lua new file mode 100644 index 0000000000..10833f6239 --- /dev/null +++ b/test/functional/fixtures/lua/test_plug/full_render/health.lua @@ -0,0 +1,12 @@ +local M = {} + +M.check = function() + vim.health.start('report 1') + vim.health.ok('life is fine') + vim.health.warn('no what installed', { 'pip what', 'make what' }) + vim.health.start('report 2') + vim.health.info('stuff is stable') + vim.health.error('why no hardcopy', { ':h :hardcopy', ':h :TOhtml' }) +end + +return M diff --git a/test/functional/fixtures/lua/test_plug/health/init.lua b/test/functional/fixtures/lua/test_plug/health/init.lua index 58162d4515..ef0c5ae3cd 100644 --- a/test/functional/fixtures/lua/test_plug/health/init.lua +++ b/test/functional/fixtures/lua/test_plug/health/init.lua @@ -1,10 +1,10 @@ local M = {} M.check = function() - vim.health.report_start("report 1") - vim.health.report_ok("everything is fine") - vim.health.report_start("report 2") - vim.health.report_ok("nothing to see here") + vim.health.start('report 1') + vim.health.ok('everything is fine') + vim.health.start('report 2') + vim.health.ok('nothing to see here') end return M diff --git a/test/functional/fixtures/lua/test_plug/submodule/health.lua b/test/functional/fixtures/lua/test_plug/submodule/health.lua index 58162d4515..ef0c5ae3cd 100644 --- a/test/functional/fixtures/lua/test_plug/submodule/health.lua +++ b/test/functional/fixtures/lua/test_plug/submodule/health.lua @@ -1,10 +1,10 @@ local M = {} M.check = function() - vim.health.report_start("report 1") - vim.health.report_ok("everything is fine") - vim.health.report_start("report 2") - vim.health.report_ok("nothing to see here") + vim.health.start('report 1') + vim.health.ok('everything is fine') + vim.health.start('report 2') + vim.health.ok('nothing to see here') end return M diff --git a/test/functional/fixtures/lua/test_plug/submodule_failed/health.lua b/test/functional/fixtures/lua/test_plug/submodule_failed/health.lua deleted file mode 100644 index ee5f4e404b..0000000000 --- a/test/functional/fixtures/lua/test_plug/submodule_failed/health.lua +++ /dev/null @@ -1,11 +0,0 @@ -local M = {} - -M.check = function() - vim.health.report_start("report 1") - vim.health.report_ok("everything is fine") - vim.health.report_warn("About to add a number to nil") - local a = nil + 2 - return a -end - -return M diff --git a/test/functional/fixtures/lua/test_plug/success1/health.lua b/test/functional/fixtures/lua/test_plug/success1/health.lua new file mode 100644 index 0000000000..ef0c5ae3cd --- /dev/null +++ b/test/functional/fixtures/lua/test_plug/success1/health.lua @@ -0,0 +1,10 @@ +local M = {} + +M.check = function() + vim.health.start('report 1') + vim.health.ok('everything is fine') + vim.health.start('report 2') + vim.health.ok('nothing to see here') +end + +return M diff --git a/test/functional/fixtures/lua/test_plug/success2/health.lua b/test/functional/fixtures/lua/test_plug/success2/health.lua new file mode 100644 index 0000000000..240a2d33de --- /dev/null +++ b/test/functional/fixtures/lua/test_plug/success2/health.lua @@ -0,0 +1,8 @@ +local M = {} + +M.check = function() + vim.health.start('another 1') + vim.health.ok('ok') +end + +return M diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua index 97d32313e5..926e12ec32 100644 --- a/test/functional/plugin/health_spec.lua +++ b/test/functional/plugin/health_spec.lua @@ -1,5 +1,4 @@ local helpers = require('test.functional.helpers')(after_each) -local global_helpers = require('test.helpers') local Screen = require('test.functional.ui.screen') local clear = helpers.clear @@ -43,20 +42,17 @@ end) describe('health.vim', function() before_each(function() clear{args={'-u', 'NORC'}} - -- Provides functions: - -- health#broken#check() - -- health#success1#check() - -- health#success2#check() + -- Provides healthcheck functions command("set runtimepath+=test/functional/fixtures") end) describe(":checkhealth", function() - it("functions health#report_*() render correctly", function() + it("functions report_*() render correctly", function() command("checkhealth full_render") helpers.expect([[ ============================================================================== - full_render: health#full_render#check + test_plug.full_render: require("test_plug.full_render.health").check() report 1 ~ - OK life is fine @@ -79,7 +75,7 @@ describe('health.vim', function() helpers.expect([[ ============================================================================== - success1: health#success1#check + test_plug: require("test_plug.health").check() report 1 ~ - OK everything is fine @@ -88,36 +84,19 @@ describe('health.vim', function() - OK nothing to see here ============================================================================== - success2: health#success2#check - - another 1 ~ - - OK ok - - ============================================================================== - test_plug: require("test_plug.health").check() + test_plug.success1: require("test_plug.success1.health").check() report 1 ~ - OK everything is fine report 2 ~ - OK nothing to see here - ]]) - end) - - it("lua plugins, skips vimscript healthchecks with the same name", function() - command("checkhealth test_plug") - -- Existing file in test/functional/fixtures/lua/test_plug/autoload/health/test_plug.vim - -- and the Lua healthcheck is used instead. - helpers.expect([[ ============================================================================== - test_plug: require("test_plug.health").check() - - report 1 ~ - - OK everything is fine + test_plug.success2: require("test_plug.success2.health").check() - report 2 ~ - - OK nothing to see here + another 1 ~ + - OK ok ]]) end) @@ -136,57 +115,6 @@ describe('health.vim', function() ]]) end) - it("lua plugins submodules with expression '*'", function() - command("checkhealth test_plug*") - local buf_lines = helpers.curbuf('get_lines', 0, -1, true) - -- avoid dealing with path separators - local received = table.concat(buf_lines, '\n', 1, #buf_lines - 5) - local expected = helpers.dedent([[ - - ============================================================================== - test_plug: require("test_plug.health").check() - - report 1 ~ - - OK everything is fine - - report 2 ~ - - OK nothing to see here - - ============================================================================== - test_plug.submodule: require("test_plug.submodule.health").check() - - report 1 ~ - - OK everything is fine - - report 2 ~ - - OK nothing to see here - - ============================================================================== - test_plug.submodule_empty: require("test_plug.submodule_empty.health").check() - - - ERROR The healthcheck report for "test_plug.submodule_empty" plugin is empty. - - ============================================================================== - test_plug.submodule_failed: require("test_plug.submodule_failed.health").check() - - - ERROR Failed to run healthcheck for "test_plug.submodule_failed" plugin. Exception: - function health#check, line 25]]) - eq(expected, received) - end) - - it("gracefully handles broken healthcheck", function() - command("checkhealth broken") - helpers.expect([[ - - ============================================================================== - broken: health#broken#check - - - ERROR Failed to run healthcheck for "broken" plugin. Exception: - function health#check[25]..health#broken#check, line 1 - caused an error - ]]) - end) - it("... including empty reports", function() command("checkhealth test_plug.submodule_empty") helpers.expect([[ @@ -198,25 +126,6 @@ describe('health.vim', function() ]]) end) - it("gracefully handles broken lua healthcheck", function() - command("checkhealth test_plug.submodule_failed") - local buf_lines = helpers.curbuf('get_lines', 0, -1, true) - local received = table.concat(buf_lines, '\n', 1, #buf_lines - 5) - -- avoid dealing with path separators - local lua_err = "attempt to perform arithmetic on a nil value" - local last_line = buf_lines[#buf_lines - 4] - assert(string.find(last_line, lua_err) ~= nil, "Lua error not present") - - local expected = global_helpers.dedent([[ - - ============================================================================== - test_plug.submodule_failed: require("test_plug.submodule_failed.health").check() - - - ERROR Failed to run healthcheck for "test_plug.submodule_failed" plugin. Exception: - function health#check, line 25]]) - eq(expected, received) - end) - it("highlights OK, ERROR", function() local screen = Screen.new(50, 12) screen:attach() @@ -236,7 +145,7 @@ describe('health.vim', function() - {Error:ERROR} No healthcheck found for "foo" plugin. | | {Bar:──────────────────────────────────────────────────}| - {Heading:success1: health#success1#check} | + {Heading:test_plug.success1: require("test_plug.success1.he}| | {Heading:report 1} | - {Ok:OK} everything is fine | |