fix(menu): fix listing of submenus (#34315)

Problem:  Listing submenus with :menu doesn't work.
Solution: Don't go to the parent of the return value of find_menu(), and
          handle empty path at the caller.

Related #8194, which actually only fixed the problem for menu_get(), not
for :menu Ex command.

(cherry picked from commit 5e470c7af5)
This commit is contained in:
zeertzjq
2025-06-05 09:18:00 +08:00
committed by github-actions[bot]
parent 25a869a097
commit f7b1b0595d
2 changed files with 89 additions and 32 deletions

View File

@ -708,9 +708,12 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
/// @return false if could not find path_name
bool menu_get(char *const path_name, int modes, list_T *list)
{
vimmenu_T *menu = find_menu(*get_root_menu(path_name), path_name, modes);
if (!menu) {
return false;
vimmenu_T *menu = *get_root_menu(path_name);
if (*path_name != NUL) {
menu = find_menu(menu, path_name, modes);
if (!menu) {
return false;
}
}
for (; menu != NULL; menu = menu->next) {
dict_T *d = menu_get_recursive(menu, modes);
@ -726,13 +729,15 @@ bool menu_get(char *const path_name, int modes, list_T *list)
return true;
}
/// Find menu matching `name` and `modes`.
/// Find menu matching `name` and `modes`. Does not handle empty `name`.
///
/// @param menu top menu to start looking from
/// @param name path towards the menu
/// @return menu if \p name is null, found menu or NULL
/// @return found menu or NULL
static vimmenu_T *find_menu(vimmenu_T *menu, char *name, int modes)
{
assert(*name);
while (*name) {
// find the end of one dot-separated name and put a NUL at the dot
char *p = menu_name_skip(name);
@ -759,31 +764,29 @@ static vimmenu_T *find_menu(vimmenu_T *menu, char *name, int modes)
}
// Found a match, search the sub-menu.
name = p;
assert(*name);
menu = menu->children;
}
return menu;
abort();
}
/// Show the mapping associated with a menu item or hierarchy in a sub-menu.
static int show_menus(char *const path_name, int modes)
{
vimmenu_T *menu = *get_root_menu(path_name);
if (menu != NULL) {
vimmenu_T *menu = NULL;
if (*path_name != NUL) {
// First, find the (sub)menu with the given name
menu = find_menu(menu, path_name, modes);
menu = find_menu(*get_root_menu(path_name), path_name, modes);
if (menu == NULL) {
return FAIL;
}
}
// When there are no menus at all, the title still needs to be shown.
// Now we have found the matching menu, and we list the mappings
// Highlight title
// Now we have found the matching menu, and we list the mappings.
msg_puts_title(_("\n--- Menus ---"));
show_menus_recursive(menu, modes, 0);
if (menu != NULL) {
show_menus_recursive(menu->parent, modes, 0);
}
return OK;
}

View File

@ -59,26 +59,80 @@ describe(':emenu', function()
end)
end)
local test_menus_cmd = [=[
aunmenu *
nnoremenu &Test.Test inormal<ESC>
inoremenu Test.Test insert
vnoremenu Test.Test x
cnoremenu Test.Test cmdmode
menu Test.Nested.test level1
menu Test.Nested.Nested2 level2
nnoremenu <script> Export.Script p
tmenu Export.Script This is the tooltip
menu ]Export.hidden thisoneshouldbehidden
nnoremenu Edit.Paste p
cnoremenu Edit.Paste <C-R>"
]=]
describe(':menu listing', function()
before_each(function()
clear()
command(test_menus_cmd)
end)
it('matches by path argument', function()
eq(
[[
--- Menus ---
500 Edit
500 Paste
c* <C-R>"]],
n.exec_capture('cmenu Edit')
)
eq(
[[
--- Menus ---
500 &Test
500 Test
n* inormal<Esc>
500 Nested
500 test
n level1
500 Nested2
n level2]],
n.exec_capture('nmenu Test')
)
eq(
[[
--- Menus ---
500 Nested
500 test
o level1
500 Nested2
o level2]],
n.exec_capture('omenu Test.Nested')
)
eq(
[[
--- Menus ---
500 Test
n* inormal<Esc>
v* x
s* x
i* insert
c* cmdmode]],
n.exec_capture('amenu Test.Test')
)
end)
end)
describe('menu_get', function()
before_each(function()
clear()
command([=[
aunmenu *
nnoremenu &Test.Test inormal<ESC>
inoremenu Test.Test insert
vnoremenu Test.Test x
cnoremenu Test.Test cmdmode
menu Test.Nested.test level1
menu Test.Nested.Nested2 level2
nnoremenu <script> Export.Script p
tmenu Export.Script This is the tooltip
menu ]Export.hidden thisoneshouldbehidden
nnoremenu Edit.Paste p
cnoremenu Edit.Paste <C-R>"
]=])
command(test_menus_cmd)
end)
it("path='', modes='a'", function()