patch 8.0.1108: cannot specify mappings for the terminal window

Problem:    Cannot specify mappings for the terminal window.
Solution:   Add the :tmap command and associated code.  (Jacob Askeland,
            closes #2073)
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 4a6332b..63aa34c 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -3259,11 +3259,18 @@
 		/* Avoid a 1 second delay when the keys start Insert mode. */
 		msg_scroll = FALSE;
 
-		if (!dangerous)
-		    ++ex_normal_busy;
-		exec_normal(TRUE);
-		if (!dangerous)
-		    --ex_normal_busy;
+#ifdef FEAT_TERMINAL
+		if (term_use_loop())
+		    terminal_loop(FALSE);
+		else
+#endif
+		{
+		    if (!dangerous)
+			++ex_normal_busy;
+		    exec_normal(TRUE);
+		    if (!dangerous)
+			--ex_normal_busy;
+		}
 		msg_scroll |= save_msg_scroll;
 	    }
 	}
diff --git a/src/ex_cmdidxs.h b/src/ex_cmdidxs.h
index 4d3bcbe..736cc38 100644
--- a/src/ex_cmdidxs.h
+++ b/src/ex_cmdidxs.h
@@ -25,12 +25,12 @@
   /* r */ 351,
   /* s */ 370,
   /* t */ 437,
-  /* u */ 473,
-  /* v */ 484,
-  /* w */ 502,
-  /* x */ 517,
-  /* y */ 526,
-  /* z */ 527
+  /* u */ 477,
+  /* v */ 488,
+  /* w */ 506,
+  /* x */ 521,
+  /* y */ 530,
+  /* z */ 531
 };
 
 /*
@@ -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, 29, 30, 31,  0, 32, 34,  0, 35,  0,  0,  0,  0,  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 },
   /* 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 = 540;
+static const int command_count = 544;
diff --git a/src/ex_cmds.h b/src/ex_cmds.h
index 161ba1d..0cc279d 100644
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -1484,7 +1484,7 @@
 			NEEDARG|EXTRA|TRLBAR|NOTRLCOM|CMDWIN,
 			ADDR_LINES),
 EX(CMD_terminal,	"terminal",	ex_terminal,
-			RANGE|BANG|FILES|TRLBAR|CMDWIN,
+			RANGE|BANG|FILES|CMDWIN,
 			ADDR_LINES),
 EX(CMD_tfirst,		"tfirst",	ex_tag,
 			RANGE|NOTADR|BANG|TRLBAR|ZEROR,
@@ -1498,12 +1498,21 @@
 EX(CMD_tlast,		"tlast",	ex_tag,
 			BANG|TRLBAR,
 			ADDR_LINES),
+EX(CMD_tmap,		"tmap",		ex_map,
+			EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN,
+			ADDR_LINES),
+EX(CMD_tmapclear,	"tmapclear",	ex_mapclear,
+			EXTRA|TRLBAR|CMDWIN,
+			ADDR_LINES),
 EX(CMD_tmenu,		"tmenu",	ex_menu,
 			RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN,
 			ADDR_LINES),
 EX(CMD_tnext,		"tnext",	ex_tag,
 			RANGE|NOTADR|BANG|TRLBAR|ZEROR,
 			ADDR_LINES),
+EX(CMD_tnoremap,	"tnoremap",	ex_map,
+			EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN,
+			ADDR_LINES),
 EX(CMD_topleft,		"topleft",	ex_wrongmodifier,
 			NEEDARG|EXTRA|NOTRLCOM,
 			ADDR_LINES),
@@ -1519,6 +1528,9 @@
 EX(CMD_tselect,		"tselect",	ex_tag,
 			BANG|TRLBAR|WORD1,
 			ADDR_LINES),
+EX(CMD_tunmap,		"tunmap",	ex_unmap,
+			EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN,
+			ADDR_LINES),
 EX(CMD_tunmenu,		"tunmenu",	ex_menu,
 			EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN,
 			ADDR_LINES),
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index c290328..3c51b3a 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -4209,6 +4209,7 @@
 	case CMD_cmap:	    case CMD_cnoremap:
 	case CMD_lmap:	    case CMD_lnoremap:
 	case CMD_smap:	    case CMD_snoremap:
+	case CMD_tmap:	    case CMD_tnoremap:
 	case CMD_xmap:	    case CMD_xnoremap:
 	    return set_context_in_map_cmd(xp, cmd, arg, forceit,
 						     FALSE, FALSE, ea.cmdidx);
@@ -4220,6 +4221,7 @@
 	case CMD_cunmap:
 	case CMD_lunmap:
 	case CMD_sunmap:
+	case CMD_tunmap:
 	case CMD_xunmap:
 	    return set_context_in_map_cmd(xp, cmd, arg, forceit,
 						      FALSE, TRUE, ea.cmdidx);
@@ -4231,6 +4233,7 @@
 	case CMD_cmapclear:
 	case CMD_lmapclear:
 	case CMD_smapclear:
+	case CMD_tmapclear:
 	case CMD_xmapclear:
 	    xp->xp_context = EXPAND_MAPCLEAR;
 	    xp->xp_pattern = arg;
diff --git a/src/getchar.c b/src/getchar.c
index 63d6542..f222427 100644
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -59,7 +59,7 @@
  * Returns a value between 0 and 255, index in maphash.
  * Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode.
  */
