diff options
Diffstat (limited to 'src/nvim/screen.c')
-rw-r--r-- | src/nvim/screen.c | 2910 |
1 files changed, 944 insertions, 1966 deletions
diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 3c91d764bf..e99f9b9153 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -67,6 +67,7 @@ #include "nvim/api/extmark.h" #include "nvim/api/private/helpers.h" +#include "nvim/api/ui.h" #include "nvim/api/vim.h" #include "nvim/arabic.h" #include "nvim/ascii.h" @@ -75,6 +76,7 @@ #include "nvim/cursor.h" #include "nvim/cursor_shape.h" #include "nvim/decoration.h" +#include "nvim/decoration_provider.h" #include "nvim/diff.h" #include "nvim/edit.h" #include "nvim/eval.h" @@ -86,13 +88,16 @@ #include "nvim/fold.h" #include "nvim/garray.h" #include "nvim/getchar.h" +#include "nvim/grid_defs.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/lib/kvec.h" #include "nvim/log.h" #include "nvim/lua/executor.h" #include "nvim/main.h" #include "nvim/mark.h" +#include "nvim/match.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" @@ -126,21 +131,8 @@ #define MB_FILLER_CHAR '<' /* character used when a double-width character * doesn't fit. */ -typedef kvec_withinit_t(DecorProvider *, 4) Providers; - -// temporary buffer for rendering a single screenline, so it can be -// compared with previous contents to calculate smallest delta. -// Per-cell attributes -static size_t linebuf_size = 0; -static schar_T *linebuf_char = NULL; -static sattr_T *linebuf_attr = NULL; - static match_T search_hl; // used for 'hlsearch' highlight matching -StlClickDefinition *tab_page_click_defs = NULL; - -long tab_page_click_defs_size = 0; - // for line_putchar. Contains the state that needs to be remembered from // putting one character to the next. typedef struct { @@ -160,44 +152,20 @@ static bool msg_grid_invalid = false; static bool resizing = false; +typedef struct { + NS ns_id; + uint64_t mark_id; + int win_row; + int win_col; +} WinExtmark; +static kvec_t(WinExtmark) win_extmark_arr INIT(= KV_INITIAL_VALUE); #ifdef INCLUDE_GENERATED_DECLARATIONS # include "screen.c.generated.h" #endif -#define SEARCH_HL_PRIORITY 0 static char *provider_err = NULL; -static bool provider_invoke(NS ns_id, const char *name, LuaRef ref, Array args, bool default_true) -{ - Error err = ERROR_INIT; - - textlock++; - provider_active = true; - Object ret = nlua_call_ref(ref, name, args, true, &err); - provider_active = false; - textlock--; - - if (!ERROR_SET(&err) - && api_object_to_bool(ret, "provider %s retval", default_true, &err)) { - return true; - } - - if (ERROR_SET(&err)) { - const char *ns_name = describe_ns(ns_id); - ELOG("error in provider %s:%s: %s", ns_name, name, err.msg); - bool verbose_errs = true; // TODO(bfredl): - if (verbose_errs && provider_err == NULL) { - static char errbuf[IOSIZE]; - snprintf(errbuf, sizeof errbuf, "%s: %s", ns_name, err.msg); - provider_err = xstrdup(errbuf); - } - } - - api_free_object(ret); - return false; -} - /// Redraw a window later, with update_screen(type). /// /// Set must_redraw only if not already set to a higher value. @@ -304,20 +272,12 @@ void redrawWinline(win_T *wp, linenr_T lnum) } } -/* - * update all windows that are editing the current buffer - */ -void update_curbuf(int type) -{ - redraw_curbuf_later(type); - update_screen(type); -} - /// called when the status bars for the buffer 'buf' need to be updated void redraw_buf_status_later(buf_T *buf) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == buf && wp->w_status_height) { + if (wp->w_buffer == buf && (wp->w_status_height || (wp == curwin && global_stl_height()) + || wp->w_winbar_height)) { wp->w_redr_status = true; if (must_redraw < VALID) { must_redraw = VALID; @@ -326,6 +286,25 @@ void redraw_buf_status_later(buf_T *buf) } } +void redraw_win_signcol(win_T *wp) +{ + // If we can compute a change in the automatic sizing of the sign column + // under 'signcolumn=auto:X' and signs currently placed in the buffer, better + // figuring it out here so we can redraw the entire screen for it. + int scwidth = wp->w_scwidth; + wp->w_scwidth = win_signcol_count(wp); + if (wp->w_scwidth != scwidth) { + changed_line_abv_curs_win(wp); + } +} + +/// Update all windows that are editing the current buffer. +void update_curbuf(int type) +{ + redraw_curbuf_later(type); + update_screen(type); +} + /// Redraw the parts of the screen that is marked for redraw. /// /// Most code shouldn't call this directly, rather use redraw_later() and @@ -335,6 +314,7 @@ void redraw_buf_status_later(buf_T *buf) int update_screen(int type) { static bool did_intro = false; + bool is_stl_global = global_stl_height() > 0; // Don't do anything if the screen structures are (not yet) valid. // A VimResized autocmd can invoke redrawing in the middle of a resize, @@ -353,10 +333,11 @@ int update_screen(int type) type = must_redraw; } - /* must_redraw is reset here, so that when we run into some weird - * reason to redraw while busy redrawing (e.g., asynchronous - * scrolling), or update_topline() in win_update() will cause a - * scroll, the screen will be redrawn later or in win_update(). */ + // must_redraw is reset here, so that when we run into some weird + // reason to redraw while busy redrawing (e.g., asynchronous + // scrolling), or update_topline() in win_update() will cause a + // scroll, or a decoration provider requires a redraw, the screen + // will be redrawn later or in win_update(). must_redraw = 0; } @@ -396,9 +377,9 @@ int update_screen(int type) int valid = MAX(Rows - msg_scrollsize(), 0); if (msg_grid.chars) { // non-displayed part of msg_grid is considered invalid. - for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.Rows); i++) { + for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.rows); i++) { grid_clear_line(&msg_grid, msg_grid.line_offset[i], - msg_grid.Columns, false); + msg_grid.cols, false); } } if (msg_use_msgsep()) { @@ -406,7 +387,7 @@ int update_screen(int type) // CLEAR is already handled if (type == NOT_VALID && !ui_has(kUIMultigrid) && msg_scrolled) { ui_comp_set_screen_valid(false); - for (int i = valid; i < Rows-p_ch; i++) { + for (int i = valid; i < Rows - p_ch; i++) { grid_clear_line(&default_grid, default_grid.line_offset[i], Columns, false); } @@ -417,12 +398,15 @@ int update_screen(int type) if (W_ENDROW(wp) > valid) { wp->w_redr_type = MAX(wp->w_redr_type, NOT_VALID); } - if (W_ENDROW(wp) + wp->w_status_height > valid) { + if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height > valid) { wp->w_redr_status = true; } } + if (is_stl_global && Rows - p_ch - 1 > valid) { + curwin->w_redr_status = true; + } } - msg_grid_set_pos(Rows-p_ch, false); + msg_grid_set_pos(Rows - p_ch, false); msg_grid_invalid = false; } else if (msg_scrolled > Rows - 5) { // clearing is faster type = CLEAR; @@ -442,13 +426,15 @@ int update_screen(int type) wp->w_redr_type = REDRAW_TOP; } else { wp->w_redr_type = NOT_VALID; - if (W_ENDROW(wp) + wp->w_status_height - <= msg_scrolled) { - wp->w_redr_status = TRUE; + if (wp->w_winrow + wp->w_winbar_height <= msg_scrolled) { + wp->w_redr_status = true; } } } } + if (is_stl_global && Rows - p_ch - 1 <= msg_scrolled) { + curwin->w_redr_status = true; + } redraw_cmdline = true; redraw_tabline = true; } @@ -484,33 +470,13 @@ int update_screen(int type) // After disabling msgsep the grid might not have been deallocated yet, // hence we also need to check msg_grid.chars if (type == NOT_VALID && (msg_use_grid() || msg_grid.chars)) { - grid_fill(&default_grid, Rows-p_ch, Rows, 0, Columns, ' ', ' ', 0); + grid_fill(&default_grid, Rows - p_ch, Rows, 0, Columns, ' ', ' ', 0); } ui_comp_set_screen_valid(true); - Providers providers; - kvi_init(providers); - for (size_t i = 0; i < kv_size(decor_providers); i++) { - DecorProvider *p = &kv_A(decor_providers, i); - if (!p->active) { - continue; - } - - bool active; - if (p->redraw_start != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 2); - args.items[0] = INTEGER_OBJ(display_tick); - args.items[1] = INTEGER_OBJ(type); - active = provider_invoke(p->ns_id, "start", p->redraw_start, args, true); - } else { - active = true; - } - - if (active) { - kvi_push(providers, p); - } - } + DecorProviders providers; + decor_providers_start(&providers, type, &provider_err); // "start" callback could have changed highlights for global elements if (win_check_ns_hl(NULL)) { @@ -579,14 +545,7 @@ int update_screen(int type) } if (buf->b_mod_tick_decor < display_tick) { - for (size_t i = 0; i < kv_size(providers); i++) { - DecorProvider *p = kv_A(providers, i); - if (p && p->redraw_buf != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 1); - args.items[0] = BUFFER_OBJ(buf->handle); - provider_invoke(p->ns_id, "buf", p->redraw_buf, args, true); - } - } + decor_providers_invoke_buf(buf, &providers, &provider_err); buf->b_mod_tick_decor = display_tick; } } @@ -599,7 +558,6 @@ int update_screen(int type) bool did_one = false; search_hl.rm.regprog = NULL; - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_redr_type == CLEAR && wp->w_floating && wp->w_grid_alloc.chars) { grid_invalidate(&wp->w_grid_alloc); @@ -621,8 +579,9 @@ int update_screen(int type) win_update(wp, &providers); } - // redraw status line after the window to minimize cursor movement + // redraw status line and window bar after the window to minimize cursor movement if (wp->w_redr_status) { + win_redr_winbar(wp); win_redr_status(wp); } } @@ -634,8 +593,6 @@ int update_screen(int type) pum_redraw(); } - send_grid_resize = false; - /* Reset b_mod_set flags. Going through all windows is probably faster * than going through all buffers (there could be many buffers). */ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { @@ -656,21 +613,9 @@ int update_screen(int type) } did_intro = true; - for (size_t i = 0; i < kv_size(providers); i++) { - DecorProvider *p = kv_A(providers, i); - if (!p->active) { - continue; - } - - if (p->redraw_end != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 1); - args.items[0] = INTEGER_OBJ(display_tick); - provider_invoke(p->ns_id, "end", p->redraw_end, args, true); - } - } + decor_providers_invoke_end(&providers, &provider_err); kvi_destroy(providers); - // either cmdline is cleared, not drawn or mode is last drawn cmdline_was_last_drawn = false; return OK; @@ -686,18 +631,18 @@ bool conceal_cursor_line(const win_T *wp) if (*wp->w_p_cocu == NUL) { return false; } - if (get_real_state() & VISUAL) { + if (get_real_state() & MODE_VISUAL) { c = 'v'; - } else if (State & INSERT) { + } else if (State & MODE_INSERT) { c = 'i'; - } else if (State & NORMAL) { + } else if (State & MODE_NORMAL) { c = 'n'; - } else if (State & CMDLINE) { + } else if (State & MODE_CMDLINE) { c = 'c'; } else { return false; } - return vim_strchr(wp->w_p_cocu, c) != NULL; + return vim_strchr((char *)wp->w_p_cocu, c) != NULL; } // Check if the cursor line needs to be redrawn because of 'concealcursor'. @@ -716,15 +661,11 @@ void conceal_check_cursor_line(void) /// Whether cursorline is drawn in a special way /// -/// If true, both old and new cursorline will need -/// to be redrawn when moving cursor within windows. -/// TODO(bfredl): VIsual_active shouldn't be needed, but is used to fix a glitch -/// caused by scrolling. +/// If true, both old and new cursorline will need to be redrawn when moving cursor within windows. bool win_cursorline_standout(const win_T *wp) FUNC_ATTR_NONNULL_ALL { - return wp->w_p_cul - || (wp->w_p_cole > 0 && (VIsual_active || !conceal_cursor_line(wp))); + return wp->w_p_cul || (wp->w_p_cole > 0 && !conceal_cursor_line(wp)); } /* @@ -754,8 +695,11 @@ bool win_cursorline_standout(const win_T *wp) * mid: from mid_start to mid_end (update inversion or changed text) * bot: from bot_start to last row (when scrolled up) */ -static void win_update(win_T *wp, Providers *providers) +static void win_update(win_T *wp, DecorProviders *providers) { + bool called_decor_providers = false; +win_update_start: + ; buf_T *buf = wp->w_buffer; int type; int top_end = 0; /* Below last row of the top area that needs @@ -790,12 +734,6 @@ static void win_update(win_T *wp, Providers *providers) linenr_T mod_bot = 0; int save_got_int; - - // If we can compute a change in the automatic sizing of the sign column - // under 'signcolumn=auto:X' and signs currently placed in the buffer, better - // figuring it out here so we can redraw the entire screen for it. - buf_signcols(buf); - type = wp->w_redr_type; if (type >= NOT_VALID) { @@ -803,21 +741,27 @@ static void win_update(win_T *wp, Providers *providers) wp->w_lines_valid = 0; } - // Window is zero-height: nothing to draw. - if (wp->w_grid.Rows == 0) { + // Window is zero-height: Only need to draw the separator + if (wp->w_grid.rows == 0) { + // draw the horizontal separator below this window + draw_hsep_win(wp); + draw_sep_connectors_win(wp); wp->w_redr_type = 0; return; } // Window is zero-width: Only need to draw the separator. - if (wp->w_grid.Columns == 0) { + if (wp->w_grid.cols == 0) { // draw the vertical separator right of this window - draw_vsep_win(wp, 0); + draw_vsep_win(wp); + draw_sep_connectors_win(wp); wp->w_redr_type = 0; return; } - init_search_hl(wp); + redraw_win_signcol(wp); + + init_search_hl(wp, &search_hl); /* Force redraw when width of 'number' or 'relativenumber' column * changes. */ @@ -987,7 +931,7 @@ static void win_update(win_T *wp, Providers *providers) if (mod_top != 0 && wp->w_topline == mod_top && (!wp->w_lines[0].wl_valid - || wp->w_topline <= wp->w_lines[0].wl_lnum)) { + || wp->w_topline == wp->w_lines[0].wl_lnum)) { // w_topline is the first changed line and window is not scrolled, // the scrolling from changed lines will be done further down. } else if (wp->w_lines[0].wl_valid @@ -1006,7 +950,7 @@ static void win_update(win_T *wp, Providers *providers) j = 0; for (ln = wp->w_topline; ln < wp->w_lines[0].wl_lnum; ln++) { j++; - if (j >= wp->w_grid.Rows - 2) { + if (j >= wp->w_grid.rows - 2) { break; } (void)hasFoldingWin(wp, ln, NULL, &ln, true, NULL); @@ -1014,13 +958,13 @@ static void win_update(win_T *wp, Providers *providers) } else { j = wp->w_lines[0].wl_lnum - wp->w_topline; } - if (j < wp->w_grid.Rows - 2) { // not too far off + if (j < wp->w_grid.rows - 2) { // not too far off i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1); // insert extra lines for previously invisible filler lines if (wp->w_lines[0].wl_lnum != wp->w_topline) { i += win_get_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill; } - if (i != 0 && i < wp->w_grid.Rows - 2) { // less than a screen off + if (i != 0 && i < wp->w_grid.rows - 2) { // less than a screen off // Try to insert the correct number of lines. // If not the last window, delete the lines at the bottom. // win_ins_lines may fail when the terminal can't do it. @@ -1033,8 +977,8 @@ static void win_update(win_T *wp, Providers *providers) // Move the entries that were scrolled, disable // the entries for the lines to be redrawn. - if ((wp->w_lines_valid += j) > wp->w_grid.Rows) { - wp->w_lines_valid = wp->w_grid.Rows; + if ((wp->w_lines_valid += j) > wp->w_grid.rows) { + wp->w_lines_valid = wp->w_grid.rows; } for (idx = wp->w_lines_valid; idx - j >= 0; idx--) { wp->w_lines[idx] = wp->w_lines[idx - j]; @@ -1087,7 +1031,7 @@ static void win_update(win_T *wp, Providers *providers) row -= wp->w_topfill; if (row > 0) { win_scroll_lines(wp, 0, -row); - bot_start = wp->w_grid.Rows - row; + bot_start = wp->w_grid.rows - row; } if ((row == 0 || bot_start < 999) && wp->w_lines_valid != 0) { /* @@ -1103,7 +1047,7 @@ static void win_update(win_T *wp, Providers *providers) /* stop at line that didn't fit, unless it is still * valid (no lines deleted) */ if (row > 0 && bot_start + row - + (int)wp->w_lines[j].wl_size > wp->w_grid.Rows) { + + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) { wp->w_lines_valid = idx + 1; break; } @@ -1128,18 +1072,18 @@ static void win_update(win_T *wp, Providers *providers) // When starting redraw in the first line, redraw all lines. if (mid_start == 0) { - mid_end = wp->w_grid.Rows; + mid_end = wp->w_grid.rows; } } else { // Not VALID or INVERTED: redraw all lines. mid_start = 0; - mid_end = wp->w_grid.Rows; + mid_end = wp->w_grid.rows; } if (type == SOME_VALID) { // SOME_VALID: redraw all lines. mid_start = 0; - mid_end = wp->w_grid.Rows; + mid_end = wp->w_grid.rows; type = NOT_VALID; } @@ -1215,19 +1159,40 @@ static void win_update(win_T *wp, Providers *providers) */ if (VIsual_mode == Ctrl_V) { colnr_T fromc, toc; - int save_ve_flags = ve_flags; + unsigned int save_ve_flags = curwin->w_ve_flags; if (curwin->w_p_lbr) { - ve_flags = VE_ALL; + curwin->w_ve_flags = VE_ALL; } getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc); - ve_flags = save_ve_flags; toc++; + curwin->w_ve_flags = save_ve_flags; // Highlight to the end of the line, unless 'virtualedit' has // "block". - if (curwin->w_curswant == MAXCOL && !(ve_flags & VE_BLOCK)) { - toc = MAXCOL; + if (curwin->w_curswant == MAXCOL) { + if (get_ve_flags() & VE_BLOCK) { + pos_T pos; + int cursor_above = curwin->w_cursor.lnum < VIsual.lnum; + + // Need to find the longest line. + toc = 0; + pos.coladd = 0; + for (pos.lnum = curwin->w_cursor.lnum; + cursor_above ? pos.lnum <= VIsual.lnum : pos.lnum >= VIsual.lnum; + pos.lnum += cursor_above ? 1 : -1) { + colnr_T t; + + pos.col = STRLEN(ml_get_buf(wp->w_buffer, pos.lnum, false)); + getvvcol(wp, &pos, NULL, NULL, &t); + if (toc < t) { + toc = t; + } + } + toc++; + } else { + toc = MAXCOL; + } } if (fromc != wp->w_old_cursor_fcol @@ -1305,7 +1270,7 @@ static void win_update(win_T *wp, Providers *providers) } } srow += mid_start; - mid_end = wp->w_grid.Rows; + mid_end = wp->w_grid.rows; for (; idx < wp->w_lines_valid; idx++) { // find end if (wp->w_lines[idx].wl_valid && wp->w_lines[idx].wl_lnum >= to + 1) { @@ -1346,37 +1311,27 @@ static void win_update(win_T *wp, Providers *providers) srow = 0; lnum = wp->w_topline; // first line shown in window - decor_redraw_reset(buf, &decor_state); - - Providers line_providers; - kvi_init(line_providers); + win_extmark_arr.size = 0; - linenr_T knownmax = ((wp->w_valid & VALID_BOTLINE) - ? wp->w_botline - : (wp->w_topline + wp->w_height_inner)); + decor_redraw_reset(buf, &decor_state); - for (size_t k = 0; k < kv_size(*providers); k++) { - DecorProvider *p = kv_A(*providers, k); - if (p && p->redraw_win != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 4); - args.items[0] = WINDOW_OBJ(wp->handle); - args.items[1] = BUFFER_OBJ(buf->handle); - // TODO(bfredl): we are not using this, but should be first drawn line? - args.items[2] = INTEGER_OBJ(wp->w_topline-1); - args.items[3] = INTEGER_OBJ(knownmax); - if (provider_invoke(p->ns_id, "win", p->redraw_win, args, true)) { - kvi_push(line_providers, p); - } + DecorProviders line_providers; + decor_providers_invoke_win(wp, providers, &line_providers, &provider_err); + (void)win_signcol_count(wp); // check if provider changed signcol width + if (must_redraw != 0) { + must_redraw = 0; + if (!called_decor_providers) { + called_decor_providers = true; + goto win_update_start; } } - win_check_ns_hl(wp); - + bool cursorline_standout = win_cursorline_standout(wp); for (;;) { /* stop updating when reached the end of the window (check for _past_ * the end of the window is at the end of the loop) */ - if (row == wp->w_grid.Rows) { + if (row == wp->w_grid.rows) { didline = true; break; } @@ -1417,8 +1372,8 @@ static void win_update(win_T *wp, Providers *providers) // if lines were inserted or deleted || (wp->w_match_head != NULL && buf->b_mod_xlines != 0))))) - || (wp->w_p_cul && (lnum == wp->w_cursor.lnum - || lnum == wp->w_last_cursorline))) { + || (cursorline_standout && lnum == wp->w_cursor.lnum) + || lnum == wp->w_last_cursorline) { if (lnum == mod_top) { top_to_mod = false; } @@ -1481,7 +1436,7 @@ static void win_update(win_T *wp, Providers *providers) new_rows += plines_win(wp, l, true); } j++; - if (new_rows > wp->w_grid.Rows - row - 2) { + if (new_rows > wp->w_grid.rows - row - 2) { // it's getting too much, must redraw the rest new_rows = 9999; break; @@ -1493,17 +1448,17 @@ static void win_update(win_T *wp, Providers *providers) * remaining text or scrolling fails, must redraw the * rest. If scrolling works, must redraw the text * below the scrolled text. */ - if (row - xtra_rows >= wp->w_grid.Rows - 2) { + if (row - xtra_rows >= wp->w_grid.rows - 2) { mod_bot = MAXLNUM; } else { win_scroll_lines(wp, row, xtra_rows); - bot_start = wp->w_grid.Rows + xtra_rows; + bot_start = wp->w_grid.rows + xtra_rows; } } else if (xtra_rows > 0) { /* May scroll text down. If there is not enough * remaining text of scrolling fails, must redraw the * rest. */ - if (row + xtra_rows >= wp->w_grid.Rows - 2) { + if (row + xtra_rows >= wp->w_grid.rows - 2) { mod_bot = MAXLNUM; } else { win_scroll_lines(wp, row + old_rows, xtra_rows); @@ -1531,7 +1486,7 @@ static void win_update(win_T *wp, Providers *providers) wp->w_lines[j] = wp->w_lines[i]; // stop at a line that won't fit if (x + (int)wp->w_lines[j].wl_size - > wp->w_grid.Rows) { + > wp->w_grid.rows) { wp->w_lines_valid = j + 1; break; } @@ -1545,8 +1500,8 @@ static void win_update(win_T *wp, Providers *providers) // move entries in w_lines[] downwards j -= i; wp->w_lines_valid += j; - if (wp->w_lines_valid > wp->w_grid.Rows) { - wp->w_lines_valid = wp->w_grid.Rows; + if (wp->w_lines_valid > wp->w_grid.rows) { + wp->w_lines_valid = wp->w_grid.rows; } for (i = wp->w_lines_valid; i - j >= idx; i--) { wp->w_lines[i] = wp->w_lines[i - j]; @@ -1577,13 +1532,13 @@ static void win_update(win_T *wp, Providers *providers) && wp->w_lines[idx].wl_lnum == lnum && lnum > wp->w_topline && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE)) - && srow + wp->w_lines[idx].wl_size > wp->w_grid.Rows + && srow + wp->w_lines[idx].wl_size > wp->w_grid.rows && win_get_fill(wp, lnum) == 0) { // This line is not going to fit. Don't draw anything here, // will draw "@ " lines below. - row = wp->w_grid.Rows + 1; + row = wp->w_grid.rows + 1; } else { - prepare_search_hl(wp, lnum); + prepare_search_hl(wp, &search_hl, lnum); // Let the syntax stuff know we skipped a few lines. if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum && syntax_present(wp)) { @@ -1592,26 +1547,26 @@ static void win_update(win_T *wp, Providers *providers) // Display one line row = win_line(wp, lnum, srow, - foldinfo.fi_lines ? srow : wp->w_grid.Rows, + foldinfo.fi_lines ? srow : wp->w_grid.rows, mod_top == 0, false, foldinfo, &line_providers); - wp->w_lines[idx].wl_folded = foldinfo.fi_lines != 0; - wp->w_lines[idx].wl_lastlnum = lnum; - did_update = DID_LINE; - - if (foldinfo.fi_lines > 0) { - did_update = DID_FOLD; + if (foldinfo.fi_lines == 0) { + wp->w_lines[idx].wl_folded = false; + wp->w_lines[idx].wl_lastlnum = lnum; + did_update = DID_LINE; + syntax_last_parsed = lnum; + } else { foldinfo.fi_lines--; + wp->w_lines[idx].wl_folded = true; wp->w_lines[idx].wl_lastlnum = lnum + foldinfo.fi_lines; + did_update = DID_FOLD; } - - syntax_last_parsed = lnum; } wp->w_lines[idx].wl_lnum = lnum; wp->w_lines[idx].wl_valid = true; - if (row > wp->w_grid.Rows) { // past end of grid + if (row > wp->w_grid.rows) { // past end of grid // we may need the size of that too long line later on if (dollar_vcol == -1) { wp->w_lines[idx].wl_size = plines_win(wp, lnum, true); @@ -1625,17 +1580,17 @@ static void win_update(win_T *wp, Providers *providers) idx++; lnum += foldinfo.fi_lines + 1; } else { - if (wp->w_p_rnu) { - // 'relativenumber' set: The text doesn't need to be drawn, but - // the number column nearly always does. + if (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum) { + // 'relativenumber' set and cursor moved vertically: The + // text doesn't need to be drawn, but the number column does. foldinfo_T info = fold_info(wp, lnum); - (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true, + (void)win_line(wp, lnum, srow, wp->w_grid.rows, true, true, info, &line_providers); } // This line does not need to be drawn, advance to the next one. row += wp->w_lines[idx++].wl_size; - if (row > wp->w_grid.Rows) { // past end of screen + if (row > wp->w_grid.rows) { // past end of screen break; } lnum = wp->w_lines[idx - 1].wl_lastlnum + 1; @@ -1651,6 +1606,11 @@ static void win_update(win_T *wp, Providers *providers) * End of loop over all window lines. */ + // Now that the window has been redrawn with the old and new cursor line, + // update w_last_cursorline. + wp->w_last_cursorline = cursorline_standout ? wp->w_cursor.lnum : 0; + + wp->w_last_cursor_lnum_rnu = wp->w_p_rnu ? wp->w_cursor.lnum : 0; if (idx > wp->w_lines_valid) { wp->w_lines_valid = idx; @@ -1678,39 +1638,41 @@ static void win_update(win_T *wp, Providers *providers) * Don't overwrite it, it can be edited. */ wp->w_botline = lnum + 1; - } else if (win_get_fill(wp, lnum) >= wp->w_grid.Rows - srow) { + } else if (win_get_fill(wp, lnum) >= wp->w_grid.rows - srow) { // Window ends in filler lines. wp->w_botline = lnum; - wp->w_filler_rows = wp->w_grid.Rows - srow; + wp->w_filler_rows = wp->w_grid.rows - srow; } else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate" - int scr_row = wp->w_grid.Rows - 1; + int scr_row = wp->w_grid.rows - 1; // Last line isn't finished: Display "@@@" in the last screen line. - grid_puts_len(&wp->w_grid, (char_u *)"@@", 2, scr_row, 0, at_attr); + grid_puts_len(&wp->w_grid, (char_u *)"@@", MIN(wp->w_grid.cols, 2), scr_row, 0, at_attr); - grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, wp->w_grid.Columns, + grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, wp->w_grid.cols, '@', ' ', at_attr); set_empty_rows(wp, srow); wp->w_botline = lnum; } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline" + int start_col = wp->w_grid.cols - 3; + // Last line isn't finished: Display "@@@" at the end. - grid_fill(&wp->w_grid, wp->w_grid.Rows - 1, wp->w_grid.Rows, - wp->w_grid.Columns - 3, wp->w_grid.Columns, '@', '@', at_attr); + grid_fill(&wp->w_grid, wp->w_grid.rows - 1, wp->w_grid.rows, + MAX(start_col, 0), wp->w_grid.cols, '@', '@', at_attr); set_empty_rows(wp, srow); wp->w_botline = lnum; } else { - win_draw_end(wp, '@', ' ', true, srow, wp->w_grid.Rows, HLF_AT); + win_draw_end(wp, '@', ' ', true, srow, wp->w_grid.rows, HLF_AT); wp->w_botline = lnum; } } else { if (eof) { // we hit the end of the file wp->w_botline = buf->b_ml.ml_line_count + 1; j = win_get_fill(wp, wp->w_botline); - if (j > 0 && !wp->w_botfill && row < wp->w_grid.Rows) { + if (j > 0 && !wp->w_botfill && row < wp->w_grid.rows) { // Display filler text below last line. win_line() will check // for ml_line_count+1 and only draw filler lines foldinfo_T info = FOLDINFO_INIT; - row = win_line(wp, wp->w_botline, row, wp->w_grid.Rows, + row = win_line(wp, wp->w_botline, row, wp->w_grid.rows, false, false, info, &line_providers); } } else if (dollar_vcol == -1) { @@ -1719,14 +1681,16 @@ static void win_update(win_T *wp, Providers *providers) // make sure the rest of the screen is blank // write the 'eob' character to rows that aren't part of the file. - win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', false, row, wp->w_grid.Rows, + win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', false, row, wp->w_grid.rows, HLF_EOB); } kvi_destroy(line_providers); if (wp->w_redr_type >= REDRAW_TOP) { - draw_vsep_win(wp, 0); + draw_vsep_win(wp); + draw_hsep_win(wp); + draw_sep_connectors_win(wp); } syn_set_timeout(NULL); @@ -1735,6 +1699,13 @@ static void win_update(win_T *wp, Providers *providers) wp->w_old_topfill = wp->w_topfill; wp->w_old_botfill = wp->w_botfill; + // Send win_extmarks if needed + for (size_t n = 0; n < kv_size(win_extmark_arr); n++) { + ui_call_win_extmark(wp->w_grid_alloc.handle, wp->handle, + kv_A(win_extmark_arr, n).ns_id, kv_A(win_extmark_arr, n).mark_id, + kv_A(win_extmark_arr, n).win_row, kv_A(win_extmark_arr, n).win_col); + } + if (dollar_vcol == -1) { /* * There is a trick with w_botline. If we invalidate it on each @@ -1766,12 +1737,11 @@ static void win_update(win_T *wp, Providers *providers) } } - // restore got_int, unless CTRL-C was hit while redrawing if (!got_int) { got_int = save_got_int; } -} // NOLINT(readability/fn_size) +} /// Returns width of the signcolumn that should be used for the whole window /// @@ -1795,8 +1765,8 @@ static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row, { int nn = off + width; - if (nn > wp->w_grid.Columns) { - nn = wp->w_grid.Columns; + if (nn > wp->w_grid.cols) { + nn = wp->w_grid.cols; } if (wp->w_p_rl) { @@ -1825,7 +1795,7 @@ static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, i win_hl_attr(wp, HLF_FC)); } // draw the sign column - int count = win_signcol_count(wp); + int count = wp->w_scwidth; if (count > 0) { n = win_fill_end(wp, ' ', ' ', n, win_signcol_width(wp) * count, row, endrow, win_hl_attr(wp, HLF_SC)); @@ -1845,13 +1815,12 @@ static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, i grid_fill(&wp->w_grid, row, endrow, W_ENDCOL(wp) - 1 - n, W_ENDCOL(wp) - n, c1, c2, attr); } else { - grid_fill(&wp->w_grid, row, endrow, n, wp->w_grid.Columns, c1, c2, attr); + grid_fill(&wp->w_grid, row, endrow, n, wp->w_grid.cols, c1, c2, attr); } set_empty_rows(wp, row); } - /// Advance **color_cols /// /// @return true when there are columns to draw. @@ -1869,7 +1838,7 @@ static int compute_foldcolumn(win_T *wp, int col) { int fdc = win_fdccol_count(wp); int wmw = wp == curwin && p_wmw == 0 ? 1 : p_wmw; - int wwidth = wp->w_grid.Columns; + int wwidth = wp->w_grid.cols; if (fdc > wwidth - (col + wmw)) { fdc = wwidth - (col + wmw); @@ -1883,8 +1852,8 @@ static int compute_foldcolumn(win_T *wp, int col) static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, bool rl, int vcol) { const char_u *p = (char_u *)s->p; - int cells = utf_ptr2cells(p); - int c_len = utfc_ptr2len(p); + int cells = utf_ptr2cells((char *)p); + int c_len = utfc_ptr2len((char *)p); int u8c, u8cc[MAX_MCO]; if (cells > maxcells) { return -1; @@ -1900,7 +1869,7 @@ static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, b schar_from_ascii(dest[0], *p); s->prev_c = u8c; } else { - if (p_arshape && !p_tbidi && arabic_char(u8c)) { + if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) { // Do Arabic shaping. int pc, pc1, nc; int pcc[MAX_MCO]; @@ -1911,7 +1880,7 @@ static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, b if (rl) { pc = s->prev_c; pc1 = s->prev_c1; - nc = utf_ptr2char(p + c_len); + nc = utf_ptr2char((char *)p + c_len); s->prev_c1 = u8cc[0]; } else { pc = utfc_ptr2char(p + c_len, pcc); @@ -1934,7 +1903,6 @@ done: return cells; } - /// Fills the foldcolumn at "p" for window "wp". /// Only to be called when 'foldcolumn' > 0. /// @@ -1960,7 +1928,7 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_ level = foldinfo.fi_level; // If the column is too narrow, we start at the lowest level that - // fits and use numbers to indicated the depth. + // fits and use numbers to indicate the depth. first_level = level - fdc - closed + 1; if (first_level < 1) { first_level = 1; @@ -1978,7 +1946,7 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_ symbol = '>'; } - len = utf_char2bytes(symbol, &p[char_counter]); + len = utf_char2bytes(symbol, (char *)&p[char_counter]); char_counter += len; if (first_level + i >= level) { i++; @@ -1992,11 +1960,43 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_ char_counter -= len; memset(&p[char_counter], ' ', len); } - len = utf_char2bytes(wp->w_p_fcs_chars.foldclosed, &p[char_counter]); + len = utf_char2bytes(wp->w_p_fcs_chars.foldclosed, (char *)&p[char_counter]); char_counter += len; } - return MAX(char_counter + (fdc-i), (size_t)fdc); + return MAX(char_counter + (fdc - i), (size_t)fdc); +} + +static inline void provider_err_virt_text(linenr_T lnum, char *err) +{ + Decoration err_decor = DECORATION_INIT; + int hl_err = syn_check_group(S_LEN("ErrorMsg")); + kv_push(err_decor.virt_text, + ((VirtTextChunk){ .text = provider_err, + .hl_id = hl_err })); + err_decor.virt_text_width = mb_string2cells(err); + decor_add_ephemeral(lnum - 1, 0, lnum - 1, 0, &err_decor, 0, 0); +} + +static inline void get_line_number_str(win_T *wp, linenr_T lnum, char_u *buf, size_t buf_len) +{ + long num; + char *fmt = "%*ld "; + + if (wp->w_p_nu && !wp->w_p_rnu) { + // 'number' + 'norelativenumber' + num = (long)lnum; + } else { + // 'relativenumber', don't use negative numbers + num = labs((long)get_cursor_rel_lnum(wp, lnum)); + if (num == 0 && wp->w_p_nu && wp->w_p_rnu) { + // 'number' + 'relativenumber' + num = lnum; + fmt = "%-*ld "; + } + } + + snprintf((char *)buf, buf_len, fmt, number_width(wp), num); } /// Display line "lnum" of window 'wp' on the screen. @@ -2014,7 +2014,7 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_ /// /// @return the number of last row the line occupies. static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, - bool number_only, foldinfo_T foldinfo, Providers *providers) + bool number_only, foldinfo_T foldinfo, DecorProviders *providers) { int c = 0; // init for GCC long vcol = 0; // virtual column (for tabs) @@ -2111,13 +2111,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc int line_attr_save; int line_attr_lowprio = 0; // low-priority attribute for the line int line_attr_lowprio_save; - matchitem_T *cur; // points to the match list - match_T *shl; // points to search_hl or a match - bool shl_flag; // flag to indicate whether search_hl - // has been processed or not - bool prevcol_hl_flag; // flag to indicate whether prevcol - // equals startcol of search_hl or one - // of the matches int prev_c = 0; // previous Arabic character int prev_c1 = 0; // first composing char for prev_c @@ -2139,13 +2132,13 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // draw_state: items that are drawn in sequence: #define WL_START 0 // nothing done yet -#define WL_CMDLINE WL_START + 1 // cmdline window column -#define WL_FOLD WL_CMDLINE + 1 // 'foldcolumn' -#define WL_SIGN WL_FOLD + 1 // column for signs -#define WL_NR WL_SIGN + 1 // line number -#define WL_BRI WL_NR + 1 // 'breakindent' -#define WL_SBR WL_BRI + 1 // 'showbreak' or 'diff' -#define WL_LINE WL_SBR + 1 // text in the line +#define WL_CMDLINE (WL_START + 1) // cmdline window column +#define WL_FOLD (WL_CMDLINE + 1) // 'foldcolumn' +#define WL_SIGN (WL_FOLD + 1) // column for signs +#define WL_NR (WL_SIGN + 1) // line number +#define WL_BRI (WL_NR + 1) // 'breakindent' +#define WL_SBR (WL_BRI + 1) // 'showbreak' or 'diff' +#define WL_LINE (WL_SBR + 1) // text in the line int draw_state = WL_START; // what to draw next int syntax_flags = 0; @@ -2177,13 +2170,14 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc row = startrow; buf_T *buf = wp->w_buffer; - bool end_fill = (lnum == buf->b_ml.ml_line_count+1); + bool end_fill = (lnum == buf->b_ml.ml_line_count + 1); if (!number_only) { // To speed up the loop below, set extra_check when there is linebreak, // trailing white space and/or syntax processing to be done. extra_check = wp->w_p_lbr; - if (syntax_present(wp) && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow) { + if (syntax_present(wp) && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow + && !has_fold && !end_fill) { // Prepare for syntax highlighting in this line. When there is an // error, stop syntax highlighting. save_did_emsg = did_emsg; @@ -2200,37 +2194,14 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } } - has_decor = decor_redraw_line(wp->w_buffer, lnum-1, - &decor_state); + has_decor = decor_redraw_line(buf, lnum - 1, &decor_state); - for (size_t k = 0; k < kv_size(*providers); k++) { - DecorProvider *p = kv_A(*providers, k); - if (p && p->redraw_line != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 3); - args.items[0] = WINDOW_OBJ(wp->handle); - args.items[1] = BUFFER_OBJ(buf->handle); - args.items[2] = INTEGER_OBJ(lnum-1); - if (provider_invoke(p->ns_id, "line", p->redraw_line, args, true)) { - has_decor = true; - } else { - // return 'false' or error: skip rest of this window - kv_A(*providers, k) = NULL; - } - - win_check_ns_hl(wp); - } - } + providers_invoke_line(wp, providers, lnum - 1, &has_decor, &provider_err); if (provider_err) { - Decoration err_decor = DECORATION_INIT; - int hl_err = syn_check_group(S_LEN("ErrorMsg")); - kv_push(err_decor.virt_text, - ((VirtTextChunk){ .text = provider_err, - .hl_id = hl_err })); - err_decor.virt_text_width = mb_string2cells((char_u *)provider_err); - decor_add_ephemeral(lnum-1, 0, lnum-1, 0, &err_decor); - provider_err = NULL; + provider_err_virt_text(lnum, provider_err); has_decor = true; + provider_err = NULL; } if (has_decor) { @@ -2413,7 +2384,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) { line_attr_lowprio = cul_attr; } else { - if (!(State & INSERT) && bt_quickfix(wp->w_buffer) + if (!(State & MODE_INSERT) && bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum) { line_attr = hl_combine_attr(cul_attr, line_attr); } else { @@ -2425,12 +2396,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } area_highlighting = true; } - // Update w_last_cursorline even if Visual mode is active. - wp->w_last_cursorline = wp->w_cursor.lnum; } memset(sattrs, 0, sizeof(sattrs)); num_signs = buf_get_signattrs(wp->w_buffer, lnum, sattrs); + decor_redraw_signs(buf, lnum - 1, &num_signs, sattrs); // If this line has a sign with line highlighting set line_attr. // TODO(bfredl, vigoux): this should not take priority over decoration! @@ -2490,6 +2460,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (wp->w_p_list && !has_fold && !end_fill) { if (wp->w_p_lcs_chars.space || wp->w_p_lcs_chars.multispace != NULL + || wp->w_p_lcs_chars.leadmultispace != NULL || wp->w_p_lcs_chars.trail || wp->w_p_lcs_chars.lead || wp->w_p_lcs_chars.nbsp) { @@ -2504,7 +2475,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc trailcol += (colnr_T)(ptr - line); } // find end of leading whitespace - if (wp->w_p_lcs_chars.lead) { + if (wp->w_p_lcs_chars.lead || wp->w_p_lcs_chars.leadmultispace != NULL) { leadcol = 0; while (ascii_iswhite(ptr[leadcol])) { leadcol++; @@ -2557,7 +2528,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc ptr = prev_ptr; // If the character fits on the screen, don't need to skip it. // Except for a TAB. - if (utf_ptr2cells(ptr) >= c || *ptr == TAB) { + if (utf_ptr2cells((char *)ptr) >= c || *ptr == TAB) { n_skip = v - vcol; } } @@ -2637,69 +2608,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } } - /* - * Handle highlighting the last used search pattern and matches. - * Do this for both search_hl and the match list. - */ - cur = wp->w_match_head; - shl_flag = false; - while ((cur != NULL || !shl_flag) && !number_only - && !has_fold && !end_fill) { - if (!shl_flag) { - shl = &search_hl; - shl_flag = true; - } else { - shl = &cur->hl; // -V595 - } - shl->startcol = MAXCOL; - shl->endcol = MAXCOL; - shl->attr_cur = 0; - shl->is_addpos = false; - v = (ptr - line); - if (cur != NULL) { - cur->pos.cur = 0; - } - next_search_hl(wp, shl, lnum, (colnr_T)v, - shl == &search_hl ? NULL : cur); - if (wp->w_s->b_syn_slow) { - has_syntax = false; - } - - // Need to get the line again, a multi-line regexp may have made it - // invalid. - line = ml_get_buf(wp->w_buffer, lnum, false); - ptr = line + v; - - if (shl->lnum != 0 && shl->lnum <= lnum) { - if (shl->lnum == lnum) { - shl->startcol = shl->rm.startpos[0].col; - } else { - shl->startcol = 0; - } - if (lnum == shl->lnum + shl->rm.endpos[0].lnum - - shl->rm.startpos[0].lnum) { - shl->endcol = shl->rm.endpos[0].col; - } else { - shl->endcol = MAXCOL; - } - // Highlight one character for an empty match. - if (shl->startcol == shl->endcol) { - if (line[shl->endcol] != NUL) { - shl->endcol += utfc_ptr2len(line + shl->endcol); - } else { - ++shl->endcol; - } - } - if ((long)shl->startcol < v) { // match at leftcol - shl->attr_cur = shl->attr; - search_attr = shl->attr; - search_attr_from_match = shl != &search_hl; - } - area_highlighting = true; - } - if (shl != &search_hl && cur != NULL) { - cur = cur->next; - } + if (!number_only && !has_fold && !end_fill) { + v = ptr - line; + area_highlighting |= prepare_search_hl_line(wp, lnum, (colnr_T)v, + &line, &search_hl, &search_attr, + &search_attr_from_match); + ptr = line + v; // "line" may have been updated } unsigned off = 0; // Offset relative start of line @@ -2708,7 +2622,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // Rightleft window: process the text in the normal direction, but put // it in linebuf_char[off] from right to left. Start at the // rightmost column of the window. - col = grid->Columns - 1; + col = grid->cols - 1; off += col; } @@ -2723,6 +2637,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // Repeat for the whole displayed line. for (;;) { int has_match_conc = 0; ///< match wants to conceal + int decor_conceal = 0; + bool did_decrement_ptr = false; // Skip this quickly when working on the text. @@ -2771,13 +2687,17 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc draw_state = WL_SIGN; /* Show the sign column when there are any signs in this * buffer or when using Netbeans. */ - int count = win_signcol_count(wp); - if (count > 0) { + if (wp->w_scwidth > 0) { get_sign_display_info(false, wp, lnum, sattrs, row, - startrow, filler_lines, filler_todo, count, + startrow, filler_lines, filler_todo, &c_extra, &c_final, extra, sizeof(extra), - &p_extra, &n_extra, - &char_attr, &draw_state, &sign_idx); + &p_extra, &n_extra, &char_attr, sign_idx); + sign_idx++; + if (sign_idx < wp->w_scwidth) { + draw_state = WL_SIGN - 1; + } else { + sign_idx = 0; + } } } @@ -2792,34 +2712,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // in 'lnum', then display the sign instead of the line // number. if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' - && num_signs > 0) { - int count = win_signcol_count(wp); + && num_signs > 0 && sign_get_attr(SIGN_TEXT, sattrs, 0, 1)) { get_sign_display_info(true, wp, lnum, sattrs, row, - startrow, filler_lines, filler_todo, count, + startrow, filler_lines, filler_todo, &c_extra, &c_final, extra, sizeof(extra), - &p_extra, &n_extra, - &char_attr, &draw_state, &sign_idx); + &p_extra, &n_extra, &char_attr, sign_idx); } else { + // Draw the line number (empty space after wrapping). if (row == startrow + filler_lines) { - // Draw the line number (empty space after wrapping). */ - long num; - char *fmt = "%*ld "; - - if (wp->w_p_nu && !wp->w_p_rnu) { - // 'number' + 'norelativenumber' - num = (long)lnum; - } else { - // 'relativenumber', don't use negative numbers - num = labs((long)get_cursor_rel_lnum(wp, lnum)); - if (num == 0 && wp->w_p_nu && wp->w_p_rnu) { - // 'number' + 'relativenumber' - num = lnum; - fmt = "%-*ld "; - } - } - - snprintf((char *)extra, sizeof(extra), - fmt, number_width(wp), num); + get_line_number_str(wp, lnum, (char_u *)extra, sizeof(extra)); if (wp->w_skipcol > 0) { for (p_extra = extra; *p_extra == ' '; p_extra++) { *p_extra = '-'; @@ -2827,9 +2728,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } if (wp->w_p_rl) { // reverse line numbers // like rl_mirror(), but keep the space at the end - char_u *p2 = skipwhite(extra); + char_u *p2 = (char_u *)skipwhite((char *)extra); p2 = skiptowhite(p2) - 1; - for (char_u *p1 = skipwhite(extra); p1 < p2; p1++, p2--) { + for (char_u *p1 = (char_u *)skipwhite((char *)extra); p1 < p2; p1++, p2--) { const int t = *p1; *p1 = *p2; *p2 = t; @@ -2837,41 +2738,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } p_extra = extra; c_extra = NUL; - c_final = NUL; } else { c_extra = ' '; - c_final = NUL; } + c_final = NUL; n_extra = number_width(wp) + 1; - char_attr = win_hl_attr(wp, HLF_N); - - if (wp->w_p_rnu && lnum < wp->w_cursor.lnum) { - // Use LineNrAbove - char_attr = win_hl_attr(wp, HLF_LNA); - } - if (wp->w_p_rnu && lnum > wp->w_cursor.lnum) { - // Use LineNrBelow - char_attr = win_hl_attr(wp, HLF_LNB); - } - - sign_attrs_T *num_sattr = sign_get_attr(SIGN_NUMHL, sattrs, 0, 1); - if (num_sattr != NULL) { - // :sign defined with "numhl" highlight. - char_attr = num_sattr->sat_numhl; - } else if (wp->w_p_cul - && lnum == wp->w_cursor.lnum - && (wp->w_p_culopt_flags & CULOPT_NBR) - && (row == startrow - || wp->w_p_culopt_flags & CULOPT_LINE) - && filler_todo == 0) { - // When 'cursorline' is set highlight the line number of - // the current line differently. - // When 'cursorlineopt' has "screenline" only highlight - // the line number itself. - // TODO(vim): Can we use CursorLine instead of CursorLineNr - // when CursorLineNr isn't set? - char_attr = win_hl_attr(wp, HLF_CLN); - } + char_attr = get_line_number_attr(wp, lnum, row, startrow, filler_lines, sattrs); } } } @@ -2932,7 +2804,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (wp->w_p_rl) { n_extra = col + 1; } else { - n_extra = grid->Columns - col; + n_extra = grid->cols - col; } char_attr = 0; } else if (filler_todo > 0) { @@ -2947,7 +2819,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (wp->w_p_rl) { n_extra = col + 1; } else { - n_extra = grid->Columns - col; + n_extra = grid->cols - col; } char_attr = win_hl_attr(wp, HLF_DED); } @@ -3005,7 +2877,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) { line_attr_lowprio = cul_attr; } else { - if (!(State & INSERT) && bt_quickfix(wp->w_buffer) + if (!(State & MODE_INSERT) && bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum) { line_attr = hl_combine_attr(cul_attr, line_attr); } else { @@ -3021,15 +2893,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc && vcol >= (long)wp->w_virtcol) || (number_only && draw_state > WL_NR)) && filler_todo <= 0) { - draw_virt_text(buf, win_col_offset, &col, grid->Columns); - grid_put_linebuf(grid, row, 0, col, -grid->Columns, wp->w_p_rl, wp, + draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row); + grid_put_linebuf(grid, row, 0, col, -grid->cols, wp->w_p_rl, wp, wp->w_hl_attr_normal, false); // Pretend we have finished updating the window. Except when // 'cursorcolumn' is set. if (wp->w_p_cuc) { row = wp->w_cline_row + wp->w_cline_height; } else { - row = grid->Rows; + row = grid->rows; } break; } @@ -3057,19 +2929,19 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (draw_state == WL_LINE && has_fold - && col < grid->Columns + && col < grid->cols && n_extra == 0 && row == startrow) { // fill rest of line with 'fold' c_extra = wp->w_p_fcs_chars.fold; c_final = NUL; - n_extra = wp->w_p_rl ? (col + 1) : (grid->Columns - col); + n_extra = wp->w_p_rl ? (col + 1) : (grid->cols - col); } if (draw_state == WL_LINE && has_fold - && col >= grid->Columns + && col >= grid->cols && n_extra != 0 && row == startrow) { // Truncate the folding. @@ -3080,7 +2952,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // handle Visual or match highlighting in this line if (vcol == fromcol || (vcol + 1 == fromcol && n_extra == 0 - && utf_ptr2cells(ptr) > 1) + && utf_ptr2cells((char *)ptr) > 1) || ((int)vcol_prev == fromcol_prev && vcol_prev < vcol // not at margin && vcol < tocol)) { @@ -3096,115 +2968,13 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } if (!n_extra) { - /* - * Check for start/end of search pattern match. - * After end, check for start/end of next match. - * When another match, have to check for start again. - * Watch out for matching an empty string! - * Do this for 'search_hl' and the match list (ordered by - * priority). - */ + // Check for start/end of 'hlsearch' and other matches. + // After end, check for start/end of next match. + // When another match, have to check for start again. v = (ptr - line); - cur = wp->w_match_head; - shl_flag = false; - while (cur != NULL || !shl_flag) { - if (!shl_flag - && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { - shl = &search_hl; - shl_flag = true; - } else { - shl = &cur->hl; - } - if (cur != NULL) { - cur->pos.cur = 0; - } - bool pos_inprogress = true; // mark that a position match search is - // in progress - while (shl->rm.regprog != NULL - || (cur != NULL && pos_inprogress)) { - if (shl->startcol != MAXCOL - && v >= (long)shl->startcol - && v < (long)shl->endcol) { - int tmp_col = v + utfc_ptr2len(ptr); - - if (shl->endcol < tmp_col) { - shl->endcol = tmp_col; - } - shl->attr_cur = shl->attr; - // Match with the "Conceal" group results in hiding - // the match. - if (cur != NULL - && shl != &search_hl - && syn_name2id("Conceal") == cur->hlg_id) { - has_match_conc = v == (long)shl->startcol ? 2 : 1; - match_conc = cur->conceal_char; - } else { - has_match_conc = 0; - } - } else if (v == (long)shl->endcol) { - shl->attr_cur = 0; - - next_search_hl(wp, shl, lnum, (colnr_T)v, - shl == &search_hl ? NULL : cur); - pos_inprogress = !(cur == NULL || cur->pos.cur == 0); - - // Need to get the line again, a multi-line regexp - // may have made it invalid. - line = ml_get_buf(wp->w_buffer, lnum, false); - ptr = line + v; - - if (shl->lnum == lnum) { - shl->startcol = shl->rm.startpos[0].col; - if (shl->rm.endpos[0].lnum == 0) { - shl->endcol = shl->rm.endpos[0].col; - } else { - shl->endcol = MAXCOL; - } - - if (shl->startcol == shl->endcol) { - // highlight empty match, try again after it - shl->endcol += utfc_ptr2len(line + shl->endcol); - } - - // Loop to check if the match starts at the - // current position - continue; - } - } - break; - } - if (shl != &search_hl && cur != NULL) { - cur = cur->next; - } - } - - /* Use attributes from match with highest priority among - * 'search_hl' and the match list. */ - search_attr_from_match = false; - search_attr = search_hl.attr_cur; - cur = wp->w_match_head; - shl_flag = false; - while (cur != NULL || !shl_flag) { - if (!shl_flag - && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { - shl = &search_hl; - shl_flag = true; - } else { - shl = &cur->hl; - } - if (shl->attr_cur != 0) { - search_attr = shl->attr_cur; - search_attr_from_match = shl != &search_hl; - } - if (shl != &search_hl && cur != NULL) { - cur = cur->next; - } - } - // Only highlight one character after the last column. - if (*ptr == NUL - && (wp->w_p_list && lcs_eol_one == -1)) { - search_attr = 0; - } + search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &search_hl, &has_match_conc, + &match_conc, lcs_eol_one, &search_attr_from_match); + ptr = line + v; // "line" may have been changed // Do not allow a conceal over EOL otherwise EOL will be missed // and bad things happen. @@ -3237,6 +3007,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (area_attr != 0) { char_attr = hl_combine_attr(line_attr, area_attr); + if (!highlight_match) { + // let search highlight show in Visual area if possible + char_attr = hl_combine_attr(search_attr, char_attr); + } } else if (search_attr != 0) { char_attr = hl_combine_attr(line_attr, search_attr); } @@ -3282,7 +3056,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc mb_c = c; // If the UTF-8 character is more than one byte: // Decode it into "mb_c". - mb_l = utfc_ptr2len(p_extra); + mb_l = utfc_ptr2len((char *)p_extra); mb_utf8 = false; if (mb_l > n_extra) { mb_l = 1; @@ -3296,7 +3070,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } // If a double-width char doesn't fit display a '>' in the last column. - if ((wp->w_p_rl ? (col <= 0) : (col >= grid->Columns - 1)) + if ((wp->w_p_rl ? (col <= 0) : (col >= grid->cols - 1)) && utf_char2cells(mb_c) == 2) { c = '>'; mb_c = c; @@ -3335,7 +3109,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc mb_c = c; // If the UTF-8 character is more than one byte: Decode it // into "mb_c". - mb_l = utfc_ptr2len(ptr); + mb_l = utfc_ptr2len((char *)ptr); mb_utf8 = false; if (mb_l > 1) { mb_c = utfc_ptr2char(ptr, u8cc); @@ -3383,7 +3157,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } } else if (mb_l == 0) { // at the NUL at end-of-line mb_l = 1; - } else if (p_arshape && !p_tbidi && arabic_char(mb_c)) { + } else if (p_arshape && !p_tbidi && ARABIC_CHAR(mb_c)) { // Do Arabic shaping. int pc, pc1, nc; int pcc[MAX_MCO]; @@ -3393,7 +3167,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (wp->w_p_rl) { pc = prev_c; pc1 = prev_c1; - nc = utf_ptr2char(ptr + mb_l); + nc = utf_ptr2char((char *)ptr + mb_l); prev_c1 = u8cc[0]; } else { pc = utfc_ptr2char(ptr + mb_l, pcc); @@ -3410,7 +3184,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // last column; the character is displayed at the start of the // next line. if ((wp->w_p_rl ? (col <= 0) : - (col >= grid->Columns - 1)) + (col >= grid->cols - 1)) && utf_char2cells(mb_c) == 2) { c = '>'; mb_c = c; @@ -3465,6 +3239,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc did_emsg = save_did_emsg; } + if (wp->w_s->b_syn_slow) { + has_syntax = false; + } + // Need to get the line again, a multi-line regexp may // have made it invalid. line = ml_get_buf(wp->w_buffer, lnum, false); @@ -3526,7 +3304,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc /* In Insert mode only highlight a word that * doesn't touch the cursor. */ if (spell_hlf != HLF_COUNT - && (State & INSERT) != 0 + && (State & MODE_INSERT) && wp->w_cursor.lnum == lnum && wp->w_cursor.col >= (colnr_T)(prev_ptr - line) @@ -3578,7 +3356,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (has_decor && v > 0) { bool selected = (area_active || (area_highlighting && noinvcur && (colnr_T)vcol == wp->w_virtcol)); - int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v-1, off, + int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v - 1, off, selected, &decor_state); if (extmark_attr != 0) { if (!attr_pri) { @@ -3587,6 +3365,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc char_attr = hl_combine_attr(extmark_attr, char_attr); } } + + decor_conceal = decor_state.conceal; + if (decor_conceal && decor_state.conceal_char) { + decor_conceal = 2; // really?? + } } // Found last space before word: check for line break. @@ -3606,7 +3389,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } } - if (c == TAB && n_extra + col > grid->Columns) { + if (c == TAB && n_extra + col > grid->cols) { n_extra = tabstop_padding(vcol, wp->w_buffer->b_p_ts, wp->w_buffer->b_p_vts_array) - 1; } @@ -3664,10 +3447,22 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } } - if ((trailcol != MAXCOL && ptr > line + trailcol && c == ' ') - || (leadcol != 0 && ptr < line + leadcol && c == ' ')) { - c = (ptr > line + trailcol) ? wp->w_p_lcs_chars.trail - : wp->w_p_lcs_chars.lead; + if (c == ' ' && ((trailcol != MAXCOL && ptr > line + trailcol) + || (leadcol != 0 && ptr < line + leadcol))) { + if (leadcol != 0 && in_multispace && ptr < line + leadcol + && wp->w_p_lcs_chars.leadmultispace != NULL) { + c = wp->w_p_lcs_chars.leadmultispace[multispace_pos++]; + if (wp->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) { + multispace_pos = 0; + } + } else if (ptr > line + trailcol && wp->w_p_lcs_chars.trail) { + c = wp->w_p_lcs_chars.trail; + } else if (ptr < line + leadcol && wp->w_p_lcs_chars.lead) { + c = wp->w_p_lcs_chars.lead; + } else if (leadcol != 0 && wp->w_p_lcs_chars.space) { + c = wp->w_p_lcs_chars.space; + } + n_attr = 1; extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = char_attr; // save current attr @@ -3720,10 +3515,13 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc tab_len += n_extra - tab_len; } - // if n_extra > 0, it gives the number of chars + // If n_extra > 0, it gives the number of chars // to use for a tab, else we need to calculate the width - // for a tab + // for a tab. int len = (tab_len * utf_char2len(wp->w_p_lcs_chars.tab2)); + if (wp->w_p_lcs_chars.tab3) { + len += utf_char2len(wp->w_p_lcs_chars.tab3); + } if (n_extra > 0) { len += n_extra - tab_len; } @@ -3740,13 +3538,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } int lcs = wp->w_p_lcs_chars.tab2; - // if tab3 is given, need to change the char - // for tab + // if tab3 is given, use it for the last char if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) { lcs = wp->w_p_lcs_chars.tab3; } - utf_char2bytes(lcs, p); - p += utf_char2len(lcs); + p += utf_char2bytes(lcs, (char *)p); n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0); } p_extra = p_extra_free; @@ -3808,7 +3604,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc || ((fromcol >= 0 || fromcol_prev >= 0) && tocol > vcol && VIsual_mode != Ctrl_V - && (wp->w_p_rl ? (col >= 0) : (col < grid->Columns)) + && (wp->w_p_rl ? (col >= 0) : (col < grid->cols)) && !(noinvcur && lnum == wp->w_cursor.lnum && (colnr_T)vcol == wp->w_virtcol))) @@ -3880,28 +3676,33 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc && virtual_active() && tocol != MAXCOL && vcol < tocol - && (wp->w_p_rl ? (col >= 0) : (col < grid->Columns))) { + && (wp->w_p_rl ? (col >= 0) : (col < grid->cols))) { c = ' '; ptr--; // put it back at the NUL } } if (wp->w_p_cole > 0 - && (wp != curwin || lnum != wp->w_cursor.lnum - || conceal_cursor_line(wp)) - && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0) - && !(lnum_in_visual_area - && vim_strchr(wp->w_p_cocu, 'v') == NULL)) { + && (wp != curwin || lnum != wp->w_cursor.lnum || conceal_cursor_line(wp)) + && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0 || decor_conceal > 0) + && !(lnum_in_visual_area && vim_strchr((char *)wp->w_p_cocu, 'v') == NULL)) { char_attr = conceal_attr; - if ((prev_syntax_id != syntax_seqnr || has_match_conc > 1) + if (((prev_syntax_id != syntax_seqnr && (syntax_flags & HL_CONCEAL) != 0) + || has_match_conc > 1 || decor_conceal > 1) && (syn_get_sub_char() != NUL || (has_match_conc && match_conc) + || (decor_conceal && decor_state.conceal_char) || wp->w_p_cole == 1) && wp->w_p_cole != 3) { // First time at this concealed item: display one // character. if (has_match_conc && match_conc) { c = match_conc; + } else if (decor_conceal && decor_state.conceal_char) { + c = decor_state.conceal_char; + if (decor_state.conceal_attr) { + char_attr = decor_state.conceal_attr; + } } else if (syn_get_sub_char() != NUL) { c = syn_get_sub_char(); } else if (wp->w_p_lcs_chars.conceal != NUL) { @@ -3957,7 +3758,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc && conceal_cursor_line(wp) && (int)wp->w_virtcol <= vcol + n_skip) { if (wp->w_p_rl) { - wp->w_wcol = grid->Columns - col + boguscols - 1; + wp->w_wcol = grid->cols - col + boguscols - 1; } else { wp->w_wcol = col - boguscols; } @@ -4006,30 +3807,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // At end of the text line or just after the last character. if (c == NUL && eol_hl_off == 0) { - long prevcol = (ptr - line) - 1; - - // we're not really at that column when skipping some text - if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol) { - prevcol++; - } + // flag to indicate whether prevcol equals startcol of search_hl or + // one of the matches + bool prevcol_hl_flag = get_prevcol_hl_flag(wp, &search_hl, + (long)(ptr - line) - 1); // Invert at least one char, used for Visual and empty line or // highlight match at end of line. If it's beyond the last // char on the screen, just overwrite that one (tricky!) Not // needed when a '$' was displayed for 'list'. - prevcol_hl_flag = false; - if (!search_hl.is_addpos && prevcol == (long)search_hl.startcol) { - prevcol_hl_flag = true; - } else { - cur = wp->w_match_head; - while (cur != NULL) { - if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol) { - prevcol_hl_flag = true; - break; - } - cur = cur->next; - } - } if (wp->w_p_lcs_chars.eol == lcs_eol_one && ((area_attr != 0 && vcol == fromcol && (VIsual_mode != Ctrl_V @@ -4044,7 +3830,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc n = 1; } } else { - if (col >= grid->Columns) { + if (col >= grid->cols) { n = -1; } } @@ -4060,25 +3846,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (area_attr == 0 && !has_fold) { // Use attributes from match with highest priority among // 'search_hl' and the match list. - char_attr = search_hl.attr; - cur = wp->w_match_head; - shl_flag = false; - while (cur != NULL || !shl_flag) { - if (!shl_flag - && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { - shl = &search_hl; - shl_flag = true; - } else { - shl = &cur->hl; - } - if ((ptr - line) - 1 == (long)shl->startcol - && (shl == &search_hl || !shl->is_addpos)) { - char_attr = shl->attr; - } - if (shl != &search_hl && cur != NULL) { - cur = cur->next; - } - } + get_search_match_hl(wp, &search_hl, (long)(ptr - line), &char_attr); } int eol_attr = char_attr; @@ -4130,7 +3898,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (((wp->w_p_cuc && (int)wp->w_virtcol >= VCOL_HLC - eol_hl_off && (int)wp->w_virtcol < - (long)grid->Columns * (row - startrow + 1) + v + (long)grid->cols * (row - startrow + 1) + v && lnum != wp->w_cursor.lnum) || draw_color_col || line_attr_lowprio || line_attr || diff_hlf != (hlf_T)0 || has_virttext)) { @@ -4168,7 +3936,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc int col_stride = wp->w_p_rl ? -1 : 1; - while (wp->w_p_rl ? col >= 0 : col < grid->Columns) { + while (wp->w_p_rl ? col >= 0 : col < grid->cols) { schar_from_ascii(linebuf_char[off], ' '); col += col_stride; if (draw_color_col) { @@ -4204,7 +3972,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // terminal buffers may need to highlight beyond the end of the // logical line int n = wp->w_p_rl ? -1 : 1; - while (col >= 0 && col < grid->Columns) { + while (col >= 0 && col < grid->cols) { schar_from_ascii(linebuf_char[off], ' '); linebuf_attr[off] = vcol >= TERM_ATTRS_MAX ? 0 : term_attrs[vcol]; off += n; @@ -4213,8 +3981,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } } - draw_virt_text(buf, win_col_offset, &col, grid->Columns); - grid_put_linebuf(grid, row, 0, col, grid->Columns, wp->w_p_rl, wp, + draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row); + grid_put_linebuf(grid, row, 0, col, grid->cols, wp->w_p_rl, wp, wp->w_hl_attr_normal, false); row++; @@ -4235,10 +4003,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // Show "extends" character from 'listchars' if beyond the line end and // 'list' is set. if (wp->w_p_lcs_chars.ext != NUL + && draw_state == WL_LINE && wp->w_p_list && !wp->w_p_wrap && filler_todo <= 0 - && (wp->w_p_rl ? col == 0 : col == grid->Columns - 1) + && (wp->w_p_rl ? col == 0 : col == grid->cols - 1) && !has_fold && (*ptr != NUL || lcs_eol_one > 0 @@ -4373,7 +4142,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc n_attr = 0; } - if (utf_char2cells(mb_c) > 1) { // Need to fill two screen columns. if (wp->w_p_rl) { @@ -4428,9 +4196,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc * At end of screen line and there is more to come: Display the line * so far. If there is no more to display it is caught above. */ - if ((wp->w_p_rl ? (col < 0) : (col >= grid->Columns)) + if ((wp->w_p_rl ? (col < 0) : (col >= grid->cols)) && foldinfo.fi_lines == 0 - && (*ptr != NUL + && (draw_state != WL_LINE + || *ptr != NUL || filler_todo > 0 || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL && p_extra != at_end_str) @@ -4440,7 +4209,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc && filler_todo <= 0 // Not drawing diff filler lines. && lcs_eol_one != -1 // Haven't printed the lcs_eol character. && row != endrow - 1 // Not the last line being displayed. - && (grid->Columns == Columns // Window spans the width of the screen, + && (grid->cols == Columns // Window spans the width of the screen, || ui_has(kUIMultigrid)) // or has dedicated grid. && !wp->w_p_rl; // Not right-to-left. @@ -4452,21 +4221,21 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc assert(i >= 0); int offset = kv_A(virt_lines, i).left_col ? 0 : win_col_offset; draw_virt_text_item(buf, offset, kv_A(virt_lines, i).line, - kHlModeReplace, grid->Columns, offset); + kHlModeReplace, grid->cols, offset); } } else { - draw_virt_text(buf, win_col_offset, &draw_col, grid->Columns); + draw_virt_text(wp, buf, win_col_offset, &draw_col, grid->cols, row); } - grid_put_linebuf(grid, row, 0, draw_col, grid->Columns, wp->w_p_rl, + grid_put_linebuf(grid, row, 0, draw_col, grid->cols, wp->w_p_rl, wp, wp->w_hl_attr_normal, wrap); if (wrap) { ScreenGrid *current_grid = grid; int current_row = row, dummy_col = 0; // dummy_col unused - screen_adjust_grid(¤t_grid, ¤t_row, &dummy_col); + grid_adjust(¤t_grid, ¤t_row, &dummy_col); // Force a redraw of the first column of the next line. - current_grid->attrs[current_grid->line_offset[current_row+1]] = -1; + current_grid->attrs[current_grid->line_offset[current_row + 1]] = -1; // Remember that the line wraps, used for modeless copy. current_grid->line_wraps[current_row] = true; @@ -4485,7 +4254,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // When the window is too narrow draw all "@" lines. if (draw_state != WL_LINE && filler_todo <= 0) { - win_draw_end(wp, '@', ' ', true, row, wp->w_grid.Rows, HLF_AT); + win_draw_end(wp, '@', ' ', true, row, wp->w_grid.rows, HLF_AT); row = endrow; } @@ -4498,7 +4267,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc col = 0; off = 0; if (wp->w_p_rl) { - col = grid->Columns - 1; // col is not used if breaking! + col = grid->cols - 1; // col is not used if breaking! off += col; } @@ -4524,7 +4293,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } // for every character in the line // After an empty line check first word for capital. - if (*skipwhite(line) == NUL) { + if (*skipwhite((char *)line) == NUL) { capcol_lnum = lnum + 1; cap_col = 0; } @@ -4534,14 +4303,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc return row; } -void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col) +void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int max_col, int win_row) { DecorState *state = &decor_state; int right_pos = max_col; bool do_eol = state->eol_col > -1; for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange *item = &kv_A(state->active, i); - if (!(item->start_row == state->row && kv_size(item->decor.virt_text))) { + if (!(item->start_row == state->row + && (kv_size(item->decor.virt_text) || item->decor.ui_watched))) { continue; } if (item->win_col == -1) { @@ -4551,18 +4321,26 @@ void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col) } else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) { item->win_col = state->eol_col; } else if (item->decor.virt_text_pos == kVTWinCol) { - item->win_col = MAX(item->decor.col+col_off, 0); + item->win_col = MAX(item->decor.col + col_off, 0); } } if (item->win_col < 0) { continue; } - - int col = draw_virt_text_item(buf, item->win_col, item->decor.virt_text, - item->decor.hl_mode, max_col, item->win_col-col_off); + int col; + if (item->decor.ui_watched) { + // send mark position to UI + col = item->win_col; + WinExtmark m = { item->ns_id, item->mark_id, win_row, col }; + kv_push(win_extmark_arr, m); + } + if (kv_size(item->decor.virt_text)) { + col = draw_virt_text_item(buf, item->win_col, item->decor.virt_text, + item->decor.hl_mode, max_col, item->win_col - col_off); + } item->win_col = -2; // deactivate if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) { - state->eol_col = col+1; + state->eol_col = col + 1; } *end_col = MAX(*end_col, col); @@ -4593,6 +4371,9 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, break; } } + if (!*s.p) { + continue; + } int attr; bool through = false; if (hl_mode == kHlModeCombine) { @@ -4605,7 +4386,7 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, } schar_T dummy[2]; int cells = line_putchar(buf, &s, through ? dummy : &linebuf_char[col], - max_col-col, false, vcol); + max_col - col, false, vcol); // if we failed to emit a char, we still need to advance cells = MAX(cells, 1); @@ -4617,25 +4398,6 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, return col; } -/// Determine if dedicated window grid should be used or the default_grid -/// -/// If UI did not request multigrid support, draw all windows on the -/// default_grid. -/// -/// NB: this function can only been used with window grids in a context where -/// win_grid_alloc already has been called! -/// -/// If the default_grid is used, adjust window relative positions to global -/// screen positions. -void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off) -{ - if ((*grid)->target) { - *row_off += (*grid)->row_offset; - *col_off += (*grid)->col_offset; - *grid = (*grid)->target; - } -} - // Return true if CursorLineSign highlight is to be used. static bool use_cursor_line_sign(win_T *wp, linenr_T lnum) { @@ -4644,18 +4406,65 @@ static bool use_cursor_line_sign(win_T *wp, linenr_T lnum) && (wp->w_p_culopt_flags & CULOPT_NBR); } +/// Return true if CursorLineNr highlight is to be used for the number column. +/// +/// - 'cursorline' must be set +/// - lnum must be the cursor line +/// - 'cursorlineopt' has "number" +/// - don't highlight filler lines (when in diff mode) +/// - When line is wrapped and 'cursorlineopt' does not have "line", only highlight the line number +/// itself on the first screenline of the wrapped line, otherwise highlight the number column of +/// all screenlines of the wrapped line. +static bool use_cursor_line_nr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines) +{ + return wp->w_p_cul + && lnum == wp->w_cursor.lnum + && (wp->w_p_culopt_flags & CULOPT_NBR) + && (row == startrow + filler_lines + || (row > startrow + filler_lines + && (wp->w_p_culopt_flags & CULOPT_LINE))); +} + +static int get_line_number_attr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines, + sign_attrs_T *sattrs) +{ + sign_attrs_T *num_sattr = sign_get_attr(SIGN_NUMHL, sattrs, 0, 1); + if (num_sattr != NULL) { + // :sign defined with "numhl" highlight. + return num_sattr->sat_numhl; + } + + if (wp->w_p_rnu) { + if (lnum < wp->w_cursor.lnum) { + // Use LineNrAbove + return win_hl_attr(wp, HLF_LNA); + } + if (lnum > wp->w_cursor.lnum) { + // Use LineNrBelow + return win_hl_attr(wp, HLF_LNB); + } + } + + if (use_cursor_line_nr(wp, lnum, row, startrow, filler_lines)) { + // TODO(vim): Can we use CursorLine instead of CursorLineNr + // when CursorLineNr isn't set? + return win_hl_attr(wp, HLF_CLN); + } + + return win_hl_attr(wp, HLF_N); +} + // Get information needed to display the sign in line 'lnum' in window 'wp'. // If 'nrcol' is TRUE, the sign is going to be displayed in the number column. // Otherwise the sign is going to be displayed in the sign column. // // @param count max number of signs // @param[out] n_extrap number of characters from pp_extra to display -// @param[in, out] sign_idxp Index of the displayed sign +// @param sign_idxp Index of the displayed sign static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_attrs_T sattrs[], int row, int startrow, int filler_lines, int filler_todo, - int count, int *c_extrap, int *c_finalp, char_u *extra, - size_t extra_size, char_u **pp_extra, int *n_extrap, - int *char_attrp, int *draw_statep, int *sign_idxp) + int *c_extrap, int *c_finalp, char_u *extra, size_t extra_size, + char_u **pp_extra, int *n_extrap, int *char_attrp, int sign_idx) { // Draw cells with the sign value or blank. *c_extrap = ' '; @@ -4672,7 +4481,7 @@ static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_att } if (row == startrow + filler_lines && filler_todo <= 0) { - sign_attrs_T *sattr = sign_get_attr(SIGN_TEXT, sattrs, *sign_idxp, count); + sign_attrs_T *sattr = sign_get_attr(SIGN_TEXT, sattrs, sign_idx, wp->w_scwidth); if (sattr != NULL) { *pp_extra = sattr->sat_text; if (*pp_extra != NULL) { @@ -4694,10 +4503,10 @@ static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_att // TODO(oni-link): Is sign text already extended to // full cell width? - assert((size_t)win_signcol_width(wp) >= mb_string2cells(*pp_extra)); + assert((size_t)win_signcol_width(wp) >= mb_string2cells((char *)(*pp_extra))); // symbol(s) bytes + (filling spaces) (one byte each) *n_extrap = symbol_blen + - (win_signcol_width(wp) - mb_string2cells(*pp_extra)); + (win_signcol_width(wp) - mb_string2cells((char *)(*pp_extra))); assert(extra_size > (size_t)symbol_blen); memset(extra, ' ', extra_size); @@ -4715,205 +4524,6 @@ static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_att } } } - - (*sign_idxp)++; - if (*sign_idxp < count) { - *draw_statep = WL_SIGN - 1; - } else { - *sign_idxp = 0; - } -} - - -/* - * Check whether the given character needs redrawing: - * - the (first byte of the) character is different - * - the attributes are different - * - the character is multi-byte and the next byte is different - * - the character is two cells wide and the second cell differs. - */ -static int grid_char_needs_redraw(ScreenGrid *grid, int off_from, int off_to, int cols) -{ - return (cols > 0 - && ((schar_cmp(linebuf_char[off_from], grid->chars[off_to]) - || linebuf_attr[off_from] != grid->attrs[off_to] - || (line_off2cells(linebuf_char, off_from, off_from + cols) > 1 - && schar_cmp(linebuf_char[off_from + 1], - grid->chars[off_to + 1]))) - || rdb_flags & RDB_NODELTA)); -} - -/// Move one buffered line to the window grid, but only the characters that -/// have actually changed. Handle insert/delete character. -/// "coloff" gives the first column on the grid for this line. -/// "endcol" gives the columns where valid characters are. -/// "clear_width" is the width of the window. It's > 0 if the rest of the line -/// needs to be cleared, negative otherwise. -/// "rlflag" is TRUE in a rightleft window: -/// When TRUE and "clear_width" > 0, clear columns 0 to "endcol" -/// When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width" -/// If "wrap" is true, then hint to the UI that "row" contains a line -/// which has wrapped into the next row. -static void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int clear_width, - int rlflag, win_T *wp, int bg_attr, bool wrap) -{ - unsigned off_from; - unsigned off_to; - unsigned max_off_from; - unsigned max_off_to; - int col = 0; - bool redraw_this; // Does character need redraw? - bool redraw_next; // redraw_this for next character - bool clear_next = false; - int char_cells; // 1: normal char - // 2: occupies two display cells - int start_dirty = -1, end_dirty = 0; - - // TODO(bfredl): check all callsites and eliminate - // Check for illegal row and col, just in case - if (row >= grid->Rows) { - row = grid->Rows - 1; - } - if (endcol > grid->Columns) { - endcol = grid->Columns; - } - - screen_adjust_grid(&grid, &row, &coloff); - - // Safety check. Avoids clang warnings down the call stack. - if (grid->chars == NULL || row >= grid->Rows || coloff >= grid->Columns) { - DLOG("invalid state, skipped"); - return; - } - - off_from = 0; - off_to = grid->line_offset[row] + coloff; - max_off_from = linebuf_size; - max_off_to = grid->line_offset[row] + grid->Columns; - - if (rlflag) { - // Clear rest first, because it's left of the text. - if (clear_width > 0) { - while (col <= endcol && grid->chars[off_to][0] == ' ' - && grid->chars[off_to][1] == NUL - && grid->attrs[off_to] == bg_attr) { - ++off_to; - ++col; - } - if (col <= endcol) { - grid_fill(grid, row, row + 1, col + coloff, endcol + coloff + 1, - ' ', ' ', bg_attr); - } - } - col = endcol + 1; - off_to = grid->line_offset[row] + col + coloff; - off_from += col; - endcol = (clear_width > 0 ? clear_width : -clear_width); - } - - if (bg_attr) { - for (int c = col; c < endcol; c++) { - linebuf_attr[off_from+c] = - hl_combine_attr(bg_attr, linebuf_attr[off_from+c]); - } - } - - redraw_next = grid_char_needs_redraw(grid, off_from, off_to, endcol - col); - - while (col < endcol) { - char_cells = 1; - if (col + 1 < endcol) { - char_cells = line_off2cells(linebuf_char, off_from, max_off_from); - } - redraw_this = redraw_next; - redraw_next = grid_char_needs_redraw(grid, off_from + char_cells, - off_to + char_cells, - endcol - col - char_cells); - - if (redraw_this) { - if (start_dirty == -1) { - start_dirty = col; - } - end_dirty = col + char_cells; - // When writing a single-width character over a double-width - // character and at the end of the redrawn text, need to clear out - // the right half of the old character. - // Also required when writing the right half of a double-width - // char over the left half of an existing one - if (col + char_cells == endcol - && ((char_cells == 1 - && grid_off2cells(grid, off_to, max_off_to) > 1) - || (char_cells == 2 - && grid_off2cells(grid, off_to, max_off_to) == 1 - && grid_off2cells(grid, off_to + 1, max_off_to) > 1))) { - clear_next = true; - } - - schar_copy(grid->chars[off_to], linebuf_char[off_from]); - if (char_cells == 2) { - schar_copy(grid->chars[off_to+1], linebuf_char[off_from+1]); - } - - grid->attrs[off_to] = linebuf_attr[off_from]; - // For simplicity set the attributes of second half of a - // double-wide character equal to the first half. - if (char_cells == 2) { - grid->attrs[off_to + 1] = linebuf_attr[off_from]; - } - } - - off_to += char_cells; - off_from += char_cells; - col += char_cells; - } - - if (clear_next) { - // Clear the second half of a double-wide character of which the left - // half was overwritten with a single-wide character. - schar_from_ascii(grid->chars[off_to], ' '); - end_dirty++; - } - - int clear_end = -1; - if (clear_width > 0 && !rlflag) { - // blank out the rest of the line - // TODO(bfredl): we could cache winline widths - while (col < clear_width) { - if (grid->chars[off_to][0] != ' ' - || grid->chars[off_to][1] != NUL - || grid->attrs[off_to] != bg_attr) { - grid->chars[off_to][0] = ' '; - grid->chars[off_to][1] = NUL; - grid->attrs[off_to] = bg_attr; - if (start_dirty == -1) { - start_dirty = col; - end_dirty = col; - } else if (clear_end == -1) { - end_dirty = endcol; - } - clear_end = col+1; - } - col++; - off_to++; - } - } - - if (clear_width > 0 || wp->w_width != grid->Columns) { - // If we cleared after the end of the line, it did not wrap. - // For vsplit, line wrapping is not possible. - grid->line_wraps[row] = false; - } - - if (clear_end < end_dirty) { - clear_end = end_dirty; - } - if (start_dirty == -1) { - start_dirty = end_dirty; - } - if (clear_end > start_dirty) { - ui_line(grid, row, coloff+start_dirty, coloff+end_dirty, coloff+clear_end, - bg_attr, wrap); - } } /* @@ -4932,30 +4542,34 @@ void rl_mirror(char_u *str) } } -/* - * mark all status lines for redraw; used after first :cd - */ +/// Mark all status lines and window bars for redraw; used after first :cd void status_redraw_all(void) { + bool is_stl_global = global_stl_height() != 0; + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_status_height) { + if ((!is_stl_global && wp->w_status_height) || (is_stl_global && wp == curwin) + || wp->w_winbar_height) { wp->w_redr_status = true; redraw_later(wp, VALID); } } } -/// Marks all status lines of the current buffer for redraw. +/// Marks all status lines and window bars of the current buffer for redraw. void status_redraw_curbuf(void) { status_redraw_buf(curbuf); } -/// Marks all status lines of the specified buffer for redraw. +/// Marks all status lines and window bars of the given buffer for redraw. void status_redraw_buf(buf_T *buf) { + bool is_stl_global = global_stl_height() != 0; + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_status_height != 0 && wp->w_buffer == buf) { + if (wp->w_buffer == buf && ((!is_stl_global && wp->w_status_height) + || (is_stl_global && wp == curwin) || wp->w_winbar_height)) { wp->w_redr_status = true; redraw_later(wp, VALID); } @@ -4969,6 +4583,7 @@ void redraw_statuslines(void) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_redr_status) { + win_redr_winbar(wp); win_redr_status(wp); } } @@ -4999,10 +4614,8 @@ void win_redraw_last_status(const frame_T *frp) } } -/* - * Draw the verticap separator right of window "wp" starting with line "row". - */ -static void draw_vsep_win(win_T *wp, int row) +/// Draw the vertical separator right of window "wp" +static void draw_vsep_win(win_T *wp) { int hl; int c; @@ -5010,15 +4623,97 @@ static void draw_vsep_win(win_T *wp, int row) if (wp->w_vsep_width) { // draw the vertical separator right of this window c = fillchar_vsep(wp, &hl); - grid_fill(&default_grid, wp->w_winrow + row, W_ENDROW(wp), + grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp), W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl); } } +/// Draw the horizontal separator below window "wp" +static void draw_hsep_win(win_T *wp) +{ + int hl; + int c; + + if (wp->w_hsep_height) { + // draw the horizontal separator below this window + c = fillchar_hsep(wp, &hl); + grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1, + wp->w_wincol, W_ENDCOL(wp), c, c, hl); + } +} + +/// Get the separator connector for specified window corner of window "wp" +static int get_corner_sep_connector(win_T *wp, WindowCorner corner) +{ + // It's impossible for windows to be connected neither vertically nor horizontally + // So if they're not vertically connected, assume they're horizontally connected + if (vsep_connected(wp, corner)) { + if (hsep_connected(wp, corner)) { + return wp->w_p_fcs_chars.verthoriz; + } else if (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) { + return wp->w_p_fcs_chars.vertright; + } else { + return wp->w_p_fcs_chars.vertleft; + } + } else if (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) { + return wp->w_p_fcs_chars.horizdown; + } else { + return wp->w_p_fcs_chars.horizup; + } +} -/* - * Get the length of an item as it will be shown in the status line. - */ +/// Draw separator connecting characters on the corners of window "wp" +static void draw_sep_connectors_win(win_T *wp) +{ + // Don't draw separator connectors unless global statusline is enabled and the window has + // either a horizontal or vertical separator + if (global_stl_height() == 0 || !(wp->w_hsep_height == 1 || wp->w_vsep_width == 1)) { + return; + } + + int hl = win_hl_attr(wp, HLF_C); + + // Determine which edges of the screen the window is located on so we can avoid drawing separators + // on corners contained in those edges + bool win_at_top; + bool win_at_bottom = wp->w_hsep_height == 0; + bool win_at_left; + bool win_at_right = wp->w_vsep_width == 0; + frame_T *frp; + + for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) { + if (frp->fr_parent->fr_layout == FR_COL && frp->fr_prev != NULL) { + break; + } + } + win_at_top = frp->fr_parent == NULL; + for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) { + if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_prev != NULL) { + break; + } + } + win_at_left = frp->fr_parent == NULL; + + // Draw the appropriate separator connector in every corner where drawing them is necessary + if (!(win_at_top || win_at_left)) { + grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_LEFT), + wp->w_winrow - 1, wp->w_wincol - 1, hl); + } + if (!(win_at_top || win_at_right)) { + grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_RIGHT), + wp->w_winrow - 1, W_ENDCOL(wp), hl); + } + if (!(win_at_bottom || win_at_left)) { + grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_LEFT), + W_ENDROW(wp), wp->w_wincol - 1, hl); + } + if (!(win_at_bottom || win_at_right)) { + grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_RIGHT), + W_ENDROW(wp), W_ENDCOL(wp), hl); + } +} + +/// Get the length of an item as it will be shown in the status line. static int status_match_len(expand_T *xp, char_u *s) { int len = 0; @@ -5027,13 +4722,13 @@ static int status_match_len(expand_T *xp, char_u *s) || xp->xp_context == EXPAND_MENUNAMES); // Check for menu separators - replace with '|'. - if (emenu && menu_is_separator(s)) { + if (emenu && menu_is_separator((char *)s)) { return 1; } while (*s != NUL) { s += skip_status_match_char(xp, s); - len += ptr2cells(s); + len += ptr2cells((char *)s); MB_PTR_ADV(s); } @@ -5153,7 +4848,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in clen = len; i = first_match; - while ((long)(clen + status_match_len(xp, L_MATCH(i)) + 2) < Columns) { + while (clen + status_match_len(xp, L_MATCH(i)) + 2 < Columns) { if (i == match) { selstart = buf + len; selstart_col = clen; @@ -5163,7 +4858,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in // Check for menu separators - replace with '|' emenu = (xp->xp_context == EXPAND_MENUS || xp->xp_context == EXPAND_MENUNAMES); - if (emenu && menu_is_separator(s)) { + if (emenu && menu_is_separator((char *)s)) { STRCPY(buf + len, transchar('|')); l = (int)STRLEN(buf + len); len += l; @@ -5171,8 +4866,8 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in } else { for (; *s != NUL; ++s) { s += skip_status_match_char(xp, s); - clen += ptr2cells(s); - if ((l = utfc_ptr2len(s)) > 1) { + clen += ptr2cells((char *)s); + if ((l = utfc_ptr2len((char *)s)) > 1) { STRNCPY(buf + len, s, l); // NOLINT(runtime/printf) s += l - 1; len += l; @@ -5219,7 +4914,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in // Create status line if needed by setting 'laststatus' to 2. // Set 'winminheight' to zero to avoid that the window is // resized. - if (lastwin->w_status_height == 0) { + if (lastwin->w_status_height == 0 && global_stl_height() == 0) { save_p_ls = p_ls; save_p_wmh = p_wmh; p_ls = 2; @@ -5255,12 +4950,15 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in static void win_redr_status(win_T *wp) { int row; + int col; char_u *p; int len; int fillchar; int attr; + int width; int this_ru_col; - static int busy = FALSE; + bool is_stl_global = global_stl_height() > 0; + static int busy = false; // May get here recursively when 'statusline' (indirectly) // invokes ":redrawstatus". Simply ignore the call then. @@ -5271,9 +4969,9 @@ static void win_redr_status(win_T *wp) } busy = true; - wp->w_redr_status = FALSE; - if (wp->w_status_height == 0) { - // no status line, can only be last window + wp->w_redr_status = false; + if (wp->w_status_height == 0 && !(is_stl_global && wp == curwin)) { + // no status line, either global statusline is enabled or the window is a last window redraw_cmdline = true; } else if (!redrawing()) { // Don't redraw right now, do it later. Don't update status line when @@ -5284,6 +4982,7 @@ static void win_redr_status(win_T *wp) redraw_custom_statusline(wp); } else { fillchar = fillchar_status(&attr, wp); + width = is_stl_global ? Columns : wp->w_width; get_trans_bufname(wp->w_buffer); p = NameBuff; @@ -5296,25 +4995,25 @@ static void win_redr_status(win_T *wp) *(p + len++) = ' '; } if (bt_help(wp->w_buffer)) { - STRCPY(p + len, _("[Help]")); + snprintf((char *)p + len, MAXPATHL - len, "%s", _("[Help]")); len += (int)STRLEN(p + len); } if (wp->w_p_pvw) { - STRCPY(p + len, _("[Preview]")); + snprintf((char *)p + len, MAXPATHL - len, "%s", _("[Preview]")); len += (int)STRLEN(p + len); } if (bufIsChanged(wp->w_buffer)) { - STRCPY(p + len, "[+]"); - len += 3; + snprintf((char *)p + len, MAXPATHL - len, "%s", "[+]"); + len += (int)STRLEN(p + len); } if (wp->w_buffer->b_p_ro) { - STRCPY(p + len, _("[RO]")); + snprintf((char *)p + len, MAXPATHL - len, "%s", _("[RO]")); // len += (int)STRLEN(p + len); // dead assignment } - this_ru_col = ru_col - (Columns - wp->w_width); - if (this_ru_col < (wp->w_width + 1) / 2) { - this_ru_col = (wp->w_width + 1) / 2; + this_ru_col = ru_col - (Columns - width); + if (this_ru_col < (width + 1) / 2) { + this_ru_col = (width + 1) / 2; } if (this_ru_col <= 1) { p = (char_u *)"<"; // No room for file name! @@ -5323,13 +5022,13 @@ static void win_redr_status(win_T *wp) int clen = 0, i; // Count total number of display cells. - clen = (int)mb_string2cells(p); + clen = (int)mb_string2cells((char *)p); // Find first character that will fit. // Going from start to end is much faster for DBCS. for (i = 0; p[i] != NUL && clen >= this_ru_col - 1; - i += utfc_ptr2len(p + i)) { - clen -= utf_ptr2cells(p + i); + i += utfc_ptr2len((char *)p + i)) { + clen -= utf_ptr2cells((char *)p + i); } len = clen; if (i > 0) { @@ -5339,12 +5038,13 @@ static void win_redr_status(win_T *wp) } } - row = W_ENDROW(wp); - grid_puts(&default_grid, p, row, wp->w_wincol, attr); - grid_fill(&default_grid, row, row + 1, len + wp->w_wincol, - this_ru_col + wp->w_wincol, fillchar, fillchar, attr); + row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp); + col = is_stl_global ? 0 : wp->w_wincol; + grid_puts(&default_grid, p, row, col, attr); + grid_fill(&default_grid, row, row + 1, len + col, + this_ru_col + col, fillchar, fillchar, attr); - if (get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL) + if (get_keymap_str(wp, "<%s>", (char *)NameBuff, MAXPATHL) && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) { grid_puts(&default_grid, NameBuff, row, (int)(this_ru_col - STRLEN(NameBuff) - 1), attr); @@ -5384,12 +5084,12 @@ static void redraw_custom_statusline(win_T *wp) entered = true; did_emsg = false; - win_redr_custom(wp, false); + win_redr_custom(wp, false, false); if (did_emsg) { // When there is an error disable the statusline, otherwise the // display is messed up with errors and a redraw triggers the problem // again and again. - set_string_option_direct("statusline", -1, (char_u *)"", + set_string_option_direct("statusline", -1, "", OPT_FREE | (*wp->w_p_stl != NUL ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR); } @@ -5397,6 +5097,37 @@ static void redraw_custom_statusline(win_T *wp) entered = false; } +static void win_redr_winbar(win_T *wp) +{ + static bool entered = false; + + // Return when called recursively. This can happen when the winbar contains an expression + // that triggers a redraw. + if (entered) { + return; + } + entered = true; + + if (wp->w_winbar_height == 0 || !redrawing()) { + // Do nothing. + } else if (*p_wbr != NUL || *wp->w_p_wbr != NUL) { + int saved_did_emsg = did_emsg; + + did_emsg = false; + win_redr_custom(wp, true, false); + if (did_emsg) { + // When there is an error disable the winbar, otherwise the + // display is messed up with errors and a redraw triggers the problem + // again and again. + set_string_option_direct("winbar", -1, "", + OPT_FREE | (*wp->w_p_stl != NUL + ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR); + } + did_emsg |= saved_did_emsg; + } + entered = false; +} + /// Only call if (wp->w_vsep_width != 0). /// /// @return true if the status line of window "wp" is connected to the status @@ -5421,15 +5152,85 @@ bool stl_connected(win_T *wp) return false; } +/// Check if horizontal separator of window "wp" at specified window corner is connected to the +/// horizontal separator of another window +/// Assumes global statusline is enabled +static bool hsep_connected(win_T *wp, WindowCorner corner) +{ + bool before = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT); + int sep_row = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) + ? wp->w_winrow - 1 : W_ENDROW(wp); + frame_T *fr = wp->w_frame; + + while (fr->fr_parent != NULL) { + if (fr->fr_parent->fr_layout == FR_ROW && (before ? fr->fr_prev : fr->fr_next) != NULL) { + fr = before ? fr->fr_prev : fr->fr_next; + break; + } + fr = fr->fr_parent; + } + if (fr->fr_parent == NULL) { + return false; + } + while (fr->fr_layout != FR_LEAF) { + fr = fr->fr_child; + if (fr->fr_parent->fr_layout == FR_ROW && before) { + while (fr->fr_next != NULL) { + fr = fr->fr_next; + } + } else { + while (fr->fr_next != NULL && frame2win(fr)->w_winrow + fr->fr_height < sep_row) { + fr = fr->fr_next; + } + } + } + + return (sep_row == fr->fr_win->w_winrow - 1 || sep_row == W_ENDROW(fr->fr_win)); +} + +/// Check if vertical separator of window "wp" at specified window corner is connected to the +/// vertical separator of another window +static bool vsep_connected(win_T *wp, WindowCorner corner) +{ + bool before = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT); + int sep_col = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) + ? wp->w_wincol - 1 : W_ENDCOL(wp); + frame_T *fr = wp->w_frame; + + while (fr->fr_parent != NULL) { + if (fr->fr_parent->fr_layout == FR_COL && (before ? fr->fr_prev : fr->fr_next) != NULL) { + fr = before ? fr->fr_prev : fr->fr_next; + break; + } + fr = fr->fr_parent; + } + if (fr->fr_parent == NULL) { + return false; + } + while (fr->fr_layout != FR_LEAF) { + fr = fr->fr_child; + if (fr->fr_parent->fr_layout == FR_COL && before) { + while (fr->fr_next != NULL) { + fr = fr->fr_next; + } + } else { + while (fr->fr_next != NULL && frame2win(fr)->w_wincol + fr->fr_width < sep_col) { + fr = fr->fr_next; + } + } + } + + return (sep_col == fr->fr_win->w_wincol - 1 || sep_col == W_ENDCOL(fr->fr_win)); +} /// Get the value to show for the language mappings, active 'keymap'. /// /// @param fmt format string containing one %s item /// @param buf buffer for the result /// @param len length of buffer -bool get_keymap_str(win_T *wp, char_u *fmt, char_u *buf, int len) +bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len) { - char_u *p; + char *p; if (wp->w_buffer->b_p_iminsert != B_IMODE_LMAP) { return false; @@ -5438,7 +5239,7 @@ bool get_keymap_str(win_T *wp, char_u *fmt, char_u *buf, int len) { buf_T *old_curbuf = curbuf; win_T *old_curwin = curwin; - char_u *s; + char *s; curbuf = wp->w_buffer; curwin = wp; @@ -5450,12 +5251,12 @@ bool get_keymap_str(win_T *wp, char_u *fmt, char_u *buf, int len) curwin = old_curwin; if (p == NULL || *p == NUL) { if (wp->w_buffer->b_kmap_state & KEYMAP_LOADED) { - p = wp->w_buffer->b_p_keymap; + p = (char *)wp->w_buffer->b_p_keymap; } else { - p = (char_u *)"lang"; + p = "lang"; } } - if (vim_snprintf((char *)buf, len, (char *)fmt, p) > len - 1) { + if (vim_snprintf(buf, len, fmt, p) > len - 1) { buf[0] = NUL; } xfree(s); @@ -5463,11 +5264,9 @@ bool get_keymap_str(win_T *wp, char_u *fmt, char_u *buf, int len) return buf[0] != NUL; } -/* - * Redraw the status line or ruler of window "wp". - * When "wp" is NULL redraw the tab pages line from 'tabline'. - */ -static void win_redr_custom(win_T *wp, bool draw_ruler) +/// Redraw the status line, window bar or ruler of window "wp". +/// When "wp" is NULL redraw the tab pages line from 'tabline'. +static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) { static bool entered = false; int attr; @@ -5479,14 +5278,15 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) int n; int len; int fillchar; - char_u buf[MAXPATHL]; + char buf[MAXPATHL]; char_u *stl; - char_u *p; + char *p; stl_hlrec_t *hltab; StlClickRecord *tabtab; int use_sandbox = false; win_T *ewp; int p_crb_save; + bool is_stl_global = global_stl_height() > 0; ScreenGrid *grid = &default_grid; @@ -5507,10 +5307,41 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) attr = HL_ATTR(HLF_TPF); maxwidth = Columns; use_sandbox = was_set_insecurely(wp, "tabline", 0); + } else if (draw_winbar) { + stl = (char_u *)((*wp->w_p_wbr != NUL) ? wp->w_p_wbr : p_wbr); + row = -1; // row zero is first row of text + col = 0; + grid = &wp->w_grid; + grid_adjust(&grid, &row, &col); + + if (row < 0) { + return; + } + + fillchar = wp->w_p_fcs_chars.wbr; + attr = (wp == curwin) ? win_hl_attr(wp, HLF_WBR) : win_hl_attr(wp, HLF_WBRNC); + maxwidth = wp->w_width_inner; + use_sandbox = was_set_insecurely(wp, "winbar", 0); + + stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size); + // Allocate / resize the click definitions array for winbar if needed. + if (wp->w_winbar_height && wp->w_winbar_click_defs_size < (size_t)maxwidth) { + xfree(wp->w_winbar_click_defs); + wp->w_winbar_click_defs_size = (size_t)maxwidth; + wp->w_winbar_click_defs = xcalloc(wp->w_winbar_click_defs_size, sizeof(StlClickRecord)); + } } else { - row = W_ENDROW(wp); + row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp); fillchar = fillchar_status(&attr, wp); - maxwidth = wp->w_width; + maxwidth = is_stl_global ? Columns : wp->w_width; + + stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size); + // Allocate / resize the click definitions array for statusline if needed. + if (wp->w_status_click_defs_size < (size_t)maxwidth) { + xfree(wp->w_status_click_defs); + wp->w_status_click_defs_size = maxwidth; + wp->w_status_click_defs = xcalloc(wp->w_status_click_defs_size, sizeof(StlClickRecord)); + } if (draw_ruler) { stl = p_ruf; @@ -5528,12 +5359,12 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) stl = p_ruf; } } - col = ru_col - (Columns - wp->w_width); - if (col < (wp->w_width + 1) / 2) { - col = (wp->w_width + 1) / 2; + col = ru_col - (Columns - maxwidth); + if (col < (maxwidth + 1) / 2) { + col = (maxwidth + 1) / 2; } - maxwidth = wp->w_width - col; - if (!wp->w_status_height) { + maxwidth = maxwidth - col; + if (!wp->w_status_height && !is_stl_global) { grid = &msg_grid_adj; row = Rows - 1; maxwidth--; // writing in last column may cause scrolling @@ -5551,7 +5382,7 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) use_sandbox = was_set_insecurely(wp, "statusline", *wp->w_p_stl == NUL ? 0 : OPT_LOCAL); } - col += wp->w_wincol; + col += is_stl_global ? 0 : wp->w_wincol; } if (maxwidth <= 0) { @@ -5568,13 +5399,13 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) * might change the option value and free the memory. */ stl = vim_strsave(stl); width = - build_stl_str_hl(ewp, buf, sizeof(buf), stl, use_sandbox, + build_stl_str_hl(ewp, buf, sizeof(buf), (char *)stl, use_sandbox, fillchar, maxwidth, &hltab, &tabtab); xfree(stl); ewp->w_p_crb = p_crb_save; // Make all characters printable. - p = (char_u *)transstr((const char *)buf, true); + p = transstr(buf, true); len = STRLCPY(buf, p, sizeof(buf)); len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1; xfree(p); @@ -5595,8 +5426,8 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) p = buf; for (n = 0; hltab[n].start != NULL; n++) { int textlen = (int)(hltab[n].start - p); - grid_puts_len(grid, p, textlen, row, col, curattr); - col += vim_strnsize(p, textlen); + grid_puts_len(grid, (char_u *)p, textlen, row, col, curattr); + col += vim_strnsize((char_u *)p, textlen); p = hltab[n].start; if (hltab[n].userhl == 0) { @@ -5610,31 +5441,43 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) } } // Make sure to use an empty string instead of p, if p is beyond buf + len. - grid_puts(grid, p >= buf + len ? (char_u *)"" : p, row, col, + grid_puts(grid, p >= buf + len ? (char_u *)"" : (char_u *)p, row, col, curattr); grid_puts_line_flush(false); - if (wp == NULL) { - // Fill the tab_page_click_defs array for clicking in the tab pages line. - col = 0; - len = 0; - p = buf; - StlClickDefinition cur_click_def = { - .type = kStlClickDisabled, - }; - for (n = 0; tabtab[n].start != NULL; n++) { - len += vim_strnsize(p, (int)(tabtab[n].start - (char *)p)); - while (col < len) { - tab_page_click_defs[col++] = cur_click_def; - } - p = (char_u *)tabtab[n].start; - cur_click_def = tabtab[n].def; + // Fill the tab_page_click_defs, w_status_click_defs or w_winbar_click_defs array for clicking + // in the tab page line, status line or window bar + StlClickDefinition *click_defs = (wp == NULL) ? tab_page_click_defs + : draw_winbar ? wp->w_winbar_click_defs + : wp->w_status_click_defs; + + if (click_defs == NULL) { + goto theend; + } + + col = 0; + len = 0; + p = buf; + StlClickDefinition cur_click_def = { + .type = kStlClickDisabled, + }; + for (n = 0; tabtab[n].start != NULL; n++) { + len += vim_strnsize((char_u *)p, (int)(tabtab[n].start - p)); + while (col < len) { + click_defs[col++] = cur_click_def; } - while (col < Columns) { - tab_page_click_defs[col++] = cur_click_def; + p = (char *)tabtab[n].start; + cur_click_def = tabtab[n].def; + if ((wp != NULL) && !(cur_click_def.type == kStlClickDisabled + || cur_click_def.type == kStlClickFuncRun)) { + // window bar and status line only support click functions + cur_click_def.type = kStlClickDisabled; } } + while (col < maxwidth) { + click_defs[col++] = cur_click_def; + } theend: entered = false; @@ -5653,9 +5496,9 @@ static void win_redr_border(win_T *wp) int *attrs = wp->w_float_config.border_attr; int *adj = wp->w_border_adj; - int irow = wp->w_height_inner, icol = wp->w_width_inner; + int irow = wp->w_height_inner + wp->w_winbar_height, icol = wp->w_width_inner; - char_u* title = wp->w_float_config.title; + char* title = wp->w_float_config.title; size_t n_title = wp->w_float_config.n_title; stl_hlrec_t* title_hl = wp->w_float_config.title_hl; @@ -5691,7 +5534,7 @@ static void win_redr_border(win_T *wp) int attr; // Draw the title if in the correct position. if (i > title_pos && n_title > 0 && i < icol - 2) { - cc = utfc_ptr2char(title, m8); + cc = utfc_ptr2char((char_u*) title, m8); len = utfc_ptr2len(title); n_title -= len; title += len; @@ -5709,373 +5552,44 @@ static void win_redr_border(win_T *wp) memcpy(ch, chars[1], sizeof(schar_T)); attr = attrs[1]; } - grid_put_schar(grid, 0, i+adj[3], ch, attr); + grid_put_schar(grid, 0, i + adj[3], ch, attr); } if (adj[1]) { - grid_put_schar(grid, 0, icol+adj[3], chars[2], attrs[2]); + grid_put_schar(grid, 0, icol + adj[3], chars[2], attrs[2]); } grid_puts_line_flush(false); } for (int i = 0; i < irow; i++) { if (adj[3]) { - grid_puts_line_start(grid, i+adj[0]); - grid_put_schar(grid, i+adj[0], 0, chars[7], attrs[7]); + grid_puts_line_start(grid, i + adj[0]); + grid_put_schar(grid, i + adj[0], 0, chars[7], attrs[7]); grid_puts_line_flush(false); } if (adj[1]) { int ic = (i == 0 && !adj[0] && chars[2][0]) ? 2 : 3; - grid_puts_line_start(grid, i+adj[0]); - grid_put_schar(grid, i+adj[0], icol+adj[3], chars[ic], attrs[ic]); + grid_puts_line_start(grid, i + adj[0]); + grid_put_schar(grid, i + adj[0], icol + adj[3], chars[ic], attrs[ic]); grid_puts_line_flush(false); } } if (adj[2]) { - grid_puts_line_start(grid, irow+adj[0]); + grid_puts_line_start(grid, irow + adj[0]); if (adj[3]) { - grid_put_schar(grid, irow+adj[0], 0, chars[6], attrs[6]); + grid_put_schar(grid, irow + adj[0], 0, chars[6], attrs[6]); } for (int i = 0; i < icol; i++) { int ic = (i == 0 && !adj[3] && chars[6][0]) ? 6 : 5; - grid_put_schar(grid, irow+adj[0], i+adj[3], chars[ic], attrs[ic]); + grid_put_schar(grid, irow + adj[0], i + adj[3], chars[ic], attrs[ic]); } if (adj[1]) { - grid_put_schar(grid, irow+adj[0], icol+adj[3], chars[4], attrs[4]); + grid_put_schar(grid, irow + adj[0], icol + adj[3], chars[4], attrs[4]); } grid_puts_line_flush(false); } } -// Low-level functions to manipulate individual character cells on the -// screen grid. - -/// Put a ASCII character in a screen cell. -static void schar_from_ascii(char_u *p, const char c) -{ - p[0] = c; - p[1] = 0; -} - -/// Put a unicode character in a screen cell. -static int schar_from_char(char_u *p, int c) -{ - int len = utf_char2bytes(c, p); - p[len] = NUL; - return len; -} - -/// Put a unicode char, and up to MAX_MCO composing chars, in a screen cell. -static int schar_from_cc(char_u *p, int c, int u8cc[MAX_MCO]) -{ - int len = utf_char2bytes(c, p); - for (int i = 0; i < MAX_MCO; i++) { - if (u8cc[i] == 0) { - break; - } - len += utf_char2bytes(u8cc[i], p + len); - } - p[len] = 0; - return len; -} - -/// compare the contents of two screen cells. -static int schar_cmp(char_u *sc1, char_u *sc2) -{ - return STRNCMP(sc1, sc2, sizeof(schar_T)); -} - -/// copy the contents of screen cell `sc2` into cell `sc1` -static void schar_copy(char_u *sc1, char_u *sc2) -{ - STRLCPY(sc1, sc2, sizeof(schar_T)); -} - -static int line_off2cells(schar_T *line, size_t off, size_t max_off) -{ - return (off + 1 < max_off && line[off + 1][0] == 0) ? 2 : 1; -} - -/// Return number of display cells for char at grid->chars[off]. -/// We make sure that the offset used is less than "max_off". -static int grid_off2cells(ScreenGrid *grid, size_t off, size_t max_off) -{ - return line_off2cells(grid->chars, off, max_off); -} - -/// 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 grid_lefthalve(ScreenGrid *grid, int row, int col) -{ - screen_adjust_grid(&grid, &row, &col); - - return grid_off2cells(grid, grid->line_offset[row] + col, - grid->line_offset[row] + grid->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 grid_fix_col(ScreenGrid *grid, int col, int row) -{ - int coloff = 0; - screen_adjust_grid(&grid, &row, &coloff); - - col += coloff; - if (grid->chars != NULL && col > 0 - && grid->chars[grid->line_offset[row] + col][0] == 0) { - return col - 1 - coloff; - } - return col - coloff; -} - -/// output a single character directly to the grid -void grid_putchar(ScreenGrid *grid, int c, int row, int col, int attr) -{ - char_u buf[MB_MAXBYTES + 1]; - - buf[utf_char2bytes(c, buf)] = NUL; - grid_puts(grid, buf, row, col, attr); -} - -/// get a single character directly from grid.chars into "bytes[]". -/// Also return its attribute in *attrp; -void grid_getbytes(ScreenGrid *grid, int row, int col, char_u *bytes, int *attrp) -{ - unsigned off; - - screen_adjust_grid(&grid, &row, &col); - - // safety check - if (grid->chars != NULL && row < grid->Rows && col < grid->Columns) { - off = grid->line_offset[row] + col; - *attrp = grid->attrs[off]; - schar_copy(bytes, grid->chars[off]); - } -} - - -/// put string '*text' on the window grid at position 'row' and 'col', with -/// attributes 'attr', and update chars[] and attrs[]. -/// Note: only outputs within one row, message is truncated at grid boundary! -/// Note: if grid, row and/or col is invalid, nothing is done. -void grid_puts(ScreenGrid *grid, char_u *text, int row, int col, int attr) -{ - grid_puts_len(grid, text, -1, row, col, attr); -} - -static ScreenGrid *put_dirty_grid = NULL; -static int put_dirty_row = -1; -static int put_dirty_first = INT_MAX; -static int put_dirty_last = 0; - -/// Start a group of grid_puts_len calls that builds a single grid line. -/// -/// Must be matched with a grid_puts_line_flush call before moving to -/// another line. -void grid_puts_line_start(ScreenGrid *grid, int row) -{ - int col = 0; // unused - screen_adjust_grid(&grid, &row, &col); - assert(put_dirty_row == -1); - put_dirty_row = row; - put_dirty_grid = grid; -} - -void grid_put_schar(ScreenGrid *grid, int row, int col, char_u *schar, int attr) -{ - assert(put_dirty_row == row); - unsigned int off = grid->line_offset[row] + col; - if (grid->attrs[off] != attr || schar_cmp(grid->chars[off], schar)) { - schar_copy(grid->chars[off], schar); - grid->attrs[off] = attr; - - put_dirty_first = MIN(put_dirty_first, col); - // TODO(bfredl): Y U NO DOUBLEWIDTH? - put_dirty_last = MAX(put_dirty_last, col+1); - } -} - -/// like grid_puts(), but output "text[len]". When "len" is -1 output up to -/// a NUL. -void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col, int attr) -{ - unsigned off; - char_u *ptr = text; - int len = textlen; - int c; - unsigned max_off; - int mbyte_blen = 1; - int mbyte_cells = 1; - int u8c = 0; - int u8cc[MAX_MCO]; - int clear_next_cell = FALSE; - int prev_c = 0; // previous Arabic character - int pc, nc, nc1; - int pcc[MAX_MCO]; - int need_redraw; - bool do_flush = false; - - screen_adjust_grid(&grid, &row, &col); - - // Safety check. The check for negative row and column is to fix issue - // vim/vim#4102. TODO(neovim): find out why row/col could be negative. - if (grid->chars == NULL - || row >= grid->Rows || row < 0 - || col >= grid->Columns || col < 0) { - return; - } - - if (put_dirty_row == -1) { - grid_puts_line_start(grid, row); - do_flush = true; - } else { - if (grid != put_dirty_grid || row != put_dirty_row) { - abort(); - } - } - off = grid->line_offset[row] + col; - - // When drawing over the right half of a double-wide char clear out the - // left half. Only needed in a terminal. - if (grid != &default_grid && col == 0 && grid_invalid_row(grid, row)) { - // redraw the previous cell, make it empty - put_dirty_first = -1; - put_dirty_last = MAX(put_dirty_last, 1); - } - - max_off = grid->line_offset[row] + grid->Columns; - while (col < grid->Columns - && (len < 0 || (int)(ptr - text) < len) - && *ptr != NUL) { - c = *ptr; - // check if this is the first byte of a multibyte - if (len > 0) { - mbyte_blen = utfc_ptr2len_len(ptr, (int)((text + len) - ptr)); - } else { - mbyte_blen = utfc_ptr2len(ptr); - } - if (len >= 0) { - u8c = utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr)); - } else { - u8c = utfc_ptr2char(ptr, u8cc); - } - mbyte_cells = utf_char2cells(u8c); - if (p_arshape && !p_tbidi && arabic_char(u8c)) { - // Do Arabic shaping. - if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len) { - // Past end of string to be displayed. - nc = NUL; - nc1 = NUL; - } else { - nc = utfc_ptr2char_len(ptr + mbyte_blen, pcc, - (int)((text + len) - ptr - mbyte_blen)); - nc1 = pcc[0]; - } - pc = prev_c; - prev_c = u8c; - u8c = arabic_shape(u8c, &c, &u8cc[0], nc, nc1, pc); - } else { - prev_c = u8c; - } - if (col + mbyte_cells > grid->Columns) { - // Only 1 cell left, but character requires 2 cells: - // display a '>' in the last column to avoid wrapping. */ - c = '>'; - u8c = '>'; - u8cc[0] = 0; - mbyte_cells = 1; - } - - schar_T buf; - schar_from_cc(buf, u8c, u8cc); - - - need_redraw = schar_cmp(grid->chars[off], buf) - || (mbyte_cells == 2 && grid->chars[off + 1][0] != 0) - || grid->attrs[off] != attr - || exmode_active; - - if (need_redraw) { - // When at the end of the text and overwriting a two-cell - // character with a one-cell character, need to clear the next - // cell. Also when overwriting the left half of a two-cell char - // with the right half of a two-cell char. Do this only once - // (utf8_off2cells() may return 2 on the right half). - if (clear_next_cell) { - clear_next_cell = false; - } else if ((len < 0 ? ptr[mbyte_blen] == NUL - : ptr + mbyte_blen >= text + len) - && ((mbyte_cells == 1 - && grid_off2cells(grid, off, max_off) > 1) - || (mbyte_cells == 2 - && grid_off2cells(grid, off, max_off) == 1 - && grid_off2cells(grid, off + 1, max_off) > 1))) { - clear_next_cell = true; - } - - // When at the start of the text and overwriting the right half of a - // two-cell character in the same grid, truncate that into a '>'. - if (ptr == text && col > 0 && grid->chars[off][0] == 0) { - grid->chars[off - 1][0] = '>'; - grid->chars[off - 1][1] = 0; - } - - schar_copy(grid->chars[off], buf); - grid->attrs[off] = attr; - if (mbyte_cells == 2) { - grid->chars[off + 1][0] = 0; - grid->attrs[off + 1] = attr; - } - put_dirty_first = MIN(put_dirty_first, col); - put_dirty_last = MAX(put_dirty_last, col+mbyte_cells); - } - - off += mbyte_cells; - col += mbyte_cells; - ptr += mbyte_blen; - if (clear_next_cell) { - // This only happens at the end, display one space next. - ptr = (char_u *)" "; - len = -1; - } - } - - if (do_flush) { - grid_puts_line_flush(true); - } -} - -/// End a group of grid_puts_len calls and send the screen buffer to the UI -/// layer. -/// -/// @param set_cursor Move the visible cursor to the end of the changed region. -/// This is a workaround for not yet refactored code paths -/// and shouldn't be used in new code. -void grid_puts_line_flush(bool set_cursor) -{ - assert(put_dirty_row != -1); - if (put_dirty_first < put_dirty_last) { - if (set_cursor) { - ui_grid_cursor_goto(put_dirty_grid->handle, put_dirty_row, - MIN(put_dirty_last, put_dirty_grid->Columns-1)); - } - if (!put_dirty_grid->throttled) { - ui_line(put_dirty_grid, put_dirty_row, put_dirty_first, put_dirty_last, - put_dirty_last, 0, false); - } else if (put_dirty_grid->dirty_col) { - if (put_dirty_last > put_dirty_grid->dirty_col[put_dirty_row]) { - put_dirty_grid->dirty_col[put_dirty_row] = put_dirty_last; - } - } - put_dirty_first = INT_MAX; - put_dirty_last = 0; - } - put_dirty_row = -1; - put_dirty_grid = NULL; -} - /* * Prepare for 'hlsearch' highlighting. */ @@ -6100,372 +5614,6 @@ static void end_search_hl(void) } } - -/* - * Init for calling prepare_search_hl(). - */ -static void init_search_hl(win_T *wp) - FUNC_ATTR_NONNULL_ALL -{ - // Setup for match and 'hlsearch' highlighting. Disable any previous - // match - matchitem_T *cur = wp->w_match_head; - while (cur != NULL) { - cur->hl.rm = cur->match; - if (cur->hlg_id == 0) { - cur->hl.attr = 0; - } else { - cur->hl.attr = syn_id2attr(cur->hlg_id); - } - cur->hl.buf = wp->w_buffer; - cur->hl.lnum = 0; - cur->hl.first_lnum = 0; - // Set the time limit to 'redrawtime'. - cur->hl.tm = profile_setlimit(p_rdt); - cur = cur->next; - } - search_hl.buf = wp->w_buffer; - search_hl.lnum = 0; - search_hl.first_lnum = 0; - search_hl.attr = win_hl_attr(wp, HLF_L); - - // time limit is set at the toplevel, for all windows -} - -/* - * Advance to the match in window "wp" line "lnum" or past it. - */ -static void prepare_search_hl(win_T *wp, linenr_T lnum) - FUNC_ATTR_NONNULL_ALL -{ - matchitem_T *cur; // points to the match list - match_T *shl; // points to search_hl or a match - bool shl_flag; // flag to indicate whether search_hl - // has been processed or not - - // When using a multi-line pattern, start searching at the top - // of the window or just after a closed fold. - // Do this both for search_hl and the match list. - cur = wp->w_match_head; - shl_flag = false; - while (cur != NULL || shl_flag == false) { - if (shl_flag == false) { - shl = &search_hl; - shl_flag = true; - } else { - shl = &cur->hl; // -V595 - } - if (shl->rm.regprog != NULL - && shl->lnum == 0 - && re_multiline(shl->rm.regprog)) { - if (shl->first_lnum == 0) { - for (shl->first_lnum = lnum; - shl->first_lnum > wp->w_topline; - shl->first_lnum--) { - if (hasFoldingWin(wp, shl->first_lnum - 1, NULL, NULL, true, NULL)) { - break; - } - } - } - if (cur != NULL) { - cur->pos.cur = 0; - } - bool pos_inprogress = true; // mark that a position match search is - // in progress - int n = 0; - while (shl->first_lnum < lnum && (shl->rm.regprog != NULL - || (cur != NULL && pos_inprogress))) { - next_search_hl(wp, shl, shl->first_lnum, (colnr_T)n, - shl == &search_hl ? NULL : cur); - pos_inprogress = !(cur == NULL || cur->pos.cur == 0); - if (shl->lnum != 0) { - shl->first_lnum = shl->lnum - + shl->rm.endpos[0].lnum - - shl->rm.startpos[0].lnum; - n = shl->rm.endpos[0].col; - } else { - ++shl->first_lnum; - n = 0; - } - } - } - if (shl != &search_hl && cur != NULL) { - cur = cur->next; - } - } -} - -/// Search for a next 'hlsearch' or match. -/// Uses shl->buf. -/// Sets shl->lnum and shl->rm contents. -/// Note: Assumes a previous match is always before "lnum", unless -/// shl->lnum is zero. -/// Careful: Any pointers for buffer lines will become invalid. -/// -/// @param shl points to search_hl or a match -/// @param mincol minimal column for a match -/// @param cur to retrieve match positions if any -static void next_search_hl(win_T *win, match_T *shl, linenr_T lnum, colnr_T mincol, - matchitem_T *cur) - FUNC_ATTR_NONNULL_ARG(2) -{ - linenr_T l; - colnr_T matchcol; - long nmatched = 0; - int save_called_emsg = called_emsg; - - // for :{range}s/pat only highlight inside the range - if (lnum < search_first_line || lnum > search_last_line) { - shl->lnum = 0; - return; - } - - if (shl->lnum != 0) { - // Check for three situations: - // 1. If the "lnum" is below a previous match, start a new search. - // 2. If the previous match includes "mincol", use it. - // 3. Continue after the previous match. - l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum; - if (lnum > l) { - shl->lnum = 0; - } else if (lnum < l || shl->rm.endpos[0].col > mincol) { - return; - } - } - - /* - * Repeat searching for a match until one is found that includes "mincol" - * or none is found in this line. - */ - called_emsg = FALSE; - for (;;) { - // Stop searching after passing the time limit. - if (profile_passed_limit(shl->tm)) { - shl->lnum = 0; // no match found in time - break; - } - // Three situations: - // 1. No useful previous match: search from start of line. - // 2. Not Vi compatible or empty match: continue at next character. - // Break the loop if this is beyond the end of the line. - // 3. Vi compatible searching: continue at end of previous match. - if (shl->lnum == 0) { - matchcol = 0; - } else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL - || (shl->rm.endpos[0].lnum == 0 - && shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) { - char_u *ml; - - matchcol = shl->rm.startpos[0].col; - ml = ml_get_buf(shl->buf, lnum, false) + matchcol; - if (*ml == NUL) { - ++matchcol; - shl->lnum = 0; - break; - } - matchcol += utfc_ptr2len(ml); - } else { - matchcol = shl->rm.endpos[0].col; - } - - shl->lnum = lnum; - if (shl->rm.regprog != NULL) { - // Remember whether shl->rm is using a copy of the regprog in - // cur->match. - bool regprog_is_copy = (shl != &search_hl - && cur != NULL - && shl == &cur->hl - && cur->match.regprog == cur->hl.rm.regprog); - int timed_out = false; - - nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol, - &(shl->tm), &timed_out); - // Copy the regprog, in case it got freed and recompiled. - if (regprog_is_copy) { - cur->match.regprog = cur->hl.rm.regprog; - } - if (called_emsg || got_int || timed_out) { - // Error while handling regexp: stop using this regexp. - if (shl == &search_hl) { - // don't free regprog in the match list, it's a copy - vim_regfree(shl->rm.regprog); - set_no_hlsearch(true); - } - shl->rm.regprog = NULL; - shl->lnum = 0; - got_int = FALSE; // avoid the "Type :quit to exit Vim" message - break; - } - } else if (cur != NULL) { - nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol); - } - if (nmatched == 0) { - shl->lnum = 0; // no match found - break; - } - if (shl->rm.startpos[0].lnum > 0 - || shl->rm.startpos[0].col >= mincol - || nmatched > 1 - || shl->rm.endpos[0].col > mincol) { - shl->lnum += shl->rm.startpos[0].lnum; - break; // useful match found - } - - // Restore called_emsg for assert_fails(). - called_emsg = save_called_emsg; - } -} - -/// @param shl points to a match. Fill on match. -/// @param posmatch match positions -/// @param mincol minimal column for a match -/// -/// @return one on match, otherwise return zero. -static int next_search_hl_pos(match_T *shl, linenr_T lnum, posmatch_T *posmatch, colnr_T mincol) - FUNC_ATTR_NONNULL_ALL -{ - int i; - int found = -1; - - shl->lnum = 0; - for (i = posmatch->cur; i < MAXPOSMATCH; i++) { - llpos_T *pos = &posmatch->pos[i]; - - if (pos->lnum == 0) { - break; - } - if (pos->len == 0 && pos->col < mincol) { - continue; - } - if (pos->lnum == lnum) { - if (found >= 0) { - // if this match comes before the one at "found" then swap - // them - if (pos->col < posmatch->pos[found].col) { - llpos_T tmp = *pos; - - *pos = posmatch->pos[found]; - posmatch->pos[found] = tmp; - } - } else { - found = i; - } - } - } - posmatch->cur = 0; - if (found >= 0) { - colnr_T start = posmatch->pos[found].col == 0 - ? 0: posmatch->pos[found].col - 1; - colnr_T end = posmatch->pos[found].col == 0 - ? MAXCOL : start + posmatch->pos[found].len; - - shl->lnum = lnum; - shl->rm.startpos[0].lnum = 0; - shl->rm.startpos[0].col = start; - shl->rm.endpos[0].lnum = 0; - shl->rm.endpos[0].col = end; - shl->is_addpos = true; - posmatch->cur = found + 1; - return 1; - } - return 0; -} - - -/// Fill the grid from 'start_row' to 'end_row', from 'start_col' to 'end_col' -/// with character 'c1' in first column followed by 'c2' in the other columns. -/// Use attributes 'attr'. -void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, int c1, - int c2, int attr) -{ - schar_T sc; - - int row_off = 0, col_off = 0; - screen_adjust_grid(&grid, &row_off, &col_off); - start_row += row_off; - end_row += row_off; - start_col += col_off; - end_col += col_off; - - // safety check - if (end_row > grid->Rows) { - end_row = grid->Rows; - } - if (end_col > grid->Columns) { - end_col = grid->Columns; - } - - // nothing to do - if (start_row >= end_row || start_col >= end_col) { - return; - } - - for (int row = start_row; row < end_row; row++) { - // When drawing over the right half of a double-wide char clear - // out the left half. When drawing over the left half of a - // double wide-char clear out the right half. Only needed in a - // terminal. - if (start_col > 0 && grid_fix_col(grid, start_col, row) != start_col) { - grid_puts_len(grid, (char_u *)" ", 1, row, start_col - 1, 0); - } - if (end_col < grid->Columns - && grid_fix_col(grid, end_col, row) != end_col) { - grid_puts_len(grid, (char_u *)" ", 1, row, end_col, 0); - } - - // if grid was resized (in ext_multigrid mode), the UI has no redraw updates - // for the newly resized grid. It is better mark everything as dirty and - // send all the updates. - int dirty_first = INT_MAX; - int dirty_last = 0; - - int col = start_col; - schar_from_char(sc, c1); - int lineoff = grid->line_offset[row]; - for (col = start_col; col < end_col; col++) { - int off = lineoff + col; - if (schar_cmp(grid->chars[off], sc) - || grid->attrs[off] != attr) { - schar_copy(grid->chars[off], sc); - grid->attrs[off] = attr; - if (dirty_first == INT_MAX) { - dirty_first = col; - } - dirty_last = col+1; - } - if (col == start_col) { - schar_from_char(sc, c2); - } - } - if (dirty_last > dirty_first) { - // TODO(bfredl): support a cleared suffix even with a batched line? - if (put_dirty_row == row) { - put_dirty_first = MIN(put_dirty_first, dirty_first); - put_dirty_last = MAX(put_dirty_last, dirty_last); - } else if (grid->throttled) { - // Note: assumes msg_grid is the only throttled grid - assert(grid == &msg_grid); - int dirty = 0; - if (attr != HL_ATTR(HLF_MSG) || c2 != ' ') { - dirty = dirty_last; - } else if (c1 != ' ') { - dirty = dirty_first + 1; - } - if (grid->dirty_col && dirty > grid->dirty_col[row]) { - grid->dirty_col[row] = dirty; - } - } else { - int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' '); - ui_line(grid, row, dirty_first, last, dirty_last, attr, false); - } - } - - if (end_col == grid->Columns) { - grid->line_wraps[row] = false; - } - } -} - /// Check if there should be a delay. Used before clearing or redrawing the /// screen or the command line. void check_for_delay(bool check_msg_scroll) @@ -6482,95 +5630,15 @@ void check_for_delay(bool check_msg_scroll) } } -/// (Re)allocates a window grid if size changed while in ext_multigrid mode. -/// Updates size, offsets and handle for the grid regardless. -/// -/// If "doclear" is true, don't try to copy from the old grid rather clear the -/// resized grid. -void win_grid_alloc(win_T *wp) -{ - ScreenGrid *grid = &wp->w_grid; - ScreenGrid *grid_allocated = &wp->w_grid_alloc; - - int rows = wp->w_height_inner; - int cols = wp->w_width_inner; - int total_rows = wp->w_height_outer; - int total_cols = wp->w_width_outer; - - bool want_allocation = ui_has(kUIMultigrid) || wp->w_floating; - bool has_allocation = (grid_allocated->chars != NULL); - - if (grid->Rows != rows) { - wp->w_lines_valid = 0; - xfree(wp->w_lines); - wp->w_lines = xcalloc(rows+1, sizeof(wline_T)); - } - - int was_resized = false; - if (want_allocation && (!has_allocation - || grid_allocated->Rows != total_rows - || grid_allocated->Columns != total_cols)) { - grid_alloc(grid_allocated, total_rows, total_cols, - wp->w_grid_alloc.valid, false); - grid_allocated->valid = true; - if (wp->w_floating && wp->w_float_config.border) { - wp->w_redr_border = true; - } - was_resized = true; - } else if (!want_allocation && has_allocation) { - // Single grid mode, all rendering will be redirected to default_grid. - // Only keep track of the size and offset of the window. - grid_free(grid_allocated); - grid_allocated->valid = false; - was_resized = true; - } else if (want_allocation && has_allocation && !wp->w_grid_alloc.valid) { - grid_invalidate(grid_allocated); - grid_allocated->valid = true; - } - - grid->Rows = rows; - grid->Columns = cols; - - if (want_allocation) { - grid->target = grid_allocated; - grid->row_offset = wp->w_border_adj[0]; - grid->col_offset = wp->w_border_adj[3]; - } else { - grid->target = &default_grid; - grid->row_offset = wp->w_winrow; - grid->col_offset = wp->w_wincol; - } - - // send grid resize event if: - // - a grid was just resized - // - screen_resize was called and all grid sizes must be sent - // - the UI wants multigrid event (necessary) - if ((send_grid_resize || was_resized) && want_allocation) { - ui_call_grid_resize(grid_allocated->handle, - grid_allocated->Columns, grid_allocated->Rows); - } -} - -/// assign a handle to the grid. The grid need not be allocated. -void grid_assign_handle(ScreenGrid *grid) -{ - static int last_grid_handle = DEFAULT_GRID_HANDLE; - - // only assign a grid handle if not already - if (grid->handle == 0) { - grid->handle = ++last_grid_handle; - } -} - /// Resize the screen to Rows and Columns. /// /// Allocate default_grid.chars[] and other grid arrays. /// /// There may be some time between setting Rows and Columns and (re)allocating /// default_grid arrays. This happens when starting up and when -/// (manually) changing the shell size. Always use default_grid.Rows and +/// (manually) changing the screen size. Always use default_grid.rows and /// default_grid.Columns to access items in default_grid.chars[]. Use Rows -/// and Columns for positioning text etc. where the final size of the shell is +/// and Columns for positioning text etc. where the final size of the screen is /// needed. void screenalloc(void) { @@ -6589,8 +5657,8 @@ retry: // when Rows and Columns have been set and we have started doing full // screen stuff. if ((default_grid.chars != NULL - && Rows == default_grid.Rows - && Columns == default_grid.Columns + && Rows == default_grid.rows + && Columns == default_grid.cols ) || Rows == 0 || Columns == 0 @@ -6605,14 +5673,14 @@ retry: */ ++RedrawingDisabled; - // win_new_shellsize will recompute floats position, but tell the + // win_new_screensize will recompute floats position, but tell the // compositor to not redraw them yet ui_comp_set_screen_valid(false); if (msg_grid.chars) { msg_grid_invalid = true; } - win_new_shellsize(); // fit the windows in the new sized shell + win_new_screensize(); // fit the windows in the new sized screen comp_col(); // recompute columns for shown command and ruler @@ -6630,7 +5698,7 @@ retry: StlClickDefinition *new_tab_page_click_defs = xcalloc((size_t)Columns, sizeof(*new_tab_page_click_defs)); - clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size); + stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size); xfree(tab_page_click_defs); tab_page_click_defs = new_tab_page_click_defs; @@ -6661,90 +5729,19 @@ retry: resizing = false; } -void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid) -{ - int new_row; - ScreenGrid new = *grid; - assert(rows >= 0 && columns >= 0); - size_t ncells = (size_t)rows * columns; - new.chars = xmalloc(ncells * sizeof(schar_T)); - new.attrs = xmalloc(ncells * sizeof(sattr_T)); - new.line_offset = xmalloc((size_t)(rows * sizeof(unsigned))); - new.line_wraps = xmalloc((size_t)(rows * sizeof(char_u))); - - new.Rows = rows; - new.Columns = columns; - - for (new_row = 0; new_row < new.Rows; new_row++) { - new.line_offset[new_row] = new_row * new.Columns; - new.line_wraps[new_row] = false; - - grid_clear_line(&new, new.line_offset[new_row], columns, valid); - - if (copy) { - // If the screen is not going to be cleared, copy as much as - // possible from the old screen to the new one and clear the rest - // (used when resizing the window at the "--more--" prompt or when - // executing an external command, for the GUI). - if (new_row < grid->Rows && grid->chars != NULL) { - int len = MIN(grid->Columns, new.Columns); - memmove(new.chars + new.line_offset[new_row], - grid->chars + grid->line_offset[new_row], - (size_t)len * sizeof(schar_T)); - memmove(new.attrs + new.line_offset[new_row], - grid->attrs + grid->line_offset[new_row], - (size_t)len * sizeof(sattr_T)); - } - } - } - grid_free(grid); - *grid = new; - - // Share a single scratch buffer for all grids, by - // ensuring it is as wide as the widest grid. - if (linebuf_size < (size_t)columns) { - xfree(linebuf_char); - xfree(linebuf_attr); - linebuf_char = xmalloc(columns * sizeof(schar_T)); - linebuf_attr = xmalloc(columns * sizeof(sattr_T)); - linebuf_size = columns; - } -} - -void grid_free(ScreenGrid *grid) -{ - xfree(grid->chars); - xfree(grid->attrs); - xfree(grid->line_offset); - xfree(grid->line_wraps); - - grid->chars = NULL; - grid->attrs = NULL; - grid->line_offset = NULL; - grid->line_wraps = NULL; -} - -/// Doesn't allow reinit, so must only be called by free_all_mem! -void screen_free_all_mem(void) -{ - grid_free(&default_grid); - xfree(linebuf_char); - xfree(linebuf_attr); -} - -/// Clear tab_page_click_defs table +/// Clear status line, window bar or tab page line click definition table /// /// @param[out] tpcd Table to clear. /// @param[in] tpcd_size Size of the table. -void clear_tab_page_click_defs(StlClickDefinition *const tpcd, const long tpcd_size) +void stl_clear_click_defs(StlClickDefinition *const click_defs, const long click_defs_size) { - if (tpcd != NULL) { - for (long i = 0; i < tpcd_size; i++) { - if (i == 0 || tpcd[i].func != tpcd[i - 1].func) { - xfree(tpcd[i].func); + if (click_defs != NULL) { + for (long i = 0; i < click_defs_size; i++) { + if (i == 0 || click_defs[i].func != click_defs[i - 1].func) { + xfree(click_defs[i].func); } } - memset(tpcd, 0, (size_t)tpcd_size * sizeof(tpcd[0])); + memset(click_defs, 0, (size_t)click_defs_size * sizeof(click_defs[0])); } } @@ -6760,9 +5757,9 @@ void screenclear(void) } // blank out the default grid - for (i = 0; i < default_grid.Rows; i++) { + for (i = 0; i < default_grid.rows; i++) { grid_clear_line(&default_grid, default_grid.line_offset[i], - default_grid.Columns, true); + default_grid.cols, true); default_grid.line_wraps[i] = false; } @@ -6799,28 +5796,6 @@ void screenclear(void) } } -/// clear a line in the grid starting at "off" until "width" characters -/// are cleared. -void grid_clear_line(ScreenGrid *grid, unsigned off, int width, bool valid) -{ - for (int col = 0; col < width; col++) { - schar_from_ascii(grid->chars[off + col], ' '); - } - int fill = valid ? 0 : -1; - (void)memset(grid->attrs + off, fill, (size_t)width * sizeof(sattr_T)); -} - -void grid_invalidate(ScreenGrid *grid) -{ - (void)memset(grid->attrs, -1, sizeof(sattr_T) * grid->Rows * grid->Columns); -} - -bool grid_invalid_row(ScreenGrid *grid, int row) -{ - return grid->attrs[grid->line_offset[row]] < 0; -} - - /// Copy part of a grid line for vertically split window. static void linecopy(ScreenGrid *grid, int to, int from, int col, int width) { @@ -6833,12 +5808,17 @@ static void linecopy(ScreenGrid *grid, int to, int from, int col, int width) width * sizeof(sattr_T)); } -/* - * Set cursor to its position in the current window. - */ +/// Set cursor to its position in the current window. void setcursor(void) { - if (redrawing()) { + setcursor_mayforce(false); +} + +/// Set cursor to its position in the current window. +/// @param force when true, also when not redrawing. +void setcursor_mayforce(bool force) +{ + if (force || redrawing()) { validate_cursor(); ScreenGrid *grid = &curwin->w_grid; @@ -6848,11 +5828,11 @@ void setcursor(void) // With 'rightleft' set and the cursor on a double-wide character, // position it on the leftmost column. col = curwin->w_width_inner - curwin->w_wcol - - ((utf_ptr2cells(get_cursor_pos_ptr()) == 2 + - ((utf_ptr2cells((char *)get_cursor_pos_ptr()) == 2 && vim_isprintc(gchar_cursor())) ? 2 : 1); } - screen_adjust_grid(&grid, &row, &col); + grid_adjust(&grid, &row, &col); ui_grid_cursor_goto(grid->handle, row, col); } } @@ -6868,16 +5848,16 @@ void win_scroll_lines(win_T *wp, int row, int line_count) } // No lines are being moved, just draw over the entire area - if (row + abs(line_count) >= wp->w_grid.Rows) { + if (row + abs(line_count) >= wp->w_grid.rows) { return; } if (line_count < 0) { grid_del_lines(&wp->w_grid, row, -line_count, - wp->w_grid.Rows, 0, wp->w_grid.Columns); + wp->w_grid.rows, 0, wp->w_grid.cols); } else { grid_ins_lines(&wp->w_grid, row, line_count, - wp->w_grid.Rows, 0, wp->w_grid.Columns); + wp->w_grid.rows, 0, wp->w_grid.cols); } } @@ -6891,7 +5871,6 @@ void win_scroll_lines(win_T *wp, int row, int line_count) * screen changes, and in the meantime, everything still works. */ - /// insert lines on the screen and move the existing lines down /// 'line_count' is the number of lines to be inserted. /// 'end' is the line after the scrolled part. Normally it is Rows. @@ -6905,7 +5884,7 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, unsigned temp; int row_off = 0; - screen_adjust_grid(&grid, &row_off, &col); + grid_adjust(&grid, &row_off, &col); row += row_off; end += row_off; @@ -6916,7 +5895,7 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, // Shift line_offset[] line_count down to reflect the inserted lines. // Clear the inserted lines. for (i = 0; i < line_count; i++) { - if (width != grid->Columns) { + if (width != grid->cols) { // need to copy part of a line j = end - 1 - i; while ((j -= line_count) >= row) { @@ -6934,15 +5913,13 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, } grid->line_offset[j + line_count] = temp; grid->line_wraps[j + line_count] = false; - grid_clear_line(grid, temp, grid->Columns, false); + grid_clear_line(grid, temp, grid->cols, false); } } if (!grid->throttled) { - ui_call_grid_scroll(grid->handle, row, end, col, col+width, -line_count, 0); + ui_call_grid_scroll(grid->handle, row, end, col, col + width, -line_count, 0); } - - return; } /// delete lines on the screen and move lines up. @@ -6956,7 +5933,7 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, unsigned temp; int row_off = 0; - screen_adjust_grid(&grid, &row_off, &col); + grid_adjust(&grid, &row_off, &col); row += row_off; end += row_off; @@ -6967,7 +5944,7 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, // Now shift line_offset[] line_count up to reflect the deleted lines. // Clear the inserted lines. for (i = 0; i < line_count; i++) { - if (width != grid->Columns) { + if (width != grid->cols) { // need to copy part of a line j = row + i; while ((j += line_count) <= end - 1) { @@ -6986,18 +5963,15 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, } grid->line_offset[j - line_count] = temp; grid->line_wraps[j - line_count] = false; - grid_clear_line(grid, temp, grid->Columns, false); + grid_clear_line(grid, temp, grid->cols, false); } } if (!grid->throttled) { - ui_call_grid_scroll(grid->handle, row, end, col, col+width, line_count, 0); + ui_call_grid_scroll(grid->handle, row, end, col, col + width, line_count, 0); } - - return; } - // Show the current mode and ruler. // // If clear_cmdline is true, clear the rest of the cmdline. @@ -7022,8 +5996,8 @@ int showmode(void) msg_grid_validate(); do_mode = ((p_smd && msg_silent == 0) - && ((State & TERM_FOCUS) - || (State & INSERT) + && ((State & MODE_TERMINAL) + || (State & MODE_INSERT) || restart_edit != NUL || VIsual_active)); if (do_mode || reg_recording != 0) { @@ -7069,13 +6043,13 @@ int showmode(void) length = (Rows - msg_row) * Columns - 3; } if (edit_submode_extra != NULL) { - length -= vim_strsize(edit_submode_extra); + length -= vim_strsize((char *)edit_submode_extra); } if (length > 0) { if (edit_submode_pre != NULL) { - length -= vim_strsize(edit_submode_pre); + length -= vim_strsize((char *)edit_submode_pre); } - if (length - vim_strsize(edit_submode) > 0) { + if (length - vim_strsize((char *)edit_submode) > 0) { if (edit_submode_pre != NULL) { msg_puts_attr((const char *)edit_submode_pre, attr); } @@ -7092,13 +6066,13 @@ int showmode(void) } } } else { - if (State & TERM_FOCUS) { + if (State & MODE_TERMINAL) { msg_puts_attr(_(" TERMINAL"), attr); } else if (State & VREPLACE_FLAG) { msg_puts_attr(_(" VREPLACE"), attr); } else if (State & REPLACE_FLAG) { msg_puts_attr(_(" REPLACE"), attr); - } else if (State & INSERT) { + } else if (State & MODE_INSERT) { if (p_ri) { msg_puts_attr(_(" REVERSE"), attr); } @@ -7114,15 +6088,15 @@ int showmode(void) if (p_hkmap) { msg_puts_attr(_(" Hebrew"), attr); } - if (State & LANGMAP) { + if (State & MODE_LANGMAP) { if (curwin->w_p_arab) { msg_puts_attr(_(" Arabic"), attr); - } else if (get_keymap_str(curwin, (char_u *)" (%s)", - NameBuff, MAXPATHL)) { + } else if (get_keymap_str(curwin, " (%s)", + (char *)NameBuff, MAXPATHL)) { msg_puts_attr((char *)NameBuff, attr); } } - if ((State & INSERT) && p_paste) { + if ((State & MODE_INSERT) && p_paste) { msg_puts_attr(_(" (paste)"), attr); } @@ -7184,10 +6158,10 @@ int showmode(void) clear_showcmd(); } - // If the last window has no status line, the ruler is after the mode - // message and must be redrawn + // If the last window has no status line and global statusline is disabled, + // the ruler is after the mode message and must be redrawn win_T *last = lastwin_nofloating(); - if (redrawing() && last->w_status_height == 0) { + if (redrawing() && last->w_status_height == 0 && global_stl_height() == 0) { win_redr_ruler(last, true); } redraw_cmdline = false; @@ -7221,6 +6195,10 @@ void unshowmode(bool force) // Clear the mode message. void clearmode(void) { + if (p_ch <= 0 && !ui_has(kUIMessages)) { + return; + } + const int save_msg_row = msg_row; const int save_msg_col = msg_col; @@ -7238,6 +6216,10 @@ void clearmode(void) static void recording_mode(int attr) { + if (p_ch <= 0 && !ui_has(kUIMessages)) { + return; + } + msg_puts_attr(_("recording"), attr); if (!shortmess(SHM_RECORDING)) { char s[4]; @@ -7283,10 +6265,9 @@ void draw_tabline(void) return; } - // Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect. assert(Columns == tab_page_click_defs_size); - clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size); + stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size); // Use the 'tabline' option if it's set. if (*p_tal != NUL) { @@ -7295,10 +6276,9 @@ void draw_tabline(void) // Check for an error. If there is one we would loop in redrawing the // screen. Avoid that by making 'tabline' empty. did_emsg = false; - win_redr_custom(NULL, false); + win_redr_custom(NULL, false, false); if (did_emsg) { - set_string_option_direct("tabline", -1, - (char_u *)"", OPT_FREE, SID_ERROR); + set_string_option_direct("tabline", -1, "", OPT_FREE, SID_ERROR); } did_emsg |= saved_did_emsg; } else { @@ -7332,7 +6312,6 @@ void draw_tabline(void) wp = tp->tp_firstwin; } - if (tp->tp_topframe == topframe) { attr = win_hl_attr(cwp, HLF_TPS); } @@ -7354,7 +6333,6 @@ void draw_tabline(void) } } - if (modified || wincount > 1) { if (wincount > 1) { vim_snprintf((char *)NameBuff, MAXPATHL, "%d", wincount); @@ -7376,11 +6354,11 @@ void draw_tabline(void) if (room > 0) { // Get buffer name in NameBuff[] get_trans_bufname(cwp->w_buffer); - (void)shorten_dir(NameBuff); - len = vim_strsize(NameBuff); + shorten_dir(NameBuff); + len = vim_strsize((char *)NameBuff); p = NameBuff; while (len > room) { - len -= ptr2cells(p); + len -= ptr2cells((char *)p); MB_PTR_ADV(p); } if (len > Columns - col - 1) { @@ -7429,35 +6407,49 @@ void draw_tabline(void) void ui_ext_tabline_update(void) { - Array tabs = ARRAY_DICT_INIT; + Arena arena = ARENA_EMPTY; + arena_start(&arena, &ui_ext_fixblk); + + size_t n_tabs = 0; FOR_ALL_TABS(tp) { - Dictionary tab_info = ARRAY_DICT_INIT; - PUT(tab_info, "tab", TABPAGE_OBJ(tp->handle)); + n_tabs++; + } + + Array tabs = arena_array(&arena, n_tabs); + FOR_ALL_TABS(tp) { + Dictionary tab_info = arena_dict(&arena, 2); + PUT_C(tab_info, "tab", TABPAGE_OBJ(tp->handle)); win_T *cwp = (tp == curtab) ? curwin : tp->tp_curwin; get_trans_bufname(cwp->w_buffer); - PUT(tab_info, "name", STRING_OBJ(cstr_to_string((char *)NameBuff))); + PUT_C(tab_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff)))); - ADD(tabs, DICTIONARY_OBJ(tab_info)); + ADD_C(tabs, DICTIONARY_OBJ(tab_info)); + } + + size_t n_buffers = 0; + FOR_ALL_BUFFERS(buf) { + n_buffers += buf->b_p_bl ? 1 : 0; } - Array buffers = ARRAY_DICT_INIT; + Array buffers = arena_array(&arena, n_buffers); FOR_ALL_BUFFERS(buf) { // Do not include unlisted buffers if (!buf->b_p_bl) { continue; } - Dictionary buffer_info = ARRAY_DICT_INIT; - PUT(buffer_info, "buffer", BUFFER_OBJ(buf->handle)); + Dictionary buffer_info = arena_dict(&arena, 2); + PUT_C(buffer_info, "buffer", BUFFER_OBJ(buf->handle)); get_trans_bufname(buf); - PUT(buffer_info, "name", STRING_OBJ(cstr_to_string((char *)NameBuff))); + PUT_C(buffer_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff)))); - ADD(buffers, DICTIONARY_OBJ(buffer_info)); + ADD_C(buffers, DICTIONARY_OBJ(buffer_info)); } ui_call_tabline_update(curtab->handle, tabs, curbuf->handle, buffers); + arena_mem_free(arena_finish(&arena), &ui_ext_fixblk); } /* @@ -7469,9 +6461,9 @@ void get_trans_bufname(buf_T *buf) if (buf_spname(buf) != NULL) { STRLCPY(NameBuff, buf_spname(buf), MAXPATHL); } else { - home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE); + home_replace(buf, buf->b_fname, (char *)NameBuff, MAXPATHL, true); } - trans_characters(NameBuff, MAXPATHL); + trans_characters((char *)NameBuff, MAXPATHL); } /* @@ -7502,16 +6494,22 @@ int fillchar_status(int *attr, win_T *wp) return '='; } -/* - * Get the character to use in a separator between vertically split windows. - * Get its attributes in "*attr". - */ +/// Get the character to use in a separator between vertically split windows. +/// Get its attributes in "*attr". static int fillchar_vsep(win_T *wp, int *attr) { *attr = win_hl_attr(wp, HLF_C); return wp->w_p_fcs_chars.vert; } +/// Get the character to use in a separator between horizontally split windows. +/// Get its attributes in "*attr". +static int fillchar_hsep(win_T *wp, int *attr) +{ + *attr = win_hl_attr(wp, HLF_C); + return wp->w_p_fcs_chars.horiz; +} + /* * Return TRUE if redrawing should currently be done. */ @@ -7526,7 +6524,8 @@ int redrawing(void) */ int messaging(void) { - return !(p_lz && char_avail() && !KeyTyped); + return !(p_lz && char_avail() && !KeyTyped) + && (p_ch > 0 || ui_has(kUIMessages)); } /// Show current status info in ruler and various other places @@ -7537,11 +6536,15 @@ void showruler(bool always) if (!always && !redrawing()) { return; } - if ((*p_stl != NUL || *curwin->w_p_stl != NUL) && curwin->w_status_height) { + if ((*p_stl != NUL || *curwin->w_p_stl != NUL) + && (curwin->w_status_height || global_stl_height())) { redraw_custom_statusline(curwin); } else { win_redr_ruler(curwin, always); } + if (*p_wbr != NUL || *curwin->w_p_wbr != NUL) { + win_redr_winbar(curwin); + } if (need_maketitle || (p_icon && (stl_syntax & STL_IN_ICON)) @@ -7556,6 +6559,7 @@ void showruler(bool always) static void win_redr_ruler(win_T *wp, bool always) { + bool is_stl_global = global_stl_height() > 0; static bool did_show_ext_ruler = false; // If 'ruler' off or redrawing disabled, don't do anything @@ -7573,32 +6577,25 @@ static void win_redr_ruler(win_T *wp, bool always) // Don't draw the ruler while doing insert-completion, it might overwrite // the (long) mode message. - if (wp == lastwin && lastwin->w_status_height == 0) { + if (wp == lastwin && lastwin->w_status_height == 0 && !is_stl_global) { if (edit_submode != NULL) { return; } } - if (*p_ruf) { - int save_called_emsg = called_emsg; - - called_emsg = false; - win_redr_custom(wp, true); - if (called_emsg) { - set_string_option_direct("rulerformat", -1, (char_u *)"", - OPT_FREE, SID_ERROR); + if (*p_ruf && p_ch > 0 && !ui_has(kUIMessages)) { + const int called_emsg_before = called_emsg; + win_redr_custom(wp, false, true); + if (called_emsg > called_emsg_before) { + set_string_option_direct("rulerformat", -1, "", OPT_FREE, SID_ERROR); } - called_emsg |= save_called_emsg; return; } - /* - * Check if not in Insert mode and the line is empty (will show "0-1"). - */ - int empty_line = FALSE; - if (!(State & INSERT) - && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE) == NUL) { - empty_line = TRUE; + // Check if not in Insert mode and the line is empty (will show "0-1"). + int empty_line = false; + if ((State & MODE_INSERT) == 0 && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, false) == NUL) { + empty_line = true; } /* @@ -7628,6 +6625,12 @@ static void win_redr_ruler(win_T *wp, bool always) off = wp->w_wincol; width = wp->w_width; part_of_status = true; + } else if (is_stl_global) { + row = Rows - p_ch - 1; + fillchar = fillchar_status(&attr, wp); + off = 0; + width = Columns; + part_of_status = true; } else { row = Rows - 1; fillchar = ' '; @@ -7636,6 +6639,10 @@ static void win_redr_ruler(win_T *wp, bool always) off = 0; } + if (!part_of_status && p_ch < 1 && !ui_has(kUIMessages)) { + return; + } + // In list mode virtcol needs to be recomputed colnr_T virtcol = wp->w_virtcol; if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) { @@ -7645,13 +6652,13 @@ static void win_redr_ruler(win_T *wp, bool always) } #define RULER_BUF_LEN 70 - char_u buffer[RULER_BUF_LEN]; + char buffer[RULER_BUF_LEN]; /* * Some sprintfs return the length, some return a pointer. * To avoid portability problems we use strlen() here. */ - vim_snprintf((char *)buffer, RULER_BUF_LEN, "%" PRId64 ",", + vim_snprintf(buffer, RULER_BUF_LEN, "%" PRId64 ",", (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? (int64_t)0L : (int64_t)wp->w_cursor.lnum); size_t len = STRLEN(buffer); @@ -7667,7 +6674,7 @@ static void win_redr_ruler(win_T *wp, bool always) int i = (int)STRLEN(buffer); get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1); int o = i + vim_strsize(buffer + i + 1); - if (wp->w_status_height == 0) { // can't use last char of screen + if (wp->w_status_height == 0 && !is_stl_global) { // can't use last char of screen o++; } int this_ru_col = ru_col - (Columns - width); @@ -7689,11 +6696,11 @@ static void win_redr_ruler(win_T *wp, bool always) } if (ui_has(kUIMessages) && !part_of_status) { - Array content = ARRAY_DICT_INIT; - Array chunk = ARRAY_DICT_INIT; - ADD(chunk, INTEGER_OBJ(attr)); - ADD(chunk, STRING_OBJ(cstr_to_string((char *)buffer))); - ADD(content, ARRAY_OBJ(chunk)); + MAXSIZE_TEMP_ARRAY(content, 1); + MAXSIZE_TEMP_ARRAY(chunk, 2); + ADD_C(chunk, INTEGER_OBJ(attr)); + ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)buffer))); + ADD_C(content, ARRAY_OBJ(chunk)); ui_call_msg_ruler(content); did_show_ext_ruler = true; } else { @@ -7712,7 +6719,7 @@ static void win_redr_ruler(win_T *wp, bool always) } ScreenGrid *grid = part_of_status ? &default_grid : &msg_grid_adj; - grid_puts(grid, buffer, row, this_ru_col + off, attr); + grid_puts(grid, (char_u *)buffer, row, this_ru_col + off, attr); grid_fill(grid, row, row + 1, this_ru_col + off + (int)STRLEN(buffer), off + width, fillchar, fillchar, attr); @@ -7815,14 +6822,12 @@ static void margin_columns_win(win_T *wp, int *left_col, int *right_col) prev_col_off = cur_col_off; } -/// Set dimensions of the Nvim application "shell". +/// Set dimensions of the Nvim application "screen". void screen_resize(int width, int height) { - static bool recursive = false; - // Avoid recursiveness, can happen when setting the window size causes // another window-changed signal. - if (updating_screen || recursive) { + if (updating_screen || resizing_screen) { return; } @@ -7830,9 +6835,9 @@ void screen_resize(int width, int height) return; } - if (State == HITRETURN || State == SETWSIZE) { + if (State == MODE_HITRETURN || State == MODE_SETWSIZE) { // postpone the resizing - State = SETWSIZE; + State = MODE_SETWSIZE; return; } @@ -7844,13 +6849,13 @@ void screen_resize(int width, int height) return; } - recursive = true; + resizing_screen = true; Rows = height; Columns = width; - check_shellsize(); + check_screensize(); int max_p_ch = Rows - min_rows() + 1; - if (!ui_has(kUIMessages) && p_ch > max_p_ch) { + if (!ui_has(kUIMessages) && p_ch > 0 && p_ch > max_p_ch) { p_ch = max_p_ch ? max_p_ch : 1; } height = Rows; @@ -7861,11 +6866,11 @@ void screen_resize(int width, int height) send_grid_resize = true; - /* The window layout used to be adjusted here, but it now happens in - * screenalloc() (also invoked from screenclear()). That is because the - * "recursive" check above may skip this, but not screenalloc(). */ + /// The window layout used to be adjusted here, but it now happens in + /// screenalloc() (also invoked from screenclear()). That is because the + /// recursize "resizing_screen" check above may skip this, but not screenalloc(). - if (State != ASKMORE && State != EXTERNCMD && State != CONFIRM) { + if (State != MODE_ASKMORE && State != MODE_EXTERNCMD && State != MODE_CONFIRM) { screenclear(); } @@ -7884,7 +6889,7 @@ void screen_resize(int width, int height) * Always need to call update_screen() or screenalloc(), to make * sure Rows/Columns and the size of the screen is correct! */ - if (State == ASKMORE || State == EXTERNCMD || State == CONFIRM + if (State == MODE_ASKMORE || State == MODE_EXTERNCMD || State == MODE_CONFIRM || exmode_active) { screenalloc(); if (msg_grid.chars) { @@ -7898,7 +6903,7 @@ void screen_resize(int width, int height) if (curwin->w_p_scb) { do_check_scrollbind(true); } - if (State & CMDLINE) { + if (State & MODE_CMDLINE) { redraw_popupmenu = false; update_screen(NOT_VALID); redrawcmdline(); @@ -7922,52 +6927,26 @@ void screen_resize(int width, int height) } ui_flush(); } - recursive = false; + resizing_screen = false; } -/// Check if the new Nvim application "shell" dimensions are valid. +/// Check if the new Nvim application "screen" dimensions are valid. /// Correct it if it's too small or way too big. -void check_shellsize(void) +void check_screensize(void) { + // Limit Rows and Columns to avoid an overflow in Rows * Columns. if (Rows < min_rows()) { // need room for one window and command line Rows = min_rows(); + } else if (Rows > 1000) { + Rows = 1000; } - limit_screen_size(); -} -// Limit Rows and Columns to avoid an overflow in Rows * Columns. -void limit_screen_size(void) -{ if (Columns < MIN_COLUMNS) { Columns = MIN_COLUMNS; } else if (Columns > 10000) { Columns = 10000; } - - if (Rows > 1000) { - Rows = 1000; - } -} - -void win_new_shellsize(void) -{ - static long old_Rows = 0; - static long old_Columns = 0; - - if (old_Rows != Rows) { - // If 'window' uses the whole screen, keep it using that. - // Don't change it when set with "-w size" on the command line. - if (p_window == old_Rows - 1 || (old_Rows == 0 && p_window == 0)) { - p_window = Rows - 1; - } - old_Rows = Rows; - shell_new_rows(); // update window sizes - } - if (old_Columns != Columns) { - old_Columns = Columns; - shell_new_columns(); // update window sizes - } } win_T *get_win_by_grid_handle(handle_T handle) @@ -7979,4 +6958,3 @@ win_T *get_win_by_grid_handle(handle_T handle) } return NULL; } - |