mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	@@ -4969,6 +4969,8 @@ MatchParen	The character under the cursor or just before it, if it
 | 
			
		||||
 | 
			
		||||
							*hl-ModeMsg*
 | 
			
		||||
ModeMsg		'showmode' message (e.g., "-- INSERT --")
 | 
			
		||||
							*hl-MsgArea*
 | 
			
		||||
MsgArea		Area for messages and cmdline
 | 
			
		||||
							*hl-MsgSeparator*
 | 
			
		||||
MsgSeparator	Separator for scrolled messages, `msgsep` flag of 'display'
 | 
			
		||||
							*hl-MoreMsg*
 | 
			
		||||
 
 | 
			
		||||
@@ -543,6 +543,8 @@ The multigrid extension gives UIs more control over how windows are displayed:
 | 
			
		||||
  occupies on the global layout. So the UI could use a different font size
 | 
			
		||||
  per-window. Or reserve space around the border of the window for its own
 | 
			
		||||
  elements, such as scrollbars from the UI toolkit.
 | 
			
		||||
- A dedicated grid is used for messages, which may scroll over the window
 | 
			
		||||
  area. (Alternatively |ext_messages| can be used).
 | 
			
		||||
 | 
			
		||||
By default, the grid size is handled by Nvim and set to the outer grid size
 | 
			
		||||
(i.e. the size of the window frame in Nvim) whenever the split is created.
 | 
			
		||||
@@ -573,19 +575,20 @@ tabs.
 | 
			
		||||
["win_hide", grid]
 | 
			
		||||
	Stop displaying the window. The window can be shown again later.
 | 
			
		||||
 | 
			
		||||
["win_scroll_over_start"]
 | 
			
		||||
	Hint that following `grid_scroll` on the default grid should
 | 
			
		||||
	scroll over windows. This is a temporary workaround to allow
 | 
			
		||||
	UIs to use the builtin message drawing. Later on, messages will be
 | 
			
		||||
	drawn on a dedicated grid. Using |ui-messages| also avoids this issue.
 | 
			
		||||
 | 
			
		||||
["win_scroll_over_reset"]
 | 
			
		||||
	Hint that scrolled over windows should be redrawn again, and not be
 | 
			
		||||
	overdrawn by default grid scrolling anymore.
 | 
			
		||||
 | 
			
		||||
["win_close", grid]
 | 
			
		||||
	Close the window.
 | 
			
		||||
 | 
			
		||||
["msg_set_pos", grid, row, scrolled, sep_char]
 | 
			
		||||
	Display messages on `grid`.  The grid will be displayed at `row` on the
 | 
			
		||||
	default grid (grid=1), covering the full column width. `scrolled`
 | 
			
		||||
	indicates whether the message area has been scrolled to cover other
 | 
			
		||||
	grids. It can be useful to draw a separator then ('display' msgsep
 | 
			
		||||
	flag). The Builtin TUI draws a full line filled with `sep_char` and
 | 
			
		||||
	|hl-MsgSeparator| highlight.
 | 
			
		||||
 | 
			
		||||
	When |ext_messages| is active, no message grid is used, and this event
 | 
			
		||||
	will not be sent.
 | 
			
		||||
 | 
			
		||||
==============================================================================
 | 
			
		||||
Popupmenu Events						 *ui-popupmenu*
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -133,8 +133,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
 | 
			
		||||
  ui->set_title = remote_ui_set_title;
 | 
			
		||||
  ui->set_icon = remote_ui_set_icon;
 | 
			
		||||
  ui->option_set = remote_ui_option_set;
 | 
			
		||||
  ui->win_scroll_over_start = remote_ui_win_scroll_over_start;
 | 
			
		||||
  ui->win_scroll_over_reset = remote_ui_win_scroll_over_reset;
 | 
			
		||||
  ui->msg_set_pos = remote_ui_msg_set_pos;
 | 
			
		||||
  ui->event = remote_ui_event;
 | 
			
		||||
  ui->inspect = remote_ui_inspect;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -112,9 +112,7 @@ void win_hide(Integer grid)
 | 
			
		||||
  FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
 | 
			
		||||
void win_close(Integer grid)
 | 
			
		||||
  FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
 | 
			
		||||
void win_scroll_over_start(void)
 | 
			
		||||
  FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL;
 | 
			
		||||
void win_scroll_over_reset(void)
 | 
			
		||||
void msg_set_pos(Integer grid, Integer row, Boolean scrolled, String sep_char)
 | 
			
		||||
  FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL;
 | 
			
		||||
 | 
			
		||||
