diff options
| author | Eliseo Martínez <eliseomarmol@gmail.com> | 2014-05-12 02:25:17 +0200 |
|---|---|---|
| committer | Eliseo Martínez <eliseomarmol@gmail.com> | 2014-05-15 20:46:01 +0200 |
| commit | da51dc9cf202772f60bd2da975dbef257bd9237c (patch) | |
| tree | 5c16b93238a153f55634e9323077f30c8133970c /src/nvim/screen.c | |
| parent | ffe61e5ba1721340ca51d56bae3ddaca415fb5bc (diff) | |
| download | rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.tar.gz rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.tar.bz2 rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.zip | |
Introduce nvim namespace: Move files.
Move files from src/ to src/nvim/.
- src/nvim/ becomes the new root dir for nvim executable sources.
- src/libnvim/ is planned to become root dir of the neovim library.
Diffstat (limited to 'src/nvim/screen.c')
| -rw-r--r-- | src/nvim/screen.c | 8008 |
1 files changed, 8008 insertions, 0 deletions
diff --git a/src/nvim/screen.c b/src/nvim/screen.c new file mode 100644 index 0000000000..aa021c032e --- /dev/null +++ b/src/nvim/screen.c @@ -0,0 +1,8008 @@ +/* + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * screen.c: code for displaying on the screen + * + * 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. + * + * ScreenLines[off] Contains a copy of the whole screen, as it is currently + * displayed (excluding text written by external commands). + * ScreenAttrs[off] Contains the associated attributes. + * LineOffset[row] Contains the offset into ScreenLines*[] and ScreenAttrs[] + * for each line. + * LineWraps[row] Flag for each line whether it wraps to the next line. + * + * For double-byte characters, two consecutive bytes in ScreenLines[] can form + * one character which occupies two display cells. + * For UTF-8 a multi-byte character is converted to Unicode and stored in + * ScreenLinesUC[]. ScreenLines[] contains the first byte only. For an ASCII + * character without composing chars ScreenLinesUC[] will be 0 and + * ScreenLinesC[][] is not used. When the character occupies two display + * cells the next byte in ScreenLines[] is 0. + * ScreenLinesC[][] contain up to 'maxcombine' composing characters + * (drawn on top of the first character). There is 0 after the last one used. + * ScreenLines2[] is only used for euc-jp to store the second byte if the + * first byte is 0x8e (single-width character). + * + * The screen_*() functions write to the screen and handle updating + * ScreenLines[]. + * + * update_screen() is the function that updates all windows and status lines. + * It is called form 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 line 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(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(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(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(NOT_VALID). + * TODO: 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 <string.h> + +#include "vim.h" +#include "arabic.h" +#include "screen.h" +#include "buffer.h" +#include "charset.h" +#include "diff.h" +#include "eval.h" +#include "ex_cmds.h" +#include "ex_cmds2.h" +#include "ex_getln.h" +#include "farsi.h" +#include "fileio.h" +#include "fold.h" +#include "getchar.h" +#include "main.h" +#include "mbyte.h" +#include "memline.h" +#include "memory.h" +#include "menu.h" +#include "message.h" +#include "misc1.h" +#include "misc2.h" +#include "garray.h" +#include "move.h" +#include "normal.h" +#include "option.h" +#include "path.h" +#include "popupmnu.h" +#include "quickfix.h" +#include "regexp.h" +#include "search.h" +#include "spell.h" +#include "syntax.h" +#include "term.h" +#include "ui.h" +#include "undo.h" +#include "version.h" +#include "window.h" + +#define MB_FILLER_CHAR '<' /* character used when a double-width character + * doesn't fit. */ + +/* + * The attributes that are actually active for writing to the screen. + */ +static int screen_attr = 0; + +/* + * Positioning the cursor is reduced by remembering the last position. + * Mostly used by windgoto() and screen_char(). + */ +static int screen_cur_row, screen_cur_col; /* last known cursor position */ + +static match_T search_hl; /* used for 'hlsearch' highlight matching */ + +static foldinfo_T win_foldinfo; /* info for 'foldcolumn' */ + +/* + * Buffer for one screen line (characters and attributes). + */ +static schar_T *current_ScreenLine; + +static void win_update(win_T *wp); +static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, + hlf_T hl); +static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, + linenr_T lnum, + int row); +static void fill_foldcolumn(char_u *p, win_T *wp, int closed, + linenr_T lnum); +static void copy_text_attr(int off, char_u *buf, int len, int attr); +static int win_line(win_T *, linenr_T, int, int, int nochange); +static int char_needs_redraw(int off_from, int off_to, int cols); +static void screen_line(int row, int coloff, int endcol, + int clear_width, + int rlflag); +# define SCREEN_LINE(r, o, e, c, rl) screen_line((r), (o), (e), (c), (rl)) +static void draw_vsep_win(win_T *wp, int row); +static void redraw_custom_statusline(win_T *wp); +#define SEARCH_HL_PRIORITY 0 +static void start_search_hl(void); +static void end_search_hl(void); +static void init_search_hl(win_T *wp); +static void prepare_search_hl(win_T *wp, linenr_T lnum); +static void next_search_hl(win_T *win, match_T *shl, linenr_T lnum, + colnr_T mincol); +static void screen_start_highlight(int attr); +static void screen_char(unsigned off, int row, int col); +static void screen_char_2(unsigned off, int row, int col); +static void screenclear2(void); +static void lineclear(unsigned off, int width); +static void lineinvalid(unsigned off, int width); +static void linecopy(int to, int from, win_T *wp); +static void redraw_block(int row, int end, win_T *wp); +static int win_do_lines(win_T *wp, int row, int line_count, + int mayclear, + int del); +static void win_rest_invalid(win_T *wp); +static void msg_pos_mode(void); +static void draw_tabline(void); +static int fillchar_status(int *attr, int is_curwin); +static int fillchar_vsep(int *attr); +static void win_redr_custom(win_T *wp, int draw_ruler); +static void win_redr_ruler(win_T *wp, int always); + +//signs column +static void update_prepare(void); +static void update_finish(void); +static int draw_signcolumn (win_T *wp); + +/* Ugly global: overrule attribute used by screen_char() */ +static int screen_char_attr = 0; + +/* + * Redraw the current 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(int type) +{ + redraw_win_later(curwin, type); +} + +void redraw_win_later(win_T *wp, int type) +{ + if (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; + } +} + +/* + * Force a complete redraw later. Also resets the highlighting. To be used + * after executing a shell command that messes up the screen. + */ +void redraw_later_clear(void) +{ + redraw_all_later(CLEAR); + /* Use attributes that is very unlikely to appear in text. */ + screen_attr = HL_BOLD | HL_UNDERLINE | HL_INVERSE; +} + +/* + * Mark all windows to be redrawn later. + */ +void redraw_all_later(int type) +{ + win_T *wp; + + FOR_ALL_WINDOWS(wp) + { + redraw_win_later(wp, type); + } +} + +/* + * 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) +{ + win_T *wp; + + FOR_ALL_WINDOWS(wp) + { + if (wp->w_buffer == buf) + redraw_win_later(wp, type); + } +} + +/* + * Redraw as soon as possible. When the command line is not scrolled redraw + * right away and restore what was on the command line. + * Return a code indicating what happened. + */ +int redraw_asap(int type) +{ + int rows; + int r; + int ret = 0; + schar_T *screenline; /* copy from ScreenLines[] */ + sattr_T *screenattr; /* copy from ScreenAttrs[] */ + int i; + u8char_T *screenlineUC = NULL; /* copy from ScreenLinesUC[] */ + u8char_T *screenlineC[MAX_MCO]; /* copy from ScreenLinesC[][] */ + schar_T *screenline2 = NULL; /* copy from ScreenLines2[] */ + + redraw_later(type); + if (msg_scrolled || (State != NORMAL && State != NORMAL_BUSY)) + return ret; + + /* Allocate space to save the text displayed in the command line area. */ + rows = Rows - cmdline_row; + screenline = xmalloc((size_t)(rows * Columns * sizeof(schar_T))); + screenattr = xmalloc((size_t)(rows * Columns * sizeof(sattr_T))); + + if (enc_utf8) { + screenlineUC = xmalloc((size_t)(rows * Columns * sizeof(u8char_T))); + + for (i = 0; i < p_mco; ++i) { + screenlineC[i] = xmalloc((size_t)(rows * Columns * sizeof(u8char_T))); + } + } + if (enc_dbcs == DBCS_JPNU) { + screenline2 = xmalloc((size_t)(rows * Columns * sizeof(schar_T))); + } + + /* Save the text displayed in the command line area. */ + for (r = 0; r < rows; ++r) { + memmove(screenline + r * Columns, + ScreenLines + LineOffset[cmdline_row + r], + (size_t)Columns * sizeof(schar_T)); + memmove(screenattr + r * Columns, + ScreenAttrs + LineOffset[cmdline_row + r], + (size_t)Columns * sizeof(sattr_T)); + if (enc_utf8) { + memmove(screenlineUC + r * Columns, + ScreenLinesUC + LineOffset[cmdline_row + r], + (size_t)Columns * sizeof(u8char_T)); + for (i = 0; i < p_mco; ++i) + memmove(screenlineC[i] + r * Columns, + ScreenLinesC[r] + LineOffset[cmdline_row + r], + (size_t)Columns * sizeof(u8char_T)); + } + if (enc_dbcs == DBCS_JPNU) + memmove(screenline2 + r * Columns, + ScreenLines2 + LineOffset[cmdline_row + r], + (size_t)Columns * sizeof(schar_T)); + } + + update_screen(0); + ret = 3; + + if (must_redraw == 0) { + int off = (int)(current_ScreenLine - ScreenLines); + + /* Restore the text displayed in the command line area. */ + for (r = 0; r < rows; ++r) { + memmove(current_ScreenLine, + screenline + r * Columns, + (size_t)Columns * sizeof(schar_T)); + memmove(ScreenAttrs + off, + screenattr + r * Columns, + (size_t)Columns * sizeof(sattr_T)); + if (enc_utf8) { + memmove(ScreenLinesUC + off, + screenlineUC + r * Columns, + (size_t)Columns * sizeof(u8char_T)); + for (i = 0; i < p_mco; ++i) + memmove(ScreenLinesC[i] + off, + screenlineC[i] + r * Columns, + (size_t)Columns * sizeof(u8char_T)); + } + if (enc_dbcs == DBCS_JPNU) + memmove(ScreenLines2 + off, + screenline2 + r * Columns, + (size_t)Columns * sizeof(schar_T)); + SCREEN_LINE(cmdline_row + r, 0, Columns, Columns, FALSE); + } + ret = 4; + } + + free(screenline); + free(screenattr); + if (enc_utf8) { + free(screenlineUC); + for (i = 0; i < p_mco; ++i) + free(screenlineC[i]); + } + if (enc_dbcs == DBCS_JPNU) + free(screenline2); + + /* Show the intro message when appropriate. */ + maybe_intro_message(); + + setcursor(); + + return ret; +} + +/* + * 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 ( + linenr_T lnum, + int invalid /* window line height is invalid now */ +) +{ + int i; + + if (curwin->w_redraw_top == 0 || curwin->w_redraw_top > lnum) + curwin->w_redraw_top = lnum; + if (curwin->w_redraw_bot == 0 || curwin->w_redraw_bot < lnum) + curwin->w_redraw_bot = lnum; + redraw_later(VALID); + + if (invalid) { + /* A w_lines[] entry for this lnum has become invalid. */ + i = find_wl_entry(curwin, lnum); + if (i >= 0) + curwin->w_lines[i].wl_valid = FALSE; + } +} + +/* + * update all windows that are editing the current buffer + */ +void update_curbuf(int type) +{ + redraw_curbuf_later(type); + update_screen(type); +} + +/* + * update_screen() + * + * Based on the current value of curwin->w_topline, transfer a screenfull + * of stuff from Filemem to ScreenLines[], and update curwin->w_botline. + */ +void update_screen(int type) +{ + win_T *wp; + static int did_intro = FALSE; + int did_one; + + /* Don't do anything if the screen structures are (not yet) valid. */ + if (!screen_valid(TRUE)) + return; + + 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, 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) { + redraw_later(type); /* remember type for next time */ + must_redraw = type; + if (type > INVERTED_ALL) + curwin->w_lines_valid = 0; /* don't use w_lines[].wl_size now */ + return; + } + + updating_screen = TRUE; + ++display_tick; /* let syntax code know we're in a next round of + * display updating */ + + /* + * if the screen was scrolled up when displaying a message, scroll it down + */ + if (msg_scrolled) { + clear_cmdline = TRUE; + if (msg_scrolled > Rows - 5) /* clearing is faster */ + type = CLEAR; + else if (type != CLEAR) { + check_for_delay(FALSE); + if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows, NULL) == FAIL) + type = CLEAR; + FOR_ALL_WINDOWS(wp) + { + if (W_WINROW(wp) < msg_scrolled) { + if (W_WINROW(wp) + wp->w_height > 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 - W_WINROW(wp); + wp->w_redr_type = REDRAW_TOP; + } else { + wp->w_redr_type = NOT_VALID; + if (W_WINROW(wp) + wp->w_height + W_STATUS_HEIGHT(wp) + <= msg_scrolled) + wp->w_redr_status = TRUE; + } + } + } + redraw_cmdline = TRUE; + redraw_tabline = TRUE; + } + msg_scrolled = 0; + need_wait_return = FALSE; + } + + /* reset cmdline_row now (may have been changed temporarily) */ + compute_cmdrow(); + + /* Check for changed highlighting */ + if (need_highlight_changed) + highlight_changed(); + + if (type == CLEAR) { /* first clear screen */ + screenclear(); /* will reset clear_cmdline */ + type = NOT_VALID; + } + + 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) + draw_tabline(); + + /* + * Correct stored syntax highlighting info for changes in each displayed + * buffer. Each buffer must only be done once. + */ + FOR_ALL_WINDOWS(wp) + { + if (wp->w_buffer->b_mod_set) { + win_T *wwp; + + /* Check if we already did this buffer. */ + for (wwp = firstwin; wwp != wp; wwp = wwp->w_next) + if (wwp->w_buffer == wp->w_buffer) + break; + if ( + wwp == wp && + syntax_present(wp)) + syn_stack_apply_changes(wp->w_buffer); + } + } + + /* + * Go from top to bottom through the windows, redrawing the ones that need + * it. + */ + did_one = FALSE; + search_hl.rm.regprog = NULL; + FOR_ALL_WINDOWS(wp) + { + if (wp->w_redr_type != 0) { + cursor_off(); + if (!did_one) { + did_one = TRUE; + start_search_hl(); + } + win_update(wp); + } + + /* redraw status line after the window to minimize cursor movement */ + if (wp->w_redr_status) { + cursor_off(); + win_redr_status(wp); + } + } + end_search_hl(); + /* May need to redraw the popup menu. */ + if (pum_visible()) + 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 (wp = firstwin; wp != NULL; wp = wp->w_next) + wp->w_buffer->b_mod_set = FALSE; + + updating_screen = FALSE; + + /* 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; + +} + +/* + * Return TRUE if the cursor line in window "wp" may be concealed, according + * to the 'concealcursor' option. + */ +int conceal_cursor_line(win_T *wp) +{ + int c; + + if (*wp->w_p_cocu == NUL) + return FALSE; + if (get_real_state() & VISUAL) + c = 'v'; + else if (State & INSERT) + c = 'i'; + else if (State & NORMAL) + c = 'n'; + else if (State & CMDLINE) + c = 'c'; + else + return FALSE; + return vim_strchr(wp->w_p_cocu, c) != NULL; +} + +/* + * Check if the cursor line needs to be redrawn because of 'concealcursor'. + */ +void conceal_check_cursur_line(void) +{ + if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin)) { + need_cursor_line_redraw = TRUE; + /* Need to recompute cursor column, e.g., when starting Visual mode + * without concealing. */ + curs_columns(TRUE); + } +} + +void update_single_line(win_T *wp, linenr_T lnum) +{ + int row; + int j; + + if (lnum >= wp->w_topline && lnum < wp->w_botline + && foldedCount(wp, lnum, &win_foldinfo) == 0) { + row = 0; + for (j = 0; j < wp->w_lines_valid; ++j) { + if (lnum == wp->w_lines[j].wl_lnum) { + screen_start(); /* not sure of screen cursor */ + init_search_hl(wp); + start_search_hl(); + prepare_search_hl(wp, lnum); + win_line(wp, lnum, row, row + wp->w_lines[j].wl_size, FALSE); + end_search_hl(); + break; + } + row += wp->w_lines[j].wl_size; + } + } + need_cursor_line_redraw = FALSE; +} + + +/* + * Prepare for updating one or more windows. + * Caller must check for "updating_screen" already set to avoid recursiveness. + */ +static void update_prepare() +{ + cursor_off(); + updating_screen = TRUE; + start_search_hl(); +} + +/* + * Finish updating one or more windows. + */ +static void update_finish() +{ + if (redraw_cmdline) { + showmode(); + } + + end_search_hl(); + updating_screen = FALSE; +} + +void update_debug_sign(buf_T *buf, linenr_T lnum) +{ + win_T *wp; + int doit = FALSE; + win_foldinfo.fi_level = 0; + + /* update/delete a specific mark */ + FOR_ALL_WINDOWS(wp) + { + if (buf != NULL && lnum > 0) { + if (wp->w_buffer == buf && 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_win_later(wp, VALID); + } + } else { + redraw_win_later(wp, VALID); + } + if (wp->w_redr_type != 0) { + doit = TRUE; + } + } + + /* Return when there is nothing to do, screen updating is already + * happening (recursive call) or still starting up. */ + if (!doit || updating_screen || starting) { + return; + } + + /* update all windows that need updating */ + update_prepare(); + + for (wp = firstwin; wp; wp = wp->w_next) { + if (wp->w_redr_type != 0) { + win_update(wp); + } + if (wp->w_redr_status) { + win_redr_status(wp); + } + } + + update_finish(); +} + +/* + * Return TRUE when window "wp" has a column to draw signs in. + */ +static int draw_signcolumn(win_T *wp) +{ + return (wp->w_buffer->b_signlist != NULL); +} + + +/* + * 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) +{ + 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 */ + int scrolled_down = FALSE; /* TRUE when scrolled down when + w_topline got smaller a bit */ + matchitem_T *cur; /* points to the match list */ + int 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 */ + + int eof = FALSE; /* if TRUE, we hit the end of the file */ + int didline = FALSE; /* if TRUE, we finished the last line */ + int i; + long j; + static int recursive = FALSE; /* being called recursively */ + int old_botline = wp->w_botline; + long fold_count; + /* remember what happened to the previous line, to know if + * check_visual_highlight() can be used */ +#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: nothing to draw. */ + if (wp->w_height == 0) { + wp->w_redr_type = 0; + return; + } + + /* Window is zero-width: Only need to draw the separator. */ + if (wp->w_width == 0) { + /* draw the vertical separator right of this window */ + draw_vsep_win(wp, 0); + wp->w_redr_type = 0; + return; + } + + init_search_hl(wp); + + /* 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; + } 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; + wp->w_redraw_top = 0; /* reset for next time */ + wp->w_redraw_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 { + 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 (wp->w_p_fdc > 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; + } + + /* + * 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 = 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; + } + + /* Trick: we want to avoid clearing the screen twice. screenclear() will + * set "screen_cleared" to TRUE. The special value MAYBE (which is still + * non-zero and thus not FALSE) will indicate that screenclear() was not + * called. */ + if (screen_cleared) + screen_cleared = MAYBE; + + /* + * 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) { + /* + * w_topline is the first changed line, the scrolling 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, |