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
# API level
set(NVIM_API_LEVEL 5) # Bump this after any API change.
set(NVIM_API_LEVEL 6) # Bump this after any API change.
set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change.
set(NVIM_API_PRERELEASE false)
set(NVIM_API_PRERELEASE true)
file(TO_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR}/.git FORCED_GIT_DIR)
include(GetGitRevisionDescription)

View File

@@ -12,9 +12,10 @@ Nvim UI protocol *ui*
UI Events *ui-events*
GUIs can be implemented as external processes communicating with Nvim over the
RPC API. The UI model consists of a terminal-like grid with a single,
monospace font size. Some elements (UI "widgets") can be drawn separately from
the grid ("externalized").
RPC API. The default UI model consists of a terminal-like grid with a single,
monospace font size. The UI can opt-in to have windows drawn on separate
grids, as well as to have some elements (UI "widgets") be drawn by the UI
itself rather than by nvim ("externalized").
*ui-options*
@@ -32,6 +33,7 @@ a dictionary with these (optional) keys:
`ext_cmdline` Externalize the cmdline. |ui-cmdline|
`ext_wildmenu` Externalize the wildmenu. |ui-wildmenu|
`ext_linegrid` Use new revision of the grid events. |ui-linegrid|
`ext_multigrid` Use per-window grid based events. |ui-multigrid|
`ext_hlstate` Use detailed highlight state. |ui-hlstate|
Specifying a non-existent option is an error. UIs can check the |api-metadata|
@@ -46,7 +48,7 @@ Each update event is itself an array whose first element is the event name and
remaining elements are event-parameter tuples. This allows multiple events of
the same kind to be sent in a row without the event name being repeated. This
batching is mostly used for "grid_line", because each "grid_line" event puts
contents in one screen line, but clients must be prepared for multiple argument
contents in one grid line, but clients must be prepared for multiple argument
sets being batched for all event kinds.
Events must be handled in-order. A "flush" event is sent when nvim is done
@@ -61,10 +63,12 @@ By default, Nvim sends |ui-global| and |ui-grid-old| events; these suffice to
implement a terminal-like interface. However there are two revisions of the
grid part of the protocol. The newer revision |ui-linegrid|, enabled by
`ext_linegrid` option, has a more effecient representation of text (especially
highlighted text), and room for futher enhancements that will use
multiple grids. The older revision is available and used by default only for
backwards compatibility reasons. New UIs are strongly recommended to use
|ui-linegrid|, as further protocol extensions will require it.
highlighted text), and allows extensions that use multiple grids.
The older revision is available and used by default only for backwards
compatibility reasons. New UIs are strongly recommended to use |ui-linegrid|,
as further protocol extensions require it. The |ui-multigrid| extension
enables |ui-linegrid| implicitly.
Nvim optionally sends screen elements "semantically" as structured events
instead of raw grid-lines, controlled by |ui-ext-options|. The UI must present
@@ -220,7 +224,8 @@ Most of these events take a `grid` index as first parameter. Grid 1 is the
global grid used by default for the entire editor screen state. Grids other
than that will be defined by future extensions. Just activating the
`ext_linegrid` option by itself will never cause any additional grids to be
created.
created. To enable per-window grids, `ext_multigrid` option should be set (see
|ui-multigrid|).
Highlight attribute groups are predefined. UIs should maintain a table to map
numerical highlight `id`:s to the actual attributes.
@@ -475,19 +480,66 @@ because 'winhighlight' was used. UI items will be transmitted, even if the
highlight group is cleared, so `ui_name` can always be used to reliably identify
screen elements, even if no attributes have been applied.
==============================================================================
Multigrid Events *ui-multigrid*
Only sent if `ext_multigrid` option is set in |ui-options|. Enables the
`ext_linegrid` extension implicitly.
The multigrid extension gives the UIs more control over how windows are
displayed. The UIs receive updates on a separate grid for each window. The UIs
can set the grid size independently of how much space the window occupies on
the global layout. This enables the UIs to set a different font size for each
window if the UI so desires. The UI can also reserve space around the border
of the window for its own elements, for instance scrollbars from the UI
toolkit.
By default, the grid size is handled by nvim and set to the outer grid size
(i.e. the size of the window frame in nvim) whenever the split is created.
Once a UI sets a grid size, nvim does not handle the size for that grid and
the UI must change the grid size whenever the outer size is changed. To
delegate the handling of grid size back to nvim, the UIs should request the
size (0, 0).
A window can be hidden and redisplayed without its grid being deallocated.
This can happen multiple times for the same window, for instance when switching
tabs.
["win_pos", grid, win, start_row, start_col, width, height]
Set the position and size of the grid in nvim (i.e. the outer grid
size). If the window was previously hidden, it should now be shown
again.
["win_hide", grid]
Stop displaying the window.
["win_scroll_over_start"]
Hint that following `grid_scroll` on the default grid should
scroll over windows. This is a temporary workaround to allow
UIs to use the builtin message drawing. Later on, messages will be
drawn on a dedicated grid.
["win_scroll_over_reset"]
Hint that scrolled over windows should be redrawn again, and not be
overdrawn by default grid scrolling anymore.
See |ui-linegrid| for grid events.
See |nvim_ui_try_resize_grid| in |api-ui| to request changing the grid size.
==============================================================================
Popupmenu Events *ui-popupmenu*
Only sent if `ext_popupmenu` option is set in |ui-options|
["popupmenu_show", items, selected, row, col]
["popupmenu_show", items, selected, row, col, grid]
Show |popupmenu-completion|. `items` is an array of completion items
to show; each item is an array of the form [word, kind, menu, info] as
defined at |complete-items|, except that `word` is replaced by `abbr`
if present. `selected` is the initially-selected item, a zero-based
index into the array of items (-1 if no item is selected). `row` and
`col` give the anchor position, where the first character of the
completed word will be.
completed word will be. When |ui-multigrid| is used, `grid` is the
grid for the anchor position.
["popupmenu_select", selected]
Select an item in the current popupmenu. `selected` is a zero-based

