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 #3439) Add a menu test.
diff --git a/src/ex_cmdidxs.h b/src/ex_cmdidxs.h
index 736cc38..1c435ca 100644
--- a/src/ex_cmdidxs.h
+++ b/src/ex_cmdidxs.h
@@ -25,12 +25,12 @@
   /* r */ 351,
   /* s */ 370,
   /* t */ 437,
-  /* u */ 477,
-  /* v */ 488,
-  /* w */ 506,
-  /* x */ 521,
-  /* y */ 530,
-  /* z */ 531
+  /* u */ 480,
+  /* v */ 491,
+  /* w */ 509,
+  /* x */ 524,
+  /* y */ 533,
+  /* z */ 534
 };
 
 /*
@@ -60,7 +60,7 @@
   /* q */ {  2,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
   /* r */ {  0,  0,  0,  0,  0,  0,  0,  0, 11,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 18,  0,  0,  0,  0 },
   /* s */ {  2,  6, 15,  0, 18, 22,  0, 24, 25,  0,  0, 28, 30, 34, 38, 40,  0, 48,  0, 49,  0, 61, 62,  0, 63,  0 },
-  /* t */ {  2,  0, 19,  0, 22, 24,  0, 25,  0, 26,  0, 27, 28, 31, 33, 34,  0, 35, 37,  0, 38,  0,  0,  0,  0,  0 },
+  /* t */ {  2,  0, 19,  0, 22, 24,  0, 25,  0, 26,  0, 27, 31, 34, 36, 37,  0, 38, 40,  0, 41,  0,  0,  0,  0,  0 },
   /* u */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 10,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
   /* v */ {  0,  0,  0,  0,  1,  0,  0,  0,  4,  0,  0,  0,  9, 12,  0,  0,  0,  0, 15,  0, 16,  0,  0,  0,  0,  0 },
   /* w */ {  2,  0,  0,  0,  0,  0,  0,  3,  4,  0,  0,  0,  0,  8,  0,  9, 10,  0, 12,  0, 13, 14,  0,  0,  0,  0 },
@@ -69,4 +69,4 @@
   /* z */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 }
 };
 
-static const int command_count = 544;
+static const int command_count = 547;
diff --git a/src/ex_cmds.h b/src/ex_cmds.h
index 187efea..c5e0bf4 100644
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -20,9 +20,10 @@
  * 1. Add an entry in the table below.  Keep it sorted on the shortest
  *    version of the command name that works.  If it doesn't start with a
  *    lower case letter, add it at the end.
- * 2. Add a "case: CMD_xxx" in the big switch in ex_docmd.c.
- * 3. Add an entry in the index for Ex commands at ":help ex-cmd-index".
- * 4. Add documentation in ../doc/xxx.txt.  Add a tag for both the short and
+ * 2. Run "make cmdidxs" to re-generate ex_cmdidxs.h.
+ * 3. Add a "case: CMD_xxx" in the big switch in ex_docmd.c.
+ * 4. Add an entry in the index for Ex commands at ":help ex-cmd-index".
+ * 5. Add documentation in ../doc/xxx.txt.  Add a tag for both the short and
  *    long name of the command.
  */
 
