patch 9.1.0908: not possible to configure :messages

Problem:  not possible to configure :messages
Solution: add the 'messagesopt' option (Shougo Matsushita)

closes: #16068

Co-authored-by: h_east <h.east.727@gmail.com>
Signed-off-by: Shougo Matsushita <Shougo.Matsu@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt
index 69d8bc7..3fcbd95 100644
--- a/runtime/doc/message.txt
+++ b/runtime/doc/message.txt
@@ -1,4 +1,4 @@
-*message.txt*   For Vim version 9.1.  Last change: 2024 Nov 14
+*message.txt*   For Vim version 9.1.  Last change: 2024 Dec 06
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -29,7 +29,7 @@
 				Clear messages, keeping only the {count} most
 				recent ones.
 
-The number of remembered messages is determined by the 'msghistory' option.
+The number of remembered messages is determined by the 'messagesopt' option.
 
 								*g<*
 The "g<" command can be used to see the last page of previous command output.
@@ -837,6 +837,7 @@
 text then use |g<|.  This only works when 'more' is set.
 
 To reduce the number of hit-enter prompts:
+- Set 'messagesopt'.
 - Set 'cmdheight' to 2 or higher.
 - Add flags to 'shortmess'.
 - Reset 'showcmd' and/or 'ruler'.
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 273ea64..a31d041 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1,4 +1,4 @@
-*options.txt*	For Vim version 9.1.  Last change: 2024 Dec 03
+*options.txt*	For Vim version 9.1.  Last change: 2024 Dec 06
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -4439,7 +4439,7 @@
 			global
 	A history of ":" commands, and a history of previous search patterns
 	is remembered.  This option decides how many entries may be stored in