View File

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

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;
}
@@ -245,6 +245,28 @@ static void ui_set_option(UI *ui, bool init, String name, Object value,
name.data);
}
/// Tell nvim to resize a grid. Nvim sends grid_resize event with the
/// requested grid size is within size limits and with maximum allowed size
/// otherwise.
///
/// On invalid grid handle, fails with error.
///
/// @param grid The handle of the grid to be changed.
/// @param width The new requested width.
/// @param height The new requested height.
void nvim_ui_try_resize_grid(uint64_t channel_id, Integer grid, Integer width,
Integer height, Error *error)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY
{
if (!pmap_has(uint64_t)(connected_uis, channel_id)) {
api_set_error(error, kErrorTypeException,
"UI not attached to channel: %" PRId64, channel_id);
return;
}
ui_grid_resize((handle_T)grid, (int)width, (int)height, error);
}
/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush().
static void push_call(UI *ui, const char *name, Array args)
{

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,
Integer left, Integer right, Integer rows, Integer cols)
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
void grid_destroy(Integer grid)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void popupmenu_show(Array items, Integer selected, Integer row, Integer col)
void win_pos(Integer grid, Integer win, Integer startrow,
Integer startcol, Integer width, Integer height)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void win_hide(Integer grid)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void win_scroll_over_start(void)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void win_scroll_over_reset(void)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void popupmenu_show(Array items, Integer selected,
Integer row, Integer col, Integer grid)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void popupmenu_hide(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;

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 ret = ARRAY_DICT_INIT;
if (row < 0 || row >= screen_Rows
|| col < 0 || col >= screen_Columns) {
if (row < 0 || row >= default_grid.Rows
|| col < 0 || col >= default_grid.Columns) {
return ret;
}
size_t off = LineOffset[(size_t)row] + (size_t)col;
ADD(ret, STRING_OBJ(cstr_to_string((char *)ScreenLines[off])));
int attr = ScreenAttrs[off];
size_t off = default_grid.line_offset[(size_t)row] + (size_t)col;
ADD(ret, STRING_OBJ(cstr_to_string((char *)default_grid.chars[off])));
int attr = default_grid.attrs[off];
ADD(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, err)));
// will not work first time
if (!highlight_use_hlstate()) {

View File

@@ -18,6 +18,8 @@ typedef struct {
// for garray_T
#include "nvim/garray.h"
// for ScreenGrid
#include "nvim/grid_defs.h"
// for HLF_COUNT
#include "nvim/highlight_defs.h"
// for pos_T, lpos_T and linenr_T
@@ -1183,6 +1185,9 @@ struct window_S {
int w_tagstackidx; /* idx just below active entry */
int w_tagstacklen; /* number of tags on stack */
ScreenGrid w_grid; // the grid specific to the window
bool w_pos_changed; // true if window position changed
/*
* w_fraction is the fractional row of the cursor within the window, from
* 0 at the top row to FRACTION_MULT at the last row.

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

View File

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

View File

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

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

View File

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

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 */
if (wp->w_height + wp->w_status_height < topframe->fr_height
&& (fprintf(fd,
"exe '%dresize ' . ((&lines * %" PRId64
" + %" PRId64 ") / %" PRId64 ")",
n, (int64_t)wp->w_height,
(int64_t)(Rows / 2), (int64_t)Rows) < 0
"exe '%dresize ' . ((&lines * %" PRId64
" + %" PRId64 ") / %" PRId64 ")",
n, (int64_t)wp->w_grid.Rows,
(int64_t)(Rows / 2), (int64_t)Rows) < 0
|| put_eol(fd) == FAIL))
return FAIL;
@@ -9459,8 +9459,8 @@ put_view(
" * winheight(0) + %" PRId64 ") / %" PRId64 ")",
(int64_t)wp->w_cursor.lnum,
(int64_t)(wp->w_cursor.lnum - wp->w_topline),
(int64_t)(wp->w_height / 2),
(int64_t)wp->w_height) < 0
(int64_t)(wp->w_grid.Rows / 2),
(int64_t)wp->w_grid.Rows) < 0
|| put_eol(fd) == FAIL
|| put_line(fd, "if s:l < 1 | let s:l = 1 | endif") == FAIL
|| put_line(fd, "exe s:l") == FAIL

View File

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

View File

@@ -91,9 +91,10 @@ EXTERN struct nvim_stats_s {
/*
* Number of Rows and Columns in the screen.
* Must be long to be able to use them as options in option.c.
* Note: Use screen_Rows and screen_Columns to access items in ScreenLines[].
* They may have different values when the screen wasn't (re)allocated yet
* after setting Rows or Columns (e.g., when starting up).
* Note: Use default_grid.Rows and default_grid.Columns to access items in
* default_grid.chars[]. They may have different values when the screen
* wasn't (re)allocated yet after setting Rows or Columns (e.g., when starting
* up).
*/
#define DFLT_COLS 80 // default value for 'columns'
#define DFLT_ROWS 24 // default value for 'lines'
@@ -128,45 +129,6 @@ typedef off_t off_T;
# endif
#endif
/*
* The characters and attributes cached for the screen.
*/
typedef char_u schar_T[(MAX_MCO+1) * 4 + 1];
typedef int16_t sattr_T;
/// ScreenLines[] contains a copy of the whole screen, as it currently is
/// displayed. It is a single block of screen cells, the size of the screen
/// plus one line. The extra line used as a buffer while redrawing a window
/// line, so it can be compared with the previous state of that line. This way
/// we can avoid sending bigger updates than neccessary to the Ul layer.
///
/// Screen cells are stored as NUL-terminated UTF-8 strings, and a cell can
/// contain up to MAX_MCO composing characters after the base character.
/// The composing characters are to be drawn on top of the original character.
/// The content after the NUL is not defined (so comparison must be done a
/// single cell at a time). Double-width characters are stored in the left cell,
/// and the right cell should only contain the empty string. When a part of the
/// screen is cleared, the cells should be filled with a single whitespace char.
///
/// ScreenAttrs[] contains the highlighting attribute for each cell.
/// LineOffset[n] is the offset from ScreenLines[] and ScreenAttrs[] for the
/// start of line 'n'. These offsets are in general not linear, as full screen
/// scrolling is implemented by rotating the offsets in the LineOffset array.
/// LineWraps[] is an array of boolean flags indicating if the screen line wraps
/// to the next line. It can only be true if a window occupies the entire screen
/// width.
///
///
/// Note: before the screen is initialized and when out of memory these can be
/// NULL.
EXTERN schar_T *ScreenLines INIT(= NULL);
EXTERN sattr_T *ScreenAttrs INIT(= NULL);
EXTERN unsigned *LineOffset INIT(= NULL);
EXTERN char_u *LineWraps INIT(= NULL); /* line wraps to next line */
EXTERN int screen_Rows INIT(= 0); /* actual size of ScreenLines[] */
EXTERN int screen_Columns INIT(= 0); /* actual size of ScreenLines[] */
/*
* When vgetc() is called, it sets mod_mask to the set of modifiers that are
* held down based on the MOD_MASK_* symbols that are read first.
@@ -228,11 +190,8 @@ EXTERN int compl_cont_status INIT(= 0);
# define CONT_LOCAL 32 /* for ctrl_x_mode 0, ^X^P/^X^N do a local
* expansion, (eg use complete=.) */
/*
* Functions for putting characters in the command line,
* while keeping ScreenLines[] updated.
*/
EXTERN int cmdmsg_rl INIT(= FALSE); /* cmdline is drawn right to left */
// state for putting characters in the message area
EXTERN int cmdmsg_rl INIT(= false); // cmdline is drawn right to left
EXTERN int msg_col;
EXTERN int msg_row;
EXTERN int msg_scrolled; /* Number of screen lines that windows have

59
src/nvim/grid_defs.h Normal file
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)
{
screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
screen_puts(s, (int)Rows - 1, 0, HL_ATTR(HLF_R));
grid_fill(&default_grid, (int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ',
0);
grid_puts(&default_grid, s, (int)Rows - 1, 0, HL_ATTR(HLF_R));
ui_flush();
}

View File

@@ -98,7 +98,7 @@ static int get_attr_entry(HlEntry entry)
return id;
}
/// When a UI connects, we need to send it the table of higlights used so far.
/// When a UI connects, we need to send it the table of highlights used so far.
void ui_send_all_hls(UI *ui)
{
for (size_t i = 1; i < kv_size(attr_entries); i++) {
@@ -212,13 +212,7 @@ void clear_hl_tables(bool reinit)
map_clear(int, int)(combine_attr_entries);
highlight_attr_set_all();
highlight_changed();
redraw_all_later(NOT_VALID);
if (ScreenAttrs) {
// the meaning of 0 doesn't change anyway
// but the rest must be retransmitted
memset(ScreenAttrs, 0,
sizeof(*ScreenAttrs) * (size_t)(screen_Rows * screen_Columns));
}
screen_invalidate_highlights();
} else {
kv_destroy(attr_entries);
map_free(HlEntry, int)(attr_entry_ids);

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

View File

@@ -556,13 +556,6 @@ size_t mb_string2cells(const char_u *str)
return clen;
}
/// Return number of display cells for char at ScreenLines[off].
/// We make sure that the offset used is less than "max_off".
int utf_off2cells(unsigned off, unsigned max_off)
{
return (off + 1 < max_off && ScreenLines[off + 1][0] == 0) ? 2 : 1;
}
/// Convert a UTF-8 byte sequence to a wide character
///
/// If the sequence is illegal or truncated by a NUL then the first byte is
@@ -1822,32 +1815,6 @@ const char *mb_unescape(const char **const pp)
return NULL;
}
/*
* Return true if the character at "row"/"col" on the screen is the left side
* of a double-width character.
* Caller must make sure "row" and "col" are not invalid!
*/
bool mb_lefthalve(int row, int col)
{
return utf_off2cells(LineOffset[row] + col,
LineOffset[row] + screen_Columns) > 1;
}
/*
* Correct a position on the screen, if it's the right half of a double-wide
* char move it to the left half. Returns the corrected column.
*/
int mb_fix_col(int col, int row)
{
col = check_col(col);
row = check_row(row);
if (ScreenLines != NULL && col > 0
&& ScreenLines[LineOffset[row] + col][0] == 0) {
return col - 1;
}
return col;
}
/*
* Skip the Vim specific head of a 'encoding' name.
@@ -2524,23 +2491,3 @@ char_u * string_convert_ext(const vimconv_T *const vcp, char_u *ptr,
return retval;
}
// Check bounds for column number
static int check_col(int col)
{
if (col < 0)
return 0;
if (col >= screen_Columns)
return screen_Columns - 1;
return col;
}
// Check bounds for row number
static int check_row(int row)
{
if (row < 0)
return 0;
if (row >= screen_Rows)
return screen_Rows - 1;
return row;
}

View File

@@ -19,13 +19,6 @@
#define MB_BYTE2LEN(b) utf8len_tab[b]
#define MB_BYTE2LEN_CHECK(b) (((b) < 0 || (b) > 255) ? 1 : utf8len_tab[b])
/// Maximum value for 'maxcombine'
///
/// At most that number of composing characters may be attached to the leading
/// character by various `utfc_*` functions. Note that some functions do not
/// have this limit.
enum { MAX_MCO = 6 };
// max length of an unicode char
#define MB_MAXCHAR 6

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,7 @@
#include "nvim/types.h"
#include "nvim/buffer_defs.h"
#include "nvim/grid_defs.h"
#include "nvim/pos.h"
/*
@@ -20,6 +21,18 @@
#define NOT_VALID 40 /* buffer needs complete redraw */
#define CLEAR 50 /* screen messed up, clear it */
/// By default, all widows are draw on a single rectangular grid, represented by
/// this ScreenGrid instance. In multigrid mode each window will have its own
/// grid, then this is only used for global screen elements that hasn't been
/// externalized.
///
/// Note: before the screen is initialized and when out of memory these can be
/// NULL.
EXTERN ScreenGrid default_grid INIT(= { 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0,
0, 0, 0 });
#define DEFAULT_GRID_HANDLE 1 // handle for the default_grid
/// Status line click definition
typedef struct {
enum {

View File

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

View File

@@ -13,6 +13,9 @@ typedef unsigned char char_u;
// Can hold one decoded UTF-8 character.
typedef uint32_t u8char_T;
// Opaque handle used by API clients to refer to various objects in vim
typedef int handle_T;
typedef struct expand expand_T;
#endif // NVIM_TYPES_H

View File

@@ -57,6 +57,7 @@ static int busy = 0;
static int mode_idx = SHAPE_IDX_N;
static bool pending_mode_info_update = false;
static bool pending_mode_update = false;
static handle_T cursor_grid_handle = DEFAULT_GRID_HANDLE;
#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL
# define UI_LOG(funname, ...)
@@ -196,13 +197,6 @@ void ui_refresh(void)
row = col = 0;
pending_cursor_update = true;
ui_default_colors_set();
int save_p_lz = p_lz;
p_lz = false; // convince redrawing() to return true ...
screen_resize(width, height);
p_lz = save_p_lz;
for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
ui_ext[i] = ext_widgets[i];
if (i < kUIGlobalCount) {
@@ -210,6 +204,14 @@ void ui_refresh(void)
BOOLEAN_OBJ(ext_widgets[i]));
}
}
ui_default_colors_set();
int save_p_lz = p_lz;
p_lz = false; // convince redrawing() to return true ...
screen_resize(width, height);
p_lz = save_p_lz;
ui_mode_info_set();
pending_mode_update = true;
ui_cursor_shape();
@@ -315,12 +317,18 @@ void ui_set_ext_option(UI *ui, UIExtension ext, bool active)
}
}
void ui_line(int row, int startcol, int endcol, int clearcol, int clearattr,
bool wrap)
void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol,
int clearattr, bool wrap)
{
size_t off = LineOffset[row]+(size_t)startcol;
UI_CALL(raw_line, 1, row, startcol, endcol, clearcol, clearattr, wrap,
(const schar_T *)ScreenLines+off, (const sattr_T *)ScreenAttrs+off);
size_t off = grid->line_offset[row] + (size_t)startcol;
int row_off = ui_is_external(kUIMultigrid) ? 0 : grid->row_offset;
int col_off = ui_is_external(kUIMultigrid) ? 0 : grid->col_offset;
UI_CALL(raw_line, grid->handle, row_off + row, col_off + startcol,
col_off + endcol, col_off + clearcol, clearattr, wrap,
(const schar_T *)grid->chars + off,
(const sattr_T *)grid->attrs + off);
if (p_wd) { // 'writedelay': flush & delay each time.
int old_row = row, old_col = col;
// If'writedelay is active, we set the cursor to highlight what was drawn
@@ -334,11 +342,23 @@ void ui_line(int row, int startcol, int endcol, int clearcol, int clearattr,
void ui_cursor_goto(int new_row, int new_col)
{
if (new_row == row && new_col == col) {
ui_grid_cursor_goto(&default_grid, new_row, new_col);
}
void ui_grid_cursor_goto(ScreenGrid *grid, int new_row, int new_col)
{
new_row += ui_is_external(kUIMultigrid) ? 0 : grid->row_offset;
new_col += ui_is_external(kUIMultigrid) ? 0 : grid->col_offset;
int handle = ui_is_external(kUIMultigrid) ? grid->handle
: DEFAULT_GRID_HANDLE;
if (new_row == row && new_col == col && handle == cursor_grid_handle) {
return;
}
row = new_row;
col = new_col;
cursor_grid_handle = handle;
pending_cursor_update = true;
}
@@ -360,8 +380,9 @@ int ui_current_col(void)
void ui_flush(void)
{
cmdline_ui_flush();
win_ui_flush();
if (pending_cursor_update) {
ui_call_grid_cursor_goto(1, row, col);
ui_call_grid_cursor_goto(cursor_grid_handle, row, col);
pending_cursor_update = false;
}
if (pending_mode_info_update) {
@@ -421,3 +442,22 @@ Array ui_array(void)
}
return all_uis;
}
void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error)
{
if (grid_handle == DEFAULT_GRID_HANDLE) {
screen_resize(width, height);
return;
}
win_T *wp = get_win_by_grid_handle(grid_handle);
if (wp == NULL) {
api_set_error(error, kErrorTypeValidation,
"No window with the given handle");
return;
}
wp->w_grid.requested_rows = (int)height;
wp->w_grid.requested_cols = (int)width;
redraw_win_later(wp, SOME_VALID);
}

View File

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

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);
screen_puts_len(p, l, (int)row, (int)col,
*p == '<' ? HL_ATTR(HLF_8) : attr);
grid_puts_len(&default_grid, p, l, (int)row, (int)col,
*p == '<' ? HL_ATTR(HLF_8) : attr);
col += clen;
}
}

