aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/drawscreen.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-11-29 21:52:58 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-11-29 21:52:58 +0000
commit931bffbda3668ddc609fc1da8f9eb576b170aa52 (patch)
treed8c1843a95da5ea0bb4acc09f7e37843d9995c86 /src/nvim/drawscreen.c
parent142d9041391780ac15b89886a54015fdc5c73995 (diff)
parent4a8bf24ac690004aedf5540fa440e788459e5e34 (diff)
downloadrneovim-userreg.tar.gz
rneovim-userreg.tar.bz2
rneovim-userreg.zip
Merge remote-tracking branch 'upstream/master' into userreguserreg
Diffstat (limited to 'src/nvim/drawscreen.c')
-rw-r--r--src/nvim/drawscreen.c1031
1 files changed, 863 insertions, 168 deletions
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index 04c342e068..6cc623cb72 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -1,6 +1,3 @@
-// This is an open source non-commercial project. Dear PVS-Studio, please check
-// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-
// drawscreen.c: Code for updating all the windows on the screen.
// This is the top level, drawline.c is the middle and grid.c/screen.c the lower level.
@@ -56,24 +53,32 @@
#include <assert.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "klib/kvec.h"
#include "nvim/api/private/defs.h"
-#include "nvim/ascii.h"
+#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
+#include "nvim/cursor.h"
#include "nvim/decoration.h"
#include "nvim/decoration_provider.h"
#include "nvim/diff.h"
+#include "nvim/digraph.h"
#include "nvim/drawline.h"
#include "nvim/drawscreen.h"
+#include "nvim/eval.h"
#include "nvim/ex_getln.h"
-#include "nvim/extmark_defs.h"
#include "nvim/fold.h"
+#include "nvim/func_attr.h"
+#include "nvim/getchar.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
@@ -86,20 +91,25 @@
#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/option.h"
+#include "nvim/option_vars.h"
#include "nvim/plines.h"
#include "nvim/popupmenu.h"
-#include "nvim/pos.h"
+#include "nvim/pos_defs.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
+#include "nvim/search.h"
+#include "nvim/sign_defs.h"
+#include "nvim/spell.h"
+#include "nvim/state.h"
#include "nvim/statusline.h"
+#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/terminal.h"
-#include "nvim/types.h"
+#include "nvim/types_defs.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
#include "nvim/version.h"
-#include "nvim/vim.h"
+#include "nvim/vim_defs.h"
#include "nvim/window.h"
/// corner value flags for hsep_connected and vsep_connected
@@ -118,8 +128,6 @@ static bool redraw_popupmenu = false;
static bool msg_grid_invalid = false;
static bool resizing_autocmd = false;
-static char *provider_err = NULL;
-
/// Check if the cursor line needs to be redrawn because of 'concealcursor'.
///
/// When cursor is moved at the same time, both lines will be redrawn regardless.
@@ -202,7 +210,7 @@ bool default_grid_alloc(void)
void screenclear(void)
{
- check_for_delay(false);
+ msg_check_for_delay(false);
if (starting == NO_SCREEN || default_grid.chars == NULL) {
return;
@@ -212,7 +220,6 @@ void screenclear(void)
for (int i = 0; i < default_grid.rows; i++) {
grid_clear_line(&default_grid, default_grid.line_offset[i],
default_grid.cols, true);
- default_grid.line_wraps[i] = false;
}
ui_call_grid_clear(1); // clear the display
@@ -318,11 +325,15 @@ void screen_resize(int width, int height)
resizing_autocmd = false;
redraw_all_later(UPD_CLEAR);
+ if (State != MODE_ASKMORE && State != MODE_EXTERNCMD && State != MODE_CONFIRM) {
+ screenclear();
+ }
+
if (starting != NO_SCREEN) {
maketitle();
changed_line_abv_curs();
- invalidate_botline();
+ invalidate_botline(curwin);
// We only redraw when it's needed:
// - While at the more prompt or executing an external command, don't
@@ -370,6 +381,32 @@ void screen_resize(int width, int height)
resizing_screen = false;
}
+/// Check if the new Nvim application "screen" dimensions are valid.
+/// Correct it if it's too small or way too big.
+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;
+ }
+
+ if (Columns < MIN_COLUMNS) {
+ Columns = MIN_COLUMNS;
+ } else if (Columns > 10000) {
+ Columns = 10000;
+ }
+}
+
+/// Return true if redrawing should currently be done.
+bool redrawing(void)
+{
+ return !RedrawingDisabled
+ && !(p_lz && char_avail() && !KeyTyped && !do_redraw);
+}
+
/// Redraw the parts of the screen that is marked for redraw.
///
/// Most code shouldn't call this directly, rather use redraw_later() and
@@ -411,6 +448,15 @@ int update_screen(void)
display_tick++; // let syntax code know we're in a next round of
// display updating
+ // glyph cache full, very rare
+ if (schar_cache_clear_if_full()) {
+ // must use CLEAR, as the contents of screen buffers cannot be
+ // compared to their previous state here.
+ // TODO(bfredl): if start to cache schar_T values in places (like fcs/lcs)
+ // we need to revalidate these here as well!
+ type = MAX(type, UPD_CLEAR);
+ }
+
// Tricky: vim code can reset msg_scrolled behind our back, so need
// separate bookkeeping for now.
if (msg_did_scroll) {
@@ -430,7 +476,7 @@ int update_screen(void)
// non-displayed part of msg_grid is considered invalid.
for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.rows); i++) {
grid_clear_line(&msg_grid, msg_grid.line_offset[i],
- msg_grid.cols, false);
+ msg_grid.cols, i < p_ch);
}
}
msg_grid.throttled = false;
@@ -507,7 +553,7 @@ int update_screen(void)
ui_comp_set_screen_valid(true);
DecorProviders providers;
- decor_providers_start(&providers, &provider_err);
+ decor_providers_start(&providers);
// "start" callback could have changed highlights for global elements
if (win_check_ns_hl(NULL)) {
@@ -516,7 +562,7 @@ int update_screen(void)
}
if (clear_cmdline) { // going to clear cmdline (done below)
- check_for_delay(false);
+ msg_check_for_delay(false);
}
// Force redraw when width of 'number' or 'relativenumber' column
@@ -540,9 +586,9 @@ int update_screen(void)
draw_tabline();
}
- // Correct stored syntax highlighting info for changes in each displayed
- // buffer. Each buffer must only be done once.
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ // Correct stored syntax highlighting info for changes in each displayed
+ // buffer. Each buffer must only be done once.
update_window_hl(wp, type >= UPD_NOT_VALID || hl_changed);
buf_T *buf = wp->w_buffer;
@@ -554,10 +600,19 @@ int update_screen(void)
}
if (buf->b_mod_tick_decor < display_tick) {
- decor_providers_invoke_buf(buf, &providers, &provider_err);
+ decor_providers_invoke_buf(buf, &providers);
buf->b_mod_tick_decor = display_tick;
}
}
+
+ // Reset 'statuscolumn' if there is no dedicated signcolumn but it is invalid.
+ if (*wp->w_p_stc != NUL && wp->w_minscwidth <= SCL_NO
+ && (wp->w_buffer->b_signcols.invalid_bot || !wp->w_buffer->b_signcols.sentinel)) {
+ wp->w_nrwidth_line_count = 0;
+ wp->w_valid &= ~VALID_WCOL;
+ wp->w_redr_type = UPD_NOT_VALID;
+ wp->w_buffer->b_signcols.invalid_bot = 0;
+ }
}
// Go from top to bottom through the windows, redrawing the ones that need it.
@@ -624,7 +679,7 @@ int update_screen(void)
}
did_intro = true;
- decor_providers_invoke_end(&providers, &provider_err);
+ decor_providers_invoke_end(&providers);
kvi_destroy(providers);
// either cmdline is cleared, not drawn or mode is last drawn
@@ -632,20 +687,56 @@ int update_screen(void)
return OK;
}
-static void win_border_redr_title(win_T *wp, ScreenGrid *grid, int col)
+/// Prepare for 'hlsearch' highlighting.
+void start_search_hl(void)
+{
+ if (!p_hls || no_hlsearch) {
+ return;
+ }
+
+ end_search_hl(); // just in case it wasn't called before
+ last_pat_prog(&screen_search_hl.rm);
+ // Set the time limit to 'redrawtime'.
+ screen_search_hl.tm = profile_setlimit(p_rdt);
+}
+
+/// Clean up for 'hlsearch' highlighting.
+void end_search_hl(void)
{
- VirtText title_chunks = wp->w_float_config.title_chunks;
+ if (screen_search_hl.rm.regprog == NULL) {
+ return;
+ }
- for (size_t i = 0; i < title_chunks.size; i++) {
- char *text = title_chunks.items[i].text;
- int cell = (int)mb_string2cells(text);
- int hl_id = title_chunks.items[i].hl_id;
- int attr = hl_id ? syn_id2attr(hl_id) : 0;
- grid_puts(grid, text, 0, col, attr);
- col += cell;
+ vim_regfree(screen_search_hl.rm.regprog);
+ screen_search_hl.rm.regprog = NULL;
+}
+
+static void win_redr_bordertext(win_T *wp, VirtText vt, int col)
+{
+ for (size_t i = 0; i < kv_size(vt);) {
+ int attr = 0;
+ char *text = next_virt_text_chunk(vt, &i, &attr);
+ if (text == NULL) {
+ break;
+ }
+ attr = hl_apply_winblend(wp, attr);
+ col += grid_line_puts(col, text, -1, attr);
}
}
+int win_get_bordertext_col(int total_col, int text_width, AlignTextPos align)
+{
+ switch (align) {
+ case kAlignLeft:
+ return 1;
+ case kAlignCenter:
+ return MAX((total_col - text_width) / 2 + 1, 1);
+ case kAlignRight:
+ return MAX(total_col - text_width + 1, 1);
+ }
+ UNREACHABLE;
+}
+
static void win_redr_border(win_T *wp)
{
wp->w_redr_border = false;
@@ -655,102 +746,459 @@ static void win_redr_border(win_T *wp)
ScreenGrid *grid = &wp->w_grid_alloc;
- schar_T *chars = wp->w_float_config.border_chars;
+ schar_T chars[8];
+ for (int i = 0; i < 8; i++) {
+ chars[i] = schar_from_str(wp->w_float_config.border_chars[i]);
+ }
int *attrs = wp->w_float_config.border_attr;
int *adj = wp->w_border_adj;
- int irow = wp->w_height_inner + wp->w_winbar_height, icol = wp->w_width_inner;
+ int irow = wp->w_height_inner + wp->w_winbar_height;
+ int icol = wp->w_width_inner;
if (adj[0]) {
- grid_puts_line_start(grid, 0);
+ grid_line_start(grid, 0);
if (adj[3]) {
- grid_put_schar(grid, 0, 0, chars[0], attrs[0]);
+ grid_line_put_schar(0, chars[0], attrs[0]);
}
for (int i = 0; i < icol; i++) {
- grid_put_schar(grid, 0, i + adj[3], chars[1], attrs[1]);
+ grid_line_put_schar(i + adj[3], chars[1], attrs[1]);
}
if (wp->w_float_config.title) {
- int title_col = 0;
- int title_width = wp->w_float_config.title_width;
- AlignTextPos title_pos = wp->w_float_config.title_pos;
-
- if (title_pos == kAlignCenter) {
- title_col = (icol - title_width) / 2 + 1;
- } else {
- title_col = title_pos == kAlignLeft ? 1 : icol - title_width + 1;
- }
-
- win_border_redr_title(wp, grid, title_col);
+ int title_col = win_get_bordertext_col(icol, wp->w_float_config.title_width,
+ wp->w_float_config.title_pos);
+ win_redr_bordertext(wp, wp->w_float_config.title_chunks, title_col);
}
if (adj[1]) {
- grid_put_schar(grid, 0, icol + adj[3], chars[2], attrs[2]);
+ grid_line_put_schar(icol + adj[3], chars[2], attrs[2]);
}
- grid_puts_line_flush(false);
+ grid_line_flush();
}
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_flush(false);
+ grid_line_start(grid, i + adj[0]);
+ grid_line_put_schar(0, chars[7], attrs[7]);
+ grid_line_flush();
}
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_flush(false);
+ int ic = (i == 0 && !adj[0] && chars[2]) ? 2 : 3;
+ grid_line_start(grid, i + adj[0]);
+ grid_line_put_schar(icol + adj[3], chars[ic], attrs[ic]);
+ grid_line_flush();
}
}
if (adj[2]) {
- grid_puts_line_start(grid, irow + adj[0]);
+ grid_line_start(grid, irow + adj[0]);
if (adj[3]) {
- grid_put_schar(grid, irow + adj[0], 0, chars[6], attrs[6]);
+ grid_line_put_schar(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]);
+ int ic = (i == 0 && !adj[3] && chars[6]) ? 6 : 5;
+ grid_line_put_schar(i + adj[3], chars[ic], attrs[ic]);
+ }
+
+ if (wp->w_float_config.footer) {
+ int footer_col = win_get_bordertext_col(icol, wp->w_float_config.footer_width,
+ wp->w_float_config.footer_pos);
+ win_redr_bordertext(wp, wp->w_float_config.footer_chunks, footer_col);
}
if (adj[1]) {
- grid_put_schar(grid, irow + adj[0], icol + adj[3], chars[4], attrs[4]);
+ grid_line_put_schar(icol + adj[3], chars[4], attrs[4]);
+ }
+ grid_line_flush();
+ }
+}
+
+/// Set cursor to its position in the current window.
+void setcursor(void)
+{
+ 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;
+ int row = curwin->w_wrow;
+ int col = curwin->w_wcol;
+ if (curwin->w_p_rl) {
+ // 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
+ && vim_isprintc(gchar_cursor())) ? 2 : 1);
}
- grid_puts_line_flush(false);
+
+ grid_adjust(&grid, &row, &col);
+ ui_grid_cursor_goto(grid->handle, row, col);
}
}
/// Show current cursor info in ruler and various other places
///
/// @param always if false, only show ruler if position has changed.
-void show_cursor_info(bool always)
+void show_cursor_info_later(bool force)
{
- if (!always && !redrawing()) {
- return;
+ int state = get_real_state();
+ int empty_line = (State & MODE_INSERT) == 0
+ && *ml_get_buf(curwin->w_buffer, curwin->w_cursor.lnum) == NUL;
+
+ // Only draw when something changed.
+ validate_virtcol_win(curwin);
+ if (force
+ || curwin->w_cursor.lnum != curwin->w_stl_cursor.lnum
+ || curwin->w_cursor.col != curwin->w_stl_cursor.col
+ || curwin->w_virtcol != curwin->w_stl_virtcol
+ || curwin->w_cursor.coladd != curwin->w_stl_cursor.coladd
+ || curwin->w_topline != curwin->w_stl_topline
+ || curwin->w_buffer->b_ml.ml_line_count != curwin->w_stl_line_count
+ || curwin->w_topfill != curwin->w_stl_topfill
+ || empty_line != curwin->w_stl_empty
+ || reg_recording != curwin->w_stl_recording
+ || state != curwin->w_stl_state
+ || (VIsual_active && VIsual_mode != curwin->w_stl_visual_mode)) {
+ if (curwin->w_status_height || global_stl_height()) {
+ curwin->w_redr_status = true;
+ } else {
+ redraw_cmdline = true;
+ }
+
+ if (*p_wbr != NUL || *curwin->w_p_wbr != NUL) {
+ curwin->w_redr_status = true;
+ }
+
+ if ((p_icon && (stl_syntax & STL_IN_ICON))
+ || (p_title && (stl_syntax & STL_IN_TITLE))) {
+ need_maketitle = true;
+ }
+ }
+
+ curwin->w_stl_cursor = curwin->w_cursor;
+ curwin->w_stl_virtcol = curwin->w_virtcol;
+ curwin->w_stl_empty = (char)empty_line;
+ curwin->w_stl_topline = curwin->w_topline;
+ curwin->w_stl_line_count = curwin->w_buffer->b_ml.ml_line_count;
+ curwin->w_stl_topfill = curwin->w_topfill;
+ curwin->w_stl_recording = reg_recording;
+ curwin->w_stl_state = state;
+ if (VIsual_active) {
+ curwin->w_stl_visual_mode = VIsual_mode;
}
+}
+
+/// @return true when postponing displaying the mode message: when not redrawing
+/// or inside a mapping.
+bool skip_showmode(void)
+{
+ // Call char_avail() only when we are going to show something, because it
+ // takes a bit of time. redrawing() may also call char_avail().
+ if (global_busy || msg_silent != 0 || !redrawing() || (char_avail() && !KeyTyped)) {
+ redraw_mode = true; // show mode later
+ return true;
+ }
+ return false;
+}
+
+/// Show the current mode and ruler.
+///
+/// If clear_cmdline is true, clear the rest of the cmdline.
+/// If clear_cmdline is false there may be a message there that needs to be
+/// cleared only if a mode is shown.
+/// If redraw_mode is true show or clear the mode.
+/// @return the length of the message (0 if no message).
+int showmode(void)
+{
+ int length = 0;
+
+ if (ui_has(kUIMessages) && clear_cmdline) {
+ msg_ext_clear(true);
+ }
+
+ // don't make non-flushed message part of the showmode
+ msg_ext_ui_flush();
+
+ msg_grid_validate();
+
+ int do_mode = ((p_smd && msg_silent == 0)
+ && ((State & MODE_TERMINAL)
+ || (State & MODE_INSERT)
+ || restart_edit != NUL
+ || VIsual_active));
+
+ bool can_show_mode = (p_ch != 0 || ui_has(kUIMessages));
+ if ((do_mode || reg_recording != 0) && can_show_mode) {
+ int sub_attr;
+ if (skip_showmode()) {
+ return 0; // show mode later
+ }
+
+ bool nwr_save = need_wait_return;
+
+ // wait a bit before overwriting an important message
+ msg_check_for_delay(false);
+
+ // if the cmdline is more than one line high, erase top lines
+ bool need_clear = clear_cmdline;
+ if (clear_cmdline && cmdline_row < Rows - 1) {
+ msg_clr_cmdline(); // will reset clear_cmdline
+ }
+
+ // Position on the last line in the window, column 0
+ msg_pos_mode();
+ int attr = HL_ATTR(HLF_CM); // Highlight mode
+
+ // When the screen is too narrow to show the entire mode message,
+ // avoid scrolling and truncate instead.
+ msg_no_more = true;
+ int save_lines_left = lines_left;
+ lines_left = 0;
+
+ if (do_mode) {
+ msg_puts_attr("--", attr);
+ // CTRL-X in Insert mode
+ if (edit_submode != NULL && !shortmess(SHM_COMPLETIONMENU)) {
+ // These messages can get long, avoid a wrap in a narrow window.
+ // Prefer showing edit_submode_extra. With external messages there
+ // is no imposed limit.
+ if (ui_has(kUIMessages)) {
+ length = INT_MAX;
+ } else {
+ length = (Rows - msg_row) * Columns - 3;
+ }
+ if (edit_submode_extra != NULL) {
+ length -= vim_strsize(edit_submode_extra);
+ }
+ if (length > 0) {
+ if (edit_submode_pre != NULL) {
+ length -= vim_strsize(edit_submode_pre);
+ }
+ if (length - vim_strsize(edit_submode) > 0) {
+ if (edit_submode_pre != NULL) {
+ msg_puts_attr(edit_submode_pre, attr);
+ }
+ msg_puts_attr(edit_submode, attr);
+ }
+ if (edit_submode_extra != NULL) {
+ msg_puts_attr(" ", attr); // Add a space in between.
+ if ((int)edit_submode_highl < HLF_COUNT) {
+ sub_attr = win_hl_attr(curwin, (int)edit_submode_highl);
+ } else {
+ sub_attr = attr;
+ }
+ msg_puts_attr(edit_submode_extra, sub_attr);
+ }
+ }
+ } else {
+ 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 & MODE_INSERT) {
+ if (p_ri) {
+ msg_puts_attr(_(" REVERSE"), attr);
+ }
+ msg_puts_attr(_(" INSERT"), attr);
+ } else if (restart_edit == 'I' || restart_edit == 'i'
+ || restart_edit == 'a' || restart_edit == 'A') {
+ if (curbuf->terminal) {
+ msg_puts_attr(_(" (terminal)"), attr);
+ } else {
+ msg_puts_attr(_(" (insert)"), attr);
+ }
+ } else if (restart_edit == 'R') {
+ msg_puts_attr(_(" (replace)"), attr);
+ } else if (restart_edit == 'V') {
+ msg_puts_attr(_(" (vreplace)"), attr);
+ }
+ if (State & MODE_LANGMAP) {
+ if (curwin->w_p_arab) {
+ msg_puts_attr(_(" Arabic"), attr);
+ } else if (get_keymap_str(curwin, " (%s)",
+ NameBuff, MAXPATHL)) {
+ msg_puts_attr(NameBuff, attr);
+ }
+ }
+ if ((State & MODE_INSERT) && p_paste) {
+ msg_puts_attr(_(" (paste)"), attr);
+ }
+
+ if (VIsual_active) {
+ char *p;
+
+ // Don't concatenate separate words to avoid translation
+ // problems.
+ switch ((VIsual_select ? 4 : 0)
+ + (VIsual_mode == Ctrl_V) * 2
+ + (VIsual_mode == 'V')) {
+ case 0:
+ p = N_(" VISUAL"); break;
+ case 1:
+ p = N_(" VISUAL LINE"); break;
+ case 2:
+ p = N_(" VISUAL BLOCK"); break;
+ case 4:
+ p = N_(" SELECT"); break;
+ case 5:
+ p = N_(" SELECT LINE"); break;
+ default:
+ p = N_(" SELECT BLOCK"); break;
+ }
+ msg_puts_attr(_(p), attr);
+ }
+ msg_puts_attr(" --", attr);
+ }
+
+ need_clear = true;
+ }
+ if (reg_recording != 0
+ && edit_submode == NULL // otherwise it gets too long
+ ) {
+ recording_mode(attr);
+ need_clear = true;
+ }
+
+ mode_displayed = true;
+ if (need_clear || clear_cmdline || redraw_mode) {
+ msg_clr_eos();
+ }
+ msg_didout = false; // overwrite this message
+ length = msg_col;
+ msg_col = 0;
+ msg_no_more = false;
+ lines_left = save_lines_left;
+ need_wait_return = nwr_save; // never ask for hit-return for this
+ } else if (clear_cmdline && msg_silent == 0) {
+ // Clear the whole command line. Will reset "clear_cmdline".
+ msg_clr_cmdline();
+ } else if (redraw_mode) {
+ msg_pos_mode();
+ msg_clr_eos();
+ }
+
+ // NB: also handles clearing the showmode if it was empty or disabled
+ msg_ext_flush_showmode();
+
+ // In Visual mode the size of the selected area must be redrawn.
+ if (VIsual_active) {
+ clear_showcmd();
+ }
+
+ // If the current or last window has no status line and global statusline is disabled,
+ // the ruler is after the mode message and must be redrawn
+ win_T *ruler_win = curwin->w_status_height == 0 ? curwin : lastwin_nofloating();
+ if (redrawing() && ruler_win->w_status_height == 0 && global_stl_height() == 0
+ && !(p_ch == 0 && !ui_has(kUIMessages))) {
+ grid_line_start(&msg_grid_adj, Rows - 1);
+ win_redr_ruler(ruler_win);
+ grid_line_flush();
+ }
+
+ redraw_cmdline = false;
+ redraw_mode = false;
+ clear_cmdline = false;
+
+ return length;
+}
+
+/// Position for a mode message.
+static void msg_pos_mode(void)
+{
+ msg_col = 0;
+ msg_row = Rows - 1;
+}
- win_check_ns_hl(curwin);
- if ((*p_stl != NUL || *curwin->w_p_stl != NUL)
- && (curwin->w_status_height || global_stl_height())) {
- redraw_custom_statusline(curwin);
+/// Delete mode message. Used when ESC is typed which is expected to end
+/// Insert mode (but Insert mode didn't end yet!).
+/// Caller should check "mode_displayed".
+void unshowmode(bool force)
+{
+ // Don't delete it right now, when not redrawing or inside a mapping.
+ if (!redrawing() || (!force && char_avail() && !KeyTyped)) {
+ redraw_cmdline = true; // delete mode later
} else {
- win_redr_ruler(curwin, always);
+ clearmode();
}
- if (*p_wbr != NUL || *curwin->w_p_wbr != NUL) {
- win_redr_winbar(curwin);
+}
+
+// Clear the mode message.
+void clearmode(void)
+{
+ const int save_msg_row = msg_row;
+ const int save_msg_col = msg_col;
+
+ msg_ext_ui_flush();
+ msg_pos_mode();
+ if (reg_recording != 0) {
+ recording_mode(HL_ATTR(HLF_CM));
}
+ msg_clr_eos();
+ msg_ext_flush_showmode();
- if (need_maketitle
- || (p_icon && (stl_syntax & STL_IN_ICON))
- || (p_title && (stl_syntax & STL_IN_TITLE))) {
- maketitle();
+ msg_col = save_msg_col;
+ msg_row = save_msg_row;
+}
+
+static void recording_mode(int attr)
+{
+ msg_puts_attr(_("recording"), attr);
+ if (shortmess(SHM_RECORDING)) {
+ return;
}
- win_check_ns_hl(NULL);
- // Redraw the tab pages line if needed.
- if (redraw_tabline) {
- draw_tabline();
+ char s[4];
+ snprintf(s, ARRAY_SIZE(s), " @%c", reg_recording);
+ msg_puts_attr(s, attr);
+}
+
+#define COL_RULER 17 // columns needed by standard ruler
+
+/// Compute columns for ruler and shown command. 'sc_col' is also used to
+/// decide what the maximum length of a message on the status line can be.
+/// If there is a status line for the last window, 'sc_col' is independent
+/// of 'ru_col'.
+void comp_col(void)
+{
+ bool last_has_status = last_stl_height(false) > 0;
+
+ sc_col = 0;
+ ru_col = 0;
+ if (p_ru) {
+ ru_col = (ru_wid ? ru_wid : COL_RULER) + 1;
+ // no last status line, adjust sc_col
+ if (!last_has_status) {
+ sc_col = ru_col;
+ }
+ }
+ if (p_sc) {
+ sc_col += SHOWCMD_COLS;
+ if (!p_ru || last_has_status) { // no need for separating space
+ sc_col++;
+ }
+ }
+ assert(sc_col >= 0
+ && INT_MIN + sc_col <= Columns);
+ sc_col = Columns - sc_col;
+ assert(ru_col >= 0
+ && INT_MIN + ru_col <= Columns);
+ ru_col = Columns - ru_col;
+ if (sc_col <= 0) { // screen too narrow, will become a mess
+ sc_col = 1;
}
+ if (ru_col <= 0) {
+ ru_col = 1;
+ }
+ set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
}
static void redraw_win_signcol(win_T *wp)
@@ -762,6 +1210,7 @@ static void redraw_win_signcol(win_T *wp)
wp->w_scwidth = win_signcol_count(wp);
if (wp->w_scwidth != scwidth) {
changed_line_abv_curs_win(wp);
+ redraw_later(wp, UPD_NOT_VALID);
}
}
@@ -844,8 +1293,8 @@ static void draw_vsep_win(win_T *wp)
}
// draw the vertical separator right of this window
- int hl;
- int c = fillchar_vsep(wp, &hl);
+ int hl = win_hl_attr(wp, HLF_C);
+ int c = wp->w_p_fcs_chars.vert;
grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp),
W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl);
}
@@ -858,30 +1307,32 @@ static void draw_hsep_win(win_T *wp)
}
// draw the horizontal separator below this window
- int hl;
- int c = fillchar_hsep(wp, &hl);
+ int hl = win_hl_attr(wp, HLF_C);
+ int c = wp->w_p_fcs_chars.horiz;
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)
+static schar_T 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
+ int c;
if (vsep_connected(wp, corner)) {
if (hsep_connected(wp, corner)) {
- return wp->w_p_fcs_chars.verthoriz;
+ c = wp->w_p_fcs_chars.verthoriz;
} else if (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) {
- return wp->w_p_fcs_chars.vertright;
+ c = wp->w_p_fcs_chars.vertright;
} else {
- return wp->w_p_fcs_chars.vertleft;
+ c = wp->w_p_fcs_chars.vertleft;
}
} else if (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) {
- return wp->w_p_fcs_chars.horizdown;
+ c = wp->w_p_fcs_chars.horizdown;
} else {
- return wp->w_p_fcs_chars.horizup;
+ c = wp->w_p_fcs_chars.horizup;
}
+ return schar_from_char(c);
}
/// Draw separator connecting characters on the corners of window "wp"
@@ -917,21 +1368,31 @@ static void draw_sep_connectors_win(win_T *wp)
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);
+ // Make sure not to send cursor position updates to ui.
+ bool top_left = !(win_at_top || win_at_left);
+ bool top_right = !(win_at_top || win_at_right);
+ bool bot_left = !(win_at_bottom || win_at_left);
+ bool bot_right = !(win_at_bottom || win_at_right);
+
+ if (top_left) {
+ grid_line_start(&default_grid, wp->w_winrow - 1);
+ grid_line_put_schar(wp->w_wincol - 1, get_corner_sep_connector(wp, WC_TOP_LEFT), hl);
+ grid_line_flush();
+ }
+ if (top_right) {
+ grid_line_start(&default_grid, wp->w_winrow - 1);
+ grid_line_put_schar(W_ENDCOL(wp), get_corner_sep_connector(wp, WC_TOP_RIGHT), hl);
+ grid_line_flush();
+ }
+ if (bot_left) {
+ grid_line_start(&default_grid, W_ENDROW(wp));
+ grid_line_put_schar(wp->w_wincol - 1, get_corner_sep_connector(wp, WC_BOTTOM_LEFT), hl);
+ grid_line_flush();
+ }
+ if (bot_right) {
+ grid_line_start(&default_grid, W_ENDROW(wp));
+ grid_line_put_schar(W_ENDCOL(wp), get_corner_sep_connector(wp, WC_BOTTOM_RIGHT), hl);
+ grid_line_flush();
}
}
@@ -992,9 +1453,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
int type = wp->w_redr_type;
if (type >= UPD_NOT_VALID) {
- // TODO(bfredl): should only be implied for CLEAR, not NOT_VALID!
wp->w_redr_status = true;
-
wp->w_lines_valid = 0;
}
@@ -1027,25 +1486,41 @@ static void win_update(win_T *wp, DecorProviders *providers)
win_extmark_arr.size = 0;
- decor_redraw_reset(buf, &decor_state);
+ decor_redraw_reset(wp, &decor_state);
DecorProviders line_providers;
- decor_providers_invoke_win(wp, providers, &line_providers, &provider_err);
+ decor_providers_invoke_win(wp, providers, &line_providers);
redraw_win_signcol(wp);
init_search_hl(wp, &screen_search_hl);
- // Force redraw when width of 'number' or 'relativenumber' column
- // changes.
- int nrwidth = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0;
- if (wp->w_nrwidth != nrwidth) {
- type = UPD_NOT_VALID;
- wp->w_nrwidth = nrwidth;
-
- if (buf->terminal) {
- terminal_check_size(buf->terminal);
+ // Make sure skipcol is valid, it depends on various options and the window
+ // width.
+ if (wp->w_skipcol > 0) {
+ int w = 0;
+ int width1 = wp->w_width_inner - win_col_off(wp);
+ int width2 = width1 + win_col_off2(wp);
+ int add = width1;
+
+ while (w < wp->w_skipcol) {
+ if (w > 0) {
+ add = width2;
+ }
+ w += add;
+ }
+ if (w != wp->w_skipcol) {
+ // always round down, the higher value may not be valid
+ wp->w_skipcol = w - add;
}
+ }
+
+ const int nrwidth_before = wp->w_nrwidth;
+ int nrwidth_new = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0;
+ // Force redraw when width of 'number' or 'relativenumber' column changes.
+ if (wp->w_nrwidth != nrwidth_new) {
+ type = UPD_NOT_VALID;
+ wp->w_nrwidth = nrwidth_new;
} else if (buf->b_mod_set
&& buf->b_mod_xlines != 0
&& wp->w_redraw_top != 0) {
@@ -1099,8 +1574,6 @@ static void win_update(win_T *wp, DecorProviders *providers)
}
}
if (mod_top != 0 && hasAnyFolding(wp)) {
- linenr_T lnumt, lnumb;
-
// A change in a line can cause lines above it to become folded or
// unfolded. Find the top most buffer line that may be affected.
// If the line was previously folded and displayed, get the first
@@ -1111,8 +1584,8 @@ static void win_update(win_T *wp, DecorProviders *providers)
// the line below it. If there is no valid entry, use w_topline.
// Find the first valid w_lines[] entry below mod_bot. Set lnumb
// to this line. If there is no valid entry, use MAXLNUM.
- lnumt = wp->w_topline;
- lnumb = MAXLNUM;
+ linenr_T lnumt = wp->w_topline;
+ linenr_T lnumb = MAXLNUM;
for (int i = 0; i < wp->w_lines_valid; i++) {
if (wp->w_lines[i].wl_valid) {
if (wp->w_lines[i].wl_lastlnum < mod_top) {
@@ -1168,11 +1641,11 @@ static void win_update(win_T *wp, DecorProviders *providers)
// When only displaying the lines at the top, set top_end. Used when
// window has scrolled down for msg_scrolled.
if (type == UPD_REDRAW_TOP) {
- long j = 0;
+ int j = 0;
for (int i = 0; i < wp->w_lines_valid; i++) {
j += wp->w_lines[i].wl_size;
if (j >= wp->w_upd_rows) {
- top_end = (int)j;
+ top_end = j;
break;
}
}
@@ -1205,7 +1678,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
|| (wp->w_topline == wp->w_lines[0].wl_lnum
&& wp->w_topfill > wp->w_old_topfill))) {
// New topline is above old topline: May scroll down.
- long j;
+ int j;
if (hasAnyFolding(wp)) {
linenr_T ln;
@@ -1223,7 +1696,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
j = wp->w_lines[0].wl_lnum - wp->w_topline;
}
if (j < wp->w_grid.rows - 2) { // not too far off
- int i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1);
+ int i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1, true);
// 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;
@@ -1265,7 +1738,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
// needs updating.
// try to find wp->w_topline in wp->w_lines[].wl_lnum
- long j = -1;
+ int j = -1;
int row = 0;
for (int i = 0; i < wp->w_lines_valid; i++) {
if (wp->w_lines[i].wl_valid
@@ -1304,7 +1777,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
// bot_start to the first row that needs redrawing.
bot_start = 0;
int idx = 0;
- for (;;) {
+ while (true) {
wp->w_lines[idx] = wp->w_lines[j];
// stop at line that didn't fit, unless it is still
// valid (no lines deleted)
@@ -1417,7 +1890,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
// First compute the actual start and end column.
if (VIsual_mode == Ctrl_V) {
colnr_T fromc, toc;
- unsigned int save_ve_flags = curwin->w_ve_flags;
+ unsigned save_ve_flags = curwin->w_ve_flags;
if (curwin->w_p_lbr) {
curwin->w_ve_flags = VE_ALL;
@@ -1441,7 +1914,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
pos.lnum += cursor_above ? 1 : -1) {
colnr_T t;
- pos.col = (colnr_T)strlen(ml_get_buf(wp->w_buffer, pos.lnum, false));
+ pos.col = (colnr_T)strlen(ml_get_buf(wp->w_buffer, pos.lnum));
getvvcol(wp, &pos, NULL, NULL, &t);
if (toc < t) {
toc = t;
@@ -1548,19 +2021,34 @@ static void win_update(win_T *wp, DecorProviders *providers)
wp->w_old_visual_col = 0;
}
- bool cursorline_standout = win_cursorline_standout(wp);
+ foldinfo_T cursorline_fi = { 0 };
+ wp->w_cursorline = win_cursorline_standout(wp) ? wp->w_cursor.lnum : 0;
+ if (wp->w_p_cul) {
+ // Make sure that the cursorline on a closed fold is redrawn
+ cursorline_fi = fold_info(wp, wp->w_cursor.lnum);
+ if (cursorline_fi.fi_level != 0 && cursorline_fi.fi_lines > 0) {
+ wp->w_cursorline = cursorline_fi.fi_lnum;
+ }
+ }
win_check_ns_hl(wp);
+ spellvars_T spv = { 0 };
+ linenr_T lnum = wp->w_topline; // first line shown in window
+ // Initialize spell related variables for the first drawn line.
+ if (spell_check_window(wp)) {
+ spv.spv_has_spell = true;
+ spv.spv_unchanged = mod_top == 0;
+ }
+
// Update all the window rows.
int idx = 0; // first entry in w_lines[].wl_size
int row = 0; // current window row to display
int srow = 0; // starting row of the current line
- linenr_T lnum = wp->w_topline; // first line shown in window
bool eof = false; // if true, we hit the end of the file
bool didline = false; // if true, we finished the last line
- for (;;) {
+ while (true) {
// 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) {
@@ -1604,7 +2092,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
// if lines were inserted or deleted
|| (wp->w_match_head != NULL
&& buf->b_mod_xlines != 0)))))
- || (cursorline_standout && lnum == wp->w_cursor.lnum)
+ || lnum == wp->w_cursorline
|| lnum == wp->w_last_cursorline) {
if (lnum == mod_top) {
top_to_mod = false;
@@ -1620,8 +2108,6 @@ static void win_update(win_T *wp, DecorProviders *providers)
&& !(dollar_vcol >= 0 && mod_bot == mod_top + 1)
&& row >= top_end) {
int old_rows = 0;
- int new_rows = 0;
- int xtra_rows;
linenr_T l;
int i;
@@ -1656,14 +2142,20 @@ static void win_update(win_T *wp, DecorProviders *providers)
bot_start = 0;
bot_scroll_start = 0;
} else {
+ int new_rows = 0;
// Able to count old number of rows: Count new window
// rows, and may insert/delete lines
- long j = idx;
+ int j = idx;
for (l = lnum; l < mod_bot; l++) {
if (hasFoldingWin(wp, l, NULL, &l, true, NULL)) {
new_rows++;
} else if (l == wp->w_topline) {
- new_rows += plines_win_nofill(wp, l, true) + wp->w_topfill;
+ int n = plines_win_nofill(wp, l, false) + wp->w_topfill;
+ n -= adjust_plines_for_skipcol(wp);
+ if (n > wp->w_height_inner) {
+ n = wp->w_height_inner;
+ }
+ new_rows += n;
} else {
new_rows += plines_win(wp, l, true);
}
@@ -1674,7 +2166,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
break;
}
}
- xtra_rows = new_rows - old_rows;
+ int xtra_rows = new_rows - old_rows;
if (xtra_rows < 0) {
// May scroll text up. If there is not enough
// remaining text or scrolling fails, must redraw the
@@ -1711,17 +2203,17 @@ static void win_update(win_T *wp, DecorProviders *providers)
int x = row + new_rows;
// move entries in w_lines[] upwards
- for (;;) {
+ while (true) {
// stop at last valid entry in w_lines[]
if (i >= wp->w_lines_valid) {
- wp->w_lines_valid = (int)j;
+ wp->w_lines_valid = j;
break;
}
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_lines_valid = (int)j + 1;
+ wp->w_lines_valid = j + 1;
break;
}
x += wp->w_lines[j++].wl_size;
@@ -1756,7 +2248,8 @@ static void win_update(win_T *wp, DecorProviders *providers)
// When lines are folded, display one line for all of them.
// Otherwise, display normally (can be several display lines when
// 'wrap' is on).
- foldinfo_T foldinfo = fold_info(wp, lnum);
+ foldinfo_T foldinfo = wp->w_p_cul && lnum == wp->w_cursor.lnum
+ ? cursorline_fi : fold_info(wp, lnum);
if (foldinfo.fi_lines == 0
&& idx < wp->w_lines_valid
@@ -1778,9 +2271,10 @@ static void win_update(win_T *wp, DecorProviders *providers)
}
// Display one line
- row = win_line(wp, lnum, srow,
- foldinfo.fi_lines ? srow : wp->w_grid.rows,
- mod_top == 0, false, foldinfo, &line_providers, &provider_err);
+ spellvars_T zero_spv = { 0 };
+ row = win_line(wp, lnum, srow, wp->w_grid.rows, false,
+ foldinfo.fi_lines > 0 ? &zero_spv : &spv,
+ foldinfo, &line_providers);
if (foldinfo.fi_lines == 0) {
wp->w_lines[idx].wl_folded = false;
@@ -1792,6 +2286,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
wp->w_lines[idx].wl_folded = true;
wp->w_lines[idx].wl_lastlnum = lnum + foldinfo.fi_lines;
did_update = DID_FOLD;
+ spv.spv_capcol_lnum = 0;
}
}
@@ -1815,9 +2310,9 @@ static void win_update(win_T *wp, DecorProviders *providers)
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,
- info, &line_providers, &provider_err);
+ foldinfo_T info = wp->w_p_cul && lnum == wp->w_cursor.lnum
+ ? cursorline_fi : fold_info(wp, lnum);
+ (void)win_line(wp, lnum, srow, wp->w_grid.rows, true, &spv, info, &line_providers);
}
// This line does not need to be drawn, advance to the next one.
@@ -1827,6 +2322,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
}
lnum = wp->w_lines[idx - 1].wl_lastlnum + 1;
did_update = DID_NONE;
+ spv.spv_capcol_lnum = 0;
}
// 'statuscolumn' width has changed or errored, start from the top.
@@ -1837,7 +2333,8 @@ static void win_update(win_T *wp, DecorProviders *providers)
lnum = wp->w_topline;
wp->w_lines_valid = 0;
wp->w_valid &= ~VALID_WCOL;
- decor_providers_invoke_win(wp, providers, &line_providers, &provider_err);
+ decor_redraw_reset(wp, &decor_state);
+ decor_providers_invoke_win(wp, providers, &line_providers);
continue;
}
@@ -1850,7 +2347,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
// 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_cursorline = wp->w_cursorline;
wp->w_last_cursor_lnum_rnu = wp->w_p_rnu ? wp->w_cursor.lnum : 0;
@@ -1880,24 +2377,21 @@ static void win_update(win_T *wp, DecorProviders *providers)
wp->w_botline = lnum;
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 symbol = wp->w_p_fcs_chars.lastline;
- char fillbuf[12]; // 2 characters of 6 bytes
- int charlen = utf_char2bytes(symbol, &fillbuf[0]);
- utf_char2bytes(symbol, &fillbuf[charlen]);
-
// Last line isn't finished: Display "@@@" in the last screen line.
- grid_puts_len(&wp->w_grid, fillbuf, MIN(wp->w_grid.cols, 2) * charlen, scr_row, 0, at_attr);
- grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, wp->w_grid.cols, symbol, ' ', at_attr);
+ grid_line_start(&wp->w_grid, wp->w_grid.rows - 1);
+ grid_line_fill(0, MIN(wp->w_grid.cols, 3), wp->w_p_fcs_chars.lastline, at_attr);
+ grid_line_fill(3, wp->w_grid.cols, ' ', at_attr);
+ grid_line_flush();
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;
- int symbol = wp->w_p_fcs_chars.lastline;
-
// Last line isn't finished: Display "@@@" at the end.
- grid_fill(&wp->w_grid, wp->w_grid.rows - 1, wp->w_grid.rows,
- MAX(start_col, 0), wp->w_grid.cols, symbol, symbol, at_attr);
+ // If this would split a doublewidth char in two, we need to display "@@@@" instead
+ grid_line_start(&wp->w_grid, wp->w_grid.rows - 1);
+ int width = grid_line_getchar(MAX(wp->w_grid.cols - 3, 0), NULL) == NUL ? 4 : 3;
+ grid_line_fill(MAX(wp->w_grid.cols - width, 0), wp->w_grid.cols,
+ wp->w_p_fcs_chars.lastline, at_attr);
+ grid_line_flush();
set_empty_rows(wp, srow);
wp->w_botline = lnum;
} else {
@@ -1908,13 +2402,14 @@ static void win_update(win_T *wp, DecorProviders *providers)
} else {
if (eof) { // we hit the end of the file
wp->w_botline = buf->b_ml.ml_line_count + 1;
- long j = win_get_fill(wp, wp->w_botline);
+ int j = win_get_fill(wp, wp->w_botline);
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 = { 0 };
- row = win_line(wp, wp->w_botline, row, wp->w_grid.rows,
- false, false, info, &line_providers, &provider_err);
+ spellvars_T zero_spv = { 0 };
+ foldinfo_T zero_foldinfo = { 0 };
+ row = win_line(wp, wp->w_botline, row, wp->w_grid.rows, false, &zero_spv,
+ zero_foldinfo, &line_providers);
}
} else if (dollar_vcol == -1) {
wp->w_botline = lnum;
@@ -1988,12 +2483,162 @@ static void win_update(win_T *wp, DecorProviders *providers)
}
}
+ if (nrwidth_before != wp->w_nrwidth && buf->terminal) {
+ terminal_check_size(buf->terminal);
+ }
+
// restore got_int, unless CTRL-C was hit while redrawing
if (!got_int) {
got_int = save_got_int;
}
}
+/// Scroll `line_count` lines at 'row' in window 'wp'.
+///
+/// Positive `line_count` means scrolling down, so that more space is available
+/// at 'row'. Negative `line_count` implies deleting lines at `row`.
+void win_scroll_lines(win_T *wp, int row, int line_count)
+{
+ if (!redrawing() || line_count == 0) {
+ return;
+ }
+
+ // No lines are being moved, just draw over the entire area
+ 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.cols);
+ } else {
+ grid_ins_lines(&wp->w_grid, row, line_count,
+ wp->w_grid.rows, 0, wp->w_grid.cols);
+ }
+}
+
+/// Call grid_fill() with columns adjusted for 'rightleft' if needed.
+/// Return the new offset.
+static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row, int endrow,
+ int attr)
+{
+ int nn = off + width;
+ const int endcol = wp->w_grid.cols;
+
+ if (nn > endcol) {
+ nn = endcol;
+ }
+
+ if (wp->w_p_rl) {
+ grid_fill(&wp->w_grid, row, endrow, endcol - nn, endcol - off, c1, c2, attr);
+ } else {
+ grid_fill(&wp->w_grid, row, endrow, off, nn, c1, c2, attr);
+ }
+
+ return nn;
+}
+
+/// Clear lines near the end of the window and mark the unused lines with "c1".
+/// Use "c2" as filler character.
+/// When "draw_margin" is true, then draw the sign/fold/number columns.
+void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, int endrow, hlf_T hl)
+{
+ assert(hl >= 0 && hl < HLF_COUNT);
+ int n = 0;
+
+ if (draw_margin) {
+ // draw the fold column
+ int fdc = compute_foldcolumn(wp, 0);
+ if (fdc > 0) {
+ n = win_fill_end(wp, ' ', ' ', n, fdc, row, endrow,
+ win_hl_attr(wp, HLF_FC));
+ }
+ // draw the sign column
+ int count = wp->w_scwidth;
+ if (count > 0) {
+ n = win_fill_end(wp, ' ', ' ', n, SIGN_WIDTH * count, row,
+ endrow, win_hl_attr(wp, HLF_SC));
+ }
+ // draw the number column
+ if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) == NULL) {
+ n = win_fill_end(wp, ' ', ' ', n, number_width(wp) + 1, row, endrow,
+ win_hl_attr(wp, HLF_N));
+ }
+ }
+
+ int attr = hl_combine_attr(win_bg_attr(wp), win_hl_attr(wp, (int)hl));
+
+ const int endcol = wp->w_grid.cols;
+ if (wp->w_p_rl) {
+ grid_fill(&wp->w_grid, row, endrow, 0, endcol - 1 - n, c2, c2, attr);
+ grid_fill(&wp->w_grid, row, endrow, endcol - 1 - n, endcol - n, c1, c2, attr);
+ } else {
+ grid_fill(&wp->w_grid, row, endrow, n, endcol, c1, c2, attr);
+ }
+}
+
+/// Compute the width of the foldcolumn. Based on 'foldcolumn' and how much
+/// space is available for window "wp", minus "col".
+int compute_foldcolumn(win_T *wp, int col)
+{
+ int fdc = win_fdccol_count(wp);
+ int wmw = wp == curwin && p_wmw == 0 ? 1 : (int)p_wmw;
+ int wwidth = wp->w_grid.cols;
+
+ if (fdc > wwidth - (col + wmw)) {
+ fdc = wwidth - (col + wmw);
+ }
+ return fdc;
+}
+
+/// Return the width of the 'number' and 'relativenumber' column.
+/// Caller may need to check if 'number' or 'relativenumber' is set.
+/// Otherwise it depends on 'numberwidth' and the line count.
+int number_width(win_T *wp)
+{
+ linenr_T lnum;
+
+ if (wp->w_p_rnu && !wp->w_p_nu) {
+ // cursor line shows "0"
+ lnum = wp->w_height_inner;
+ } else {
+ // cursor line shows absolute line number
+ lnum = wp->w_buffer->b_ml.ml_line_count;
+ }
+
+ if (lnum == wp->w_nrwidth_line_count) {
+ return wp->w_nrwidth_width;
+ }
+ wp->w_nrwidth_line_count = lnum;
+
+ // reset for 'statuscolumn'
+ if (*wp->w_p_stc != NUL) {
+ wp->w_statuscol_line_count = 0; // make sure width is re-estimated
+ wp->w_nrwidth_width = (wp->w_p_nu || wp->w_p_rnu) * (int)wp->w_p_nuw;
+ return wp->w_nrwidth_width;
+ }
+
+ int n = 0;
+ do {
+ lnum /= 10;
+ n++;
+ } while (lnum > 0);
+
+ // 'numberwidth' gives the minimal width plus one
+ if (n < wp->w_p_nuw - 1) {
+ n = (int)wp->w_p_nuw - 1;
+ }
+
+ // If 'signcolumn' is set to 'number' and there is a sign to display, then
+ // the minimal width for the number column is 2.
+ if (n < 2 && wp->w_buffer->b_signs_with_text && wp->w_minscwidth == SCL_NUM) {
+ n = 2;
+ }
+
+ wp->w_nrwidth_width = n;
+ return n;
+}
+
/// Redraw a window later, with wp->w_redr_type >= type.
///
/// Set must_redraw only if not already set to a higher value.
@@ -2097,7 +2742,7 @@ void status_redraw_all(void)
bool is_stl_global = global_stl_height() != 0;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if ((!is_stl_global && wp->w_status_height) || (is_stl_global && wp == curwin)
+ if ((!is_stl_global && wp->w_status_height) || wp == curwin
|| wp->w_winbar_height) {
wp->w_redr_status = true;
redraw_later(wp, UPD_VALID);
@@ -2123,6 +2768,11 @@ void status_redraw_buf(buf_T *buf)
redraw_later(wp, UPD_VALID);
}
}
+ // Redraw the ruler if it is in the command line and was not marked for redraw above
+ if (p_ru && !curwin->w_status_height && !curwin->w_redr_status) {
+ redraw_cmdline = true;
+ redraw_later(curwin, UPD_VALID);
+ }
}
/// Redraw all status lines that need to be redrawn.
@@ -2182,3 +2832,48 @@ void redrawWinline(win_T *wp, linenr_T lnum)
redraw_later(wp, UPD_VALID);
}
}
+
+/// Return true if the cursor line in window "wp" may be concealed, according
+/// to the 'concealcursor' option.
+bool conceal_cursor_line(const win_T *wp)
+ FUNC_ATTR_NONNULL_ALL
+{
+ int c;
+
+ if (*wp->w_p_cocu == NUL) {
+ return false;
+ }
+ if (get_real_state() & MODE_VISUAL) {
+ c = 'v';
+ } else if (State & MODE_INSERT) {
+ c = 'i';
+ } else if (State & MODE_NORMAL) {
+ c = 'n';
+ } else if (State & MODE_CMDLINE) {
+ c = 'c';
+ } else {
+ return false;
+ }
+ return vim_strchr(wp->w_p_cocu, c) != NULL;
+}
+
+/// 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.
+bool win_cursorline_standout(const win_T *wp)
+ FUNC_ATTR_NONNULL_ALL
+{
+ return wp->w_p_cul || (wp->w_p_cole > 0 && !conceal_cursor_line(wp));
+}
+
+/// Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is set.
+/// Also when concealing is on and 'concealcursor' is not active.
+void redraw_for_cursorline(win_T *wp)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if ((wp->w_valid & VALID_CROW) == 0 && !pum_visible()
+ && (wp->w_p_rnu || win_cursorline_standout(wp))) {
+ // win_line() will redraw the number column and cursorline only.
+ redraw_later(wp, UPD_VALID);
+ }
+}