Merge pull request #8455 from UtkarshMe/grid-split

implement ext_multigrid: draw each window on a separate resizable grid.
This commit is contained in:
Björn Linse
2018-12-31 17:37:50 +01:00
committed by GitHub
45 changed files with 3279 additions and 1143 deletions

View File

@@ -116,9 +116,9 @@ set(NVIM_VERSION_PATCH 0)
set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers
# API level # 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_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) file(TO_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR}/.git FORCED_GIT_DIR)
include(GetGitRevisionDescription) include(GetGitRevisionDescription)

View File

@@ -12,9 +12,10 @@ Nvim UI protocol *ui*
UI Events *ui-events* UI Events *ui-events*
GUIs can be implemented as external processes communicating with Nvim over the 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, RPC API. The default UI model consists of a terminal-like grid with a single,
monospace font size. Some elements (UI "widgets") can be drawn separately from monospace font size. The UI can opt-in to have windows drawn on separate
the grid ("externalized"). grids, as well as to have some elements (UI "widgets") be drawn by the UI
itself rather than by nvim ("externalized").
*ui-options* *ui-options*
@@ -32,6 +33,7 @@ a dictionary with these (optional) keys:
`ext_cmdline` Externalize the cmdline. |ui-cmdline| `ext_cmdline` Externalize the cmdline. |ui-cmdline|
`ext_wildmenu` Externalize the wildmenu. |ui-wildmenu| `ext_wildmenu` Externalize the wildmenu. |ui-wildmenu|
`ext_linegrid` Use new revision of the grid events. |ui-linegrid| `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| `ext_hlstate` Use detailed highlight state. |ui-hlstate|
Specifying a non-existent option is an error. UIs can check the |api-metadata| 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 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 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 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. sets being batched for all event kinds.
Events must be handled in-order. A "flush" event is sent when nvim is done 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 implement a terminal-like interface. However there are two revisions of the
grid part of the protocol. The newer revision |ui-linegrid|, enabled by grid part of the protocol. The newer revision |ui-linegrid|, enabled by
`ext_linegrid` option, has a more effecient representation of text (especially `ext_linegrid` option, has a more effecient representation of text (especially
highlighted text), and room for futher enhancements that will use highlighted text), and allows extensions that use multiple grids.
multiple grids. The older revision is available and used by default only for
backwards compatibility reasons. New UIs are strongly recommended to use The older revision is available and used by default only for backwards
|ui-linegrid|, as further protocol extensions will require it. 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 Nvim optionally sends screen elements "semantically" as structured events
instead of raw grid-lines, controlled by |ui-ext-options|. The UI must present 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 global grid used by default for the entire editor screen state. Grids other
than that will be defined by future extensions. Just activating the than that will be defined by future extensions. Just activating the
`ext_linegrid` option by itself will never cause any additional grids to be `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 Highlight attribute groups are predefined. UIs should maintain a table to map
numerical highlight `id`:s to the actual attributes. 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 highlight group is cleared, so `ui_name` can always be used to reliably identify
screen elements, even if no attributes have been applied. 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* Popupmenu Events *ui-popupmenu*
Only sent if `ext_popupmenu` option is set in |ui-options| 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 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 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` defined at |complete-items|, except that `word` is replaced by `abbr`
if present. `selected` is the initially-selected item, a zero-based 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 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 `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] ["popupmenu_select", selected]
Select an item in the current popupmenu. `selected` is a zero-based Select an item in the current popupmenu. `selected` is a zero-based

View File

