Merge pull request #33680 from justinmk/release

This commit is contained in:
Justin M. Keyes
2025-04-27 15:32:22 -07:00
committed by GitHub
4 changed files with 173 additions and 104 deletions

View File

@@ -9,18 +9,18 @@
============================================================================== ==============================================================================
Checkhealth *vim.health* *health* Checkhealth *vim.health* *health*
vim.health is a minimal framework to help users troubleshoot configuration and vim.health is a minimal framework to help users troubleshoot configuration and
any other environment conditions that a plugin might care about. Nvim ships any other environment conditions that a plugin might care about. Nvim ships
with healthchecks for configuration, performance, python support, ruby with healthchecks for configuration, performance, python support, ruby
support, clipboard support, and more. support, clipboard support, and more.
To run all healthchecks, use: >vim To run all healthchecks, use: >vim
:checkhealth :checkhealth
< <
Plugin authors are encouraged to write new healthchecks. |health-dev| Plugin authors are encouraged to write new healthchecks. |health-dev|
COMMANDS *health-commands* COMMANDS *health-commands*
*:che* *:checkhealth* *:che* *:checkhealth*
@@ -56,7 +56,6 @@ Local mappings in the healthcheck buffer:
q Closes the window. q Closes the window.
Global configuration: Global configuration:
*g:health* *g:health*
g:health Dictionary with the following optional keys: g:health Dictionary with the following optional keys:
- `style` (`'float'|nil`) Set to "float" to display :checkhealth in - `style` (`'float'|nil`) Set to "float" to display :checkhealth in
@@ -65,16 +64,26 @@ g:health Dictionary with the following optional keys:
Example: >lua Example: >lua
vim.g.health = { style = 'float' } vim.g.health = { style = 'float' }
Local configuration:
Checkhealth sets its buffer filetype to "checkhealth". You can customize the
buffer by handling the |FileType| event. For example if you don't want emojis
in the health report: >vim
autocmd FileType checkhealth :set modifiable | silent! %s/\v( ?[^\x00-\x7F])//g
<
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
Create a healthcheck *health-dev* Create a healthcheck *health-dev*
Healthchecks are functions that check the user environment, configuration, or Healthchecks are functions that check the user environment, configuration, or
any other prerequisites that a plugin cares about. Nvim ships with any other prerequisites that a plugin cares about. Nvim ships with
healthchecks in: healthchecks in:
- $VIMRUNTIME/autoload/health/ $VIMRUNTIME/autoload/health/
- $VIMRUNTIME/lua/vim/lsp/health.lua $VIMRUNTIME/lua/vim/lsp/health.lua
- $VIMRUNTIME/lua/vim/treesitter/health.lua $VIMRUNTIME/lua/vim/treesitter/health.lua
- and more... and more...
To add a new healthcheck for your own plugin, simply create a "health.lua" To add a new healthcheck for your own plugin, simply create a "health.lua"
module on 'runtimepath' that returns a table with a "check()" function. Then module on 'runtimepath' that returns a table with a "check()" function. Then
@@ -82,20 +91,19 @@ module on 'runtimepath' that returns a table with a "check()" function. Then
For example if your plugin is named "foo", define your healthcheck module at For example if your plugin is named "foo", define your healthcheck module at
one of these locations (on 'runtimepath'): one of these locations (on 'runtimepath'):
- lua/foo/health/init.lua lua/foo/health/init.lua
- lua/foo/health.lua lua/foo/health.lua
If your plugin also provides a submodule named "bar" for which you want If your plugin also provides a submodule named "bar" for which you want a
a separate healthcheck, define the healthcheck at one of these locations: separate healthcheck, define the healthcheck at one of these locations:
- lua/foo/bar/health/init.lua lua/foo/bar/health/init.lua
- lua/foo/bar/health.lua lua/foo/bar/health.lua
All such health modules must return a Lua table containing a `check()` All such health modules must return a Lua table containing a `check()`
function. function.
Copy this sample code into `lua/foo/health.lua`, replacing "foo" in the path Copy this sample code into `lua/foo/health.lua`, replacing "foo" in the path
with your plugin name: >lua with your plugin name: >lua
local M = {} local M = {}
M.check = function() M.check = function()
@@ -111,6 +119,7 @@ with your plugin name: >lua
end end
return M return M
<
error({msg}, {...}) *vim.health.error()* error({msg}, {...}) *vim.health.error()*

