Patch 9.0.0288
Problem:    When 'cmdheight' is zero some messages are not displayed.
Solution:   Use a popup notification window.
Files:      runtime/doc/options.txt, src/popupwin.c, src/proto/popupwin.pro,
            src/feature.h, src/structs.h, src/time.c, src/proto/time.pro,
            src/message.c, src/proto/message.pro, src/screen.c,
            src/testdir/test_messages.vim


*** ../vim-9.0.0287/runtime/doc/options.txt     2022-08-26 16:58:46.135489352 
+0100
--- runtime/doc/options.txt     2022-08-27 16:57:47.541146693 +0100
***************
*** 1763,1769 ****
        page can have a different value.
  
        When 'cmdheight' is zero, there is no command-line unless it is being
!       used.  Any messages will cause the |hit-enter| prompt.
  
                                                *'cmdwinheight'* *'cwh'*
  'cmdwinheight' 'cwh'  number  (default 7)
--- 1789,1799 ----
        page can have a different value.
  
        When 'cmdheight' is zero, there is no command-line unless it is being
!       used.  Informative messages will be displayed in a popup notification
!       window at the bottom if the window, using the MessageWindow highlight
!       group {only if compiled with the +popupwin and +timers features},
!       otherwise they will not be displayed.  Other messages will cause the
!       |hit-enter| prompt.  Expect some other unexpected behavior too.
  
                                                *'cmdwinheight'* *'cwh'*
  'cmdwinheight' 'cwh'  number  (default 7)
*** ../vim-9.0.0287/src/popupwin.c      2022-08-14 14:16:07.999582175 +0100
--- src/popupwin.c      2022-08-27 18:43:29.590734722 +0100
***************
*** 412,426 ****
  }
  
  #if defined(FEAT_TIMERS)
      static void