-#define MAP_HASH(mode, c1) (((mode) & (NORMAL + VISUAL + SELECTMODE + OP_PENDING)) ? (c1) : ((c1) ^ 0x80))
+#define MAP_HASH(mode, c1) (((mode) & (NORMAL + VISUAL + SELECTMODE + OP_PENDING + TERMINAL)) ? (c1) : ((c1) ^ 0x80))
 
 /*
  * Each mapping is put in one of the 256 hash lists, to speed up finding it.
@@ -3188,6 +3188,7 @@
  * for :xmap  mode is VISUAL
  * for :smap  mode is SELECTMODE
  * for :omap  mode is OP_PENDING
+ * for :tmap  mode is TERMINAL
  *
  * for :abbr  mode is INSERT + CMDLINE
  * for :iabbr mode is INSERT
@@ -3832,6 +3833,8 @@
 	mode = SELECTMODE;			/* :smap */
     else if (modec == 'o')
 	mode = OP_PENDING;			/* :omap */
+    else if (modec == 't')
+	mode = TERMINAL;			/* :tmap */
     else
     {
 	--p;
@@ -4892,6 +4895,9 @@
 		    case LANGMAP:
 			c1 = 'l';
 			break;
+		    case TERMINAL:
+			c1 = 't';
+			break;
 		    default:
 			IEMSG(_("E228: makemap: Illegal mode"));
 			return FAIL;
diff --git a/src/gui.c b/src/gui.c
index 97fac50..f714d7a 100644
--- a/src/gui.c
+++ b/src/gui.c
@@ -1101,7 +1101,7 @@
 	 * When in a terminal window use the shape/color specified there.
 	 */
 #ifdef FEAT_TERMINAL
-	if (use_terminal_cursor())
+	if (terminal_is_active())
 	    shape = term_get_cursor_shape(&shape_fg, &shape_bg);
 	else
 #endif
diff --git a/src/main.c b/src/main.c
index 5840085..e2c04f6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1363,7 +1363,7 @@
 		/* If terminal_loop() returns OK we got a key that is handled
 		 * in Normal model.  With FAIL we first need to position the
 		 * cursor and the screen needs to be redrawn. */
-		if (terminal_loop() == OK)
+		if (terminal_loop(TRUE) == OK)
 		    normal_cmd(&oa, TRUE);
 	    }
 	    else
diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro
index 639318d..7f64b90 100644
--- a/src/proto/terminal.pro
+++ b/src/proto/terminal.pro
@@ -7,10 +7,10 @@
 int term_in_normal_mode(void);
 void term_enter_job_mode(void);
 int send_keys_to_term(term_T *term, int c, int typed);
-int use_terminal_cursor(void);
+int terminal_is_active(void);
 cursorentry_T *term_get_cursor_shape(guicolor_T *fg, guicolor_T *bg);
 int term_use_loop(void);
-int terminal_loop(void);
+int terminal_loop(int blocking);
 void term_job_ended(job_T *job);
 void term_channel_closed(channel_T *ch);
 int term_update_window(win_T *wp);
diff --git a/src/terminal.c b/src/terminal.c
index 1a112f4..db17417 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -38,10 +38,9 @@
  * in tl_scrollback are no longer used.
  *
  * TODO:
- * - patch to add tmap, jakalope (Jacob Askeland) #2073
+ * - test_terminal_no_cmd hangs (Christian)
  * - Redirecting output does not work on MS-Windows, Test_terminal_redir_file()
  *   is disabled.
- * - test_terminal_no_cmd hangs (Christian)
  * - implement term_setsize()
  * - add test for giving error for invalid 'termsize' value.
  * - support minimal size when 'termsize' is "rows*cols".
@@ -56,7 +55,9 @@
  *   mouse in the Terminal window for copy/paste.
  * - when 'encoding' is not utf-8, or the job is using another encoding, setup
  *   conversions.
