diff src/menu.c @ 19657:da791e5c0139 v8.2.0385

patch 8.2.0385: menu functionality insufficiently tested Commit: https://github.com/vim/vim/commit/0eabd4dc8ff50658f0ea0e92c7918a42242f6b80 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Mar 15 16:13:53 2020 +0100 patch 8.2.0385: menu functionality insufficiently tested Problem: Menu functionality insufficiently tested. Solution: Add tests. Add menu_info(). (Yegappan Lakshmanan, closes https://github.com/vim/vim/issues/5760)
author Bram Moolenaar <Bram@vim.org>
date Sun, 15 Mar 2020 16:15:04 +0100
parents 22f0dda71638
children 897cb43a5c72
line wrap: on
line diff
--- a/src/menu.c
+++ b/src/menu.c
@@ -1685,6 +1685,49 @@ get_menu_cmd_modes(
 }
 
 /*
+ * Return the string representation of the menu modes. Does the opposite
+ * of get_menu_cmd_modes().
+ */
+    static char_u *
+get_menu_mode_str(int modes)
+{
+    if ((modes & (MENU_INSERT_MODE | MENU_CMDLINE_MODE | MENU_NORMAL_MODE |
+		  MENU_VISUAL_MODE | MENU_SELECT_MODE | MENU_OP_PENDING_MODE))
+	    == (MENU_INSERT_MODE | MENU_CMDLINE_MODE | MENU_NORMAL_MODE |
+		MENU_VISUAL_MODE | MENU_SELECT_MODE | MENU_OP_PENDING_MODE))
+	return (char_u *)"a";
+    if ((modes & (MENU_NORMAL_MODE | MENU_VISUAL_MODE | MENU_SELECT_MODE |
+		  MENU_OP_PENDING_MODE))
+	    == (MENU_NORMAL_MODE | MENU_VISUAL_MODE | MENU_SELECT_MODE |
+		MENU_OP_PENDING_MODE))
+	return (char_u *)" ";
+    if ((modes & (MENU_INSERT_MODE | MENU_CMDLINE_MODE))
+	    == (MENU_INSERT_MODE | MENU_CMDLINE_MODE))
+	return (char_u *)"!";
+    if ((modes & (MENU_VISUAL_MODE | MENU_SELECT_MODE))
+	    == (MENU_VISUAL_MODE | MENU_SELECT_MODE))
+	return (char_u *)"v";
+    if (modes & MENU_VISUAL_MODE)
+	return (char_u *)"x";
+    if (modes & MENU_SELECT_MODE)
+	return (char_u *)"s";
+    if (modes & MENU_OP_PENDING_MODE)
+	return (char_u *)"o";
+    if (modes & MENU_INSERT_MODE)
+	return (char_u *)"i";
+    if (modes & MENU_TERMINAL_MODE)
+	return (char_u *)"tl";
+    if (modes & MENU_CMDLINE_MODE)
+	return (char_u *)"c";
+    if (modes & MENU_NORMAL_MODE)
+	return (char_u *)"n";
+    if (modes & MENU_TIP_MODE)
+	return (char_u *)"t";
+
+    return (char_u *)"";
+}
+
+/*
  * Modify a menu name starting with "PopUp" to include the mode character.
  * Returns the name in allocated memory (NULL for failure).
  */
@@ -2393,40 +2436,21 @@ execute_menu(exarg_T *eap, vimmenu_T *me
 }
 
 /*
- * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
- * execute it.
+ * Lookup a menu by the descriptor name e.g. "File.New"
+ * Returns NULL if the menu is not found
  */
