diff options
author | zeertzjq <zeertzjq@outlook.com> | 2022-06-30 17:17:27 +0800 |
---|---|---|
committer | zeertzjq <zeertzjq@outlook.com> | 2022-07-01 10:17:39 +0800 |
commit | 015778a3817178a8fdc1150ef1b0eaa3dde776f1 (patch) | |
tree | 5675fea3870277dd861333ac01dac494d797d7e9 | |
parent | 5551a29d065d8b6632b61f327f22da9166c8f9c6 (diff) | |
download | rneovim-015778a3817178a8fdc1150ef1b0eaa3dde776f1.tar.gz rneovim-015778a3817178a8fdc1150ef1b0eaa3dde776f1.tar.bz2 rneovim-015778a3817178a8fdc1150ef1b0eaa3dde776f1.zip |
vim-patch:8.1.0487: no menus specifically for the terminal window
Problem: No menus specifically for the terminal window.
Solution: Add :tlmenu. (Yee Cheng Chin, closes vim/vim#3439) Add a menu test.
https://github.com/vim/vim/commit/4c5d815256099b50eca2ec5bf8f9aaa67a890211
ADDR_OHTER comes from patch 8.1.1241, which has already been ported.
-rw-r--r-- | runtime/delmenu.vim | 1 | ||||
-rw-r--r-- | runtime/doc/autocmd.txt | 5 | ||||
-rw-r--r-- | runtime/doc/gui.txt | 22 | ||||
-rw-r--r-- | runtime/doc/index.txt | 11 | ||||
-rw-r--r-- | runtime/doc/nvim_terminal_emulator.txt | 3 | ||||
-rw-r--r-- | runtime/doc/usr_42.txt | 3 | ||||
-rw-r--r-- | runtime/menu.vim | 3 | ||||
-rw-r--r-- | runtime/syntax/vim.vim | 2 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 5 | ||||
-rw-r--r-- | src/nvim/ex_cmds.lua | 20 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 3 | ||||
-rw-r--r-- | src/nvim/menu.c | 221 | ||||
-rw-r--r-- | src/nvim/menu.h | 1 | ||||
-rw-r--r-- | src/nvim/popupmnu.c | 2 | ||||
-rw-r--r-- | src/nvim/testdir/test_menu.vim | 34 |
15 files changed, 246 insertions, 90 deletions
diff --git a/runtime/delmenu.vim b/runtime/delmenu.vim index 5c20290152..040cc09aa9 100644 --- a/runtime/delmenu.vim +++ b/runtime/delmenu.vim @@ -5,6 +5,7 @@ " Last Change: 2019 Dec 10 aunmenu * +tlunmenu * unlet! g:did_install_default_menus unlet! g:did_install_syntax_menu diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index bf231044a0..59e5c078a3 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -727,13 +727,14 @@ MenuPopup Just before showing the popup menu (under the right mouse button). Useful for adjusting the menu for what is under the cursor or mouse pointer. - The pattern is matched against a single - character representing the mode: + The pattern is matched against one or two + characters representing the mode: n Normal v Visual o Operator-pending i Insert c Command line + tl Terminal *ModeChanged* ModeChanged After changing the mode. The pattern is matched against `'old_mode:new_mode'`, for diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt index 4205362484..b3b8b2d2c0 100644 --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -195,6 +195,10 @@ the mouse button down on this will pop up a menu containing the item "Big Changes", which is a sub-menu containing the item "Delete All Spaces", which when selected, performs the operation. +To create a menu for terminal mode, use |:tlmenu| instead of |:tmenu| unlike +key mapping (|:tmap|). This is because |:tmenu| is already used for defining +tooltips for menus. See |terminal-input|. + Special characters in a menu name: & The next character is the shortcut key. Make sure each @@ -214,9 +218,9 @@ this menu can be used. The second part is shown as "Open :e". The ":e" is right aligned, and the "O" is underlined, to indicate it is the shortcut. *:am* *:amenu* *:an* *:anoremenu* -The ":amenu" command can be used to define menu entries for all modes at once. -To make the command work correctly, a character is automatically inserted for -some modes: +The ":amenu" command can be used to define menu entries for all modes at once, +expect for Terminal mode. To make the command work correctly, a character is +automatically inserted for some modes: mode inserted appended ~ Normal nothing nothing Visual <C-C> <C-\><C-G> @@ -469,6 +473,16 @@ Executing Menus *execute-menus* insert-mode menu Eg: > :emenu File.Exit +:[range]em[enu] {mode} {menu} Like above, but execute the menu for {mode}: + 'n': |:nmenu| Normal mode + 'v': |:vmenu| Visual mode + 's': |:smenu| Select mode + 'o': |:omenu| Operator-pending mode + 't': |:tlmenu| Terminal mode + 'i': |:imenu| Insert mode + 'c': |:cmenu| Cmdline mode + + You can use :emenu to access useful menu items you may have got used to from GUI mode. See 'wildmenu' for an option that works well with this. See |console-menus| for an example. @@ -547,6 +561,8 @@ See section |42.4| in the user manual. :tu[nmenu] {menupath} Remove a tip for a menu or tool. {only in X11 and Win32 GUI} +Note: To create menus for terminal mode, use |:tlmenu| instead. + When a tip is defined for a menu item, it appears in the command-line area when the mouse is over that item, much like a standard Windows menu hint in the status bar. (Except when Vim is in Command-line mode, when of course diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 279645009b..25b98ae4ab 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1622,17 +1622,20 @@ tag command action ~ |:tjump| :tj[ump] like ":tselect", but jump directly when there is only one match |:tlast| :tl[ast] jump to last matching tag -|:tmapclear| :tmapc[lear] remove all mappings for Terminal-Job mode -|:tmap| :tma[p] like ":map" but for Terminal-Job mode +|:tlmenu| :tlm[enu] add menu for |Terminal-mode| +|:tlnoremenu| :tln[oremenu] like ":noremenu" but for |Terminal-mode| +|:tlunmenu| :tlu[nmenu] remove menu for |Terminal-mode| +|:tmapclear| :tmapc[lear] remove all mappings for |Terminal-mode| +|:tmap| :tma[p] like ":map" but for |Terminal-mode| |:tmenu| :tm[enu] define menu tooltip |:tnext| :tn[ext] jump to next matching tag -|:tnoremap| :tno[remap] like ":noremap" but for Terminal-Job mode +|:tnoremap| :tno[remap] like ":noremap" but for |Terminal-mode| |:topleft| :to[pleft] make split window appear at top or far left |:tprevious| :tp[revious] jump to previous matching tag |:trewind| :tr[ewind] jump to first matching tag |:try| :try execute commands, abort on error or exception |:tselect| :ts[elect] list matching tags and select one -|:tunmap| :tunma[p] like ":unmap" but for Terminal-Job mode +|:tunmap| :tunma[p] like ":unmap" but for |Terminal-mode| |:tunmenu| :tu[nmenu] remove menu tooltip |:undo| :u[ndo] undo last change(s) |:undojoin| :undoj[oin] join next change with previous undo block diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt index a4c25009a9..487ccdb0c5 100644 --- a/runtime/doc/nvim_terminal_emulator.txt +++ b/runtime/doc/nvim_terminal_emulator.txt @@ -80,6 +80,9 @@ To use `ALT+{h,j,k,l}` to navigate windows from any mode: > :nnoremap <A-k> <C-w>k :nnoremap <A-l> <C-w>l +You can also create menus similar to terminal mode mappings, but you have to +use |:tlmenu| instead of |:tmenu|. + Mouse input has the following behavior: - If the program has enabled mouse events, the corresponding events will be diff --git a/runtime/doc/usr_42.txt b/runtime/doc/usr_42.txt index ff3ae7057a..470f4e0fe5 100644 --- a/runtime/doc/usr_42.txt +++ b/runtime/doc/usr_42.txt @@ -150,7 +150,8 @@ like the variations on the ":map" command: :menu! Insert and Command-line mode :imenu Insert mode :cmenu Command-line mode - :amenu All modes + :tlmenu Terminal mode + :amenu All modes (except for Terminal mode) To avoid that the commands of a menu item are being mapped, use the command ":noremenu", ":nnoremenu", ":anoremenu", etc. diff --git a/runtime/menu.vim b/runtime/menu.vim index 956b941971..e20720dbd2 100644 --- a/runtime/menu.vim +++ b/runtime/menu.vim @@ -164,6 +164,9 @@ if exists(':tlmenu') endif nnoremenu 20.360 &Edit.&Paste<Tab>"+gP "+gP cnoremenu &Edit.&Paste<Tab>"+gP <C-R>+ +if exists(':tlmenu') + tlnoremenu &Edit.&Paste<Tab>"+gP <C-W>"+ +endif exe 'vnoremenu <script> &Edit.&Paste<Tab>"+gP ' . paste#paste_cmd['v'] exe 'inoremenu <script> &Edit.&Paste<Tab>"+gP ' . paste#paste_cmd['i'] nnoremenu 20.370 &Edit.Put\ &Before<Tab>[p [p diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 3a5b6b6418..6353a40d6a 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -411,7 +411,7 @@ syn case match " Menus: {{{2 " ===== syn cluster vimMenuList contains=vimMenuBang,vimMenuPriority,vimMenuName,vimMenuMod -syn keyword vimCommand am[enu] an[oremenu] aun[menu] cme[nu] cnoreme[nu] cunme[nu] ime[nu] inoreme[nu] iunme[nu] me[nu] nme[nu] nnoreme[nu] noreme[nu] nunme[nu] ome[nu] onoreme[nu] ounme[nu] unme[nu] vme[nu] vnoreme[nu] vunme[nu] skipwhite nextgroup=@vimMenuList +syn keyword vimCommand am[enu] an[oremenu] aun[menu] cme[nu] cnoreme[nu] cunme[nu] ime[nu] inoreme[nu] iunme[nu] me[nu] nme[nu] nnoreme[nu] noreme[nu] nunme[nu] ome[nu] onoreme[nu] ounme[nu] tlm[enu] tln[oremenu] tlu[nmenu] unme[nu] vme[nu] vnoreme[nu] vunme[nu] skipwhite nextgroup=@vimMenuList syn match vimMenuName "[^ \t\\<]\+" contained nextgroup=vimMenuNameMore,vimMenuMap syn match vimMenuPriority "\d\+\(\.\d\+\)*" contained skipwhite nextgroup=vimMenuName syn match vimMenuNameMore "\c\\\s\|<tab>\|\\\." contained nextgroup=vimMenuName,vimMenuNameMore contains=vimNotation diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index a32d2b10a9..e5142f714e 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1144,8 +1144,9 @@ enum { MENU_INDEX_OP_PENDING = 3, MENU_INDEX_INSERT = 4, MENU_INDEX_CMDLINE = 5, - MENU_INDEX_TIP = 6, - MENU_MODES = 7, + MENU_INDEX_TERMINAL = 6, + MENU_INDEX_TIP = 7, + MENU_MODES = 8, }; typedef struct VimMenu vimmenu_T; diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index cc69921883..69ff37f23c 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -191,7 +191,7 @@ module.cmds = { }, { command='behave', - flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN), + flags=bit.bor(BANG, NEEDARG, WORD1, TRLBAR, CMDWIN), addr_type='ADDR_NONE', func='ex_behave', }, @@ -2879,6 +2879,24 @@ module.cmds = { func='ex_tag', }, { + command='tlmenu', + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', + func='ex_menu', + }, + { + command='tlnoremenu', + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', + func='ex_menu', + }, + { + command='tlunmenu', + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', + func='ex_menu', + }, + { command='tmenu', flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), addr_type='ADDR_OTHER', diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 2ca038e56d..8fe62ae424 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -4060,6 +4060,9 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu: + case CMD_tlmenu: + case CMD_tlnoremenu: + case CMD_tlunmenu: case CMD_tmenu: case CMD_tunmenu: case CMD_popup: diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 73472ff1c4..34df096709 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -38,7 +38,7 @@ #endif /// The character for each menu mode -static char menu_mode_chars[] = { 'n', 'v', 's', 'o', 'i', 'c', 't' }; +static char *menu_mode_chars[] = { "n", "v", "s", "o", "i", "c", "tl", "t" }; static char e_notsubmenu[] = N_("E327: Part of menu-item path is not sub-menu"); static char e_othermode[] = N_("E328: Menu only exists in another mode"); @@ -725,7 +725,7 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes) (menu->noremap[bit] & REMAP_NONE) ? 1 : 0); tv_dict_add_nr(impl, S_LEN("sid"), (menu->noremap[bit] & REMAP_SCRIPT) ? 1 : 0); - tv_dict_add_dict(commands, &menu_mode_chars[bit], 1, impl); + tv_dict_add_dict(commands, menu_mode_chars[bit], 1, impl); } } } else { @@ -861,7 +861,7 @@ static void show_menus_recursive(vimmenu_T *menu, int modes, int depth) for (i = 0; i < depth + 2; i++) { msg_puts(" "); } - msg_putchar(menu_mode_chars[bit]); + msg_puts(menu_mode_chars[bit]); if (menu->noremap[bit] == REMAP_NONE) { msg_putchar('*'); } else if (menu->noremap[bit] == REMAP_SCRIPT) { @@ -1215,6 +1215,11 @@ int get_menu_cmd_modes(const char *cmd, bool forceit, int *noremap, int *unmenu) modes = MENU_INSERT_MODE; break; case 't': + if (*cmd == 'l') { // tlmenu, tlunmenu, tlnoremenu + modes = MENU_TERMINAL_MODE; + cmd++; + break; + } modes = MENU_TIP_MODE; // tmenu break; case 'c': // cmenu @@ -1261,9 +1266,13 @@ static char *popup_mode_name(char *name, int idx) size_t len = STRLEN(name); assert(len >= 4); - char *p = xstrnsave(name, len + 1); - memmove(p + 6, p + 5, len - 4); - p[5] = menu_mode_chars[idx]; + char *mode_chars = menu_mode_chars[idx]; + size_t mode_chars_len = strlen(mode_chars); + char *p = xstrnsave(name, len + mode_chars_len); + memmove(p + 5 + mode_chars_len, p + 5, len - 4); + for (size_t i = 0; i < mode_chars_len; i++) { + p[5 + i] = menu_mode_chars[idx][i]; + } return p; } @@ -1359,9 +1368,13 @@ static int menu_is_hidden(char *name) static int get_menu_mode(void) { + if (State & MODE_TERMINAL) { + return MENU_INDEX_TERMINAL; + } if (VIsual_active) { - if (VIsual_select) + if (VIsual_select) { return MENU_INDEX_SELECT; + } return MENU_INDEX_VISUAL; } if (State & MODE_INSERT) { @@ -1397,21 +1410,19 @@ int get_menu_mode_flag(void) /// etc. void show_popupmenu(void) { - int mode = get_menu_mode(); - if (mode == MENU_INDEX_INVALID) { + int menu_mode = get_menu_mode(); + if (menu_mode == MENU_INDEX_INVALID) { return; } - mode = menu_mode_chars[mode]; + char *mode = menu_mode_chars[menu_mode]; + size_t mode_len = strlen(mode); - char ename[2]; - ename[0] = (char)mode; - ename[1] = NUL; - apply_autocmds(EVENT_MENUPOPUP, ename, NULL, false, curbuf); + apply_autocmds(EVENT_MENUPOPUP, mode, NULL, false, curbuf); vimmenu_T *menu; for (menu = root_menu; menu != NULL; menu = menu->next) { - if (STRNCMP("PopUp", menu->name, 5) == 0 && menu->name[5] == mode) { + if (STRNCMP("PopUp", menu->name, 5) == 0 && STRNCMP(menu->name + 5, mode, mode_len) == 0) { break; } } @@ -1422,72 +1433,71 @@ void show_popupmenu(void) } } -// Execute "menu". Use by ":emenu" and the window toolbar. -// "eap" is NULL for the window toolbar. -void execute_menu(const exarg_T *eap, vimmenu_T *menu) +/// Execute "menu". Use by ":emenu" and the window toolbar. +/// @param eap NULL for the window toolbar. +/// @param mode_idx specify a MENU_INDEX_ value, use -1 to depend on the current state +void execute_menu(const exarg_T *eap, vimmenu_T *menu, int mode_idx) FUNC_ATTR_NONNULL_ARG(2) { - int idx = -1; - char *mode; - - // Use the Insert mode entry when returning to Insert mode. - if (((State & MODE_INSERT) || restart_edit) && !current_sctx.sc_sid) { - mode = "Insert"; - idx = MENU_INDEX_INSERT; - } else if (State & MODE_CMDLINE) { - mode = "Command"; - idx = MENU_INDEX_CMDLINE; - } else if (get_real_state() & MODE_VISUAL) { - // Detect real visual mode -- if we are really in visual mode we - // don't need to do any guesswork to figure out what the selection - // is. Just execute the visual binding for the menu. - mode = "Visual"; - idx = MENU_INDEX_VISUAL; - } else if (eap != NULL && eap->addr_count) { - pos_T tpos; - - mode = "Visual"; - idx = MENU_INDEX_VISUAL; - - // GEDDES: This is not perfect - but it is a - // quick way of detecting whether we are doing this from a - // selection - see if the range matches up with the visual - // select start and end. - if ((curbuf->b_visual.vi_start.lnum == eap->line1) - && (curbuf->b_visual.vi_end.lnum) == eap->line2) { - // Set it up for visual mode - equivalent to gv. - VIsual_mode = curbuf->b_visual.vi_mode; - tpos = curbuf->b_visual.vi_end; - curwin->w_cursor = curbuf->b_visual.vi_start; - curwin->w_curswant = curbuf->b_visual.vi_curswant; - } else { - // Set it up for line-wise visual mode - VIsual_mode = 'V'; - curwin->w_cursor.lnum = eap->line1; - curwin->w_cursor.col = 1; - tpos.lnum = eap->line2; - tpos.col = MAXCOL; - tpos.coladd = 0; - } + int idx = mode_idx; + + if (idx < 0) { + // Use the Insert mode entry when returning to Insert mode. + if (((State & MODE_INSERT) || restart_edit) && !current_sctx.sc_sid) { + idx = MENU_INDEX_INSERT; + } else if (State & MODE_CMDLINE) { + idx = MENU_INDEX_CMDLINE; + } else if (State & MODE_TERMINAL) { + idx = MENU_INDEX_TERMINAL; + } else if (get_real_state() & MODE_VISUAL) { + // Detect real visual mode -- if we are really in visual mode we + // don't need to do any guesswork to figure out what the selection + // is. Just execute the visual binding for the menu. + idx = MENU_INDEX_VISUAL; + } else if (eap != NULL && eap->addr_count) { + pos_T tpos; + + idx = MENU_INDEX_VISUAL; + + // GEDDES: This is not perfect - but it is a + // quick way of detecting whether we are doing this from a + // selection - see if the range matches up with the visual + // select start and end. + if ((curbuf->b_visual.vi_start.lnum == eap->line1) + && (curbuf->b_visual.vi_end.lnum) == eap->line2) { + // Set it up for visual mode - equivalent to gv. + VIsual_mode = curbuf->b_visual.vi_mode; + tpos = curbuf->b_visual.vi_end; + curwin->w_cursor = curbuf->b_visual.vi_start; + curwin->w_curswant = curbuf->b_visual.vi_curswant; + } else { + // Set it up for line-wise visual mode + VIsual_mode = 'V'; + curwin->w_cursor.lnum = eap->line1; + curwin->w_cursor.col = 1; + tpos.lnum = eap->line2; + tpos.col = MAXCOL; + tpos.coladd = 0; + } - // Activate visual mode - VIsual_active = TRUE; - VIsual_reselect = TRUE; - check_cursor(); - VIsual = curwin->w_cursor; - curwin->w_cursor = tpos; + // Activate visual mode + VIsual_active = true; + VIsual_reselect = true; + check_cursor(); + VIsual = curwin->w_cursor; + curwin->w_cursor = tpos; - check_cursor(); + check_cursor(); - // Adjust the cursor to make sure it is in the correct pos - // for exclusive mode - if (*p_sel == 'e' && gchar_cursor() != NUL) { - curwin->w_cursor.col++; + // Adjust the cursor to make sure it is in the correct pos + // for exclusive mode + if (*p_sel == 'e' && gchar_cursor() != NUL) { + curwin->w_cursor.col++; + } } } if (idx == -1 || eap == NULL) { - mode = "Normal"; idx = MENU_INDEX_NORMAL; } @@ -1511,6 +1521,30 @@ void execute_menu(const exarg_T *eap, vimmenu_T *menu) menu->silent[idx]); } } else if (eap != NULL) { + char *mode; + switch (idx) { + case MENU_INDEX_VISUAL: + mode = "Visual"; + break; + case MENU_INDEX_SELECT: + mode = "Select"; + break; + case MENU_INDEX_OP_PENDING: + mode = "Op-pending"; + break; + case MENU_INDEX_TERMINAL: + mode = "Terminal"; + break; + case MENU_INDEX_INSERT: + mode = "Insert"; + break; + case MENU_INDEX_CMDLINE: + mode = "Cmdline"; + break; + // case MENU_INDEX_TIP: cannot happen + default: + mode = "Normal"; + } semsg(_("E335: Menu not defined for %s mode"), mode); } } @@ -1519,9 +1553,43 @@ void execute_menu(const exarg_T *eap, vimmenu_T *menu) // execute it. void ex_emenu(exarg_T *eap) { - char *saved_name = xstrdup(eap->arg); + char *arg = eap->arg; + int mode_idx = -1; + + if (arg[0] && ascii_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); + } + + char *saved_name = xstrdup(arg); vimmenu_T *menu = *get_root_menu(saved_name); char *name = saved_name; + bool gave_emsg = false; while (*name) { // Find in the menu hierarchy char *p = menu_name_skip(name); @@ -1530,6 +1598,7 @@ void ex_emenu(exarg_T *eap) if (menu_name_equal(name, menu)) { if (*p == NUL && menu->children != NULL) { emsg(_("E333: Menu path must lead to a menu item")); + gave_emsg = true; menu = NULL; } else if (*p != NUL && menu->children == NULL) { emsg(_(e_notsubmenu)); @@ -1547,12 +1616,14 @@ void ex_emenu(exarg_T *eap) } xfree(saved_name); if (menu == NULL) { - semsg(_("E334: Menu not found: %s"), eap->arg); + if (!gave_emsg) { + semsg(_("E334: Menu not found: %s"), arg); + } return; } // Found the menu, so execute. - execute_menu(eap, menu); + execute_menu(eap, menu, mode_idx); } /// Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy. diff --git a/src/nvim/menu.h b/src/nvim/menu.h index 5c65918d79..9a60ebfb83 100644 --- a/src/nvim/menu.h +++ b/src/nvim/menu.h @@ -18,6 +18,7 @@ #define MENU_OP_PENDING_MODE (1 << MENU_INDEX_OP_PENDING) #define MENU_INSERT_MODE (1 << MENU_INDEX_INSERT) #define MENU_CMDLINE_MODE (1 << MENU_INDEX_CMDLINE) +#define MENU_TERMINAL_MODE (1 << MENU_INDEX_TERMINAL) #define MENU_TIP_MODE (1 << MENU_INDEX_TIP) #define MENU_ALL_MODES ((1 << MENU_INDEX_TIP) - 1) /// @} diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 087e9bb041..a34faa8fcc 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -989,7 +989,7 @@ static void pum_execute_menu(vimmenu_T *menu, int mode) for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) { if ((mp->modes & mp->enabled & mode) && idx++ == pum_selected) { memset(&ea, 0, sizeof(ea)); - execute_menu(&ea, mp); + execute_menu(&ea, mp, -1); break; } } diff --git a/src/nvim/testdir/test_menu.vim b/src/nvim/testdir/test_menu.vim index de6d4aa359..8ce6868267 100644 --- a/src/nvim/testdir/test_menu.vim +++ b/src/nvim/testdir/test_menu.vim @@ -36,3 +36,37 @@ func Test_translate_menu() source $VIMRUNTIME/delmenu.vim endfunc + +func Test_menu_commands() + nmenu 2 Test.FooBar :let g:did_menu = 'normal'<CR> + vmenu 2 Test.FooBar :let g:did_menu = 'visual'<CR> + smenu 2 Test.FooBar :let g:did_menu = 'select'<CR> + omenu 2 Test.FooBar :let g:did_menu = 'op-pending'<CR> + tlmenu 2 Test.FooBar :let g:did_menu = 'terminal'<CR> + imenu 2 Test.FooBar :let g:did_menu = 'insert'<CR> + cmenu 2 Test.FooBar :let g:did_menu = 'cmdline'<CR> + emenu n Test.FooBar + call assert_equal('normal', g:did_menu) + emenu v Test.FooBar + call assert_equal('visual', g:did_menu) + emenu s Test.FooBar + call assert_equal('select', g:did_menu) + emenu o Test.FooBar + call assert_equal('op-pending', g:did_menu) + emenu t Test.FooBar + call assert_equal('terminal', g:did_menu) + emenu i Test.FooBar + call assert_equal('insert', g:did_menu) + emenu c Test.FooBar + call assert_equal('cmdline', g:did_menu) + + aunmenu Test.FooBar + tlunmenu Test.FooBar + call assert_fails('emenu n Test.FooBar', 'E334:') + + nmenu 2 Test.FooBar.Child :let g:did_menu = 'foobar'<CR> + call assert_fails('emenu n Test.FooBar', 'E333:') + nunmenu Test.FooBar.Child + + unlet g:did_menu +endfun |