Merge #8194 from justinmk/fix-menu

fix ":menu Item.SubItem", fix menu_get("foo")
This commit is contained in:
Justin M. Keyes
2019-01-27 01:32:12 +01:00
committed by GitHub
3 changed files with 78 additions and 55 deletions

View File

@@ -5666,23 +5666,24 @@ max({expr}) Return the maximum value of all items in {expr}.
menu_get({path}, {modes}) *menu_get()* menu_get({path}, {modes}) *menu_get()*
Returns a |List| of |Dictionaries| describing |menus| (defined Returns a |List| of |Dictionaries| describing |menus| (defined
by |:menu|, |:amenu|, etc.). by |:menu|, |:amenu|, …), including |hidden-menus|.
{path} limits the result to a subtree of the menu hierarchy
(empty string matches all menus). E.g. to get items in the {path} matches a menu by name, or all menus if {path} is an
"File" menu subtree: > empty string. Example: >
:echo menu_get('File','') :echo menu_get('File','')
:echo menu_get('')
< <
{modes} is a string of zero or more modes (see |maparg()| or {modes} is a string of zero or more modes (see |maparg()| or
|creating-menus| for the list of modes). "a" means "all". |creating-menus| for the list of modes). "a" means "all".
For example: > Example: >
nnoremenu &Test.Test inormal nnoremenu &Test.Test inormal
inoremenu Test.Test insert inoremenu Test.Test insert
vnoremenu Test.Test x vnoremenu Test.Test x
echo menu_get("") echo menu_get("")
<
returns something like this: < returns something like this: >
>
[ { [ {
"hidden": 0, "hidden": 0,
"name": "Test", "name": "Test",

View File

@@ -660,7 +660,8 @@ static void free_menu_string(vimmenu_T *menu, int idx)
/// ///
/// @param[in] menu if null, starts from root_menu /// @param[in] menu if null, starts from root_menu
/// @param modes, a choice of \ref MENU_MODES /// @param modes, a choice of \ref MENU_MODES
/// @return a dict with name/commands /// @return dict with name/commands
/// @see show_menus_recursive
/// @see menu_get /// @see menu_get
static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes) static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
{ {
@@ -715,10 +716,10 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
// visit recursively all children // visit recursively all children
list_T *const children_list = tv_list_alloc(kListLenMayKnow); list_T *const children_list = tv_list_alloc(kListLenMayKnow);
for (menu = menu->children; menu != NULL; menu = menu->next) { for (menu = menu->children; menu != NULL; menu = menu->next) {
dict_T *dic = menu_get_recursive(menu, modes); dict_T *d = menu_get_recursive(menu, modes);
if (tv_dict_len(dict) > 0) { if (tv_dict_len(d) > 0) {
tv_list_append_dict(children_list, dic); tv_list_append_dict(children_list, d);
} }
} }
tv_dict_add_list(dict, S_LEN("submenus"), children_list); tv_dict_add_list(dict, S_LEN("submenus"), children_list);
} }
@@ -734,42 +735,51 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
/// @return false if could not find path_name /// @return false if could not find path_name
bool menu_get(char_u *const path_name, int modes, list_T *list) bool menu_get(char_u *const path_name, int modes, list_T *list)
{ {
vimmenu_T *menu; vimmenu_T *menu = find_menu(root_menu, path_name, modes);
menu = find_menu(root_menu, path_name, modes);
if (!menu) { if (!menu) {
return false; return false;
} }
for (; menu != NULL; menu = menu->next) { for (; menu != NULL; menu = menu->next) {
dict_T *dict = menu_get_recursive(menu, modes); dict_T *d = menu_get_recursive(menu, modes);
if (dict && tv_dict_len(dict) > 0) { if (d && tv_dict_len(d) > 0) {
tv_list_append_dict(list, dict); tv_list_append_dict(list, d);
}
if (*path_name != NUL) {
// If a (non-empty) path query was given, only the first node in the
// find_menu() result is relevant. Else we want all nodes.
break;
} }
} }
return true; return true;
} }
/// Find menu matching required name and modes /// Find menu matching `name` and `modes`.
/// ///
/// @param menu top menu to start looking from /// @param menu top menu to start looking from
/// @param name path towards the menu /// @param name path towards the menu
/// @return menu if \p name is null, found menu or NULL /// @return menu if \p name is null, found menu or NULL
vimmenu_T * static vimmenu_T *find_menu(vimmenu_T *menu, char_u *name, int modes)
find_menu(vimmenu_T *menu, char_u * name, int modes)
{ {
char_u *p; char_u *p;
while (*name) { while (*name) {
// find the end of one dot-separated name and put a NUL at the dot
p = menu_name_skip(name); p = menu_name_skip(name);
while (menu != NULL) { while (menu != NULL) {
if (menu_name_equal(name, menu)) { if (menu_name_equal(name, menu)) {
/* Found menu */ // Found menu
if (*p != NUL && menu->children == NULL) { if (*p != NUL && menu->children == NULL) {
EMSG(_(e_notsubmenu)); if (*p != NUL) {
return NULL; EMSG(_(e_notsubmenu));
} else if ((menu->modes & modes) == 0x0) { return NULL;
EMSG(_(e_othermode)); } else if ((menu->modes & modes) == 0x0) {
return NULL; EMSG(_(e_othermode));
return NULL;
}
}
if (*p == NUL) { // found a full match
return menu;
} }
break; break;
} }
@@ -780,6 +790,7 @@ find_menu(vimmenu_T *menu, char_u * name, int modes)
EMSG2(_(e_nomenu), name); EMSG2(_(e_nomenu), name);
return NULL; return NULL;
} }
// Found a match, search the sub-menu.
name = p; name = p;
menu = menu->children; menu = menu->children;
} }
@@ -1235,7 +1246,7 @@ static char_u *popup_mode_name(char_u *name, int idx)
/// ///
/// @return a pointer to allocated memory. /// @return a pointer to allocated memory.
static char_u *menu_text(const char_u *str, int *mnemonic, char_u **actext) static char_u *menu_text(const char_u *str, int *mnemonic, char_u **actext)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT
FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(1)
{ {
char_u *p; char_u *p;

View File

@@ -63,25 +63,27 @@ describe('menu_get', function()
before_each(function() before_each(function()
clear() clear()
command('nnoremenu &Test.Test inormal<ESC>') command([=[
command('inoremenu Test.Test insert') nnoremenu &Test.Test inormal<ESC>
command('vnoremenu Test.Test x') inoremenu Test.Test insert
command('cnoremenu Test.Test cmdmode') vnoremenu Test.Test x
command('menu Test.Nested.test level1') cnoremenu Test.Test cmdmode
command('menu Test.Nested.Nested2 level2') menu Test.Nested.test level1
menu Test.Nested.Nested2 level2
command('nnoremenu <script> Export.Script p') nnoremenu <script> Export.Script p
command('tmenu Export.Script This is the tooltip') tmenu Export.Script This is the tooltip
command('menu ]Export.hidden thisoneshouldbehidden') menu ]Export.hidden thisoneshouldbehidden
command('nnoremenu Edit.Paste p') nnoremenu Edit.Paste p
command('cnoremenu Edit.Paste <C-R>"') cnoremenu Edit.Paste <C-R>"
]=])
end) end)
it("path='', modes='a'", function() it("path='', modes='a'", function()
local m = funcs.menu_get("","a"); local m = funcs.menu_get("","a");
-- HINT: To print the expected table and regenerate the tests: -- HINT: To print the expected table and regenerate the tests:
-- print(require('pl.pretty').dump(m)) -- print(require('inspect')(m))
local expected = { local expected = {
{ {
shortcut = "T", shortcut = "T",
@@ -306,10 +308,13 @@ describe('menu_get', function()
eq(expected, m) eq(expected, m)
end) end)
it('matching path, default modes', function() it('matching path, all modes', function()
local m = funcs.menu_get("Export", "a") local m = funcs.menu_get("Export", "a")
local expected = { local expected = { {
{ hidden = 0,
name = "Export",
priority = 500,
submenus = { {
tooltip = "This is the tooltip", tooltip = "This is the tooltip",
hidden = 0, hidden = 0,
name = "Script", name = "Script",
@@ -323,8 +328,8 @@ describe('menu_get', function()
silent = 0 silent = 0
} }
} }
} } }
} } }
eq(expected, m) eq(expected, m)
end) end)
@@ -349,8 +354,6 @@ describe('menu_get', function()
name = "Test", name = "Test",
hidden = 0 hidden = 0
}, },
{
}
}, },
priority = 500, priority = 500,
name = "Test" name = "Test"
@@ -363,14 +366,22 @@ describe('menu_get', function()
local m = funcs.menu_get("Test","i") local m = funcs.menu_get("Test","i")
local expected = { local expected = {
{ {
mappings = { shortcut = "T",
i = { submenus = {
sid = 1, {
noremap = 1, mappings = {
enabled = 1, i = {
rhs = "insert", sid = 1,
silent = 0 noremap = 1,
} enabled = 1,
rhs = "insert",
silent = 0
},
},
priority = 500,
name = "Test",
hidden = 0
},
}, },
priority = 500, priority = 500,
name = "Test", name = "Test",