Merge #7399 ':checkhealth (built-in)'

This commit is contained in:
Justin M. Keyes 2017-10-17 22:43:46 +02:00 committed by GitHub
commit 0cba3da26e
14 changed files with 154 additions and 111 deletions

View File

@ -18,7 +18,7 @@ function! s:enhance_syntax() abort
syntax match healthBar "|" contained conceal syntax match healthBar "|" contained conceal
highlight link healthHelp Identifier highlight link healthHelp Identifier
" We do not care about markdown syntax errors in :CheckHealth output. " We do not care about markdown syntax errors in :checkhealth output.
highlight! link markdownError Normal highlight! link markdownError Normal
endfunction endfunction
@ -159,7 +159,10 @@ endfunction
" Translates a list of plugin names to healthcheck function names. " Translates a list of plugin names to healthcheck function names.
function! s:to_fn_names(plugin_names) abort function! s:to_fn_names(plugin_names) abort
let healthchecks = [] let healthchecks = []
for p in a:plugin_names 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') call add(healthchecks, 'health#'.p.'#check')
endfor endfor
return healthchecks return healthchecks

View File

@ -4,12 +4,19 @@ function! s:check_config() abort
let ok = v:true let ok = v:true
call health#report_start('Configuration') call health#report_start('Configuration')
" If $VIM is empty we don't care. Else make sure it is valid.
if !empty($VIM) && !filereadable($VIM.'/runtime/doc/nvim.txt')
let ok = v:false
call health#report_error("$VIM is invalid: ".$VIM)
endif
if exists('$NVIM_TUI_ENABLE_CURSOR_SHAPE') if exists('$NVIM_TUI_ENABLE_CURSOR_SHAPE')
let ok = v:false let ok = v:false
call health#report_warn("$NVIM_TUI_ENABLE_CURSOR_SHAPE is ignored in Nvim 0.2+", call health#report_warn("$NVIM_TUI_ENABLE_CURSOR_SHAPE is ignored in Nvim 0.2+",
\ [ "Use the 'guicursor' option to configure cursor shape. :help 'guicursor'", \ [ "Use the 'guicursor' option to configure cursor shape. :help 'guicursor'",
\ 'https://github.com/neovim/neovim/wiki/Following-HEAD#20170402' ]) \ 'https://github.com/neovim/neovim/wiki/Following-HEAD#20170402' ])
endif endif
if &paste if &paste
let ok = v:false let ok = v:false
call health#report_error("'paste' is enabled. This option is only for pasting text.\nIt should not be set in your config.", call health#report_error("'paste' is enabled. This option is only for pasting text.\nIt should not be set in your config.",

View File

@ -75,7 +75,7 @@ let s:prog = provider#ruby#Detect()
let s:plugin_path = expand('<sfile>:p:h') . '/script_host.rb' let s:plugin_path = expand('<sfile>:p:h') . '/script_host.rb'
if empty(s:prog) if empty(s:prog)
let s:err = 'Cannot find the neovim RubyGem. Try :CheckHealth' let s:err = 'Cannot find the neovim RubyGem. Try :checkhealth'
endif endif
call remote#host#RegisterClone('legacy-ruby-provider', 'ruby') call remote#host#RegisterClone('legacy-ruby-provider', 'ruby')

View File

