commit 5e470c7af556a7d02db88f3ba0c80b84715d8129
parent b16dc698cf445635fe094fc3482d1647341ed952
Author: zeertzjq <zeertzjq@outlook.com>
Date: Thu, 5 Jun 2025 09:18:00 +0800
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.
Diffstat:
2 files changed, 88 insertions(+), 31 deletions(-)
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
@@ -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;
}
diff --git a/test/functional/ex_cmds/menu_spec.lua b/test/functional/ex_cmds/menu_spec.lua
@@ -59,26 +59,80 @@ describe(':emenu', function()
end)
end)
-describe('menu_get', function()
+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([=[
- 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
+ command(test_menus_cmd)
+ end)
- nnoremenu <script> Export.Script p
- tmenu Export.Script This is the tooltip
- menu ]Export.hidden thisoneshouldbehidden
+ 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)
- nnoremenu Edit.Paste p
- cnoremenu Edit.Paste <C-R>"
- ]=])
+describe('menu_get', function()
+ before_each(function()
+ clear()
+ command(test_menus_cmd)
end)
it("path='', modes='a'", function()