! popup_add_timeout(win_T *wp, int time)
  {
      char_u        cbbuf[50];
      char_u        *ptr = cbbuf;
      typval_T      tv;
  
      vim_snprintf((char *)cbbuf, sizeof(cbbuf),
!                                      "(_) => popup_close(%d)", wp->w_id);
      if (get_lambda_tv_and_compile(&ptr, &tv, FALSE, &EVALARG_EVALUATE) == OK)
      {
        wp->w_popup_timer = create_timer(time, 0);
--- 412,431 ----
  }
  
  #if defined(FEAT_TIMERS)
+ /*
+  * Add a timer to "wp" with "time".
+  * If "close" is true use popup_close(), otherwise popup_hide().
+  */
      static void
! popup_add_timeout(win_T *wp, int time, int close)
  {
      char_u        cbbuf[50];
      char_u        *ptr = cbbuf;
      typval_T      tv;
  
      vim_snprintf((char *)cbbuf, sizeof(cbbuf),
!               close ? "(_) => popup_close(%d)" : "(_) => popup_hide(%d)",
!               wp->w_id);
      if (get_lambda_tv_and_compile(&ptr, &tv, FALSE, &EVALARG_EVALUATE) == OK)
      {
        wp->w_popup_timer = create_timer(time, 0);
***************
*** 669,675 ****
  
            if (syn_name2id((char_u *)linehl) == 0)
                linehl = "PmenuSel";
!           sign_define_by_name(sign_name, NULL, (char_u *)linehl, NULL, NULL, 
NULL, NULL);
        }
  
        sign_place(&sign_id, (char_u *)"PopUpMenu", sign_name,
--- 674,681 ----
  
            if (syn_name2id((char_u *)linehl) == 0)
                linehl = "PmenuSel";
!           sign_define_by_name(sign_name, NULL, (char_u *)linehl,
!                                                      NULL, NULL, NULL, NULL);
        }
  
        sign_place(&sign_id, (char_u *)"PopUpMenu", sign_name,
***************
*** 905,911 ****
      // Add timer to close the popup after some time.
      nr = dict_get_number(dict, "time");
      if (nr > 0)
!       popup_add_timeout(wp, nr);
  #endif
  
      di = dict_find(dict, (char_u *)"moved", -1);
--- 911,917 ----
      // Add timer to close the popup after some time.
      nr = dict_get_number(dict, "time");
      if (nr > 0)
!       popup_add_timeout(wp, nr, TRUE);
  #endif
  
      di = dict_find(dict, (char_u *)"moved", -1);
***************
*** 1289,1294 ****
--- 1295,1303 ----
            if (wp->w_winrow >= Rows)
                wp->w_winrow = Rows - 1;
        }
+       if (wp->w_popup_pos == POPPOS_BOTTOM)
+           // assume that each buffer line takes one screen line
+           wp->w_winrow = MAX(Rows - wp->w_buffer->b_ml.ml_line_count - 1, 0);
  
        if (!use_wantcol)
            center_hor = TRUE;
***************
*** 1649,1654 ****
--- 1658,1664 ----
      TYPE_ATCURSOR,
      TYPE_BEVAL,
      TYPE_NOTIFICATION,
+     TYPE_MESSAGE_WIN, // similar to TYPE_NOTIFICATION
      TYPE_DIALOG,
      TYPE_MENU,
      TYPE_PREVIEW,     // preview window
***************
*** 1656,1661 ****
--- 1666,1680 ----
  } create_type_T;
  
  /*
+  * Return TRUE if "type" is TYPE_NOTIFICATION or TYPE_MESSAGE_WIN.
+  */
+     static int
+ popup_is_notification(create_type_T type)
+ {
+     return type == TYPE_NOTIFICATION || type == TYPE_MESSAGE_WIN;
+ }
+ 
+ /*
   * Make "buf" empty and set the contents to "text".
   * Used by popup_create() and popup_settext().
   */
***************
*** 1914,1919 ****
--- 1933,1953 ----
  #endif
  
  /*
+  * Set the color for a notification window.
+  */
+     static void
+ popup_update_color(win_T *wp, create_type_T type)
+ {
+     char    *hiname = type == TYPE_MESSAGE_WIN
+                                      ? "MessageWindow" : "PopupNotification";
+     int               nr = syn_name2id((char_u *)hiname);
+ 
+     set_string_option_direct_in_win(wp, (char_u *)"wincolor", -1,
+               (char_u *)(nr == 0 ? "WarningMsg" : hiname),
+               OPT_FREE|OPT_LOCAL, 0);
+ }
+ 
+ /*
   * popup_create({text}, {options})
   * popup_atcursor({text}, {options})
   * etc.
***************
*** 1928,1934 ****
      int               new_buffer;
      buf_T     *buf = NULL;
      dict_T    *d = NULL;
-     int               nr;
      int               i;
  
      if (argvars != NULL)
--- 1962,1967 ----
***************
*** 1975,1981 ****
      {
        if (dict_has_key(d, "tabpage"))
            tabnr = (int)dict_get_number(d, "tabpage");
!       else if (type == TYPE_NOTIFICATION)
            tabnr = -1;  // notifications are global by default
        else
            tabnr = 0;
--- 2008,2014 ----
      {
        if (dict_has_key(d, "tabpage"))
            tabnr = (int)dict_get_number(d, "tabpage");
!       else if (popup_is_notification(type))
            tabnr = -1;  // notifications are global by default
        else
            tabnr = 0;
***************
*** 2101,2107 ****
      wp->w_zindex = POPUPWIN_DEFAULT_ZINDEX;
      wp->w_popup_close = POPCLOSE_NONE;
  
!     if (type == TYPE_NOTIFICATION)
      {
        win_T  *twp, *nextwin;
        int     height = buf->b_ml.ml_line_count + 3;
--- 2134,2140 ----
      wp->w_zindex = POPUPWIN_DEFAULT_ZINDEX;
      wp->w_popup_close = POPCLOSE_NONE;
  
!     if (popup_is_notification(type))
      {
        win_T  *twp, *nextwin;
        int     height = buf->b_ml.ml_line_count + 3;
***************
*** 2140,2149 ****
        wp->w_popup_padding[1] = 1;
        wp->w_popup_padding[3] = 1;
  
!       nr = syn_name2id((char_u *)"PopupNotification");
!       set_string_option_direct_in_win(wp, (char_u *)"wincolor", -1,
!               (char_u *)(nr == 0 ? "WarningMsg" : "PopupNotification"),
!               OPT_FREE|OPT_LOCAL, 0);
      }
  
      if (type == TYPE_DIALOG || type == TYPE_MENU)
--- 2173,2179 ----
        wp->w_popup_padding[1] = 1;
        wp->w_popup_padding[3] = 1;
  
!       popup_update_color(wp, type);
      }
  
      if (type == TYPE_DIALOG || type == TYPE_MENU)
***************
*** 2203,2210 ****
        apply_options(wp, d, TRUE);
  
  #ifdef FEAT_TIMERS
!     if (type == TYPE_NOTIFICATION && wp->w_popup_timer == NULL)
!       popup_add_timeout(wp, 3000);
  #endif
  
      popup_adjust_position(wp);
--- 2233,2240 ----
        apply_options(wp, d, TRUE);
  
  #ifdef FEAT_TIMERS
!     if (popup_is_notification(type) && wp->w_popup_timer == NULL)
!       popup_add_timeout(wp, 3000, type == TYPE_NOTIFICATION);
  #endif
  
      popup_adjust_position(wp);
***************
*** 4408,4413 ****
--- 4438,4523 ----
  }
  #endif
  
+ #if defined(HAS_MESSAGE_WINDOW) || defined(PROTO)
+ 
+ // Window used for messages when 'winheight' is zero.
+ static win_T *message_win = NULL;
+ 
+ /*
+  * Get the message window.
+  * Returns NULL if something failed.
+  */
+     win_T *
+ popup_get_message_win(void)
+ {
+     if (message_win == NULL)
+     {
+       int i;
+ 
+       message_win = popup_create(NULL, NULL, TYPE_MESSAGE_WIN);
+ 
+       if (message_win == NULL)
+           return NULL;
+ 
+       // use the full screen width
+       message_win->w_width = Columns;
+ 
+       // position at bottom of screen
+       message_win->w_popup_pos = POPPOS_BOTTOM;
+       message_win->w_wantcol = 1;
+       message_win->w_minwidth = 9999;
+ 
+       // no padding, border at the top
+       for (i = 0; i < 4; ++i)
+           message_win->w_popup_padding[i] = 0;
+       for (i = 1; i < 4; ++i)
+           message_win->w_popup_border[i] = 0;
+ 
+       if (message_win->w_popup_timer != NULL)
+           message_win->w_popup_timer->tr_keep = TRUE;
+     }
+     return message_win;
+ }
+ 
+ /*
+  * If the message window is not visible: show it
+  * If the message window is visible: reset the timeout
+  */
+     void
+ popup_show_message_win(void)
+ {
+     if (message_win != NULL)
+     {
+       if ((message_win->w_popup_flags & POPF_HIDDEN) != 0)
+       {
+           // the highlight may have changed.
+           popup_update_color(message_win, TYPE_MESSAGE_WIN);
+           popup_show(message_win);
+       }
+       else if (message_win->w_popup_timer != NULL)
+           timer_start(message_win->w_popup_timer);
+     }
+ }
+ 
+     int
+ popup_message_win_visible(void)
+ {
+     return message_win != NULL
+       && (message_win->w_popup_flags & POPF_HIDDEN) == 0;
+ }
+ 
+ /*
+  * If the message window is visible: hide it.
+  */
+     void
+ popup_hide_message_win(void)
+ {
+     if (message_win != NULL)
+       popup_hide(message_win);
+ }
+ 
+ #endif
+ 
  /*
   * Close any popup for a text property associated with "win".
   * Return TRUE if a popup was closed.
*** ../vim-9.0.0287/src/proto/popupwin.pro      2022-06-27 23:15:19.000000000 
+0100
--- src/proto/popupwin.pro      2022-08-27 14:14:30.084468674 +0100
***************
*** 62,67 ****
--- 62,71 ----
  void popup_close_preview(void);
  void popup_hide_info(void);
  void popup_close_info(void);
+ win_T *popup_get_message_win(void);
+ void popup_show_message_win(void);
+ int popup_message_win_visible(void);
+ void popup_hide_message_win(void);
  int popup_win_closed(win_T *win);
  void popup_set_title(win_T *wp);
  void popup_update_preview_title(void);
*** ../vim-9.0.0287/src/feature.h       2022-08-26 17:52:47.886406161 +0100
--- src/feature.h       2022-08-27 13:27:09.088667917 +0100
***************
*** 1064,1069 ****
--- 1064,1076 ----
  # define FEAT_PROP_POPUP
  #endif
  
+ /*
+  * +message_window    use a popup for messages when 'cmdheight' is zero
+  */
+ #if defined(FEAT_PROP_POPUP) && defined(FEAT_TIMERS)
+ # define HAS_MESSAGE_WINDOW
+ #endif
+ 
  #if defined(FEAT_SYN_HL) && defined(FEAT_RELTIME)
  // Can limit syntax highlight time to 'redrawtime'.
  # define SYN_TIME_LIMIT 1
*** ../vim-9.0.0287/src/structs.h       2022-08-26 22:36:32.480565634 +0100
--- src/structs.h       2022-08-27 15:18:40.750176557 +0100
***************
*** 2569,2574 ****
--- 2569,2575 ----
      proftime_T        tr_due;             // when the callback is to be 
invoked
      char      tr_firing;          // when TRUE callback is being called
      char      tr_paused;          // when TRUE callback is not invoked
+     char      tr_keep;            // when TRUE keep timer after it fired
      int               tr_repeat;          // number of times to repeat, -1 
forever
      long      tr_interval;        // msec
      callback_T        tr_callback;
***************
*** 2605,2610 ****
--- 2606,2612 ----
      POPPOS_BOTRIGHT,
      POPPOS_TOPRIGHT,
      POPPOS_CENTER,
+     POPPOS_BOTTOM,    // bottom of popup at bottom of screen
      POPPOS_NONE
  } poppos_T;
  