@@ -176,7 +177,7 @@
 			BANG|RANGE|NOTADR|BUFNAME|COUNT|EXTRA|TRLBAR,
 			ADDR_BUFFERS),
 EX(CMD_behave,		"behave",	ex_behave,
-			NEEDARG|WORD1|TRLBAR|CMDWIN,
+			BANG|NEEDARG|WORD1|TRLBAR|CMDWIN,
 			ADDR_LINES),
 EX(CMD_belowright,	"belowright",	ex_wrongmodifier,
 			NEEDARG|EXTRA|NOTRLCOM,
@@ -1498,6 +1499,15 @@
 EX(CMD_tlast,		"tlast",	ex_tag,
 			BANG|TRLBAR,
 			ADDR_LINES),
+EX(CMD_tlmenu,		"tlmenu",	ex_menu,
+			RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN,
+			ADDR_LINES),
+EX(CMD_tlnoremenu,	"tlnoremenu",	ex_menu,
+			RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN,
+			ADDR_LINES),
+EX(CMD_tlunmenu,	"tlunmenu",	ex_menu,
+			RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN,
+			ADDR_LINES),
 EX(CMD_tmenu,		"tmenu",	ex_menu,
 			RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN,
 			ADDR_LINES),
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 70acfc8..d00ca56 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -4283,6 +4283,7 @@
 	case CMD_omenu:	    case CMD_onoremenu:	    case CMD_ounmenu:
 	case CMD_imenu:	    case CMD_inoremenu:	    case CMD_iunmenu:
 	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:	    case CMD_tearoff:	    case CMD_emenu:
 	    return set_context_in_menu_cmd(xp, cmd, arg, forceit);
diff --git a/src/menu.c b/src/menu.c
index d253e72..782235a 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -58,7 +58,7 @@
 static char_u *menu_translate_tab_and_shift(char_u *arg_start);
 
 /* The character for each menu mode */
-static char_u	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_u e_notsubmenu[] = N_("E327: Part of menu-item path is not sub-menu");
 static char_u e_othermode[] = N_("E328: Menu only exists in another mode");
@@ -1196,7 +1196,7 @@
 		    return;
 		for (i = 0; i < depth + 2; i++)
 		    MSG_PUTS("  ");
-		msg_putchar(menu_mode_chars[bit]);
+		msg_puts((char_u*)menu_mode_chars[bit]);
 		if (menu->noremap[bit] == REMAP_NONE)
 		    msg_putchar('*');
 		else if (menu->noremap[bit] == REMAP_SCRIPT)
@@ -1645,6 +1645,12 @@
 	    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 */
@@ -1687,12 +1693,18 @@
 {
     char_u	*p;
     int		len = (int)STRLEN(name);
+    char	*mode_chars = menu_mode_chars[idx];
+    int		mode_chars_len = (int)strlen(mode_chars);
+    int		i;
 
-    p = vim_strnsave(name, len + 1);
+    p = vim_strnsave(name, len + mode_chars_len);
     if (p != NULL)
     {
-	mch_memmove(p + 6, p + 5, (size_t)(len - 4));
-	p[5] = menu_mode_chars[idx];
+	mch_memmove(p + 5 + mode_chars_len, p + 5, (size_t)(len - 4));
+	for (i = 0; i < mode_chars_len; ++i)
+	{
+	    p[5 + i] = menu_mode_chars[idx][i];
+	}
     }
     return p;
 }
@@ -1712,6 +1724,10 @@
 	idx = MENU_INDEX_INSERT;
     else if (state & CMDLINE)
 	idx = MENU_INDEX_CMDLINE;
+#ifdef FEAT_TERMINAL
+    else if (term_use_loop())
+	idx = MENU_INDEX_TERMINAL;
+#endif
     else if (VIsual_active)
     {
 	if (VIsual_select)
@@ -1872,6 +1888,12 @@
     static int
 get_menu_mode(void)
 {
+#ifdef FEAT_TERMINAL
+    if (term_use_loop())
+    {
+	return MENU_INDEX_TERMINAL;
+    }
+#endif
     if (VIsual_active)
     {
 	if (VIsual_select)
@@ -1910,23 +1932,20 @@
 show_popupmenu(void)
 {
     vimmenu_T	*menu;
-    int		mode;
+    int		menu_mode;
+    char*	mode;
+    int		mode_len;
 
-    mode = get_menu_mode();
-    if (mode == MENU_INDEX_INVALID)
+    menu_mode = get_menu_mode();
+    if (menu_mode == MENU_INDEX_INVALID)
 	return;
-    mode = menu_mode_chars[mode];
+    mode = menu_mode_chars[menu_mode];
+    mode_len = (int)strlen(mode);
 
-    {
-	char_u	    ename[2];
-
-	ename[0] = mode;
-	ename[1] = NUL;
-	apply_autocmds(EVENT_MENUPOPUP, ename, NULL, FALSE, curbuf);
-    }
+    apply_autocmds(EVENT_MENUPOPUP, (char_u*)mode, NULL, FALSE, curbuf);
 
     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;
 
     /* Only show a popup when it is defined and has entries */
@@ -2249,82 +2268,86 @@
 /*
  * Execute "menu".  Use by ":emenu" and the window toolbar.
  * "eap" is NULL for the window toolbar.
+ * "mode_idx" specifies a MENU_INDEX_ value, use -1 to depend on the current
+ * state.
  */
     void
-execute_menu(exarg_T *eap, vimmenu_T *menu)
+execute_menu(exarg_T *eap, vimmenu_T *menu, int mode_idx)
 {
-    char_u	*mode;
-    int		idx = -1;
+    int		idx = mode_idx;
 
-    /* Use the Insert mode entry when returning to Insert mode. */
-    if (restart_edit
+    if (idx < 0)
+    {
+	/* Use the Insert mode entry when returning to Insert mode. */
+	if (restart_edit
 #ifdef FEAT_EVAL
-	    && !current_sctx.sc_sid
+		&& !current_sctx.sc_sid
 #endif
-	    )
-    {
-	mode = (char_u *)"Insert";
-	idx = MENU_INDEX_INSERT;
-    }
-    else if (VIsual_active)
-    {
-	mode = (char_u *)"Visual";
-	idx = MENU_INDEX_VISUAL;
-    }
-    else if (eap != NULL && eap->addr_count)
-    {
-	pos_T	tpos;
-
-	mode = (char_u *)"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;
+	    idx = MENU_INDEX_INSERT;
 	}
-	else
+#ifdef FEAT_TERMINAL
+	else if (term_use_loop())
 	{
-	    /* 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;
+	    idx = MENU_INDEX_TERMINAL;
+	}
+#endif
+	else if (VIsual_active)
+	{
+	    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;
 #ifdef FEAT_VIRTUALEDIT
-	    tpos.coladd = 0;
+		tpos.coladd = 0;
 #endif
+	    }
+
+	    /* Activate visual mode */
+	    VIsual_active = TRUE;
+	    VIsual_reselect = TRUE;
+	    check_cursor();
+	    VIsual = curwin->w_cursor;
+	    curwin->w_cursor = tpos;
+
+	    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;
 	}
-
-	/* Activate visual mode */
-	VIsual_active = TRUE;
-	VIsual_reselect = TRUE;
-	check_cursor();
-	VIsual = curwin->w_cursor;
-	curwin->w_cursor = tpos;
-
-	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;
     }
 
     /* For the WinBar menu always use the Normal mode menu. */
     if (idx == -1 || eap == NULL)
-    {
-	mode = (char_u *)"Normal";
 	idx = MENU_INDEX_NORMAL;
-    }
 
     if (idx != MENU_INDEX_INVALID && menu->strings[idx] != NULL)
     {
@@ -2351,7 +2374,35 @@
 						     TRUE, menu->silent[idx]);
     }
     else if (eap != NULL)
+    {
+	char_u	*mode;
+
+	switch (idx)
+	{
+	    case MENU_INDEX_VISUAL:
+		mode = (char_u *)"Visual";
+		break;
+	    case MENU_INDEX_SELECT:
+		mode = (char_u *)"Select";
+		break;
+	    case MENU_INDEX_OP_PENDING:
+		mode = (char_u *)"Op-pending";
+		break;
+	    case MENU_INDEX_TERMINAL:
+		mode = (char_u *)"Terminal";
+		break;
+	    case MENU_INDEX_INSERT:
+		mode = (char_u *)"Insert";
+		break;
+	    case MENU_INDEX_CMDLINE:
+		mode = (char_u *)"Cmdline";
+		break;
+	    // case MENU_INDEX_TIP: cannot happen
+	    default:
+		mode = (char_u *)"Normal";
+	}
 	EMSG2(_("E335: Menu not defined for %s mode"), mode);
+    }
 }
 
 /*
@@ -2364,9 +2415,29 @@
     vimmenu_T	*menu;
     char_u	*name;
     char_u	*saved_name;
+    char_u	*arg = eap->arg;
     char_u	*p;
+    int		gave_emsg = FALSE;
+    int		mode_idx = -1;
 
-    saved_name = vim_strsave(eap->arg);
+    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: EMSG2(_(e_invarg2), arg);
+		     return;
+	}
+	arg = skipwhite(arg + 2);
+    }
+
+    saved_name = vim_strsave(arg);
     if (saved_name == NULL)
 	return;
 
@@ -2384,6 +2455,7 @@
 		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)
@@ -2403,12 +2475,13 @@
     vim_free(saved_name);
     if (menu == NULL)
     {
-	EMSG2(_("E334: Menu not found: %s"), eap->arg);
+	if (!gave_emsg)
+	    EMSG2(_("E334: Menu not found: %s"), arg);
 	return;
     }
 
-    /* Found the menu, so execute. */
-    execute_menu(eap, menu);
+    // Found the menu, so execute.
+    execute_menu(eap, menu, mode_idx);
 }
 
 /*
@@ -2445,7 +2518,7 @@
 		check_cursor();
 	    }
 
-	    execute_menu(NULL, item->wb_menu);
+	    execute_menu(NULL, item->wb_menu, -1);
 
 	    if (save_curwin != NULL)
 	    {
diff --git a/src/popupmnu.c b/src/popupmnu.c
index 0f920dc..c481d4b 100644
--- a/src/popupmnu.c
+++ b/src/popupmnu.c
@@ -1176,7 +1176,7 @@
 	if ((mp->modes & mp->enabled & mode) && idx++ == pum_selected)
 	{
 	    vim_memset(&ea, 0, sizeof(ea));
-	    execute_menu(&ea, mp);
+	    execute_menu(&ea, mp, -1);
 	    break;
 	}
 }
diff --git a/src/proto/menu.pro b/src/proto/menu.pro
index 63f8d11..411c040 100644
--- a/src/proto/menu.pro
+++ b/src/proto/menu.pro
@@ -19,7 +19,7 @@
 void gui_update_menus(int modes);
 int gui_is_menu_shortcut(int key);
 void gui_mch_toggle_tearoffs(int enable);
-void execute_menu(exarg_T *eap, vimmenu_T *menu);
+void execute_menu(exarg_T *eap, vimmenu_T *menu, int mode_idx);
 void ex_emenu(exarg_T *eap);
 void winbar_click(win_T *wp, int col);
 vimmenu_T *gui_find_menu(char_u *path_name);
diff --git a/src/structs.h b/src/structs.h
index 560ce42..a0c06b5 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -3101,8 +3101,9 @@
 #define MENU_INDEX_OP_PENDING	3
 #define MENU_INDEX_INSERT	4
 #define MENU_INDEX_CMDLINE	5
-#define MENU_INDEX_TIP		6
-#define MENU_MODES		7
+#define MENU_INDEX_TERMINAL	6
+#define MENU_INDEX_TIP		7
+#define MENU_MODES		8
 
 /* Menu modes */
 #define MENU_NORMAL_MODE	(1 << MENU_INDEX_NORMAL)
@@ -3111,6 +3112,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)
 /*note MENU_INDEX_TIP is not a 'real' mode*/
diff --git a/src/testdir/test_menu.vim b/src/testdir/test_menu.vim
index 055d944..6462d51 100644
--- a/src/testdir/test_menu.vim
+++ b/src/testdir/test_menu.vim
@@ -30,3 +30,37 @@
 
   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
diff --git a/src/version.c b/src/version.c
index 726f5ee..4189eb0 100644
--- a/src/version.c
+++ b/src/version.c
@@ -793,6 +793,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    487,
+/**/
     486,
 /**/
     485,