diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt index e296141c39..4205362484 100644 --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -577,8 +577,8 @@ a menu item - you don't need to do a :tunmenu as well. 5.9 Popup Menus -In the Win32 GUI, you can cause a menu to popup at the cursor. This behaves -similarly to the PopUp menus except that any menu tree can be popped up. +You can cause a menu to popup at the cursor. This behaves similarly to the +PopUp menus except that any menu tree can be popped up. This command is for backwards compatibility, using it is discouraged, because it behaves in a strange way. @@ -587,7 +587,6 @@ it behaves in a strange way. :popu[p] {name} Popup the menu {name}. The menu named must have at least one subentry, but need not appear on the menu-bar (see |hidden-menus|). - {only available for Win32 GUI} :popu[p]! {name} Like above, but use the position of the mouse pointer instead of the cursor. diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index b18bdefc2a..cc69921883 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -1991,7 +1991,7 @@ module.cmds = { command='popup', flags=bit.bor(NEEDARG, EXTRA, BANG, TRLBAR, NOTRLCOM, CMDWIN), addr_type='ADDR_NONE', - func='ex_ni', + func='ex_popup', }, { command='ppop', diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 4c9c1665fd..2ca038e56d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -62,6 +62,7 @@ #include "nvim/os/time.h" #include "nvim/os_unix.h" #include "nvim/path.h" +#include "nvim/popupmnu.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" #include "nvim/screen.h" @@ -7985,6 +7986,11 @@ static void ex_nogui(exarg_T *eap) eap->errmsg = N_("E25: Nvim does not have a built-in GUI"); } +static void ex_popup(exarg_T *eap) +{ + pum_make_popup(eap->arg, eap->forceit); +} + static void ex_swapname(exarg_T *eap) { if (curbuf->b_ml.ml_mfp == NULL || curbuf->b_ml.ml_mfp->mf_fname == NULL) { diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 5e4dc12e40..73472ff1c4 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -1382,6 +1382,16 @@ static int get_menu_mode(void) return MENU_INDEX_INVALID; } +int get_menu_mode_flag(void) +{ + int mode = get_menu_mode(); + + if (mode == MENU_INDEX_INVALID) { + return 0; + } + return 1 << mode; +} + /// Display the Special "PopUp" menu as a pop-up at the current mouse /// position. The "PopUpn" menu is for Normal mode, "PopUpi" for Insert mode, /// etc. @@ -1545,6 +1555,52 @@ void ex_emenu(exarg_T *eap) execute_menu(eap, menu); } +/// Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy. +vimmenu_T *menu_find(const char *path_name) +{ + vimmenu_T *menu = *get_root_menu(path_name); + char *saved_name = xstrdup(path_name); + char *name = saved_name; + while (*name) { + // find the end of one dot-separated name and put a NUL at the dot + char *p = menu_name_skip(name); + + while (menu != NULL) { + if (menu_name_equal(name, menu)) { + if (menu->children == NULL) { + // found a menu item instead of a sub-menu + if (*p == NUL) { + emsg(_("E336: Menu path must lead to a sub-menu")); + } else { + emsg(_(e_notsubmenu)); + } + menu = NULL; + goto theend; + } + if (*p == NUL) { // found a full match + goto theend; + } + break; + } + menu = menu->next; + } + if (menu == NULL) { // didn't find it + break; + } + + // Found a match, search the sub-menu. + menu = menu->children; + name = p; + } + + if (menu == NULL) { + emsg(_("E337: Menu not found - check menu names")); + } +theend: + xfree(saved_name); + return menu; +} + /* * Translation of menu names. Just a simple lookup table. */ diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index cf87f469b9..82ad409c39 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -1000,9 +1000,12 @@ void pum_show_popupmenu(vimmenu_T *menu) { pum_undisplay(true); pum_size = 0; + int mode = get_menu_mode_flag(); for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) { - pum_size++; + if (menu_is_separator(mp->dname) || (mp->modes & mp->enabled & mode)) { + pum_size++; + } } int idx = 0; @@ -1011,7 +1014,7 @@ void pum_show_popupmenu(vimmenu_T *menu) for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) { if (menu_is_separator(mp->dname)) { array[idx++].pum_text = (char_u *)""; - } else { + } else if (mp->modes & mp->enabled & mode) { array[idx++].pum_text = (char_u *)mp->dname; } } @@ -1079,3 +1082,18 @@ void pum_show_popupmenu(vimmenu_T *menu) xfree(array); pum_undisplay(true); } + +void pum_make_popup(const char *path_name, int use_mouse_pos) +{ + if (!use_mouse_pos) { + // Hack: set mouse position at the cursor so that the menu pops up + // around there. + mouse_row = curwin->w_winrow + curwin->w_wrow; + mouse_col = curwin->w_wincol + curwin->w_wcol; + } + + vimmenu_T *menu = menu_find(path_name); + if (menu != NULL) { + pum_show_popupmenu(menu); + } +} diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 858463efbd..002bcd92a4 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -3748,7 +3748,7 @@ describe('API', function() eq("foo", meths.cmd({ cmd = "Foo" }, { output = true })) end) it('errors if command is not implemented', function() - eq("Command not implemented: popup", pcall_err(meths.cmd, { cmd = "popup" }, {})) + eq("Command not implemented: winpos", pcall_err(meths.cmd, { cmd = "winpos" }, {})) end) it('works with empty arguments list', function() meths.cmd({ cmd = "update" }, {})