View File

@@ -997,6 +997,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
p_wh = i;
}
// Send the window positions to the UI
oldwin->w_pos_changed = true;
return OK;
}
@@ -1341,6 +1344,9 @@ static void win_rotate(int upwards, int count)
(void)win_comp_pos();
}
wp1->w_pos_changed = true;
wp2->w_pos_changed = true;
redraw_all_later(NOT_VALID);
}
@@ -1423,6 +1429,9 @@ void win_move_after(win_T *win1, win_T *win2)
redraw_later(NOT_VALID);
}
win_enter(win1, false);
win1->w_pos_changed = true;
win2->w_pos_changed = true;
}
/*
@@ -2059,6 +2068,7 @@ int win_close(win_T *win, bool free_buf)
if (help_window)
restore_snapshot(SNAP_HELP_IDX, close_curwin);
curwin->w_pos_changed = true;
redraw_all_later(NOT_VALID);
return OK;
}
@@ -3108,6 +3118,10 @@ int win_new_tabpage(int after, char_u *filename)
redraw_all_later(NOT_VALID);
if (ui_is_external(kUIMultigrid)) {
tabpage_check_windows(tp);
}
apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf);
apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf);
apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf);
@@ -3299,11 +3313,16 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au
int old_off = tp->tp_firstwin->w_winrow;
win_T *next_prevwin = tp->tp_prevwin;
tabpage_T *old_curtab = curtab;
curtab = tp;
firstwin = tp->tp_firstwin;
lastwin = tp->tp_lastwin;
topframe = tp->tp_topframe;
if (old_curtab != curtab && ui_is_external(kUIMultigrid)) {
tabpage_check_windows(old_curtab);
}
/* We would like doing the TabEnter event first, but we don't have a
* valid current window yet, which may break some commands.
* This triggers autocommands, thus may make "tp" invalid. */
@@ -3339,6 +3358,20 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au
must_redraw = NOT_VALID;
}
/// called when changing current tabpage from old_curtab to curtab
static void tabpage_check_windows(tabpage_T *old_curtab)
{
win_T *next_wp;
for (win_T *wp = old_curtab->tp_firstwin; wp; wp = next_wp) {
next_wp = wp->w_next;
wp->w_pos_changed = true;
}
for (win_T *wp = firstwin; wp; wp = wp->w_next) {
wp->w_pos_changed = true;
}
}
/*
* Go to tab page "n". For ":tab N" and "Ngt".
* When "n" is 9999 go to the last tab page.
@@ -3860,6 +3893,8 @@ static win_T *win_alloc(win_T *after, int hidden)
new_wp->handle = ++last_win_id;
handle_register_window(new_wp);
grid_assign_handle(&new_wp->w_grid);
// Init w: variables.
new_wp->w_vars = tv_dict_alloc();
init_var_dict(new_wp->w_vars, &new_wp->w_winvar, VAR_SCOPE);
@@ -3958,6 +3993,8 @@ win_free (
xfree(wp->w_p_cc_cols);
win_free_grid(wp, false);
if (wp != aucmd_win)
win_remove(wp, tp);
if (autocmd_busy) {
@@ -3970,6 +4007,20 @@ win_free (
unblock_autocmds();
}
void win_free_grid(win_T *wp, bool reinit)
{
if (wp->w_grid.handle != 0 && ui_is_external(kUIMultigrid)) {
ui_call_grid_destroy(wp->w_grid.handle);
wp->w_grid.handle = 0;
}
grid_free(&wp->w_grid);
if (reinit) {
// if a float is turned into a split and back into a float, the grid
// data structure will be reused
memset(&wp->w_grid, 0, sizeof(wp->w_grid));
}
}
/*
* Append window "wp" in the window list after window "after".
*/
@@ -4071,8 +4122,8 @@ static void frame_remove(frame_T *frp)
void win_alloc_lines(win_T *wp)
{
wp->w_lines_valid = 0;
assert(Rows >= 0);
wp->w_lines = xcalloc(Rows, sizeof(wline_T));
assert(wp->w_grid.Rows >= 0);
wp->w_lines = xcalloc(MAX(wp->w_grid.Rows + 1, Rows), sizeof(wline_T));
}
/*
@@ -4202,7 +4253,8 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col)
wp->w_winrow = *row;
wp->w_wincol = *col;
redraw_win_later(wp, NOT_VALID);
wp->w_redr_status = TRUE;
wp->w_redr_status = true;
wp->w_pos_changed = true;
}
*row += wp->w_height + wp->w_status_height;
*col += wp->w_width + wp->w_vsep_width;
@@ -4255,8 +4307,9 @@ void win_setheight_win(int height, win_T *win)
* If there is extra space created between the last window and the command
* line, clear it.
*/
if (full_screen && msg_scrolled == 0 && row < cmdline_row)
screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0);
if (full_screen && msg_scrolled == 0 && row < cmdline_row) {
grid_fill(&default_grid, row, cmdline_row, 0, (int)Columns, ' ', ' ', 0);
}
cmdline_row = row;
msg_row = row;
msg_col = 0;
@@ -4706,7 +4759,7 @@ void win_drag_status_line(win_T *dragwin, int offset)
fr = fr->fr_next;
}
row = win_comp_pos();
screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0);
grid_fill(&default_grid, row, cmdline_row, 0, (int)Columns, ' ', ' ', 0);
cmdline_row = row;
p_ch = Rows - cmdline_row;
if (p_ch < 1)
@@ -4866,6 +4919,8 @@ void win_new_height(win_T *wp, int height)
if (!exiting) {
scroll_to_fraction(wp, prev_height);
}
wp->w_pos_changed = true;
}
void scroll_to_fraction(win_T *wp, int prev_height)
@@ -4996,6 +5051,7 @@ void win_new_width(win_T *wp, int width)
0);
}
}
wp->w_pos_changed = true;
}
void win_comp_scroll(win_T *wp)
@@ -5052,10 +5108,11 @@ void command_height(void)
/* Recompute window positions. */
(void)win_comp_pos();
/* clear the lines added to cmdline */
if (full_screen)
screen_fill(cmdline_row, (int)Rows, 0,
(int)Columns, ' ', ' ', 0);
// clear the lines added to cmdline
if (full_screen) {
grid_fill(&default_grid, cmdline_row, (int)Rows, 0, (int)Columns, ' ',
' ', 0);
}
msg_row = cmdline_row;
redraw_cmdline = TRUE;
return;
@@ -6031,3 +6088,23 @@ void win_findbuf(typval_T *argvars, list_T *list)
}
}
}
void win_ui_flush(void)
{
if (!ui_is_external(kUIMultigrid)) {
return;
}
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp->w_pos_changed && wp->w_grid.chars != NULL) {
if (tp == curtab) {
ui_call_win_pos(wp->w_grid.handle, wp->handle, wp->w_winrow,
wp->w_wincol, wp->w_width, wp->w_height);
} else {
ui_call_win_hide(wp->w_grid.handle);
}
wp->w_pos_changed = false;
}
}
}

View File

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

View File

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

View File

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

View File

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

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_linegrid=false,
ext_hlstate=false,
ext_multigrid=false,
}
clear(...)

View File

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

View File

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

View File

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