comparison 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
comparison
equal deleted inserted replaced
19656:df9b03a56543 19657:da791e5c0139
1683 *unmenu = (*cmd == 'u'); 1683 *unmenu = (*cmd == 'u');
1684 return modes; 1684 return modes;
1685 } 1685 }
1686 1686
1687 /* 1687 /*
1688 * Return the string representation of the menu modes. Does the opposite
1689 * of get_menu_cmd_modes().
1690 */
1691 static char_u *
1692 get_menu_mode_str(int modes)
1693 {
1694 if ((modes & (MENU_INSERT_MODE | MENU_CMDLINE_MODE | MENU_NORMAL_MODE |
1695 MENU_VISUAL_MODE | MENU_SELECT_MODE | MENU_OP_PENDING_MODE))
1696 == (MENU_INSERT_MODE | MENU_CMDLINE_MODE | MENU_NORMAL_MODE |
1697 MENU_VISUAL_MODE | MENU_SELECT_MODE | MENU_OP_PENDING_MODE))
1698 return (char_u *)"a";
1699 if ((modes & (MENU_NORMAL_MODE | MENU_VISUAL_MODE | MENU_SELECT_MODE |
1700 MENU_OP_PENDING_MODE))
1701 == (MENU_NORMAL_MODE | MENU_VISUAL_MODE | MENU_SELECT_MODE |
1702 MENU_OP_PENDING_MODE))
1703 return (char_u *)" ";
1704 if ((modes & (MENU_INSERT_MODE | MENU_CMDLINE_MODE))
1705 == (MENU_INSERT_MODE | MENU_CMDLINE_MODE))
1706 return (char_u *)"!";
1707 if ((modes & (MENU_VISUAL_MODE | MENU_SELECT_MODE))
1708 == (MENU_VISUAL_MODE | MENU_SELECT_MODE))
1709 return (char_u *)"v";
1710 if (modes & MENU_VISUAL_MODE)
1711 return (char_u *)"x";
1712 if (modes & MENU_SELECT_MODE)
1713 return (char_u *)"s";
1714 if (modes & MENU_OP_PENDING_MODE)
1715 return (char_u *)"o";
1716 if (modes & MENU_INSERT_MODE)
1717 return (char_u *)"i";
1718 if (modes & MENU_TERMINAL_MODE)
1719 return (char_u *)"tl";
1720 if (modes & MENU_CMDLINE_MODE)
1721 return (char_u *)"c";
1722 if (modes & MENU_NORMAL_MODE)
1723 return (char_u *)"n";
1724 if (modes & MENU_TIP_MODE)
1725 return (char_u *)"t";
1726
1727 return (char_u *)"";
1728 }
1729
1730 /*
1688 * Modify a menu name starting with "PopUp" to include the mode character. 1731 * Modify a menu name starting with "PopUp" to include the mode character.
1689 * Returns the name in allocated memory (NULL for failure). 1732 * Returns the name in allocated memory (NULL for failure).
1690 */ 1733 */
1691 static char_u * 1734 static char_u *
1692 popup_mode_name(char_u *name, int idx) 1735 popup_mode_name(char_u *name, int idx)
2391 semsg(_("E335: Menu not defined for %s mode"), mode); 2434 semsg(_("E335: Menu not defined for %s mode"), mode);
2392 } 2435 }
2393 } 2436 }
2394 2437
2395 /* 2438 /*
2396 * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and 2439 * Lookup a menu by the descriptor name e.g. "File.New"
2397 * execute it. 2440 * Returns NULL if the menu is not found
2398 */ 2441 */
2399 void 2442 static vimmenu_T *
2400 ex_emenu(exarg_T *eap) 2443 menu_getbyname(char_u *name_arg)
2401 { 2444 {
2402 vimmenu_T *menu;
2403 char_u *name; 2445 char_u *name;
2404 char_u *saved_name; 2446 char_u *saved_name;
2405 char_u *arg = eap->arg; 2447 vimmenu_T *menu;
2406 char_u *p; 2448 char_u *p;
2407 int gave_emsg = FALSE; 2449 int gave_emsg = FALSE;
2408 int mode_idx = -1; 2450
2409 2451 saved_name = vim_strsave(name_arg);
2410 if (arg[0] && VIM_ISWHITE(arg[1]))
2411 {
2412 switch (arg[0])
2413 {
2414 case 'n': mode_idx = MENU_INDEX_NORMAL; break;
2415 case 'v': mode_idx = MENU_INDEX_VISUAL; break;
2416 case 's': mode_idx = MENU_INDEX_SELECT; break;
2417 case 'o': mode_idx = MENU_INDEX_OP_PENDING; break;
2418 case 't': mode_idx = MENU_INDEX_TERMINAL; break;
2419 case 'i': mode_idx = MENU_INDEX_INSERT; break;
2420 case 'c': mode_idx = MENU_INDEX_CMDLINE; break;
2421 default: semsg(_(e_invarg2), arg);
2422 return;
2423 }
2424 arg = skipwhite(arg + 2);
2425 }
2426
2427 saved_name = vim_strsave(arg);
2428 if (saved_name == NULL) 2452 if (saved_name == NULL)
2429 return; 2453 return NULL;
2430 2454
2431 menu = *get_root_menu(saved_name); 2455 menu = *get_root_menu(saved_name);
2432 name = saved_name; 2456 name = saved_name;
2433 while (*name) 2457 while (*name)
2434 { 2458 {
2461 } 2485 }
2462 vim_free(saved_name); 2486 vim_free(saved_name);
2463 if (menu == NULL) 2487 if (menu == NULL)
2464 { 2488 {
2465 if (!gave_emsg) 2489 if (!gave_emsg)
2466 semsg(_("E334: Menu not found: %s"), arg); 2490 semsg(_("E334: Menu not found: %s"), name_arg);
2491 return NULL;
2492 }
2493
2494 return menu;
2495 }
2496
2497 /*
2498 * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
2499 * execute it.
2500 */
2501 void
2502 ex_emenu(exarg_T *eap)
2503 {
2504 vimmenu_T *menu;
2505 char_u *arg = eap->arg;
2506 int mode_idx = -1;
2507
2508 if (arg[0] && VIM_ISWHITE(arg[1]))
2509 {
2510 switch (arg[0])
2511 {
2512 case 'n': mode_idx = MENU_INDEX_NORMAL; break;
2513 case 'v': mode_idx = MENU_INDEX_VISUAL; break;
2514 case 's': mode_idx = MENU_INDEX_SELECT; break;
2515 case 'o': mode_idx = MENU_INDEX_OP_PENDING; break;
2516 case 't': mode_idx = MENU_INDEX_TERMINAL; break;
2517 case 'i': mode_idx = MENU_INDEX_INSERT; break;
2518 case 'c': mode_idx = MENU_INDEX_CMDLINE; break;
2519 default: semsg(_(e_invarg2), arg);
2520 return;
2521 }
2522 arg = skipwhite(arg + 2);
2523 }
2524
2525 menu = menu_getbyname(arg);
2526 if (menu == NULL)
2467 return; 2527 return;
2468 }
2469 2528
2470 // Found the menu, so execute. 2529 // Found the menu, so execute.
2471 execute_menu(eap, menu, mode_idx); 2530 execute_menu(eap, menu, mode_idx);
2472 } 2531 }
2473 2532
2771 arg = skipwhite(arg); 2830 arg = skipwhite(arg);
2772 2831
2773 return arg; 2832 return arg;
2774 } 2833 }
2775 2834
2835 /*
2836 * Get the information about a menu item in mode 'which'
2837 */
2838 static int
2839 menuitem_getinfo(vimmenu_T *menu, int modes, dict_T *dict)
2840 {
2841 int status;
2842
2843 if (menu_is_tearoff(menu->dname)) // skip tearoff menu item
2844 return OK;
2845
2846 status = dict_add_string(dict, "name", menu->name);
2847 if (status == OK)
2848 status = dict_add_string(dict, "display", menu->dname);
2849 if (status == OK && menu->actext != NULL)
2850 status = dict_add_string(dict, "accel", menu->actext);
2851 if (status == OK)
2852 status = dict_add_number(dict, "priority", menu->priority);
2853 if (status == OK)
2854 status = dict_add_string(dict, "modes",
2855 get_menu_mode_str(menu->modes));
2856 #ifdef FEAT_TOOLBAR
2857 if (status == OK && menu->iconfile != NULL)
2858 status = dict_add_string(dict, "icon", menu->iconfile);
2859 if (status == OK && menu->iconidx >= 0)
2860 status = dict_add_number(dict, "iconidx", menu->iconidx);
2861 #endif
2862 if (status == OK)
2863 {
2864 char_u buf[NUMBUFLEN];
2865
2866 if (has_mbyte)
2867 buf[utf_char2bytes(menu->mnemonic, buf)] = NUL;
2868 else
2869 {
2870 buf[0] = (char_u)menu->mnemonic;
2871 buf[1] = NUL;
2872 }
2873 status = dict_add_string(dict, "shortcut", buf);
2874 }
2875 if (status == OK && menu->children == NULL)
2876 {
2877 int bit;
2878
2879 // Get the first mode in which the menu is available
2880 for (bit = 0; (bit < MENU_MODES) && !((1 << bit) & modes); bit++)
2881 ;
2882 if (menu->strings[bit] != NULL)
2883 status = dict_add_string(dict, "rhs",
2884 *menu->strings[bit] == NUL ?
2885 vim_strsave((char_u *)"<Nop>") :
2886 str2special_save(menu->strings[bit], FALSE));
2887 if (status == OK)
2888 status = dict_add_bool(dict, "noremenu",
2889 menu->noremap[bit] == REMAP_NONE);
2890 if (status == OK)
2891 status = dict_add_bool(dict, "script",
2892 menu->noremap[bit] == REMAP_SCRIPT);
2893 if (status == OK)
2894 status = dict_add_bool(dict, "silent", menu->silent[bit]);
2895 if (status == OK)
2896 status = dict_add_bool(dict, "enabled",
2897 ((menu->enabled & (1 << bit)) != 0));
2898 }
2899 // If there are submenus, add all the submenu display names
2900 if (status == OK && menu->children != NULL)
2901 {
2902 list_T *l = list_alloc();
2903 vimmenu_T *child;
2904
2905 if (l == NULL)
2906 return FAIL;
2907
2908 dict_add_list(dict, "submenus", l);
2909 child = menu->children;
2910 while (child)
2911 {
2912 if (!menu_is_tearoff(child->dname)) // skip tearoff menu
2913 list_append_string(l, child->dname, -1);
2914 child = child->next;
2915 }
2916 }
2917
2918 return status;
2919 }
2920
2921 /*
2922 * "menu_info()" function
2923 * Return information about a menu (including all the child menus)
2924 */
2925 void
2926 f_menu_info(typval_T *argvars, typval_T *rettv)
2927 {
2928 char_u *menu_name;
2929 char_u *which;
2930 int modes;
2931 char_u *saved_name;
2932 char_u *name;
2933 vimmenu_T *menu;
2934 dict_T *retdict;
2935
2936 if (rettv_dict_alloc(rettv) != OK)
2937 return;
2938 retdict = rettv->vval.v_dict;
2939
2940 menu_name = tv_get_string_chk(&argvars[0]);
2941 if (menu_name == NULL)
2942 return;
2943
2944 // menu mode
2945 if (argvars[1].v_type != VAR_UNKNOWN)
2946 which = tv_get_string_chk(&argvars[1]);
2947 else
2948 which = (char_u *)""; // Default is modes for "menu"
2949 if (which == NULL)
2950 return;
2951
2952 modes = get_menu_cmd_modes(which, *which == '!', NULL, NULL);
2953
2954 // Locate the specified menu or menu item
2955 menu = *get_root_menu(menu_name);
2956 saved_name = vim_strsave(menu_name);
2957 if (saved_name == NULL)
2958 return;
2959 if (*saved_name != NUL)
2960 {
2961 char_u *p;
2962
2963 name = saved_name;
2964 while (*name)
2965 {
2966 // Find in the menu hierarchy
2967 p = menu_name_skip(name);
2968 while (menu != NULL)
2969 {
2970 if (menu_name_equal(name, menu))
2971 break;
2972 menu = menu->next;
2973 }
2974 if (menu == NULL || *p == NUL)
2975 break;
2976 menu = menu->children;
2977 name = p;
2978 }
2979 }
2980 vim_free(saved_name);
2981
2982 if (menu == NULL) // specified menu not found
2983 return;
2984
2985 if (menu->modes & modes)
2986 menuitem_getinfo(menu, modes, retdict);
2987 }
2988
2776 #endif // FEAT_MENU 2989 #endif // FEAT_MENU