aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/screen.c
diff options
context:
space:
mode:
authorJosh Rahm <rahm@google.com>2022-08-19 12:26:08 -0600
committerJosh Rahm <rahm@google.com>2022-08-19 13:06:41 -0600
commita7237662f96933efe29eed8212464571e3778cd0 (patch)
tree27930202726b4251437c8cfa53069f65b4db90dc /src/nvim/screen.c
parent02292344929069ea63c0bb872cc22d552d86b67f (diff)
parentb2f979b30beac67906b2dd717fcb6a34f46f5e54 (diff)
downloadrneovim-tmp.tar.gz
rneovim-tmp.tar.bz2
rneovim-tmp.zip
Merge branch 'master' of https://github.com/neovim/neovim into rahmtmp
Diffstat (limited to 'src/nvim/screen.c')
-rw-r--r--src/nvim/screen.c5795
1 files changed, 411 insertions, 5384 deletions
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 6f4400e531..b343b167f8 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -1,629 +1,52 @@
// 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
-// screen.c: code for displaying on the screen
-//
+// screen.c: Lower level code for displaying on the screen.
+// grid.c contains some other lower-level code.
+
// Output to the screen (console, terminal emulator or GUI window) is minimized
// by remembering what is already on the screen, and only updating the parts
// that changed.
-//
-// The grid_*() functions write to the screen and handle updating grid->lines[].
-//
-// update_screen() is the function that updates all windows and status lines.
-// It is called from the main loop when must_redraw is non-zero. It may be
-// called from other places when an immediate screen update is needed.
-//
-// The part of the buffer that is displayed in a window is set with:
-// - w_topline (first buffer line in window)
-// - w_topfill (filler lines above the first line)
-// - w_leftcol (leftmost window cell in window),
-// - w_skipcol (skipped window cells of first line)
-//
-// Commands that only move the cursor around in a window, do not need to take
-// action to update the display. The main loop will check if w_topline is
-// valid and update it (scroll the window) when needed.
-//
-// Commands that scroll a window change w_topline and must call
-// check_cursor() to move the cursor into the visible part of the window, and
-// call redraw_later(wp, VALID) to have the window displayed by update_screen()
-// later.
-//
-// Commands that change text in the buffer must call changed_bytes() or
-// changed_lines() to mark the area that changed and will require updating
-// later. The main loop will call update_screen(), which will update each
-// window that shows the changed buffer. This assumes text above the change
-// can remain displayed as it is. Text after the change may need updating for
-// scrolling, folding and syntax highlighting.
-//
-// Commands that change how a window is displayed (e.g., setting 'list') or
-// invalidate the contents of a window in another way (e.g., change fold
-// settings), must call redraw_later(wp, NOT_VALID) to have the whole window
-// redisplayed by update_screen() later.
-//
-// Commands that change how a buffer is displayed (e.g., setting 'tabstop')
-// must call redraw_curbuf_later(NOT_VALID) to have all the windows for the
-// buffer redisplayed by update_screen() later.
-//
-// Commands that change highlighting and possibly cause a scroll too must call
-// redraw_later(wp, SOME_VALID) to update the whole window but still use
-// scrolling to avoid redrawing everything. But the length of displayed lines
-// must not change, use NOT_VALID then.
-//
-// Commands that move the window position must call redraw_later(wp, NOT_VALID).
-// TODO(neovim): should minimize redrawing by scrolling when possible.
-//
-// Commands that change everything (e.g., resizing the screen) must call
-// redraw_all_later(NOT_VALID) or redraw_all_later(CLEAR).
-//
-// Things that are handled indirectly:
-// - When messages scroll the screen up, msg_scrolled will be set and
-// update_screen() called to redraw.
-///
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
-#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"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#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"
-#include "nvim/ex_cmds.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
-#include "nvim/grid_defs.h"
+#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
-#include "nvim/indent.h"
-#include "nvim/insexpand.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"
#include "nvim/menu.h"
-#include "nvim/message.h"
#include "nvim/move.h"
-#include "nvim/normal.h"
#include "nvim/option.h"
-#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
-#include "nvim/path.h"
-#include "nvim/plines.h"
-#include "nvim/popupmnu.h"
-#include "nvim/quickfix.h"
+#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/search.h"
-#include "nvim/sign.h"
-#include "nvim/spell.h"
#include "nvim/state.h"
-#include "nvim/strings.h"
-#include "nvim/syntax.h"
-#include "nvim/terminal.h"
-#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
#include "nvim/undo.h"
-#include "nvim/version.h"
-#include "nvim/vim.h"
#include "nvim/window.h"
-#define MB_FILLER_CHAR '<' /* character used when a double-width character
- * doesn't fit. */
-
-static match_T search_hl; // used for 'hlsearch' highlight matching
-
-// for line_putchar. Contains the state that needs to be remembered from
-// putting one character to the next.
-typedef struct {
- const char *p;
- int prev_c; // previous Arabic character
- int prev_c1; // first composing char for prev_c
-} LineState;
-#define LINE_STATE(p) { p, 0, 0 }
-
-/// Whether to call "ui_call_grid_resize" in win_grid_alloc
-static bool send_grid_resize = false;
-
-static bool conceal_cursor_used = false;
-
-static bool redraw_popupmenu = false;
-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
-static char *provider_err = NULL;
-
-/// Redraw a window later, with update_screen(type).
-///
-/// Set must_redraw only if not already set to a higher value.
-/// e.g. if must_redraw is CLEAR, type NOT_VALID will do nothing.
-void redraw_later(win_T *wp, int type)
- FUNC_ATTR_NONNULL_ALL
-{
- if (!exiting && wp->w_redr_type < type) {
- wp->w_redr_type = type;
- if (type >= NOT_VALID) {
- wp->w_lines_valid = 0;
- }
- if (must_redraw < type) { // must_redraw is the maximum of all windows
- must_redraw = type;
- }
- }
-}
-
-/*
- * Mark all windows to be redrawn later.
- */
-void redraw_all_later(int type)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- redraw_later(wp, type);
- }
- // This may be needed when switching tabs.
- if (must_redraw < type) {
- must_redraw = type;
- }
-}
-
-void screen_invalidate_highlights(void)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- redraw_later(wp, NOT_VALID);
- wp->w_grid_alloc.valid = false;
- }
-}
-
-/*
- * Mark all windows that are editing the current buffer to be updated later.
- */
-void redraw_curbuf_later(int type)
-{
- redraw_buf_later(curbuf, type);
-}
-
-void redraw_buf_later(buf_T *buf, int type)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf) {
- redraw_later(wp, type);
- }
- }
-}
-
-void redraw_buf_line_later(buf_T *buf, linenr_T line)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf
- && line >= wp->w_topline && line < wp->w_botline) {
- redrawWinline(wp, line);
- }
- }
-}
-
-void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf
- && lastline >= wp->w_topline && firstline < wp->w_botline) {
- if (wp->w_redraw_top == 0 || wp->w_redraw_top > firstline) {
- wp->w_redraw_top = firstline;
- }
- if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lastline) {
- wp->w_redraw_bot = lastline;
- }
- redraw_later(wp, VALID);
- }
- }
-}
-
-/*
- * Changed something in the current window, at buffer line "lnum", that
- * requires that line and possibly other lines to be redrawn.
- * Used when entering/leaving Insert mode with the cursor on a folded line.
- * Used to remove the "$" from a change command.
- * Note that when also inserting/deleting lines w_redraw_top and w_redraw_bot
- * may become invalid and the whole window will have to be redrawn.
- */
-void redrawWinline(win_T *wp, linenr_T lnum)
- FUNC_ATTR_NONNULL_ALL
-{
- if (lnum >= wp->w_topline
- && lnum < wp->w_botline) {
- if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) {
- wp->w_redraw_top = lnum;
- }
- if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) {
- wp->w_redraw_bot = lnum;
- }
- redraw_later(wp, VALID);
- }
-}
-
-/// 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 || (wp == curwin && global_stl_height())
- || wp->w_winbar_height)) {
- wp->w_redr_status = true;
- if (must_redraw < VALID) {
- must_redraw = VALID;
- }
- }
- }
-}
-
-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
-/// and redraw_all_later() to mark parts of the screen as needing a redraw.
-///
-/// @param type set to a NOT_VALID to force redraw of entire screen
-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,
- // which would bypass the checks in screen_resize for popupmenu etc.
- if (!default_grid.chars || resizing) {
- return FAIL;
- }
-
- // May have postponed updating diffs.
- if (need_diff_redraw) {
- diff_redraw(true);
- }
-
- if (must_redraw) {
- if (type < must_redraw) { // use maximal 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, or a decoration provider requires a redraw, the screen
- // will be redrawn later or in win_update().
- must_redraw = 0;
- }
-
- // Need to update w_lines[].
- if (curwin->w_lines_valid == 0 && type < NOT_VALID) {
- type = NOT_VALID;
- }
-
- /* Postpone the redrawing when it's not needed and when being called
- * recursively. */
- if (!redrawing() || updating_screen) {
- must_redraw = type;
- if (type > INVERTED_ALL) {
- curwin->w_lines_valid = 0; // don't use w_lines[].wl_size now
- }
- return FAIL;
- }
- updating_screen = 1;
-
- display_tick++; // let syntax code know we're in a next round of
- // display updating
-
- // Tricky: vim code can reset msg_scrolled behind our back, so need
- // separate bookkeeping for now.
- if (msg_did_scroll) {
- msg_did_scroll = false;
- msg_scrolled_at_flush = 0;
- }
-
- if (type >= CLEAR || !default_grid.valid) {
- ui_comp_set_screen_valid(false);
- }
-
- // if the screen was scrolled up when displaying a message, scroll it down
- if (msg_scrolled || msg_grid_invalid) {
- clear_cmdline = true;
- 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++) {
- grid_clear_line(&msg_grid, msg_grid.line_offset[i],
- msg_grid.cols, false);
- }
- }
- if (msg_use_msgsep()) {
- msg_grid.throttled = false;
- // 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++) {
- grid_clear_line(&default_grid, default_grid.line_offset[i],
- Columns, false);
- }
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_floating) {
- continue;
- }
- if (W_ENDROW(wp) > valid) {
- wp->w_redr_type = MAX(wp->w_redr_type, NOT_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 - (int)p_ch, false);
- msg_grid_invalid = false;
- } else if (msg_scrolled > Rows - 5) { // clearing is faster
- type = CLEAR;
- } else if (type != CLEAR) {
- check_for_delay(false);
- grid_ins_lines(&default_grid, 0, msg_scrolled, Rows, 0, Columns);
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_floating) {
- continue;
- }
- if (wp->w_winrow < msg_scrolled) {
- if (W_ENDROW(wp) > msg_scrolled
- && wp->w_redr_type < REDRAW_TOP
- && wp->w_lines_valid > 0
- && wp->w_topline == wp->w_lines[0].wl_lnum) {
- wp->w_upd_rows = msg_scrolled - wp->w_winrow;
- wp->w_redr_type = REDRAW_TOP;
- } else {
- wp->w_redr_type = NOT_VALID;
- 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;
- }
- msg_scrolled = 0;
- msg_scrolled_at_flush = 0;
- need_wait_return = false;
- }
-
- win_ui_flush();
- msg_ext_check_clear();
-
- // reset cmdline_row now (may have been changed temporarily)
- compute_cmdrow();
-
- bool hl_changed = false;
- // Check for changed highlighting
- if (need_highlight_changed) {
- highlight_changed();
- hl_changed = true;
- }
-
- if (type == CLEAR) { // first clear screen
- screenclear(); // will reset clear_cmdline
- cmdline_screen_cleared(); // clear external cmdline state
- type = NOT_VALID;
- // must_redraw may be set indirectly, avoid another redraw later
- must_redraw = 0;
- } else if (!default_grid.valid) {
- grid_invalidate(&default_grid);
- default_grid.valid = true;
- }
-
- // 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 - (int)p_ch, Rows, 0, Columns, ' ', ' ', 0);
- }
-
- ui_comp_set_screen_valid(true);
-
- DecorProviders providers;
- decor_providers_start(&providers, type, &provider_err);
-
- // "start" callback could have changed highlights for global elements
- if (win_check_ns_hl(NULL)) {
- redraw_cmdline = true;
- redraw_tabline = true;
- }
-
- if (clear_cmdline) { // going to clear cmdline (done below)
- check_for_delay(false);
- }
-
- /* Force redraw when width of 'number' or 'relativenumber' column
- * changes. */
- if (curwin->w_redr_type < NOT_VALID
- && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu)
- ? number_width(curwin) : 0)) {
- curwin->w_redr_type = NOT_VALID;
- }
-
- /*
- * Only start redrawing if there is really something to do.
- */
- if (type == INVERTED) {
- update_curswant();
- }
- if (curwin->w_redr_type < type
- && !((type == VALID
- && curwin->w_lines[0].wl_valid
- && curwin->w_topfill == curwin->w_old_topfill
- && curwin->w_botfill == curwin->w_old_botfill
- && curwin->w_topline == curwin->w_lines[0].wl_lnum)
- || (type == INVERTED
- && VIsual_active
- && curwin->w_old_cursor_lnum == curwin->w_cursor.lnum
- && curwin->w_old_visual_mode == VIsual_mode
- && (curwin->w_valid & VALID_VIRTCOL)
- && curwin->w_old_curswant == curwin->w_curswant)
- )) {
- curwin->w_redr_type = type;
- }
-
- // Redraw the tab pages line if needed.
- if (redraw_tabline || type >= NOT_VALID) {
- update_window_hl(curwin, type >= NOT_VALID);
- FOR_ALL_TABS(tp) {
- if (tp != curtab) {
- update_window_hl(tp->tp_curwin, type >= NOT_VALID);
- }
- }
- 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) {
- update_window_hl(wp, type >= NOT_VALID || hl_changed);
-
- buf_T *buf = wp->w_buffer;
- if (buf->b_mod_set) {
- if (buf->b_mod_tick_syn < display_tick
- && syntax_present(wp)) {
- syn_stack_apply_changes(buf);
- buf->b_mod_tick_syn = display_tick;
- }
-
- if (buf->b_mod_tick_decor < display_tick) {
- decor_providers_invoke_buf(buf, &providers, &provider_err);
- buf->b_mod_tick_decor = display_tick;
- }
- }
- }
-
- /*
- * Go from top to bottom through the windows, redrawing the ones that need
- * it.
- */
- 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);
- wp->w_redr_type = NOT_VALID;
- }
-
- // reallocate grid if needed.
- win_grid_alloc(wp);
-
- if (wp->w_redr_border || wp->w_redr_type >= NOT_VALID) {
- win_redr_border(wp);
- }
+static char e_conflicts_with_value_of_listchars[] = N_("E834: Conflicts with value of 'listchars'");
+static char e_conflicts_with_value_of_fillchars[] = N_("E835: Conflicts with value of 'fillchars'");
- if (wp->w_redr_type != 0) {
- if (!did_one) {
- did_one = true;
- start_search_hl();
- }
- win_update(wp, &providers);
- }
-
- // 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);
- }
- }
-
- end_search_hl();
-
- // May need to redraw the popup menu.
- if (pum_drawn() && must_redraw_pum) {
- pum_redraw();
- }
-
- /* 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) {
- wp->w_buffer->b_mod_set = false;
- }
-
- updating_screen = 0;
-
- /* Clear or redraw the command line. Done last, because scrolling may
- * mess up the command line. */
- if (clear_cmdline || redraw_cmdline) {
- showmode();
- }
-
- // May put up an introductory message when not editing a file
- if (!did_intro) {
- maybe_intro_message();
- }
- did_intro = 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;
-}
-
-// Return true if the cursor line in window "wp" may be concealed, according
-// to the 'concealcursor' option.
+/// 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
{
@@ -646,20 +69,6 @@ bool conceal_cursor_line(const win_T *wp)
return vim_strchr((char *)wp->w_p_cocu, c) != 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.
-void conceal_check_cursor_line(void)
-{
- bool should_conceal = conceal_cursor_line(curwin);
- if (curwin->w_p_cole > 0 && (conceal_cursor_used != should_conceal)) {
- redrawWinline(curwin, curwin->w_cursor.lnum);
- // Need to recompute cursor column, e.g., when starting Visual mode
- // without concealing.
- curs_columns(curwin, true);
- }
-}
-
/// 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.
@@ -669,1081 +78,6 @@ bool win_cursorline_standout(const win_T *wp)
return wp->w_p_cul || (wp->w_p_cole > 0 && !conceal_cursor_line(wp));
}
-/*
- * Update a single window.
- *
- * This may cause the windows below it also to be redrawn (when clearing the
- * screen or scrolling lines).
- *
- * How the window is redrawn depends on wp->w_redr_type. Each type also
- * implies the one below it.
- * NOT_VALID redraw the whole window
- * SOME_VALID redraw the whole window but do scroll when possible
- * REDRAW_TOP redraw the top w_upd_rows window lines, otherwise like VALID
- * INVERTED redraw the changed part of the Visual area
- * INVERTED_ALL redraw the whole Visual area
- * VALID 1. scroll up/down to adjust for a changed w_topline
- * 2. update lines at the top when scrolled down
- * 3. redraw changed text:
- * - if wp->w_buffer->b_mod_set set, update lines between
- * b_mod_top and b_mod_bot.
- * - if wp->w_redraw_top non-zero, redraw lines between
- * wp->w_redraw_top and wp->w_redr_bot.
- * - continue redrawing when syntax status is invalid.
- * 4. if scrolled up, update lines at the bottom.
- * This results in three areas that may need updating:
- * top: from first row to top_end (when scrolled down)
- * 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, 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
- updating. 0 when no top area updating. */
- int mid_start = 999; /* first row of the mid area that needs
- updating. 999 when no mid area updating. */
- int mid_end = 0; /* Below last row of the mid area that needs
- updating. 0 when no mid area updating. */
- int bot_start = 999; /* first row of the bot area that needs
- updating. 999 when no bot area updating */
- bool scrolled_down = false; // true when scrolled down when w_topline got smaller a bit
- bool top_to_mod = false; // redraw above mod_top
-
- int row; // current window row to display
- linenr_T lnum; // current buffer lnum to display
- int idx; // current index in w_lines[]
- int srow; // starting row of the current line
-
- bool eof = false; // if true, we hit the end of the file
- bool didline = false; // if true, we finished the last line
- int i;
- long j;
- static bool recursive = false; // being called recursively
- const linenr_T old_botline = wp->w_botline;
- // Remember what happened to the previous line.
-#define DID_NONE 1 // didn't update a line
-#define DID_LINE 2 // updated a normal line
-#define DID_FOLD 3 // updated a folded line
- int did_update = DID_NONE;
- linenr_T syntax_last_parsed = 0; // last parsed text line
- linenr_T mod_top = 0;
- linenr_T mod_bot = 0;
- int save_got_int;
-
- type = wp->w_redr_type;
-
- if (type >= NOT_VALID) {
- wp->w_redr_status = true;
- wp->w_lines_valid = 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.cols == 0) {
- // draw the vertical separator right of this window
- draw_vsep_win(wp);
- draw_sep_connectors_win(wp);
- wp->w_redr_type = 0;
- return;
- }
-
- redraw_win_signcol(wp);
-
- init_search_hl(wp, &search_hl);
-
- /* Force redraw when width of 'number' or 'relativenumber' column
- * changes. */
- i = (wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) : 0;
- if (wp->w_nrwidth != i) {
- type = NOT_VALID;
- wp->w_nrwidth = i;
-
- if (buf->terminal) {
- terminal_check_size(buf->terminal);
- }
- } else if (buf->b_mod_set
- && buf->b_mod_xlines != 0
- && wp->w_redraw_top != 0) {
- // When there are both inserted/deleted lines and specific lines to be
- // redrawn, w_redraw_top and w_redraw_bot may be invalid, just redraw
- // everything (only happens when redrawing is off for while).
- type = NOT_VALID;
- } else {
- /*
- * Set mod_top to the first line that needs displaying because of
- * changes. Set mod_bot to the first line after the changes.
- */
- mod_top = wp->w_redraw_top;
- if (wp->w_redraw_bot != 0) {
- mod_bot = wp->w_redraw_bot + 1;
- } else {
- mod_bot = 0;
- }
- if (buf->b_mod_set) {
- if (mod_top == 0 || mod_top > buf->b_mod_top) {
- mod_top = buf->b_mod_top;
- /* Need to redraw lines above the change that may be included
- * in a pattern match. */
- if (syntax_present(wp)) {
- mod_top -= buf->b_s.b_syn_sync_linebreaks;
- if (mod_top < 1) {
- mod_top = 1;
- }
- }
- }
- if (mod_bot == 0 || mod_bot < buf->b_mod_bot) {
- mod_bot = buf->b_mod_bot;
- }
-
- // When 'hlsearch' is on and using a multi-line search pattern, a
- // change in one line may make the Search highlighting in a
- // previous line invalid. Simple solution: redraw all visible
- // lines above the change.
- // Same for a match pattern.
- if (search_hl.rm.regprog != NULL
- && re_multiline(search_hl.rm.regprog)) {
- top_to_mod = true;
- } else {
- const matchitem_T *cur = wp->w_match_head;
- while (cur != NULL) {
- if (cur->match.regprog != NULL
- && re_multiline(cur->match.regprog)) {
- top_to_mod = true;
- break;
- }
- cur = cur->next;
- }
- }
- }
- 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
- * line of that fold. If the line is folded now, get the first
- * folded line. Use the minimum of these two.
- */
-
- /* Find last valid w_lines[] entry above mod_top. Set lnumt to
- * 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;
- for (i = 0; i < wp->w_lines_valid; ++i) {
- if (wp->w_lines[i].wl_valid) {
- if (wp->w_lines[i].wl_lastlnum < mod_top) {
- lnumt = wp->w_lines[i].wl_lastlnum + 1;
- }
- if (lnumb == MAXLNUM && wp->w_lines[i].wl_lnum >= mod_bot) {
- lnumb = wp->w_lines[i].wl_lnum;
- // When there is a fold column it might need updating
- // in the next line ("J" just above an open fold).
- if (compute_foldcolumn(wp, 0) > 0) {
- lnumb++;
- }
- }
- }
- }
-
- (void)hasFoldingWin(wp, mod_top, &mod_top, NULL, true, NULL);
- if (mod_top > lnumt) {
- mod_top = lnumt;
- }
-
- // Now do the same for the bottom line (one above mod_bot).
- mod_bot--;
- (void)hasFoldingWin(wp, mod_bot, NULL, &mod_bot, true, NULL);
- mod_bot++;
- if (mod_bot < lnumb) {
- mod_bot = lnumb;
- }
- }
-
- /* When a change starts above w_topline and the end is below
- * w_topline, start redrawing at w_topline.
- * If the end of the change is above w_topline: do like no change was
- * made, but redraw the first line to find changes in syntax. */
- if (mod_top != 0 && mod_top < wp->w_topline) {
- if (mod_bot > wp->w_topline) {
- mod_top = wp->w_topline;
- } else if (syntax_present(wp)) {
- top_end = 1;
- }
- }
-
- /* When line numbers are displayed need to redraw all lines below
- * inserted/deleted lines. */
- if (mod_top != 0 && buf->b_mod_xlines != 0 && wp->w_p_nu) {
- mod_bot = MAXLNUM;
- }
- }
- wp->w_redraw_top = 0; // reset for next time
- wp->w_redraw_bot = 0;
-
- /*
- * When only displaying the lines at the top, set top_end. Used when
- * window has scrolled down for msg_scrolled.
- */
- if (type == REDRAW_TOP) {
- j = 0;
- for (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;
- break;
- }
- }
- if (top_end == 0) {
- // not found (cannot happen?): redraw everything
- type = NOT_VALID;
- } else {
- // top area defined, the rest is VALID
- type = VALID;
- }
- }
-
- /*
- * If there are no changes on the screen that require a complete redraw,
- * handle three cases:
- * 1: we are off the top of the screen by a few lines: scroll down
- * 2: wp->w_topline is below wp->w_lines[0].wl_lnum: may scroll up
- * 3: wp->w_topline is wp->w_lines[0].wl_lnum: find first entry in
- * w_lines[] that needs updating.
- */
- if ((type == VALID || type == SOME_VALID
- || type == INVERTED || type == INVERTED_ALL)
- && !wp->w_botfill && !wp->w_old_botfill) {
- if (mod_top != 0
- && wp->w_topline == mod_top
- && (!wp->w_lines[0].wl_valid
- || 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
- && (wp->w_topline < wp->w_lines[0].wl_lnum
- || (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.
- */
- if (hasAnyFolding(wp)) {
- linenr_T ln;
-
- /* count the number of lines we are off, counting a sequence
- * of folded lines as one */
- j = 0;
- for (ln = wp->w_topline; ln < wp->w_lines[0].wl_lnum; ln++) {
- j++;
- if (j >= wp->w_grid.rows - 2) {
- break;
- }
- (void)hasFoldingWin(wp, ln, NULL, &ln, true, NULL);
- }
- } else {
- j = wp->w_lines[0].wl_lnum - wp->w_topline;
- }
- 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
- // 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.
- win_scroll_lines(wp, 0, i);
- if (wp->w_lines_valid != 0) {
- // Need to update rows that are new, stop at the
- // first one that scrolled down.
- top_end = i;
- scrolled_down = true;
-
- // Move the entries that were scrolled, disable
- // the entries for the lines to be redrawn.
- if ((wp->w_lines_valid += (linenr_T)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];
- }
- while (idx >= 0) {
- wp->w_lines[idx--].wl_valid = false;
- }
- }
- } else {
- mid_start = 0; // redraw all lines
- }
- } else {
- mid_start = 0; // redraw all lines
- }
- } else {
- /*
- * New topline is at or below old topline: May scroll up.
- * When topline didn't change, find first entry in w_lines[] that
- * needs updating.
- */
-
- // try to find wp->w_topline in wp->w_lines[].wl_lnum
- j = -1;
- row = 0;
- for (i = 0; i < wp->w_lines_valid; i++) {
- if (wp->w_lines[i].wl_valid
- && wp->w_lines[i].wl_lnum == wp->w_topline) {
- j = i;
- break;
- }
- row += wp->w_lines[i].wl_size;
- }
- if (j == -1) {