*** ../vim-9.0.0287/src/time.c  2022-07-23 09:52:00.341814264 +0100
--- src/time.c  2022-08-27 13:49:00.156757678 +0100
***************
*** 464,474 ****
        timer->tr_repeat = repeat - 1;
      timer->tr_interval = msec;
  
!     profile_setlimit(msec, &timer->tr_due);
      return timer;
  }
  
  /*
   * Invoke the callback of "timer".
   */
      static void
--- 464,484 ----
        timer->tr_repeat = repeat - 1;
      timer->tr_interval = msec;
  
!     timer_start(timer);
      return timer;
  }
  
  /*
+  * (Re)start a timer.
+  */
+     void
+ timer_start(timer_T *timer)
+ {
+     profile_setlimit(timer->tr_interval, &timer->tr_due);
+     timer->tr_paused = FALSE;
+ }
+ 
+ /*
   * Invoke the callback of "timer".
   */
      static void
***************
*** 603,610 ****
            else
            {
                this_due = -1;
!               remove_timer(timer);
!               free_timer(timer);
            }
        }
        if (this_due > 0 && (next_due == -1 || next_due > this_due))
--- 613,625 ----
            else
            {
                this_due = -1;
!               if (timer->tr_keep)
!                   timer->tr_paused = TRUE;
!               else
!               {
!                   remove_timer(timer);
!                   free_timer(timer);
!               }
            }
        }
        if (this_due > 0 && (next_due == -1 || next_due > this_due))