@ -5,50 +5,42 @@ Author: TJ DeVries <devries.timothyj@gmail.com>
Type <M-]> to see the table of contents. Type <M-]> to see the table of contents.
============================================================================== ==============================================================================
Introduction *healthcheck* *health.vim-intro* Introduction *health*
Troubleshooting user configuration problems is a time-consuming task that health.vim is a minimal framework to help with troubleshooting user
developers want to minimize. health.vim provides a simple framework for plugin configuration. Nvim ships with healthchecks for configuration, performance,
authors to hook into, and for users to invoke, to check and report the user's python support, ruby support, clipboard support, and more.
configuration and environment. Type this command to try it: >
:CheckHealth To run the healthchecks, use this command: >
:checkhealth
< <
For example, some users have broken or unusual Python setups, which breaks the Plugin authors are encouraged to write new healthchecks. |health-dev|
|:python| command. |:CheckHealth| detects several common Python configuration
problems and reports them. If the Neovim Python module is not installed, it
shows a warning: >
You have not installed the Neovim Python module
You might want to try `pip install Neovim`
<
Plugin authors are encouraged to add healthchecks, see |health.vim-dev|.
============================================================================== ==============================================================================
Commands and functions *health.vim-manual* Commands *health-commands*
Commands *:checkhealth* *:CheckHealth*
------------------------------------------------------------------------------ :checkhealth Run all healthchecks.
*:CheckHealth* *E5009*
:CheckHealth Run all healthchecks and show the output in a new Nvim depends on the |$VIMRUNTIME| environment variable
tabpage. These healthchecks are included by default: to find the standard "runtime files" for syntax
- python2 highlighting, filetype-specific behavior, and standard
- python3 plugins such as :checkhealth. If $VIMRUNTIME is invalid
- ruby then those features will not work.
- remote plugin
:CheckHealth {plugins} :checkhealth {plugins}
Run healthchecks for one or more plugins. E.g. to run Run healthcheck(s) for one or more plugins. E.g. to run
only the standard Nvim healthcheck: > only the standard Nvim healthcheck: >
:CheckHealth nvim :checkhealth nvim
< To run the healthchecks for the "foo" and "bar" plugins < To run the healthchecks for the "foo" and "bar" plugins
(assuming these plugins are on your 'runtimepath' and (assuming these plugins are on your 'runtimepath' and
they have implemented health#foo#check() and they have implemented health#foo#check() and
health#bar#check(), respectively): > health#bar#check(), respectively): >
:CheckHealth foo bar :checkhealth foo bar
< <
Functions ==============================================================================
------------------------------------------------------------------------------ Functions *health-functions*
health.vim functions are for creating new healthchecks. They mostly just do health.vim functions are for creating new healthchecks. They mostly just do
some layout and formatting, to give users a consistent presentation. some layout and formatting, to give users a consistent presentation.
@ -59,51 +51,49 @@ health#report_start({name}) *health#report_start*
per section. per section.
health#report_info({msg}) *health#report_info* health#report_info({msg}) *health#report_info*
Displays an informational message. Reports an informational message.
health#report_ok({msg}) *health#report_ok* health#report_ok({msg}) *health#report_ok*
Displays a "success" message. Reports a "success" message.
health#report_warn({msg}, [{advice}]) *health#report_warn* health#report_warn({msg}, [{advice}]) *health#report_warn*
Displays a warning. {advice} is an optional List of suggestions. Reports a warning. {advice} is an optional List of suggestions.
health#report_error({msg}, [{advice}]) *health#report_error* health#report_error({msg}, [{advice}]) *health#report_error*
Displays an error. {advice} is an optional List of suggestions. Reports an error. {advice} is an optional List of suggestions.
health#{plugin}#check() *health.user_checker* health#{plugin}#check() *health.user_checker*
This is the form of a healthcheck definition. Call the above functions Healthcheck function for {plugin}. Called by |:checkhealth|
from this function, then |:CheckHealth| does the rest. Example: > automatically. Example: >
function! health#my_plug#check() abort function! health#my_plug#check() abort
silent call s:check_environment_vars() silent call s:check_environment_vars()
silent call s:check_python_configuration() silent call s:check_python_configuration()
endfunction endfunction
< <
The function will be found and called automatically when the user
invokes |:CheckHealth|.
All output will be captured from the healthcheck. Use the All output will be captured from the healthcheck. Use the
health#report_* functions so that your healthcheck has a format health#report_* functions so that your healthcheck has a format
consistent with the standard healthchecks. consistent with the standard healthchecks.
============================================================================== ==============================================================================
Create a healthcheck *health.vim-dev* Create a healthcheck *health-dev*
Healthchecks are functions that check the health of the system. Neovim has Healthchecks are functions that check the user environment, configuration,
built-in checkers, found in $VIMRUNTIME/autoload/health/. etc. Nvim has built-in healthchecks in $VIMRUNTIME/autoload/health/.
To add a new checker for your own plugin, simply define a To add a new healthcheck for your own plugin, simply define a
health#{plugin}#check() function in autoload/health/{plugin}.vim. health#{plugin}#check() function in autoload/health/{plugin}.vim.
|:CheckHealth| automatically finds and invokes such functions. |:checkhealth| automatically finds and invokes such functions.
If your plugin is named "jslint", then its healthcheck function must be > If your plugin is named "foo", then its healthcheck function must be >
health#jslint#check() health#foo#check()
defined in this file on 'runtimepath': > defined in this file on 'runtimepath': >
autoload/health/jslint.vim autoload/health/foo.vim
Here's a sample to get started: > Copy this sample code into autoload/health/foo.vim and replace "foo" with your
function! health#jslint#check() abort plugin name: >
function! health#foo#check() abort
call health#report_start('sanity checks') call health#report_start('sanity checks')
" perform arbitrary checks " perform arbitrary checks
" ... " ...
@ -111,8 +101,8 @@ Here's a sample to get started: >
if looks_good if looks_good
call health#report_ok('found required dependencies') call health#report_ok('found required dependencies')
else else
call health#report_error('cannot find jslint', call health#report_error('cannot find foo',
\ ['npm install --save jslint']) \ ['npm install --save foo'])
endif endif
endfunction endfunction

