From 2877672d70e76f71ae1190090b8aea7044d458be Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 25 Dec 2023 10:21:13 +0800 Subject: [PATCH] feat(health): make :checkhealth support more split modifiers (#26731) --- runtime/doc/news.txt | 3 +- runtime/doc/windows.txt | 7 +- runtime/lua/vim/health.lua | 29 ++--- src/nvim/eval.c | 33 ------ src/nvim/ex_docmd.c | 44 +++++++- test/functional/plugin/health_spec.lua | 147 ++++++++++++------------- 6 files changed, 125 insertions(+), 138 deletions(-) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 9b40c4a596..454c9becc5 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -366,7 +366,8 @@ The following changes to existing APIs or features add new behavior. • Attempting to set an invalid keycode option (e.g. `set t_foo=123`) no longer gives an error. -• |:checkhealth| buffer can now be opened in a split window using |:vertical| or |:horizontal|. +• |:checkhealth| buffer can now be opened in a split window using modifiers like + |:vertical|, |:horizontal| and |:botright|. ============================================================================== REMOVED FEATURES *news-removed* diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index d6fce89f23..b71e7c80ab 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -242,9 +242,10 @@ and 'winminwidth' are relevant. *:hor* *:horizontal* :hor[izontal] {cmd} Execute {cmd}. Currently only makes a difference for - `horizontal wincmd =`, which will equalize windows only - horizontally, and |:terminal|, which will open a |terminal| - buffer in a split window. + the following commands: + - `:wincmd =`: equalize windows only horizontally. + - |:terminal|: open a |terminal| buffer in a split window. + - |:checkhealth|: open a healthcheck buffer in a split window. :lefta[bove] {cmd} *:lefta* *:leftabove* :abo[veleft] {cmd} *:abo* *:aboveleft* diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua index 4659ab1694..6c96a703bb 100644 --- a/runtime/lua/vim/health.lua +++ b/runtime/lua/vim/health.lua @@ -266,29 +266,22 @@ M._complete = function() return vim.tbl_keys(unique) end --- Runs the specified healthchecks. --- Runs all discovered healthchecks if plugin_names is empty. --- splitmod controls how the healthcheck window opens: "vertical", "horizontal" or "tab" -function M._check(splitmod, plugin_names) +--- Runs the specified healthchecks. +--- Runs all discovered healthchecks if plugin_names is empty. +--- +--- @param mods string command modifiers that affect splitting a window. +function M._check(mods, plugin_names) local healthchecks = plugin_names == '' and get_healthcheck('*') or get_healthcheck(plugin_names) local emptybuf = vim.fn.bufnr('$') == 1 and vim.fn.getline(1) == '' and 1 == vim.fn.line('$') - local mod = function() - if splitmod == 'vertical' then - return 'vertical sbuffer' - elseif splitmod == 'horizontal' then - return 'horizontal sbuffer' - elseif emptybuf then - -- if this is the default buffer when Nvim starts, open healthcheck directly - return 'buffer' - else - -- if not specified otherwise open healthcheck in a tab - return 'tab sbuffer' - end - end + + -- When no command modifiers are used: + -- - If the current buffer is empty, open healthcheck directly. + -- - If not specified otherwise open healthcheck in a tab. + local buf_cmd = #mods > 0 and (mods .. ' sbuffer') or emptybuf and 'buffer' or 'tab sbuffer' local bufnr = vim.api.nvim_create_buf(true, true) - vim.cmd(mod() .. ' ' .. bufnr) + vim.cmd(buf_cmd .. ' ' .. bufnr) if vim.fn.bufexists('health://') == 1 then vim.cmd.bwipe('health://') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a1ac1de1df..540c10e494 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8810,39 +8810,6 @@ void eval_fmt_source_name_line(char *buf, size_t bufsize) } } -/// ":checkhealth [plugins]" -void ex_checkhealth(exarg_T *eap) -{ - Error err = ERROR_INIT; - MAXSIZE_TEMP_ARRAY(args, 2); - if (cmdmod.cmod_split & WSP_VERT) { - ADD_C(args, CSTR_AS_OBJ("vertical")); - } else if (cmdmod.cmod_split & WSP_HOR) { - ADD_C(args, CSTR_AS_OBJ("horizontal")); - } else { - ADD_C(args, CSTR_AS_OBJ("tab")); - } - ADD_C(args, CSTR_AS_OBJ(eap->arg)); - NLUA_EXEC_STATIC("return vim.health._check(...)", args, &err); - if (!ERROR_SET(&err)) { - return; - } - - 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) { typval_T rettv; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 70c8dc9019..964ddca887 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7386,6 +7386,44 @@ void set_pressedreturn(bool val) ex_pressedreturn = val; } +/// ":checkhealth [plugins]" +static void ex_checkhealth(exarg_T *eap) +{ + Error err = ERROR_INIT; + MAXSIZE_TEMP_ARRAY(args, 2); + + char mods[1024]; + size_t mods_len = 0; + mods[0] = NUL; + + if (cmdmod.cmod_tab > 0 || cmdmod.cmod_split != 0) { + bool multi_mods = false; + mods_len = add_win_cmd_modifiers(mods, &cmdmod, &multi_mods); + assert(mods_len < sizeof(mods)); + } + ADD_C(args, STRING_OBJ(((String){ .data = mods, .size = mods_len }))); + ADD_C(args, CSTR_AS_OBJ(eap->arg)); + + NLUA_EXEC_STATIC("return vim.health._check(...)", args, &err); + if (!ERROR_SET(&err)) { + return; + } + + 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); +} + static void ex_terminal(exarg_T *eap) { char ex_cmd[1024]; @@ -7393,10 +7431,8 @@ static void ex_terminal(exarg_T *eap) if (cmdmod.cmod_tab > 0 || cmdmod.cmod_split != 0) { bool multi_mods = false; - - // ex_cmd must be a null terminated string before passing to add_win_cmd_modifiers - ex_cmd[0] = '\0'; - + // ex_cmd must be a null-terminated string before passing to add_win_cmd_modifiers + ex_cmd[0] = NUL; len = add_win_cmd_modifiers(ex_cmd, &cmdmod, &multi_mods); assert(len < sizeof(ex_cmd)); int result = snprintf(ex_cmd + len, sizeof(ex_cmd) - len, " new"); diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua index 7f23c3e7db..9120655724 100644 --- a/test/functional/plugin/health_spec.lua +++ b/test/functional/plugin/health_spec.lua @@ -6,7 +6,7 @@ local curbuf_contents = helpers.curbuf_contents local command = helpers.command local eq, neq, matches = helpers.eq, helpers.neq, helpers.matches local getcompletion = helpers.funcs.getcompletion -local feed = helpers.feed +local insert = helpers.insert local source = helpers.source local exec_lua = helpers.exec_lua @@ -237,16 +237,19 @@ describe(':checkhealth window', function() ]]} end) - it("opens in vsplit window when no buffer created", function() + local function test_health_vsplit(left, emptybuf, mods) local screen = Screen.new(50, 20) screen:attach({ext_multigrid=true}) - command("vertical checkhealth success1") - screen:expect{grid=[[ + if not emptybuf then + insert('hello') + end + command(mods .. ' checkhealth success1') + screen:expect(([[ ## grid 1 - [4:-------------------------]│[2:------------------------]|*19 + %s [3:--------------------------------------------------]| ## grid 2 - | + %s | ~ |*18 ## grid 3 | @@ -265,21 +268,44 @@ describe(':checkhealth window', function() - OK nothing to see here | | ~ |*4 - ]]} - end) + ]]):format(left + and '[4:-------------------------]│[2:------------------------]|*19' + or '[2:------------------------]│[4:-------------------------]|*19', + emptybuf and ' ' or 'hello') + ) + end - it("opens in split window when no buffer created", function() + for _, mods in ipairs({ 'vertical', 'leftabove vertical', 'topleft vertical' }) do + it(('opens in left vsplit window with :%s and no buffer created'):format(mods), function() + test_health_vsplit(true, true, mods) + end) + it(('opens in left vsplit window with :%s and non-empty buffer'):format(mods), function() + test_health_vsplit(true, false, mods) + end) + end + + for _, mods in ipairs({ 'rightbelow vertical', 'botright vertical' }) do + it(('opens in right vsplit window with :%s and no buffer created'):format(mods), function() + test_health_vsplit(false, true, mods) + end) + it(('opens in right vsplit window with :%s and non-empty buffer'):format(mods), function() + test_health_vsplit(false, false, mods) + end) + end + + local function test_health_split(top, emptybuf, mods) local screen = Screen.new(50, 25) screen:attach({ext_multigrid=true}) - command("horizontal checkhealth success1") - screen:expect{grid=[[ + if not emptybuf then + insert('hello') + end + command(mods .. ' checkhealth success1') + screen:expect(([[ ## grid 1 - [4:--------------------------------------------------]|*12 - health:// | - [2:--------------------------------------------------]|*11 +%s [3:--------------------------------------------------]| ## grid 2 - | + %s | ~ |*10 ## grid 3 | @@ -296,74 +322,37 @@ describe(':checkhealth window', function() report 2 | - OK nothing to see here | | - ]]} - end) - - it("opens in split window", function() - local screen = Screen.new(50, 25) - screen:attach({ext_multigrid=true}) - feed('ihello') - feed('') - command("horizontal checkhealth success1") - screen:expect{grid=[[ - ## grid 1 + ]]):format(top + and [[ [4:--------------------------------------------------]|*12 health:// | + [2:--------------------------------------------------]|*11]] + or ([[ [2:--------------------------------------------------]|*11 - [3:--------------------------------------------------]| - ## grid 2 - hello | - ~ |*10 - ## grid 3 - | - ## grid 4 - ^ | - ──────────────────────────────────────────────────| - ──────────────────────────── | - test_plug.success1: require("test_plug.success1. | - health").check() | - | - report 1 | - - OK everything is fine | - | - report 2 | - - OK nothing to see here | - | - ]]} - end) + [No Name] %s | + [4:--------------------------------------------------]|*12]] + ):format(emptybuf and ' ' or '[+]'), + emptybuf and ' ' or 'hello') + ) + end - it("opens in vsplit window", function() - local screen = Screen.new(50, 25) - screen:attach({ext_multigrid=true}) - feed('ihello') - feed('') - command("vertical checkhealth success1") - screen:expect{grid=[[ - ## grid 1 - [4:-------------------------]│[2:------------------------]|*24 - [3:--------------------------------------------------]| - ## grid 2 - hello | - ~ |*23 - ## grid 3 - | - ## grid 4 - ^ | - ─────────────────────────|*3 - ─── | - test_plug.success1: | - require("test_plug. | - success1.health").check()| - | - report 1 | - - OK everything is fine | - | - report 2 | - - OK nothing to see here | - | - ~ |*9 - ]]} - end) + for _, mods in ipairs({ 'horizontal', 'leftabove', 'topleft' }) do + it(('opens in top split window with :%s and no buffer created'):format(mods), function() + test_health_split(true, true, mods) + end) + it(('opens in top split window with :%s and non-empty buffer'):format(mods), function() + test_health_split(true, false, mods) + end) + end + + for _, mods in ipairs({ 'rightbelow', 'botright' }) do + it(('opens in bottom split window with :%s and no buffer created'):format(mods), function() + test_health_split(false, true, mods) + end) + it(('opens in bottom split window with :%s and non-empty buffer'):format(mods), function() + test_health_split(false, false, mods) + end) + end it("opens in tab", function() -- create an empty buffer called "my_buff"