1
0
forked from aniani/vim

patch 8.2.0385: menu functionality insufficiently tested

Problem:    Menu functionality insufficiently tested.
Solution:   Add tests.  Add menu_info(). (Yegappan Lakshmanan, closes #5760)
This commit is contained in:
Bram Moolenaar
2020-03-15 16:13:53 +01:00
parent 5e4d1eba95
commit 0eabd4dc8f
10 changed files with 721 additions and 31 deletions

View File

@@ -1684,6 +1684,49 @@ get_menu_cmd_modes(
return 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 *menu, int mode_idx)
}
/*
* 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,10 +2487,45 @@ ex_emenu(exarg_T *eap)
if (menu == NULL)
{
if (!gave_emsg)
semsg(_("E334: Menu not found: %s"), arg);
return;
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_start)
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