View File

@@ -343,6 +343,8 @@ PLUGINS
• 'commentstring' values can now be specified in a Treesitter capture's • 'commentstring' values can now be specified in a Treesitter capture's
`bo.commentstring` metadata field, providing finer grained support for `bo.commentstring` metadata field, providing finer grained support for
languages like `JSX`. languages like `JSX`.
• Customize :checkhealth by handling a `FileType checkhealth` event.
|health-usage|
STARTUP STARTUP
@@ -437,6 +439,7 @@ UI
• |ui-messages| content chunks now also contain the highlight group ID. • |ui-messages| content chunks now also contain the highlight group ID.
• |:checkhealth| can display in a floating window, controlled by the • |:checkhealth| can display in a floating window, controlled by the
|g:health| variable. |g:health| variable.
• |:checkhealth| shows a summary in the header for every healthcheck.
VIMSCRIPT VIMSCRIPT

View File

@@ -1,16 +1,16 @@
--- @brief --- @brief
---<pre>help
--- vim.health is a minimal framework to help users troubleshoot configuration and
--- any other environment conditions that a plugin might care about. Nvim ships
--- with healthchecks for configuration, performance, python support, ruby
--- support, clipboard support, and more.
--- ---
--- To run all healthchecks, use: >vim --- vim.health is a minimal framework to help users troubleshoot configuration and any other
--- environment conditions that a plugin might care about. Nvim ships with healthchecks for
--- configuration, performance, python support, ruby support, clipboard support, and more.
--- ---
--- To run all healthchecks, use:
--- ```vim
--- :checkhealth --- :checkhealth
--- < --- ```
--- Plugin authors are encouraged to write new healthchecks. |health-dev| --- Plugin authors are encouraged to write new healthchecks. |health-dev|
--- ---
---<pre>help
--- COMMANDS *health-commands* --- COMMANDS *health-commands*
--- ---
--- *:che* *:checkhealth* --- *:che* *:checkhealth*
@@ -46,7 +46,6 @@
--- q Closes the window. --- q Closes the window.
--- ---
--- Global configuration: --- Global configuration:
---
--- *g:health* --- *g:health*
--- g:health Dictionary with the following optional keys: --- g:health Dictionary with the following optional keys:
--- - `style` (`'float'|nil`) Set to "float" to display :checkhealth in --- - `style` (`'float'|nil`) Set to "float" to display :checkhealth in
@@ -55,37 +54,48 @@
--- Example: >lua --- Example: >lua
--- vim.g.health = { style = 'float' } --- vim.g.health = { style = 'float' }
--- ---
---</pre>
---
--- Local configuration:
---
--- Checkhealth sets its buffer filetype to "checkhealth". You can customize the buffer by handling
--- the |FileType| event. For example if you don't want emojis in the health report:
--- ```vim
--- autocmd FileType checkhealth :set modifiable | silent! %s/\v( ?[^\x00-\x7F])//g
--- ```
---
---<pre>help
--- -------------------------------------------------------------------------------- --- --------------------------------------------------------------------------------
--- Create a healthcheck *health-dev* --- Create a healthcheck *health-dev*
---</pre>
--- ---
--- Healthchecks are functions that check the user environment, configuration, or --- Healthchecks are functions that check the user environment, configuration, or any other
--- any other prerequisites that a plugin cares about. Nvim ships with --- prerequisites that a plugin cares about. Nvim ships with healthchecks in:
--- healthchecks in:
--- - $VIMRUNTIME/autoload/health/ --- - $VIMRUNTIME/autoload/health/
--- - $VIMRUNTIME/lua/vim/lsp/health.lua --- - $VIMRUNTIME/lua/vim/lsp/health.lua
--- - $VIMRUNTIME/lua/vim/treesitter/health.lua --- - $VIMRUNTIME/lua/vim/treesitter/health.lua
--- - and more... --- - and more...
--- ---
--- To add a new healthcheck for your own plugin, simply create a "health.lua" --- To add a new healthcheck for your own plugin, simply create a "health.lua" module on
--- module on 'runtimepath' that returns a table with a "check()" function. Then --- 'runtimepath' that returns a table with a "check()" function. Then |:checkhealth| will
--- |:checkhealth| will automatically find and invoke the function. --- automatically find and invoke the function.
--- ---
--- For example if your plugin is named "foo", define your healthcheck module at --- For example if your plugin is named "foo", define your healthcheck module at
--- one of these locations (on 'runtimepath'): --- one of these locations (on 'runtimepath'):
--- - lua/foo/health/init.lua --- - lua/foo/health/init.lua
--- - lua/foo/health.lua --- - lua/foo/health.lua
--- ---
--- If your plugin also provides a submodule named "bar" for which you want --- If your plugin also provides a submodule named "bar" for which you want a separate healthcheck,
--- a separate healthcheck, define the healthcheck at one of these locations: --- define the healthcheck at one of these locations:
--- - lua/foo/bar/health/init.lua --- - lua/foo/bar/health/init.lua
--- - lua/foo/bar/health.lua --- - lua/foo/bar/health.lua
--- ---
--- All such health modules must return a Lua table containing a `check()` --- All such health modules must return a Lua table containing a `check()` function.
--- function.
--- ---
--- Copy this sample code into `lua/foo/health.lua`, replacing "foo" in the path --- Copy this sample code into `lua/foo/health.lua`, replacing "foo" in the path with your plugin
--- with your plugin name: >lua --- name:
--- ---
--- ```lua
--- local M = {} --- local M = {}
--- ---
--- M.check = function() --- M.check = function()
@@ -101,11 +111,12 @@
--- end --- end
--- ---
--- return M --- return M
---</pre> --- ```
local M = {} local M = {}
local s_output = {} ---@type string[] local s_output = {} ---@type string[]
local check_summary = { warn = 0, error = 0 }
-- From a path return a list [{name}, {func}, {type}] representing a healthcheck -- From a path return a list [{name}, {func}, {type}] representing a healthcheck
local function filepath_to_healthcheck(path) local function filepath_to_healthcheck(path)
@@ -286,6 +297,7 @@ end
function M.warn(msg, ...) function M.warn(msg, ...)
local input = format_report_message('⚠️ WARNING', msg, ...) local input = format_report_message('⚠️ WARNING', msg, ...)
collect_output(input) collect_output(input)
check_summary['warn'] = check_summary['warn'] + 1
end end
--- Reports an error. --- Reports an error.
@@ -295,6 +307,7 @@ end
function M.error(msg, ...) function M.error(msg, ...)
local input = format_report_message('❌ ERROR', msg, ...) local input = format_report_message('❌ ERROR', msg, ...)
collect_output(input) collect_output(input)
check_summary['error'] = check_summary['error'] + 1
end end
local path2name = function(path) local path2name = function(path)
@@ -341,6 +354,23 @@ M._complete = function()
return rv return rv
end end
--- Gets the results heading for the current report section.
---
---@return string
local function get_summary()
local s = ''
local errors = check_summary['error']
local warns = check_summary['warn']
s = s .. (warns > 0 and (' %2d ⚠️'):format(warns) or '')
s = s .. (errors > 0 and (' %2d ❌'):format(errors) or '')
if errors == 0 and warns == 0 then
s = s .. ''
end
return s
end
--- Runs the specified healthchecks. --- Runs the specified healthchecks.
--- Runs all discovered healthchecks if plugin_names is empty. --- Runs all discovered healthchecks if plugin_names is empty.
--- ---
@@ -385,7 +415,6 @@ function M._check(mods, plugin_names)
vim.cmd.bwipe('health://') vim.cmd.bwipe('health://')
end end
vim.cmd.file('health://') vim.cmd.file('health://')
vim.cmd.setfiletype('checkhealth')
-- This should only happen when doing `:checkhealth vim` -- This should only happen when doing `:checkhealth vim`
if next(healthchecks) == nil then if next(healthchecks) == nil then
@@ -399,9 +428,9 @@ function M._check(mods, plugin_names)
local func = value[1] local func = value[1]
local type = value[2] local type = value[2]
s_output = {} s_output = {}
check_summary = { warn = 0, error = 0 }
if func == '' then if func == '' then
s_output = {}
M.error('No healthcheck found for "' .. name .. '" plugin.') M.error('No healthcheck found for "' .. name .. '" plugin.')
end end
if type == 'v' then if type == 'v' then
@@ -422,10 +451,12 @@ function M._check(mods, plugin_names)
M.error('The healthcheck report for "' .. name .. '" plugin is empty.') M.error('The healthcheck report for "' .. name .. '" plugin is empty.')
end end
local report = get_summary()
local replen = vim.fn.strwidth(report)
local header = { local header = {
string.rep('=', 78), string.rep('=', 78),
-- Example: `foo.health: [ …] require("foo.health").check()` -- Example: `foo.health: [ …] 1 ⚠️ 5 ❌`
('%s: %s%s'):format(name, (' '):rep(76 - name:len() - func:len()), func), ('%s: %s%s'):format(name, (' '):rep(76 - name:len() - replen), report),
'', '',
} }
@@ -463,6 +494,7 @@ function M._check(mods, plugin_names)
-- Once we're done writing checks, set nomodifiable. -- Once we're done writing checks, set nomodifiable.
vim.bo[bufnr].modifiable = false vim.bo[bufnr].modifiable = false
vim.cmd.setfiletype('checkhealth')
end end
return M return M