-    void
-ex_emenu(exarg_T *eap)
+    static vimmenu_T *
+menu_getbyname(char_u *name_arg)
 {
-    vimmenu_T	*menu;
     char_u	*name;
     char_u	*saved_name;
-    char_u	*arg = eap->arg;
+    vimmenu_T	*menu;
     char_u	*p;
     int		gave_emsg = FALSE;
-    int		mode_idx = -1;
 
-    if (arg[0] && VIM_ISWHITE(arg[1]))
-    {
-	switch (arg[0])
-	{
-	    case 'n': mode_idx = MENU_INDEX_NORMAL; break;
-	    case 'v': mode_idx = MENU_INDEX_VISUAL; break;
-	    case 's': mode_idx = MENU_INDEX_SELECT; break;
-	    case 'o': mode_idx = MENU_INDEX_OP_PENDING; break;
-	    case 't': mode_idx = MENU_INDEX_TERMINAL; break;
-	    case 'i': mode_idx = MENU_INDEX_INSERT; break;
-	    case 'c': mode_idx = MENU_INDEX_CMDLINE; break;
-	    default: semsg(_(e_invarg2), arg);
-		     return;
-	}
-	arg = skipwhite(arg + 2);
-    }
-
-    saved_name = vim_strsave(arg);
+    saved_name = vim_strsave(name_arg);
     if (saved_name == NULL)
-	return;
+	return NULL;
 
     menu = *get_root_menu(saved_name);
     name = saved_name;
@@ -2463,9 +2487,44 @@ ex_emenu(exarg_T *eap)
     if (menu == NULL)
     {
 	if (!gave_emsg)
-	    semsg(_("E334: Menu not found: %s"), arg);
+	    semsg(_("E334: Menu not found: %s"), name_arg);
+	return NULL;
+    }
+
+    return menu;
+}
+
+/*
+ * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
+ * execute it.
+ */
+    void
+ex_emenu(exarg_T *eap)
+{
+    vimmenu_T	*menu;
+    char_u	*arg = eap->arg;
+    int		mode_idx = -1;
+
+    if (arg[0] && VIM_ISWHITE(arg[1]))
+    {
+	switch (arg[0])
+	{
+	    case 'n': mode_idx = MENU_INDEX_NORMAL; break;
+	    case 'v': mode_idx = MENU_INDEX_VISUAL; break;
+	    case 's': mode_idx = MENU_INDEX_SELECT; break;
+	    case 'o': mode_idx = MENU_INDEX_OP_PENDING; break;
+	    case 't': mode_idx = MENU_INDEX_TERMINAL; break;
+	    case 'i': mode_idx = MENU_INDEX_INSERT; break;
+	    case 'c': mode_idx = MENU_INDEX_CMDLINE; break;
+	    default: semsg(_(e_invarg2), arg);
+		     return;
+	}
+	arg = skipwhite(arg + 2);
+    }
+
+    menu = menu_getbyname(arg);
+    if (menu == NULL)
 	return;
-    }
 
     // Found the menu, so execute.
     execute_menu(eap, menu, mode_idx);
@@ -2773,4 +2832,158 @@ menu_translate_tab_and_shift(char_u *arg
     return arg;
 }
 