-	each of these histories (see |cmdline-editing| and 'msghistory' for
+	each of these histories (see |cmdline-editing| and 'messagesopt' for
 	the number of messages to remember).
 	The maximum value is 10000.
 	NOTE: This option is set to the Vi default value when 'compatible' is
@@ -5634,6 +5634,26 @@
 	generated from a list of items, e.g., the Buffers menu.  Changing this
 	option has no direct effect, the menu must be refreshed first.
 
+						*'messagesopt'* *'mopt'*
+'messagesopt' 'mopt'	string	(default "hit-enter,history:500")
+			global
+
+	Option settings when outputting messages.  It can consist of the
+	following items.  Items must be separated by a comma.
+
+	hit-enter	Use |hit-enter| prompt when the message is longer than
+			'cmdheight' size.
+
+	wait:{n}	Ignored when "hit-enter" is present.  Instead of using
+			|hit-enter| prompt, will simply wait for {n}
+			milliseconds so the user has a chance to read the
+			message, use 0 to disable sleep (but then the user may
+			miss an important message).
+
+	history:{n}	Determines how many entries are remembered in the
+			|:messages| history.  The maximum value is 10000.
+			Setting it to zero clears the message history.
+
 						*'mkspellmem'* *'msm'*
 'mkspellmem' 'msm'	string	(default "460000,2000,500")
 			global
@@ -5917,13 +5937,6 @@
 	time in msec between two mouse clicks for the second click to be
 	recognized as a multi click.
 
-						*'msghistory'* *'mhi'*
-'msghistory' 'mhi'	number	(default 500)
-			global
-	Determines how many entries are remembered in the |:messages| history.
-	The maximum value is 10000.
-	Setting it to zero clears the message history.
-
 
 						    *'mzquantum'* *'mzq'*
 'mzquantum' 'mzq'	number	(default 100)
diff --git a/runtime/doc/tags b/runtime/doc/tags
index ad87006..f0ca51b 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -499,9 +499,9 @@
 'menc'	options.txt	/*'menc'*
 'menuitems'	options.txt	/*'menuitems'*
 'mesg'	vi_diff.txt	/*'mesg'*
+'messagesopt'	options.txt	/*'messagesopt'*
 'mfd'	options.txt	/*'mfd'*
 'mh'	options.txt	/*'mh'*
-'mhi'	options.txt	/*'mhi'*
 'mis'	options.txt	/*'mis'*
 'mkspellmem'	options.txt	/*'mkspellmem'*
 'ml'	options.txt	/*'ml'*
@@ -517,6 +517,7 @@
 'modelines'	options.txt	/*'modelines'*
 'modifiable'	options.txt	/*'modifiable'*
 'modified'	options.txt	/*'modified'*
+'mopt'	options.txt	/*'mopt'*
 'more'	options.txt	/*'more'*
 'mouse'	options.txt	/*'mouse'*
 'mousef'	options.txt	/*'mousef'*
@@ -532,7 +533,6 @@
 'mousetime'	options.txt	/*'mousetime'*
 'mp'	options.txt	/*'mp'*
 'mps'	options.txt	/*'mps'*
-'msghistory'	options.txt	/*'msghistory'*
 'msm'	options.txt	/*'msm'*
 'mzq'	options.txt	/*'mzq'*
 'mzquantum'	options.txt	/*'mzquantum'*
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index f5f77b3..312a1c2 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -1,4 +1,4 @@
-*version9.txt*  For Vim version 9.1.  Last change: 2024 Dec 05
+*version9.txt*  For Vim version 9.1.  Last change: 2024 Dec 06
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -41667,7 +41667,7 @@
 			popup
 'findfunc'		Vim function to obtain the results for a |:find|
 			command
-'msghistory'		Max number of messages to remember
+'messagesopt'		configure |:messages| and |hit-enter| prompt
 'winfixbuf'		Keep buffer focused in a window
 'tabclose'		Which tab page to focus after closing a tab page
 't_xo'			Terminal uses XON/XOFF handshaking (e.g. vt420)
diff --git a/runtime/optwin.vim b/runtime/optwin.vim
index c8170da..c3f917c 100644
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -749,8 +749,8 @@
 call <SID>BinOptionG("terse", &terse)
 call <SID>AddOption("shortmess", gettext("list of flags to make messages shorter"))
 call <SID>OptionG("shm", &shm)
-call <SID>AddOption("msghistory", gettext("how many messages are remembered"))
-call append("$", " \tset mhi=" . &mhi)
+call <SID>AddOption("messagesopt", gettext("Option settings when outputting messages"))
+call <SID>OptionG("mopt", &mopt)
 call <SID>AddOption("showcmd", gettext("show (partial) command keys in location given by 'showcmdloc'"))
 let &sc = s:old_sc
 call <SID>BinOptionG("sc", &sc)
diff --git a/src/message.c b/src/message.c
index 15eaaae..34a2692 100644
--- a/src/message.c
+++ b/src/message.c
@@ -16,6 +16,7 @@
 #include "vim.h"
 
 static void add_msg_hist(char_u *s, int len, int attr);
+static void check_msg_hist(void);
 static void hit_return_msg(void);
 static void msg_home_replace_attr(char_u *fname, int attr);
 static void msg_puts_attr_len(char *str, int maxlen, int attr);
@@ -51,6 +52,21 @@
 static struct msg_hist *first_msg_hist = NULL;
 static struct msg_hist *last_msg_hist = NULL;
 static int msg_hist_len = 0;
+static int msg_hist_max = 500;		// The default max value is 500
+
+// flags obtained from the 'messagesopt' option
+#define MESSAGES_HIT_ENTER	0x001
+#define MESSAGES_WAIT		0x002
+#define MESSAGES_HISTORY	0x004
+
+// args in 'messagesopt' option
+#define MESSAGES_OPT_HIT_ENTER "hit-enter"
+#define MESSAGES_OPT_WAIT "wait:"
+#define MESSAGES_OPT_HISTORY "history:"
+
+// The default is "hit-enter,history:500"
+static int msg_flags = MESSAGES_HIT_ENTER | MESSAGES_HISTORY;
+static int msg_wait = 0;
 
 static FILE *verbose_fd = NULL;
 static int  verbose_did_open = FALSE;
@@ -1060,14 +1076,76 @@
     return OK;
 }
 
-    void
+    static void
 check_msg_hist(void)
 {
     // Don't let the message history get too big
-    while (msg_hist_len > 0 && msg_hist_len > p_mhi)
+    while (msg_hist_len > 0 && msg_hist_len > msg_hist_max)
 	(void)delete_first_msg();
 }
 
+
+    int
+messagesopt_changed(void)
+{
+    char_u	*p;
+    int		messages_flags_new = 0;
+    int		messages_wait_new = 0;
+    int		messages_history_new = 0;
+
+    p = p_meo;
+    while (*p != NUL)
+    {
+	if (STRNCMP(p, MESSAGES_OPT_HIT_ENTER,
+	     STRLEN_LITERAL(MESSAGES_OPT_HIT_ENTER)) == 0)
+	{
+	    p += STRLEN_LITERAL(MESSAGES_OPT_HIT_ENTER);
+	    messages_flags_new |= MESSAGES_HIT_ENTER;
+	}
+	else if (STRNCMP(p, MESSAGES_OPT_WAIT,
+		  STRLEN_LITERAL(MESSAGES_OPT_WAIT)) == 0
+	    && VIM_ISDIGIT(p[STRLEN_LITERAL(MESSAGES_OPT_WAIT)]))
+	{
+	    p += STRLEN_LITERAL(MESSAGES_OPT_WAIT);
+	    messages_wait_new = getdigits(&p);
+	    messages_flags_new |= MESSAGES_WAIT;
+	}
+	else if (STRNCMP(p, MESSAGES_OPT_HISTORY,
+		  STRLEN_LITERAL(MESSAGES_OPT_HISTORY)) == 0
+	    && VIM_ISDIGIT(p[STRLEN_LITERAL(MESSAGES_OPT_HISTORY)]))
+	{
+	    p += STRLEN_LITERAL(MESSAGES_OPT_HISTORY);
+	    messages_history_new = getdigits(&p);
+	    messages_flags_new |= MESSAGES_HISTORY;
+	}
+
+	if (*p != ',' && *p != NUL)
+	    return FAIL;
+	if (*p == ',')
+	    ++p;
+    }
+
+    // Either "wait" or "hit-enter" is required
+    if (!(messages_flags_new & (MESSAGES_HIT_ENTER | MESSAGES_WAIT)))
+        return FAIL;
+
+    // "history" must be set
+    if (!(messages_flags_new & MESSAGES_HISTORY))
+        return FAIL;
+
+    // "history" must be <= 10000
+    if (messages_history_new > 10000)
+        return FAIL;
+
+    msg_flags = messages_flags_new;
+    msg_wait = messages_wait_new;
+
+    msg_hist_max = messages_history_new;
+    check_msg_hist();
+
+    return OK;
+}
+
 /*
  * ":messages" command.
  */
@@ -1228,118 +1306,127 @@
 	if (need_check_timestamps)
 	    check_timestamps(FALSE);
 
-	hit_return_msg();
-
-	do
+	if (msg_flags & MESSAGES_HIT_ENTER)
 	{
-	    // Remember "got_int", if it is set vgetc() probably returns a
-	    // CTRL-C, but we need to loop then.
-	    had_got_int = got_int;
+	    hit_return_msg();
 
-	    // Don't do mappings here, we put the character back in the
-	    // typeahead buffer.
-	    ++no_mapping;
-	    ++allow_keys;
+	    do
+	    {
+		// Remember "got_int", if it is set vgetc() probably returns a
+		// CTRL-C, but we need to loop then.
+		had_got_int = got_int;
 
-	    // Temporarily disable Recording. If Recording is active, the
-	    // character will be recorded later, since it will be added to the
-	    // typebuf after the loop
-	    save_reg_recording = reg_recording;
-	    save_scriptout = scriptout;
-	    reg_recording = 0;
-	    scriptout = NULL;
-	    c = safe_vgetc();
-	    if (had_got_int && !global_busy)
-		got_int = FALSE;
-	    --no_mapping;
-	    --allow_keys;
-	    reg_recording = save_reg_recording;
-	    scriptout = save_scriptout;
+		// Don't do mappings here, we put the character back in the
+		// typeahead buffer.
+		++no_mapping;
+		++allow_keys;
+
+		// Temporarily disable Recording. If Recording is active, the
+		// character will be recorded later, since it will be added to the
+		// typebuf after the loop
+		save_reg_recording = reg_recording;
+		save_scriptout = scriptout;
+		reg_recording = 0;
+		scriptout = NULL;
+		c = safe_vgetc();
+		if (had_got_int && !global_busy)
+		    got_int = FALSE;
+		--no_mapping;
+		--allow_keys;
+		reg_recording = save_reg_recording;
+		scriptout = save_scriptout;
 
 #ifdef FEAT_CLIPBOARD
-	    // Strange way to allow copying (yanking) a modeless selection at
-	    // the hit-enter prompt.  Use CTRL-Y, because the same is used in
-	    // Cmdline-mode and it's harmless when there is no selection.
-	    if (c == Ctrl_Y && clip_star.state == SELECT_DONE)
-	    {
-		clip_copy_modeless_selection(TRUE);
-		c = K_IGNORE;
-	    }
-#endif
-
-	    /*
-	     * Allow scrolling back in the messages.
-	     * Also accept scroll-down commands when messages fill the screen,
-	     * to avoid that typing one 'j' too many makes the messages
-	     * disappear.
-	     */
-	    if (p_more && !p_cp)
-	    {
-		if (c == 'b' || c == 'k' || c == 'u' || c == 'g'
-						|| c == K_UP || c == K_PAGEUP)
+		// Strange way to allow copying (yanking) a modeless selection at
+		// the hit-enter prompt.  Use CTRL-Y, because the same is used in
+		// Cmdline-mode and it's harmless when there is no selection.
+		if (c == Ctrl_Y && clip_star.state == SELECT_DONE)
 		{
-		    if (msg_scrolled > Rows)
-			// scroll back to show older messages
-			do_more_prompt(c);
-		    else
-		    {
-			msg_didout = FALSE;
-			c = K_IGNORE;
-			msg_col =
-#ifdef FEAT_RIGHTLEFT
-			    cmdmsg_rl ? Columns - 1 :
-#endif
-			    0;
-		    }
-		    if (quit_more)
-		    {
-			c = CAR;		// just pretend CR was hit
-			quit_more = FALSE;
-			got_int = FALSE;
-		    }
-		    else if (c != K_IGNORE)
-		    {
-			c = K_IGNORE;
-			hit_return_msg();
-		    }
-		}
-		else if (msg_scrolled > Rows - 2
-			 && (c == 'j' || c == 'd' || c == 'f'
-					   || c == K_DOWN || c == K_PAGEDOWN))
+		    clip_copy_modeless_selection(TRUE);
 		    c = K_IGNORE;
-	    }
-	} while ((had_got_int && c == Ctrl_C)
-				|| c == K_IGNORE
-#ifdef FEAT_GUI
-				|| c == K_VER_SCROLLBAR || c == K_HOR_SCROLLBAR
+		}
 #endif
-				|| c == K_LEFTDRAG   || c == K_LEFTRELEASE
-				|| c == K_MIDDLEDRAG || c == K_MIDDLERELEASE
-				|| c == K_RIGHTDRAG  || c == K_RIGHTRELEASE
-				|| c == K_MOUSELEFT  || c == K_MOUSERIGHT
-				|| c == K_MOUSEDOWN  || c == K_MOUSEUP
-				|| c == K_MOUSEMOVE
-				|| (!mouse_has(MOUSE_RETURN)
-				    && mouse_row < msg_row
-				    && (c == K_LEFTMOUSE
-					|| c == K_MIDDLEMOUSE
-					|| c == K_RIGHTMOUSE
-					|| c == K_X1MOUSE
-					|| c == K_X2MOUSE))
-				);
-	ui_breakcheck();
 
-	// Avoid that the mouse-up event causes Visual mode to start.
-	if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE
-					  || c == K_X1MOUSE || c == K_X2MOUSE)
-	    (void)jump_to_mouse(MOUSE_SETPOS, NULL, 0);
-	else if (vim_strchr((char_u *)"\r\n ", c) == NULL && c != Ctrl_C)
+		/*
+		* Allow scrolling back in the messages.
+		* Also accept scroll-down commands when messages fill the screen,
+		* to avoid that typing one 'j' too many makes the messages
+		* disappear.
+		*/
+		if (p_more && !p_cp)
+		{
+		    if (c == 'b' || c == 'k' || c == 'u' || c == 'g'
+						    || c == K_UP || c == K_PAGEUP)
+		    {
+			if (msg_scrolled > Rows)
+			    // scroll back to show older messages
+			    do_more_prompt(c);
+			else
+			{
+			    msg_didout = FALSE;
+			    c = K_IGNORE;
+			    msg_col =
+#ifdef FEAT_RIGHTLEFT
+				cmdmsg_rl ? Columns - 1 :
+#endif
+				0;
+			}
+			if (quit_more)
+			{
+			    c = CAR;		// just pretend CR was hit
+			    quit_more = FALSE;
+			    got_int = FALSE;
+			}
+			else if (c != K_IGNORE)
+			{
+			    c = K_IGNORE;
+			    hit_return_msg();
+			}
+		    }
+		    else if (msg_scrolled > Rows - 2
+			    && (c == 'j' || c == 'd' || c == 'f'
+					    || c == K_DOWN || c == K_PAGEDOWN))
+			c = K_IGNORE;
+		}
+	    } while ((had_got_int && c == Ctrl_C)
+				    || c == K_IGNORE
+#ifdef FEAT_GUI
+				    || c == K_VER_SCROLLBAR || c == K_HOR_SCROLLBAR
+#endif
+				    || c == K_LEFTDRAG   || c == K_LEFTRELEASE
+				    || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE
+				    || c == K_RIGHTDRAG  || c == K_RIGHTRELEASE
+				    || c == K_MOUSELEFT  || c == K_MOUSERIGHT
+				    || c == K_MOUSEDOWN  || c == K_MOUSEUP
+				    || c == K_MOUSEMOVE
+				    || (!mouse_has(MOUSE_RETURN)
+					&& mouse_row < msg_row
+					&& (c == K_LEFTMOUSE
+					    || c == K_MIDDLEMOUSE
+					    || c == K_RIGHTMOUSE
+					    || c == K_X1MOUSE
+					    || c == K_X2MOUSE))
+				    );
+	    ui_breakcheck();
+
+	    // Avoid that the mouse-up event causes Visual mode to start.
+	    if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE
+					    || c == K_X1MOUSE || c == K_X2MOUSE)
+		(void)jump_to_mouse(MOUSE_SETPOS, NULL, 0);
+	    else if (vim_strchr((char_u *)"\r\n ", c) == NULL && c != Ctrl_C)
+	    {
+		// Put the character back in the typeahead buffer.  Don't use the
+		// stuff buffer, because lmaps wouldn't work.
+		ins_char_typebuf(vgetc_char, vgetc_mod_mask);
+		do_redraw = TRUE;	    // need a redraw even though there is
+					// typeahead
+	    }
+	}
+	else
 	{
-	    // Put the character back in the typeahead buffer.  Don't use the
-	    // stuff buffer, because lmaps wouldn't work.
-	    ins_char_typebuf(vgetc_char, vgetc_mod_mask);
-	    do_redraw = TRUE;	    // need a redraw even though there is
-				    // typeahead
+	    c = CAR;
+	    // Wait to allow the user to verify the output.
+	    do_sleep(msg_wait, TRUE);
 	}
     }
     redir_off = FALSE;
