xref: /vim-8.2.3635/src/screen.c (revision fc65cabb)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * screen.c: code for displaying on the screen
12  *
13  * Output to the screen (console, terminal emulator or GUI window) is minimized
14  * by remembering what is already on the screen, and only updating the parts
15  * that changed.
16  *
17  * ScreenLines[off]  Contains a copy of the whole screen, as it is currently
18  *		     displayed (excluding text written by external commands).
19  * ScreenAttrs[off]  Contains the associated attributes.
20  * LineOffset[row]   Contains the offset into ScreenLines*[] and ScreenAttrs[]
21  *		     for each line.
22  * LineWraps[row]    Flag for each line whether it wraps to the next line.
23  *
24  * For double-byte characters, two consecutive bytes in ScreenLines[] can form
25  * one character which occupies two display cells.
26  * For UTF-8 a multi-byte character is converted to Unicode and stored in
27  * ScreenLinesUC[].  ScreenLines[] contains the first byte only.  For an ASCII
28  * character without composing chars ScreenLinesUC[] will be 0 and
29  * ScreenLinesC[][] is not used.  When the character occupies two display
30  * cells the next byte in ScreenLines[] is 0.
31  * ScreenLinesC[][] contain up to 'maxcombine' composing characters
32  * (drawn on top of the first character).  There is 0 after the last one used.
33  * ScreenLines2[] is only used for euc-jp to store the second byte if the
34  * first byte is 0x8e (single-width character).
35  *
36  * The screen_*() functions write to the screen and handle updating
37  * ScreenLines[].
38  *
39  * update_screen() is the function that updates all windows and status lines.
40  * It is called form the main loop when must_redraw is non-zero.  It may be
41  * called from other places when an immediate screen update is needed.
42  *
43  * The part of the buffer that is displayed in a window is set with:
44  * - w_topline (first buffer line in window)
45  * - w_topfill (filler lines above the first line)
46  * - w_leftcol (leftmost window cell in window),
47  * - w_skipcol (skipped window cells of first line)
48  *
49  * Commands that only move the cursor around in a window, do not need to take
50  * action to update the display.  The main loop will check if w_topline is
51  * valid and update it (scroll the window) when needed.
52  *
53  * Commands that scroll a window change w_topline and must call
54  * check_cursor() to move the cursor into the visible part of the window, and
55  * call redraw_later(VALID) to have the window displayed by update_screen()
56  * later.
57  *
58  * Commands that change text in the buffer must call changed_bytes() or
59  * changed_lines() to mark the area that changed and will require updating
60  * later.  The main loop will call update_screen(), which will update each
61  * window that shows the changed buffer.  This assumes text above the change
62  * can remain displayed as it is.  Text after the change may need updating for
63  * scrolling, folding and syntax highlighting.
64  *
65  * Commands that change how a window is displayed (e.g., setting 'list') or
66  * invalidate the contents of a window in another way (e.g., change fold
67  * settings), must call redraw_later(NOT_VALID) to have the whole window
68  * redisplayed by update_screen() later.
69  *
70  * Commands that change how a buffer is displayed (e.g., setting 'tabstop')
71  * must call redraw_curbuf_later(NOT_VALID) to have all the windows for the
72  * buffer redisplayed by update_screen() later.
73  *
74  * Commands that change highlighting and possibly cause a scroll too must call
75  * redraw_later(SOME_VALID) to update the whole window but still use scrolling
76  * to avoid redrawing everything.  But the length of displayed lines must not
77  * change, use NOT_VALID then.
78  *
79  * Commands that move the window position must call redraw_later(NOT_VALID).
80  * TODO: should minimize redrawing by scrolling when possible.
81  *
82  * Commands that change everything (e.g., resizing the screen) must call
83  * redraw_all_later(NOT_VALID) or redraw_all_later(CLEAR).
84  *
85  * Things that are handled indirectly:
86  * - When messages scroll the screen up, msg_scrolled will be set and
87  *   update_screen() called to redraw.
88  */
89 
90 #include "vim.h"
91 
92 #define MB_FILLER_CHAR '<'  /* character used when a double-width character
93 			     * doesn't fit. */
94 
95 /*
96  * The attributes that are actually active for writing to the screen.
97  */
98 static int	screen_attr = 0;
99 
100 /*
101  * Positioning the cursor is reduced by remembering the last position.
102  * Mostly used by windgoto() and screen_char().
103  */
104 static int	screen_cur_row, screen_cur_col;	/* last known cursor position */
105 
106 #ifdef FEAT_SEARCH_EXTRA
107 static match_T search_hl;	/* used for 'hlsearch' highlight matching */
108 #endif
109 
110 #if defined(FEAT_MENU) || defined(FEAT_FOLDING)
111 static int text_to_screenline(win_T *wp, char_u *text, int col);
112 #endif
113 #ifdef FEAT_FOLDING
114 static foldinfo_T win_foldinfo;	/* info for 'foldcolumn' */
115 static int compute_foldcolumn(win_T *wp, int col);
116 #endif
117 
118 /* Flag that is set when drawing for a callback, not from the main command
119  * loop. */
120 static int redrawing_for_callback = 0;
121 
122 /*
123  * Buffer for one screen line (characters and attributes).
124  */
125 static schar_T	*current_ScreenLine;
126 
127 static void win_update(win_T *wp);
128 static void win_redr_status(win_T *wp, int ignore_pum);
129 static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T hl);
130 #ifdef FEAT_FOLDING
131 static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T lnum, int row);
132 static void fill_foldcolumn(char_u *p, win_T *wp, int closed, linenr_T lnum);
133 static void copy_text_attr(int off, char_u *buf, int len, int attr);
134 #endif
135 static int win_line(win_T *, linenr_T, int, int, int nochange);
136 static int char_needs_redraw(int off_from, int off_to, int cols);
137 static void draw_vsep_win(win_T *wp, int row);
138 #ifdef FEAT_STL_OPT
139 static void redraw_custom_statusline(win_T *wp);
140 #endif
141 #ifdef FEAT_SEARCH_EXTRA
142 # define SEARCH_HL_PRIORITY 0
143 static void start_search_hl(void);
144 static void end_search_hl(void);
145 static void init_search_hl(win_T *wp);
146 static void prepare_search_hl(win_T *wp, linenr_T lnum);
147 static void next_search_hl(win_T *win, match_T *shl, linenr_T lnum, colnr_T mincol, matchitem_T *cur);
148 static int next_search_hl_pos(match_T *shl, linenr_T lnum, posmatch_T *pos, colnr_T mincol);
149 #endif
150 static void screen_start_highlight(int attr);
151 static void screen_char(unsigned off, int row, int col);
152 #ifdef FEAT_MBYTE
153 static void screen_char_2(unsigned off, int row, int col);
154 #endif
155 static void screenclear2(void);
156 static void lineclear(unsigned off, int width, int attr);
157 static void lineinvalid(unsigned off, int width);
158 static void linecopy(int to, int from, win_T *wp);
159 static void redraw_block(int row, int end, win_T *wp);
160 static int win_do_lines(win_T *wp, int row, int line_count, int mayclear, int del, int clear_attr);
161 static void win_rest_invalid(win_T *wp);
162 static void msg_pos_mode(void);
163 static void recording_mode(int attr);
164 static void draw_tabline(void);
165 static int fillchar_status(int *attr, win_T *wp);
166 static int fillchar_vsep(int *attr);
167 #ifdef FEAT_MENU
168 static void redraw_win_toolbar(win_T *wp);
169 #endif
170 #ifdef FEAT_STL_OPT
171 static void win_redr_custom(win_T *wp, int draw_ruler);
172 #endif
173 #ifdef FEAT_CMDL_INFO
174 static void win_redr_ruler(win_T *wp, int always, int ignore_pum);
175 #endif
176 
177 /* Ugly global: overrule attribute used by screen_char() */
178 static int screen_char_attr = 0;
179 
180 #if defined(FEAT_SYN_HL) && defined(FEAT_RELTIME)
181 /* Can limit syntax highlight time to 'redrawtime'. */
182 # define SYN_TIME_LIMIT 1
183 #endif
184 
185 #ifdef FEAT_RIGHTLEFT
186 # define HAS_RIGHTLEFT(x) x
187 #else
188 # define HAS_RIGHTLEFT(x) FALSE
189 #endif
190 
191 /*
192  * Redraw the current window later, with update_screen(type).
193  * Set must_redraw only if not already set to a higher value.
194  * e.g. if must_redraw is CLEAR, type NOT_VALID will do nothing.
195  */
196     void
197 redraw_later(int type)
198 {
199     redraw_win_later(curwin, type);
200 }
201 
202     void
203 redraw_win_later(
204     win_T	*wp,
205     int		type)
206 {
207     if (!exiting && wp->w_redr_type < type)
208     {
209 	wp->w_redr_type = type;
210 	if (type >= NOT_VALID)
211 	    wp->w_lines_valid = 0;
212 	if (must_redraw < type)	/* must_redraw is the maximum of all windows */
213 	    must_redraw = type;
214     }
215 }
216 
217 /*
218  * Force a complete redraw later.  Also resets the highlighting.  To be used
219  * after executing a shell command that messes up the screen.
220  */
221     void
222 redraw_later_clear(void)
223 {
224     redraw_all_later(CLEAR);
225 #ifdef FEAT_GUI
226     if (gui.in_use)
227 	/* Use a code that will reset gui.highlight_mask in
228 	 * gui_stop_highlight(). */
229 	screen_attr = HL_ALL + 1;
230     else
231 #endif
232 	/* Use attributes that is very unlikely to appear in text. */
233 	screen_attr = HL_BOLD | HL_UNDERLINE | HL_INVERSE | HL_STRIKETHROUGH;
234 }
235 
236 /*
237  * Mark all windows to be redrawn later.
238  */
239     void
240 redraw_all_later(int type)
241 {
242     win_T	*wp;
243 
244     FOR_ALL_WINDOWS(wp)
245     {
246 	redraw_win_later(wp, type);
247     }
248 }
249 
250 /*
251  * Mark all windows that are editing the current buffer to be updated later.
252  */
253     void
254 redraw_curbuf_later(int type)
255 {
256     redraw_buf_later(curbuf, type);
257 }
258 
259     void
260 redraw_buf_later(buf_T *buf, int type)
261 {
262     win_T	*wp;
263 
264     FOR_ALL_WINDOWS(wp)
265     {
266 	if (wp->w_buffer == buf)
267 	    redraw_win_later(wp, type);
268     }
269 }
270 
271     void
272 redraw_buf_and_status_later(buf_T *buf, int type)
273 {
274     win_T	*wp;
275 
276 #ifdef FEAT_WILDMENU
277     if (wild_menu_showing != 0)
278 	/* Don't redraw while the command line completion is displayed, it
279 	 * would disappear. */
280 	return;
281 #endif
282     FOR_ALL_WINDOWS(wp)
283     {
284 	if (wp->w_buffer == buf)
285 	{
286 	    redraw_win_later(wp, type);
287 	    wp->w_redr_status = TRUE;
288 	}
289     }
290 }
291 
292 /*
293  * Redraw as soon as possible.  When the command line is not scrolled redraw
294  * right away and restore what was on the command line.
295  * Return a code indicating what happened.
296  */
297     int
298 redraw_asap(int type)
299 {
300     int		rows;
301     int		cols = screen_Columns;
302     int		r;
303     int		ret = 0;
304     schar_T	*screenline;	/* copy from ScreenLines[] */
305     sattr_T	*screenattr;	/* copy from ScreenAttrs[] */
306 #ifdef FEAT_MBYTE
307     int		i;
308     u8char_T	*screenlineUC = NULL;	/* copy from ScreenLinesUC[] */
309     u8char_T	*screenlineC[MAX_MCO];	/* copy from ScreenLinesC[][] */
310     schar_T	*screenline2 = NULL;	/* copy from ScreenLines2[] */
311 #endif
312 
313     redraw_later(type);
314     if (msg_scrolled || (State != NORMAL && State != NORMAL_BUSY) || exiting)
315 	return ret;
316 
317     /* Allocate space to save the text displayed in the command line area. */
318     rows = screen_Rows - cmdline_row;
319     screenline = (schar_T *)lalloc(
320 			   (long_u)(rows * cols * sizeof(schar_T)), FALSE);
321     screenattr = (sattr_T *)lalloc(
322 			   (long_u)(rows * cols * sizeof(sattr_T)), FALSE);
323     if (screenline == NULL || screenattr == NULL)
324 	ret = 2;
325 #ifdef FEAT_MBYTE
326     if (enc_utf8)
327     {
328 	screenlineUC = (u8char_T *)lalloc(
329 			  (long_u)(rows * cols * sizeof(u8char_T)), FALSE);
330 	if (screenlineUC == NULL)
331 	    ret = 2;
332 	for (i = 0; i < p_mco; ++i)
333 	{
334 	    screenlineC[i] = (u8char_T *)lalloc(
335 			  (long_u)(rows * cols * sizeof(u8char_T)), FALSE);
336 	    if (screenlineC[i] == NULL)
337 		ret = 2;
338 	}
339     }
340     if (enc_dbcs == DBCS_JPNU)
341     {
342 	screenline2 = (schar_T *)lalloc(
343 			   (long_u)(rows * cols * sizeof(schar_T)), FALSE);
344 	if (screenline2 == NULL)
345 	    ret = 2;
346     }
347 #endif
348 
349     if (ret != 2)
350     {
351 	/* Save the text displayed in the command line area. */
352 	for (r = 0; r < rows; ++r)
353 	{
354 	    mch_memmove(screenline + r * cols,
355 			ScreenLines + LineOffset[cmdline_row + r],
356 			(size_t)cols * sizeof(schar_T));
357 	    mch_memmove(screenattr + r * cols,
358 			ScreenAttrs + LineOffset[cmdline_row + r],
359 			(size_t)cols * sizeof(sattr_T));
360 #ifdef FEAT_MBYTE
361 	    if (enc_utf8)
362 	    {
363 		mch_memmove(screenlineUC + r * cols,
364 			    ScreenLinesUC + LineOffset[cmdline_row + r],
365 			    (size_t)cols * sizeof(u8char_T));
366 		for (i = 0; i < p_mco; ++i)
367 		    mch_memmove(screenlineC[i] + r * cols,
368 				ScreenLinesC[i] + LineOffset[cmdline_row + r],
369 				(size_t)cols * sizeof(u8char_T));
370 	    }
371 	    if (enc_dbcs == DBCS_JPNU)
372 		mch_memmove(screenline2 + r * cols,
373 			    ScreenLines2 + LineOffset[cmdline_row + r],
374 			    (size_t)cols * sizeof(schar_T));
375 #endif
376 	}
377 
378 	update_screen(0);
379 	ret = 3;
380 
381 	if (must_redraw == 0)
382 	{
383 	    int	off = (int)(current_ScreenLine - ScreenLines);
384 
385 	    /* Restore the text displayed in the command line area. */
386 	    for (r = 0; r < rows; ++r)
387 	    {
388 		mch_memmove(current_ScreenLine,
389 			    screenline + r * cols,
390 			    (size_t)cols * sizeof(schar_T));
391 		mch_memmove(ScreenAttrs + off,
392 			    screenattr + r * cols,
393 			    (size_t)cols * sizeof(sattr_T));
394 #ifdef FEAT_MBYTE
395 		if (enc_utf8)
396 		{
397 		    mch_memmove(ScreenLinesUC + off,
398 				screenlineUC + r * cols,
399 				(size_t)cols * sizeof(u8char_T));
400 		    for (i = 0; i < p_mco; ++i)
401 			mch_memmove(ScreenLinesC[i] + off,
402 				    screenlineC[i] + r * cols,
403 				    (size_t)cols * sizeof(u8char_T));
404 		}
405 		if (enc_dbcs == DBCS_JPNU)
406 		    mch_memmove(ScreenLines2 + off,
407 				screenline2 + r * cols,
408 				(size_t)cols * sizeof(schar_T));
409 #endif
410 		screen_line(cmdline_row + r, 0, cols, cols, FALSE);
411 	    }
412 	    ret = 4;
413 	}
414     }
415 
416     vim_free(screenline);
417     vim_free(screenattr);
418 #ifdef FEAT_MBYTE
419     if (enc_utf8)
420     {
421 	vim_free(screenlineUC);
422 	for (i = 0; i < p_mco; ++i)
423 	    vim_free(screenlineC[i]);
424     }
425     if (enc_dbcs == DBCS_JPNU)
426 	vim_free(screenline2);
427 #endif
428 
429     /* Show the intro message when appropriate. */
430     maybe_intro_message();
431 
432     setcursor();
433 
434     return ret;
435 }
436 
437 /*
438  * Invoked after an asynchronous callback is called.
439  * If an echo command was used the cursor needs to be put back where
440  * it belongs. If highlighting was changed a redraw is needed.
441  * If "call_update_screen" is FALSE don't call update_screen() when at the
442  * command line.
443  */
444     void
445 redraw_after_callback(int call_update_screen)
446 {
447     ++redrawing_for_callback;
448 
449     if (State == HITRETURN || State == ASKMORE)
450 	; // do nothing
451     else if (State & CMDLINE)
452     {
453 	// Don't redraw when in prompt_for_number().
454 	if (cmdline_row > 0)
455 	{
456 	    // Redrawing only works when the screen didn't scroll. Don't clear
457 	    // wildmenu entries.
458 	    if (msg_scrolled == 0
459 #ifdef FEAT_WILDMENU
460 		    && wild_menu_showing == 0
461 #endif
462 		    && call_update_screen)
463 		update_screen(0);
464 
465 	    // Redraw in the same position, so that the user can continue
466 	    // editing the command.
467 	    redrawcmdline_ex(FALSE);
468 	}
469     }
470     else if (State & (NORMAL | INSERT | TERMINAL))
471     {
472 	// keep the command line if possible
473 	update_screen(VALID_NO_UPDATE);
474 	setcursor();
475     }
476     cursor_on();
477 #ifdef FEAT_GUI
478     if (gui.in_use && !gui_mch_is_blink_off())
479 	// Don't update the cursor when it is blinking and off to avoid
480 	// flicker.
481 	out_flush_cursor(FALSE, FALSE);
482     else
483 #endif
484 	out_flush();
485 
486     --redrawing_for_callback;
487 }
488 
489 /*
490  * Changed something in the current window, at buffer line "lnum", that
491  * requires that line and possibly other lines to be redrawn.
492  * Used when entering/leaving Insert mode with the cursor on a folded line.
493  * Used to remove the "$" from a change command.
494  * Note that when also inserting/deleting lines w_redraw_top and w_redraw_bot
495  * may become invalid and the whole window will have to be redrawn.
496  */
497     void
498 redrawWinline(
499     linenr_T	lnum,
500     int		invalid UNUSED)	/* window line height is invalid now */
501 {
502 #ifdef FEAT_FOLDING
503     int		i;
504 #endif
505 
506     if (curwin->w_redraw_top == 0 || curwin->w_redraw_top > lnum)
507 	curwin->w_redraw_top = lnum;
508     if (curwin->w_redraw_bot == 0 || curwin->w_redraw_bot < lnum)
509 	curwin->w_redraw_bot = lnum;
510     redraw_later(VALID);
511 
512 #ifdef FEAT_FOLDING
513     if (invalid)
514     {
515 	/* A w_lines[] entry for this lnum has become invalid. */
516 	i = find_wl_entry(curwin, lnum);
517 	if (i >= 0)
518 	    curwin->w_lines[i].wl_valid = FALSE;
519     }
520 #endif
521 }
522 
523     void
524 reset_updating_screen(int may_resize_shell UNUSED)
525 {
526     updating_screen = FALSE;
527 #ifdef FEAT_GUI
528     if (may_resize_shell)
529 	gui_may_resize_shell();
530 #endif
531 #ifdef FEAT_TERMINAL
532     term_check_channel_closed_recently();
533 #endif
534 
535 #ifdef HAVE_DROP_FILE
536     // If handle_drop() was called while updating_screen was TRUE need to
537     // handle the drop now.
538     handle_any_postponed_drop();
539 #endif
540 }
541 
542 /*
543  * Update all windows that are editing the current buffer.
544  */
545     void
546 update_curbuf(int type)
547 {
548     redraw_curbuf_later(type);
549     update_screen(type);
550 }
551 
552 /*
553  * Based on the current value of curwin->w_topline, transfer a screenfull
554  * of stuff from Filemem to ScreenLines[], and update curwin->w_botline.
555  * Return OK when the screen was updated, FAIL if it was not done.
556  */
557     int
558 update_screen(int type_arg)
559 {
560     int		type = type_arg;
561     win_T	*wp;
562     static int	did_intro = FALSE;
563 #if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_CLIPBOARD)
564     int		did_one;
565 #endif
566 #ifdef FEAT_GUI
567     int		did_undraw = FALSE;
568     int		gui_cursor_col;
569     int		gui_cursor_row;
570 #endif
571     int		no_update = FALSE;
572 
573     /* Don't do anything if the screen structures are (not yet) valid. */
574     if (!screen_valid(TRUE))
575 	return FAIL;
576 
577     if (type == VALID_NO_UPDATE)
578     {
579 	no_update = TRUE;
580 	type = 0;
581     }
582 
583     if (must_redraw)
584     {
585 	if (type < must_redraw)	    /* use maximal type */
586 	    type = must_redraw;
587 
588 	/* must_redraw is reset here, so that when we run into some weird
589 	 * reason to redraw while busy redrawing (e.g., asynchronous
590 	 * scrolling), or update_topline() in win_update() will cause a
591 	 * scroll, the screen will be redrawn later or in win_update(). */
592 	must_redraw = 0;
593     }
594 
595     /* May need to update w_lines[]. */
596     if (curwin->w_lines_valid == 0 && type < NOT_VALID
597 #ifdef FEAT_TERMINAL
598 	    && !term_do_update_window(curwin)
599 #endif
600 		)
601 	type = NOT_VALID;
602 
603     /* Postpone the redrawing when it's not needed and when being called
604      * recursively. */
605     if (!redrawing() || updating_screen)
606     {
607 	redraw_later(type);		/* remember type for next time */
608 	must_redraw = type;
609 	if (type > INVERTED_ALL)
610 	    curwin->w_lines_valid = 0;	/* don't use w_lines[].wl_size now */
611 	return FAIL;
612     }
613 
614     updating_screen = TRUE;
615 #ifdef FEAT_SYN_HL
616     ++display_tick;	    /* let syntax code know we're in a next round of
617 			     * display updating */
618 #endif
619     if (no_update)
620 	++no_win_do_lines_ins;
621 
622     /*
623      * if the screen was scrolled up when displaying a message, scroll it down
624      */
625     if (msg_scrolled)
626     {
627 	clear_cmdline = TRUE;
628 	if (msg_scrolled > Rows - 5)	    /* clearing is faster */
629 	    type = CLEAR;
630 	else if (type != CLEAR)
631 	{
632 	    check_for_delay(FALSE);
633 	    if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows, 0, NULL)
634 								       == FAIL)
635 		type = CLEAR;
636 	    FOR_ALL_WINDOWS(wp)
637 	    {
638 		if (W_WINROW(wp) < msg_scrolled)
639 		{
640 		    if (W_WINROW(wp) + wp->w_height > msg_scrolled
641 			    && wp->w_redr_type < REDRAW_TOP
642 			    && wp->w_lines_valid > 0
643 			    && wp->w_topline == wp->w_lines[0].wl_lnum)
644 		    {
645 			wp->w_upd_rows = msg_scrolled - W_WINROW(wp);
646 			wp->w_redr_type = REDRAW_TOP;
647 		    }
648 		    else
649 		    {
650 			wp->w_redr_type = NOT_VALID;
651 			if (W_WINROW(wp) + wp->w_height + wp->w_status_height
652 							       <= msg_scrolled)
653 			    wp->w_redr_status = TRUE;
654 		    }
655 		}
656 	    }
657 	    if (!no_update)
658 		redraw_cmdline = TRUE;
659 	    redraw_tabline = TRUE;
660 	}
661 	msg_scrolled = 0;
662 	need_wait_return = FALSE;
663     }
664 
665     /* reset cmdline_row now (may have been changed temporarily) */
666     compute_cmdrow();
667 
668     /* Check for changed highlighting */
669     if (need_highlight_changed)
670 	highlight_changed();
671 
672     if (type == CLEAR)		/* first clear screen */
673     {
674 	screenclear();		/* will reset clear_cmdline */
675 	type = NOT_VALID;
676 	/* must_redraw may be set indirectly, avoid another redraw later */
677 	must_redraw = 0;
678     }
679 
680     if (clear_cmdline)		/* going to clear cmdline (done below) */
681 	check_for_delay(FALSE);
682 
683 #ifdef FEAT_LINEBREAK
684     /* Force redraw when width of 'number' or 'relativenumber' column
685      * changes. */
686     if (curwin->w_redr_type < NOT_VALID
687 	   && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu)
688 				    ? number_width(curwin) : 0))
689 	curwin->w_redr_type = NOT_VALID;
690 #endif
691 
692     /*
693      * Only start redrawing if there is really something to do.
694      */
695     if (type == INVERTED)
696 	update_curswant();
697     if (curwin->w_redr_type < type
698 	    && !((type == VALID
699 		    && curwin->w_lines[0].wl_valid
700 #ifdef FEAT_DIFF
701 		    && curwin->w_topfill == curwin->w_old_topfill
702 		    && curwin->w_botfill == curwin->w_old_botfill
703 #endif
704 		    && curwin->w_topline == curwin->w_lines[0].wl_lnum)
705 		|| (type == INVERTED
706 		    && VIsual_active
707 		    && curwin->w_old_cursor_lnum == curwin->w_cursor.lnum
708 		    && curwin->w_old_visual_mode == VIsual_mode
709 		    && (curwin->w_valid & VALID_VIRTCOL)
710 		    && curwin->w_old_curswant == curwin->w_curswant)
711 		))
712 	curwin->w_redr_type = type;
713 
714     /* Redraw the tab pages line if needed. */
715     if (redraw_tabline || type >= NOT_VALID)
716 	draw_tabline();
717 
718 #ifdef FEAT_SYN_HL
719     /*
720      * Correct stored syntax highlighting info for changes in each displayed
721      * buffer.  Each buffer must only be done once.
722      */
723     FOR_ALL_WINDOWS(wp)
724     {
725 	if (wp->w_buffer->b_mod_set)
726 	{
727 	    win_T	*wwp;
728 
729 	    /* Check if we already did this buffer. */
730 	    for (wwp = firstwin; wwp != wp; wwp = wwp->w_next)
731 		if (wwp->w_buffer == wp->w_buffer)
732 		    break;
733 	    if (wwp == wp && syntax_present(wp))
734 		syn_stack_apply_changes(wp->w_buffer);
735 	}
736     }
737 #endif
738 
739     /*
740      * Go from top to bottom through the windows, redrawing the ones that need
741      * it.
742      */
743 #if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_CLIPBOARD)
744     did_one = FALSE;
745 #endif
746 #ifdef FEAT_SEARCH_EXTRA
747     search_hl.rm.regprog = NULL;
748 #endif
749     FOR_ALL_WINDOWS(wp)
750     {
751 	if (wp->w_redr_type != 0)
752 	{
753 	    cursor_off();
754 #if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_CLIPBOARD)
755 	    if (!did_one)
756 	    {
757 		did_one = TRUE;
758 # ifdef FEAT_SEARCH_EXTRA
759 		start_search_hl();
760 # endif
761 # ifdef FEAT_CLIPBOARD
762 		/* When Visual area changed, may have to update selection. */
763 		if (clip_star.available && clip_isautosel_star())
764 		    clip_update_selection(&clip_star);
765 		if (clip_plus.available && clip_isautosel_plus())
766 		    clip_update_selection(&clip_plus);
767 # endif
768 #ifdef FEAT_GUI
769 		/* Remove the cursor before starting to do anything, because
770 		 * scrolling may make it difficult to redraw the text under
771 		 * it. */
772 		if (gui.in_use && wp == curwin)
773 		{
774 		    gui_cursor_col = gui.cursor_col;
775 		    gui_cursor_row = gui.cursor_row;
776 		    gui_undraw_cursor();
777 		    did_undraw = TRUE;
778 		}
779 #endif
780 	    }
781 #endif
782 	    win_update(wp);
783 	}
784 
785 	/* redraw status line after the window to minimize cursor movement */
786 	if (wp->w_redr_status)
787 	{
788 	    cursor_off();
789 	    win_redr_status(wp, TRUE); // any popup menu will be redrawn below
790 	}
791     }
792 #if defined(FEAT_SEARCH_EXTRA)
793     end_search_hl();
794 #endif
795 #ifdef FEAT_INS_EXPAND
796     /* May need to redraw the popup menu. */
797     pum_may_redraw();
798 #endif
799 
800     /* Reset b_mod_set flags.  Going through all windows is probably faster
801      * than going through all buffers (there could be many buffers). */
802     FOR_ALL_WINDOWS(wp)
803 	wp->w_buffer->b_mod_set = FALSE;
804 
805     reset_updating_screen(TRUE);
806 
807     /* Clear or redraw the command line.  Done last, because scrolling may
808      * mess up the command line. */
809     if (clear_cmdline || redraw_cmdline)
810 	showmode();
811 
812     if (no_update)
813 	--no_win_do_lines_ins;
814 
815     /* May put up an introductory message when not editing a file */
816     if (!did_intro)
817 	maybe_intro_message();
818     did_intro = TRUE;
819 
820 #ifdef FEAT_GUI
821     /* Redraw the cursor and update the scrollbars when all screen updating is
822      * done. */
823     if (gui.in_use)
824     {
825 	if (did_undraw && !gui_mch_is_blink_off())
826 	{
827 	    mch_disable_flush();
828 	    out_flush();	/* required before updating the cursor */
829 	    mch_enable_flush();
830 
831 	    /* Put the GUI position where the cursor was, gui_update_cursor()
832 	     * uses that. */
833 	    gui.col = gui_cursor_col;
834 	    gui.row = gui_cursor_row;
835 # ifdef FEAT_MBYTE
836 	    gui.col = mb_fix_col(gui.col, gui.row);
837 # endif
838 	    gui_update_cursor(FALSE, FALSE);
839 	    gui_may_flush();
840 	    screen_cur_col = gui.col;
841 	    screen_cur_row = gui.row;
842 	}
843 	else
844 	    out_flush();
845 	gui_update_scrollbars(FALSE);
846     }
847 #endif
848     return OK;
849 }
850 
851 #if defined(FEAT_SIGNS) || defined(FEAT_GUI) || defined(FEAT_CONCEAL)
852 /*
853  * Prepare for updating one or more windows.
854  * Caller must check for "updating_screen" already set to avoid recursiveness.
855  */
856     static void
857 update_prepare(void)
858 {
859     cursor_off();
860     updating_screen = TRUE;
861 #ifdef FEAT_GUI
862     /* Remove the cursor before starting to do anything, because scrolling may
863      * make it difficult to redraw the text under it. */
864     if (gui.in_use)
865 	gui_undraw_cursor();
866 #endif
867 #ifdef FEAT_SEARCH_EXTRA
868     start_search_hl();
869 #endif
870 }
871 
872 /*
873  * Finish updating one or more windows.
874  */
875     static void
876 update_finish(void)
877 {
878     if (redraw_cmdline)
879 	showmode();
880 
881 # ifdef FEAT_SEARCH_EXTRA
882     end_search_hl();
883 # endif
884 
885     reset_updating_screen(TRUE);
886 
887 # ifdef FEAT_GUI
888     /* Redraw the cursor and update the scrollbars when all screen updating is
889      * done. */
890     if (gui.in_use)
891     {
892 	out_flush_cursor(FALSE, FALSE);
893 	gui_update_scrollbars(FALSE);
894     }
895 # endif
896 }
897 #endif
898 
899 #if defined(FEAT_CONCEAL) || defined(PROTO)
900 /*
901  * Return TRUE if the cursor line in window "wp" may be concealed, according
902  * to the 'concealcursor' option.
903  */
904     int
905 conceal_cursor_line(win_T *wp)
906 {
907     int		c;
908 
909     if (*wp->w_p_cocu == NUL)
910 	return FALSE;
911     if (get_real_state() & VISUAL)
912 	c = 'v';
913     else if (State & INSERT)
914 	c = 'i';
915     else if (State & NORMAL)
916 	c = 'n';
917     else if (State & CMDLINE)
918 	c = 'c';
919     else
920 	return FALSE;
921     return vim_strchr(wp->w_p_cocu, c) != NULL;
922 }
923 
924 /*
925  * Check if the cursor line needs to be redrawn because of 'concealcursor'.
926  */
927     void
928 conceal_check_cursor_line(void)
929 {
930     if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin))
931     {
932 	need_cursor_line_redraw = TRUE;
933 	/* Need to recompute cursor column, e.g., when starting Visual mode
934 	 * without concealing. */
935 	curs_columns(TRUE);
936     }
937 }
938 
939     void
940 update_single_line(win_T *wp, linenr_T lnum)
941 {
942     int		row;
943     int		j;
944 #ifdef SYN_TIME_LIMIT
945     proftime_T	syntax_tm;
946 #endif
947 
948     /* Don't do anything if the screen structures are (not yet) valid. */
949     if (!screen_valid(TRUE) || updating_screen)
950 	return;
951 
952     if (lnum >= wp->w_topline && lnum < wp->w_botline
953 				 && foldedCount(wp, lnum, &win_foldinfo) == 0)
954     {
955 #ifdef SYN_TIME_LIMIT
956 	/* Set the time limit to 'redrawtime'. */
957 	profile_setlimit(p_rdt, &syntax_tm);
958 	syn_set_timeout(&syntax_tm);
959 #endif
960 	update_prepare();
961 
962 	row = 0;
963 	for (j = 0; j < wp->w_lines_valid; ++j)
964 	{
965 	    if (lnum == wp->w_lines[j].wl_lnum)
966 	    {
967 		screen_start();	/* not sure of screen cursor */
968 # ifdef FEAT_SEARCH_EXTRA
969 		init_search_hl(wp);
970 		start_search_hl();
971 		prepare_search_hl(wp, lnum);
972 # endif
973 		win_line(wp, lnum, row, row + wp->w_lines[j].wl_size, FALSE);
974 # if defined(FEAT_SEARCH_EXTRA)
975 		end_search_hl();
976 # endif
977 		break;
978 	    }
979 	    row += wp->w_lines[j].wl_size;
980 	}
981 
982 	update_finish();
983 
984 #ifdef SYN_TIME_LIMIT
985 	syn_set_timeout(NULL);
986 #endif
987     }
988     need_cursor_line_redraw = FALSE;
989 }
990 #endif
991 
992 #if defined(FEAT_SIGNS) || defined(PROTO)
993     void
994 update_debug_sign(buf_T *buf, linenr_T lnum)
995 {
996     win_T	*wp;
997     int		doit = FALSE;
998 
999 # ifdef FEAT_FOLDING
1000     win_foldinfo.fi_level = 0;
1001 # endif
1002 
1003     /* update/delete a specific mark */
1004     FOR_ALL_WINDOWS(wp)
1005     {
1006 	if (buf != NULL && lnum > 0)
1007 	{
1008 	    if (wp->w_buffer == buf && lnum >= wp->w_topline
1009 						      && lnum < wp->w_botline)
1010 	    {
1011 		if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum)
1012 		    wp->w_redraw_top = lnum;
1013 		if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum)
1014 		    wp->w_redraw_bot = lnum;
1015 		redraw_win_later(wp, VALID);
1016 	    }
1017 	}
1018 	else
1019 	    redraw_win_later(wp, VALID);
1020 	if (wp->w_redr_type != 0)
1021 	    doit = TRUE;
1022     }
1023 
1024     /* Return when there is nothing to do, screen updating is already
1025      * happening (recursive call), messages on the screen or still starting up.
1026      */
1027     if (!doit || updating_screen
1028 	    || State == ASKMORE || State == HITRETURN
1029 	    || msg_scrolled
1030 #ifdef FEAT_GUI
1031 	    || gui.starting
1032 #endif
1033 	    || starting)
1034 	return;
1035 
1036     /* update all windows that need updating */
1037     update_prepare();
1038 
1039     FOR_ALL_WINDOWS(wp)
1040     {
1041 	if (wp->w_redr_type != 0)
1042 	    win_update(wp);
1043 	if (wp->w_redr_status)
1044 	    win_redr_status(wp, FALSE);
1045     }
1046 
1047     update_finish();
1048 }
1049 #endif
1050 
1051 
1052 #if defined(FEAT_GUI) || defined(PROTO)
1053 /*
1054  * Update a single window, its status line and maybe the command line msg.
1055  * Used for the GUI scrollbar.
1056  */
1057     void
1058 updateWindow(win_T *wp)
1059 {
1060     /* return if already busy updating */
1061     if (updating_screen)
1062 	return;
1063 
1064     update_prepare();
1065 
1066 #ifdef FEAT_CLIPBOARD
1067     /* When Visual area changed, may have to update selection. */
1068     if (clip_star.available && clip_isautosel_star())
1069 	clip_update_selection(&clip_star);
1070     if (clip_plus.available && clip_isautosel_plus())
1071 	clip_update_selection(&clip_plus);
1072 #endif
1073 
1074     win_update(wp);
1075 
1076     /* When the screen was cleared redraw the tab pages line. */
1077     if (redraw_tabline)
1078 	draw_tabline();
1079 
1080     if (wp->w_redr_status
1081 # ifdef FEAT_CMDL_INFO
1082 	    || p_ru
1083 # endif
1084 # ifdef FEAT_STL_OPT
1085 	    || *p_stl != NUL || *wp->w_p_stl != NUL
1086 # endif
1087 	    )
1088 	win_redr_status(wp, FALSE);
1089 
1090     update_finish();
1091 }
1092 #endif
1093 
1094 /*
1095  * Update a single window.
1096  *
1097  * This may cause the windows below it also to be redrawn (when clearing the
1098  * screen or scrolling lines).
1099  *
1100  * How the window is redrawn depends on wp->w_redr_type.  Each type also
1101  * implies the one below it.
1102  * NOT_VALID	redraw the whole window
1103  * SOME_VALID	redraw the whole window but do scroll when possible
1104  * REDRAW_TOP	redraw the top w_upd_rows window lines, otherwise like VALID
1105  * INVERTED	redraw the changed part of the Visual area
1106  * INVERTED_ALL	redraw the whole Visual area
1107  * VALID	1. scroll up/down to adjust for a changed w_topline
1108  *		2. update lines at the top when scrolled down
1109  *		3. redraw changed text:
1110  *		   - if wp->w_buffer->b_mod_set set, update lines between
1111  *		     b_mod_top and b_mod_bot.
1112  *		   - if wp->w_redraw_top non-zero, redraw lines between
1113  *		     wp->w_redraw_top and wp->w_redr_bot.
1114  *		   - continue redrawing when syntax status is invalid.
1115  *		4. if scrolled up, update lines at the bottom.
1116  * This results in three areas that may need updating:
1117  * top:	from first row to top_end (when scrolled down)
1118  * mid: from mid_start to mid_end (update inversion or changed text)
1119  * bot: from bot_start to last row (when scrolled up)
1120  */
1121     static void
1122 win_update(win_T *wp)
1123 {
1124     buf_T	*buf = wp->w_buffer;
1125     int		type;
1126     int		top_end = 0;	/* Below last row of the top area that needs
1127 				   updating.  0 when no top area updating. */
1128     int		mid_start = 999;/* first row of the mid area that needs
1129 				   updating.  999 when no mid area updating. */
1130     int		mid_end = 0;	/* Below last row of the mid area that needs
1131 				   updating.  0 when no mid area updating. */
1132     int		bot_start = 999;/* first row of the bot area that needs
1133 				   updating.  999 when no bot area updating */
1134     int		scrolled_down = FALSE;	/* TRUE when scrolled down when
1135 					   w_topline got smaller a bit */
1136 #ifdef FEAT_SEARCH_EXTRA
1137     matchitem_T *cur;		/* points to the match list */
1138     int		top_to_mod = FALSE;    /* redraw above mod_top */
1139 #endif
1140 
1141     int		row;		/* current window row to display */
1142     linenr_T	lnum;		/* current buffer lnum to display */
1143     int		idx;		/* current index in w_lines[] */
1144     int		srow;		/* starting row of the current line */
1145 
1146     int		eof = FALSE;	/* if TRUE, we hit the end of the file */
1147     int		didline = FALSE; /* if TRUE, we finished the last line */
1148     int		i;
1149     long	j;
1150     static int	recursive = FALSE;	/* being called recursively */
1151     int		old_botline = wp->w_botline;
1152 #ifdef FEAT_FOLDING
1153     long	fold_count;
1154 #endif
1155 #ifdef FEAT_SYN_HL
1156     /* remember what happened to the previous line, to know if
1157      * check_visual_highlight() can be used */
1158 #define DID_NONE 1	/* didn't update a line */
1159 #define DID_LINE 2	/* updated a normal line */
1160 #define DID_FOLD 3	/* updated a folded line */
1161     int		did_update = DID_NONE;
1162     linenr_T	syntax_last_parsed = 0;		/* last parsed text line */
1163 #endif
1164     linenr_T	mod_top = 0;
1165     linenr_T	mod_bot = 0;
1166 #if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA)
1167     int		save_got_int;
1168 #endif
1169 #ifdef SYN_TIME_LIMIT
1170     proftime_T	syntax_tm;
1171 #endif
1172 
1173     type = wp->w_redr_type;
1174 
1175     if (type == NOT_VALID)
1176     {
1177 	wp->w_redr_status = TRUE;
1178 	wp->w_lines_valid = 0;
1179     }
1180 
1181     /* Window is zero-height: nothing to draw. */
1182     if (wp->w_height + WINBAR_HEIGHT(wp) == 0)
1183     {
1184 	wp->w_redr_type = 0;
1185 	return;
1186     }
1187 
1188     /* Window is zero-width: Only need to draw the separator. */
1189     if (wp->w_width == 0)
1190     {
1191 	/* draw the vertical separator right of this window */
1192 	draw_vsep_win(wp, 0);
1193 	wp->w_redr_type = 0;
1194 	return;
1195     }
1196 
1197 #ifdef FEAT_TERMINAL
1198     // If this window contains a terminal, redraw works completely differently.
1199     if (term_do_update_window(wp))
1200     {
1201 	term_update_window(wp);
1202 # ifdef FEAT_MENU
1203 	/* Draw the window toolbar, if there is one. */
1204 	if (winbar_height(wp) > 0)
1205 	    redraw_win_toolbar(wp);
1206 # endif
1207 	wp->w_redr_type = 0;
1208 	return;
1209     }
1210 #endif
1211 
1212 #ifdef FEAT_SEARCH_EXTRA
1213     init_search_hl(wp);
1214 #endif
1215 
1216 #ifdef FEAT_LINEBREAK
1217     /* Force redraw when width of 'number' or 'relativenumber' column
1218      * changes. */
1219     i = (wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) : 0;
1220     if (wp->w_nrwidth != i)
1221     {
1222 	type = NOT_VALID;
1223 	wp->w_nrwidth = i;
1224     }
1225     else
1226 #endif
1227 
1228     if (buf->b_mod_set && buf->b_mod_xlines != 0 && wp->w_redraw_top != 0)
1229     {
1230 	/*
1231 	 * When there are both inserted/deleted lines and specific lines to be
1232 	 * redrawn, w_redraw_top and w_redraw_bot may be invalid, just redraw
1233 	 * everything (only happens when redrawing is off for while).
1234 	 */
1235 	type = NOT_VALID;
1236     }
1237     else
1238     {
1239 	/*
1240 	 * Set mod_top to the first line that needs displaying because of
1241 	 * changes.  Set mod_bot to the first line after the changes.
1242 	 */
1243 	mod_top = wp->w_redraw_top;
1244 	if (wp->w_redraw_bot != 0)
1245 	    mod_bot = wp->w_redraw_bot + 1;
1246 	else
1247 	    mod_bot = 0;
1248 	wp->w_redraw_top = 0;	/* reset for next time */
1249 	wp->w_redraw_bot = 0;
1250 	if (buf->b_mod_set)
1251 	{
1252 	    if (mod_top == 0 || mod_top > buf->b_mod_top)
1253 	    {
1254 		mod_top = buf->b_mod_top;
1255 #ifdef FEAT_SYN_HL
1256 		/* Need to redraw lines above the change that may be included
1257 		 * in a pattern match. */
1258 		if (syntax_present(wp))
1259 		{
1260 		    mod_top -= buf->b_s.b_syn_sync_linebreaks;
1261 		    if (mod_top < 1)
1262 			mod_top = 1;
1263 		}
1264 #endif
1265 	    }
1266 	    if (mod_bot == 0 || mod_bot < buf->b_mod_bot)
1267 		mod_bot = buf->b_mod_bot;
1268 
1269 #ifdef FEAT_SEARCH_EXTRA
1270 	    /* When 'hlsearch' is on and using a multi-line search pattern, a
1271 	     * change in one line may make the Search highlighting in a
1272 	     * previous line invalid.  Simple solution: redraw all visible
1273 	     * lines above the change.
1274 	     * Same for a match pattern.
1275 	     */
1276 	    if (search_hl.rm.regprog != NULL
1277 					&& re_multiline(search_hl.rm.regprog))
1278 		top_to_mod = TRUE;
1279 	    else
1280 	    {
1281 		cur = wp->w_match_head;
1282 		while (cur != NULL)
1283 		{
1284 		    if (cur->match.regprog != NULL
1285 					   && re_multiline(cur->match.regprog))
1286 		    {
1287 			top_to_mod = TRUE;
1288 			break;
1289 		    }
1290 		    cur = cur->next;
1291 		}
1292 	    }
1293 #endif
1294 	}
1295 #ifdef FEAT_FOLDING
1296 	if (mod_top != 0 && hasAnyFolding(wp))
1297 	{
1298 	    linenr_T	lnumt, lnumb;
1299 
1300 	    /*
1301 	     * A change in a line can cause lines above it to become folded or
1302 	     * unfolded.  Find the top most buffer line that may be affected.
1303 	     * If the line was previously folded and displayed, get the first
1304 	     * line of that fold.  If the line is folded now, get the first
1305 	     * folded line.  Use the minimum of these two.
1306 	     */
1307 
1308 	    /* Find last valid w_lines[] entry above mod_top.  Set lnumt to
1309 	     * the line below it.  If there is no valid entry, use w_topline.
1310 	     * Find the first valid w_lines[] entry below mod_bot.  Set lnumb
1311 	     * to this line.  If there is no valid entry, use MAXLNUM. */
1312 	    lnumt = wp->w_topline;
1313 	    lnumb = MAXLNUM;
1314 	    for (i = 0; i < wp->w_lines_valid; ++i)
1315 		if (wp->w_lines[i].wl_valid)
1316 		{
1317 		    if (wp->w_lines[i].wl_lastlnum < mod_top)
1318 			lnumt = wp->w_lines[i].wl_lastlnum + 1;
1319 		    if (lnumb == MAXLNUM && wp->w_lines[i].wl_lnum >= mod_bot)
1320 		    {
1321 			lnumb = wp->w_lines[i].wl_lnum;
1322 			/* When there is a fold column it might need updating
1323 			 * in the next line ("J" just above an open fold). */
1324 			if (compute_foldcolumn(wp, 0) > 0)
1325 			    ++lnumb;
1326 		    }
1327 		}
1328 
1329 	    (void)hasFoldingWin(wp, mod_top, &mod_top, NULL, TRUE, NULL);
1330 	    if (mod_top > lnumt)
1331 		mod_top = lnumt;
1332 
1333 	    /* Now do the same for the bottom line (one above mod_bot). */
1334 	    --mod_bot;
1335 	    (void)hasFoldingWin(wp, mod_bot, NULL, &mod_bot, TRUE, NULL);
1336 	    ++mod_bot;
1337 	    if (mod_bot < lnumb)
1338 		mod_bot = lnumb;
1339 	}
1340 #endif
1341 
1342 	/* When a change starts above w_topline and the end is below
1343 	 * w_topline, start redrawing at w_topline.
1344 	 * If the end of the change is above w_topline: do like no change was
1345 	 * made, but redraw the first line to find changes in syntax. */
1346 	if (mod_top != 0 && mod_top < wp->w_topline)
1347 	{
1348 	    if (mod_bot > wp->w_topline)
1349 		mod_top = wp->w_topline;
1350 #ifdef FEAT_SYN_HL
1351 	    else if (syntax_present(wp))
1352 		top_end = 1;
1353 #endif
1354 	}
1355 
1356 	/* When line numbers are displayed need to redraw all lines below
1357 	 * inserted/deleted lines. */
1358 	if (mod_top != 0 && buf->b_mod_xlines != 0 && wp->w_p_nu)
1359 	    mod_bot = MAXLNUM;
1360     }
1361 
1362     /*
1363      * When only displaying the lines at the top, set top_end.  Used when
1364      * window has scrolled down for msg_scrolled.
1365      */
1366     if (type == REDRAW_TOP)
1367     {
1368 	j = 0;
1369 	for (i = 0; i < wp->w_lines_valid; ++i)
1370 	{
1371 	    j += wp->w_lines[i].wl_size;
1372 	    if (j >= wp->w_upd_rows)
1373 	    {
1374 		top_end = j;
1375 		break;
1376 	    }
1377 	}
1378 	if (top_end == 0)
1379 	    /* not found (cannot happen?): redraw everything */
1380 	    type = NOT_VALID;
1381 	else
1382 	    /* top area defined, the rest is VALID */
1383 	    type = VALID;
1384     }
1385 
1386     /* Trick: we want to avoid clearing the screen twice.  screenclear() will
1387      * set "screen_cleared" to TRUE.  The special value MAYBE (which is still
1388      * non-zero and thus not FALSE) will indicate that screenclear() was not
1389      * called. */
1390     if (screen_cleared)
1391 	screen_cleared = MAYBE;
1392 
1393     /*
1394      * If there are no changes on the screen that require a complete redraw,
1395      * handle three cases:
1396      * 1: we are off the top of the screen by a few lines: scroll down
1397      * 2: wp->w_topline is below wp->w_lines[0].wl_lnum: may scroll up
1398      * 3: wp->w_topline is wp->w_lines[0].wl_lnum: find first entry in
1399      *    w_lines[] that needs updating.
1400      */
1401     if ((type == VALID || type == SOME_VALID
1402 				  || type == INVERTED || type == INVERTED_ALL)
1403 #ifdef FEAT_DIFF
1404 	    && !wp->w_botfill && !wp->w_old_botfill
1405 #endif
1406 	    )
1407     {
1408 	if (mod_top != 0 && wp->w_topline == mod_top)
1409 	{
1410 	    /*
1411 	     * w_topline is the first changed line, the scrolling will be done
1412 	     * further down.
1413 	     */
1414 	}
1415 	else if (wp->w_lines[0].wl_valid
1416 		&& (wp->w_topline < wp->w_lines[0].wl_lnum
1417 #ifdef FEAT_DIFF
1418 		    || (wp->w_topline == wp->w_lines[0].wl_lnum
1419 			&& wp->w_topfill > wp->w_old_topfill)
1420 #endif
1421 		   ))
1422 	{
1423 	    /*
1424 	     * New topline is above old topline: May scroll down.
1425 	     */
1426 #ifdef FEAT_FOLDING
1427 	    if (hasAnyFolding(wp))
1428 	    {
1429 		linenr_T ln;
1430 
1431 		/* count the number of lines we are off, counting a sequence
1432 		 * of folded lines as one */
1433 		j = 0;
1434 		for (ln = wp->w_topline; ln < wp->w_lines[0].wl_lnum; ++ln)
1435 		{
1436 		    ++j;
1437 		    if (j >= wp->w_height - 2)
1438 			break;
1439 		    (void)hasFoldingWin(wp, ln, NULL, &ln, TRUE, NULL);
1440 		}
1441 	    }
1442 	    else
1443 #endif
1444 		j = wp->w_lines[0].wl_lnum - wp->w_topline;
1445 	    if (j < wp->w_height - 2)		/* not too far off */
1446 	    {
1447 		i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1);
1448 #ifdef FEAT_DIFF
1449 		/* insert extra lines for previously invisible filler lines */
1450 		if (wp->w_lines[0].wl_lnum != wp->w_topline)
1451 		    i += diff_check_fill(wp, wp->w_lines[0].wl_lnum)
1452 							  - wp->w_old_topfill;
1453 #endif
1454 		if (i < wp->w_height - 2)	/* less than a screen off */
1455 		{
1456 		    /*
1457 		     * Try to insert the correct number of lines.
1458 		     * If not the last window, delete the lines at the bottom.
1459 		     * win_ins_lines may fail when the terminal can't do it.
1460 		     */
1461 		    if (i > 0)
1462 			check_for_delay(FALSE);
1463 		    if (win_ins_lines(wp, 0, i, FALSE, wp == firstwin) == OK)
1464 		    {
1465 			if (wp->w_lines_valid != 0)
1466 			{
1467 			    /* Need to update rows that are new, stop at the
1468 			     * first one that scrolled down. */
1469 			    top_end = i;
1470 			    scrolled_down = TRUE;
1471 
1472 			    /* Move the entries that were scrolled, disable
1473 			     * the entries for the lines to be redrawn. */
1474 			    if ((wp->w_lines_valid += j) > wp->w_height)
1475 				wp->w_lines_valid = wp->w_height;
1476 			    for (idx = wp->w_lines_valid; idx - j >= 0; idx--)
1477 				wp->w_lines[idx] = wp->w_lines[idx - j];
1478 			    while (idx >= 0)
1479 				wp->w_lines[idx--].wl_valid = FALSE;
1480 			}
1481 		    }
1482 		    else
1483 			mid_start = 0;		/* redraw all lines */
1484 		}
1485 		else
1486 		    mid_start = 0;		/* redraw all lines */
1487 	    }
1488 	    else
1489 		mid_start = 0;		/* redraw all lines */
1490 	}
1491 	else
1492 	{
1493 	    /*
1494 	     * New topline is at or below old topline: May scroll up.
1495 	     * When topline didn't change, find first entry in w_lines[] that
1496 	     * needs updating.
1497 	     */
1498 
1499 	    /* try to find wp->w_topline in wp->w_lines[].wl_lnum */
1500 	    j = -1;
1501 	    row = 0;
1502 	    for (i = 0; i < wp->w_lines_valid; i++)
1503 	    {
1504 		if (wp->w_lines[i].wl_valid
1505 			&& wp->w_lines[i].wl_lnum == wp->w_topline)
1506 		{
1507 		    j = i;
1508 		    break;
1509 		}
1510 		row += wp->w_lines[i].wl_size;
1511 	    }
1512 	    if (j == -1)
1513 	    {
1514 		/* if wp->w_topline is not in wp->w_lines[].wl_lnum redraw all
1515 		 * lines */
1516 		mid_start = 0;
1517 	    }
1518 	    else
1519 	    {
1520 		/*
1521 		 * Try to delete the correct number of lines.
1522 		 * wp->w_topline is at wp->w_lines[i].wl_lnum.
1523 		 */
1524 #ifdef FEAT_DIFF
1525 		/* If the topline didn't change, delete old filler lines,
1526 		 * otherwise delete filler lines of the new topline... */
1527 		if (wp->w_lines[0].wl_lnum == wp->w_topline)
1528 		    row += wp->w_old_topfill;
1529 		else
1530 		    row += diff_check_fill(wp, wp->w_topline);
1531 		/* ... but don't delete new filler lines. */
1532 		row -= wp->w_topfill;
1533 #endif
1534 		if (row > 0)
1535 		{
1536 		    check_for_delay(FALSE);
1537 		    if (win_del_lines(wp, 0, row, FALSE, wp == firstwin, 0)
1538 									 == OK)
1539 			bot_start = wp->w_height - row;
1540 		    else
1541 			mid_start = 0;		/* redraw all lines */
1542 		}
1543 		if ((row == 0 || bot_start < 999) && wp->w_lines_valid != 0)
1544 		{
1545 		    /*
1546 		     * Skip the lines (below the deleted lines) that are still
1547 		     * valid and don't need redrawing.	Copy their info
1548 		     * upwards, to compensate for the deleted lines.  Set
1549 		     * bot_start to the first row that needs redrawing.
1550 		     */
1551 		    bot_start = 0;
1552 		    idx = 0;
1553 		    for (;;)
1554 		    {
1555 			wp->w_lines[idx] = wp->w_lines[j];
1556 			/* stop at line that didn't fit, unless it is still
1557 			 * valid (no lines deleted) */
1558 			if (row > 0 && bot_start + row
1559 				 + (int)wp->w_lines[j].wl_size > wp->w_height)
1560 			{
1561 			    wp->w_lines_valid = idx + 1;
1562 			    break;
1563 			}
1564 			bot_start += wp->w_lines[idx++].wl_size;
1565 
1566 			/* stop at the last valid entry in w_lines[].wl_size */
1567 			if (++j >= wp->w_lines_valid)
1568 			{
1569 			    wp->w_lines_valid = idx;
1570 			    break;
1571 			}
1572 		    }
1573 #ifdef FEAT_DIFF
1574 		    /* Correct the first entry for filler lines at the top
1575 		     * when it won't get updated below. */
1576 		    if (wp->w_p_diff && bot_start > 0)
1577 			wp->w_lines[0].wl_size =
1578 			    plines_win_nofill(wp, wp->w_topline, TRUE)
1579 							      + wp->w_topfill;
1580 #endif
1581 		}
1582 	    }
1583 	}
1584 
1585 	/* When starting redraw in the first line, redraw all lines.  When
1586 	 * there is only one window it's probably faster to clear the screen
1587 	 * first. */
1588 	if (mid_start == 0)
1589 	{
1590 	    mid_end = wp->w_height;
1591 	    if (ONE_WINDOW)
1592 	    {
1593 		/* Clear the screen when it was not done by win_del_lines() or
1594 		 * win_ins_lines() above, "screen_cleared" is FALSE or MAYBE
1595 		 * then. */
1596 		if (screen_cleared != TRUE)
1597 		    screenclear();
1598 		/* The screen was cleared, redraw the tab pages line. */
1599 		if (redraw_tabline)
1600 		    draw_tabline();
1601 	    }
1602 	}
1603 
1604 	/* When win_del_lines() or win_ins_lines() caused the screen to be
1605 	 * cleared (only happens for the first window) or when screenclear()
1606 	 * was called directly above, "must_redraw" will have been set to
1607 	 * NOT_VALID, need to reset it here to avoid redrawing twice. */
1608 	if (screen_cleared == TRUE)
1609 	    must_redraw = 0;
1610     }
1611     else
1612     {
1613 	/* Not VALID or INVERTED: redraw all lines. */
1614 	mid_start = 0;
1615 	mid_end = wp->w_height;
1616     }
1617 
1618     if (type == SOME_VALID)
1619     {
1620 	/* SOME_VALID: redraw all lines. */
1621 	mid_start = 0;
1622 	mid_end = wp->w_height;
1623 	type = NOT_VALID;
1624     }
1625 
1626     /* check if we are updating or removing the inverted part */
1627     if ((VIsual_active && buf == curwin->w_buffer)
1628 	    || (wp->w_old_cursor_lnum != 0 && type != NOT_VALID))
1629     {
1630 	linenr_T    from, to;
1631 
1632 	if (VIsual_active)
1633 	{
1634 	    if (VIsual_active
1635 		    && (VIsual_mode != wp->w_old_visual_mode
1636 			|| type == INVERTED_ALL))
1637 	    {
1638 		/*
1639 		 * If the type of Visual selection changed, redraw the whole
1640 		 * selection.  Also when the ownership of the X selection is
1641 		 * gained or lost.
1642 		 */
1643 		if (curwin->w_cursor.lnum < VIsual.lnum)
1644 		{
1645 		    from = curwin->w_cursor.lnum;
1646 		    to = VIsual.lnum;
1647 		}
1648 		else
1649 		{
1650 		    from = VIsual.lnum;
1651 		    to = curwin->w_cursor.lnum;
1652 		}
1653 		/* redraw more when the cursor moved as well */
1654 		if (wp->w_old_cursor_lnum < from)
1655 		    from = wp->w_old_cursor_lnum;
1656 		if (wp->w_old_cursor_lnum > to)
1657 		    to = wp->w_old_cursor_lnum;
1658 		if (wp->w_old_visual_lnum < from)
1659 		    from = wp->w_old_visual_lnum;
1660 		if (wp->w_old_visual_lnum > to)
1661 		    to = wp->w_old_visual_lnum;
1662 	    }
1663 	    else
1664 	    {
1665 		/*
1666 		 * Find the line numbers that need to be updated: The lines
1667 		 * between the old cursor position and the current cursor
1668 		 * position.  Also check if the Visual position changed.
1669 		 */
1670 		if (curwin->w_cursor.lnum < wp->w_old_cursor_lnum)
1671 		{
1672 		    from = curwin->w_cursor.lnum;
1673 		    to = wp->w_old_cursor_lnum;
1674 		}
1675 		else
1676 		{
1677 		    from = wp->w_old_cursor_lnum;
1678 		    to = curwin->w_cursor.lnum;
1679 		    if (from == 0)	/* Visual mode just started */
1680 			from = to;
1681 		}
1682 
1683 		if (VIsual.lnum != wp->w_old_visual_lnum
1684 					|| VIsual.col != wp->w_old_visual_col)
1685 		{
1686 		    if (wp->w_old_visual_lnum < from
1687 						&& wp->w_old_visual_lnum != 0)
1688 			from = wp->w_old_visual_lnum;
1689 		    if (wp->w_old_visual_lnum > to)
1690 			to = wp->w_old_visual_lnum;
1691 		    if (VIsual.lnum < from)
1692 			from = VIsual.lnum;
1693 		    if (VIsual.lnum > to)
1694 			to = VIsual.lnum;
1695 		}
1696 	    }
1697 
1698 	    /*
1699 	     * If in block mode and changed column or curwin->w_curswant:
1700 	     * update all lines.
1701 	     * First compute the actual start and end column.
1702 	     */
1703 	    if (VIsual_mode == Ctrl_V)
1704 	    {
1705 		colnr_T	    fromc, toc;
1706 #if defined(FEAT_VIRTUALEDIT) && defined(FEAT_LINEBREAK)
1707 		int	    save_ve_flags = ve_flags;
1708 
1709 		if (curwin->w_p_lbr)
1710 		    ve_flags = VE_ALL;
1711 #endif
1712 		getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc);
1713 #if defined(FEAT_VIRTUALEDIT) && defined(FEAT_LINEBREAK)
1714 		ve_flags = save_ve_flags;
1715 #endif
1716 		++toc;
1717 		if (curwin->w_curswant == MAXCOL)
1718 		    toc = MAXCOL;
1719 
1720 		if (fromc != wp->w_old_cursor_fcol
1721 			|| toc != wp->w_old_cursor_lcol)
1722 		{
1723 		    if (from > VIsual.lnum)
1724 			from = VIsual.lnum;
1725 		    if (to < VIsual.lnum)
1726 			to = VIsual.lnum;
1727 		}
1728 		wp->w_old_cursor_fcol = fromc;
1729 		wp->w_old_cursor_lcol = toc;
1730 	    }
1731 	}
1732 	else
1733 	{
1734 	    /* Use the line numbers of the old Visual area. */
1735 	    if (wp->w_old_cursor_lnum < wp->w_old_visual_lnum)
1736 	    {
1737 		from = wp->w_old_cursor_lnum;
1738 		to = wp->w_old_visual_lnum;
1739 	    }
1740 	    else
1741 	    {
1742 		from = wp->w_old_visual_lnum;
1743 		to = wp->w_old_cursor_lnum;
1744 	    }
1745 	}
1746 
1747 	/*
1748 	 * There is no need to update lines above the top of the window.
1749 	 */
1750 	if (from < wp->w_topline)
1751 	    from = wp->w_topline;
1752 
1753 	/*
1754 	 * If we know the value of w_botline, use it to restrict the update to
1755 	 * the lines that are visible in the window.
1756 	 */
1757 	if (wp->w_valid & VALID_BOTLINE)
1758 	{
1759 	    if (from >= wp->w_botline)
1760 		from = wp->w_botline - 1;
1761 	    if (to >= wp->w_botline)
1762 		to = wp->w_botline - 1;
1763 	}
1764 
1765 	/*
1766 	 * Find the minimal part to be updated.
1767 	 * Watch out for scrolling that made entries in w_lines[] invalid.
1768 	 * E.g., CTRL-U makes the first half of w_lines[] invalid and sets
1769 	 * top_end; need to redraw from top_end to the "to" line.
1770 	 * A middle mouse click with a Visual selection may change the text
1771 	 * above the Visual area and reset wl_valid, do count these for
1772 	 * mid_end (in srow).
1773 	 */
1774 	if (mid_start > 0)
1775 	{
1776 	    lnum = wp->w_topline;
1777 	    idx = 0;
1778 	    srow = 0;
1779 	    if (scrolled_down)
1780 		mid_start = top_end;
1781 	    else
1782 		mid_start = 0;
1783 	    while (lnum < from && idx < wp->w_lines_valid)	/* find start */
1784 	    {
1785 		if (wp->w_lines[idx].wl_valid)
1786 		    mid_start += wp->w_lines[idx].wl_size;
1787 		else if (!scrolled_down)
1788 		    srow += wp->w_lines[idx].wl_size;
1789 		++idx;
1790 # ifdef FEAT_FOLDING
1791 		if (idx < wp->w_lines_valid && wp->w_lines[idx].wl_valid)
1792 		    lnum = wp->w_lines[idx].wl_lnum;
1793 		else
1794 # endif
1795 		    ++lnum;
1796 	    }
1797 	    srow += mid_start;
1798 	    mid_end = wp->w_height;
1799 	    for ( ; idx < wp->w_lines_valid; ++idx)		/* find end */
1800 	    {
1801 		if (wp->w_lines[idx].wl_valid
1802 			&& wp->w_lines[idx].wl_lnum >= to + 1)
1803 		{
1804 		    /* Only update until first row of this line */
1805 		    mid_end = srow;
1806 		    break;
1807 		}
1808 		srow += wp->w_lines[idx].wl_size;
1809 	    }
1810 	}
1811     }
1812 
1813     if (VIsual_active && buf == curwin->w_buffer)
1814     {
1815 	wp->w_old_visual_mode = VIsual_mode;
1816 	wp->w_old_cursor_lnum = curwin->w_cursor.lnum;
1817 	wp->w_old_visual_lnum = VIsual.lnum;
1818 	wp->w_old_visual_col = VIsual.col;
1819 	wp->w_old_curswant = curwin->w_curswant;
1820     }
1821     else
1822     {
1823 	wp->w_old_visual_mode = 0;
1824 	wp->w_old_cursor_lnum = 0;
1825 	wp->w_old_visual_lnum = 0;
1826 	wp->w_old_visual_col = 0;
1827     }
1828 
1829 #if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA)
1830     /* reset got_int, otherwise regexp won't work */
1831     save_got_int = got_int;
1832     got_int = 0;
1833 #endif
1834 #ifdef SYN_TIME_LIMIT
1835     /* Set the time limit to 'redrawtime'. */
1836     profile_setlimit(p_rdt, &syntax_tm);
1837     syn_set_timeout(&syntax_tm);
1838 #endif
1839 #ifdef FEAT_FOLDING
1840     win_foldinfo.fi_level = 0;
1841 #endif
1842 
1843 #ifdef FEAT_MENU
1844     /*
1845      * Draw the window toolbar, if there is one.
1846      * TODO: only when needed.
1847      */
1848     if (winbar_height(wp) > 0)
1849 	redraw_win_toolbar(wp);
1850 #endif
1851 
1852     /*
1853      * Update all the window rows.
1854      */
1855     idx = 0;		/* first entry in w_lines[].wl_size */
1856     row = 0;
1857     srow = 0;
1858     lnum = wp->w_topline;	/* first line shown in window */
1859     for (;;)
1860     {
1861 	/* stop updating when reached the end of the window (check for _past_
1862 	 * the end of the window is at the end of the loop) */
1863 	if (row == wp->w_height)
1864 	{
1865 	    didline = TRUE;
1866 	    break;
1867 	}
1868 
1869 	/* stop updating when hit the end of the file */
1870 	if (lnum > buf->b_ml.ml_line_count)
1871 	{
1872 	    eof = TRUE;
1873 	    break;
1874 	}
1875 
1876 	/* Remember the starting row of the line that is going to be dealt
1877 	 * with.  It is used further down when the line doesn't fit. */
1878 	srow = row;
1879 
1880 	/*
1881 	 * Update a line when it is in an area that needs updating, when it
1882 	 * has changes or w_lines[idx] is invalid.
1883 	 * bot_start may be halfway a wrapped line after using
1884 	 * win_del_lines(), check if the current line includes it.
1885 	 * When syntax folding is being used, the saved syntax states will
1886 	 * already have been updated, we can't see where the syntax state is
1887 	 * the same again, just update until the end of the window.
1888 	 */
1889 	if (row < top_end
1890 		|| (row >= mid_start && row < mid_end)
1891 #ifdef FEAT_SEARCH_EXTRA
1892 		|| top_to_mod
1893 #endif
1894 		|| idx >= wp->w_lines_valid
1895 		|| (row + wp->w_lines[idx].wl_size > bot_start)
1896 		|| (mod_top != 0
1897 		    && (lnum == mod_top
1898 			|| (lnum >= mod_top
1899 			    && (lnum < mod_bot
1900 #ifdef FEAT_SYN_HL
1901 				|| did_update == DID_FOLD
1902 				|| (did_update == DID_LINE
1903 				    && syntax_present(wp)
1904 				    && (
1905 # ifdef FEAT_FOLDING
1906 					(foldmethodIsSyntax(wp)
1907 						      && hasAnyFolding(wp)) ||
1908 # endif
1909 					syntax_check_changed(lnum)))
1910 #endif
1911 #ifdef FEAT_SEARCH_EXTRA
1912 				/* match in fixed position might need redraw
1913 				 * if lines were inserted or deleted */
1914 				|| (wp->w_match_head != NULL
1915 						    && buf->b_mod_xlines != 0)
1916 #endif
1917 				)))))
1918 	{
1919 #ifdef FEAT_SEARCH_EXTRA
1920 	    if (lnum == mod_top)
1921 		top_to_mod = FALSE;
1922 #endif
1923 
1924 	    /*
1925 	     * When at start of changed lines: May scroll following lines
1926 	     * up or down to minimize redrawing.
1927 	     * Don't do this when the change continues until the end.
1928 	     * Don't scroll when dollar_vcol >= 0, keep the "$".
1929 	     */
1930 	    if (lnum == mod_top
1931 		    && mod_bot != MAXLNUM
1932 		    && !(dollar_vcol >= 0 && mod_bot == mod_top + 1))
1933 	    {
1934 		int		old_rows = 0;
1935 		int		new_rows = 0;
1936 		int		xtra_rows;
1937 		linenr_T	l;
1938 
1939 		/* Count the old number of window rows, using w_lines[], which
1940 		 * should still contain the sizes for the lines as they are
1941 		 * currently displayed. */
1942 		for (i = idx; i < wp->w_lines_valid; ++i)
1943 		{
1944 		    /* Only valid lines have a meaningful wl_lnum.  Invalid
1945 		     * lines are part of the changed area. */
1946 		    if (wp->w_lines[i].wl_valid
1947 			    && wp->w_lines[i].wl_lnum == mod_bot)
1948 			break;
1949 		    old_rows += wp->w_lines[i].wl_size;
1950 #ifdef FEAT_FOLDING
1951 		    if (wp->w_lines[i].wl_valid
1952 			    && wp->w_lines[i].wl_lastlnum + 1 == mod_bot)
1953 		    {
1954 			/* Must have found the last valid entry above mod_bot.
1955 			 * Add following invalid entries. */
1956 			++i;
1957 			while (i < wp->w_lines_valid
1958 						  && !wp->w_lines[i].wl_valid)
1959 			    old_rows += wp->w_lines[i++].wl_size;
1960 			break;
1961 		    }
1962 #endif
1963 		}
1964 
1965 		if (i >= wp->w_lines_valid)
1966 		{
1967 		    /* We can't find a valid line below the changed lines,
1968 		     * need to redraw until the end of the window.
1969 		     * Inserting/deleting lines has no use. */
1970 		    bot_start = 0;
1971 		}
1972 		else
1973 		{
1974 		    /* Able to count old number of rows: Count new window
1975 		     * rows, and may insert/delete lines */
1976 		    j = idx;
1977 		    for (l = lnum; l < mod_bot; ++l)
1978 		    {
1979 #ifdef FEAT_FOLDING
1980 			if (hasFoldingWin(wp, l, NULL, &l, TRUE, NULL))
1981 			    ++new_rows;
1982 			else
1983 #endif
1984 #ifdef FEAT_DIFF
1985 			    if (l == wp->w_topline)
1986 			    new_rows += plines_win_nofill(wp, l, TRUE)
1987 							      + wp->w_topfill;
1988 			else
1989 #endif
1990 			    new_rows += plines_win(wp, l, TRUE);
1991 			++j;
1992 			if (new_rows > wp->w_height - row - 2)
1993 			{
1994 			    /* it's getting too much, must redraw the rest */
1995 			    new_rows = 9999;
1996 			    break;
1997 			}
1998 		    }
1999 		    xtra_rows = new_rows - old_rows;
2000 		    if (xtra_rows < 0)
2001 		    {
2002 			/* May scroll text up.  If there is not enough
2003 			 * remaining text or scrolling fails, must redraw the
2004 			 * rest.  If scrolling works, must redraw the text
2005 			 * below the scrolled text. */
2006 			if (row - xtra_rows >= wp->w_height - 2)
2007 			    mod_bot = MAXLNUM;
2008 			else
2009 			{
2010 			    check_for_delay(FALSE);
2011 			    if (win_del_lines(wp, row,
2012 					  -xtra_rows, FALSE, FALSE, 0) == FAIL)
2013 				mod_bot = MAXLNUM;
2014 			    else
2015 				bot_start = wp->w_height + xtra_rows;
2016 			}
2017 		    }
2018 		    else if (xtra_rows > 0)
2019 		    {
2020 			/* May scroll text down.  If there is not enough
2021 			 * remaining text of scrolling fails, must redraw the
2022 			 * rest. */
2023 			if (row + xtra_rows >= wp->w_height - 2)
2024 			    mod_bot = MAXLNUM;
2025 			else
2026 			{
2027 			    check_for_delay(FALSE);
2028 			    if (win_ins_lines(wp, row + old_rows,
2029 					     xtra_rows, FALSE, FALSE) == FAIL)
2030 				mod_bot = MAXLNUM;
2031 			    else if (top_end > row + old_rows)
2032 				/* Scrolled the part at the top that requires
2033 				 * updating down. */
2034 				top_end += xtra_rows;
2035 			}
2036 		    }
2037 
2038 		    /* When not updating the rest, may need to move w_lines[]
2039 		     * entries. */
2040 		    if (mod_bot != MAXLNUM && i != j)
2041 		    {
2042 			if (j < i)
2043 			{
2044 			    int x = row + new_rows;
2045 
2046 			    /* move entries in w_lines[] upwards */
2047 			    for (;;)
2048 			    {
2049 				/* stop at last valid entry in w_lines[] */
2050 				if (i >= wp->w_lines_valid)
2051 				{
2052 				    wp->w_lines_valid = j;
2053 				    break;
2054 				}
2055 				wp->w_lines[j] = wp->w_lines[i];
2056 				/* stop at a line that won't fit */
2057 				if (x + (int)wp->w_lines[j].wl_size
2058 							   > wp->w_height)
2059 				{
2060 				    wp->w_lines_valid = j + 1;
2061 				    break;
2062 				}
2063 				x += wp->w_lines[j++].wl_size;
2064 				++i;
2065 			    }
2066 			    if (bot_start > x)
2067 				bot_start = x;
2068 			}
2069 			else /* j > i */
2070 			{
2071 			    /* move entries in w_lines[] downwards */
2072 			    j -= i;
2073 			    wp->w_lines_valid += j;
2074 			    if (wp->w_lines_valid > wp->w_height)
2075 				wp->w_lines_valid = wp->w_height;
2076 			    for (i = wp->w_lines_valid; i - j >= idx; --i)
2077 				wp->w_lines[i] = wp->w_lines[i - j];
2078 
2079 			    /* The w_lines[] entries for inserted lines are
2080 			     * now invalid, but wl_size may be used above.
2081 			     * Reset to zero. */
2082 			    while (i >= idx)
2083 			    {
2084 				wp->w_lines[i].wl_size = 0;
2085 				wp->w_lines[i--].wl_valid = FALSE;
2086 			    }
2087 			}
2088 		    }
2089 		}
2090 	    }
2091 
2092 #ifdef FEAT_FOLDING
2093 	    /*
2094 	     * When lines are folded, display one line for all of them.
2095 	     * Otherwise, display normally (can be several display lines when
2096 	     * 'wrap' is on).
2097 	     */
2098 	    fold_count = foldedCount(wp, lnum, &win_foldinfo);
2099 	    if (fold_count != 0)
2100 	    {
2101 		fold_line(wp, fold_count, &win_foldinfo, lnum, row);
2102 		++row;
2103 		--fold_count;
2104 		wp->w_lines[idx].wl_folded = TRUE;
2105 		wp->w_lines[idx].wl_lastlnum = lnum + fold_count;
2106 # ifdef FEAT_SYN_HL
2107 		did_update = DID_FOLD;
2108 # endif
2109 	    }
2110 	    else
2111 #endif
2112 	    if (idx < wp->w_lines_valid
2113 		    && wp->w_lines[idx].wl_valid
2114 		    && wp->w_lines[idx].wl_lnum == lnum
2115 		    && lnum > wp->w_topline
2116 		    && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
2117 		    && srow + wp->w_lines[idx].wl_size > wp->w_height
2118 #ifdef FEAT_DIFF
2119 		    && diff_check_fill(wp, lnum) == 0
2120 #endif
2121 		    )
2122 	    {
2123 		/* This line is not going to fit.  Don't draw anything here,
2124 		 * will draw "@  " lines below. */
2125 		row = wp->w_height + 1;
2126 	    }
2127 	    else
2128 	    {
2129 #ifdef FEAT_SEARCH_EXTRA
2130 		prepare_search_hl(wp, lnum);
2131 #endif
2132 #ifdef FEAT_SYN_HL
2133 		/* Let the syntax stuff know we skipped a few lines. */
2134 		if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum
2135 						       && syntax_present(wp))
2136 		    syntax_end_parsing(syntax_last_parsed + 1);
2137 #endif
2138 
2139 		/*
2140 		 * Display one line.
2141 		 */
2142 		row = win_line(wp, lnum, srow, wp->w_height, mod_top == 0);
2143 
2144 #ifdef FEAT_FOLDING
2145 		wp->w_lines[idx].wl_folded = FALSE;
2146 		wp->w_lines[idx].wl_lastlnum = lnum;
2147 #endif
2148 #ifdef FEAT_SYN_HL
2149 		did_update = DID_LINE;
2150 		syntax_last_parsed = lnum;
2151 #endif
2152 	    }
2153 
2154 	    wp->w_lines[idx].wl_lnum = lnum;
2155 	    wp->w_lines[idx].wl_valid = TRUE;
2156 
2157 	    /* Past end of the window or end of the screen. Note that after
2158 	     * resizing wp->w_height may be end up too big. That's a problem
2159 	     * elsewhere, but prevent a crash here. */
2160 	    if (row > wp->w_height || row + wp->w_winrow >= Rows)
2161 	    {
2162 		/* we may need the size of that too long line later on */
2163 		if (dollar_vcol == -1)
2164 		    wp->w_lines[idx].wl_size = plines_win(wp, lnum, TRUE);
2165 		++idx;
2166 		break;
2167 	    }
2168 	    if (dollar_vcol == -1)
2169 		wp->w_lines[idx].wl_size = row - srow;
2170 	    ++idx;
2171 #ifdef FEAT_FOLDING
2172 	    lnum += fold_count + 1;
2173 #else
2174 	    ++lnum;
2175 #endif
2176 	}
2177 	else
2178 	{
2179 	    /* This line does not need updating, advance to the next one */
2180 	    row += wp->w_lines[idx++].wl_size;
2181 	    if (row > wp->w_height)	/* past end of screen */
2182 		break;
2183 #ifdef FEAT_FOLDING
2184 	    lnum = wp->w_lines[idx - 1].wl_lastlnum + 1;
2185 #else
2186 	    ++lnum;
2187 #endif
2188 #ifdef FEAT_SYN_HL
2189 	    did_update = DID_NONE;
2190 #endif
2191 	}
2192 
2193 	if (lnum > buf->b_ml.ml_line_count)
2194 	{
2195 	    eof = TRUE;
2196 	    break;
2197 	}
2198     }
2199     /*
2200      * End of loop over all window lines.
2201      */
2202 
2203 #ifdef FEAT_VTP
2204     /* Rewrite the character at the end of the screen line. */
2205     if (use_vtp())
2206     {
2207 	int i;
2208 
2209 	for (i = 0; i < Rows; ++i)
2210 # ifdef FEAT_MBYTE
2211 	    if (enc_utf8)
2212 		if ((*mb_off2cells)(LineOffset[i] + Columns - 2,
2213 					   LineOffset[i] + screen_Columns) > 1)
2214 		    screen_draw_rectangle(i, Columns - 2, 1, 2, FALSE);
2215 		else
2216 		    screen_draw_rectangle(i, Columns - 1, 1, 1, FALSE);
2217 	    else
2218 # endif
2219 		screen_char(LineOffset[i] + Columns - 1, i, Columns - 1);
2220     }
2221 #endif
2222 
2223     if (idx > wp->w_lines_valid)
2224 	wp->w_lines_valid = idx;
2225 
2226 #ifdef FEAT_SYN_HL
2227     /*
2228      * Let the syntax stuff know we stop parsing here.
2229      */
2230     if (syntax_last_parsed != 0 && syntax_present(wp))
2231 	syntax_end_parsing(syntax_last_parsed + 1);
2232 #endif
2233 
2234     /*
2235      * If we didn't hit the end of the file, and we didn't finish the last
2236      * line we were working on, then the line didn't fit.
2237      */
2238     wp->w_empty_rows = 0;
2239 #ifdef FEAT_DIFF
2240     wp->w_filler_rows = 0;
2241 #endif
2242     if (!eof && !didline)
2243     {
2244 	if (lnum == wp->w_topline)
2245 	{
2246 	    /*
2247 	     * Single line that does not fit!
2248 	     * Don't overwrite it, it can be edited.
2249 	     */
2250 	    wp->w_botline = lnum + 1;
2251 	}
2252 #ifdef FEAT_DIFF
2253 	else if (diff_check_fill(wp, lnum) >= wp->w_height - srow)
2254 	{
2255 	    /* Window ends in filler lines. */
2256 	    wp->w_botline = lnum;
2257 	    wp->w_filler_rows = wp->w_height - srow;
2258 	}
2259 #endif
2260 	else if (dy_flags & DY_TRUNCATE)	/* 'display' has "truncate" */
2261 	{
2262 	    int scr_row = W_WINROW(wp) + wp->w_height - 1;
2263 
2264 	    /*
2265 	     * Last line isn't finished: Display "@@@" in the last screen line.
2266 	     */
2267 	    screen_puts_len((char_u *)"@@", 2, scr_row, wp->w_wincol,
2268 							      HL_ATTR(HLF_AT));
2269 	    screen_fill(scr_row, scr_row + 1,
2270 		    (int)wp->w_wincol + 2, (int)W_ENDCOL(wp),
2271 		    '@', ' ', HL_ATTR(HLF_AT));
2272 	    set_empty_rows(wp, srow);
2273 	    wp->w_botline = lnum;
2274 	}
2275 	else if (dy_flags & DY_LASTLINE)	/* 'display' has "lastline" */
2276 	{
2277 	    /*
2278 	     * Last line isn't finished: Display "@@@" at the end.
2279 	     */
2280 	    screen_fill(W_WINROW(wp) + wp->w_height - 1,
2281 		    W_WINROW(wp) + wp->w_height,
2282 		    (int)W_ENDCOL(wp) - 3, (int)W_ENDCOL(wp),
2283 		    '@', '@', HL_ATTR(HLF_AT));
2284 	    set_empty_rows(wp, srow);
2285 	    wp->w_botline = lnum;
2286 	}
2287 	else
2288 	{
2289 	    win_draw_end(wp, '@', ' ', srow, wp->w_height, HLF_AT);
2290 	    wp->w_botline = lnum;
2291 	}
2292     }
2293     else
2294     {
2295 	draw_vsep_win(wp, row);
2296 	if (eof)		/* we hit the end of the file */
2297 	{
2298 	    wp->w_botline = buf->b_ml.ml_line_count + 1;
2299 #ifdef FEAT_DIFF
2300 	    j = diff_check_fill(wp, wp->w_botline);
2301 	    if (j > 0 && !wp->w_botfill)
2302 	    {
2303 		/*
2304 		 * Display filler lines at the end of the file
2305 		 */
2306 		if (char2cells(fill_diff) > 1)
2307 		    i = '-';
2308 		else
2309 		    i = fill_diff;
2310 		if (row + j > wp->w_height)
2311 		    j = wp->w_height - row;
2312 		win_draw_end(wp, i, i, row, row + (int)j, HLF_DED);
2313 		row += j;
2314 	    }
2315 #endif
2316 	}
2317 	else if (dollar_vcol == -1)
2318 	    wp->w_botline = lnum;
2319 
2320 	/* make sure the rest of the screen is blank */
2321 	/* put '~'s on rows that aren't part of the file. */
2322 	win_draw_end(wp, '~', ' ', row, wp->w_height, HLF_EOB);
2323     }
2324 
2325 #ifdef SYN_TIME_LIMIT
2326     syn_set_timeout(NULL);
2327 #endif
2328 
2329     /* Reset the type of redrawing required, the window has been updated. */
2330     wp->w_redr_type = 0;
2331 #ifdef FEAT_DIFF
2332     wp->w_old_topfill = wp->w_topfill;
2333     wp->w_old_botfill = wp->w_botfill;
2334 #endif
2335 
2336     if (dollar_vcol == -1)
2337     {
2338 	/*
2339 	 * There is a trick with w_botline.  If we invalidate it on each
2340 	 * change that might modify it, this will cause a lot of expensive
2341 	 * calls to plines() in update_topline() each time.  Therefore the
2342 	 * value of w_botline is often approximated, and this value is used to
2343 	 * compute the value of w_topline.  If the value of w_botline was
2344 	 * wrong, check that the value of w_topline is correct (cursor is on
2345 	 * the visible part of the text).  If it's not, we need to redraw
2346 	 * again.  Mostly this just means scrolling up a few lines, so it
2347 	 * doesn't look too bad.  Only do this for the current window (where
2348 	 * changes are relevant).
2349 	 */
2350 	wp->w_valid |= VALID_BOTLINE;
2351 	if (wp == curwin && wp->w_botline != old_botline && !recursive)
2352 	{
2353 	    recursive = TRUE;
2354 	    curwin->w_valid &= ~VALID_TOPLINE;
2355 	    update_topline();	/* may invalidate w_botline again */
2356 	    if (must_redraw != 0)
2357 	    {
2358 		/* Don't update for changes in buffer again. */
2359 		i = curbuf->b_mod_set;
2360 		curbuf->b_mod_set = FALSE;
2361 		win_update(curwin);
2362 		must_redraw = 0;
2363 		curbuf->b_mod_set = i;
2364 	    }
2365 	    recursive = FALSE;
2366 	}
2367     }
2368 
2369 #if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA)
2370     /* restore got_int, unless CTRL-C was hit while redrawing */
2371     if (!got_int)
2372 	got_int = save_got_int;
2373 #endif
2374 }
2375 
2376 /*
2377  * Clear the rest of the window and mark the unused lines with "c1".  use "c2"
2378  * as the filler character.
2379  */
2380     static void
2381 win_draw_end(
2382     win_T	*wp,
2383     int		c1,
2384     int		c2,
2385     int		row,
2386     int		endrow,
2387     hlf_T	hl)
2388 {
2389 #if defined(FEAT_FOLDING) || defined(FEAT_SIGNS) || defined(FEAT_CMDWIN)
2390     int		n = 0;
2391 # define FDC_OFF n
2392 #else
2393 # define FDC_OFF 0
2394 #endif
2395 #ifdef FEAT_FOLDING
2396     int		fdc = compute_foldcolumn(wp, 0);
2397 #endif
2398 
2399 #ifdef FEAT_RIGHTLEFT
2400     if (wp->w_p_rl)
2401     {
2402 	/* No check for cmdline window: should never be right-left. */
2403 # ifdef FEAT_FOLDING
2404 	n = fdc;
2405 
2406 	if (n > 0)
2407 	{
2408 	    /* draw the fold column at the right */
2409 	    if (n > wp->w_width)
2410 		n = wp->w_width;
2411 	    screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
2412 		    W_ENDCOL(wp) - n, (int)W_ENDCOL(wp),
2413 		    ' ', ' ', HL_ATTR(HLF_FC));
2414 	}
2415 # endif
2416 # ifdef FEAT_SIGNS
2417 	if (signcolumn_on(wp))
2418 	{
2419 	    int nn = n + 2;
2420 
2421 	    /* draw the sign column left of the fold column */
2422 	    if (nn > wp->w_width)
2423 		nn = wp->w_width;
2424 	    screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
2425 		    W_ENDCOL(wp) - nn, (int)W_ENDCOL(wp) - n,
2426 		    ' ', ' ', HL_ATTR(HLF_SC));
2427 	    n = nn;
2428 	}
2429 # endif
2430 	screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
2431 		wp->w_wincol, W_ENDCOL(wp) - 1 - FDC_OFF,
2432 		c2, c2, HL_ATTR(hl));
2433 	screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
2434 		W_ENDCOL(wp) - 1 - FDC_OFF, W_ENDCOL(wp) - FDC_OFF,
2435 		c1, c2, HL_ATTR(hl));
2436     }
2437     else
2438 #endif
2439     {
2440 #ifdef FEAT_CMDWIN
2441 	if (cmdwin_type != 0 && wp == curwin)
2442 	{
2443 	    /* draw the cmdline character in the leftmost column */
2444 	    n = 1;
2445 	    if (n > wp->w_width)
2446 		n = wp->w_width;
2447 	    screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
2448 		    wp->w_wincol, (int)wp->w_wincol + n,
2449 		    cmdwin_type, ' ', HL_ATTR(HLF_AT));
2450 	}
2451 #endif
2452 #ifdef FEAT_FOLDING
2453 	if (fdc > 0)
2454 	{
2455 	    int	    nn = n + fdc;
2456 
2457 	    /* draw the fold column at the left */
2458 	    if (nn > wp->w_width)
2459 		nn = wp->w_width;
2460 	    screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
2461 		    wp->w_wincol + n, (int)wp->w_wincol + nn,
2462 		    ' ', ' ', HL_ATTR(HLF_FC));
2463 	    n = nn;
2464 	}
2465 #endif
2466 #ifdef FEAT_SIGNS
2467 	if (signcolumn_on(wp))
2468 	{
2469 	    int	    nn = n + 2;
2470 
2471 	    /* draw the sign column after the fold column */
2472 	    if (nn > wp->w_width)
2473 		nn = wp->w_width;
2474 	    screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
2475 		    wp->w_wincol + n, (int)wp->w_wincol + nn,
2476 		    ' ', ' ', HL_ATTR(HLF_SC));
2477 	    n = nn;
2478 	}
2479 #endif
2480 	screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
2481 		wp->w_wincol + FDC_OFF, (int)W_ENDCOL(wp),
2482 		c1, c2, HL_ATTR(hl));
2483     }
2484     set_empty_rows(wp, row);
2485 }
2486 
2487 #ifdef FEAT_SYN_HL
2488 static int advance_color_col(int vcol, int **color_cols);
2489 
2490 /*
2491  * Advance **color_cols and return TRUE when there are columns to draw.
2492  */
2493     static int
2494 advance_color_col(int vcol, int **color_cols)
2495 {
2496     while (**color_cols >= 0 && vcol > **color_cols)
2497 	++*color_cols;
2498     return (**color_cols >= 0);
2499 }
2500 #endif
2501 
2502 #if defined(FEAT_MENU) || defined(FEAT_FOLDING)
2503 /*
2504  * Copy "text" to ScreenLines using "attr".
2505  * Returns the next screen column.
2506  */
2507     static int
2508 text_to_screenline(win_T *wp, char_u *text, int col)
2509 {
2510     int		off = (int)(current_ScreenLine - ScreenLines);
2511 
2512 #ifdef FEAT_MBYTE
2513     if (has_mbyte)
2514     {
2515 	int	cells;
2516 	int	u8c, u8cc[MAX_MCO];
2517 	int	i;
2518 	int	idx;
2519 	int	c_len;
2520 	char_u	*p;
2521 # ifdef FEAT_ARABIC
2522 	int	prev_c = 0;		/* previous Arabic character */
2523 	int	prev_c1 = 0;		/* first composing char for prev_c */
2524 # endif
2525 
2526 # ifdef FEAT_RIGHTLEFT
2527 	if (wp->w_p_rl)
2528 	    idx = off;
2529 	else
2530 # endif
2531 	    idx = off + col;
2532 
2533 	/* Store multibyte characters in ScreenLines[] et al. correctly. */
2534 	for (p = text; *p != NUL; )
2535 	{
2536 	    cells = (*mb_ptr2cells)(p);
2537 	    c_len = (*mb_ptr2len)(p);
2538 	    if (col + cells > wp->w_width
2539 # ifdef FEAT_RIGHTLEFT
2540 		    - (wp->w_p_rl ? col : 0)
2541 # endif
2542 		    )
2543 		break;
2544 	    ScreenLines[idx] = *p;
2545 	    if (enc_utf8)
2546 	    {
2547 		u8c = utfc_ptr2char(p, u8cc);
2548 		if (*p < 0x80 && u8cc[0] == 0)
2549 		{
2550 		    ScreenLinesUC[idx] = 0;
2551 #ifdef FEAT_ARABIC
2552 		    prev_c = u8c;
2553 #endif
2554 		}
2555 		else
2556 		{
2557 #ifdef FEAT_ARABIC
2558 		    if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c))
2559 		    {
2560 			/* Do Arabic shaping. */
2561 			int	pc, pc1, nc;
2562 			int	pcc[MAX_MCO];
2563 			int	firstbyte = *p;
2564 
2565 			/* The idea of what is the previous and next
2566 			 * character depends on 'rightleft'. */
2567 			if (wp->w_p_rl)
2568 			{
2569 			    pc = prev_c;
2570 			    pc1 = prev_c1;
2571 			    nc = utf_ptr2char(p + c_len);
2572 			    prev_c1 = u8cc[0];
2573 			}
2574 			else
2575 			{
2576 			    pc = utfc_ptr2char(p + c_len, pcc);
2577 			    nc = prev_c;
2578 			    pc1 = pcc[0];
2579 			}
2580 			prev_c = u8c;
2581 
2582 			u8c = arabic_shape(u8c, &firstbyte, &u8cc[0],
2583 								 pc, pc1, nc);
2584 			ScreenLines[idx] = firstbyte;
2585 		    }
2586 		    else
2587 			prev_c = u8c;
2588 #endif
2589 		    /* Non-BMP character: display as ? or fullwidth ?. */
2590 #ifdef UNICODE16
2591 		    if (u8c >= 0x10000)
2592 			ScreenLinesUC[idx] = (cells == 2) ? 0xff1f : (int)'?';
2593 		    else
2594 #endif
2595 			ScreenLinesUC[idx] = u8c;
2596 		    for (i = 0; i < Screen_mco; ++i)
2597 		    {
2598 			ScreenLinesC[i][idx] = u8cc[i];
2599 			if (u8cc[i] == 0)
2600 			    break;
2601 		    }
2602 		}
2603 		if (cells > 1)
2604 		    ScreenLines[idx + 1] = 0;
2605 	    }
2606 	    else if (enc_dbcs == DBCS_JPNU && *p == 0x8e)
2607 		/* double-byte single width character */
2608 		ScreenLines2[idx] = p[1];
2609 	    else if (cells > 1)
2610 		/* double-width character */
2611 		ScreenLines[idx + 1] = p[1];
2612 	    col += cells;
2613 	    idx += cells;
2614 	    p += c_len;
2615 	}
2616     }
2617     else
2618 #endif
2619     {
2620 	int len = (int)STRLEN(text);
2621 
2622 	if (len > wp->w_width - col)
2623 	    len = wp->w_width - col;
2624 	if (len > 0)
2625 	{
2626 #ifdef FEAT_RIGHTLEFT
2627 	    if (wp->w_p_rl)
2628 		STRNCPY(current_ScreenLine, text, len);
2629 	    else
2630 #endif
2631 		STRNCPY(current_ScreenLine + col, text, len);
2632 	    col += len;
2633 	}
2634     }
2635     return col;
2636 }
2637 #endif
2638 
2639 #ifdef FEAT_FOLDING
2640 /*
2641  * Compute the width of the foldcolumn.  Based on 'foldcolumn' and how much
2642  * space is available for window "wp", minus "col".
2643  */
2644     static int
2645 compute_foldcolumn(win_T *wp, int col)
2646 {
2647     int fdc = wp->w_p_fdc;
2648     int wmw = wp == curwin && p_wmw == 0 ? 1 : p_wmw;
2649     int wwidth = wp->w_width;
2650 
2651     if (fdc > wwidth - (col + wmw))
2652 	fdc = wwidth - (col + wmw);
2653     return fdc;
2654 }
2655 
2656 /*
2657  * Display one folded line.
2658  */
2659     static void
2660 fold_line(
2661     win_T	*wp,
2662     long	fold_count,
2663     foldinfo_T	*foldinfo,
2664     linenr_T	lnum,
2665     int		row)
2666 {
2667     char_u	buf[FOLD_TEXT_LEN];
2668     pos_T	*top, *bot;
2669     linenr_T	lnume = lnum + fold_count - 1;
2670     int		len;
2671     char_u	*text;
2672     int		fdc;
2673     int		col;
2674     int		txtcol;
2675     int		off = (int)(current_ScreenLine - ScreenLines);
2676     int		ri;
2677 
2678     /* Build the fold line:
2679      * 1. Add the cmdwin_type for the command-line window
2680      * 2. Add the 'foldcolumn'
2681      * 3. Add the 'number' or 'relativenumber' column
2682      * 4. Compose the text
2683      * 5. Add the text
2684      * 6. set highlighting for the Visual area an other text
2685      */
2686     col = 0;
2687 
2688     /*
2689      * 1. Add the cmdwin_type for the command-line window
2690      * Ignores 'rightleft', this window is never right-left.
2691      */
2692 #ifdef FEAT_CMDWIN
2693     if (cmdwin_type != 0 && wp == curwin)
2694     {
2695 	ScreenLines[off] = cmdwin_type;
2696 	ScreenAttrs[off] = HL_ATTR(HLF_AT);
2697 #ifdef FEAT_MBYTE
2698 	if (enc_utf8)
2699 	    ScreenLinesUC[off] = 0;
2700 #endif
2701 	++col;
2702     }
2703 #endif
2704 
2705     /*
2706      * 2. Add the 'foldcolumn'
2707      *    Reduce the width when there is not enough space.
2708      */
2709     fdc = compute_foldcolumn(wp, col);
2710     if (fdc > 0)
2711     {
2712 	fill_foldcolumn(buf, wp, TRUE, lnum);
2713 #ifdef FEAT_RIGHTLEFT
2714 	if (wp->w_p_rl)
2715 	{
2716 	    int		i;
2717 
2718 	    copy_text_attr(off + wp->w_width - fdc - col, buf, fdc,
2719 							     HL_ATTR(HLF_FC));
2720 	    /* reverse the fold column */
2721 	    for (i = 0; i < fdc; ++i)
2722 		ScreenLines[off + wp->w_width - i - 1 - col] = buf[i];
2723 	}
2724 	else
2725 #endif
2726 	    copy_text_attr(off + col, buf, fdc, HL_ATTR(HLF_FC));
2727 	col += fdc;
2728     }
2729 
2730 #ifdef FEAT_RIGHTLEFT
2731 # define RL_MEMSET(p, v, l) \
2732     do { \
2733 	if (wp->w_p_rl) \
2734 	    for (ri = 0; ri < l; ++ri) \
2735 	       ScreenAttrs[off + (wp->w_width - (p) - (l)) + ri] = v; \
2736 	 else \
2737 	    for (ri = 0; ri < l; ++ri) \
2738 	       ScreenAttrs[off + (p) + ri] = v; \
2739     } while (0)
2740 #else
2741 # define RL_MEMSET(p, v, l) \
2742     do { \
2743 	for (ri = 0; ri < l; ++ri) \
2744 	    ScreenAttrs[off + (p) + ri] = v; \
2745     } while (0)
2746 #endif
2747 
2748     /* Set all attributes of the 'number' or 'relativenumber' column and the
2749      * text */
2750     RL_MEMSET(col, HL_ATTR(HLF_FL), wp->w_width - col);
2751 
2752 #ifdef FEAT_SIGNS
2753     /* If signs are being displayed, add two spaces. */
2754     if (signcolumn_on(wp))
2755     {
2756 	len = wp->w_width - col;
2757 	if (len > 0)
2758 	{
2759 	    if (len > 2)
2760 		len = 2;
2761 # ifdef FEAT_RIGHTLEFT
2762 	    if (wp->w_p_rl)
2763 		/* the line number isn't reversed */
2764 		copy_text_attr(off + wp->w_width - len - col,
2765 					(char_u *)"  ", len, HL_ATTR(HLF_FL));
2766 	    else
2767 # endif
2768 		copy_text_attr(off + col, (char_u *)"  ", len, HL_ATTR(HLF_FL));
2769 	    col += len;
2770 	}
2771     }
2772 #endif
2773 
2774     /*
2775      * 3. Add the 'number' or 'relativenumber' column
2776      */
2777     if (wp->w_p_nu || wp->w_p_rnu)
2778     {
2779 	len = wp->w_width - col;
2780 	if (len > 0)
2781 	{
2782 	    int	    w = number_width(wp);
2783 	    long    num;
2784 	    char    *fmt = "%*ld ";
2785 
2786 	    if (len > w + 1)
2787 		len = w + 1;
2788 
2789 	    if (wp->w_p_nu && !wp->w_p_rnu)
2790 		/* 'number' + 'norelativenumber' */
2791 		num = (long)lnum;
2792 	    else
2793 	    {
2794 		/* 'relativenumber', don't use negative numbers */
2795 		num = labs((long)get_cursor_rel_lnum(wp, lnum));
2796 		if (num == 0 && wp->w_p_nu && wp->w_p_rnu)
2797 		{
2798 		    /* 'number' + 'relativenumber': cursor line shows absolute
2799 		     * line number */
2800 		    num = lnum;
2801 		    fmt = "%-*ld ";
2802 		}
2803 	    }
2804 
2805 	    sprintf((char *)buf, fmt, w, num);
2806 #ifdef FEAT_RIGHTLEFT
2807 	    if (wp->w_p_rl)
2808 		/* the line number isn't reversed */
2809 		copy_text_attr(off + wp->w_width - len - col, buf, len,
2810 							     HL_ATTR(HLF_FL));
2811 	    else
2812 #endif
2813 		copy_text_attr(off + col, buf, len, HL_ATTR(HLF_FL));
2814 	    col += len;
2815 	}
2816     }
2817 
2818     /*
2819      * 4. Compose the folded-line string with 'foldtext', if set.
2820      */
2821     text = get_foldtext(wp, lnum, lnume, foldinfo, buf);
2822 
2823     txtcol = col;	/* remember where text starts */
2824 
2825     /*
2826      * 5. move the text to current_ScreenLine.  Fill up with "fill_fold".
2827      *    Right-left text is put in columns 0 - number-col, normal text is put
2828      *    in columns number-col - window-width.
2829      */
2830     col = text_to_screenline(wp, text, col);
2831 
2832     /* Fill the rest of the line with the fold filler */
2833 #ifdef FEAT_RIGHTLEFT
2834     if (wp->w_p_rl)
2835 	col -= txtcol;
2836 #endif
2837     while (col < wp->w_width
2838 #ifdef FEAT_RIGHTLEFT
2839 		    - (wp->w_p_rl ? txtcol : 0)
2840 #endif
2841 	    )
2842     {
2843 #ifdef FEAT_MBYTE
2844 	if (enc_utf8)
2845 	{
2846 	    if (fill_fold >= 0x80)
2847 	    {
2848 		ScreenLinesUC[off + col] = fill_fold;
2849 		ScreenLinesC[0][off + col] = 0;
2850 		ScreenLines[off + col] = 0x80; /* avoid storing zero */
2851 	    }
2852 	    else
2853 	    {
2854 		ScreenLinesUC[off + col] = 0;
2855 		ScreenLines[off + col] = fill_fold;
2856 	    }
2857 	    col++;
2858 	}
2859 	else
2860 #endif
2861 	    ScreenLines[off + col++] = fill_fold;
2862     }
2863 
2864     if (text != buf)
2865 	vim_free(text);
2866 
2867     /*
2868      * 6. set highlighting for the Visual area an other text.
2869      * If all folded lines are in the Visual area, highlight the line.
2870      */
2871     if (VIsual_active && wp->w_buffer == curwin->w_buffer)
2872     {
2873 	if (LTOREQ_POS(curwin->w_cursor, VIsual))
2874 	{
2875 	    /* Visual is after curwin->w_cursor */
2876 	    top = &curwin->w_cursor;
2877 	    bot = &VIsual;
2878 	}
2879 	else
2880 	{
2881 	    /* Visual is before curwin->w_cursor */
2882 	    top = &VIsual;
2883 	    bot = &curwin->w_cursor;
2884 	}
2885 	if (lnum >= top->lnum
2886 		&& lnume <= bot->lnum
2887 		&& (VIsual_mode != 'v'
2888 		    || ((lnum > top->lnum
2889 			    || (lnum == top->lnum
2890 				&& top->col == 0))
2891 			&& (lnume < bot->lnum
2892 			    || (lnume == bot->lnum
2893 				&& (bot->col - (*p_sel == 'e'))
2894 		>= (colnr_T)STRLEN(ml_get_buf(wp->w_buffer, lnume, FALSE)))))))
2895 	{
2896 	    if (VIsual_mode == Ctrl_V)
2897 	    {
2898 		/* Visual block mode: highlight the chars part of the block */
2899 		if (wp->w_old_cursor_fcol + txtcol < (colnr_T)wp->w_width)
2900 		{
2901 		    if (wp->w_old_cursor_lcol != MAXCOL
2902 			     && wp->w_old_cursor_lcol + txtcol
2903 						       < (colnr_T)wp->w_width)
2904 			len = wp->w_old_cursor_lcol;
2905 		    else
2906 			len = wp->w_width - txtcol;
2907 		    RL_MEMSET(wp->w_old_cursor_fcol + txtcol, HL_ATTR(HLF_V),
2908 					    len - (int)wp->w_old_cursor_fcol);
2909 		}
2910 	    }
2911 	    else
2912 	    {
2913 		/* Set all attributes of the text */
2914 		RL_MEMSET(txtcol, HL_ATTR(HLF_V), wp->w_width - txtcol);
2915 	    }
2916 	}
2917     }
2918 
2919 #ifdef FEAT_SYN_HL
2920     /* Show colorcolumn in the fold line, but let cursorcolumn override it. */
2921     if (wp->w_p_cc_cols)
2922     {
2923 	int i = 0;
2924 	int j = wp->w_p_cc_cols[i];
2925 	int old_txtcol = txtcol;
2926 
2927 	while (j > -1)
2928 	{
2929 	    txtcol += j;
2930 	    if (wp->w_p_wrap)
2931 		txtcol -= wp->w_skipcol;
2932 	    else
2933 		txtcol -= wp->w_leftcol;
2934 	    if (txtcol >= 0 && txtcol < wp->w_width)
2935 		ScreenAttrs[off + txtcol] = hl_combine_attr(
2936 				    ScreenAttrs[off + txtcol], HL_ATTR(HLF_MC));
2937 	    txtcol = old_txtcol;
2938 	    j = wp->w_p_cc_cols[++i];
2939 	}
2940     }
2941 
2942     /* Show 'cursorcolumn' in the fold line. */
2943     if (wp->w_p_cuc)
2944     {
2945 	txtcol += wp->w_virtcol;
2946 	if (wp->w_p_wrap)
2947 	    txtcol -= wp->w_skipcol;
2948 	else
2949 	    txtcol -= wp->w_leftcol;
2950 	if (txtcol >= 0 && txtcol < wp->w_width)
2951 	    ScreenAttrs[off + txtcol] = hl_combine_attr(
2952 				 ScreenAttrs[off + txtcol], HL_ATTR(HLF_CUC));
2953     }
2954 #endif
2955 
2956     screen_line(row + W_WINROW(wp), wp->w_wincol, (int)wp->w_width,
2957 						     (int)wp->w_width, FALSE);
2958 
2959     /*
2960      * Update w_cline_height and w_cline_folded if the cursor line was
2961      * updated (saves a call to plines() later).
2962      */
2963     if (wp == curwin
2964 	    && lnum <= curwin->w_cursor.lnum
2965 	    && lnume >= curwin->w_cursor.lnum)
2966     {
2967 	curwin->w_cline_row = row;
2968 	curwin->w_cline_height = 1;
2969 	curwin->w_cline_folded = TRUE;
2970 	curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
2971     }
2972 }
2973 
2974 /*
2975  * Copy "buf[len]" to ScreenLines["off"] and set attributes to "attr".
2976  */
2977     static void
2978 copy_text_attr(
2979     int		off,
2980     char_u	*buf,
2981     int		len,
2982     int		attr)
2983 {
2984     int		i;
2985 
2986     mch_memmove(ScreenLines + off, buf, (size_t)len);
2987 # ifdef FEAT_MBYTE
2988     if (enc_utf8)
2989 	vim_memset(ScreenLinesUC + off, 0, sizeof(u8char_T) * (size_t)len);
2990 # endif
2991     for (i = 0; i < len; ++i)
2992 	ScreenAttrs[off + i] = attr;
2993 }
2994 
2995 /*
2996  * Fill the foldcolumn at "p" for window "wp".
2997  * Only to be called when 'foldcolumn' > 0.
2998  */
2999     static void
3000 fill_foldcolumn(
3001     char_u	*p,
3002     win_T	*wp,
3003     int		closed,		/* TRUE of FALSE */
3004     linenr_T	lnum)		/* current line number */
3005 {
3006     int		i = 0;
3007     int		level;
3008     int		first_level;
3009     int		empty;
3010     int		fdc = compute_foldcolumn(wp, 0);
3011 
3012     /* Init to all spaces. */
3013     vim_memset(p, ' ', (size_t)fdc);
3014 
3015     level = win_foldinfo.fi_level;
3016     if (level > 0)
3017     {
3018 	/* If there is only one column put more info in it. */
3019 	empty = (fdc == 1) ? 0 : 1;
3020 
3021 	/* If the column is too narrow, we start at the lowest level that
3022 	 * fits and use numbers to indicated the depth. */
3023 	first_level = level - fdc - closed + 1 + empty;
3024 	if (first_level < 1)
3025 	    first_level = 1;
3026 
3027 	for (i = 0; i + empty < fdc; ++i)
3028 	{
3029 	    if (win_foldinfo.fi_lnum == lnum
3030 			      && first_level + i >= win_foldinfo.fi_low_level)
3031 		p[i] = '-';
3032 	    else if (first_level == 1)
3033 		p[i] = '|';
3034 	    else if (first_level + i <= 9)
3035 		p[i] = '0' + first_level + i;
3036 	    else
3037 		p[i] = '>';
3038 	    if (first_level + i == level)
3039 		break;
3040 	}
3041     }
3042     if (closed)
3043 	p[i >= fdc ? i - 1 : i] = '+';
3044 }
3045 #endif /* FEAT_FOLDING */
3046 
3047 /*
3048  * Display line "lnum" of window 'wp' on the screen.
3049  * Start at row "startrow", stop when "endrow" is reached.
3050  * wp->w_virtcol needs to be valid.
3051  *
3052  * Return the number of last row the line occupies.
3053  */
3054     static int
3055 win_line(
3056     win_T	*wp,
3057     linenr_T	lnum,
3058     int		startrow,
3059     int		endrow,
3060     int		nochange UNUSED)	/* not updating for changed text */
3061 {
3062     int		col = 0;		/* visual column on screen */
3063     unsigned	off;			/* offset in ScreenLines/ScreenAttrs */
3064     int		c = 0;			/* init for GCC */
3065     long	vcol = 0;		/* virtual column (for tabs) */
3066 #ifdef FEAT_LINEBREAK
3067     long	vcol_sbr = -1;		/* virtual column after showbreak */
3068 #endif
3069     long	vcol_prev = -1;		/* "vcol" of previous character */
3070     char_u	*line;			/* current line */
3071     char_u	*ptr;			/* current position in "line" */
3072     int		row;			/* row in the window, excl w_winrow */
3073     int		screen_row;		/* row on the screen, incl w_winrow */
3074 
3075     char_u	extra[18];		/* "%ld" and 'fdc' must fit in here */
3076     int		n_extra = 0;		/* number of extra chars */
3077     char_u	*p_extra = NULL;	/* string of extra chars, plus NUL */
3078     char_u	*p_extra_free = NULL;   /* p_extra needs to be freed */
3079     int		c_extra = NUL;		/* extra chars, all the same */
3080     int		extra_attr = 0;		/* attributes when n_extra != 0 */
3081     static char_u *at_end_str = (char_u *)""; /* used for p_extra when
3082 					   displaying lcs_eol at end-of-line */
3083     int		lcs_eol_one = lcs_eol;	/* lcs_eol until it's been used */
3084     int		lcs_prec_todo = lcs_prec;   /* lcs_prec until it's been used */
3085 
3086     /* saved "extra" items for when draw_state becomes WL_LINE (again) */
3087     int		saved_n_extra = 0;
3088     char_u	*saved_p_extra = NULL;
3089     int		saved_c_extra = 0;
3090     int		saved_char_attr = 0;
3091 
3092     int		n_attr = 0;		/* chars with special attr */
3093     int		saved_attr2 = 0;	/* char_attr saved for n_attr */
3094     int		n_attr3 = 0;		/* chars with overruling special attr */
3095     int		saved_attr3 = 0;	/* char_attr saved for n_attr3 */
3096 
3097     int		n_skip = 0;		/* nr of chars to skip for 'nowrap' */
3098 
3099     int		fromcol, tocol;		/* start/end of inverting */
3100     int		fromcol_prev = -2;	/* start of inverting after cursor */
3101     int		noinvcur = FALSE;	/* don't invert the cursor */
3102     pos_T	*top, *bot;
3103     int		lnum_in_visual_area = FALSE;
3104     pos_T	pos;
3105     long	v;
3106 
3107     int		char_attr = 0;		/* attributes for next character */
3108     int		attr_pri = FALSE;	/* char_attr has priority */
3109     int		area_highlighting = FALSE; /* Visual or incsearch highlighting
3110 					      in this line */
3111     int		attr = 0;		/* attributes for area highlighting */
3112     int		area_attr = 0;		/* attributes desired by highlighting */
3113     int		search_attr = 0;	/* attributes desired by 'hlsearch' */
3114 #ifdef FEAT_SYN_HL
3115     int		vcol_save_attr = 0;	/* saved attr for 'cursorcolumn' */
3116     int		syntax_attr = 0;	/* attributes desired by syntax */
3117     int		has_syntax = FALSE;	/* this buffer has syntax highl. */
3118     int		save_did_emsg;
3119     int		eol_hl_off = 0;		/* 1 if highlighted char after EOL */
3120     int		draw_color_col = FALSE;	/* highlight colorcolumn */
3121     int		*color_cols = NULL;	/* pointer to according columns array */
3122 #endif
3123 #ifdef FEAT_SPELL
3124     int		has_spell = FALSE;	/* this buffer has spell checking */
3125 # define SPWORDLEN 150
3126     char_u	nextline[SPWORDLEN * 2];/* text with start of the next line */
3127     int		nextlinecol = 0;	/* column where nextline[] starts */
3128     int		nextline_idx = 0;	/* index in nextline[] where next line
3129 					   starts */
3130     int		spell_attr = 0;		/* attributes desired by spelling */
3131     int		word_end = 0;		/* last byte with same spell_attr */
3132     static linenr_T  checked_lnum = 0;	/* line number for "checked_col" */
3133     static int	checked_col = 0;	/* column in "checked_lnum" up to which
3134 					 * there are no spell errors */
3135     static int	cap_col = -1;		/* column to check for Cap word */
3136     static linenr_T capcol_lnum = 0;	/* line number where "cap_col" used */
3137     int		cur_checked_col = 0;	/* checked column for current line */
3138 #endif
3139     int		extra_check;		/* has syntax or linebreak */
3140 #ifdef FEAT_MBYTE
3141     int		multi_attr = 0;		/* attributes desired by multibyte */
3142     int		mb_l = 1;		/* multi-byte byte length */
3143     int		mb_c = 0;		/* decoded multi-byte character */
3144     int		mb_utf8 = FALSE;	/* screen char is UTF-8 char */
3145     int		u8cc[MAX_MCO];		/* composing UTF-8 chars */
3146 #endif
3147 #ifdef FEAT_DIFF
3148     int		filler_lines;		/* nr of filler lines to be drawn */
3149     int		filler_todo;		/* nr of filler lines still to do + 1 */
3150     hlf_T	diff_hlf = (hlf_T)0;	/* type of diff highlighting */
3151     int		change_start = MAXCOL;	/* first col of changed area */
3152     int		change_end = -1;	/* last col of changed area */
3153 #endif
3154     colnr_T	trailcol = MAXCOL;	/* start of trailing spaces */
3155 #ifdef FEAT_LINEBREAK
3156     int		need_showbreak = FALSE; /* overlong line, skipping first x
3157 					   chars */
3158 #endif
3159 #if defined(FEAT_SIGNS) || defined(FEAT_QUICKFIX) \
3160 	|| defined(FEAT_SYN_HL) || defined(FEAT_DIFF)
3161 # define LINE_ATTR
3162     int		line_attr = 0;		/* attribute for the whole line */
3163 #endif
3164 #ifdef FEAT_SEARCH_EXTRA
3165     matchitem_T *cur;			/* points to the match list */
3166     match_T	*shl;			/* points to search_hl or a match */
3167     int		shl_flag;		/* flag to indicate whether search_hl
3168 					   has been processed or not */
3169     int		pos_inprogress;		/* marks that position match search is
3170 					   in progress */
3171     int		prevcol_hl_flag;	/* flag to indicate whether prevcol
3172 					   equals startcol of search_hl or one
3173 					   of the matches */
3174 #endif
3175 #ifdef FEAT_ARABIC
3176     int		prev_c = 0;		/* previous Arabic character */
3177     int		prev_c1 = 0;		/* first composing char for prev_c */
3178 #endif
3179 #if defined(LINE_ATTR)
3180     int		did_line_attr = 0;
3181 #endif
3182 #ifdef FEAT_TERMINAL
3183     int		get_term_attr = FALSE;
3184     int		term_attr = 0;		/* background for terminal window */
3185 #endif
3186 
3187     /* draw_state: items that are drawn in sequence: */
3188 #define WL_START	0		/* nothing done yet */
3189 #ifdef FEAT_CMDWIN
3190 # define WL_CMDLINE	WL_START + 1	/* cmdline window column */
3191 #else
3192 # define WL_CMDLINE	WL_START
3193 #endif
3194 #ifdef FEAT_FOLDING
3195 # define WL_FOLD	WL_CMDLINE + 1	/* 'foldcolumn' */
3196 #else
3197 # define WL_FOLD	WL_CMDLINE
3198 #endif
3199 #ifdef FEAT_SIGNS
3200 # define WL_SIGN	WL_FOLD + 1	/* column for signs */
3201 #else
3202 # define WL_SIGN	WL_FOLD		/* column for signs */
3203 #endif
3204 #define WL_NR		WL_SIGN + 1	/* line number */
3205 #ifdef FEAT_LINEBREAK
3206 # define WL_BRI		WL_NR + 1	/* 'breakindent' */
3207 #else
3208 # define WL_BRI		WL_NR
3209 #endif
3210 #if defined(FEAT_LINEBREAK) || defined(FEAT_DIFF)
3211 # define WL_SBR		WL_BRI + 1	/* 'showbreak' or 'diff' */
3212 #else
3213 # define WL_SBR		WL_BRI
3214 #endif
3215 #define WL_LINE		WL_SBR + 1	/* text in the line */
3216     int		draw_state = WL_START;	/* what to draw next */
3217 #if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
3218     int		feedback_col = 0;
3219     int		feedback_old_attr = -1;
3220 #endif
3221 
3222 #ifdef FEAT_CONCEAL
3223     int		syntax_flags	= 0;
3224     int		syntax_seqnr	= 0;
3225     int		prev_syntax_id	= 0;
3226     int		conceal_attr	= HL_ATTR(HLF_CONCEAL);
3227     int		is_concealing	= FALSE;
3228     int		boguscols	= 0;	/* nonexistent columns added to force
3229 					   wrapping */
3230     int		vcol_off	= 0;	/* offset for concealed characters */
3231     int		did_wcol	= FALSE;
3232     int		match_conc	= 0;	/* cchar for match functions */
3233     int		has_match_conc  = 0;	/* match wants to conceal */
3234     int		old_boguscols   = 0;
3235 # define VCOL_HLC (vcol - vcol_off)
3236 # define FIX_FOR_BOGUSCOLS \
3237     { \
3238 	n_extra += vcol_off; \
3239 	vcol -= vcol_off; \
3240 	vcol_off = 0; \
3241 	col -= boguscols; \
3242 	old_boguscols = boguscols; \
3243 	boguscols = 0; \
3244     }
3245 #else
3246 # define VCOL_HLC (vcol)
3247 #endif
3248 
3249     if (startrow > endrow)		/* past the end already! */
3250 	return startrow;
3251 
3252     row = startrow;
3253     screen_row = row + W_WINROW(wp);
3254 
3255     /*
3256      * To speed up the loop below, set extra_check when there is linebreak,
3257      * trailing white space and/or syntax processing to be done.
3258      */
3259 #ifdef FEAT_LINEBREAK
3260     extra_check = wp->w_p_lbr;
3261 #else
3262     extra_check = 0;
3263 #endif
3264 #ifdef FEAT_SYN_HL
3265     if (syntax_present(wp) && !wp->w_s->b_syn_error
3266 # ifdef SYN_TIME_LIMIT
3267 	    && !wp->w_s->b_syn_slow
3268 # endif
3269        )
3270     {
3271 	/* Prepare for syntax highlighting in this line.  When there is an
3272 	 * error, stop syntax highlighting. */
3273 	save_did_emsg = did_emsg;
3274 	did_emsg = FALSE;
3275 	syntax_start(wp, lnum);
3276 	if (did_emsg)
3277 	    wp->w_s->b_syn_error = TRUE;
3278 	else
3279 	{
3280 	    did_emsg = save_did_emsg;
3281 #ifdef SYN_TIME_LIMIT
3282 	    if (!wp->w_s->b_syn_slow)
3283 #endif
3284 	    {
3285 		has_syntax = TRUE;
3286 		extra_check = TRUE;
3287 	    }
3288 	}
3289     }
3290 
3291     /* Check for columns to display for 'colorcolumn'. */
3292     color_cols = wp->w_p_cc_cols;
3293     if (color_cols != NULL)
3294 	draw_color_col = advance_color_col(VCOL_HLC, &color_cols);
3295 #endif
3296 
3297 #ifdef FEAT_TERMINAL
3298     if (term_show_buffer(wp->w_buffer))
3299     {
3300 	extra_check = TRUE;
3301 	get_term_attr = TRUE;
3302 	term_attr = term_get_attr(wp->w_buffer, lnum, -1);
3303     }
3304 #endif
3305 
3306 #ifdef FEAT_SPELL
3307     if (wp->w_p_spell
3308 	    && *wp->w_s->b_p_spl != NUL
3309 	    && wp->w_s->b_langp.ga_len > 0
3310 	    && *(char **)(wp->w_s->b_langp.ga_data) != NULL)
3311     {
3312 	/* Prepare for spell checking. */
3313 	has_spell = TRUE;
3314 	extra_check = TRUE;
3315 
3316 	/* Get the start of the next line, so that words that wrap to the next
3317 	 * line are found too: "et<line-break>al.".
3318 	 * Trick: skip a few chars for C/shell/Vim comments */
3319 	nextline[SPWORDLEN] = NUL;
3320 	if (lnum < wp->w_buffer->b_ml.ml_line_count)
3321 	{
3322 	    line = ml_get_buf(wp->w_buffer, lnum + 1, FALSE);
3323 	    spell_cat_line(nextline + SPWORDLEN, line, SPWORDLEN);
3324 	}
3325 
3326 	/* When a word wrapped from the previous line the start of the current
3327 	 * line is valid. */
3328 	if (lnum == checked_lnum)
3329 	    cur_checked_col = checked_col;
3330 	checked_lnum = 0;
3331 
3332 	/* When there was a sentence end in the previous line may require a
3333 	 * word starting with capital in this line.  In line 1 always check
3334 	 * the first word. */
3335 	if (lnum != capcol_lnum)
3336 	    cap_col = -1;
3337 	if (lnum == 1)
3338 	    cap_col = 0;
3339 	capcol_lnum = 0;
3340     }
3341 #endif
3342 
3343     /*
3344      * handle visual active in this window
3345      */
3346     fromcol = -10;
3347     tocol = MAXCOL;
3348     if (VIsual_active && wp->w_buffer == curwin->w_buffer)
3349     {
3350 					/* Visual is after curwin->w_cursor */
3351 	if (LTOREQ_POS(curwin->w_cursor, VIsual))
3352 	{
3353 	    top = &curwin->w_cursor;
3354 	    bot = &VIsual;
3355 	}
3356 	else				/* Visual is before curwin->w_cursor */
3357 	{
3358 	    top = &VIsual;
3359 	    bot = &curwin->w_cursor;
3360 	}
3361 	lnum_in_visual_area = (lnum >= top->lnum && lnum <= bot->lnum);
3362 	if (VIsual_mode == Ctrl_V)	/* block mode */
3363 	{
3364 	    if (lnum_in_visual_area)
3365 	    {
3366 		fromcol = wp->w_old_cursor_fcol;
3367 		tocol = wp->w_old_cursor_lcol;
3368 	    }
3369 	}
3370 	else				/* non-block mode */
3371 	{
3372 	    if (lnum > top->lnum && lnum <= bot->lnum)
3373 		fromcol = 0;
3374 	    else if (lnum == top->lnum)
3375 	    {
3376 		if (VIsual_mode == 'V')	/* linewise */
3377 		    fromcol = 0;
3378 		else
3379 		{
3380 		    getvvcol(wp, top, (colnr_T *)&fromcol, NULL, NULL);
3381 		    if (gchar_pos(top) == NUL)
3382 			tocol = fromcol + 1;
3383 		}
3384 	    }
3385 	    if (VIsual_mode != 'V' && lnum == bot->lnum)
3386 	    {
3387 		if (*p_sel == 'e' && bot->col == 0
3388 #ifdef FEAT_VIRTUALEDIT
3389 			&& bot->coladd == 0
3390 #endif
3391 		   )
3392 		{
3393 		    fromcol = -10;
3394 		    tocol = MAXCOL;
3395 		}
3396 		else if (bot->col == MAXCOL)
3397 		    tocol = MAXCOL;
3398 		else
3399 		{
3400 		    pos = *bot;
3401 		    if (*p_sel == 'e')
3402 			getvvcol(wp, &pos, (colnr_T *)&tocol, NULL, NULL);
3403 		    else
3404 		    {
3405 			getvvcol(wp, &pos, NULL, NULL, (colnr_T *)&tocol);
3406 			++tocol;
3407 		    }
3408 		}
3409 	    }
3410 	}
3411 
3412 	/* Check if the character under the cursor should not be inverted */
3413 	if (!highlight_match && lnum == curwin->w_cursor.lnum && wp == curwin
3414 #ifdef FEAT_GUI
3415 		&& !gui.in_use
3416 #endif
3417 		)
3418 	    noinvcur = TRUE;
3419 
3420 	/* if inverting in this line set area_highlighting */
3421 	if (fromcol >= 0)
3422 	{
3423 	    area_highlighting = TRUE;
3424 	    attr = HL_ATTR(HLF_V);
3425 #if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
3426 	    if ((clip_star.available && !clip_star.owned
3427 						     && clip_isautosel_star())
3428 		    || (clip_plus.available && !clip_plus.owned
3429 						    && clip_isautosel_plus()))
3430 		attr = HL_ATTR(HLF_VNC);
3431 #endif
3432 	}
3433     }
3434 
3435     /*
3436      * handle 'incsearch' and ":s///c" highlighting
3437      */
3438     else if (highlight_match
3439 	    && wp == curwin
3440 	    && lnum >= curwin->w_cursor.lnum
3441 	    && lnum <= curwin->w_cursor.lnum + search_match_lines)
3442     {
3443 	if (lnum == curwin->w_cursor.lnum)
3444 	    getvcol(curwin, &(curwin->w_cursor),
3445 					     (colnr_T *)&fromcol, NULL, NULL);
3446 	else
3447 	    fromcol = 0;
3448 	if (lnum == curwin->w_cursor.lnum + search_match_lines)
3449 	{
3450 	    pos.lnum = lnum;
3451 	    pos.col = search_match_endcol;
3452 	    getvcol(curwin, &pos, (colnr_T *)&tocol, NULL, NULL);
3453 	}
3454 	else
3455 	    tocol = MAXCOL;
3456 	/* do at least one character; happens when past end of line */
3457 	if (fromcol == tocol)
3458 	    tocol = fromcol + 1;
3459 	area_highlighting = TRUE;
3460 	attr = HL_ATTR(HLF_I);
3461     }
3462 
3463 #ifdef FEAT_DIFF
3464     filler_lines = diff_check(wp, lnum);
3465     if (filler_lines < 0)
3466     {
3467 	if (filler_lines == -1)
3468 	{
3469 	    if (diff_find_change(wp, lnum, &change_start, &change_end))
3470 		diff_hlf = HLF_ADD;	/* added line */
3471 	    else if (change_start == 0)
3472 		diff_hlf = HLF_TXD;	/* changed text */
3473 	    else
3474 		diff_hlf = HLF_CHD;	/* changed line */
3475 	}
3476 	else
3477 	    diff_hlf = HLF_ADD;		/* added line */
3478 	filler_lines = 0;
3479 	area_highlighting = TRUE;
3480     }
3481     if (lnum == wp->w_topline)
3482 	filler_lines = wp->w_topfill;
3483     filler_todo = filler_lines;
3484 #endif
3485 
3486 #ifdef LINE_ATTR
3487 # ifdef FEAT_SIGNS
3488     /* If this line has a sign with line highlighting set line_attr. */
3489     v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL);
3490     if (v != 0)
3491 	line_attr = sign_get_attr((int)v, TRUE);
3492 # endif
3493 # if defined(FEAT_QUICKFIX)
3494     /* Highlight the current line in the quickfix window. */
3495     if (bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum)
3496 	line_attr = HL_ATTR(HLF_QFL);
3497 # endif
3498     if (line_attr != 0)
3499 	area_highlighting = TRUE;
3500 #endif
3501 
3502     line = ml_get_buf(wp->w_buffer, lnum, FALSE);
3503     ptr = line;
3504 
3505 #ifdef FEAT_SPELL
3506     if (has_spell)
3507     {
3508 	/* For checking first word with a capital skip white space. */
3509 	if (cap_col == 0)
3510 	    cap_col = getwhitecols(line);
3511 
3512 	/* To be able to spell-check over line boundaries copy the end of the
3513 	 * current line into nextline[].  Above the start of the next line was
3514 	 * copied to nextline[SPWORDLEN]. */
3515 	if (nextline[SPWORDLEN] == NUL)
3516 	{
3517 	    /* No next line or it is empty. */
3518 	    nextlinecol = MAXCOL;
3519 	    nextline_idx = 0;
3520 	}
3521 	else
3522 	{
3523 	    v = (long)STRLEN(line);
3524 	    if (v < SPWORDLEN)
3525 	    {
3526 		/* Short line, use it completely and append the start of the
3527 		 * next line. */
3528 		nextlinecol = 0;
3529 		mch_memmove(nextline, line, (size_t)v);
3530 		STRMOVE(nextline + v, nextline + SPWORDLEN);
3531 		nextline_idx = v + 1;
3532 	    }
3533 	    else
3534 	    {
3535 		/* Long line, use only the last SPWORDLEN bytes. */
3536 		nextlinecol = v - SPWORDLEN;
3537 		mch_memmove(nextline, line + nextlinecol, SPWORDLEN);
3538 		nextline_idx = SPWORDLEN + 1;
3539 	    }
3540 	}
3541     }
3542 #endif
3543 
3544     if (wp->w_p_list)
3545     {
3546 	if (lcs_space || lcs_trail)
3547 	    extra_check = TRUE;
3548 	/* find start of trailing whitespace */
3549 	if (lcs_trail)
3550 	{
3551 	    trailcol = (colnr_T)STRLEN(ptr);
3552 	    while (trailcol > (colnr_T)0 && VIM_ISWHITE(ptr[trailcol - 1]))
3553 		--trailcol;
3554 	    trailcol += (colnr_T) (ptr - line);
3555 	}
3556     }
3557 
3558     /*
3559      * 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the
3560      * first character to be displayed.
3561      */
3562     if (wp->w_p_wrap)
3563 	v = wp->w_skipcol;
3564     else
3565 	v = wp->w_leftcol;
3566     if (v > 0)
3567     {
3568 #ifdef FEAT_MBYTE
3569 	char_u	*prev_ptr = ptr;
3570 #endif
3571 	while (vcol < v && *ptr != NUL)
3572 	{
3573 	    c = win_lbr_chartabsize(wp, line, ptr, (colnr_T)vcol, NULL);
3574 	    vcol += c;
3575 #ifdef FEAT_MBYTE
3576 	    prev_ptr = ptr;
3577 #endif
3578 	    MB_PTR_ADV(ptr);
3579 	}
3580 
3581 	/* When:
3582 	 * - 'cuc' is set, or
3583 	 * - 'colorcolumn' is set, or
3584 	 * - 'virtualedit' is set, or
3585 	 * - the visual mode is active,
3586 	 * the end of the line may be before the start of the displayed part.
3587 	 */
3588 	if (vcol < v && (
3589 #ifdef FEAT_SYN_HL
3590 	     wp->w_p_cuc || draw_color_col ||
3591 #endif
3592 #ifdef FEAT_VIRTUALEDIT
3593 	     virtual_active() ||
3594 #endif
3595 	     (VIsual_active && wp->w_buffer == curwin->w_buffer)))
3596 	{
3597 	    vcol = v;
3598 	}
3599 
3600 	/* Handle a character that's not completely on the screen: Put ptr at
3601 	 * that character but skip the first few screen characters. */
3602 	if (vcol > v)
3603 	{
3604 	    vcol -= c;
3605 #ifdef FEAT_MBYTE
3606 	    ptr = prev_ptr;
3607 #else
3608 	    --ptr;
3609 #endif
3610 	    /* If the character fits on the screen, don't need to skip it.
3611 	     * Except for a TAB. */
3612 	    if ((
3613 #ifdef FEAT_MBYTE
3614 			(*mb_ptr2cells)(ptr) >= c ||
3615 #endif
3616 		       *ptr == TAB) && col == 0)
3617 	       n_skip = v - vcol;
3618 	}
3619 
3620 	/*
3621 	 * Adjust for when the inverted text is before the screen,
3622 	 * and when the start of the inverted text is before the screen.
3623 	 */
3624 	if (tocol <= vcol)
3625 	    fromcol = 0;
3626 	else if (fromcol >= 0 && fromcol < vcol)
3627 	    fromcol = vcol;
3628 
3629 #ifdef FEAT_LINEBREAK
3630 	/* When w_skipcol is non-zero, first line needs 'showbreak' */
3631 	if (wp->w_p_wrap)
3632 	    need_showbreak = TRUE;
3633 #endif
3634 #ifdef FEAT_SPELL
3635 	/* When spell checking a word we need to figure out the start of the
3636 	 * word and if it's badly spelled or not. */
3637 	if (has_spell)
3638 	{
3639 	    int		len;
3640 	    colnr_T	linecol = (colnr_T)(ptr - line);
3641 	    hlf_T	spell_hlf = HLF_COUNT;
3642 
3643 	    pos = wp->w_cursor;
3644 	    wp->w_cursor.lnum = lnum;
3645 	    wp->w_cursor.col = linecol;
3646 	    len = spell_move_to(wp, FORWARD, TRUE, TRUE, &spell_hlf);
3647 
3648 	    /* spell_move_to() may call ml_get() and make "line" invalid */
3649 	    line = ml_get_buf(wp->w_buffer, lnum, FALSE);
3650 	    ptr = line + linecol;
3651 
3652 	    if (len == 0 || (int)wp->w_cursor.col > ptr - line)
3653 	    {
3654 		/* no bad word found at line start, don't check until end of a
3655 		 * word */
3656 		spell_hlf = HLF_COUNT;
3657 		word_end = (int)(spell_to_word_end(ptr, wp) - line + 1);
3658 	    }
3659 	    else
3660 	    {
3661 		/* bad word found, use attributes until end of word */
3662 		word_end = wp->w_cursor.col + len + 1;
3663 
3664 		/* Turn index into actual attributes. */
3665 		if (spell_hlf != HLF_COUNT)
3666 		    spell_attr = highlight_attr[spell_hlf];
3667 	    }
3668 	    wp->w_cursor = pos;
3669 
3670 # ifdef FEAT_SYN_HL
3671 	    /* Need to restart syntax highlighting for this line. */
3672 	    if (has_syntax)
3673 		syntax_start(wp, lnum);
3674 # endif
3675 	}
3676 #endif
3677     }
3678 
3679     /*
3680      * Correct highlighting for cursor that can't be disabled.
3681      * Avoids having to check this for each character.
3682      */
3683     if (fromcol >= 0)
3684     {
3685 	if (noinvcur)
3686 	{
3687 	    if ((colnr_T)fromcol == wp->w_virtcol)
3688 	    {
3689 		/* highlighting starts at cursor, let it start just after the
3690 		 * cursor */
3691 		fromcol_prev = fromcol;
3692 		fromcol = -1;
3693 	    }
3694 	    else if ((colnr_T)fromcol < wp->w_virtcol)
3695 		/* restart highlighting after the cursor */
3696 		fromcol_prev = wp->w_virtcol;
3697 	}
3698 	if (fromcol >= tocol)
3699 	    fromcol = -1;
3700     }
3701 
3702 #ifdef FEAT_SEARCH_EXTRA
3703     /*
3704      * Handle highlighting the last used search pattern and matches.
3705      * Do this for both search_hl and the match list.
3706      */
3707     cur = wp->w_match_head;
3708     shl_flag = FALSE;
3709     while (cur != NULL || shl_flag == FALSE)
3710     {
3711 	if (shl_flag == FALSE)
3712 	{
3713 	    shl = &search_hl;
3714 	    shl_flag = TRUE;
3715 	}
3716 	else
3717 	    shl = &cur->hl;
3718 	shl->startcol = MAXCOL;
3719 	shl->endcol = MAXCOL;
3720 	shl->attr_cur = 0;
3721 	shl->is_addpos = FALSE;
3722 	v = (long)(ptr - line);
3723 	if (cur != NULL)
3724 	    cur->pos.cur = 0;
3725 	next_search_hl(wp, shl, lnum, (colnr_T)v,
3726 					       shl == &search_hl ? NULL : cur);
3727 
3728 	/* Need to get the line again, a multi-line regexp may have made it
3729 	 * invalid. */
3730 	line = ml_get_buf(wp->w_buffer, lnum, FALSE);
3731 	ptr = line + v;
3732 
3733 	if (shl->lnum != 0 && shl->lnum <= lnum)
3734 	{
3735 	    if (shl->lnum == lnum)
3736 		shl->startcol = shl->rm.startpos[0].col;
3737 	    else
3738 		shl->startcol = 0;
3739 	    if (lnum == shl->lnum + shl->rm.endpos[0].lnum
3740 						- shl->rm.startpos[0].lnum)
3741 		shl->endcol = shl->rm.endpos[0].col;
3742 	    else
3743 		shl->endcol = MAXCOL;
3744 	    /* Highlight one character for an empty match. */
3745 	    if (shl->startcol == shl->endcol)
3746 	    {
3747 #ifdef FEAT_MBYTE
3748 		if (has_mbyte && line[shl->endcol] != NUL)
3749 		    shl->endcol += (*mb_ptr2len)(line + shl->endcol);
3750 		else
3751 #endif
3752 		    ++shl->endcol;
3753 	    }
3754 	    if ((long)shl->startcol < v)  /* match at leftcol */
3755 	    {
3756 		shl->attr_cur = shl->attr;
3757 		search_attr = shl->attr;
3758 	    }
3759 	    area_highlighting = TRUE;
3760 	}
3761 	if (shl != &search_hl && cur != NULL)
3762 	    cur = cur->next;
3763     }
3764 #endif
3765 
3766 #ifdef FEAT_SYN_HL
3767     /* Cursor line highlighting for 'cursorline' in the current window.  Not
3768      * when Visual mode is active, because it's not clear what is selected
3769      * then. */
3770     if (wp->w_p_cul && lnum == wp->w_cursor.lnum
3771 					 && !(wp == curwin && VIsual_active))
3772     {
3773 	line_attr = HL_ATTR(HLF_CUL);
3774 	area_highlighting = TRUE;
3775     }
3776 #endif
3777 
3778     off = (unsigned)(current_ScreenLine - ScreenLines);
3779     col = 0;
3780 #ifdef FEAT_RIGHTLEFT
3781     if (wp->w_p_rl)
3782     {
3783 	/* Rightleft window: process the text in the normal direction, but put
3784 	 * it in current_ScreenLine[] from right to left.  Start at the
3785 	 * rightmost column of the window. */
3786 	col = wp->w_width - 1;
3787 	off += col;
3788     }
3789 #endif
3790 
3791     /*
3792      * Repeat for the whole displayed line.
3793      */
3794     for (;;)
3795     {
3796 #ifdef FEAT_CONCEAL
3797 	has_match_conc = 0;
3798 #endif
3799 	/* Skip this quickly when working on the text. */
3800 	if (draw_state != WL_LINE)
3801 	{
3802 #ifdef FEAT_CMDWIN
3803 	    if (draw_state == WL_CMDLINE - 1 && n_extra == 0)
3804 	    {
3805 		draw_state = WL_CMDLINE;
3806 		if (cmdwin_type != 0 && wp == curwin)
3807 		{
3808 		    /* Draw the cmdline character. */
3809 		    n_extra = 1;
3810 		    c_extra = cmdwin_type;
3811 		    char_attr = HL_ATTR(HLF_AT);
3812 		}
3813 	    }
3814 #endif
3815 
3816 #ifdef FEAT_FOLDING
3817 	    if (draw_state == WL_FOLD - 1 && n_extra == 0)
3818 	    {
3819 		int fdc = compute_foldcolumn(wp, 0);
3820 
3821 		draw_state = WL_FOLD;
3822 		if (fdc > 0)
3823 		{
3824 		    /* Draw the 'foldcolumn'.  Allocate a buffer, "extra" may
3825 		     * already be in use. */
3826 		    vim_free(p_extra_free);
3827 		    p_extra_free = alloc(12 + 1);
3828 
3829 		    if (p_extra_free != NULL)
3830 		    {
3831 			fill_foldcolumn(p_extra_free, wp, FALSE, lnum);
3832 			n_extra = fdc;
3833 			p_extra_free[n_extra] = NUL;
3834 			p_extra = p_extra_free;
3835 			c_extra = NUL;
3836 			char_attr = HL_ATTR(HLF_FC);
3837 		    }
3838 		}
3839 	    }
3840 #endif
3841 
3842 #ifdef FEAT_SIGNS
3843 	    if (draw_state == WL_SIGN - 1 && n_extra == 0)
3844 	    {
3845 		draw_state = WL_SIGN;
3846 		/* Show the sign column when there are any signs in this
3847 		 * buffer or when using Netbeans. */
3848 		if (signcolumn_on(wp))
3849 		{
3850 		    int	text_sign;
3851 # ifdef FEAT_SIGN_ICONS
3852 		    int	icon_sign;
3853 # endif
3854 
3855 		    /* Draw two cells with the sign value or blank. */
3856 		    c_extra = ' ';
3857 		    char_attr = HL_ATTR(HLF_SC);
3858 		    n_extra = 2;
3859 
3860 		    if (row == startrow
3861 #ifdef FEAT_DIFF
3862 			    + filler_lines && filler_todo <= 0
3863 #endif
3864 			    )
3865 		    {
3866 			text_sign = buf_getsigntype(wp->w_buffer, lnum,
3867 								   SIGN_TEXT);
3868 # ifdef FEAT_SIGN_ICONS
3869 			icon_sign = buf_getsigntype(wp->w_buffer, lnum,
3870 								   SIGN_ICON);
3871 			if (gui.in_use && icon_sign != 0)
3872 			{
3873 			    /* Use the image in this position. */
3874 			    c_extra = SIGN_BYTE;
3875 #  ifdef FEAT_NETBEANS_INTG
3876 			    if (buf_signcount(wp->w_buffer, lnum) > 1)
3877 				c_extra = MULTISIGN_BYTE;
3878 #  endif
3879 			    char_attr = icon_sign;
3880 			}
3881 			else
3882 # endif
3883 			    if (text_sign != 0)
3884 			{
3885 			    p_extra = sign_get_text(text_sign);
3886 			    if (p_extra != NULL)
3887 			    {
3888 				c_extra = NUL;
3889 				n_extra = (int)STRLEN(p_extra);
3890 			    }
3891 			    char_attr = sign_get_attr(text_sign, FALSE);
3892 			}
3893 		    }
3894 		}
3895 	    }
3896 #endif
3897 
3898 	    if (draw_state == WL_NR - 1 && n_extra == 0)
3899 	    {
3900 		draw_state = WL_NR;
3901 		/* Display the absolute or relative line number. After the
3902 		 * first fill with blanks when the 'n' flag isn't in 'cpo' */
3903 		if ((wp->w_p_nu || wp->w_p_rnu)
3904 			&& (row == startrow
3905 #ifdef FEAT_DIFF
3906 			    + filler_lines
3907 #endif
3908 			    || vim_strchr(p_cpo, CPO_NUMCOL) == NULL))
3909 		{
3910 		    /* Draw the line number (empty space after wrapping). */
3911 		    if (row == startrow
3912 #ifdef FEAT_DIFF
3913 			    + filler_lines
3914 #endif
3915 			    )
3916 		    {
3917 			long num;
3918 			char *fmt = "%*ld ";
3919 
3920 			if (wp->w_p_nu && !wp->w_p_rnu)
3921 			    /* 'number' + 'norelativenumber' */
3922 			    num = (long)lnum;
3923 			else
3924 			{
3925 			    /* 'relativenumber', don't use negative numbers */
3926 			    num = labs((long)get_cursor_rel_lnum(wp, lnum));
3927 			    if (num == 0 && wp->w_p_nu && wp->w_p_rnu)
3928 			    {
3929 				/* 'number' + 'relativenumber' */
3930 				num = lnum;
3931 				fmt = "%-*ld ";
3932 			    }
3933 			}
3934 
3935 			sprintf((char *)extra, fmt,
3936 						number_width(wp), num);
3937 			if (wp->w_skipcol > 0)
3938 			    for (p_extra = extra; *p_extra == ' '; ++p_extra)
3939 				*p_extra = '-';
3940 #ifdef FEAT_RIGHTLEFT
3941 			if (wp->w_p_rl)		    /* reverse line numbers */
3942 			    rl_mirror(extra);
3943 #endif
3944 			p_extra = extra;
3945 			c_extra = NUL;
3946 		    }
3947 		    else
3948 			c_extra = ' ';
3949 		    n_extra = number_width(wp) + 1;
3950 		    char_attr = HL_ATTR(HLF_N);
3951 #ifdef FEAT_SYN_HL
3952 		    /* When 'cursorline' is set highlight the line number of
3953 		     * the current line differently.
3954 		     * TODO: Can we use CursorLine instead of CursorLineNr
3955 		     * when CursorLineNr isn't set? */
3956 		    if ((wp->w_p_cul || wp->w_p_rnu)
3957 						 && lnum == wp->w_cursor.lnum)
3958 			char_attr = HL_ATTR(HLF_CLN);
3959 #endif
3960 		}
3961 	    }
3962 
3963 #ifdef FEAT_LINEBREAK
3964 	    if (wp->w_p_brisbr && draw_state == WL_BRI - 1
3965 					     && n_extra == 0 && *p_sbr != NUL)
3966 		/* draw indent after showbreak value */
3967 		draw_state = WL_BRI;
3968 	    else if (wp->w_p_brisbr && draw_state == WL_SBR && n_extra == 0)
3969 		/* After the showbreak, draw the breakindent */
3970 		draw_state = WL_BRI - 1;
3971 
3972 	    /* draw 'breakindent': indent wrapped text accordingly */
3973 	    if (draw_state == WL_BRI - 1 && n_extra == 0)
3974 	    {
3975 		draw_state = WL_BRI;
3976 		/* if need_showbreak is set, breakindent also applies */
3977 		if (wp->w_p_bri && n_extra == 0
3978 					 && (row != startrow || need_showbreak)
3979 # ifdef FEAT_DIFF
3980 			&& filler_lines == 0
3981 # endif
3982 		   )
3983 		{
3984 		    char_attr = 0;
3985 # ifdef FEAT_DIFF
3986 		    if (diff_hlf != (hlf_T)0)
3987 		    {
3988 			char_attr = HL_ATTR(diff_hlf);
3989 #  ifdef FEAT_SYN_HL
3990 			if (wp->w_p_cul && lnum == wp->w_cursor.lnum)
3991 			    char_attr = hl_combine_attr(char_attr,
3992 							    HL_ATTR(HLF_CUL));
3993 #  endif
3994 		    }
3995 # endif
3996 		    p_extra = NULL;
3997 		    c_extra = ' ';
3998 		    n_extra = get_breakindent_win(wp,
3999 				       ml_get_buf(wp->w_buffer, lnum, FALSE));
4000 		    /* Correct end of highlighted area for 'breakindent',
4001 		     * required when 'linebreak' is also set. */
4002 		    if (tocol == vcol)
4003 			tocol += n_extra;
4004 		}
4005 	    }
4006 #endif
4007 
4008 #if defined(FEAT_LINEBREAK) || defined(FEAT_DIFF)
4009 	    if (draw_state == WL_SBR - 1 && n_extra == 0)
4010 	    {
4011 		draw_state = WL_SBR;
4012 # ifdef FEAT_DIFF
4013 		if (filler_todo > 0)
4014 		{
4015 		    /* Draw "deleted" diff line(s). */
4016 		    if (char2cells(fill_diff) > 1)
4017 			c_extra = '-';
4018 		    else
4019 			c_extra = fill_diff;
4020 #  ifdef FEAT_RIGHTLEFT
4021 		    if (wp->w_p_rl)
4022 			n_extra = col + 1;
4023 		    else
4024 #  endif
4025 			n_extra = wp->w_width - col;
4026 		    char_attr = HL_ATTR(HLF_DED);
4027 		}
4028 # endif
4029 # ifdef FEAT_LINEBREAK
4030 		if (*p_sbr != NUL && need_showbreak)
4031 		{
4032 		    /* Draw 'showbreak' at the start of each broken line. */
4033 		    p_extra = p_sbr;
4034 		    c_extra = NUL;
4035 		    n_extra = (int)STRLEN(p_sbr);
4036 		    char_attr = HL_ATTR(HLF_AT);
4037 		    need_showbreak = FALSE;
4038 		    vcol_sbr = vcol + MB_CHARLEN(p_sbr);
4039 		    /* Correct end of highlighted area for 'showbreak',
4040 		     * required when 'linebreak' is also set. */
4041 		    if (tocol == vcol)
4042 			tocol += n_extra;
4043 #ifdef FEAT_SYN_HL
4044 		    /* combine 'showbreak' with 'cursorline' */
4045 		    if (wp->w_p_cul && lnum == wp->w_cursor.lnum)
4046 			char_attr = hl_combine_attr(char_attr,
4047 							    HL_ATTR(HLF_CUL));
4048 #endif
4049 		}
4050 # endif
4051 	    }
4052 #endif
4053 
4054 	    if (draw_state == WL_LINE - 1 && n_extra == 0)
4055 	    {
4056 		draw_state = WL_LINE;
4057 		if (saved_n_extra)
4058 		{
4059 		    /* Continue item from end of wrapped line. */
4060 		    n_extra = saved_n_extra;
4061 		    c_extra = saved_c_extra;
4062 		    p_extra = saved_p_extra;
4063 		    char_attr = saved_char_attr;
4064 		}
4065 		else
4066 		    char_attr = 0;
4067 	    }
4068 	}
4069 
4070 	/* When still displaying '$' of change command, stop at cursor */
4071 	if (dollar_vcol >= 0 && wp == curwin
4072 		   && lnum == wp->w_cursor.lnum && vcol >= (long)wp->w_virtcol
4073 #ifdef FEAT_DIFF
4074 				   && filler_todo <= 0
4075 #endif
4076 		)
4077 	{
4078 	    screen_line(screen_row, wp->w_wincol, col, -(int)wp->w_width,
4079 						    HAS_RIGHTLEFT(wp->w_p_rl));
4080 	    /* Pretend we have finished updating the window.  Except when
4081 	     * 'cursorcolumn' is set. */
4082 #ifdef FEAT_SYN_HL
4083 	    if (wp->w_p_cuc)
4084 		row = wp->w_cline_row + wp->w_cline_height;
4085 	    else
4086 #endif
4087 		row = wp->w_height;
4088 	    break;
4089 	}
4090 
4091 	if (draw_state == WL_LINE && area_highlighting)
4092 	{
4093 	    /* handle Visual or match highlighting in this line */
4094 	    if (vcol == fromcol
4095 #ifdef FEAT_MBYTE
4096 		    || (has_mbyte && vcol + 1 == fromcol && n_extra == 0
4097 			&& (*mb_ptr2cells)(ptr) > 1)
4098 #endif
4099 		    || ((int)vcol_prev == fromcol_prev
4100 			&& vcol_prev < vcol	/* not at margin */
4101 			&& vcol < tocol))
4102 		area_attr = attr;		/* start highlighting */
4103 	    else if (area_attr != 0
4104 		    && (vcol == tocol
4105 			|| (noinvcur && (colnr_T)vcol == wp->w_virtcol)))
4106 		area_attr = 0;			/* stop highlighting */
4107 
4108 #ifdef FEAT_SEARCH_EXTRA
4109 	    if (!n_extra)
4110 	    {
4111 		/*
4112 		 * Check for start/end of search pattern match.
4113 		 * After end, check for start/end of next match.
4114 		 * When another match, have to check for start again.
4115 		 * Watch out for matching an empty string!
4116 		 * Do this for 'search_hl' and the match list (ordered by
4117 		 * priority).
4118 		 */
4119 		v = (long)(ptr - line);
4120 		cur = wp->w_match_head;
4121 		shl_flag = FALSE;
4122 		while (cur != NULL || shl_flag == FALSE)
4123 		{
4124 		    if (shl_flag == FALSE
4125 			    && ((cur != NULL
4126 				    && cur->priority > SEARCH_HL_PRIORITY)
4127 				|| cur == NULL))
4128 		    {
4129 			shl = &search_hl;
4130 			shl_flag = TRUE;
4131 		    }
4132 		    else
4133 			shl = &cur->hl;
4134 		    if (cur != NULL)
4135 			cur->pos.cur = 0;
4136 		    pos_inprogress = TRUE;
4137 		    while (shl->rm.regprog != NULL
4138 					   || (cur != NULL && pos_inprogress))
4139 		    {
4140 			if (shl->startcol != MAXCOL
4141 				&& v >= (long)shl->startcol
4142 				&& v < (long)shl->endcol)
4143 			{
4144 #ifdef FEAT_MBYTE
4145 			    int tmp_col = v + MB_PTR2LEN(ptr);
4146 
4147 			    if (shl->endcol < tmp_col)
4148 				shl->endcol = tmp_col;
4149 #endif
4150 			    shl->attr_cur = shl->attr;
4151 #ifdef FEAT_CONCEAL
4152 			    if (cur != NULL && syn_name2id((char_u *)"Conceal")
4153 							       == cur->hlg_id)
4154 			    {
4155 				has_match_conc =
4156 					     v == (long)shl->startcol ? 2 : 1;
4157 				match_conc = cur->conceal_char;
4158 			    }
4159 			    else
4160 				has_match_conc = match_conc = 0;
4161 #endif
4162 			}
4163 			else if (v == (long)shl->endcol)
4164 			{
4165 			    shl->attr_cur = 0;
4166 			    next_search_hl(wp, shl, lnum, (colnr_T)v,
4167 					       shl == &search_hl ? NULL : cur);
4168 			    pos_inprogress = cur == NULL || cur->pos.cur == 0
4169 							       ? FALSE : TRUE;
4170 
4171 			    /* Need to get the line again, a multi-line regexp
4172 			     * may have made it invalid. */
4173 			    line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4174 			    ptr = line + v;
4175 
4176 			    if (shl->lnum == lnum)
4177 			    {
4178 				shl->startcol = shl->rm.startpos[0].col;
4179 				if (shl->rm.endpos[0].lnum == 0)
4180 				    shl->endcol = shl->rm.endpos[0].col;
4181 				else
4182 				    shl->endcol = MAXCOL;
4183 
4184 				if (shl->startcol == shl->endcol)
4185 				{
4186 				    /* highlight empty match, try again after
4187 				     * it */
4188 #ifdef FEAT_MBYTE
4189 				    if (has_mbyte)
4190 					shl->endcol += (*mb_ptr2len)(line
4191 							       + shl->endcol);
4192 				    else
4193 #endif
4194 					++shl->endcol;
4195 				}
4196 
4197 				/* Loop to check if the match starts at the
4198 				 * current position */
4199 				continue;
4200 			    }
4201 			}
4202 			break;
4203 		    }
4204 		    if (shl != &search_hl && cur != NULL)
4205 			cur = cur->next;
4206 		}
4207 
4208 		/* Use attributes from match with highest priority among
4209 		 * 'search_hl' and the match list. */
4210 		search_attr = search_hl.attr_cur;
4211 		cur = wp->w_match_head;
4212 		shl_flag = FALSE;
4213 		while (cur != NULL || shl_flag == FALSE)
4214 		{
4215 		    if (shl_flag == FALSE
4216 			    && ((cur != NULL
4217 				    && cur->priority > SEARCH_HL_PRIORITY)
4218 				|| cur == NULL))
4219 		    {
4220 			shl = &search_hl;
4221 			shl_flag = TRUE;
4222 		    }
4223 		    else
4224 			shl = &cur->hl;
4225 		    if (shl->attr_cur != 0)
4226 			search_attr = shl->attr_cur;
4227 		    if (shl != &search_hl && cur != NULL)
4228 			cur = cur->next;
4229 		}
4230 		/* Only highlight one character after the last column. */
4231 		if (*ptr == NUL && (did_line_attr >= 1
4232 				    || (wp->w_p_list && lcs_eol_one == -1)))
4233 		    search_attr = 0;
4234 	    }
4235 #endif
4236 
4237 #ifdef FEAT_DIFF
4238 	    if (diff_hlf != (hlf_T)0)
4239 	    {
4240 		if (diff_hlf == HLF_CHD && ptr - line >= change_start
4241 							      && n_extra == 0)
4242 		    diff_hlf = HLF_TXD;		/* changed text */
4243 		if (diff_hlf == HLF_TXD && ptr - line > change_end
4244 							      && n_extra == 0)
4245 		    diff_hlf = HLF_CHD;		/* changed line */
4246 		line_attr = HL_ATTR(diff_hlf);
4247 		if (wp->w_p_cul && lnum == wp->w_cursor.lnum)
4248 		    line_attr = hl_combine_attr(line_attr, HL_ATTR(HLF_CUL));
4249 	    }
4250 #endif
4251 
4252 	    /* Decide which of the highlight attributes to use. */
4253 	    attr_pri = TRUE;
4254 #ifdef LINE_ATTR
4255 	    if (area_attr != 0)
4256 		char_attr = hl_combine_attr(line_attr, area_attr);
4257 	    else if (search_attr != 0)
4258 		char_attr = hl_combine_attr(line_attr, search_attr);
4259 		/* Use line_attr when not in the Visual or 'incsearch' area
4260 		 * (area_attr may be 0 when "noinvcur" is set). */
4261 	    else if (line_attr != 0 && ((fromcol == -10 && tocol == MAXCOL)
4262 				|| vcol < fromcol || vcol_prev < fromcol_prev
4263 				|| vcol >= tocol))
4264 		char_attr = line_attr;
4265 #else
4266 	    if (area_attr != 0)
4267 		char_attr = area_attr;
4268 	    else if (search_attr != 0)
4269 		char_attr = search_attr;
4270 #endif
4271 	    else
4272 	    {
4273 		attr_pri = FALSE;
4274 #ifdef FEAT_SYN_HL
4275 		if (has_syntax)
4276 		    char_attr = syntax_attr;
4277 		else
4278 #endif
4279 		    char_attr = 0;
4280 	    }
4281 	}
4282 
4283 	/*
4284 	 * Get the next character to put on the screen.
4285 	 */
4286 	/*
4287 	 * The "p_extra" points to the extra stuff that is inserted to
4288 	 * represent special characters (non-printable stuff) and other
4289 	 * things.  When all characters are the same, c_extra is used.
4290 	 * "p_extra" must end in a NUL to avoid mb_ptr2len() reads past
4291 	 * "p_extra[n_extra]".
4292 	 * For the '$' of the 'list' option, n_extra == 1, p_extra == "".
4293 	 */
4294 	if (n_extra > 0)
4295 	{
4296 	    if (c_extra != NUL)
4297 	    {
4298 		c = c_extra;
4299 #ifdef FEAT_MBYTE
4300 		mb_c = c;	/* doesn't handle non-utf-8 multi-byte! */
4301 		if (enc_utf8 && utf_char2len(c) > 1)
4302 		{
4303 		    mb_utf8 = TRUE;
4304 		    u8cc[0] = 0;
4305 		    c = 0xc0;
4306 		}
4307 		else
4308 		    mb_utf8 = FALSE;
4309 #endif
4310 	    }
4311 	    else
4312 	    {
4313 		c = *p_extra;
4314 #ifdef FEAT_MBYTE
4315 		if (has_mbyte)
4316 		{
4317 		    mb_c = c;
4318 		    if (enc_utf8)
4319 		    {
4320 			/* If the UTF-8 character is more than one byte:
4321 			 * Decode it into "mb_c". */
4322 			mb_l = utfc_ptr2len(p_extra);
4323 			mb_utf8 = FALSE;
4324 			if (mb_l > n_extra)
4325 			    mb_l = 1;
4326 			else if (mb_l > 1)
4327 			{
4328 			    mb_c = utfc_ptr2char(p_extra, u8cc);
4329 			    mb_utf8 = TRUE;
4330 			    c = 0xc0;
4331 			}
4332 		    }
4333 		    else
4334 		    {
4335 			/* if this is a DBCS character, put it in "mb_c" */
4336 			mb_l = MB_BYTE2LEN(c);
4337 			if (mb_l >= n_extra)
4338 			    mb_l = 1;
4339 			else if (mb_l > 1)
4340 			    mb_c = (c << 8) + p_extra[1];
4341 		    }
4342 		    if (mb_l == 0)  /* at the NUL at end-of-line */
4343 			mb_l = 1;
4344 
4345 		    /* If a double-width char doesn't fit display a '>' in the
4346 		     * last column. */
4347 		    if ((
4348 # ifdef FEAT_RIGHTLEFT
4349 			    wp->w_p_rl ? (col <= 0) :
4350 # endif
4351 				    (col >= wp->w_width - 1))
4352 			    && (*mb_char2cells)(mb_c) == 2)
4353 		    {
4354 			c = '>';
4355 			mb_c = c;
4356 			mb_l = 1;
4357 			mb_utf8 = FALSE;
4358 			multi_attr = HL_ATTR(HLF_AT);
4359 			/* put the pointer back to output the double-width
4360 			 * character at the start of the next line. */
4361 			++n_extra;
4362 			--p_extra;
4363 		    }
4364 		    else
4365 		    {
4366 			n_extra -= mb_l - 1;
4367 			p_extra += mb_l - 1;
4368 		    }
4369 		}
4370 #endif
4371 		++p_extra;
4372 	    }
4373 	    --n_extra;
4374 	}
4375 	else
4376 	{
4377 #ifdef FEAT_LINEBREAK
4378 	    int c0;
4379 #endif
4380 
4381 	    if (p_extra_free != NULL)
4382 		VIM_CLEAR(p_extra_free);
4383 	    /*
4384 	     * Get a character from the line itself.
4385 	     */
4386 	    c = *ptr;
4387 #ifdef FEAT_LINEBREAK
4388 	    c0 = *ptr;
4389 #endif
4390 #ifdef FEAT_MBYTE
4391 	    if (has_mbyte)
4392 	    {
4393 		mb_c = c;
4394 		if (enc_utf8)
4395 		{
4396 		    /* If the UTF-8 character is more than one byte: Decode it
4397 		     * into "mb_c". */
4398 		    mb_l = utfc_ptr2len(ptr);
4399 		    mb_utf8 = FALSE;
4400 		    if (mb_l > 1)
4401 		    {
4402 			mb_c = utfc_ptr2char(ptr, u8cc);
4403 			/* Overlong encoded ASCII or ASCII with composing char
4404 			 * is displayed normally, except a NUL. */
4405 			if (mb_c < 0x80)
4406 			{
4407 			    c = mb_c;
4408 # ifdef FEAT_LINEBREAK
4409 			    c0 = mb_c;
4410 # endif
4411 			}
4412 			mb_utf8 = TRUE;
4413 
4414 			/* At start of the line we can have a composing char.
4415 			 * Draw it as a space with a composing char. */
4416 			if (utf_iscomposing(mb_c))
4417 			{
4418 			    int i;
4419 
4420 			    for (i = Screen_mco - 1; i > 0; --i)
4421 				u8cc[i] = u8cc[i - 1];
4422 			    u8cc[0] = mb_c;
4423 			    mb_c = ' ';
4424 			}
4425 		    }
4426 
4427 		    if ((mb_l == 1 && c >= 0x80)
4428 			    || (mb_l >= 1 && mb_c == 0)
4429 			    || (mb_l > 1 && (!vim_isprintc(mb_c)
4430 # ifdef UNICODE16
4431 							 || mb_c >= 0x10000
4432 # endif
4433 							 )))
4434 		    {
4435 			/*
4436 			 * Illegal UTF-8 byte: display as <xx>.
4437 			 * Non-BMP character : display as ? or fullwidth ?.
4438 			 */
4439 # ifdef UNICODE16
4440 			if (mb_c < 0x10000)
4441 # endif
4442 			{
4443 			    transchar_hex(extra, mb_c);
4444 # ifdef FEAT_RIGHTLEFT
4445 			    if (wp->w_p_rl)		/* reverse */
4446 				rl_mirror(extra);
4447 # endif
4448 			}
4449 # ifdef UNICODE16
4450 			else if (utf_char2cells(mb_c) != 2)
4451 			    STRCPY(extra, "?");
4452 			else
4453 			    /* 0xff1f in UTF-8: full-width '?' */
4454 			    STRCPY(extra, "\357\274\237");
4455 # endif
4456 
4457 			p_extra = extra;
4458 			c = *p_extra;
4459 			mb_c = mb_ptr2char_adv(&p_extra);
4460 			mb_utf8 = (c >= 0x80);
4461 			n_extra = (int)STRLEN(p_extra);
4462 			c_extra = NUL;
4463 			if (area_attr == 0 && search_attr == 0)
4464 			{
4465 			    n_attr = n_extra + 1;
4466 			    extra_attr = HL_ATTR(HLF_8);
4467 			    saved_attr2 = char_attr; /* save current attr */
4468 			}
4469 		    }
4470 		    else if (mb_l == 0)  /* at the NUL at end-of-line */
4471 			mb_l = 1;
4472 #ifdef FEAT_ARABIC
4473 		    else if (p_arshape && !p_tbidi && ARABIC_CHAR(mb_c))
4474 		    {
4475 			/* Do Arabic shaping. */
4476 			int	pc, pc1, nc;
4477 			int	pcc[MAX_MCO];
4478 
4479 			/* The idea of what is the previous and next
4480 			 * character depends on 'rightleft'. */
4481 			if (wp->w_p_rl)
4482 			{
4483 			    pc = prev_c;
4484 			    pc1 = prev_c1;
4485 			    nc = utf_ptr2char(ptr + mb_l);
4486 			    prev_c1 = u8cc[0];
4487 			}
4488 			else
4489 			{
4490 			    pc = utfc_ptr2char(ptr + mb_l, pcc);
4491 			    nc = prev_c;
4492 			    pc1 = pcc[0];
4493 			}
4494 			prev_c = mb_c;
4495 
4496 			mb_c = arabic_shape(mb_c, &c, &u8cc[0], pc, pc1, nc);
4497 		    }
4498 		    else
4499 			prev_c = mb_c;
4500 #endif
4501 		}
4502 		else	/* enc_dbcs */
4503 		{
4504 		    mb_l = MB_BYTE2LEN(c);
4505 		    if (mb_l == 0)  /* at the NUL at end-of-line */
4506 			mb_l = 1;
4507 		    else if (mb_l > 1)
4508 		    {
4509 			/* We assume a second byte below 32 is illegal.
4510 			 * Hopefully this is OK for all double-byte encodings!
4511 			 */
4512 			if (ptr[1] >= 32)
4513 			    mb_c = (c << 8) + ptr[1];
4514 			else
4515 			{
4516 			    if (ptr[1] == NUL)
4517 			    {
4518 				/* head byte at end of line */
4519 				mb_l = 1;
4520 				transchar_nonprint(extra, c);
4521 			    }
4522 			    else
4523 			    {
4524 				/* illegal tail byte */
4525 				mb_l = 2;
4526 				STRCPY(extra, "XX");
4527 			    }
4528 			    p_extra = extra;
4529 			    n_extra = (int)STRLEN(extra) - 1;
4530 			    c_extra = NUL;
4531 			    c = *p_extra++;
4532 			    if (area_attr == 0 && search_attr == 0)
4533 			    {
4534 				n_attr = n_extra + 1;
4535 				extra_attr = HL_ATTR(HLF_8);
4536 				saved_attr2 = char_attr; /* save current attr */
4537 			    }
4538 			    mb_c = c;
4539 			}
4540 		    }
4541 		}
4542 		/* If a double-width char doesn't fit display a '>' in the
4543 		 * last column; the character is displayed at the start of the
4544 		 * next line. */
4545 		if ((
4546 # ifdef FEAT_RIGHTLEFT
4547 			    wp->w_p_rl ? (col <= 0) :
4548 # endif
4549 				(col >= wp->w_width - 1))
4550 			&& (*mb_char2cells)(mb_c) == 2)
4551 		{
4552 		    c = '>';
4553 		    mb_c = c;
4554 		    mb_utf8 = FALSE;
4555 		    mb_l = 1;
4556 		    multi_attr = HL_ATTR(HLF_AT);
4557 		    /* Put pointer back so that the character will be
4558 		     * displayed at the start of the next line. */
4559 		    --ptr;
4560 		}
4561 		else if (*ptr != NUL)
4562 		    ptr += mb_l - 1;
4563 
4564 		/* If a double-width char doesn't fit at the left side display
4565 		 * a '<' in the first column.  Don't do this for unprintable
4566 		 * characters. */
4567 		if (n_skip > 0 && mb_l > 1 && n_extra == 0)
4568 		{
4569 		    n_extra = 1;
4570 		    c_extra = MB_FILLER_CHAR;
4571 		    c = ' ';
4572 		    if (area_attr == 0 && search_attr == 0)
4573 		    {
4574 			n_attr = n_extra + 1;
4575 			extra_attr = HL_ATTR(HLF_AT);
4576 			saved_attr2 = char_attr; /* save current attr */
4577 		    }
4578 		    mb_c = c;
4579 		    mb_utf8 = FALSE;
4580 		    mb_l = 1;
4581 		}
4582 
4583 	    }
4584 #endif
4585 	    ++ptr;
4586 
4587 	    if (extra_check)
4588 	    {
4589 #ifdef FEAT_SPELL
4590 		int	can_spell = TRUE;
4591 #endif
4592 
4593 #ifdef FEAT_TERMINAL
4594 		if (get_term_attr)
4595 		{
4596 		    syntax_attr = term_get_attr(wp->w_buffer, lnum, vcol);
4597 
4598 		    if (!attr_pri)
4599 			char_attr = syntax_attr;
4600 		    else
4601 			char_attr = hl_combine_attr(syntax_attr, char_attr);
4602 		}
4603 #endif
4604 
4605 #ifdef FEAT_SYN_HL
4606 		/* Get syntax attribute, unless still at the start of the line
4607 		 * (double-wide char that doesn't fit). */
4608 		v = (long)(ptr - line);
4609 		if (has_syntax && v > 0)
4610 		{
4611 		    /* Get the syntax attribute for the character.  If there
4612 		     * is an error, disable syntax highlighting. */
4613 		    save_did_emsg = did_emsg;
4614 		    did_emsg = FALSE;
4615 
4616 		    syntax_attr = get_syntax_attr((colnr_T)v - 1,
4617 # ifdef FEAT_SPELL
4618 						has_spell ? &can_spell :
4619 # endif
4620 						NULL, FALSE);
4621 
4622 		    if (did_emsg)
4623 		    {
4624 			wp->w_s->b_syn_error = TRUE;
4625 			has_syntax = FALSE;
4626 		    }
4627 		    else
4628 			did_emsg = save_did_emsg;
4629 #ifdef SYN_TIME_LIMIT
4630 		    if (wp->w_s->b_syn_slow)
4631 			has_syntax = FALSE;
4632 #endif
4633 
4634 		    /* Need to get the line again, a multi-line regexp may
4635 		     * have made it invalid. */
4636 		    line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4637 		    ptr = line + v;
4638 
4639 		    if (!attr_pri)
4640 			char_attr = syntax_attr;
4641 		    else
4642 			char_attr = hl_combine_attr(syntax_attr, char_attr);
4643 # ifdef FEAT_CONCEAL
4644 		    /* no concealing past the end of the line, it interferes
4645 		     * with line highlighting */
4646 		    if (c == NUL)
4647 			syntax_flags = 0;
4648 		    else
4649 			syntax_flags = get_syntax_info(&syntax_seqnr);
4650 # endif
4651 		}
4652 #endif
4653 
4654 #ifdef FEAT_SPELL
4655 		/* Check spelling (unless at the end of the line).
4656 		 * Only do this when there is no syntax highlighting, the
4657 		 * @Spell cluster is not used or the current syntax item
4658 		 * contains the @Spell cluster. */
4659 		if (has_spell && v >= word_end && v > cur_checked_col)
4660 		{
4661 		    spell_attr = 0;
4662 # ifdef FEAT_SYN_HL
4663 		    if (!attr_pri)
4664 			char_attr = syntax_attr;
4665 # endif
4666 		    if (c != 0 && (
4667 # ifdef FEAT_SYN_HL
4668 				!has_syntax ||
4669 # endif
4670 				can_spell))
4671 		    {
4672 			char_u	*prev_ptr, *p;
4673 			int	len;
4674 			hlf_T	spell_hlf = HLF_COUNT;
4675 # ifdef FEAT_MBYTE
4676 			if (has_mbyte)
4677 			{
4678 			    prev_ptr = ptr - mb_l;
4679 			    v -= mb_l - 1;
4680 			}
4681 			else
4682 # endif
4683 			    prev_ptr = ptr - 1;
4684 
4685 			/* Use nextline[] if possible, it has the start of the
4686 			 * next line concatenated. */
4687 			if ((prev_ptr - line) - nextlinecol >= 0)
4688 			    p = nextline + (prev_ptr - line) - nextlinecol;
4689 			else
4690 			    p = prev_ptr;
4691 			cap_col -= (int)(prev_ptr - line);
4692 			len = spell_check(wp, p, &spell_hlf, &cap_col,
4693 								    nochange);
4694 			word_end = v + len;
4695 
4696 			/* In Insert mode only highlight a word that
4697 			 * doesn't touch the cursor. */
4698 			if (spell_hlf != HLF_COUNT
4699 				&& (State & INSERT) != 0
4700 				&& wp->w_cursor.lnum == lnum
4701 				&& wp->w_cursor.col >=
4702 						    (colnr_T)(prev_ptr - line)
4703 				&& wp->w_cursor.col < (colnr_T)word_end)
4704 			{
4705 			    spell_hlf = HLF_COUNT;
4706 			    spell_redraw_lnum = lnum;
4707 			}
4708 
4709 			if (spell_hlf == HLF_COUNT && p != prev_ptr
4710 				       && (p - nextline) + len > nextline_idx)
4711 			{
4712 			    /* Remember that the good word continues at the
4713 			     * start of the next line. */
4714 			    checked_lnum = lnum + 1;
4715 			    checked_col = (int)((p - nextline) + len - nextline_idx);
4716 			}
4717 
4718 			/* Turn index into actual attributes. */
4719 			if (spell_hlf != HLF_COUNT)
4720 			    spell_attr = highlight_attr[spell_hlf];
4721 
4722 			if (cap_col > 0)
4723 			{
4724 			    if (p != prev_ptr
4725 				   && (p - nextline) + cap_col >= nextline_idx)
4726 			    {
4727 				/* Remember that the word in the next line
4728 				 * must start with a capital. */
4729 				capcol_lnum = lnum + 1;
4730 				cap_col = (int)((p - nextline) + cap_col
4731 							       - nextline_idx);
4732 			    }
4733 			    else
4734 				/* Compute the actual column. */
4735 				cap_col += (int)(prev_ptr - line);
4736 			}
4737 		    }
4738 		}
4739 		if (spell_attr != 0)
4740 		{
4741 		    if (!attr_pri)
4742 			char_attr = hl_combine_attr(char_attr, spell_attr);
4743 		    else
4744 			char_attr = hl_combine_attr(spell_attr, char_attr);
4745 		}
4746 #endif
4747 #ifdef FEAT_LINEBREAK
4748 		/*
4749 		 * Found last space before word: check for line break.
4750 		 */
4751 		if (wp->w_p_lbr && c0 == c
4752 				  && VIM_ISBREAK(c) && !VIM_ISBREAK((int)*ptr))
4753 		{
4754 # ifdef FEAT_MBYTE
4755 		    int mb_off = has_mbyte ? (*mb_head_off)(line, ptr - 1) : 0;
4756 # endif
4757 		    char_u *p = ptr - (
4758 # ifdef FEAT_MBYTE
4759 				mb_off +
4760 # endif
4761 				1);
4762 
4763 		    /* TODO: is passing p for start of the line OK? */
4764 		    n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol,
4765 								    NULL) - 1;
4766 		    if (c == TAB && n_extra + col > wp->w_width)
4767 # ifdef FEAT_VARTABS
4768 			n_extra = tabstop_padding(vcol, wp->w_buffer->b_p_ts,
4769 					      wp->w_buffer->b_p_vts_array) - 1;
4770 # else
4771 			n_extra = (int)wp->w_buffer->b_p_ts
4772 				       - vcol % (int)wp->w_buffer->b_p_ts - 1;
4773 # endif
4774 
4775 # ifdef FEAT_MBYTE
4776 		    c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' ';
4777 # else
4778 		    c_extra = ' ';
4779 # endif
4780 		    if (VIM_ISWHITE(c))
4781 		    {
4782 #ifdef FEAT_CONCEAL
4783 			if (c == TAB)
4784 			    /* See "Tab alignment" below. */
4785 			    FIX_FOR_BOGUSCOLS;
4786 #endif
4787 			if (!wp->w_p_list)
4788 			    c = ' ';
4789 		    }
4790 		}
4791 #endif
4792 
4793 		/* 'list': change char 160 to lcs_nbsp and space to lcs_space.
4794 		 */
4795 		if (wp->w_p_list
4796 			&& (((c == 160
4797 #ifdef FEAT_MBYTE
4798 			      || (mb_utf8 && (mb_c == 160 || mb_c == 0x202f))
4799 #endif
4800 			     ) && lcs_nbsp)
4801 			|| (c == ' ' && lcs_space && ptr - line <= trailcol)))
4802 		{
4803 		    c = (c == ' ') ? lcs_space : lcs_nbsp;
4804 		    if (area_attr == 0 && search_attr == 0)
4805 		    {
4806 			n_attr = 1;
4807 			extra_attr = HL_ATTR(HLF_8);
4808 			saved_attr2 = char_attr; /* save current attr */
4809 		    }
4810 #ifdef FEAT_MBYTE
4811 		    mb_c = c;
4812 		    if (enc_utf8 && utf_char2len(c) > 1)
4813 		    {
4814 			mb_utf8 = TRUE;
4815 			u8cc[0] = 0;
4816 			c = 0xc0;
4817 		    }
4818 		    else
4819 			mb_utf8 = FALSE;
4820 #endif
4821 		}
4822 
4823 		if (trailcol != MAXCOL && ptr > line + trailcol && c == ' ')
4824 		{
4825 		    c = lcs_trail;
4826 		    if (!attr_pri)
4827 		    {
4828 			n_attr = 1;
4829 			extra_attr = HL_ATTR(HLF_8);
4830 			saved_attr2 = char_attr; /* save current attr */
4831 		    }
4832 #ifdef FEAT_MBYTE
4833 		    mb_c = c;
4834 		    if (enc_utf8 && utf_char2len(c) > 1)
4835 		    {
4836 			mb_utf8 = TRUE;
4837 			u8cc[0] = 0;
4838 			c = 0xc0;
4839 		    }
4840 		    else
4841 			mb_utf8 = FALSE;
4842 #endif
4843 		}
4844 	    }
4845 
4846 	    /*
4847 	     * Handling of non-printable characters.
4848 	     */
4849 	    if (!vim_isprintc(c))
4850 	    {
4851 		/*
4852 		 * when getting a character from the file, we may have to
4853 		 * turn it into something else on the way to putting it
4854 		 * into "ScreenLines".
4855 		 */
4856 		if (c == TAB && (!wp->w_p_list || lcs_tab1))
4857 		{
4858 		    int tab_len = 0;
4859 		    long vcol_adjusted = vcol; /* removed showbreak length */
4860 #ifdef FEAT_LINEBREAK
4861 		    /* only adjust the tab_len, when at the first column
4862 		     * after the showbreak value was drawn */
4863 		    if (*p_sbr != NUL && vcol == vcol_sbr && wp->w_p_wrap)
4864 			vcol_adjusted = vcol - MB_CHARLEN(p_sbr);
4865 #endif
4866 		    /* tab amount depends on current column */
4867 #ifdef FEAT_VARTABS
4868 		    tab_len = tabstop_padding(vcol_adjusted,
4869 					      wp->w_buffer->b_p_ts,
4870 					      wp->w_buffer->b_p_vts_array) - 1;
4871 #else
4872 		    tab_len = (int)wp->w_buffer->b_p_ts
4873 			       - vcol_adjusted % (int)wp->w_buffer->b_p_ts - 1;
4874 #endif
4875 
4876 #ifdef FEAT_LINEBREAK
4877 		    if (!wp->w_p_lbr || !wp->w_p_list)
4878 #endif
4879 		    /* tab amount depends on current column */
4880 			n_extra = tab_len;
4881 #ifdef FEAT_LINEBREAK
4882 		    else
4883 		    {
4884 			char_u *p;
4885 			int	len = n_extra;
4886 			int	i;
4887 			int	saved_nextra = n_extra;
4888 
4889 #ifdef FEAT_CONCEAL
4890 			if (vcol_off > 0)
4891 			    /* there are characters to conceal */
4892 			    tab_len += vcol_off;
4893 			/* boguscols before FIX_FOR_BOGUSCOLS macro from above
4894 			 */
4895 			if (wp->w_p_list && lcs_tab1 && old_boguscols > 0
4896 							 && n_extra > tab_len)
4897 			    tab_len += n_extra - tab_len;
4898 #endif
4899 
4900 			/* if n_extra > 0, it gives the number of chars, to
4901 			 * use for a tab, else we need to calculate the width
4902 			 * for a tab */
4903 #ifdef FEAT_MBYTE
4904 			len = (tab_len * mb_char2len(lcs_tab2));
4905 			if (n_extra > 0)
4906 			    len += n_extra - tab_len;
4907 #endif
4908 			c = lcs_tab1;
4909 			p = alloc((unsigned)(len + 1));
4910 			vim_memset(p, ' ', len);
4911 			p[len] = NUL;
4912 			vim_free(p_extra_free);
4913 			p_extra_free = p;
4914 			for (i = 0; i < tab_len; i++)
4915 			{
4916 			    if (*p == NUL)
4917 			    {
4918 				tab_len = i;
4919 				break;
4920 			    }
4921 #ifdef FEAT_MBYTE
4922 			    mb_char2bytes(lcs_tab2, p);
4923 			    p += mb_char2len(lcs_tab2);
4924 			    n_extra += mb_char2len(lcs_tab2)
4925 						 - (saved_nextra > 0 ? 1 : 0);
4926 #else
4927 			    p[i] = lcs_tab2;
4928 #endif
4929 			}
4930 			p_extra = p_extra_free;
4931 #ifdef FEAT_CONCEAL
4932 			/* n_extra will be increased by FIX_FOX_BOGUSCOLS
4933 			 * macro below, so need to adjust for that here */
4934 			if (vcol_off > 0)
4935 			    n_extra -= vcol_off;
4936 #endif
4937 		    }
4938 #endif
4939 #ifdef FEAT_CONCEAL
4940 		    {
4941 			int vc_saved = vcol_off;
4942 
4943 			/* Tab alignment should be identical regardless of
4944 			 * 'conceallevel' value. So tab compensates of all
4945 			 * previous concealed characters, and thus resets
4946 			 * vcol_off and boguscols accumulated so far in the
4947 			 * line. Note that the tab can be longer than
4948 			 * 'tabstop' when there are concealed characters. */
4949 			FIX_FOR_BOGUSCOLS;
4950 
4951 			/* Make sure, the highlighting for the tab char will be
4952 			 * correctly set further below (effectively reverts the
4953 			 * FIX_FOR_BOGSUCOLS macro */
4954 			if (n_extra == tab_len + vc_saved && wp->w_p_list
4955 								  && lcs_tab1)
4956 			    tab_len += vc_saved;
4957 		    }
4958 #endif
4959 #ifdef FEAT_MBYTE
4960 		    mb_utf8 = FALSE;	/* don't draw as UTF-8 */
4961 #endif
4962 		    if (wp->w_p_list)
4963 		    {
4964 			c = lcs_tab1;
4965 #ifdef FEAT_LINEBREAK
4966 			if (wp->w_p_lbr)
4967 			    c_extra = NUL; /* using p_extra from above */
4968 			else
4969 #endif
4970 			    c_extra = lcs_tab2;
4971 			n_attr = tab_len + 1;
4972 			extra_attr = HL_ATTR(HLF_8);
4973 			saved_attr2 = char_attr; /* save current attr */
4974 #ifdef FEAT_MBYTE
4975 			mb_c = c;
4976 			if (enc_utf8 && utf_char2len(c) > 1)
4977 			{
4978 			    mb_utf8 = TRUE;
4979 			    u8cc[0] = 0;
4980 			    c = 0xc0;
4981 			}
4982 #endif
4983 		    }
4984 		    else
4985 		    {
4986 			c_extra = ' ';
4987 			c = ' ';
4988 		    }
4989 		}
4990 		else if (c == NUL
4991 			&& (wp->w_p_list
4992 			    || ((fromcol >= 0 || fromcol_prev >= 0)
4993 				&& tocol > vcol
4994 				&& VIsual_mode != Ctrl_V
4995 				&& (
4996 # ifdef FEAT_RIGHTLEFT
4997 				    wp->w_p_rl ? (col >= 0) :
4998 # endif
4999 				    (col < wp->w_width))
5000 				&& !(noinvcur
5001 				    && lnum == wp->w_cursor.lnum
5002 				    && (colnr_T)vcol == wp->w_virtcol)))
5003 			&& lcs_eol_one > 0)
5004 		{
5005 		    /* Display a '$' after the line or highlight an extra
5006 		     * character if the line break is included. */
5007 #if defined(FEAT_DIFF) || defined(LINE_ATTR)
5008 		    /* For a diff line the highlighting continues after the
5009 		     * "$". */
5010 		    if (
5011 # ifdef FEAT_DIFF
5012 			    diff_hlf == (hlf_T)0
5013 #  ifdef LINE_ATTR
5014 			    &&
5015 #  endif
5016 # endif
5017 # ifdef LINE_ATTR
5018 			    line_attr == 0
5019 # endif
5020 		       )
5021 #endif
5022 		    {
5023 #ifdef FEAT_VIRTUALEDIT
5024 			/* In virtualedit, visual selections may extend
5025 			 * beyond end of line. */
5026 			if (area_highlighting && virtual_active()
5027 				&& tocol != MAXCOL && vcol < tocol)
5028 			    n_extra = 0;
5029 			else
5030 #endif
5031 			{
5032 			    p_extra = at_end_str;
5033 			    n_extra = 1;
5034 			    c_extra = NUL;
5035 			}
5036 		    }
5037 		    if (wp->w_p_list && lcs_eol > 0)
5038 			c = lcs_eol;
5039 		    else
5040 			c = ' ';
5041 		    lcs_eol_one = -1;
5042 		    --ptr;	    /* put it back at the NUL */
5043 		    if (!attr_pri)
5044 		    {
5045 			extra_attr = HL_ATTR(HLF_AT);
5046 			n_attr = 1;
5047 		    }
5048 #ifdef FEAT_MBYTE
5049 		    mb_c = c;
5050 		    if (enc_utf8 && utf_char2len(c) > 1)
5051 		    {
5052 			mb_utf8 = TRUE;
5053 			u8cc[0] = 0;
5054 			c = 0xc0;
5055 		    }
5056 		    else
5057 			mb_utf8 = FALSE;	/* don't draw as UTF-8 */
5058 #endif
5059 		}
5060 		else if (c != NUL)
5061 		{
5062 		    p_extra = transchar(c);
5063 		    if (n_extra == 0)
5064 			n_extra = byte2cells(c) - 1;
5065 #ifdef FEAT_RIGHTLEFT
5066 		    if ((dy_flags & DY_UHEX) && wp->w_p_rl)
5067 			rl_mirror(p_extra);	/* reverse "<12>" */
5068 #endif
5069 		    c_extra = NUL;
5070 #ifdef FEAT_LINEBREAK
5071 		    if (wp->w_p_lbr)
5072 		    {
5073 			char_u *p;
5074 
5075 			c = *p_extra;
5076 			p = alloc((unsigned)n_extra + 1);
5077 			vim_memset(p, ' ', n_extra);
5078 			STRNCPY(p, p_extra + 1, STRLEN(p_extra) - 1);
5079 			p[n_extra] = NUL;
5080 			vim_free(p_extra_free);
5081 			p_extra_free = p_extra = p;
5082 		    }
5083 		    else
5084 #endif
5085 		    {
5086 			n_extra = byte2cells(c) - 1;
5087 			c = *p_extra++;
5088 		    }
5089 		    if (!attr_pri)
5090 		    {
5091 			n_attr = n_extra + 1;
5092 			extra_attr = HL_ATTR(HLF_8);
5093 			saved_attr2 = char_attr; /* save current attr */
5094 		    }
5095 #ifdef FEAT_MBYTE
5096 		    mb_utf8 = FALSE;	/* don't draw as UTF-8 */
5097 #endif
5098 		}
5099 #ifdef FEAT_VIRTUALEDIT
5100 		else if (VIsual_active
5101 			 && (VIsual_mode == Ctrl_V
5102 			     || VIsual_mode == 'v')
5103 			 && virtual_active()
5104 			 && tocol != MAXCOL
5105 			 && vcol < tocol
5106 			 && (
5107 # ifdef FEAT_RIGHTLEFT
5108 			    wp->w_p_rl ? (col >= 0) :
5109 # endif
5110 			    (col < wp->w_width)))
5111 		{
5112 		    c = ' ';
5113 		    --ptr;	    /* put it back at the NUL */
5114 		}
5115 #endif
5116 #if defined(LINE_ATTR)
5117 		else if ((
5118 # ifdef FEAT_DIFF
5119 			    diff_hlf != (hlf_T)0 ||
5120 # endif
5121 # ifdef FEAT_TERMINAL
5122 			    term_attr != 0 ||
5123 # endif
5124 			    line_attr != 0
5125 			) && (
5126 # ifdef FEAT_RIGHTLEFT
5127 			    wp->w_p_rl ? (col >= 0) :
5128 # endif
5129 			    (col
5130 # ifdef FEAT_CONCEAL
5131 				- boguscols
5132 # endif
5133 					    < wp->w_width)))
5134 		{
5135 		    /* Highlight until the right side of the window */
5136 		    c = ' ';
5137 		    --ptr;	    /* put it back at the NUL */
5138 
5139 		    /* Remember we do the char for line highlighting. */
5140 		    ++did_line_attr;
5141 
5142 		    /* don't do search HL for the rest of the line */
5143 		    if (line_attr != 0 && char_attr == search_attr
5144 					&& (did_line_attr > 1
5145 					    || (wp->w_p_list && lcs_eol > 0)))
5146 			char_attr = line_attr;
5147 # ifdef FEAT_DIFF
5148 		    if (diff_hlf == HLF_TXD)
5149 		    {
5150 			diff_hlf = HLF_CHD;
5151 			if (attr == 0 || char_attr != attr)
5152 			{
5153 			    char_attr = HL_ATTR(diff_hlf);
5154 			    if (wp->w_p_cul && lnum == wp->w_cursor.lnum)
5155 				char_attr = hl_combine_attr(char_attr,
5156 							    HL_ATTR(HLF_CUL));
5157 			}
5158 		    }
5159 # endif
5160 # ifdef FEAT_TERMINAL
5161 		    if (term_attr != 0)
5162 		    {
5163 			char_attr = term_attr;
5164 			if (wp->w_p_cul && lnum == wp->w_cursor.lnum)
5165 			    char_attr = hl_combine_attr(char_attr,
5166 							    HL_ATTR(HLF_CUL));
5167 		    }
5168 # endif
5169 		}
5170 #endif
5171 	    }
5172 
5173 #ifdef FEAT_CONCEAL
5174 	    if (   wp->w_p_cole > 0
5175 		&& (wp != curwin || lnum != wp->w_cursor.lnum ||
5176 							conceal_cursor_line(wp) )
5177 		&& ( (syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0)
5178 		&& !(lnum_in_visual_area
5179 				    && vim_strchr(wp->w_p_cocu, 'v') == NULL))
5180 	    {
5181 		char_attr = conceal_attr;
5182 		if ((prev_syntax_id != syntax_seqnr || has_match_conc > 1)
5183 			&& (syn_get_sub_char() != NUL || match_conc
5184 							 || wp->w_p_cole == 1)
5185 			&& wp->w_p_cole != 3)
5186 		{
5187 		    /* First time at this concealed item: display one
5188 		     * character. */
5189 		    if (match_conc)
5190 			c = match_conc;
5191 		    else if (syn_get_sub_char() != NUL)
5192 			c = syn_get_sub_char();
5193 		    else if (lcs_conceal != NUL)
5194 			c = lcs_conceal;
5195 		    else
5196 			c = ' ';
5197 
5198 		    prev_syntax_id = syntax_seqnr;
5199 
5200 		    if (n_extra > 0)
5201 			vcol_off += n_extra;
5202 		    vcol += n_extra;
5203 		    if (wp->w_p_wrap && n_extra > 0)
5204 		    {
5205 # ifdef FEAT_RIGHTLEFT
5206 			if (wp->w_p_rl)
5207 			{
5208 			    col -= n_extra;
5209 			    boguscols -= n_extra;
5210 			}
5211 			else
5212 # endif
5213 			{
5214 			    boguscols += n_extra;
5215 			    col += n_extra;
5216 			}
5217 		    }
5218 		    n_extra = 0;
5219 		    n_attr = 0;
5220 		}
5221 		else if (n_skip == 0)
5222 		{
5223 		    is_concealing = TRUE;
5224 		    n_skip = 1;
5225 		}
5226 # ifdef FEAT_MBYTE
5227 		mb_c = c;
5228 		if (enc_utf8 && utf_char2len(c) > 1)
5229 		{
5230 		    mb_utf8 = TRUE;
5231 		    u8cc[0] = 0;
5232 		    c = 0xc0;
5233 		}
5234 		else
5235 		    mb_utf8 = FALSE;	/* don't draw as UTF-8 */
5236 # endif
5237 	    }
5238 	    else
5239 	    {
5240 		prev_syntax_id = 0;
5241 		is_concealing = FALSE;
5242 	    }
5243 #endif /* FEAT_CONCEAL */
5244 	}
5245 
5246 #ifdef FEAT_CONCEAL
5247 	/* In the cursor line and we may be concealing characters: correct
5248 	 * the cursor column when we reach its position. */
5249 	if (!did_wcol && draw_state == WL_LINE
5250 		&& wp == curwin && lnum == wp->w_cursor.lnum
5251 		&& conceal_cursor_line(wp)
5252 		&& (int)wp->w_virtcol <= vcol + n_skip)
5253 	{
5254 #  ifdef FEAT_RIGHTLEFT
5255 	    if (wp->w_p_rl)
5256 		wp->w_wcol = wp->w_width - col + boguscols - 1;
5257 	    else
5258 #  endif
5259 		wp->w_wcol = col - boguscols;
5260 	    wp->w_wrow = row;
5261 	    did_wcol = TRUE;
5262 	}
5263 #endif
5264 
5265 	/* Don't override visual selection highlighting. */
5266 	if (n_attr > 0
5267 		&& draw_state == WL_LINE
5268 		&& !attr_pri)
5269 	    char_attr = extra_attr;
5270 
5271 #if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
5272 	/* XIM don't send preedit_start and preedit_end, but they send
5273 	 * preedit_changed and commit.  Thus Vim can't set "im_is_active", use
5274 	 * im_is_preediting() here. */
5275 	if (p_imst == IM_ON_THE_SPOT
5276 		&& xic != NULL
5277 		&& lnum == wp->w_cursor.lnum
5278 		&& (State & INSERT)
5279 		&& !p_imdisable
5280 		&& im_is_preediting()
5281 		&& draw_state == WL_LINE)
5282 	{
5283 	    colnr_T tcol;
5284 
5285 	    if (preedit_end_col == MAXCOL)
5286 		getvcol(curwin, &(wp->w_cursor), &tcol, NULL, NULL);
5287 	    else
5288 		tcol = preedit_end_col;
5289 	    if ((long)preedit_start_col <= vcol && vcol < (long)tcol)
5290 	    {
5291 		if (feedback_old_attr < 0)
5292 		{
5293 		    feedback_col = 0;
5294 		    feedback_old_attr = char_attr;
5295 		}
5296 		char_attr = im_get_feedback_attr(feedback_col);
5297 		if (char_attr < 0)
5298 		    char_attr = feedback_old_attr;
5299 		feedback_col++;
5300 	    }
5301 	    else if (feedback_old_attr >= 0)
5302 	    {
5303 		char_attr = feedback_old_attr;
5304 		feedback_old_attr = -1;
5305 		feedback_col = 0;
5306 	    }
5307 	}
5308 #endif
5309 	/*
5310 	 * Handle the case where we are in column 0 but not on the first
5311 	 * character of the line and the user wants us to show us a
5312 	 * special character (via 'listchars' option "precedes:<char>".
5313 	 */
5314 	if (lcs_prec_todo != NUL
5315 		&& wp->w_p_list
5316 		&& (wp->w_p_wrap ? wp->w_skipcol > 0 : wp->w_leftcol > 0)
5317 #ifdef FEAT_DIFF
5318 		&& filler_todo <= 0
5319 #endif
5320 		&& draw_state > WL_NR
5321 		&& c != NUL)
5322 	{
5323 	    c = lcs_prec;
5324 	    lcs_prec_todo = NUL;
5325 #ifdef FEAT_MBYTE
5326 	    if (has_mbyte && (*mb_char2cells)(mb_c) > 1)
5327 	    {
5328 		/* Double-width character being overwritten by the "precedes"
5329 		 * character, need to fill up half the character. */
5330 		c_extra = MB_FILLER_CHAR;
5331 		n_extra = 1;
5332 		n_attr = 2;
5333 		extra_attr = HL_ATTR(HLF_AT);
5334 	    }
5335 	    mb_c = c;
5336 	    if (enc_utf8 && utf_char2len(c) > 1)
5337 	    {
5338 		mb_utf8 = TRUE;
5339 		u8cc[0] = 0;
5340 		c = 0xc0;
5341 	    }
5342 	    else
5343 		mb_utf8 = FALSE;	/* don't draw as UTF-8 */
5344 #endif
5345 	    if (!attr_pri)
5346 	    {
5347 		saved_attr3 = char_attr; /* save current attr */
5348 		char_attr = HL_ATTR(HLF_AT); /* later copied to char_attr */
5349 		n_attr3 = 1;
5350 	    }
5351 	}
5352 
5353 	/*
5354 	 * At end of the text line or just after the last character.
5355 	 */
5356 	if (c == NUL
5357 #if defined(LINE_ATTR)
5358 		|| did_line_attr == 1
5359 #endif
5360 		)
5361 	{
5362 #ifdef FEAT_SEARCH_EXTRA
5363 	    long prevcol = (long)(ptr - line) - (c == NUL);
5364 
5365 	    /* we're not really at that column when skipping some text */
5366 	    if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
5367 		++prevcol;
5368 #endif
5369 
5370 	    /* Invert at least one char, used for Visual and empty line or
5371 	     * highlight match at end of line. If it's beyond the last
5372 	     * char on the screen, just overwrite that one (tricky!)  Not
5373 	     * needed when a '$' was displayed for 'list'. */
5374 #ifdef FEAT_SEARCH_EXTRA
5375 	    prevcol_hl_flag = FALSE;
5376 	    if (!search_hl.is_addpos && prevcol == (long)search_hl.startcol)
5377 		prevcol_hl_flag = TRUE;
5378 	    else
5379 	    {
5380 		cur = wp->w_match_head;
5381 		while (cur != NULL)
5382 		{
5383 		    if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol)
5384 		    {
5385 			prevcol_hl_flag = TRUE;
5386 			break;
5387 		    }
5388 		    cur = cur->next;
5389 		}
5390 	    }
5391 #endif
5392 	    if (lcs_eol == lcs_eol_one
5393 		    && ((area_attr != 0 && vcol == fromcol
5394 			    && (VIsual_mode != Ctrl_V
5395 				|| lnum == VIsual.lnum
5396 				|| lnum == curwin->w_cursor.lnum)
5397 			    && c == NUL)
5398 #ifdef FEAT_SEARCH_EXTRA
5399 			/* highlight 'hlsearch' match at end of line */
5400 			|| (prevcol_hl_flag == TRUE
5401 # ifdef FEAT_SYN_HL
5402 			    && !(wp->w_p_cul && lnum == wp->w_cursor.lnum
5403 				    && !(wp == curwin && VIsual_active))
5404 # endif
5405 # ifdef FEAT_DIFF
5406 			    && diff_hlf == (hlf_T)0
5407 # endif
5408 # if defined(LINE_ATTR)
5409 			    && did_line_attr <= 1
5410 # endif
5411 			   )
5412 #endif
5413 		       ))
5414 	    {
5415 		int n = 0;
5416 
5417 #ifdef FEAT_RIGHTLEFT
5418 		if (wp->w_p_rl)
5419 		{
5420 		    if (col < 0)
5421 			n = 1;
5422 		}
5423 		else
5424 #endif
5425 		{
5426 		    if (col >= wp->w_width)
5427 			n = -1;
5428 		}
5429 		if (n != 0)
5430 		{
5431 		    /* At the window boundary, highlight the last character
5432 		     * instead (better than nothing). */
5433 		    off += n;
5434 		    col += n;
5435 		}
5436 		else
5437 		{
5438 		    /* Add a blank character to highlight. */
5439 		    ScreenLines[off] = ' ';
5440 #ifdef FEAT_MBYTE
5441 		    if (enc_utf8)
5442 			ScreenLinesUC[off] = 0;
5443 #endif
5444 		}
5445 #ifdef FEAT_SEARCH_EXTRA
5446 		if (area_attr == 0)
5447 		{
5448 		    /* Use attributes from match with highest priority among
5449 		     * 'search_hl' and the match list. */
5450 		    char_attr = search_hl.attr;
5451 		    cur = wp->w_match_head;
5452 		    shl_flag = FALSE;
5453 		    while (cur != NULL || shl_flag == FALSE)
5454 		    {
5455 			if (shl_flag == FALSE
5456 				&& ((cur != NULL
5457 					&& cur->priority > SEARCH_HL_PRIORITY)
5458 				    || cur == NULL))
5459 			{
5460 			    shl = &search_hl;
5461 			    shl_flag = TRUE;
5462 			}
5463 			else
5464 			    shl = &cur->hl;
5465 			if ((ptr - line) - 1 == (long)shl->startcol
5466 				&& (shl == &search_hl || !shl->is_addpos))
5467 			    char_attr = shl->attr;
5468 			if (shl != &search_hl && cur != NULL)
5469 			    cur = cur->next;
5470 		    }
5471 		}
5472 #endif
5473 		ScreenAttrs[off] = char_attr;
5474 #ifdef FEAT_RIGHTLEFT
5475 		if (wp->w_p_rl)
5476 		{
5477 		    --col;
5478 		    --off;
5479 		}
5480 		else
5481 #endif
5482 		{
5483 		    ++col;
5484 		    ++off;
5485 		}
5486 		++vcol;
5487 #ifdef FEAT_SYN_HL
5488 		eol_hl_off = 1;
5489 #endif
5490 	    }
5491 	}
5492 
5493 	/*
5494 	 * At end of the text line.
5495 	 */
5496 	if (c == NUL)
5497 	{
5498 #ifdef FEAT_SYN_HL
5499 	    if (eol_hl_off > 0 && vcol - eol_hl_off == (long)wp->w_virtcol
5500 		    && lnum == wp->w_cursor.lnum)
5501 	    {
5502 		/* highlight last char after line */
5503 		--col;
5504 		--off;
5505 		--vcol;
5506 	    }
5507 
5508 	    /* Highlight 'cursorcolumn' & 'colorcolumn' past end of the line. */
5509 	    if (wp->w_p_wrap)
5510 		v = wp->w_skipcol;
5511 	    else
5512 		v = wp->w_leftcol;
5513 
5514 	    /* check if line ends before left margin */
5515 	    if (vcol < v + col - win_col_off(wp))
5516 		vcol = v + col - win_col_off(wp);
5517 #ifdef FEAT_CONCEAL
5518 	    /* Get rid of the boguscols now, we want to draw until the right
5519 	     * edge for 'cursorcolumn'. */
5520 	    col -= boguscols;
5521 	    boguscols = 0;
5522 #endif
5523 
5524 	    if (draw_color_col)
5525 		draw_color_col = advance_color_col(VCOL_HLC, &color_cols);
5526 
5527 	    if (((wp->w_p_cuc
5528 		      && (int)wp->w_virtcol >= VCOL_HLC - eol_hl_off
5529 		      && (int)wp->w_virtcol <
5530 					wp->w_width * (row - startrow + 1) + v
5531 		      && lnum != wp->w_cursor.lnum)
5532 		    || draw_color_col)
5533 # ifdef FEAT_RIGHTLEFT
5534 		    && !wp->w_p_rl
5535 # endif
5536 		    )
5537 	    {
5538 		int	rightmost_vcol = 0;
5539 		int	i;
5540 
5541 		if (wp->w_p_cuc)
5542 		    rightmost_vcol = wp->w_virtcol;
5543 		if (draw_color_col)
5544 		    /* determine rightmost colorcolumn to possibly draw */
5545 		    for (i = 0; color_cols[i] >= 0; ++i)
5546 			if (rightmost_vcol < color_cols[i])
5547 			    rightmost_vcol = color_cols[i];
5548 
5549 		while (col < wp->w_width)
5550 		{
5551 		    ScreenLines[off] = ' ';
5552 #ifdef FEAT_MBYTE
5553 		    if (enc_utf8)
5554 			ScreenLinesUC[off] = 0;
5555 #endif
5556 		    ++col;
5557 		    if (draw_color_col)
5558 			draw_color_col = advance_color_col(VCOL_HLC,
5559 								 &color_cols);
5560 
5561 		    if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol)
5562 			ScreenAttrs[off++] = HL_ATTR(HLF_CUC);
5563 		    else if (draw_color_col && VCOL_HLC == *color_cols)
5564 			ScreenAttrs[off++] = HL_ATTR(HLF_MC);
5565 		    else
5566 			ScreenAttrs[off++] = 0;
5567 
5568 		    if (VCOL_HLC >= rightmost_vcol)
5569 			break;
5570 
5571 		    ++vcol;
5572 		}
5573 	    }
5574 #endif
5575 
5576 	    screen_line(screen_row, wp->w_wincol, col,
5577 				  (int)wp->w_width, HAS_RIGHTLEFT(wp->w_p_rl));
5578 	    row++;
5579 
5580 	    /*
5581 	     * Update w_cline_height and w_cline_folded if the cursor line was
5582 	     * updated (saves a call to plines() later).
5583 	     */
5584 	    if (wp == curwin && lnum == curwin->w_cursor.lnum)
5585 	    {
5586 		curwin->w_cline_row = startrow;
5587 		curwin->w_cline_height = row - startrow;
5588 #ifdef FEAT_FOLDING
5589 		curwin->w_cline_folded = FALSE;
5590 #endif
5591 		curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
5592 	    }
5593 
5594 	    break;
5595 	}
5596 
5597 	/* line continues beyond line end */
5598 	if (lcs_ext
5599 		&& !wp->w_p_wrap
5600 #ifdef FEAT_DIFF
5601 		&& filler_todo <= 0
5602 #endif
5603 		&& (
5604 #ifdef FEAT_RIGHTLEFT
5605 		    wp->w_p_rl ? col == 0 :
5606 #endif
5607 		    col == wp->w_width - 1)
5608 		&& (*ptr != NUL
5609 		    || (wp->w_p_list && lcs_eol_one > 0)
5610 		    || (n_extra && (c_extra != NUL || *p_extra != NUL))))
5611 	{
5612 	    c = lcs_ext;
5613 	    char_attr = HL_ATTR(HLF_AT);
5614 #ifdef FEAT_MBYTE
5615 	    mb_c = c;
5616 	    if (enc_utf8 && utf_char2len(c) > 1)
5617 	    {
5618 		mb_utf8 = TRUE;
5619 		u8cc[0] = 0;
5620 		c = 0xc0;
5621 	    }
5622 	    else
5623 		mb_utf8 = FALSE;
5624 #endif
5625 	}
5626 
5627 #ifdef FEAT_SYN_HL
5628 	/* advance to the next 'colorcolumn' */
5629 	if (draw_color_col)
5630 	    draw_color_col = advance_color_col(VCOL_HLC, &color_cols);
5631 
5632 	/* Highlight the cursor column if 'cursorcolumn' is set.  But don't
5633 	 * highlight the cursor position itself.
5634 	 * Also highlight the 'colorcolumn' if it is different than
5635 	 * 'cursorcolumn' */
5636 	vcol_save_attr = -1;
5637 	if (draw_state == WL_LINE && !lnum_in_visual_area
5638 		&& search_attr == 0 && area_attr == 0)
5639 	{
5640 	    if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol
5641 						 && lnum != wp->w_cursor.lnum)
5642 	    {
5643 		vcol_save_attr = char_attr;
5644 		char_attr = hl_combine_attr(char_attr, HL_ATTR(HLF_CUC));
5645 	    }
5646 	    else if (draw_color_col && VCOL_HLC == *color_cols)
5647 	    {
5648 		vcol_save_attr = char_attr;
5649 		char_attr = hl_combine_attr(char_attr, HL_ATTR(HLF_MC));
5650 	    }
5651 	}
5652 #endif
5653 
5654 	/*
5655 	 * Store character to be displayed.
5656 	 * Skip characters that are left of the screen for 'nowrap'.
5657 	 */
5658 	vcol_prev = vcol;
5659 	if (draw_state < WL_LINE || n_skip <= 0)
5660 	{
5661 	    /*
5662 	     * Store the character.
5663 	     */
5664 #if defined(FEAT_RIGHTLEFT) && defined(FEAT_MBYTE)
5665 	    if (has_mbyte && wp->w_p_rl && (*mb_char2cells)(mb_c) > 1)
5666 	    {
5667 		/* A double-wide character is: put first halve in left cell. */
5668 		--off;
5669 		--col;
5670 	    }
5671 #endif
5672 	    ScreenLines[off] = c;
5673 #ifdef FEAT_MBYTE
5674 	    if (enc_dbcs == DBCS_JPNU)
5675 	    {
5676 		if ((mb_c & 0xff00) == 0x8e00)
5677 		    ScreenLines[off] = 0x8e;
5678 		ScreenLines2[off] = mb_c & 0xff;
5679 	    }
5680 	    else if (enc_utf8)
5681 	    {
5682 		if (mb_utf8)
5683 		{
5684 		    int i;
5685 
5686 		    ScreenLinesUC[off] = mb_c;
5687 		    if ((c & 0xff) == 0)
5688 			ScreenLines[off] = 0x80;   /* avoid storing zero */
5689 		    for (i = 0; i < Screen_mco; ++i)
5690 		    {
5691 			ScreenLinesC[i][off] = u8cc[i];
5692 			if (u8cc[i] == 0)
5693 			    break;
5694 		    }
5695 		}
5696 		else
5697 		    ScreenLinesUC[off] = 0;
5698 	    }
5699 	    if (multi_attr)
5700 	    {
5701 		ScreenAttrs[off] = multi_attr;
5702 		multi_attr = 0;
5703 	    }
5704 	    else
5705 #endif
5706 		ScreenAttrs[off] = char_attr;
5707 
5708 #ifdef FEAT_MBYTE
5709 	    if (has_mbyte && (*mb_char2cells)(mb_c) > 1)
5710 	    {
5711 		/* Need to fill two screen columns. */
5712 		++off;
5713 		++col;
5714 		if (enc_utf8)
5715 		    /* UTF-8: Put a 0 in the second screen char. */
5716 		    ScreenLines[off] = 0;
5717 		else
5718 		    /* DBCS: Put second byte in the second screen char. */
5719 		    ScreenLines[off] = mb_c & 0xff;
5720 		if (draw_state > WL_NR
5721 #ifdef FEAT_DIFF
5722 			&& filler_todo <= 0
5723 #endif
5724 			)
5725 		    ++vcol;
5726 		/* When "tocol" is halfway a character, set it to the end of
5727 		 * the character, otherwise highlighting won't stop. */
5728 		if (tocol == vcol)
5729 		    ++tocol;
5730 #ifdef FEAT_RIGHTLEFT
5731 		if (wp->w_p_rl)
5732 		{
5733 		    /* now it's time to backup one cell */
5734 		    --off;
5735 		    --col;
5736 		}
5737 #endif
5738 	    }
5739 #endif
5740 #ifdef FEAT_RIGHTLEFT
5741 	    if (wp->w_p_rl)
5742 	    {
5743 		--off;
5744 		--col;
5745 	    }
5746 	    else
5747 #endif
5748 	    {
5749 		++off;
5750 		++col;
5751 	    }
5752 	}
5753 #ifdef FEAT_CONCEAL
5754 	else if (wp->w_p_cole > 0 && is_concealing)
5755 	{
5756 	    --n_skip;
5757 	    ++vcol_off;
5758 	    if (n_extra > 0)
5759 		vcol_off += n_extra;
5760 	    if (wp->w_p_wrap)
5761 	    {
5762 		/*
5763 		 * Special voodoo required if 'wrap' is on.
5764 		 *
5765 		 * Advance the column indicator to force the line
5766 		 * drawing to wrap early. This will make the line
5767 		 * take up the same screen space when parts are concealed,
5768 		 * so that cursor line computations aren't messed up.
5769 		 *
5770 		 * To avoid the fictitious advance of 'col' causing
5771 		 * trailing junk to be written out of the screen line
5772 		 * we are building, 'boguscols' keeps track of the number
5773 		 * of bad columns we have advanced.
5774 		 */
5775 		if (n_extra > 0)
5776 		{
5777 		    vcol += n_extra;
5778 # ifdef FEAT_RIGHTLEFT
5779 		    if (wp->w_p_rl)
5780 		    {
5781 			col -= n_extra;
5782 			boguscols -= n_extra;
5783 		    }
5784 		    else
5785 # endif
5786 		    {
5787 			col += n_extra;
5788 			boguscols += n_extra;
5789 		    }
5790 		    n_extra = 0;
5791 		    n_attr = 0;
5792 		}
5793 
5794 
5795 # ifdef FEAT_MBYTE
5796 		if (has_mbyte && (*mb_char2cells)(mb_c) > 1)
5797 		{
5798 		    /* Need to fill two screen columns. */
5799 #  ifdef FEAT_RIGHTLEFT
5800 		    if (wp->w_p_rl)
5801 		    {
5802 			--boguscols;
5803 			--col;
5804 		    }
5805 		    else
5806 #  endif
5807 		    {
5808 			++boguscols;
5809 			++col;
5810 		    }
5811 		}
5812 # endif
5813 
5814 # ifdef FEAT_RIGHTLEFT
5815 		if (wp->w_p_rl)
5816 		{
5817 		    --boguscols;
5818 		    --col;
5819 		}
5820 		else
5821 # endif
5822 		{
5823 		    ++boguscols;
5824 		    ++col;
5825 		}
5826 	    }
5827 	    else
5828 	    {
5829 		if (n_extra > 0)
5830 		{
5831 		    vcol += n_extra;
5832 		    n_extra = 0;
5833 		    n_attr = 0;
5834 		}
5835 	    }
5836 
5837 	}
5838 #endif /* FEAT_CONCEAL */
5839 	else
5840 	    --n_skip;
5841 
5842 	/* Only advance the "vcol" when after the 'number' or 'relativenumber'
5843 	 * column. */
5844 	if (draw_state > WL_NR
5845 #ifdef FEAT_DIFF
5846 		&& filler_todo <= 0
5847 #endif
5848 		)
5849 	    ++vcol;
5850 
5851 #ifdef FEAT_SYN_HL
5852 	if (vcol_save_attr >= 0)
5853 	    char_attr = vcol_save_attr;
5854 #endif
5855 
5856 	/* restore attributes after "predeces" in 'listchars' */
5857 	if (draw_state > WL_NR && n_attr3 > 0 && --n_attr3 == 0)
5858 	    char_attr = saved_attr3;
5859 
5860 	/* restore attributes after last 'listchars' or 'number' char */
5861 	if (n_attr > 0 && draw_state == WL_LINE && --n_attr == 0)
5862 	    char_attr = saved_attr2;
5863 
5864 	/*
5865 	 * At end of screen line and there is more to come: Display the line
5866 	 * so far.  If there is no more to display it is caught above.
5867 	 */
5868 	if ((
5869 #ifdef FEAT_RIGHTLEFT
5870 	    wp->w_p_rl ? (col < 0) :
5871 #endif
5872 				    (col >= wp->w_width))
5873 		&& (*ptr != NUL
5874 #ifdef FEAT_DIFF
5875 		    || filler_todo > 0
5876 #endif
5877 		    || (wp->w_p_list && lcs_eol != NUL && p_extra != at_end_str)
5878 		    || (n_extra != 0 && (c_extra != NUL || *p_extra != NUL)))
5879 		)
5880 	{
5881 #ifdef FEAT_CONCEAL
5882 	    screen_line(screen_row, wp->w_wincol, col - boguscols,
5883 				  (int)wp->w_width, HAS_RIGHTLEFT(wp->w_p_rl));
5884 	    boguscols = 0;
5885 #else
5886 	    screen_line(screen_row, wp->w_wincol, col,
5887 				  (int)wp->w_width, HAS_RIGHTLEFT(wp->w_p_rl));
5888 #endif
5889 	    ++row;
5890 	    ++screen_row;
5891 
5892 	    /* When not wrapping and finished diff lines, or when displayed
5893 	     * '$' and highlighting until last column, break here. */
5894 	    if ((!wp->w_p_wrap
5895 #ifdef FEAT_DIFF
5896 		    && filler_todo <= 0
5897 #endif
5898 		    ) || lcs_eol_one == -1)
5899 		break;
5900 
5901 	    /* When the window is too narrow draw all "@" lines. */
5902 	    if (draw_state != WL_LINE
5903 #ifdef FEAT_DIFF
5904 		    && filler_todo <= 0
5905 #endif
5906 		    )
5907 	    {
5908 		win_draw_end(wp, '@', ' ', row, wp->w_height, HLF_AT);
5909 		draw_vsep_win(wp, row);
5910 		row = endrow;
5911 	    }
5912 
5913 	    /* When line got too long for screen break here. */
5914 	    if (row == endrow)
5915 	    {
5916 		++row;
5917 		break;
5918 	    }
5919 
5920 	    if (screen_cur_row == screen_row - 1
5921 #ifdef FEAT_DIFF
5922 		     && filler_todo <= 0
5923 #endif
5924 		     && wp->w_width == Columns)
5925 	    {
5926 		/* Remember that the line wraps, used for modeless copy. */
5927 		LineWraps[screen_row - 1] = TRUE;
5928 
5929 		/*
5930 		 * Special trick to make copy/paste of wrapped lines work with
5931 		 * xterm/screen: write an extra character beyond the end of
5932 		 * the line. This will work with all terminal types
5933 		 * (regardless of the xn,am settings).
5934 		 * Only do this on a fast tty.
5935 		 * Only do this if the cursor is on the current line
5936 		 * (something has been written in it).
5937 		 * Don't do this for the GUI.
5938 		 * Don't do this for double-width characters.
5939 		 * Don't do this for a window not at the right screen border.
5940 		 */
5941 		if (p_tf
5942 #ifdef FEAT_GUI
5943 			 && !gui.in_use
5944 #endif
5945 #ifdef FEAT_MBYTE
5946 			 && !(has_mbyte
5947 			     && ((*mb_off2cells)(LineOffset[screen_row],
5948 				     LineOffset[screen_row] + screen_Columns)
5949 									  == 2
5950 				 || (*mb_off2cells)(LineOffset[screen_row - 1]
5951 							+ (int)Columns - 2,
5952 				     LineOffset[screen_row] + screen_Columns)
5953 									== 2))
5954 #endif
5955 		   )
5956 		{
5957 		    /* First make sure we are at the end of the screen line,
5958 		     * then output the same character again to let the
5959 		     * terminal know about the wrap.  If the terminal doesn't
5960 		     * auto-wrap, we overwrite the character. */
5961 		    if (screen_cur_col != wp->w_width)
5962 			screen_char(LineOffset[screen_row - 1]
5963 						      + (unsigned)Columns - 1,
5964 					  screen_row - 1, (int)(Columns - 1));
5965 
5966 #ifdef FEAT_MBYTE
5967 		    /* When there is a multi-byte character, just output a
5968 		     * space to keep it simple. */
5969 		    if (has_mbyte && MB_BYTE2LEN(ScreenLines[LineOffset[
5970 					screen_row - 1] + (Columns - 1)]) > 1)
5971 			out_char(' ');
5972 		    else
5973 #endif
5974 			out_char(ScreenLines[LineOffset[screen_row - 1]
5975 							    + (Columns - 1)]);
5976 		    /* force a redraw of the first char on the next line */
5977 		    ScreenAttrs[LineOffset[screen_row]] = (sattr_T)-1;
5978 		    screen_start();	/* don't know where cursor is now */
5979 		}
5980 	    }
5981 
5982 	    col = 0;
5983 	    off = (unsigned)(current_ScreenLine - ScreenLines);
5984 #ifdef FEAT_RIGHTLEFT
5985 	    if (wp->w_p_rl)
5986 	    {
5987 		col = wp->w_width - 1;	/* col is not used if breaking! */
5988 		off += col;
5989 	    }
5990 #endif
5991 
5992 	    /* reset the drawing state for the start of a wrapped line */
5993 	    draw_state = WL_START;
5994 	    saved_n_extra = n_extra;
5995 	    saved_p_extra = p_extra;
5996 	    saved_c_extra = c_extra;
5997 	    saved_char_attr = char_attr;
5998 	    n_extra = 0;
5999 	    lcs_prec_todo = lcs_prec;
6000 #ifdef FEAT_LINEBREAK
6001 # ifdef FEAT_DIFF
6002 	    if (filler_todo <= 0)
6003 # endif
6004 		need_showbreak = TRUE;
6005 #endif
6006 #ifdef FEAT_DIFF
6007 	    --filler_todo;
6008 	    /* When the filler lines are actually below the last line of the
6009 	     * file, don't draw the line itself, break here. */
6010 	    if (filler_todo == 0 && wp->w_botfill)
6011 		break;
6012 #endif
6013 	}
6014 
6015     }	/* for every character in the line */
6016 
6017 #ifdef FEAT_SPELL
6018     /* After an empty line check first word for capital. */
6019     if (*skipwhite(line) == NUL)
6020     {
6021 	capcol_lnum = lnum + 1;
6022 	cap_col = 0;
6023     }
6024 #endif
6025 
6026     vim_free(p_extra_free);
6027     return row;
6028 }
6029 
6030 #ifdef FEAT_MBYTE
6031 static int comp_char_differs(int, int);
6032 
6033 /*
6034  * Return if the composing characters at "off_from" and "off_to" differ.
6035  * Only to be used when ScreenLinesUC[off_from] != 0.
6036  */
6037     static int
6038 comp_char_differs(int off_from, int off_to)
6039 {
6040     int	    i;
6041 
6042     for (i = 0; i < Screen_mco; ++i)
6043     {
6044 	if (ScreenLinesC[i][off_from] != ScreenLinesC[i][off_to])
6045 	    return TRUE;
6046 	if (ScreenLinesC[i][off_from] == 0)
6047 	    break;
6048     }
6049     return FALSE;
6050 }
6051 #endif
6052 
6053 /*
6054  * Check whether the given character needs redrawing:
6055  * - the (first byte of the) character is different
6056  * - the attributes are different
6057  * - the character is multi-byte and the next byte is different
6058  * - the character is two cells wide and the second cell differs.
6059  */
6060     static int
6061 char_needs_redraw(int off_from, int off_to, int cols)
6062 {
6063     if (cols > 0
6064 	    && ((ScreenLines[off_from] != ScreenLines[off_to]
6065 		    || ScreenAttrs[off_from] != ScreenAttrs[off_to])
6066 
6067 #ifdef FEAT_MBYTE
6068 		|| (enc_dbcs != 0
6069 		    && MB_BYTE2LEN(ScreenLines[off_from]) > 1
6070 		    && (enc_dbcs == DBCS_JPNU && ScreenLines[off_from] == 0x8e
6071 			? ScreenLines2[off_from] != ScreenLines2[off_to]
6072 			: (cols > 1 && ScreenLines[off_from + 1]
6073 						 != ScreenLines[off_to + 1])))
6074 		|| (enc_utf8
6075 		    && (ScreenLinesUC[off_from] != ScreenLinesUC[off_to]
6076 			|| (ScreenLinesUC[off_from] != 0
6077 			    && comp_char_differs(off_from, off_to))
6078 			|| ((*mb_off2cells)(off_from, off_from + cols) > 1
6079 			    && ScreenLines[off_from + 1]
6080 						  != ScreenLines[off_to + 1])))
6081 #endif
6082 	       ))
6083 	return TRUE;
6084     return FALSE;
6085 }
6086 
6087 #if defined(FEAT_TERMINAL) || defined(PROTO)
6088 /*
6089  * Return the index in ScreenLines[] for the current screen line.
6090  */
6091     int
6092 screen_get_current_line_off()
6093 {
6094     return (int)(current_ScreenLine - ScreenLines);
6095 }
6096 #endif
6097 
6098 /*
6099  * Move one "cooked" screen line to the screen, but only the characters that
6100  * have actually changed.  Handle insert/delete character.
6101  * "coloff" gives the first column on the screen for this line.
6102  * "endcol" gives the columns where valid characters are.
6103  * "clear_width" is the width of the window.  It's > 0 if the rest of the line
6104  * needs to be cleared, negative otherwise.
6105  * "rlflag" is TRUE in a rightleft window:
6106  *    When TRUE and "clear_width" > 0, clear columns 0 to "endcol"
6107  *    When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width"
6108  */
6109     void
6110 screen_line(
6111     int	    row,
6112     int	    coloff,
6113     int	    endcol,
6114     int	    clear_width,
6115     int	    rlflag UNUSED)
6116 {
6117     unsigned	    off_from;
6118     unsigned	    off_to;
6119 #ifdef FEAT_MBYTE
6120     unsigned	    max_off_from;
6121     unsigned	    max_off_to;
6122 #endif
6123     int		    col = 0;
6124     int		    hl;
6125     int		    force = FALSE;	/* force update rest of the line */
6126     int		    redraw_this		/* bool: does character need redraw? */
6127 #ifdef FEAT_GUI
6128 				= TRUE	/* For GUI when while-loop empty */
6129 #endif
6130 				;
6131     int		    redraw_next;	/* redraw_this for next character */
6132 #ifdef FEAT_MBYTE
6133     int		    clear_next = FALSE;
6134     int		    char_cells;		/* 1: normal char */
6135 					/* 2: occupies two display cells */
6136 # define CHAR_CELLS char_cells
6137 #else
6138 # define CHAR_CELLS 1
6139 #endif
6140 
6141     /* Check for illegal row and col, just in case. */
6142     if (row >= Rows)
6143 	row = Rows - 1;
6144     if (endcol > Columns)
6145 	endcol = Columns;
6146 
6147 # ifdef FEAT_CLIPBOARD
6148     clip_may_clear_selection(row, row);
6149 # endif
6150 
6151     off_from = (unsigned)(current_ScreenLine - ScreenLines);
6152     off_to = LineOffset[row] + coloff;
6153 #ifdef FEAT_MBYTE
6154     max_off_from = off_from + screen_Columns;
6155     max_off_to = LineOffset[row] + screen_Columns;
6156 #endif
6157 
6158 #ifdef FEAT_RIGHTLEFT
6159     if (rlflag)
6160     {
6161 	/* Clear rest first, because it's left of the text. */
6162 	if (clear_width > 0)
6163 	{
6164 	    while (col <= endcol && ScreenLines[off_to] == ' '
6165 		    && ScreenAttrs[off_to] == 0
6166 # ifdef FEAT_MBYTE
6167 				  && (!enc_utf8 || ScreenLinesUC[off_to] == 0)
6168 # endif
6169 						  )
6170 	    {
6171 		++off_to;
6172 		++col;
6173 	    }
6174 	    if (col <= endcol)
6175 		screen_fill(row, row + 1, col + coloff,
6176 					    endcol + coloff + 1, ' ', ' ', 0);
6177 	}
6178 	col = endcol + 1;
6179 	off_to = LineOffset[row] + col + coloff;
6180 	off_from += col;
6181 	endcol = (clear_width > 0 ? clear_width : -clear_width);
6182     }
6183 #endif /* FEAT_RIGHTLEFT */
6184 
6185     redraw_next = char_needs_redraw(off_from, off_to, endcol - col);
6186 
6187     while (col < endcol)
6188     {
6189 #ifdef FEAT_MBYTE
6190 	if (has_mbyte && (col + 1 < endcol))
6191 	    char_cells = (*mb_off2cells)(off_from, max_off_from);
6192 	else
6193 	    char_cells = 1;
6194 #endif
6195 
6196 	redraw_this = redraw_next;
6197 	redraw_next = force || char_needs_redraw(off_from + CHAR_CELLS,
6198 			      off_to + CHAR_CELLS, endcol - col - CHAR_CELLS);
6199 
6200 #ifdef FEAT_GUI
6201 	/* If the next character was bold, then redraw the current character to
6202 	 * remove any pixels that might have spilt over into us.  This only
6203 	 * happens in the GUI.
6204 	 */
6205 	if (redraw_next && gui.in_use)
6206 	{
6207 	    hl = ScreenAttrs[off_to + CHAR_CELLS];
6208 	    if (hl > HL_ALL)
6209 		hl = syn_attr2attr(hl);
6210 	    if (hl & HL_BOLD)
6211 		redraw_this = TRUE;
6212 	}
6213 #endif
6214 
6215 	if (redraw_this)
6216 	{
6217 	    /*
6218 	     * Special handling when 'xs' termcap flag set (hpterm):
6219 	     * Attributes for characters are stored at the position where the
6220 	     * cursor is when writing the highlighting code.  The
6221 	     * start-highlighting code must be written with the cursor on the
6222 	     * first highlighted character.  The stop-highlighting code must
6223 	     * be written with the cursor just after the last highlighted
6224 	     * character.
6225 	     * Overwriting a character doesn't remove it's highlighting.  Need
6226 	     * to clear the rest of the line, and force redrawing it
6227 	     * completely.
6228 	     */
6229 	    if (       p_wiv
6230 		    && !force
6231 #ifdef FEAT_GUI
6232 		    && !gui.in_use
6233 #endif
6234 		    && ScreenAttrs[off_to] != 0
6235 		    && ScreenAttrs[off_from] != ScreenAttrs[off_to])
6236 	    {
6237 		/*
6238 		 * Need to remove highlighting attributes here.
6239 		 */
6240 		windgoto(row, col + coloff);
6241 		out_str(T_CE);		/* clear rest of this screen line */
6242 		screen_start();		/* don't know where cursor is now */
6243 		force = TRUE;		/* force redraw of rest of the line */
6244 		redraw_next = TRUE;	/* or else next char would miss out */
6245 
6246 		/*
6247 		 * If the previous character was highlighted, need to stop
6248 		 * highlighting at this character.
6249 		 */
6250 		if (col + coloff > 0 && ScreenAttrs[off_to - 1] != 0)
6251 		{
6252 		    screen_attr = ScreenAttrs[off_to - 1];
6253 		    term_windgoto(row, col + coloff);
6254 		    screen_stop_highlight();
6255 		}
6256 		else
6257 		    screen_attr = 0;	    /* highlighting has stopped */
6258 	    }
6259 #ifdef FEAT_MBYTE
6260 	    if (enc_dbcs != 0)
6261 	    {
6262 		/* Check if overwriting a double-byte with a single-byte or
6263 		 * the other way around requires another character to be
6264 		 * redrawn.  For UTF-8 this isn't needed, because comparing
6265 		 * ScreenLinesUC[] is sufficient. */
6266 		if (char_cells == 1
6267 			&& col + 1 < endcol
6268 			&& (*mb_off2cells)(off_to, max_off_to) > 1)
6269 		{
6270 		    /* Writing a single-cell character over a double-cell
6271 		     * character: need to redraw the next cell. */
6272 		    ScreenLines[off_to + 1] = 0;
6273 		    redraw_next = TRUE;
6274 		}
6275 		else if (char_cells == 2
6276 			&& col + 2 < endcol
6277 			&& (*mb_off2cells)(off_to, max_off_to) == 1
6278 			&& (*mb_off2cells)(off_to + 1, max_off_to) > 1)
6279 		{
6280 		    /* Writing the second half of a double-cell character over
6281 		     * a double-cell character: need to redraw the second
6282 		     * cell. */
6283 		    ScreenLines[off_to + 2] = 0;
6284 		    redraw_next = TRUE;
6285 		}
6286 
6287 		if (enc_dbcs == DBCS_JPNU)
6288 		    ScreenLines2[off_to] = ScreenLines2[off_from];
6289 	    }
6290 	    /* When writing a single-width character over a double-width
6291 	     * character and at the end of the redrawn text, need to clear out
6292 	     * the right halve of the old character.
6293 	     * Also required when writing the right halve of a double-width
6294 	     * char over the left halve of an existing one. */
6295 	    if (has_mbyte && col + char_cells == endcol
6296 		    && ((char_cells == 1
6297 			    && (*mb_off2cells)(off_to, max_off_to) > 1)
6298 			|| (char_cells == 2
6299 			    && (*mb_off2cells)(off_to, max_off_to) == 1
6300 			    && (*mb_off2cells)(off_to + 1, max_off_to) > 1)))
6301 		clear_next = TRUE;
6302 #endif
6303 
6304 	    ScreenLines[off_to] = ScreenLines[off_from];
6305 #ifdef FEAT_MBYTE
6306 	    if (enc_utf8)
6307 	    {
6308 		ScreenLinesUC[off_to] = ScreenLinesUC[off_from];
6309 		if (ScreenLinesUC[off_from] != 0)
6310 		{
6311 		    int	    i;
6312 
6313 		    for (i = 0; i < Screen_mco; ++i)
6314 			ScreenLinesC[i][off_to] = ScreenLinesC[i][off_from];
6315 		}
6316 	    }
6317 	    if (char_cells == 2)
6318 		ScreenLines[off_to + 1] = ScreenLines[off_from + 1];
6319 #endif
6320 
6321 #if defined(FEAT_GUI) || defined(UNIX)
6322 	    /* The bold trick makes a single column of pixels appear in the
6323 	     * next character.  When a bold character is removed, the next
6324 	     * character should be redrawn too.  This happens for our own GUI
6325 	     * and for some xterms. */
6326 	    if (
6327 # ifdef FEAT_GUI
6328 		    gui.in_use
6329 # endif
6330 # if defined(FEAT_GUI) && defined(UNIX)
6331 		    ||
6332 # endif
6333 # ifdef UNIX
6334 		    term_is_xterm
6335 # endif
6336 		    )
6337 	    {
6338 		hl = ScreenAttrs[off_to];
6339 		if (hl > HL_ALL)
6340 		    hl = syn_attr2attr(hl);
6341 		if (hl & HL_BOLD)
6342 		    redraw_next = TRUE;
6343 	    }
6344 #endif
6345 	    ScreenAttrs[off_to] = ScreenAttrs[off_from];
6346 #ifdef FEAT_MBYTE
6347 	    /* For simplicity set the attributes of second half of a
6348 	     * double-wide character equal to the first half. */
6349 	    if (char_cells == 2)
6350 		ScreenAttrs[off_to + 1] = ScreenAttrs[off_from];
6351 
6352 	    if (enc_dbcs != 0 && char_cells == 2)
6353 		screen_char_2(off_to, row, col + coloff);
6354 	    else
6355 #endif
6356 		screen_char(off_to, row, col + coloff);
6357 	}
6358 	else if (  p_wiv
6359 #ifdef FEAT_GUI
6360 		&& !gui.in_use
6361 #endif
6362 		&& col + coloff > 0)
6363 	{
6364 	    if (ScreenAttrs[off_to] == ScreenAttrs[off_to - 1])
6365 	    {
6366 		/*
6367 		 * Don't output stop-highlight when moving the cursor, it will
6368 		 * stop the highlighting when it should continue.
6369 		 */
6370 		screen_attr = 0;
6371 	    }
6372 	    else if (screen_attr != 0)
6373 		screen_stop_highlight();
6374 	}
6375 
6376 	off_to += CHAR_CELLS;
6377 	off_from += CHAR_CELLS;
6378 	col += CHAR_CELLS;
6379     }
6380 
6381 #ifdef FEAT_MBYTE
6382     if (clear_next)
6383     {
6384 	/* Clear the second half of a double-wide character of which the left
6385 	 * half was overwritten with a single-wide character. */
6386 	ScreenLines[off_to] = ' ';
6387 	if (enc_utf8)
6388 	    ScreenLinesUC[off_to] = 0;
6389 	screen_char(off_to, row, col + coloff);
6390     }
6391 #endif
6392 
6393     if (clear_width > 0
6394 #ifdef FEAT_RIGHTLEFT
6395 		    && !rlflag
6396 #endif
6397 				   )
6398     {
6399 #ifdef FEAT_GUI
6400 	int startCol = col;
6401 #endif
6402 
6403 	/* blank out the rest of the line */
6404 	while (col < clear_width && ScreenLines[off_to] == ' '
6405 						  && ScreenAttrs[off_to] == 0
6406 #ifdef FEAT_MBYTE
6407 				  && (!enc_utf8 || ScreenLinesUC[off_to] == 0)
6408 #endif
6409 						  )
6410 	{
6411 	    ++off_to;
6412 	    ++col;
6413 	}
6414 	if (col < clear_width)
6415 	{
6416 #ifdef FEAT_GUI
6417 	    /*
6418 	     * In the GUI, clearing the rest of the line may leave pixels
6419 	     * behind if the first character cleared was bold.  Some bold
6420 	     * fonts spill over the left.  In this case we redraw the previous
6421 	     * character too.  If we didn't skip any blanks above, then we
6422 	     * only redraw if the character wasn't already redrawn anyway.
6423 	     */
6424 	    if (gui.in_use && (col > startCol || !redraw_this))
6425 	    {
6426 		hl = ScreenAttrs[off_to];
6427 		if (hl > HL_ALL || (hl & HL_BOLD))
6428 		{
6429 		    int prev_cells = 1;
6430 # ifdef FEAT_MBYTE
6431 		    if (enc_utf8)
6432 			/* for utf-8, ScreenLines[char_offset + 1] == 0 means
6433 			 * that its width is 2. */
6434 			prev_cells = ScreenLines[off_to - 1] == 0 ? 2 : 1;
6435 		    else if (enc_dbcs != 0)
6436 		    {
6437 			/* find previous character by counting from first
6438 			 * column and get its width. */
6439 			unsigned off = LineOffset[row];
6440 			unsigned max_off = LineOffset[row] + screen_Columns;
6441 
6442 			while (off < off_to)
6443 			{
6444 			    prev_cells = (*mb_off2cells)(off, max_off);
6445 			    off += prev_cells;
6446 			}
6447 		    }
6448 
6449 		    if (enc_dbcs != 0 && prev_cells > 1)
6450 			screen_char_2(off_to - prev_cells, row,
6451 						   col + coloff - prev_cells);
6452 		    else
6453 # endif
6454 			screen_char(off_to - prev_cells, row,
6455 						   col + coloff - prev_cells);
6456 		}
6457 	    }
6458 #endif
6459 	    screen_fill(row, row + 1, col + coloff, clear_width + coloff,
6460 								 ' ', ' ', 0);
6461 	    off_to += clear_width - col;
6462 	    col = clear_width;
6463 	}
6464     }
6465 
6466     if (clear_width > 0)
6467     {
6468 	/* For a window that's left of another, draw the separator char. */
6469 	if (col + coloff < Columns)
6470 	{
6471 	    int c;
6472 
6473 	    c = fillchar_vsep(&hl);
6474 	    if (ScreenLines[off_to] != (schar_T)c
6475 #ifdef FEAT_MBYTE
6476 		    || (enc_utf8 && (int)ScreenLinesUC[off_to]
6477 						       != (c >= 0x80 ? c : 0))
6478 #endif
6479 		    || ScreenAttrs[off_to] != hl)
6480 	    {
6481 		ScreenLines[off_to] = c;
6482 		ScreenAttrs[off_to] = hl;
6483 #ifdef FEAT_MBYTE
6484 		if (enc_utf8)
6485 		{
6486 		    if (c >= 0x80)
6487 		    {
6488 			ScreenLinesUC[off_to] = c;
6489 			ScreenLinesC[0][off_to] = 0;
6490 		    }
6491 		    else
6492 			ScreenLinesUC[off_to] = 0;
6493 		}
6494 #endif
6495 		screen_char(off_to, row, col + coloff);
6496 	    }
6497 	}
6498 	else
6499 	    LineWraps[row] = FALSE;
6500     }
6501 }
6502 
6503 #if defined(FEAT_RIGHTLEFT) || defined(PROTO)
6504 /*
6505  * Mirror text "str" for right-left displaying.
6506  * Only works for single-byte characters (e.g., numbers).
6507  */
6508     void
6509 rl_mirror(char_u *str)
6510 {
6511     char_u	*p1, *p2;
6512     int		t;
6513 
6514     for (p1 = str, p2 = str + STRLEN(str) - 1; p1 < p2; ++p1, --p2)
6515     {
6516 	t = *p1;
6517 	*p1 = *p2;
6518 	*p2 = t;
6519     }
6520 }
6521 #endif
6522 
6523 /*
6524  * mark all status lines for redraw; used after first :cd
6525  */
6526     void
6527 status_redraw_all(void)
6528 {
6529     win_T	*wp;
6530 
6531     FOR_ALL_WINDOWS(wp)
6532 	if (wp->w_status_height)
6533 	{
6534 	    wp->w_redr_status = TRUE;
6535 	    redraw_later(VALID);
6536 	}
6537 }
6538 
6539 /*
6540  * mark all status lines of the current buffer for redraw
6541  */
6542     void
6543 status_redraw_curbuf(void)
6544 {
6545     win_T	*wp;
6546 
6547     FOR_ALL_WINDOWS(wp)
6548 	if (wp->w_status_height != 0 && wp->w_buffer == curbuf)
6549 	{
6550 	    wp->w_redr_status = TRUE;
6551 	    redraw_later(VALID);
6552 	}
6553 }
6554 
6555 /*
6556  * Redraw all status lines that need to be redrawn.
6557  */
6558     void
6559 redraw_statuslines(void)
6560 {
6561     win_T	*wp;
6562 
6563     FOR_ALL_WINDOWS(wp)
6564 	if (wp->w_redr_status)
6565 	    win_redr_status(wp, FALSE);
6566     if (redraw_tabline)
6567 	draw_tabline();
6568 }
6569 
6570 #if defined(FEAT_WILDMENU) || defined(PROTO)
6571 /*
6572  * Redraw all status lines at the bottom of frame "frp".
6573  */
6574     void
6575 win_redraw_last_status(frame_T *frp)
6576 {
6577     if (frp->fr_layout == FR_LEAF)
6578 	frp->fr_win->w_redr_status = TRUE;
6579     else if (frp->fr_layout == FR_ROW)
6580     {
6581 	for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next)
6582 	    win_redraw_last_status(frp);
6583     }
6584     else /* frp->fr_layout == FR_COL */
6585     {
6586 	frp = frp->fr_child;
6587 	while (frp->fr_next != NULL)
6588 	    frp = frp->fr_next;
6589 	win_redraw_last_status(frp);
6590     }
6591 }
6592 #endif
6593 
6594 /*
6595  * Draw the verticap separator right of window "wp" starting with line "row".
6596  */
6597     static void
6598 draw_vsep_win(win_T *wp, int row)
6599 {
6600     int		hl;
6601     int		c;
6602 
6603     if (wp->w_vsep_width)
6604     {
6605 	/* draw the vertical separator right of this window */
6606 	c = fillchar_vsep(&hl);
6607 	screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + wp->w_height,
6608 		W_ENDCOL(wp), W_ENDCOL(wp) + 1,
6609 		c, ' ', hl);
6610     }
6611 }
6612 
6613 #ifdef FEAT_WILDMENU
6614 static int status_match_len(expand_T *xp, char_u *s);
6615 static int skip_status_match_char(expand_T *xp, char_u *s);
6616 
6617 /*
6618  * Get the length of an item as it will be shown in the status line.
6619  */
6620     static int
6621 status_match_len(expand_T *xp, char_u *s)
6622 {
6623     int	len = 0;
6624 
6625 #ifdef FEAT_MENU
6626     int emenu = (xp->xp_context == EXPAND_MENUS
6627 	    || xp->xp_context == EXPAND_MENUNAMES);
6628 
6629     /* Check for menu separators - replace with '|'. */
6630     if (emenu && menu_is_separator(s))
6631 	return 1;
6632 #endif
6633 
6634     while (*s != NUL)
6635     {
6636 	s += skip_status_match_char(xp, s);
6637 	len += ptr2cells(s);
6638 	MB_PTR_ADV(s);
6639     }
6640 
6641     return len;
6642 }
6643 
6644 /*
6645  * Return the number of characters that should be skipped in a status match.
6646  * These are backslashes used for escaping.  Do show backslashes in help tags.
6647  */
6648     static int
6649 skip_status_match_char(expand_T *xp, char_u *s)
6650 {
6651     if ((rem_backslash(s) && xp->xp_context != EXPAND_HELP)
6652 #ifdef FEAT_MENU
6653 	    || ((xp->xp_context == EXPAND_MENUS
6654 		    || xp->xp_context == EXPAND_MENUNAMES)
6655 			  && (s[0] == '\t' || (s[0] == '\\' && s[1] != NUL)))
6656 #endif
6657 	   )
6658     {
6659 #ifndef BACKSLASH_IN_FILENAME
6660 	if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!')
6661 	    return 2;
6662 #endif
6663 	return 1;
6664     }
6665     return 0;
6666 }
6667 
6668 /*
6669  * Show wildchar matches in the status line.
6670  * Show at least the "match" item.
6671  * We start at item 'first_match' in the list and show all matches that fit.
6672  *
6673  * If inversion is possible we use it. Else '=' characters are used.
6674  */
6675     void
6676 win_redr_status_matches(
6677     expand_T	*xp,
6678     int		num_matches,
6679     char_u	**matches,	/* list of matches */
6680     int		match,
6681     int		showtail)
6682 {
6683 #define L_MATCH(m) (showtail ? sm_gettail(matches[m]) : matches[m])
6684     int		row;
6685     char_u	*buf;
6686     int		len;
6687     int		clen;		/* length in screen cells */
6688     int		fillchar;
6689     int		attr;
6690     int		i;
6691     int		highlight = TRUE;
6692     char_u	*selstart = NULL;
6693     int		selstart_col = 0;
6694     char_u	*selend = NULL;
6695     static int	first_match = 0;
6696     int		add_left = FALSE;
6697     char_u	*s;
6698 #ifdef FEAT_MENU
6699     int		emenu;
6700 #endif
6701 #if defined(FEAT_MBYTE) || defined(FEAT_MENU)
6702     int		l;
6703 #endif
6704 
6705     if (matches == NULL)	/* interrupted completion? */
6706 	return;
6707 
6708 #ifdef FEAT_MBYTE
6709     if (has_mbyte)
6710 	buf = alloc((unsigned)Columns * MB_MAXBYTES + 1);
6711     else
6712 #endif
6713 	buf = alloc((unsigned)Columns + 1);
6714     if (buf == NULL)
6715 	return;
6716 
6717     if (match == -1)	/* don't show match but original text */
6718     {
6719 	match = 0;
6720 	highlight = FALSE;
6721     }
6722     /* count 1 for the ending ">" */
6723     clen = status_match_len(xp, L_MATCH(match)) + 3;
6724     if (match == 0)
6725 	first_match = 0;
6726     else if (match < first_match)
6727     {
6728 	/* jumping left, as far as we can go */
6729 	first_match = match;
6730 	add_left = TRUE;
6731     }
6732     else
6733     {
6734 	/* check if match fits on the screen */
6735 	for (i = first_match; i < match; ++i)
6736 	    clen += status_match_len(xp, L_MATCH(i)) + 2;
6737 	if (first_match > 0)
6738 	    clen += 2;
6739 	/* jumping right, put match at the left */
6740 	if ((long)clen > Columns)
6741 	{
6742 	    first_match = match;
6743 	    /* if showing the last match, we can add some on the left */
6744 	    clen = 2;
6745 	    for (i = match; i < num_matches; ++i)
6746 	    {
6747 		clen += status_match_len(xp, L_MATCH(i)) + 2;
6748 		if ((long)clen >= Columns)
6749 		    break;
6750 	    }
6751 	    if (i == num_matches)
6752 		add_left = TRUE;
6753 	}
6754     }
6755     if (add_left)
6756 	while (first_match > 0)
6757 	{
6758 	    clen += status_match_len(xp, L_MATCH(first_match - 1)) + 2;
6759 	    if ((long)clen >= Columns)
6760 		break;
6761 	    --first_match;
6762 	}
6763 
6764     fillchar = fillchar_status(&attr, curwin);
6765 
6766     if (first_match == 0)
6767     {
6768 	*buf = NUL;
6769 	len = 0;
6770     }
6771     else
6772     {
6773 	STRCPY(buf, "< ");
6774 	len = 2;
6775     }
6776     clen = len;
6777 
6778     i = first_match;
6779     while ((long)(clen + status_match_len(xp, L_MATCH(i)) + 2) < Columns)
6780     {
6781 	if (i == match)
6782 	{
6783 	    selstart = buf + len;
6784 	    selstart_col = clen;
6785 	}
6786 
6787 	s = L_MATCH(i);
6788 	/* Check for menu separators - replace with '|' */
6789 #ifdef FEAT_MENU
6790 	emenu = (xp->xp_context == EXPAND_MENUS
6791 		|| xp->xp_context == EXPAND_MENUNAMES);
6792 	if (emenu && menu_is_separator(s))
6793 	{
6794 	    STRCPY(buf + len, transchar('|'));
6795 	    l = (int)STRLEN(buf + len);
6796 	    len += l;
6797 	    clen += l;
6798 	}
6799 	else
6800 #endif
6801 	    for ( ; *s != NUL; ++s)
6802 	{
6803 	    s += skip_status_match_char(xp, s);
6804 	    clen += ptr2cells(s);
6805 #ifdef FEAT_MBYTE
6806 	    if (has_mbyte && (l = (*mb_ptr2len)(s)) > 1)
6807 	    {
6808 		STRNCPY(buf + len, s, l);
6809 		s += l - 1;
6810 		len += l;
6811 	    }
6812 	    else
6813 #endif
6814 	    {
6815 		STRCPY(buf + len, transchar_byte(*s));
6816 		len += (int)STRLEN(buf + len);
6817 	    }
6818 	}
6819 	if (i == match)
6820 	    selend = buf + len;
6821 
6822 	*(buf + len++) = ' ';
6823 	*(buf + len++) = ' ';
6824 	clen += 2;
6825 	if (++i == num_matches)
6826 		break;
6827     }
6828 
6829     if (i != num_matches)
6830     {
6831 	*(buf + len++) = '>';
6832 	++clen;
6833     }
6834 
6835     buf[len] = NUL;
6836 
6837     row = cmdline_row - 1;
6838     if (row >= 0)
6839     {
6840 	if (wild_menu_showing == 0)
6841 	{
6842 	    if (msg_scrolled > 0)
6843 	    {
6844 		/* Put the wildmenu just above the command line.  If there is
6845 		 * no room, scroll the screen one line up. */
6846 		if (cmdline_row == Rows - 1)
6847 		{
6848 		    screen_del_lines(0, 0, 1, (int)Rows, TRUE, 0, NULL);
6849 		    ++msg_scrolled;
6850 		}
6851 		else
6852 		{
6853 		    ++cmdline_row;
6854 		    ++row;
6855 		}
6856 		wild_menu_showing = WM_SCROLLED;
6857 	    }
6858 	    else
6859 	    {
6860 		/* Create status line if needed by setting 'laststatus' to 2.
6861 		 * Set 'winminheight' to zero to avoid that the window is
6862 		 * resized. */
6863 		if (lastwin->w_status_height == 0)
6864 		{
6865 		    save_p_ls = p_ls;
6866 		    save_p_wmh = p_wmh;
6867 		    p_ls = 2;
6868 		    p_wmh = 0;
6869 		    last_status(FALSE);
6870 		}
6871 		wild_menu_showing = WM_SHOWN;
6872 	    }
6873 	}
6874 
6875 	screen_puts(buf, row, 0, attr);
6876 	if (selstart != NULL && highlight)
6877 	{
6878 	    *selend = NUL;
6879 	    screen_puts(selstart, row, selstart_col, HL_ATTR(HLF_WM));
6880 	}
6881 
6882 	screen_fill(row, row + 1, clen, (int)Columns, fillchar, fillchar, attr);
6883     }
6884 
6885     win_redraw_last_status(topframe);
6886     vim_free(buf);
6887 }
6888 #endif
6889 
6890 /*
6891  * Redraw the status line of window wp.
6892  *
6893  * If inversion is possible we use it. Else '=' characters are used.
6894  * If "ignore_pum" is TRUE, also redraw statusline when the popup menu is
6895  * displayed.
6896  */
6897     static void
6898 win_redr_status(win_T *wp, int ignore_pum UNUSED)
6899 {
6900     int		row;
6901     char_u	*p;
6902     int		len;
6903     int		fillchar;
6904     int		attr;
6905     int		this_ru_col;
6906     static int  busy = FALSE;
6907 
6908     /* It's possible to get here recursively when 'statusline' (indirectly)
6909      * invokes ":redrawstatus".  Simply ignore the call then. */
6910     if (busy)
6911 	return;
6912     busy = TRUE;
6913 
6914     wp->w_redr_status = FALSE;
6915     if (wp->w_status_height == 0)
6916     {
6917 	/* no status line, can only be last window */
6918 	redraw_cmdline = TRUE;
6919     }
6920     else if (!redrawing()
6921 #ifdef FEAT_INS_EXPAND
6922 	    // don't update status line when popup menu is visible and may be
6923 	    // drawn over it, unless it will be redrawn later
6924 	    || (!ignore_pum && pum_visible())
6925 #endif
6926 	    )
6927     {
6928 	/* Don't redraw right now, do it later. */
6929 	wp->w_redr_status = TRUE;
6930     }
6931 #ifdef FEAT_STL_OPT
6932     else if (*p_stl != NUL || *wp->w_p_stl != NUL)
6933     {
6934 	/* redraw custom status line */
6935 	redraw_custom_statusline(wp);
6936     }
6937 #endif
6938     else
6939     {
6940 	fillchar = fillchar_status(&attr, wp);
6941 
6942 	get_trans_bufname(wp->w_buffer);
6943 	p = NameBuff;
6944 	len = (int)STRLEN(p);
6945 
6946 	if (bt_help(wp->w_buffer)
6947 #ifdef FEAT_QUICKFIX
6948 		|| wp->w_p_pvw
6949 #endif
6950 		|| bufIsChanged(wp->w_buffer)
6951 		|| wp->w_buffer->b_p_ro)
6952 	    *(p + len++) = ' ';
6953 	if (bt_help(wp->w_buffer))
6954 	{
6955 	    STRCPY(p + len, _("[Help]"));
6956 	    len += (int)STRLEN(p + len);
6957 	}
6958 #ifdef FEAT_QUICKFIX
6959 	if (wp->w_p_pvw)
6960 	{
6961 	    STRCPY(p + len, _("[Preview]"));
6962 	    len += (int)STRLEN(p + len);
6963 	}
6964 #endif
6965 	if (bufIsChanged(wp->w_buffer)
6966 #ifdef FEAT_TERMINAL
6967 		&& !bt_terminal(wp->w_buffer)
6968 #endif
6969 		)
6970 	{
6971 	    STRCPY(p + len, "[+]");
6972 	    len += 3;
6973 	}
6974 	if (wp->w_buffer->b_p_ro)
6975 	{
6976 	    STRCPY(p + len, _("[RO]"));
6977 	    len += (int)STRLEN(p + len);
6978 	}
6979 
6980 	this_ru_col = ru_col - (Columns - wp->w_width);
6981 	if (this_ru_col < (wp->w_width + 1) / 2)
6982 	    this_ru_col = (wp->w_width + 1) / 2;
6983 	if (this_ru_col <= 1)
6984 	{
6985 	    p = (char_u *)"<";		/* No room for file name! */
6986 	    len = 1;
6987 	}
6988 	else
6989 #ifdef FEAT_MBYTE
6990 	    if (has_mbyte)
6991 	    {
6992 		int	clen = 0, i;
6993 
6994 		/* Count total number of display cells. */
6995 		clen = mb_string2cells(p, -1);
6996 
6997 		/* Find first character that will fit.
6998 		 * Going from start to end is much faster for DBCS. */
6999 		for (i = 0; p[i] != NUL && clen >= this_ru_col - 1;
7000 					      i += (*mb_ptr2len)(p + i))
7001 		    clen -= (*mb_ptr2cells)(p + i);
7002 		len = clen;
7003 		if (i > 0)
7004 		{
7005 		    p = p + i - 1;
7006 		    *p = '<';
7007 		    ++len;
7008 		}
7009 
7010 	    }
7011 	    else
7012 #endif
7013 	    if (len > this_ru_col - 1)
7014 	    {
7015 		p += len - (this_ru_col - 1);
7016 		*p = '<';
7017 		len = this_ru_col - 1;
7018 	    }
7019 
7020 	row = W_WINROW(wp) + wp->w_height;
7021 	screen_puts(p, row, wp->w_wincol, attr);
7022 	screen_fill(row, row + 1, len + wp->w_wincol,
7023 			this_ru_col + wp->w_wincol, fillchar, fillchar, attr);
7024 
7025 	if (get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL)
7026 		&& (int)(this_ru_col - len) > (int)(STRLEN(NameBuff) + 1))
7027 	    screen_puts(NameBuff, row, (int)(this_ru_col - STRLEN(NameBuff)
7028 						   - 1 + wp->w_wincol), attr);
7029 
7030 #ifdef FEAT_CMDL_INFO
7031 	win_redr_ruler(wp, TRUE, ignore_pum);
7032 #endif
7033     }
7034 
7035     /*
7036      * May need to draw the character below the vertical separator.
7037      */
7038     if (wp->w_vsep_width != 0 && wp->w_status_height != 0 && redrawing())
7039     {
7040 	if (stl_connected(wp))
7041 	    fillchar = fillchar_status(&attr, wp);
7042 	else
7043 	    fillchar = fillchar_vsep(&attr);
7044 	screen_putchar(fillchar, W_WINROW(wp) + wp->w_height, W_ENDCOL(wp),
7045 									attr);
7046     }
7047     busy = FALSE;
7048 }
7049 
7050 #ifdef FEAT_STL_OPT
7051 /*
7052  * Redraw the status line according to 'statusline' and take care of any
7053  * errors encountered.
7054  */
7055     static void
7056 redraw_custom_statusline(win_T *wp)
7057 {
7058     static int	    entered = FALSE;
7059     int		    saved_did_emsg = did_emsg;
7060 
7061     /* When called recursively return.  This can happen when the statusline
7062      * contains an expression that triggers a redraw. */
7063     if (entered)
7064 	return;
7065     entered = TRUE;
7066 
7067     did_emsg = FALSE;
7068     win_redr_custom(wp, FALSE);
7069     if (did_emsg)
7070     {
7071 	/* When there is an error disable the statusline, otherwise the
7072 	 * display is messed up with errors and a redraw triggers the problem
7073 	 * again and again. */
7074 	set_string_option_direct((char_u *)"statusline", -1,
7075 		(char_u *)"", OPT_FREE | (*wp->w_p_stl != NUL
7076 					? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
7077     }
7078     did_emsg |= saved_did_emsg;
7079     entered = FALSE;
7080 }
7081 #endif
7082 
7083 /*
7084  * Return TRUE if the status line of window "wp" is connected to the status
7085  * line of the window right of it.  If not, then it's a vertical separator.
7086  * Only call if (wp->w_vsep_width != 0).
7087  */
7088     int
7089 stl_connected(win_T *wp)
7090 {
7091     frame_T	*fr;
7092 
7093     fr = wp->w_frame;
7094     while (fr->fr_parent != NULL)
7095     {
7096 	if (fr->fr_parent->fr_layout == FR_COL)
7097 	{
7098 	    if (fr->fr_next != NULL)
7099 		break;
7100 	}
7101 	else
7102 	{
7103 	    if (fr->fr_next != NULL)
7104 		return TRUE;
7105 	}
7106 	fr = fr->fr_parent;
7107     }
7108     return FALSE;
7109 }
7110 
7111 
7112 /*
7113  * Get the value to show for the language mappings, active 'keymap'.
7114  */
7115     int
7116 get_keymap_str(
7117     win_T	*wp,
7118     char_u	*fmt,	    /* format string containing one %s item */
7119     char_u	*buf,	    /* buffer for the result */
7120     int		len)	    /* length of buffer */
7121 {
7122     char_u	*p;
7123 
7124     if (wp->w_buffer->b_p_iminsert != B_IMODE_LMAP)
7125 	return FALSE;
7126 
7127     {
7128 #ifdef FEAT_EVAL
7129 	buf_T	*old_curbuf = curbuf;
7130 	win_T	*old_curwin = curwin;
7131 	char_u	*s;
7132 
7133 	curbuf = wp->w_buffer;
7134 	curwin = wp;
7135 	STRCPY(buf, "b:keymap_name");	/* must be writable */
7136 	++emsg_skip;
7137 	s = p = eval_to_string(buf, NULL, FALSE);
7138 	--emsg_skip;
7139 	curbuf = old_curbuf;
7140 	curwin = old_curwin;
7141 	if (p == NULL || *p == NUL)
7142 #endif
7143 	{
7144 #ifdef FEAT_KEYMAP
7145 	    if (wp->w_buffer->b_kmap_state & KEYMAP_LOADED)
7146 		p = wp->w_buffer->b_p_keymap;
7147 	    else
7148 #endif
7149 		p = (char_u *)"lang";
7150 	}
7151 	if (vim_snprintf((char *)buf, len, (char *)fmt, p) > len - 1)
7152 	    buf[0] = NUL;
7153 #ifdef FEAT_EVAL
7154 	vim_free(s);
7155 #endif
7156     }
7157     return buf[0] != NUL;
7158 }
7159 
7160 #if defined(FEAT_STL_OPT) || defined(PROTO)
7161 /*
7162  * Redraw the status line or ruler of window "wp".
7163  * When "wp" is NULL redraw the tab pages line from 'tabline'.
7164  */
7165     static void
7166 win_redr_custom(
7167     win_T	*wp,
7168     int		draw_ruler)	/* TRUE or FALSE */
7169 {
7170     static int	entered = FALSE;
7171     int		attr;
7172     int		curattr;
7173     int		row;
7174     int		col = 0;
7175     int		maxwidth;
7176     int		width;
7177     int		n;
7178     int		len;
7179     int		fillchar;
7180     char_u	buf[MAXPATHL];
7181     char_u	*stl;
7182     char_u	*p;
7183     struct	stl_hlrec hltab[STL_MAX_ITEM];
7184     struct	stl_hlrec tabtab[STL_MAX_ITEM];
7185     int		use_sandbox = FALSE;
7186     win_T	*ewp;
7187     int		p_crb_save;
7188 
7189     /* There is a tiny chance that this gets called recursively: When
7190      * redrawing a status line triggers redrawing the ruler or tabline.
7191      * Avoid trouble by not allowing recursion. */
7192     if (entered)
7193 	return;
7194     entered = TRUE;
7195 
7196     /* setup environment for the task at hand */
7197     if (wp == NULL)
7198     {
7199 	/* Use 'tabline'.  Always at the first line of the screen. */
7200 	stl = p_tal;
7201 	row = 0;
7202 	fillchar = ' ';
7203 	attr = HL_ATTR(HLF_TPF);
7204 	maxwidth = Columns;
7205 # ifdef FEAT_EVAL
7206 	use_sandbox = was_set_insecurely((char_u *)"tabline", 0);
7207 # endif
7208     }
7209     else
7210     {
7211 	row = W_WINROW(wp) + wp->w_height;
7212 	fillchar = fillchar_status(&attr, wp);
7213 	maxwidth = wp->w_width;
7214 
7215 	if (draw_ruler)
7216 	{
7217 	    stl = p_ruf;
7218 	    /* advance past any leading group spec - implicit in ru_col */
7219 	    if (*stl == '%')
7220 	    {
7221 		if (*++stl == '-')
7222 		    stl++;
7223 		if (atoi((char *)stl))
7224 		    while (VIM_ISDIGIT(*stl))
7225 			stl++;
7226 		if (*stl++ != '(')
7227 		    stl = p_ruf;
7228 	    }
7229 	    col = ru_col - (Columns - wp->w_width);
7230 	    if (col < (wp->w_width + 1) / 2)
7231 		col = (wp->w_width + 1) / 2;
7232 	    maxwidth = wp->w_width - col;
7233 	    if (!wp->w_status_height)
7234 	    {
7235 		row = Rows - 1;
7236 		--maxwidth;	/* writing in last column may cause scrolling */
7237 		fillchar = ' ';
7238 		attr = 0;
7239 	    }
7240 
7241 # ifdef FEAT_EVAL
7242 	    use_sandbox = was_set_insecurely((char_u *)"rulerformat", 0);
7243 # endif
7244 	}
7245 	else
7246 	{
7247 	    if (*wp->w_p_stl != NUL)
7248 		stl = wp->w_p_stl;
7249 	    else
7250 		stl = p_stl;
7251 # ifdef FEAT_EVAL
7252 	    use_sandbox = was_set_insecurely((char_u *)"statusline",
7253 					 *wp->w_p_stl == NUL ? 0 : OPT_LOCAL);
7254 # endif
7255 	}
7256 
7257 	col += wp->w_wincol;
7258     }
7259 
7260     if (maxwidth <= 0)
7261 	goto theend;
7262 
7263     /* Temporarily reset 'cursorbind', we don't want a side effect from moving
7264      * the cursor away and back. */
7265     ewp = wp == NULL ? curwin : wp;
7266     p_crb_save = ewp->w_p_crb;
7267     ewp->w_p_crb = FALSE;
7268 
7269     /* Make a copy, because the statusline may include a function call that
7270      * might change the option value and free the memory. */
7271     stl = vim_strsave(stl);
7272     width = build_stl_str_hl(ewp, buf, sizeof(buf),
7273 				stl, use_sandbox,
7274 				fillchar, maxwidth, hltab, tabtab);
7275     vim_free(stl);
7276     ewp->w_p_crb = p_crb_save;
7277 
7278     /* Make all characters printable. */
7279     p = transstr(buf);
7280     if (p != NULL)
7281     {
7282 	vim_strncpy(buf, p, sizeof(buf) - 1);
7283 	vim_free(p);
7284     }
7285 
7286     /* fill up with "fillchar" */
7287     len = (int)STRLEN(buf);
7288     while (width < maxwidth && len < (int)sizeof(buf) - 1)
7289     {
7290 #ifdef FEAT_MBYTE
7291 	len += (*mb_char2bytes)(fillchar, buf + len);
7292 #else
7293 	buf[len++] = fillchar;
7294 #endif
7295 	++width;
7296     }
7297     buf[len] = NUL;
7298 
7299     /*
7300      * Draw each snippet with the specified highlighting.
7301      */
7302     curattr = attr;
7303     p = buf;
7304     for (n = 0; hltab[n].start != NULL; n++)
7305     {
7306 	len = (int)(hltab[n].start - p);
7307 	screen_puts_len(p, len, row, col, curattr);
7308 	col += vim_strnsize(p, len);
7309 	p = hltab[n].start;
7310 
7311 	if (hltab[n].userhl == 0)
7312 	    curattr = attr;
7313 	else if (hltab[n].userhl < 0)
7314 	    curattr = syn_id2attr(-hltab[n].userhl);
7315 #ifdef FEAT_TERMINAL
7316 	else if (wp != NULL && wp != curwin && bt_terminal(wp->w_buffer)
7317 						   && wp->w_status_height != 0)
7318 	    curattr = highlight_stltermnc[hltab[n].userhl - 1];
7319 	else if (wp != NULL && bt_terminal(wp->w_buffer)
7320 						   && wp->w_status_height != 0)
7321 	    curattr = highlight_stlterm[hltab[n].userhl - 1];
7322 #endif
7323 	else if (wp != NULL && wp != curwin && wp->w_status_height != 0)
7324 	    curattr = highlight_stlnc[hltab[n].userhl - 1];
7325 	else
7326 	    curattr = highlight_user[hltab[n].userhl - 1];
7327     }
7328     screen_puts(p, row, col, curattr);
7329 
7330     if (wp == NULL)
7331     {
7332 	/* Fill the TabPageIdxs[] array for clicking in the tab pagesline. */
7333 	col = 0;
7334 	len = 0;
7335 	p = buf;
7336 	fillchar = 0;
7337 	for (n = 0; tabtab[n].start != NULL; n++)
7338 	{
7339 	    len += vim_strnsize(p, (int)(tabtab[n].start - p));
7340 	    while (col < len)
7341 		TabPageIdxs[col++] = fillchar;
7342 	    p = tabtab[n].start;
7343 	    fillchar = tabtab[n].userhl;
7344 	}
7345 	while (col < Columns)
7346 	    TabPageIdxs[col++] = fillchar;
7347     }
7348 
7349 theend:
7350     entered = FALSE;
7351 }
7352 
7353 #endif /* FEAT_STL_OPT */
7354 
7355 /*
7356  * Output a single character directly to the screen and update ScreenLines.
7357  */
7358     void
7359 screen_putchar(int c, int row, int col, int attr)
7360 {
7361     char_u	buf[MB_MAXBYTES + 1];
7362 
7363 #ifdef FEAT_MBYTE
7364     if (has_mbyte)
7365 	buf[(*mb_char2bytes)(c, buf)] = NUL;
7366     else
7367 #endif
7368     {
7369 	buf[0] = c;
7370 	buf[1] = NUL;
7371     }
7372     screen_puts(buf, row, col, attr);
7373 }
7374 
7375 /*
7376  * Get a single character directly from ScreenLines into "bytes[]".
7377  * Also return its attribute in *attrp;
7378  */
7379     void
7380 screen_getbytes(int row, int col, char_u *bytes, int *attrp)
7381 {
7382     unsigned off;
7383 
7384     /* safety check */
7385     if (ScreenLines != NULL && row < screen_Rows && col < screen_Columns)
7386     {
7387 	off = LineOffset[row] + col;
7388 	*attrp = ScreenAttrs[off];
7389 	bytes[0] = ScreenLines[off];
7390 	bytes[1] = NUL;
7391 
7392 #ifdef FEAT_MBYTE
7393 	if (enc_utf8 && ScreenLinesUC[off] != 0)
7394 	    bytes[utfc_char2bytes(off, bytes)] = NUL;
7395 	else if (enc_dbcs == DBCS_JPNU && ScreenLines[off] == 0x8e)
7396 	{
7397 	    bytes[0] = ScreenLines[off];
7398 	    bytes[1] = ScreenLines2[off];
7399 	    bytes[2] = NUL;
7400 	}
7401 	else if (enc_dbcs && MB_BYTE2LEN(bytes[0]) > 1)
7402 	{
7403 	    bytes[1] = ScreenLines[off + 1];
7404 	    bytes[2] = NUL;
7405 	}
7406 #endif
7407     }
7408 }
7409 
7410 #ifdef FEAT_MBYTE
7411 static int screen_comp_differs(int, int*);
7412 
7413 /*
7414  * Return TRUE if composing characters for screen posn "off" differs from
7415  * composing characters in "u8cc".
7416  * Only to be used when ScreenLinesUC[off] != 0.
7417  */
7418     static int
7419 screen_comp_differs(int off, int *u8cc)
7420 {
7421     int	    i;
7422 
7423     for (i = 0; i < Screen_mco; ++i)
7424     {
7425 	if (ScreenLinesC[i][off] != (u8char_T)u8cc[i])
7426 	    return TRUE;
7427 	if (u8cc[i] == 0)
7428 	    break;
7429     }
7430     return FALSE;
7431 }
7432 #endif
7433 
7434 /*
7435  * Put string '*text' on the screen at position 'row' and 'col', with
7436  * attributes 'attr', and update ScreenLines[] and ScreenAttrs[].
7437  * Note: only outputs within one row, message is truncated at screen boundary!
7438  * Note: if ScreenLines[], row and/or col is invalid, nothing is done.
7439  */
7440     void
7441 screen_puts(
7442     char_u	*text,
7443     int		row,
7444     int		col,
7445     int		attr)
7446 {
7447     screen_puts_len(text, -1, row, col, attr);
7448 }
7449 
7450 /*
7451  * Like screen_puts(), but output "text[len]".  When "len" is -1 output up to
7452  * a NUL.
7453  */
7454     void
7455 screen_puts_len(
7456     char_u	*text,
7457     int		textlen,
7458     int		row,
7459     int		col,
7460     int		attr)
7461 {
7462     unsigned	off;
7463     char_u	*ptr = text;
7464     int		len = textlen;
7465     int		c;
7466 #ifdef FEAT_MBYTE
7467     unsigned	max_off;
7468     int		mbyte_blen = 1;
7469     int		mbyte_cells = 1;
7470     int		u8c = 0;
7471     int		u8cc[MAX_MCO];
7472     int		clear_next_cell = FALSE;
7473 # ifdef FEAT_ARABIC
7474     int		prev_c = 0;		/* previous Arabic character */
7475     int		pc, nc, nc1;
7476     int		pcc[MAX_MCO];
7477 # endif
7478 #endif
7479 #if defined(FEAT_MBYTE) || defined(FEAT_GUI) || defined(UNIX)
7480     int		force_redraw_this;
7481     int		force_redraw_next = FALSE;
7482 #endif
7483     int		need_redraw;
7484 
7485     if (ScreenLines == NULL || row >= screen_Rows)	/* safety check */
7486 	return;
7487     off = LineOffset[row] + col;
7488 
7489 #ifdef FEAT_MBYTE
7490     /* When drawing over the right halve of a double-wide char clear out the
7491      * left halve.  Only needed in a terminal. */
7492     if (has_mbyte && col > 0 && col < screen_Columns
7493 # ifdef FEAT_GUI
7494 	    && !gui.in_use
7495 # endif
7496 	    && mb_fix_col(col, row) != col)
7497     {
7498 	ScreenLines[off - 1] = ' ';
7499 	ScreenAttrs[off - 1] = 0;
7500 	if (enc_utf8)
7501 	{
7502 	    ScreenLinesUC[off - 1] = 0;
7503 	    ScreenLinesC[0][off - 1] = 0;
7504 	}
7505 	/* redraw the previous cell, make it empty */
7506 	screen_char(off - 1, row, col - 1);
7507 	/* force the cell at "col" to be redrawn */
7508 	force_redraw_next = TRUE;
7509     }
7510 #endif
7511 
7512 #ifdef FEAT_MBYTE
7513     max_off = LineOffset[row] + screen_Columns;
7514 #endif
7515     while (col < screen_Columns
7516 	    && (len < 0 || (int)(ptr - text) < len)
7517 	    && *ptr != NUL)
7518     {
7519 	c = *ptr;
7520 #ifdef FEAT_MBYTE
7521 	/* check if this is the first byte of a multibyte */
7522 	if (has_mbyte)
7523 	{
7524 	    if (enc_utf8 && len > 0)
7525 		mbyte_blen = utfc_ptr2len_len(ptr, (int)((text + len) - ptr));
7526 	    else
7527 		mbyte_blen = (*mb_ptr2len)(ptr);
7528 	    if (enc_dbcs == DBCS_JPNU && c == 0x8e)
7529 		mbyte_cells = 1;
7530 	    else if (enc_dbcs != 0)
7531 		mbyte_cells = mbyte_blen;
7532 	    else	/* enc_utf8 */
7533 	    {
7534 		if (len >= 0)
7535 		    u8c = utfc_ptr2char_len(ptr, u8cc,
7536 						   (int)((text + len) - ptr));
7537 		else
7538 		    u8c = utfc_ptr2char(ptr, u8cc);
7539 		mbyte_cells = utf_char2cells(u8c);
7540 # ifdef UNICODE16
7541 		/* Non-BMP character: display as ? or fullwidth ?. */
7542 		if (u8c >= 0x10000)
7543 		{
7544 		    u8c = (mbyte_cells == 2) ? 0xff1f : (int)'?';
7545 		    if (attr == 0)
7546 			attr = HL_ATTR(HLF_8);
7547 		}
7548 # endif
7549 # ifdef FEAT_ARABIC
7550 		if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c))
7551 		{
7552 		    /* Do Arabic shaping. */
7553 		    if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len)
7554 		    {
7555 			/* Past end of string to be displayed. */
7556 			nc = NUL;
7557 			nc1 = NUL;
7558 		    }
7559 		    else
7560 		    {
7561 			nc = utfc_ptr2char_len(ptr + mbyte_blen, pcc,
7562 				      (int)((text + len) - ptr - mbyte_blen));
7563 			nc1 = pcc[0];
7564 		    }
7565 		    pc = prev_c;
7566 		    prev_c = u8c;
7567 		    u8c = arabic_shape(u8c, &c, &u8cc[0], nc, nc1, pc);
7568 		}
7569 		else
7570 		    prev_c = u8c;
7571 # endif
7572 		if (col + mbyte_cells > screen_Columns)
7573 		{
7574 		    /* Only 1 cell left, but character requires 2 cells:
7575 		     * display a '>' in the last column to avoid wrapping. */
7576 		    c = '>';
7577 		    mbyte_cells = 1;
7578 		}
7579 	    }
7580 	}
7581 #endif
7582 
7583 #if defined(FEAT_MBYTE) || defined(FEAT_GUI) || defined(UNIX)
7584 	force_redraw_this = force_redraw_next;
7585 	force_redraw_next = FALSE;
7586 #endif
7587 
7588 	need_redraw = ScreenLines[off] != c
7589 #ifdef FEAT_MBYTE
7590 		|| (mbyte_cells == 2
7591 		    && ScreenLines[off + 1] != (enc_dbcs ? ptr[1] : 0))
7592 		|| (enc_dbcs == DBCS_JPNU
7593 		    && c == 0x8e
7594 		    && ScreenLines2[off] != ptr[1])
7595 		|| (enc_utf8
7596 		    && (ScreenLinesUC[off] !=
7597 				(u8char_T)(c < 0x80 && u8cc[0] == 0 ? 0 : u8c)
7598 			|| (ScreenLinesUC[off] != 0
7599 					  && screen_comp_differs(off, u8cc))))
7600 #endif
7601 		|| ScreenAttrs[off] != attr
7602 		|| exmode_active;
7603 
7604 	if (need_redraw
7605 #if defined(FEAT_MBYTE) || defined(FEAT_GUI) || defined(UNIX)
7606 		|| force_redraw_this
7607 #endif
7608 		)
7609 	{
7610 #if defined(FEAT_GUI) || defined(UNIX)
7611 	    /* The bold trick makes a single row of pixels appear in the next
7612 	     * character.  When a bold character is removed, the next
7613 	     * character should be redrawn too.  This happens for our own GUI
7614 	     * and for some xterms. */
7615 	    if (need_redraw && ScreenLines[off] != ' ' && (
7616 # ifdef FEAT_GUI
7617 		    gui.in_use
7618 # endif
7619 # if defined(FEAT_GUI) && defined(UNIX)
7620 		    ||
7621 # endif
7622 # ifdef UNIX
7623 		    term_is_xterm
7624 # endif
7625 		    ))
7626 	    {
7627 		int	n = ScreenAttrs[off];
7628 
7629 		if (n > HL_ALL)
7630 		    n = syn_attr2attr(n);
7631 		if (n & HL_BOLD)
7632 		    force_redraw_next = TRUE;
7633 	    }
7634 #endif
7635 #ifdef FEAT_MBYTE
7636 	    /* When at the end of the text and overwriting a two-cell
7637 	     * character with a one-cell character, need to clear the next
7638 	     * cell.  Also when overwriting the left halve of a two-cell char
7639 	     * with the right halve of a two-cell char.  Do this only once
7640 	     * (mb_off2cells() may return 2 on the right halve). */
7641 	    if (clear_next_cell)
7642 		clear_next_cell = FALSE;
7643 	    else if (has_mbyte
7644 		    && (len < 0 ? ptr[mbyte_blen] == NUL
7645 					     : ptr + mbyte_blen >= text + len)
7646 		    && ((mbyte_cells == 1 && (*mb_off2cells)(off, max_off) > 1)
7647 			|| (mbyte_cells == 2
7648 			    && (*mb_off2cells)(off, max_off) == 1
7649 			    && (*mb_off2cells)(off + 1, max_off) > 1)))
7650 		clear_next_cell = TRUE;
7651 
7652 	    /* Make sure we never leave a second byte of a double-byte behind,
7653 	     * it confuses mb_off2cells(). */
7654 	    if (enc_dbcs
7655 		    && ((mbyte_cells == 1 && (*mb_off2cells)(off, max_off) > 1)
7656 			|| (mbyte_cells == 2
7657 			    && (*mb_off2cells)(off, max_off) == 1
7658 			    && (*mb_off2cells)(off + 1, max_off) > 1)))
7659 		ScreenLines[off + mbyte_blen] = 0;
7660 #endif
7661 	    ScreenLines[off] = c;
7662 	    ScreenAttrs[off] = attr;
7663 #ifdef FEAT_MBYTE
7664 	    if (enc_utf8)
7665 	    {
7666 		if (c < 0x80 && u8cc[0] == 0)
7667 		    ScreenLinesUC[off] = 0;
7668 		else
7669 		{
7670 		    int	    i;
7671 
7672 		    ScreenLinesUC[off] = u8c;
7673 		    for (i = 0; i < Screen_mco; ++i)
7674 		    {
7675 			ScreenLinesC[i][off] = u8cc[i];
7676 			if (u8cc[i] == 0)
7677 			    break;
7678 		    }
7679 		}
7680 		if (mbyte_cells == 2)
7681 		{
7682 		    ScreenLines[off + 1] = 0;
7683 		    ScreenAttrs[off + 1] = attr;
7684 		}
7685 		screen_char(off, row, col);
7686 	    }
7687 	    else if (mbyte_cells == 2)
7688 	    {
7689 		ScreenLines[off + 1] = ptr[1];
7690 		ScreenAttrs[off + 1] = attr;
7691 		screen_char_2(off, row, col);
7692 	    }
7693 	    else if (enc_dbcs == DBCS_JPNU && c == 0x8e)
7694 	    {
7695 		ScreenLines2[off] = ptr[1];
7696 		screen_char(off, row, col);
7697 	    }
7698 	    else
7699 #endif
7700 		screen_char(off, row, col);
7701 	}
7702 #ifdef FEAT_MBYTE
7703 	if (has_mbyte)
7704 	{
7705 	    off += mbyte_cells;
7706 	    col += mbyte_cells;
7707 	    ptr += mbyte_blen;
7708 	    if (clear_next_cell)
7709 	    {
7710 		/* This only happens at the end, display one space next. */
7711 		ptr = (char_u *)" ";
7712 		len = -1;
7713 	    }
7714 	}
7715 	else
7716 #endif
7717 	{
7718 	    ++off;
7719 	    ++col;
7720 	    ++ptr;
7721 	}
7722     }
7723 
7724 #if defined(FEAT_MBYTE) || defined(FEAT_GUI) || defined(UNIX)
7725     /* If we detected the next character needs to be redrawn, but the text
7726      * doesn't extend up to there, update the character here. */
7727     if (force_redraw_next && col < screen_Columns)
7728     {
7729 # ifdef FEAT_MBYTE
7730 	if (enc_dbcs != 0 && dbcs_off2cells(off, max_off) > 1)
7731 	    screen_char_2(off, row, col);
7732 	else
7733 # endif
7734 	    screen_char(off, row, col);
7735     }
7736 #endif
7737 }
7738 
7739 #ifdef FEAT_SEARCH_EXTRA
7740 /*
7741  * Prepare for 'hlsearch' highlighting.
7742  */
7743     static void
7744 start_search_hl(void)
7745 {
7746     if (p_hls && !no_hlsearch)
7747     {
7748 	last_pat_prog(&search_hl.rm);
7749 	search_hl.attr = HL_ATTR(HLF_L);
7750 # ifdef FEAT_RELTIME
7751 	/* Set the time limit to 'redrawtime'. */
7752 	profile_setlimit(p_rdt, &search_hl.tm);
7753 # endif
7754     }
7755 }
7756 
7757 /*
7758  * Clean up for 'hlsearch' highlighting.
7759  */
7760     static void
7761 end_search_hl(void)
7762 {
7763     if (search_hl.rm.regprog != NULL)
7764     {
7765 	vim_regfree(search_hl.rm.regprog);
7766 	search_hl.rm.regprog = NULL;
7767     }
7768 }
7769 
7770 /*
7771  * Init for calling prepare_search_hl().
7772  */
7773     static void
7774 init_search_hl(win_T *wp)
7775 {
7776     matchitem_T *cur;
7777 
7778     /* Setup for match and 'hlsearch' highlighting.  Disable any previous
7779      * match */
7780     cur = wp->w_match_head;
7781     while (cur != NULL)
7782     {
7783 	cur->hl.rm = cur->match;
7784 	if (cur->hlg_id == 0)
7785 	    cur->hl.attr = 0;
7786 	else
7787 	    cur->hl.attr = syn_id2attr(cur->hlg_id);
7788 	cur->hl.buf = wp->w_buffer;
7789 	cur->hl.lnum = 0;
7790 	cur->hl.first_lnum = 0;
7791 # ifdef FEAT_RELTIME
7792 	/* Set the time limit to 'redrawtime'. */
7793 	profile_setlimit(p_rdt, &(cur->hl.tm));
7794 # endif
7795 	cur = cur->next;
7796     }
7797     search_hl.buf = wp->w_buffer;
7798     search_hl.lnum = 0;
7799     search_hl.first_lnum = 0;
7800     /* time limit is set at the toplevel, for all windows */
7801 }
7802 
7803 /*
7804  * Advance to the match in window "wp" line "lnum" or past it.
7805  */
7806     static void
7807 prepare_search_hl(win_T *wp, linenr_T lnum)
7808 {
7809     matchitem_T *cur;		/* points to the match list */
7810     match_T	*shl;		/* points to search_hl or a match */
7811     int		shl_flag;	/* flag to indicate whether search_hl
7812 				   has been processed or not */
7813     int		pos_inprogress;	/* marks that position match search is
7814 				   in progress */
7815     int		n;
7816 
7817     /*
7818      * When using a multi-line pattern, start searching at the top
7819      * of the window or just after a closed fold.
7820      * Do this both for search_hl and the match list.
7821      */
7822     cur = wp->w_match_head;
7823     shl_flag = FALSE;
7824     while (cur != NULL || shl_flag == FALSE)
7825     {
7826 	if (shl_flag == FALSE)
7827 	{
7828 	    shl = &search_hl;
7829 	    shl_flag = TRUE;
7830 	}
7831 	else
7832 	    shl = &cur->hl;
7833 	if (shl->rm.regprog != NULL
7834 		&& shl->lnum == 0
7835 		&& re_multiline(shl->rm.regprog))
7836 	{
7837 	    if (shl->first_lnum == 0)
7838 	    {
7839 # ifdef FEAT_FOLDING
7840 		for (shl->first_lnum = lnum;
7841 			   shl->first_lnum > wp->w_topline; --shl->first_lnum)
7842 		    if (hasFoldingWin(wp, shl->first_lnum - 1,
7843 						      NULL, NULL, TRUE, NULL))
7844 			break;
7845 # else
7846 		shl->first_lnum = wp->w_topline;
7847 # endif
7848 	    }
7849 	    if (cur != NULL)
7850 		cur->pos.cur = 0;
7851 	    pos_inprogress = TRUE;
7852 	    n = 0;
7853 	    while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
7854 					  || (cur != NULL && pos_inprogress)))
7855 	    {
7856 		next_search_hl(wp, shl, shl->first_lnum, (colnr_T)n,
7857 					       shl == &search_hl ? NULL : cur);
7858 		pos_inprogress = cur == NULL || cur->pos.cur == 0
7859 							      ? FALSE : TRUE;
7860 		if (shl->lnum != 0)
7861 		{
7862 		    shl->first_lnum = shl->lnum
7863 				    + shl->rm.endpos[0].lnum
7864 				    - shl->rm.startpos[0].lnum;
7865 		    n = shl->rm.endpos[0].col;
7866 		}
7867 		else
7868 		{
7869 		    ++shl->first_lnum;
7870 		    n = 0;
7871 		}
7872 	    }
7873 	}
7874 	if (shl != &search_hl && cur != NULL)
7875 	    cur = cur->next;
7876     }
7877 }
7878 
7879 /*
7880  * Search for a next 'hlsearch' or match.
7881  * Uses shl->buf.
7882  * Sets shl->lnum and shl->rm contents.
7883  * Note: Assumes a previous match is always before "lnum", unless
7884  * shl->lnum is zero.
7885  * Careful: Any pointers for buffer lines will become invalid.
7886  */
7887     static void
7888 next_search_hl(
7889     win_T	    *win,
7890     match_T	    *shl,	/* points to search_hl or a match */
7891     linenr_T	    lnum,
7892     colnr_T	    mincol,	/* minimal column for a match */
7893     matchitem_T	    *cur)	/* to retrieve match positions if any */
7894 {
7895     linenr_T	l;
7896     colnr_T	matchcol;
7897     long	nmatched;
7898     int		save_called_emsg = called_emsg;
7899 
7900     // for :{range}s/pat only highlight inside the range
7901     if (lnum < search_first_line || lnum > search_last_line)
7902     {
7903 	shl->lnum = 0;
7904 	return;
7905     }
7906 
7907     if (shl->lnum != 0)
7908     {
7909 	/* Check for three situations:
7910 	 * 1. If the "lnum" is below a previous match, start a new search.
7911 	 * 2. If the previous match includes "mincol", use it.
7912 	 * 3. Continue after the previous match.
7913 	 */
7914 	l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
7915 	if (lnum > l)
7916 	    shl->lnum = 0;
7917 	else if (lnum < l || shl->rm.endpos[0].col > mincol)
7918 	    return;
7919     }
7920 
7921     /*
7922      * Repeat searching for a match until one is found that includes "mincol"
7923      * or none is found in this line.
7924      */
7925     called_emsg = FALSE;
7926     for (;;)
7927     {
7928 #ifdef FEAT_RELTIME
7929 	/* Stop searching after passing the time limit. */
7930 	if (profile_passed_limit(&(shl->tm)))
7931 	{
7932 	    shl->lnum = 0;		/* no match found in time */
7933 	    break;
7934 	}
7935 #endif
7936 	/* Three situations:
7937 	 * 1. No useful previous match: search from start of line.
7938 	 * 2. Not Vi compatible or empty match: continue at next character.
7939 	 *    Break the loop if this is beyond the end of the line.
7940 	 * 3. Vi compatible searching: continue at end of previous match.
7941 	 */
7942 	if (shl->lnum == 0)
7943 	    matchcol = 0;
7944 	else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
7945 		|| (shl->rm.endpos[0].lnum == 0
7946 		    && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
7947 	{
7948 	    char_u	*ml;
7949 
7950 	    matchcol = shl->rm.startpos[0].col;
7951 	    ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
7952 	    if (*ml == NUL)
7953 	    {
7954 		++matchcol;
7955 		shl->lnum = 0;
7956 		break;
7957 	    }
7958 #ifdef FEAT_MBYTE
7959 	    if (has_mbyte)
7960 		matchcol += mb_ptr2len(ml);
7961 	    else
7962 #endif
7963 		++matchcol;
7964 	}
7965 	else
7966 	    matchcol = shl->rm.endpos[0].col;
7967 
7968 	shl->lnum = lnum;
7969 	if (shl->rm.regprog != NULL)
7970 	{
7971 	    /* Remember whether shl->rm is using a copy of the regprog in
7972 	     * cur->match. */
7973 	    int regprog_is_copy = (shl != &search_hl && cur != NULL
7974 				&& shl == &cur->hl
7975 				&& cur->match.regprog == cur->hl.rm.regprog);
7976 	    int timed_out = FALSE;
7977 
7978 	    nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
7979 		    matchcol,
7980 #ifdef FEAT_RELTIME
7981 		    &(shl->tm), &timed_out
7982 #else
7983 		    NULL, NULL
7984 #endif
7985 		    );
7986 	    /* Copy the regprog, in case it got freed and recompiled. */
7987 	    if (regprog_is_copy)
7988 		cur->match.regprog = cur->hl.rm.regprog;
7989 
7990 	    if (called_emsg || got_int || timed_out)
7991 	    {
7992 		/* Error while handling regexp: stop using this regexp. */
7993 		if (shl == &search_hl)
7994 		{
7995 		    /* don't free regprog in the match list, it's a copy */
7996 		    vim_regfree(shl->rm.regprog);
7997 		    set_no_hlsearch(TRUE);
7998 		}
7999 		shl->rm.regprog = NULL;
8000 		shl->lnum = 0;
8001 		got_int = FALSE;  /* avoid the "Type :quit to exit Vim"
8002 				     message */
8003 		break;
8004 	    }
8005 	}
8006 	else if (cur != NULL)
8007 	    nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
8008 	else
8009 	    nmatched = 0;
8010 	if (nmatched == 0)
8011 	{
8012 	    shl->lnum = 0;		/* no match found */
8013 	    break;
8014 	}
8015 	if (shl->rm.startpos[0].lnum > 0
8016 		|| shl->rm.startpos[0].col >= mincol
8017 		|| nmatched > 1
8018 		|| shl->rm.endpos[0].col > mincol)
8019 	{
8020 	    shl->lnum += shl->rm.startpos[0].lnum;
8021 	    break;			/* useful match found */
8022 	}
8023     }
8024 
8025     // Restore called_emsg for assert_fails().
8026     called_emsg = save_called_emsg;
8027 }
8028 
8029 /*
8030  * If there is a match fill "shl" and return one.
8031  * Return zero otherwise.
8032  */
8033     static int
8034 next_search_hl_pos(
8035     match_T	    *shl,	/* points to a match */
8036     linenr_T	    lnum,
8037     posmatch_T	    *posmatch,	/* match positions */
8038     colnr_T	    mincol)	/* minimal column for a match */
8039 {
8040     int	    i;
8041     int	    found = -1;
8042 
8043     for (i = posmatch->cur; i < MAXPOSMATCH; i++)
8044     {
8045 	llpos_T	*pos = &posmatch->pos[i];
8046 
8047 	if (pos->lnum == 0)
8048 	    break;
8049 	if (pos->len == 0 && pos->col < mincol)
8050 	    continue;
8051 	if (pos->lnum == lnum)
8052 	{
8053 	    if (found >= 0)
8054 	    {
8055 		/* if this match comes before the one at "found" then swap
8056 		 * them */
8057 		if (pos->col < posmatch->pos[found].col)
8058 		{
8059 		    llpos_T	tmp = *pos;
8060 
8061 		    *pos = posmatch->pos[found];
8062 		    posmatch->pos[found] = tmp;
8063 		}
8064 	    }
8065 	    else
8066 		found = i;
8067 	}
8068     }
8069     posmatch->cur = 0;
8070     if (found >= 0)
8071     {
8072 	colnr_T	start = posmatch->pos[found].col == 0
8073 					    ? 0 : posmatch->pos[found].col - 1;
8074 	colnr_T	end = posmatch->pos[found].col == 0
8075 				   ? MAXCOL : start + posmatch->pos[found].len;
8076 
8077 	shl->lnum = lnum;
8078 	shl->rm.startpos[0].lnum = 0;
8079 	shl->rm.startpos[0].col = start;
8080 	shl->rm.endpos[0].lnum = 0;
8081 	shl->rm.endpos[0].col = end;
8082 	shl->is_addpos = TRUE;
8083 	posmatch->cur = found + 1;
8084 	return 1;
8085     }
8086     return 0;
8087 }
8088 #endif
8089 
8090       static void
8091 screen_start_highlight(int attr)
8092 {
8093     attrentry_T *aep = NULL;
8094 
8095     screen_attr = attr;
8096     if (full_screen
8097 #ifdef WIN3264
8098 		    && termcap_active
8099 #endif
8100 				       )
8101     {
8102 #ifdef FEAT_GUI
8103 	if (gui.in_use)
8104 	{
8105 	    char	buf[20];
8106 
8107 	    /* The GUI handles this internally. */
8108 	    sprintf(buf, IF_EB("\033|%dh", ESC_STR "|%dh"), attr);
8109 	    OUT_STR(buf);
8110 	}
8111 	else
8112 #endif
8113 	{
8114 	    if (attr > HL_ALL)				/* special HL attr. */
8115 	    {
8116 		if (IS_CTERM)
8117 		    aep = syn_cterm_attr2entry(attr);
8118 		else
8119 		    aep = syn_term_attr2entry(attr);
8120 		if (aep == NULL)	    /* did ":syntax clear" */
8121 		    attr = 0;
8122 		else
8123 		    attr = aep->ae_attr;
8124 	    }
8125 	    if ((attr & HL_BOLD) && *T_MD != NUL)	/* bold */
8126 		out_str(T_MD);
8127 	    else if (aep != NULL && cterm_normal_fg_bold && (
8128 #ifdef FEAT_TERMGUICOLORS
8129 			p_tgc && aep->ae_u.cterm.fg_rgb != CTERMCOLOR
8130 			  ? aep->ae_u.cterm.fg_rgb != INVALCOLOR
8131 			  :
8132 #endif
8133 			    t_colors > 1 && aep->ae_u.cterm.fg_color))
8134 		/* If the Normal FG color has BOLD attribute and the new HL
8135 		 * has a FG color defined, clear BOLD. */
8136 		out_str(T_ME);
8137 	    if ((attr & HL_STANDOUT) && *T_SO != NUL)	/* standout */
8138 		out_str(T_SO);
8139 	    if ((attr & HL_UNDERCURL) && *T_UCS != NUL) /* undercurl */
8140 		out_str(T_UCS);
8141 	    if (((attr & HL_UNDERLINE)	    /* underline or undercurl */
8142 			|| ((attr & HL_UNDERCURL) && *T_UCS == NUL))
8143 		    && *T_US != NUL)
8144 		out_str(T_US);
8145 	    if ((attr & HL_ITALIC) && *T_CZH != NUL)	/* italic */
8146 		out_str(T_CZH);
8147 	    if ((attr & HL_INVERSE) && *T_MR != NUL)	/* inverse (reverse) */
8148 		out_str(T_MR);
8149 	    if ((attr & HL_STRIKETHROUGH) && *T_STS != NUL)	/* strike */
8150 		out_str(T_STS);
8151 
8152 	    /*
8153 	     * Output the color or start string after bold etc., in case the
8154 	     * bold etc. override the color setting.
8155 	     */
8156 	    if (aep != NULL)
8157 	    {
8158 #ifdef FEAT_TERMGUICOLORS
8159 		/* When 'termguicolors' is set but fg or bg is unset,
8160 		 * fall back to the cterm colors.   This helps for SpellBad,
8161 		 * where the GUI uses a red undercurl. */
8162 		if (p_tgc && aep->ae_u.cterm.fg_rgb != CTERMCOLOR)
8163 		{
8164 		    if (aep->ae_u.cterm.fg_rgb != INVALCOLOR)
8165 			term_fg_rgb_color(aep->ae_u.cterm.fg_rgb);
8166 		}
8167 		else
8168 #endif
8169 		if (t_colors > 1)
8170 		{
8171 		    if (aep->ae_u.cterm.fg_color)
8172 			term_fg_color(aep->ae_u.cterm.fg_color - 1);
8173 		}
8174 #ifdef FEAT_TERMGUICOLORS
8175 		if (p_tgc && aep->ae_u.cterm.bg_rgb != CTERMCOLOR)
8176 		{
8177 		    if (aep->ae_u.cterm.bg_rgb != INVALCOLOR)
8178 			term_bg_rgb_color(aep->ae_u.cterm.bg_rgb);
8179 		}
8180 		else
8181 #endif
8182 		if (t_colors > 1)
8183 		{
8184 		    if (aep->ae_u.cterm.bg_color)
8185 			term_bg_color(aep->ae_u.cterm.bg_color - 1);
8186 		}
8187 
8188 		if (!IS_CTERM)
8189 		{
8190 		    if (aep->ae_u.term.start != NULL)
8191 			out_str(aep->ae_u.term.start);
8192 		}
8193 	    }
8194 	}
8195     }
8196 }
8197 
8198       void
8199 screen_stop_highlight(void)
8200 {
8201     int	    do_ME = FALSE;	    /* output T_ME code */
8202 
8203     if (screen_attr != 0
8204 #ifdef WIN3264
8205 			&& termcap_active
8206 #endif
8207 					   )
8208     {
8209 #ifdef FEAT_GUI
8210 	if (gui.in_use)
8211 	{
8212 	    char	buf[20];
8213 
8214 	    /* use internal GUI code */
8215 	    sprintf(buf, IF_EB("\033|%dH", ESC_STR "|%dH"), screen_attr);
8216 	    OUT_STR(buf);
8217 	}
8218 	else
8219 #endif
8220 	{
8221 	    if (screen_attr > HL_ALL)			/* special HL attr. */
8222 	    {
8223 		attrentry_T *aep;
8224 
8225 		if (IS_CTERM)
8226 		{
8227 		    /*
8228 		     * Assume that t_me restores the original colors!
8229 		     */
8230 		    aep = syn_cterm_attr2entry(screen_attr);
8231 		    if (aep != NULL && ((
8232 #ifdef FEAT_TERMGUICOLORS
8233 			    p_tgc && aep->ae_u.cterm.fg_rgb != CTERMCOLOR
8234 				? aep->ae_u.cterm.fg_rgb != INVALCOLOR
8235 				:
8236 #endif
8237 				aep->ae_u.cterm.fg_color) || (
8238 #ifdef FEAT_TERMGUICOLORS
8239 			    p_tgc && aep->ae_u.cterm.bg_rgb != CTERMCOLOR
8240 				? aep->ae_u.cterm.bg_rgb != INVALCOLOR
8241 				:
8242 #endif
8243 				aep->ae_u.cterm.bg_color)))
8244 			do_ME = TRUE;
8245 		}
8246 		else
8247 		{
8248 		    aep = syn_term_attr2entry(screen_attr);
8249 		    if (aep != NULL && aep->ae_u.term.stop != NULL)
8250 		    {
8251 			if (STRCMP(aep->ae_u.term.stop, T_ME) == 0)
8252 			    do_ME = TRUE;
8253 			else
8254 			    out_str(aep->ae_u.term.stop);
8255 		    }
8256 		}
8257 		if (aep == NULL)	    /* did ":syntax clear" */
8258 		    screen_attr = 0;
8259 		else
8260 		    screen_attr = aep->ae_attr;
8261 	    }
8262 
8263 	    /*
8264 	     * Often all ending-codes are equal to T_ME.  Avoid outputting the
8265 	     * same sequence several times.
8266 	     */
8267 	    if (screen_attr & HL_STANDOUT)
8268 	    {
8269 		if (STRCMP(T_SE, T_ME) == 0)
8270 		    do_ME = TRUE;
8271 		else
8272 		    out_str(T_SE);
8273 	    }
8274 	    if ((screen_attr & HL_UNDERCURL) && *T_UCE != NUL)
8275 	    {
8276 		if (STRCMP(T_UCE, T_ME) == 0)
8277 		    do_ME = TRUE;
8278 		else
8279 		    out_str(T_UCE);
8280 	    }
8281 	    if ((screen_attr & HL_UNDERLINE)
8282 			    || ((screen_attr & HL_UNDERCURL) && *T_UCE == NUL))
8283 	    {
8284 		if (STRCMP(T_UE, T_ME) == 0)
8285 		    do_ME = TRUE;
8286 		else
8287 		    out_str(T_UE);
8288 	    }
8289 	    if (screen_attr & HL_ITALIC)
8290 	    {
8291 		if (STRCMP(T_CZR, T_ME) == 0)
8292 		    do_ME = TRUE;
8293 		else
8294 		    out_str(T_CZR);
8295 	    }
8296 	    if (screen_attr & HL_STRIKETHROUGH)
8297 	    {
8298 		if (STRCMP(T_STE, T_ME) == 0)
8299 		    do_ME = TRUE;
8300 		else
8301 		    out_str(T_STE);
8302 	    }
8303 	    if (do_ME || (screen_attr & (HL_BOLD | HL_INVERSE)))
8304 		out_str(T_ME);
8305 
8306 #ifdef FEAT_TERMGUICOLORS
8307 	    if (p_tgc)
8308 	    {
8309 		if (cterm_normal_fg_gui_color != INVALCOLOR)
8310 		    term_fg_rgb_color(cterm_normal_fg_gui_color);
8311 		if (cterm_normal_bg_gui_color != INVALCOLOR)
8312 		    term_bg_rgb_color(cterm_normal_bg_gui_color);
8313 	    }
8314 	    else
8315 #endif
8316 	    {
8317 		if (t_colors > 1)
8318 		{
8319 		    /* set Normal cterm colors */
8320 		    if (cterm_normal_fg_color != 0)
8321 			term_fg_color(cterm_normal_fg_color - 1);
8322 		    if (cterm_normal_bg_color != 0)
8323 			term_bg_color(cterm_normal_bg_color - 1);
8324 		    if (cterm_normal_fg_bold)
8325 			out_str(T_MD);
8326 		}
8327 	    }
8328 	}
8329     }
8330     screen_attr = 0;
8331 }
8332 
8333 /*
8334  * Reset the colors for a cterm.  Used when leaving Vim.
8335  * The machine specific code may override this again.
8336  */
8337     void
8338 reset_cterm_colors(void)
8339 {
8340     if (IS_CTERM)
8341     {
8342 	/* set Normal cterm colors */
8343 #ifdef FEAT_TERMGUICOLORS
8344 	if (p_tgc ? (cterm_normal_fg_gui_color != INVALCOLOR
8345 		 || cterm_normal_bg_gui_color != INVALCOLOR)
8346 		: (cterm_normal_fg_color > 0 || cterm_normal_bg_color > 0))
8347 #else
8348 	if (cterm_normal_fg_color > 0 || cterm_normal_bg_color > 0)
8349 #endif
8350 	{
8351 	    out_str(T_OP);
8352 	    screen_attr = -1;
8353 	}
8354 	if (cterm_normal_fg_bold)
8355 	{
8356 	    out_str(T_ME);
8357 	    screen_attr = -1;
8358 	}
8359     }
8360 }
8361 
8362 /*
8363  * Put character ScreenLines["off"] on the screen at position "row" and "col",
8364  * using the attributes from ScreenAttrs["off"].
8365  */
8366     static void
8367 screen_char(unsigned off, int row, int col)
8368 {
8369     int		attr;
8370 
8371     /* Check for illegal values, just in case (could happen just after
8372      * resizing). */
8373     if (row >= screen_Rows || col >= screen_Columns)
8374 	return;
8375 
8376     /* Outputting a character in the last cell on the screen may scroll the
8377      * screen up.  Only do it when the "xn" termcap property is set, otherwise
8378      * mark the character invalid (update it when scrolled up). */
8379     if (*T_XN == NUL
8380 	    && row == screen_Rows - 1 && col == screen_Columns - 1
8381 #ifdef FEAT_RIGHTLEFT
8382 	    /* account for first command-line character in rightleft mode */
8383 	    && !cmdmsg_rl
8384 #endif
8385        )
8386     {
8387 	ScreenAttrs[off] = (sattr_T)-1;
8388 	return;
8389     }
8390 
8391     /*
8392      * Stop highlighting first, so it's easier to move the cursor.
8393      */
8394     if (screen_char_attr != 0)
8395 	attr = screen_char_attr;
8396     else
8397 	attr = ScreenAttrs[off];
8398     if (screen_attr != attr)
8399 	screen_stop_highlight();
8400 
8401     windgoto(row, col);
8402 
8403     if (screen_attr != attr)
8404 	screen_start_highlight(attr);
8405 
8406 #ifdef FEAT_MBYTE
8407     if (enc_utf8 && ScreenLinesUC[off] != 0)
8408     {
8409 	char_u	    buf[MB_MAXBYTES + 1];
8410 
8411 	if (utf_ambiguous_width(ScreenLinesUC[off]))
8412 	{
8413 	    if (*p_ambw == 'd'
8414 # ifdef FEAT_GUI
8415 		    && !gui.in_use
8416 # endif
8417 		    )
8418 	    {
8419 		/* Clear the two screen cells. If the character is actually
8420 		 * single width it won't change the second cell. */
8421 		out_str((char_u *)"  ");
8422 		term_windgoto(row, col);
8423 	    }
8424 	    /* not sure where the cursor is after drawing the ambiguous width
8425 	     * character */
8426 	    screen_cur_col = 9999;
8427 	}
8428 	else if (utf_char2cells(ScreenLinesUC[off]) > 1)
8429 	    ++screen_cur_col;
8430 
8431 	/* Convert the UTF-8 character to bytes and write it. */
8432 	buf[utfc_char2bytes(off, buf)] = NUL;
8433 	out_str(buf);
8434     }
8435     else
8436 #endif
8437     {
8438 #ifdef FEAT_MBYTE
8439 	out_flush_check();
8440 #endif
8441 	out_char(ScreenLines[off]);
8442 #ifdef FEAT_MBYTE
8443 	/* double-byte character in single-width cell */
8444 	if (enc_dbcs == DBCS_JPNU && ScreenLines[off] == 0x8e)
8445 	    out_char(ScreenLines2[off]);
8446 #endif
8447     }
8448 
8449     screen_cur_col++;
8450 }
8451 
8452 #ifdef FEAT_MBYTE
8453 
8454 /*
8455  * Used for enc_dbcs only: Put one double-wide character at ScreenLines["off"]
8456  * on the screen at position 'row' and 'col'.
8457  * The attributes of the first byte is used for all.  This is required to
8458  * output the two bytes of a double-byte character with nothing in between.
8459  */
8460     static void
8461 screen_char_2(unsigned off, int row, int col)
8462 {
8463     /* Check for illegal values (could be wrong when screen was resized). */
8464     if (off + 1 >= (unsigned)(screen_Rows * screen_Columns))
8465 	return;
8466 
8467     /* Outputting the last character on the screen may scrollup the screen.
8468      * Don't to it!  Mark the character invalid (update it when scrolled up) */
8469     if (row == screen_Rows - 1 && col >= screen_Columns - 2)
8470     {
8471 	ScreenAttrs[off] = (sattr_T)-1;
8472 	return;
8473     }
8474 
8475     /* Output the first byte normally (positions the cursor), then write the
8476      * second byte directly. */
8477     screen_char(off, row, col);
8478     out_char(ScreenLines[off + 1]);
8479     ++screen_cur_col;
8480 }
8481 #endif
8482 
8483 /*
8484  * Draw a rectangle of the screen, inverted when "invert" is TRUE.
8485  * This uses the contents of ScreenLines[] and doesn't change it.
8486  */
8487     void
8488 screen_draw_rectangle(
8489     int		row,
8490     int		col,
8491     int		height,
8492     int		width,
8493     int		invert)
8494 {
8495     int		r, c;
8496     int		off;
8497 #ifdef FEAT_MBYTE
8498     int		max_off;
8499 #endif
8500 
8501     /* Can't use ScreenLines unless initialized */
8502     if (ScreenLines == NULL)
8503 	return;
8504 
8505     if (invert)
8506 	screen_char_attr = HL_INVERSE;
8507     for (r = row; r < row + height; ++r)
8508     {
8509 	off = LineOffset[r];
8510 #ifdef FEAT_MBYTE
8511 	max_off = off + screen_Columns;
8512 #endif
8513 	for (c = col; c < col + width; ++c)
8514 	{
8515 #ifdef FEAT_MBYTE
8516 	    if (enc_dbcs != 0 && dbcs_off2cells(off + c, max_off) > 1)
8517 	    {
8518 		screen_char_2(off + c, r, c);
8519 		++c;
8520 	    }
8521 	    else
8522 #endif
8523 	    {
8524 		screen_char(off + c, r, c);
8525 #ifdef FEAT_MBYTE
8526 		if (utf_off2cells(off + c, max_off) > 1)
8527 		    ++c;
8528 #endif
8529 	    }
8530 	}
8531     }
8532     screen_char_attr = 0;
8533 }
8534 
8535 /*
8536  * Redraw the characters for a vertically split window.
8537  */
8538     static void
8539 redraw_block(int row, int end, win_T *wp)
8540 {
8541     int		col;
8542     int		width;
8543 
8544 # ifdef FEAT_CLIPBOARD
8545     clip_may_clear_selection(row, end - 1);
8546 # endif
8547 
8548     if (wp == NULL)
8549     {
8550 	col = 0;
8551 	width = Columns;
8552     }
8553     else
8554     {
8555 	col = wp->w_wincol;
8556 	width = wp->w_width;
8557     }
8558     screen_draw_rectangle(row, col, end - row, width, FALSE);
8559 }
8560 
8561     static void
8562 space_to_screenline(int off, int attr)
8563 {
8564     ScreenLines[off] = ' ';
8565     ScreenAttrs[off] = attr;
8566 # ifdef FEAT_MBYTE
8567     if (enc_utf8)
8568 	ScreenLinesUC[off] = 0;
8569 # endif
8570 }
8571 
8572 /*
8573  * Fill the screen from 'start_row' to 'end_row', from 'start_col' to 'end_col'
8574  * with character 'c1' in first column followed by 'c2' in the other columns.
8575  * Use attributes 'attr'.
8576  */
8577     void
8578 screen_fill(
8579     int	    start_row,
8580     int	    end_row,
8581     int	    start_col,
8582     int	    end_col,
8583     int	    c1,
8584     int	    c2,
8585     int	    attr)
8586 {
8587     int		    row;
8588     int		    col;
8589     int		    off;
8590     int		    end_off;
8591     int		    did_delete;
8592     int		    c;
8593     int		    norm_term;
8594 #if defined(FEAT_GUI) || defined(UNIX)
8595     int		    force_next = FALSE;
8596 #endif
8597 
8598     if (end_row > screen_Rows)		/* safety check */
8599 	end_row = screen_Rows;
8600     if (end_col > screen_Columns)	/* safety check */
8601 	end_col = screen_Columns;
8602     if (ScreenLines == NULL
8603 	    || start_row >= end_row
8604 	    || start_col >= end_col)	/* nothing to do */
8605 	return;
8606 
8607     /* it's a "normal" terminal when not in a GUI or cterm */
8608     norm_term = (
8609 #ifdef FEAT_GUI
8610 	    !gui.in_use &&
8611 #endif
8612 	    !IS_CTERM);
8613     for (row = start_row; row < end_row; ++row)
8614     {
8615 #ifdef FEAT_MBYTE
8616 	if (has_mbyte
8617 # ifdef FEAT_GUI
8618 		&& !gui.in_use
8619 # endif
8620 	   )
8621 	{
8622 	    /* When drawing over the right halve of a double-wide char clear
8623 	     * out the left halve.  When drawing over the left halve of a
8624 	     * double wide-char clear out the right halve.  Only needed in a
8625 	     * terminal. */
8626 	    if (start_col > 0 && mb_fix_col(start_col, row) != start_col)
8627 		screen_puts_len((char_u *)" ", 1, row, start_col - 1, 0);
8628 	    if (end_col < screen_Columns && mb_fix_col(end_col, row) != end_col)
8629 		screen_puts_len((char_u *)" ", 1, row, end_col, 0);
8630 	}
8631 #endif
8632 	/*
8633 	 * Try to use delete-line termcap code, when no attributes or in a
8634 	 * "normal" terminal, where a bold/italic space is just a
8635 	 * space.
8636 	 */
8637 	did_delete = FALSE;
8638 	if (c2 == ' '
8639 		&& end_col == Columns
8640 		&& can_clear(T_CE)
8641 		&& (attr == 0
8642 		    || (norm_term
8643 			&& attr <= HL_ALL
8644 			&& ((attr & ~(HL_BOLD | HL_ITALIC)) == 0))))
8645 	{
8646 	    /*
8647 	     * check if we really need to clear something
8648 	     */
8649 	    col = start_col;
8650 	    if (c1 != ' ')			/* don't clear first char */
8651 		++col;
8652 
8653 	    off = LineOffset[row] + col;
8654 	    end_off = LineOffset[row] + end_col;
8655 
8656 	    /* skip blanks (used often, keep it fast!) */
8657 #ifdef FEAT_MBYTE
8658 	    if (enc_utf8)
8659 		while (off < end_off && ScreenLines[off] == ' '
8660 			  && ScreenAttrs[off] == 0 && ScreenLinesUC[off] == 0)
8661 		    ++off;
8662 	    else
8663 #endif
8664 		while (off < end_off && ScreenLines[off] == ' '
8665 						     && ScreenAttrs[off] == 0)
8666 		    ++off;
8667 	    if (off < end_off)		/* something to be cleared */
8668 	    {
8669 		col = off - LineOffset[row];
8670 		screen_stop_highlight();
8671 		term_windgoto(row, col);/* clear rest of this screen line */
8672 		out_str(T_CE);
8673 		screen_start();		/* don't know where cursor is now */
8674 		col = end_col - col;
8675 		while (col--)		/* clear chars in ScreenLines */
8676 		{
8677 		    space_to_screenline(off, 0);
8678 		    ++off;
8679 		}
8680 	    }
8681 	    did_delete = TRUE;		/* the chars are cleared now */
8682 	}
8683 
8684 	off = LineOffset[row] + start_col;
8685 	c = c1;
8686 	for (col = start_col; col < end_col; ++col)
8687 	{
8688 	    if (ScreenLines[off] != c
8689 #ifdef FEAT_MBYTE
8690 		    || (enc_utf8 && (int)ScreenLinesUC[off]
8691 						       != (c >= 0x80 ? c : 0))
8692 #endif
8693 		    || ScreenAttrs[off] != attr
8694 #if defined(FEAT_GUI) || defined(UNIX)
8695 		    || force_next
8696 #endif
8697 		    )
8698 	    {
8699 #if defined(FEAT_GUI) || defined(UNIX)
8700 		/* The bold trick may make a single row of pixels appear in
8701 		 * the next character.  When a bold character is removed, the
8702 		 * next character should be redrawn too.  This happens for our
8703 		 * own GUI and for some xterms.  */
8704 		if (
8705 # ifdef FEAT_GUI
8706 			gui.in_use
8707 # endif
8708 # if defined(FEAT_GUI) && defined(UNIX)
8709 			||
8710 # endif
8711 # ifdef UNIX
8712 			term_is_xterm
8713 # endif
8714 		   )
8715 		{
8716 		    if (ScreenLines[off] != ' '
8717 			    && (ScreenAttrs[off] > HL_ALL
8718 				|| ScreenAttrs[off] & HL_BOLD))
8719 			force_next = TRUE;
8720 		    else
8721 			force_next = FALSE;
8722 		}
8723 #endif
8724 		ScreenLines[off] = c;
8725 #ifdef FEAT_MBYTE
8726 		if (enc_utf8)
8727 		{
8728 		    if (c >= 0x80)
8729 		    {
8730 			ScreenLinesUC[off] = c;
8731 			ScreenLinesC[0][off] = 0;
8732 		    }
8733 		    else
8734 			ScreenLinesUC[off] = 0;
8735 		}
8736 #endif
8737 		ScreenAttrs[off] = attr;
8738 		if (!did_delete || c != ' ')
8739 		    screen_char(off, row, col);
8740 	    }
8741 	    ++off;
8742 	    if (col == start_col)
8743 	    {
8744 		if (did_delete)
8745 		    break;
8746 		c = c2;
8747 	    }
8748 	}
8749 	if (end_col == Columns)
8750 	    LineWraps[row] = FALSE;
8751 	if (row == Rows - 1)		/* overwritten the command line */
8752 	{
8753 	    redraw_cmdline = TRUE;
8754 	    if (start_col == 0 && end_col == Columns
8755 		    && c1 == ' ' && c2 == ' ' && attr == 0)
8756 		clear_cmdline = FALSE;	/* command line has been cleared */
8757 	    if (start_col == 0)
8758 		mode_displayed = FALSE; /* mode cleared or overwritten */
8759 	}
8760     }
8761 }
8762 
8763 /*
8764  * Check if there should be a delay.  Used before clearing or redrawing the
8765  * screen or the command line.
8766  */
8767     void
8768 check_for_delay(int check_msg_scroll)
8769 {
8770     if ((emsg_on_display || (check_msg_scroll && msg_scroll))
8771 	    && !did_wait_return
8772 	    && emsg_silent == 0)
8773     {
8774 	out_flush();
8775 	ui_delay(1000L, TRUE);
8776 	emsg_on_display = FALSE;
8777 	if (check_msg_scroll)
8778 	    msg_scroll = FALSE;
8779     }
8780 }
8781 
8782 /*
8783  * screen_valid -  allocate screen buffers if size changed
8784  *   If "doclear" is TRUE: clear screen if it has been resized.
8785  *	Returns TRUE if there is a valid screen to write to.
8786  *	Returns FALSE when starting up and screen not initialized yet.
8787  */
8788     int
8789 screen_valid(int doclear)
8790 {
8791     screenalloc(doclear);	   /* allocate screen buffers if size changed */
8792     return (ScreenLines != NULL);
8793 }
8794 
8795 /*
8796  * Resize the shell to Rows and Columns.
8797  * Allocate ScreenLines[] and associated items.
8798  *
8799  * There may be some time between setting Rows and Columns and (re)allocating
8800  * ScreenLines[].  This happens when starting up and when (manually) changing
8801  * the shell size.  Always use screen_Rows and screen_Columns to access items
8802  * in ScreenLines[].  Use Rows and Columns for positioning text etc. where the
8803  * final size of the shell is needed.
8804  */
8805     void
8806 screenalloc(int doclear)
8807 {
8808     int		    new_row, old_row;
8809 #ifdef FEAT_GUI
8810     int		    old_Rows;
8811 #endif
8812     win_T	    *wp;
8813     int		    outofmem = FALSE;
8814     int		    len;
8815     schar_T	    *new_ScreenLines;
8816 #ifdef FEAT_MBYTE
8817     u8char_T	    *new_ScreenLinesUC = NULL;
8818     u8char_T	    *new_ScreenLinesC[MAX_MCO];
8819     schar_T	    *new_ScreenLines2 = NULL;
8820     int		    i;
8821 #endif
8822     sattr_T	    *new_ScreenAttrs;
8823     unsigned	    *new_LineOffset;
8824     char_u	    *new_LineWraps;
8825     short	    *new_TabPageIdxs;
8826     tabpage_T	    *tp;
8827     static int	    entered = FALSE;		/* avoid recursiveness */
8828     static int	    done_outofmem_msg = FALSE;	/* did outofmem message */
8829     int		    retry_count = 0;
8830 
8831 retry:
8832     /*
8833      * Allocation of the screen buffers is done only when the size changes and
8834      * when Rows and Columns have been set and we have started doing full
8835      * screen stuff.
8836      */
8837     if ((ScreenLines != NULL
8838 		&& Rows == screen_Rows
8839 		&& Columns == screen_Columns
8840 #ifdef FEAT_MBYTE
8841 		&& enc_utf8 == (ScreenLinesUC != NULL)
8842 		&& (enc_dbcs == DBCS_JPNU) == (ScreenLines2 != NULL)
8843 		&& p_mco == Screen_mco
8844 #endif
8845 		)
8846 	    || Rows == 0
8847 	    || Columns == 0
8848 	    || (!full_screen && ScreenLines == NULL))
8849 	return;
8850 
8851     /*
8852      * It's possible that we produce an out-of-memory message below, which
8853      * will cause this function to be called again.  To break the loop, just
8854      * return here.
8855      */
8856     if (entered)
8857 	return;
8858     entered = TRUE;
8859 
8860     /*
8861      * Note that the window sizes are updated before reallocating the arrays,
8862      * thus we must not redraw here!
8863      */
8864     ++RedrawingDisabled;
8865 
8866     win_new_shellsize();    /* fit the windows in the new sized shell */
8867 
8868     comp_col();		/* recompute columns for shown command and ruler */
8869 
8870     /*
8871      * We're changing the size of the screen.
8872      * - Allocate new arrays for ScreenLines and ScreenAttrs.
8873      * - Move lines from the old arrays into the new arrays, clear extra
8874      *	 lines (unless the screen is going to be cleared).
8875      * - Free the old arrays.
8876      *
8877      * If anything fails, make ScreenLines NULL, so we don't do anything!
8878      * Continuing with the old ScreenLines may result in a crash, because the
8879      * size is wrong.
8880      */
8881     FOR_ALL_TAB_WINDOWS(tp, wp)
8882 	win_free_lsize(wp);
8883     if (aucmd_win != NULL)
8884 	win_free_lsize(aucmd_win);
8885 
8886     new_ScreenLines = (schar_T *)lalloc((long_u)(
8887 			      (Rows + 1) * Columns * sizeof(schar_T)), FALSE);
8888 #ifdef FEAT_MBYTE
8889     vim_memset(new_ScreenLinesC, 0, sizeof(u8char_T *) * MAX_MCO);
8890     if (enc_utf8)
8891     {
8892 	new_ScreenLinesUC = (u8char_T *)lalloc((long_u)(
8893 			     (Rows + 1) * Columns * sizeof(u8char_T)), FALSE);
8894 	for (i = 0; i < p_mco; ++i)
8895 	    new_ScreenLinesC[i] = (u8char_T *)lalloc_clear((long_u)(
8896 			     (Rows + 1) * Columns * sizeof(u8char_T)), FALSE);
8897     }
8898     if (enc_dbcs == DBCS_JPNU)
8899 	new_ScreenLines2 = (schar_T *)lalloc((long_u)(
8900 			     (Rows + 1) * Columns * sizeof(schar_T)), FALSE);
8901 #endif
8902     new_ScreenAttrs = (sattr_T *)lalloc((long_u)(
8903 			      (Rows + 1) * Columns * sizeof(sattr_T)), FALSE);
8904     new_LineOffset = (unsigned *)lalloc((long_u)(
8905 					 Rows * sizeof(unsigned)), FALSE);
8906     new_LineWraps = (char_u *)lalloc((long_u)(Rows * sizeof(char_u)), FALSE);
8907     new_TabPageIdxs = (short *)lalloc((long_u)(Columns * sizeof(short)), FALSE);
8908 
8909     FOR_ALL_TAB_WINDOWS(tp, wp)
8910     {
8911 	if (win_alloc_lines(wp) == FAIL)
8912 	{
8913 	    outofmem = TRUE;
8914 	    goto give_up;
8915 	}
8916     }
8917     if (aucmd_win != NULL && aucmd_win->w_lines == NULL
8918 					&& win_alloc_lines(aucmd_win) == FAIL)
8919 	outofmem = TRUE;
8920 give_up:
8921 
8922 #ifdef FEAT_MBYTE
8923     for (i = 0; i < p_mco; ++i)
8924 	if (new_ScreenLinesC[i] == NULL)
8925 	    break;
8926 #endif
8927     if (new_ScreenLines == NULL
8928 #ifdef FEAT_MBYTE
8929 	    || (enc_utf8 && (new_ScreenLinesUC == NULL || i != p_mco))
8930 	    || (enc_dbcs == DBCS_JPNU && new_ScreenLines2 == NULL)
8931 #endif
8932 	    || new_ScreenAttrs == NULL
8933 	    || new_LineOffset == NULL
8934 	    || new_LineWraps == NULL
8935 	    || new_TabPageIdxs == NULL
8936 	    || outofmem)
8937     {
8938 	if (ScreenLines != NULL || !done_outofmem_msg)
8939 	{
8940 	    /* guess the size */
8941 	    do_outofmem_msg((long_u)((Rows + 1) * Columns));
8942 
8943 	    /* Remember we did this to avoid getting outofmem messages over
8944 	     * and over again. */
8945 	    done_outofmem_msg = TRUE;
8946 	}
8947 	VIM_CLEAR(new_ScreenLines);
8948 #ifdef FEAT_MBYTE
8949 	VIM_CLEAR(new_ScreenLinesUC);
8950 	for (i = 0; i < p_mco; ++i)
8951 	    VIM_CLEAR(new_ScreenLinesC[i]);
8952 	VIM_CLEAR(new_ScreenLines2);
8953 #endif
8954 	VIM_CLEAR(new_ScreenAttrs);
8955 	VIM_CLEAR(new_LineOffset);
8956 	VIM_CLEAR(new_LineWraps);
8957 	VIM_CLEAR(new_TabPageIdxs);
8958     }
8959     else
8960     {
8961 	done_outofmem_msg = FALSE;
8962 
8963 	for (new_row = 0; new_row < Rows; ++new_row)
8964 	{
8965 	    new_LineOffset[new_row] = new_row * Columns;
8966 	    new_LineWraps[new_row] = FALSE;
8967 
8968 	    /*
8969 	     * If the screen is not going to be cleared, copy as much as
8970 	     * possible from the old screen to the new one and clear the rest
8971 	     * (used when resizing the window at the "--more--" prompt or when
8972 	     * executing an external command, for the GUI).
8973 	     */
8974 	    if (!doclear)
8975 	    {
8976 		(void)vim_memset(new_ScreenLines + new_row * Columns,
8977 				      ' ', (size_t)Columns * sizeof(schar_T));
8978 #ifdef FEAT_MBYTE
8979 		if (enc_utf8)
8980 		{
8981 		    (void)vim_memset(new_ScreenLinesUC + new_row * Columns,
8982 				       0, (size_t)Columns * sizeof(u8char_T));
8983 		    for (i = 0; i < p_mco; ++i)
8984 			(void)vim_memset(new_ScreenLinesC[i]
8985 							  + new_row * Columns,
8986 				       0, (size_t)Columns * sizeof(u8char_T));
8987 		}
8988 		if (enc_dbcs == DBCS_JPNU)
8989 		    (void)vim_memset(new_ScreenLines2 + new_row * Columns,
8990 				       0, (size_t)Columns * sizeof(schar_T));
8991 #endif
8992 		(void)vim_memset(new_ScreenAttrs + new_row * Columns,
8993 					0, (size_t)Columns * sizeof(sattr_T));
8994 		old_row = new_row + (screen_Rows - Rows);
8995 		if (old_row >= 0 && ScreenLines != NULL)
8996 		{
8997 		    if (screen_Columns < Columns)
8998 			len = screen_Columns;
8999 		    else
9000 			len = Columns;
9001 #ifdef FEAT_MBYTE
9002 		    /* When switching to utf-8 don't copy characters, they
9003 		     * may be invalid now.  Also when p_mco changes. */
9004 		    if (!(enc_utf8 && ScreenLinesUC == NULL)
9005 						       && p_mco == Screen_mco)
9006 #endif
9007 			mch_memmove(new_ScreenLines + new_LineOffset[new_row],
9008 				ScreenLines + LineOffset[old_row],
9009 				(size_t)len * sizeof(schar_T));
9010 #ifdef FEAT_MBYTE
9011 		    if (enc_utf8 && ScreenLinesUC != NULL
9012 						       && p_mco == Screen_mco)
9013 		    {
9014 			mch_memmove(new_ScreenLinesUC + new_LineOffset[new_row],
9015 				ScreenLinesUC + LineOffset[old_row],
9016 				(size_t)len * sizeof(u8char_T));
9017 			for (i = 0; i < p_mco; ++i)
9018 			    mch_memmove(new_ScreenLinesC[i]
9019 						    + new_LineOffset[new_row],
9020 				ScreenLinesC[i] + LineOffset[old_row],
9021 				(size_t)len * sizeof(u8char_T));
9022 		    }
9023 		    if (enc_dbcs == DBCS_JPNU && ScreenLines2 != NULL)
9024 			mch_memmove(new_ScreenLines2 + new_LineOffset[new_row],
9025 				ScreenLines2 + LineOffset[old_row],
9026 				(size_t)len * sizeof(schar_T));
9027 #endif
9028 		    mch_memmove(new_ScreenAttrs + new_LineOffset[new_row],
9029 			    ScreenAttrs + LineOffset[old_row],
9030 			    (size_t)len * sizeof(sattr_T));
9031 		}
9032 	    }
9033 	}
9034 	/* Use the last line of the screen for the current line. */
9035 	current_ScreenLine = new_ScreenLines + Rows * Columns;
9036     }
9037 
9038     free_screenlines();
9039 
9040     ScreenLines = new_ScreenLines;
9041 #ifdef FEAT_MBYTE
9042     ScreenLinesUC = new_ScreenLinesUC;
9043     for (i = 0; i < p_mco; ++i)
9044 	ScreenLinesC[i] = new_ScreenLinesC[i];
9045     Screen_mco = p_mco;
9046     ScreenLines2 = new_ScreenLines2;
9047 #endif
9048     ScreenAttrs = new_ScreenAttrs;
9049     LineOffset = new_LineOffset;
9050     LineWraps = new_LineWraps;
9051     TabPageIdxs = new_TabPageIdxs;
9052 
9053     /* It's important that screen_Rows and screen_Columns reflect the actual
9054      * size of ScreenLines[].  Set them before calling anything. */
9055 #ifdef FEAT_GUI
9056     old_Rows = screen_Rows;
9057 #endif
9058     screen_Rows = Rows;
9059     screen_Columns = Columns;
9060 
9061     must_redraw = CLEAR;	/* need to clear the screen later */
9062     if (doclear)
9063 	screenclear2();
9064 
9065 #ifdef FEAT_GUI
9066     else if (gui.in_use
9067 	    && !gui.starting
9068 	    && ScreenLines != NULL
9069 	    && old_Rows != Rows)
9070     {
9071 	(void)gui_redraw_block(0, 0, (int)Rows - 1, (int)Columns - 1, 0);
9072 	/*
9073 	 * Adjust the position of the cursor, for when executing an external
9074 	 * command.
9075 	 */
9076 	if (msg_row >= Rows)		/* Rows got smaller */
9077 	    msg_row = Rows - 1;		/* put cursor at last row */
9078 	else if (Rows > old_Rows)	/* Rows got bigger */
9079 	    msg_row += Rows - old_Rows; /* put cursor in same place */
9080 	if (msg_col >= Columns)		/* Columns got smaller */
9081 	    msg_col = Columns - 1;	/* put cursor at last column */
9082     }
9083 #endif
9084 
9085     entered = FALSE;
9086     --RedrawingDisabled;
9087 
9088     /*
9089      * Do not apply autocommands more than 3 times to avoid an endless loop
9090      * in case applying autocommands always changes Rows or Columns.
9091      */
9092     if (starting == 0 && ++retry_count <= 3)
9093     {
9094 	apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, FALSE, curbuf);
9095 	/* In rare cases, autocommands may have altered Rows or Columns,
9096 	 * jump back to check if we need to allocate the screen again. */
9097 	goto retry;
9098     }
9099 }
9100 
9101     void
9102 free_screenlines(void)
9103 {
9104 #ifdef FEAT_MBYTE
9105     int		i;
9106 
9107     vim_free(ScreenLinesUC);
9108     for (i = 0; i < Screen_mco; ++i)
9109 	vim_free(ScreenLinesC[i]);
9110     vim_free(ScreenLines2);
9111 #endif
9112     vim_free(ScreenLines);
9113     vim_free(ScreenAttrs);
9114     vim_free(LineOffset);
9115     vim_free(LineWraps);
9116     vim_free(TabPageIdxs);
9117 }
9118 
9119     void
9120 screenclear(void)
9121 {
9122     check_for_delay(FALSE);
9123     screenalloc(FALSE);	    /* allocate screen buffers if size changed */
9124     screenclear2();	    /* clear the screen */
9125 }
9126 
9127     static void
9128 screenclear2(void)
9129 {
9130     int	    i;
9131 
9132     if (starting == NO_SCREEN || ScreenLines == NULL
9133 #ifdef FEAT_GUI
9134 	    || (gui.in_use && gui.starting)
9135 #endif
9136 	    )
9137 	return;
9138 
9139 #ifdef FEAT_GUI
9140     if (!gui.in_use)
9141 #endif
9142 	screen_attr = -1;	/* force setting the Normal colors */
9143     screen_stop_highlight();	/* don't want highlighting here */
9144 
9145 #ifdef FEAT_CLIPBOARD
9146     /* disable selection without redrawing it */
9147     clip_scroll_selection(9999);
9148 #endif
9149 
9150     /* blank out ScreenLines */
9151     for (i = 0; i < Rows; ++i)
9152     {
9153 	lineclear(LineOffset[i], (int)Columns, 0);
9154 	LineWraps[i] = FALSE;
9155     }
9156 
9157     if (can_clear(T_CL))
9158     {
9159 	out_str(T_CL);		/* clear the display */
9160 	clear_cmdline = FALSE;
9161 	mode_displayed = FALSE;
9162     }
9163     else
9164     {
9165 	/* can't clear the screen, mark all chars with invalid attributes */
9166 	for (i = 0; i < Rows; ++i)
9167 	    lineinvalid(LineOffset[i], (int)Columns);
9168 	clear_cmdline = TRUE;
9169     }
9170 
9171     screen_cleared = TRUE;	/* can use contents of ScreenLines now */
9172 
9173     win_rest_invalid(firstwin);
9174     redraw_cmdline = TRUE;
9175     redraw_tabline = TRUE;
9176     if (must_redraw == CLEAR)	/* no need to clear again */
9177 	must_redraw = NOT_VALID;
9178     compute_cmdrow();
9179     msg_row = cmdline_row;	/* put cursor on last line for messages */
9180     msg_col = 0;
9181     screen_start();		/* don't know where cursor is now */
9182     msg_scrolled = 0;		/* can't scroll back */
9183     msg_didany = FALSE;
9184     msg_didout = FALSE;
9185 }
9186 
9187 /*
9188  * Clear one line in ScreenLines.
9189  */
9190     static void
9191 lineclear(unsigned off, int width, int attr)
9192 {
9193     (void)vim_memset(ScreenLines + off, ' ', (size_t)width * sizeof(schar_T));
9194 #ifdef FEAT_MBYTE
9195     if (enc_utf8)
9196 	(void)vim_memset(ScreenLinesUC + off, 0,
9197 					  (size_t)width * sizeof(u8char_T));
9198 #endif
9199     (void)vim_memset(ScreenAttrs + off, attr, (size_t)width * sizeof(sattr_T));
9200 }
9201 
9202 /*
9203  * Mark one line in ScreenLines invalid by setting the attributes to an
9204  * invalid value.
9205  */
9206     static void
9207 lineinvalid(unsigned off, int width)
9208 {
9209     (void)vim_memset(ScreenAttrs + off, -1, (size_t)width * sizeof(sattr_T));
9210 }
9211 
9212 /*
9213  * Copy part of a Screenline for vertically split window "wp".
9214  */
9215     static void
9216 linecopy(int to, int from, win_T *wp)
9217 {
9218     unsigned	off_to = LineOffset[to] + wp->w_wincol;
9219     unsigned	off_from = LineOffset[from] + wp->w_wincol;
9220 
9221     mch_memmove(ScreenLines + off_to, ScreenLines + off_from,
9222 	    wp->w_width * sizeof(schar_T));
9223 #ifdef FEAT_MBYTE
9224     if (enc_utf8)
9225     {
9226 	int	i;
9227 
9228 	mch_memmove(ScreenLinesUC + off_to, ScreenLinesUC + off_from,
9229 		wp->w_width * sizeof(u8char_T));
9230 	for (i = 0; i < p_mco; ++i)
9231 	    mch_memmove(ScreenLinesC[i] + off_to, ScreenLinesC[i] + off_from,
9232 		    wp->w_width * sizeof(u8char_T));
9233     }
9234     if (enc_dbcs == DBCS_JPNU)
9235 	mch_memmove(ScreenLines2 + off_to, ScreenLines2 + off_from,
9236 		wp->w_width * sizeof(schar_T));
9237 #endif
9238     mch_memmove(ScreenAttrs + off_to, ScreenAttrs + off_from,
9239 	    wp->w_width * sizeof(sattr_T));
9240 }
9241 
9242 /*
9243  * Return TRUE if clearing with term string "p" would work.
9244  * It can't work when the string is empty or it won't set the right background.
9245  */
9246     int
9247 can_clear(char_u *p)
9248 {
9249     return (*p != NUL && (t_colors <= 1
9250 #ifdef FEAT_GUI
9251 		|| gui.in_use
9252 #endif
9253 #ifdef FEAT_TERMGUICOLORS
9254 		|| (p_tgc && cterm_normal_bg_gui_color == INVALCOLOR)
9255 		|| (!p_tgc && cterm_normal_bg_color == 0)
9256 #else
9257 		|| cterm_normal_bg_color == 0
9258 #endif
9259 		|| *T_UT != NUL));
9260 }
9261 
9262 /*
9263  * Reset cursor position. Use whenever cursor was moved because of outputting
9264  * something directly to the screen (shell commands) or a terminal control
9265  * code.
9266  */
9267     void
9268 screen_start(void)
9269 {
9270     screen_cur_row = screen_cur_col = 9999;
9271 }
9272 
9273 /*
9274  * Move the cursor to position "row","col" in the screen.
9275  * This tries to find the most efficient way to move, minimizing the number of
9276  * characters sent to the terminal.
9277  */
9278     void
9279 windgoto(int row, int col)
9280 {
9281     sattr_T	    *p;
9282     int		    i;
9283     int		    plan;
9284     int		    cost;
9285     int		    wouldbe_col;
9286     int		    noinvcurs;
9287     char_u	    *bs;
9288     int		    goto_cost;
9289     int		    attr;
9290 
9291 #define GOTO_COST   7	/* assume a term_windgoto() takes about 7 chars */
9292 #define HIGHL_COST  5	/* assume unhighlight takes 5 chars */
9293 
9294 #define PLAN_LE	    1
9295 #define PLAN_CR	    2
9296 #define PLAN_NL	    3
9297 #define PLAN_WRITE  4
9298     /* Can't use ScreenLines unless initialized */
9299     if (ScreenLines == NULL)
9300 	return;
9301 
9302     if (col != screen_cur_col || row != screen_cur_row)
9303     {
9304 	/* Check for valid position. */
9305 	if (row < 0)	/* window without text lines? */
9306 	    row = 0;
9307 	if (row >= screen_Rows)
9308 	    row = screen_Rows - 1;
9309 	if (col >= screen_Columns)
9310 	    col = screen_Columns - 1;
9311 
9312 	/* check if no cursor movement is allowed in highlight mode */
9313 	if (screen_attr && *T_MS == NUL)
9314 	    noinvcurs = HIGHL_COST;
9315 	else
9316 	    noinvcurs = 0;
9317 	goto_cost = GOTO_COST + noinvcurs;
9318 
9319 	/*
9320 	 * Plan how to do the positioning:
9321 	 * 1. Use CR to move it to column 0, same row.
9322 	 * 2. Use T_LE to move it a few columns to the left.
9323 	 * 3. Use NL to move a few lines down, column 0.
9324 	 * 4. Move a few columns to the right with T_ND or by writing chars.
9325 	 *
9326 	 * Don't do this if the cursor went beyond the last column, the cursor
9327 	 * position is unknown then (some terminals wrap, some don't )
9328 	 *
9329 	 * First check if the highlighting attributes allow us to write
9330 	 * characters to move the cursor to the right.
9331 	 */
9332 	if (row >= screen_cur_row && screen_cur_col < Columns)
9333 	{
9334 	    /*
9335 	     * If the cursor is in the same row, bigger col, we can use CR
9336 	     * or T_LE.
9337 	     */
9338 	    bs = NULL;			    /* init for GCC */
9339 	    attr = screen_attr;
9340 	    if (row == screen_cur_row && col < screen_cur_col)
9341 	    {
9342 		/* "le" is preferred over "bc", because "bc" is obsolete */
9343 		if (*T_LE)
9344 		    bs = T_LE;		    /* "cursor left" */
9345 		else
9346 		    bs = T_BC;		    /* "backspace character (old) */
9347 		if (*bs)
9348 		    cost = (screen_cur_col - col) * (int)STRLEN(bs);
9349 		else
9350 		    cost = 999;
9351 		if (col + 1 < cost)	    /* using CR is less characters */
9352 		{
9353 		    plan = PLAN_CR;
9354 		    wouldbe_col = 0;
9355 		    cost = 1;		    /* CR is just one character */
9356 		}
9357 		else
9358 		{
9359 		    plan = PLAN_LE;
9360 		    wouldbe_col = col;
9361 		}
9362 		if (noinvcurs)		    /* will stop highlighting */
9363 		{
9364 		    cost += noinvcurs;
9365 		    attr = 0;
9366 		}
9367 	    }
9368 
9369 	    /*
9370 	     * If the cursor is above where we want to be, we can use CR LF.
9371 	     */
9372 	    else if (row > screen_cur_row)
9373 	    {
9374 		plan = PLAN_NL;
9375 		wouldbe_col = 0;
9376 		cost = (row - screen_cur_row) * 2;  /* CR LF */
9377 		if (noinvcurs)		    /* will stop highlighting */
9378 		{
9379 		    cost += noinvcurs;
9380 		    attr = 0;
9381 		}
9382 	    }
9383 
9384 	    /*
9385 	     * If the cursor is in the same row, smaller col, just use write.
9386 	     */
9387 	    else
9388 	    {
9389 		plan = PLAN_WRITE;
9390 		wouldbe_col = screen_cur_col;
9391 		cost = 0;
9392 	    }
9393 
9394 	    /*
9395 	     * Check if any characters that need to be written have the
9396 	     * correct attributes.  Also avoid UTF-8 characters.
9397 	     */
9398 	    i = col - wouldbe_col;
9399 	    if (i > 0)
9400 		cost += i;
9401 	    if (cost < goto_cost && i > 0)
9402 	    {
9403 		/*
9404 		 * Check if the attributes are correct without additionally
9405 		 * stopping highlighting.
9406 		 */
9407 		p = ScreenAttrs + LineOffset[row] + wouldbe_col;
9408 		while (i && *p++ == attr)
9409 		    --i;
9410 		if (i != 0)
9411 		{
9412 		    /*
9413 		     * Try if it works when highlighting is stopped here.
9414 		     */
9415 		    if (*--p == 0)
9416 		    {
9417 			cost += noinvcurs;
9418 			while (i && *p++ == 0)
9419 			    --i;
9420 		    }
9421 		    if (i != 0)
9422 			cost = 999;	/* different attributes, don't do it */
9423 		}
9424 #ifdef FEAT_MBYTE
9425 		if (enc_utf8)
9426 		{
9427 		    /* Don't use an UTF-8 char for positioning, it's slow. */
9428 		    for (i = wouldbe_col; i < col; ++i)
9429 			if (ScreenLinesUC[LineOffset[row] + i] != 0)
9430 			{
9431 			    cost = 999;
9432 			    break;
9433 			}
9434 		}
9435 #endif
9436 	    }
9437 
9438 	    /*
9439 	     * We can do it without term_windgoto()!
9440 	     */
9441 	    if (cost < goto_cost)
9442 	    {
9443 		if (plan == PLAN_LE)
9444 		{
9445 		    if (noinvcurs)
9446 			screen_stop_highlight();
9447 		    while (screen_cur_col > col)
9448 		    {
9449 			out_str(bs);
9450 			--screen_cur_col;
9451 		    }
9452 		}
9453 		else if (plan == PLAN_CR)
9454 		{
9455 		    if (noinvcurs)
9456 			screen_stop_highlight();
9457 		    out_char('\r');
9458 		    screen_cur_col = 0;
9459 		}
9460 		else if (plan == PLAN_NL)
9461 		{
9462 		    if (noinvcurs)
9463 			screen_stop_highlight();
9464 		    while (screen_cur_row < row)
9465 		    {
9466 			out_char('\n');
9467 			++screen_cur_row;
9468 		    }
9469 		    screen_cur_col = 0;
9470 		}
9471 
9472 		i = col - screen_cur_col;
9473 		if (i > 0)
9474 		{
9475 		    /*
9476 		     * Use cursor-right if it's one character only.  Avoids
9477 		     * removing a line of pixels from the last bold char, when
9478 		     * using the bold trick in the GUI.
9479 		     */
9480 		    if (T_ND[0] != NUL && T_ND[1] == NUL)
9481 		    {
9482 			while (i-- > 0)
9483 			    out_char(*T_ND);
9484 		    }
9485 		    else
9486 		    {
9487 			int	off;
9488 
9489 			off = LineOffset[row] + screen_cur_col;
9490 			while (i-- > 0)
9491 			{
9492 			    if (ScreenAttrs[off] != screen_attr)
9493 				screen_stop_highlight();
9494 #ifdef FEAT_MBYTE
9495 			    out_flush_check();
9496 #endif
9497 			    out_char(ScreenLines[off]);
9498 #ifdef FEAT_MBYTE
9499 			    if (enc_dbcs == DBCS_JPNU
9500 						  && ScreenLines[off] == 0x8e)
9501 				out_char(ScreenLines2[off]);
9502 #endif
9503 			    ++off;
9504 			}
9505 		    }
9506 		}
9507 	    }
9508 	}
9509 	else
9510 	    cost = 999;
9511 
9512 	if (cost >= goto_cost)
9513 	{
9514 	    if (noinvcurs)
9515 		screen_stop_highlight();
9516 	    if (row == screen_cur_row && (col > screen_cur_col)
9517 							     && *T_CRI != NUL)
9518 		term_cursor_right(col - screen_cur_col);
9519 	    else
9520 		term_windgoto(row, col);
9521 	}
9522 	screen_cur_row = row;
9523 	screen_cur_col = col;
9524     }
9525 }
9526 
9527 /*
9528  * Set cursor to its position in the current window.
9529  */
9530     void
9531 setcursor(void)
9532 {
9533     setcursor_mayforce(FALSE);
9534 }
9535 
9536 /*
9537  * Set cursor to its position in the current window.
9538  * When "force" is TRUE also when not redrawing.
9539  */
9540     void
9541 setcursor_mayforce(int force)
9542 {
9543     if (force || redrawing())
9544     {
9545 	validate_cursor();
9546 	windgoto(W_WINROW(curwin) + curwin->w_wrow,
9547 		curwin->w_wincol + (
9548 #ifdef FEAT_RIGHTLEFT
9549 		/* With 'rightleft' set and the cursor on a double-wide
9550 		 * character, position it on the leftmost column. */
9551 		curwin->w_p_rl ? ((int)curwin->w_width - curwin->w_wcol - (
9552 # ifdef FEAT_MBYTE
9553 			(has_mbyte
9554 			   && (*mb_ptr2cells)(ml_get_cursor()) == 2
9555 			   && vim_isprintc(gchar_cursor())) ? 2 :
9556 # endif
9557 			1)) :
9558 #endif
9559 							    curwin->w_wcol));
9560     }
9561 }
9562 
9563 
9564 /*
9565  * Insert 'line_count' lines at 'row' in window 'wp'.
9566  * If 'invalid' is TRUE the wp->w_lines[].wl_lnum is invalidated.
9567  * If 'mayclear' is TRUE the screen will be cleared if it is faster than
9568  * scrolling.
9569  * Returns FAIL if the lines are not inserted, OK for success.
9570  */
9571     int
9572 win_ins_lines(
9573     win_T	*wp,
9574     int		row,
9575     int		line_count,
9576     int		invalid,
9577     int		mayclear)
9578 {
9579     int		did_delete;
9580     int		nextrow;
9581     int		lastrow;
9582     int		retval;
9583 
9584     if (invalid)
9585 	wp->w_lines_valid = 0;
9586 
9587     if (wp->w_height < 5)
9588 	return FAIL;
9589 
9590     if (line_count > wp->w_height - row)
9591 	line_count = wp->w_height - row;
9592 
9593     retval = win_do_lines(wp, row, line_count, mayclear, FALSE, 0);
9594     if (retval != MAYBE)
9595 	return retval;
9596 
9597     /*
9598      * If there is a next window or a status line, we first try to delete the
9599      * lines at the bottom to avoid messing what is after the window.
9600      * If this fails and there are following windows, don't do anything to avoid
9601      * messing up those windows, better just redraw.
9602      */
9603     did_delete = FALSE;
9604     if (wp->w_next != NULL || wp->w_status_height)
9605     {
9606 	if (screen_del_lines(0, W_WINROW(wp) + wp->w_height - line_count,
9607 				  line_count, (int)Rows, FALSE, 0, NULL) == OK)
9608 	    did_delete = TRUE;
9609 	else if (wp->w_next)
9610 	    return FAIL;
9611     }
9612     /*
9613      * if no lines deleted, blank the lines that will end up below the window
9614      */
9615     if (!did_delete)
9616     {
9617 	wp->w_redr_status = TRUE;
9618 	redraw_cmdline = TRUE;
9619 	nextrow = W_WINROW(wp) + wp->w_height + wp->w_status_height;
9620 	lastrow = nextrow + line_count;
9621 	if (lastrow > Rows)
9622 	    lastrow = Rows;
9623 	screen_fill(nextrow - line_count, lastrow - line_count,
9624 		  wp->w_wincol, (int)W_ENDCOL(wp),
9625 		  ' ', ' ', 0);
9626     }
9627 
9628     if (screen_ins_lines(0, W_WINROW(wp) + row, line_count, (int)Rows, 0, NULL)
9629 								      == FAIL)
9630     {
9631 	    /* deletion will have messed up other windows */
9632 	if (did_delete)
9633 	{
9634 	    wp->w_redr_status = TRUE;
9635 	    win_rest_invalid(W_NEXT(wp));
9636 	}
9637 	return FAIL;
9638     }
9639 
9640     return OK;
9641 }
9642 
9643 /*
9644  * Delete "line_count" window lines at "row" in window "wp".
9645  * If "invalid" is TRUE curwin->w_lines[] is invalidated.
9646  * If "mayclear" is TRUE the screen will be cleared if it is faster than
9647  * scrolling
9648  * Return OK for success, FAIL if the lines are not deleted.
9649  */
9650     int
9651 win_del_lines(
9652     win_T	*wp,
9653     int		row,
9654     int		line_count,
9655     int		invalid,
9656     int		mayclear,
9657     int		clear_attr)	    /* for clearing lines */
9658 {
9659     int		retval;
9660 
9661     if (invalid)
9662 	wp->w_lines_valid = 0;
9663 
9664     if (line_count > wp->w_height - row)
9665 	line_count = wp->w_height - row;
9666 
9667     retval = win_do_lines(wp, row, line_count, mayclear, TRUE, clear_attr);
9668     if (retval != MAYBE)
9669 	return retval;
9670 
9671     if (screen_del_lines(0, W_WINROW(wp) + row, line_count,
9672 				   (int)Rows, FALSE, clear_attr, NULL) == FAIL)
9673 	return FAIL;
9674 
9675     /*
9676      * If there are windows or status lines below, try to put them at the
9677      * correct place. If we can't do that, they have to be redrawn.
9678      */
9679     if (wp->w_next || wp->w_status_height || cmdline_row < Rows - 1)
9680     {
9681 	if (screen_ins_lines(0, W_WINROW(wp) + wp->w_height - line_count,
9682 			      line_count, (int)Rows, clear_attr, NULL) == FAIL)
9683 	{
9684 	    wp->w_redr_status = TRUE;
9685 	    win_rest_invalid(wp->w_next);
9686 	}
9687     }
9688     /*
9689      * If this is the last window and there is no status line, redraw the
9690      * command line later.
9691      */
9692     else
9693 	redraw_cmdline = TRUE;
9694     return OK;
9695 }
9696 
9697 /*
9698  * Common code for win_ins_lines() and win_del_lines().
9699  * Returns OK or FAIL when the work has been done.
9700  * Returns MAYBE when not finished yet.
9701  */
9702     static int
9703 win_do_lines(
9704     win_T	*wp,
9705     int		row,
9706     int		line_count,
9707     int		mayclear,
9708     int		del,
9709     int		clear_attr)
9710 {
9711     int		retval;
9712 
9713     if (!redrawing() || line_count <= 0)
9714 	return FAIL;
9715 
9716     /* When inserting lines would result in loss of command output, just redraw
9717      * the lines. */
9718     if (no_win_do_lines_ins && !del)
9719 	return FAIL;
9720 
9721     /* only a few lines left: redraw is faster */
9722     if (mayclear && Rows - line_count < 5 && wp->w_width == Columns)
9723     {
9724 	if (!no_win_do_lines_ins)
9725 	    screenclear();	    /* will set wp->w_lines_valid to 0 */
9726 	return FAIL;
9727     }
9728 
9729     /*
9730      * Delete all remaining lines
9731      */
9732     if (row + line_count >= wp->w_height)
9733     {
9734 	screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + wp->w_height,
9735 		wp->w_wincol, (int)W_ENDCOL(wp),
9736 		' ', ' ', 0);
9737 	return OK;
9738     }
9739 
9740     /*
9741      * When scrolling, the message on the command line should be cleared,
9742      * otherwise it will stay there forever.
9743      * Don't do this when avoiding to insert lines.
9744      */
9745     if (!no_win_do_lines_ins)
9746 	clear_cmdline = TRUE;
9747 
9748     /*
9749      * If the terminal can set a scroll region, use that.
9750      * Always do this in a vertically split window.  This will redraw from
9751      * ScreenLines[] when t_CV isn't defined.  That's faster than using
9752      * win_line().
9753      * Don't use a scroll region when we are going to redraw the text, writing
9754      * a character in the lower right corner of the scroll region may cause a
9755      * scroll-up .
9756      */
9757     if (scroll_region || wp->w_width != Columns)
9758     {
9759 	if (scroll_region && (wp->w_width == Columns || *T_CSV != NUL))
9760 	    scroll_region_set(wp, row);
9761 	if (del)
9762 	    retval = screen_del_lines(W_WINROW(wp) + row, 0, line_count,
9763 				    wp->w_height - row, FALSE, clear_attr, wp);
9764 	else
9765 	    retval = screen_ins_lines(W_WINROW(wp) + row, 0, line_count,
9766 					   wp->w_height - row, clear_attr, wp);
9767 	if (scroll_region && (wp->w_width == Columns || *T_CSV != NUL))
9768 	    scroll_region_reset();
9769 	return retval;
9770     }
9771 
9772     if (wp->w_next != NULL && p_tf) /* don't delete/insert on fast terminal */
9773 	return FAIL;
9774 
9775     return MAYBE;
9776 }
9777 
9778 /*
9779  * window 'wp' and everything after it is messed up, mark it for redraw
9780  */
9781     static void
9782 win_rest_invalid(win_T *wp)
9783 {
9784     while (wp != NULL)
9785     {
9786 	redraw_win_later(wp, NOT_VALID);
9787 	wp->w_redr_status = TRUE;
9788 	wp = wp->w_next;
9789     }
9790     redraw_cmdline = TRUE;
9791 }
9792 
9793 /*
9794  * The rest of the routines in this file perform screen manipulations. The
9795  * given operation is performed physically on the screen. The corresponding
9796  * change is also made to the internal screen image. In this way, the editor
9797  * anticipates the effect of editing changes on the appearance of the screen.
9798  * That way, when we call screenupdate a complete redraw isn't usually
9799  * necessary. Another advantage is that we can keep adding code to anticipate
9800  * screen changes, and in the meantime, everything still works.
9801  */
9802 
9803 /*
9804  * types for inserting or deleting lines
9805  */
9806 #define USE_T_CAL   1
9807 #define USE_T_CDL   2
9808 #define USE_T_AL    3
9809 #define USE_T_CE    4
9810 #define USE_T_DL    5
9811 #define USE_T_SR    6
9812 #define USE_NL	    7
9813 #define USE_T_CD    8
9814 #define USE_REDRAW  9
9815 
9816 /*
9817  * insert lines on the screen and update ScreenLines[]
9818  * 'end' is the line after the scrolled part. Normally it is Rows.
9819  * When scrolling region used 'off' is the offset from the top for the region.
9820  * 'row' and 'end' are relative to the start of the region.
9821  *
9822  * return FAIL for failure, OK for success.
9823  */
9824     int
9825 screen_ins_lines(
9826     int		off,
9827     int		row,
9828     int		line_count,
9829     int		end,
9830     int		clear_attr,
9831     win_T	*wp)	    /* NULL or window to use width from */
9832 {
9833     int		i;
9834     int		j;
9835     unsigned	temp;
9836     int		cursor_row;
9837     int		cursor_col = 0;
9838     int		type;
9839     int		result_empty;
9840     int		can_ce = can_clear(T_CE);
9841 
9842     /*
9843      * FAIL if
9844      * - there is no valid screen
9845      * - the screen has to be redrawn completely
9846      * - the line count is less than one
9847      * - the line count is more than 'ttyscroll'
9848      * - redrawing for a callback and there is a modeless selection
9849      */
9850      if (!screen_valid(TRUE) || line_count <= 0 || line_count > p_ttyscroll
9851 #ifdef FEAT_CLIPBOARD
9852 	     || (clip_star.state != SELECT_CLEARED
9853 						 && redrawing_for_callback > 0)
9854 #endif
9855 	     )
9856 	return FAIL;
9857 
9858     /*
9859      * There are seven ways to insert lines:
9860      * 0. When in a vertically split window and t_CV isn't set, redraw the
9861      *    characters from ScreenLines[].
9862      * 1. Use T_CD (clear to end of display) if it exists and the result of
9863      *	  the insert is just empty lines
9864      * 2. Use T_CAL (insert multiple lines) if it exists and T_AL is not
9865      *	  present or line_count > 1. It looks better if we do all the inserts
9866      *	  at once.
9867      * 3. Use T_CDL (delete multiple lines) if it exists and the result of the
9868      *	  insert is just empty lines and T_CE is not present or line_count >
9869      *	  1.
9870      * 4. Use T_AL (insert line) if it exists.
9871      * 5. Use T_CE (erase line) if it exists and the result of the insert is
9872      *	  just empty lines.
9873      * 6. Use T_DL (delete line) if it exists and the result of the insert is
9874      *	  just empty lines.
9875      * 7. Use T_SR (scroll reverse) if it exists and inserting at row 0 and
9876      *	  the 'da' flag is not set or we have clear line capability.
9877      * 8. redraw the characters from ScreenLines[].
9878      *
9879      * Careful: In a hpterm scroll reverse doesn't work as expected, it moves
9880      * the scrollbar for the window. It does have insert line, use that if it
9881      * exists.
9882      */
9883     result_empty = (row + line_count >= end);
9884     if (wp != NULL && wp->w_width != Columns && *T_CSV == NUL)
9885 	type = USE_REDRAW;
9886     else if (can_clear(T_CD) && result_empty)
9887 	type = USE_T_CD;
9888     else if (*T_CAL != NUL && (line_count > 1 || *T_AL == NUL))
9889 	type = USE_T_CAL;
9890     else if (*T_CDL != NUL && result_empty && (line_count > 1 || !can_ce))
9891 	type = USE_T_CDL;
9892     else if (*T_AL != NUL)
9893 	type = USE_T_AL;
9894     else if (can_ce && result_empty)
9895 	type = USE_T_CE;
9896     else if (*T_DL != NUL && result_empty)
9897 	type = USE_T_DL;
9898     else if (*T_SR != NUL && row == 0 && (*T_DA == NUL || can_ce))
9899 	type = USE_T_SR;
9900     else
9901 	return FAIL;
9902 
9903     /*
9904      * For clearing the lines screen_del_lines() is used. This will also take
9905      * care of t_db if necessary.
9906      */
9907     if (type == USE_T_CD || type == USE_T_CDL ||
9908 					 type == USE_T_CE || type == USE_T_DL)
9909 	return screen_del_lines(off, row, line_count, end, FALSE, 0, wp);
9910 
9911     /*
9912      * If text is retained below the screen, first clear or delete as many
9913      * lines at the bottom of the window as are about to be inserted so that
9914      * the deleted lines won't later surface during a screen_del_lines.
9915      */
9916     if (*T_DB)
9917 	screen_del_lines(off, end - line_count, line_count, end, FALSE, 0, wp);
9918 
9919 #ifdef FEAT_CLIPBOARD
9920     /* Remove a modeless selection when inserting lines halfway the screen
9921      * or not the full width of the screen. */
9922     if (off + row > 0 || (wp != NULL && wp->w_width != Columns))
9923 	clip_clear_selection(&clip_star);
9924     else
9925 	clip_scroll_selection(-line_count);
9926 #endif
9927 
9928 #ifdef FEAT_GUI
9929     /* Don't update the GUI cursor here, ScreenLines[] is invalid until the
9930      * scrolling is actually carried out. */
9931     gui_dont_update_cursor(row + off <= gui.cursor_row);
9932 #endif
9933 
9934     if (wp != NULL && wp->w_wincol != 0 && *T_CSV != NUL && *T_CCS == NUL)
9935 	cursor_col = wp->w_wincol;
9936 
9937     if (*T_CCS != NUL)	   /* cursor relative to region */
9938 	cursor_row = row;
9939     else
9940 	cursor_row = row + off;
9941 
9942     /*
9943      * Shift LineOffset[] line_count down to reflect the inserted lines.
9944      * Clear the inserted lines in ScreenLines[].
9945      */
9946     row += off;
9947     end += off;
9948     for (i = 0; i < line_count; ++i)
9949     {
9950 	if (wp != NULL && wp->w_width != Columns)
9951 	{
9952 	    /* need to copy part of a line */
9953 	    j = end - 1 - i;
9954 	    while ((j -= line_count) >= row)
9955 		linecopy(j + line_count, j, wp);
9956 	    j += line_count;
9957 	    if (can_clear((char_u *)" "))
9958 		lineclear(LineOffset[j] + wp->w_wincol, wp->w_width,
9959 								   clear_attr);
9960 	    else
9961 		lineinvalid(LineOffset[j] + wp->w_wincol, wp->w_width);
9962 	    LineWraps[j] = FALSE;
9963 	}
9964 	else
9965 	{
9966 	    j = end - 1 - i;
9967 	    temp = LineOffset[j];
9968 	    while ((j -= line_count) >= row)
9969 	    {
9970 		LineOffset[j + line_count] = LineOffset[j];
9971 		LineWraps[j + line_count] = LineWraps[j];
9972 	    }
9973 	    LineOffset[j + line_count] = temp;
9974 	    LineWraps[j + line_count] = FALSE;
9975 	    if (can_clear((char_u *)" "))
9976 		lineclear(temp, (int)Columns, clear_attr);
9977 	    else
9978 		lineinvalid(temp, (int)Columns);
9979 	}
9980     }
9981 
9982     screen_stop_highlight();
9983     windgoto(cursor_row, cursor_col);
9984     if (clear_attr != 0)
9985 	screen_start_highlight(clear_attr);
9986 
9987     /* redraw the characters */
9988     if (type == USE_REDRAW)
9989 	redraw_block(row, end, wp);
9990     else if (type == USE_T_CAL)
9991     {
9992 	term_append_lines(line_count);
9993 	screen_start();		/* don't know where cursor is now */
9994     }
9995     else
9996     {
9997 	for (i = 0; i < line_count; i++)
9998 	{
9999 	    if (type == USE_T_AL)
10000 	    {
10001 		if (i && cursor_row != 0)
10002 		    windgoto(cursor_row, cursor_col);
10003 		out_str(T_AL);
10004 	    }
10005 	    else  /* type == USE_T_SR */
10006 		out_str(T_SR);
10007 	    screen_start();	    /* don't know where cursor is now */
10008 	}
10009     }
10010 
10011     /*
10012      * With scroll-reverse and 'da' flag set we need to clear the lines that
10013      * have been scrolled down into the region.
10014      */
10015     if (type == USE_T_SR && *T_DA)
10016     {
10017 	for (i = 0; i < line_count; ++i)
10018 	{
10019 	    windgoto(off + i, cursor_col);
10020 	    out_str(T_CE);
10021 	    screen_start();	    /* don't know where cursor is now */
10022 	}
10023     }
10024 
10025 #ifdef FEAT_GUI
10026     gui_can_update_cursor();
10027     if (gui.in_use)
10028 	out_flush();	/* always flush after a scroll */
10029 #endif
10030     return OK;
10031 }
10032 
10033 /*
10034  * Delete lines on the screen and update ScreenLines[].
10035  * "end" is the line after the scrolled part. Normally it is Rows.
10036  * When scrolling region used "off" is the offset from the top for the region.
10037  * "row" and "end" are relative to the start of the region.
10038  *
10039  * Return OK for success, FAIL if the lines are not deleted.
10040  */
10041     int
10042 screen_del_lines(
10043     int		off,
10044     int		row,
10045     int		line_count,
10046     int		end,
10047     int		force,		/* even when line_count > p_ttyscroll */
10048     int		clear_attr,	/* used for clearing lines */
10049     win_T	*wp UNUSED)	/* NULL or window to use width from */
10050 {
10051     int		j;
10052     int		i;
10053     unsigned	temp;
10054     int		cursor_row;
10055     int		cursor_col = 0;
10056     int		cursor_end;
10057     int		result_empty;	/* result is empty until end of region */
10058     int		can_delete;	/* deleting line codes can be used */
10059     int		type;
10060 
10061     /*
10062      * FAIL if
10063      * - there is no valid screen
10064      * - the screen has to be redrawn completely
10065      * - the line count is less than one
10066      * - the line count is more than 'ttyscroll'
10067      * - redrawing for a callback and there is a modeless selection
10068      */
10069     if (!screen_valid(TRUE) || line_count <= 0
10070 					|| (!force && line_count > p_ttyscroll)
10071 #ifdef FEAT_CLIPBOARD
10072 	     || (clip_star.state != SELECT_CLEARED
10073 						 && redrawing_for_callback > 0)
10074 #endif
10075        )
10076 	return FAIL;
10077 
10078     /*
10079      * Check if the rest of the current region will become empty.
10080      */
10081     result_empty = row + line_count >= end;
10082 
10083     /*
10084      * We can delete lines only when 'db' flag not set or when 'ce' option
10085      * available.
10086      */
10087     can_delete = (*T_DB == NUL || can_clear(T_CE));
10088 
10089     /*
10090      * There are six ways to delete lines:
10091      * 0. When in a vertically split window and t_CV isn't set, redraw the
10092      *    characters from ScreenLines[].
10093      * 1. Use T_CD if it exists and the result is empty.
10094      * 2. Use newlines if row == 0 and count == 1 or T_CDL does not exist.
10095      * 3. Use T_CDL (delete multiple lines) if it exists and line_count > 1 or
10096      *	  none of the other ways work.
10097      * 4. Use T_CE (erase line) if the result is empty.
10098      * 5. Use T_DL (delete line) if it exists.
10099      * 6. redraw the characters from ScreenLines[].
10100      */
10101     if (wp != NULL && wp->w_width != Columns && *T_CSV == NUL)
10102 	type = USE_REDRAW;
10103     else if (can_clear(T_CD) && result_empty)
10104 	type = USE_T_CD;
10105 #if defined(__BEOS__) && defined(BEOS_DR8)
10106     /*
10107      * USE_NL does not seem to work in Terminal of DR8 so we set T_DB="" in
10108      * its internal termcap... this works okay for tests which test *T_DB !=
10109      * NUL.  It has the disadvantage that the user cannot use any :set t_*
10110      * command to get T_DB (back) to empty_option, only :set term=... will do
10111      * the trick...
10112      * Anyway, this hack will hopefully go away with the next OS release.
10113      * (Olaf Seibert)
10114      */
10115     else if (row == 0 && T_DB == empty_option
10116 					&& (line_count == 1 || *T_CDL == NUL))
10117 #else
10118     else if (row == 0 && (
10119 #ifndef AMIGA
10120 	/* On the Amiga, somehow '\n' on the last line doesn't always scroll
10121 	 * up, so use delete-line command */
10122 			    line_count == 1 ||
10123 #endif
10124 						*T_CDL == NUL))
10125 #endif
10126 	type = USE_NL;
10127     else if (*T_CDL != NUL && line_count > 1 && can_delete)
10128 	type = USE_T_CDL;
10129     else if (can_clear(T_CE) && result_empty
10130 	    && (wp == NULL || wp->w_width == Columns))
10131 	type = USE_T_CE;
10132     else if (*T_DL != NUL && can_delete)
10133 	type = USE_T_DL;
10134     else if (*T_CDL != NUL && can_delete)
10135 	type = USE_T_CDL;
10136     else
10137 	return FAIL;
10138 
10139 #ifdef FEAT_CLIPBOARD
10140     /* Remove a modeless selection when deleting lines halfway the screen or
10141      * not the full width of the screen. */
10142     if (off + row > 0 || (wp != NULL && wp->w_width != Columns))
10143 	clip_clear_selection(&clip_star);
10144     else
10145 	clip_scroll_selection(line_count);
10146 #endif
10147 
10148 #ifdef FEAT_GUI
10149     /* Don't update the GUI cursor here, ScreenLines[] is invalid until the
10150      * scrolling is actually carried out. */
10151     gui_dont_update_cursor(gui.cursor_row >= row + off
10152 						&& gui.cursor_row < end + off);
10153 #endif
10154 
10155     if (wp != NULL && wp->w_wincol != 0 && *T_CSV != NUL && *T_CCS == NUL)
10156 	cursor_col = wp->w_wincol;
10157 
10158     if (*T_CCS != NUL)	    /* cursor relative to region */
10159     {
10160 	cursor_row = row;
10161 	cursor_end = end;
10162     }
10163     else
10164     {
10165 	cursor_row = row + off;
10166 	cursor_end = end + off;
10167     }
10168 
10169     /*
10170      * Now shift LineOffset[] line_count up to reflect the deleted lines.
10171      * Clear the inserted lines in ScreenLines[].
10172      */
10173     row += off;
10174     end += off;
10175     for (i = 0; i < line_count; ++i)
10176     {
10177 	if (wp != NULL && wp->w_width != Columns)
10178 	{
10179 	    /* need to copy part of a line */
10180 	    j = row + i;
10181 	    while ((j += line_count) <= end - 1)
10182 		linecopy(j - line_count, j, wp);
10183 	    j -= line_count;
10184 	    if (can_clear((char_u *)" "))
10185 		lineclear(LineOffset[j] + wp->w_wincol, wp->w_width,
10186 								   clear_attr);
10187 	    else
10188 		lineinvalid(LineOffset[j] + wp->w_wincol, wp->w_width);
10189 	    LineWraps[j] = FALSE;
10190 	}
10191 	else
10192 	{
10193 	    /* whole width, moving the line pointers is faster */
10194 	    j = row + i;
10195 	    temp = LineOffset[j];
10196 	    while ((j += line_count) <= end - 1)
10197 	    {
10198 		LineOffset[j - line_count] = LineOffset[j];
10199 		LineWraps[j - line_count] = LineWraps[j];
10200 	    }
10201 	    LineOffset[j - line_count] = temp;
10202 	    LineWraps[j - line_count] = FALSE;
10203 	    if (can_clear((char_u *)" "))
10204 		lineclear(temp, (int)Columns, clear_attr);
10205 	    else
10206 		lineinvalid(temp, (int)Columns);
10207 	}
10208     }
10209 
10210     if (screen_attr != clear_attr)
10211 	screen_stop_highlight();
10212     if (clear_attr != 0)
10213 	screen_start_highlight(clear_attr);
10214 
10215     /* redraw the characters */
10216     if (type == USE_REDRAW)
10217 	redraw_block(row, end, wp);
10218     else if (type == USE_T_CD)	/* delete the lines */
10219     {
10220 	windgoto(cursor_row, cursor_col);
10221 	out_str(T_CD);
10222 	screen_start();			/* don't know where cursor is now */
10223     }
10224     else if (type == USE_T_CDL)
10225     {
10226 	windgoto(cursor_row, cursor_col);
10227 	term_delete_lines(line_count);
10228 	screen_start();			/* don't know where cursor is now */
10229     }
10230     /*
10231      * Deleting lines at top of the screen or scroll region: Just scroll
10232      * the whole screen (scroll region) up by outputting newlines on the
10233      * last line.
10234      */
10235     else if (type == USE_NL)
10236     {
10237 	windgoto(cursor_end - 1, cursor_col);
10238 	for (i = line_count; --i >= 0; )
10239 	    out_char('\n');		/* cursor will remain on same line */
10240     }
10241     else
10242     {
10243 	for (i = line_count; --i >= 0; )
10244 	{
10245 	    if (type == USE_T_DL)
10246 	    {
10247 		windgoto(cursor_row, cursor_col);
10248 		out_str(T_DL);		/* delete a line */
10249 	    }
10250 	    else /* type == USE_T_CE */
10251 	    {
10252 		windgoto(cursor_row + i, cursor_col);
10253 		out_str(T_CE);		/* erase a line */
10254 	    }
10255 	    screen_start();		/* don't know where cursor is now */
10256 	}
10257     }
10258 
10259     /*
10260      * If the 'db' flag is set, we need to clear the lines that have been
10261      * scrolled up at the bottom of the region.
10262      */
10263     if (*T_DB && (type == USE_T_DL || type == USE_T_CDL))
10264     {
10265 	for (i = line_count; i > 0; --i)
10266 	{
10267 	    windgoto(cursor_end - i, cursor_col);
10268 	    out_str(T_CE);		/* erase a line */
10269 	    screen_start();		/* don't know where cursor is now */
10270 	}
10271     }
10272 
10273 #ifdef FEAT_GUI
10274     gui_can_update_cursor();
10275     if (gui.in_use)
10276 	out_flush();	/* always flush after a scroll */
10277 #endif
10278 
10279     return OK;
10280 }
10281 
10282 /*
10283  * Show the current mode and ruler.
10284  *
10285  * If clear_cmdline is TRUE, clear the rest of the cmdline.
10286  * If clear_cmdline is FALSE there may be a message there that needs to be
10287  * cleared only if a mode is shown.
10288  * Return the length of the message (0 if no message).
10289  */
10290     int
10291 showmode(void)
10292 {
10293     int		need_clear;
10294     int		length = 0;
10295     int		do_mode;
10296     int		attr;
10297     int		nwr_save;
10298 #ifdef FEAT_INS_EXPAND
10299     int		sub_attr;
10300 #endif
10301 
10302     do_mode = ((p_smd && msg_silent == 0)
10303 	    && ((State & INSERT)
10304 		|| restart_edit != NUL
10305 		|| VIsual_active));
10306     if (do_mode || reg_recording != 0)
10307     {
10308 	/*
10309 	 * Don't show mode right now, when not redrawing or inside a mapping.
10310 	 * Call char_avail() only when we are going to show something, because
10311 	 * it takes a bit of time.
10312 	 */
10313 	if (!redrawing() || (char_avail() && !KeyTyped) || msg_silent != 0)
10314 	{
10315 	    redraw_cmdline = TRUE;		/* show mode later */
10316 	    return 0;
10317 	}
10318 
10319 	nwr_save = need_wait_return;
10320 
10321 	/* wait a bit before overwriting an important message */
10322 	check_for_delay(FALSE);
10323 
10324 	/* if the cmdline is more than one line high, erase top lines */
10325 	need_clear = clear_cmdline;
10326 	if (clear_cmdline && cmdline_row < Rows - 1)
10327 	    msg_clr_cmdline();			/* will reset clear_cmdline */
10328 
10329 	/* Position on the last line in the window, column 0 */
10330 	msg_pos_mode();
10331 	cursor_off();
10332 	attr = HL_ATTR(HLF_CM);			/* Highlight mode */
10333 	if (do_mode)
10334 	{
10335 	    MSG_PUTS_ATTR("--", attr);
10336 #if defined(FEAT_XIM)
10337 	    if (
10338 # ifdef FEAT_GUI_GTK
10339 		    preedit_get_status()
10340 # else
10341 		    im_get_status()
10342 # endif
10343 	       )
10344 # ifdef FEAT_GUI_GTK /* most of the time, it's not XIM being used */
10345 		MSG_PUTS_ATTR(" IM", attr);
10346 # else
10347 		MSG_PUTS_ATTR(" XIM", attr);
10348 # endif
10349 #endif
10350 #if defined(FEAT_HANGULIN) && defined(FEAT_GUI)
10351 	    if (gui.in_use)
10352 	    {
10353 		if (hangul_input_state_get())
10354 		{
10355 		    /* HANGUL */
10356 		    if (enc_utf8)
10357 			MSG_PUTS_ATTR(" \355\225\234\352\270\200", attr);
10358 		    else
10359 			MSG_PUTS_ATTR(" \307\321\261\333", attr);
10360 		}
10361 	    }
10362 #endif
10363 #ifdef FEAT_INS_EXPAND
10364 	    /* CTRL-X in Insert mode */
10365 	    if (edit_submode != NULL && !shortmess(SHM_COMPLETIONMENU))
10366 	    {
10367 		/* These messages can get long, avoid a wrap in a narrow
10368 		 * window.  Prefer showing edit_submode_extra. */
10369 		length = (Rows - msg_row) * Columns - 3;
10370 		if (edit_submode_extra != NULL)
10371 		    length -= vim_strsize(edit_submode_extra);
10372 		if (length > 0)
10373 		{
10374 		    if (edit_submode_pre != NULL)
10375 			length -= vim_strsize(edit_submode_pre);
10376 		    if (length - vim_strsize(edit_submode) > 0)
10377 		    {
10378 			if (edit_submode_pre != NULL)
10379 			    msg_puts_attr(edit_submode_pre, attr);
10380 			msg_puts_attr(edit_submode, attr);
10381 		    }
10382 		    if (edit_submode_extra != NULL)
10383 		    {
10384 			MSG_PUTS_ATTR(" ", attr);  /* add a space in between */
10385 			if ((int)edit_submode_highl < (int)HLF_COUNT)
10386 			    sub_attr = HL_ATTR(edit_submode_highl);
10387 			else
10388 			    sub_attr = attr;
10389 			msg_puts_attr(edit_submode_extra, sub_attr);
10390 		    }
10391 		}
10392 	    }
10393 	    else
10394 #endif
10395 	    {
10396 		if (State & VREPLACE_FLAG)
10397 		    MSG_PUTS_ATTR(_(" VREPLACE"), attr);
10398 		else if (State & REPLACE_FLAG)
10399 		    MSG_PUTS_ATTR(_(" REPLACE"), attr);
10400 		else if (State & INSERT)
10401 		{
10402 #ifdef FEAT_RIGHTLEFT
10403 		    if (p_ri)
10404 			MSG_PUTS_ATTR(_(" REVERSE"), attr);
10405 #endif
10406 		    MSG_PUTS_ATTR(_(" INSERT"), attr);
10407 		}
10408 		else if (restart_edit == 'I' || restart_edit == 'A')
10409 		    MSG_PUTS_ATTR(_(" (insert)"), attr);
10410 		else if (restart_edit == 'R')
10411 		    MSG_PUTS_ATTR(_(" (replace)"), attr);
10412 		else if (restart_edit == 'V')
10413 		    MSG_PUTS_ATTR(_(" (vreplace)"), attr);
10414 #ifdef FEAT_RIGHTLEFT
10415 		if (p_hkmap)
10416 		    MSG_PUTS_ATTR(_(" Hebrew"), attr);
10417 # ifdef FEAT_FKMAP
10418 		if (p_fkmap)
10419 		    MSG_PUTS_ATTR(farsi_text_5, attr);
10420 # endif
10421 #endif
10422 #ifdef FEAT_KEYMAP
10423 		if (State & LANGMAP)
10424 		{
10425 # ifdef FEAT_ARABIC
10426 		    if (curwin->w_p_arab)
10427 			MSG_PUTS_ATTR(_(" Arabic"), attr);
10428 		    else
10429 # endif
10430 			if (get_keymap_str(curwin, (char_u *)" (%s)",
10431 							   NameBuff, MAXPATHL))
10432 			    MSG_PUTS_ATTR(NameBuff, attr);
10433 		}
10434 #endif
10435 		if ((State & INSERT) && p_paste)
10436 		    MSG_PUTS_ATTR(_(" (paste)"), attr);
10437 
10438 		if (VIsual_active)
10439 		{
10440 		    char *p;
10441 
10442 		    /* Don't concatenate separate words to avoid translation
10443 		     * problems. */
10444 		    switch ((VIsual_select ? 4 : 0)
10445 			    + (VIsual_mode == Ctrl_V) * 2
10446 			    + (VIsual_mode == 'V'))
10447 		    {
10448 			case 0:	p = N_(" VISUAL"); break;
10449 			case 1: p = N_(" VISUAL LINE"); break;
10450 			case 2: p = N_(" VISUAL BLOCK"); break;
10451 			case 4: p = N_(" SELECT"); break;
10452 			case 5: p = N_(" SELECT LINE"); break;
10453 			default: p = N_(" SELECT BLOCK"); break;
10454 		    }
10455 		    MSG_PUTS_ATTR(_(p), attr);
10456 		}
10457 		MSG_PUTS_ATTR(" --", attr);
10458 	    }
10459 
10460 	    need_clear = TRUE;
10461 	}
10462 	if (reg_recording != 0
10463 #ifdef FEAT_INS_EXPAND
10464 		&& edit_submode == NULL	    /* otherwise it gets too long */
10465 #endif
10466 		)
10467 	{
10468 	    recording_mode(attr);
10469 	    need_clear = TRUE;
10470 	}
10471 
10472 	mode_displayed = TRUE;
10473 	if (need_clear || clear_cmdline)
10474 	    msg_clr_eos();
10475 	msg_didout = FALSE;		/* overwrite this message */
10476 	length = msg_col;
10477 	msg_col = 0;
10478 	need_wait_return = nwr_save;	/* never ask for hit-return for this */
10479     }
10480     else if (clear_cmdline && msg_silent == 0)
10481 	/* Clear the whole command line.  Will reset "clear_cmdline". */
10482 	msg_clr_cmdline();
10483 
10484 #ifdef FEAT_CMDL_INFO
10485     /* In Visual mode the size of the selected area must be redrawn. */
10486     if (VIsual_active)
10487 	clear_showcmd();
10488 
10489     /* If the last window has no status line, the ruler is after the mode
10490      * message and must be redrawn */
10491     if (redrawing() && lastwin->w_status_height == 0)
10492 	win_redr_ruler(lastwin, TRUE, FALSE);
10493 #endif
10494     redraw_cmdline = FALSE;
10495     clear_cmdline = FALSE;
10496 
10497     return length;
10498 }
10499 
10500 /*
10501  * Position for a mode message.
10502  */
10503     static void
10504 msg_pos_mode(void)
10505 {
10506     msg_col = 0;
10507     msg_row = Rows - 1;
10508 }
10509 
10510 /*
10511  * Delete mode message.  Used when ESC is typed which is expected to end
10512  * Insert mode (but Insert mode didn't end yet!).
10513  * Caller should check "mode_displayed".
10514  */
10515     void
10516 unshowmode(int force)
10517 {
10518     /*
10519      * Don't delete it right now, when not redrawing or inside a mapping.
10520      */
10521     if (!redrawing() || (!force && char_avail() && !KeyTyped))
10522 	redraw_cmdline = TRUE;		/* delete mode later */
10523     else
10524 	clearmode();
10525 }
10526 
10527 /*
10528  * Clear the mode message.
10529  */
10530     void
10531 clearmode(void)
10532 {
10533     int save_msg_row = msg_row;
10534     int save_msg_col = msg_col;
10535 
10536     msg_pos_mode();
10537     if (reg_recording != 0)
10538 	recording_mode(HL_ATTR(HLF_CM));
10539     msg_clr_eos();
10540 
10541     msg_col = save_msg_col;
10542     msg_row = save_msg_row;
10543 }
10544 
10545     static void
10546 recording_mode(int attr)
10547 {
10548     MSG_PUTS_ATTR(_("recording"), attr);
10549     if (!shortmess(SHM_RECORDING))
10550     {
10551 	char_u s[4];
10552 	sprintf((char *)s, " @%c", reg_recording);
10553 	MSG_PUTS_ATTR(s, attr);
10554     }
10555 }
10556 
10557 /*
10558  * Draw the tab pages line at the top of the Vim window.
10559  */
10560     static void
10561 draw_tabline(void)
10562 {
10563     int		tabcount = 0;
10564     tabpage_T	*tp;
10565     int		tabwidth;
10566     int		col = 0;
10567     int		scol = 0;
10568     int		attr;
10569     win_T	*wp;
10570     win_T	*cwp;
10571     int		wincount;
10572     int		modified;
10573     int		c;
10574     int		len;
10575     int		attr_sel = HL_ATTR(HLF_TPS);
10576     int		attr_nosel = HL_ATTR(HLF_TP);
10577     int		attr_fill = HL_ATTR(HLF_TPF);
10578     char_u	*p;
10579     int		room;
10580     int		use_sep_chars = (t_colors < 8
10581 #ifdef FEAT_GUI
10582 					    && !gui.in_use
10583 #endif
10584 #ifdef FEAT_TERMGUICOLORS
10585 					    && !p_tgc
10586 #endif
10587 					    );
10588 
10589     if (ScreenLines == NULL)
10590 	return;
10591     redraw_tabline = FALSE;
10592 
10593 #ifdef FEAT_GUI_TABLINE
10594     /* Take care of a GUI tabline. */
10595     if (gui_use_tabline())
10596     {
10597 	gui_update_tabline();
10598 	return;
10599     }
10600 #endif
10601 
10602     if (tabline_height() < 1)
10603 	return;
10604 
10605 #if defined(FEAT_STL_OPT)
10606 
10607     /* Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect. */
10608     for (scol = 0; scol < Columns; ++scol)
10609 	TabPageIdxs[scol] = 0;
10610 
10611     /* Use the 'tabline' option if it's set. */
10612     if (*p_tal != NUL)
10613     {
10614 	int	saved_did_emsg = did_emsg;
10615 
10616 	/* Check for an error.  If there is one we would loop in redrawing the
10617 	 * screen.  Avoid that by making 'tabline' empty. */
10618 	did_emsg = FALSE;
10619 	win_redr_custom(NULL, FALSE);
10620 	if (did_emsg)
10621 	    set_string_option_direct((char_u *)"tabline", -1,
10622 					   (char_u *)"", OPT_FREE, SID_ERROR);
10623 	did_emsg |= saved_did_emsg;
10624     }
10625     else
10626 #endif
10627     {
10628 	FOR_ALL_TABPAGES(tp)
10629 	    ++tabcount;
10630 
10631 	tabwidth = (Columns - 1 + tabcount / 2) / tabcount;
10632 	if (tabwidth < 6)
10633 	    tabwidth = 6;
10634 
10635 	attr = attr_nosel;
10636 	tabcount = 0;
10637 	scol = 0;
10638 	for (tp = first_tabpage; tp != NULL && col < Columns - 4;
10639 							     tp = tp->tp_next)
10640 	{
10641 	    scol = col;
10642 
10643 	    if (tp->tp_topframe == topframe)
10644 		attr = attr_sel;
10645 	    if (use_sep_chars && col > 0)
10646 		screen_putchar('|', 0, col++, attr);
10647 
10648 	    if (tp->tp_topframe != topframe)
10649 		attr = attr_nosel;
10650 
10651 	    screen_putchar(' ', 0, col++, attr);
10652 
10653 	    if (tp == curtab)
10654 	    {
10655 		cwp = curwin;
10656 		wp = firstwin;
10657 	    }
10658 	    else
10659 	    {
10660 		cwp = tp->tp_curwin;
10661 		wp = tp->tp_firstwin;
10662 	    }
10663 
10664 	    modified = FALSE;
10665 	    for (wincount = 0; wp != NULL; wp = wp->w_next, ++wincount)
10666 		if (bufIsChanged(wp->w_buffer))
10667 		    modified = TRUE;
10668 	    if (modified || wincount > 1)
10669 	    {
10670 		if (wincount > 1)
10671 		{
10672 		    vim_snprintf((char *)NameBuff, MAXPATHL, "%d", wincount);
10673 		    len = (int)STRLEN(NameBuff);
10674 		    if (col + len >= Columns - 3)
10675 			break;
10676 		    screen_puts_len(NameBuff, len, 0, col,
10677 #if defined(FEAT_SYN_HL)
10678 					 hl_combine_attr(attr, HL_ATTR(HLF_T))
10679 #else
10680 					 attr
10681 #endif
10682 					       );
10683 		    col += len;
10684 		}
10685 		if (modified)
10686 		    screen_puts_len((char_u *)"+", 1, 0, col++, attr);
10687 		screen_putchar(' ', 0, col++, attr);
10688 	    }
10689 
10690 	    room = scol - col + tabwidth - 1;
10691 	    if (room > 0)
10692 	    {
10693 		/* Get buffer name in NameBuff[] */
10694 		get_trans_bufname(cwp->w_buffer);
10695 		shorten_dir(NameBuff);
10696 		len = vim_strsize(NameBuff);
10697 		p = NameBuff;
10698 #ifdef FEAT_MBYTE
10699 		if (has_mbyte)
10700 		    while (len > room)
10701 		    {
10702 			len -= ptr2cells(p);
10703 			MB_PTR_ADV(p);
10704 		    }
10705 		else
10706 #endif
10707 		    if (len > room)
10708 		{
10709 		    p += len - room;
10710 		    len = room;
10711 		}
10712 		if (len > Columns - col - 1)
10713 		    len = Columns - col - 1;
10714 
10715 		screen_puts_len(p, (int)STRLEN(p), 0, col, attr);
10716 		col += len;
10717 	    }
10718 	    screen_putchar(' ', 0, col++, attr);
10719 
10720 	    /* Store the tab page number in TabPageIdxs[], so that
10721 	     * jump_to_mouse() knows where each one is. */
10722 	    ++tabcount;
10723 	    while (scol < col)
10724 		TabPageIdxs[scol++] = tabcount;
10725 	}
10726 
10727 	if (use_sep_chars)
10728 	    c = '_';
10729 	else
10730 	    c = ' ';
10731 	screen_fill(0, 1, col, (int)Columns, c, c, attr_fill);
10732 
10733 	/* Put an "X" for closing the current tab if there are several. */
10734 	if (first_tabpage->tp_next != NULL)
10735 	{
10736 	    screen_putchar('X', 0, (int)Columns - 1, attr_nosel);
10737 	    TabPageIdxs[Columns - 1] = -999;
10738 	}
10739     }
10740 
10741     /* Reset the flag here again, in case evaluating 'tabline' causes it to be
10742      * set. */
10743     redraw_tabline = FALSE;
10744 }
10745 
10746 /*
10747  * Get buffer name for "buf" into NameBuff[].
10748  * Takes care of special buffer names and translates special characters.
10749  */
10750     void
10751 get_trans_bufname(buf_T *buf)
10752 {
10753     if (buf_spname(buf) != NULL)
10754 	vim_strncpy(NameBuff, buf_spname(buf), MAXPATHL - 1);
10755     else
10756 	home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE);
10757     trans_characters(NameBuff, MAXPATHL);
10758 }
10759 
10760 /*
10761  * Get the character to use in a status line.  Get its attributes in "*attr".
10762  */
10763     static int
10764 fillchar_status(int *attr, win_T *wp)
10765 {
10766     int fill;
10767 
10768 #ifdef FEAT_TERMINAL
10769     if (bt_terminal(wp->w_buffer))
10770     {
10771 	if (wp == curwin)
10772 	{
10773 	    *attr = HL_ATTR(HLF_ST);
10774 	    fill = fill_stl;
10775 	}
10776 	else
10777 	{
10778 	    *attr = HL_ATTR(HLF_STNC);
10779 	    fill = fill_stlnc;
10780 	}
10781     }
10782     else
10783 #endif
10784     if (wp == curwin)
10785     {
10786 	*attr = HL_ATTR(HLF_S);
10787 	fill = fill_stl;
10788     }
10789     else
10790     {
10791 	*attr = HL_ATTR(HLF_SNC);
10792 	fill = fill_stlnc;
10793     }
10794     /* Use fill when there is highlighting, and highlighting of current
10795      * window differs, or the fillchars differ, or this is not the
10796      * current window */
10797     if (*attr != 0 && ((HL_ATTR(HLF_S) != HL_ATTR(HLF_SNC)
10798 			|| wp != curwin || ONE_WINDOW)
10799 		    || (fill_stl != fill_stlnc)))
10800 	return fill;
10801     if (wp == curwin)
10802 	return '^';
10803     return '=';
10804 }
10805 
10806 /*
10807  * Get the character to use in a separator between vertically split windows.
10808  * Get its attributes in "*attr".
10809  */
10810     static int
10811 fillchar_vsep(int *attr)
10812 {
10813     *attr = HL_ATTR(HLF_C);
10814     if (*attr == 0 && fill_vert == ' ')
10815 	return '|';
10816     else
10817 	return fill_vert;
10818 }
10819 
10820 /*
10821  * Return TRUE if redrawing should currently be done.
10822  */
10823     int
10824 redrawing(void)
10825 {
10826 #ifdef FEAT_EVAL
10827     if (disable_redraw_for_testing)
10828 	return 0;
10829     else
10830 #endif
10831 	return (!RedrawingDisabled
10832 		       && !(p_lz && char_avail() && !KeyTyped && !do_redraw));
10833 }
10834 
10835 /*
10836  * Return TRUE if printing messages should currently be done.
10837  */
10838     int
10839 messaging(void)
10840 {
10841     return (!(p_lz && char_avail() && !KeyTyped));
10842 }
10843 
10844 #ifdef FEAT_MENU
10845 /*
10846  * Draw the window toolbar.
10847  */
10848     static void
10849 redraw_win_toolbar(win_T *wp)
10850 {
10851     vimmenu_T	*menu;
10852     int		item_idx = 0;
10853     int		item_count = 0;
10854     int		col = 0;
10855     int		next_col;
10856     int		off = (int)(current_ScreenLine - ScreenLines);
10857     int		fill_attr = syn_name2attr((char_u *)"ToolbarLine");
10858     int		button_attr = syn_name2attr((char_u *)"ToolbarButton");
10859 
10860     vim_free(wp->w_winbar_items);
10861     for (menu = wp->w_winbar->children; menu != NULL; menu = menu->next)
10862 	++item_count;
10863     wp->w_winbar_items = (winbar_item_T *)alloc_clear(
10864 			   (unsigned)sizeof(winbar_item_T) * (item_count + 1));
10865 
10866     /* TODO: use fewer spaces if there is not enough room */
10867     for (menu = wp->w_winbar->children;
10868 			  menu != NULL && col < wp->w_width; menu = menu->next)
10869     {
10870 	space_to_screenline(off + col, fill_attr);
10871 	if (++col >= wp->w_width)
10872 	    break;
10873 	if (col > 1)
10874 	{
10875 	    space_to_screenline(off + col, fill_attr);
10876 	    if (++col >= wp->w_width)
10877 		break;
10878 	}
10879 
10880 	wp->w_winbar_items[item_idx].wb_startcol = col;
10881 	space_to_screenline(off + col, button_attr);
10882 	if (++col >= wp->w_width)
10883 	    break;
10884 
10885 	next_col = text_to_screenline(wp, menu->name, col);
10886 	while (col < next_col)
10887 	{
10888 	    ScreenAttrs[off + col] = button_attr;
10889 	    ++col;
10890 	}
10891 	wp->w_winbar_items[item_idx].wb_endcol = col;
10892 	wp->w_winbar_items[item_idx].wb_menu = menu;
10893 	++item_idx;
10894 
10895 	if (col >= wp->w_width)
10896 	    break;
10897 	space_to_screenline(off + col, button_attr);
10898 	++col;
10899     }
10900     while (col < wp->w_width)
10901     {
10902 	space_to_screenline(off + col, fill_attr);
10903 	++col;
10904     }
10905     wp->w_winbar_items[item_idx].wb_menu = NULL; /* end marker */
10906 
10907     screen_line(wp->w_winrow, wp->w_wincol, (int)wp->w_width,
10908 						     (int)wp->w_width, FALSE);
10909 }
10910 #endif
10911 
10912 /*
10913  * Show current status info in ruler and various other places
10914  * If always is FALSE, only show ruler if position has changed.
10915  */
10916     void
10917 showruler(int always)
10918 {
10919     if (!always && !redrawing())
10920 	return;
10921 #ifdef FEAT_INS_EXPAND
10922     if (pum_visible())
10923     {
10924 	/* Don't redraw right now, do it later. */
10925 	curwin->w_redr_status = TRUE;
10926 	return;
10927     }
10928 #endif
10929 #if defined(FEAT_STL_OPT)
10930     if ((*p_stl != NUL || *curwin->w_p_stl != NUL) && curwin->w_status_height)
10931     {
10932 	redraw_custom_statusline(curwin);
10933     }
10934     else
10935 #endif
10936 #ifdef FEAT_CMDL_INFO
10937 	win_redr_ruler(curwin, always, FALSE);
10938 #endif
10939 
10940 #ifdef FEAT_TITLE
10941     if (need_maketitle
10942 # ifdef FEAT_STL_OPT
10943 	    || (p_icon && (stl_syntax & STL_IN_ICON))
10944 	    || (p_title && (stl_syntax & STL_IN_TITLE))
10945 # endif
10946        )
10947 	maketitle();
10948 #endif
10949     /* Redraw the tab pages line if needed. */
10950     if (redraw_tabline)
10951 	draw_tabline();
10952 }
10953 
10954 #ifdef FEAT_CMDL_INFO
10955     static void
10956 win_redr_ruler(win_T *wp, int always, int ignore_pum)
10957 {
10958 #define RULER_BUF_LEN 70
10959     char_u	buffer[RULER_BUF_LEN];
10960     int		row;
10961     int		fillchar;
10962     int		attr;
10963     int		empty_line = FALSE;
10964     colnr_T	virtcol;
10965     int		i;
10966     size_t	len;
10967     int		o;
10968     int		this_ru_col;
10969     int		off = 0;
10970     int		width = Columns;
10971 
10972     /* If 'ruler' off or redrawing disabled, don't do anything */
10973     if (!p_ru)
10974 	return;
10975 
10976     /*
10977      * Check if cursor.lnum is valid, since win_redr_ruler() may be called
10978      * after deleting lines, before cursor.lnum is corrected.
10979      */
10980     if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count)
10981 	return;
10982 
10983 #ifdef FEAT_INS_EXPAND
10984     /* Don't draw the ruler while doing insert-completion, it might overwrite
10985      * the (long) mode message. */
10986     if (wp == lastwin && lastwin->w_status_height == 0)
10987 	if (edit_submode != NULL)
10988 	    return;
10989     // Don't draw the ruler when the popup menu is visible, it may overlap.
10990     // Except when the popup menu will be redrawn anyway.
10991     if (!ignore_pum && pum_visible())
10992 	return;
10993 #endif
10994 
10995 #ifdef FEAT_STL_OPT
10996     if (*p_ruf)
10997     {
10998 	int	save_called_emsg = called_emsg;
10999 
11000 	called_emsg = FALSE;
11001 	win_redr_custom(wp, TRUE);
11002 	if (called_emsg)
11003 	    set_string_option_direct((char_u *)"rulerformat", -1,
11004 					   (char_u *)"", OPT_FREE, SID_ERROR);
11005 	called_emsg |= save_called_emsg;
11006 	return;
11007     }
11008 #endif
11009 
11010     /*
11011      * Check if not in Insert mode and the line is empty (will show "0-1").
11012      */
11013     if (!(State & INSERT)
11014 		&& *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE) == NUL)
11015 	empty_line = TRUE;
11016 
11017     /*
11018      * Only draw the ruler when something changed.
11019      */
11020     validate_virtcol_win(wp);
11021     if (       redraw_cmdline
11022 	    || always
11023 	    || wp->w_cursor.lnum != wp->w_ru_cursor.lnum
11024 	    || wp->w_cursor.col != wp->w_ru_cursor.col
11025 	    || wp->w_virtcol != wp->w_ru_virtcol
11026 #ifdef FEAT_VIRTUALEDIT
11027 	    || wp->w_cursor.coladd != wp->w_ru_cursor.coladd
11028 #endif
11029 	    || wp->w_topline != wp->w_ru_topline
11030 	    || wp->w_buffer->b_ml.ml_line_count != wp->w_ru_line_count
11031 #ifdef FEAT_DIFF
11032 	    || wp->w_topfill != wp->w_ru_topfill
11033 #endif
11034 	    || empty_line != wp->w_ru_empty)
11035     {
11036 	cursor_off();
11037 	if (wp->w_status_height)
11038 	{
11039 	    row = W_WINROW(wp) + wp->w_height;
11040 	    fillchar = fillchar_status(&attr, wp);
11041 	    off = wp->w_wincol;
11042 	    width = wp->w_width;
11043 	}
11044 	else
11045 	{
11046 	    row = Rows - 1;
11047 	    fillchar = ' ';
11048 	    attr = 0;
11049 	    width = Columns;
11050 	    off = 0;
11051 	}
11052 
11053 	/* In list mode virtcol needs to be recomputed */
11054 	virtcol = wp->w_virtcol;
11055 	if (wp->w_p_list && lcs_tab1 == NUL)
11056 	{
11057 	    wp->w_p_list = FALSE;
11058 	    getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
11059 	    wp->w_p_list = TRUE;
11060 	}
11061 
11062 	/*
11063 	 * Some sprintfs return the length, some return a pointer.
11064 	 * To avoid portability problems we use strlen() here.
11065 	 */
11066 	vim_snprintf((char *)buffer, RULER_BUF_LEN, "%ld,",
11067 		(wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
11068 		    ? 0L
11069 		    : (long)(wp->w_cursor.lnum));
11070 	len = STRLEN(buffer);
11071 	col_print(buffer + len, RULER_BUF_LEN - len,
11072 			empty_line ? 0 : (int)wp->w_cursor.col + 1,
11073 			(int)virtcol + 1);
11074 
11075 	/*
11076 	 * Add a "50%" if there is room for it.
11077 	 * On the last line, don't print in the last column (scrolls the
11078 	 * screen up on some terminals).
11079 	 */
11080 	i = (int)STRLEN(buffer);
11081 	get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1);
11082 	o = i + vim_strsize(buffer + i + 1);
11083 	if (wp->w_status_height == 0)	/* can't use last char of screen */
11084 	    ++o;
11085 	this_ru_col = ru_col - (Columns - width);
11086 	if (this_ru_col < 0)
11087 	    this_ru_col = 0;
11088 	/* Never use more than half the window/screen width, leave the other
11089 	 * half for the filename. */
11090 	if (this_ru_col < (width + 1) / 2)
11091 	    this_ru_col = (width + 1) / 2;
11092 	if (this_ru_col + o < width)
11093 	{
11094 	    /* need at least 3 chars left for get_rel_pos() + NUL */
11095 	    while (this_ru_col + o < width && RULER_BUF_LEN > i + 4)
11096 	    {
11097 #ifdef FEAT_MBYTE
11098 		if (has_mbyte)
11099 		    i += (*mb_char2bytes)(fillchar, buffer + i);
11100 		else
11101 #endif
11102 		    buffer[i++] = fillchar;
11103 		++o;
11104 	    }
11105 	    get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i);
11106 	}
11107 	/* Truncate at window boundary. */
11108 #ifdef FEAT_MBYTE
11109 	if (has_mbyte)
11110 	{
11111 	    o = 0;
11112 	    for (i = 0; buffer[i] != NUL; i += (*mb_ptr2len)(buffer + i))
11113 	    {
11114 		o += (*mb_ptr2cells)(buffer + i);
11115 		if (this_ru_col + o > width)
11116 		{
11117 		    buffer[i] = NUL;
11118 		    break;
11119 		}
11120 	    }
11121 	}
11122 	else
11123 #endif
11124 	if (this_ru_col + (int)STRLEN(buffer) > width)
11125 	    buffer[width - this_ru_col] = NUL;
11126 
11127 	screen_puts(buffer, row, this_ru_col + off, attr);
11128 	i = redraw_cmdline;
11129 	screen_fill(row, row + 1,
11130 		this_ru_col + off + (int)STRLEN(buffer),
11131 		(int)(off + width),
11132 		fillchar, fillchar, attr);
11133 	/* don't redraw the cmdline because of showing the ruler */
11134 	redraw_cmdline = i;
11135 	wp->w_ru_cursor = wp->w_cursor;
11136 	wp->w_ru_virtcol = wp->w_virtcol;
11137 	wp->w_ru_empty = empty_line;
11138 	wp->w_ru_topline = wp->w_topline;
11139 	wp->w_ru_line_count = wp->w_buffer->b_ml.ml_line_count;
11140 #ifdef FEAT_DIFF
11141 	wp->w_ru_topfill = wp->w_topfill;
11142 #endif
11143     }
11144 }
11145 #endif
11146 
11147 #if defined(FEAT_LINEBREAK) || defined(PROTO)
11148 /*
11149  * Return the width of the 'number' and 'relativenumber' column.
11150  * Caller may need to check if 'number' or 'relativenumber' is set.
11151  * Otherwise it depends on 'numberwidth' and the line count.
11152  */
11153     int
11154 number_width(win_T *wp)
11155 {
11156     int		n;
11157     linenr_T	lnum;
11158 
11159     if (wp->w_p_rnu && !wp->w_p_nu)
11160 	/* cursor line shows "0" */
11161 	lnum = wp->w_height;
11162     else
11163 	/* cursor line shows absolute line number */
11164 	lnum = wp->w_buffer->b_ml.ml_line_count;
11165 
11166     if (lnum == wp->w_nrwidth_line_count && wp->w_nuw_cached == wp->w_p_nuw)
11167 	return wp->w_nrwidth_width;
11168     wp->w_nrwidth_line_count = lnum;
11169 
11170     n = 0;
11171     do
11172     {
11173 	lnum /= 10;
11174 	++n;
11175     } while (lnum > 0);
11176 
11177     /* 'numberwidth' gives the minimal width plus one */
11178     if (n < wp->w_p_nuw - 1)
11179 	n = wp->w_p_nuw - 1;
11180 
11181     wp->w_nrwidth_width = n;
11182     wp->w_nuw_cached = wp->w_p_nuw;
11183     return n;
11184 }
11185 #endif
11186 
11187 /*
11188  * Return the current cursor column. This is the actual position on the
11189  * screen. First column is 0.
11190  */
11191     int
11192 screen_screencol(void)
11193 {
11194     return screen_cur_col;
11195 }
11196 
11197 /*
11198  * Return the current cursor row. This is the actual position on the screen.
11199  * First row is 0.
11200  */
11201     int
11202 screen_screenrow(void)
11203 {
11204     return screen_cur_row;
11205 }
11206