+/*
+ * Get the information about a menu item in mode 'which'
+ */
+    static int
+menuitem_getinfo(vimmenu_T *menu, int modes, dict_T *dict)
+{
+    int		status;
+
+    if (menu_is_tearoff(menu->dname))		// skip tearoff menu item
+	return OK;
+
+    status = dict_add_string(dict, "name", menu->name);
+    if (status == OK)
+	status = dict_add_string(dict, "display", menu->dname);
+    if (status == OK && menu->actext != NULL)
+	status = dict_add_string(dict, "accel", menu->actext);
+    if (status == OK)
+	status = dict_add_number(dict, "priority", menu->priority);
+    if (status == OK)
+	status = dict_add_string(dict, "modes",
+					get_menu_mode_str(menu->modes));
+#ifdef FEAT_TOOLBAR
+    if (status == OK && menu->iconfile != NULL)
+	status = dict_add_string(dict, "icon", menu->iconfile);
+    if (status == OK && menu->iconidx >= 0)
+	status = dict_add_number(dict, "iconidx", menu->iconidx);
+#endif
+    if (status == OK)
+    {
+	char_u	buf[NUMBUFLEN];
+
+	if (has_mbyte)
+	    buf[utf_char2bytes(menu->mnemonic, buf)] = NUL;
+	else
+	{
+	    buf[0] = (char_u)menu->mnemonic;
+	    buf[1] = NUL;
+	}
+	status = dict_add_string(dict, "shortcut", buf);
+    }
+    if (status == OK && menu->children == NULL)
+    {
+	int		bit;
+
+	// Get the first mode in which the menu is available
+	for (bit = 0; (bit < MENU_MODES) && !((1 << bit) & modes); bit++)
+	    ;
+	if (menu->strings[bit] != NULL)
+	    status = dict_add_string(dict, "rhs",
+		    *menu->strings[bit] == NUL ?
+		    vim_strsave((char_u *)"<Nop>") :
+		    str2special_save(menu->strings[bit], FALSE));
+	if (status == OK)
+	    status = dict_add_bool(dict, "noremenu",
+					menu->noremap[bit] == REMAP_NONE);
+	if (status == OK)
+	    status = dict_add_bool(dict, "script",
+					menu->noremap[bit] == REMAP_SCRIPT);
+	if (status == OK)
+	    status = dict_add_bool(dict, "silent", menu->silent[bit]);
+	if (status == OK)
+	    status = dict_add_bool(dict, "enabled",
+		    ((menu->enabled & (1 << bit)) != 0));
+    }
+    // If there are submenus, add all the submenu display names
+    if (status == OK && menu->children != NULL)
+    {
+	list_T		*l = list_alloc();
+	vimmenu_T	*child;
+
+	if (l == NULL)
+	    return FAIL;
+
+	dict_add_list(dict, "submenus", l);
+	child = menu->children;
+	while (child)
+	{
+	    if (!menu_is_tearoff(child->dname))		// skip tearoff menu
+		list_append_string(l, child->dname, -1);
+	    child = child->next;
+	}
+    }
+
+    return status;
+}
+
+/*
+ * "menu_info()" function
+ * Return information about a menu (including all the child menus)
+ */
+    void
+f_menu_info(typval_T *argvars, typval_T *rettv)
+{
+    char_u	*menu_name;
+    char_u	*which;
+    int		modes;
+    char_u	*saved_name;
+    char_u	*name;
+    vimmenu_T	*menu;
+    dict_T	*retdict;
+
+    if (rettv_dict_alloc(rettv) != OK)
+	return;
+    retdict = rettv->vval.v_dict;
+
+    menu_name = tv_get_string_chk(&argvars[0]);
+    if (menu_name == NULL)
+	return;
+
+    // menu mode
+    if (argvars[1].v_type != VAR_UNKNOWN)
+	which = tv_get_string_chk(&argvars[1]);
+    else
+	which = (char_u *)"";	    // Default is modes for "menu"
+    if (which == NULL)
+	return;
+
+    modes = get_menu_cmd_modes(which, *which == '!', NULL, NULL);
+
+    // Locate the specified menu or menu item
+    menu = *get_root_menu(menu_name);
+    saved_name = vim_strsave(menu_name);
+    if (saved_name == NULL)
+	return;
+    if (*saved_name != NUL)
+    {
+	char_u	*p;
+
+	name = saved_name;
+	while (*name)
+	{
+	    // Find in the menu hierarchy
+	    p = menu_name_skip(name);
+	    while (menu != NULL)
+	    {
+		if (menu_name_equal(name, menu))
+		    break;
+		menu = menu->next;
+	    }
+	    if (menu == NULL || *p == NUL)
+		break;
+	    menu = menu->children;
+	    name = p;
+	}
+    }
+    vim_free(saved_name);
+
+    if (menu == NULL)		// specified menu not found
+	return;
+
+    if (menu->modes & modes)
+	menuitem_getinfo(menu, modes, retdict);
+}
+
 #endif // FEAT_MENU