@@ -6,6 +6,7 @@
#include <string.h> #include <string.h>
#include "nvim/func_attr.h" #include "nvim/func_attr.h"
#include "nvim/types.h"
#define ARRAY_DICT_INIT {.size = 0, .capacity = 0, .items = NULL} #define ARRAY_DICT_INIT {.size = 0, .capacity = 0, .items = NULL}
#define STRING_INIT {.data = NULL, .size = 0} #define STRING_INIT {.data = NULL, .size = 0}
@@ -20,8 +21,6 @@
# define DictionaryOf(...) Dictionary # define DictionaryOf(...) Dictionary
#endif #endif
typedef int handle_T;
// Basic types // Basic types
typedef enum { typedef enum {
kErrorTypeNone = -1, kErrorTypeNone = -1,

View File

@@ -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; ui->ui_ext[kUILinegrid] = true;
} }
@@ -245,6 +245,28 @@ static void ui_set_option(UI *ui, bool init, String name, Object value,
name.data); 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(). /// Pushes data into UI.UIData, to be consumed later by remote_ui_flush().
static void push_call(UI *ui, const char *name, Array args) static void push_call(UI *ui, const char *name, Array args)
{ {

View File

@@ -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, void grid_scroll(Integer grid, Integer top, Integer bot,
Integer left, Integer right, Integer rows, Integer cols) Integer left, Integer right, Integer rows, Integer cols)
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL; 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; FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void popupmenu_hide(void) void popupmenu_hide(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;

View File

@@ -1920,13 +1920,13 @@ Object nvim_get_proc(Integer pid, Error *err)
Array nvim__inspect_cell(Integer row, Integer col, Error *err) Array nvim__inspect_cell(Integer row, Integer col, Error *err)
{ {
Array ret = ARRAY_DICT_INIT; Array ret = ARRAY_DICT_INIT;
if (row < 0 || row >= screen_Rows if (row < 0 || row >= default_grid.Rows
|| col < 0 || col >= screen_Columns) { || col < 0 || col >= default_grid.Columns) {
return ret; return ret;
} }
size_t off = LineOffset[(size_t)row] + (size_t)col; size_t off = default_grid.line_offset[(size_t)row] + (size_t)col;
ADD(ret, STRING_OBJ(cstr_to_string((char *)ScreenLines[off]))); ADD(ret, STRING_OBJ(cstr_to_string((char *)default_grid.chars[off])));
int attr = ScreenAttrs[off]; int attr = default_grid.attrs[off];
ADD(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, err))); ADD(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, err)));
// will not work first time // will not work first time
if (!highlight_use_hlstate()) { if (!highlight_use_hlstate()) {

View File

@@ -18,6 +18,8 @@ typedef struct {
// for garray_T // for garray_T
#include "nvim/garray.h" #include "nvim/garray.h"
// for ScreenGrid
#include "nvim/grid_defs.h"
// for HLF_COUNT // for HLF_COUNT
#include "nvim/highlight_defs.h" #include "nvim/highlight_defs.h"
// for pos_T, lpos_T and linenr_T // 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_tagstackidx; /* idx just below active entry */
int w_tagstacklen; /* number of tags on stack */ 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 * 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. * 0 at the top row to FRACTION_MULT at the last row.

View File

@@ -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(c)
&& !vim_isbreak((int)s[1]) && !vim_isbreak((int)s[1])
&& wp->w_p_wrap && 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 // Count all characters from first non-blank after a blank up to next
// non-blank after a blank. // non-blank after a blank.
numberextra = win_col_off(wp); numberextra = win_col_off(wp);
col2 = col; col2 = col;
colmax = (colnr_T)(wp->w_width - numberextra - col_adj); colmax = (colnr_T)(wp->w_grid.Columns - numberextra - col_adj);
if (col >= colmax) { if (col >= colmax) {
colmax += col_adj; 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; numberextra = numberwidth;
col += numberextra + mb_added; col += numberextra + mb_added;
if (col >= (colnr_T)wp->w_width) { if (col >= (colnr_T)wp->w_grid.Columns) {
col -= wp->w_width; col -= wp->w_grid.Columns;
numberextra = wp->w_width - (numberextra - win_col_off2(wp)); numberextra = wp->w_grid.Columns - (numberextra - win_col_off2(wp));
if (col >= numberextra && numberextra > 0) { if (col >= numberextra && numberextra > 0) {
col %= numberextra; 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); 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; added = 0;
if (*p_sbr != NUL) { 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. // Calculate effective window width.
int width = (colnr_T)wp->w_width - sbrlen - numberwidth; int width = (colnr_T)wp->w_grid.Columns - sbrlen - numberwidth;
int prev_width = col ? ((colnr_T)wp->w_width - (sbrlen + col)) : 0; int prev_width = col ? ((colnr_T)wp->w_grid.Columns - (sbrlen + col))
: 0;
if (width == 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); added += ((size - prev_width) / width) * vim_strsize(p_sbr);
if ((size - prev_width) % width) { 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 width1; // width of first line (after line number)
int width2; // width of further lines int width2; // width of further lines
if (wp->w_width == 0) { if (wp->w_grid.Columns == 0) {
// there is no border // there is no border
return false; return false;
} }
width1 = wp->w_width - win_col_off(wp); width1 = wp->w_grid.Columns - win_col_off(wp);
if ((int)vcol < width1 - 1) { if ((int)vcol < width1 - 1) {
return false; return false;

View File

@@ -120,11 +120,11 @@ static int coladvance2(
--curwin->w_curswant; --curwin->w_curswant;
} }
} else { } else {
int width = curwin->w_width - win_col_off(curwin); int width = curwin->w_grid.Columns - win_col_off(curwin);
if (finetune if (finetune
&& curwin->w_p_wrap && curwin->w_p_wrap
&& curwin->w_width != 0 && curwin->w_grid.Columns != 0
&& wcol >= (colnr_T)width) { && wcol >= (colnr_T)width) {
csize = linetabsize(line); csize = linetabsize(line);
if (csize > 0) if (csize > 0)
@@ -223,9 +223,10 @@ static int coladvance2(
} else { } else {
int b = (int)wcol - (int)col; int b = (int)wcol - (int)col;
/* The difference between wcol and col is used to set coladd. */ // The difference between wcol and col is used to set coladd.
if (b > 0 && b < (MAXCOL - 2 * curwin->w_width)) if (b > 0 && b < (MAXCOL - 2 * curwin->w_grid.Columns)) {
pos->coladd = b; pos->coladd = b;
}
col += b; col += b;
} }
@@ -436,7 +437,7 @@ bool leftcol_changed(void)
bool retval = false; bool retval = false;
changed_cline_bef_curs(); 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(); validate_virtcol();
/* /*

View File

@@ -560,7 +560,7 @@ static int insert_check(VimState *state)
if (curwin->w_wcol < s->mincol - curbuf->b_p_ts if (curwin->w_wcol < s->mincol - curbuf->b_p_ts
&& curwin->w_wrow == curwin->w_winrow && 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_cursor.lnum != curwin->w_topline
|| curwin->w_topfill > 0)) { || curwin->w_topfill > 0)) {
if (curwin->w_topfill > 0) { if (curwin->w_topfill > 0) {
@@ -1494,40 +1494,41 @@ void edit_putchar(int c, int highlight)
{ {
int attr; int attr;
if (ScreenLines != NULL) { if (curwin->w_grid.chars != NULL || default_grid.chars != NULL) {
update_topline(); /* just in case w_topline isn't valid */ update_topline(); // just in case w_topline isn't valid
validate_cursor(); validate_cursor();
if (highlight) { if (highlight) {
attr = HL_ATTR(HLF_8); attr = HL_ATTR(HLF_8);
} else { } else {
attr = 0; attr = 0;
} }
pc_row = curwin->w_winrow + curwin->w_wrow; pc_row = curwin->w_wrow;
pc_col = curwin->w_wincol; pc_col = 0;
pc_status = PC_STATUS_UNSET; pc_status = PC_STATUS_UNSET;
if (curwin->w_p_rl) { 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) { 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) { if (fix_col != pc_col) {
screen_putchar(' ', pc_row, fix_col, attr); grid_putchar(&curwin->w_grid, ' ', pc_row, fix_col, attr);
--curwin->w_wcol; curwin->w_wcol--;
pc_status = PC_STATUS_RIGHT; pc_status = PC_STATUS_RIGHT;
} }
} }
} else { } else {
pc_col += curwin->w_wcol; 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; pc_status = PC_STATUS_LEFT;
}
} }
/* save the character to be able to put it back */ /* save the character to be able to put it back */
if (pc_status == PC_STATUS_UNSET) { 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; 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) { if (pc_status == PC_STATUS_RIGHT || pc_status == PC_STATUS_LEFT) {
redrawWinline(curwin, curwin->w_cursor.lnum, false); redrawWinline(curwin, curwin->w_cursor.lnum, false);
} else { } 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(); char_u *p = get_cursor_line_ptr();
curwin->w_cursor.col -= utf_head_off(p, p + col); curwin->w_cursor.col -= utf_head_off(p, p + col);
curs_columns(false); // Recompute w_wrow and w_wcol curs_columns(false); // Recompute w_wrow and w_wcol
if (curwin->w_wcol < curwin->w_width) { if (curwin->w_wcol < curwin->w_grid.Columns) {
edit_putchar('$', FALSE); edit_putchar('$', false);
dollar_vcol = curwin->w_virtcol; dollar_vcol = curwin->w_virtcol;
} }
curwin->w_cursor.col = save_col; curwin->w_cursor.col = save_col;
@@ -5825,7 +5827,7 @@ static void check_auto_format(
/* /*
* Find out textwidth to be used for formatting: * Find out textwidth to be used for formatting:
* if 'textwidth' option is set, use it * 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. * if invalid value, use 0.
* Set default to window width (maximum 79) for "gq" operator. * Set default to window width (maximum 79) for "gq" operator.
*/ */
@@ -5840,9 +5842,10 @@ comp_textwidth (
if (textwidth == 0 && curbuf->b_p_wm) { if (textwidth == 0 && curbuf->b_p_wm) {
/* The width is the window width minus 'wrapmargin' minus all the /* The width is the window width minus 'wrapmargin' minus all the
* things that add to the margin. */ * things that add to the margin. */
textwidth = curwin->w_width - curbuf->b_p_wm; textwidth = curwin->w_grid.Columns - curbuf->b_p_wm;
if (cmdwin_type != 0) if (cmdwin_type != 0) {
textwidth -= 1; textwidth -= 1;
}
textwidth -= curwin->w_p_fdc; textwidth -= curwin->w_p_fdc;
if (signcolumn_on(curwin)) { if (signcolumn_on(curwin)) {
@@ -5855,9 +5858,10 @@ comp_textwidth (
if (textwidth < 0) if (textwidth < 0)
textwidth = 0; textwidth = 0;
if (ff && textwidth == 0) { if (ff && textwidth == 0) {
textwidth = curwin->w_width - 1; textwidth = curwin->w_grid.Columns - 1;
if (textwidth > 79) if (textwidth > 79) {
textwidth = 79; textwidth = 79;
}
} }
return textwidth; return textwidth;
} }

View File

@@ -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 row = (int)tv_get_number_chk(&argvars[0], NULL) - 1;
const int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; const int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1;
if (row < 0 || row >= screen_Rows if (row < 0 || row >= default_grid.Rows
|| col < 0 || col >= screen_Columns) { || col < 0 || col >= default_grid.Columns) {
c = -1; c = -1;
} else { } else {
c = ScreenAttrs[LineOffset[row] + col]; c = default_grid.attrs[default_grid.line_offset[row] + col];
} }
rettv->vval.v_number = c; 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 row = tv_get_number_chk(&argvars[0], NULL) - 1;
const int col = tv_get_number_chk(&argvars[1], NULL) - 1; const int col = tv_get_number_chk(&argvars[1], NULL) - 1;
if (row < 0 || row >= screen_Rows if (row < 0 || row >= default_grid.Rows
|| col < 0 || col >= screen_Columns) { || col < 0 || col >= default_grid.Columns) {
c = -1; c = -1;
} else { } else {
off = LineOffset[row] + col; off = default_grid.line_offset[row] + col;
c = utf_ptr2char(ScreenLines[off]); c = utf_ptr2char(default_grid.chars[off]);
} }
rettv->vval.v_number = c; 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, Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit,
true, false, false, cwd, true, false, false, cwd,
term_width, curwin->w_height, term_width, curwin->w_grid.Rows,
xstrdup("xterm-256color"), xstrdup("xterm-256color"),
&rettv->vval.v_number); &rettv->vval.v_number);
if (rettv->vval.v_number <= 0) { if (rettv->vval.v_number <= 0) {

View File

@@ -256,10 +256,12 @@ void ex_align(exarg_T *eap)
*/ */
if (width <= 0) if (width <= 0)
width = curbuf->b_p_tw; width = curbuf->b_p_tw;
if (width == 0 && curbuf->b_p_wm > 0) if (width == 0 && curbuf->b_p_wm > 0) {
width = curwin->w_width - curbuf->b_p_wm; width = curwin->w_grid.Columns - curbuf->b_p_wm;
if (width <= 0) }
if (width <= 0) {
width = 80; width = 80;
}
} }
if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL) 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 // Vi compatible: ":z!" uses display height, without a count uses
// 'scroll' // 'scroll'
if (eap->forceit) { if (eap->forceit) {
bigness = curwin->w_height; bigness = curwin->w_grid.Rows;
} else if (ONE_WINDOW) { } else if (ONE_WINDOW) {
bigness = curwin->w_p_scr * 2; bigness = curwin->w_p_scr * 2;
} else { } else {
bigness = curwin->w_height - 3; bigness = curwin->w_grid.Rows - 3;
} }
if (bigness < 1) { if (bigness < 1) {
bigness = 1; bigness = 1;

View File

@@ -9194,10 +9194,10 @@ static int ses_winsizes(FILE *fd, int restore_size, win_T *tab_firstwin)
/* restore height when not full height */ /* restore height when not full height */
if (wp->w_height + wp->w_status_height < topframe->fr_height if (wp->w_height + wp->w_status_height < topframe->fr_height
&& (fprintf(fd, && (fprintf(fd,
"exe '%dresize ' . ((&lines * %" PRId64 "exe '%dresize ' . ((&lines * %" PRId64
" + %" PRId64 ") / %" PRId64 ")", " + %" PRId64 ") / %" PRId64 ")",
n, (int64_t)wp->w_height, n, (int64_t)wp->w_grid.Rows,
(int64_t)(Rows / 2), (int64_t)Rows) < 0 (int64_t)(Rows / 2), (int64_t)Rows) < 0
|| put_eol(fd) == FAIL)) || put_eol(fd) == FAIL))
return FAIL; return FAIL;
@@ -9459,8 +9459,8 @@ put_view(
" * winheight(0) + %" PRId64 ") / %" PRId64 ")", " * winheight(0) + %" PRId64 ") / %" PRId64 ")",
(int64_t)wp->w_cursor.lnum, (int64_t)wp->w_cursor.lnum,
(int64_t)(wp->w_cursor.lnum - wp->w_topline), (int64_t)(wp->w_cursor.lnum - wp->w_topline),
(int64_t)(wp->w_height / 2), (int64_t)(wp->w_grid.Rows / 2),
(int64_t)wp->w_height) < 0 (int64_t)wp->w_grid.Rows) < 0
|| put_eol(fd) == FAIL || put_eol(fd) == FAIL
|| put_line(fd, "if s:l < 1 | let s:l = 1 | endif") == FAIL || put_line(fd, "if s:l < 1 | let s:l = 1 | endif") == FAIL
|| put_line(fd, "exe s:l") == FAIL || put_line(fd, "exe s:l") == FAIL

View File

@@ -2120,8 +2120,8 @@ static int vgetorpeek(int advance)
++col; ++col;
} }
curwin->w_wrow = curwin->w_cline_row curwin->w_wrow = curwin->w_cline_row
+ curwin->w_wcol / curwin->w_width; + curwin->w_wcol / curwin->w_grid.Columns;
curwin->w_wcol %= curwin->w_width; curwin->w_wcol %= curwin->w_grid.Columns;
curwin->w_wcol += curwin_col_off(); curwin->w_wcol += curwin_col_off();
col = 0; /* no correction needed */ col = 0; /* no correction needed */
} else { } else {
@@ -2129,8 +2129,8 @@ static int vgetorpeek(int advance)
col = curwin->w_cursor.col - 1; col = curwin->w_cursor.col - 1;
} }
} else if (curwin->w_p_wrap && curwin->w_wrow) { } else if (curwin->w_p_wrap && curwin->w_wrow) {
--curwin->w_wrow; curwin->w_wrow--;
curwin->w_wcol = curwin->w_width - 1; curwin->w_wcol = curwin->w_grid.Columns - 1;
col = curwin->w_cursor.col - 1; col = curwin->w_cursor.col - 1;
} }
if (col > 0 && curwin->w_wcol > 0) { if (col > 0 && curwin->w_wcol > 0) {

View File

@@ -91,9 +91,10 @@ EXTERN struct nvim_stats_s {
/* /*
* Number of Rows and Columns in the screen. * Number of Rows and Columns in the screen.
* Must be long to be able to use them as options in option.c. * 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[]. * Note: Use default_grid.Rows and default_grid.Columns to access items in
* They may have different values when the screen wasn't (re)allocated yet * default_grid.chars[]. They may have different values when the screen
* after setting Rows or Columns (e.g., when starting up). * 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_COLS 80 // default value for 'columns'
#define DFLT_ROWS 24 // default value for 'lines' #define DFLT_ROWS 24 // default value for 'lines'
@@ -128,45 +129,6 @@ typedef off_t off_T;
# endif # endif
#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 * 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. * 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 # define CONT_LOCAL 32 /* for ctrl_x_mode 0, ^X^P/^X^N do a local
* expansion, (eg use complete=.) */ * expansion, (eg use complete=.) */
/* // state for putting characters in the message area
* Functions for putting characters in the command line, EXTERN int cmdmsg_rl INIT(= false); // cmdline is drawn right to left
* while keeping ScreenLines[] updated.
*/
EXTERN int cmdmsg_rl INIT(= FALSE); /* cmdline is drawn right to left */
EXTERN int msg_col; EXTERN int msg_col;
EXTERN int msg_row; EXTERN int msg_row;
EXTERN int msg_scrolled; /* Number of screen lines that windows have EXTERN int msg_scrolled; /* Number of screen lines that windows have

59
src/nvim/grid_defs.h Normal file
View 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

View File

@@ -582,8 +582,9 @@ static void prt_header(prt_settings_T *const psettings, const int pagenum,
*/ */
static void prt_message(char_u *s) static void prt_message(char_u *s)
{ {
screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); grid_fill(&default_grid, (int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ',
screen_puts(s, (int)Rows - 1, 0, HL_ATTR(HLF_R)); 0);
grid_puts(&default_grid, s, (int)Rows - 1, 0, HL_ATTR(HLF_R));
ui_flush(); ui_flush();
} }

View File

@@ -98,7 +98,7 @@ static int get_attr_entry(HlEntry entry)
return id; 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) void ui_send_all_hls(UI *ui)
{ {
for (size_t i = 1; i < kv_size(attr_entries); i++) { 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); map_clear(int, int)(combine_attr_entries);
highlight_attr_set_all(); highlight_attr_set_all();
highlight_changed(); highlight_changed();
redraw_all_later(NOT_VALID); screen_invalidate_highlights();
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));
}
} else { } else {
kv_destroy(attr_entries); kv_destroy(attr_entries);
map_free(HlEntry, int)(attr_entry_ids); map_free(HlEntry, int)(attr_entry_ids);

View File

@@ -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 char_u *prev_line = NULL; // cached pointer to line.
static varnumber_T prev_tick = 0; // Changedtick of cached value. static varnumber_T prev_tick = 0; // Changedtick of cached value.
int bri = 0; int bri = 0;
/* window width minus window margin space, i.e. what rests for text */ // window width minus window margin space, i.e. what rests for text
const int eff_wwidth = wp->w_width const int eff_wwidth = wp->w_grid.Columns
- ((wp->w_p_nu || wp->w_p_rnu) - ((wp->w_p_nu || wp->w_p_rnu)
&& (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL)
? number_width(wp) + 1 : 0); ? number_width(wp) + 1 : 0);

View File

@@ -556,13 +556,6 @@ size_t mb_string2cells(const char_u *str)
return clen; 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 /// 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 /// 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 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. * 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; 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;
}

View File

@@ -19,13 +19,6 @@
#define MB_BYTE2LEN(b) utf8len_tab[b] #define MB_BYTE2LEN(b) utf8len_tab[b]
#define MB_BYTE2LEN_CHECK(b) (((b) < 0 || (b) > 255) ? 1 : 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 // max length of an unicode char
#define MB_MAXCHAR 6 #define MB_MAXCHAR 6

View File

@@ -698,8 +698,8 @@ void free_all_mem(void)
buf = bufref_valid(&bufref) ? nextbuf : firstbuf; buf = bufref_valid(&bufref) ? nextbuf : firstbuf;
} }
/* screenlines (can't display anything now!) */ // free screenlines (can't display anything now!)
free_screenlines(); screen_free_all_mem();
clear_hl_tables(false); clear_hl_tables(false);
list_free_log(); list_free_log();

View File

@@ -1546,10 +1546,8 @@ void msg_prt_line(char_u *s, int list)
msg_clr_eos(); msg_clr_eos();
} }
/* // Use grid_puts() to output one multi-byte character.
* Use screen_puts() to output one multi-byte character. // Return the pointer "s" advanced to the next character.
* Return the pointer "s" advanced to the next character.
*/
static char_u *screen_puts_mbyte(char_u *s, int l, int attr) static char_u *screen_puts_mbyte(char_u *s, int l, int attr)
{ {
int cw; int cw;
@@ -1563,7 +1561,7 @@ static char_u *screen_puts_mbyte(char_u *s, int l, int attr)
return s; 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) { if (cmdmsg_rl) {
msg_col -= cw; msg_col -= cw;
if (msg_col == 0) { if (msg_col == 0) {
@@ -1886,19 +1884,22 @@ int msg_scrollsize(void)
*/ */
static void msg_scroll_up(void) static void msg_scroll_up(void)
{ {
if (msg_scrolled == 0) {
ui_call_win_scroll_over_start();
}
if (dy_flags & DY_MSGSEP) { if (dy_flags & DY_MSGSEP) {
if (msg_scrolled == 0) { if (msg_scrolled == 0) {
screen_fill(Rows-p_ch-1, Rows-p_ch, 0, (int)Columns, grid_fill(&default_grid, Rows-p_ch-1, Rows-p_ch, 0, (int)Columns,
fill_msgsep, fill_msgsep, HL_ATTR(HLF_MSGSEP)); fill_msgsep, fill_msgsep, HL_ATTR(HLF_MSGSEP));
} }
int nscroll = MIN(msg_scrollsize()+1, Rows); 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 { } 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 // TODO(bfredl): when msgsep display is properly batched, this fill should be
// eliminated. // 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. // Output postponed text.
msg_didout = true; // Remember that line is not empty. 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; msg_col += *t_col;
*t_col = 0; *t_col = 0;
/* If the string starts with a composing character don't increment the /* 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 if (toscroll == -1
&& screen_ins_lines(0, 1, (int)Rows, 0, (int)Columns) == OK) { && grid_ins_lines(&default_grid, 0, 1, (int)Rows,
screen_fill(0, 1, 0, (int)Columns, ' ', ' ', 0); 0, (int)Columns) == OK) {
grid_fill(&default_grid, 0, 1, 0, (int)Columns, ' ', ' ', 0);
// display line at top // display line at top
(void)disp_sb_line(0, mp); (void)disp_sb_line(0, mp);
} else { } else {
@@ -2333,18 +2336,18 @@ static int do_more_prompt(int typed_char)
/* scroll up, display line at bottom */ /* scroll up, display line at bottom */
msg_scroll_up(); msg_scroll_up();
inc_msg_scrolled(); inc_msg_scrolled();
screen_fill((int)Rows - 2, (int)Rows - 1, 0, grid_fill(&default_grid, (int)Rows - 2, (int)Rows - 1, 0,
(int)Columns, ' ', ' ', 0); (int)Columns, ' ', ' ', 0);
mp_last = disp_sb_line((int)Rows - 2, mp_last); mp_last = disp_sb_line((int)Rows - 2, mp_last);
--toscroll; --toscroll;
} }
} }
if (toscroll <= 0) { if (toscroll <= 0) {
/* displayed the requested text, more prompt again */ // displayed the requested text, more prompt again
screen_fill((int)Rows - 1, (int)Rows, 0, grid_fill(&default_grid, (int)Rows - 1, (int)Rows, 0,
(int)Columns, ' ', ' ', 0); (int)Columns, ' ', ' ', 0);
msg_moremsg(FALSE); msg_moremsg(false);
continue; continue;
} }
@@ -2355,8 +2358,9 @@ static int do_more_prompt(int typed_char)
break; break;
} }
/* clear the --more-- message */ // clear the --more-- message
screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); grid_fill(&default_grid, (int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ',
0);
State = oldState; State = oldState;
setmouse(); setmouse();
if (quit_more) { if (quit_more) {
@@ -2452,8 +2456,8 @@ void mch_msg(char *str)
*/ */
static void msg_screen_putchar(int c, int attr) static void msg_screen_putchar(int c, int attr)
{ {
msg_didout = TRUE; /* remember that line is not empty */ msg_didout = true; // remember that line is not empty
screen_putchar(c, msg_row, msg_col, attr); grid_putchar(&default_grid, c, msg_row, msg_col, attr);
if (cmdmsg_rl) { if (cmdmsg_rl) {
if (--msg_col == 0) { if (--msg_col == 0) {
msg_col = Columns; msg_col = Columns;
@@ -2473,11 +2477,12 @@ void msg_moremsg(int full)
char_u *s = (char_u *)_("-- More --"); char_u *s = (char_u *)_("-- More --");
attr = HL_ATTR(HLF_M); attr = HL_ATTR(HLF_M);
screen_puts(s, (int)Rows - 1, 0, attr); grid_puts(&default_grid, s, (int)Rows - 1, 0, attr);
if (full) if (full) {
screen_puts((char_u *) grid_puts(&default_grid, (char_u *)
_(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "), _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "),
(int)Rows - 1, vim_strsize(s), attr); (int)Rows - 1, vim_strsize(s), attr);
}
} }
/* /*
@@ -2525,13 +2530,13 @@ void msg_clr_eos(void)
*/ */
void msg_clr_eos_force(void) void msg_clr_eos_force(void)
{ {
if (cmdmsg_rl) { int msg_startcol = (cmdmsg_rl) ? 0 : msg_col;
screen_fill(msg_row, msg_row + 1, 0, msg_col + 1, ' ', ' ', 0); int msg_endcol = (cmdmsg_rl) ? msg_col + 1 : (int)Columns;
screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
} else { grid_fill(&default_grid, msg_row, msg_row + 1, msg_startcol, msg_endcol, ' ',
screen_fill(msg_row, msg_row + 1, msg_col, (int)Columns, ' ', ' ', 0); ' ', 0);
screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); grid_fill(&default_grid, msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ',
} 0);
} }
/* /*

View File

@@ -1251,7 +1251,7 @@ int plines_win_nofill(
return 1; return 1;
} }
if (wp->w_width == 0) { if (wp->w_grid.Columns == 0) {
return 1; return 1;
} }
@@ -1261,8 +1261,8 @@ int plines_win_nofill(
} }
const int lines = plines_win_nofold(wp, lnum); const int lines = plines_win_nofold(wp, lnum);
if (winheight && lines > wp->w_height) { if (winheight && lines > wp->w_grid.Rows) {
return wp->w_height; return wp->w_grid.Rows;
} }
return lines; return lines;
} }
@@ -1292,7 +1292,7 @@ int plines_win_nofold(win_T *wp, linenr_T lnum)
/* /*
* Add column offset for 'number', 'relativenumber' and 'foldcolumn'. * 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) { if (width <= 0 || col > 32000) {
return 32000; // bigger than the number of screen columns 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) if (!wp->w_p_wrap)
return lines + 1; return lines + 1;
if (wp->w_width == 0) if (wp->w_grid.Columns == 0) {
return lines + 1; return lines + 1;
}
char_u *line = ml_get_buf(wp->w_buffer, lnum, false); char_u *line = ml_get_buf(wp->w_buffer, lnum, false);
char_u *s = line; 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. // 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) { if (width <= 0) {
return 9999; return 9999;
} }

View File

@@ -110,8 +110,9 @@ retnomove:
// Remember the character under the mouse, it might be a '-' or '+' in the // Remember the character under the mouse, it might be a '-' or '+' in the
// fold column. NB: only works for ASCII chars! // fold column. NB: only works for ASCII chars!
if (row >= 0 && row < Rows && col >= 0 && col <= Columns if (row >= 0 && row < Rows && col >= 0 && col <= Columns
&& ScreenLines != NULL) { && default_grid.chars != NULL) {
mouse_char = ScreenLines[LineOffset[row] + (unsigned)col][0]; mouse_char = default_grid.chars[default_grid.line_offset[row]
+ (unsigned)col][0];
} else { } else {
mouse_char = ' '; mouse_char = ' ';
} }

View File

@@ -83,8 +83,9 @@ static void comp_botline(win_T *wp)
redraw_for_cursorline(wp); redraw_for_cursorline(wp);
wp->w_valid |= (VALID_CROW|VALID_CHEIGHT); wp->w_valid |= (VALID_CROW|VALID_CHEIGHT);
} }
if (done + n > wp->w_height) if (done + n > wp->w_grid.Rows) {
break; break;
}
done += n; done += n;
lnum = last; lnum = last;
} }
@@ -149,9 +150,12 @@ void update_topline(void)
bool check_botline = false; bool check_botline = false;
long save_so = p_so; 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 // If there is no valid screen and when the window height is zero just use
// the cursor line. // 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_topline = curwin->w_cursor.lnum;
curwin->w_botline = curwin->w_topline; curwin->w_botline = curwin->w_topline;
curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
@@ -200,9 +204,10 @@ void update_topline(void)
check_topline = true; check_topline = true;
if (check_topline) { if (check_topline) {
int halfheight = curwin->w_height / 2 - 1; int halfheight = curwin->w_grid.Rows / 2 - 1;
if (halfheight < 2) if (halfheight < 2) {
halfheight = 2; halfheight = 2;
}
long n; long n;
if (hasAnyFolding(curwin)) { if (hasAnyFolding(curwin)) {
/* Count the number of logical lines between the cursor and /* 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 * botline - p_so (approximation of how much will be
* scrolled). */ * scrolled). */
for (linenr_T lnum = curwin->w_cursor.lnum; for (linenr_T lnum = curwin->w_cursor.lnum;
lnum >= curwin->w_botline - p_so; --lnum) { lnum >= curwin->w_botline - p_so; lnum--) {
++line_count; line_count++;
/* stop at end of file or when we know we are far off */ // stop at end of file or when we know we are far off
if (lnum <= 0 || line_count > curwin->w_height + 1) if (lnum <= 0 || line_count > curwin->w_grid.Rows + 1) {
break; break;
}
(void)hasFolding(lnum, &lnum, NULL); (void)hasFolding(lnum, &lnum, NULL);
} }
} else } else
line_count = curwin->w_cursor.lnum - curwin->w_botline line_count = curwin->w_cursor.lnum - curwin->w_botline
+ 1 + p_so; + 1 + p_so;
if (line_count <= curwin->w_height + 1) if (line_count <= curwin->w_grid.Rows + 1) {
scroll_cursor_bot(scrolljump_value(), false); scroll_cursor_bot(scrolljump_value(), false);
else } else {
scroll_cursor_halfway(false); scroll_cursor_halfway(false);
}
} }
} }
} }
@@ -346,7 +353,7 @@ void update_topline_win(win_T* win)
*/ */
static int scrolljump_value(void) 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); assert(result <= INT_MAX);
return (int)result; return (int)result;
} }
@@ -522,6 +529,7 @@ int cursor_valid(void)
*/ */
void validate_cursor(void) void validate_cursor(void)
{ {
win_grid_alloc(curwin); // we need to have w_grid.Rows/Columns updated
check_cursor_moved(curwin); check_cursor_moved(curwin);
if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW)) if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW))
curs_columns(true); curs_columns(true);
@@ -660,18 +668,19 @@ void validate_cursor_col(void)
colnr_T col = curwin->w_virtcol; colnr_T col = curwin->w_virtcol;
colnr_T off = curwin_col_off(); colnr_T off = curwin_col_off();
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 */ // long line wrapping, adjust curwin->w_wrow
if (curwin->w_p_wrap if (curwin->w_p_wrap && col >= (colnr_T)curwin->w_grid.Columns
&& col >= (colnr_T)curwin->w_width && width > 0) {
&& width > 0) // use same formula as what is used in curs_columns()
/* use same formula as what is used in curs_columns() */ col -= ((col - curwin->w_grid.Columns) / width + 1) * width;
col -= ((col - curwin->w_width) / width + 1) * width; }
if (col > (int)curwin->w_leftcol) if (col > (int)curwin->w_leftcol) {
col -= curwin->w_leftcol; col -= curwin->w_leftcol;
else } else {
col = 0; col = 0;
}
curwin->w_wcol = col; curwin->w_wcol = col;
curwin->w_valid |= VALID_WCOL; curwin->w_valid |= VALID_WCOL;
@@ -760,20 +769,20 @@ void curs_columns(
*/ */
curwin->w_wrow = curwin->w_cline_row; curwin->w_wrow = curwin->w_cline_row;
int textwidth = curwin->w_width - extra; int textwidth = curwin->w_grid.Columns - extra;
if (textwidth <= 0) { if (textwidth <= 0) {
/* No room for text, put cursor in last char of window. */ // No room for text, put cursor in last char of window.
curwin->w_wcol = curwin->w_width - 1; curwin->w_wcol = curwin->w_grid.Columns - 1;
curwin->w_wrow = curwin->w_height - 1; curwin->w_wrow = curwin->w_grid.Rows - 1;
} else if (curwin->w_p_wrap } else if (curwin->w_p_wrap
&& curwin->w_width != 0 && curwin->w_grid.Columns != 0
) { ) {
width = textwidth + curwin_col_off2(); width = textwidth + curwin_col_off2();
/* long line wrapping, adjust curwin->w_wrow */ // long line wrapping, adjust curwin->w_wrow
if (curwin->w_wcol >= curwin->w_width) { if (curwin->w_wcol >= curwin->w_grid.Columns) {
/* this same formula is used in validate_cursor_col() */ // this same formula is used in validate_cursor_col()
n = (curwin->w_wcol - curwin->w_width) / width + 1; n = (curwin->w_wcol - curwin->w_grid.Columns) / width + 1;
curwin->w_wcol -= n * width; curwin->w_wcol -= n * width;
curwin->w_wrow += n; curwin->w_wrow += n;
@@ -800,7 +809,7 @@ void curs_columns(
assert(p_siso <= INT_MAX); assert(p_siso <= INT_MAX);
int off_left = startcol - curwin->w_leftcol - (int)p_siso; int off_left = startcol - curwin->w_leftcol - (int)p_siso;
int off_right = 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) { if (off_left < 0 || off_right > 0) {
int diff = (off_left < 0) ? -off_left: off_right; int diff = (off_left < 0) ? -off_left: off_right;
@@ -843,17 +852,16 @@ void curs_columns(
prev_skipcol = curwin->w_skipcol; prev_skipcol = curwin->w_skipcol;
int p_lines = 0; int p_lines = 0;
if ((curwin->w_wrow >= curwin->w_height if ((curwin->w_wrow >= curwin->w_grid.Rows
|| ((prev_skipcol > 0 || ((prev_skipcol > 0
|| curwin->w_wrow + p_so >= curwin->w_height) || curwin->w_wrow + p_so >= curwin->w_grid.Rows)
&& (p_lines = && (p_lines =
plines_win_nofill plines_win_nofill(curwin, curwin->w_cursor.lnum, false)) - 1
(curwin, curwin->w_cursor.lnum, false)) >= curwin->w_grid.Rows))
- 1 >= curwin->w_height)) && curwin->w_grid.Rows != 0
&& curwin->w_height != 0
&& curwin->w_cursor.lnum == curwin->w_topline && curwin->w_cursor.lnum == curwin->w_topline
&& width > 0 && width > 0
&& curwin->w_width != 0 && curwin->w_grid.Columns != 0
) { ) {
/* Cursor past end of screen. Happens with a single line that does /* 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 * not fit on screen. Find a skipcol to show the text around the
@@ -875,19 +883,22 @@ void curs_columns(
} }
else else
n = p_lines; 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; extra += 2;
}
if (extra == 3 || p_lines < p_so * 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; n = curwin->w_virtcol / width;
if (n > curwin->w_height / 2) if (n > curwin->w_grid.Rows / 2) {
n -= curwin->w_height / 2; n -= curwin->w_grid.Rows / 2;
else } else {
n = 0; n = 0;
/* don't skip more than necessary */ }
if (n > p_lines - curwin->w_height + 1) // don't skip more than necessary
n = p_lines - curwin->w_height + 1; if (n > p_lines - curwin->w_grid.Rows + 1) {
n = p_lines - curwin->w_grid.Rows + 1;
}
curwin->w_skipcol = n * width; curwin->w_skipcol = n * width;
} else if (extra == 1) { } else if (extra == 1) {
/* less then 'scrolloff' lines above, decrease skipcol */ /* less then 'scrolloff' lines above, decrease skipcol */
@@ -900,18 +911,20 @@ void curs_columns(
curwin->w_skipcol -= extra * width; curwin->w_skipcol -= extra * width;
} }
} else if (extra == 2) { } else if (extra == 2) {
/* less then 'scrolloff' lines below, increase skipcol */ // less then 'scrolloff' lines below, increase skipcol
endcol = (n - curwin->w_height + 1) * width; endcol = (n - curwin->w_grid.Rows + 1) * width;
while (endcol > curwin->w_virtcol) while (endcol > curwin->w_virtcol) {
endcol -= width; endcol -= width;
if (endcol > curwin->w_skipcol) }
if (endcol > curwin->w_skipcol) {
curwin->w_skipcol = endcol; curwin->w_skipcol = endcol;
}
} }
curwin->w_wrow -= curwin->w_skipcol / width; curwin->w_wrow -= curwin->w_skipcol / width;
if (curwin->w_wrow >= curwin->w_height) { if (curwin->w_wrow >= curwin->w_grid.Rows) {
/* small window, make sure cursor is in it */ // small window, make sure cursor is in it
extra = curwin->w_wrow - curwin->w_height + 1; extra = curwin->w_wrow - curwin->w_grid.Rows + 1;
curwin->w_skipcol += extra * width; curwin->w_skipcol += extra * width;
curwin->w_wrow -= extra; curwin->w_wrow -= extra;
} }
@@ -953,9 +966,9 @@ scrolldown (
validate_cursor(); /* w_wrow needs to be valid */ validate_cursor(); /* w_wrow needs to be valid */
while (line_count-- > 0) { while (line_count-- > 0) {
if (curwin->w_topfill < diff_check(curwin, curwin->w_topline) if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)
&& curwin->w_topfill < curwin->w_height - 1) { && curwin->w_topfill < curwin->w_grid.Rows - 1) {
++curwin->w_topfill; curwin->w_topfill++;
++done; done++;
} else { } else {
if (curwin->w_topline == 1) if (curwin->w_topline == 1)
break; break;
@@ -988,15 +1001,15 @@ scrolldown (
*/ */
int wrow = curwin->w_wrow; int wrow = curwin->w_wrow;
if (curwin->w_p_wrap if (curwin->w_p_wrap
&& curwin->w_width != 0 && curwin->w_grid.Columns != 0
) { ) {
validate_virtcol(); validate_virtcol();
validate_cheight(); validate_cheight();
wrow += curwin->w_cline_height - 1 - wrow += curwin->w_cline_height - 1 -
curwin->w_virtcol / curwin->w_width; curwin->w_virtcol / curwin->w_grid.Columns;
} }
bool moved = false; 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; linenr_T first;
if (hasFolding(curwin->w_cursor.lnum, &first, NULL)) { if (hasFolding(curwin->w_cursor.lnum, &first, NULL)) {
--wrow; --wrow;
@@ -1081,14 +1094,15 @@ check_topfill (
{ {
if (wp->w_topfill > 0) { if (wp->w_topfill > 0) {
int n = plines_win_nofill(wp, wp->w_topline, true); 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) { if (down && wp->w_topline > 1) {
--wp->w_topline; --wp->w_topline;
wp->w_topfill = 0; wp->w_topfill = 0;
} else { } else {
wp->w_topfill = wp->w_height - n; wp->w_topfill = wp->w_grid.Rows - n;
if (wp->w_topfill < 0) if (wp->w_topfill < 0) {
wp->w_topfill = 0; wp->w_topfill = 0;
}
} }
} }
} }
@@ -1101,12 +1115,13 @@ check_topfill (
static void max_topfill(void) static void max_topfill(void)
{ {
int n = plines_nofill(curwin->w_topline); int n = plines_nofill(curwin->w_topline);
if (n >= curwin->w_height) if (n >= curwin->w_grid.Rows) {
curwin->w_topfill = 0; curwin->w_topfill = 0;
else { } else {
curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline); curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
if (curwin->w_topfill + n > curwin->w_height) if (curwin->w_topfill + n > curwin->w_grid.Rows) {
curwin->w_topfill = curwin->w_height - n; curwin->w_topfill = curwin->w_grid.Rows - n;
}
} }
} }
@@ -1137,14 +1152,14 @@ void scrolldown_clamp(void)
else else
end_row += plines_nofill(curwin->w_topline - 1); end_row += plines_nofill(curwin->w_topline - 1);
if (curwin->w_p_wrap if (curwin->w_p_wrap
&& curwin->w_width != 0 && curwin->w_grid.Columns != 0
) { ) {
validate_cheight(); validate_cheight();
validate_virtcol(); validate_virtcol();
end_row += curwin->w_cline_height - 1 - 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) { if (can_fill) {
++curwin->w_topfill; ++curwin->w_topfill;
check_topfill(curwin, true); check_topfill(curwin, true);
@@ -1179,10 +1194,10 @@ void scrollup_clamp(void)
int start_row = curwin->w_wrow - plines_nofill(curwin->w_topline) int start_row = curwin->w_wrow - plines_nofill(curwin->w_topline)
- curwin->w_topfill; - curwin->w_topfill;
if (curwin->w_p_wrap if (curwin->w_p_wrap
&& curwin->w_width != 0 && curwin->w_grid.Columns != 0
) { ) {
validate_virtcol(); 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 (start_row >= p_so) {
if (curwin->w_topfill > 0) if (curwin->w_topfill > 0)
@@ -1336,10 +1351,12 @@ void scroll_cursor_top(int min_scroll, int always)
else else
used += plines(bot); used += plines(bot);
} }
if (used > curwin->w_height) if (used > curwin->w_grid.Rows) {
break; break;
if (top < curwin->w_topline) }
if (top < curwin->w_topline) {
scrolled += i; scrolled += i;
}
/* /*
* If scrolling is needed, scroll at least 'sj' lines. * 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" * This makes sure we get the same position when using "k" and "j"
* in a small window. * in a small window.
*/ */
if (used > curwin->w_height) { if (used > curwin->w_grid.Rows) {
scroll_cursor_halfway(false); scroll_cursor_halfway(false);
} else { } else {
/* /*
@@ -1393,10 +1410,10 @@ void scroll_cursor_top(int min_scroll, int always)
void set_empty_rows(win_T *wp, int used) void set_empty_rows(win_T *wp, int used)
{ {
wp->w_filler_rows = 0; wp->w_filler_rows = 0;
if (used == 0) if (used == 0) {
wp->w_empty_rows = 0; /* single line that doesn't fit */ wp->w_empty_rows = 0; // single line that doesn't fit
else { } else {
wp->w_empty_rows = wp->w_height - used; wp->w_empty_rows = wp->w_grid.Rows - used;
if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count) { if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count) {
wp->w_filler_rows = diff_check_fill(wp, wp->w_botline); wp->w_filler_rows = diff_check_fill(wp, wp->w_botline);
if (wp->w_empty_rows > wp->w_filler_rows) 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) { curwin->w_topline = loff.lnum) {
loff.lnum = curwin->w_topline; loff.lnum = curwin->w_topline;
topline_back(&loff); 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; break;
}
used += loff.height; used += loff.height;
curwin->w_topfill = loff.fill; curwin->w_topfill = loff.fill;
} }
@@ -1497,12 +1515,14 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
/* Add one line above */ /* Add one line above */
topline_back(&loff); topline_back(&loff);
if (loff.height == MAXCOL) if (loff.height == MAXCOL) {
used = MAXCOL; used = MAXCOL;
else } else {
used += loff.height; used += loff.height;
if (used > curwin->w_height) }
if (used > curwin->w_grid.Rows) {
break; break;
}
if (loff.lnum >= curwin->w_botline if (loff.lnum >= curwin->w_botline
&& (loff.lnum > curwin->w_botline && (loff.lnum > curwin->w_botline
|| loff.fill <= fill_below_window) || loff.fill <= fill_below_window)
@@ -1519,8 +1539,9 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
/* Add one line below */ /* Add one line below */
botline_forw(&boff); botline_forw(&boff);
used += boff.height; used += boff.height;
if (used > curwin->w_height) if (used > curwin->w_grid.Rows) {
break; break;
}
if (extra < ( if (extra < (
mouse_dragging > 0 ? mouse_dragging - 1 : mouse_dragging > 0 ? mouse_dragging - 1 :
p_so) || scrolled < min_scroll) { p_so) || scrolled < min_scroll) {
@@ -1541,14 +1562,14 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
} }
linenr_T line_count; linenr_T line_count;
/* curwin->w_empty_rows is larger, no need to scroll */ // curwin->w_empty_rows is larger, no need to scroll
if (scrolled <= 0) if (scrolled <= 0) {
line_count = 0; line_count = 0;
/* more than a screenfull, don't scroll but redraw */ // more than a screenfull, don't scroll but redraw
else if (used > curwin->w_height) } else if (used > curwin->w_grid.Rows) {
line_count = used; line_count = used;
/* scroll minimal number of lines */ // scroll minimal number of lines
else { } else {
line_count = 0; line_count = 0;
boff.fill = curwin->w_topfill; boff.fill = curwin->w_topfill;
boff.lnum = curwin->w_topline - 1; 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. * Scroll up if the cursor is off the bottom of the screen a bit.
* Otherwise put it at 1/2 of the screen. * 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); scroll_cursor_halfway(false);
else } else {
scrollup(line_count, true); scrollup(line_count, true);
}
/* /*
* If topline didn't change we need to restore w_botline and w_empty_rows * 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) { if (boff.lnum < curbuf->b_ml.ml_line_count) {
botline_forw(&boff); botline_forw(&boff);
used += boff.height; used += boff.height;
if (used > curwin->w_height) if (used > curwin->w_grid.Rows) {
break; break;
}
below += boff.height; below += boff.height;
} else { } else {
++below; /* count a "~" line */ ++below; /* count a "~" line */
@@ -1624,8 +1647,9 @@ void scroll_cursor_halfway(int atend)
used = MAXCOL; used = MAXCOL;
else else
used += loff.height; used += loff.height;
if (used > curwin->w_height) if (used > curwin->w_grid.Rows) {
break; break;
}
above += loff.height; above += loff.height;
topline = loff.lnum; topline = loff.lnum;
topfill = loff.fill; topfill = loff.fill;
@@ -1634,8 +1658,9 @@ void scroll_cursor_halfway(int atend)
if (!hasFolding(topline, &curwin->w_topline, NULL)) if (!hasFolding(topline, &curwin->w_topline, NULL))
curwin->w_topline = topline; curwin->w_topline = topline;
curwin->w_topfill = topfill; 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; curwin->w_botfill = false;
}
check_topfill(curwin, false); check_topfill(curwin, false);
curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
curwin->w_valid |= VALID_TOPLINE; curwin->w_valid |= VALID_TOPLINE;
@@ -1662,18 +1687,20 @@ void cursor_correct(void)
} }
if (curwin->w_topline == 1) { if (curwin->w_topline == 1) {
above_wanted = 0; above_wanted = 0;
int max_off = curwin->w_height / 2; int max_off = curwin->w_grid.Rows / 2;
if (below_wanted > max_off) if (below_wanted > max_off) {
below_wanted = max_off; below_wanted = max_off;
}
} }
validate_botline(); validate_botline();
if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1 if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1
&& mouse_dragging == 0 && mouse_dragging == 0
) { ) {
below_wanted = 0; below_wanted = 0;
int max_off = (curwin->w_height - 1) / 2; int max_off = (curwin->w_grid.Rows - 1) / 2;
if (above_wanted > max_off) if (above_wanted > max_off) {
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 /* Find the line just above the new topline to get the right line
* at the bottom of the window. */ * at the bottom of the window. */
n = 0; n = 0;
while (n <= curwin->w_height && loff.lnum >= 1) { while (n <= curwin->w_grid.Rows && loff.lnum >= 1) {
topline_back(&loff); topline_back(&loff);
if (loff.height == MAXCOL) if (loff.height == MAXCOL)
n = MAXCOL; n = MAXCOL;
@@ -1935,7 +1962,7 @@ int onepage(Direction dir, long count)
*/ */
static void get_scroll_overlap(lineoff_T *lp, int dir) 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) if (lp->fill > 0)
lp->height = 1; lp->height = 1;
@@ -1989,12 +2016,13 @@ void halfpage(bool flag, linenr_T Prenum)
long scrolled = 0; long scrolled = 0;
int i; int i;
if (Prenum) if (Prenum) {
curwin->w_p_scr = (Prenum > curwin->w_height) ? curwin->w_p_scr = (Prenum > curwin->w_grid.Rows) ? curwin->w_grid.Rows
curwin->w_height : Prenum; : Prenum;
}
assert(curwin->w_p_scr <= INT_MAX); assert(curwin->w_p_scr <= INT_MAX);
int n = curwin->w_p_scr <= curwin->w_height ? (int)curwin->w_p_scr int n = curwin->w_p_scr <= curwin->w_grid.Rows ? (int)curwin->w_p_scr
: curwin->w_height; : curwin->w_grid.Rows;
update_topline(); update_topline();
validate_botline(); validate_botline();

View File

@@ -3466,10 +3466,10 @@ static void display_showcmd(void)
int len; int len;
len = (int)STRLEN(showcmd_buf); len = (int)STRLEN(showcmd_buf);
if (len == 0) if (len == 0) {
showcmd_is_clear = true; showcmd_is_clear = true;
else { } else {
screen_puts(showcmd_buf, (int)Rows - 1, sc_col, 0); grid_puts(&default_grid, showcmd_buf, (int)Rows - 1, sc_col, 0);
showcmd_is_clear = false; 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 * clear the rest of an old message by outputting up to SHOWCMD_COLS
* spaces * 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 */ 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_off1 = curwin_col_off();
col_off2 = col_off1 - curwin_col_off2(); col_off2 = col_off1 - curwin_col_off2();
width1 = curwin->w_width - col_off1; width1 = curwin->w_grid.Columns - col_off1;
width2 = curwin->w_width - col_off2; width2 = curwin->w_grid.Columns - col_off2;
if (width2 == 0) { if (width2 == 0) {
width2 = 1; // Avoid divide by zero. width2 = 1; // Avoid divide by zero.
} }
if (curwin->w_width != 0) { if (curwin->w_grid.Columns != 0) {
/* // Instead of sticking at the last character of the buffer line we
* Instead of sticking at the last character of the buffer line we // try to stick in the last column of the screen.
* try to stick in the last column of the screen.
*/
if (curwin->w_curswant == MAXCOL) { if (curwin->w_curswant == MAXCOL) {
atend = true; atend = true;
validate_virtcol(); validate_virtcol();
@@ -4255,7 +4254,7 @@ dozet:
/* "zH" - scroll screen right half-page */ /* "zH" - scroll screen right half-page */
case 'H': case 'H':
cap->count1 *= curwin->w_width / 2; cap->count1 *= curwin->w_grid.Columns / 2;
FALLTHROUGH; FALLTHROUGH;
/* "zh" - scroll screen to the right */ /* "zh" - scroll screen to the right */
@@ -4270,8 +4269,8 @@ dozet:
} }
break; break;
/* "zL" - scroll screen left half-page */ // "zL" - scroll screen left half-page
case 'L': cap->count1 *= curwin->w_width / 2; case 'L': cap->count1 *= curwin->w_grid.Columns / 2;
FALLTHROUGH; FALLTHROUGH;
/* "zl" - scroll screen to the left */ /* "zl" - scroll screen to the left */
@@ -4307,11 +4306,12 @@ dozet:
col = 0; /* like the cursor is in col 0 */ col = 0; /* like the cursor is in col 0 */
else else
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col); getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
n = curwin->w_width - curwin_col_off(); n = curwin->w_grid.Columns - curwin_col_off();
if (col + l_p_siso < n) if (col + l_p_siso < n) {
col = 0; col = 0;
else } else {
col = col + l_p_siso - n + 1; col = col + l_p_siso - n + 1;
}
if (curwin->w_leftcol != col) { if (curwin->w_leftcol != col) {
curwin->w_leftcol = col; curwin->w_leftcol = col;
redraw_later(NOT_VALID); redraw_later(NOT_VALID);
@@ -5016,11 +5016,11 @@ static void nv_scroll(cmdarg_T *cap)
/* Don't count filler lines above the window. */ /* Don't count filler lines above the window. */
used -= diff_check_fill(curwin, curwin->w_topline) used -= diff_check_fill(curwin, curwin->w_topline)
- curwin->w_topfill; - curwin->w_topfill;
validate_botline(); /* make sure w_empty_rows is valid */ validate_botline(); // make sure w_empty_rows is valid
half = (curwin->w_height - curwin->w_empty_rows + 1) / 2; 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) { 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 // Count half he number of filler lines to be "below this
* line" and half to be "above the next line". */ // line" and half to be "above the next line".
if (n > 0 && used + diff_check_fill(curwin, curwin->w_topline if (n > 0 && used + diff_check_fill(curwin, curwin->w_topline
+ n) / 2 >= half) { + n) / 2 >= half) {
--n; --n;
@@ -5032,9 +5032,10 @@ static void nv_scroll(cmdarg_T *cap)
if (hasFolding(curwin->w_topline + n, NULL, &lnum)) if (hasFolding(curwin->w_topline + n, NULL, &lnum))
n = lnum - curwin->w_topline; n = lnum - curwin->w_topline;
} }
if (n > 0 && used > curwin->w_height) if (n > 0 && used > curwin->w_grid.Rows) {
--n; n--;
} else { /* (cap->cmdchar == 'H') */ }
} else { // (cap->cmdchar == 'H')
n = cap->count1 - 1; n = cap->count1 - 1;
if (hasAnyFolding(curwin)) { if (hasAnyFolding(curwin)) {
/* Count a fold for one screen line. */ /* Count a fold for one screen line. */
@@ -6747,9 +6748,9 @@ static void nv_g_cmd(cmdarg_T *cap)
oap->motion_type = kMTCharWise; oap->motion_type = kMTCharWise;
oap->inclusive = false; oap->inclusive = false;
if (curwin->w_p_wrap 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(); int width2 = width1 + curwin_col_off2();
validate_virtcol(); 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 /* Go to the middle of the screen line. When 'number' or
* 'relativenumber' is on and lines are wrapping the middle can be more * 'relativenumber' is on and lines are wrapping the middle can be more
* to the left. */ * to the left. */
if (cap->nchar == 'm') if (cap->nchar == 'm') {
i += (curwin->w_width - curwin_col_off() i += (curwin->w_grid.Columns - curwin_col_off()
+ ((curwin->w_p_wrap && i > 0) + ((curwin->w_p_wrap && i > 0)
? curwin_col_off2() : 0)) / 2; ? curwin_col_off2() : 0)) / 2;
}
coladvance((colnr_T)i); coladvance((colnr_T)i);
if (flag) { if (flag) {
do do
@@ -6808,11 +6810,11 @@ static void nv_g_cmd(cmdarg_T *cap)
oap->motion_type = kMTCharWise; oap->motion_type = kMTCharWise;
oap->inclusive = true; oap->inclusive = true;
if (curwin->w_p_wrap if (curwin->w_p_wrap
&& curwin->w_width != 0 && curwin->w_grid.Columns != 0
) { ) {
curwin->w_curswant = MAXCOL; /* so we stay at the end */ curwin->w_curswant = MAXCOL; /* so we stay at the end */
if (cap->count1 == 1) { 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(); int width2 = width1 + curwin_col_off2();
validate_virtcol(); validate_virtcol();
@@ -6838,7 +6840,7 @@ static void nv_g_cmd(cmdarg_T *cap)
} else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false) } else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false)
clearopbeep(oap); clearopbeep(oap);
} else { } 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); coladvance((colnr_T)i);
/* Make sure we stick in this column. */ /* Make sure we stick in this column. */
@@ -7953,7 +7955,7 @@ static void get_op_vcol(
colnr_T end; colnr_T end;
if (VIsual_mode != Ctrl_V if (VIsual_mode != Ctrl_V
|| (!initial && oap->end.col < curwin->w_width)) { || (!initial && oap->end.col < curwin->w_grid.Columns)) {
return; return;
} }

View File

@@ -68,12 +68,12 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed)
int kind_width; int kind_width;
int extra_width; int extra_width;
int i; int i;
int row;
int context_lines; int context_lines;
int col;
int above_row; int above_row;
int below_row; int below_row;
int redo_count = 0; int redo_count = 0;
int row;
int col;
if (!pum_is_visible) { if (!pum_is_visible) {
// To keep the code simple, we only allow changing the // 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; below_row = cmdline_row;
// anchor position: the start of the completed word // anchor position: the start of the completed word
row = curwin->w_wrow + curwin->w_winrow; row = curwin->w_wrow;
if (curwin->w_p_rl) { 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 { } else {
col = curwin->w_wincol + curwin->w_wcol; 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 (pum_external) {
if (array_changed) { if (array_changed) {
Array arr = ARRAY_DICT_INIT; 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(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_info)));
ADD(arr, ARRAY_OBJ(item)); ADD(arr, ARRAY_OBJ(item));
} }
ui_call_popupmenu_show(arr, selected, row, col); ui_call_popupmenu_show(arr, selected, row, col, grid);
} else { } else {
ui_call_popupmenu_select(selected); ui_call_popupmenu_select(selected);
} }
@@ -351,10 +358,10 @@ void pum_redraw(void)
// prepend a space if there is room // prepend a space if there is room
if (curwin->w_p_rl) { if (curwin->w_p_rl) {
if (pum_col < curwin->w_wincol + curwin->w_width - 1) { 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) { } 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. // Display each entry, use two spaces for a Tab.
@@ -416,12 +423,13 @@ void pum_redraw(void)
size++; 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(rt_start);
xfree(st); xfree(st);
col -= width; col -= width;
} else { } 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); xfree(st);
col += width; col += width;
} }
@@ -432,10 +440,11 @@ void pum_redraw(void)
// Display two spaces for a Tab. // Display two spaces for a Tab.
if (curwin->w_p_rl) { 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; col -= 2;
} else { } else {
screen_puts_len((char_u *)" ", 2, row, col, attr); grid_puts_len(&default_grid, (char_u *)" ", 2, row, col, attr);
col += 2; col += 2;
} }
totwidth += 2; totwidth += 2;
@@ -466,36 +475,37 @@ void pum_redraw(void)
} }
if (curwin->w_p_rl) { if (curwin->w_p_rl) {
screen_fill(row, row + 1, pum_col - pum_base_width - n + 1, grid_fill(&default_grid, row, row + 1, pum_col - pum_base_width - n + 1,
col + 1, ' ', ' ', attr); col + 1, ' ', ' ', attr);
col = pum_col - pum_base_width - n + 1; col = pum_col - pum_base_width - n + 1;
} else { } else {
screen_fill(row, row + 1, col, pum_col + pum_base_width + n, grid_fill(&default_grid, row, row + 1, col,
' ', ' ', attr); pum_col + pum_base_width + n, ' ', ' ', attr);
col = pum_col + pum_base_width + n; col = pum_col + pum_base_width + n;
} }
totwidth = pum_base_width + n; totwidth = pum_base_width + n;
} }
if (curwin->w_p_rl) { if (curwin->w_p_rl) {
screen_fill(row, row + 1, pum_col - pum_width + 1, col + 1, ' ', ' ', grid_fill(&default_grid, row, row + 1, pum_col - pum_width + 1, col + 1,
attr); ' ', ' ', attr);
} else { } 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 (pum_scrollbar > 0) {
if (curwin->w_p_rl) { if (curwin->w_p_rl) {
screen_putchar(' ', row, pum_col - pum_width, grid_putchar(&default_grid, ' ', row, pum_col - pum_width,
i >= thumb_pos && i < thumb_pos + thumb_heigth i >= thumb_pos && i < thumb_pos + thumb_heigth
? attr_thumb : attr_scroll); ? attr_thumb : attr_scroll);
} else { } else {
screen_putchar(' ', row, pum_col + pum_width, grid_putchar(&default_grid, ' ', row, pum_col + pum_width,
i >= thumb_pos && i < thumb_pos + thumb_heigth i >= thumb_pos && i < thumb_pos + thumb_heigth
? attr_thumb : attr_scroll); ? attr_thumb : attr_scroll);
} }
} }
screen_puts_line_flush(false); grid_puts_line_flush(&default_grid, false);
row++; row++;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,7 @@
#include "nvim/types.h" #include "nvim/types.h"
#include "nvim/buffer_defs.h" #include "nvim/buffer_defs.h"
#include "nvim/grid_defs.h"
#include "nvim/pos.h" #include "nvim/pos.h"
/* /*
@@ -20,6 +21,18 @@
#define NOT_VALID 40 /* buffer needs complete redraw */ #define NOT_VALID 40 /* buffer needs complete redraw */
#define CLEAR 50 /* screen messed up, clear it */ #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 /// Status line click definition
typedef struct { typedef struct {
enum { enum {

View File

@@ -2166,9 +2166,10 @@ showmatch(
if (!curwin->w_p_wrap) { if (!curwin->w_p_wrap) {
getvcol(curwin, lpos, NULL, &vcol, NULL); getvcol(curwin, lpos, NULL, &vcol, NULL);
} }
if (curwin->w_p_wrap || (vcol >= curwin->w_leftcol if (curwin->w_p_wrap
&& vcol < curwin->w_leftcol + curwin->w_width)) { || (vcol >= curwin->w_leftcol
mpos = *lpos; /* save the pos, update_screen() may change it */ && vcol < curwin->w_leftcol + curwin->w_grid.Columns)) {
mpos = *lpos; // save the pos, update_screen() may change it
save_cursor = curwin->w_cursor; save_cursor = curwin->w_cursor;
save_so = p_so; save_so = p_so;
save_siso = p_siso; save_siso = p_siso;

View File

@@ -13,6 +13,9 @@ typedef unsigned char char_u;
// Can hold one decoded UTF-8 character. // Can hold one decoded UTF-8 character.
typedef uint32_t u8char_T; 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; typedef struct expand expand_T;
#endif // NVIM_TYPES_H #endif // NVIM_TYPES_H

View File

@@ -57,6 +57,7 @@ static int busy = 0;
static int mode_idx = SHAPE_IDX_N; static int mode_idx = SHAPE_IDX_N;
static bool pending_mode_info_update = false; static bool pending_mode_info_update = false;
static bool pending_mode_update = false; static bool pending_mode_update = false;
static handle_T cursor_grid_handle = DEFAULT_GRID_HANDLE;
#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL #if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL
# define UI_LOG(funname, ...) # define UI_LOG(funname, ...)
@@ -196,13 +197,6 @@ void ui_refresh(void)
row = col = 0; row = col = 0;
pending_cursor_update = true; 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++) { for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
ui_ext[i] = ext_widgets[i]; ui_ext[i] = ext_widgets[i];
if (i < kUIGlobalCount) { if (i < kUIGlobalCount) {
@@ -210,6 +204,14 @@ void ui_refresh(void)
BOOLEAN_OBJ(ext_widgets[i])); 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(); ui_mode_info_set();
pending_mode_update = true; pending_mode_update = true;
ui_cursor_shape(); 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, void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol,
bool wrap) int clearattr, bool wrap)
{ {
size_t off = LineOffset[row]+(size_t)startcol; size_t off = grid->line_offset[row] + (size_t)startcol;
UI_CALL(raw_line, 1, row, startcol, endcol, clearcol, clearattr, wrap, int row_off = ui_is_external(kUIMultigrid) ? 0 : grid->row_offset;
(const schar_T *)ScreenLines+off, (const sattr_T *)ScreenAttrs+off); 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. if (p_wd) { // 'writedelay': flush & delay each time.
int old_row = row, old_col = col; int old_row = row, old_col = col;
// If'writedelay is active, we set the cursor to highlight what was drawn // 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) 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; return;
} }
row = new_row; row = new_row;
col = new_col; col = new_col;
cursor_grid_handle = handle;
pending_cursor_update = true; pending_cursor_update = true;
} }
@@ -360,8 +380,9 @@ int ui_current_col(void)
void ui_flush(void) void ui_flush(void)
{ {
cmdline_ui_flush(); cmdline_ui_flush();
win_ui_flush();
if (pending_cursor_update) { 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; pending_cursor_update = false;
} }
if (pending_mode_info_update) { if (pending_mode_info_update) {
@@ -421,3 +442,22 @@ Array ui_array(void)
} }
return all_uis; 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);
}

View File

@@ -16,6 +16,7 @@ typedef enum {
kUIWildmenu, kUIWildmenu,
#define kUIGlobalCount (kUIWildmenu+1) #define kUIGlobalCount (kUIWildmenu+1)
kUILinegrid, kUILinegrid,
kUIMultigrid,
kUIHlState, kUIHlState,
kUIExtCount, kUIExtCount,
} UIExtension; } UIExtension;
@@ -26,6 +27,7 @@ EXTERN const char *ui_ext_names[] INIT(= {
"ext_tabline", "ext_tabline",
"ext_wildmenu", "ext_wildmenu",
"ext_linegrid", "ext_linegrid",
"ext_multigrid",
"ext_hlstate", "ext_hlstate",
}); });

View File

@@ -2271,8 +2271,8 @@ static void do_intro_line(long row, char_u *mesg, int attr)
} }
} }
assert(row <= INT_MAX && col <= INT_MAX); assert(row <= INT_MAX && col <= INT_MAX);
screen_puts_len(p, l, (int)row, (int)col, grid_puts_len(&default_grid, p, l, (int)row, (int)col,
*p == '<' ? HL_ATTR(HLF_8) : attr); *p == '<' ? HL_ATTR(HLF_8) : attr);
col += clen; col += clen;
} }
} }

View File

@@ -997,6 +997,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
p_wh = i; p_wh = i;
} }
// Send the window positions to the UI
oldwin->w_pos_changed = true;
return OK; return OK;
} }
@@ -1341,6 +1344,9 @@ static void win_rotate(int upwards, int count)
(void)win_comp_pos(); (void)win_comp_pos();
} }
wp1->w_pos_changed = true;
wp2->w_pos_changed = true;
redraw_all_later(NOT_VALID); redraw_all_later(NOT_VALID);
} }
@@ -1423,6 +1429,9 @@ void win_move_after(win_T *win1, win_T *win2)
redraw_later(NOT_VALID); redraw_later(NOT_VALID);
} }
win_enter(win1, false); 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) if (help_window)
restore_snapshot(SNAP_HELP_IDX, close_curwin); restore_snapshot(SNAP_HELP_IDX, close_curwin);
curwin->w_pos_changed = true;
redraw_all_later(NOT_VALID); redraw_all_later(NOT_VALID);
return OK; return OK;
} }
@@ -3108,6 +3118,10 @@ int win_new_tabpage(int after, char_u *filename)
redraw_all_later(NOT_VALID); 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_WINNEW, NULL, NULL, false, curbuf);
apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf);
apply_autocmds(EVENT_TABNEW, filename, filename, 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; int old_off = tp->tp_firstwin->w_winrow;
win_T *next_prevwin = tp->tp_prevwin; win_T *next_prevwin = tp->tp_prevwin;
tabpage_T *old_curtab = curtab;
curtab = tp; curtab = tp;
firstwin = tp->tp_firstwin; firstwin = tp->tp_firstwin;
lastwin = tp->tp_lastwin; lastwin = tp->tp_lastwin;
topframe = tp->tp_topframe; 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 /* We would like doing the TabEnter event first, but we don't have a
* valid current window yet, which may break some commands. * valid current window yet, which may break some commands.
* This triggers autocommands, thus may make "tp" invalid. */ * 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; 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". * Go to tab page "n". For ":tab N" and "Ngt".
* When "n" is 9999 go to the last tab page. * 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; new_wp->handle = ++last_win_id;
handle_register_window(new_wp); handle_register_window(new_wp);
grid_assign_handle(&new_wp->w_grid);
// Init w: variables. // Init w: variables.
new_wp->w_vars = tv_dict_alloc(); new_wp->w_vars = tv_dict_alloc();
init_var_dict(new_wp->w_vars, &new_wp->w_winvar, VAR_SCOPE); 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); xfree(wp->w_p_cc_cols);
win_free_grid(wp, false);
if (wp != aucmd_win) if (wp != aucmd_win)
win_remove(wp, tp); win_remove(wp, tp);
if (autocmd_busy) { if (autocmd_busy) {
@@ -3970,6 +4007,20 @@ win_free (
unblock_autocmds(); 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". * 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) void win_alloc_lines(win_T *wp)
{ {
wp->w_lines_valid = 0; wp->w_lines_valid = 0;
assert(Rows >= 0); assert(wp->w_grid.Rows >= 0);
wp->w_lines = xcalloc(Rows, sizeof(wline_T)); 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_winrow = *row;
wp->w_wincol = *col; wp->w_wincol = *col;
redraw_win_later(wp, NOT_VALID); 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; *row += wp->w_height + wp->w_status_height;
*col += wp->w_width + wp->w_vsep_width; *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 * If there is extra space created between the last window and the command
* line, clear it. * line, clear it.
*/ */
if (full_screen && msg_scrolled == 0 && row < cmdline_row) if (full_screen && msg_scrolled == 0 && row < cmdline_row) {
screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); grid_fill(&default_grid, row, cmdline_row, 0, (int)Columns, ' ', ' ', 0);
}
cmdline_row = row; cmdline_row = row;
msg_row = row; msg_row = row;
msg_col = 0; msg_col = 0;
@@ -4706,7 +4759,7 @@ void win_drag_status_line(win_T *dragwin, int offset)
fr = fr->fr_next; fr = fr->fr_next;
} }
row = win_comp_pos(); 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; cmdline_row = row;
p_ch = Rows - cmdline_row; p_ch = Rows - cmdline_row;
if (p_ch < 1) if (p_ch < 1)
@@ -4866,6 +4919,8 @@ void win_new_height(win_T *wp, int height)
if (!exiting) { if (!exiting) {
scroll_to_fraction(wp, prev_height); scroll_to_fraction(wp, prev_height);
} }
wp->w_pos_changed = true;
} }
void scroll_to_fraction(win_T *wp, int prev_height) void scroll_to_fraction(win_T *wp, int prev_height)
@@ -4996,6 +5051,7 @@ void win_new_width(win_T *wp, int width)
0); 0);
} }
} }
wp->w_pos_changed = true;
} }
void win_comp_scroll(win_T *wp) void win_comp_scroll(win_T *wp)
@@ -5052,10 +5108,11 @@ void command_height(void)
/* Recompute window positions. */ /* Recompute window positions. */
(void)win_comp_pos(); (void)win_comp_pos();
/* clear the lines added to cmdline */ // clear the lines added to cmdline
if (full_screen) if (full_screen) {
screen_fill(cmdline_row, (int)Rows, 0, grid_fill(&default_grid, cmdline_row, (int)Rows, 0, (int)Columns, ' ',
(int)Columns, ' ', ' ', 0); ' ', 0);
}
msg_row = cmdline_row; msg_row = cmdline_row;
redraw_cmdline = TRUE; redraw_cmdline = TRUE;
return; 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;
}
}
}

