aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/screen.c
diff options
context:
space:
mode:
authorEliseo Martínez <eliseomarmol@gmail.com>2014-05-12 02:25:17 +0200
committerEliseo Martínez <eliseomarmol@gmail.com>2014-05-15 20:46:01 +0200
commitda51dc9cf202772f60bd2da975dbef257bd9237c (patch)
tree5c16b93238a153f55634e9323077f30c8133970c /src/nvim/screen.c
parentffe61e5ba1721340ca51d56bae3ddaca415fb5bc (diff)
downloadrneovim-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.c8008
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,