View File

@ -20,7 +20,7 @@ Note: Only the Vim 7.3 API is supported; bindeval (Vim 7.4) is not.
PYTHON QUICKSTART ~ PYTHON QUICKSTART ~
If you used a package manager to install Nvim, you might already have the If you used a package manager to install Nvim, you might already have the
required `neovim` Python package. Run |:CheckHealth| to see if your system is required `neovim` Python package. Run |:checkhealth| to see if your system is
up-to-date. up-to-date.
Following are steps to install the package with Python's `pip` tool. Following are steps to install the package with Python's `pip` tool.
@ -88,7 +88,7 @@ Ruby integration *provider-ruby*
Nvim supports the Vim legacy |ruby-vim| interface via external Ruby Nvim supports the Vim legacy |ruby-vim| interface via external Ruby
interpreters connected via |RPC|. interpreters connected via |RPC|.
Run |:CheckHealth| to see if your system is up-to-date. Run |:checkhealth| to see if your system is up-to-date.
RUBY QUICKSTART ~ RUBY QUICKSTART ~

View File

@ -633,54 +633,40 @@ though.
============================================================================== ==============================================================================
3. $VIM and $VIMRUNTIME 3. $VIM and $VIMRUNTIME
*$VIM* *$VIM*
The environment variable "$VIM" is used to locate various user files for Vim, The environment variable "$VIM" is used to locate various user files for Nvim,
such as the user startup script |init.vim|. This depends on the system, see such as the user startup script |init.vim|. This depends on the system, see
|startup|. |startup|.
To avoid the need for every user to set the $VIM environment variable, Vim Nvim will try to get the value for $VIM in this order:
will try to get the value for $VIM in this order:
1. The value defined by the $VIM environment variable. You can use this to 1. Environment variable $VIM, if it is set.
make Vim look in a specific directory for its support files. Example: > 2. Path derived from the 'helpfile' option, unless it contains some
setenv VIM /home/paul/vim environment variable too (default is "$VIMRUNTIME/doc/help.txt"). File
2. The path from 'helpfile' is used, unless it contains some environment name ("help.txt", etc.) is removed. Trailing directory names are removed,
variable too (the default is "$VIMRUNTIME/doc/help.txt": chicken-egg in this order: "doc", "runtime".
problem). The file name ("help.txt" or any other) is removed. Then 3. Path derived from the location of the `nvim` executable.
trailing directory names are removed, in this order: "doc", "runtime" and 4. Compile-time defined installation directory (see output of ":version").
"vim{version}" (e.g., "vim54").
3. For MSDOS and Win32 Vim tries to use the directory name of the After doing this once, Nvim sets the $VIM environment variable.
executable. If it ends in "/src", this is removed. This is useful if you
unpacked the .zip file in some directory, and adjusted the search path to
find the vim executable. Trailing directory names are removed, in this
order: "runtime" and "vim{version}" (e.g., "vim54").
4. For Unix the compile-time defined installation directory is used (see the
output of ":version").
Once Vim has done this once, it will set the $VIM environment variable. To
change it later, use a ":let" command like this: >
:let $VIM = "/home/paul/vim/"
<
*$VIMRUNTIME* *$VIMRUNTIME*
The environment variable "$VIMRUNTIME" is used to locate various support The environment variable "$VIMRUNTIME" is used to locate various support
files, such as the on-line documentation and files used for syntax files, such as the documentation and syntax-highlighting files. For example,
highlighting. For example, the main help file is normally the main help file is normally "$VIMRUNTIME/doc/help.txt".
"$VIMRUNTIME/doc/help.txt".
You don't normally set $VIMRUNTIME yourself, but let Vim figure it out. This
is the order used to find the value of $VIMRUNTIME:
1. If the environment variable $VIMRUNTIME is set, it is used. You can use
this when the runtime files are in an unusual location.
2. If "$VIM/vim{version}" exists, it is used. {version} is the version
number of Vim, without any '-' or '.'. For example: "$VIM/vim54". This is
the normal value for $VIMRUNTIME.
3. If "$VIM/runtime" exists, it is used.
4. The value of $VIM is used. This is for backwards compatibility with older
versions.
5. If "../share/nvim/runtime" exists relative to |v:progpath|, it is used.
6. When the 'helpfile' option is set and doesn't contain a '$', its value is
used, with "doc/help.txt" removed from the end.
Once Vim has done this once, it will set the $VIMRUNTIME environment variable. Nvim will try to get the value for $VIMRUNTIME in this order:
To change it later, use a ":let" command like this: >
:let $VIMRUNTIME = "/home/piet/vim/vim54" 1. Environment variable $VIMRUNTIME, if it is set.
2. Directory path "$VIM/vim{version}", if it exists, where {version} is the
Vim version number without '-' or '.'. For example: "$VIM/vim54".
3. Directory path "$VIM/runtime", if it exists.
4. Value of $VIM environment variable. This is for backwards compatibility
with older Vim versions.
5. If "../share/nvim/runtime" exists relative to |v:progpath|, it is used.
6. Path derived from the 'helpfile' option (if it doesn't contain '$') with
"doc/help.txt" removed from the end.
After doing this once, Nvim sets the $VIMRUNTIME environment variable.
In case you need the value of $VIMRUNTIME in a shell (e.g., for a script that In case you need the value of $VIMRUNTIME in a shell (e.g., for a script that
greps in the help files) you might be able to use this: > greps in the help files) you might be able to use this: >

View File

@ -127,7 +127,7 @@ Variables:
|v:windowid| is always available (for use by external UIs) |v:windowid| is always available (for use by external UIs)
Commands: Commands:
|:CheckHealth| |:checkhealth|
|:drop| is available on all platforms |:drop| is available on all platforms
|:Man| is available by default, with many improvements such as completion |:Man| is available by default, with many improvements such as completion

View File

@ -22850,3 +22850,32 @@ void eval_format_source_name_line(char *buf, size_t bufsize)
(sourcing_name ? sourcing_name : (char_u *)"?"), (sourcing_name ? sourcing_name : (char_u *)"?"),
(sourcing_name ? sourcing_lnum : 0)); (sourcing_name ? sourcing_lnum : 0));
} }
/// ":checkhealth [plugins]"
void ex_checkhealth(exarg_T *eap)
{
bool found = !!find_func((char_u *)"health#check");
if (!found
&& script_autoload("health#check", sizeof("health#check") - 1, false)) {
found = !!find_func((char_u *)"health#check");
}
if (!found) {
const char *vimruntime_env = os_getenv("VIMRUNTIME");
if (vimruntime_env == NULL) {
EMSG(_("E5009: $VIMRUNTIME is empty or unset"));
return;
} else {
EMSG2(_("E5009: Invalid $VIMRUNTIME: %s"), os_getenv("VIMRUNTIME"));
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);
}

View File

@ -450,6 +450,12 @@ return {
addr_type=ADDR_LINES, addr_type=ADDR_LINES,
func='ex_changes', func='ex_changes',
}, },
{
command='checkhealth',
flags=bit.bor(EXTRA, TRLBAR),
addr_type=ADDR_LINES,
func='ex_checkhealth',
},
{ {
command='checkpath', command='checkpath',
flags=bit.bor(TRLBAR, BANG, CMDWIN), flags=bit.bor(TRLBAR, BANG, CMDWIN),

View File

@ -5535,7 +5535,7 @@ int get_default_register_name(void)
static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing) static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
{ {
#define MSG_NO_CLIP "clipboard: No provider. " \ #define MSG_NO_CLIP "clipboard: No provider. " \
"Try \":CheckHealth\" or \":h clipboard\"." "Try \":checkhealth\" or \":h clipboard\"."
yankreg_T *target = NULL; yankreg_T *target = NULL;
bool explicit_cb_reg = (*name == '*' || *name == '+'); bool explicit_cb_reg = (*name == '*' || *name == '+');

View File

@ -1365,7 +1365,7 @@ void intro_message(int colon)
N_("https://neovim.io/community"), N_("https://neovim.io/community"),
"", "",
N_("type :help nvim<Enter> if you are new! "), N_("type :help nvim<Enter> if you are new! "),
N_("type :CheckHealth<Enter> to optimize Nvim"), N_("type :checkhealth<Enter> to optimize Nvim"),
N_("type :q<Enter> to exit "), N_("type :q<Enter> to exit "),
N_("type :help<Enter> for help "), N_("type :help<Enter> for help "),
"", "",

View File

@ -100,7 +100,7 @@ describe('clipboard', function()
^ | ^ |
~ | ~ |
~ | ~ |
clipboard: No provider. Try ":CheckHealth" or ":h clipboard". | clipboard: No provider. Try ":checkhealth" or ":h clipboard". |
]], nil, {{bold = true, foreground = Screen.colors.Blue}}) ]], nil, {{bold = true, foreground = Screen.colors.Blue}})
end) end)
@ -112,7 +112,7 @@ describe('clipboard', function()
feed_command('redir @+> | bogus_cmd | redir END') feed_command('redir @+> | bogus_cmd | redir END')
screen:expect([[ screen:expect([[
~ | ~ |
clipboard: No provider. Try ":CheckHealth" or ":h clipboard". | clipboard: No provider. Try ":checkhealth" or ":h clipboard". |
E492: Not an editor command: bogus_cmd | redir END | E492: Not an editor command: bogus_cmd | redir END |
Press ENTER or type command to continue^ | Press ENTER or type command to continue^ |
]], nil, {{bold = true, foreground = Screen.colors.Blue}}) ]], nil, {{bold = true, foreground = Screen.colors.Blue}})
@ -132,7 +132,7 @@ describe('clipboard', function()
^ | ^ |
~ | ~ |
~ | ~ |
clipboard: No provider. Try ":CheckHealth" or ":h clipboard". | clipboard: No provider. Try ":checkhealth" or ":h clipboard". |
]], nil, {{bold = true, foreground = Screen.colors.Blue}}) ]], nil, {{bold = true, foreground = Screen.colors.Blue}})
end) end)

