diff options
Diffstat (limited to 'src/drawscreen.c')
-rw-r--r-- | src/drawscreen.c | 3112 |
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, buf, len, + HL_ATTR(HLF_FL)); + else +#endif + copy_text_attr(off + col, buf, len, HL_ATTR(HLF_FL)); + col += len; + } + } + + // 4. Compose the folded-line string with 'foldtext', if set. + text = get_foldtext(wp, lnum, lnume, foldinfo, buf); + + txtcol = col; // remember where text starts + + // 5. move the text to current_ScreenLine. Fill up with "fill_fold". + // Right-left text is put in columns 0 - number-col, normal text is put + // in columns number-col - window-width. + col = text_to_screenline(wp, text, col); + + // Fill the rest of the line with the fold filler +#ifdef FEAT_RIGHTLEFT + if (wp->w_p_rl) + col -= txtcol; +#endif |