From f7b1b0595d1be4458a36c4993938d0837a54eaa0 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 5 Jun 2025 09:18:00 +0800 Subject: [PATCH] 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 5e470c7af556a7d02db88f3ba0c80b84715d8129) --- src/nvim/menu.c | 33 +++++----- test/functional/ex_cmds/menu_spec.lua | 88 +++++++++++++++++++++------ 2 files changed, 89 insertions(+), 32 deletions(-) diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 5f14ea1eed..5762e43e94 100644 --- 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 index e73f705a29..4cade5e25e 100644 --- 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) +local test_menus_cmd = [=[ + aunmenu * + + nnoremenu &Test.Test inormal + inoremenu Test.Test insert + vnoremenu Test.Test x + cnoremenu Test.Test cmdmode + menu Test.Nested.test level1 + menu Test.Nested.Nested2 level2 + + nnoremenu