View File

@@ -1261,6 +1261,7 @@ describe('API', function()
ext_tabline = false, ext_tabline = false,
ext_wildmenu = false, ext_wildmenu = false,
ext_linegrid = screen._options.ext_linegrid or false, ext_linegrid = screen._options.ext_linegrid or false,
ext_multigrid = false,
ext_hlstate=false, ext_hlstate=false,
height = 4, height = 4,
rgb = true, rgb = true,

View File

@@ -84,6 +84,10 @@ end
local session, loop_running, last_error local session, loop_running, last_error
local function get_session()
return session
end
local function set_session(s, keep) local function set_session(s, keep)
if session and not keep then if session and not keep then
session:close() session:close()
@@ -164,34 +168,34 @@ local function expect_msg_seq(...)
error(final_error) error(final_error)
end end
local function call_and_stop_on_error(...) local function call_and_stop_on_error(lsession, ...)
local status, result = copcall(...) -- luacheck: ignore local status, result = copcall(...) -- luacheck: ignore
if not status then if not status then
session:stop() lsession:stop()
last_error = result last_error = result
return '' return ''
end end
return result return result
end 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 local on_request, on_notification, on_setup
if request_cb then if request_cb then
function on_request(method, args) 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
end end
if notification_cb then if notification_cb then
function on_notification(method, args) 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
end end
if setup_cb then if setup_cb then
function on_setup() function on_setup()
call_and_stop_on_error(setup_cb) call_and_stop_on_error(lsession, setup_cb)
end end
end end
@@ -205,6 +209,10 @@ local function run(request_cb, notification_cb, setup_cb, timeout)
end end
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() local function stop()
session:stop() session:stop()
end end
@@ -677,6 +685,7 @@ local module = {
buffer = buffer, buffer = buffer,
bufmeths = bufmeths, bufmeths = bufmeths,
call = nvim_call, call = nvim_call,
create_callindex = create_callindex,
clear = clear, clear = clear,
command = nvim_command, command = nvim_command,
connect = connect, connect = connect,
@@ -701,6 +710,7 @@ local module = {
filter = filter, filter = filter,
funcs = funcs, funcs = funcs,
get_pathsep = get_pathsep, get_pathsep = get_pathsep,
get_session = get_session,
insert = insert, insert = insert,
iswin = iswin, iswin = iswin,
map = map, map = map,
@@ -732,6 +742,7 @@ local module = {
retry = retry, retry = retry,
rmdir = rmdir, rmdir = rmdir,
run = run, run = run,
run_session = run_session,
set_session = set_session, set_session = set_session,
set_shell_powershell = set_shell_powershell, set_shell_powershell = set_shell_powershell,
skip_fragile = skip_fragile, skip_fragile = skip_fragile,

View File

@@ -72,10 +72,10 @@ local function screen_setup(extra_rows, command, cols)
if command == default_command then if command == default_command then
-- Wait for "tty ready" to be printed before each test or the terminal may -- Wait for "tty ready" to be printed before each test or the terminal may
-- still be in canonical mode (will echo characters for example). -- still be in canonical mode (will echo characters for example).
local empty_line = (' '):rep(cols + 1) local empty_line = (' '):rep(cols)
local expected = { local expected = {
'tty ready'..(' '):rep(cols - 8), 'tty ready'..(' '):rep(cols - 9),
'{1: }' ..(' '):rep(cols), '{1: }' ..(' '):rep(cols - 1),
empty_line, empty_line,
empty_line, empty_line,
empty_line, empty_line,
@@ -85,8 +85,8 @@ local function screen_setup(extra_rows, command, cols)
table.insert(expected, empty_line) table.insert(expected, empty_line)
end end
table.insert(expected, '{3:-- TERMINAL --}' .. ((' '):rep(cols - 13))) table.insert(expected, '{3:-- TERMINAL --}' .. ((' '):rep(cols - 14)))
screen:expect(table.concat(expected, '\n')) screen:expect(table.concat(expected, '|\n')..'|')
else else
-- This eval also acts as a wait(). -- This eval also acts as a wait().
if 0 == nvim('eval', "exists('b:terminal_job_id')") then if 0 == nvim('eval', "exists('b:terminal_job_id')") then

View File

@@ -259,10 +259,10 @@ describe('tui', function()
feed_data(':echo map(nvim_list_uis(), {k,v -> sort(items(v))})\013') feed_data(':echo map(nvim_list_uis(), {k,v -> sort(items(v))})\013')
screen:expect([=[ screen:expect([=[
[[['ext_cmdline', v:false], ['ext_hlstate', v:fals| [[['ext_cmdline', v:false], ['ext_hlstate', v:fals|
e], ['ext_linegrid', v:true], ['ext_popupmenu', v:| e], ['ext_linegrid', v:true], ['ext_multigrid', v:|
false], ['ext_tabline', v:false], ['ext_wildmenu',| false], ['ext_popupmenu', v:false], ['ext_tabline'|
v:false], ['height', 6], ['rgb', v:false], ['widt| , v:false], ['ext_wildmenu', v:false], ['height', |
h', 50]]] | 6], ['rgb', v:false], ['width', 50]]] |
{10:Press ENTER or type command to continue}{1: } | {10:Press ENTER or type command to continue}{1: } |
{3:-- TERMINAL --} | {3:-- TERMINAL --} |
]=]) ]=])
@@ -401,7 +401,7 @@ describe('tui FocusGained/FocusLost', function()
-- Exit cmdline-mode. Redraws from timers/events are blocked during -- Exit cmdline-mode. Redraws from timers/events are blocked during
-- cmdline-mode, so the buffer won't be updated until we exit cmdline-mode. -- cmdline-mode, so the buffer won't be updated until we exit cmdline-mode.
feed_data('\n') feed_data('\n')
screen:expect{any='lost'..(' '):rep(46)..'\ngained'} screen:expect{any='lost'..(' '):rep(46)..'|\ngained'}
end) end)
end) end)

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,7 @@ describe('ui receives option updates', function()
ext_wildmenu=false, ext_wildmenu=false,
ext_linegrid=false, ext_linegrid=false,
ext_hlstate=false, ext_hlstate=false,
ext_multigrid=false,
} }
clear(...) clear(...)

View File

@@ -75,9 +75,11 @@ local global_helpers = require('test.helpers')
local deepcopy = global_helpers.deepcopy local deepcopy = global_helpers.deepcopy
local shallowcopy = global_helpers.shallowcopy local shallowcopy = global_helpers.shallowcopy
local helpers = require('test.functional.helpers')(nil) 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 eq = helpers.eq
local dedent = helpers.dedent local dedent = helpers.dedent
local get_session = helpers.get_session
local create_callindex = helpers.create_callindex
local inspect = require('inspect') local inspect = require('inspect')
@@ -155,6 +157,8 @@ function Screen.new(width, height)
cmdline_block = {}, cmdline_block = {},
wildmenu_items = nil, wildmenu_items = nil,
wildmenu_selected = nil, wildmenu_selected = nil,
win_position = {},
_session = nil,
_default_attr_ids = nil, _default_attr_ids = nil,
_default_attr_ignore = nil, _default_attr_ignore = nil,
_mouse_enabled = true, _mouse_enabled = true,
@@ -165,11 +169,19 @@ function Screen.new(width, height)
_new_attrs = false, _new_attrs = false,
_width = width, _width = width,
_height = height, _height = height,
_grids = {},
_cursor = { _cursor = {
row = 1, col = 1 grid = 1, row = 1, col = 1
}, },
_busy = false _busy = false,
}, Screen) }, 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 return self
end end
@@ -189,34 +201,50 @@ function Screen:set_hlstate_cterm(val)
self._hlstate_cterm = val self._hlstate_cterm = val
end end
function Screen:attach(options) function Screen:attach(options, session)
if session == nil then
session = get_session()
end
if options == nil then if options == nil then
options = {} options = {}
end end
if options.ext_linegrid == nil then if options.ext_linegrid == nil then
options.ext_linegrid = true options.ext_linegrid = true
end end
self._session = session
self._options = options self._options = options
self._clear_attrs = (options.ext_linegrid and {{},{}}) or {} self._clear_attrs = (options.ext_linegrid and {{},{}}) or {}
self:_handle_resize(self._width, self._height) 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 if self._options.rgb == nil then
-- nvim defaults to rgb=true internally, -- nvim defaults to rgb=true internally,
-- simplify test code by doing the same. -- simplify test code by doing the same.
self._options.rgb = true self._options.rgb = true
end end
if self._options.ext_multigrid then
self._options.ext_linegrid = true
end
self._session = session
end end
function Screen:detach() function Screen:detach()
uimeths.detach() self.uimeths.detach()
self._session = nil
end end
function Screen:try_resize(columns, rows) 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 end
function Screen:set_option(option, value) function Screen:set_option(option, value)
uimeths.set_option(option, value) self.uimeths.set_option(option, value)
self._options[option] = value self._options[option] = value
end end
@@ -321,7 +349,6 @@ function Screen:expect(expected, attr_ids, attr_ignore)
-- value. -- value.
grid = dedent(grid:gsub('\n[ ]+$', ''), 0) grid = dedent(grid:gsub('\n[ ]+$', ''), 0)
for row in grid:gmatch('[^\n]+') do 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) table.insert(expected_rows, row)
end end
end end
@@ -341,19 +368,11 @@ function Screen:expect(expected, attr_ids, attr_ignore)
end end
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 if self._options.ext_hlstate and self._new_attrs then
attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids or {}) attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids or {})
end end
local actual_rows = {} local actual_rows = self:render(not expected.any, attr_state)
for i = 1, self._height do
actual_rows[i] = self:_row_repr(self._rows[i], attr_state)
end
if expected.any ~= nil then if expected.any ~= nil then
-- Search for `any` anywhere in the screen lines. -- Search for `any` anywhere in the screen lines.
@@ -362,13 +381,17 @@ function Screen:expect(expected, attr_ids, attr_ignore)
return ( return (
'Failed to match any screen lines.\n' 'Failed to match any screen lines.\n'
.. 'Expected (anywhere): "' .. expected.any .. '"\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
end end
if grid ~= nil then if grid ~= nil then
-- `expected` must match the screen lines exactly. -- `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 if expected_rows[i] ~= actual_rows[i] then
local msg_expected_rows = {} local msg_expected_rows = {}
for j = 1, #expected_rows do for j = 1, #expected_rows do
@@ -378,8 +401,8 @@ function Screen:expect(expected, attr_ids, attr_ignore)
actual_rows[i] = '*' .. actual_rows[i] actual_rows[i] = '*' .. actual_rows[i]
return ( return (
'Row ' .. tostring(i) .. ' did not match.\n' 'Row ' .. tostring(i) .. ' did not match.\n'
..'Expected:\n |'..table.concat(msg_expected_rows, '|\n |')..'|\n' ..'Expected:\n |'..table.concat(msg_expected_rows, '\n |')..'\n'
..'Actual:\n |'..table.concat(actual_rows, '|\n |')..'|\n\n'..[[ ..'Actual:\n |'..table.concat(actual_rows, '\n |')..'\n\n'..[[
To print the expect() call that would assert the current screen state, use To print the expect() call that would assert the current screen state, use
screen:snapshot_util(). In case of non-deterministic failures, use screen:snapshot_util(). In case of non-deterministic failures, use
screen:redraw_debug() to show all intermediate screen states. ]]) screen:redraw_debug() to show all intermediate screen states. ]])
@@ -465,7 +488,7 @@ function Screen:_wait(check, flags)
if not err then if not err then
success_seen = true success_seen = true
if did_miminal_timeout then if did_miminal_timeout then
helpers.stop() self._session:stop()
end end
elseif success_seen and #args > 0 then elseif success_seen and #args > 0 then
failure_after_success = true failure_after_success = true
@@ -474,7 +497,7 @@ function Screen:_wait(check, flags)
return true return true
end end
run(nil, notification_cb, nil, minimal_timeout) run_session(self._session, nil, notification_cb, nil, minimal_timeout)
if not did_flush then if not did_flush then
err = "no flush received" err = "no flush received"
elseif not checked then elseif not checked then
@@ -487,7 +510,7 @@ function Screen:_wait(check, flags)
if not success_seen then if not success_seen then
did_miminal_timeout = true did_miminal_timeout = true
run(nil, notification_cb, nil, timeout-minimal_timeout) run_session(self._session, nil, notification_cb, nil, timeout-minimal_timeout)
end end
local did_warn = false local did_warn = false
@@ -547,7 +570,7 @@ function Screen:sleep(ms)
assert(method == 'redraw') assert(method == 'redraw')
self:_redraw(args) self:_redraw(args)
end end
run(nil, notification_cb, nil, ms) run_session(self._session, nil, notification_cb, nil, ms)
end end
function Screen:_redraw(updates) function Screen:_redraw(updates)
@@ -579,6 +602,22 @@ function Screen:set_on_event_handler(callback)
end end
function Screen:_handle_resize(width, height) 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 = {} local rows = {}
for _ = 1, height do for _ = 1, height do
local cols = {} local cols = {}
@@ -587,22 +626,36 @@ function Screen:_handle_resize(width, height)
end end
table.insert(rows, cols) table.insert(rows, cols)
end end
self._cursor.row = 1 if grid > 1 and self._grids[grid] ~= nil then
self._cursor.col = 1 local old = self._grids[grid]
self._rows = rows for i = 1, min(height,old.height) do
self._width = width for j = 1, min(width,old.width) do
self._height = height rows[i][j] = old.rows[i][j]
self._scroll_region = { end
top = 1, bot = height, left = 1, right = width 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 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 end
function Screen:_handle_grid_resize(grid, width, height) function Screen:_handle_win_scroll_over_reset()
assert(grid == 1) self.scroll_over = false
self:_handle_resize(width, height) end
function Screen:_handle_flush()
end end
function Screen:_reset() function Screen:_reset()
@@ -641,20 +694,27 @@ function Screen:_handle_clear()
-- newer clients, to check we remain compatible with both kind of clients, -- newer clients, to check we remain compatible with both kind of clients,
-- ensure the scroll region is in a reset state. -- ensure the scroll region is in a reset state.
local expected_region = { 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) eq(expected_region, self._scroll_region)
self:_clear_block(1, self._height, 1, self._width) self:_handle_grid_clear(1)
end end
function Screen:_handle_grid_clear(grid) function Screen:_handle_grid_clear(grid)
assert(grid == 1) self:_clear_block(self._grids[grid], 1, self._grids[grid].height, 1, self._grids[grid].width)
self:_clear_block(1, self._height, 1, self._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 end
function Screen:_handle_eol_clear() function Screen:_handle_eol_clear()
local row, col = self._cursor.row, self._cursor.col 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 end
function Screen:_handle_cursor_goto(row, col) function Screen:_handle_cursor_goto(row, col)
@@ -663,7 +723,7 @@ function Screen:_handle_cursor_goto(row, col)
end end
function Screen:_handle_grid_cursor_goto(grid, row, col) function Screen:_handle_grid_cursor_goto(grid, row, col)
assert(grid == 1) self._cursor.grid = grid
self._cursor.row = row + 1 self._cursor.row = row + 1
self._cursor.col = col + 1 self._cursor.col = col + 1
end end
@@ -704,13 +764,18 @@ function Screen:_handle_scroll(count)
self:_handle_grid_scroll(1, top-1, bot, left-1, right, count, 0) self:_handle_grid_scroll(1, top-1, bot, left-1, right, count, 0)
end 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 top = top+1
left = left+1 left = left+1
assert(grid == 1)
assert(cols == 0) assert(cols == 0)
local grid = self._grids[g]
local start, stop, step local start, stop, step
if rows > 0 then if rows > 0 then
start = top start = top
stop = bot - rows stop = bot - rows
@@ -723,8 +788,8 @@ function Screen:_handle_grid_scroll(grid, top, bot, left, right, rows, cols)
-- shift scroll region -- shift scroll region
for i = start, stop, step do for i = start, stop, step do
local target = self._rows[i] local target = grid.rows[i]
local source = self._rows[i + rows] local source = grid.rows[i + rows]
for j = left, right do for j = left, right do
target[j].text = source[j].text target[j].text = source[j].text
target[j].attrs = source[j].attrs 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 -- clear invalid rows
for i = stop + step, stop + rows, step do 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
end end
@@ -744,13 +809,35 @@ function Screen:_handle_hl_attr_define(id, rgb_attrs, cterm_attrs, info)
self._new_attrs = true self._new_attrs = true
end 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) function Screen:_handle_highlight_set(attrs)
self._attrs = attrs self._attrs = attrs
end end
function Screen:_handle_put(str) function Screen:_handle_put(str)
assert(not self._options.ext_linegrid) 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.text = str
cell.attrs = self._attrs cell.attrs = self._attrs
cell.hl_id = -1 cell.hl_id = -1
@@ -759,8 +846,7 @@ end
function Screen:_handle_grid_line(grid, row, col, items) function Screen:_handle_grid_line(grid, row, col, items)
assert(self._options.ext_linegrid) assert(self._options.ext_linegrid)
assert(grid == 1) local line = self._grids[grid].rows[row+1]
local line = self._rows[row+1]
local colpos = col+1 local colpos = col+1
local hl = self._clear_attrs local hl = self._clear_attrs
local hl_id = 0 local hl_id = 0
@@ -887,45 +973,68 @@ function Screen:_handle_wildmenu_hide()
self.wildmenu_items, self.wildmenu_pos = nil, nil self.wildmenu_items, self.wildmenu_pos = nil, nil
end end
function Screen:_clear_block(top, bot, left, right) function Screen:_clear_block(grid, top, bot, left, right)
for i = top, bot do for i = top, bot do
self:_clear_row_section(i, left, right) self:_clear_row_section(grid, i, left, right)
end end
end end
function Screen:_clear_row_section(rownum, startcol, stopcol) function Screen:_clear_row_section(grid, rownum, startcol, stopcol)
local row = self._rows[rownum] local row = grid.rows[rownum]
for i = startcol, stopcol do for i = startcol, stopcol do
row[i].text = ' ' row[i].text = ' '
row[i].attrs = self._clear_attrs row[i].attrs = self._clear_attrs
end end
end end
function Screen:_row_repr(row, attr_state) function Screen:_row_repr(gridnr, rownr, attr_state, cursor)
local rv = {} local rv = {}
local current_attr_id local current_attr_id
for i = 1, self._width do local i = 1
local attrs = row[i].attrs local has_windows = self._options.ext_multigrid and gridnr == 1
if self._options.ext_linegrid then if self.scroll_over and self.scroll_over_pos < rownr then
attrs = attrs[(self._options.rgb and 1) or 2] 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 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 if not did_window then
-- close current attribute bracket, add it before any whitespace local attrs = row[i].attrs
-- up to the current cell if self._options.ext_linegrid then
-- table.insert(rv, backward_find_meaningful(rv, i), '}') attrs = attrs[(self._options.rgb and 1) or 2]
table.insert(rv, '}') end
current_attr_id = nil 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 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 end
if current_attr_id then if current_attr_id then
table.insert(rv, '}') table.insert(rv, '}')
@@ -997,7 +1106,23 @@ function Screen:redraw_debug(attrs, ignore, timeout)
if timeout == nil then if timeout == nil then
timeout = 250 timeout = 250
end 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 end
function Screen:print_snapshot(attrs, ignore) 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) attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids)
end end
local lines = {} local lines = self:render(true, attr_state, true)
for i = 1, self._height do
table.insert(lines, " "..self:_row_repr(self._rows[i], attr_state).."|")
end
local ext_state = self:_extstate_repr(attr_state) local ext_state = self:_extstate_repr(attr_state)
local keys = false local keys = false

View File

@@ -165,7 +165,7 @@ describe("'wildmenu'", function()
feed([[:<Tab>]]) -- Invoke wildmenu. feed([[:<Tab>]]) -- Invoke wildmenu.
-- Check only the last 2 lines, because the shell output is -- Check only the last 2 lines, because the shell output is
-- system-dependent. -- system-dependent.
expect_stay_unchanged{any='! # & < = > @ > \n:!^'} expect_stay_unchanged{any='! # & < = > @ > |\n:!^'}
end) end)
end) end)

View File

@@ -49,8 +49,8 @@ local check_logs_useless_lines = {
local function eq(expected, actual, ctx) local function eq(expected, actual, ctx)
return assert.are.same(expected, actual, ctx) return assert.are.same(expected, actual, ctx)
end end
local function neq(expected, actual) local function neq(expected, actual, context)
return assert.are_not.same(expected, actual) return assert.are_not.same(expected, actual, context)
end end
local function ok(res) local function ok(res)
return assert.is_true(res) return assert.is_true(res)