summaryrefslogtreecommitdiffstats
path: root/src/drawscreen.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-09-19 23:06:20 +0200
committerBram Moolenaar <Bram@vim.org>2019-09-19 23:06:20 +0200
commit7528d1f6b5422750eb778dfb550cfd0b0e540964 (patch)
tree4282e05be0cf40c3e1920bdb4f2b5abe88e820ff /src/drawscreen.c
parentcd67059c0c3abf1e28aa66458abdf6f338252eb2 (diff)
patch 8.1.2057: the screen.c file is much too bigv8.1.2057
Problem: The screen.c file is much too big. Solution: Split it in three parts. (Yegappan Lakshmanan, closes #4943)
Diffstat (limited to 'src/drawscreen.c')
-rw-r--r--src/drawscreen.c3112
1 files changed, 3112 insertions, 0 deletions
diff --git a/src/drawscreen.c b/src/drawscreen.c
new file mode 100644
index 0000000000..f6776a1a18
--- /dev/null
+++ b/src/drawscreen.c
@@ -0,0 +1,3112 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * 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.
+ */
+
+/*
+ * drawscreen.c: Code for updating all the windows on the screen.
+ * This is the top level, drawline.c is the middle and screen.c the lower
+ * level.
+ *
+ * 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 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(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 "vim.h"
+
+static void win_update(win_T *wp);
+#ifdef FEAT_STL_OPT
+static void redraw_custom_statusline(win_T *wp);
+#endif
+
+/*
+ * Based on the current value of curwin->w_topline, transfer a screenfull
+ * of stuff from Filemem to ScreenLines[], and update curwin->w_botline.
+ * Return OK when the screen was updated, FAIL if it was not done.
+ */
+ int
+update_screen(int type_arg)
+{
+ int type = type_arg;
+ win_T *wp;
+ static int did_intro = FALSE;
+#if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_CLIPBOARD)
+ int did_one;
+#endif
+#ifdef FEAT_GUI
+ int did_undraw = FALSE;
+ int gui_cursor_col = 0;
+ int gui_cursor_row = 0;
+#endif
+ int no_update = FALSE;
+
+ // Don't do anything if the screen structures are (not yet) valid.
+ if (!screen_valid(TRUE))
+ return FAIL;
+
+ if (type == VALID_NO_UPDATE)
+ {
+ no_update = TRUE;
+ type = 0;
+ }
+
+#ifdef FEAT_EVAL
+ {
+ buf_T *buf;
+
+ // Before updating the screen, notify any listeners of changed text.
+ FOR_ALL_BUFFERS(buf)
+ invoke_listeners(buf);
+ }
+#endif
+
+#ifdef FEAT_DIFF
+ // May have postponed updating diffs.
+ if (need_diff_redraw)
+ diff_redraw(TRUE);
+#endif
+
+ 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;
+ }
+
+ // May need to update w_lines[].
+ if (curwin->w_lines_valid == 0 && type < NOT_VALID
+#ifdef FEAT_TERMINAL
+ && !term_do_update_window(curwin)
+#endif
+ )
+ 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 FAIL;
+ }
+ updating_screen = TRUE;
+
+#ifdef FEAT_TEXT_PROP
+ // Update popup_mask if needed. This may set w_redraw_top and w_redraw_bot
+ // in some windows.
+ may_update_popup_mask(type);
+#endif
+
+#ifdef FEAT_SYN_HL
+ ++display_tick; // let syntax code know we're in a next round of
+ // display updating
+#endif
+ if (no_update)
+ ++no_win_do_lines_ins;
+
+ // 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, 0, NULL)
+ == FAIL)
+ type = CLEAR;
+ FOR_ALL_WINDOWS(wp)
+ {
+ if (wp->w_winrow < 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 + wp->w_status_height
+ <= msg_scrolled)
+ wp->w_redr_status = TRUE;
+ }
+ }
+ }
+ if (!no_update)
+ 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;
+ // must_redraw may be set indirectly, avoid another redraw later
+ must_redraw = 0;
+ }
+
+ if (clear_cmdline) // going to clear cmdline (done below)
+ check_for_delay(FALSE);
+
+#ifdef FEAT_LINEBREAK
+ // 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;
+#endif
+
+ // 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
+#ifdef FEAT_DIFF
+ && curwin->w_topfill == curwin->w_old_topfill
+ && curwin->w_botfill == curwin->w_old_botfill
+#endif
+ && 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();
+
+#ifdef FEAT_SYN_HL
+ // 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);
+ }
+ }
+#endif
+
+ // Go from top to bottom through the windows, redrawing the ones that need
+ // it.
+#if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_CLIPBOARD)
+ did_one = FALSE;
+#endif
+#ifdef FEAT_SEARCH_EXTRA
+ screen_search_hl.rm.regprog = NULL;
+#endif
+ FOR_ALL_WINDOWS(wp)
+ {
+ if (wp->w_redr_type != 0)
+ {
+ cursor_off();
+#if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_CLIPBOARD)
+ if (!did_one)
+ {
+ did_one = TRUE;
+# ifdef FEAT_SEARCH_EXTRA
+ start_search_hl();
+# endif
+# ifdef FEAT_CLIPBOARD
+ // When Visual area changed, may have to update selection.
+ if (clip_star.available && clip_isautosel_star())
+ clip_update_selection(&clip_star);
+ if (clip_plus.available && clip_isautosel_plus())
+ clip_update_selection(&clip_plus);
+# endif
+#ifdef FEAT_GUI
+ // Remove the cursor before starting to do anything, because
+ // scrolling may make it difficult to redraw the text under
+ // it.
+ if (gui.in_use && wp == curwin)
+ {
+ gui_cursor_col = gui.cursor_col;
+ gui_cursor_row = gui.cursor_row;
+ gui_undraw_cursor();
+ did_undraw = TRUE;
+ }
+#endif
+ }
+#endif
+ win_update(wp);
+ }
+
+ // redraw status line after the window to minimize cursor movement
+ if (wp->w_redr_status)
+ {
+ cursor_off();
+ win_redr_status(wp, TRUE); // any popup menu will be redrawn below
+ }
+ }
+#if defined(FEAT_SEARCH_EXTRA)
+ end_search_hl();
+#endif
+ // May need to redraw the popup menu.
+ pum_may_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(wp)
+ wp->w_buffer->b_mod_set = FALSE;
+
+#ifdef FEAT_TEXT_PROP
+ // Display popup windows on top of the windows and command line.
+ update_popups(win_update);
+#endif
+
+ after_updating_screen(TRUE);
+
+ // Clear or redraw the command line. Done last, because scrolling may
+ // mess up the command line.
+ if (clear_cmdline || redraw_cmdline || redraw_mode)
+ showmode();
+
+ if (no_update)
+ --no_win_do_lines_ins;
+
+ // May put up an introductory message when not editing a file
+ if (!did_intro)
+ maybe_intro_message();
+ did_intro = TRUE;
+
+#ifdef FEAT_GUI
+ // Redraw the cursor and update the scrollbars when all screen updating is
+ // done.
+ if (gui.in_use)
+ {
+ if (did_undraw && !gui_mch_is_blink_off())
+ {
+ mch_disable_flush();
+ out_flush(); // required before updating the cursor
+ mch_enable_flush();
+
+ // Put the GUI position where the cursor was, gui_update_cursor()
+ // uses that.
+ gui.col = gui_cursor_col;
+ gui.row = gui_cursor_row;
+ gui.col = mb_fix_col(gui.col, gui.row);
+ gui_update_cursor(FALSE, FALSE);
+ gui_may_flush();
+ screen_cur_col = gui.col;
+ screen_cur_row = gui.row;
+ }
+ else
+ out_flush();
+ gui_update_scrollbars(FALSE);
+ }
+#endif
+ return OK;
+}
+
+/*
+ * Redraw the status line of window wp.
+ *
+ * If inversion is possible we use it. Else '=' characters are used.
+ * If "ignore_pum" is TRUE, also redraw statusline when the popup menu is
+ * displayed.
+ */
+ void
+win_redr_status(win_T *wp, int ignore_pum UNUSED)
+{
+ int row;
+ char_u *p;
+ int len;
+ int fillchar;
+ int attr;
+ int this_ru_col;
+ static int busy = FALSE;
+
+ // It's possible to get here recursively when 'statusline' (indirectly)
+ // invokes ":redrawstatus". Simply ignore the call then.
+ if (busy)
+ return;
+ busy = TRUE;
+
+ wp->w_redr_status = FALSE;
+ if (wp->w_status_height == 0)
+ {
+ // no status line, can only be last window
+ redraw_cmdline = TRUE;
+ }
+ else if (!redrawing()
+ // don't update status line when popup menu is visible and may be
+ // drawn over it, unless it will be redrawn later
+ || (!ignore_pum && pum_visible()))
+ {
+ // Don't redraw right now, do it later.
+ wp->w_redr_status = TRUE;
+ }
+#ifdef FEAT_STL_OPT
+ else if (*p_stl != NUL || *wp->w_p_stl != NUL)
+ {
+ // redraw custom status line
+ redraw_custom_statusline(wp);
+ }
+#endif
+ else
+ {
+ fillchar = fillchar_status(&attr, wp);
+
+ get_trans_bufname(wp->w_buffer);
+ p = NameBuff;
+ len = (int)STRLEN(p);
+
+ if (bt_help(wp->w_buffer)
+#ifdef FEAT_QUICKFIX
+ || wp->w_p_pvw
+#endif
+ || bufIsChanged(wp->w_buffer)
+ || wp->w_buffer->b_p_ro)
+ *(p + len++) = ' ';
+ if (bt_help(wp->w_buffer))
+ {
+ STRCPY(p + len, _("[Help]"));
+ len += (int)STRLEN(p + len);
+ }
+#ifdef FEAT_QUICKFIX
+ if (wp->w_p_pvw)
+ {
+ STRCPY(p + len, _("[Preview]"));
+ len += (int)STRLEN(p + len);
+ }
+#endif
+ if (bufIsChanged(wp->w_buffer)
+#ifdef FEAT_TERMINAL
+ && !bt_terminal(wp->w_buffer)
+#endif
+ )
+ {
+ STRCPY(p + len, "[+]");
+ len += 3;
+ }
+ if (wp->w_buffer->b_p_ro)
+ {
+ STRCPY(p + len, _("[RO]"));
+ len += (int)STRLEN(p + len);
+ }
+
+ this_ru_col = ru_col - (Columns - wp->w_width);
+ if (this_ru_col < (wp->w_width + 1) / 2)
+ this_ru_col = (wp->w_width + 1) / 2;
+ if (this_ru_col <= 1)
+ {
+ p = (char_u *)"<"; // No room for file name!
+ len = 1;
+ }
+ else if (has_mbyte)
+ {
+ int clen = 0, i;
+
+ // Count total number of display cells.
+ clen = mb_string2cells(p, -1);
+
+ // Find first character that will fit.
+ // Going from start to end is much faster for DBCS.
+ for (i = 0; p[i] != NUL && clen >= this_ru_col - 1;
+ i += (*mb_ptr2len)(p + i))
+ clen -= (*mb_ptr2cells)(p + i);
+ len = clen;
+ if (i > 0)
+ {
+ p = p + i - 1;
+ *p = '<';
+ ++len;
+ }
+
+ }
+ else if (len > this_ru_col - 1)
+ {
+ p += len - (this_ru_col - 1);
+ *p = '<';
+ len = this_ru_col - 1;
+ }
+
+ row = W_WINROW(wp) + wp->w_height;
+ screen_puts(p, row, wp->w_wincol, attr);
+ screen_fill(row, row + 1, len + wp->w_wincol,
+ this_ru_col + wp->w_wincol, fillchar, fillchar, attr);
+
+ if (get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL)
+ && (int)(this_ru_col - len) > (int)(STRLEN(NameBuff) + 1))
+ screen_puts(NameBuff, row, (int)(this_ru_col - STRLEN(NameBuff)
+ - 1 + wp->w_wincol), attr);
+
+#ifdef FEAT_CMDL_INFO
+ win_redr_ruler(wp, TRUE, ignore_pum);
+#endif
+ }
+
+ /*
+ * May need to draw the character below the vertical separator.
+ */
+ if (wp->w_vsep_width != 0 && wp->w_status_height != 0 && redrawing())
+ {
+ if (stl_connected(wp))
+ fillchar = fillchar_status(&attr, wp);
+ else
+ fillchar = fillchar_vsep(&attr);
+ screen_putchar(fillchar, W_WINROW(wp) + wp->w_height, W_ENDCOL(wp),
+ attr);
+ }
+ busy = FALSE;
+}
+
+#ifdef FEAT_STL_OPT
+/*
+ * Redraw the status line according to 'statusline' and take care of any
+ * errors encountered.
+ */
+ static void
+redraw_custom_statusline(win_T *wp)
+{
+ static int entered = FALSE;
+ int saved_did_emsg = did_emsg;
+
+ // When called recursively return. This can happen when the statusline
+ // contains an expression that triggers a redraw.
+ if (entered)
+ return;
+ entered = TRUE;
+
+ did_emsg = FALSE;
+ win_redr_custom(wp, FALSE);
+ if (did_emsg)
+ {
+ // When there is an error disable the statusline, otherwise the
+ // display is messed up with errors and a redraw triggers the problem
+ // again and again.
+ set_string_option_direct((char_u *)"statusline", -1,
+ (char_u *)"", OPT_FREE | (*wp->w_p_stl != NUL
+ ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
+ }
+ did_emsg |= saved_did_emsg;
+ entered = FALSE;
+}
+#endif
+
+/*
+ * Show current status info in ruler and various other places
+ * If always is FALSE, only show ruler if position has changed.
+ */
+ void
+showruler(int always)
+{
+ if (!always && !redrawing())
+ return;
+ if (pum_visible())
+ {
+ // Don't redraw right now, do it later.
+ curwin->w_redr_status = TRUE;
+ return;
+ }
+#if defined(FEAT_STL_OPT)
+ if ((*p_stl != NUL || *curwin->w_p_stl != NUL) && curwin->w_status_height)
+ redraw_custom_statusline(curwin);
+ else
+#endif
+#ifdef FEAT_CMDL_INFO
+ win_redr_ruler(curwin, always, FALSE);
+#endif
+
+#ifdef FEAT_TITLE
+ if (need_maketitle
+# ifdef FEAT_STL_OPT
+ || (p_icon && (stl_syntax & STL_IN_ICON))
+ || (p_title && (stl_syntax & STL_IN_TITLE))
+# endif
+ )
+ maketitle();
+#endif
+ // Redraw the tab pages line if needed.
+ if (redraw_tabline)
+ draw_tabline();
+}
+
+#if defined(FEAT_CMDL_INFO) || defined(PROTO)
+ void
+win_redr_ruler(win_T *wp, int always, int ignore_pum)
+{
+#define RULER_BUF_LEN 70
+ char_u buffer[RULER_BUF_LEN];
+ int row;
+ int fillchar;
+ int attr;
+ int empty_line = FALSE;
+ colnr_T virtcol;
+ int i;
+ size_t len;
+ int o;
+ int this_ru_col;
+ int off = 0;
+ int width;
+
+ // If 'ruler' off or redrawing disabled, don't do anything
+ if (!p_ru)
+ return;
+
+ /*
+ * Check if cursor.lnum is valid, since win_redr_ruler() may be called
+ * after deleting lines, before cursor.lnum is corrected.
+ */
+ if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count)
+ return;
+
+ // Don't draw the ruler while doing insert-completion, it might overwrite
+ // the (long) mode message.
+ if (wp == lastwin && lastwin->w_status_height == 0)
+ if (edit_submode != NULL)
+ return;
+ // Don't draw the ruler when the popup menu is visible, it may overlap.
+ // Except when the popup menu will be redrawn anyway.
+ if (!ignore_pum && pum_visible())
+ return;
+
+#ifdef FEAT_STL_OPT
+ if (*p_ruf)
+ {
+ int save_called_emsg = called_emsg;
+
+ called_emsg = FALSE;
+ win_redr_custom(wp, TRUE);
+ if (called_emsg)
+ set_string_option_direct((char_u *)"rulerformat", -1,
+ (char_u *)"", OPT_FREE, SID_ERROR);
+ called_emsg |= save_called_emsg;
+ return;
+ }
+#endif
+
+ /*
+ * Check if not in Insert mode and the line is empty (will show "0-1").
+ */
+ if (!(State & INSERT)
+ && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE) == NUL)
+ empty_line = TRUE;
+
+ /*
+ * Only draw the ruler when something changed.
+ */
+ validate_virtcol_win(wp);
+ if ( redraw_cmdline
+ || always
+ || wp->w_cursor.lnum != wp->w_ru_cursor.lnum
+ || wp->w_cursor.col != wp->w_ru_cursor.col
+ || wp->w_virtcol != wp->w_ru_virtcol
+ || wp->w_cursor.coladd != wp->w_ru_cursor.coladd
+ || wp->w_topline != wp->w_ru_topline
+ || wp->w_buffer->b_ml.ml_line_count != wp->w_ru_line_count
+#ifdef FEAT_DIFF
+ || wp->w_topfill != wp->w_ru_topfill
+#endif
+ || empty_line != wp->w_ru_empty)
+ {
+ cursor_off();
+ if (wp->w_status_height)
+ {
+ row = W_WINROW(wp) + wp->w_height;
+ fillchar = fillchar_status(&attr, wp);
+ off = wp->w_wincol;
+ width = wp->w_width;
+ }
+ else
+ {
+ row = Rows - 1;
+ fillchar = ' ';
+ attr = 0;
+ width = Columns;
+ off = 0;
+ }
+
+ // In list mode virtcol needs to be recomputed
+ virtcol = wp->w_virtcol;
+ if (wp->w_p_list && lcs_tab1 == NUL)
+ {
+ wp->w_p_list = FALSE;
+ getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
+ wp->w_p_list = TRUE;
+ }
+
+ /*
+ * Some sprintfs return the length, some return a pointer.
+ * To avoid portability problems we use strlen() here.
+ */
+ vim_snprintf((char *)buffer, RULER_BUF_LEN, "%ld,",
+ (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
+ ? 0L
+ : (long)(wp->w_cursor.lnum));
+ len = STRLEN(buffer);
+ col_print(buffer + len, RULER_BUF_LEN - len,
+ empty_line ? 0 : (int)wp->w_cursor.col + 1,
+ (int)virtcol + 1);
+
+ /*
+ * Add a "50%" if there is room for it.
+ * On the last line, don't print in the last column (scrolls the
+ * screen up on some terminals).
+ */
+ i = (int)STRLEN(buffer);
+ get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1);
+ o = i + vim_strsize(buffer + i + 1);
+ if (wp->w_status_height == 0) // can't use last char of screen
+ ++o;
+ this_ru_col = ru_col - (Columns - width);
+ if (this_ru_col < 0)
+ this_ru_col = 0;
+ // Never use more than half the window/screen width, leave the other
+ // half for the filename.
+ if (this_ru_col < (width + 1) / 2)
+ this_ru_col = (width + 1) / 2;
+ if (this_ru_col + o < width)
+ {
+ // need at least 3 chars left for get_rel_pos() + NUL
+ while (this_ru_col + o < width && RULER_BUF_LEN > i + 4)
+ {
+ if (has_mbyte)
+ i += (*mb_char2bytes)(fillchar, buffer + i);
+ else
+ buffer[i++] = fillchar;
+ ++o;
+ }
+ get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i);
+ }
+ // Truncate at window boundary.
+ if (has_mbyte)
+ {
+ o = 0;
+ for (i = 0; buffer[i] != NUL; i += (*mb_ptr2len)(buffer + i))
+ {
+ o += (*mb_ptr2cells)(buffer + i);
+ if (this_ru_col + o > width)
+ {
+ buffer[i] = NUL;
+ break;
+ }
+ }
+ }
+ else if (this_ru_col + (int)STRLEN(buffer) > width)
+ buffer[width - this_ru_col] = NUL;
+
+ screen_puts(buffer, row, this_ru_col + off, attr);
+ i = redraw_cmdline;
+ screen_fill(row, row + 1,
+ this_ru_col + off + (int)STRLEN(buffer),
+ (int)(off + width),
+ fillchar, fillchar, attr);
+ // don't redraw the cmdline because of showing the ruler
+ redraw_cmdline = i;
+ wp->w_ru_cursor = wp->w_cursor;
+ wp->w_ru_virtcol = wp->w_virtcol;
+ wp->w_ru_empty = empty_line;
+ wp->w_ru_topline = wp->w_topline;
+ wp->w_ru_line_count = wp->w_buffer->b_ml.ml_line_count;
+#ifdef FEAT_DIFF
+ wp->w_ru_topfill = wp->w_topfill;
+#endif
+ }
+}
+#endif
+
+/*
+ * To be called when "updating_screen" was set before and now the postponed
+ * side effects may take place.
+ */
+ void
+after_updating_screen(int may_resize_shell UNUSED)
+{
+ updating_screen = FALSE;
+#ifdef FEAT_GUI
+ if (may_resize_shell)
+ gui_may_resize_shell();
+#endif
+#ifdef FEAT_TERMINAL
+ term_check_channel_closed_recently();
+#endif
+
+#ifdef HAVE_DROP_FILE
+ // If handle_drop() was called while updating_screen was TRUE need to
+ // handle the drop now.
+ handle_any_postponed_drop();
+#endif
+}
+
+/*
+ * Update all windows that are editing the current buffer.
+ */
+ void
+update_curbuf(int type)
+{
+ redraw_curbuf_later(type);
+ update_screen(type);
+}
+
+#if defined(FEAT_MENU) || defined(FEAT_FOLDING)
+/*
+ * Copy "text" to ScreenLines using "attr".
+ * Returns the next screen column.
+ */
+ static int
+text_to_screenline(win_T *wp, char_u *text, int col)
+{
+ int off = (int)(current_ScreenLine - ScreenLines);
+
+ if (has_mbyte)
+ {
+ int cells;
+ int u8c, u8cc[MAX_MCO];
+ int i;
+ int idx;
+ int c_len;
+ char_u *p;
+# ifdef FEAT_ARABIC
+ int prev_c = 0; // previous Arabic character
+ int prev_c1 = 0; // first composing char for prev_c
+# endif
+
+# ifdef FEAT_RIGHTLEFT
+ if (wp->w_p_rl)
+ idx = off;
+ else
+# endif
+ idx = off + col;
+
+ // Store multibyte characters in ScreenLines[] et al. correctly.
+ for (p = text; *p != NUL; )
+ {
+ cells = (*mb_ptr2cells)(p);
+ c_len = (*mb_ptr2len)(p);
+ if (col + cells > wp->w_width
+# ifdef FEAT_RIGHTLEFT
+ - (wp->w_p_rl ? col : 0)
+# endif
+ )
+ break;
+ ScreenLines[idx] = *p;
+ if (enc_utf8)
+ {
+ u8c = utfc_ptr2char(p, u8cc);
+ if (*p < 0x80 && u8cc[0] == 0)
+ {
+ ScreenLinesUC[idx] = 0;
+#ifdef FEAT_ARABIC
+ prev_c = u8c;
+#endif
+ }
+ else
+ {
+#ifdef FEAT_ARABIC
+ if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c))
+ {
+ // Do Arabic shaping.
+ int pc, pc1, nc;
+ int pcc[MAX_MCO];
+ int firstbyte = *p;
+
+ // The idea of what is the previous and next
+ // character depends on 'rightleft'.
+ if (wp->w_p_rl)
+ {
+ pc = prev_c;
+ pc1 = prev_c1;
+ nc = utf_ptr2char(p + c_len);
+ prev_c1 = u8cc[0];
+ }
+ else
+ {
+ pc = utfc_ptr2char(p + c_len, pcc);
+ nc = prev_c;
+ pc1 = pcc[0];
+ }
+ prev_c = u8c;
+
+ u8c = arabic_shape(u8c, &firstbyte, &u8cc[0],
+ pc, pc1, nc);
+ ScreenLines[idx] = firstbyte;
+ }
+ else
+ prev_c = u8c;
+#endif
+ // Non-BMP character: display as ? or fullwidth ?.
+ ScreenLinesUC[idx] = u8c;
+ for (i = 0; i < Screen_mco; ++i)
+ {
+ ScreenLinesC[i][idx] = u8cc[i];
+ if (u8cc[i] == 0)
+ break;
+ }
+ }
+ if (cells > 1)
+ ScreenLines[idx + 1] = 0;
+ }
+ else if (enc_dbcs == DBCS_JPNU && *p == 0x8e)
+ // double-byte single width character
+ ScreenLines2[idx] = p[1];
+ else if (cells > 1)
+ // double-width character
+ ScreenLines[idx + 1] = p[1];
+ col += cells;
+ idx += cells;
+ p += c_len;
+ }
+ }
+ else
+ {
+ int len = (int)STRLEN(text);
+
+ if (len > wp->w_width - col)
+ len = wp->w_width - col;
+ if (len > 0)
+ {
+#ifdef FEAT_RIGHTLEFT
+ if (wp->w_p_rl)
+ mch_memmove(current_ScreenLine, text, len);
+ else
+#endif
+ mch_memmove(current_ScreenLine + col, text, len);
+ col += len;
+ }
+ }
+ return col;
+}
+#endif
+
+#ifdef FEAT_MENU
+/*
+ * Draw the window toolbar.
+ */
+ static void
+redraw_win_toolbar(win_T *wp)
+{
+ vimmenu_T *menu;
+ int item_idx = 0;
+ int item_count = 0;
+ int col = 0;
+ int next_col;
+ int off = (int)(current_ScreenLine - ScreenLines);
+ int fill_attr = syn_name2attr((char_u *)"ToolbarLine");
+ int button_attr = syn_name2attr((char_u *)"ToolbarButton");
+
+ vim_free(wp->w_winbar_items);
+ for (menu = wp->w_winbar->children; menu != NULL; menu = menu->next)
+ ++item_count;
+ wp->w_winbar_items = ALLOC_CLEAR_MULT(winbar_item_T, item_count + 1);
+
+ // TODO: use fewer spaces if there is not enough room
+ for (menu = wp->w_winbar->children;
+ menu != NULL && col < wp->w_width; menu = menu->next)
+ {
+ space_to_screenline(off + col, fill_attr);
+ if (++col >= wp->w_width)
+ break;
+ if (col > 1)
+ {
+ space_to_screenline(off + col, fill_attr);
+ if (++col >= wp->w_width)
+ break;
+ }
+
+ wp->w_winbar_items[item_idx].wb_startcol = col;
+ space_to_screenline(off + col, button_attr);
+ if (++col >= wp->w_width)
+ break;
+
+ next_col = text_to_screenline(wp, menu->name, col);
+ while (col < next_col)
+ {
+ ScreenAttrs[off + col] = button_attr;
+ ++col;
+ }
+ wp->w_winbar_items[item_idx].wb_endcol = col;
+ wp->w_winbar_items[item_idx].wb_menu = menu;
+ ++item_idx;
+
+ if (col >= wp->w_width)
+ break;
+ space_to_screenline(off + col, button_attr);
+ ++col;
+ }
+ while (col < wp->w_width)
+ {
+ space_to_screenline(off + col, fill_attr);
+ ++col;
+ }
+ wp->w_winbar_items[item_idx].wb_menu = NULL; // end marker
+
+ screen_line(wp->w_winrow, wp->w_wincol, (int)wp->w_width,
+ (int)wp->w_width, 0);
+}
+#endif
+
+#if defined(FEAT_FOLDING) || defined(PROTO)
+/*
+ * Copy "buf[len]" to ScreenLines["off"] and set attributes to "attr".
+ */
+ static void
+copy_text_attr(
+ int off,
+ char_u *buf,
+ int len,
+ int attr)
+{
+ int i;
+
+ mch_memmove(ScreenLines + off, buf, (size_t)len);
+ if (enc_utf8)
+ vim_memset(ScreenLinesUC + off, 0, sizeof(u8char_T) * (size_t)len);
+ for (i = 0; i < len; ++i)
+ ScreenAttrs[off + i] = attr;
+}
+
+/*
+ * Display one folded line.
+ */
+ static void
+fold_line(
+ win_T *wp,
+ long fold_count,
+ foldinfo_T *foldinfo,
+ linenr_T lnum,
+ int row)
+{
+ char_u buf[FOLD_TEXT_LEN];
+ pos_T *top, *bot;
+ linenr_T lnume = lnum + fold_count - 1;
+ int len;
+ char_u *text;
+ int fdc;
+ int col;
+ int txtcol;
+ int off = (int)(current_ScreenLine - ScreenLines);
+ int ri;
+
+ // Build the fold line:
+ // 1. Add the cmdwin_type for the command-line window
+ // 2. Add the 'foldcolumn'
+ // 3. Add the 'number' or 'relativenumber' column
+ // 4. Compose the text
+ // 5. Add the text
+ // 6. set highlighting for the Visual area an other text
+ col = 0;
+
+ // 1. Add the cmdwin_type for the command-line window
+ // Ignores 'rightleft', this window is never right-left.
+#ifdef FEAT_CMDWIN
+ if (cmdwin_type != 0 && wp == curwin)
+ {
+ ScreenLines[off] = cmdwin_type;
+ ScreenAttrs[off] = HL_ATTR(HLF_AT);
+ if (enc_utf8)
+ ScreenLinesUC[off] = 0;
+ ++col;
+ }
+#endif
+
+ // 2. Add the 'foldcolumn'
+ // Reduce the width when there is not enough space.
+ fdc = compute_foldcolumn(wp, col);
+ if (fdc > 0)
+ {
+ fill_foldcolumn(buf, wp, TRUE, lnum);
+#ifdef FEAT_RIGHTLEFT
+ if (wp->w_p_rl)
+ {
+ int i;
+
+ copy_text_attr(off + wp->w_width - fdc - col, buf, fdc,
+ HL_ATTR(HLF_FC));
+ // reverse the fold column
+ for (i = 0; i < fdc; ++i)
+ ScreenLines[off + wp->w_width - i - 1 - col] = buf[i];
+ }
+ else
+#endif
+ copy_text_attr(off + col, buf, fdc, HL_ATTR(HLF_FC));
+ col += fdc;
+ }
+
+#ifdef FEAT_RIGHTLEFT
+# define RL_MEMSET(p, v, l) \
+ do { \
+ if (wp->w_p_rl) \
+ for (ri = 0; ri < l; ++ri) \
+ ScreenAttrs[off + (wp->w_width - (p) - (l)) + ri] = v; \
+ else \
+ for (ri = 0; ri < l; ++ri) \
+ ScreenAttrs[off + (p) + ri] = v; \
+ } while (0)
+#else
+# define RL_MEMSET(p, v, l) \
+ do { \
+ for (ri = 0; ri < l; ++ri) \
+ ScreenAttrs[off + (p) + ri] = v; \
+ } while (0)
+#endif
+
+ // Set all attributes of the 'number' or 'relativenumber' column and the
+ // text
+ RL_MEMSET(col, HL_ATTR(HLF_FL), wp->w_width - col);
+
+#ifdef FEAT_SIGNS
+ // If signs are being displayed, add two spaces.
+ if (signcolumn_on(wp))
+ {
+ len = wp->w_width - col;
+ if (len > 0)
+ {
+ if (len > 2)
+ len = 2;
+# ifdef FEAT_RIGHTLEFT
+ if (wp->w_p_rl)
+ // the line number isn't reversed
+ copy_text_attr(off + wp->w_width - len - col,
+ (char_u *)" ", len, HL_ATTR(HLF_FL));
+ else
+# endif
+ copy_text_attr(off + col, (char_u *)" ", len, HL_ATTR(HLF_FL));
+ col += len;
+ }
+ }
+#endif
+
+ // 3. Add the 'number' or 'relativenumber' column
+ if (wp->w_p_nu || wp->w_p_rnu)
+ {
+ len = wp->w_width - col;
+ if (len > 0)
+ {
+ int w = number_width(wp);
+ long num;
+ char *fmt = "%*ld ";
+
+ if (len > w + 1)
+ len = w + 1;
+
+ if (wp->w_p_nu && !wp->w_p_rnu)
+ // 'number' + 'norelativenumber'
+ num = (long)lnum;
+ else
+ {
+ // 'relativenumber', don't use negative numbers
+ num = labs((long)get_cursor_rel_lnum(wp, lnum));
+ if (num == 0 && wp->w_p_nu && wp->w_p_rnu)
+ {
+ // 'number' + 'relativenumber': cursor line shows absolute
+ // line number
+ num = lnum;
+ fmt = "%-*ld ";
+ }
+ }
+
+ sprintf((char *)buf, fmt, w, num);
+#ifdef FEAT_RIGHTLEFT
+ if (wp->w_p_rl)
+ // the line number isn't reversed
+ copy_text_attr(off + wp->w_width - len - col