diff --git a/src/option.c b/src/option.c
index 31b00be..f069d0f 100644
--- a/src/option.c
+++ b/src/option.c
@@ -3864,31 +3864,6 @@
     return NULL;
 }
 
-/*
- * Process the updated 'msghistory' option value.
- */
-    char *
-did_set_msghistory(optset_T *args UNUSED)
-{
-    char *errmsg = NULL;
-
-    // 'msghistory' must be positive
-    if (p_mhi < 0)
-    {
-	errmsg = e_argument_must_be_positive;
-	p_mhi = 0;
-    }
-    else if (p_mhi > 10000)
-    {
-	errmsg = e_invalid_argument;
-	p_mhi = 10000;
-    }
-
-    check_msg_hist();
-
-    return errmsg;
-}
-
 #if defined(FEAT_LINEBREAK) || defined(PROTO)
 /*
  * Process the new 'numberwidth' option value.
diff --git a/src/option.h b/src/option.h
index 18a7c2a..e747add 100644
--- a/src/option.h
+++ b/src/option.h
@@ -775,6 +775,7 @@
 #ifdef FEAT_MENU
 EXTERN long	p_mis;		// 'menuitems'
 #endif
+EXTERN char_u	*p_meo;		// 'messagesopt'
 #ifdef FEAT_SPELL
 EXTERN char_u	*p_msm;		// 'mkspellmem'
 #endif
@@ -794,7 +795,6 @@
 #endif
 EXTERN long	p_mouset;	// 'mousetime'
 EXTERN int	p_more;		// 'more'
-EXTERN long	p_mhi;		// 'msghistory'
 #ifdef FEAT_MZSCHEME
 EXTERN long	p_mzq;		// 'mzquantum
 # if defined(DYNAMIC_MZSCHEME)
diff --git a/src/optiondefs.h b/src/optiondefs.h
index 2959232..f42178b 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -1695,6 +1695,9 @@
     {"mesg",	    NULL,   P_BOOL|P_VI_DEF,
 			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
+    {"messagesopt","mopt",  P_STRING|P_ALLOCED|P_VI_DEF|P_ONECOMMA|P_COLON|P_NODUP,
+			    (char_u *)&p_meo, PV_NONE, did_set_messagesopt, expand_set_messagesopt,
+			    {(char_u *)"hit-enter,history:500", (char_u *)NULL} SCTX_INIT},
     {"mkspellmem",  "msm",  P_STRING|P_VI_DEF|P_EXPAND|P_SECURE,
 #ifdef FEAT_SPELL
 			    (char_u *)&p_msm, PV_NONE, did_set_mkspellmem, NULL,
@@ -1778,9 +1781,6 @@
     {"mousetime",   "mouset",	P_NUM|P_VI_DEF,
 			    (char_u *)&p_mouset, PV_NONE, NULL, NULL,
 			    {(char_u *)500L, (char_u *)0L} SCTX_INIT},
-    {"msghistory","mhi",    P_NUM|P_VI_DEF,
-			    (char_u *)&p_mhi, PV_NONE, did_set_msghistory, NULL,
-			    {(char_u *)500L, (char_u *)0L} SCTX_INIT},
     {"mzquantum",  "mzq",   P_NUM,
 #ifdef FEAT_MZSCHEME
 			    (char_u *)&p_mzq, PV_NONE, did_set_mzquantum, NULL,
diff --git a/src/optionstr.c b/src/optionstr.c
index d7cb38a..08b2350 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -3040,6 +3040,30 @@
     return NULL;
 }
 
+/*
+ * Process the updated 'messagesopt' option value.
+ */
+    char *
+did_set_messagesopt(optset_T *args UNUSED)
+{
+    if (messagesopt_changed() == FAIL)
+	return e_invalid_argument;
+
+    return NULL;
+}
+
+    int
+expand_set_messagesopt(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    static char *(p_meo_values[]) = {"hit-enter", "wait:", "history:", NULL};
+    return expand_set_opt_string(
+	    args,
+	    p_meo_values,
+	    ARRAY_LENGTH(p_meo_values) - 1,
+	    numMatches,
+	    matches);
+}
+
 #if defined(FEAT_SPELL) || defined(PROTO)
 /*
  * The 'mkspellmem' option is changed.
diff --git a/src/proto/message.pro b/src/proto/message.pro
index 1c11444..34b3818 100644
--- a/src/proto/message.pro
+++ b/src/proto/message.pro
@@ -18,7 +18,7 @@
 char *msg_trunc_attr(char *s, int force, int attr);
 char_u *msg_may_trunc(int force, char_u *s);
 int delete_first_msg(void);
-void check_msg_hist(void);
+int messagesopt_changed(void);
 void ex_messages(exarg_T *eap);
 void msg_end_prompt(void);
 void wait_return(int redraw);
diff --git a/src/proto/option.pro b/src/proto/option.pro
index 8aa49c0..83f32aa 100644
--- a/src/proto/option.pro
+++ b/src/proto/option.pro
@@ -56,7 +56,6 @@
 char *did_set_modifiable(optset_T *args);
 char *did_set_modified(optset_T *args);
 char *did_set_mousehide(optset_T *args);
-char *did_set_msghistory(optset_T *args);
 char *did_set_number_relativenumber(optset_T *args);
 char *did_set_numberwidth(optset_T *args);
 char *did_set_paste(optset_T *args);
diff --git a/src/proto/optionstr.pro b/src/proto/optionstr.pro
index 561faa8..75a8d73 100644
--- a/src/proto/optionstr.pro
+++ b/src/proto/optionstr.pro
@@ -111,6 +111,8 @@
 char *did_set_lispoptions(optset_T *args);
 int expand_set_lispoptions(optexpand_T *args, int *numMatches, char_u ***matches);
 char *did_set_matchpairs(optset_T *args);
+char *did_set_messagesopt(optset_T *args);
+int expand_set_messagesopt(optexpand_T *args, int *numMatches, char_u ***matches);
 char *did_set_mkspellmem(optset_T *args);
 char *did_set_mouse(optset_T *args);
 int expand_set_mouse(optexpand_T *args, int *numMatches, char_u ***matches);
diff --git a/src/testdir/gen_opt_test.vim b/src/testdir/gen_opt_test.vim
index ac9401e..8bfa57d 100644
--- a/src/testdir/gen_opt_test.vim
+++ b/src/testdir/gen_opt_test.vim
@@ -85,7 +85,6 @@
       \ 'imstyle': [[0, 1], [-1, 2, 999]],
       \ 'lines': [[2, 24, 1000], [-1, 0, 1]],
       \ 'linespace': [[-1, 0, 2, 4, 999], ['']],
-      \ 'msghistory': [[0, 1, 100, 10000], [-1, 10001]],
       \ 'numberwidth': [[1, 4, 8, 10, 11, 20], [-1, 0, 21]],
       \ 'regexpengine': [[0, 1, 2], [-1, 3, 999]],
       \ 'report': [[0, 1, 2, 9999], [-1]],
@@ -233,6 +232,11 @@
       \		'eol:\\u21b5', 'eol:\\U000021b5', 'eol:x,space:y'],
       \		['xxx', 'eol:']],
       \ 'matchpairs': [['', '(:)', '(:),<:>'], ['xxx']],
+      \ 'messagesopt': [['hit-enter,history:1', 'hit-enter,history:10000',
+      \		'history:100,wait:100', 'history:0,wait:0',
+      \		'hit-enter,history:1,wait:1'],
+      \		['xxx', 'history:500', 'hit-enter,history:-1',
+      \		'hit-enter,history:10001', 'hit-enter']],
       \ 'mkspellmem': [['10000,100,12'], ['', 'xxx', '10000,100']],
       \ 'mouse': [['', 'n', 'v', 'i', 'c', 'h', 'a', 'r', 'nvi'],
       \		['xxx', 'n,v,i']],
diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim
index 2fbce74..6d4fdd7 100644
--- a/src/testdir/test_cmdline.vim
+++ b/src/testdir/test_cmdline.vim
@@ -4032,30 +4032,4 @@
   let &shellslash = save_shellslash
 endfunc
 
-func Test_msghistory()
-  " After setting 'msghistory' to 2 and outputting a message 4 times with
-  " :echomsg, is the number of output lines of :messages 2?
-  set msghistory=2
-  echomsg 'foo'
-  echomsg 'bar'
-  echomsg 'baz'
-  echomsg 'foobar'
-  call assert_equal(['baz', 'foobar'], GetMessages())
-
-  " When the number of messages is 10 and 'msghistory' is changed to 5, is the
-  " number of output lines of :messages 5?
-  set msghistory=10
-  for num in range(1, 10)
-    echomsg num
-  endfor
-  set msghistory=5
-  call assert_equal(5, len(GetMessages()))
-
-  " Check empty list
-  set msghistory=0
-  call assert_true(empty(GetMessages()))
-
-  set msghistory&
-endfunc
-
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_messages.vim b/src/testdir/test_messages.vim
index 46f3368..1b5f809 100644
--- a/src/testdir/test_messages.vim
+++ b/src/testdir/test_messages.vim
@@ -211,6 +211,7 @@
 " Test more-prompt (see :help more-prompt).
 func Test_message_more()
   CheckRunVimInTerminal
+
   let buf = RunVimInTerminal('', {'rows': 6})
   call term_sendkeys(buf, ":call setline(1, range(1, 100))\n")
 
@@ -657,5 +658,50 @@
   call StopVimInTerminal(buf)
 endfunc
 
+func Test_messagesopt_history()
+  " After setting 'messagesopt' "history" to 2 and outputting a message 4 times
+  " with :echomsg, is the number of output lines of :messages 2?
+  set messagesopt=hit-enter,history:2
+  echomsg 'foo'
+  echomsg 'bar'
+  echomsg 'baz'
+  echomsg 'foobar'
+  call assert_equal(['baz', 'foobar'], GetMessages())
+
+  " When the number of messages is 10 and 'messagesopt' "history" is changed to
+  " 5, is the number of output lines of :messages 5?
+  set messagesopt=hit-enter,history:10
+  for num in range(1, 10)
+    echomsg num
+  endfor
+  set messagesopt=hit-enter,history:5
+  call assert_equal(5, len(GetMessages()))
+
+  " Check empty list
+  set messagesopt=hit-enter,history:0
+  call assert_true(empty(GetMessages()))
+
+  set messagesopt&
+endfunc
+
+func Test_messagesopt_wait()
+  CheckRunVimInTerminal
+
+  let buf = RunVimInTerminal('', {'rows': 6, 'cols': 45})
+  call term_sendkeys(buf, ":set cmdheight=1\n")
+
+  " Check hit-enter prompt
+  call term_sendkeys(buf, ":set messagesopt=hit-enter,history:500\n")
+  call term_sendkeys(buf, ":echo 'foo' | echo 'bar' echo 'baz'\n")
+  call WaitForAssert({-> assert_equal('Press ENTER or type command to continue', term_getline(buf, 6))})
+
+  " Check no hit-enter prompt when "wait:" is set
+  call term_sendkeys(buf, ":set messagesopt=wait:100,history:500\n")
+  call term_sendkeys(buf, ":echo 'foo' | echo 'bar' echo 'baz'\n")
+  call WaitForAssert({-> assert_equal('                           0,0-1         All', term_getline(buf, 6))})
+
+  " clean up
+  call StopVimInTerminal(buf)
+endfunc
 
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim
index cd66cdf..34efbb5 100644
--- a/src/testdir/test_options.vim
+++ b/src/testdir/test_options.vim
@@ -633,6 +633,10 @@
   call feedkeys(":set hl=8b i\<Left>\<Left>\<Tab>\<C-B>\"\<CR>", 'xt')
   call assert_equal("\"set hl=8bi i", @:)
 
+  " messagesopt
+  call assert_equal(['history:', 'hit-enter', 'wait:'],
+        \ getcompletion('set messagesopt+=', 'cmdline')->sort())
+
   "
   " Test flag lists
   "
@@ -732,7 +736,6 @@
   call assert_fails('set backupcopy=', 'E474:')
   call assert_fails('set regexpengine=3', 'E474:')
   call assert_fails('set history=10001', 'E474:')
-  call assert_fails('set msghistory=10001', 'E474:')
   call assert_fails('set numberwidth=21', 'E474:')
   call assert_fails('set colorcolumn=-a', 'E474:')
   call assert_fails('set colorcolumn=a', 'E474:')
@@ -746,7 +749,6 @@
   endif
   call assert_fails('set helpheight=-1', 'E487:')
   call assert_fails('set history=-1', 'E487:')
-  call assert_fails('set msghistory=-1', 'E487:')
   call assert_fails('set report=-1', 'E487:')
   call assert_fails('set shiftwidth=-1', 'E487:')
   call assert_fails('set sidescroll=-1', 'E487:')
@@ -2509,6 +2511,7 @@
         \ ['lispoptions', 'expr:1', 'a123'],
         \ ['listchars', 'tab:->', 'tab:'],
         \ ['matchpairs', '<:>', '<:'],
+        \ ['messagesopt', 'hit-enter,history:100', 'a123'],
         \ ['mkspellmem', '100000,1000,100', '100000'],
         \ ['mouse', 'nvi', 'z'],
         \ ['mousemodel', 'extend', 'a123'],
diff --git a/src/version.c b/src/version.c
index bdd6053..4cb67ef 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    908,
+/**/
     907,
 /**/
     906,