mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	| @@ -4969,6 +4969,8 @@ MatchParen	The character under the cursor or just before it, if it | |||||||
|  |  | ||||||
| 							*hl-ModeMsg* | 							*hl-ModeMsg* | ||||||
| ModeMsg		'showmode' message (e.g., "-- INSERT --") | ModeMsg		'showmode' message (e.g., "-- INSERT --") | ||||||
|  | 							*hl-MsgArea* | ||||||
|  | MsgArea		Area for messages and cmdline | ||||||
| 							*hl-MsgSeparator* | 							*hl-MsgSeparator* | ||||||
| MsgSeparator	Separator for scrolled messages, `msgsep` flag of 'display' | MsgSeparator	Separator for scrolled messages, `msgsep` flag of 'display' | ||||||
| 							*hl-MoreMsg* | 							*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 |   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 |   per-window. Or reserve space around the border of the window for its own | ||||||
|   elements, such as scrollbars from the UI toolkit. |   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 | 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. | (i.e. the size of the window frame in Nvim) whenever the split is created. | ||||||
| @@ -573,19 +575,20 @@ tabs. | |||||||
| ["win_hide", grid] | ["win_hide", grid] | ||||||
| 	Stop displaying the window. The window can be shown again later. | 	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] | ["win_close", grid] | ||||||
| 	Close the window. | 	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* | 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_title = remote_ui_set_title; | ||||||
|   ui->set_icon = remote_ui_set_icon; |   ui->set_icon = remote_ui_set_icon; | ||||||
|   ui->option_set = remote_ui_option_set; |   ui->option_set = remote_ui_option_set; | ||||||
|   ui->win_scroll_over_start = remote_ui_win_scroll_over_start; |   ui->msg_set_pos = remote_ui_msg_set_pos; | ||||||
|   ui->win_scroll_over_reset = remote_ui_win_scroll_over_reset; |  | ||||||
|   ui->event = remote_ui_event; |   ui->event = remote_ui_event; | ||||||
|   ui->inspect = remote_ui_inspect; |   ui->inspect = remote_ui_inspect; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -112,9 +112,7 @@ void win_hide(Integer grid) | |||||||
|   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; |   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; | ||||||
| void win_close(Integer grid) | void win_close(Integer grid) | ||||||
|   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; |   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; | ||||||
| void win_scroll_over_start(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 win_scroll_over_reset(void) |  | ||||||
|   FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL; |   FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL; | ||||||
|  |  | ||||||
| void popupmenu_show(Array items, Integer selected, | 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 |         buf == curbuf ? (int64_t)curwin->w_cursor.lnum | ||||||
|                       : (int64_t)buflist_findlnum(buf)); |                       : (int64_t)buflist_findlnum(buf)); | ||||||
|     msg_outtrans(IObuff); |     msg_outtrans(IObuff); | ||||||
|     ui_flush();            // output one line at a time |     line_breakcheck(); | ||||||
|     os_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 |  * "screenattr()" function | ||||||
|  */ |  */ | ||||||
| @@ -14655,13 +14670,15 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) | |||||||
| { | { | ||||||
|   int c; |   int c; | ||||||
|  |  | ||||||
|   const int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; |   int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; | ||||||
|   const int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; |   int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; | ||||||
|   if (row < 0 || row >= default_grid.Rows |   if (row < 0 || row >= default_grid.Rows | ||||||
|       || col < 0 || col >= default_grid.Columns) { |       || col < 0 || col >= default_grid.Columns) { | ||||||
|     c = -1; |     c = -1; | ||||||
|   } else { |   } 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; |   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) | static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) | ||||||
| { | { | ||||||
|   int off; |  | ||||||
|   int c; |   int c; | ||||||
|  |  | ||||||
|   const int row = tv_get_number_chk(&argvars[0], NULL) - 1; |   int row = tv_get_number_chk(&argvars[0], NULL) - 1; | ||||||
|   const int col = tv_get_number_chk(&argvars[1], NULL) - 1; |   int col = tv_get_number_chk(&argvars[1], NULL) - 1; | ||||||
|   if (row < 0 || row >= default_grid.Rows |   if (row < 0 || row >= default_grid.Rows | ||||||
|       || col < 0 || col >= default_grid.Columns) { |       || col < 0 || col >= default_grid.Columns) { | ||||||
|     c = -1; |     c = -1; | ||||||
|   } else { |   } else { | ||||||
|     off = default_grid.line_offset[row] + col; |     ScreenGrid *grid = &default_grid; | ||||||
|     c = utf_ptr2char(default_grid.chars[off]); |     screenchar_adjust_grid(&grid, &row, &col); | ||||||
|  |     c = utf_ptr2char(grid->chars[grid->line_offset[row] + col]); | ||||||
|   } |   } | ||||||
|   rettv->vval.v_number = c; |   rettv->vval.v_number = c; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -310,6 +310,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) | |||||||
|     cmdmsg_rl = false; |     cmdmsg_rl = false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   msg_grid_validate(); | ||||||
|  |  | ||||||
|   redir_off = true;             // don't redirect the typed command |   redir_off = true;             // don't redirect the typed command | ||||||
|   if (!cmd_silent) { |   if (!cmd_silent) { | ||||||
|     gotocmdline(true); |     gotocmdline(true); | ||||||
| @@ -908,7 +910,7 @@ static int command_line_execute(VimState *state, int key) | |||||||
|  |  | ||||||
|       if (!cmd_silent) { |       if (!cmd_silent) { | ||||||
|         if (!ui_has(kUICmdline)) { |         if (!ui_has(kUICmdline)) { | ||||||
|           ui_cursor_goto(msg_row, 0); |           cmd_cursor_goto(msg_row, 0); | ||||||
|         } |         } | ||||||
|         ui_flush(); |         ui_flush(); | ||||||
|       } |       } | ||||||
| @@ -2323,7 +2325,7 @@ redraw: | |||||||
|           } |           } | ||||||
|         } |         } | ||||||
|         msg_clr_eos(); |         msg_clr_eos(); | ||||||
|         ui_cursor_goto(msg_row, msg_col); |         cmd_cursor_goto(msg_row, msg_col); | ||||||
|         continue; |         continue; | ||||||
|       } |       } | ||||||
|  |  | ||||||
| @@ -2391,7 +2393,7 @@ redraw: | |||||||
|     line_ga.ga_len += len; |     line_ga.ga_len += len; | ||||||
|     escaped = FALSE; |     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; |     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 |     /* 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 */ |   /* when 'incsearch' is set there may be no command line while redrawing */ | ||||||
|   if (ccline.cmdbuff == NULL) { |   if (ccline.cmdbuff == NULL) { | ||||||
|     ui_cursor_goto(cmdline_row, 0); |     cmd_cursor_goto(cmdline_row, 0); | ||||||
|     msg_clr_eos(); |     msg_clr_eos(); | ||||||
|     return; |     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) | void gotocmdline(int clr) | ||||||
| @@ -3519,13 +3528,15 @@ void gotocmdline(int clr) | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   msg_start(); |   msg_start(); | ||||||
|   if (cmdmsg_rl) |   if (cmdmsg_rl) { | ||||||
|     msg_col = Columns - 1; |     msg_col = Columns - 1; | ||||||
|   else |   } else { | ||||||
|     msg_col = 0;            /* always start in column 0 */ |     msg_col = 0;  // always start in column 0 | ||||||
|   if (clr)                  /* clear the bottom line(s) */ |   } | ||||||
|     msg_clr_eos();          /* will reset clear_cmdline */ |   if (clr) {  // clear the bottom line(s) | ||||||
|   ui_cursor_goto(cmdline_row, 0); |     msg_clr_eos();  // will reset clear_cmdline | ||||||
|  |   } | ||||||
|  |   cmd_cursor_goto(cmdline_row, 0); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|   | |||||||
| @@ -43,6 +43,10 @@ typedef struct { | |||||||
|   unsigned *line_offset; |   unsigned *line_offset; | ||||||
|   char_u   *line_wraps; |   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. |   // the size of the allocated grid. | ||||||
|   int Rows; |   int Rows; | ||||||
|   int Columns; |   int Columns; | ||||||
| @@ -50,21 +54,38 @@ typedef struct { | |||||||
|   // The state of the grid is valid. Otherwise it needs to be redrawn. |   // The state of the grid is valid. Otherwise it needs to be redrawn. | ||||||
|   bool valid; |   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 row_offset; | ||||||
|   int col_offset; |   int col_offset; | ||||||
|  |  | ||||||
|   // whether the compositor should blend the grid with the background grid |   // whether the compositor should blend the grid with the background grid | ||||||
|   bool blending; |   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_row; | ||||||
|   int comp_col; |   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; |   size_t comp_index; | ||||||
|  |  | ||||||
|  |   // compositor should momentarily ignore the grid. Used internally when | ||||||
|  |   // moving around grids etc. | ||||||
|   bool comp_disabled; |   bool comp_disabled; | ||||||
| } ScreenGrid; | } ScreenGrid; | ||||||
|  |  | ||||||
| #define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, 0, 0, false, 0, 0, \ | #define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, false, \ | ||||||
|                            false, 0, 0, 0,  false } |                            false, 0, 0, false, true, 0, 0, 0,  false } | ||||||
|  |  | ||||||
| #endif  // NVIM_GRID_DEFS_H | #endif  // NVIM_GRID_DEFS_H | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ | |||||||
| #include "nvim/highlight.h" | #include "nvim/highlight.h" | ||||||
| #include "nvim/highlight_defs.h" | #include "nvim/highlight_defs.h" | ||||||
| #include "nvim/map.h" | #include "nvim/map.h" | ||||||
|  | #include "nvim/message.h" | ||||||
| #include "nvim/popupmnu.h" | #include "nvim/popupmnu.h" | ||||||
| #include "nvim/screen.h" | #include "nvim/screen.h" | ||||||
| #include "nvim/syntax.h" | #include "nvim/syntax.h" | ||||||
| @@ -161,6 +162,8 @@ int hl_get_ui_attr(int idx, int final_id, bool optional) | |||||||
|     if (pum_drawn()) { |     if (pum_drawn()) { | ||||||
|       must_redraw_pum = true; |       must_redraw_pum = true; | ||||||
|     } |     } | ||||||
|  |   } else if (idx == HLF_MSG) { | ||||||
|  |     msg_grid.blending = attrs.hl_blend > -1; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (optional && !available) { |   if (optional && !available) { | ||||||
|   | |||||||
| @@ -93,6 +93,7 @@ typedef enum { | |||||||
|   , HLF_INACTIVE    // NormalNC: Normal text in non-current windows |   , HLF_INACTIVE    // NormalNC: Normal text in non-current windows | ||||||
|   , HLF_MSGSEP      // message separator line |   , HLF_MSGSEP      // message separator line | ||||||
|   , HLF_NFLOAT      // Floating window |   , HLF_NFLOAT      // Floating window | ||||||
|  |   , HLF_MSG         // Message area | ||||||
|   , HLF_COUNT       // MUST be the last one |   , HLF_COUNT       // MUST be the last one | ||||||
| } hlf_T; | } hlf_T; | ||||||
|  |  | ||||||
| @@ -146,6 +147,7 @@ EXTERN const char *hlf_names[] INIT(= { | |||||||
|   [HLF_INACTIVE] = "NormalNC", |   [HLF_INACTIVE] = "NormalNC", | ||||||
|   [HLF_MSGSEP] = "MsgSeparator", |   [HLF_MSGSEP] = "MsgSeparator", | ||||||
|   [HLF_NFLOAT] = "NormalFloat", |   [HLF_NFLOAT] = "NormalFloat", | ||||||
|  |   [HLF_MSG] = "MsgArea", | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3523,10 +3523,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, | |||||||
|             xfree(name); |             xfree(name); | ||||||
|  |  | ||||||
|             // pretend screen didn't scroll, need redraw anyway |             // pretend screen didn't scroll, need redraw anyway | ||||||
|             // TODO(bfredl): when doing the message grid refactor, |             msg_reset_scroll(); | ||||||
|             // simplify this special case. |  | ||||||
|             msg_scrolled = 0; |  | ||||||
|             redraw_all_later(NOT_VALID); |  | ||||||
|           } |           } | ||||||
|  |  | ||||||
|           if (choice > 0) { |           if (choice > 0) { | ||||||
|   | |||||||
| @@ -35,7 +35,9 @@ | |||||||
| #include "nvim/screen.h" | #include "nvim/screen.h" | ||||||
| #include "nvim/strings.h" | #include "nvim/strings.h" | ||||||
| #include "nvim/syntax.h" | #include "nvim/syntax.h" | ||||||
|  | #include "nvim/highlight.h" | ||||||
| #include "nvim/ui.h" | #include "nvim/ui.h" | ||||||
|  | #include "nvim/ui_compositor.h" | ||||||
| #include "nvim/mouse.h" | #include "nvim/mouse.h" | ||||||
| #include "nvim/os/os.h" | #include "nvim/os/os.h" | ||||||
| #include "nvim/os/input.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 | /// Shouldn't clear message after leaving cmdline | ||||||
| static bool msg_ext_keep_after_cmdline = false; | 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 |  * msg(s) - displays the string 's' on the status line | ||||||
|  * When terminal not initialized (yet) mch_errmsg(..) is used. |  * 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) | static char_u *screen_puts_mbyte(char_u *s, int l, int attr) | ||||||
| { | { | ||||||
|   int cw; |   int cw; | ||||||
|  |   attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr); | ||||||
|  |  | ||||||
|   msg_didout = true;            // remember that line is not empty |   msg_didout = true;            // remember that line is not empty | ||||||
|   cw = utf_ptr2cells(s); |   cw = utf_ptr2cells(s); | ||||||
| @@ -1711,7 +1783,7 @@ static char_u *screen_puts_mbyte(char_u *s, int l, int attr) | |||||||
|     return s; |     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) { |   if (cmdmsg_rl) { | ||||||
|     msg_col -= cw; |     msg_col -= cw; | ||||||
|     if (msg_col == 0) { |     if (msg_col == 0) { | ||||||
| @@ -1900,6 +1972,8 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   msg_grid_validate(); | ||||||
|  |  | ||||||
|   cmdline_was_last_drawn = redrawing_cmdline; |   cmdline_was_last_drawn = redrawing_cmdline; | ||||||
|  |  | ||||||
|   while ((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL) { |   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) |       if (msg_no_more && lines_left == 0) | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|       /* Scroll the screen up one line. */ |       // Scroll the screen up one line. | ||||||
|       msg_scroll_up(); |       bool has_last_char = (*s >= ' ' && !cmdmsg_rl); | ||||||
|  |       msg_scroll_up(!has_last_char); | ||||||
|  |  | ||||||
|       msg_row = Rows - 2; |       msg_row = Rows - 2; | ||||||
|       if (msg_col >= Columns)           /* can happen after screen resize */ |       if (msg_col >= Columns)           /* can happen after screen resize */ | ||||||
|         msg_col = Columns - 1; |         msg_col = Columns - 1; | ||||||
|  |  | ||||||
|       // Display char in last column before showing more-prompt. |       // Display char in last column before showing more-prompt. | ||||||
|       if (*s >= ' ' && !cmdmsg_rl) { |       if (has_last_char) { | ||||||
|         if (maxlen >= 0) { |         if (maxlen >= 0) { | ||||||
|           // Avoid including composing chars after the end. |           // Avoid including composing chars after the end. | ||||||
|           l = utfc_ptr2len_len(s, (int)((str + maxlen) - s)); |           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; |         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) { |       if (p_more) { | ||||||
|         // Store text for scrolling back. |         // Store text for scrolling back. | ||||||
|         store_sb_text((char_u **)&sb_str, (char_u *)s, attr, &sb_col, true); |         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; |   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. |  * 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) { |   if (may_throttle && msg_dothrottle()) { | ||||||
|     ui_call_win_scroll_over_start(); |     msg_grid.throttled = true; | ||||||
|     msg_did_scroll = true; |  | ||||||
|   } |   } | ||||||
|   if (dy_flags & DY_MSGSEP) { |   msg_did_scroll = true; | ||||||
|     if (msg_scrolled == 0) { |   if (msg_use_msgsep()) { | ||||||
|       grid_fill(&default_grid, Rows-p_ch-1, Rows-p_ch, 0, (int)Columns, |     if (msg_grid_pos > 0) { | ||||||
|                 curwin->w_p_fcs_chars.msgsep, curwin->w_p_fcs_chars.msgsep, |       msg_grid_set_pos(msg_grid_pos-1, true); | ||||||
|                 HL_ATTR(HLF_MSGSEP)); |     } else { | ||||||
|  |       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; | ||||||
|     } |     } | ||||||
|     int nscroll = MIN(msg_scrollsize()+1, Rows); |  | ||||||
|     grid_del_lines(&default_grid, Rows-nscroll, 1, Rows, 0, Columns); |  | ||||||
|   } else { |   } else { | ||||||
|     grid_del_lines(&default_grid, 0, 1, (int)Rows, 0, Columns); |     grid_del_lines(&msg_grid_adj, 0, 1, Rows, 0, Columns); | ||||||
|   } |   } | ||||||
|   // TODO(bfredl): when msgsep display is properly batched, this fill should be |  | ||||||
|   // eliminated. |   grid_fill(&msg_grid_adj, Rows-1, Rows, 0, Columns, ' ', ' ', | ||||||
|   grid_fill(&default_grid, Rows-1, Rows, 0, (int)Columns, ' ', ' ', 0); |             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; |       break; | ||||||
|     mp = mp->sb_next; |     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; |   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) | 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. |   // Output postponed text. | ||||||
|   msg_didout = true;  // Remember that line is not empty. |   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); |                 attr); | ||||||
|   msg_col += *t_col; |   msg_col += *t_col; | ||||||
|   *t_col = 0; |   *t_col = 0; | ||||||
| @@ -2514,14 +2696,14 @@ static int do_more_prompt(int typed_char) | |||||||
|           } |           } | ||||||
|  |  | ||||||
|           if (toscroll == -1) { |           if (toscroll == -1) { | ||||||
|             grid_ins_lines(&default_grid, 0, 1, (int)Rows, 0, (int)Columns); |             grid_ins_lines(&msg_grid_adj, 0, 1, Rows, 0, Columns); | ||||||
|             grid_fill(&default_grid, 0, 1, 0, (int)Columns, ' ', ' ', 0); |  | ||||||
|             // display line at top |             // display line at top | ||||||
|             (void)disp_sb_line(0, mp); |             (void)disp_sb_line(0, mp); | ||||||
|           } else { |           } else { | ||||||
|             /* redisplay all lines */ |             // redisplay all lines | ||||||
|             screenclear(); |             // TODO(bfredl): this case is not optimized (though only concerns | ||||||
|             for (i = 0; mp != NULL && i < Rows - 1; ++i) { |             // event fragmentization, not unnecessary scroll events). | ||||||
|  |             for (i = 0; mp != NULL && i < Rows - 1; i++) { | ||||||
|               mp = disp_sb_line(i, mp); |               mp = disp_sb_line(i, mp); | ||||||
|               ++msg_scrolled; |               ++msg_scrolled; | ||||||
|             } |             } | ||||||
| @@ -2531,20 +2713,24 @@ static int do_more_prompt(int typed_char) | |||||||
|       } else { |       } else { | ||||||
|         /* First display any text that we scrolled back. */ |         /* First display any text that we scrolled back. */ | ||||||
|         while (toscroll > 0 && mp_last != NULL) { |         while (toscroll > 0 && mp_last != NULL) { | ||||||
|           /* scroll up, display line at bottom */ |           if (msg_dothrottle() && !msg_grid.throttled) { | ||||||
|           msg_scroll_up(); |             // 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(); |           inc_msg_scrolled(); | ||||||
|           grid_fill(&default_grid, (int)Rows - 2, (int)Rows - 1, 0, |           mp_last = disp_sb_line(Rows - 2, mp_last); | ||||||
|                     (int)Columns, ' ', ' ', 0); |           toscroll--; | ||||||
|           mp_last = disp_sb_line((int)Rows - 2, mp_last); |  | ||||||
|           --toscroll; |  | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       if (toscroll <= 0) { |       if (toscroll <= 0) { | ||||||
|         // displayed the requested text, more prompt again |         // displayed the requested text, more prompt again | ||||||
|         grid_fill(&default_grid, (int)Rows - 1, (int)Rows, 0, |         grid_fill(&msg_grid_adj, Rows - 1, Rows, 0, Columns, ' ', ' ', | ||||||
|                   (int)Columns, ' ', ' ', 0); |                   HL_ATTR(HLF_MSG)); | ||||||
|         msg_moremsg(false); |         msg_moremsg(false); | ||||||
|         continue; |         continue; | ||||||
|       } |       } | ||||||
| @@ -2557,8 +2743,11 @@ static int do_more_prompt(int typed_char) | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   // clear the --more-- message |   // clear the --more-- message | ||||||
|   grid_fill(&default_grid, (int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', |   grid_fill(&msg_grid_adj, Rows - 1, Rows, 0, Columns, ' ', ' ', 0); | ||||||
|             0); |   redraw_cmdline = true; | ||||||
|  |   clear_cmdline = false; | ||||||
|  |   mode_displayed = false; | ||||||
|  |  | ||||||
|   State = oldState; |   State = oldState; | ||||||
|   setmouse(); |   setmouse(); | ||||||
|   if (quit_more) { |   if (quit_more) { | ||||||
| @@ -2607,8 +2796,9 @@ void mch_msg(char *str) | |||||||
|  */ |  */ | ||||||
| static void msg_screen_putchar(int c, int attr) | 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 |   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 (cmdmsg_rl) { | ||||||
|     if (--msg_col == 0) { |     if (--msg_col == 0) { | ||||||
|       msg_col = Columns; |       msg_col = Columns; | ||||||
| @@ -2628,11 +2818,11 @@ void msg_moremsg(int full) | |||||||
|   char_u      *s = (char_u *)_("-- More --"); |   char_u      *s = (char_u *)_("-- More --"); | ||||||
|  |  | ||||||
|   attr = HL_ATTR(HLF_M); |   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) { |   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 "), |               _(" 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; |     return; | ||||||
|   } |   } | ||||||
|   int msg_startcol = (cmdmsg_rl) ? 0 : msg_col; |   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, ' ', |   if (msg_grid.chars && msg_row < msg_grid_pos) { | ||||||
|             ' ', 0); |     // TODO(bfredl): ugly, this state should already been validated at this | ||||||
|   grid_fill(&default_grid, msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ', |     // point. But msg_clr_eos() is called in a lot of places. | ||||||
|             0); |     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; |     return FALSE; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // @TODO(bfredl): calling flush here inhibits substantial performance |   // NOTE: ui_flush() used to be called here. This had to be removed, as it | ||||||
|   // improvements. Caller should call ui_flush before waiting on user input or |   // inhibited substantial performance improvements. It is assumed that relevant | ||||||
|   // CPU busywork. |   // callers invoke ui_flush() before going into CPU busywork, or restricted | ||||||
|   ui_flush();  // calls msg_ext_ui_flush |   // event processing after displaying a message to the user. | ||||||
|  |   msg_ext_ui_flush(); | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ | |||||||
|  |  | ||||||
| #include "nvim/macros.h" | #include "nvim/macros.h" | ||||||
| #include "nvim/types.h" | #include "nvim/types.h" | ||||||
|  | #include "nvim/grid_defs.h" | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Types of dialogs passed to do_dialog(). |  * Types of dialogs passed to do_dialog(). | ||||||
| @@ -90,6 +91,23 @@ extern MessageHistoryEntry *last_msg_hist; | |||||||
|  |  | ||||||
| EXTERN bool msg_ext_need_clear INIT(= false); | 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 | #ifdef INCLUDE_GENERATED_DECLARATIONS | ||||||
| # include "message.h.generated.h" | # include "message.h.generated.h" | ||||||
| #endif | #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); |   win_T *wp_grid = mouse_find_grid_win(gridp, rowp, colp); | ||||||
|   if (wp_grid) { |   if (wp_grid) { | ||||||
|     return wp_grid; |     return wp_grid; | ||||||
|  |   } else if (*gridp > 1) { | ||||||
|  |     return NULL; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|   frame_T     *fp; |   frame_T     *fp; | ||||||
|  |  | ||||||
|   fp = topframe; |   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) | 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); |     win_T *wp = get_win_by_grid_handle(*gridp); | ||||||
|     if (wp && wp->w_grid.chars |     if (wp && wp->w_grid.chars | ||||||
|         && !(wp->w_floating && !wp->w_float_config.focusable)) { |         && !(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) { |   } else if (*gridp == 0) { | ||||||
|     ScreenGrid *grid = ui_comp_mouse_focus(*rowp, *colp); |     ScreenGrid *grid = ui_comp_mouse_focus(*rowp, *colp); | ||||||
|     FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { |     FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { | ||||||
|       if (&wp->w_grid != grid || !wp->w_float_config.focusable) { |       if (&wp->w_grid != grid) { | ||||||
|         continue; |         continue; | ||||||
|       } |       } | ||||||
|       *gridp = grid->handle; |       *gridp = grid->handle; | ||||||
|   | |||||||
| @@ -3455,16 +3455,18 @@ static void display_showcmd(void) | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   msg_grid_validate(); | ||||||
|   int showcmd_row = Rows - 1; |   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) { |   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 |   // clear the rest of an old message by outputting up to SHOWCMD_COLS spaces | ||||||
|   grid_puts(&default_grid, (char_u *)"          " + len, showcmd_row, |   grid_puts(&msg_grid_adj, (char_u *)"          " + len, showcmd_row, | ||||||
|             sc_col + len, 0); |             sc_col + len, HL_ATTR(HLF_MSG)); | ||||||
|  |  | ||||||
|   grid_puts_line_flush(false); |   grid_puts_line_flush(false); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2989,6 +2989,7 @@ ambw_end: | |||||||
|       errmsg = e_invarg; |       errmsg = e_invarg; | ||||||
|     } else { |     } else { | ||||||
|       (void)init_chartab(); |       (void)init_chartab(); | ||||||
|  |       msg_grid_validate(); | ||||||
|     } |     } | ||||||
|   } else if (varp == &p_ead) {  // 'eadirection' |   } else if (varp == &p_ead) {  // 'eadirection' | ||||||
|     if (check_opt_strings(p_ead, p_ead_values, false) != OK) { |     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_LASTLINE             0x001 | ||||||
| #define DY_TRUNCATE             0x002 | #define DY_TRUNCATE             0x002 | ||||||
| #define DY_UHEX                 0x004 | #define DY_UHEX                 0x004 | ||||||
|  | // code should use msg_use_msgsep() to check if msgsep is active | ||||||
| #define DY_MSGSEP               0x008 | #define DY_MSGSEP               0x008 | ||||||
| EXTERN int p_ed;                // 'edcompatible' | EXTERN int p_ed;                // 'edcompatible' | ||||||
| EXTERN int p_emoji;             // 'emoji' | EXTERN int p_emoji;             // 'emoji' | ||||||
|   | |||||||
| @@ -152,6 +152,7 @@ static bool send_grid_resize = false; | |||||||
| static bool conceal_cursor_used = false; | static bool conceal_cursor_used = false; | ||||||
|  |  | ||||||
| static bool redraw_popupmenu = false; | static bool redraw_popupmenu = false; | ||||||
|  | static bool msg_grid_invalid = false; | ||||||
|  |  | ||||||
| static bool resizing = 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 |   // Tricky: vim code can reset msg_scrolled behind our back, so need | ||||||
|   // separate bookkeeping for now. |   // separate bookkeeping for now. | ||||||
|   if (msg_did_scroll) { |   if (msg_did_scroll) { | ||||||
|     ui_call_win_scroll_over_reset(); |  | ||||||
|     msg_did_scroll = false; |     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 the screen was scrolled up when displaying a message, scroll it down | ||||||
|   if (msg_scrolled) { |   if (msg_scrolled || msg_grid_invalid) { | ||||||
|     clear_cmdline = true; |     clear_cmdline = true; | ||||||
|     if (dy_flags & DY_MSGSEP) { |     int valid = MAX(Rows - msg_scrollsize(), 0); | ||||||
|       int valid = MAX(Rows - msg_scrollsize(), 0); |     if (msg_grid.chars) { | ||||||
|       if (valid == 0) { |       // non-displayed part of msg_grid is considered invalid. | ||||||
|         redraw_tabline = true; |       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); | ||||||
|       } |       } | ||||||
|       FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { |     } | ||||||
|         if (W_ENDROW(wp) > valid) { |     if (msg_use_msgsep()) { | ||||||
|           wp->w_redr_type = NOT_VALID; |       msg_grid.throttled = false; | ||||||
|           wp->w_lines_valid = 0; |       // CLEAR is already handled | ||||||
|         } |       if (type == NOT_VALID && !ui_has(kUIMultigrid) && msg_scrolled) { | ||||||
|         if (W_ENDROW(wp) + wp->w_status_height > valid) { |         ui_comp_set_screen_valid(false); | ||||||
|           wp->w_redr_status = true; |         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 |     } else if (msg_scrolled > Rows - 5) {  // clearing is faster | ||||||
|       type = CLEAR; |       type = CLEAR; | ||||||
|     } else if (type != CLEAR) { |     } else if (type != CLEAR) { | ||||||
| @@ -368,12 +379,10 @@ int update_screen(int type) | |||||||
|       redraw_tabline = TRUE; |       redraw_tabline = TRUE; | ||||||
|     } |     } | ||||||
|     msg_scrolled = 0; |     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(); |   win_ui_flush_positions(); | ||||||
|   msg_ext_check_clear(); |   msg_ext_check_clear(); | ||||||
|  |  | ||||||
| @@ -394,6 +403,11 @@ int update_screen(int type) | |||||||
|     grid_invalidate(&default_grid); |     grid_invalidate(&default_grid); | ||||||
|     default_grid.valid = true; |     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); |   ui_comp_set_screen_valid(true); | ||||||
|  |  | ||||||
|   if (clear_cmdline)            /* going to clear cmdline (done below) */ |   if (clear_cmdline)            /* going to clear cmdline (done below) */ | ||||||
| @@ -4310,9 +4324,13 @@ win_line ( | |||||||
| void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off) | void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off) | ||||||
| { | { | ||||||
|   if (!(*grid)->chars && *grid != &default_grid) { |   if (!(*grid)->chars && *grid != &default_grid) { | ||||||
|     *row_off += (*grid)->row_offset; |       *row_off += (*grid)->row_offset; | ||||||
|     *col_off += (*grid)->col_offset; |       *col_off += (*grid)->col_offset; | ||||||
|     *grid = &default_grid; |     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 |         /* Put the wildmenu just above the command line.  If there is | ||||||
|          * no room, scroll the screen one line up. */ |          * no room, scroll the screen one line up. */ | ||||||
|         if (cmdline_row == Rows - 1) { |         if (cmdline_row == Rows - 1) { | ||||||
|           msg_scroll_up(); |           msg_scroll_up(false); | ||||||
|           msg_scrolled++; |           msg_scrolled++; | ||||||
|         } else { |         } else { | ||||||
|           cmdline_row++; |           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) { |     if (selstart != NULL && highlight) { | ||||||
|       *selend = NUL; |       *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); |               fillchar, fillchar, attr); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -5350,6 +5373,8 @@ static int put_dirty_last = 0; | |||||||
| /// another line. | /// another line. | ||||||
| void grid_puts_line_start(ScreenGrid *grid, int row) | void grid_puts_line_start(ScreenGrid *grid, int row) | ||||||
| { | { | ||||||
|  |   int col = 0;  // unused | ||||||
|  |   screen_adjust_grid(&grid, &row, &col); | ||||||
|   assert(put_dirty_row == -1); |   assert(put_dirty_row == -1); | ||||||
|   put_dirty_row = row; |   put_dirty_row = row; | ||||||
|   put_dirty_grid = grid; |   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); |   screen_adjust_grid(&grid, &row, &col); | ||||||
|  |  | ||||||
|   // Safety check. The check for negative row and column is to fix issue |   // 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 |   if (grid->chars == NULL | ||||||
|       || row >= grid->Rows || row < 0 |       || row >= grid->Rows || row < 0 | ||||||
|       || col >= grid->Columns || col < 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, |       ui_grid_cursor_goto(put_dirty_grid->handle, put_dirty_row, | ||||||
|                           MIN(put_dirty_last, put_dirty_grid->Columns-1)); |                           MIN(put_dirty_last, put_dirty_grid->Columns-1)); | ||||||
|     } |     } | ||||||
|     ui_line(put_dirty_grid, put_dirty_row, put_dirty_first, put_dirty_last, |     if (!put_dirty_grid->throttled) { | ||||||
|             put_dirty_last, 0, false); |       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_first = INT_MAX; | ||||||
|     put_dirty_last = 0; |     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) { |       if (put_dirty_row == row) { | ||||||
|         put_dirty_first = MIN(put_dirty_first, dirty_first); |         put_dirty_first = MIN(put_dirty_first, dirty_first); | ||||||
|         put_dirty_last = MAX(put_dirty_last, dirty_last); |         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 { |       } else { | ||||||
|         int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' '); |         int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' '); | ||||||
|         ui_line(grid, row, dirty_first, last, dirty_last, attr, false); |         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) { |     if (end_col == grid->Columns) { | ||||||
|       grid->line_wraps[row] = false; |       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 |   // win_new_shellsize will recompute floats position, but tell the | ||||||
|   // compositor to not redraw them yet |   // compositor to not redraw them yet | ||||||
|   ui_comp_set_screen_valid(false); |   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 */ |   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_scrolled = 0;  // can't scroll back | ||||||
|   msg_didany = false; |   msg_didany = false; | ||||||
|   msg_didout = 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 | /// clear a line in the grid starting at "off" until "width" characters | ||||||
| /// are cleared. | /// are cleared. | ||||||
| static void grid_clear_line(ScreenGrid *grid, unsigned off, int width, | void grid_clear_line(ScreenGrid *grid, unsigned off, int width, bool valid) | ||||||
|                             bool valid) |  | ||||||
| { | { | ||||||
|   for (int col = 0; col < width; col++) { |   for (int col = 0; col < width; col++) { | ||||||
|     schar_from_ascii(grid->chars[off + 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, | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ui_call_grid_scroll(grid->handle, row, end, col, col+width, -line_count, 0); |   if (!grid->throttled) { | ||||||
|  |     ui_call_grid_scroll(grid->handle, row, end, col, col+width, -line_count, 0); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   return; |   return; | ||||||
| } | } | ||||||
| @@ -6412,7 +6452,9 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ui_call_grid_scroll(grid->handle, row, end, col, col+width, line_count, 0); |   if (!grid->throttled) { | ||||||
|  |     ui_call_grid_scroll(grid->handle, row, end, col, col+width, line_count, 0); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   return; |   return; | ||||||
| } | } | ||||||
| @@ -6440,6 +6482,8 @@ int showmode(void) | |||||||
|   // don't make non-flushed message part of the showmode |   // don't make non-flushed message part of the showmode | ||||||
|   msg_ext_ui_flush(); |   msg_ext_ui_flush(); | ||||||
|  |  | ||||||
|  |   msg_grid_validate(); | ||||||
|  |  | ||||||
|   do_mode = ((p_smd && msg_silent == 0) |   do_mode = ((p_smd && msg_silent == 0) | ||||||
|              && ((State & TERM_FOCUS) |              && ((State & TERM_FOCUS) | ||||||
|                  || (State & INSERT) |                  || (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); |       ScreenGrid *grid = part_of_status ? &default_grid : &msg_grid_adj; | ||||||
|       i = redraw_cmdline; |       grid_puts(grid, buffer, row, this_ru_col + off, attr); | ||||||
|       grid_fill(&default_grid, row, row + 1, |       grid_fill(grid, row, row + 1, | ||||||
|                 this_ru_col + off + (int)STRLEN(buffer), off + width, fillchar, |                 this_ru_col + off + (int)STRLEN(buffer), off + width, fillchar, | ||||||
|                 fillchar, attr); |                 fillchar, attr); | ||||||
|       // don't redraw the cmdline because of showing the ruler |  | ||||||
|       redraw_cmdline = i; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     wp->w_ru_cursor = wp->w_cursor; |     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 |     if (State == ASKMORE || State == EXTERNCMD || State == CONFIRM | ||||||
|         || exmode_active) { |         || exmode_active) { | ||||||
|       screenalloc(); |       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(); |       repeat_message(); | ||||||
|     } else { |     } else { | ||||||
|       if (curwin->w_p_scb) |       if (curwin->w_p_scb) | ||||||
|   | |||||||
| @@ -7533,6 +7533,9 @@ void highlight_changed(void) | |||||||
|                                          hlf == (int)HLF_INACTIVE); |                                          hlf == (int)HLF_INACTIVE); | ||||||
|  |  | ||||||
|     if (highlight_attr[hlf] != highlight_attr_last[hlf]) { |     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]), |       ui_call_hl_group_set(cstr_as_string((char *)hlf_names[hlf]), | ||||||
|                            highlight_attr[hlf]); |                            highlight_attr[hlf]); | ||||||
|       highlight_attr_last[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, | void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol, | ||||||
|              int clearattr, bool wrap) |              int clearattr, bool wrap) | ||||||
| { | { | ||||||
|  |   assert(0 <= row && row < grid->Rows); | ||||||
|   LineFlags flags = wrap ? kLineFlagWrap : 0; |   LineFlags flags = wrap ? kLineFlagWrap : 0; | ||||||
|   if (startcol == -1) { |   if (startcol == -1) { | ||||||
|     startcol = 0; |     startcol = 0; | ||||||
| @@ -404,6 +405,7 @@ void ui_flush(void) | |||||||
|   cmdline_ui_flush(); |   cmdline_ui_flush(); | ||||||
|   win_ui_flush_positions(); |   win_ui_flush_positions(); | ||||||
|   msg_ext_ui_flush(); |   msg_ext_ui_flush(); | ||||||
|  |   msg_scroll_flush(); | ||||||
|  |  | ||||||
|   if (pending_cursor_update) { |   if (pending_cursor_update) { | ||||||
|     ui_call_grid_cursor_goto(cursor_grid_handle, cursor_row, cursor_col); |     ui_call_grid_cursor_goto(cursor_grid_handle, cursor_row, cursor_col); | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ | |||||||
| #include "nvim/ui.h" | #include "nvim/ui.h" | ||||||
| #include "nvim/highlight.h" | #include "nvim/highlight.h" | ||||||
| #include "nvim/memory.h" | #include "nvim/memory.h" | ||||||
|  | #include "nvim/message.h" | ||||||
| #include "nvim/popupmnu.h" | #include "nvim/popupmnu.h" | ||||||
| #include "nvim/ui_compositor.h" | #include "nvim/ui_compositor.h" | ||||||
| #include "nvim/ugrid.h" | #include "nvim/ugrid.h" | ||||||
| @@ -46,8 +47,11 @@ static int chk_width = 0, chk_height = 0; | |||||||
| static ScreenGrid *curgrid; | static ScreenGrid *curgrid; | ||||||
|  |  | ||||||
| static bool valid_screen = true; | static bool valid_screen = true; | ||||||
| static bool msg_scroll_mode = false; | static int msg_current_row = INT_MAX; | ||||||
| static int msg_first_invalid = 0; | 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; | 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_scroll = ui_comp_grid_scroll; | ||||||
|   compositor->grid_cursor_goto = ui_comp_grid_cursor_goto; |   compositor->grid_cursor_goto = ui_comp_grid_cursor_goto; | ||||||
|   compositor->raw_line = ui_comp_raw_line; |   compositor->raw_line = ui_comp_raw_line; | ||||||
|   compositor->win_scroll_over_start = ui_comp_win_scroll_over_start; |   compositor->msg_set_pos = ui_comp_msg_set_pos; | ||||||
|   compositor->win_scroll_over_reset = ui_comp_win_scroll_over_reset; |  | ||||||
|  |  | ||||||
|   // Be unopinionated: will be attached together with a "real" ui anyway |   // Be unopinionated: will be attached together with a "real" ui anyway | ||||||
|   compositor->width = INT_MAX; |   compositor->width = INT_MAX; | ||||||
| @@ -158,8 +161,19 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width, | |||||||
|     } |     } | ||||||
| #endif | #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); |     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--; |       insert_at--; | ||||||
|     } |     } | ||||||
|     if (insert_at > 1 && !on_top) { |     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) | 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--) { |   for (ssize_t i = (ssize_t)kv_size(layers)-1; i > 0; i--) { | ||||||
|     ScreenGrid *grid = kv_A(layers, 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) { |         && col >= grid->comp_col && col < grid->comp_col+grid->Columns) { | ||||||
|       return grid; |       return grid; | ||||||
|     } |     } | ||||||
| @@ -337,10 +351,30 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, | |||||||
|     assert(until > col); |     assert(until > col); | ||||||
|     assert(until <= default_grid.Columns); |     assert(until <= default_grid.Columns); | ||||||
|     size_t n = (size_t)(until-col); |     size_t n = (size_t)(until-col); | ||||||
|     size_t off = grid->line_offset[row-grid->comp_row] |  | ||||||
|                  + (size_t)(col-grid->comp_col); |     if (row == msg_sep_row && grid->comp_index <= msg_grid.comp_index) { | ||||||
|     memcpy(linebuf+(col-startcol), grid->chars+off, n * sizeof(*linebuf)); |       // TODO(bfredl): when we implement borders around floating windows, then | ||||||
|     memcpy(attrbuf+(col-startcol), grid->attrs+off, n * sizeof(*attrbuf)); |       // 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' |     // 'pumblend' and 'winblend' | ||||||
|     if (grid->blending) { |     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) { |     } else if (n > 1 && linebuf[col-startcol+1][0] == NUL) { | ||||||
|       skipstart = 0; |       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; |     col = until; | ||||||
|   } |   } | ||||||
| @@ -500,9 +526,12 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, | |||||||
|     endcol = MIN(endcol, clearcol); |     endcol = MIN(endcol, clearcol); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (flags & kLineFlagInvalid |   bool above_msg = (kv_A(layers, kv_size(layers)-1) == &msg_grid | ||||||
|       || kv_size(layers) > curgrid->comp_index+1 |                     && row < msg_current_row-(msg_was_scrolled?1:0)); | ||||||
|       || curgrid->blending) { |   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_debug(row, row+1, startcol, clearcol, dbghl_composed, true); | ||||||
|     compose_line(row, startcol, clearcol, flags); |     compose_line(row, startcol, clearcol, flags); | ||||||
|   } else { |   } 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) | void ui_comp_set_screen_valid(bool valid) | ||||||
| { | { | ||||||
|   valid_screen = valid; |   valid_screen = valid; | ||||||
|  |   if (!valid) { | ||||||
|  |     msg_sep_row = -1; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| // TODO(bfredl): These events are somewhat of a hack. multiline messages | static void ui_comp_msg_set_pos(UI *ui, Integer grid, Integer row, | ||||||
| // should later on be a separate grid, then this would just be ordinary |                                 Boolean scrolled, String sep_char) | ||||||
| // ui_comp_put_grid and ui_comp_remove_grid calls. |  | ||||||
| static void ui_comp_win_scroll_over_start(UI *ui) |  | ||||||
| { | { | ||||||
|   msg_scroll_mode = true; |   msg_grid.comp_row = (int)row; | ||||||
|   msg_first_invalid = ui->height; |   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) |   if (row > msg_current_row && ui_comp_should_draw()) { | ||||||
| { |     compose_area(MAX(msg_current_row-1, 0), row, 0, default_grid.Columns); | ||||||
|   msg_scroll_mode = false; |   } else if (row < msg_current_row && ui_comp_should_draw() | ||||||
|   for (size_t i = 1; i < kv_size(layers); i++) { |              && msg_current_row < Rows) { | ||||||
|     ScreenGrid *grid = kv_A(layers, i); |     int delta = msg_current_row - (int)row; | ||||||
|     if (grid->comp_row+grid->Rows > msg_first_invalid) { |     if (msg_grid.blending) { | ||||||
|       compose_area(msg_first_invalid, grid->comp_row+grid->Rows, |       int first_row = MAX((int)row-(scrolled?1:0), 0); | ||||||
|                    grid->comp_col, grid->comp_col+grid->Columns); |       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, | 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; |   left += curgrid->comp_col; | ||||||
|   right += curgrid->comp_col; |   right += curgrid->comp_col; | ||||||
|   bool covered = kv_size(layers) > curgrid->comp_index+1 || curgrid->blending; |   bool covered = kv_size(layers) > curgrid->comp_index+1 || curgrid->blending; | ||||||
|   if (!msg_scroll_mode && covered) { |  | ||||||
|  |   if (covered) { | ||||||
|     // TODO(bfredl): |     // TODO(bfredl): | ||||||
|     // 1. check if rectangles actually overlap |     // 1. check if rectangles actually overlap | ||||||
|     // 2. calulate subareas that can scroll. |     // 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); |     compose_area(top, bot, left, right); | ||||||
|   } else { |   } else { | ||||||
|     msg_first_invalid = MIN(msg_first_invalid, (int)top); |  | ||||||
|     ui_composed_call_grid_scroll(1, top, bot, left, right, rows, cols); |     ui_composed_call_grid_scroll(1, top, bot, left, right, rows, cols); | ||||||
|     if (rdb_flags & RDB_COMPOSITOR) { |     if (rdb_flags & RDB_COMPOSITOR) { | ||||||
|       debug_delay(2); |       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, |       ui_comp_put_grid(&wp->w_grid, comp_row, comp_col, wp->w_height, | ||||||
|                        wp->w_width, valid, on_top); |                        wp->w_width, valid, on_top); | ||||||
|       ui_check_cursor_grid(wp->w_grid.handle); |       ui_check_cursor_grid(wp->w_grid.handle); | ||||||
|  |       wp->w_grid.focusable = wp->w_float_config.focusable; | ||||||
|       if (!valid) { |       if (!valid) { | ||||||
|         wp->w_grid.valid = false; |         wp->w_grid.valid = false; | ||||||
|         redraw_win_later(wp, NOT_VALID); |         redraw_win_later(wp, NOT_VALID); | ||||||
| @@ -5359,6 +5360,9 @@ void win_drag_status_line(win_T *dragwin, int offset) | |||||||
|   } |   } | ||||||
|   row = win_comp_pos(); |   row = win_comp_pos(); | ||||||
|   grid_fill(&default_grid, row, cmdline_row, 0, Columns, ' ', ' ', 0); |   grid_fill(&default_grid, row, cmdline_row, 0, Columns, ' ', ' ', 0); | ||||||
|  |   if (msg_grid.chars) { | ||||||
|  |     clear_cmdline = true; | ||||||
|  |   } | ||||||
|   cmdline_row = row; |   cmdline_row = row; | ||||||
|   p_ch = Rows - cmdline_row; |   p_ch = Rows - cmdline_row; | ||||||
|   if (p_ch < 1) |   if (p_ch < 1) | ||||||
|   | |||||||
| @@ -222,6 +222,7 @@ describe('timers', function() | |||||||
|       let g:val = 0 |       let g:val = 0 | ||||||
|       func! MyHandler(timer) |       func! MyHandler(timer) | ||||||
|         echo "evil" |         echo "evil" | ||||||
|  |         redraw | ||||||
|         let g:val = 1 |         let g:val = 1 | ||||||
|       endfunc |       endfunc | ||||||
|     ]]) |     ]]) | ||||||
|   | |||||||
| @@ -56,6 +56,8 @@ local function screen_setup(extra_rows, command, cols, opts) | |||||||
|     [8] = {foreground = 15, background = 1}, -- error message |     [8] = {foreground = 15, background = 1}, -- error message | ||||||
|     [9] = {foreground = 4}, |     [9] = {foreground = 4}, | ||||||
|     [10] = {foreground = 121},  -- "Press ENTER" in embedded :terminal session. |     [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}) |   screen:attach(opts or {rgb=false}) | ||||||
|   | |||||||
| @@ -89,6 +89,85 @@ describe('TUI', function() | |||||||
|     eq(2, eval("1+1"))  -- Still alive? |     eq(2, eval("1+1"))  -- Still alive? | ||||||
|   end) |   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() |   it('accepts basic utf-8 input', function() | ||||||
|     feed_data('iabc\ntest1\ntest2') |     feed_data('iabc\ntest1\ntest2') | ||||||
|     screen:expect([[ |     screen:expect([[ | ||||||
|   | |||||||
| @@ -158,7 +158,7 @@ describe(':terminal with multigrid', function() | |||||||
|       [2:--------------------------------------------------]| |       [2:--------------------------------------------------]| | ||||||
|       [2:--------------------------------------------------]| |       [2:--------------------------------------------------]| | ||||||
|       [2:--------------------------------------------------]| |       [2:--------------------------------------------------]| | ||||||
|       {3:-- TERMINAL --}                                    | |       [3:--------------------------------------------------]| | ||||||
|     ## grid 2 |     ## grid 2 | ||||||
|       tty ready                                         | |       tty ready                                         | | ||||||
|       {1: }                                                 | |       {1: }                                                 | | ||||||
| @@ -166,6 +166,8 @@ describe(':terminal with multigrid', function() | |||||||
|                                                         | |                                                         | | ||||||
|                                                         | |                                                         | | ||||||
|                                                         | |                                                         | | ||||||
|  |     ## grid 3 | ||||||
|  |       {3:-- TERMINAL --}                                    | | ||||||
|     ]]) |     ]]) | ||||||
|  |  | ||||||
|     screen:try_resize_grid(2, 20, 10) |     screen:try_resize_grid(2, 20, 10) | ||||||
| @@ -180,7 +182,7 @@ describe(':terminal with multigrid', function() | |||||||
|         [2:--------------------------------------------------]| |         [2:--------------------------------------------------]| | ||||||
|         [2:--------------------------------------------------]| |         [2:--------------------------------------------------]| | ||||||
|         [2:--------------------------------------------------]| |         [2:--------------------------------------------------]| | ||||||
|         {3:-- TERMINAL --}                                    | |         [3:--------------------------------------------------]| | ||||||
|       ## grid 2 |       ## grid 2 | ||||||
|         tty ready           | |         tty ready           | | ||||||
|         rows: 10, cols: 20  | |         rows: 10, cols: 20  | | ||||||
| @@ -192,6 +194,8 @@ describe(':terminal with multigrid', function() | |||||||
|                             | |                             | | ||||||
|                             | |                             | | ||||||
|                             | |                             | | ||||||
|  |       ## grid 3 | ||||||
|  |         {3:-- TERMINAL --}                                    | | ||||||
|       ]]) |       ]]) | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -207,11 +211,13 @@ describe(':terminal with multigrid', function() | |||||||
|         [2:--------------------------------------------------]| |         [2:--------------------------------------------------]| | ||||||
|         [2:--------------------------------------------------]| |         [2:--------------------------------------------------]| | ||||||
|         [2:--------------------------------------------------]| |         [2:--------------------------------------------------]| | ||||||
|         {3:-- TERMINAL --}                                    | |         [3:--------------------------------------------------]| | ||||||
|       ## grid 2 |       ## grid 2 | ||||||
|         rows: 10, cols: 20                                                    | |         rows: 10, cols: 20                                                    | | ||||||
|         rows: 3, cols: 70                                                     | |         rows: 3, cols: 70                                                     | | ||||||
|         {1: }                                                                     | |         {1: }                                                                     | | ||||||
|  |       ## grid 3 | ||||||
|  |         {3:-- TERMINAL --}                                    | | ||||||
|       ]]) |       ]]) | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -227,7 +233,7 @@ describe(':terminal with multigrid', function() | |||||||
|         [2:--------------------------------------------------]| |         [2:--------------------------------------------------]| | ||||||
|         [2:--------------------------------------------------]| |         [2:--------------------------------------------------]| | ||||||
|         [2:--------------------------------------------------]| |         [2:--------------------------------------------------]| | ||||||
|         {3:-- TERMINAL --}                                    | |         [3:--------------------------------------------------]| | ||||||
|       ## grid 2 |       ## grid 2 | ||||||
|         tty ready                                         | |         tty ready                                         | | ||||||
|         rows: 10, cols: 20                                | |         rows: 10, cols: 20                                | | ||||||
| @@ -235,6 +241,8 @@ describe(':terminal with multigrid', function() | |||||||
|         rows: 6, cols: 50                                 | |         rows: 6, cols: 50                                 | | ||||||
|         {1: }                                                 | |         {1: }                                                 | | ||||||
|                                                           | |                                                           | | ||||||
|  |       ## grid 3 | ||||||
|  |         {3:-- TERMINAL --}                                    | | ||||||
|       ]]) |       ]]) | ||||||
|     end |     end | ||||||
|   end) |   end) | ||||||
|   | |||||||
| @@ -216,10 +216,10 @@ describe('ui/cursor', function() | |||||||
|         if m.blinkwait then m.blinkwait = 700 end |         if m.blinkwait then m.blinkwait = 700 end | ||||||
|       end |       end | ||||||
|       if m.hl_id then |       if m.hl_id then | ||||||
|           m.hl_id = 54 |           m.hl_id = 55 | ||||||
|           m.attr = {background = Screen.colors.DarkGray} |           m.attr = {background = Screen.colors.DarkGray} | ||||||
|       end |       end | ||||||
|       if m.id_lm then m.id_lm = 55 end |       if m.id_lm then m.id_lm = 56 end | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     -- Assert the new expectation. |     -- Assert the new expectation. | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ local function test_embed(ext_linegrid) | |||||||
|       [2] = {bold = true, foreground = Screen.colors.SeaGreen4}, |       [2] = {bold = true, foreground = Screen.colors.SeaGreen4}, | ||||||
|       [3] = {bold = true, foreground = Screen.colors.Blue1}, |       [3] = {bold = true, foreground = Screen.colors.Blue1}, | ||||||
|       [4] = {bold = true, foreground = Screen.colors.Green}, |       [4] = {bold = true, foreground = Screen.colors.Green}, | ||||||
|  |       [5] = {bold = true, reverse = true}, | ||||||
|     }) |     }) | ||||||
|   end |   end | ||||||
|  |  | ||||||
| @@ -53,7 +54,7 @@ local function test_embed(ext_linegrid) | |||||||
|                                                                   | |                                                                   | | ||||||
|                                                                   | |                                                                   | | ||||||
|                                                                   | |                                                                   | | ||||||
|                                                                   | |       {5:                                                            }| | ||||||
|       Error detected while processing pre-vimrc command line:     | |       Error detected while processing pre-vimrc command line:     | | ||||||
|       foo                                                         | |       foo                                                         | | ||||||
|       {1:bar}                                                         | |       {1:bar}                                                         | | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -933,17 +933,29 @@ end) | |||||||
|  |  | ||||||
|  |  | ||||||
| describe("MsgSeparator highlight and msgsep fillchar", function() | describe("MsgSeparator highlight and msgsep fillchar", function() | ||||||
|   before_each(clear) |   local screen | ||||||
|   it("works", function() |   before_each(function() | ||||||
|     local screen = Screen.new(50,5) |     clear() | ||||||
|  |     screen = Screen.new(50,5) | ||||||
|     screen:set_default_attr_ids({ |     screen:set_default_attr_ids({ | ||||||
|       [1] = {bold=true, foreground=Screen.colors.Blue}, |       [1] = {bold=true, foreground=Screen.colors.Blue}, | ||||||
|       [2] = {bold=true, reverse=true}, |       [2] = {bold=true, reverse=true}, | ||||||
|       [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, |       [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, | ||||||
|       [4] = {background = Screen.colors.Cyan, bold = true, reverse = true}, |       [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() |     screen:attach() | ||||||
|  |   end) | ||||||
|  |  | ||||||
|  |   it("works", function() | ||||||
|  |  | ||||||
|     -- defaults |     -- defaults | ||||||
|     feed_command("ls") |     feed_command("ls") | ||||||
| @@ -1000,6 +1012,61 @@ describe("MsgSeparator highlight and msgsep fillchar", function() | |||||||
|       {3:Press ENTER or type command to continue}^           | |       {3:Press ENTER or type command to continue}^           | | ||||||
|     ]]) |     ]]) | ||||||
|   end) |   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) | end) | ||||||
|  |  | ||||||
| describe("'winhighlight' highlight", function() | describe("'winhighlight' highlight", function() | ||||||
|   | |||||||
| @@ -39,7 +39,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|       {5:~                                       }| |       {5:~                                       }| | ||||||
|       {5:~                                       }| |       {5:~                                       }| | ||||||
|       {5:~                                       }| |       {5:~                                       }| | ||||||
|       {6:search hit BOTTOM, continuing at TOP}    | |       {8:search hit BOTTOM, continuing at TOP}{7:    }| | ||||||
|     ]], { |     ]], { | ||||||
|     [1] = {{foreground = Screen.colors.Magenta}, |     [1] = {{foreground = Screen.colors.Magenta}, | ||||||
|            {{hi_name = "Constant", kind = "syntax"}}}, |            {{hi_name = "Constant", kind = "syntax"}}}, | ||||||
| @@ -52,6 +52,8 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|            {{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}}, |            {{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}}, | ||||||
|     [6] = {{foreground = Screen.colors.Red}, |     [6] = {{foreground = Screen.colors.Red}, | ||||||
|            {{hi_name = "WarningMsg", ui_name = "WarningMsg", kind = "ui"}}}, |            {{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) |   end) | ||||||
|  |  | ||||||
| @@ -66,6 +68,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|             {{hi_name = "StatusLineNC", ui_name = "StatusLineNC" , kind = "ui"}}}, |             {{hi_name = "StatusLineNC", ui_name = "StatusLineNC" , kind = "ui"}}}, | ||||||
|       [5] = {{}, {{hi_name = "StatusLine", ui_name = "StatusLine", kind = "ui"}}}, |       [5] = {{}, {{hi_name = "StatusLine", ui_name = "StatusLine", kind = "ui"}}}, | ||||||
|       [6] = {{}, {{hi_name = "StatusLineNC", ui_name = "StatusLineNC", 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("hi clear VertSplit") | ||||||
|     command("vsplit") |     command("vsplit") | ||||||
| @@ -78,7 +81,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|       {2:~                   }{1:│}{2:~                  }| |       {2:~                   }{1:│}{2:~                  }| | ||||||
|       {2:~                   }{1:│}{2:~                  }| |       {2:~                   }{1:│}{2:~                  }| | ||||||
|       {3:[No Name]            }{4:[No Name]          }| |       {3:[No Name]            }{4:[No Name]          }| | ||||||
|                                               | |       {7:                                        }| | ||||||
|     ]]) |     ]]) | ||||||
|  |  | ||||||
|     command("hi clear StatusLine | hi clear StatuslineNC") |     command("hi clear StatusLine | hi clear StatuslineNC") | ||||||
| @@ -90,7 +93,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|       {2:~                   }{1:│}{2:~                  }| |       {2:~                   }{1:│}{2:~                  }| | ||||||
|       {2:~                   }{1:│}{2:~                  }| |       {2:~                   }{1:│}{2:~                  }| | ||||||
|       {5:[No Name]            }{6:[No Name]          }| |       {5:[No Name]            }{6:[No Name]          }| | ||||||
|                                               | |       {7:                                        }| | ||||||
|     ]]) |     ]]) | ||||||
|  |  | ||||||
|     -- redrawing is done even if visible highlights didn't change |     -- 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:~                   }| | ||||||
|       {2:~                  }{1:│}{2:~                   }| |       {2:~                  }{1:│}{2:~                   }| | ||||||
|       {6:[No Name]           }{5:[No Name]           }| |       {6:[No Name]           }{5:[No Name]           }| | ||||||
|                                               | |       {7:                                        }| | ||||||
|     ]]) |     ]]) | ||||||
|  |  | ||||||
|   end) |   end) | ||||||
| @@ -120,7 +123,8 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|         [8] = {{foreground = Screen.colors.Blue1, bold = true, reverse = true}, {6, 2}}, |         [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"}}}, |         [9] = {{bold = true, foreground = Screen.colors.Brown}, {{hi_name = "Statement", ui_name = "NormalNC", kind = "ui"}}}, | ||||||
|         [10] = {{bold = true, foreground = Screen.colors.Brown}, {9, 1}}, |         [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") |     command("set number") | ||||||
| @@ -134,7 +138,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|       {1:  1 }                                    | |       {1:  1 }                                    | | ||||||
|       {2:~                                       }| |       {2:~                                       }| | ||||||
|       {4:[No Name]                               }| |       {4:[No Name]                               }| | ||||||
|                                               | |       {12:                                        }| | ||||||
|     ]]) |     ]]) | ||||||
|  |  | ||||||
|     command("set winhl=LineNr:ErrorMsg") |     command("set winhl=LineNr:ErrorMsg") | ||||||
| @@ -146,7 +150,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|       {1:  1 }                                    | |       {1:  1 }                                    | | ||||||
|       {2:~                                       }| |       {2:~                                       }| | ||||||
|       {4:[No Name]                               }| |       {4:[No Name]                               }| | ||||||
|                                               | |       {12:                                        }| | ||||||
|     ]]) |     ]]) | ||||||
|  |  | ||||||
|     command("set winhl=Normal:MsgSeparator,NormalNC:Statement") |     command("set winhl=Normal:MsgSeparator,NormalNC:Statement") | ||||||
| @@ -158,7 +162,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|       {1:  1 }                                    | |       {1:  1 }                                    | | ||||||
|       {2:~                                       }| |       {2:~                                       }| | ||||||
|       {4:[No Name]                               }| |       {4:[No Name]                               }| | ||||||
|                                               | |       {12:                                        }| | ||||||
|     ]]) |     ]]) | ||||||
|  |  | ||||||
|     command("wincmd w") |     command("wincmd w") | ||||||
| @@ -170,7 +174,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|       {1:  1 }^                                    | |       {1:  1 }^                                    | | ||||||
|       {2:~                                       }| |       {2:~                                       }| | ||||||
|       {3:[No Name]                               }| |       {3:[No Name]                               }| | ||||||
|                                               | |       {12:                                        }| | ||||||
|     ]]) |     ]]) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -182,6 +186,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|       [4] = {{foreground = 52479}, {2, 1}}, |       [4] = {{foreground = 52479}, {2, 1}}, | ||||||
|       [5] = {{foreground = 4259839}, {{kind = "term"}}}, |       [5] = {{foreground = 4259839}, {{kind = "term"}}}, | ||||||
|       [6] = {{foreground = 4259839}, {5, 1}}, |       [6] = {{foreground = 4259839}, {5, 1}}, | ||||||
|  |       [7] = {{}, {{hi_name = "MsgArea", ui_name = "MsgArea", kind = "ui"}}}, | ||||||
|     }) |     }) | ||||||
|     command('enew | call termopen(["'..nvim_dir..'/tty-test"])') |     command('enew | call termopen(["'..nvim_dir..'/tty-test"])') | ||||||
|     screen:expect([[ |     screen:expect([[ | ||||||
| @@ -192,7 +197,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|                                               | |                                               | | ||||||
|                                               | |                                               | | ||||||
|                                               | |                                               | | ||||||
|                                               | |       {7:                                        }| | ||||||
|     ]]) |     ]]) | ||||||
|  |  | ||||||
|     thelpers.feed_data('x ') |     thelpers.feed_data('x ') | ||||||
| @@ -210,7 +215,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|                                                 | |                                                 | | ||||||
|                                                 | |                                                 | | ||||||
|                                                 | |                                                 | | ||||||
|                                                 | |         {7:                                        }| | ||||||
|       ]]) |       ]]) | ||||||
|     else |     else | ||||||
|       screen:expect([[ |       screen:expect([[ | ||||||
| @@ -221,7 +226,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|                                                 | |                                                 | | ||||||
|                                                 | |                                                 | | ||||||
|                                                 | |                                                 | | ||||||
|                                                 | |         {7:                                        }| | ||||||
|       ]]) |       ]]) | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -236,7 +241,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|                                                 | |                                                 | | ||||||
|                                                 | |                                                 | | ||||||
|                                                 | |                                                 | | ||||||
|                                                 | |         {7:                                        }| | ||||||
|       ]]) |       ]]) | ||||||
|     else |     else | ||||||
|       screen:expect([[ |       screen:expect([[ | ||||||
| @@ -247,7 +252,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|                                                 | |                                                 | | ||||||
|                                                 | |                                                 | | ||||||
|                                                 | |                                                 | | ||||||
|                                                 | |         {7:                                        }| | ||||||
|       ]]) |       ]]) | ||||||
|     end |     end | ||||||
|   end) |   end) | ||||||
| @@ -259,6 +264,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|     screen:set_default_attr_ids({ |     screen:set_default_attr_ids({ | ||||||
|         [1] = {{bold = true, foreground = Screen.colors.Blue1}, {foreground = 12}, {{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}}, |         [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"}}}, |         [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([[ |     screen:expect([[ | ||||||
|       ^                                        | |       ^                                        | | ||||||
| @@ -268,7 +274,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|       {1:~                                       }| |       {1:~                                       }| | ||||||
|       {1:~                                       }| |       {1:~                                       }| | ||||||
|       {1:~                                       }| |       {1:~                                       }| | ||||||
|                                               | |       {3:                                        }| | ||||||
|     ]]) |     ]]) | ||||||
|  |  | ||||||
|     command("hi NonText guifg=Red gui=reverse ctermfg=Green cterm=italic") |     command("hi NonText guifg=Red gui=reverse ctermfg=Green cterm=italic") | ||||||
| @@ -280,7 +286,7 @@ describe('ext_hlstate detailed highlights', function() | |||||||
|       {2:~                                       }| |       {2:~                                       }| | ||||||
|       {2:~                                       }| |       {2:~                                       }| | ||||||
|       {2:~                                       }| |       {2:~                                       }| | ||||||
|                                               | |       {3:                                        }| | ||||||
|     ]]) |     ]]) | ||||||
|  |  | ||||||
|   end) |   end) | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1128,7 +1128,7 @@ describe('builtin popupmenu', function() | |||||||
|       prefix      | |       prefix      | | ||||||
|       bef{n: word  }  | |       bef{n: word  }  | | ||||||
|       tex{n: }^        | |       tex{n: }^        | | ||||||
|       {2:-- }{s: text   } | |       {2:-- INSERT -} | | ||||||
|     ]]) |     ]]) | ||||||
|  |  | ||||||
|     -- can't draw the pum, but check we don't crash |     -- can't draw the pum, but check we don't crash | ||||||
| @@ -1597,6 +1597,54 @@ describe('builtin popupmenu', function() | |||||||
|     ]]) |     ]]) | ||||||
|   end) |   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() |   it("'pumblend' RGB-color", function() | ||||||
|     screen:try_resize(60,14) |     screen:try_resize(60,14) | ||||||
|     screen:set_default_attr_ids({ |     screen:set_default_attr_ids({ | ||||||
|   | |||||||
| @@ -159,6 +159,8 @@ function Screen.new(width, height) | |||||||
|     wildmenu_selected = nil, |     wildmenu_selected = nil, | ||||||
|     win_position = {}, |     win_position = {}, | ||||||
|     float_pos = {}, |     float_pos = {}, | ||||||
|  |     msg_grid = nil, | ||||||
|  |     msg_grid_pos = nil, | ||||||
|     _session = nil, |     _session = nil, | ||||||
|     messages = {}, |     messages = {}, | ||||||
|     msg_history = {}, |     msg_history = {}, | ||||||
| @@ -391,27 +393,31 @@ function Screen:expect(expected, attr_ids, attr_ignore, ...) | |||||||
|     end |     end | ||||||
|  |  | ||||||
|     if grid ~= nil then |     if grid ~= nil then | ||||||
|  |       local err_msg, msg_expected_rows = nil, {} | ||||||
|       -- `expected` must match the screen lines exactly. |       -- `expected` must match the screen lines exactly. | ||||||
|       if #actual_rows ~= #expected_rows then |       if #actual_rows ~= #expected_rows then | ||||||
|         return "Expected screen state's row count(" .. #expected_rows |         err_msg = "Expected screen height " .. #expected_rows | ||||||
|         .. ') differs from configured height(' .. #actual_rows .. ') of Screen.' |         .. ' differs from actual height ' .. #actual_rows .. '.' | ||||||
|       end |       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 |         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] |           msg_expected_rows[i] = '*' .. msg_expected_rows[i] | ||||||
|           actual_rows[i] = '*' .. actual_rows[i] |           if i <= #actual_rows then | ||||||
|           return ( |             actual_rows[i] = '*' .. actual_rows[i] | ||||||
|             'Row ' .. tostring(i) .. ' did not match.\n' |           end | ||||||
|             ..'Expected:\n  |'..table.concat(msg_expected_rows, '\n  |')..'\n' |           if err_msg == nil then | ||||||
|             ..'Actual:\n  |'..table.concat(actual_rows, '\n  |')..'\n\n'..[[ |             err_msg = 'Row ' .. tostring(i) .. ' did not match.' | ||||||
|  |           end | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  |       if err_msg ~= nil then | ||||||
|  |         return ( | ||||||
|  |           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 | To print the expect() call that would assert the current screen state, use | ||||||
| screen:snapshot_util(). In case of non-deterministic failures, use | screen:snapshot_util(). In case of non-deterministic failures, use | ||||||
| screen:redraw_debug() to show all intermediate screen states.  ]]) | screen:redraw_debug() to show all intermediate screen states.  ]]) | ||||||
|         end |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|  |  | ||||||
| @@ -672,13 +678,12 @@ function Screen:_handle_grid_resize(grid, width, height) | |||||||
|   } |   } | ||||||
| end | 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() | function Screen:_handle_msg_set_pos(grid, row, scrolled, char) | ||||||
|    self.scroll_over = false |   self.msg_grid = grid | ||||||
|  |   self.msg_grid_pos = row | ||||||
|  |   self.msg_scrolled = scrolled | ||||||
|  |   self.msg_sep_char = char | ||||||
| end | end | ||||||
|  |  | ||||||
| function Screen:_handle_flush() | function Screen:_handle_flush() | ||||||
| @@ -819,10 +824,6 @@ function Screen:_handle_scroll(count) | |||||||
| end | end | ||||||
|  |  | ||||||
| function Screen:_handle_grid_scroll(g, top, bot, left, right, rows, cols) | 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 |   top = top+1 | ||||||
|   left = left+1 |   left = left+1 | ||||||
|   assert(cols == 0) |   assert(cols == 0) | ||||||
| @@ -1072,10 +1073,10 @@ function Screen:_row_repr(gridnr, rownr, attr_state, cursor) | |||||||
|   local current_attr_id |   local current_attr_id | ||||||
|   local i = 1 |   local i = 1 | ||||||
|   local has_windows = self._options.ext_multigrid and gridnr == 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] |   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 |   while i <= #row do | ||||||
|     local did_window = false |     local did_window = false | ||||||
|     if has_windows then |     if has_windows then | ||||||
| @@ -1214,12 +1215,17 @@ function Screen:render(headers, attr_state, preview) | |||||||
|   for igrid,grid in pairs(self._grids) do |   for igrid,grid in pairs(self._grids) do | ||||||
|     if headers then |     if headers then | ||||||
|       local suffix = "" |       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)" |         suffix = " (hidden)" | ||||||
|       end |       end | ||||||
|       table.insert(rv, "## grid "..igrid..suffix) |       table.insert(rv, "## grid "..igrid..suffix) | ||||||
|     end |     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 cursor = self._cursor.grid == igrid and self._cursor.row == i | ||||||
|       local prefix = (headers or preview) and "  " or "" |       local prefix = (headers or preview) and "  " or "" | ||||||
|       table.insert(rv, prefix..self:_row_repr(igrid, i, attr_state, cursor).."|") |       table.insert(rv, prefix..self:_row_repr(igrid, i, attr_state, cursor).."|") | ||||||
| @@ -1298,6 +1304,7 @@ function Screen:print_snapshot(attrs, ignore) | |||||||
|     end |     end | ||||||
|     attrstr = (", attr_ids={\n"..table.concat(attrstrs, "\n").."\n}") |     attrstr = (", attr_ids={\n"..table.concat(attrstrs, "\n").."\n}") | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   print( "\nscreen:expect{grid=[[") |   print( "\nscreen:expect{grid=[[") | ||||||
|   print(kwargs.grid) |   print(kwargs.grid) | ||||||
|   io.stdout:write( "]]"..attrstr) |   io.stdout:write( "]]"..attrstr) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Björn Linse
					Björn Linse