***************
*** 826,831 ****
--- 841,847 ----
      else
      {
        int     paused = (int)tv_get_bool(&argvars[1]);
+ 
        timer = find_timer((int)tv_get_number(&argvars[0]));
        if (timer != NULL)
            timer->tr_paused = paused;
*** ../vim-9.0.0287/src/proto/time.pro  2022-06-27 23:15:27.000000000 +0100
--- src/proto/time.pro  2022-08-27 13:55:36.099318843 +0100
***************
*** 9,14 ****
--- 9,15 ----
  void f_strptime(typval_T *argvars, typval_T *rettv);
  long proftime_time_left(proftime_T *due, proftime_T *now);
  timer_T *create_timer(long msec, int repeat);
+ void timer_start(timer_T *timer);
  long check_due_timer(void);
  void stop_timer(timer_T *timer);
  int set_ref_in_timer(int copyID);
*** ../vim-9.0.0287/src/message.c       2022-08-22 15:19:12.732328943 +0100
--- src/message.c       2022-08-27 20:47:18.903862418 +0100
***************
*** 954,961 ****
      int               n;
      int               room;
  
!     // If something unexpected happened "room" may be negative, check for that
!     // just in case.
      room = (int)(Rows - cmdline_row - 1) * Columns + sc_col - 1;
      if (room > 0 && (force || (shortmess(SHM_TRUNC) && !exmode_active))
            && (n = (int)STRLEN(s) - room) > 0 && p_ch > 0)
--- 954,961 ----
      int               n;
      int               room;
  
!     // If 'cmdheight' is zero or something unexpected happened "room" may be
!     // negative.
      room = (int)(Rows - cmdline_row - 1) * Columns + sc_col - 1;
      if (room > 0 && (force || (shortmess(SHM_TRUNC) && !exmode_active))
            && (n = (int)STRLEN(s) - room) > 0 && p_ch > 0)
***************
*** 1421,1426 ****
--- 1421,1441 ----
  #endif
  
  /*
+  * Return TRUE when the message popup window should be used.
+  */
+     int
+ use_message_window(void)
+ {
+ #ifdef HAS_MESSAGE_WINDOW
+     // TRUE if there is no command line showing ('cmdheight' is zero and not
+     // already editing or showing a message) use a popup window for messages.
+     return p_ch == 0 && cmdline_row >= Rows;
+ #else
+     return FALSE;
+ #endif
+ }
+ 
+ /*
   * Prepare for outputting characters in the command line.
   */
      void
***************
*** 1444,1450 ****
      }
  #endif
  