- * - In the GUI use a terminal emulator for :!cmd.
+ * - In the GUI use a terminal emulator for :!cmd.  Make the height the same as
+ *   the window and position it higher up when it gets filled, so it looks like
+ *   the text scrolls up.
  * - Copy text in the vterm to the Vim buffer once in a while, so that
  *   completion works.
  * - add an optional limit for the scrollback size.  When reaching it remove
@@ -1201,23 +1202,22 @@
  * Get a key from the user without mapping.
  * Note: while waiting a terminal may be closed and freed if the channel is
  * closed and ++close was used.
- * TODO: use terminal mode mappings.
+ * Uses terminal mode mappings.
  */
     static int
 term_vgetc()
 {
     int c;
+    int save_State = State;
 
-    ++no_mapping;
-    ++allow_keys;
+    State = TERMINAL;
     got_int = FALSE;
 #ifdef WIN3264
     ctrl_break_was_pressed = FALSE;
 #endif
     c = vgetc();
     got_int = FALSE;
-    --no_mapping;
-    --allow_keys;
+    State = save_State;
     return c;
 }
 
@@ -1406,7 +1406,7 @@
  * Return TRUE when the cursor of the terminal should be displayed.
  */
     int
-use_terminal_cursor()
+terminal_is_active()
 {
     return in_terminal_loop != NULL;
 }
@@ -1496,13 +1496,15 @@
 
 /*
  * Wait for input and send it to the job.
+ * When "blocking" is TRUE wait for a character to be typed.  Otherwise return
+ * when there is no more typahead.
  * Return when the start of a CTRL-W command is typed or anything else that
  * should be handled as a Normal mode command.
  * Returns OK if a typed character is to be handled in Normal mode, FAIL if
  * the terminal was closed.
  */
     int
-terminal_loop(void)
+terminal_loop(int blocking)
 {
     int		c;
     int		termkey = 0;
@@ -1539,7 +1541,7 @@
     }
 #endif
 
-    for (;;)
+    while (blocking || vpeekc() != NUL)
     {
 	/* TODO: skip screen update when handling a sequence of keys. */
 	/* Repeat redrawing in case a message is received while redrawing. */
@@ -1561,7 +1563,7 @@
 	if (ctrl_break_was_pressed)
 	    mch_signal_job(curbuf->b_term->tl_job, (char_u *)"kill");
 #endif
-
+	/* Was either CTRL-W (termkey) or CTRL-\ pressed? */
 	if (c == (termkey == 0 ? Ctrl_W : termkey) || c == Ctrl_BSL)
 	{
 	    int	    prev_c = c;
diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim
index 4596d91..4a639bd 100644
--- a/src/testdir/test_terminal.vim
+++ b/src/testdir/test_terminal.vim
@@ -620,3 +620,40 @@
     call delete('Xfile')
   endif
 endfunc
+
+func TerminalTmap(remap)
+  let buf = Run_shell_in_terminal({})
+  call assert_equal('t', mode())
+
+  if a:remap
+    tmap 123 456
+  else
+    tnoremap 123 456
+  endif
+  tmap 456 abcde
+  call assert_equal('456', maparg('123', 't'))
+  call assert_equal('abcde', maparg('456', 't'))
+  call feedkeys("123", 'tx')
+  call term_wait(buf)
+  let lnum = term_getcursor(buf)[0]
+  if a:remap
+    call assert_match('abcde', term_getline(buf, lnum))
+  else
+    call assert_match('456', term_getline(buf, lnum))
+  endif
+
+  call term_sendkeys(buf, "\r")
+  call Stop_shell_in_terminal(buf)
+  call term_wait(buf)
+
+  tunmap 123
+  tunmap 456
+  call assert_equal('', maparg('123', 't'))
+  close
+  unlet g:job
+endfunc
+
+func Test_terminal_tmap()
+  call TerminalTmap(1)
+  call TerminalTmap(0)
+endfunc
diff --git a/src/version.c b/src/version.c
index 09923fa..c8191cc 100644
--- a/src/version.c
+++ b/src/version.c
@@ -770,6 +770,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1108,
+/**/
     1107,
 /**/
     1106,
diff --git a/src/vim.h b/src/vim.h
index 93dc951..cbab101 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -716,9 +716,10 @@
 #define SHOWMATCH	(0x700 + INSERT) /* show matching paren */
 #define CONFIRM		0x800	/* ":confirm" prompt */
 #define SELECTMODE	0x1000	/* Select mode, only for mappings */
+#define TERMINAL        0x2000  /* Terminal mode */
 
-#define MAP_ALL_MODES	(0x3f | SELECTMODE)	/* all mode bits used for
-						 * mapping */
+/* all mode bits used for mapping */
+#define MAP_ALL_MODES	(0x3f | SELECTMODE | TERMINAL)
 
 /* directions */
 #define FORWARD			1