void popupmenu_show(Array items, Integer selected,
 | 
			
		||||
 
 | 
			
		||||
@@ -2654,8 +2654,7 @@ void buflist_list(exarg_T *eap)
 | 
			
		||||
        buf == curbuf ? (int64_t)curwin->w_cursor.lnum
 | 
			
		||||
                      : (int64_t)buflist_findlnum(buf));
 | 
			
		||||
    msg_outtrans(IObuff);
 | 
			
		||||
    ui_flush();            // output one line at a time
 | 
			
		||||
    os_breakcheck();
 | 
			
		||||
    line_breakcheck();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14648,6 +14648,21 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void screenchar_adjust_grid(ScreenGrid **grid, int *row, int *col)
 | 
			
		||||
{
 | 
			
		||||
  // TODO(bfredl): this is a hack for legacy tests which use screenchar()
 | 
			
		||||
  // to check printed messages on the screen (but not floats etc
 | 
			
		||||
  // as these are not legacy features). If the compositor is refactored to
 | 
			
		||||
  // have its own buffer, this should just read from it instead.
 | 
			
		||||
  msg_scroll_flush();
 | 
			
		||||
  if (msg_grid.chars && msg_grid.comp_index > 0 && *row >= msg_grid.comp_row
 | 
			
		||||
      && *row < (msg_grid.Rows + msg_grid.comp_row)
 | 
			
		||||
      && *col < msg_grid.Columns) {
 | 
			
		||||
    *grid = &msg_grid;
 | 
			
		||||
    *row -= msg_grid.comp_row;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * "screenattr()" function
 | 
			
		||||
 */
 | 
			
		||||
@@ -14655,13 +14670,15 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
 | 
			
		||||
{
 | 
			
		||||
  int c;
 | 
			
		||||
 | 
			
		||||
  const int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1;
 | 
			
		||||
  const int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1;
 | 
			
		||||
  int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1;
 | 
			
		||||
  int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1;
 | 
			
		||||
  if (row < 0 || row >= default_grid.Rows
 | 
			
		||||
      || col < 0 || col >= default_grid.Columns) {
 | 
			
		||||
    c = -1;
 | 
			
		||||
  } else {
 | 
			
		||||
    c = default_grid.attrs[default_grid.line_offset[row] + col];
 | 
			
		||||
    ScreenGrid *grid = &default_grid;
 | 
			
		||||
    screenchar_adjust_grid(&grid, &row, &col);
 | 
			
		||||
    c = grid->attrs[grid->line_offset[row] + col];
 | 
			
		||||
  }
 | 
			
		||||
  rettv->vval.v_number = c;
 | 
			
		||||
}
 | 
			
		||||
@@ -14671,17 +14688,17 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
 | 
			
		||||
 */
 | 
			
		||||
static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
 | 
			
		||||
{
 | 
			
		||||
  int off;
 | 
			
		||||
  int c;
 | 
			
		||||
 | 
			
		||||
  const int row = tv_get_number_chk(&argvars[0], NULL) - 1;
 | 
			
		||||
  const int col = tv_get_number_chk(&argvars[1], NULL) - 1;
 | 
			
		||||
  int row = tv_get_number_chk(&argvars[0], NULL) - 1;
 | 
			
		||||
  int col = tv_get_number_chk(&argvars[1], NULL) - 1;
 | 
			
		||||
  if (row < 0 || row >= default_grid.Rows
 | 
			
		||||
      || col < 0 || col >= default_grid.Columns) {
 | 
			
		||||
    c = -1;
 | 
			
		||||
  } else {
 | 
			
		||||
    off = default_grid.line_offset[row] + col;
 | 
			
		||||
    c = utf_ptr2char(default_grid.chars[off]);
 | 
			
		||||
    ScreenGrid *grid = &default_grid;
 | 
			
		||||
    screenchar_adjust_grid(&grid, &row, &col);
 | 
			
		||||
    c = utf_ptr2char(grid->chars[grid->line_offset[row] + col]);
 | 
			
		||||
  }
 | 
			
		||||
  rettv->vval.v_number = c;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -310,6 +310,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
 | 
			
		||||
    cmdmsg_rl = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  msg_grid_validate();
 | 
			
		||||
 | 
			
		||||
  redir_off = true;             // don't redirect the typed command
 | 
			
		||||
  if (!cmd_silent) {
 | 
			
		||||
    gotocmdline(true);
 | 
			
		||||
@@ -908,7 +910,7 @@ static int command_line_execute(VimState *state, int key)
 | 
			
		||||
 | 
			
		||||
      if (!cmd_silent) {
 | 
			
		||||
        if (!ui_has(kUICmdline)) {
 | 
			
		||||
          ui_cursor_goto(msg_row, 0);
 | 
			
		||||
          cmd_cursor_goto(msg_row, 0);
 | 
			
		||||
        }
 | 
			
		||||
        ui_flush();
 | 
			
		||||
      }
 | 
			
		||||
@@ -2323,7 +2325,7 @@ redraw:
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        msg_clr_eos();
 | 
			
		||||
        ui_cursor_goto(msg_row, msg_col);
 | 
			
		||||
        cmd_cursor_goto(msg_row, msg_col);
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@@ -2391,7 +2393,7 @@ redraw:
 | 
			
		||||
    line_ga.ga_len += len;
 | 
			
		||||
    escaped = FALSE;
 | 
			
		||||
 | 
			
		||||
    ui_cursor_goto(msg_row, msg_col);
 | 
			
		||||
    cmd_cursor_goto(msg_row, msg_col);
 | 
			
		||||
    pend = (char_u *)(line_ga.ga_data) + line_ga.ga_len;
 | 
			
		||||
 | 
			
		||||
    /* We are done when a NL is entered, but not when it comes after an
 | 
			
		||||
@@ -3436,7 +3438,7 @@ void redrawcmd(void)
 | 
			
		||||
 | 
			
		||||
  /* when 'incsearch' is set there may be no command line while redrawing */
 | 
			
		||||
  if (ccline.cmdbuff == NULL) {
 | 
			
		||||
    ui_cursor_goto(cmdline_row, 0);
 | 
			
		||||
    cmd_cursor_goto(cmdline_row, 0);
 | 
			
		||||
    msg_clr_eos();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
@@ -3510,7 +3512,14 @@ static void cursorcmd(void)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ui_cursor_goto(msg_row, msg_col);
 | 
			
		||||
  cmd_cursor_goto(msg_row, msg_col);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cmd_cursor_goto(int row, int col)
 | 
			
		||||
{
 | 
			
		||||
  ScreenGrid *grid = &msg_grid_adj;
 | 
			
		||||
  screen_adjust_grid(&grid, &row, &col);
 | 
			
		||||
  ui_grid_cursor_goto(grid->handle, row, col);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gotocmdline(int clr)
 | 
			
		||||
@@ -3519,13 +3528,15 @@ void gotocmdline(int clr)
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  msg_start();
 | 
			
		||||
  if (cmdmsg_rl)
 | 
			
		||||
  if (cmdmsg_rl) {
 | 
			
		||||
    msg_col = Columns - 1;
 | 
			
		||||
  else
 | 
			
		||||
    msg_col = 0;            /* always start in column 0 */
 | 
			
		||||
  if (clr)                  /* clear the bottom line(s) */
 | 
			
		||||
    msg_clr_eos();          /* will reset clear_cmdline */
 | 
			
		||||
  ui_cursor_goto(cmdline_row, 0);
 | 
			
		||||
  } else {
 | 
			
		||||
    msg_col = 0;  // always start in column 0
 | 
			
		||||
  }
 | 
			
		||||
  if (clr) {  // clear the bottom line(s)
 | 
			
		||||
    msg_clr_eos();  // will reset clear_cmdline
 | 
			
		||||
  }
 | 
			
		||||
  cmd_cursor_goto(cmdline_row, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,10 @@ typedef struct {
 | 
			
		||||
  unsigned *line_offset;
 | 
			
		||||
  char_u   *line_wraps;
 | 
			
		||||
 | 
			
		||||
  // last column that was drawn (not cleared with the default background).
 | 
			
		||||
  // only used when "throttled" is set. Not allocated by grid_alloc!
 | 
			
		||||
  int *dirty_col;
 | 
			
		||||
 | 
			
		||||
  // the size of the allocated grid.
 | 
			
		||||
  int Rows;
 | 
			
		||||
  int Columns;
 | 
			
		||||
@@ -50,21 +54,38 @@ typedef struct {
 | 
			
		||||
  // The state of the grid is valid. Otherwise it needs to be redrawn.
 | 
			
		||||
  bool valid;
 | 
			
		||||
 | 
			
		||||
  // offsets for the grid relative to the global screen
 | 
			
		||||
  // only draw internally and don't send updates yet to the compositor or
 | 
			
		||||
  // external UI.
 | 
			
		||||
  bool throttled;
 | 
			
		||||
 | 
			
		||||
  // offsets for the grid relative to the global screen. Used by screen.c
 | 
			
		||||
  // for windows that don't have w_grid->chars etc allocated
 | 
			
		||||
  int row_offset;
 | 
			
		||||
  int col_offset;
 | 
			
		||||
 | 
			
		||||
  // whether the compositor should blend the grid with the background grid
 | 
			
		||||
  bool blending;
 | 
			
		||||
 | 
			
		||||
  // state owned by the compositor.
 | 
			
		||||
  // whether the grid can be focused with mouse clicks.
 | 
			
		||||
  bool focusable;
 | 
			
		||||
 | 
			
		||||
  // Below is state owned by the compositor. Should generally not be set/read
 | 
			
		||||
  // outside this module, except for specific compatibilty hacks
 | 
			
		||||
 | 
			
		||||
  // position of the grid on the composed screen.
 | 
			
		||||
  int comp_row;
 | 
			
		||||
  int comp_col;
 | 
			
		||||
 | 
			
		||||
  // z-index of the grid. Grids with higher index is draw on top.
 | 
			
		||||
  // default_grid.comp_index is always zero.
 | 
			
		||||
  size_t comp_index;
 | 
			
		||||
 | 
			
		||||
  // compositor should momentarily ignore the grid. Used internally when
 | 
			
		||||
  // moving around grids etc.
 | 
			
		||||
  bool comp_disabled;
 | 
			
		||||
} ScreenGrid;
 | 
			
		||||
 | 
			
		||||
#define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, 0, 0, false, 0, 0, \
 | 
			
		||||
                           false, 0, 0, 0,  false }
 | 
			
		||||
#define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, false, \
 | 
			
		||||
                           false, 0, 0, false, true, 0, 0, 0,  false }
 | 
			
		||||
 | 
			
		||||
#endif  // NVIM_GRID_DEFS_H
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@
 | 
			
		||||
#include "nvim/highlight.h"
 | 
			
		||||
#include "nvim/highlight_defs.h"
 | 
			
		||||
#include "nvim/map.h"
 | 
			
		||||
#include "nvim/message.h"
 | 
			
		||||
#include "nvim/popupmnu.h"
 | 
			
		||||
#include "nvim/screen.h"
 | 
			
		||||
#include "nvim/syntax.h"
 | 
			
		||||
@@ -161,6 +162,8 @@ int hl_get_ui_attr(int idx, int final_id, bool optional)
 | 
			
		||||
    if (pum_drawn()) {
 | 
			
		||||
      must_redraw_pum = true;
 | 
			
		||||
    }
 | 
			
		||||
  } else if (idx == HLF_MSG) {
 | 
			
		||||
    msg_grid.blending = attrs.hl_blend > -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (optional && !available) {
 | 
			
		||||
 
 | 
			
		||||
@@ -93,6 +93,7 @@ typedef enum {
 | 
			
		||||
  , HLF_INACTIVE    // NormalNC: Normal text in non-current windows
 | 
			
		||||
  , HLF_MSGSEP      // message separator line
 | 
			
		||||
  , HLF_NFLOAT      // Floating window
 | 
			
		||||
  , HLF_MSG         // Message area
 | 
			
		||||
  , HLF_COUNT       // MUST be the last one
 | 
			
		||||
} hlf_T;
 | 
			
		||||
 | 
			
		||||
@@ -146,6 +147,7 @@ EXTERN const char *hlf_names[] INIT(= {
 | 
			
		||||
  [HLF_INACTIVE] = "NormalNC",
 | 
			
		||||
  [HLF_MSGSEP] = "MsgSeparator",
 | 
			
		||||
  [HLF_NFLOAT] = "NormalFloat",
 | 
			
		||||
  [HLF_MSG] = "MsgArea",
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3523,10 +3523,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname,
 | 
			
		||||
            xfree(name);
 | 
			
		||||
 | 
			
		||||
            // pretend screen didn't scroll, need redraw anyway
 | 
			
		||||
            // TODO(bfredl): when doing the message grid refactor,
 | 
			
		||||
            // simplify this special case.
 | 
			
		||||
            msg_scrolled = 0;
 | 
			
		||||
            redraw_all_later(NOT_VALID);
 | 
			
		||||
            msg_reset_scroll();
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (choice > 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,9 @@
 | 
			
		||||
#include "nvim/screen.h"
 | 
			
		||||
#include "nvim/strings.h"
 | 
			
		||||
#include "nvim/syntax.h"
 | 
			
		||||
#include "nvim/highlight.h"
 | 
			
		||||
#include "nvim/ui.h"
 | 
			
		||||
#include "nvim/ui_compositor.h"
 | 
			
		||||
#include "nvim/mouse.h"
 | 
			
		||||
#include "nvim/os/os.h"
 | 
			
		||||
#include "nvim/os/input.h"
 | 
			
		||||
@@ -124,6 +126,75 @@ static int msg_ext_visible = 0;  ///< number of messages currently visible
 | 
			
		||||
/// Shouldn't clear message after leaving cmdline
 | 
			
		||||
static bool msg_ext_keep_after_cmdline = false;
 | 
			
		||||
 | 
			
		||||
static int msg_grid_pos_at_flush = 0;
 | 
			
		||||
static int msg_grid_scroll_discount = 0;
 | 
			
		||||
 | 
			
		||||
static void ui_ext_msg_set_pos(int row, bool scrolled)
 | 
			
		||||
{
 | 
			
		||||
  char buf[MAX_MCO];
 | 
			
		||||
  size_t size = utf_char2bytes(curwin->w_p_fcs_chars.msgsep, (char_u *)buf);
 | 
			
		||||
  buf[size] = '\0';
 | 
			
		||||
  ui_call_msg_set_pos(msg_grid.handle, row, scrolled,
 | 
			
		||||
                      (String){ .data = buf, .size = size });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void msg_grid_set_pos(int row, bool scrolled)
 | 
			
		||||
{
 | 
			
		||||
  if (!msg_grid.throttled) {
 | 
			
		||||
    ui_ext_msg_set_pos(row, scrolled);
 | 
			
		||||
    msg_grid_pos_at_flush = row;
 | 
			
		||||
  }
 | 
			
		||||
  msg_grid_pos = row;
 | 
			
		||||
  if (msg_grid.chars) {
 | 
			
		||||
    msg_grid_adj.row_offset = -row;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void msg_grid_validate(void)
 | 
			
		||||
{
 | 
			
		||||
  grid_assign_handle(&msg_grid);
 | 
			
		||||
  bool should_alloc = msg_dothrottle();
 | 
			
		||||
  if (msg_grid.Rows != Rows || msg_grid.Columns != Columns
 | 
			
		||||
      || (should_alloc && !msg_grid.chars)) {
 | 
			
		||||
    // TODO(bfredl): eventually should be set to "invalid". I e all callers
 | 
			
		||||
    // will use the grid including clear to EOS if necessary.
 | 
			
		||||
    grid_alloc(&msg_grid, Rows, Columns, false, true);
 | 
			
		||||
 | 
			
		||||
    xfree(msg_grid.dirty_col);
 | 
			
		||||
    msg_grid.dirty_col = xcalloc(Rows, sizeof(*msg_grid.dirty_col));
 | 
			
		||||
 | 
			
		||||
    // Tricky: allow resize while pager is active
 | 
			
		||||
    int pos = msg_scrolled ? msg_grid_pos : Rows - p_ch;
 | 
			
		||||
    ui_comp_put_grid(&msg_grid, pos, 0, msg_grid.Rows, msg_grid.Columns,
 | 
			
		||||
                     false, true);
 | 
			
		||||
    ui_call_grid_resize(msg_grid.handle, msg_grid.Columns, msg_grid.Rows);
 | 
			
		||||
 | 
			
		||||
    msg_grid.throttled = false;  // don't throttle in 'cmdheight' area
 | 
			
		||||
    msg_scrolled_at_flush = msg_scrolled;
 | 
			
		||||
    msg_grid.focusable = false;
 | 
			
		||||
    if (!msg_scrolled) {
 | 
			
		||||
      msg_grid_set_pos(Rows - p_ch, false);
 | 
			
		||||
    }
 | 
			
		||||
  } else if (!should_alloc && msg_grid.chars) {
 | 
			
		||||
    ui_comp_remove_grid(&msg_grid);
 | 
			
		||||
    grid_free(&msg_grid);
 | 
			
		||||
    XFREE_CLEAR(msg_grid.dirty_col);
 | 
			
		||||
    ui_call_grid_destroy(msg_grid.handle);
 | 
			
		||||
    msg_grid.throttled = false;
 | 
			
		||||
    msg_grid_adj.row_offset = 0;
 | 
			
		||||
    redraw_cmdline = true;
 | 
			
		||||
  } else if (msg_grid.chars && !msg_scrolled && msg_grid_pos != Rows - p_ch) {
 | 
			
		||||
    msg_grid_set_pos(Rows - p_ch, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (msg_grid.chars && cmdline_row < msg_grid_pos) {
 | 
			
		||||
    // TODO(bfredl): this should already be the case, but fails in some
 | 
			
		||||
    // "batched" executions where compute_cmdrow() use stale positions or
 | 
			
		||||
    // something.
 | 
			
		||||
    cmdline_row = msg_grid_pos;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * msg(s) - displays the string 's' on the status line
 | 
			
		||||
 * When terminal not initialized (yet) mch_errmsg(..) is used.
 | 
			
		||||
@@ -1701,6 +1772,7 @@ void msg_prt_line(char_u *s, int list)
 | 
			
		||||
static char_u *screen_puts_mbyte(char_u *s, int l, int attr)
 | 
			
		||||
{
 | 
			
		||||
  int cw;
 | 
			
		||||
  attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr);
 | 
			
		||||
 | 
			
		||||
  msg_didout = true;            // remember that line is not empty
 | 
			
		||||
  cw = utf_ptr2cells(s);
 | 
			
		||||
@@ -1711,7 +1783,7 @@ static char_u *screen_puts_mbyte(char_u *s, int l, int attr)
 | 
			
		||||
    return s;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  grid_puts_len(&default_grid, s, l, msg_row, msg_col, attr);
 | 
			
		||||
  grid_puts_len(&msg_grid_adj, s, l, msg_row, msg_col, attr);
 | 
			
		||||
  if (cmdmsg_rl) {
 | 
			
		||||
    msg_col -= cw;
 | 
			
		||||
    if (msg_col == 0) {
 | 
			
		||||
@@ -1900,6 +1972,8 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr,
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  msg_grid_validate();
 | 
			
		||||
 | 
			
		||||
  cmdline_was_last_drawn = redrawing_cmdline;
 | 
			
		||||
 | 
			
		||||
  while ((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL) {
 | 
			
		||||
@@ -1929,15 +2003,16 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr,
 | 
			
		||||
      if (msg_no_more && lines_left == 0)
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      /* Scroll the screen up one line. */
 | 
			
		||||
      msg_scroll_up();
 | 
			
		||||
      // Scroll the screen up one line.
 | 
			
		||||
      bool has_last_char = (*s >= ' ' && !cmdmsg_rl);
 | 
			
		||||
      msg_scroll_up(!has_last_char);
 | 
			
		||||
 | 
			
		||||
      msg_row = Rows - 2;
 | 
			
		||||
      if (msg_col >= Columns)           /* can happen after screen resize */
 | 
			
		||||
        msg_col = Columns - 1;
 | 
			
		||||
 | 
			
		||||
      // Display char in last column before showing more-prompt.
 | 
			
		||||
      if (*s >= ' ' && !cmdmsg_rl) {
 | 
			
		||||
      if (has_last_char) {
 | 
			
		||||
        if (maxlen >= 0) {
 | 
			
		||||
          // Avoid including composing chars after the end.
 | 
			
		||||
          l = utfc_ptr2len_len(s, (int)((str + maxlen) - s));
 | 
			
		||||
@@ -1950,6 +2025,15 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr,
 | 
			
		||||
        did_last_char = false;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Tricky: if last cell will be written, delay the throttle until
 | 
			
		||||
      // after the first scroll. Otherwise we would need to keep track of it.
 | 
			
		||||
      if (has_last_char && msg_dothrottle()) {
 | 
			
		||||
        if (!msg_grid.throttled) {
 | 
			
		||||
          msg_grid_scroll_discount++;
 | 
			
		||||
        }
 | 
			
		||||
        msg_grid.throttled = true;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (p_more) {
 | 
			
		||||
        // Store text for scrolling back.
 | 
			
		||||
        store_sb_text((char_u **)&sb_str, (char_u *)s, attr, &sb_col, true);
 | 
			
		||||
@@ -2074,29 +2158,121 @@ int msg_scrollsize(void)
 | 
			
		||||
  return msg_scrolled + p_ch + 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool msg_dothrottle(void)
 | 
			
		||||
{
 | 
			
		||||
  return default_grid.chars && msg_use_msgsep()
 | 
			
		||||
         && !ui_has(kUIMessages);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool msg_use_msgsep(void)
 | 
			
		||||
{
 | 
			
		||||
  // the full-screen scroll behavior doesn't really make sense with
 | 
			
		||||
  // 'ext_multigrid'
 | 
			
		||||
  return ((dy_flags & DY_MSGSEP) || ui_has(kUIMultigrid));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Scroll the screen up one line for displaying the next message line.
 | 
			
		||||
 */
 | 
			
		||||
void msg_scroll_up(void)
 | 
			
		||||
void msg_scroll_up(bool may_throttle)
 | 
			
		||||
{
 | 
			
		||||
  if (!msg_did_scroll) {
 | 
			
		||||
    ui_call_win_scroll_over_start();
 | 
			
		||||
  if (may_throttle && msg_dothrottle()) {
 | 
			
		||||
    msg_grid.throttled = true;
 | 
			
		||||
  }
 | 
			
		||||
  msg_did_scroll = true;
 | 
			
		||||
  }
 | 
			
		||||
  if (dy_flags & DY_MSGSEP) {
 | 
			
		||||
    if (msg_scrolled == 0) {
 | 
			
		||||
      grid_fill(&default_grid, Rows-p_ch-1, Rows-p_ch, 0, (int)Columns,
 | 
			
		||||
                curwin->w_p_fcs_chars.msgsep, curwin->w_p_fcs_chars.msgsep,
 | 
			
		||||
                HL_ATTR(HLF_MSGSEP));
 | 
			
		||||
    }
 | 
			
		||||
    int nscroll = MIN(msg_scrollsize()+1, Rows);
 | 
			
		||||
    grid_del_lines(&default_grid, Rows-nscroll, 1, Rows, 0, Columns);
 | 
			
		||||
  if (msg_use_msgsep()) {
 | 
			
		||||
    if (msg_grid_pos > 0) {
 | 
			
		||||
      msg_grid_set_pos(msg_grid_pos-1, true);
 | 
			
		||||
    } else {
 | 
			
		||||
    grid_del_lines(&default_grid, 0, 1, (int)Rows, 0, Columns);
 | 
			
		||||
      grid_del_lines(&msg_grid, 0, 1, msg_grid.Rows, 0, msg_grid.Columns);
 | 
			
		||||
      memmove(msg_grid.dirty_col, msg_grid.dirty_col+1,
 | 
			
		||||
              (msg_grid.Rows-1) * sizeof(*msg_grid.dirty_col));
 | 
			
		||||
      msg_grid.dirty_col[msg_grid.Rows-1] = 0;
 | 
			
		||||
    }
 | 
			
		||||
  // TODO(bfredl): when msgsep display is properly batched, this fill should be
 | 
			
		||||
  // eliminated.
 | 
			
		||||
  grid_fill(&default_grid, Rows-1, Rows, 0, (int)Columns, ' ', ' ', 0);
 | 
			
		||||
  } else {
 | 
			
		||||
    grid_del_lines(&msg_grid_adj, 0, 1, Rows, 0, Columns);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  grid_fill(&msg_grid_adj, Rows-1, Rows, 0, Columns, ' ', ' ',
 | 
			
		||||
            HL_ATTR(HLF_MSG));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Send throttled message output to UI clients
 | 
			
		||||
///
 | 
			
		||||
/// The way message.c uses the grid_xx family of functions is quite inefficient
 | 
			
		||||
/// relative to the "gridline" UI protocol used by TUI and modern clients.
 | 
			
		||||
/// For instance scrolling is done one line at a time. By throttling drawing
 | 
			
		||||
/// on the message grid, we can coalesce scrolling to a single grid_scroll
 | 
			
		||||
/// per screen update.
 | 
			
		||||
///
 | 
			
		||||
/// NB: The bookkeeping is quite messy, and rests on a bunch of poorly
 | 
			
		||||
/// documented assumtions. For instance that the message area always grows while
 | 
			
		||||
/// being throttled, messages are only being output on the last line etc.
 | 
			
		||||
///
 | 
			
		||||
/// Probably message scrollback storage should reimplented as a file_buffer, and
 | 
			
		||||
/// message scrolling in TUI be reimplemented as a modal floating window. Then
 | 
			
		||||
/// we get throttling "for free" using standard redraw_win_later code paths.
 | 
			
		||||
void msg_scroll_flush(void)
 | 
			
		||||
{
 | 
			
		||||
  if (!msg_grid.throttled) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  msg_grid.throttled = false;
 | 
			
		||||
  int pos_delta = msg_grid_pos_at_flush - msg_grid_pos;
 | 
			
		||||
  assert(pos_delta >= 0);
 | 
			
		||||
  int delta = MIN(msg_scrolled - msg_scrolled_at_flush, msg_grid.Rows);
 | 
			
		||||
 | 
			
		||||
  if (pos_delta > 0) {
 | 
			
		||||
    ui_ext_msg_set_pos(msg_grid_pos, true);
 | 
			
		||||
    msg_grid_pos_at_flush = msg_grid_pos;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int to_scroll = delta-pos_delta-msg_grid_scroll_discount;
 | 
			
		||||
  assert(to_scroll >= 0);
 | 
			
		||||
 | 
			
		||||
  // TODO(bfredl): msg_grid_pos should be 0 already when starting scrolling
 | 
			
		||||
  // but this sometimes fails in "headless" message printing.
 | 
			
		||||
  if (to_scroll > 0 && msg_grid_pos == 0) {
 | 
			
		||||
    ui_call_grid_scroll(msg_grid.handle, 0, Rows, 0, Columns, to_scroll, 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (int i = MAX(Rows-MAX(delta, 1), 0); i < Rows; i++) {
 | 
			
		||||
    int row = i-msg_grid_pos;
 | 
			
		||||
    assert(row >= 0);
 | 
			
		||||
    ui_line(&msg_grid, row, 0, msg_grid.dirty_col[row], msg_grid.Columns,
 | 
			
		||||
            HL_ATTR(HLF_MSG), false);
 | 
			
		||||
    msg_grid.dirty_col[row] = 0;
 | 
			
		||||
  }
 | 
			
		||||
  msg_scrolled_at_flush = msg_scrolled;
 | 
			
		||||
  msg_grid_scroll_discount = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void msg_reset_scroll(void)
 | 
			
		||||
{
 | 
			
		||||
  if (ui_has(kUIMessages)) {
 | 
			
		||||
    msg_ext_clear(true);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  // TODO(bfredl): some duplicate logic with update_screen(). Later on
 | 
			
		||||
  // we should properly disentangle message clear with full screen redraw.
 | 
			
		||||
  if (msg_dothrottle()) {
 | 
			
		||||
    msg_grid.throttled = false;
 | 
			
		||||
    // TODO(bfredl): risk for extra flicker i e with
 | 
			
		||||
    // "nvim -o has_swap also_has_swap"
 | 
			
		||||
    msg_grid_set_pos(Rows - p_ch, false);
 | 
			
		||||
    clear_cmdline = true;
 | 
			
		||||
    if (msg_grid.chars) {
 | 
			
		||||
      // non-displayed part of msg_grid is considered invalid.
 | 
			
		||||
      for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.Rows); i++) {
 | 
			
		||||
        grid_clear_line(&msg_grid, msg_grid.line_offset[i],
 | 
			
		||||
                        (int)msg_grid.Columns, false);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    redraw_all_later(NOT_VALID);
 | 
			
		||||
  }
 | 
			
		||||
  msg_scrolled = 0;
 | 
			
		||||
  msg_scrolled_at_flush = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -2285,6 +2461,11 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp)
 | 
			
		||||
      break;
 | 
			
		||||
    mp = mp->sb_next;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (msg_col < Columns) {
 | 
			
		||||
    grid_fill(&msg_grid_adj, row, row+1, msg_col, Columns, ' ', ' ',
 | 
			
		||||
              HL_ATTR(HLF_MSG));
 | 
			
		||||
  }
 | 
			
		||||
  return mp->sb_next;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2293,9 +2474,10 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp)
 | 
			
		||||
 */
 | 
			
		||||
static void t_puts(int *t_col, const char_u *t_s, const char_u *s, int attr)
 | 
			
		||||
{
 | 
			
		||||
  attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr);
 | 
			
		||||
  // Output postponed text.
 | 
			
		||||
  msg_didout = true;  // Remember that line is not empty.
 | 
			
		||||
  grid_puts_len(&default_grid, (char_u *)t_s, (int)(s - t_s), msg_row, msg_col,
 | 
			
		||||
  grid_puts_len(&msg_grid_adj, (char_u *)t_s, (int)(s - t_s), msg_row, msg_col,
 | 
			
		||||
                attr);
 | 
			
		||||
  msg_col += *t_col;
 | 
			
		||||
  *t_col = 0;
 | 
			
		||||
@@ -2514,14 +2696,14 @@ static int do_more_prompt(int typed_char)
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (toscroll == -1) {
 | 
			
		||||
            grid_ins_lines(&default_grid, 0, 1, (int)Rows, 0, (int)Columns);
 | 
			
		||||
            grid_fill(&default_grid, 0, 1, 0, (int)Columns, ' ', ' ', 0);
 | 
			
		||||
            grid_ins_lines(&msg_grid_adj, 0, 1, Rows, 0, Columns);
 | 
			
		||||
            // display line at top
 | 
			
		||||
            (void)disp_sb_line(0, mp);
 | 
			
		||||
          } else {
 | 
			
		||||
            /* redisplay all lines */
 | 
			
		||||
            screenclear();
 | 
			
		||||
            for (i = 0; mp != NULL && i < Rows - 1; ++i) {
 | 
			
		||||
            // redisplay all lines
 | 
			
		||||
            // TODO(bfredl): this case is not optimized (though only concerns
 | 
			
		||||
            // event fragmentization, not unnecessary scroll events).
 | 
			
		||||
            for (i = 0; mp != NULL && i < Rows - 1; i++) {
 | 
			
		||||
              mp = disp_sb_line(i, mp);
 | 
			
		||||
              ++msg_scrolled;
 | 
			
		||||
            }
 | 
			
		||||
@@ -2531,20 +2713,24 @@ static int do_more_prompt(int typed_char)
 | 
			
		||||
      } else {
 | 
			
		||||
        /* First display any text that we scrolled back. */
 | 
			
		||||
        while (toscroll > 0 && mp_last != NULL) {
 | 
			
		||||
          /* scroll up, display line at bottom */
 | 
			
		||||
          msg_scroll_up();
 | 
			
		||||
          if (msg_dothrottle() && !msg_grid.throttled) {
 | 
			
		||||
            // Tricky: we redraw at one line higher than usual. Therefore
 | 
			
		||||
            // the non-flushed area is one line larger.
 | 
			
		||||
            msg_scrolled_at_flush--;
 | 
			
		||||
            msg_grid_scroll_discount++;
 | 
			
		||||
          }
 | 
			
		||||
          // scroll up, display line at bottom
 | 
			
		||||
          msg_scroll_up(true);
 | 
			
		||||
          inc_msg_scrolled();
 | 
			
		||||
          grid_fill(&default_grid, (int)Rows - 2, (int)Rows - 1, 0,
 | 
			
		||||
                    (int)Columns, ' ', ' ', 0);
 | 
			
		||||
          mp_last = disp_sb_line((int)Rows - 2, mp_last);
 | 
			
		||||
          --toscroll;
 | 
			
		||||
          mp_last = disp_sb_line(Rows - 2, mp_last);
 | 
			
		||||
          toscroll--;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (toscroll <= 0) {
 | 
			
		||||
        // displayed the requested text, more prompt again
 | 
			
		||||
        grid_fill(&default_grid, (int)Rows - 1, (int)Rows, 0,
 | 
			
		||||
                  (int)Columns, ' ', ' ', 0);
 | 
			
		||||
        grid_fill(&msg_grid_adj, Rows - 1, Rows, 0, Columns, ' ', ' ',
 | 
			
		||||
                  HL_ATTR(HLF_MSG));
 | 
			
		||||
        msg_moremsg(false);
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
@@ -2557,8 +2743,11 @@ static int do_more_prompt(int typed_char)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // clear the --more-- message
 | 
			
		||||
  grid_fill(&default_grid, (int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ',
 | 
			
		||||
            0);
 | 
			
		||||
  grid_fill(&msg_grid_adj, Rows - 1, Rows, 0, Columns, ' ', ' ', 0);
 | 
			
		||||
  redraw_cmdline = true;
 | 
			
		||||
  clear_cmdline = false;
 | 
			
		||||
  mode_displayed = false;
 | 
			
		||||
 | 
			
		||||
  State = oldState;
 | 
			
		||||
  setmouse();
 | 
			
		||||
  if (quit_more) {
 | 
			
		||||
@@ -2607,8 +2796,9 @@ void mch_msg(char *str)
 | 
			
		||||
 */
 | 
			
		||||
static void msg_screen_putchar(int c, int attr)
 | 
			
		||||
{
 | 
			
		||||
  attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr);
 | 
			
		||||
  msg_didout = true;            // remember that line is not empty
 | 
			
		||||
  grid_putchar(&default_grid, c, msg_row, msg_col, attr);
 | 
			
		||||
  grid_putchar(&msg_grid_adj, c, msg_row, msg_col, attr);
 | 
			
		||||
  if (cmdmsg_rl) {
 | 
			
		||||
    if (--msg_col == 0) {
 | 
			
		||||
      msg_col = Columns;
 | 
			
		||||
@@ -2628,11 +2818,11 @@ void msg_moremsg(int full)
 | 
			
		||||
  char_u      *s = (char_u *)_("-- More --");
 | 
			
		||||
 | 
			
		||||
  attr = HL_ATTR(HLF_M);
 | 
			
		||||
  grid_puts(&default_grid, s, (int)Rows - 1, 0, attr);
 | 
			
		||||
  grid_puts(&msg_grid_adj, s, Rows - 1, 0, attr);
 | 
			
		||||
  if (full) {
 | 
			
		||||
    grid_puts(&default_grid, (char_u *)
 | 
			
		||||
    grid_puts(&msg_grid_adj, (char_u *)
 | 
			
		||||
              _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "),
 | 
			
		||||
              (int)Rows - 1, vim_strsize(s), attr);
 | 
			
		||||
              Rows - 1, vim_strsize(s), attr);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2685,12 +2875,24 @@ void msg_clr_eos_force(void)
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  int msg_startcol = (cmdmsg_rl) ? 0 : msg_col;
 | 
			
		||||
  int msg_endcol = (cmdmsg_rl) ? msg_col + 1 : (int)Columns;
 | 
			
		||||
  int msg_endcol = (cmdmsg_rl) ? msg_col + 1 : Columns;
 | 
			
		||||
 | 
			
		||||
  grid_fill(&default_grid, msg_row, msg_row + 1, msg_startcol, msg_endcol, ' ',
 | 
			
		||||
            ' ', 0);
 | 
			
		||||
  grid_fill(&default_grid, msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ',
 | 
			
		||||
            0);
 | 
			
		||||
  if (msg_grid.chars && msg_row < msg_grid_pos) {
 | 
			
		||||
    // TODO(bfredl): ugly, this state should already been validated at this
 | 
			
		||||
    // point. But msg_clr_eos() is called in a lot of places.
 | 
			
		||||
    msg_row = msg_grid_pos;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  grid_fill(&msg_grid_adj, msg_row, msg_row + 1, msg_startcol, msg_endcol, ' ',
 | 
			
		||||
            ' ', HL_ATTR(HLF_MSG));
 | 
			
		||||
  grid_fill(&msg_grid_adj, msg_row + 1, Rows, 0, Columns, ' ', ' ',
 | 
			
		||||
            HL_ATTR(HLF_MSG));
 | 
			
		||||
 | 
			
		||||
  redraw_cmdline = true;  // overwritten the command line
 | 
			
		||||
  if (msg_row < Rows-1 || msg_col == (cmdmsg_rl ? Columns : 0)) {
 | 
			
		||||
    clear_cmdline = false;  // command line has been cleared
 | 
			
		||||
    mode_displayed = false;  // mode cleared or overwritten
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -2721,10 +2923,11 @@ int msg_end(void)
 | 
			
		||||
    return FALSE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // @TODO(bfredl): calling flush here inhibits substantial performance
 | 
			
		||||
  // improvements. Caller should call ui_flush before waiting on user input or
 | 
			
		||||
  // CPU busywork.
 | 
			
		||||
  ui_flush();  // calls msg_ext_ui_flush
 | 
			
		||||
  // NOTE: ui_flush() used to be called here. This had to be removed, as it
 | 
			
		||||
  // inhibited substantial performance improvements. It is assumed that relevant
 | 
			
		||||
  // callers invoke ui_flush() before going into CPU busywork, or restricted
 | 
			
		||||
  // event processing after displaying a message to the user.
 | 
			
		||||
  msg_ext_ui_flush();
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@
 | 
			
		||||
 | 
			
		||||
#include "nvim/macros.h"
 | 
			
		||||
#include "nvim/types.h"
 | 
			
		||||
#include "nvim/grid_defs.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Types of dialogs passed to do_dialog().
 | 
			
		||||
@@ -90,6 +91,23 @@ extern MessageHistoryEntry *last_msg_hist;
 | 
			
		||||
 | 
			
		||||
EXTERN bool msg_ext_need_clear INIT(= false);
 | 
			
		||||
 | 
			
		||||
// allocated grid for messages. Used when display+=msgsep is set, or
 | 
			
		||||
// ext_multigrid is active. See also the description at msg_scroll_flush()
 | 
			
		||||
EXTERN ScreenGrid msg_grid INIT(= SCREEN_GRID_INIT);
 | 
			
		||||
EXTERN int msg_grid_pos INIT(= 0);
 | 
			
		||||
 | 
			
		||||
// "adjusted" message grid. This grid accepts positions relative to
 | 
			
		||||
// default_grid. Internally it will be translated to a position on msg_grid
 | 
			
		||||
// relative to the start of the message area, or directly mapped to default_grid
 | 
			
		||||
// for legacy (display-=msgsep) message scroll behavior.
 | 
			
		||||
// // TODO(bfredl): refactor "internal" message logic, msg_row etc
 | 
			
		||||
// to use the correct positions already.
 | 
			
		||||
EXTERN ScreenGrid msg_grid_adj INIT(= SCREEN_GRID_INIT);
 | 
			
		||||
 | 
			
		||||
// value of msg_scrolled at latest msg_scroll_flush.
 | 
			
		||||
EXTERN int msg_scrolled_at_flush INIT(= 0);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
 | 
			
		||||
# include "message.h.generated.h"
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -440,8 +440,11 @@ win_T *mouse_find_win(int *gridp, int *rowp, int *colp)
 | 
			
		||||
  win_T *wp_grid = mouse_find_grid_win(gridp, rowp, colp);
 | 
			
		||||
  if (wp_grid) {
 | 
			
		||||
    return wp_grid;
 | 
			
		||||
  } else if (*gridp > 1) {
 | 
			
		||||
    return NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  frame_T     *fp;
 | 
			
		||||
 | 
			
		||||
  fp = topframe;
 | 
			
		||||
@@ -475,7 +478,10 @@ win_T *mouse_find_win(int *gridp, int *rowp, int *colp)
 | 
			
		||||
 | 
			
		||||
static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp)
 | 
			
		||||
{
 | 
			
		||||
  if (*gridp > 1) {
 | 
			
		||||
  if (*gridp == msg_grid.handle) {
 | 
			
		||||
    rowp += msg_grid_pos;
 | 
			
		||||
    *gridp = DEFAULT_GRID_HANDLE;
 | 
			
		||||
  } else if (*gridp > 1) {
 | 
			
		||||
    win_T *wp = get_win_by_grid_handle(*gridp);
 | 
			
		||||
    if (wp && wp->w_grid.chars
 | 
			
		||||
        && !(wp->w_floating && !wp->w_float_config.focusable)) {
 | 
			
		||||
@@ -486,7 +492,7 @@ static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp)
 | 
			
		||||
  } else if (*gridp == 0) {
 | 
			
		||||
    ScreenGrid *grid = ui_comp_mouse_focus(*rowp, *colp);
 | 
			
		||||
    FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
 | 
			
		||||
      if (&wp->w_grid != grid || !wp->w_float_config.focusable) {
 | 
			
		||||
      if (&wp->w_grid != grid) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      *gridp = grid->handle;
 | 
			
		||||
 
 | 
			
		||||
@@ -3455,16 +3455,18 @@ static void display_showcmd(void)
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  msg_grid_validate();
 | 
			
		||||
  int showcmd_row = Rows - 1;
 | 
			
		||||
  grid_puts_line_start(&default_grid, showcmd_row);
 | 
			
		||||
  grid_puts_line_start(&msg_grid_adj, showcmd_row);
 | 
			
		||||
 | 
			
		||||
  if (!showcmd_is_clear) {
 | 
			
		||||
    grid_puts(&default_grid, showcmd_buf, showcmd_row, sc_col, 0);
 | 
			
		||||
    grid_puts(&msg_grid_adj, showcmd_buf, showcmd_row, sc_col,
 | 
			
		||||
              HL_ATTR(HLF_MSG));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // clear the rest of an old message by outputting up to SHOWCMD_COLS spaces
 | 
			
		||||
  grid_puts(&default_grid, (char_u *)"          " + len, showcmd_row,
 | 
			
		||||
            sc_col + len, 0);
 | 
			
		||||
  grid_puts(&msg_grid_adj, (char_u *)"          " + len, showcmd_row,
 | 
			
		||||
            sc_col + len, HL_ATTR(HLF_MSG));
 | 
			
		||||
 | 
			
		||||
  grid_puts_line_flush(false);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2989,6 +2989,7 @@ ambw_end:
 | 
			
		||||
      errmsg = e_invarg;
 | 
			
		||||
    } else {
 | 
			
		||||
      (void)init_chartab();
 | 
			
		||||
      msg_grid_validate();
 | 
			
		||||
    }
 | 
			
		||||
  } else if (varp == &p_ead) {  // 'eadirection'
 | 
			
		||||
    if (check_opt_strings(p_ead, p_ead_values, false) != OK) {
 | 
			
		||||
 
 | 
			
		||||
@@ -399,6 +399,7 @@ static char *(p_dy_values[]) = { "lastline", "truncate", "uhex", "msgsep",
 | 
			
		||||
#define DY_LASTLINE             0x001
 | 
			
		||||
#define DY_TRUNCATE             0x002
 | 
			
		||||
#define DY_UHEX                 0x004
 | 
			
		||||
// code should use msg_use_msgsep() to check if msgsep is active
 | 
			
		||||
#define DY_MSGSEP               0x008
 | 
			
		||||
EXTERN int p_ed;                // 'edcompatible'
 | 
			
		||||
EXTERN int p_emoji;             // 'emoji'
 | 
			
		||||
 
 | 
			
		||||
@@ -152,6 +152,7 @@ static bool send_grid_resize = false;
 | 
			
		||||
static bool conceal_cursor_used = false;
 | 
			
		||||
 | 
			
		||||
static bool redraw_popupmenu = false;
 | 
			
		||||
static bool msg_grid_invalid = false;
 | 
			
		||||
 | 
			
		||||
static bool resizing = false;
 | 
			
		||||
 | 
			
		||||
@@ -318,27 +319,37 @@ int update_screen(int type)
 | 
			
		||||
  // Tricky: vim code can reset msg_scrolled behind our back, so need
 | 
			
		||||
  // separate bookkeeping for now.
 | 
			
		||||
  if (msg_did_scroll) {
 | 
			
		||||
    ui_call_win_scroll_over_reset();
 | 
			
		||||
    msg_did_scroll = false;
 | 
			
		||||
    msg_scrolled_at_flush = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (type >= CLEAR || !default_grid.valid) {
 | 
			
		||||
    ui_comp_set_screen_valid(false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // if the screen was scrolled up when displaying a message, scroll it down
 | 
			
		||||
  if (msg_scrolled) {
 | 
			
		||||
  if (msg_scrolled || msg_grid_invalid) {
 | 
			
		||||
    clear_cmdline = true;
 | 
			
		||||
    if (dy_flags & DY_MSGSEP) {
 | 
			
		||||
    int valid = MAX(Rows - msg_scrollsize(), 0);
 | 
			
		||||
      if (valid == 0) {
 | 
			
		||||
        redraw_tabline = true;
 | 
			
		||||
      }
 | 
			
		||||
      FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
 | 
			
		||||
        if (W_ENDROW(wp) > valid) {
 | 
			
		||||
          wp->w_redr_type = NOT_VALID;
 | 
			
		||||
          wp->w_lines_valid = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (W_ENDROW(wp) + wp->w_status_height > valid) {
 | 
			
		||||
          wp->w_redr_status = true;
 | 
			
		||||
    if (msg_grid.chars) {
 | 
			
		||||
      // non-displayed part of msg_grid is considered invalid.
 | 
			
		||||
      for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.Rows); i++) {
 | 
			
		||||
        grid_clear_line(&msg_grid, msg_grid.line_offset[i],
 | 
			
		||||
                        (int)msg_grid.Columns, false);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (msg_use_msgsep()) {
 | 
			
		||||
      msg_grid.throttled = false;
 | 
			
		||||
      // CLEAR is already handled
 | 
			
		||||
      if (type == NOT_VALID && !ui_has(kUIMultigrid) && msg_scrolled) {
 | 
			
		||||
        ui_comp_set_screen_valid(false);
 | 
			
		||||
        for (int i = valid; i < Rows-p_ch; i++) {
 | 
			
		||||
          grid_clear_line(&default_grid, default_grid.line_offset[i],
 | 
			
		||||
                          Columns, false);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      msg_grid_set_pos(Rows-p_ch, false);
 | 
			
		||||
      msg_grid_invalid = false;
 | 
			
		||||
    } else if (msg_scrolled > Rows - 5) {  // clearing is faster
 | 
			
		||||
      type = CLEAR;
 | 
			
		||||
    } else if (type != CLEAR) {
 | 
			
		||||
@@ -368,12 +379,10 @@ int update_screen(int type)
 | 
			
		||||
      redraw_tabline = TRUE;
 | 
			
		||||
    }
 | 
			
		||||
    msg_scrolled = 0;
 | 
			
		||||
    need_wait_return = FALSE;
 | 
			
		||||
    msg_scrolled_at_flush = 0;
 | 
			
		||||
    need_wait_return = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (type >= CLEAR || !default_grid.valid) {
 | 
			
		||||
    ui_comp_set_screen_valid(false);
 | 
			
		||||
  }
 | 
			
		||||
  win_ui_flush_positions();
 | 
			
		||||
  msg_ext_check_clear();
 | 
			
		||||
 | 
			
		||||
@@ -394,6 +403,11 @@ int update_screen(int type)
 | 
			
		||||
    grid_invalidate(&default_grid);
 | 
			
		||||
    default_grid.valid = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (type == NOT_VALID && msg_dothrottle()) {
 | 
			
		||||
    grid_fill(&default_grid, Rows-p_ch, Rows, 0, Columns, ' ', ' ', 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ui_comp_set_screen_valid(true);
 | 
			
		||||
 | 
			
		||||
  if (clear_cmdline)            /* going to clear cmdline (done below) */
 | 
			
		||||
@@ -4312,8 +4326,12 @@ void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off)
 | 
			
		||||
  if (!(*grid)->chars && *grid != &default_grid) {
 | 
			
		||||
      *row_off += (*grid)->row_offset;
 | 
			
		||||
      *col_off += (*grid)->col_offset;
 | 
			
		||||
    if (*grid == &msg_grid_adj && msg_grid.chars) {
 | 
			
		||||
      *grid = &msg_grid;
 | 
			
		||||
    } else {
 | 
			
		||||
      *grid = &default_grid;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -4799,7 +4817,7 @@ win_redr_status_matches (
 | 
			
		||||
        /* Put the wildmenu just above the command line.  If there is
 | 
			
		||||
         * no room, scroll the screen one line up. */
 | 
			
		||||
        if (cmdline_row == Rows - 1) {
 | 
			
		||||
          msg_scroll_up();
 | 
			
		||||
          msg_scroll_up(false);
 | 
			
		||||
          msg_scrolled++;
 | 
			
		||||
        } else {
 | 
			
		||||
          cmdline_row++;
 | 
			
		||||
@@ -4821,13 +4839,18 @@ win_redr_status_matches (
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    grid_puts(&default_grid, buf, row, 0, attr);
 | 
			
		||||
    // Tricky: wildmenu can be drawn either over a status line, or at empty
 | 
			
		||||
    // scrolled space in the message output
 | 
			
		||||
    ScreenGrid *grid = (wild_menu_showing == WM_SCROLLED)
 | 
			
		||||
                        ? &msg_grid_adj : &default_grid;
 | 
			
		||||
 | 
			
		||||
    grid_puts(grid, buf, row, 0, attr);
 | 
			
		||||
    if (selstart != NULL && highlight) {
 | 
			
		||||
      *selend = NUL;
 | 
			
		||||
      grid_puts(&default_grid, selstart, row, selstart_col, HL_ATTR(HLF_WM));
 | 
			
		||||
      grid_puts(grid, selstart, row, selstart_col, HL_ATTR(HLF_WM));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    grid_fill(&default_grid, row, row + 1, clen, Columns,
 | 
			
		||||
    grid_fill(grid, row, row + 1, clen, Columns,
 | 
			
		||||
              fillchar, fillchar, attr);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -5350,6 +5373,8 @@ static int put_dirty_last = 0;
 | 
			
		||||
/// another line.
 | 
			
		||||
void grid_puts_line_start(ScreenGrid *grid, int row)
 | 
			
		||||
{
 | 
			
		||||
  int col = 0;  // unused
 | 
			
		||||
  screen_adjust_grid(&grid, &row, &col);
 | 
			
		||||
  assert(put_dirty_row == -1);
 | 
			
		||||
  put_dirty_row = row;
 | 
			
		||||
  put_dirty_grid = grid;
 | 
			
		||||
@@ -5379,7 +5404,7 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
 | 
			
		||||
  screen_adjust_grid(&grid, &row, &col);
 | 
			
		||||
 | 
			
		||||
  // Safety check. The check for negative row and column is to fix issue
 | 
			
		||||
  // vim/vim#4102. TODO: find out why row/col could be negative.
 | 
			
		||||
  // vim/vim#4102. TODO(neovim): find out why row/col could be negative.
 | 
			
		||||
  if (grid->chars == NULL
 | 
			
		||||
      || row >= grid->Rows || row < 0
 | 
			
		||||
      || col >= grid->Columns || col < 0) {
 | 
			
		||||
@@ -5511,8 +5536,14 @@ void grid_puts_line_flush(bool set_cursor)
 | 
			
		||||
      ui_grid_cursor_goto(put_dirty_grid->handle, put_dirty_row,
 | 
			
		||||
                          MIN(put_dirty_last, put_dirty_grid->Columns-1));
 | 
			
		||||
    }
 | 
			
		||||
    if (!put_dirty_grid->throttled) {
 | 
			
		||||
      ui_line(put_dirty_grid, put_dirty_row, put_dirty_first, put_dirty_last,
 | 
			
		||||
              put_dirty_last, 0, false);
 | 
			
		||||
    } else if (put_dirty_grid->dirty_col) {
 | 
			
		||||
      if (put_dirty_last > put_dirty_grid->dirty_col[put_dirty_row]) {
 | 
			
		||||
        put_dirty_grid->dirty_col[put_dirty_row] = put_dirty_last;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    put_dirty_first = INT_MAX;
 | 
			
		||||
    put_dirty_last = 0;
 | 
			
		||||
  }
 | 
			
		||||
@@ -5886,6 +5917,18 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col,
 | 
			
		||||
      if (put_dirty_row == row) {
 | 
			
		||||
        put_dirty_first = MIN(put_dirty_first, dirty_first);
 | 
			
		||||
        put_dirty_last = MAX(put_dirty_last, dirty_last);
 | 
			
		||||
      } else if (grid->throttled) {
 | 
			
		||||
        // Note: assumes msg_grid is the only throttled grid
 | 
			
		||||
        assert(grid == &msg_grid);
 | 
			
		||||
        int dirty = 0;
 | 
			
		||||
        if (attr != HL_ATTR(HLF_MSG) || c2 != ' ') {
 | 
			
		||||
          dirty = dirty_last;
 | 
			
		||||
        } else if (c1 != ' ') {
 | 
			
		||||
          dirty = dirty_first + 1;
 | 
			
		||||
        }
 | 
			
		||||
        if (grid->dirty_col && dirty > grid->dirty_col[row]) {
 | 
			
		||||
          grid->dirty_col[row] = dirty;
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' ');
 | 
			
		||||
        ui_line(grid, row, dirty_first, last, dirty_last, attr, false);
 | 
			
		||||
@@ -5895,19 +5938,6 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col,
 | 
			
		||||
    if (end_col == grid->Columns) {
 | 
			
		||||
      grid->line_wraps[row] = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO(bfredl): The relevant caller should do this
 | 
			
		||||
    if (row == Rows - 1 && !ui_has(kUIMessages)) {
 | 
			
		||||
      // overwritten the command line
 | 
			
		||||
      redraw_cmdline = true;
 | 
			
		||||
      if (start_col == 0 && end_col == Columns
 | 
			
		||||
          && c1 == ' ' && c2 == ' ' && attr == 0) {
 | 
			
		||||
        clear_cmdline = false;  // command line has been cleared
 | 
			
		||||
      }
 | 
			
		||||
      if (start_col == 0) {
 | 
			
		||||
        mode_displayed = false;  // mode cleared or overwritten
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -6039,6 +6069,9 @@ retry:
 | 
			
		||||
  // win_new_shellsize will recompute floats position, but tell the
 | 
			
		||||
  // compositor to not redraw them yet
 | 
			
		||||
  ui_comp_set_screen_valid(false);
 | 
			
		||||
  if (msg_grid.chars) {
 | 
			
		||||
    msg_grid_invalid = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  win_new_shellsize();      /* fit the windows in the new sized shell */
 | 
			
		||||
 | 
			
		||||
@@ -6217,12 +6250,17 @@ void screenclear(void)
 | 
			
		||||
  msg_scrolled = 0;  // can't scroll back
 | 
			
		||||
  msg_didany = false;
 | 
			
		||||
  msg_didout = false;
 | 
			
		||||
  if (HL_ATTR(HLF_MSG) > 0 && msg_dothrottle() && msg_grid.chars) {
 | 
			
		||||
    grid_invalidate(&msg_grid);
 | 
			
		||||
    msg_grid_validate();
 | 
			
		||||
    msg_grid_invalid = false;
 | 
			
		||||
    clear_cmdline = true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// clear a line in the grid starting at "off" until "width" characters
 | 
			
		||||
/// are cleared.
 | 
			
		||||
static void grid_clear_line(ScreenGrid *grid, unsigned off, int width,
 | 
			
		||||
                            bool valid)
 | 
			
		||||
void grid_clear_line(ScreenGrid *grid, unsigned off, int width, bool valid)
 | 
			
		||||
{
 | 
			
		||||
  for (int col = 0; col < width; col++) {
 | 
			
		||||
    schar_from_ascii(grid->chars[off + col], ' ');
 | 
			
		||||
@@ -6361,7 +6399,9 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!grid->throttled) {
 | 
			
		||||
    ui_call_grid_scroll(grid->handle, row, end, col, col+width, -line_count, 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return;
 | 
			
		||||
}
 | 
			
		||||
@@ -6412,7 +6452,9 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!grid->throttled) {
 | 
			
		||||
    ui_call_grid_scroll(grid->handle, row, end, col, col+width, line_count, 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return;
 | 
			
		||||
}
 | 
			
		||||
@@ -6440,6 +6482,8 @@ int showmode(void)
 | 
			
		||||
  // don't make non-flushed message part of the showmode
 | 
			
		||||
  msg_ext_ui_flush();
 | 
			
		||||
 | 
			
		||||
  msg_grid_validate();
 | 
			
		||||
 | 
			
		||||
  do_mode = ((p_smd && msg_silent == 0)
 | 
			
		||||
             && ((State & TERM_FOCUS)
 | 
			
		||||
                 || (State & INSERT)
 | 
			
		||||
@@ -7094,13 +7138,11 @@ static void win_redr_ruler(win_T *wp, int always)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      grid_puts(&default_grid, buffer, row, this_ru_col + off, attr);
 | 
			
		||||
      i = redraw_cmdline;
 | 
			
		||||
      grid_fill(&default_grid, row, row + 1,
 | 
			
		||||
      ScreenGrid *grid = part_of_status ? &default_grid : &msg_grid_adj;
 | 
			
		||||
      grid_puts(grid, buffer, row, this_ru_col + off, attr);
 | 
			
		||||
      grid_fill(grid, row, row + 1,
 | 
			
		||||
                this_ru_col + off + (int)STRLEN(buffer), off + width, fillchar,
 | 
			
		||||
                fillchar, attr);
 | 
			
		||||
      // don't redraw the cmdline because of showing the ruler
 | 
			
		||||
      redraw_cmdline = i;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    wp->w_ru_cursor = wp->w_cursor;
 | 
			
		||||
@@ -7214,6 +7256,12 @@ void screen_resize(int width, int height)
 | 
			
		||||
    if (State == ASKMORE || State == EXTERNCMD || State == CONFIRM
 | 
			
		||||
        || exmode_active) {
 | 
			
		||||
      screenalloc();
 | 
			
		||||
      if (msg_grid.chars) {
 | 
			
		||||
        msg_grid_validate();
 | 
			
		||||
      }
 | 
			
		||||
      // TODO(bfredl): sometimes messes up the output. Implement clear+redraw
 | 
			
		||||
      // also for the pager? (or: what if the pager was just a modal window?)
 | 
			
		||||
      ui_comp_set_screen_valid(true);
 | 
			
		||||
      repeat_message();
 | 
			
		||||
    } else {
 | 
			
		||||
      if (curwin->w_p_scb)
 | 
			
		||||
 
 | 
			
		||||
@@ -7533,6 +7533,9 @@ void highlight_changed(void)
 | 
			
		||||
                                         hlf == (int)HLF_INACTIVE);
 | 
			
		||||
 | 
			
		||||
    if (highlight_attr[hlf] != highlight_attr_last[hlf]) {
 | 
			
		||||
      if (hlf == HLF_MSG) {
 | 
			
		||||
        clear_cmdline = true;
 | 
			
		||||
      }
 | 
			
		||||
      ui_call_hl_group_set(cstr_as_string((char *)hlf_names[hlf]),
 | 
			
		||||
                           highlight_attr[hlf]);
 | 
			
		||||
      highlight_attr_last[hlf] = highlight_attr[hlf];
 | 
			
		||||
 
 | 
			
		||||
@@ -333,6 +333,7 @@ void ui_set_ext_option(UI *ui, UIExtension ext, bool active)
 | 
			
		||||
void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol,
 | 
			
		||||
             int clearattr, bool wrap)
 | 
			
		||||
{
 | 
			
		||||
  assert(0 <= row && row < grid->Rows);
 | 
			
		||||
  LineFlags flags = wrap ? kLineFlagWrap : 0;
 | 
			
		||||
  if (startcol == -1) {
 | 
			
		||||
    startcol = 0;
 | 
			
		||||
@@ -404,6 +405,7 @@ void ui_flush(void)
 | 
			
		||||
  cmdline_ui_flush();
 | 
			
		||||
  win_ui_flush_positions();
 | 
			
		||||
  msg_ext_ui_flush();
 | 
			
		||||
  msg_scroll_flush();
 | 
			
		||||
 | 
			
		||||
  if (pending_cursor_update) {
 | 
			
		||||
    ui_call_grid_cursor_goto(cursor_grid_handle, cursor_row, cursor_col);
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@
 | 
			
		||||
#include "nvim/ui.h"
 | 
			
		||||
#include "nvim/highlight.h"
 | 
			
		||||
#include "nvim/memory.h"
 | 
			
		||||
#include "nvim/message.h"
 | 
			
		||||
#include "nvim/popupmnu.h"
 | 
			
		||||
#include "nvim/ui_compositor.h"
 | 
			
		||||
#include "nvim/ugrid.h"
 | 
			
		||||
@@ -46,8 +47,11 @@ static int chk_width = 0, chk_height = 0;
 | 
			
		||||
static ScreenGrid *curgrid;
 | 
			
		||||
 | 
			
		||||
static bool valid_screen = true;
 | 
			
		||||
static bool msg_scroll_mode = false;
 | 
			
		||||
static int msg_first_invalid = 0;
 | 
			
		||||
static int msg_current_row = INT_MAX;
 | 
			
		||||
static bool msg_was_scrolled = false;
 | 
			
		||||
 | 
			
		||||
static int msg_sep_row = -1;
 | 
			
		||||
static schar_T msg_sep_char = { ' ', NUL };
 | 
			
		||||
 | 
			
		||||
static int dbghl_normal, dbghl_clear, dbghl_composed, dbghl_recompose;
 | 
			
		||||
 | 
			
		||||
@@ -63,8 +67,7 @@ void ui_comp_init(void)
 | 
			
		||||
  compositor->grid_scroll = ui_comp_grid_scroll;
 | 
			
		||||
  compositor->grid_cursor_goto = ui_comp_grid_cursor_goto;
 | 
			
		||||
  compositor->raw_line = ui_comp_raw_line;
 | 
			
		||||
  compositor->win_scroll_over_start = ui_comp_win_scroll_over_start;
 | 
			
		||||
  compositor->win_scroll_over_reset = ui_comp_win_scroll_over_reset;
 | 
			
		||||
  compositor->msg_set_pos = ui_comp_msg_set_pos;
 | 
			
		||||
 | 
			
		||||
  // Be unopinionated: will be attached together with a "real" ui anyway
 | 
			
		||||
  compositor->width = INT_MAX;
 | 
			
		||||
@@ -158,8 +161,19 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width,
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    // TODO(bfredl): this is pretty ad-hoc, add a proper z-order/priority
 | 
			
		||||
    // scheme. For now:
 | 
			
		||||
    // - msg_grid is always on top.
 | 
			
		||||
    // - pum_grid is on top of all windows but not msg_grid. Except for when
 | 
			
		||||
    //   wildoptions=pum, and completing the cmdline with scrolled messages,
 | 
			
		||||
    //   then the pum has to be drawn over the scrolled messages.
 | 
			
		||||
    size_t insert_at = kv_size(layers);
 | 
			
		||||
    if (kv_A(layers, insert_at-1) == &pum_grid) {
 | 
			
		||||
    bool cmd_completion = (grid == &pum_grid && (State & CMDLINE)
 | 
			
		||||
                           && (wop_flags & WOP_PUM));
 | 
			
		||||
    if (kv_A(layers, insert_at-1) == &msg_grid && !cmd_completion) {
 | 
			
		||||
      insert_at--;
 | 
			
		||||
    }
 | 
			
		||||
    if (kv_A(layers, insert_at-1) == &pum_grid && (grid != &msg_grid)) {
 | 
			
		||||
      insert_at--;
 | 
			
		||||
    }
 | 
			
		||||
    if (insert_at > 1 && !on_top) {
 | 
			
		||||
@@ -280,10 +294,10 @@ static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle,
 | 
			
		||||
 | 
			
		||||
ScreenGrid *ui_comp_mouse_focus(int row, int col)
 | 
			
		||||
{
 | 
			
		||||
  // TODO(bfredl): click "through" unfocusable grids?
 | 
			
		||||
  for (ssize_t i = (ssize_t)kv_size(layers)-1; i > 0; i--) {
 | 
			
		||||
    ScreenGrid *grid = kv_A(layers, i);
 | 
			
		||||
    if (row >= grid->comp_row && row < grid->comp_row+grid->Rows
 | 
			
		||||
    if (grid->focusable
 | 
			
		||||
        && row >= grid->comp_row && row < grid->comp_row+grid->Rows
 | 
			
		||||
        && col >= grid->comp_col && col < grid->comp_col+grid->Columns) {
 | 
			
		||||
      return grid;
 | 
			
		||||
    }
 | 
			
		||||
@@ -337,10 +351,30 @@ static void compose_line(Integer row, Integer startcol, Integer endcol,
 | 
			
		||||
    assert(until > col);
 | 
			
		||||
    assert(until <= default_grid.Columns);
 | 
			
		||||
    size_t n = (size_t)(until-col);
 | 
			
		||||
 | 
			
		||||
    if (row == msg_sep_row && grid->comp_index <= msg_grid.comp_index) {
 | 
			
		||||
      // TODO(bfredl): when we implement borders around floating windows, then
 | 
			
		||||
      // msgsep can just be a border "around" the message grid.
 | 
			
		||||
      grid = &msg_grid;
 | 
			
		||||
      sattr_T msg_sep_attr = (sattr_T)HL_ATTR(HLF_MSGSEP);
 | 
			
		||||
      for (int i = col; i < until; i++) {
 | 
			
		||||
        memcpy(linebuf[i-startcol], msg_sep_char, sizeof(*linebuf));
 | 
			
		||||
        attrbuf[i-startcol] = msg_sep_attr;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      size_t off = grid->line_offset[row-grid->comp_row]
 | 
			
		||||
                   + (size_t)(col-grid->comp_col);
 | 
			
		||||
      memcpy(linebuf+(col-startcol), grid->chars+off, n * sizeof(*linebuf));
 | 
			
		||||
      memcpy(attrbuf+(col-startcol), grid->attrs+off, n * sizeof(*attrbuf));
 | 
			
		||||
      if (grid->comp_col+grid->Columns > until
 | 
			
		||||
          && grid->chars[off+n][0] == NUL) {
 | 
			
		||||
        linebuf[until-1-startcol][0] = ' ';
 | 
			
		||||
        linebuf[until-1-startcol][1] = '\0';
 | 
			
		||||
        if (col == startcol && n == 1) {
 | 
			
		||||
          skipstart = 0;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 'pumblend' and 'winblend'
 | 
			
		||||
    if (grid->blending) {
 | 
			
		||||
@@ -375,14 +409,6 @@ static void compose_line(Integer row, Integer startcol, Integer endcol,
 | 
			
		||||
    } else if (n > 1 && linebuf[col-startcol+1][0] == NUL) {
 | 
			
		||||
      skipstart = 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (grid->comp_col+grid->Columns > until
 | 
			
		||||
        && grid->chars[off+n][0] == NUL) {
 | 
			
		||||
      linebuf[until-1-startcol][0] = ' ';
 | 
			
		||||
      linebuf[until-1-startcol][1] = '\0';
 | 
			
		||||
      if (col == startcol && n == 1) {
 | 
			
		||||
        skipstart = 0;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    col = until;
 | 
			
		||||
  }
 | 
			
		||||
@@ -500,9 +526,12 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row,
 | 
			
		||||
    endcol = MIN(endcol, clearcol);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (flags & kLineFlagInvalid
 | 
			
		||||
      || kv_size(layers) > curgrid->comp_index+1
 | 
			
		||||
      || curgrid->blending) {
 | 
			
		||||
  bool above_msg = (kv_A(layers, kv_size(layers)-1) == &msg_grid
 | 
			
		||||
                    && row < msg_current_row-(msg_was_scrolled?1:0));
 | 
			
		||||
  bool covered = kv_size(layers)-(above_msg?1:0) > curgrid->comp_index+1;
 | 
			
		||||
  // TODO(bfredl): eventually should just fix compose_line to respect clearing
 | 
			
		||||
  // and optimize it for uncovered lines.
 | 
			
		||||
  if (flags & kLineFlagInvalid || covered || curgrid->blending) {
 | 
			
		||||
    compose_debug(row, row+1, startcol, clearcol, dbghl_composed, true);
 | 
			
		||||
    compose_line(row, startcol, clearcol, flags);
 | 
			
		||||
  } else {
 | 
			
		||||
@@ -519,27 +548,44 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row,
 | 
			
		||||
void ui_comp_set_screen_valid(bool valid)
 | 
			
		||||
{
 | 
			
		||||
  valid_screen = valid;
 | 
			
		||||
  if (!valid) {
 | 
			
		||||
    msg_sep_row = -1;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO(bfredl): These events are somewhat of a hack. multiline messages
 | 
			
		||||
// should later on be a separate grid, then this would just be ordinary
 | 
			
		||||
// ui_comp_put_grid and ui_comp_remove_grid calls.
 | 
			
		||||
static void ui_comp_win_scroll_over_start(UI *ui)
 | 
			
		||||
static void ui_comp_msg_set_pos(UI *ui, Integer grid, Integer row,
 | 
			
		||||
                                Boolean scrolled, String sep_char)
 | 
			
		||||
{
 | 
			
		||||
  msg_scroll_mode = true;
 | 
			
		||||
  msg_first_invalid = ui->height;
 | 
			
		||||
}
 | 
			
		||||
  msg_grid.comp_row = (int)row;
 | 
			
		||||
  if (scrolled && row > 0) {
 | 
			
		||||
    msg_sep_row = (int)row-1;
 | 
			
		||||
    if (sep_char.data) {
 | 
			
		||||
      STRLCPY(msg_sep_char, sep_char.data, sizeof(msg_sep_char));
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    msg_sep_row = -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
static void ui_comp_win_scroll_over_reset(UI *ui)
 | 
			
		||||
{
 | 
			
		||||
  msg_scroll_mode = false;
 | 
			
		||||
  for (size_t i = 1; i < kv_size(layers); i++) {
 | 
			
		||||
    ScreenGrid *grid = kv_A(layers, i);
 | 
			
		||||
    if (grid->comp_row+grid->Rows > msg_first_invalid) {
 | 
			
		||||
      compose_area(msg_first_invalid, grid->comp_row+grid->Rows,
 | 
			
		||||
                   grid->comp_col, grid->comp_col+grid->Columns);
 | 
			
		||||
  if (row > msg_current_row && ui_comp_should_draw()) {
 | 
			
		||||
    compose_area(MAX(msg_current_row-1, 0), row, 0, default_grid.Columns);
 | 
			
		||||
  } else if (row < msg_current_row && ui_comp_should_draw()
 | 
			
		||||
             && msg_current_row < Rows) {
 | 
			
		||||
    int delta = msg_current_row - (int)row;
 | 
			
		||||
    if (msg_grid.blending) {
 | 
			
		||||
      int first_row = MAX((int)row-(scrolled?1:0), 0);
 | 
			
		||||
      compose_area(first_row, Rows-delta, 0, Columns);
 | 
			
		||||
    } else {
 | 
			
		||||
      // scroll separator togheter with message text
 | 
			
		||||
      int first_row = MAX((int)row-(msg_was_scrolled?1:0), 0);
 | 
			
		||||
      ui_composed_call_grid_scroll(1, first_row, Rows, 0, Columns, delta, 0);
 | 
			
		||||
      if (scrolled && !msg_was_scrolled && row > 0) {
 | 
			
		||||
        compose_area(row-1, row, 0, Columns);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  msg_current_row = (int)row;
 | 
			
		||||
  msg_was_scrolled = scrolled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top,
 | 
			
		||||
@@ -554,7 +600,8 @@ static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top,
 | 
			
		||||
  left += curgrid->comp_col;
 | 
			
		||||
  right += curgrid->comp_col;
 | 
			
		||||
  bool covered = kv_size(layers) > curgrid->comp_index+1 || curgrid->blending;
 | 
			
		||||
  if (!msg_scroll_mode && covered) {
 | 
			
		||||
 | 
			
		||||
  if (covered) {
 | 
			
		||||
    // TODO(bfredl):
 | 
			
		||||
    // 1. check if rectangles actually overlap
 | 
			
		||||
    // 2. calulate subareas that can scroll.
 | 
			
		||||
@@ -565,7 +612,6 @@ static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top,
 | 
			
		||||
    }
 | 
			
		||||
    compose_area(top, bot, left, right);
 | 
			
		||||
  } else {
 | 
			
		||||
    msg_first_invalid = MIN(msg_first_invalid, (int)top);
 | 
			
		||||
    ui_composed_call_grid_scroll(1, top, bot, left, right, rows, cols);
 | 
			
		||||
    if (rdb_flags & RDB_COMPOSITOR) {
 | 
			
		||||
      debug_delay(2);
 | 
			
		||||
 
 | 
			
		||||
@@ -697,6 +697,7 @@ static void ui_ext_win_position(win_T *wp)
 | 
			
		||||
      ui_comp_put_grid(&wp->w_grid, comp_row, comp_col, wp->w_height,
 | 
			
		||||
                       wp->w_width, valid, on_top);
 | 
			
		||||
      ui_check_cursor_grid(wp->w_grid.handle);
 | 
			
		||||
      wp->w_grid.focusable = wp->w_float_config.focusable;
 | 
			
		||||
      if (!valid) {
 | 
			
		||||
        wp->w_grid.valid = false;
 | 
			
		||||
        redraw_win_later(wp, NOT_VALID);
 | 
			
		||||
@@ -5359,6 +5360,9 @@ void win_drag_status_line(win_T *dragwin, int offset)
 | 
			
		||||
  }
 | 
			
		||||
  row = win_comp_pos();
 | 
			
		||||
  grid_fill(&default_grid, row, cmdline_row, 0, Columns, ' ', ' ', 0);
 | 
			
		||||
  if (msg_grid.chars) {
 | 
			
		||||
    clear_cmdline = true;
 | 
			
		||||
  }
 | 
			
		||||
  cmdline_row = row;
 | 
			
		||||
  p_ch = Rows - cmdline_row;
 | 
			
		||||
  if (p_ch < 1)
 | 
			
		||||
 
 | 
			
		||||
@@ -222,6 +222,7 @@ describe('timers', function()
 | 
			
		||||
      let g:val = 0
 | 
			
		||||
      func! MyHandler(timer)
 | 
			
		||||
        echo "evil"
 | 
			
		||||
        redraw
 | 
			
		||||
        let g:val = 1
 | 
			
		||||
      endfunc
 | 
			
		||||
    ]])
 | 
			
		||||
 
 | 
			
		||||
@@ -56,6 +56,8 @@ local function screen_setup(extra_rows, command, cols, opts)
 | 
			
		||||
    [8] = {foreground = 15, background = 1}, -- error message
 | 
			
		||||
    [9] = {foreground = 4},
 | 
			
		||||
    [10] = {foreground = 121},  -- "Press ENTER" in embedded :terminal session.
 | 
			
		||||
    [11] = {foreground = tonumber('0x00000b')},
 | 
			
		||||
    [12] = {reverse = true, foreground = tonumber('0x000079')},
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  screen:attach(opts or {rgb=false})
 | 
			
		||||
 
 | 
			
		||||
@@ -89,6 +89,85 @@ describe('TUI', function()
 | 
			
		||||
    eq(2, eval("1+1"))  -- Still alive?
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  it('accepts resize while pager is active', function()
 | 
			
		||||
    child_session:request("nvim_command", [[
 | 
			
		||||
    set more
 | 
			
		||||
    func! ManyErr()
 | 
			
		||||
      for i in range(10)
 | 
			
		||||
        echoerr "FAIL ".i
 | 
			
		||||
      endfor
 | 
			
		||||
    endfunc
 | 
			
		||||
    ]])
 | 
			
		||||
    feed_data(':call ManyErr()\r')
 | 
			
		||||
    screen:expect{grid=[[
 | 
			
		||||
      {8:Error detected while processing function ManyErr:} |
 | 
			
		||||
      {11:line    2:}                                        |
 | 
			
		||||
      {8:FAIL 0}                                            |
 | 
			
		||||
      {8:FAIL 1}                                            |
 | 
			
		||||
      {8:FAIL 2}                                            |
 | 
			
		||||
      {10:-- More --}{1: }                                       |
 | 
			
		||||
      {3:-- TERMINAL --}                                    |
 | 
			
		||||
    ]]}
 | 
			
		||||
 | 
			
		||||
    feed_data('d')
 | 
			
		||||
    screen:expect{grid=[[
 | 
			
		||||
      {8:FAIL 1}                                            |
 | 
			
		||||
      {8:FAIL 2}                                            |
 | 
			
		||||
      {8:FAIL 3}                                            |
 | 
			
		||||
      {8:FAIL 4}                                            |
 | 
			
		||||
      {8:FAIL 5}                                            |
 | 
			
		||||
      {10:-- More --}{1: }                                       |
 | 
			
		||||
      {3:-- TERMINAL --}                                    |
 | 
			
		||||
    ]]}
 | 
			
		||||
 | 
			
		||||
    screen:try_resize(50,5)
 | 
			
		||||
    screen:expect{grid=[[
 | 
			
		||||
      {8:FAIL 1}                                            |
 | 
			
		||||
      {8:FAIL 2}                                            |
 | 
			
		||||
      {8:FAIL 3}                                            |
 | 
			
		||||
      {10:-- More -- SPACE/d/j: screen/page/line down, b/u/}{12:k}|
 | 
			
		||||
      {3:-- TERMINAL --}                                    |
 | 
			
		||||
    ]]}
 | 
			
		||||
 | 
			
		||||
    -- TODO(bfredl): messes up the output (just like vim does).
 | 
			
		||||
    feed_data('g')
 | 
			
		||||
    screen:expect{grid=[[
 | 
			
		||||
      {8:FAIL 1}        )                                   |
 | 
			
		||||
      {8:Error detected while processing function ManyErr:} |
 | 
			
		||||
      {11:line    2:}                                        |
 | 
			
		||||
      {10:-- More --}{1: }                                       |
 | 
			
		||||
      {3:-- TERMINAL --}                                    |
 | 
			
		||||
    ]]}
 | 
			
		||||
 | 
			
		||||
    screen:try_resize(50,10)
 | 
			
		||||
    screen:expect{grid=[[
 | 
			
		||||
      {8:FAIL 1}        )                                   |
 | 
			
		||||
      {8:Error detected while processing function ManyErr:} |
 | 
			
		||||
      {11:line    2:}                                        |
 | 
			
		||||
      {10:-- More --}                                        |
 | 
			
		||||
      {10:                                                  }|
 | 
			
		||||
      {10:                                                  }|
 | 
			
		||||
      {10:                                                  }|
 | 
			
		||||
      {10:                                                  }|
 | 
			
		||||
      {10:-- More -- SPACE/d/j: screen/page/line down, b/u/}{12:k}|
 | 
			
		||||
      {3:-- TERMINAL --}                                    |
 | 
			
		||||
    ]]}
 | 
			
		||||
 | 
			
		||||
    feed_data('\003')
 | 
			
		||||
    screen:expect{grid=[[
 | 
			
		||||
      {1: }                                                 |
 | 
			
		||||
      {4:~                                                 }|
 | 
			
		||||
      {4:~                                                 }|
 | 
			
		||||
      {4:~                                                 }|
 | 
			
		||||
      {4:~                                                 }|
 | 
			
		||||
      {4:~                                                 }|
 | 
			
		||||
      {4:~                                                 }|
 | 
			
		||||
      {5:[No Name]                                         }|
 | 
			
		||||
                                                        |
 | 
			
		||||
      {3:-- TERMINAL --}                                    |
 | 
			
		||||
    ]]}
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  it('accepts basic utf-8 input', function()
 | 
			
		||||
    feed_data('iabc\ntest1\ntest2')
 | 
			
		||||
    screen:expect([[
 | 
			
		||||
 
 | 
			
		||||
@@ -158,7 +158,7 @@ describe(':terminal with multigrid', function()
 | 
			
		||||
      [2:--------------------------------------------------]|
 | 
			
		||||
      [2:--------------------------------------------------]|
 | 
			
		||||
      [2:--------------------------------------------------]|
 | 
			
		||||
      {3:-- TERMINAL --}                                    |
 | 
			
		||||
      [3:--------------------------------------------------]|
 | 
			
		||||
    ## grid 2
 | 
			
		||||
      tty ready                                         |
 | 
			
		||||
      {1: }                                                 |
 | 
			
		||||
@@ -166,6 +166,8 @@ describe(':terminal with multigrid', function()
 | 
			
		||||
                                                        |
 | 
			
		||||
                                                        |
 | 
			
		||||
                                                        |
 | 
			
		||||
    ## grid 3
 | 
			
		||||
      {3:-- TERMINAL --}                                    |
 | 
			
		||||
    ]])
 | 
			
		||||
 | 
			
		||||
    screen:try_resize_grid(2, 20, 10)
 | 
			
		||||
@@ -180,7 +182,7 @@ describe(':terminal with multigrid', function()
 | 
			
		||||
        [2:--------------------------------------------------]|
 | 
			
		||||
        [2:--------------------------------------------------]|
 | 
			
		||||
        [2:--------------------------------------------------]|
 | 
			
		||||
        {3:-- TERMINAL --}                                    |
 | 
			
		||||
        [3:--------------------------------------------------]|
 | 
			
		||||
      ## grid 2
 | 
			
		||||
        tty ready           |
 | 
			
		||||
        rows: 10, cols: 20  |
 | 
			
		||||
@@ -192,6 +194,8 @@ describe(':terminal with multigrid', function()
 | 
			
		||||
                            |
 | 
			
		||||
                            |
 | 
			
		||||
                            |
 | 
			
		||||
      ## grid 3
 | 
			
		||||
        {3:-- TERMINAL --}                                    |
 | 
			
		||||
      ]])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@@ -207,11 +211,13 @@ describe(':terminal with multigrid', function()
 | 
			
		||||
        [2:--------------------------------------------------]|
 | 
			
		||||
        [2:--------------------------------------------------]|
 | 
			
		||||
        [2:--------------------------------------------------]|
 | 
			
		||||
        {3:-- TERMINAL --}                                    |
 | 
			
		||||
        [3:--------------------------------------------------]|
 | 
			
		||||
      ## grid 2
 | 
			
		||||
        rows: 10, cols: 20                                                    |
 | 
			
		||||
        rows: 3, cols: 70                                                     |
 | 
			
		||||
        {1: }                                                                     |
 | 
			
		||||
      ## grid 3
 | 
			
		||||
        {3:-- TERMINAL --}                                    |
 | 
			
		||||
      ]])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@@ -227,7 +233,7 @@ describe(':terminal with multigrid', function()
 | 
			
		||||
        [2:--------------------------------------------------]|
 | 
			
		||||
        [2:--------------------------------------------------]|
 | 
			
		||||
        [2:--------------------------------------------------]|
 | 
			
		||||
        {3:-- TERMINAL --}                                    |
 | 
			
		||||
        [3:--------------------------------------------------]|
 | 
			
		||||
      ## grid 2
 | 
			
		||||
        tty ready                                         |
 | 
			
		||||
        rows: 10, cols: 20                                |
 | 
			
		||||
@@ -235,6 +241,8 @@ describe(':terminal with multigrid', function()
 | 
			
		||||
        rows: 6, cols: 50                                 |
 | 
			
		||||
        {1: }                                                 |
 | 
			
		||||
                                                          |
 | 
			
		||||
      ## grid 3
 | 
			
		||||
        {3:-- TERMINAL --}                                    |
 | 
			
		||||
      ]])
 | 
			
		||||
    end
 | 
			
		||||
  end)
 | 
			
		||||
 
 | 
			
		||||
@@ -216,10 +216,10 @@ describe('ui/cursor', function()
 | 
			
		||||
        if m.blinkwait then m.blinkwait = 700 end
 | 
			
		||||
      end
 | 
			
		||||
      if m.hl_id then
 | 
			
		||||
          m.hl_id = 54
 | 
			
		||||
          m.hl_id = 55
 | 
			
		||||
          m.attr = {background = Screen.colors.DarkGray}
 | 
			
		||||
      end
 | 
			
		||||
      if m.id_lm then m.id_lm = 55 end
 | 
			
		||||
      if m.id_lm then m.id_lm = 56 end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    -- Assert the new expectation.
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ local function test_embed(ext_linegrid)
 | 
			
		||||
      [2] = {bold = true, foreground = Screen.colors.SeaGreen4},
 | 
			
		||||
      [3] = {bold = true, foreground = Screen.colors.Blue1},
 | 
			
		||||
      [4] = {bold = true, foreground = Screen.colors.Green},
 | 
			
		||||
      [5] = {bold = true, reverse = true},
 | 
			
		||||
    })
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -53,7 +54,7 @@ local function test_embed(ext_linegrid)
 | 
			
		||||
                                                                  |
 | 
			
		||||
                                                                  |
 | 
			
		||||
                                                                  |
 | 
			
		||||
                                                                  |
 | 
			
		||||
      {5:                                                            }|
 | 
			
		||||
      Error detected while processing pre-vimrc command line:     |
 | 
			
		||||
      foo                                                         |
 | 
			
		||||
      {1:bar}                                                         |
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -933,17 +933,29 @@ end)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
describe("MsgSeparator highlight and msgsep fillchar", function()
 | 
			
		||||
  before_each(clear)
 | 
			
		||||
  it("works", function()
 | 
			
		||||
    local screen = Screen.new(50,5)
 | 
			
		||||
  local screen
 | 
			
		||||
  before_each(function()
 | 
			
		||||
    clear()
 | 
			
		||||
    screen = Screen.new(50,5)
 | 
			
		||||
    screen:set_default_attr_ids({
 | 
			
		||||
      [1] = {bold=true, foreground=Screen.colors.Blue},
 | 
			
		||||
      [2] = {bold=true, reverse=true},
 | 
			
		||||
      [3] = {bold = true, foreground = Screen.colors.SeaGreen4},
 | 
			
		||||
      [4] = {background = Screen.colors.Cyan, bold = true, reverse = true},
 | 
			
		||||
      [5] = {bold = true, background = Screen.colors.Magenta}
 | 
			
		||||
      [5] = {bold = true, background = Screen.colors.Magenta},
 | 
			
		||||
      [6] = {background = Screen.colors.WebGray},
 | 
			
		||||
      [7] = {background = Screen.colors.WebGray, bold = true, foreground = Screen.colors.SeaGreen4},
 | 
			
		||||
      [8] = {foreground = Screen.colors.Grey0, background = Screen.colors.Gray60},
 | 
			
		||||
      [9] = {foreground = Screen.colors.Grey40, background = Screen.colors.Gray60},
 | 
			
		||||
      [10] = {foreground = tonumber('0x000019'), background = Screen.colors.Gray60},
 | 
			
		||||
      [11] = {background = Screen.colors.Gray60, bold = true, foreground = tonumber('0x666699')},
 | 
			
		||||
      [12] = {background = Screen.colors.Gray60, bold = true, foreground = tonumber('0x297d4e')},
 | 
			
		||||
      [13] = {background = tonumber('0xff4cff'), bold = true, foreground = tonumber('0xb200ff')},
 | 
			
		||||
    })
 | 
			
		||||
    screen:attach()
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  it("works", function()
 | 
			
		||||
 | 
			
		||||
    -- defaults
 | 
			
		||||
    feed_command("ls")
 | 
			
		||||
@@ -1000,6 +1012,61 @@ describe("MsgSeparator highlight and msgsep fillchar", function()
 | 
			
		||||
      {3:Press ENTER or type command to continue}^           |
 | 
			
		||||
    ]])
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  it("and MsgArea", function()
 | 
			
		||||
    feed_command("hi MsgArea guibg=Gray")
 | 
			
		||||
    screen:expect{grid=[[
 | 
			
		||||
      ^                                                  |
 | 
			
		||||
      {1:~                                                 }|
 | 
			
		||||
      {1:~                                                 }|
 | 
			
		||||
      {1:~                                                 }|
 | 
			
		||||
      {6:                                                  }|
 | 
			
		||||
    ]]}
 | 
			
		||||
    feed(":ls")
 | 
			
		||||
    screen:expect{grid=[[
 | 
			
		||||
                                                        |
 | 
			
		||||
      {1:~                                                 }|
 | 
			
		||||
      {1:~                                                 }|
 | 
			
		||||
      {1:~                                                 }|
 | 
			
		||||
      {6::ls^                                               }|
 | 
			
		||||
    ]]}
 | 
			
		||||
    feed(":<cr>")
 | 
			
		||||
    screen:expect{grid=[[
 | 
			
		||||
                                                        |
 | 
			
		||||
      {2:                                                  }|
 | 
			
		||||
      {6::ls:                                              }|
 | 
			
		||||
      {6:  1 %a   "[No Name]"                    line 1    }|
 | 
			
		||||
      {7:Press ENTER or type command to continue}{6:^           }|
 | 
			
		||||
    ]]}
 | 
			
		||||
 | 
			
		||||
    -- support madness^Wblending of message "overlay"
 | 
			
		||||
    feed_command("hi MsgArea blend=20")
 | 
			
		||||
    feed_command("hi clear MsgSeparator")
 | 
			
		||||
    feed_command("hi MsgSeparator blend=30 guibg=Magenta")
 | 
			
		||||
    screen:expect{grid=[[
 | 
			
		||||
      ^                                                  |
 | 
			
		||||
      {1:~                                                 }|
 | 
			
		||||
      {1:~                                                 }|
 | 
			
		||||
      {1:~                                                 }|
 | 
			
		||||
      {8::hi}{9: }{8:MsgSeparator}{9: }{8:blend=30}{9: }{8:guibg=Magenta}{9:           }|
 | 
			
		||||
    ]]}
 | 
			
		||||
    feed(":ls")
 | 
			
		||||
    screen:expect{grid=[[
 | 
			
		||||
                                                        |
 | 
			
		||||
      {1:~                                                 }|
 | 
			
		||||
      {1:~                                                 }|
 | 
			
		||||
      {1:~                                                 }|
 | 
			
		||||
      {8::ls}{9:^                                               }|
 | 
			
		||||
    ]]}
 | 
			
		||||
    feed("<cr>")
 | 
			
		||||
    screen:expect{grid=[[
 | 
			
		||||
                                                        |
 | 
			
		||||
      {13:~                                                 }|
 | 
			
		||||
      {10::ls}{11:                                               }|
 | 
			
		||||
      {11:~ }{10:1}{11: }{10:%a}{11:   }{10:"[No}{11: }{10:Name]"}{11:                    }{10:line}{11: }{10:1}{11:    }|
 | 
			
		||||
      {12:Press}{9: }{12:ENTER}{9: }{12:or}{9: }{12:type}{9: }{12:command}{9: }{12:to}{9: }{12:continue}{9:^           }|
 | 
			
		||||
    ]]}
 | 
			
		||||
  end)
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
describe("'winhighlight' highlight", function()
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
      {5:~                                       }|
 | 
			
		||||
      {5:~                                       }|
 | 
			
		||||
      {5:~                                       }|
 | 
			
		||||
      {6:search hit BOTTOM, continuing at TOP}    |
 | 
			
		||||
      {8:search hit BOTTOM, continuing at TOP}{7:    }|
 | 
			
		||||
    ]], {
 | 
			
		||||
    [1] = {{foreground = Screen.colors.Magenta},
 | 
			
		||||
           {{hi_name = "Constant", kind = "syntax"}}},
 | 
			
		||||
@@ -52,6 +52,8 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
           {{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}},
 | 
			
		||||
    [6] = {{foreground = Screen.colors.Red},
 | 
			
		||||
           {{hi_name = "WarningMsg", ui_name = "WarningMsg", kind = "ui"}}},
 | 
			
		||||
    [7] = {{}, {{hi_name = "MsgArea", ui_name = "MsgArea", kind = "ui"}}},
 | 
			
		||||
    [8] = {{foreground = Screen.colors.Red}, {7, 6}},
 | 
			
		||||
    })
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
@@ -66,6 +68,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
            {{hi_name = "StatusLineNC", ui_name = "StatusLineNC" , kind = "ui"}}},
 | 
			
		||||
      [5] = {{}, {{hi_name = "StatusLine", ui_name = "StatusLine", kind = "ui"}}},
 | 
			
		||||
      [6] = {{}, {{hi_name = "StatusLineNC", ui_name = "StatusLineNC", kind = "ui"}}},
 | 
			
		||||
      [7] = {{}, {{hi_name = "MsgArea", ui_name = "MsgArea", kind = "ui"}}},
 | 
			
		||||
    })
 | 
			
		||||
    command("hi clear VertSplit")
 | 
			
		||||
    command("vsplit")
 | 
			
		||||
@@ -78,7 +81,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
      {2:~                   }{1:│}{2:~                  }|
 | 
			
		||||
      {2:~                   }{1:│}{2:~                  }|
 | 
			
		||||
      {3:[No Name]            }{4:[No Name]          }|
 | 
			
		||||
                                              |
 | 
			
		||||
      {7:                                        }|
 | 
			
		||||
    ]])
 | 
			
		||||
 | 
			
		||||
    command("hi clear StatusLine | hi clear StatuslineNC")
 | 
			
		||||
@@ -90,7 +93,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
      {2:~                   }{1:│}{2:~                  }|
 | 
			
		||||
      {2:~                   }{1:│}{2:~                  }|
 | 
			
		||||
      {5:[No Name]            }{6:[No Name]          }|
 | 
			
		||||
                                              |
 | 
			
		||||
      {7:                                        }|
 | 
			
		||||
    ]])
 | 
			
		||||
 | 
			
		||||
    -- redrawing is done even if visible highlights didn't change
 | 
			
		||||
@@ -103,7 +106,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
      {2:~                  }{1:│}{2:~                   }|
 | 
			
		||||
      {2:~                  }{1:│}{2:~                   }|
 | 
			
		||||
      {6:[No Name]           }{5:[No Name]           }|
 | 
			
		||||
                                              |
 | 
			
		||||
      {7:                                        }|
 | 
			
		||||
    ]])
 | 
			
		||||
 | 
			
		||||
  end)
 | 
			
		||||
@@ -120,7 +123,8 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
        [8] = {{foreground = Screen.colors.Blue1, bold = true, reverse = true}, {6, 2}},
 | 
			
		||||
        [9] = {{bold = true, foreground = Screen.colors.Brown}, {{hi_name = "Statement", ui_name = "NormalNC", kind = "ui"}}},
 | 
			
		||||
        [10] = {{bold = true, foreground = Screen.colors.Brown}, {9, 1}},
 | 
			
		||||
        [11] = {{bold = true, foreground = Screen.colors.Blue1}, {9, 2}}
 | 
			
		||||
        [11] = {{bold = true, foreground = Screen.colors.Blue1}, {9, 2}},
 | 
			
		||||
        [12] = {{}, {{hi_name = "MsgArea", ui_name = "MsgArea", kind = "ui"}}},
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    command("set number")
 | 
			
		||||
@@ -134,7 +138,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
      {1:  1 }                                    |
 | 
			
		||||
      {2:~                                       }|
 | 
			
		||||
      {4:[No Name]                               }|
 | 
			
		||||
                                              |
 | 
			
		||||
      {12:                                        }|
 | 
			
		||||
    ]])
 | 
			
		||||
 | 
			
		||||
    command("set winhl=LineNr:ErrorMsg")
 | 
			
		||||
@@ -146,7 +150,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
      {1:  1 }                                    |
 | 
			
		||||
      {2:~                                       }|
 | 
			
		||||
      {4:[No Name]                               }|
 | 
			
		||||
                                              |
 | 
			
		||||
      {12:                                        }|
 | 
			
		||||
    ]])
 | 
			
		||||
 | 
			
		||||
    command("set winhl=Normal:MsgSeparator,NormalNC:Statement")
 | 
			
		||||
@@ -158,7 +162,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
      {1:  1 }                                    |
 | 
			
		||||
      {2:~                                       }|
 | 
			
		||||
      {4:[No Name]                               }|
 | 
			
		||||
                                              |
 | 
			
		||||
      {12:                                        }|
 | 
			
		||||
    ]])
 | 
			
		||||
 | 
			
		||||
    command("wincmd w")
 | 
			
		||||
@@ -170,7 +174,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
      {1:  1 }^                                    |
 | 
			
		||||
      {2:~                                       }|
 | 
			
		||||
      {3:[No Name]                               }|
 | 
			
		||||
                                              |
 | 
			
		||||
      {12:                                        }|
 | 
			
		||||
    ]])
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
@@ -182,6 +186,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
      [4] = {{foreground = 52479}, {2, 1}},
 | 
			
		||||
      [5] = {{foreground = 4259839}, {{kind = "term"}}},
 | 
			
		||||
      [6] = {{foreground = 4259839}, {5, 1}},
 | 
			
		||||
      [7] = {{}, {{hi_name = "MsgArea", ui_name = "MsgArea", kind = "ui"}}},
 | 
			
		||||
    })
 | 
			
		||||
    command('enew | call termopen(["'..nvim_dir..'/tty-test"])')
 | 
			
		||||
    screen:expect([[
 | 
			
		||||
@@ -192,7 +197,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
                                              |
 | 
			
		||||
                                              |
 | 
			
		||||
                                              |
 | 
			
		||||
                                              |
 | 
			
		||||
      {7:                                        }|
 | 
			
		||||
    ]])
 | 
			
		||||
 | 
			
		||||
    thelpers.feed_data('x ')
 | 
			
		||||
@@ -210,7 +215,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
                                                |
 | 
			
		||||
                                                |
 | 
			
		||||
                                                |
 | 
			
		||||
                                                |
 | 
			
		||||
        {7:                                        }|
 | 
			
		||||
      ]])
 | 
			
		||||
    else
 | 
			
		||||
      screen:expect([[
 | 
			
		||||
@@ -221,7 +226,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
                                                |
 | 
			
		||||
                                                |
 | 
			
		||||
                                                |
 | 
			
		||||
                                                |
 | 
			
		||||
        {7:                                        }|
 | 
			
		||||
      ]])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@@ -236,7 +241,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
                                                |
 | 
			
		||||
                                                |
 | 
			
		||||
                                                |
 | 
			
		||||
                                                |
 | 
			
		||||
        {7:                                        }|
 | 
			
		||||
      ]])
 | 
			
		||||
    else
 | 
			
		||||
      screen:expect([[
 | 
			
		||||
@@ -247,7 +252,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
                                                |
 | 
			
		||||
                                                |
 | 
			
		||||
                                                |
 | 
			
		||||
                                                |
 | 
			
		||||
        {7:                                        }|
 | 
			
		||||
      ]])
 | 
			
		||||
    end
 | 
			
		||||
  end)
 | 
			
		||||
@@ -259,6 +264,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
    screen:set_default_attr_ids({
 | 
			
		||||
        [1] = {{bold = true, foreground = Screen.colors.Blue1}, {foreground = 12}, {{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}},
 | 
			
		||||
        [2] = {{reverse = true, foreground = Screen.colors.Red}, {foreground = 10, italic=true}, {{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}},
 | 
			
		||||
        [3] = {{}, {}, {{hi_name = "MsgArea", ui_name = "MsgArea", kind = "ui"}}},
 | 
			
		||||
    })
 | 
			
		||||
    screen:expect([[
 | 
			
		||||
      ^                                        |
 | 
			
		||||
@@ -268,7 +274,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
      {1:~                                       }|
 | 
			
		||||
      {1:~                                       }|
 | 
			
		||||
      {1:~                                       }|
 | 
			
		||||
                                              |
 | 
			
		||||
      {3:                                        }|
 | 
			
		||||
    ]])
 | 
			
		||||
 | 
			
		||||
    command("hi NonText guifg=Red gui=reverse ctermfg=Green cterm=italic")
 | 
			
		||||
@@ -280,7 +286,7 @@ describe('ext_hlstate detailed highlights', function()
 | 
			
		||||
      {2:~                                       }|
 | 
			
		||||
      {2:~                                       }|
 | 
			
		||||
      {2:~                                       }|
 | 
			
		||||
                                              |
 | 
			
		||||
      {3:                                        }|
 | 
			
		||||
    ]])
 | 
			
		||||
 | 
			
		||||
  end)
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1128,7 +1128,7 @@ describe('builtin popupmenu', function()
 | 
			
		||||
      prefix      |
 | 
			
		||||
      bef{n: word  }  |
 | 
			
		||||
      tex{n: }^        |
 | 
			
		||||
      {2:-- }{s: text   } |
 | 
			
		||||
      {2:-- INSERT -} |
 | 
			
		||||
    ]])
 | 
			
		||||
 | 
			
		||||
    -- can't draw the pum, but check we don't crash
 | 
			
		||||
@@ -1597,6 +1597,54 @@ describe('builtin popupmenu', function()
 | 
			
		||||
    ]])
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  it('works with wildoptions=pum with scrolled mesages ', function()
 | 
			
		||||
    screen:try_resize(40,10)
 | 
			
		||||
    command('set wildmenu')
 | 
			
		||||
    command('set wildoptions=pum')
 | 
			
		||||
 | 
			
		||||
    feed(':echoerr "fail"|echoerr "error"<cr>')
 | 
			
		||||
    screen:expect{grid=[[
 | 
			
		||||
                                              |
 | 
			
		||||
      {1:~                                       }|
 | 
			
		||||
      {1:~                                       }|
 | 
			
		||||
      {1:~                                       }|
 | 
			
		||||
      {1:~                                       }|
 | 
			
		||||
      {1:~                                       }|
 | 
			
		||||
      {4:                                        }|
 | 
			
		||||
      {6:fail}                                    |
 | 
			
		||||
      {6:error}                                   |
 | 
			
		||||
      {5:Press ENTER or type command to continue}^ |
 | 
			
		||||
    ]]}
 | 
			
		||||
 | 
			
		||||
    feed(':sign <tab>')
 | 
			
		||||
    screen:expect{grid=[[
 | 
			
		||||
                                              |
 | 
			
		||||
      {1:~                                       }|
 | 
			
		||||
      {1:~                                       }|
 | 
			
		||||
      {1:~    }{s: define         }{1:                   }|
 | 
			
		||||
      {1:~    }{n: jump           }{1:                   }|
 | 
			
		||||
      {1:~    }{n: list           }{1:                   }|
 | 
			
		||||
      {4:     }{n: place          }{4:                   }|
 | 
			
		||||
      {6:fail} {n: undefine       }                   |
 | 
			
		||||
      {6:error}{n: unplace        }                   |
 | 
			
		||||
      :sign define^                            |
 | 
			
		||||
    ]]}
 | 
			
		||||
 | 
			
		||||
    feed('d')
 | 
			
		||||
    screen:expect{grid=[[
 | 
			
		||||
                                              |
 | 
			
		||||
      {1:~                                       }|
 | 
			
		||||
      {1:~                                       }|
 | 
			
		||||
      {1:~                                       }|
 | 
			
		||||
      {1:~                                       }|
 | 
			
		||||
      {1:~                                       }|
 | 
			
		||||
      {4:                                        }|
 | 
			
		||||
      {6:fail}                                    |
 | 
			
		||||
      {6:error}                                   |
 | 
			
		||||
      :sign defined^                           |
 | 
			
		||||
    ]]}
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  it("'pumblend' RGB-color", function()
 | 
			
		||||
    screen:try_resize(60,14)
 | 
			
		||||
    screen:set_default_attr_ids({
 | 
			
		||||
 
 | 
			
		||||
@@ -159,6 +159,8 @@ function Screen.new(width, height)
 | 
			
		||||
    wildmenu_selected = nil,
 | 
			
		||||
    win_position = {},
 | 
			
		||||
    float_pos = {},
 | 
			
		||||
    msg_grid = nil,
 | 
			
		||||
    msg_grid_pos = nil,
 | 
			
		||||
    _session = nil,
 | 
			
		||||
    messages = {},
 | 
			
		||||
    msg_history = {},
 | 
			
		||||
@@ -391,29 +393,33 @@ function Screen:expect(expected, attr_ids, attr_ignore, ...)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    if grid ~= nil then
 | 
			
		||||
      local err_msg, msg_expected_rows = nil, {}
 | 
			
		||||
      -- `expected` must match the screen lines exactly.
 | 
			
		||||
      if #actual_rows ~= #expected_rows then
 | 
			
		||||
        return "Expected screen state's row count(" .. #expected_rows
 | 
			
		||||
        .. ') differs from configured height(' .. #actual_rows .. ') of Screen.'
 | 
			
		||||
        err_msg = "Expected screen height " .. #expected_rows
 | 
			
		||||
        .. ' differs from actual height ' .. #actual_rows .. '.'
 | 
			
		||||
      end
 | 
			
		||||
      for i = 1, #actual_rows do
 | 
			
		||||
      for i = 1, #expected_rows do
 | 
			
		||||
         msg_expected_rows[i] = expected_rows[i]
 | 
			
		||||
        if expected_rows[i] ~= actual_rows[i] and expected_rows[i] ~= "{IGNORE}|" then
 | 
			
		||||
          local msg_expected_rows = {}
 | 
			
		||||
          for j = 1, #expected_rows do
 | 
			
		||||
            msg_expected_rows[j] = expected_rows[j]
 | 
			
		||||
          end
 | 
			
		||||
          msg_expected_rows[i] = '*' .. msg_expected_rows[i]
 | 
			
		||||
          if i <= #actual_rows then
 | 
			
		||||
            actual_rows[i] = '*' .. actual_rows[i]
 | 
			
		||||
          end
 | 
			
		||||
          if err_msg == nil then
 | 
			
		||||
            err_msg = 'Row ' .. tostring(i) .. ' did not match.'
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
      if err_msg ~= nil then
 | 
			
		||||
        return (
 | 
			
		||||
            'Row ' .. tostring(i) .. ' did not match.\n'
 | 
			
		||||
            ..'Expected:\n  |'..table.concat(msg_expected_rows, '\n  |')..'\n'
 | 
			
		||||
          err_msg..'\nExpected:\n  |'..table.concat(msg_expected_rows, '\n  |')..'\n'
 | 
			
		||||
          ..'Actual:\n  |'..table.concat(actual_rows, '\n  |')..'\n\n'..[[
 | 
			
		||||
To print the expect() call that would assert the current screen state, use
 | 
			
		||||
screen:snapshot_util(). In case of non-deterministic failures, use
 | 
			
		||||
screen:redraw_debug() to show all intermediate screen states.  ]])
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    -- UI extensions. The default expectations should cover the case of
 | 
			
		||||
    -- the ext_ feature being disabled, or the feature currently not activated
 | 
			
		||||
@@ -672,13 +678,12 @@ function Screen:_handle_grid_resize(grid, width, height)
 | 
			
		||||
  }
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Screen:_handle_win_scroll_over_start()
 | 
			
		||||
   self.scroll_over = true
 | 
			
		||||
   self.scroll_over_pos = self._grids[1].height
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Screen:_handle_win_scroll_over_reset()
 | 
			
		||||
   self.scroll_over = false
 | 
			
		||||
function Screen:_handle_msg_set_pos(grid, row, scrolled, char)
 | 
			
		||||
  self.msg_grid = grid
 | 
			
		||||
  self.msg_grid_pos = row
 | 
			
		||||
  self.msg_scrolled = scrolled
 | 
			
		||||
  self.msg_sep_char = char
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Screen:_handle_flush()
 | 
			
		||||
@@ -819,10 +824,6 @@ function Screen:_handle_scroll(count)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Screen:_handle_grid_scroll(g, top, bot, left, right, rows, cols)
 | 
			
		||||
  if self.scroll_over and g == 1 and top < self.scroll_over_pos then
 | 
			
		||||
    self.scroll_over_pos = top
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  top = top+1
 | 
			
		||||
  left = left+1
 | 
			
		||||
  assert(cols == 0)
 | 
			
		||||
@@ -1072,10 +1073,10 @@ function Screen:_row_repr(gridnr, rownr, attr_state, cursor)
 | 
			
		||||
  local current_attr_id
 | 
			
		||||
  local i = 1
 | 
			
		||||
  local has_windows = self._options.ext_multigrid and gridnr == 1
 | 
			
		||||
  if self.scroll_over and self.scroll_over_pos < rownr then
 | 
			
		||||
    has_windows = false
 | 
			
		||||
  end
 | 
			
		||||
  local row = self._grids[gridnr].rows[rownr]
 | 
			
		||||
  if has_windows and self.msg_grid and self.msg_grid_pos < rownr then
 | 
			
		||||
    return '['..self.msg_grid..':'..string.rep('-',#row)..']'
 | 
			
		||||
  end
 | 
			
		||||
  while i <= #row do
 | 
			
		||||
    local did_window = false
 | 
			
		||||
    if has_windows then
 | 
			
		||||
@@ -1214,12 +1215,17 @@ function Screen:render(headers, attr_state, preview)
 | 
			
		||||
  for igrid,grid in pairs(self._grids) do
 | 
			
		||||
    if headers then
 | 
			
		||||
      local suffix = ""
 | 
			
		||||
      if igrid > 1 and self.win_position[igrid] == nil and self.float_pos[igrid] == nil then
 | 
			
		||||
      if igrid > 1 and self.win_position[igrid] == nil
 | 
			
		||||
        and self.float_pos[igrid] == nil and self.msg_grid ~= igrid then
 | 
			
		||||
        suffix = " (hidden)"
 | 
			
		||||
      end
 | 
			
		||||
      table.insert(rv, "## grid "..igrid..suffix)
 | 
			
		||||
    end
 | 
			
		||||
    for i = 1, grid.height do
 | 
			
		||||
    local height = grid.height
 | 
			
		||||
    if igrid == self.msg_grid then
 | 
			
		||||
      height = self._grids[1].height - self.msg_grid_pos
 | 
			
		||||
    end
 | 
			
		||||
    for i = 1, height do
 | 
			
		||||
      local cursor = self._cursor.grid == igrid and self._cursor.row == i
 | 
			
		||||
      local prefix = (headers or preview) and "  " or ""
 | 
			
		||||
      table.insert(rv, prefix..self:_row_repr(igrid, i, attr_state, cursor).."|")
 | 
			
		||||
@@ -1298,6 +1304,7 @@ function Screen:print_snapshot(attrs, ignore)
 | 
			
		||||
    end
 | 
			
		||||
    attrstr = (", attr_ids={\n"..table.concat(attrstrs, "\n").."\n}")
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  print( "\nscreen:expect{grid=[[")
 | 
			
		||||
  print(kwargs.grid)
 | 
			
		||||
  io.stdout:write( "]]"..attrstr)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user