!     if (!msg_scroll && full_screen)   // overwrite last message
      {
        msg_row = cmdline_row;
        msg_col =
--- 1459,1478 ----
      }
  #endif
  
!     if (use_message_window())
!     {
!       if (popup_message_win_visible() && msg_col > 0)
!       {
!           win_T *wp = popup_get_message_win();
! 
!           curbuf = wp->w_buffer;
!           ml_append(wp->w_buffer->b_ml.ml_line_count,
!                                             (char_u *)"", (colnr_T)0, FALSE);
!           curbuf = curwin->w_buffer;
!       }
!       msg_col = 0;
!     }
!     else if (!msg_scroll && full_screen)      // overwrite last message
      {
        msg_row = cmdline_row;
        msg_col =
***************
*** 2195,2200 ****
--- 2223,2268 ----
      need_fileinfo = FALSE;
  }
  
+ // values for "where"
+ #define PUT_APPEND 0          // append to "lnum"
+ #define PUT_TRUNC 1           // replace "lnum"
+ #define PUT_BELOW 2           // add below "lnum"
+                               //
+ #ifdef HAS_MESSAGE_WINDOW
+ 
+ /*
+  * Put text "t_s" until "s" in the message window.
+  * "where" specifies where to put the text.
+  */
+     static void
+ put_msg_win(win_T *wp, int where, char_u *t_s, char_u *end, linenr_T lnum)
+ {
+     int           c = *end;
+ 
+     *end = NUL;
+     if (where == PUT_BELOW)
+       ml_append_buf(wp->w_buffer, lnum, t_s, (colnr_T)0, FALSE);
+     else
+     {
+       char_u *newp;
+ 
+       curbuf = wp->w_buffer;
+       if (where == PUT_APPEND)
+           newp = concat_str(ml_get(lnum), t_s);
+       else
+           newp = vim_strnsave(t_s, end - t_s);
+       ml_replace(lnum, newp, FALSE);
+       curbuf = curwin->w_buffer;
+     }
+     redraw_win_later(wp, UPD_NOT_VALID);
+ 
+     // set msg_col so that a newline is written if needed
+     msg_col = STRLEN(t_s);
+ 
+     *end = c;
+ }
+ #endif
+ 
  /*
   * The display part of msg_puts_attr_len().
   * May be called recursively to display scroll-back text.
***************
*** 2215,2220 ****
--- 2283,2325 ----
      int               sb_col = msg_col;
      int               wrap;
      int               did_last_char;
+     int               where = PUT_APPEND;
+ #ifdef HAS_MESSAGE_WINDOW
+     win_T     *msg_win = NULL;
+     linenr_T    lnum = 1;
+ 
+     if (use_message_window())
+     {
+       msg_win = popup_get_message_win();
+ 
+       if (msg_win != NULL)
+       {
+           if (!popup_message_win_visible())
+           {
+               if (*str == NL)
+               {
+                   // When not showing the message window and the output
+                   // starts with a NL show the message normally.
+                   msg_win = NULL;
+               }
+               else
+               {
+                   // currently hidden, make it empty
+                   curbuf = msg_win->w_buffer;
+                   while ((curbuf->b_ml.ml_flags & ML_EMPTY) == 0)
+                       ml_delete(1);
+                   curbuf = curwin->w_buffer;
+               }
+           }
+           else
+           {
+               lnum = msg_win->w_buffer->b_ml.ml_line_count;
+               if (msg_col == 0)
+                   where = PUT_TRUNC;
+           }
+       }
+     }
+ #endif
  
      did_wait_return = FALSE;
      while ((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL)
***************
*** 2244,2258 ****
             * ourselves).
             */
            if (t_col > 0)
                // output postponed text
