diff options
| author | Josh Rahm <rahm@google.com> | 2022-08-19 12:26:08 -0600 |
|---|---|---|
| committer | Josh Rahm <rahm@google.com> | 2022-08-19 13:06:41 -0600 |
| commit | a7237662f96933efe29eed8212464571e3778cd0 (patch) | |
| tree | 27930202726b4251437c8cfa53069f65b4db90dc /src/nvim/screen.c | |
| parent | 02292344929069ea63c0bb872cc22d552d86b67f (diff) | |
| parent | b2f979b30beac67906b2dd717fcb6a34f46f5e54 (diff) | |
| download | rneovim-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.c | 5795 |
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) { |