diff options
| author | Josh Rahm <joshuarahm@gmail.com> | 2023-11-30 20:35:25 +0000 |
|---|---|---|
| committer | Josh Rahm <joshuarahm@gmail.com> | 2023-11-30 20:35:25 +0000 |
| commit | 1b7b916b7631ddf73c38e3a0070d64e4636cb2f3 (patch) | |
| tree | cd08258054db80bb9a11b1061bb091c70b76926a /src/nvim/drawscreen.c | |
| parent | eaa89c11d0f8aefbb512de769c6c82f61a8baca3 (diff) | |
| parent | 4a8bf24ac690004aedf5540fa440e788459e5e34 (diff) | |
| download | rneovim-aucmd_textputpost.tar.gz rneovim-aucmd_textputpost.tar.bz2 rneovim-aucmd_textputpost.zip | |
Merge remote-tracking branch 'upstream/master' into aucmd_textputpostaucmd_textputpost
Diffstat (limited to 'src/nvim/drawscreen.c')
| -rw-r--r-- | src/nvim/drawscreen.c | 1031 |
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. - |