View File

@@ -78,6 +78,15 @@ describe(':checkhealth', function()
]]) ]])
) )
end) end)
it("vim.provider works with a misconfigured 'shell'", function()
clear()
command([[set shell=echo\ WRONG!!!]])
command('let g:loaded_perl_provider = 0')
command('let g:loaded_python3_provider = 0')
command('checkhealth vim.provider')
eq(nil, string.match(curbuf_contents(), 'WRONG!!!'))
end)
end) end)
describe('vim.health', function() describe('vim.health', function()
@@ -91,7 +100,7 @@ describe('vim.health', function()
n.expect([[ n.expect([[
============================================================================== ==============================================================================
test_plug.full_render: require("test_plug.full_render.health").check() test_plug.full_render: 1 ⚠️ 1 ❌
report 1 ~ report 1 ~
- ✅ OK life is fine - ✅ OK life is fine
@@ -109,12 +118,39 @@ describe('vim.health', function()
]]) ]])
end) end)
it('user FileType handler can modify report', function()
-- Define a FileType autocmd that removes emoji chars.
source [[
autocmd FileType checkhealth :set modifiable | silent! %s/\v( ?[^\x00-\x7F])//g
checkhealth full_render
]]
n.expect([[
==============================================================================
test_plug.full_render: 1 1
report 1 ~
- OK life is fine
- WARNING no what installed
- ADVICE:
- pip what
- make what
report 2 ~
- stuff is stable
- ERROR why no hardcopy
- ADVICE:
- :help |:hardcopy|
- :help |:TOhtml|
]])
end)
it('concatenates multiple reports', function() it('concatenates multiple reports', function()
command('checkhealth success1 success2 test_plug') command('checkhealth success1 success2 test_plug')
n.expect([[ n.expect([[
============================================================================== ==============================================================================
test_plug: require("test_plug.health").check() test_plug:
report 1 ~ report 1 ~
- ✅ OK everything is fine - ✅ OK everything is fine
@@ -123,7 +159,7 @@ describe('vim.health', function()
- ✅ OK nothing to see here - ✅ OK nothing to see here
============================================================================== ==============================================================================
test_plug.success1: require("test_plug.success1.health").check() test_plug.success1:
report 1 ~ report 1 ~
- ✅ OK everything is fine - ✅ OK everything is fine
@@ -132,7 +168,7 @@ describe('vim.health', function()
- ✅ OK nothing to see here - ✅ OK nothing to see here
============================================================================== ==============================================================================
test_plug.success2: require("test_plug.success2.health").check() test_plug.success2:
another 1 ~ another 1 ~
- ✅ OK ok - ✅ OK ok
@@ -144,7 +180,7 @@ describe('vim.health', function()
n.expect([[ n.expect([[
============================================================================== ==============================================================================
test_plug.submodule: require("test_plug.submodule.health").check() test_plug.submodule:
report 1 ~ report 1 ~
- ✅ OK everything is fine - ✅ OK everything is fine
@@ -159,7 +195,7 @@ describe('vim.health', function()
n.expect([[ n.expect([[
============================================================================== ==============================================================================
test_plug.submodule_empty: require("test_plug.submodule_empty.health").check() test_plug.submodule_empty: 1 ❌
- ❌ ERROR The healthcheck report for "test_plug.submodule_empty" plugin is empty. - ❌ ERROR The healthcheck report for "test_plug.submodule_empty" plugin is empty.
]]) ]])
@@ -185,7 +221,7 @@ describe('vim.health', function()
- ❌ {Error:ERROR} No healthcheck found for "foo" plugin. | - ❌ {Error:ERROR} No healthcheck found for "foo" plugin. |
| |
{Bar: }| {Bar: }|
{h1:test_plug.success1: require("test_pl}| {h1:test_plug.success1: }|
| |
{h2:report 1} | {h2:report 1} |
- ✅ {Ok:OK} everything is fine | - ✅ {Ok:OK} everything is fine |
@@ -200,7 +236,7 @@ describe('vim.health', function()
n.expect([[ n.expect([[
============================================================================== ==============================================================================
non_existent_healthcheck: non_existent_healthcheck: 1 ❌
- ❌ ERROR No healthcheck found for "non_existent_healthcheck" plugin. - ❌ ERROR No healthcheck found for "non_existent_healthcheck" plugin.
]]) ]])
@@ -218,7 +254,7 @@ describe('vim.health', function()
n.expect([[ n.expect([[
============================================================================== ==============================================================================
test_plug.lua: require("test_plug.lua.health").check() test_plug.lua:
nested lua/ directory ~ nested lua/ directory ~
- ✅ OK everything is ok - ✅ OK everything is ok
@@ -236,7 +272,7 @@ describe('vim.health', function()
n.expect([[ n.expect([[
============================================================================== ==============================================================================
nest: require("nest.health").check() nest:
healthy pack ~ healthy pack ~
- ✅ OK healthy ok - ✅ OK healthy ok
@@ -245,17 +281,6 @@ describe('vim.health', function()
end) end)
end) end)
describe(':checkhealth vim.provider', function()
it("works correctly with a wrongly configured 'shell'", function()
clear()
command([[set shell=echo\ WRONG!!!]])
command('let g:loaded_perl_provider = 0')
command('let g:loaded_python3_provider = 0')
command('checkhealth vim.provider')
eq(nil, string.match(curbuf_contents(), 'WRONG!!!'))
end)
end)
describe(':checkhealth window', function() describe(':checkhealth window', function()
before_each(function() before_each(function()
clear { args = { '-u', 'NORC', '+set runtimepath+=test/functional/fixtures' } } clear { args = { '-u', 'NORC', '+set runtimepath+=test/functional/fixtures' } }
@@ -281,14 +306,14 @@ describe(':checkhealth window', function()
^ | ^ |
{14: }| {14: }|
{14: } | {14: } |
{h1:test_plug.success1: }| {h1:test_plug. }|
{h1:require("test_plug.success1.health").check()} | {h1:success1: }|
{h1: ✅} |
| |
{h2:report 1} | {h2:report 1} |
- ✅ {32:OK} everything is fine | - ✅ {32:OK} everything is fine |
| |
{h2:report 2} | {h2:report 2} |
- ✅ {32:OK} nothing to see here |
## grid 3 ## grid 3
| |
]], ]],
@@ -324,8 +349,8 @@ describe(':checkhealth window', function()
{14: } | {14: } |
{h1:test_plug. }| {h1:test_plug. }|
{h1:success1: }| {h1:success1: }|
{h1:require("test_plug. }| {h1: }|
{h1:success1.health").check()}| {h1: ✅} |
| |
{h2:report 1} | {h2:report 1} |
- ✅ {32:OK} everything is | - ✅ {32:OK} everything is |
@@ -383,15 +408,15 @@ describe(':checkhealth window', function()
^ | ^ |
| |
| |
test_plug.success1: | test_plug. |
require("test_plug.success1.health").check() | success1: |
✅ |
| |
report 1 | report 1 |
- ✅ OK everything is fine | - ✅ OK everything is fine |
| |
report 2 | report 2 |
- ✅ OK nothing to see here | - ✅ OK nothing to see here |
|
]]):format( ]]):format(
top top
and [[ and [[