Mercurial > vim
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 |