View File

@ -2,7 +2,29 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
local plugin_helpers = require('test.functional.plugin.helpers') local plugin_helpers = require('test.functional.plugin.helpers')
local clear = helpers.clear
local curbuf_contents = helpers.curbuf_contents
local command = helpers.command local command = helpers.command
local eq = helpers.eq
describe(':checkhealth', function()
it("detects invalid $VIMRUNTIME", function()
clear({
env={ VIMRUNTIME='bogus', },
})
local status, err = pcall(command, 'checkhealth')
eq(false, status)
eq('Invalid $VIMRUNTIME: bogus', string.match(err, 'Invalid.*'))
end)
it("detects invalid $VIM", function()
clear()
-- Do this after startup, otherwise it just breaks $VIMRUNTIME.
command("let $VIM='zub'")
command("checkhealth nvim")
eq("ERROR: $VIM is invalid: zub",
string.match(curbuf_contents(), "ERROR: $VIM .* zub"))
end)
end)
describe('health.vim', function() describe('health.vim', function()
before_each(function() before_each(function()
@ -14,7 +36,7 @@ describe('health.vim', function()
command("set runtimepath+=test/functional/fixtures") command("set runtimepath+=test/functional/fixtures")
end) end)
it("reports", function() it("health#report_*()", function()
helpers.source([[ helpers.source([[
let g:health_report = execute([ let g:health_report = execute([
\ "call health#report_start('Check Bar')", \ "call health#report_start('Check Bar')",
@ -44,9 +66,9 @@ describe('health.vim', function()
end) end)
describe(":CheckHealth", function() describe(":checkhealth", function()
it("concatenates multiple reports", function() it("concatenates multiple reports", function()
command("CheckHealth success1 success2") command("checkhealth success1 success2")
helpers.expect([[ helpers.expect([[
health#success1#check health#success1#check
@ -65,7 +87,7 @@ describe('health.vim', function()
end) end)
it("gracefully handles broken healthcheck", function() it("gracefully handles broken healthcheck", function()
command("CheckHealth broken") command("checkhealth broken")
helpers.expect([[ helpers.expect([[
health#broken#check health#broken#check
@ -89,7 +111,7 @@ describe('health.vim', function()
Bar = { foreground=Screen.colors.Purple }, Bar = { foreground=Screen.colors.Purple },
Bullet = { bold=true, foreground=Screen.colors.Brown }, Bullet = { bold=true, foreground=Screen.colors.Brown },
}) })
command("CheckHealth foo success1") command("checkhealth foo success1")
command("1tabclose") command("1tabclose")
command("set laststatus=0") command("set laststatus=0")
screen:expect([[ screen:expect([[
@ -107,7 +129,7 @@ describe('health.vim', function()
end) end)
it("gracefully handles invalid healthcheck", function() it("gracefully handles invalid healthcheck", function()
command("CheckHealth non_existent_healthcheck") command("checkhealth non_existent_healthcheck")
helpers.expect([[ helpers.expect([[
health#non_existent_healthcheck#check health#non_existent_healthcheck#check

View File

@ -16,7 +16,7 @@ do
clear() clear()
if missing_provider('ruby') then if missing_provider('ruby') then
pending( pending(
"Cannot find the neovim RubyGem. Try :CheckHealth", "Cannot find the neovim RubyGem. Try :checkhealth",
function() end) function() end)
return return
end end