mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	Merge pull request #8455 from UtkarshMe/grid-split
implement ext_multigrid: draw each window on a separate resizable grid.
This commit is contained in:
		| @@ -116,9 +116,9 @@ set(NVIM_VERSION_PATCH 0) | ||||
| set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers | ||||
|  | ||||
| # API level | ||||
| set(NVIM_API_LEVEL 5)         # Bump this after any API change. | ||||
| set(NVIM_API_LEVEL 6)         # Bump this after any API change. | ||||
| set(NVIM_API_LEVEL_COMPAT 0)  # Adjust this after a _breaking_ API change. | ||||
| set(NVIM_API_PRERELEASE false) | ||||
| set(NVIM_API_PRERELEASE true) | ||||
|  | ||||
| file(TO_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR}/.git FORCED_GIT_DIR) | ||||
| include(GetGitRevisionDescription) | ||||
|   | ||||
| @@ -12,9 +12,10 @@ Nvim UI protocol							  *ui* | ||||
| UI Events							    *ui-events* | ||||
|  | ||||
| GUIs can be implemented as external processes communicating with Nvim over the | ||||
| RPC API. The UI model consists of a terminal-like grid with a single, | ||||
| monospace font size. Some elements (UI "widgets") can be drawn separately from | ||||
| the grid ("externalized"). | ||||
| RPC API. The default UI model consists of a terminal-like grid with a single, | ||||
| monospace font size. The UI can opt-in to have windows drawn on separate | ||||
| grids, as well as to have some elements (UI "widgets") be drawn by the UI | ||||
| itself rather than by nvim ("externalized"). | ||||
|  | ||||
|  | ||||
| 								  *ui-options* | ||||
| @@ -32,6 +33,7 @@ a dictionary with these (optional) keys: | ||||
| 	`ext_cmdline`		Externalize the cmdline. |ui-cmdline| | ||||
| 	`ext_wildmenu`		Externalize the wildmenu. |ui-wildmenu| | ||||
| 	`ext_linegrid`		Use new revision of the grid events. |ui-linegrid| | ||||
| 	`ext_multigrid`		Use per-window grid based events. |ui-multigrid| | ||||
| 	`ext_hlstate`		Use detailed highlight state. |ui-hlstate| | ||||
|  | ||||
| Specifying a non-existent option is an error. UIs can check the |api-metadata| | ||||
| @@ -46,7 +48,7 @@ Each update event is itself an array whose first element is the event name and | ||||
| remaining elements are event-parameter tuples. This allows multiple events of | ||||
| the same kind to be sent in a row without the event name being repeated. This | ||||
| batching is mostly used for "grid_line", because each "grid_line" event puts | ||||
| contents in one screen line, but clients must be prepared for multiple argument | ||||
| contents in one grid line, but clients must be prepared for multiple argument | ||||
| sets being batched for all event kinds. | ||||
|  | ||||
| Events must be handled in-order. A "flush" event is sent when nvim is done | ||||
| @@ -61,10 +63,12 @@ By default, Nvim sends |ui-global| and |ui-grid-old| events; these suffice to | ||||
| implement a terminal-like interface. However there are two revisions of the | ||||
| grid part of the protocol. The newer revision |ui-linegrid|, enabled by | ||||
| `ext_linegrid` option, has a more effecient representation of text (especially | ||||
| highlighted text), and room for futher enhancements that will use | ||||
| multiple grids. The older revision is available and used by default only for | ||||
| backwards compatibility reasons. New UIs are strongly recommended to use | ||||
| |ui-linegrid|, as further protocol extensions will require it. | ||||
| highlighted text), and allows extensions that use multiple grids. | ||||
|  | ||||
| The older revision is available and used by default only for backwards | ||||
| compatibility reasons. New UIs are strongly recommended to use |ui-linegrid|, | ||||
| as further protocol extensions require it. The |ui-multigrid| extension | ||||
| enables |ui-linegrid| implicitly. | ||||
|  | ||||
| Nvim optionally sends screen elements "semantically" as structured events | ||||
| instead of raw grid-lines, controlled by |ui-ext-options|. The UI must present | ||||
| @@ -220,7 +224,8 @@ Most of these events take a `grid` index as first parameter.  Grid 1 is the | ||||
| global grid used by default for the entire editor screen state. Grids other | ||||
| than that will be defined by future extensions. Just activating the | ||||
| `ext_linegrid` option by itself will never cause any additional grids to be | ||||
| created. | ||||
| created.  To enable per-window grids, `ext_multigrid` option should be set (see | ||||
| |ui-multigrid|). | ||||
|  | ||||
| Highlight attribute groups are predefined. UIs should maintain a table to map | ||||
| numerical highlight `id`:s to the actual attributes. | ||||
| @@ -475,19 +480,66 @@ because 'winhighlight' was used. UI items will be transmitted, even if the | ||||
| highlight group is cleared, so `ui_name` can always be used to reliably identify | ||||
| screen elements, even if no attributes have been applied. | ||||
|  | ||||
| ============================================================================== | ||||
| Multigrid Events 	  					 *ui-multigrid* | ||||
|  | ||||
| Only sent if `ext_multigrid` option is set in |ui-options|. Enables the | ||||
| `ext_linegrid` extension implicitly. | ||||
|  | ||||
| The multigrid extension gives the UIs more control over how windows are | ||||
| displayed. The UIs receive updates on a separate grid for each window. The UIs | ||||
| can set the grid size independently of how much space the window occupies on | ||||
| the global layout. This enables the UIs to set a different font size for each | ||||
| window if the UI so desires. The UI can also reserve space around the border | ||||
| of the window for its own elements, for instance scrollbars from the UI | ||||
| toolkit. | ||||
|  | ||||
| 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. | ||||
| Once a UI sets a grid size, nvim does not handle the size for that grid and | ||||
| the UI must change the grid size whenever the outer size is changed. To | ||||
| delegate the handling of grid size back to nvim, the UIs should request the | ||||
| size (0, 0). | ||||
|  | ||||
| A window can be hidden and redisplayed without its grid being deallocated. | ||||
| This can happen multiple times for the same window, for instance when switching | ||||
| tabs. | ||||
|  | ||||
| ["win_pos", grid, win, start_row, start_col, width, height] | ||||
| 	Set the position and size of the grid in nvim (i.e. the outer grid | ||||
| 	size). If the window was previously hidden, it should now be shown | ||||
| 	again. | ||||
|  | ||||
| ["win_hide", grid] | ||||
| 	Stop displaying the window. | ||||
|  | ||||
| ["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. | ||||
|  | ||||
| ["win_scroll_over_reset"] | ||||
| 	Hint that scrolled over windows should be redrawn again, and not be | ||||
| 	overdrawn by default grid scrolling anymore. | ||||
|  | ||||
| See |ui-linegrid| for grid events. | ||||
| See |nvim_ui_try_resize_grid| in |api-ui| to request changing the grid size. | ||||
|  | ||||
| ============================================================================== | ||||
| Popupmenu Events						 *ui-popupmenu* | ||||
|  | ||||
| Only sent if `ext_popupmenu` option is set in |ui-options| | ||||
|  | ||||
| ["popupmenu_show", items, selected, row, col] | ||||
| ["popupmenu_show", items, selected, row, col, grid] | ||||
| 	Show |popupmenu-completion|. `items` is an array of completion items | ||||
| 	to show; each item is an array of the form [word, kind, menu, info] as | ||||
| 	defined at |complete-items|, except that `word` is replaced by `abbr` | ||||
| 	if present.  `selected` is the initially-selected item, a zero-based | ||||
| 	index into the array of items (-1 if no item is selected). `row` and | ||||
| 	`col` give the anchor position, where the first character of the | ||||
| 	completed word will be. | ||||
| 	completed word will be. When |ui-multigrid| is used, `grid` is the | ||||
| 	grid for the anchor position. | ||||
|  | ||||
| ["popupmenu_select", selected] | ||||
| 	Select an item in the current popupmenu. `selected` is a zero-based | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
| #include <string.h> | ||||
|  | ||||
| #include "nvim/func_attr.h" | ||||
| #include "nvim/types.h" | ||||
|  | ||||
| #define ARRAY_DICT_INIT {.size = 0, .capacity = 0, .items = NULL} | ||||
| #define STRING_INIT {.data = NULL, .size = 0} | ||||
| @@ -20,8 +21,6 @@ | ||||
| # define DictionaryOf(...) Dictionary | ||||
| #endif | ||||
|  | ||||
| typedef int handle_T; | ||||
|  | ||||
| // Basic types | ||||
| typedef enum { | ||||
|   kErrorTypeNone = -1, | ||||
|   | ||||
| @@ -126,7 +126,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (ui->ui_ext[kUIHlState]) { | ||||
|   if (ui->ui_ext[kUIHlState] || ui->ui_ext[kUIMultigrid]) { | ||||
|     ui->ui_ext[kUILinegrid] = true; | ||||
|   } | ||||
|  | ||||
| @@ -245,6 +245,28 @@ static void ui_set_option(UI *ui, bool init, String name, Object value, | ||||
|                 name.data); | ||||
| } | ||||
|  | ||||
| /// Tell nvim to resize a grid. Nvim sends grid_resize event with the | ||||
| /// requested grid size is within size limits and with maximum allowed size | ||||
| /// otherwise. | ||||
| /// | ||||
| /// On invalid grid handle, fails with error. | ||||
| /// | ||||
| /// @param grid    The handle of the grid to be changed. | ||||
| /// @param width   The new requested width. | ||||
| /// @param height  The new requested height. | ||||
| void nvim_ui_try_resize_grid(uint64_t channel_id, Integer grid, Integer width, | ||||
|                              Integer height, Error *error) | ||||
|   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY | ||||
| { | ||||
|   if (!pmap_has(uint64_t)(connected_uis, channel_id)) { | ||||
|     api_set_error(error, kErrorTypeException, | ||||
|                   "UI not attached to channel: %" PRId64, channel_id); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   ui_grid_resize((handle_T)grid, (int)width, (int)height, error); | ||||
| } | ||||
|  | ||||
| /// Pushes data into UI.UIData, to be consumed later by remote_ui_flush(). | ||||
| static void push_call(UI *ui, const char *name, Array args) | ||||
| { | ||||
|   | ||||
| @@ -81,8 +81,21 @@ void grid_line(Integer grid, Integer row, Integer col_start, Array data) | ||||
| void grid_scroll(Integer grid, Integer top, Integer bot, | ||||
|                  Integer left, Integer right, Integer rows, Integer cols) | ||||
|   FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL; | ||||
| void grid_destroy(Integer grid) | ||||
|   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; | ||||
|  | ||||
| void popupmenu_show(Array items, Integer selected, Integer row, Integer col) | ||||
| void win_pos(Integer grid, Integer win, Integer startrow, | ||||
|              Integer startcol, Integer width, Integer height) | ||||
|   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; | ||||
| void win_hide(Integer grid) | ||||
|   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; | ||||
| void win_scroll_over_start(void) | ||||
|   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; | ||||
| void win_scroll_over_reset(void) | ||||
|   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; | ||||
|  | ||||
| void popupmenu_show(Array items, Integer selected, | ||||
|                     Integer row, Integer col, Integer grid) | ||||
|   FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; | ||||
| void popupmenu_hide(void) | ||||
|   FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; | ||||
|   | ||||
| @@ -1920,13 +1920,13 @@ Object nvim_get_proc(Integer pid, Error *err) | ||||
| Array nvim__inspect_cell(Integer row, Integer col, Error *err) | ||||
| { | ||||
|   Array ret = ARRAY_DICT_INIT; | ||||
|   if (row < 0 || row >= screen_Rows | ||||
|       || col < 0 || col >= screen_Columns) { | ||||
|   if (row < 0 || row >= default_grid.Rows | ||||
|       || col < 0 || col >= default_grid.Columns) { | ||||
|     return ret; | ||||
|   } | ||||
|   size_t off = LineOffset[(size_t)row] + (size_t)col; | ||||
|   ADD(ret, STRING_OBJ(cstr_to_string((char *)ScreenLines[off]))); | ||||
|   int attr = ScreenAttrs[off]; | ||||
|   size_t off = default_grid.line_offset[(size_t)row] + (size_t)col; | ||||
|   ADD(ret, STRING_OBJ(cstr_to_string((char *)default_grid.chars[off]))); | ||||
|   int attr = default_grid.attrs[off]; | ||||
|   ADD(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, err))); | ||||
|   // will not work first time | ||||
|   if (!highlight_use_hlstate()) { | ||||
|   | ||||
| @@ -18,6 +18,8 @@ typedef struct { | ||||
|  | ||||
| // for garray_T | ||||
| #include "nvim/garray.h" | ||||
| // for ScreenGrid | ||||
| #include "nvim/grid_defs.h" | ||||
| // for HLF_COUNT | ||||
| #include "nvim/highlight_defs.h" | ||||
| // for pos_T, lpos_T and linenr_T | ||||
| @@ -1183,6 +1185,9 @@ struct window_S { | ||||
|   int w_tagstackidx;                    /* idx just below active entry */ | ||||
|   int w_tagstacklen;                    /* number of tags on stack */ | ||||
|  | ||||
|   ScreenGrid w_grid;                    // the grid specific to the window | ||||
|   bool w_pos_changed;                   // true if window position changed | ||||
|  | ||||
|   /* | ||||
|    * w_fraction is the fractional row of the cursor within the window, from | ||||
|    * 0 at the top row to FRACTION_MULT at the last row. | ||||
|   | ||||
| @@ -1022,12 +1022,12 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he | ||||
|       && vim_isbreak(c) | ||||
|       && !vim_isbreak((int)s[1]) | ||||
|       && wp->w_p_wrap | ||||
|       && (wp->w_width != 0)) { | ||||
|       && (wp->w_grid.Columns != 0)) { | ||||
|     // Count all characters from first non-blank after a blank up to next | ||||
|     // non-blank after a blank. | ||||
|     numberextra = win_col_off(wp); | ||||
|     col2 = col; | ||||
|     colmax = (colnr_T)(wp->w_width - numberextra - col_adj); | ||||
|     colmax = (colnr_T)(wp->w_grid.Columns - numberextra - col_adj); | ||||
|  | ||||
|     if (col >= colmax) { | ||||
|         colmax += col_adj; | ||||
| @@ -1076,9 +1076,9 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he | ||||
|     numberextra = numberwidth; | ||||
|     col += numberextra + mb_added; | ||||
|  | ||||
|     if (col >= (colnr_T)wp->w_width) { | ||||
|       col -= wp->w_width; | ||||
|       numberextra = wp->w_width - (numberextra - win_col_off2(wp)); | ||||
|     if (col >= (colnr_T)wp->w_grid.Columns) { | ||||
|       col -= wp->w_grid.Columns; | ||||
|       numberextra = wp->w_grid.Columns - (numberextra - win_col_off2(wp)); | ||||
|       if (col >= numberextra && numberextra > 0) { | ||||
|         col %= numberextra; | ||||
|       } | ||||
| @@ -1097,16 +1097,17 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he | ||||
|       numberwidth -= win_col_off2(wp); | ||||
|     } | ||||
|  | ||||
|     if (col == 0 || (col + size + sbrlen > (colnr_T)wp->w_width)) { | ||||
|     if (col == 0 || (col + size + sbrlen > (colnr_T)wp->w_grid.Columns)) { | ||||
|       added = 0; | ||||
|  | ||||
|       if (*p_sbr != NUL) { | ||||
|         if (size + sbrlen + numberwidth > (colnr_T)wp->w_width) { | ||||
|         if (size + sbrlen + numberwidth > (colnr_T)wp->w_grid.Columns) { | ||||
|           // Calculate effective window width. | ||||
|           int width = (colnr_T)wp->w_width - sbrlen - numberwidth; | ||||
|           int prev_width = col ? ((colnr_T)wp->w_width - (sbrlen + col)) : 0; | ||||
|           int width = (colnr_T)wp->w_grid.Columns - sbrlen - numberwidth; | ||||
|           int prev_width = col ? ((colnr_T)wp->w_grid.Columns - (sbrlen + col)) | ||||
|                                : 0; | ||||
|           if (width == 0) { | ||||
|             width = (colnr_T)wp->w_width; | ||||
|             width = (colnr_T)wp->w_grid.Columns; | ||||
|           } | ||||
|           added += ((size - prev_width) / width) * vim_strsize(p_sbr); | ||||
|           if ((size - prev_width) % width) { | ||||
| @@ -1175,11 +1176,11 @@ bool in_win_border(win_T *wp, colnr_T vcol) | ||||
|   int width1;             // width of first line (after line number) | ||||
|   int width2;             // width of further lines | ||||
|  | ||||
|   if (wp->w_width == 0) { | ||||
|   if (wp->w_grid.Columns == 0) { | ||||
|     // there is no border | ||||
|     return false; | ||||
|   } | ||||
|   width1 = wp->w_width - win_col_off(wp); | ||||
|   width1 = wp->w_grid.Columns - win_col_off(wp); | ||||
|  | ||||
|   if ((int)vcol < width1 - 1) { | ||||
|     return false; | ||||
|   | ||||
| @@ -120,11 +120,11 @@ static int coladvance2( | ||||
|         --curwin->w_curswant; | ||||
|     } | ||||
|   } else { | ||||
|     int width = curwin->w_width - win_col_off(curwin); | ||||
|     int width = curwin->w_grid.Columns - win_col_off(curwin); | ||||
|  | ||||
|     if (finetune | ||||
|         && curwin->w_p_wrap | ||||
|         && curwin->w_width != 0 | ||||
|         && curwin->w_grid.Columns != 0 | ||||
|         && wcol >= (colnr_T)width) { | ||||
|       csize = linetabsize(line); | ||||
|       if (csize > 0) | ||||
| @@ -223,9 +223,10 @@ static int coladvance2( | ||||
|     } else { | ||||
|       int b = (int)wcol - (int)col; | ||||
|  | ||||
|       /* The difference between wcol and col is used to set coladd. */ | ||||
|       if (b > 0 && b < (MAXCOL - 2 * curwin->w_width)) | ||||
|       // The difference between wcol and col is used to set coladd. | ||||
|       if (b > 0 && b < (MAXCOL - 2 * curwin->w_grid.Columns)) { | ||||
|         pos->coladd = b; | ||||
|       } | ||||
|  | ||||
|       col += b; | ||||
|     } | ||||
| @@ -436,7 +437,7 @@ bool leftcol_changed(void) | ||||
|   bool retval = false; | ||||
|  | ||||
|   changed_cline_bef_curs(); | ||||
|   lastcol = curwin->w_leftcol + curwin->w_width - curwin_col_off() - 1; | ||||
|   lastcol = curwin->w_leftcol + curwin->w_grid.Columns - curwin_col_off() - 1; | ||||
|   validate_virtcol(); | ||||
|  | ||||
|   /* | ||||
|   | ||||
| @@ -560,7 +560,7 @@ static int insert_check(VimState *state) | ||||
|  | ||||
|     if (curwin->w_wcol < s->mincol - curbuf->b_p_ts | ||||
|         && curwin->w_wrow == curwin->w_winrow | ||||
|         + curwin->w_height - 1 - p_so | ||||
|         + curwin->w_grid.Rows - 1 - p_so | ||||
|         && (curwin->w_cursor.lnum != curwin->w_topline | ||||
|             || curwin->w_topfill > 0)) { | ||||
|       if (curwin->w_topfill > 0) { | ||||
| @@ -1494,40 +1494,41 @@ void edit_putchar(int c, int highlight) | ||||
| { | ||||
|   int attr; | ||||
|  | ||||
|   if (ScreenLines != NULL) { | ||||
|     update_topline();           /* just in case w_topline isn't valid */ | ||||
|   if (curwin->w_grid.chars != NULL || default_grid.chars != NULL) { | ||||
|     update_topline();  // just in case w_topline isn't valid | ||||
|     validate_cursor(); | ||||
|     if (highlight) { | ||||
|       attr = HL_ATTR(HLF_8); | ||||
|     } else { | ||||
|       attr = 0; | ||||
|     } | ||||
|     pc_row = curwin->w_winrow + curwin->w_wrow; | ||||
|     pc_col = curwin->w_wincol; | ||||
|     pc_row = curwin->w_wrow; | ||||
|     pc_col = 0; | ||||
|     pc_status = PC_STATUS_UNSET; | ||||
|     if (curwin->w_p_rl) { | ||||
|       pc_col += curwin->w_width - 1 - curwin->w_wcol; | ||||
|       pc_col += curwin->w_grid.Columns - 1 - curwin->w_wcol; | ||||
|       if (has_mbyte) { | ||||
|         int fix_col = mb_fix_col(pc_col, pc_row); | ||||
|         int fix_col = grid_fix_col(&curwin->w_grid, pc_col, pc_row); | ||||
|  | ||||
|         if (fix_col != pc_col) { | ||||
|           screen_putchar(' ', pc_row, fix_col, attr); | ||||
|           --curwin->w_wcol; | ||||
|           grid_putchar(&curwin->w_grid, ' ', pc_row, fix_col, attr); | ||||
|           curwin->w_wcol--; | ||||
|           pc_status = PC_STATUS_RIGHT; | ||||
|         } | ||||
|       } | ||||
|     } else { | ||||
|       pc_col += curwin->w_wcol; | ||||
|       if (mb_lefthalve(pc_row, pc_col)) | ||||
|       if (grid_lefthalve(&curwin->w_grid, pc_row, pc_col)) { | ||||
|         pc_status = PC_STATUS_LEFT; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     /* save the character to be able to put it back */ | ||||
|     if (pc_status == PC_STATUS_UNSET) { | ||||
|       screen_getbytes(pc_row, pc_col, pc_bytes, &pc_attr); | ||||
|       grid_getbytes(&curwin->w_grid, pc_row, pc_col, pc_bytes, &pc_attr); | ||||
|       pc_status = PC_STATUS_SET; | ||||
|     } | ||||
|     screen_putchar(c, pc_row, pc_col, attr); | ||||
|     grid_putchar(&curwin->w_grid, c, pc_row, pc_col, attr); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -1543,7 +1544,8 @@ void edit_unputchar(void) | ||||
|     if (pc_status == PC_STATUS_RIGHT || pc_status == PC_STATUS_LEFT) { | ||||
|       redrawWinline(curwin, curwin->w_cursor.lnum, false); | ||||
|     } else { | ||||
|       screen_puts(pc_bytes, pc_row - msg_scrolled, pc_col, pc_attr); | ||||
|       grid_puts(&curwin->w_grid, pc_bytes, pc_row - msg_scrolled, pc_col, | ||||
|                 pc_attr); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -1566,8 +1568,8 @@ void display_dollar(colnr_T col) | ||||
|   char_u *p = get_cursor_line_ptr(); | ||||
|   curwin->w_cursor.col -= utf_head_off(p, p + col); | ||||
|   curs_columns(false);              // Recompute w_wrow and w_wcol | ||||
|   if (curwin->w_wcol < curwin->w_width) { | ||||
|     edit_putchar('$', FALSE); | ||||
|   if (curwin->w_wcol < curwin->w_grid.Columns) { | ||||
|     edit_putchar('$', false); | ||||
|     dollar_vcol = curwin->w_virtcol; | ||||
|   } | ||||
|   curwin->w_cursor.col = save_col; | ||||
| @@ -5825,7 +5827,7 @@ static void check_auto_format( | ||||
| /* | ||||
|  * Find out textwidth to be used for formatting: | ||||
|  *	if 'textwidth' option is set, use it | ||||
|  *	else if 'wrapmargin' option is set, use curwin->w_width - 'wrapmargin' | ||||
|  *	else if 'wrapmargin' option is set, use curwin->w_grid.Columns-'wrapmargin' | ||||
|  *	if invalid value, use 0. | ||||
|  *	Set default to window width (maximum 79) for "gq" operator. | ||||
|  */ | ||||
| @@ -5840,9 +5842,10 @@ comp_textwidth ( | ||||
|   if (textwidth == 0 && curbuf->b_p_wm) { | ||||
|     /* The width is the window width minus 'wrapmargin' minus all the | ||||
|      * things that add to the margin. */ | ||||
|     textwidth = curwin->w_width - curbuf->b_p_wm; | ||||
|     if (cmdwin_type != 0) | ||||
|     textwidth = curwin->w_grid.Columns - curbuf->b_p_wm; | ||||
|     if (cmdwin_type != 0) { | ||||
|       textwidth -= 1; | ||||
|     } | ||||
|     textwidth -= curwin->w_p_fdc; | ||||
|  | ||||
|     if (signcolumn_on(curwin)) { | ||||
| @@ -5855,9 +5858,10 @@ comp_textwidth ( | ||||
|   if (textwidth < 0) | ||||
|     textwidth = 0; | ||||
|   if (ff && textwidth == 0) { | ||||
|     textwidth = curwin->w_width - 1; | ||||
|     if (textwidth > 79) | ||||
|     textwidth = curwin->w_grid.Columns - 1; | ||||
|     if (textwidth > 79) { | ||||
|       textwidth = 79; | ||||
|     } | ||||
|   } | ||||
|   return textwidth; | ||||
| } | ||||
|   | ||||
| @@ -14023,11 +14023,11 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) | ||||
|  | ||||
|   const int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; | ||||
|   const int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; | ||||
|   if (row < 0 || row >= screen_Rows | ||||
|       || col < 0 || col >= screen_Columns) { | ||||
|   if (row < 0 || row >= default_grid.Rows | ||||
|       || col < 0 || col >= default_grid.Columns) { | ||||
|     c = -1; | ||||
|   } else { | ||||
|     c = ScreenAttrs[LineOffset[row] + col]; | ||||
|     c = default_grid.attrs[default_grid.line_offset[row] + col]; | ||||
|   } | ||||
|   rettv->vval.v_number = c; | ||||
| } | ||||
| @@ -14042,12 +14042,12 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) | ||||
|  | ||||
|   const int row = tv_get_number_chk(&argvars[0], NULL) - 1; | ||||
|   const int col = tv_get_number_chk(&argvars[1], NULL) - 1; | ||||
|   if (row < 0 || row >= screen_Rows | ||||
|       || col < 0 || col >= screen_Columns) { | ||||
|   if (row < 0 || row >= default_grid.Rows | ||||
|       || col < 0 || col >= default_grid.Columns) { | ||||
|     c = -1; | ||||
|   } else { | ||||
|     off = LineOffset[row] + col; | ||||
|     c = utf_ptr2char(ScreenLines[off]); | ||||
|     off = default_grid.line_offset[row] + col; | ||||
|     c = utf_ptr2char(default_grid.chars[off]); | ||||
|   } | ||||
|   rettv->vval.v_number = c; | ||||
| } | ||||
| @@ -16774,10 +16774,10 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   uint16_t term_width = MAX(0, curwin->w_width - win_col_off(curwin)); | ||||
|   uint16_t term_width = MAX(0, curwin->w_grid.Columns - win_col_off(curwin)); | ||||
|   Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, | ||||
|                                     true, false, false, cwd, | ||||
|                                     term_width, curwin->w_height, | ||||
|                                     term_width, curwin->w_grid.Rows, | ||||
|                                     xstrdup("xterm-256color"), | ||||
|                                     &rettv->vval.v_number); | ||||
|   if (rettv->vval.v_number <= 0) { | ||||
|   | ||||
| @@ -256,10 +256,12 @@ void ex_align(exarg_T *eap) | ||||
|      */ | ||||
|     if (width <= 0) | ||||
|       width = curbuf->b_p_tw; | ||||
|     if (width == 0 && curbuf->b_p_wm > 0) | ||||
|       width = curwin->w_width - curbuf->b_p_wm; | ||||
|     if (width <= 0) | ||||
|     if (width == 0 && curbuf->b_p_wm > 0) { | ||||
|       width = curwin->w_grid.Columns - curbuf->b_p_wm; | ||||
|     } | ||||
|     if (width <= 0) { | ||||
|       width = 80; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL) | ||||
| @@ -2870,11 +2872,11 @@ void ex_z(exarg_T *eap) | ||||
|   // Vi compatible: ":z!" uses display height, without a count uses | ||||
|   // 'scroll' | ||||
|   if (eap->forceit) { | ||||
|     bigness = curwin->w_height; | ||||
|     bigness = curwin->w_grid.Rows; | ||||
|   } else if (ONE_WINDOW) { | ||||
|     bigness = curwin->w_p_scr * 2; | ||||
|   } else { | ||||
|     bigness = curwin->w_height - 3; | ||||
|     bigness = curwin->w_grid.Rows - 3; | ||||
|   } | ||||
|   if (bigness < 1) { | ||||
|     bigness = 1; | ||||
|   | ||||
| @@ -9194,10 +9194,10 @@ static int ses_winsizes(FILE *fd, int restore_size, win_T *tab_firstwin) | ||||
|       /* restore height when not full height */ | ||||
|       if (wp->w_height + wp->w_status_height < topframe->fr_height | ||||
|           && (fprintf(fd, | ||||
|                   "exe '%dresize ' . ((&lines * %" PRId64 | ||||
|                   " + %" PRId64 ") / %" PRId64 ")", | ||||
|                   n, (int64_t)wp->w_height, | ||||
|                   (int64_t)(Rows / 2), (int64_t)Rows) < 0 | ||||
|                       "exe '%dresize ' . ((&lines * %" PRId64 | ||||
|                       " + %" PRId64 ") / %" PRId64 ")", | ||||
|                       n, (int64_t)wp->w_grid.Rows, | ||||
|                       (int64_t)(Rows / 2), (int64_t)Rows) < 0 | ||||
|               || put_eol(fd) == FAIL)) | ||||
|         return FAIL; | ||||
|  | ||||
| @@ -9459,8 +9459,8 @@ put_view( | ||||
|                 " * winheight(0) + %" PRId64 ") / %" PRId64 ")", | ||||
|                 (int64_t)wp->w_cursor.lnum, | ||||
|                 (int64_t)(wp->w_cursor.lnum - wp->w_topline), | ||||
|                 (int64_t)(wp->w_height / 2), | ||||
|                 (int64_t)wp->w_height) < 0 | ||||
|                 (int64_t)(wp->w_grid.Rows / 2), | ||||
|                 (int64_t)wp->w_grid.Rows) < 0 | ||||
|         || put_eol(fd) == FAIL | ||||
|         || put_line(fd, "if s:l < 1 | let s:l = 1 | endif") == FAIL | ||||
|         || put_line(fd, "exe s:l") == FAIL | ||||
|   | ||||
| @@ -2120,8 +2120,8 @@ static int vgetorpeek(int advance) | ||||
|                     ++col; | ||||
|                 } | ||||
|                 curwin->w_wrow = curwin->w_cline_row | ||||
|                                  + curwin->w_wcol / curwin->w_width; | ||||
|                 curwin->w_wcol %= curwin->w_width; | ||||
|                                  + curwin->w_wcol / curwin->w_grid.Columns; | ||||
|                 curwin->w_wcol %= curwin->w_grid.Columns; | ||||
|                 curwin->w_wcol += curwin_col_off(); | ||||
|                 col = 0;                        /* no correction needed */ | ||||
|               } else { | ||||
| @@ -2129,8 +2129,8 @@ static int vgetorpeek(int advance) | ||||
|                 col = curwin->w_cursor.col - 1; | ||||
|               } | ||||
|             } else if (curwin->w_p_wrap && curwin->w_wrow) { | ||||
|               --curwin->w_wrow; | ||||
|               curwin->w_wcol = curwin->w_width - 1; | ||||
|               curwin->w_wrow--; | ||||
|               curwin->w_wcol = curwin->w_grid.Columns - 1; | ||||
|               col = curwin->w_cursor.col - 1; | ||||
|             } | ||||
|             if (col > 0 && curwin->w_wcol > 0) { | ||||
|   | ||||
| @@ -91,9 +91,10 @@ EXTERN struct nvim_stats_s { | ||||
| /* | ||||
|  * Number of Rows and Columns in the screen. | ||||
|  * Must be long to be able to use them as options in option.c. | ||||
|  * Note: Use screen_Rows and screen_Columns to access items in ScreenLines[]. | ||||
|  * They may have different values when the screen wasn't (re)allocated yet | ||||
|  * after setting Rows or Columns (e.g., when starting up). | ||||
|  * Note: Use default_grid.Rows and default_grid.Columns to access items in | ||||
|  * default_grid.chars[]. They may have different values when the screen | ||||
|  * wasn't (re)allocated yet after setting Rows or Columns (e.g., when starting | ||||
|  * up). | ||||
|  */ | ||||
| #define DFLT_COLS       80              // default value for 'columns' | ||||
| #define DFLT_ROWS       24              // default value for 'lines' | ||||
| @@ -128,45 +129,6 @@ typedef off_t off_T; | ||||
| # endif | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * The characters and attributes cached for the screen. | ||||
|  */ | ||||
| typedef char_u schar_T[(MAX_MCO+1) * 4 + 1]; | ||||
| typedef int16_t sattr_T; | ||||
|  | ||||
| /// ScreenLines[] contains a copy of the whole screen, as it currently is | ||||
| /// displayed. It is a single block of screen cells, the size of the screen | ||||
| /// plus one line. The extra line used as a buffer while redrawing a window | ||||
| /// line, so it can be compared with the previous state of that line. This way | ||||
| /// we can avoid sending bigger updates than neccessary to the Ul layer. | ||||
| /// | ||||
| /// Screen cells are stored as NUL-terminated UTF-8 strings, and a cell can | ||||
| /// contain up to MAX_MCO composing characters after the base character. | ||||
| /// The composing characters are to be drawn on top of the original character. | ||||
| /// The content after the NUL is not defined (so comparison must be done a | ||||
| /// single cell at a time). Double-width characters are stored in the left cell, | ||||
| /// and the right cell should only contain the empty string. When a part of the | ||||
| /// screen is cleared, the cells should be filled with a single whitespace char. | ||||
| /// | ||||
| /// ScreenAttrs[] contains the highlighting attribute for each cell. | ||||
| /// LineOffset[n] is the offset from ScreenLines[] and ScreenAttrs[] for the | ||||
| /// start of line 'n'. These offsets are in general not linear, as full screen | ||||
| /// scrolling is implemented by rotating the offsets in the LineOffset array. | ||||
| /// LineWraps[] is an array of boolean flags indicating if the screen line wraps | ||||
| /// to the next line. It can only be true if a window occupies the entire screen | ||||
| /// width. | ||||
| /// | ||||
| /// | ||||
| /// Note: before the screen is initialized and when out of memory these can be | ||||
| /// NULL. | ||||
| EXTERN schar_T  *ScreenLines INIT(= NULL); | ||||
| EXTERN sattr_T  *ScreenAttrs INIT(= NULL); | ||||
| EXTERN unsigned *LineOffset INIT(= NULL); | ||||
| EXTERN char_u   *LineWraps INIT(= NULL);        /* line wraps to next line */ | ||||
|  | ||||
| EXTERN int screen_Rows INIT(= 0);           /* actual size of ScreenLines[] */ | ||||
| EXTERN int screen_Columns INIT(= 0);        /* actual size of ScreenLines[] */ | ||||
|  | ||||
| /* | ||||
|  * When vgetc() is called, it sets mod_mask to the set of modifiers that are | ||||
|  * held down based on the MOD_MASK_* symbols that are read first. | ||||
| @@ -228,11 +190,8 @@ EXTERN int compl_cont_status INIT(= 0); | ||||
| # define CONT_LOCAL     32      /* for ctrl_x_mode 0, ^X^P/^X^N do a local | ||||
|                                  * expansion, (eg use complete=.) */ | ||||
|  | ||||
| /* | ||||
|  * Functions for putting characters in the command line, | ||||
|  * while keeping ScreenLines[] updated. | ||||
|  */ | ||||
| EXTERN int cmdmsg_rl INIT(= FALSE);         /* cmdline is drawn right to left */ | ||||
| // state for putting characters in the message area | ||||
| EXTERN int cmdmsg_rl INIT(= false);  // cmdline is drawn right to left | ||||
| EXTERN int msg_col; | ||||
| EXTERN int msg_row; | ||||
| EXTERN int msg_scrolled;        /* Number of screen lines that windows have | ||||
|   | ||||
							
								
								
									
										59
									
								
								src/nvim/grid_defs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/nvim/grid_defs.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| #ifndef NVIM_GRID_DEFS_H | ||||
| #define NVIM_GRID_DEFS_H | ||||
|  | ||||
| #include <stdint.h> | ||||
|  | ||||
| #include "nvim/types.h" | ||||
|  | ||||
| #define MAX_MCO  6  // maximum value for 'maxcombine' | ||||
|  | ||||
| // The characters and attributes drawn on grids. | ||||
| typedef char_u schar_T[(MAX_MCO+1) * 4 + 1]; | ||||
| typedef int16_t sattr_T; | ||||
|  | ||||
| /// ScreenGrid represents a resizable rectuangular grid displayed by UI clients. | ||||
| /// | ||||
| /// chars[] contains the UTF-8 text that is currently displayed on the grid. | ||||
| /// It is stored as a single block of cells. When redrawing a part of the grid, | ||||
| /// the new state can be compared with the existing state of the grid. This way | ||||
| /// we can avoid sending bigger updates than neccessary to the Ul layer. | ||||
| /// | ||||
| /// Screen cells are stored as NUL-terminated UTF-8 strings, and a cell can | ||||
| /// contain up to MAX_MCO composing characters after the base character. | ||||
| /// The composing characters are to be drawn on top of the original character. | ||||
| /// The content after the NUL is not defined (so comparison must be done a | ||||
| /// single cell at a time). Double-width characters are stored in the left cell, | ||||
| /// and the right cell should only contain the empty string. When a part of the | ||||
| /// screen is cleared, the cells should be filled with a single whitespace char. | ||||
| /// | ||||
| /// attrs[] contains the highlighting attribute for each cell. | ||||
| /// line_offset[n] is the offset from chars[] and attrs[] for the | ||||
| /// start of line 'n'. These offsets are in general not linear, as full screen | ||||
| /// scrolling is implemented by rotating the offsets in the line_offset array. | ||||
| /// line_wraps[] is an array of boolean flags indicating if the screen line | ||||
| /// wraps to the next line. It can only be true if a window occupies the entire | ||||
| /// screen width. | ||||
| typedef struct { | ||||
|   handle_T handle; | ||||
|  | ||||
|   schar_T  *chars; | ||||
|   sattr_T  *attrs; | ||||
|   unsigned *line_offset; | ||||
|   char_u   *line_wraps; | ||||
|  | ||||
|   // the size of the allocated grid. | ||||
|   int Rows; | ||||
|   int Columns; | ||||
|  | ||||
|   // offsets for the grid relative to the global screen | ||||
|   int row_offset; | ||||
|   int col_offset; | ||||
|  | ||||
|   // grid size requested by the UI. Used for window grids only. | ||||
|   int requested_rows; | ||||
|   int requested_cols; | ||||
|  | ||||
|   int was_resized; | ||||
| } ScreenGrid; | ||||
|  | ||||
| #endif  // NVIM_GRID_DEFS_H | ||||
| @@ -582,8 +582,9 @@ static void prt_header(prt_settings_T *const psettings, const int pagenum, | ||||
|  */ | ||||
| static void prt_message(char_u *s) | ||||
| { | ||||
|   screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); | ||||
|   screen_puts(s, (int)Rows - 1, 0, HL_ATTR(HLF_R)); | ||||
|   grid_fill(&default_grid, (int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', | ||||
|             0); | ||||
|   grid_puts(&default_grid, s, (int)Rows - 1, 0, HL_ATTR(HLF_R)); | ||||
|   ui_flush(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -98,7 +98,7 @@ static int get_attr_entry(HlEntry entry) | ||||
|   return id; | ||||
| } | ||||
|  | ||||
| /// When a UI connects, we need to send it the table of higlights used so far. | ||||
| /// When a UI connects, we need to send it the table of highlights used so far. | ||||
| void ui_send_all_hls(UI *ui) | ||||
| { | ||||
|   for (size_t i = 1; i < kv_size(attr_entries); i++) { | ||||
| @@ -212,13 +212,7 @@ void clear_hl_tables(bool reinit) | ||||
|     map_clear(int, int)(combine_attr_entries); | ||||
|     highlight_attr_set_all(); | ||||
|     highlight_changed(); | ||||
|     redraw_all_later(NOT_VALID); | ||||
|     if (ScreenAttrs) { | ||||
|       // the meaning of 0 doesn't change anyway | ||||
|       // but the rest must be retransmitted | ||||
|       memset(ScreenAttrs, 0, | ||||
|              sizeof(*ScreenAttrs) * (size_t)(screen_Rows * screen_Columns)); | ||||
|     } | ||||
|     screen_invalidate_highlights(); | ||||
|   } else { | ||||
|     kv_destroy(attr_entries); | ||||
|     map_free(HlEntry, int)(attr_entry_ids); | ||||
|   | ||||
| @@ -463,8 +463,8 @@ int get_breakindent_win(win_T *wp, char_u *line) | ||||
|   static char_u *prev_line = NULL;  // cached pointer to line. | ||||
|   static varnumber_T prev_tick = 0;  // Changedtick of cached value. | ||||
|   int bri = 0; | ||||
|   /* window width minus window margin space, i.e. what rests for text */ | ||||
|   const int eff_wwidth = wp->w_width | ||||
|   // window width minus window margin space, i.e. what rests for text | ||||
|   const int eff_wwidth = wp->w_grid.Columns | ||||
|     - ((wp->w_p_nu || wp->w_p_rnu) | ||||
|         && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) | ||||
|         ? number_width(wp) + 1 : 0); | ||||
|   | ||||
| @@ -556,13 +556,6 @@ size_t mb_string2cells(const char_u *str) | ||||
|   return clen; | ||||
| } | ||||
|  | ||||
| /// Return number of display cells for char at ScreenLines[off]. | ||||
| /// We make sure that the offset used is less than "max_off". | ||||
| int utf_off2cells(unsigned off, unsigned max_off) | ||||
| { | ||||
|   return (off + 1 < max_off && ScreenLines[off + 1][0] == 0) ? 2 : 1; | ||||
| } | ||||
|  | ||||
| /// Convert a UTF-8 byte sequence to a wide character | ||||
| /// | ||||
| /// If the sequence is illegal or truncated by a NUL then the first byte is | ||||
| @@ -1822,32 +1815,6 @@ const char *mb_unescape(const char **const pp) | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Return true if the character at "row"/"col" on the screen is the left side | ||||
|  * of a double-width character. | ||||
|  * Caller must make sure "row" and "col" are not invalid! | ||||
|  */ | ||||
| bool mb_lefthalve(int row, int col) | ||||
| { | ||||
|   return utf_off2cells(LineOffset[row] + col, | ||||
|                        LineOffset[row] + screen_Columns) > 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Correct a position on the screen, if it's the right half of a double-wide | ||||
|  * char move it to the left half.  Returns the corrected column. | ||||
|  */ | ||||
| int mb_fix_col(int col, int row) | ||||
| { | ||||
|   col = check_col(col); | ||||
|   row = check_row(row); | ||||
|   if (ScreenLines != NULL && col > 0 | ||||
|       && ScreenLines[LineOffset[row] + col][0] == 0) { | ||||
|     return col - 1; | ||||
|   } | ||||
|   return col; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Skip the Vim specific head of a 'encoding' name. | ||||
| @@ -2524,23 +2491,3 @@ char_u * string_convert_ext(const vimconv_T *const vcp, char_u *ptr, | ||||
|  | ||||
|   return retval; | ||||
| } | ||||
|  | ||||
| // Check bounds for column number | ||||
| static int check_col(int col) | ||||
| { | ||||
|   if (col < 0) | ||||
|     return 0; | ||||
|   if (col >= screen_Columns) | ||||
|     return screen_Columns - 1; | ||||
|   return col; | ||||
| } | ||||
|  | ||||
| // Check bounds for row number | ||||
| static int check_row(int row) | ||||
| { | ||||
|   if (row < 0) | ||||
|     return 0; | ||||
|   if (row >= screen_Rows) | ||||
|     return screen_Rows - 1; | ||||
|   return row; | ||||
| } | ||||
|   | ||||
| @@ -19,13 +19,6 @@ | ||||
| #define MB_BYTE2LEN(b)         utf8len_tab[b] | ||||
| #define MB_BYTE2LEN_CHECK(b)   (((b) < 0 || (b) > 255) ? 1 : utf8len_tab[b]) | ||||
|  | ||||
| /// Maximum value for 'maxcombine' | ||||
| /// | ||||
| /// At most that number of composing characters may be attached to the leading | ||||
| /// character by various `utfc_*` functions. Note that some functions do not | ||||
| /// have this limit. | ||||
| enum { MAX_MCO = 6 }; | ||||
|  | ||||
| // max length of an unicode char | ||||
| #define MB_MAXCHAR     6 | ||||
|  | ||||
|   | ||||
| @@ -698,8 +698,8 @@ void free_all_mem(void) | ||||
|     buf = bufref_valid(&bufref) ? nextbuf : firstbuf; | ||||
|   } | ||||
|  | ||||
|   /* screenlines (can't display anything now!) */ | ||||
|   free_screenlines(); | ||||
|   // free screenlines (can't display anything now!) | ||||
|   screen_free_all_mem(); | ||||
|  | ||||
|   clear_hl_tables(false); | ||||
|   list_free_log(); | ||||
|   | ||||
| @@ -1546,10 +1546,8 @@ void msg_prt_line(char_u *s, int list) | ||||
|   msg_clr_eos(); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Use screen_puts() to output one multi-byte character. | ||||
|  * Return the pointer "s" advanced to the next character. | ||||
|  */ | ||||
| // Use grid_puts() to output one multi-byte character. | ||||
| // Return the pointer "s" advanced to the next character. | ||||
| static char_u *screen_puts_mbyte(char_u *s, int l, int attr) | ||||
| { | ||||
|   int cw; | ||||
| @@ -1563,7 +1561,7 @@ static char_u *screen_puts_mbyte(char_u *s, int l, int attr) | ||||
|     return s; | ||||
|   } | ||||
|  | ||||
|   screen_puts_len(s, l, msg_row, msg_col, attr); | ||||
|   grid_puts_len(&default_grid, s, l, msg_row, msg_col, attr); | ||||
|   if (cmdmsg_rl) { | ||||
|     msg_col -= cw; | ||||
|     if (msg_col == 0) { | ||||
| @@ -1886,19 +1884,22 @@ int msg_scrollsize(void) | ||||
|  */ | ||||
| static void msg_scroll_up(void) | ||||
| { | ||||
|   if (msg_scrolled == 0) { | ||||
|     ui_call_win_scroll_over_start(); | ||||
|   } | ||||
|   if (dy_flags & DY_MSGSEP) { | ||||
|     if (msg_scrolled == 0) { | ||||
|       screen_fill(Rows-p_ch-1, Rows-p_ch, 0, (int)Columns, | ||||
|                   fill_msgsep, fill_msgsep, HL_ATTR(HLF_MSGSEP)); | ||||
|       grid_fill(&default_grid, Rows-p_ch-1, Rows-p_ch, 0, (int)Columns, | ||||
|                 fill_msgsep, fill_msgsep, HL_ATTR(HLF_MSGSEP)); | ||||
|     } | ||||
|     int nscroll = MIN(msg_scrollsize()+1, Rows); | ||||
|     screen_del_lines(Rows-nscroll, 1, Rows, 0, Columns); | ||||
|     grid_del_lines(&default_grid, Rows-nscroll, 1, Rows, 0, Columns); | ||||
|   } else { | ||||
|     screen_del_lines(0, 1, (int)Rows, 0, Columns); | ||||
|     grid_del_lines(&default_grid, 0, 1, (int)Rows, 0, Columns); | ||||
|   } | ||||
|   // TODO(bfredl): when msgsep display is properly batched, this fill should be | ||||
|   // eliminated. | ||||
|   screen_fill(Rows-1, Rows, 0, (int)Columns, ' ', ' ', 0); | ||||
|   grid_fill(&default_grid, Rows-1, Rows, 0, (int)Columns, ' ', ' ', 0); | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -2097,7 +2098,8 @@ static void t_puts(int *t_col, const char_u *t_s, const char_u *s, int attr) | ||||
| { | ||||
|   // Output postponed text. | ||||
|   msg_didout = true;  // Remember that line is not empty. | ||||
|   screen_puts_len((char_u *)t_s, (int)(s - t_s), msg_row, msg_col, attr); | ||||
|   grid_puts_len(&default_grid, (char_u *)t_s, (int)(s - t_s), msg_row, msg_col, | ||||
|                 attr); | ||||
|   msg_col += *t_col; | ||||
|   *t_col = 0; | ||||
|   /* If the string starts with a composing character don't increment the | ||||
| @@ -2313,8 +2315,9 @@ static int do_more_prompt(int typed_char) | ||||
|           } | ||||
|  | ||||
|           if (toscroll == -1 | ||||
|               && screen_ins_lines(0, 1, (int)Rows, 0, (int)Columns) == OK) { | ||||
|             screen_fill(0, 1, 0, (int)Columns, ' ', ' ', 0); | ||||
|               && grid_ins_lines(&default_grid, 0, 1, (int)Rows, | ||||
|                                 0, (int)Columns) == OK) { | ||||
|             grid_fill(&default_grid, 0, 1, 0, (int)Columns, ' ', ' ', 0); | ||||
|             // display line at top | ||||
|             (void)disp_sb_line(0, mp); | ||||
|           } else { | ||||
| @@ -2333,18 +2336,18 @@ static int do_more_prompt(int typed_char) | ||||
|           /* scroll up, display line at bottom */ | ||||
|           msg_scroll_up(); | ||||
|           inc_msg_scrolled(); | ||||
|           screen_fill((int)Rows - 2, (int)Rows - 1, 0, | ||||
|               (int)Columns, ' ', ' ', 0); | ||||
|           grid_fill(&default_grid, (int)Rows - 2, (int)Rows - 1, 0, | ||||
|                     (int)Columns, ' ', ' ', 0); | ||||
|           mp_last = disp_sb_line((int)Rows - 2, mp_last); | ||||
|           --toscroll; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if (toscroll <= 0) { | ||||
|         /* displayed the requested text, more prompt again */ | ||||
|         screen_fill((int)Rows - 1, (int)Rows, 0, | ||||
|             (int)Columns, ' ', ' ', 0); | ||||
|         msg_moremsg(FALSE); | ||||
|         // displayed the requested text, more prompt again | ||||
|         grid_fill(&default_grid, (int)Rows - 1, (int)Rows, 0, | ||||
|                   (int)Columns, ' ', ' ', 0); | ||||
|         msg_moremsg(false); | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
| @@ -2355,8 +2358,9 @@ static int do_more_prompt(int typed_char) | ||||
|     break; | ||||
|   } | ||||
|  | ||||
|   /* clear the --more-- message */ | ||||
|   screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); | ||||
|   // clear the --more-- message | ||||
|   grid_fill(&default_grid, (int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', | ||||
|             0); | ||||
|   State = oldState; | ||||
|   setmouse(); | ||||
|   if (quit_more) { | ||||
| @@ -2452,8 +2456,8 @@ void mch_msg(char *str) | ||||
|  */ | ||||
| static void msg_screen_putchar(int c, int attr) | ||||
| { | ||||
|   msg_didout = TRUE;            /* remember that line is not empty */ | ||||
|   screen_putchar(c, msg_row, msg_col, attr); | ||||
|   msg_didout = true;            // remember that line is not empty | ||||
|   grid_putchar(&default_grid, c, msg_row, msg_col, attr); | ||||
|   if (cmdmsg_rl) { | ||||
|     if (--msg_col == 0) { | ||||
|       msg_col = Columns; | ||||
| @@ -2473,11 +2477,12 @@ void msg_moremsg(int full) | ||||
|   char_u      *s = (char_u *)_("-- More --"); | ||||
|  | ||||
|   attr = HL_ATTR(HLF_M); | ||||
|   screen_puts(s, (int)Rows - 1, 0, attr); | ||||
|   if (full) | ||||
|     screen_puts((char_u *) | ||||
|         _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "), | ||||
|         (int)Rows - 1, vim_strsize(s), attr); | ||||
|   grid_puts(&default_grid, s, (int)Rows - 1, 0, attr); | ||||
|   if (full) { | ||||
|     grid_puts(&default_grid, (char_u *) | ||||
|               _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "), | ||||
|               (int)Rows - 1, vim_strsize(s), attr); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -2525,13 +2530,13 @@ void msg_clr_eos(void) | ||||
|  */ | ||||
| void msg_clr_eos_force(void) | ||||
| { | ||||
|   if (cmdmsg_rl) { | ||||
|     screen_fill(msg_row, msg_row + 1, 0, msg_col + 1, ' ', ' ', 0); | ||||
|     screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); | ||||
|   } else { | ||||
|     screen_fill(msg_row, msg_row + 1, msg_col, (int)Columns, ' ', ' ', 0); | ||||
|     screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); | ||||
|   } | ||||
|   int msg_startcol = (cmdmsg_rl) ? 0 : msg_col; | ||||
|   int msg_endcol = (cmdmsg_rl) ? msg_col + 1 : (int)Columns; | ||||
|  | ||||
|   grid_fill(&default_grid, msg_row, msg_row + 1, msg_startcol, msg_endcol, ' ', | ||||
|             ' ', 0); | ||||
|   grid_fill(&default_grid, msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ', | ||||
|             0); | ||||
| } | ||||
|  | ||||
| /* | ||||
|   | ||||
| @@ -1251,7 +1251,7 @@ int plines_win_nofill( | ||||
|     return 1; | ||||
|   } | ||||
|  | ||||
|   if (wp->w_width == 0) { | ||||
|   if (wp->w_grid.Columns == 0) { | ||||
|     return 1; | ||||
|   } | ||||
|  | ||||
| @@ -1261,8 +1261,8 @@ int plines_win_nofill( | ||||
|   } | ||||
|  | ||||
|   const int lines = plines_win_nofold(wp, lnum); | ||||
|   if (winheight && lines > wp->w_height) { | ||||
|     return wp->w_height; | ||||
|   if (winheight && lines > wp->w_grid.Rows) { | ||||
|     return wp->w_grid.Rows; | ||||
|   } | ||||
|   return lines; | ||||
| } | ||||
| @@ -1292,7 +1292,7 @@ int plines_win_nofold(win_T *wp, linenr_T lnum) | ||||
|   /* | ||||
|    * Add column offset for 'number', 'relativenumber' and 'foldcolumn'. | ||||
|    */ | ||||
|   width = wp->w_width - win_col_off(wp); | ||||
|   width = wp->w_grid.Columns - win_col_off(wp); | ||||
|   if (width <= 0 || col > 32000) { | ||||
|     return 32000;  // bigger than the number of screen columns | ||||
|   } | ||||
| @@ -1318,8 +1318,9 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) | ||||
|   if (!wp->w_p_wrap) | ||||
|     return lines + 1; | ||||
|  | ||||
|   if (wp->w_width == 0) | ||||
|   if (wp->w_grid.Columns == 0) { | ||||
|     return lines + 1; | ||||
|   } | ||||
|  | ||||
|   char_u *line = ml_get_buf(wp->w_buffer, lnum, false); | ||||
|   char_u *s = line; | ||||
| @@ -1340,7 +1341,7 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) | ||||
|   } | ||||
|  | ||||
|   // Add column offset for 'number', 'relativenumber', 'foldcolumn', etc. | ||||
|   int width = wp->w_width - win_col_off(wp); | ||||
|   int width = wp->w_grid.Columns - win_col_off(wp); | ||||
|   if (width <= 0) { | ||||
|     return 9999; | ||||
|   } | ||||
|   | ||||
| @@ -110,8 +110,9 @@ retnomove: | ||||
|   // Remember the character under the mouse, it might be a '-' or '+' in the | ||||
|   // fold column. NB: only works for ASCII chars! | ||||
|   if (row >= 0 && row < Rows && col >= 0 && col <= Columns | ||||
|       && ScreenLines != NULL) { | ||||
|     mouse_char = ScreenLines[LineOffset[row] + (unsigned)col][0]; | ||||
|       && default_grid.chars != NULL) { | ||||
|     mouse_char = default_grid.chars[default_grid.line_offset[row] | ||||
|                                     + (unsigned)col][0]; | ||||
|   } else { | ||||
|     mouse_char = ' '; | ||||
|   } | ||||
|   | ||||
							
								
								
									
										236
									
								
								src/nvim/move.c
									
									
									
									
									
								
							
							
						
						
									
										236
									
								
								src/nvim/move.c
									
									
									
									
									
								
							| @@ -83,8 +83,9 @@ static void comp_botline(win_T *wp) | ||||
|       redraw_for_cursorline(wp); | ||||
|       wp->w_valid |= (VALID_CROW|VALID_CHEIGHT); | ||||
|     } | ||||
|     if (done + n > wp->w_height) | ||||
|     if (done + n > wp->w_grid.Rows) { | ||||
|       break; | ||||
|     } | ||||
|     done += n; | ||||
|     lnum = last; | ||||
|   } | ||||
| @@ -149,9 +150,12 @@ void update_topline(void) | ||||
|   bool check_botline = false; | ||||
|   long save_so = p_so; | ||||
|  | ||||
|   // need to have w_grid.Rows/Columns updated | ||||
|   win_grid_alloc(curwin); | ||||
|  | ||||
|   // If there is no valid screen and when the window height is zero just use | ||||
|   // the cursor line. | ||||
|   if (!screen_valid(true) || curwin->w_height == 0) { | ||||
|   if (!screen_valid(true) || curwin->w_grid.Rows == 0) { | ||||
|     curwin->w_topline = curwin->w_cursor.lnum; | ||||
|     curwin->w_botline = curwin->w_topline; | ||||
|     curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; | ||||
| @@ -200,9 +204,10 @@ void update_topline(void) | ||||
|       check_topline = true; | ||||
|  | ||||
|     if (check_topline) { | ||||
|       int halfheight = curwin->w_height / 2 - 1; | ||||
|       if (halfheight < 2) | ||||
|       int halfheight = curwin->w_grid.Rows / 2 - 1; | ||||
|       if (halfheight < 2) { | ||||
|         halfheight = 2; | ||||
|       } | ||||
|       long n; | ||||
|       if (hasAnyFolding(curwin)) { | ||||
|         /* Count the number of logical lines between the cursor and | ||||
| @@ -289,20 +294,22 @@ void update_topline(void) | ||||
|            * botline - p_so (approximation of how much will be | ||||
|            * scrolled). */ | ||||
|           for (linenr_T lnum = curwin->w_cursor.lnum; | ||||
|                lnum >= curwin->w_botline - p_so; --lnum) { | ||||
|             ++line_count; | ||||
|             /* stop at end of file or when we know we are far off */ | ||||
|             if (lnum <= 0 || line_count > curwin->w_height + 1) | ||||
|                lnum >= curwin->w_botline - p_so; lnum--) { | ||||
|             line_count++; | ||||
|             // stop at end of file or when we know we are far off | ||||
|             if (lnum <= 0 || line_count > curwin->w_grid.Rows + 1) { | ||||
|               break; | ||||
|             } | ||||
|             (void)hasFolding(lnum, &lnum, NULL); | ||||
|           } | ||||
|         } else | ||||
|           line_count = curwin->w_cursor.lnum - curwin->w_botline | ||||
|                        + 1 + p_so; | ||||
|         if (line_count <= curwin->w_height + 1) | ||||
|         if (line_count <= curwin->w_grid.Rows + 1) { | ||||
|           scroll_cursor_bot(scrolljump_value(), false); | ||||
|         else | ||||
|         } else { | ||||
|           scroll_cursor_halfway(false); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -346,7 +353,7 @@ void update_topline_win(win_T* win) | ||||
|  */ | ||||
| static int scrolljump_value(void) | ||||
| { | ||||
|   long result = p_sj >= 0 ? p_sj : (curwin->w_height * -p_sj) / 100; | ||||
|   long result = p_sj >= 0 ? p_sj : (curwin->w_grid.Rows * -p_sj) / 100; | ||||
|   assert(result <= INT_MAX); | ||||
|   return (int)result; | ||||
| } | ||||
| @@ -522,6 +529,7 @@ int cursor_valid(void) | ||||
|  */ | ||||
| void validate_cursor(void) | ||||
| { | ||||
|   win_grid_alloc(curwin);  // we need to have w_grid.Rows/Columns updated | ||||
|   check_cursor_moved(curwin); | ||||
|   if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW)) | ||||
|     curs_columns(true); | ||||
| @@ -660,18 +668,19 @@ void validate_cursor_col(void) | ||||
|     colnr_T col = curwin->w_virtcol; | ||||
|     colnr_T off = curwin_col_off(); | ||||
|     col += off; | ||||
|     int width = curwin->w_width - off + curwin_col_off2(); | ||||
|     int width = curwin->w_grid.Columns - off + curwin_col_off2(); | ||||
|  | ||||
|     /* long line wrapping, adjust curwin->w_wrow */ | ||||
|     if (curwin->w_p_wrap | ||||
|         && col >= (colnr_T)curwin->w_width | ||||
|         && width > 0) | ||||
|       /* use same formula as what is used in curs_columns() */ | ||||
|       col -= ((col - curwin->w_width) / width + 1) * width; | ||||
|     if (col > (int)curwin->w_leftcol) | ||||
|     // long line wrapping, adjust curwin->w_wrow | ||||
|     if (curwin->w_p_wrap && col >= (colnr_T)curwin->w_grid.Columns | ||||
|         && width > 0) { | ||||
|       // use same formula as what is used in curs_columns() | ||||
|       col -= ((col - curwin->w_grid.Columns) / width + 1) * width; | ||||
|     } | ||||
|     if (col > (int)curwin->w_leftcol) { | ||||
|       col -= curwin->w_leftcol; | ||||
|     else | ||||
|     } else { | ||||
|       col = 0; | ||||
|     } | ||||
|     curwin->w_wcol = col; | ||||
|  | ||||
|     curwin->w_valid |= VALID_WCOL; | ||||
| @@ -760,20 +769,20 @@ void curs_columns( | ||||
|    */ | ||||
|   curwin->w_wrow = curwin->w_cline_row; | ||||
|  | ||||
|   int textwidth = curwin->w_width - extra; | ||||
|   int textwidth = curwin->w_grid.Columns - extra; | ||||
|   if (textwidth <= 0) { | ||||
|     /* No room for text, put cursor in last char of window. */ | ||||
|     curwin->w_wcol = curwin->w_width - 1; | ||||
|     curwin->w_wrow = curwin->w_height - 1; | ||||
|     // No room for text, put cursor in last char of window. | ||||
|     curwin->w_wcol = curwin->w_grid.Columns - 1; | ||||
|     curwin->w_wrow = curwin->w_grid.Rows - 1; | ||||
|   } else if (curwin->w_p_wrap | ||||
|              && curwin->w_width != 0 | ||||
|              && curwin->w_grid.Columns != 0 | ||||
|              ) { | ||||
|     width = textwidth + curwin_col_off2(); | ||||
|  | ||||
|     /* long line wrapping, adjust curwin->w_wrow */ | ||||
|     if (curwin->w_wcol >= curwin->w_width) { | ||||
|       /* this same formula is used in validate_cursor_col() */ | ||||
|       n = (curwin->w_wcol - curwin->w_width) / width + 1; | ||||
|     // long line wrapping, adjust curwin->w_wrow | ||||
|     if (curwin->w_wcol >= curwin->w_grid.Columns) { | ||||
|       // this same formula is used in validate_cursor_col() | ||||
|       n = (curwin->w_wcol - curwin->w_grid.Columns) / width + 1; | ||||
|       curwin->w_wcol -= n * width; | ||||
|       curwin->w_wrow += n; | ||||
|  | ||||
| @@ -800,7 +809,7 @@ void curs_columns( | ||||
|     assert(p_siso <= INT_MAX); | ||||
|     int off_left = startcol - curwin->w_leftcol - (int)p_siso; | ||||
|     int off_right = | ||||
|         endcol - curwin->w_leftcol - curwin->w_width + (int)p_siso + 1; | ||||
|         endcol - curwin->w_leftcol - curwin->w_grid.Columns + (int)p_siso + 1; | ||||
|     if (off_left < 0 || off_right > 0) { | ||||
|       int diff = (off_left < 0) ? -off_left: off_right; | ||||
|  | ||||
| @@ -843,17 +852,16 @@ void curs_columns( | ||||
|   prev_skipcol = curwin->w_skipcol; | ||||
|  | ||||
|   int p_lines = 0; | ||||
|   if ((curwin->w_wrow >= curwin->w_height | ||||
|   if ((curwin->w_wrow >= curwin->w_grid.Rows | ||||
|        || ((prev_skipcol > 0 | ||||
|             || curwin->w_wrow + p_so >= curwin->w_height) | ||||
|             || curwin->w_wrow + p_so >= curwin->w_grid.Rows) | ||||
|            && (p_lines = | ||||
|                  plines_win_nofill | ||||
|                    (curwin, curwin->w_cursor.lnum, false)) | ||||
|            - 1 >= curwin->w_height)) | ||||
|       && curwin->w_height != 0 | ||||
|                plines_win_nofill(curwin, curwin->w_cursor.lnum, false)) - 1 | ||||
|            >= curwin->w_grid.Rows)) | ||||
|       && curwin->w_grid.Rows != 0 | ||||
|       && curwin->w_cursor.lnum == curwin->w_topline | ||||
|       && width > 0 | ||||
|       && curwin->w_width != 0 | ||||
|       && curwin->w_grid.Columns != 0 | ||||
|       ) { | ||||
|     /* Cursor past end of screen.  Happens with a single line that does | ||||
|      * not fit on screen.  Find a skipcol to show the text around the | ||||
| @@ -875,19 +883,22 @@ void curs_columns( | ||||
|     } | ||||
|     else | ||||
|       n = p_lines; | ||||
|     if ((colnr_T)n >= curwin->w_height + curwin->w_skipcol / width) | ||||
|     if ((colnr_T)n >= curwin->w_grid.Rows + curwin->w_skipcol / width) { | ||||
|       extra += 2; | ||||
|     } | ||||
|  | ||||
|     if (extra == 3 || p_lines < p_so * 2) { | ||||
|       /* not enough room for 'scrolloff', put cursor in the middle */ | ||||
|       // not enough room for 'scrolloff', put cursor in the middle | ||||
|       n = curwin->w_virtcol / width; | ||||
|       if (n > curwin->w_height / 2) | ||||
|         n -= curwin->w_height / 2; | ||||
|       else | ||||
|       if (n > curwin->w_grid.Rows / 2) { | ||||
|         n -= curwin->w_grid.Rows / 2; | ||||
|       } else { | ||||
|         n = 0; | ||||
|       /* don't skip more than necessary */ | ||||
|       if (n > p_lines - curwin->w_height + 1) | ||||
|         n = p_lines - curwin->w_height + 1; | ||||
|       } | ||||
|       // don't skip more than necessary | ||||
|       if (n > p_lines - curwin->w_grid.Rows + 1) { | ||||
|         n = p_lines - curwin->w_grid.Rows + 1; | ||||
|       } | ||||
|       curwin->w_skipcol = n * width; | ||||
|     } else if (extra == 1) { | ||||
|       /* less then 'scrolloff' lines above, decrease skipcol */ | ||||
| @@ -900,18 +911,20 @@ void curs_columns( | ||||
|         curwin->w_skipcol -= extra * width; | ||||
|       } | ||||
|     } else if (extra == 2) { | ||||
|       /* less then 'scrolloff' lines below, increase skipcol */ | ||||
|       endcol = (n - curwin->w_height + 1) * width; | ||||
|       while (endcol > curwin->w_virtcol) | ||||
|       // less then 'scrolloff' lines below, increase skipcol | ||||
|       endcol = (n - curwin->w_grid.Rows + 1) * width; | ||||
|       while (endcol > curwin->w_virtcol) { | ||||
|         endcol -= width; | ||||
|       if (endcol > curwin->w_skipcol) | ||||
|       } | ||||
|       if (endcol > curwin->w_skipcol) { | ||||
|         curwin->w_skipcol = endcol; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     curwin->w_wrow -= curwin->w_skipcol / width; | ||||
|     if (curwin->w_wrow >= curwin->w_height) { | ||||
|       /* small window, make sure cursor is in it */ | ||||
|       extra = curwin->w_wrow - curwin->w_height + 1; | ||||
|     if (curwin->w_wrow >= curwin->w_grid.Rows) { | ||||
|       // small window, make sure cursor is in it | ||||
|       extra = curwin->w_wrow - curwin->w_grid.Rows + 1; | ||||
|       curwin->w_skipcol += extra * width; | ||||
|       curwin->w_wrow -= extra; | ||||
|     } | ||||
| @@ -953,9 +966,9 @@ scrolldown ( | ||||
|   validate_cursor();            /* w_wrow needs to be valid */ | ||||
|   while (line_count-- > 0) { | ||||
|     if (curwin->w_topfill < diff_check(curwin, curwin->w_topline) | ||||
|         && curwin->w_topfill < curwin->w_height - 1) { | ||||
|       ++curwin->w_topfill; | ||||
|       ++done; | ||||
|         && curwin->w_topfill < curwin->w_grid.Rows - 1) { | ||||
|       curwin->w_topfill++; | ||||
|       done++; | ||||
|     } else { | ||||
|       if (curwin->w_topline == 1) | ||||
|         break; | ||||
| @@ -988,15 +1001,15 @@ scrolldown ( | ||||
|    */ | ||||
|   int wrow = curwin->w_wrow; | ||||
|   if (curwin->w_p_wrap | ||||
|       && curwin->w_width != 0 | ||||
|       && curwin->w_grid.Columns != 0 | ||||
|       ) { | ||||
|     validate_virtcol(); | ||||
|     validate_cheight(); | ||||
|     wrow += curwin->w_cline_height - 1 - | ||||
|             curwin->w_virtcol / curwin->w_width; | ||||
|             curwin->w_virtcol / curwin->w_grid.Columns; | ||||
|   } | ||||
|   bool moved = false; | ||||
|   while (wrow >= curwin->w_height && curwin->w_cursor.lnum > 1) { | ||||
|   while (wrow >= curwin->w_grid.Rows && curwin->w_cursor.lnum > 1) { | ||||
|     linenr_T first; | ||||
|     if (hasFolding(curwin->w_cursor.lnum, &first, NULL)) { | ||||
|       --wrow; | ||||
| @@ -1081,14 +1094,15 @@ check_topfill ( | ||||
| { | ||||
|   if (wp->w_topfill > 0) { | ||||
|     int n = plines_win_nofill(wp, wp->w_topline, true); | ||||
|     if (wp->w_topfill + n > wp->w_height) { | ||||
|     if (wp->w_topfill + n > wp->w_grid.Rows) { | ||||
|       if (down && wp->w_topline > 1) { | ||||
|         --wp->w_topline; | ||||
|         wp->w_topfill = 0; | ||||
|       } else { | ||||
|         wp->w_topfill = wp->w_height - n; | ||||
|         if (wp->w_topfill < 0) | ||||
|         wp->w_topfill = wp->w_grid.Rows - n; | ||||
|         if (wp->w_topfill < 0) { | ||||
|           wp->w_topfill = 0; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -1101,12 +1115,13 @@ check_topfill ( | ||||
| static void max_topfill(void) | ||||
| { | ||||
|   int n = plines_nofill(curwin->w_topline); | ||||
|   if (n >= curwin->w_height) | ||||
|   if (n >= curwin->w_grid.Rows) { | ||||
|     curwin->w_topfill = 0; | ||||
|   else { | ||||
|   } else { | ||||
|     curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline); | ||||
|     if (curwin->w_topfill + n > curwin->w_height) | ||||
|       curwin->w_topfill = curwin->w_height - n; | ||||
|     if (curwin->w_topfill + n > curwin->w_grid.Rows) { | ||||
|       curwin->w_topfill = curwin->w_grid.Rows - n; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -1137,14 +1152,14 @@ void scrolldown_clamp(void) | ||||
|   else | ||||
|     end_row += plines_nofill(curwin->w_topline - 1); | ||||
|   if (curwin->w_p_wrap | ||||
|       && curwin->w_width != 0 | ||||
|       && curwin->w_grid.Columns != 0 | ||||
|       ) { | ||||
|     validate_cheight(); | ||||
|     validate_virtcol(); | ||||
|     end_row += curwin->w_cline_height - 1 - | ||||
|                curwin->w_virtcol / curwin->w_width; | ||||
|                curwin->w_virtcol / curwin->w_grid.Columns; | ||||
|   } | ||||
|   if (end_row < curwin->w_height - p_so) { | ||||
|   if (end_row < curwin->w_grid.Rows - p_so) { | ||||
|     if (can_fill) { | ||||
|       ++curwin->w_topfill; | ||||
|       check_topfill(curwin, true); | ||||
| @@ -1179,10 +1194,10 @@ void scrollup_clamp(void) | ||||
|   int start_row = curwin->w_wrow - plines_nofill(curwin->w_topline) | ||||
|               - curwin->w_topfill; | ||||
|   if (curwin->w_p_wrap | ||||
|       && curwin->w_width != 0 | ||||
|       && curwin->w_grid.Columns != 0 | ||||
|       ) { | ||||
|     validate_virtcol(); | ||||
|     start_row -= curwin->w_virtcol / curwin->w_width; | ||||
|     start_row -= curwin->w_virtcol / curwin->w_grid.Columns; | ||||
|   } | ||||
|   if (start_row >= p_so) { | ||||
|     if (curwin->w_topfill > 0) | ||||
| @@ -1336,10 +1351,12 @@ void scroll_cursor_top(int min_scroll, int always) | ||||
|       else | ||||
|         used += plines(bot); | ||||
|     } | ||||
|     if (used > curwin->w_height) | ||||
|     if (used > curwin->w_grid.Rows) { | ||||
|       break; | ||||
|     if (top < curwin->w_topline) | ||||
|     } | ||||
|     if (top < curwin->w_topline) { | ||||
|       scrolled += i; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * If scrolling is needed, scroll at least 'sj' lines. | ||||
| @@ -1359,7 +1376,7 @@ void scroll_cursor_top(int min_scroll, int always) | ||||
|    * This makes sure we get the same position when using "k" and "j" | ||||
|    * in a small window. | ||||
|    */ | ||||
|   if (used > curwin->w_height) { | ||||
|   if (used > curwin->w_grid.Rows) { | ||||
|     scroll_cursor_halfway(false); | ||||
|   } else { | ||||
|     /* | ||||
| @@ -1393,10 +1410,10 @@ void scroll_cursor_top(int min_scroll, int always) | ||||
| void set_empty_rows(win_T *wp, int used) | ||||
| { | ||||
|   wp->w_filler_rows = 0; | ||||
|   if (used == 0) | ||||
|     wp->w_empty_rows = 0;       /* single line that doesn't fit */ | ||||
|   else { | ||||
|     wp->w_empty_rows = wp->w_height - used; | ||||
|   if (used == 0) { | ||||
|     wp->w_empty_rows = 0;  // single line that doesn't fit | ||||
|   } else { | ||||
|     wp->w_empty_rows = wp->w_grid.Rows - used; | ||||
|     if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count) { | ||||
|       wp->w_filler_rows = diff_check_fill(wp, wp->w_botline); | ||||
|       if (wp->w_empty_rows > wp->w_filler_rows) | ||||
| @@ -1439,8 +1456,9 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) | ||||
|          curwin->w_topline = loff.lnum) { | ||||
|       loff.lnum = curwin->w_topline; | ||||
|       topline_back(&loff); | ||||
|       if (loff.height == MAXCOL || used + loff.height > curwin->w_height) | ||||
|       if (loff.height == MAXCOL || used + loff.height > curwin->w_grid.Rows) { | ||||
|         break; | ||||
|       } | ||||
|       used += loff.height; | ||||
|       curwin->w_topfill = loff.fill; | ||||
|     } | ||||
| @@ -1497,12 +1515,14 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) | ||||
|  | ||||
|     /* Add one line above */ | ||||
|     topline_back(&loff); | ||||
|     if (loff.height == MAXCOL) | ||||
|     if (loff.height == MAXCOL) { | ||||
|       used = MAXCOL; | ||||
|     else | ||||
|     } else { | ||||
|       used += loff.height; | ||||
|     if (used > curwin->w_height) | ||||
|     } | ||||
|     if (used > curwin->w_grid.Rows) { | ||||
|       break; | ||||
|     } | ||||
|     if (loff.lnum >= curwin->w_botline | ||||
|         && (loff.lnum > curwin->w_botline | ||||
|             || loff.fill <= fill_below_window) | ||||
| @@ -1519,8 +1539,9 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) | ||||
|       /* Add one line below */ | ||||
|       botline_forw(&boff); | ||||
|       used += boff.height; | ||||
|       if (used > curwin->w_height) | ||||
|       if (used > curwin->w_grid.Rows) { | ||||
|         break; | ||||
|       } | ||||
|       if (extra < ( | ||||
|             mouse_dragging > 0 ? mouse_dragging - 1 : | ||||
|             p_so) || scrolled < min_scroll) { | ||||
| @@ -1541,14 +1562,14 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) | ||||
|   } | ||||
|  | ||||
|   linenr_T line_count; | ||||
|   /* curwin->w_empty_rows is larger, no need to scroll */ | ||||
|   if (scrolled <= 0) | ||||
|   // curwin->w_empty_rows is larger, no need to scroll | ||||
|   if (scrolled <= 0) { | ||||
|     line_count = 0; | ||||
|   /* more than a screenfull, don't scroll but redraw */ | ||||
|   else if (used > curwin->w_height) | ||||
|   // more than a screenfull, don't scroll but redraw | ||||
|   } else if (used > curwin->w_grid.Rows) { | ||||
|     line_count = used; | ||||
|   /* scroll minimal number of lines */ | ||||
|   else { | ||||
|   // scroll minimal number of lines | ||||
|   } else { | ||||
|     line_count = 0; | ||||
|     boff.fill = curwin->w_topfill; | ||||
|     boff.lnum = curwin->w_topline - 1; | ||||
| @@ -1566,10 +1587,11 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) | ||||
|    * Scroll up if the cursor is off the bottom of the screen a bit. | ||||
|    * Otherwise put it at 1/2 of the screen. | ||||
|    */ | ||||
|   if (line_count >= curwin->w_height && line_count > min_scroll) | ||||
|   if (line_count >= curwin->w_grid.Rows && line_count > min_scroll) { | ||||
|     scroll_cursor_halfway(false); | ||||
|   else | ||||
|   } else { | ||||
|     scrollup(line_count, true); | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * If topline didn't change we need to restore w_botline and w_empty_rows | ||||
| @@ -1608,8 +1630,9 @@ void scroll_cursor_halfway(int atend) | ||||
|       if (boff.lnum < curbuf->b_ml.ml_line_count) { | ||||
|         botline_forw(&boff); | ||||
|         used += boff.height; | ||||
|         if (used > curwin->w_height) | ||||
|         if (used > curwin->w_grid.Rows) { | ||||
|           break; | ||||
|         } | ||||
|         below += boff.height; | ||||
|       } else { | ||||
|         ++below;                    /* count a "~" line */ | ||||
| @@ -1624,8 +1647,9 @@ void scroll_cursor_halfway(int atend) | ||||
|         used = MAXCOL; | ||||
|       else | ||||
|         used += loff.height; | ||||
|       if (used > curwin->w_height) | ||||
|       if (used > curwin->w_grid.Rows) { | ||||
|         break; | ||||
|       } | ||||
|       above += loff.height; | ||||
|       topline = loff.lnum; | ||||
|       topfill = loff.fill; | ||||
| @@ -1634,8 +1658,9 @@ void scroll_cursor_halfway(int atend) | ||||
|   if (!hasFolding(topline, &curwin->w_topline, NULL)) | ||||
|     curwin->w_topline = topline; | ||||
|   curwin->w_topfill = topfill; | ||||
|   if (old_topline > curwin->w_topline + curwin->w_height) | ||||
|   if (old_topline > curwin->w_topline + curwin->w_grid.Rows) { | ||||
|     curwin->w_botfill = false; | ||||
|   } | ||||
|   check_topfill(curwin, false); | ||||
|   curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); | ||||
|   curwin->w_valid |= VALID_TOPLINE; | ||||
| @@ -1662,18 +1687,20 @@ void cursor_correct(void) | ||||
|   } | ||||
|   if (curwin->w_topline == 1) { | ||||
|     above_wanted = 0; | ||||
|     int max_off = curwin->w_height / 2; | ||||
|     if (below_wanted > max_off) | ||||
|     int max_off = curwin->w_grid.Rows / 2; | ||||
|     if (below_wanted > max_off) { | ||||
|       below_wanted = max_off; | ||||
|     } | ||||
|   } | ||||
|   validate_botline(); | ||||
|   if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1 | ||||
|       && mouse_dragging == 0 | ||||
|       ) { | ||||
|     below_wanted = 0; | ||||
|     int max_off = (curwin->w_height - 1) / 2; | ||||
|     if (above_wanted > max_off) | ||||
|     int max_off = (curwin->w_grid.Rows - 1) / 2; | ||||
|     if (above_wanted > max_off) { | ||||
|       above_wanted = max_off; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* | ||||
| @@ -1844,7 +1871,7 @@ int onepage(Direction dir, long count) | ||||
|       /* Find the line just above the new topline to get the right line | ||||
|        * at the bottom of the window. */ | ||||
|       n = 0; | ||||
|       while (n <= curwin->w_height && loff.lnum >= 1) { | ||||
|       while (n <= curwin->w_grid.Rows && loff.lnum >= 1) { | ||||
|         topline_back(&loff); | ||||
|         if (loff.height == MAXCOL) | ||||
|           n = MAXCOL; | ||||
| @@ -1935,7 +1962,7 @@ int onepage(Direction dir, long count) | ||||
|  */ | ||||
| static void get_scroll_overlap(lineoff_T *lp, int dir) | ||||
| { | ||||
|   int min_height = curwin->w_height - 2; | ||||
|   int min_height = curwin->w_grid.Rows - 2; | ||||
|  | ||||
|   if (lp->fill > 0) | ||||
|     lp->height = 1; | ||||
| @@ -1989,12 +2016,13 @@ void halfpage(bool flag, linenr_T Prenum) | ||||
|   long scrolled = 0; | ||||
|   int i; | ||||
|  | ||||
|   if (Prenum) | ||||
|     curwin->w_p_scr = (Prenum > curwin->w_height) ? | ||||
|                       curwin->w_height : Prenum; | ||||
|   if (Prenum) { | ||||
|     curwin->w_p_scr = (Prenum > curwin->w_grid.Rows) ? curwin->w_grid.Rows | ||||
|                                                      : Prenum; | ||||
|   } | ||||
|   assert(curwin->w_p_scr <= INT_MAX); | ||||
|   int n = curwin->w_p_scr <= curwin->w_height ? (int)curwin->w_p_scr | ||||
|                                               : curwin->w_height; | ||||
|   int n = curwin->w_p_scr <= curwin->w_grid.Rows ? (int)curwin->w_p_scr | ||||
|                                               : curwin->w_grid.Rows; | ||||
|  | ||||
|   update_topline(); | ||||
|   validate_botline(); | ||||
|   | ||||
| @@ -3466,10 +3466,10 @@ static void display_showcmd(void) | ||||
|   int len; | ||||
|  | ||||
|   len = (int)STRLEN(showcmd_buf); | ||||
|   if (len == 0) | ||||
|   if (len == 0) { | ||||
|     showcmd_is_clear = true; | ||||
|   else { | ||||
|     screen_puts(showcmd_buf, (int)Rows - 1, sc_col, 0); | ||||
|   } else { | ||||
|     grid_puts(&default_grid, showcmd_buf, (int)Rows - 1, sc_col, 0); | ||||
|     showcmd_is_clear = false; | ||||
|   } | ||||
|  | ||||
| @@ -3477,7 +3477,8 @@ static void display_showcmd(void) | ||||
|    * clear the rest of an old message by outputting up to SHOWCMD_COLS | ||||
|    * spaces | ||||
|    */ | ||||
|   screen_puts((char_u *)"          " + len, (int)Rows - 1, sc_col + len, 0); | ||||
|   grid_puts(&default_grid, (char_u *)"          " + len, (int)Rows - 1, | ||||
|             sc_col + len, 0); | ||||
|  | ||||
|   setcursor();              /* put cursor back where it belongs */ | ||||
| } | ||||
| @@ -3905,18 +3906,16 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) | ||||
|  | ||||
|   col_off1 = curwin_col_off(); | ||||
|   col_off2 = col_off1 - curwin_col_off2(); | ||||
|   width1 = curwin->w_width - col_off1; | ||||
|   width2 = curwin->w_width - col_off2; | ||||
|   width1 = curwin->w_grid.Columns - col_off1; | ||||
|   width2 = curwin->w_grid.Columns - col_off2; | ||||
|  | ||||
|   if (width2 == 0) { | ||||
|     width2 = 1;  // Avoid divide by zero. | ||||
|   } | ||||
|  | ||||
|   if (curwin->w_width != 0) { | ||||
|     /* | ||||
|      * Instead of sticking at the last character of the buffer line we | ||||
|      * try to stick in the last column of the screen. | ||||
|      */ | ||||
|   if (curwin->w_grid.Columns != 0) { | ||||
|     // Instead of sticking at the last character of the buffer line we | ||||
|     // try to stick in the last column of the screen. | ||||
|     if (curwin->w_curswant == MAXCOL) { | ||||
|       atend = true; | ||||
|       validate_virtcol(); | ||||
| @@ -4255,7 +4254,7 @@ dozet: | ||||
|  | ||||
|   /* "zH" - scroll screen right half-page */ | ||||
|   case 'H': | ||||
|     cap->count1 *= curwin->w_width / 2; | ||||
|     cap->count1 *= curwin->w_grid.Columns / 2; | ||||
|     FALLTHROUGH; | ||||
|  | ||||
|   /* "zh" - scroll screen to the right */ | ||||
| @@ -4270,8 +4269,8 @@ dozet: | ||||
|     } | ||||
|     break; | ||||
|  | ||||
|   /* "zL" - scroll screen left half-page */ | ||||
|   case 'L':   cap->count1 *= curwin->w_width / 2; | ||||
|   // "zL" - scroll screen left half-page | ||||
|   case 'L':   cap->count1 *= curwin->w_grid.Columns / 2; | ||||
|     FALLTHROUGH; | ||||
|  | ||||
|   /* "zl" - scroll screen to the left */ | ||||
| @@ -4307,11 +4306,12 @@ dozet: | ||||
|         col = 0;                        /* like the cursor is in col 0 */ | ||||
|       else | ||||
|         getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col); | ||||
|       n = curwin->w_width - curwin_col_off(); | ||||
|       if (col + l_p_siso < n) | ||||
|       n = curwin->w_grid.Columns - curwin_col_off(); | ||||
|       if (col + l_p_siso < n) { | ||||
|         col = 0; | ||||
|       else | ||||
|       } else { | ||||
|         col = col + l_p_siso - n + 1; | ||||
|       } | ||||
|       if (curwin->w_leftcol != col) { | ||||
|         curwin->w_leftcol = col; | ||||
|         redraw_later(NOT_VALID); | ||||
| @@ -5016,11 +5016,11 @@ static void nv_scroll(cmdarg_T *cap) | ||||
|       /* Don't count filler lines above the window. */ | ||||
|       used -= diff_check_fill(curwin, curwin->w_topline) | ||||
|               - curwin->w_topfill; | ||||
|       validate_botline();           /* make sure w_empty_rows is valid */ | ||||
|       half = (curwin->w_height - curwin->w_empty_rows + 1) / 2; | ||||
|       for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; ++n) { | ||||
|         /* Count half he number of filler lines to be "below this | ||||
|          * line" and half to be "above the next line". */ | ||||
|       validate_botline();  // make sure w_empty_rows is valid | ||||
|       half = (curwin->w_grid.Rows - curwin->w_empty_rows + 1) / 2; | ||||
|       for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; n++) { | ||||
|         // Count half he number of filler lines to be "below this | ||||
|         // line" and half to be "above the next line". | ||||
|         if (n > 0 && used + diff_check_fill(curwin, curwin->w_topline | ||||
|                 + n) / 2 >= half) { | ||||
|           --n; | ||||
| @@ -5032,9 +5032,10 @@ static void nv_scroll(cmdarg_T *cap) | ||||
|         if (hasFolding(curwin->w_topline + n, NULL, &lnum)) | ||||
|           n = lnum - curwin->w_topline; | ||||
|       } | ||||
|       if (n > 0 && used > curwin->w_height) | ||||
|         --n; | ||||
|     } else { /* (cap->cmdchar == 'H') */ | ||||
|       if (n > 0 && used > curwin->w_grid.Rows) { | ||||
|         n--; | ||||
|       } | ||||
|     } else {  // (cap->cmdchar == 'H') | ||||
|       n = cap->count1 - 1; | ||||
|       if (hasAnyFolding(curwin)) { | ||||
|         /* Count a fold for one screen line. */ | ||||
| @@ -6747,9 +6748,9 @@ static void nv_g_cmd(cmdarg_T *cap) | ||||
|     oap->motion_type = kMTCharWise; | ||||
|     oap->inclusive = false; | ||||
|     if (curwin->w_p_wrap | ||||
|         && curwin->w_width != 0 | ||||
|         && curwin->w_grid.Columns != 0 | ||||
|         ) { | ||||
|       int width1 = curwin->w_width - curwin_col_off(); | ||||
|       int width1 = curwin->w_grid.Columns - curwin_col_off(); | ||||
|       int width2 = width1 + curwin_col_off2(); | ||||
|  | ||||
|       validate_virtcol(); | ||||
| @@ -6761,10 +6762,11 @@ static void nv_g_cmd(cmdarg_T *cap) | ||||
|     /* Go to the middle of the screen line.  When 'number' or | ||||
|      * 'relativenumber' is on and lines are wrapping the middle can be more | ||||
|      * to the left. */ | ||||
|     if (cap->nchar == 'm') | ||||
|       i += (curwin->w_width - curwin_col_off() | ||||
|     if (cap->nchar == 'm') { | ||||
|       i += (curwin->w_grid.Columns - curwin_col_off() | ||||
|             + ((curwin->w_p_wrap && i > 0) | ||||
|                ? curwin_col_off2() : 0)) / 2; | ||||
|     } | ||||
|     coladvance((colnr_T)i); | ||||
|     if (flag) { | ||||
|       do | ||||
| @@ -6808,11 +6810,11 @@ static void nv_g_cmd(cmdarg_T *cap) | ||||
|     oap->motion_type = kMTCharWise; | ||||
|     oap->inclusive = true; | ||||
|     if (curwin->w_p_wrap | ||||
|         && curwin->w_width != 0 | ||||
|         && curwin->w_grid.Columns != 0 | ||||
|         ) { | ||||
|       curwin->w_curswant = MAXCOL;              /* so we stay at the end */ | ||||
|       if (cap->count1 == 1) { | ||||
|         int width1 = curwin->w_width - col_off; | ||||
|         int width1 = curwin->w_grid.Columns - col_off; | ||||
|         int width2 = width1 + curwin_col_off2(); | ||||
|  | ||||
|         validate_virtcol(); | ||||
| @@ -6838,7 +6840,7 @@ static void nv_g_cmd(cmdarg_T *cap) | ||||
|       } else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false) | ||||
|         clearopbeep(oap); | ||||
|     } else { | ||||
|       i = curwin->w_leftcol + curwin->w_width - col_off - 1; | ||||
|       i = curwin->w_leftcol + curwin->w_grid.Columns - col_off - 1; | ||||
|       coladvance((colnr_T)i); | ||||
|  | ||||
|       /* Make sure we stick in this column. */ | ||||
| @@ -7953,7 +7955,7 @@ static void get_op_vcol( | ||||
|   colnr_T end; | ||||
|  | ||||
|   if (VIsual_mode != Ctrl_V | ||||
|       || (!initial && oap->end.col < curwin->w_width)) { | ||||
|       || (!initial && oap->end.col < curwin->w_grid.Columns)) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -68,12 +68,12 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed) | ||||
|   int kind_width; | ||||
|   int extra_width; | ||||
|   int i; | ||||
|   int row; | ||||
|   int context_lines; | ||||
|   int col; | ||||
|   int above_row; | ||||
|   int below_row; | ||||
|   int redo_count = 0; | ||||
|   int row; | ||||
|   int col; | ||||
|  | ||||
|   if (!pum_is_visible) { | ||||
|     // To keep the code simple, we only allow changing the | ||||
| @@ -90,13 +90,20 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed) | ||||
|     below_row = cmdline_row; | ||||
|  | ||||
|     // anchor position: the start of the completed word | ||||
|     row = curwin->w_wrow + curwin->w_winrow; | ||||
|     row = curwin->w_wrow; | ||||
|     if (curwin->w_p_rl) { | ||||
|       col = curwin->w_wincol + curwin->w_width - curwin->w_wcol - 1; | ||||
|       col = curwin->w_width - curwin->w_wcol - 1; | ||||
|     } else { | ||||
|       col = curwin->w_wincol + curwin->w_wcol; | ||||
|     } | ||||
|  | ||||
|     int grid = (int)curwin->w_grid.handle; | ||||
|     if (!ui_is_external(kUIMultigrid)) { | ||||
|       grid = (int)default_grid.handle; | ||||
|       row += curwin->w_winrow; | ||||
|       col += curwin->w_wincol; | ||||
|     } | ||||
|  | ||||
|     if (pum_external) { | ||||
|       if (array_changed) { | ||||
|         Array arr = ARRAY_DICT_INIT; | ||||
| @@ -108,7 +115,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed) | ||||
|           ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_info))); | ||||
|           ADD(arr, ARRAY_OBJ(item)); | ||||
|         } | ||||
|         ui_call_popupmenu_show(arr, selected, row, col); | ||||
|         ui_call_popupmenu_show(arr, selected, row, col, grid); | ||||
|       } else { | ||||
|         ui_call_popupmenu_select(selected); | ||||
|       } | ||||
| @@ -351,10 +358,10 @@ void pum_redraw(void) | ||||
|     // prepend a space if there is room | ||||
|     if (curwin->w_p_rl) { | ||||
|       if (pum_col < curwin->w_wincol + curwin->w_width - 1) { | ||||
|         screen_putchar(' ', row, pum_col + 1, attr); | ||||
|         grid_putchar(&default_grid, ' ', row, pum_col + 1, attr); | ||||
|       } | ||||
|     } else if (pum_col > 0) { | ||||
|       screen_putchar(' ', row, pum_col - 1, attr); | ||||
|       grid_putchar(&default_grid, ' ', row, pum_col - 1, attr); | ||||
|     } | ||||
|  | ||||
|     // Display each entry, use two spaces for a Tab. | ||||
| @@ -416,12 +423,13 @@ void pum_redraw(void) | ||||
|                   size++; | ||||
|                 } | ||||
|               } | ||||
|               screen_puts_len(rt, (int)STRLEN(rt), row, col - size + 1, attr); | ||||
|               grid_puts_len(&default_grid, rt, (int)STRLEN(rt), row, | ||||
|                             col - size + 1, attr); | ||||
|               xfree(rt_start); | ||||
|               xfree(st); | ||||
|               col -= width; | ||||
|             } else { | ||||
|               screen_puts_len(st, (int)STRLEN(st), row, col, attr); | ||||
|               grid_puts_len(&default_grid, st, (int)STRLEN(st), row, col, attr); | ||||
|               xfree(st); | ||||
|               col += width; | ||||
|             } | ||||
| @@ -432,10 +440,11 @@ void pum_redraw(void) | ||||
|  | ||||
|             // Display two spaces for a Tab. | ||||
|             if (curwin->w_p_rl) { | ||||
|               screen_puts_len((char_u *)"  ", 2, row, col - 1, attr); | ||||
|               grid_puts_len(&default_grid, (char_u *)"  ", 2, row, col - 1, | ||||
|                             attr); | ||||
|               col -= 2; | ||||
|             } else { | ||||
|               screen_puts_len((char_u *)"  ", 2, row, col, attr); | ||||
|               grid_puts_len(&default_grid, (char_u *)"  ", 2, row, col, attr); | ||||
|               col += 2; | ||||
|             } | ||||
|             totwidth += 2; | ||||
| @@ -466,36 +475,37 @@ void pum_redraw(void) | ||||
|       } | ||||
|  | ||||
|       if (curwin->w_p_rl) { | ||||
|         screen_fill(row, row + 1, pum_col - pum_base_width - n + 1, | ||||
|                     col + 1, ' ', ' ', attr); | ||||
|         grid_fill(&default_grid, row, row + 1, pum_col - pum_base_width - n + 1, | ||||
|                   col + 1, ' ', ' ', attr); | ||||
|         col = pum_col - pum_base_width - n + 1; | ||||
|       } else { | ||||
|         screen_fill(row, row + 1, col, pum_col + pum_base_width + n, | ||||
|                     ' ', ' ', attr); | ||||
|         grid_fill(&default_grid, row, row + 1, col, | ||||
|                   pum_col + pum_base_width + n, ' ', ' ', attr); | ||||
|         col = pum_col + pum_base_width + n; | ||||
|       } | ||||
|       totwidth = pum_base_width + n; | ||||
|     } | ||||
|  | ||||
|     if (curwin->w_p_rl) { | ||||
|       screen_fill(row, row + 1, pum_col - pum_width + 1, col + 1, ' ', ' ', | ||||
|                   attr); | ||||
|       grid_fill(&default_grid, row, row + 1, pum_col - pum_width + 1, col + 1, | ||||
|                 ' ', ' ', attr); | ||||
|     } else { | ||||
|       screen_fill(row, row + 1, col, pum_col + pum_width, ' ', ' ', attr); | ||||
|       grid_fill(&default_grid, row, row + 1, col, pum_col + pum_width, ' ', ' ', | ||||
|                 attr); | ||||
|     } | ||||
|  | ||||
|     if (pum_scrollbar > 0) { | ||||
|       if (curwin->w_p_rl) { | ||||
|         screen_putchar(' ', row, pum_col - pum_width, | ||||
|                        i >= thumb_pos && i < thumb_pos + thumb_heigth | ||||
|                        ? attr_thumb : attr_scroll); | ||||
|         grid_putchar(&default_grid, ' ', row, pum_col - pum_width, | ||||
|                      i >= thumb_pos && i < thumb_pos + thumb_heigth | ||||
|                      ? attr_thumb : attr_scroll); | ||||
|       } else { | ||||
|         screen_putchar(' ', row, pum_col + pum_width, | ||||
|                        i >= thumb_pos && i < thumb_pos + thumb_heigth | ||||
|                        ? attr_thumb : attr_scroll); | ||||
|         grid_putchar(&default_grid, ' ', row, pum_col + pum_width, | ||||
|                      i >= thumb_pos && i < thumb_pos + thumb_heigth | ||||
|                      ? attr_thumb : attr_scroll); | ||||
|       } | ||||
|     } | ||||
|     screen_puts_line_flush(false); | ||||
|     grid_puts_line_flush(&default_grid, false); | ||||
|     row++; | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										1452
									
								
								src/nvim/screen.c
									
									
									
									
									
								
							
							
						
						
									
										1452
									
								
								src/nvim/screen.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -5,6 +5,7 @@ | ||||
|  | ||||
| #include "nvim/types.h" | ||||
| #include "nvim/buffer_defs.h" | ||||
| #include "nvim/grid_defs.h" | ||||
| #include "nvim/pos.h" | ||||
|  | ||||
| /* | ||||
| @@ -20,6 +21,18 @@ | ||||
| #define NOT_VALID               40  /* buffer needs complete redraw */ | ||||
| #define CLEAR                   50  /* screen messed up, clear it */ | ||||
|  | ||||
| /// By default, all widows are draw on a single rectangular grid, represented by | ||||
| /// this ScreenGrid instance. In multigrid mode each window will have its own | ||||
| /// grid, then this is only used for global screen elements that hasn't been | ||||
| /// externalized. | ||||
| /// | ||||
| /// Note: before the screen is initialized and when out of memory these can be | ||||
| /// NULL. | ||||
| EXTERN ScreenGrid default_grid INIT(= { 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0, | ||||
|                                         0, 0, 0 }); | ||||
|  | ||||
| #define DEFAULT_GRID_HANDLE 1  // handle for the default_grid | ||||
|  | ||||
| /// Status line click definition | ||||
| typedef struct { | ||||
|   enum { | ||||
|   | ||||
| @@ -2166,9 +2166,10 @@ showmatch( | ||||
|     if (!curwin->w_p_wrap) { | ||||
|       getvcol(curwin, lpos, NULL, &vcol, NULL); | ||||
|     } | ||||
|     if (curwin->w_p_wrap || (vcol >= curwin->w_leftcol | ||||
|                              && vcol < curwin->w_leftcol + curwin->w_width)) { | ||||
|       mpos = *lpos;          /* save the pos, update_screen() may change it */ | ||||
|     if (curwin->w_p_wrap | ||||
|         || (vcol >= curwin->w_leftcol | ||||
|             && vcol < curwin->w_leftcol + curwin->w_grid.Columns)) { | ||||
|       mpos = *lpos;  // save the pos, update_screen() may change it | ||||
|       save_cursor = curwin->w_cursor; | ||||
|       save_so = p_so; | ||||
|       save_siso = p_siso; | ||||
|   | ||||
| @@ -13,6 +13,9 @@ typedef unsigned char char_u; | ||||
| // Can hold one decoded UTF-8 character. | ||||
| typedef uint32_t u8char_T; | ||||
|  | ||||
| // Opaque handle used by API clients to refer to various objects in vim | ||||
| typedef int handle_T; | ||||
|  | ||||
| typedef struct expand expand_T; | ||||
|  | ||||
| #endif  // NVIM_TYPES_H | ||||
|   | ||||
| @@ -57,6 +57,7 @@ static int busy = 0; | ||||
| static int mode_idx = SHAPE_IDX_N; | ||||
| static bool pending_mode_info_update = false; | ||||
| static bool pending_mode_update = false; | ||||
| static handle_T cursor_grid_handle = DEFAULT_GRID_HANDLE; | ||||
|  | ||||
| #if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL | ||||
| # define UI_LOG(funname, ...) | ||||
| @@ -196,13 +197,6 @@ void ui_refresh(void) | ||||
|   row = col = 0; | ||||
|   pending_cursor_update = true; | ||||
|  | ||||
|   ui_default_colors_set(); | ||||
|  | ||||
|   int save_p_lz = p_lz; | ||||
|   p_lz = false;  // convince redrawing() to return true ... | ||||
|   screen_resize(width, height); | ||||
|   p_lz = save_p_lz; | ||||
|  | ||||
|   for (UIExtension i = 0; (int)i < kUIExtCount; i++) { | ||||
|     ui_ext[i] = ext_widgets[i]; | ||||
|     if (i < kUIGlobalCount) { | ||||
| @@ -210,6 +204,14 @@ void ui_refresh(void) | ||||
|                          BOOLEAN_OBJ(ext_widgets[i])); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ui_default_colors_set(); | ||||
|  | ||||
|   int save_p_lz = p_lz; | ||||
|   p_lz = false;  // convince redrawing() to return true ... | ||||
|   screen_resize(width, height); | ||||
|   p_lz = save_p_lz; | ||||
|  | ||||
|   ui_mode_info_set(); | ||||
|   pending_mode_update = true; | ||||
|   ui_cursor_shape(); | ||||
| @@ -315,12 +317,18 @@ void ui_set_ext_option(UI *ui, UIExtension ext, bool active) | ||||
|   } | ||||
| } | ||||
|  | ||||
| void ui_line(int row, int startcol, int endcol, int clearcol, int clearattr, | ||||
|              bool wrap) | ||||
| void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol, | ||||
|              int clearattr, bool wrap) | ||||
| { | ||||
|   size_t off = LineOffset[row]+(size_t)startcol; | ||||
|   UI_CALL(raw_line, 1, row, startcol, endcol, clearcol, clearattr, wrap, | ||||
|           (const schar_T *)ScreenLines+off, (const sattr_T *)ScreenAttrs+off); | ||||
|   size_t off = grid->line_offset[row] + (size_t)startcol; | ||||
|   int row_off = ui_is_external(kUIMultigrid) ? 0 : grid->row_offset; | ||||
|   int col_off = ui_is_external(kUIMultigrid) ? 0 : grid->col_offset; | ||||
|  | ||||
|   UI_CALL(raw_line, grid->handle, row_off + row, col_off + startcol, | ||||
|           col_off + endcol, col_off + clearcol, clearattr, wrap, | ||||
|           (const schar_T *)grid->chars + off, | ||||
|           (const sattr_T *)grid->attrs + off); | ||||
|  | ||||
|   if (p_wd) {  // 'writedelay': flush & delay each time. | ||||
|     int old_row = row, old_col = col; | ||||
|     // If'writedelay is active, we set the cursor to highlight what was drawn | ||||
| @@ -334,11 +342,23 @@ void ui_line(int row, int startcol, int endcol, int clearcol, int clearattr, | ||||
|  | ||||
| void ui_cursor_goto(int new_row, int new_col) | ||||
| { | ||||
|   if (new_row == row && new_col == col) { | ||||
|   ui_grid_cursor_goto(&default_grid, new_row, new_col); | ||||
| } | ||||
|  | ||||
| void ui_grid_cursor_goto(ScreenGrid *grid, int new_row, int new_col) | ||||
| { | ||||
|   new_row += ui_is_external(kUIMultigrid) ? 0 : grid->row_offset; | ||||
|   new_col += ui_is_external(kUIMultigrid) ? 0 : grid->col_offset; | ||||
|   int handle = ui_is_external(kUIMultigrid) ? grid->handle | ||||
|                                             : DEFAULT_GRID_HANDLE; | ||||
|  | ||||
|   if (new_row == row && new_col == col && handle == cursor_grid_handle) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   row = new_row; | ||||
|   col = new_col; | ||||
|   cursor_grid_handle = handle; | ||||
|   pending_cursor_update = true; | ||||
| } | ||||
|  | ||||
| @@ -360,8 +380,9 @@ int ui_current_col(void) | ||||
| void ui_flush(void) | ||||
| { | ||||
|   cmdline_ui_flush(); | ||||
|   win_ui_flush(); | ||||
|   if (pending_cursor_update) { | ||||
|     ui_call_grid_cursor_goto(1, row, col); | ||||
|     ui_call_grid_cursor_goto(cursor_grid_handle, row, col); | ||||
|     pending_cursor_update = false; | ||||
|   } | ||||
|   if (pending_mode_info_update) { | ||||
| @@ -421,3 +442,22 @@ Array ui_array(void) | ||||
|   } | ||||
|   return all_uis; | ||||
| } | ||||
|  | ||||
| void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error) | ||||
| { | ||||
|   if (grid_handle == DEFAULT_GRID_HANDLE) { | ||||
|     screen_resize(width, height); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   win_T *wp = get_win_by_grid_handle(grid_handle); | ||||
|   if (wp == NULL) { | ||||
|     api_set_error(error, kErrorTypeValidation, | ||||
|                   "No window with the given handle"); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   wp->w_grid.requested_rows = (int)height; | ||||
|   wp->w_grid.requested_cols = (int)width; | ||||
|   redraw_win_later(wp, SOME_VALID); | ||||
| } | ||||
|   | ||||
| @@ -16,6 +16,7 @@ typedef enum { | ||||
|   kUIWildmenu, | ||||
| #define kUIGlobalCount (kUIWildmenu+1) | ||||
|   kUILinegrid, | ||||
|   kUIMultigrid, | ||||
|   kUIHlState, | ||||
|   kUIExtCount, | ||||
| } UIExtension; | ||||
| @@ -26,6 +27,7 @@ EXTERN const char *ui_ext_names[] INIT(= { | ||||
|   "ext_tabline", | ||||
|   "ext_wildmenu", | ||||
|   "ext_linegrid", | ||||
|   "ext_multigrid", | ||||
|   "ext_hlstate", | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -2271,8 +2271,8 @@ static void do_intro_line(long row, char_u *mesg, int attr) | ||||
|       } | ||||
|     } | ||||
|     assert(row <= INT_MAX && col <= INT_MAX); | ||||
|     screen_puts_len(p, l, (int)row, (int)col, | ||||
|                     *p == '<' ? HL_ATTR(HLF_8) : attr); | ||||
|     grid_puts_len(&default_grid, p, l, (int)row, (int)col, | ||||
|                   *p == '<' ? HL_ATTR(HLF_8) : attr); | ||||
|     col += clen; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -997,6 +997,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) | ||||
|     p_wh = i; | ||||
|   } | ||||
|  | ||||
|   // Send the window positions to the UI | ||||
|   oldwin->w_pos_changed = true; | ||||
|  | ||||
|   return OK; | ||||
| } | ||||
|  | ||||
| @@ -1341,6 +1344,9 @@ static void win_rotate(int upwards, int count) | ||||
|     (void)win_comp_pos(); | ||||
|   } | ||||
|  | ||||
|   wp1->w_pos_changed = true; | ||||
|   wp2->w_pos_changed = true; | ||||
|  | ||||
|   redraw_all_later(NOT_VALID); | ||||
| } | ||||
|  | ||||
| @@ -1423,6 +1429,9 @@ void win_move_after(win_T *win1, win_T *win2) | ||||
|     redraw_later(NOT_VALID); | ||||
|   } | ||||
|   win_enter(win1, false); | ||||
|  | ||||
|   win1->w_pos_changed = true; | ||||
|   win2->w_pos_changed = true; | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -2059,6 +2068,7 @@ int win_close(win_T *win, bool free_buf) | ||||
|   if (help_window) | ||||
|     restore_snapshot(SNAP_HELP_IDX, close_curwin); | ||||
|  | ||||
|   curwin->w_pos_changed = true; | ||||
|   redraw_all_later(NOT_VALID); | ||||
|   return OK; | ||||
| } | ||||
| @@ -3108,6 +3118,10 @@ int win_new_tabpage(int after, char_u *filename) | ||||
|  | ||||
|     redraw_all_later(NOT_VALID); | ||||
|  | ||||
|     if (ui_is_external(kUIMultigrid)) { | ||||
|         tabpage_check_windows(tp); | ||||
|     } | ||||
|  | ||||
|     apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); | ||||
|     apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); | ||||
|     apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf); | ||||
| @@ -3299,11 +3313,16 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au | ||||
|   int old_off = tp->tp_firstwin->w_winrow; | ||||
|   win_T       *next_prevwin = tp->tp_prevwin; | ||||
|  | ||||
|   tabpage_T *old_curtab = curtab; | ||||
|   curtab = tp; | ||||
|   firstwin = tp->tp_firstwin; | ||||
|   lastwin = tp->tp_lastwin; | ||||
|   topframe = tp->tp_topframe; | ||||
|  | ||||
|   if (old_curtab != curtab && ui_is_external(kUIMultigrid)) { | ||||
|      tabpage_check_windows(old_curtab); | ||||
|   } | ||||
|  | ||||
|   /* We would like doing the TabEnter event first, but we don't have a | ||||
|    * valid current window yet, which may break some commands. | ||||
|    * This triggers autocommands, thus may make "tp" invalid. */ | ||||
| @@ -3339,6 +3358,20 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au | ||||
|   must_redraw = NOT_VALID; | ||||
| } | ||||
|  | ||||
| /// called when changing current tabpage from old_curtab to curtab | ||||
| static void tabpage_check_windows(tabpage_T *old_curtab) | ||||
| { | ||||
|   win_T *next_wp; | ||||
|   for (win_T *wp = old_curtab->tp_firstwin; wp; wp = next_wp) { | ||||
|     next_wp = wp->w_next; | ||||
|     wp->w_pos_changed = true; | ||||
|   } | ||||
|  | ||||
|   for (win_T *wp = firstwin; wp; wp = wp->w_next) { | ||||
|     wp->w_pos_changed = true; | ||||
|   } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Go to tab page "n".  For ":tab N" and "Ngt". | ||||
|  * When "n" is 9999 go to the last tab page. | ||||
| @@ -3860,6 +3893,8 @@ static win_T *win_alloc(win_T *after, int hidden) | ||||
|   new_wp->handle = ++last_win_id; | ||||
|   handle_register_window(new_wp); | ||||
|  | ||||
|   grid_assign_handle(&new_wp->w_grid); | ||||
|  | ||||
|   // Init w: variables. | ||||
|   new_wp->w_vars = tv_dict_alloc(); | ||||
|   init_var_dict(new_wp->w_vars, &new_wp->w_winvar, VAR_SCOPE); | ||||
| @@ -3958,6 +3993,8 @@ win_free ( | ||||
|  | ||||
|   xfree(wp->w_p_cc_cols); | ||||
|  | ||||
|   win_free_grid(wp, false); | ||||
|  | ||||
|   if (wp != aucmd_win) | ||||
|     win_remove(wp, tp); | ||||
|   if (autocmd_busy) { | ||||
| @@ -3970,6 +4007,20 @@ win_free ( | ||||
|   unblock_autocmds(); | ||||
| } | ||||
|  | ||||
| void win_free_grid(win_T *wp, bool reinit) | ||||
| { | ||||
|   if (wp->w_grid.handle != 0 && ui_is_external(kUIMultigrid)) { | ||||
|     ui_call_grid_destroy(wp->w_grid.handle); | ||||
|     wp->w_grid.handle = 0; | ||||
|   } | ||||
|   grid_free(&wp->w_grid); | ||||
|   if (reinit) { | ||||
|     // if a float is turned into a split and back into a float, the grid | ||||
|     // data structure will be reused | ||||
|     memset(&wp->w_grid, 0, sizeof(wp->w_grid)); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Append window "wp" in the window list after window "after". | ||||
|  */ | ||||
| @@ -4071,8 +4122,8 @@ static void frame_remove(frame_T *frp) | ||||
| void win_alloc_lines(win_T *wp) | ||||
| { | ||||
|   wp->w_lines_valid = 0; | ||||
|   assert(Rows >= 0); | ||||
|   wp->w_lines = xcalloc(Rows, sizeof(wline_T)); | ||||
|   assert(wp->w_grid.Rows >= 0); | ||||
|   wp->w_lines = xcalloc(MAX(wp->w_grid.Rows + 1, Rows), sizeof(wline_T)); | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -4202,7 +4253,8 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col) | ||||
|       wp->w_winrow = *row; | ||||
|       wp->w_wincol = *col; | ||||
|       redraw_win_later(wp, NOT_VALID); | ||||
|       wp->w_redr_status = TRUE; | ||||
|       wp->w_redr_status = true; | ||||
|       wp->w_pos_changed = true; | ||||
|     } | ||||
|     *row += wp->w_height + wp->w_status_height; | ||||
|     *col += wp->w_width + wp->w_vsep_width; | ||||
| @@ -4255,8 +4307,9 @@ void win_setheight_win(int height, win_T *win) | ||||
|    * If there is extra space created between the last window and the command | ||||
|    * line, clear it. | ||||
|    */ | ||||
|   if (full_screen && msg_scrolled == 0 && row < cmdline_row) | ||||
|     screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); | ||||
|   if (full_screen && msg_scrolled == 0 && row < cmdline_row) { | ||||
|     grid_fill(&default_grid, row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); | ||||
|   } | ||||
|   cmdline_row = row; | ||||
|   msg_row = row; | ||||
|   msg_col = 0; | ||||
| @@ -4706,7 +4759,7 @@ void win_drag_status_line(win_T *dragwin, int offset) | ||||
|       fr = fr->fr_next; | ||||
|   } | ||||
|   row = win_comp_pos(); | ||||
|   screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); | ||||
|   grid_fill(&default_grid, row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); | ||||
|   cmdline_row = row; | ||||
|   p_ch = Rows - cmdline_row; | ||||
|   if (p_ch < 1) | ||||
| @@ -4866,6 +4919,8 @@ void win_new_height(win_T *wp, int height) | ||||
|   if (!exiting) { | ||||
|     scroll_to_fraction(wp, prev_height); | ||||
|   } | ||||
|  | ||||
|   wp->w_pos_changed = true; | ||||
| } | ||||
|  | ||||
| void scroll_to_fraction(win_T *wp, int prev_height) | ||||
| @@ -4996,6 +5051,7 @@ void win_new_width(win_T *wp, int width) | ||||
|                       0); | ||||
|     } | ||||
|   } | ||||
|   wp->w_pos_changed = true; | ||||
| } | ||||
|  | ||||
| void win_comp_scroll(win_T *wp) | ||||
| @@ -5052,10 +5108,11 @@ void command_height(void) | ||||
|       /* Recompute window positions. */ | ||||
|       (void)win_comp_pos(); | ||||
|  | ||||
|       /* clear the lines added to cmdline */ | ||||
|       if (full_screen) | ||||
|         screen_fill(cmdline_row, (int)Rows, 0, | ||||
|             (int)Columns, ' ', ' ', 0); | ||||
|       // clear the lines added to cmdline | ||||
|       if (full_screen) { | ||||
|         grid_fill(&default_grid, cmdline_row, (int)Rows, 0, (int)Columns, ' ', | ||||
|                   ' ', 0); | ||||
|       } | ||||
|       msg_row = cmdline_row; | ||||
|       redraw_cmdline = TRUE; | ||||
|       return; | ||||
| @@ -6031,3 +6088,23 @@ void win_findbuf(typval_T *argvars, list_T *list) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void win_ui_flush(void) | ||||
| { | ||||
|   if (!ui_is_external(kUIMultigrid)) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   FOR_ALL_TAB_WINDOWS(tp, wp) { | ||||
|     if (wp->w_pos_changed && wp->w_grid.chars != NULL) { | ||||
|       if (tp == curtab) { | ||||
|         ui_call_win_pos(wp->w_grid.handle, wp->handle, wp->w_winrow, | ||||
|                         wp->w_wincol, wp->w_width, wp->w_height); | ||||
|       } else { | ||||
|         ui_call_win_hide(wp->w_grid.handle); | ||||
|       } | ||||
|       wp->w_pos_changed = false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1261,6 +1261,7 @@ describe('API', function() | ||||
|           ext_tabline = false, | ||||
|           ext_wildmenu = false, | ||||
|           ext_linegrid = screen._options.ext_linegrid or false, | ||||
|           ext_multigrid = false, | ||||
|           ext_hlstate=false, | ||||
|           height = 4, | ||||
|           rgb = true, | ||||
|   | ||||
| @@ -84,6 +84,10 @@ end | ||||
|  | ||||
| local session, loop_running, last_error | ||||
|  | ||||
| local function get_session() | ||||
|   return session | ||||
| end | ||||
|  | ||||
| local function set_session(s, keep) | ||||
|   if session and not keep then | ||||
|     session:close() | ||||
| @@ -164,34 +168,34 @@ local function expect_msg_seq(...) | ||||
|   error(final_error) | ||||
| end | ||||
|  | ||||
| local function call_and_stop_on_error(...) | ||||
| local function call_and_stop_on_error(lsession, ...) | ||||
|   local status, result = copcall(...)  -- luacheck: ignore | ||||
|   if not status then | ||||
|     session:stop() | ||||
|     lsession:stop() | ||||
|     last_error = result | ||||
|     return '' | ||||
|   end | ||||
|   return result | ||||
| end | ||||
|  | ||||
| local function run(request_cb, notification_cb, setup_cb, timeout) | ||||
| local function run_session(lsession, request_cb, notification_cb, setup_cb, timeout) | ||||
|   local on_request, on_notification, on_setup | ||||
|  | ||||
|   if request_cb then | ||||
|     function on_request(method, args) | ||||
|       return call_and_stop_on_error(request_cb, method, args) | ||||
|       return call_and_stop_on_error(lsession, request_cb, method, args) | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   if notification_cb then | ||||
|     function on_notification(method, args) | ||||
|       call_and_stop_on_error(notification_cb, method, args) | ||||
|       call_and_stop_on_error(lsession, notification_cb, method, args) | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   if setup_cb then | ||||
|     function on_setup() | ||||
|       call_and_stop_on_error(setup_cb) | ||||
|       call_and_stop_on_error(lsession, setup_cb) | ||||
|     end | ||||
|   end | ||||
|  | ||||
| @@ -205,6 +209,10 @@ local function run(request_cb, notification_cb, setup_cb, timeout) | ||||
|   end | ||||
| end | ||||
|  | ||||
| local function run(request_cb, notification_cb, setup_cb, timeout) | ||||
|   run_session(session, request_cb, notification_cb, setup_cb, timeout) | ||||
| end | ||||
|  | ||||
| local function stop() | ||||
|   session:stop() | ||||
| end | ||||
| @@ -677,6 +685,7 @@ local module = { | ||||
|   buffer = buffer, | ||||
|   bufmeths = bufmeths, | ||||
|   call = nvim_call, | ||||
|   create_callindex = create_callindex, | ||||
|   clear = clear, | ||||
|   command = nvim_command, | ||||
|   connect = connect, | ||||
| @@ -701,6 +710,7 @@ local module = { | ||||
|   filter = filter, | ||||
|   funcs = funcs, | ||||
|   get_pathsep = get_pathsep, | ||||
|   get_session = get_session, | ||||
|   insert = insert, | ||||
|   iswin = iswin, | ||||
|   map = map, | ||||
| @@ -732,6 +742,7 @@ local module = { | ||||
|   retry = retry, | ||||
|   rmdir = rmdir, | ||||
|   run = run, | ||||
|   run_session = run_session, | ||||
|   set_session = set_session, | ||||
|   set_shell_powershell = set_shell_powershell, | ||||
|   skip_fragile = skip_fragile, | ||||
|   | ||||
| @@ -72,10 +72,10 @@ local function screen_setup(extra_rows, command, cols) | ||||
|   if command == default_command then | ||||
|     -- Wait for "tty ready" to be printed before each test or the terminal may | ||||
|     -- still be in canonical mode (will echo characters for example). | ||||
|     local empty_line = (' '):rep(cols + 1) | ||||
|     local empty_line = (' '):rep(cols) | ||||
|     local expected = { | ||||
|       'tty ready'..(' '):rep(cols - 8), | ||||
|       '{1: }'    ..(' '):rep(cols), | ||||
|       'tty ready'..(' '):rep(cols - 9), | ||||
|       '{1: }'    ..(' '):rep(cols - 1), | ||||
|       empty_line, | ||||
|       empty_line, | ||||
|       empty_line, | ||||
| @@ -85,8 +85,8 @@ local function screen_setup(extra_rows, command, cols) | ||||
|       table.insert(expected, empty_line) | ||||
|     end | ||||
|  | ||||
|     table.insert(expected, '{3:-- TERMINAL --}' .. ((' '):rep(cols - 13))) | ||||
|     screen:expect(table.concat(expected, '\n')) | ||||
|     table.insert(expected, '{3:-- TERMINAL --}' .. ((' '):rep(cols - 14))) | ||||
|     screen:expect(table.concat(expected, '|\n')..'|') | ||||
|   else | ||||
|     -- This eval also acts as a wait(). | ||||
|     if 0 == nvim('eval', "exists('b:terminal_job_id')") then | ||||
|   | ||||
| @@ -259,10 +259,10 @@ describe('tui', function() | ||||
|     feed_data(':echo map(nvim_list_uis(), {k,v -> sort(items(v))})\013') | ||||
|     screen:expect([=[ | ||||
|       [[['ext_cmdline', v:false], ['ext_hlstate', v:fals| | ||||
|       e], ['ext_linegrid', v:true], ['ext_popupmenu', v:| | ||||
|       false], ['ext_tabline', v:false], ['ext_wildmenu',| | ||||
|        v:false], ['height', 6], ['rgb', v:false], ['widt| | ||||
|       h', 50]]]                                         | | ||||
|       e], ['ext_linegrid', v:true], ['ext_multigrid', v:| | ||||
|       false], ['ext_popupmenu', v:false], ['ext_tabline'| | ||||
|       , v:false], ['ext_wildmenu', v:false], ['height', | | ||||
|       6], ['rgb', v:false], ['width', 50]]]             | | ||||
|       {10:Press ENTER or type command to continue}{1: }          | | ||||
|       {3:-- TERMINAL --}                                    | | ||||
|     ]=]) | ||||
| @@ -401,7 +401,7 @@ describe('tui FocusGained/FocusLost', function() | ||||
|       -- Exit cmdline-mode. Redraws from timers/events are blocked during | ||||
|       -- cmdline-mode, so the buffer won't be updated until we exit cmdline-mode. | ||||
|       feed_data('\n') | ||||
|       screen:expect{any='lost'..(' '):rep(46)..'\ngained'} | ||||
|       screen:expect{any='lost'..(' '):rep(46)..'|\ngained'} | ||||
|     end) | ||||
|   end) | ||||
|  | ||||
|   | ||||
							
								
								
									
										1524
									
								
								test/functional/ui/multigrid_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1524
									
								
								test/functional/ui/multigrid_spec.lua
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -26,6 +26,7 @@ describe('ui receives option updates', function() | ||||
|       ext_wildmenu=false, | ||||
|       ext_linegrid=false, | ||||
|       ext_hlstate=false, | ||||
|       ext_multigrid=false, | ||||
|     } | ||||
|  | ||||
|     clear(...) | ||||
|   | ||||
| @@ -75,9 +75,11 @@ local global_helpers = require('test.helpers') | ||||
| local deepcopy = global_helpers.deepcopy | ||||
| local shallowcopy = global_helpers.shallowcopy | ||||
| local helpers = require('test.functional.helpers')(nil) | ||||
| local request, run, uimeths = helpers.request, helpers.run, helpers.uimeths | ||||
| local request, run_session = helpers.request, helpers.run_session | ||||
| local eq = helpers.eq | ||||
| local dedent = helpers.dedent | ||||
| local get_session = helpers.get_session | ||||
| local create_callindex = helpers.create_callindex | ||||
|  | ||||
| local inspect = require('inspect') | ||||
|  | ||||
| @@ -155,6 +157,8 @@ function Screen.new(width, height) | ||||
|     cmdline_block = {}, | ||||
|     wildmenu_items = nil, | ||||
|     wildmenu_selected = nil, | ||||
|     win_position = {}, | ||||
|     _session = nil, | ||||
|     _default_attr_ids = nil, | ||||
|     _default_attr_ignore = nil, | ||||
|     _mouse_enabled = true, | ||||
| @@ -165,11 +169,19 @@ function Screen.new(width, height) | ||||
|     _new_attrs = false, | ||||
|     _width = width, | ||||
|     _height = height, | ||||
|     _grids = {}, | ||||
|     _cursor = { | ||||
|       row = 1, col = 1 | ||||
|       grid = 1, row = 1, col = 1 | ||||
|     }, | ||||
|     _busy = false | ||||
|     _busy = false, | ||||
|   }, Screen) | ||||
|   local function ui(method, ...) | ||||
|     local status, rv = self._session:request('nvim_ui_'..method, ...) | ||||
|     if not status then | ||||
|       error(rv[2]) | ||||
|     end | ||||
|   end | ||||
|   self.uimeths = create_callindex(ui) | ||||
|   return self | ||||
| end | ||||
|  | ||||
| @@ -189,34 +201,50 @@ function Screen:set_hlstate_cterm(val) | ||||
|   self._hlstate_cterm = val | ||||
| end | ||||
|  | ||||
| function Screen:attach(options) | ||||
| function Screen:attach(options, session) | ||||
|   if session == nil then | ||||
|     session = get_session() | ||||
|   end | ||||
|   if options == nil then | ||||
|     options = {} | ||||
|   end | ||||
|   if options.ext_linegrid == nil then | ||||
|     options.ext_linegrid = true | ||||
|   end | ||||
|  | ||||
|   self._session = session | ||||
|   self._options = options | ||||
|   self._clear_attrs = (options.ext_linegrid and {{},{}}) or {} | ||||
|   self:_handle_resize(self._width, self._height) | ||||
|   uimeths.attach(self._width, self._height, options) | ||||
|   self.uimeths.attach(self._width, self._height, options) | ||||
|   if self._options.rgb == nil then | ||||
|     -- nvim defaults to rgb=true internally, | ||||
|     -- simplify test code by doing the same. | ||||
|     self._options.rgb = true | ||||
|   end | ||||
|   if self._options.ext_multigrid then | ||||
|     self._options.ext_linegrid = true | ||||
|   end | ||||
|   self._session = session | ||||
| end | ||||
|  | ||||
| function Screen:detach() | ||||
|   uimeths.detach() | ||||
|   self.uimeths.detach() | ||||
|   self._session = nil | ||||
| end | ||||
|  | ||||
| function Screen:try_resize(columns, rows) | ||||
|   uimeths.try_resize(columns, rows) | ||||
|   self._width = columns | ||||
|   self._height = rows | ||||
|   self.uimeths.try_resize(columns, rows) | ||||
| end | ||||
|  | ||||
| function Screen:try_resize_grid(grid, columns, rows) | ||||
|   self.uimeths.try_resize_grid(grid, columns, rows) | ||||
| end | ||||
|  | ||||
| function Screen:set_option(option, value) | ||||
|   uimeths.set_option(option, value) | ||||
|   self.uimeths.set_option(option, value) | ||||
|   self._options[option] = value | ||||
| end | ||||
|  | ||||
| @@ -321,7 +349,6 @@ function Screen:expect(expected, attr_ids, attr_ignore) | ||||
|     -- value. | ||||
|     grid = dedent(grid:gsub('\n[ ]+$', ''), 0) | ||||
|     for row in grid:gmatch('[^\n]+') do | ||||
|       row = row:sub(1, #row - 1) -- Last char must be the screen delimiter. | ||||
|       table.insert(expected_rows, row) | ||||
|     end | ||||
|   end | ||||
| @@ -341,19 +368,11 @@ function Screen:expect(expected, attr_ids, attr_ignore) | ||||
|       end | ||||
|     end | ||||
|  | ||||
|     if grid ~= nil and self._height ~= #expected_rows then | ||||
|       return ("Expected screen state's row count(" .. #expected_rows | ||||
|               .. ') differs from configured height(' .. self._height .. ') of Screen.') | ||||
|     end | ||||
|  | ||||
|     if self._options.ext_hlstate and self._new_attrs then | ||||
|       attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids or {}) | ||||
|     end | ||||
|  | ||||
|     local actual_rows = {} | ||||
|     for i = 1, self._height do | ||||
|       actual_rows[i] = self:_row_repr(self._rows[i], attr_state) | ||||
|     end | ||||
|     local actual_rows = self:render(not expected.any, attr_state) | ||||
|  | ||||
|     if expected.any ~= nil then | ||||
|       -- Search for `any` anywhere in the screen lines. | ||||
| @@ -362,13 +381,17 @@ function Screen:expect(expected, attr_ids, attr_ignore) | ||||
|         return ( | ||||
|           'Failed to match any screen lines.\n' | ||||
|           .. 'Expected (anywhere): "' .. expected.any .. '"\n' | ||||
|           .. 'Actual:\n  |' .. table.concat(actual_rows, '|\n  |') .. '|\n\n') | ||||
|           .. 'Actual:\n  |' .. table.concat(actual_rows, '\n  |') .. '\n\n') | ||||
|       end | ||||
|     end | ||||
|  | ||||
|     if grid ~= nil then | ||||
|       -- `expected` must match the screen lines exactly. | ||||
|       for i = 1, self._height do | ||||
|       if #actual_rows ~= #expected_rows then | ||||
|         return "Expected screen state's row count(" .. #expected_rows | ||||
|         .. ') differs from configured height(' .. #actual_rows .. ') of Screen.' | ||||
|       end | ||||
|       for i = 1, #actual_rows do | ||||
|         if expected_rows[i] ~= actual_rows[i] then | ||||
|           local msg_expected_rows = {} | ||||
|           for j = 1, #expected_rows do | ||||
| @@ -378,8 +401,8 @@ function Screen:expect(expected, attr_ids, attr_ignore) | ||||
|           actual_rows[i] = '*' .. actual_rows[i] | ||||
|           return ( | ||||
|             'Row ' .. tostring(i) .. ' did not match.\n' | ||||
|             ..'Expected:\n  |'..table.concat(msg_expected_rows, '|\n  |')..'|\n' | ||||
|             ..'Actual:\n  |'..table.concat(actual_rows, '|\n  |')..'|\n\n'..[[ | ||||
|             ..'Expected:\n  |'..table.concat(msg_expected_rows, '\n  |')..'\n' | ||||
|             ..'Actual:\n  |'..table.concat(actual_rows, '\n  |')..'\n\n'..[[ | ||||
| To print the expect() call that would assert the current screen state, use | ||||
| screen:snapshot_util(). In case of non-deterministic failures, use | ||||
| screen:redraw_debug() to show all intermediate screen states.  ]]) | ||||
| @@ -465,7 +488,7 @@ function Screen:_wait(check, flags) | ||||
|     if not err then | ||||
|       success_seen = true | ||||
|       if did_miminal_timeout then | ||||
|         helpers.stop() | ||||
|         self._session:stop() | ||||
|       end | ||||
|     elseif success_seen and #args > 0 then | ||||
|       failure_after_success = true | ||||
| @@ -474,7 +497,7 @@ function Screen:_wait(check, flags) | ||||
|  | ||||
|     return true | ||||
|   end | ||||
|   run(nil, notification_cb, nil, minimal_timeout) | ||||
|   run_session(self._session, nil, notification_cb, nil, minimal_timeout) | ||||
|   if not did_flush then | ||||
|     err = "no flush received" | ||||
|   elseif not checked then | ||||
| @@ -487,7 +510,7 @@ function Screen:_wait(check, flags) | ||||
|  | ||||
|   if not success_seen then | ||||
|     did_miminal_timeout = true | ||||
|     run(nil, notification_cb, nil, timeout-minimal_timeout) | ||||
|     run_session(self._session, nil, notification_cb, nil, timeout-minimal_timeout) | ||||
|   end | ||||
|  | ||||
|   local did_warn = false | ||||
| @@ -547,7 +570,7 @@ function Screen:sleep(ms) | ||||
|     assert(method == 'redraw') | ||||
|     self:_redraw(args) | ||||
|   end | ||||
|   run(nil, notification_cb, nil, ms) | ||||
|   run_session(self._session, nil, notification_cb, nil, ms) | ||||
| end | ||||
|  | ||||
| function Screen:_redraw(updates) | ||||
| @@ -579,6 +602,22 @@ function Screen:set_on_event_handler(callback) | ||||
| end | ||||
|  | ||||
| function Screen:_handle_resize(width, height) | ||||
|   self:_handle_grid_resize(1, width, height) | ||||
|   self._scroll_region = { | ||||
|     top = 1, bot = height, left = 1, right = width | ||||
|   } | ||||
|   self._grid = self._grids[1] | ||||
| end | ||||
|  | ||||
| local function min(x,y) | ||||
|   if x < y then | ||||
|     return x | ||||
|   else | ||||
|     return y | ||||
|   end | ||||
| end | ||||
|  | ||||
| function Screen:_handle_grid_resize(grid, width, height) | ||||
|   local rows = {} | ||||
|   for _ = 1, height do | ||||
|     local cols = {} | ||||
| @@ -587,22 +626,36 @@ function Screen:_handle_resize(width, height) | ||||
|     end | ||||
|     table.insert(rows, cols) | ||||
|   end | ||||
|   self._cursor.row = 1 | ||||
|   self._cursor.col = 1 | ||||
|   self._rows = rows | ||||
|   self._width = width | ||||
|   self._height = height | ||||
|   self._scroll_region = { | ||||
|     top = 1, bot = height, left = 1, right = width | ||||
|   if grid > 1 and self._grids[grid] ~= nil then | ||||
|     local old = self._grids[grid] | ||||
|     for i = 1, min(height,old.height) do | ||||
|       for j = 1, min(width,old.width) do | ||||
|         rows[i][j] = old.rows[i][j] | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   if self._cursor.grid == grid then | ||||
|     self._cursor.row = 1 | ||||
|     self._cursor.col = 1 | ||||
|   end | ||||
|   self._grids[grid] = { | ||||
|     rows=rows, | ||||
|     width=width, | ||||
|     height=height, | ||||
|   } | ||||
| end | ||||
|  | ||||
| function Screen:_handle_flush() | ||||
| function Screen:_handle_win_scroll_over_start() | ||||
|    self.scroll_over = true | ||||
|    self.scroll_over_pos = self._grids[1].height | ||||
| end | ||||
|  | ||||
| function Screen:_handle_grid_resize(grid, width, height) | ||||
|   assert(grid == 1) | ||||
|   self:_handle_resize(width, height) | ||||
| function Screen:_handle_win_scroll_over_reset() | ||||
|    self.scroll_over = false | ||||
| end | ||||
|  | ||||
| function Screen:_handle_flush() | ||||
| end | ||||
|  | ||||
| function Screen:_reset() | ||||
| @@ -641,20 +694,27 @@ function Screen:_handle_clear() | ||||
|   -- newer clients, to check we remain compatible with both kind of clients, | ||||
|   -- ensure the scroll region is in a reset state. | ||||
|   local expected_region = { | ||||
|     top = 1, bot = self._height, left = 1, right = self._width | ||||
|     top = 1, bot = self._grid.height, left = 1, right = self._grid.width | ||||
|   } | ||||
|   eq(expected_region, self._scroll_region) | ||||
|   self:_clear_block(1, self._height, 1, self._width) | ||||
|   self:_handle_grid_clear(1) | ||||
| end | ||||
|  | ||||
| function Screen:_handle_grid_clear(grid) | ||||
|   assert(grid == 1) | ||||
|   self:_clear_block(1, self._height, 1, self._width) | ||||
|   self:_clear_block(self._grids[grid], 1, self._grids[grid].height, 1, self._grids[grid].width) | ||||
| end | ||||
|  | ||||
| function Screen:_handle_grid_destroy(grid) | ||||
|   self._grids[grid] = nil | ||||
|   if self._options.ext_multigrid then | ||||
|     assert(self.win_position[grid]) | ||||
|     self.win_position[grid] = nil | ||||
|   end | ||||
| end | ||||
|  | ||||
| function Screen:_handle_eol_clear() | ||||
|   local row, col = self._cursor.row, self._cursor.col | ||||
|   self:_clear_block(row, row, col, self._scroll_region.right) | ||||
|   self:_clear_block(self._grid, row, row, col, self._grid.width) | ||||
| end | ||||
|  | ||||
| function Screen:_handle_cursor_goto(row, col) | ||||
| @@ -663,7 +723,7 @@ function Screen:_handle_cursor_goto(row, col) | ||||
| end | ||||
|  | ||||
| function Screen:_handle_grid_cursor_goto(grid, row, col) | ||||
|   assert(grid == 1) | ||||
|   self._cursor.grid = grid | ||||
|   self._cursor.row = row + 1 | ||||
|   self._cursor.col = col + 1 | ||||
| end | ||||
| @@ -704,13 +764,18 @@ function Screen:_handle_scroll(count) | ||||
|   self:_handle_grid_scroll(1, top-1, bot, left-1, right, count, 0) | ||||
| end | ||||
|  | ||||
| function Screen:_handle_grid_scroll(grid, 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 | ||||
|   left = left+1 | ||||
|   assert(grid == 1) | ||||
|   assert(cols == 0) | ||||
|   local grid = self._grids[g] | ||||
|   local start, stop, step | ||||
|  | ||||
|  | ||||
|   if rows > 0 then | ||||
|     start = top | ||||
|     stop = bot - rows | ||||
| @@ -723,8 +788,8 @@ function Screen:_handle_grid_scroll(grid, top, bot, left, right, rows, cols) | ||||
|  | ||||
|   -- shift scroll region | ||||
|   for i = start, stop, step do | ||||
|     local target = self._rows[i] | ||||
|     local source = self._rows[i + rows] | ||||
|     local target = grid.rows[i] | ||||
|     local source = grid.rows[i + rows] | ||||
|     for j = left, right do | ||||
|       target[j].text = source[j].text | ||||
|       target[j].attrs = source[j].attrs | ||||
| @@ -734,7 +799,7 @@ function Screen:_handle_grid_scroll(grid, top, bot, left, right, rows, cols) | ||||
|  | ||||
|   -- clear invalid rows | ||||
|   for i = stop + step, stop + rows, step do | ||||
|     self:_clear_row_section(i, left, right) | ||||
|     self:_clear_row_section(grid, i, left, right) | ||||
|   end | ||||
| end | ||||
|  | ||||
| @@ -744,13 +809,35 @@ function Screen:_handle_hl_attr_define(id, rgb_attrs, cterm_attrs, info) | ||||
|   self._new_attrs = true | ||||
| end | ||||
|  | ||||
| function Screen:_handle_win_pos(grid, win, startrow, startcol, width, height) | ||||
|     self.win_position[grid] = { | ||||
|         win = win, | ||||
|         startrow = startrow, | ||||
|         startcol = startcol, | ||||
|         width = width, | ||||
|         height = height | ||||
|     } | ||||
| end | ||||
|  | ||||
| function Screen:_handle_win_hide(grid) | ||||
|   self.win_position[grid] = nil | ||||
| end | ||||
|  | ||||
| function Screen:get_hl(val) | ||||
|   if self._options.ext_newgrid then | ||||
|     return self._attr_table[val][1] | ||||
|   else | ||||
|     return val | ||||
|   end | ||||
| end | ||||
|  | ||||
| function Screen:_handle_highlight_set(attrs) | ||||
|   self._attrs = attrs | ||||
| end | ||||
|  | ||||
| function Screen:_handle_put(str) | ||||
|   assert(not self._options.ext_linegrid) | ||||
|   local cell = self._rows[self._cursor.row][self._cursor.col] | ||||
|   local cell = self._grid.rows[self._cursor.row][self._cursor.col] | ||||
|   cell.text = str | ||||
|   cell.attrs = self._attrs | ||||
|   cell.hl_id = -1 | ||||
| @@ -759,8 +846,7 @@ end | ||||
|  | ||||
| function Screen:_handle_grid_line(grid, row, col, items) | ||||
|   assert(self._options.ext_linegrid) | ||||
|   assert(grid == 1) | ||||
|   local line = self._rows[row+1] | ||||
|   local line = self._grids[grid].rows[row+1] | ||||
|   local colpos = col+1 | ||||
|   local hl = self._clear_attrs | ||||
|   local hl_id = 0 | ||||
| @@ -887,45 +973,68 @@ function Screen:_handle_wildmenu_hide() | ||||
|   self.wildmenu_items, self.wildmenu_pos = nil, nil | ||||
| end | ||||
|  | ||||
| function Screen:_clear_block(top, bot, left, right) | ||||
| function Screen:_clear_block(grid, top, bot, left, right) | ||||
|   for i = top, bot do | ||||
|     self:_clear_row_section(i, left, right) | ||||
|     self:_clear_row_section(grid, i, left, right) | ||||
|   end | ||||
| end | ||||
|  | ||||
| function Screen:_clear_row_section(rownum, startcol, stopcol) | ||||
|   local row = self._rows[rownum] | ||||
| function Screen:_clear_row_section(grid, rownum, startcol, stopcol) | ||||
|   local row = grid.rows[rownum] | ||||
|   for i = startcol, stopcol do | ||||
|     row[i].text = ' ' | ||||
|     row[i].attrs = self._clear_attrs | ||||
|   end | ||||
| end | ||||
|  | ||||
| function Screen:_row_repr(row, attr_state) | ||||
| function Screen:_row_repr(gridnr, rownr, attr_state, cursor) | ||||
|   local rv = {} | ||||
|   local current_attr_id | ||||
|   for i = 1, self._width do | ||||
|     local attrs = row[i].attrs | ||||
|     if self._options.ext_linegrid then | ||||
|       attrs = attrs[(self._options.rgb and 1) or 2] | ||||
|   local i = 1 | ||||
|   local has_windows = self._options.ext_multigrid and gridnr == 1 | ||||
|   if self.scroll_over and self.scroll_over_pos < rownr then | ||||
|     has_windows = false | ||||
|   end | ||||
|   local row = self._grids[gridnr].rows[rownr] | ||||
|   while i <= #row do | ||||
|     local did_window = false | ||||
|     if has_windows then | ||||
|       for id,pos in pairs(self.win_position) do | ||||
|         if i-1 == pos.startcol and pos.startrow <= rownr-1 and rownr-1 < pos.startrow + pos.height then | ||||
|           if current_attr_id then | ||||
|             -- close current attribute bracket | ||||
|             table.insert(rv, '}') | ||||
|             current_attr_id = nil | ||||
|           end | ||||
|           table.insert(rv, '['..id..':'..string.rep('-',pos.width)..']') | ||||
|           i = i + pos.width | ||||
|           did_window = true | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|     local attr_id = self:_get_attr_id(attr_state, attrs, row[i].hl_id) | ||||
|     if current_attr_id and attr_id ~= current_attr_id then | ||||
|       -- close current attribute bracket, add it before any whitespace | ||||
|       -- up to the current cell | ||||
|       -- table.insert(rv, backward_find_meaningful(rv, i), '}') | ||||
|       table.insert(rv, '}') | ||||
|       current_attr_id = nil | ||||
|  | ||||
|     if not did_window then | ||||
|       local attrs = row[i].attrs | ||||
|       if self._options.ext_linegrid then | ||||
|         attrs = attrs[(self._options.rgb and 1) or 2] | ||||
|       end | ||||
|       local attr_id = self:_get_attr_id(attr_state, attrs, row[i].hl_id) | ||||
|       if current_attr_id and attr_id ~= current_attr_id then | ||||
|         -- close current attribute bracket | ||||
|         table.insert(rv, '}') | ||||
|         current_attr_id = nil | ||||
|       end | ||||
|       if not current_attr_id and attr_id then | ||||
|         -- open a new attribute bracket | ||||
|         table.insert(rv, '{' .. attr_id .. ':') | ||||
|         current_attr_id = attr_id | ||||
|       end | ||||
|       if not self._busy and cursor and self._cursor.col == i then | ||||
|         table.insert(rv, '^') | ||||
|       end | ||||
|       table.insert(rv, row[i].text) | ||||
|       i = i + 1 | ||||
|     end | ||||
|     if not current_attr_id and attr_id then | ||||
|       -- open a new attribute bracket | ||||
|       table.insert(rv, '{' .. attr_id .. ':') | ||||
|       current_attr_id = attr_id | ||||
|     end | ||||
|     if not self._busy and self._rows[self._cursor.row] == row and self._cursor.col == i then | ||||
|       table.insert(rv, '^') | ||||
|     end | ||||
|     table.insert(rv, row[i].text) | ||||
|   end | ||||
|   if current_attr_id then | ||||
|     table.insert(rv, '}') | ||||
| @@ -997,7 +1106,23 @@ function Screen:redraw_debug(attrs, ignore, timeout) | ||||
|   if timeout == nil then | ||||
|     timeout = 250 | ||||
|   end | ||||
|   run(nil, notification_cb, nil, timeout) | ||||
|   run_session(self._session, nil, notification_cb, nil, timeout) | ||||
| end | ||||
|  | ||||
| function Screen:render(headers, attr_state, preview) | ||||
|   headers = headers and self._options.ext_multigrid | ||||
|   local rv = {} | ||||
|   for igrid,grid in pairs(self._grids) do | ||||
|     if headers then | ||||
|       table.insert(rv, "## grid "..igrid) | ||||
|     end | ||||
|     for i = 1, grid.height do | ||||
|       local cursor = self._cursor.grid == igrid and self._cursor.row == i | ||||
|       local prefix = (headers or preview) and "  " or "" | ||||
|       table.insert(rv, prefix..self:_row_repr(igrid, i, attr_state, cursor).."|") | ||||
|     end | ||||
|   end | ||||
|   return rv | ||||
| end | ||||
|  | ||||
| function Screen:print_snapshot(attrs, ignore) | ||||
| @@ -1020,10 +1145,7 @@ function Screen:print_snapshot(attrs, ignore) | ||||
|     attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids) | ||||
|   end | ||||
|  | ||||
|   local lines = {} | ||||
|   for i = 1, self._height do | ||||
|     table.insert(lines, "  "..self:_row_repr(self._rows[i], attr_state).."|") | ||||
|   end | ||||
|   local lines = self:render(true, attr_state, true) | ||||
|  | ||||
|   local ext_state = self:_extstate_repr(attr_state) | ||||
|   local keys = false | ||||
|   | ||||
| @@ -165,7 +165,7 @@ describe("'wildmenu'", function() | ||||
|     feed([[:<Tab>]])      -- Invoke wildmenu. | ||||
|     -- Check only the last 2 lines, because the shell output is | ||||
|     -- system-dependent. | ||||
|     expect_stay_unchanged{any='!  #  &  <  =  >  @  >   \n:!^'} | ||||
|     expect_stay_unchanged{any='!  #  &  <  =  >  @  >   |\n:!^'} | ||||
|   end) | ||||
| end) | ||||
|  | ||||
|   | ||||
| @@ -49,8 +49,8 @@ local check_logs_useless_lines = { | ||||
| local function eq(expected, actual, ctx) | ||||
|   return assert.are.same(expected, actual, ctx) | ||||
| end | ||||
| local function neq(expected, actual) | ||||
|   return assert.are_not.same(expected, actual) | ||||
| local function neq(expected, actual, context) | ||||
|   return assert.are_not.same(expected, actual, context) | ||||
| end | ||||
| local function ok(res) | ||||
|   return assert.is_true(res) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Björn Linse
					Björn Linse