!               t_puts(&t_col, t_s, s, attr);
  
            // When no more prompt and no more room, truncate here
            if (msg_no_more && lines_left == 0)
                break;
  
!           // Scroll the screen up one line.
!           msg_scroll_up();
  
            msg_row = Rows - 2;
            if (msg_col >= Columns)     // can happen after screen resize
--- 2349,2377 ----
             * ourselves).
             */
            if (t_col > 0)
+           {
                // output postponed text
! #ifdef HAS_MESSAGE_WINDOW
!               if (msg_win != NULL)
!               {
!                   put_msg_win(msg_win, where, t_s, s, lnum);
!                   t_col = 0;
!                   where = PUT_BELOW;
!               }
!               else
! #endif
!                   t_puts(&t_col, t_s, s, attr);
!           }
  
            // When no more prompt and no more room, truncate here
            if (msg_no_more && lines_left == 0)
                break;
  
! #ifdef HAS_MESSAGE_WINDOW
!           if (msg_win == NULL)
! #endif
!               // Scroll the screen up one line.
!               msg_scroll_up();
  
            msg_row = Rows - 2;
            if (msg_col >= Columns)     // can happen after screen resize
***************
*** 2285,2302 ****
                // store text for scrolling back
                store_sb_text(&sb_str, s, attr, &sb_col, TRUE);
  
!           inc_msg_scrolled();
!           need_wait_return = TRUE; // may need wait_return in main()
!           redraw_cmdline = TRUE;
!           if (cmdline_row > 0 && !exmode_active)
!               --cmdline_row;
! 
!           /*
!            * If screen is completely filled and 'more' is set then wait
!            * for a character.
!            */
!           if (lines_left > 0)
!               --lines_left;
            if (p_more && lines_left == 0 && State != MODE_HITRETURN
                                            && !msg_no_more && !exmode_active)
            {
--- 2404,2428 ----
                // store text for scrolling back
                store_sb_text(&sb_str, s, attr, &sb_col, TRUE);
  
! #ifdef HAS_MESSAGE_WINDOW
!           if (msg_win == NULL)
!           {
! #endif
!               inc_msg_scrolled();
!               need_wait_return = TRUE; // may need wait_return in main()
!               redraw_cmdline = TRUE;
!               if (cmdline_row > 0 && !exmode_active)
!                   --cmdline_row;
! 
!               /*
!                * If screen is completely filled and 'more' is set then wait
!                * for a character.
!                */
!               if (lines_left > 0)
!                   --lines_left;
! #ifdef HAS_MESSAGE_WINDOW
!           }
! #endif
            if (p_more && lines_left == 0 && State != MODE_HITRETURN
                                            && !msg_no_more && !exmode_active)
            {
***************
*** 2322,2329 ****
                                            && msg_col + t_col >= Columns - 1);
        if (t_col > 0 && (wrap || *s == '\r' || *s == '\b'
                                                 || *s == '\t' || *s == BELL))
            // output any postponed text
!           t_puts(&t_col, t_s, s, attr);
  
        if (wrap && p_more && !recurse)
            // store text for scrolling back
--- 2448,2466 ----
                                            && msg_col + t_col >= Columns - 1);
        if (t_col > 0 && (wrap || *s == '\r' || *s == '\b'
                                                 || *s == '\t' || *s == BELL))
+       {
            // output any postponed text
! #ifdef HAS_MESSAGE_WINDOW
!           if (msg_win != NULL)
!           {
!               put_msg_win(msg_win, where, t_s, s, lnum);
!               t_col = 0;
!               where = PUT_BELOW;
!           }
!           else
! #endif
!               t_puts(&t_col, t_s, s, attr);
!       }
  
        if (wrap && p_more && !recurse)
            // store text for scrolling back
***************
*** 2331,2337 ****
  
        if (*s == '\n')             // go to next line
        {
!           msg_didout = FALSE;     // remember that line is empty
  #ifdef FEAT_RIGHTLEFT
            if (cmdmsg_rl)
                msg_col = Columns - 1;
--- 2468,2487 ----
  
        if (*s == '\n')             // go to next line
        {
! #ifdef HAS_MESSAGE_WINDOW
!           if (msg_win != NULL)
!           {
!               // Ignore a NL when the buffer is empty, it is used to scroll
!               // up the text.
!               if ((msg_win->w_buffer->b_ml.ml_flags & ML_EMPTY) == 0)
!               {
!                   put_msg_win(msg_win, PUT_BELOW, t_s, t_s, lnum);
!                   ++lnum;
!               }
!           }
!           else
! #endif
!               msg_didout = FALSE;         // remember that line is empty
  #ifdef FEAT_RIGHTLEFT
            if (cmdmsg_rl)
                msg_col = Columns - 1;
***************
*** 2344,2349 ****
--- 2494,2500 ----
        else if (*s == '\r')        // go to column 0
        {
            msg_col = 0;
+           where = PUT_TRUNC;
        }
        else if (*s == '\b')        // go to previous char
        {
***************
*** 2352,2360 ****
        }
        else if (*s == TAB)         // translate Tab into spaces
        {
!           do
!               msg_screen_putchar(' ', attr);
!           while (msg_col & 7);
        }
        else if (*s == BELL)            // beep (from ":sh")
            vim_beep(BO_SH);
--- 2503,2516 ----
        }
        else if (*s == TAB)         // translate Tab into spaces
        {
! #ifdef HAS_MESSAGE_WINDOW
!           if (msg_win != NULL)
!               msg_col = (msg_col + 7) % 8;
!           else
! #endif
!               do
!                   msg_screen_putchar(' ', attr);
!               while (msg_col & 7);
        }
        else if (*s == BELL)            // beep (from ":sh")
            vim_beep(BO_SH);
***************
*** 2403,2409 ****
  
      // output any postponed text
      if (t_col > 0)
!       t_puts(&t_col, t_s, s, attr);
      if (p_more && !recurse)
        store_sb_text(&sb_str, s, attr, &sb_col, FALSE);
  
--- 2559,2577 ----
  
      // output any postponed text
      if (t_col > 0)
!     {
! #ifdef HAS_MESSAGE_WINDOW
!       if (msg_win != NULL)
!           put_msg_win(msg_win, where, t_s, s, lnum);
!       else
! #endif
!           t_puts(&t_col, t_s, s, attr);
!     }
! 
! #ifdef HAS_MESSAGE_WINDOW
!     if (msg_win != NULL)
!       popup_show_message_win();
! #endif
      if (p_more && !recurse)
        store_sb_text(&sb_str, s, attr, &sb_col, FALSE);
  
***************
*** 2431,2436 ****
--- 2599,2608 ----
      static void
  msg_scroll_up(void)
  {
+ #ifdef HAS_MESSAGE_WINDOW
+     if (use_message_window())
+       return;
+ #endif
  #ifdef FEAT_GUI
      // Remove the cursor before scrolling, ScreenLines[] is going
      // to become invalid.
*** ../vim-9.0.0287/src/proto/message.pro       2022-07-28 12:34:06.527917764 
+0100
--- src/proto/message.pro       2022-08-27 14:14:38.608460926 +0100
***************
*** 23,28 ****
--- 23,29 ----
  void wait_return(int redraw);
  void set_keep_msg(char_u *s, int attr);
  void set_keep_msg_from_hist(void);
+ int use_message_window(void);
  void msg_start(void);
  void msg_starthere(void);
  void msg_putchar(int c);
*** ../vim-9.0.0287/src/screen.c        2022-08-26 16:58:46.139489368 +0100
--- src/screen.c        2022-08-27 17:52:56.821625921 +0100
***************
*** 4211,4220 ****
      int               nwr_save;
      int               sub_attr;
  
!     do_mode = ((p_smd && msg_silent == 0)
            && ((State & MODE_INSERT)
                || restart_edit != NUL
!               || VIsual_active));
      if (do_mode || reg_recording != 0)
      {
        if (skip_showmode())
--- 4211,4220 ----
      int               nwr_save;
      int               sub_attr;
  
!     do_mode = p_smd && msg_silent == 0 && p_ch > 0
            && ((State & MODE_INSERT)
                || restart_edit != NUL
!               || VIsual_active);
      if (do_mode || reg_recording != 0)
      {
        if (skip_showmode())
*** ../vim-9.0.0287/src/testdir/test_messages.vim       2022-08-11 
14:13:14.862778345 +0100
--- src/testdir/test_messages.vim       2022-08-27 19:01:49.971950399 +0100
***************
*** 392,409 ****
    set cmdheight=0
    set showcmd
    redraw!
  
    echo 'test echo'
!   call assert_equal(116, screenchar(&lines, 1))
    redraw!
  
    echomsg 'test echomsg'
!   call assert_equal(116, screenchar(&lines, 1))
    redraw!
  
!   call feedkeys(":ls\<CR>", "xt")
!   call assert_equal(':ls', Screenline(&lines - 1))
!   redraw!
  
    let char = getchar(0)
    call assert_match(char, 0)
--- 392,422 ----
    set cmdheight=0
    set showcmd
    redraw!
+   let using_popupwin = has('timers') && has('popupwin')
  
    echo 'test echo'
!   if using_popupwin
!     redraw
!     call assert_equal('test echo', Screenline(&lines))
!   else
!     call assert_equal(116, screenchar(&lines, 1))
!   endif
    redraw!
  
    echomsg 'test echomsg'
!   if using_popupwin
!     redraw
!     call assert_equal('test echomsg', Screenline(&lines))
!   else
!     call assert_equal(116, screenchar(&lines, 1))
!   endif
    redraw!
  
!   if !using_popupwin
!     call feedkeys(":ls\<CR>", "xt")
!     call assert_equal(':ls', Screenline(&lines))
!     redraw!
!   endif
  
    let char = getchar(0)
    call assert_match(char, 0)
***************
*** 420,425 ****
--- 433,441 ----
    call assert_equal('otherstring', getline(1))
  
    call feedkeys("g\<C-g>", "xt")
+   if using_popupwin
+     redraw
+   endif
    call assert_match(
          \ 'Col 1 of 11; Line 1 of 1; Word 1 of 1',
          \ Screenline(&lines))
*** ../vim-9.0.0287/src/version.c       2022-08-27 21:24:22.709002324 +0100
--- src/version.c       2022-08-27 21:26:22.755363314 +0100
***************
*** 709,710 ****
--- 709,712 ----
  {   /* Add new patch number below this line */
+ /**/
+     288,
  /**/

-- 
Behold the warranty!  The bold print giveth and the fine print taketh.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220827203036.152F11C066C%40moolenaar.net.

Raspunde prin e-mail lui