blob: 17a385c9244c6b3677d5897545733b9a4cd99629 [file] [log] [blame]
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001/* 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 * drawscreen.c: Code for updating all the windows on the screen.
12 * This is the top level, drawline.c is the middle and screen.c the lower
13 * level.
14 *
15 * update_screen() is the function that updates all windows and status lines.
16 * It is called form the main loop when must_redraw is non-zero. It may be
17 * called from other places when an immediate screen update is needed.
18 *
19 * The part of the buffer that is displayed in a window is set with:
20 * - w_topline (first buffer line in window)
21 * - w_topfill (filler lines above the first line)
22 * - w_leftcol (leftmost window cell in window),
23 * - w_skipcol (skipped window cells of first line)
24 *
25 * Commands that only move the cursor around in a window, do not need to take
26 * action to update the display. The main loop will check if w_topline is
27 * valid and update it (scroll the window) when needed.
28 *
29 * Commands that scroll a window change w_topline and must call
30 * check_cursor() to move the cursor into the visible part of the window, and
31 * call redraw_later(VALID) to have the window displayed by update_screen()
32 * later.
33 *
34 * Commands that change text in the buffer must call changed_bytes() or
35 * changed_lines() to mark the area that changed and will require updating
36 * later. The main loop will call update_screen(), which will update each
37 * window that shows the changed buffer. This assumes text above the change
38 * can remain displayed as it is. Text after the change may need updating for
39 * scrolling, folding and syntax highlighting.
40 *
41 * Commands that change how a window is displayed (e.g., setting 'list') or
42 * invalidate the contents of a window in another way (e.g., change fold
43 * settings), must call redraw_later(NOT_VALID) to have the whole window
44 * redisplayed by update_screen() later.
45 *
46 * Commands that change how a buffer is displayed (e.g., setting 'tabstop')
47 * must call redraw_curbuf_later(NOT_VALID) to have all the windows for the
48 * buffer redisplayed by update_screen() later.
49 *
50 * Commands that change highlighting and possibly cause a scroll too must call
51 * redraw_later(SOME_VALID) to update the whole window but still use scrolling
52 * to avoid redrawing everything. But the length of displayed lines must not
53 * change, use NOT_VALID then.
54 *
55 * Commands that move the window position must call redraw_later(NOT_VALID).
56 * TODO: should minimize redrawing by scrolling when possible.
57 *
58 * Commands that change everything (e.g., resizing the screen) must call
59 * redraw_all_later(NOT_VALID) or redraw_all_later(CLEAR).
60 *
61 * Things that are handled indirectly:
62 * - When messages scroll the screen up, msg_scrolled will be set and
63 * update_screen() called to redraw.
64 */
65
66#include "vim.h"
67
68static void win_update(win_T *wp);
69#ifdef FEAT_STL_OPT
70static void redraw_custom_statusline(win_T *wp);
71#endif
Bram Moolenaare52e0c82020-02-28 22:20:10 +010072#if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_CLIPBOARD)
73static int did_update_one_window;
74#endif
Bram Moolenaar7528d1f2019-09-19 23:06:20 +020075
76/*
77 * Based on the current value of curwin->w_topline, transfer a screenfull
78 * of stuff from Filemem to ScreenLines[], and update curwin->w_botline.
79 * Return OK when the screen was updated, FAIL if it was not done.
80 */
81 int
82update_screen(int type_arg)
83{
84 int type = type_arg;
85 win_T *wp;
86 static int did_intro = FALSE;
Bram Moolenaar7528d1f2019-09-19 23:06:20 +020087#ifdef FEAT_GUI
Bram Moolenaare52e0c82020-02-28 22:20:10 +010088 int did_one = FALSE;
Bram Moolenaar7528d1f2019-09-19 23:06:20 +020089 int did_undraw = FALSE;
90 int gui_cursor_col = 0;
91 int gui_cursor_row = 0;
92#endif
93 int no_update = FALSE;
94
95 // Don't do anything if the screen structures are (not yet) valid.
96 if (!screen_valid(TRUE))
97 return FAIL;
98
99 if (type == VALID_NO_UPDATE)
100 {
101 no_update = TRUE;
102 type = 0;
103 }
104
105#ifdef FEAT_EVAL
106 {
107 buf_T *buf;
108
109 // Before updating the screen, notify any listeners of changed text.
110 FOR_ALL_BUFFERS(buf)
111 invoke_listeners(buf);
112 }
113#endif
114
115#ifdef FEAT_DIFF
116 // May have postponed updating diffs.
117 if (need_diff_redraw)
118 diff_redraw(TRUE);
119#endif
120
121 if (must_redraw)
122 {
123 if (type < must_redraw) // use maximal type
124 type = must_redraw;
125
126 // must_redraw is reset here, so that when we run into some weird
127 // reason to redraw while busy redrawing (e.g., asynchronous
128 // scrolling), or update_topline() in win_update() will cause a
129 // scroll, the screen will be redrawn later or in win_update().
130 must_redraw = 0;
131 }
132
133 // May need to update w_lines[].
134 if (curwin->w_lines_valid == 0 && type < NOT_VALID
135#ifdef FEAT_TERMINAL
136 && !term_do_update_window(curwin)
137#endif
138 )
139 type = NOT_VALID;
140
141 // Postpone the redrawing when it's not needed and when being called
142 // recursively.
143 if (!redrawing() || updating_screen)
144 {
145 redraw_later(type); // remember type for next time
146 must_redraw = type;
147 if (type > INVERTED_ALL)
148 curwin->w_lines_valid = 0; // don't use w_lines[].wl_size now
149 return FAIL;
150 }
151 updating_screen = TRUE;
152
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100153#ifdef FEAT_PROP_POPUP
Bram Moolenaar7528d1f2019-09-19 23:06:20 +0200154 // Update popup_mask if needed. This may set w_redraw_top and w_redraw_bot
155 // in some windows.
156 may_update_popup_mask(type);
157#endif
158
159#ifdef FEAT_SYN_HL
160 ++display_tick; // let syntax code know we're in a next round of
161 // display updating
162#endif
163 if (no_update)
164 ++no_win_do_lines_ins;
165
166 // if the screen was scrolled up when displaying a message, scroll it down
167 if (msg_scrolled)
168 {
169 clear_cmdline = TRUE;
170 if (msg_scrolled > Rows - 5) // clearing is faster
171 type = CLEAR;
172 else if (type != CLEAR)
173 {
174 check_for_delay(FALSE);
175 if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows, 0, NULL)
176 == FAIL)
177 type = CLEAR;
178 FOR_ALL_WINDOWS(wp)
179 {
180 if (wp->w_winrow < msg_scrolled)
181 {
182 if (W_WINROW(wp) + wp->w_height > msg_scrolled
183 && wp->w_redr_type < REDRAW_TOP
184 && wp->w_lines_valid > 0
185 && wp->w_topline == wp->w_lines[0].wl_lnum)
186 {
187 wp->w_upd_rows = msg_scrolled - W_WINROW(wp);
188 wp->w_redr_type = REDRAW_TOP;
189 }
190 else
191 {
192 wp->w_redr_type = NOT_VALID;
193 if (W_WINROW(wp) + wp->w_height + wp->w_status_height
194 <= msg_scrolled)
195 wp->w_redr_status = TRUE;
196 }
197 }
198 }
199 if (!no_update)
200 redraw_cmdline = TRUE;
201 redraw_tabline = TRUE;
202 }
203 msg_scrolled = 0;
204 need_wait_return = FALSE;
205 }
206
207 // reset cmdline_row now (may have been changed temporarily)
208 compute_cmdrow();
209
210 // Check for changed highlighting
211 if (need_highlight_changed)
212 highlight_changed();
213
214 if (type == CLEAR) // first clear screen
215 {
216 screenclear(); // will reset clear_cmdline
217 type = NOT_VALID;
218 // must_redraw may be set indirectly, avoid another redraw later
219 must_redraw = 0;
220 }
221
222 if (clear_cmdline) // going to clear cmdline (done below)
223 check_for_delay(FALSE);
224
225#ifdef FEAT_LINEBREAK
226 // Force redraw when width of 'number' or 'relativenumber' column
227 // changes.
228 if (curwin->w_redr_type < NOT_VALID
229 && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu)
230 ? number_width(curwin) : 0))
231 curwin->w_redr_type = NOT_VALID;
232#endif
233
234 // Only start redrawing if there is really something to do.
235 if (type == INVERTED)
236 update_curswant();
237 if (curwin->w_redr_type < type
238 && !((type == VALID
239 && curwin->w_lines[0].wl_valid
240#ifdef FEAT_DIFF
241 && curwin->w_topfill == curwin->w_old_topfill
242 && curwin->w_botfill == curwin->w_old_botfill
243#endif
244 && curwin->w_topline == curwin->w_lines[0].wl_lnum)
245 || (type == INVERTED
246 && VIsual_active
247 && curwin->w_old_cursor_lnum == curwin->w_cursor.lnum
248 && curwin->w_old_visual_mode == VIsual_mode
249 && (curwin->w_valid & VALID_VIRTCOL)
250 && curwin->w_old_curswant == curwin->w_curswant)
251 ))
252 curwin->w_redr_type = type;
253
254 // Redraw the tab pages line if needed.
255 if (redraw_tabline || type >= NOT_VALID)
256 draw_tabline();
257
258#ifdef FEAT_SYN_HL
259 // Correct stored syntax highlighting info for changes in each displayed
260 // buffer. Each buffer must only be done once.
261 FOR_ALL_WINDOWS(wp)
262 {
263 if (wp->w_buffer->b_mod_set)
264 {
265 win_T *wwp;
266
267 // Check if we already did this buffer.
268 for (wwp = firstwin; wwp != wp; wwp = wwp->w_next)
269 if (wwp->w_buffer == wp->w_buffer)
270 break;
271 if (wwp == wp && syntax_present(wp))
272 syn_stack_apply_changes(wp->w_buffer);
273 }
274 }
275#endif
276
277 // Go from top to bottom through the windows, redrawing the ones that need
278 // it.
279#if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_CLIPBOARD)
Bram Moolenaare52e0c82020-02-28 22:20:10 +0100280 did_update_one_window = FALSE;
Bram Moolenaar7528d1f2019-09-19 23:06:20 +0200281#endif
282#ifdef FEAT_SEARCH_EXTRA
283 screen_search_hl.rm.regprog = NULL;
284#endif
285 FOR_ALL_WINDOWS(wp)
286 {
287 if (wp->w_redr_type != 0)
288 {
289 cursor_off();
Bram Moolenaare52e0c82020-02-28 22:20:10 +0100290#ifdef FEAT_GUI
Bram Moolenaar7528d1f2019-09-19 23:06:20 +0200291 if (!did_one)
292 {
293 did_one = TRUE;
Bram Moolenaare52e0c82020-02-28 22:20:10 +0100294
Bram Moolenaar7528d1f2019-09-19 23:06:20 +0200295 // Remove the cursor before starting to do anything, because
296 // scrolling may make it difficult to redraw the text under
297 // it.
298 if (gui.in_use && wp == curwin)
299 {
300 gui_cursor_col = gui.cursor_col;
301 gui_cursor_row = gui.cursor_row;
302 gui_undraw_cursor();
303 did_undraw = TRUE;
304 }
Bram Moolenaar7528d1f2019-09-19 23:06:20 +0200305 }
306#endif
Bram Moolenaare52e0c82020-02-28 22:20:10 +0100307
Bram Moolenaar7528d1f2019-09-19 23:06:20 +0200308 win_update(wp);
309 }
310
311 // redraw status line after the window to minimize cursor movement
312 if (wp->w_redr_status)
313 {
314 cursor_off();
315 win_redr_status(wp, TRUE); // any popup menu will be redrawn below
316 }
317 }
318#if defined(FEAT_SEARCH_EXTRA)
319 end_search_hl();
320#endif
321 // May need to redraw the popup menu.
322 pum_may_redraw();
323
324 // Reset b_mod_set flags. Going through all windows is probably faster
325 // than going through all buffers (there could be many buffers).
326 FOR_ALL_WINDOWS(wp)
327 wp->w_buffer->b_mod_set = FALSE;
328
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100329#ifdef FEAT_PROP_POPUP
Bram Moolenaar7528d1f2019-09-19 23:06:20 +0200330 // Display popup windows on top of the windows and command line.
331 update_popups(win_update);
332#endif
333
334 after_updating_screen(TRUE);
335
336 // Clear or redraw the command line. Done last, because scrolling may
337 // mess up the command line.
338 if (clear_cmdline || redraw_cmdline || redraw_mode)
339 showmode();
340
341 if (no_update)
342 --no_win_do_lines_ins;
343
344 // May put up an introductory message when not editing a file
345 if (!did_intro)
346 maybe_intro_message();
347 did_intro = TRUE;
348
349#ifdef FEAT_GUI
350 // Redraw the cursor and update the scrollbars when all screen updating is
351 // done.
352 if (gui.in_use)
353 {
354 if (did_undraw && !gui_mch_is_blink_off())
355 {
356 mch_disable_flush();
357 out_flush(); // required before updating the cursor
358 mch_enable_flush();
359
360 // Put the GUI position where the cursor was, gui_update_cursor()
361 // uses that.
362 gui.col = gui_cursor_col;
363 gui.row = gui_cursor_row;
364 gui.col = mb_fix_col(gui.col, gui.row);
365 gui_update_cursor(FALSE, FALSE);
366 gui_may_flush();
367 screen_cur_col = gui.col;
368 screen_cur_row = gui.row;
369 }
370 else
371 out_flush();
372 gui_update_scrollbars(FALSE);
373 }
374#endif
375 return OK;
376}
377
378/*
379 * Redraw the status line of window wp.
380 *
381 * If inversion is possible we use it. Else '=' characters are used.
382 * If "ignore_pum" is TRUE, also redraw statusline when the popup menu is
383 * displayed.
384 */
385 void
386win_redr_status(win_T *wp, int ignore_pum UNUSED)
387{
388 int row;
389 char_u *p;
390 int len;
391 int fillchar;
392 int attr;
393 int this_ru_col;
394 static int busy = FALSE;
395
396 // It's possible to get here recursively when 'statusline' (indirectly)
397 // invokes ":redrawstatus". Simply ignore the call then.
398 if (busy)
399 return;
400 busy = TRUE;
401
402 wp->w_redr_status = FALSE;
403 if (wp->w_status_height == 0)
404 {
405 // no status line, can only be last window
406 redraw_cmdline = TRUE;
407 }
408 else if (!redrawing()
409 // don't update status line when popup menu is visible and may be
410 // drawn over it, unless it will be redrawn later
411 || (!ignore_pum && pum_visible()))
412 {
413 // Don't redraw right now, do it later.
414 wp->w_redr_status = TRUE;
415 }
416#ifdef FEAT_STL_OPT
417 else if (*p_stl != NUL || *wp->w_p_stl != NUL)
418 {
419 // redraw custom status line
420 redraw_custom_statusline(wp);
421 }
422#endif
423 else
424 {
425 fillchar = fillchar_status(&attr, wp);
426
427 get_trans_bufname(wp->w_buffer);
428 p = NameBuff;
429 len = (int)STRLEN(p);
430
431 if (bt_help(wp->w_buffer)
432#ifdef FEAT_QUICKFIX
433 || wp->w_p_pvw
434#endif
435 || bufIsChanged(wp->w_buffer)
436 || wp->w_buffer->b_p_ro)
437 *(p + len++) = ' ';
438 if (bt_help(wp->w_buffer))
439 {
440 STRCPY(p + len, _("[Help]"));
441 len += (int)STRLEN(p + len);
442 }
443#ifdef FEAT_QUICKFIX
444 if (wp->w_p_pvw)
445 {
446 STRCPY(p + len, _("[Preview]"));
447 len += (int)STRLEN(p + len);
448 }
449#endif
450 if (bufIsChanged(wp->w_buffer)
451#ifdef FEAT_TERMINAL
452 && !bt_terminal(wp->w_buffer)
453#endif
454 )
455 {
456 STRCPY(p + len, "[+]");
457 len += 3;
458 }
459 if (wp->w_buffer->b_p_ro)
460 {
461 STRCPY(p + len, _("[RO]"));
462 len += (int)STRLEN(p + len);
463 }
464
465 this_ru_col = ru_col - (Columns - wp->w_width);
466 if (this_ru_col < (wp->w_width + 1) / 2)
467 this_ru_col = (wp->w_width + 1) / 2;
468 if (this_ru_col <= 1)
469 {
470 p = (char_u *)"<"; // No room for file name!
471 len = 1;
472 }
473 else if (has_mbyte)
474 {
475 int clen = 0, i;
476
477 // Count total number of display cells.
478 clen = mb_string2cells(p, -1);
479
480 // Find first character that will fit.
481 // Going from start to end is much faster for DBCS.
482 for (i = 0; p[i] != NUL && clen >= this_ru_col - 1;
483 i += (*mb_ptr2len)(p + i))
484 clen -= (*mb_ptr2cells)(p + i);
485 len = clen;
486 if (i > 0)
487 {
488 p = p + i - 1;
489 *p = '<';
490 ++len;
491 }
492
493 }
494 else if (len > this_ru_col - 1)
495 {
496 p += len - (this_ru_col - 1);
497 *p = '<';
498 len = this_ru_col - 1;
499 }
500
501 row = W_WINROW(wp) + wp->w_height;
502 screen_puts(p, row, wp->w_wincol, attr);
503 screen_fill(row, row + 1, len + wp->w_wincol,
504 this_ru_col + wp->w_wincol, fillchar, fillchar, attr);
505
506 if (get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL)
507 && (int)(this_ru_col - len) > (int)(STRLEN(NameBuff) + 1))
508 screen_puts(NameBuff, row, (int)(this_ru_col - STRLEN(NameBuff)
509 - 1 + wp->w_wincol), attr);
510
511#ifdef FEAT_CMDL_INFO
512 win_redr_ruler(wp, TRUE, ignore_pum);
513#endif
514 }
515
516 /*
517 * May need to draw the character below the vertical separator.
518 */
519 if (wp->w_vsep_width != 0 && wp->w_status_height != 0 && redrawing())
520 {
521 if (stl_connected(wp))
522 fillchar = fillchar_status(&attr, wp);
523 else
524 fillchar = fillchar_vsep(&attr);
525 screen_putchar(fillchar, W_WINROW(wp) + wp->w_height, W_ENDCOL(wp),
526 attr);
527 }
528 busy = FALSE;
529}
530
531#ifdef FEAT_STL_OPT
532/*
533 * Redraw the status line according to 'statusline' and take care of any
534 * errors encountered.
535 */
536 static void
537redraw_custom_statusline(win_T *wp)
538{
539 static int entered = FALSE;
540 int saved_did_emsg = did_emsg;
541
542 // When called recursively return. This can happen when the statusline
543 // contains an expression that triggers a redraw.
544 if (entered)
545 return;
546 entered = TRUE;
547
548 did_emsg = FALSE;
549 win_redr_custom(wp, FALSE);
550 if (did_emsg)
551 {
552 // When there is an error disable the statusline, otherwise the
553 // display is messed up with errors and a redraw triggers the problem
554 // again and again.
555 set_string_option_direct((char_u *)"statusline", -1,
556 (char_u *)"", OPT_FREE | (*wp->w_p_stl != NUL
557 ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
558 }
559 did_emsg |= saved_did_emsg;
560 entered = FALSE;
561}
562#endif
563
564/*
565 * Show current status info in ruler and various other places
566 * If always is FALSE, only show ruler if position has changed.
567 */
568 void
569showruler(int always)
570{
571 if (!always && !redrawing())
572 return;
573 if (pum_visible())
574 {
575 // Don't redraw right now, do it later.
576 curwin->w_redr_status = TRUE;
577 return;
578 }
579#if defined(FEAT_STL_OPT)
580 if ((*p_stl != NUL || *curwin->w_p_stl != NUL) && curwin->w_status_height)
581 redraw_custom_statusline(curwin);
582 else
583#endif
584#ifdef FEAT_CMDL_INFO
585 win_redr_ruler(curwin, always, FALSE);
586#endif
587
588#ifdef FEAT_TITLE
589 if (need_maketitle
590# ifdef FEAT_STL_OPT
591 || (p_icon && (stl_syntax & STL_IN_ICON))
592 || (p_title && (stl_syntax & STL_IN_TITLE))
593# endif
594 )
595 maketitle();
596#endif
597 // Redraw the tab pages line if needed.
598 if (redraw_tabline)
599 draw_tabline();
600}
601
602#if defined(FEAT_CMDL_INFO) || defined(PROTO)
603 void
604win_redr_ruler(win_T *wp, int always, int ignore_pum)
605{
606#define RULER_BUF_LEN 70
607 char_u buffer[RULER_BUF_LEN];
608 int row;
609 int fillchar;
610 int attr;
611 int empty_line = FALSE;
612 colnr_T virtcol;
613 int i;
614 size_t len;
615 int o;
616 int this_ru_col;
617 int off = 0;
618 int width;
619
620 // If 'ruler' off or redrawing disabled, don't do anything
621 if (!p_ru)
622 return;
623
624 /*
625 * Check if cursor.lnum is valid, since win_redr_ruler() may be called
626 * after deleting lines, before cursor.lnum is corrected.
627 */
628 if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count)
629 return;
630
631 // Don't draw the ruler while doing insert-completion, it might overwrite
632 // the (long) mode message.
633 if (wp == lastwin && lastwin->w_status_height == 0)
634 if (edit_submode != NULL)
635 return;
636 // Don't draw the ruler when the popup menu is visible, it may overlap.
637 // Except when the popup menu will be redrawn anyway.
638 if (!ignore_pum && pum_visible())
639 return;
640
641#ifdef FEAT_STL_OPT
642 if (*p_ruf)
643 {
Bram Moolenaar53989552019-12-23 22:59:18 +0100644 int called_emsg_before = called_emsg;
Bram Moolenaar7528d1f2019-09-19 23:06:20 +0200645
Bram Moolenaar7528d1f2019-09-19 23:06:20 +0200646 win_redr_custom(wp, TRUE);
Bram Moolenaar53989552019-12-23 22:59:18 +0100647 if (called_emsg > called_emsg_before)
Bram Moolenaar7528d1f2019-09-19 23:06:20 +0200648 set_string_option_direct((char_u *)"rulerformat", -1,
649 (char_u *)"", OPT_FREE, SID_ERROR);
Bram Moolenaar7528d1f2019-09-19 23:06:20 +0200650 return;
651 }
652#endif
653
654 /*
655 * Check if not in Insert mode and the line is empty (will show "0-1").
656 */
657 if (!(State & INSERT)
658 && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE) == NUL)
659 empty_line = TRUE;
660
661 /*
662 * Only draw the ruler when something changed.
663 */
664 validate_virtcol_win(wp);
665 if ( redraw_cmdline
666 || always
667 || wp->w_cursor.lnum != wp->w_ru_cursor.lnum
668 || wp->w_cursor.col != wp->w_ru_cursor.col
669 || wp->w_virtcol != wp->w_ru_virtcol
670 || wp->w_cursor.coladd != wp->w_ru_cursor.coladd
671 || wp->w_topline != wp->w_ru_topline
672 || wp->w_buffer->b_ml.ml_line_count != wp->w_ru_line_count
673#ifdef FEAT_DIFF
674 || wp->w_topfill != wp->w_ru_topfill
675#endif
676 || empty_line != wp->w_ru_empty)
677 {
678 cursor_off();
679 if (wp->w_status_height)
680 {
681 row = W_WINROW(wp) + wp->w_height;
682 fillchar = fillchar_status(&attr, wp);
683 off = wp->w_wincol;
684 width = wp->w_width;
685 }
686 else
687 {
688 row = Rows - 1;
689 fillchar = ' ';
690 attr = 0;
691 width = Columns;
692 off = 0;
693 }
694
695 // In list mode virtcol needs to be recomputed
696 virtcol = wp->w_virtcol;
697 if (wp->w_p_list && lcs_tab1 == NUL)
698 {
699 wp->w_p_list = FALSE;
700 getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
701 wp->w_p_list = TRUE;
702 }
703
704 /*
705 * Some sprintfs return the length, some return a pointer.
706 * To avoid portability problems we use strlen() here.
707 */
708 vim_snprintf((char *)buffer, RULER_BUF_LEN, "%ld,",
709 (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
710 ? 0L
711 : (long)(wp->w_cursor.lnum));
712 len = STRLEN(buffer);
713 col_print(buffer + len, RULER_BUF_LEN - len,
714 empty_line ? 0 : (int)wp->w_cursor.col + 1,
715 (int)virtcol + 1);
716
717 /*
718 * Add a "50%" if there is room for it.
719 * On the last line, don't print in the last column (scrolls the
720 * screen up on some terminals).
721 */
722 i = (int)STRLEN(buffer);
723 get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1);
724 o = i + vim_strsize(buffer + i + 1);
725 if (wp->w_status_height == 0) // can't use last char of screen
726 ++o;
727 this_ru_col = ru_col - (Columns - width);
728 if (this_ru_col < 0)
729 this_ru_col = 0;
730 // Never use more than half the window/screen width, leave the other
731 // half for the filename.
732 if (this_ru_col < (width + 1) / 2)
733 this_ru_col = (width + 1) / 2;
734 if (this_ru_col + o < width)
735 {
736 // need at least 3 chars left for get_rel_pos() + NUL
737 while (this_ru_col + o < width && RULER_BUF_LEN > i + 4)
738 {
739 if (has_mbyte)
740 i += (*mb_char2bytes)(fillchar, buffer + i);
741 else
742 buffer[i++] = fillchar;
743 ++o;
744 }
745 get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i);
746 }
747 // Truncate at window boundary.
748 if (has_mbyte)
749 {
750 o = 0;
751 for (i = 0; buffer[i] != NUL; i += (*mb_ptr2len)(buffer + i))
752 {
753 o += (*mb_ptr2cells)(buffer + i);
754 if (this_ru_col + o > width)
755 {
756 buffer[i] = NUL;
757 break;
758 }
759 }
760 }
761 else if (this_ru_col + (int)STRLEN(buffer) > width)
762 buffer[width - this_ru_col] = NUL;
763
764 screen_puts(buffer, row, this_ru_col + off, attr);
765 i = redraw_cmdline;
766 screen_fill(row, row + 1,
767 this_ru_col + off + (int)STRLEN(buffer),
768 (int)(off + width),
769 fillchar, fillchar, attr);
770 // don't redraw the cmdline because of showing the ruler
771 redraw_cmdline = i;
772 wp->w_ru_cursor = wp->w_cursor;
773 wp->w_ru_virtcol = wp->w_virtcol;
774 wp->w_ru_empty = empty_line;
775 wp->w_ru_topline = wp->w_topline;
776 wp->w_ru_line_count = wp->w_buffer->b_ml.ml_line_count;
777#ifdef FEAT_DIFF
778 wp->w_ru_topfill = wp->w_topfill;
779#endif
780 }
781}
782#endif
783
784/*
785 * To be called when "updating_screen" was set before and now the postponed
786 * side effects may take place.
787 */
788 void
789after_updating_screen(int may_resize_shell UNUSED)
790{
791 updating_screen = FALSE;
792#ifdef FEAT_GUI
793 if (may_resize_shell)
794 gui_may_resize_shell();
795#endif
796#ifdef FEAT_TERMINAL
797 term_check_channel_closed_recently();
798#endif
799
800#ifdef HAVE_DROP_FILE
801 // If handle_drop() was called while updating_screen was TRUE need to
802 // handle the drop now.
803 handle_any_postponed_drop();
804#endif
805}
806
807/*
808 * Update all windows that are editing the current buffer.
809 */
810 void
811update_curbuf(int type)
812{
813 redraw_curbuf_later(type);
814 update_screen(type);
815}
816
817#if defined(FEAT_MENU) || defined(FEAT_FOLDING)
818/*
819 * Copy "text" to ScreenLines using "attr".
820 * Returns the next screen column.
821 */
822 static int
823text_to_screenline(win_T *wp, char_u *text, int col)
824{
825 int off = (int)(current_ScreenLine - ScreenLines);
826
827 if (has_mbyte)
828 {
829 int cells;
830 int u8c, u8cc[MAX_MCO];
831 int i;
832 int idx;
833 int c_len;
834 char_u *p;
835# ifdef FEAT_ARABIC
836 int prev_c = 0; // previous Arabic character
837 int prev_c1 = 0; // first composing char for prev_c
838# endif
839
840# ifdef FEAT_RIGHTLEFT
841 if (wp->w_p_rl)
842 idx = off;
843 else
844# endif
845 idx = off + col;
846
847 // Store multibyte characters in ScreenLines[] et al. correctly.
848 for (p = text; *p != NUL; )
849 {
850 cells = (*mb_ptr2cells)(p);
851 c_len = (*mb_ptr2len)(p);
852 if (col + cells > wp->w_width
853# ifdef FEAT_RIGHTLEFT
854 - (wp->w_p_rl ? col : 0)
855# endif
856 )
857 break;
858 ScreenLines[idx] = *p;
859 if (enc_utf8)
860 {
861 u8c = utfc_ptr2char(p, u8cc);
862 if (*p < 0x80 && u8cc[0] == 0)
863 {
864 ScreenLinesUC[idx] = 0;
865#ifdef FEAT_ARABIC
866 prev_c = u8c;
867#endif
868 }
869 else
870 {
871#ifdef FEAT_ARABIC
872 if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c))
873 {
874 // Do Arabic shaping.
875 int pc, pc1, nc;
876 int pcc[MAX_MCO];
877 int firstbyte = *p;
878
879 // The idea of what is the previous and next
880 // character depends on 'rightleft'.
881 if (wp->w_p_rl)
882 {
883 pc = prev_c;
884 pc1 = prev_c1;
885 nc = utf_ptr2char(p + c_len);
886 prev_c1 = u8cc[0];
887 }
888 else
889 {
890 pc = utfc_ptr2char(p + c_len, pcc);
891 nc = prev_c;
892 pc1 = pcc[0];
893 }
894 prev_c = u8c;
895
896 u8c = arabic_shape(u8c, &firstbyte, &u8cc[0],
897 pc, pc1, nc);
898 ScreenLines[idx] = firstbyte;
899 }
900 else
901 prev_c = u8c;
902#endif
903 // Non-BMP character: display as ? or fullwidth ?.
904 ScreenLinesUC[idx] = u8c;
905 for (i = 0; i < Screen_mco; ++i)
906 {
907 ScreenLinesC[i][idx] = u8cc[i];
908 if (u8cc[i] == 0)
909 break;
910 }
911 }
912 if (cells > 1)
913 ScreenLines[idx + 1] = 0;
914 }
915 else if (enc_dbcs == DBCS_JPNU && *p == 0x8e)
916 // double-byte single width character
917 ScreenLines2[idx] = p[1];
918 else if (cells > 1)
919 // double-width character
920 ScreenLines[idx + 1] = p[1];
921 col += cells;
922 idx += cells;
923 p += c_len;
924 }
925 }
926 else
927 {
928 int len = (int)STRLEN(text);
929
930 if (len > wp->w_width - col)
931 len = wp->w_width - col;
932 if (len > 0)
933 {
934#ifdef FEAT_RIGHTLEFT
935 if (wp->w_p_rl)
936 mch_memmove(current_ScreenLine, text, len);
937 else
938#endif
939 mch_memmove(current_ScreenLine + col, text, len);
940 col += len;
941 }
942 }
943 return col;
944}
945#endif
946
947#ifdef FEAT_MENU
948/*
949 * Draw the window toolbar.
950 */
951 static void
952redraw_win_toolbar(win_T *wp)
953{
954 vimmenu_T *menu;
955 int item_idx = 0;
956 int item_count = 0;
957 int col = 0;
958 int next_col;
959 int off = (int)(current_ScreenLine - ScreenLines);
960 int fill_attr = syn_name2attr((char_u *)"ToolbarLine");
961 int button_attr = syn_name2attr((char_u *)"ToolbarButton");
962
963 vim_free(wp->w_winbar_items);
964 for (menu = wp->w_winbar->children; menu != NULL; menu = menu->next)
965 ++item_count;
966 wp->w_winbar_items = ALLOC_CLEAR_MULT(winbar_item_T, item_count + 1);
967
968 // TODO: use fewer spaces if there is not enough room
969 for (menu = wp->w_winbar->children;
970 menu != NULL && col < wp->w_width; menu = menu->next)
971 {
972 space_to_screenline(off + col, fill_attr);
973 if (++col >= wp->w_width)
974 break;
975 if (col > 1)
976 {
977 space_to_screenline(off + col, fill_attr);
978 if (++col >= wp->w_width)
979 break;
980 }
981
982 wp->w_winbar_items[item_idx].wb_startcol = col;
983 space_to_screenline(off + col, button_attr);
984 if (++col >= wp->w_width)
985 break;
986
987 next_col = text_to_screenline(wp, menu->name, col);
988 while (col < next_col)
989 {
990 ScreenAttrs[off + col] = button_attr;
991 ++col;
992 }
993 wp->w_winbar_items[item_idx].wb_endcol = col;
994 wp->w_winbar_items[item_idx].wb_menu = menu;
995 ++item_idx;
996
997 if (col >= wp->w_width)
998 break;
999 space_to_screenline(off + col, button_attr);
1000 ++col;
1001 }
1002 while (col < wp->w_width)
1003 {
1004 space_to_screenline(off + col, fill_attr);
1005 ++col;
1006 }
1007 wp->w_winbar_items[item_idx].wb_menu = NULL; // end marker
1008
1009 screen_line(wp->w_winrow, wp->w_wincol, (int)wp->w_width,
1010 (int)wp->w_width, 0);
1011}
1012#endif
1013
1014#if defined(FEAT_FOLDING) || defined(PROTO)
1015/*
1016 * Copy "buf[len]" to ScreenLines["off"] and set attributes to "attr".
1017 */
1018 static void
1019copy_text_attr(
1020 int off,
1021 char_u *buf,
1022 int len,
1023 int attr)
1024{
1025 int i;
1026
1027 mch_memmove(ScreenLines + off, buf, (size_t)len);
1028 if (enc_utf8)
1029 vim_memset(ScreenLinesUC + off, 0, sizeof(u8char_T) * (size_t)len);
1030 for (i = 0; i < len; ++i)
1031 ScreenAttrs[off + i] = attr;
1032}
1033
1034/*
1035 * Display one folded line.
1036 */
1037 static void
1038fold_line(
1039 win_T *wp,
1040 long fold_count,
1041 foldinfo_T *foldinfo,
1042 linenr_T lnum,
1043 int row)
1044{
1045 char_u buf[FOLD_TEXT_LEN];
1046 pos_T *top, *bot;
1047 linenr_T lnume = lnum + fold_count - 1;
1048 int len;
1049 char_u *text;
1050 int fdc;
1051 int col;
1052 int txtcol;
1053 int off = (int)(current_ScreenLine - ScreenLines);
1054 int ri;
1055
1056 // Build the fold line:
1057 // 1. Add the cmdwin_type for the command-line window
1058 // 2. Add the 'foldcolumn'
1059 // 3. Add the 'number' or 'relativenumber' column
1060 // 4. Compose the text
1061 // 5. Add the text
1062 // 6. set highlighting for the Visual area an other text
1063 col = 0;
1064
1065 // 1. Add the cmdwin_type for the command-line window
1066 // Ignores 'rightleft', this window is never right-left.
1067#ifdef FEAT_CMDWIN
1068 if (cmdwin_type != 0 && wp == curwin)
1069 {
1070 ScreenLines[off] = cmdwin_type;
1071 ScreenAttrs[off] = HL_ATTR(HLF_AT);
1072 if (enc_utf8)
1073 ScreenLinesUC[off] = 0;
1074 ++col;
1075 }
1076#endif
1077
1078 // 2. Add the 'foldcolumn'
1079 // Reduce the width when there is not enough space.
1080 fdc = compute_foldcolumn(wp, col);
1081 if (fdc > 0)
1082 {
1083 fill_foldcolumn(buf, wp, TRUE, lnum);
1084#ifdef FEAT_RIGHTLEFT
1085 if (wp->w_p_rl)
1086 {
1087 int i;
1088
1089 copy_text_attr(off + wp->w_width - fdc - col, buf, fdc,
1090 HL_ATTR(HLF_FC));
1091 // reverse the fold column
1092 for (i = 0; i < fdc; ++i)
1093 ScreenLines[off + wp->w_width - i - 1 - col] = buf[i];
1094 }
1095 else
1096#endif
1097 copy_text_attr(off + col, buf, fdc, HL_ATTR(HLF_FC));
1098 col += fdc;
1099 }
1100
1101#ifdef FEAT_RIGHTLEFT
1102# define RL_MEMSET(p, v, l) \
1103 do { \
1104 if (wp->w_p_rl) \
1105 for (ri = 0; ri < l; ++ri) \
1106 ScreenAttrs[off + (wp->w_width - (p) - (l)) + ri] = v; \
1107 else \
1108 for (ri = 0; ri < l; ++ri) \
1109 ScreenAttrs[off + (p) + ri] = v; \
1110 } while (0)
1111#else
1112# define RL_MEMSET(p, v, l) \
1113 do { \
1114 for (ri = 0; ri < l; ++ri) \
1115 ScreenAttrs[off + (p) + ri] = v; \
1116 } while (0)
1117#endif
1118
1119 // Set all attributes of the 'number' or 'relativenumber' column and the
1120 // text
1121 RL_MEMSET(col, HL_ATTR(HLF_FL), wp->w_width - col);
1122
1123#ifdef FEAT_SIGNS
1124 // If signs are being displayed, add two spaces.
1125 if (signcolumn_on(wp))
1126 {
1127 len = wp->w_width - col;
1128 if (len > 0)
1129 {
1130 if (len > 2)
1131 len = 2;
1132# ifdef FEAT_RIGHTLEFT
1133 if (wp->w_p_rl)
1134 // the line number isn't reversed
1135 copy_text_attr(off + wp->w_width - len - col,
1136 (char_u *)" ", len, HL_ATTR(HLF_FL));
1137 else
1138# endif
1139 copy_text_attr(off + col, (char_u *)" ", len, HL_ATTR(HLF_FL));
1140 col += len;
1141 }
1142 }
1143#endif
1144
1145 // 3. Add the 'number' or 'relativenumber' column
1146 if (wp->w_p_nu || wp->w_p_rnu)
1147 {
1148 len = wp->w_width - col;
1149 if (len > 0)
1150 {
1151 int w = number_width(wp);
1152 long num;
1153 char *fmt = "%*ld ";
1154
1155 if (len > w + 1)
1156 len = w + 1;
1157
1158 if (wp->w_p_nu && !wp->w_p_rnu)
1159 // 'number' + 'norelativenumber'
1160 num = (long)lnum;
1161 else
1162 {
1163 // 'relativenumber', don't use negative numbers
1164 num = labs((long)get_cursor_rel_lnum(wp, lnum));
1165 if (num == 0 && wp->w_p_nu && wp->w_p_rnu)
1166 {
1167 // 'number' + 'relativenumber': cursor line shows absolute
1168 // line number
1169 num = lnum;
1170 fmt = "%-*ld ";
1171 }
1172 }
1173
1174 sprintf((char *)buf, fmt, w, num);
1175#ifdef FEAT_RIGHTLEFT
1176 if (wp->w_p_rl)
1177 // the line number isn't reversed
1178 copy_text_attr(off + wp->w_width - len - col, buf, len,
1179 HL_ATTR(HLF_FL));
1180 else
1181#endif
1182 copy_text_attr(off + col, buf, len, HL_ATTR(HLF_FL));
1183 col += len;
1184 }
1185 }
1186
1187 // 4. Compose the folded-line string with 'foldtext', if set.
1188 text = get_foldtext(wp, lnum, lnume, foldinfo, buf);
1189
1190 txtcol = col; // remember where text starts
1191
1192 // 5. move the text to current_ScreenLine. Fill up with "fill_fold".
1193 // Right-left text is put in columns 0 - number-col, normal text is put
1194 // in columns number-col - window-width.
1195 col = text_to_screenline(wp, text, col);
1196
1197 // Fill the rest of the line with the fold filler
1198#ifdef FEAT_RIGHTLEFT
1199 if (wp->w_p_rl)
1200 col -= txtcol;
1201#endif
1202 while (col < wp->w_width
1203#ifdef FEAT_RIGHTLEFT
1204 - (wp->w_p_rl ? txtcol : 0)
1205#endif
1206 )
1207 {
1208 if (enc_utf8)
1209 {
1210 if (fill_fold >= 0x80)
1211 {
1212 ScreenLinesUC[off + col] = fill_fold;
1213 ScreenLinesC[0][off + col] = 0;
1214 ScreenLines[off + col] = 0x80; // avoid storing zero
1215 }
1216 else
1217 {
1218 ScreenLinesUC[off + col] = 0;
1219 ScreenLines[off + col] = fill_fold;
1220 }
1221 col++;
1222 }
1223 else
1224 ScreenLines[off + col++] = fill_fold;
1225 }
1226
1227 if (text != buf)
1228 vim_free(text);
1229
1230 // 6. set highlighting for the Visual area an other text.
1231 // If all folded lines are in the Visual area, highlight the line.
1232 if (VIsual_active && wp->w_buffer == curwin->w_buffer)
1233 {
1234 if (LTOREQ_POS(curwin->w_cursor, VIsual))
1235 {
1236 // Visual is after curwin->w_cursor
1237 top = &curwin->w_cursor;
1238 bot = &VIsual;
1239 }
1240 else
1241 {
1242 // Visual is before curwin->w_cursor
1243 top = &VIsual;
1244 bot = &curwin->w_cursor;
1245 }
1246 if (lnum >= top->lnum
1247 && lnume <= bot->lnum
1248 && (VIsual_mode != 'v'
1249 || ((lnum > top->lnum
1250 || (lnum == top->lnum
1251 && top->col == 0))
1252 && (lnume < bot->lnum
1253 || (lnume == bot->lnum
1254 && (bot->col - (*p_sel == 'e'))
1255 >= (colnr_T)STRLEN(ml_get_buf(wp->w_buffer, lnume, FALSE)))))))
1256 {
1257 if (VIsual_mode == Ctrl_V)
1258 {
1259 // Visual block mode: highlight the chars part of the block
1260 if (wp->w_old_cursor_fcol + txtcol < (colnr_T)wp->w_width)
1261 {
1262 if (wp->w_old_cursor_lcol != MAXCOL
1263 && wp->w_old_cursor_lcol + txtcol
1264 < (colnr_T)wp->w_width)
1265 len = wp->w_old_cursor_lcol;
1266 else
1267 len = wp->w_width - txtcol;
1268 RL_MEMSET(wp->w_old_cursor_fcol + txtcol, HL_ATTR(HLF_V),
1269 len - (int)wp->w_old_cursor_fcol);
1270 }
1271 }
1272 else
1273 {
1274 // Set all attributes of the text
1275 RL_MEMSET(txtcol, HL_ATTR(HLF_V), wp->w_width - txtcol);
1276 }
1277 }
1278 }
1279
1280#ifdef FEAT_SYN_HL
1281 // Show colorcolumn in the fold line, but let cursorcolumn override it.
1282 if (wp->w_p_cc_cols)
1283 {
1284 int i = 0;
1285 int j = wp->w_p_cc_cols[i];
1286 int old_txtcol = txtcol;
1287
1288 while (j > -1)
1289 {
1290 txtcol += j;
1291 if (wp->w_p_wrap)
1292 txtcol -= wp->w_skipcol;
1293 else
1294 txtcol -= wp->w_leftcol;
1295 if (txtcol >= 0 && txtcol < wp->w_width)
1296 ScreenAttrs[off + txtcol] = hl_combine_attr(
1297 ScreenAttrs[off + txtcol], HL_ATTR(HLF_MC));
1298 txtcol = old_txtcol;
1299 j = wp->w_p_cc_cols[++i];
1300 }
1301 }
1302
1303 // Show 'cursorcolumn' in the fold line.
1304 if (wp->w_p_cuc)
1305 {
1306 txtcol += wp->w_virtcol;
1307 if (wp->w_p_wrap)
1308 txtcol -= wp->w_skipcol;
1309 else
1310 txtcol -= wp->w_leftcol;
1311 if (txtcol >= 0 && txtcol < wp->w_width)
1312 ScreenAttrs[off + txtcol] = hl_combine_attr(
1313 ScreenAttrs[off + txtcol], HL_ATTR(HLF_CUC));
1314 }
1315#endif
1316
1317 screen_line(row + W_WINROW(wp), wp->w_wincol, (int)wp->w_width,
1318 (int)wp->w_width, 0);
1319
1320 // Update w_cline_height and w_cline_folded if the cursor line was
1321 // updated (saves a call to plines() later).
1322 if (wp == curwin
1323 && lnum <= curwin->w_cursor.lnum
1324 && lnume >= curwin->w_cursor.lnum)
1325 {
1326 curwin->w_cline_row = row;
1327 curwin->w_cline_height = 1;
1328 curwin->w_cline_folded = TRUE;
1329 curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
1330 }
1331}
1332#endif
1333
1334/*
1335 * Update a single window.
1336 *
1337 * This may cause the windows below it also to be redrawn (when clearing the
1338 * screen or scrolling lines).
1339 *
1340 * How the window is redrawn depends on wp->w_redr_type. Each type also
1341 * implies the one below it.
1342 * NOT_VALID redraw the whole window
1343 * SOME_VALID redraw the whole window but do scroll when possible
1344 * REDRAW_TOP redraw the top w_upd_rows window lines, otherwise like VALID
1345 * INVERTED redraw the changed part of the Visual area
1346 * INVERTED_ALL redraw the whole Visual area
1347 * VALID 1. scroll up/down to adjust for a changed w_topline
1348 * 2. update lines at the top when scrolled down
1349 * 3. redraw changed text:
1350 * - if wp->w_buffer->b_mod_set set, update lines between
1351 * b_mod_top and b_mod_bot.
1352 * - if wp->w_redraw_top non-zero, redraw lines between
1353 * wp->w_redraw_top and wp->w_redr_bot.
1354 * - continue redrawing when syntax status is invalid.
1355 * 4. if scrolled up, update lines at the bottom.
1356 * This results in three areas that may need updating:
1357 * top: from first row to top_end (when scrolled down)
1358 * mid: from mid_start to mid_end (update inversion or changed text)
1359 * bot: from bot_start to last row (when scrolled up)
1360 */
1361 static void
1362win_update(win_T *wp)
1363{
1364 buf_T *buf = wp->w_buffer;
1365 int type;
1366 int top_end = 0; // Below last row of the top area that needs
1367 // updating. 0 when no top area updating.
1368 int mid_start = 999;// first row of the mid area that needs
1369 // updating. 999 when no mid area updating.
1370 int mid_end = 0; // Below last row of the mid area that needs
1371 // updating. 0 when no mid area updating.
1372 int bot_start = 999;// first row of the bot area that needs
1373 // updating. 999 when no bot area updating
1374 int scrolled_down = FALSE; // TRUE when scrolled down when
1375 // w_topline got smaller a bit
1376#ifdef FEAT_SEARCH_EXTRA
1377 int top_to_mod = FALSE; // redraw above mod_top
1378#endif
1379
1380 int row; // current window row to display
1381 linenr_T lnum; // current buffer lnum to display
1382 int idx; // current index in w_lines[]
1383 int srow; // starting row of the current line
1384
1385 int eof = FALSE; // if TRUE, we hit the end of the file
1386 int didline = FALSE; // if TRUE, we finished the last line
1387 int i;
1388 long j;
1389 static int recursive = FALSE; // being called recursively
Bram Moolenaarcbee6352019-11-12 20:49:15 +01001390 linenr_T old_botline = wp->w_botline;
1391#ifdef FEAT_CONCEAL
1392 int old_wrow = wp->w_wrow;
1393 int old_wcol = wp->w_wcol;
1394#endif
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001395#ifdef FEAT_FOLDING
1396 long fold_count;
1397#endif
1398#ifdef FEAT_SYN_HL
1399 // remember what happened to the previous line, to know if
1400 // check_visual_highlight() can be used
1401#define DID_NONE 1 // didn't update a line
1402#define DID_LINE 2 // updated a normal line
1403#define DID_FOLD 3 // updated a folded line
1404 int did_update = DID_NONE;
1405 linenr_T syntax_last_parsed = 0; // last parsed text line
1406#endif
1407 linenr_T mod_top = 0;
1408 linenr_T mod_bot = 0;
1409#if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA)
1410 int save_got_int;
1411#endif
1412#ifdef SYN_TIME_LIMIT
1413 proftime_T syntax_tm;
1414#endif
1415
Bram Moolenaare52e0c82020-02-28 22:20:10 +01001416#if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_CLIPBOARD)
1417 // This needs to be done only for the first window when update_screen() is
1418 // called.
1419 if (!did_update_one_window)
1420 {
1421 did_update_one_window = TRUE;
1422# ifdef FEAT_SEARCH_EXTRA
1423 start_search_hl();
1424# endif
1425# ifdef FEAT_CLIPBOARD
1426 // When Visual area changed, may have to update selection.
1427 if (clip_star.available && clip_isautosel_star())
1428 clip_update_selection(&clip_star);
1429 if (clip_plus.available && clip_isautosel_plus())
1430 clip_update_selection(&clip_plus);
1431# endif
1432 }
1433#endif
1434
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001435 type = wp->w_redr_type;
1436
1437 if (type == NOT_VALID)
1438 {
1439 wp->w_redr_status = TRUE;
1440 wp->w_lines_valid = 0;
1441 }
1442
1443 // Window is zero-height: nothing to draw.
1444 if (wp->w_height + WINBAR_HEIGHT(wp) == 0)
1445 {
1446 wp->w_redr_type = 0;
1447 return;
1448 }
1449
1450 // Window is zero-width: Only need to draw the separator.
1451 if (wp->w_width == 0)
1452 {
1453 // draw the vertical separator right of this window
1454 draw_vsep_win(wp, 0);
1455 wp->w_redr_type = 0;
1456 return;
1457 }
1458
1459#ifdef FEAT_TERMINAL
1460 // If this window contains a terminal, redraw works completely differently.
1461 if (term_do_update_window(wp))
1462 {
1463 term_update_window(wp);
1464# ifdef FEAT_MENU
1465 // Draw the window toolbar, if there is one.
1466 if (winbar_height(wp) > 0)
1467 redraw_win_toolbar(wp);
1468# endif
1469 wp->w_redr_type = 0;
1470 return;
1471 }
1472#endif
1473
1474#ifdef FEAT_SEARCH_EXTRA
1475 init_search_hl(wp, &screen_search_hl);
1476#endif
1477
1478#ifdef FEAT_LINEBREAK
1479 // Force redraw when width of 'number' or 'relativenumber' column
1480 // changes.
1481 i = (wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) : 0;
1482 if (wp->w_nrwidth != i)
1483 {
1484 type = NOT_VALID;
1485 wp->w_nrwidth = i;
1486 }
1487 else
1488#endif
1489
1490 if (buf->b_mod_set && buf->b_mod_xlines != 0 && wp->w_redraw_top != 0)
1491 {
1492 // When there are both inserted/deleted lines and specific lines to be
1493 // redrawn, w_redraw_top and w_redraw_bot may be invalid, just redraw
1494 // everything (only happens when redrawing is off for while).
1495 type = NOT_VALID;
1496 }
1497 else
1498 {
1499 // Set mod_top to the first line that needs displaying because of
1500 // changes. Set mod_bot to the first line after the changes.
1501 mod_top = wp->w_redraw_top;
1502 if (wp->w_redraw_bot != 0)
1503 mod_bot = wp->w_redraw_bot + 1;
1504 else
1505 mod_bot = 0;
1506 if (buf->b_mod_set)
1507 {
1508 if (mod_top == 0 || mod_top > buf->b_mod_top)
1509 {
1510 mod_top = buf->b_mod_top;
1511#ifdef FEAT_SYN_HL
1512 // Need to redraw lines above the change that may be included
1513 // in a pattern match.
1514 if (syntax_present(wp))
1515 {
1516 mod_top -= buf->b_s.b_syn_sync_linebreaks;
1517 if (mod_top < 1)
1518 mod_top = 1;
1519 }
1520#endif
1521 }
1522 if (mod_bot == 0 || mod_bot < buf->b_mod_bot)
1523 mod_bot = buf->b_mod_bot;
1524
1525#ifdef FEAT_SEARCH_EXTRA
1526 // When 'hlsearch' is on and using a multi-line search pattern, a
1527 // change in one line may make the Search highlighting in a
1528 // previous line invalid. Simple solution: redraw all visible
1529 // lines above the change.
1530 // Same for a match pattern.
1531 if (screen_search_hl.rm.regprog != NULL
1532 && re_multiline(screen_search_hl.rm.regprog))
1533 top_to_mod = TRUE;
1534 else
1535 {
1536 matchitem_T *cur = wp->w_match_head;
1537
1538 while (cur != NULL)
1539 {
1540 if (cur->match.regprog != NULL
1541 && re_multiline(cur->match.regprog))
1542 {
1543 top_to_mod = TRUE;
1544 break;
1545 }
1546 cur = cur->next;
1547 }
1548 }
1549#endif
1550 }
1551#ifdef FEAT_FOLDING
1552 if (mod_top != 0 && hasAnyFolding(wp))
1553 {
1554 linenr_T lnumt, lnumb;
1555
1556 // A change in a line can cause lines above it to become folded or
1557 // unfolded. Find the top most buffer line that may be affected.
1558 // If the line was previously folded and displayed, get the first
1559 // line of that fold. If the line is folded now, get the first
1560 // folded line. Use the minimum of these two.
1561
1562 // Find last valid w_lines[] entry above mod_top. Set lnumt to
1563 // the line below it. If there is no valid entry, use w_topline.
1564 // Find the first valid w_lines[] entry below mod_bot. Set lnumb
1565 // to this line. If there is no valid entry, use MAXLNUM.
1566 lnumt = wp->w_topline;
1567 lnumb = MAXLNUM;
1568 for (i = 0; i < wp->w_lines_valid; ++i)
1569 if (wp->w_lines[i].wl_valid)
1570 {
1571 if (wp->w_lines[i].wl_lastlnum < mod_top)
1572 lnumt = wp->w_lines[i].wl_lastlnum + 1;
1573 if (lnumb == MAXLNUM && wp->w_lines[i].wl_lnum >= mod_bot)
1574 {
1575 lnumb = wp->w_lines[i].wl_lnum;
1576 // When there is a fold column it might need updating
1577 // in the next line ("J" just above an open fold).
1578 if (compute_foldcolumn(wp, 0) > 0)
1579 ++lnumb;
1580 }
1581 }
1582
1583 (void)hasFoldingWin(wp, mod_top, &mod_top, NULL, TRUE, NULL);
1584 if (mod_top > lnumt)
1585 mod_top = lnumt;
1586
1587 // Now do the same for the bottom line (one above mod_bot).
1588 --mod_bot;
1589 (void)hasFoldingWin(wp, mod_bot, NULL, &mod_bot, TRUE, NULL);
1590 ++mod_bot;
1591 if (mod_bot < lnumb)
1592 mod_bot = lnumb;
1593 }
1594#endif
1595
1596 // When a change starts above w_topline and the end is below
1597 // w_topline, start redrawing at w_topline.
1598 // If the end of the change is above w_topline: do like no change was
1599 // made, but redraw the first line to find changes in syntax.
1600 if (mod_top != 0 && mod_top < wp->w_topline)
1601 {
1602 if (mod_bot > wp->w_topline)
1603 mod_top = wp->w_topline;
1604#ifdef FEAT_SYN_HL
1605 else if (syntax_present(wp))
1606 top_end = 1;
1607#endif
1608 }
1609
1610 // When line numbers are displayed need to redraw all lines below
1611 // inserted/deleted lines.
1612 if (mod_top != 0 && buf->b_mod_xlines != 0 && wp->w_p_nu)
1613 mod_bot = MAXLNUM;
1614 }
1615 wp->w_redraw_top = 0; // reset for next time
1616 wp->w_redraw_bot = 0;
1617
1618 // When only displaying the lines at the top, set top_end. Used when
1619 // window has scrolled down for msg_scrolled.
1620 if (type == REDRAW_TOP)
1621 {
1622 j = 0;
1623 for (i = 0; i < wp->w_lines_valid; ++i)
1624 {
1625 j += wp->w_lines[i].wl_size;
1626 if (j >= wp->w_upd_rows)
1627 {
1628 top_end = j;
1629 break;
1630 }
1631 }
1632 if (top_end == 0)
1633 // not found (cannot happen?): redraw everything
1634 type = NOT_VALID;
1635 else
1636 // top area defined, the rest is VALID
1637 type = VALID;
1638 }
1639
1640 // Trick: we want to avoid clearing the screen twice. screenclear() will
1641 // set "screen_cleared" to TRUE. The special value MAYBE (which is still
1642 // non-zero and thus not FALSE) will indicate that screenclear() was not
1643 // called.
1644 if (screen_cleared)
1645 screen_cleared = MAYBE;
1646
1647 // If there are no changes on the screen that require a complete redraw,
1648 // handle three cases:
1649 // 1: we are off the top of the screen by a few lines: scroll down
1650 // 2: wp->w_topline is below wp->w_lines[0].wl_lnum: may scroll up
1651 // 3: wp->w_topline is wp->w_lines[0].wl_lnum: find first entry in
1652 // w_lines[] that needs updating.
1653 if ((type == VALID || type == SOME_VALID
1654 || type == INVERTED || type == INVERTED_ALL)
1655#ifdef FEAT_DIFF
1656 && !wp->w_botfill && !wp->w_old_botfill
1657#endif
1658 )
1659 {
1660 if (mod_top != 0 && wp->w_topline == mod_top)
1661 {
1662 // w_topline is the first changed line, the scrolling will be done
1663 // further down.
1664 }
1665 else if (wp->w_lines[0].wl_valid
1666 && (wp->w_topline < wp->w_lines[0].wl_lnum
1667#ifdef FEAT_DIFF
1668 || (wp->w_topline == wp->w_lines[0].wl_lnum
1669 && wp->w_topfill > wp->w_old_topfill)
1670#endif
1671 ))
1672 {
1673 // New topline is above old topline: May scroll down.
1674#ifdef FEAT_FOLDING
1675 if (hasAnyFolding(wp))
1676 {
1677 linenr_T ln;
1678
1679 // count the number of lines we are off, counting a sequence
1680 // of folded lines as one
1681 j = 0;
1682 for (ln = wp->w_topline; ln < wp->w_lines[0].wl_lnum; ++ln)
1683 {
1684 ++j;
1685 if (j >= wp->w_height - 2)
1686 break;
1687 (void)hasFoldingWin(wp, ln, NULL, &ln, TRUE, NULL);
1688 }
1689 }
1690 else
1691#endif
1692 j = wp->w_lines[0].wl_lnum - wp->w_topline;
1693 if (j < wp->w_height - 2) // not too far off
1694 {
1695 i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1);
1696#ifdef FEAT_DIFF
1697 // insert extra lines for previously invisible filler lines
1698 if (wp->w_lines[0].wl_lnum != wp->w_topline)
1699 i += diff_check_fill(wp, wp->w_lines[0].wl_lnum)
1700 - wp->w_old_topfill;
1701#endif
1702 if (i < wp->w_height - 2) // less than a screen off
1703 {
1704 // Try to insert the correct number of lines.
1705 // If not the last window, delete the lines at the bottom.
1706 // win_ins_lines may fail when the terminal can't do it.
1707 if (i > 0)
1708 check_for_delay(FALSE);
1709 if (win_ins_lines(wp, 0, i, FALSE, wp == firstwin) == OK)
1710 {
1711 if (wp->w_lines_valid != 0)
1712 {
1713 // Need to update rows that are new, stop at the
1714 // first one that scrolled down.
1715 top_end = i;
1716 scrolled_down = TRUE;
1717
1718 // Move the entries that were scrolled, disable
1719 // the entries for the lines to be redrawn.
1720 if ((wp->w_lines_valid += j) > wp->w_height)
1721 wp->w_lines_valid = wp->w_height;
1722 for (idx = wp->w_lines_valid; idx - j >= 0; idx--)
1723 wp->w_lines[idx] = wp->w_lines[idx - j];
1724 while (idx >= 0)
1725 wp->w_lines[idx--].wl_valid = FALSE;
1726 }
1727 }
1728 else
1729 mid_start = 0; // redraw all lines
1730 }
1731 else
1732 mid_start = 0; // redraw all lines
1733 }
1734 else
1735 mid_start = 0; // redraw all lines
1736 }
1737 else
1738 {
1739 // New topline is at or below old topline: May scroll up.
1740 // When topline didn't change, find first entry in w_lines[] that
1741 // needs updating.
1742
1743 // try to find wp->w_topline in wp->w_lines[].wl_lnum
1744 j = -1;
1745 row = 0;
1746 for (i = 0; i < wp->w_lines_valid; i++)
1747 {
1748 if (wp->w_lines[i].wl_valid
1749 && wp->w_lines[i].wl_lnum == wp->w_topline)
1750 {
1751 j = i;
1752 break;
1753 }
1754 row += wp->w_lines[i].wl_size;
1755 }
1756 if (j == -1)
1757 {
1758 // if wp->w_topline is not in wp->w_lines[].wl_lnum redraw all
1759 // lines
1760 mid_start = 0;
1761 }
1762 else
1763 {
1764 // Try to delete the correct number of lines.
1765 // wp->w_topline is at wp->w_lines[i].wl_lnum.
1766#ifdef FEAT_DIFF
1767 // If the topline didn't change, delete old filler lines,
1768 // otherwise delete filler lines of the new topline...
1769 if (wp->w_lines[0].wl_lnum == wp->w_topline)
1770 row += wp->w_old_topfill;
1771 else
1772 row += diff_check_fill(wp, wp->w_topline);
1773 // ... but don't delete new filler lines.
1774 row -= wp->w_topfill;
1775#endif
1776 if (row > 0)
1777 {
1778 check_for_delay(FALSE);
1779 if (win_del_lines(wp, 0, row, FALSE, wp == firstwin, 0)
1780 == OK)
1781 bot_start = wp->w_height - row;
1782 else
1783 mid_start = 0; // redraw all lines
1784 }
1785 if ((row == 0 || bot_start < 999) && wp->w_lines_valid != 0)
1786 {
1787 // Skip the lines (below the deleted lines) that are still
1788 // valid and don't need redrawing. Copy their info
1789 // upwards, to compensate for the deleted lines. Set
1790 // bot_start to the first row that needs redrawing.
1791 bot_start = 0;
1792 idx = 0;
1793 for (;;)
1794 {
1795 wp->w_lines[idx] = wp->w_lines[j];
1796 // stop at line that didn't fit, unless it is still
1797 // valid (no lines deleted)
1798 if (row > 0 && bot_start + row
1799 + (int)wp->w_lines[j].wl_size > wp->w_height)
1800 {
1801 wp->w_lines_valid = idx + 1;
1802 break;
1803 }
1804 bot_start += wp->w_lines[idx++].wl_size;
1805
1806 // stop at the last valid entry in w_lines[].wl_size
1807 if (++j >= wp->w_lines_valid)
1808 {
1809 wp->w_lines_valid = idx;
1810 break;
1811 }
1812 }
1813#ifdef FEAT_DIFF
1814 // Correct the first entry for filler lines at the top
1815 // when it won't get updated below.
1816 if (wp->w_p_diff && bot_start > 0)
1817 wp->w_lines[0].wl_size =
1818 plines_win_nofill(wp, wp->w_topline, TRUE)
1819 + wp->w_topfill;
1820#endif
1821 }
1822 }
1823 }
1824
1825 // When starting redraw in the first line, redraw all lines. When
1826 // there is only one window it's probably faster to clear the screen
1827 // first.
1828 if (mid_start == 0)
1829 {
1830 mid_end = wp->w_height;
1831 if (ONE_WINDOW && !WIN_IS_POPUP(wp))
1832 {
1833 // Clear the screen when it was not done by win_del_lines() or
1834 // win_ins_lines() above, "screen_cleared" is FALSE or MAYBE
1835 // then.
1836 if (screen_cleared != TRUE)
1837 screenclear();
1838 // The screen was cleared, redraw the tab pages line.
1839 if (redraw_tabline)
1840 draw_tabline();
1841 }
1842 }
1843
1844 // When win_del_lines() or win_ins_lines() caused the screen to be
1845 // cleared (only happens for the first window) or when screenclear()
1846 // was called directly above, "must_redraw" will have been set to
1847 // NOT_VALID, need to reset it here to avoid redrawing twice.
1848 if (screen_cleared == TRUE)
1849 must_redraw = 0;
1850 }
1851 else
1852 {
1853 // Not VALID or INVERTED: redraw all lines.
1854 mid_start = 0;
1855 mid_end = wp->w_height;
1856 }
1857
1858 if (type == SOME_VALID)
1859 {
1860 // SOME_VALID: redraw all lines.
1861 mid_start = 0;
1862 mid_end = wp->w_height;
1863 type = NOT_VALID;
1864 }
1865
1866 // check if we are updating or removing the inverted part
1867 if ((VIsual_active && buf == curwin->w_buffer)
1868 || (wp->w_old_cursor_lnum != 0 && type != NOT_VALID))
1869 {
1870 linenr_T from, to;
1871
1872 if (VIsual_active)
1873 {
1874 if (VIsual_active
1875 && (VIsual_mode != wp->w_old_visual_mode
1876 || type == INVERTED_ALL))
1877 {
1878 // If the type of Visual selection changed, redraw the whole
1879 // selection. Also when the ownership of the X selection is
1880 // gained or lost.
1881 if (curwin->w_cursor.lnum < VIsual.lnum)
1882 {
1883 from = curwin->w_cursor.lnum;
1884 to = VIsual.lnum;
1885 }
1886 else
1887 {
1888 from = VIsual.lnum;
1889 to = curwin->w_cursor.lnum;
1890 }
1891 // redraw more when the cursor moved as well
1892 if (wp->w_old_cursor_lnum < from)
1893 from = wp->w_old_cursor_lnum;
1894 if (wp->w_old_cursor_lnum > to)
1895 to = wp->w_old_cursor_lnum;
1896 if (wp->w_old_visual_lnum < from)
1897 from = wp->w_old_visual_lnum;
1898 if (wp->w_old_visual_lnum > to)
1899 to = wp->w_old_visual_lnum;
1900 }
1901 else
1902 {
1903 // Find the line numbers that need to be updated: The lines
1904 // between the old cursor position and the current cursor
1905 // position. Also check if the Visual position changed.
1906 if (curwin->w_cursor.lnum < wp->w_old_cursor_lnum)
1907 {
1908 from = curwin->w_cursor.lnum;
1909 to = wp->w_old_cursor_lnum;
1910 }
1911 else
1912 {
1913 from = wp->w_old_cursor_lnum;
1914 to = curwin->w_cursor.lnum;
1915 if (from == 0) // Visual mode just started
1916 from = to;
1917 }
1918
1919 if (VIsual.lnum != wp->w_old_visual_lnum
1920 || VIsual.col != wp->w_old_visual_col)
1921 {
1922 if (wp->w_old_visual_lnum < from
1923 && wp->w_old_visual_lnum != 0)
1924 from = wp->w_old_visual_lnum;
1925 if (wp->w_old_visual_lnum > to)
1926 to = wp->w_old_visual_lnum;
1927 if (VIsual.lnum < from)
1928 from = VIsual.lnum;
1929 if (VIsual.lnum > to)
1930 to = VIsual.lnum;
1931 }
1932 }
1933
1934 // If in block mode and changed column or curwin->w_curswant:
1935 // update all lines.
1936 // First compute the actual start and end column.
1937 if (VIsual_mode == Ctrl_V)
1938 {
1939 colnr_T fromc, toc;
1940#if defined(FEAT_LINEBREAK)
1941 int save_ve_flags = ve_flags;
1942
1943 if (curwin->w_p_lbr)
1944 ve_flags = VE_ALL;
1945#endif
1946 getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc);
1947#if defined(FEAT_LINEBREAK)
1948 ve_flags = save_ve_flags;
1949#endif
1950 ++toc;
1951 if (curwin->w_curswant == MAXCOL)
1952 toc = MAXCOL;
1953
1954 if (fromc != wp->w_old_cursor_fcol
1955 || toc != wp->w_old_cursor_lcol)
1956 {
1957 if (from > VIsual.lnum)
1958 from = VIsual.lnum;
1959 if (to < VIsual.lnum)
1960 to = VIsual.lnum;
1961 }
1962 wp->w_old_cursor_fcol = fromc;
1963 wp->w_old_cursor_lcol = toc;
1964 }
1965 }
1966 else
1967 {
1968 // Use the line numbers of the old Visual area.
1969 if (wp->w_old_cursor_lnum < wp->w_old_visual_lnum)
1970 {
1971 from = wp->w_old_cursor_lnum;
1972 to = wp->w_old_visual_lnum;
1973 }
1974 else
1975 {
1976 from = wp->w_old_visual_lnum;
1977 to = wp->w_old_cursor_lnum;
1978 }
1979 }
1980
1981 // There is no need to update lines above the top of the window.
1982 if (from < wp->w_topline)
1983 from = wp->w_topline;
1984
1985 // If we know the value of w_botline, use it to restrict the update to
1986 // the lines that are visible in the window.
1987 if (wp->w_valid & VALID_BOTLINE)
1988 {
1989 if (from >= wp->w_botline)
1990 from = wp->w_botline - 1;
1991 if (to >= wp->w_botline)
1992 to = wp->w_botline - 1;
1993 }
1994
1995 // Find the minimal part to be updated.
1996 // Watch out for scrolling that made entries in w_lines[] invalid.
1997 // E.g., CTRL-U makes the first half of w_lines[] invalid and sets
1998 // top_end; need to redraw from top_end to the "to" line.
1999 // A middle mouse click with a Visual selection may change the text
2000 // above the Visual area and reset wl_valid, do count these for
2001 // mid_end (in srow).
2002 if (mid_start > 0)
2003 {
2004 lnum = wp->w_topline;
2005 idx = 0;
2006 srow = 0;
2007 if (scrolled_down)
2008 mid_start = top_end;
2009 else
2010 mid_start = 0;
2011 while (lnum < from && idx < wp->w_lines_valid) // find start
2012 {
2013 if (wp->w_lines[idx].wl_valid)
2014 mid_start += wp->w_lines[idx].wl_size;
2015 else if (!scrolled_down)
2016 srow += wp->w_lines[idx].wl_size;
2017 ++idx;
2018# ifdef FEAT_FOLDING
2019 if (idx < wp->w_lines_valid && wp->w_lines[idx].wl_valid)
2020 lnum = wp->w_lines[idx].wl_lnum;
2021 else
2022# endif
2023 ++lnum;
2024 }
2025 srow += mid_start;
2026 mid_end = wp->w_height;
2027 for ( ; idx < wp->w_lines_valid; ++idx) // find end
2028 {
2029 if (wp->w_lines[idx].wl_valid
2030 && wp->w_lines[idx].wl_lnum >= to + 1)
2031 {
2032 // Only update until first row of this line
2033 mid_end = srow;
2034 break;
2035 }
2036 srow += wp->w_lines[idx].wl_size;
2037 }
2038 }
2039 }
2040
2041 if (VIsual_active && buf == curwin->w_buffer)
2042 {
2043 wp->w_old_visual_mode = VIsual_mode;
2044 wp->w_old_cursor_lnum = curwin->w_cursor.lnum;
2045 wp->w_old_visual_lnum = VIsual.lnum;
2046 wp->w_old_visual_col = VIsual.col;
2047 wp->w_old_curswant = curwin->w_curswant;
2048 }
2049 else
2050 {
2051 wp->w_old_visual_mode = 0;
2052 wp->w_old_cursor_lnum = 0;
2053 wp->w_old_visual_lnum = 0;
2054 wp->w_old_visual_col = 0;
2055 }
2056
2057#if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA)
2058 // reset got_int, otherwise regexp won't work
2059 save_got_int = got_int;
2060 got_int = 0;
2061#endif
2062#ifdef SYN_TIME_LIMIT
2063 // Set the time limit to 'redrawtime'.
2064 profile_setlimit(p_rdt, &syntax_tm);
2065 syn_set_timeout(&syntax_tm);
2066#endif
2067#ifdef FEAT_FOLDING
2068 win_foldinfo.fi_level = 0;
2069#endif
2070
2071#ifdef FEAT_MENU
2072 // Draw the window toolbar, if there is one.
2073 // TODO: only when needed.
2074 if (winbar_height(wp) > 0)
2075 redraw_win_toolbar(wp);
2076#endif
2077
2078 // Update all the window rows.
2079 idx = 0; // first entry in w_lines[].wl_size
2080 row = 0;
2081 srow = 0;
2082 lnum = wp->w_topline; // first line shown in window
2083 for (;;)
2084 {
2085 // stop updating when reached the end of the window (check for _past_
2086 // the end of the window is at the end of the loop)
2087 if (row == wp->w_height)
2088 {
2089 didline = TRUE;
2090 break;
2091 }
2092
2093 // stop updating when hit the end of the file
2094 if (lnum > buf->b_ml.ml_line_count)
2095 {
2096 eof = TRUE;
2097 break;
2098 }
2099
2100 // Remember the starting row of the line that is going to be dealt
2101 // with. It is used further down when the line doesn't fit.
2102 srow = row;
2103
2104 // Update a line when it is in an area that needs updating, when it
2105 // has changes or w_lines[idx] is invalid.
2106 // "bot_start" may be halfway a wrapped line after using
2107 // win_del_lines(), check if the current line includes it.
2108 // When syntax folding is being used, the saved syntax states will
2109 // already have been updated, we can't see where the syntax state is
2110 // the same again, just update until the end of the window.
2111 if (row < top_end
2112 || (row >= mid_start && row < mid_end)
2113#ifdef FEAT_SEARCH_EXTRA
2114 || top_to_mod
2115#endif
2116 || idx >= wp->w_lines_valid
2117 || (row + wp->w_lines[idx].wl_size > bot_start)
2118 || (mod_top != 0
2119 && (lnum == mod_top
2120 || (lnum >= mod_top
2121 && (lnum < mod_bot
2122#ifdef FEAT_SYN_HL
2123 || did_update == DID_FOLD
2124 || (did_update == DID_LINE
2125 && syntax_present(wp)
2126 && (
2127# ifdef FEAT_FOLDING
2128 (foldmethodIsSyntax(wp)
2129 && hasAnyFolding(wp)) ||
2130# endif
2131 syntax_check_changed(lnum)))
2132#endif
2133#ifdef FEAT_SEARCH_EXTRA
2134 // match in fixed position might need redraw
2135 // if lines were inserted or deleted
2136 || (wp->w_match_head != NULL
2137 && buf->b_mod_xlines != 0)
2138#endif
Bram Moolenaar11a58af2019-10-24 22:32:31 +02002139 ))))
2140#ifdef FEAT_SYN_HL
2141 || (wp->w_p_cul && (lnum == wp->w_cursor.lnum
2142 || lnum == wp->w_last_cursorline))
2143#endif
2144 )
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02002145 {
2146#ifdef FEAT_SEARCH_EXTRA
2147 if (lnum == mod_top)
2148 top_to_mod = FALSE;
2149#endif
2150
2151 // When at start of changed lines: May scroll following lines
2152 // up or down to minimize redrawing.
2153 // Don't do this when the change continues until the end.
2154 // Don't scroll when dollar_vcol >= 0, keep the "$".
2155 if (lnum == mod_top
2156 && mod_bot != MAXLNUM
2157 && !(dollar_vcol >= 0 && mod_bot == mod_top + 1))
2158 {
2159 int old_rows = 0;
2160 int new_rows = 0;
2161 int xtra_rows;
2162 linenr_T l;
2163
2164 // Count the old number of window rows, using w_lines[], which
2165 // should still contain the sizes for the lines as they are
2166 // currently displayed.
2167 for (i = idx; i < wp->w_lines_valid; ++i)
2168 {
2169 // Only valid lines have a meaningful wl_lnum. Invalid
2170 // lines are part of the changed area.
2171 if (wp->w_lines[i].wl_valid
2172 && wp->w_lines[i].wl_lnum == mod_bot)
2173 break;
2174 old_rows += wp->w_lines[i].wl_size;
2175#ifdef FEAT_FOLDING
2176 if (wp->w_lines[i].wl_valid
2177 && wp->w_lines[i].wl_lastlnum + 1 == mod_bot)
2178 {
2179 // Must have found the last valid entry above mod_bot.
2180 // Add following invalid entries.
2181 ++i;
2182 while (i < wp->w_lines_valid
2183 && !wp->w_lines[i].wl_valid)
2184 old_rows += wp->w_lines[i++].wl_size;
2185 break;
2186 }
2187#endif
2188 }
2189
2190 if (i >= wp->w_lines_valid)
2191 {
2192 // We can't find a valid line below the changed lines,
2193 // need to redraw until the end of the window.
2194 // Inserting/deleting lines has no use.
2195 bot_start = 0;
2196 }
2197 else
2198 {
2199 // Able to count old number of rows: Count new window
2200 // rows, and may insert/delete lines
2201 j = idx;
2202 for (l = lnum; l < mod_bot; ++l)
2203 {
2204#ifdef FEAT_FOLDING
2205 if (hasFoldingWin(wp, l, NULL, &l, TRUE, NULL))
2206 ++new_rows;
2207 else
2208#endif
2209#ifdef FEAT_DIFF
2210 if (l == wp->w_topline)
2211 new_rows += plines_win_nofill(wp, l, TRUE)
2212 + wp->w_topfill;
2213 else
2214#endif
2215 new_rows += plines_win(wp, l, TRUE);
2216 ++j;
2217 if (new_rows > wp->w_height - row - 2)
2218 {
2219 // it's getting too much, must redraw the rest
2220 new_rows = 9999;
2221 break;
2222 }
2223 }
2224 xtra_rows = new_rows - old_rows;
2225 if (xtra_rows < 0)
2226 {
2227 // May scroll text up. If there is not enough
2228 // remaining text or scrolling fails, must redraw the
2229 // rest. If scrolling works, must redraw the text
2230 // below the scrolled text.
2231 if (row - xtra_rows >= wp->w_height - 2)
2232 mod_bot = MAXLNUM;
2233 else
2234 {
2235 check_for_delay(FALSE);
2236 if (win_del_lines(wp, row,
2237 -xtra_rows, FALSE, FALSE, 0) == FAIL)
2238 mod_bot = MAXLNUM;
2239 else
2240 bot_start = wp->w_height + xtra_rows;
2241 }
2242 }
2243 else if (xtra_rows > 0)
2244 {
2245 // May scroll text down. If there is not enough
2246 // remaining text of scrolling fails, must redraw the
2247 // rest.
2248 if (row + xtra_rows >= wp->w_height - 2)
2249 mod_bot = MAXLNUM;
2250 else
2251 {
2252 check_for_delay(FALSE);
2253 if (win_ins_lines(wp, row + old_rows,
2254 xtra_rows, FALSE, FALSE) == FAIL)
2255 mod_bot = MAXLNUM;
2256 else if (top_end > row + old_rows)
2257 // Scrolled the part at the top that requires
2258 // updating down.
2259 top_end += xtra_rows;
2260 }
2261 }
2262
2263 // When not updating the rest, may need to move w_lines[]
2264 // entries.
2265 if (mod_bot != MAXLNUM && i != j)
2266 {
2267 if (j < i)
2268 {
2269 int x = row + new_rows;
2270
2271 // move entries in w_lines[] upwards
2272 for (;;)
2273 {
2274 // stop at last valid entry in w_lines[]
2275 if (i >= wp->w_lines_valid)
2276 {
2277 wp->w_lines_valid = j;
2278 break;
2279 }
2280 wp->w_lines[j] = wp->w_lines[i];
2281 // stop at a line that won't fit
2282 if (x + (int)wp->w_lines[j].wl_size
2283 > wp->w_height)
2284 {
2285 wp->w_lines_valid = j + 1;
2286 break;
2287 }
2288 x += wp->w_lines[j++].wl_size;
2289 ++i;
2290 }
2291 if (bot_start > x)
2292 bot_start = x;
2293 }
2294 else // j > i
2295 {
2296 // move entries in w_lines[] downwards
2297 j -= i;
2298 wp->w_lines_valid += j;
2299 if (wp->w_lines_valid > wp->w_height)
2300 wp->w_lines_valid = wp->w_height;
2301 for (i = wp->w_lines_valid; i - j >= idx; --i)
2302 wp->w_lines[i] = wp->w_lines[i - j];
2303
2304 // The w_lines[] entries for inserted lines are
2305 // now invalid, but wl_size may be used above.
2306 // Reset to zero.
2307 while (i >= idx)
2308 {
2309 wp->w_lines[i].wl_size = 0;
2310 wp->w_lines[i--].wl_valid = FALSE;
2311 }
2312 }
2313 }
2314 }
2315 }
2316
2317#ifdef FEAT_FOLDING
2318 // When lines are folded, display one line for all of them.
2319 // Otherwise, display normally (can be several display lines when
2320 // 'wrap' is on).
2321 fold_count = foldedCount(wp, lnum, &win_foldinfo);
2322 if (fold_count != 0)
2323 {
2324 fold_line(wp, fold_count, &win_foldinfo, lnum, row);
2325 ++row;
2326 --fold_count;
2327 wp->w_lines[idx].wl_folded = TRUE;
2328 wp->w_lines[idx].wl_lastlnum = lnum + fold_count;
2329# ifdef FEAT_SYN_HL
2330 did_update = DID_FOLD;
2331# endif
2332 }
2333 else
2334#endif
2335 if (idx < wp->w_lines_valid
2336 && wp->w_lines[idx].wl_valid
2337 && wp->w_lines[idx].wl_lnum == lnum
2338 && lnum > wp->w_topline
2339 && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
2340 && !WIN_IS_POPUP(wp)
2341 && srow + wp->w_lines[idx].wl_size > wp->w_height
2342#ifdef FEAT_DIFF
2343 && diff_check_fill(wp, lnum) == 0
2344#endif
2345 )
2346 {
2347 // This line is not going to fit. Don't draw anything here,
2348 // will draw "@ " lines below.
2349 row = wp->w_height + 1;
2350 }
2351 else
2352 {
2353#ifdef FEAT_SEARCH_EXTRA
2354 prepare_search_hl(wp, &screen_search_hl, lnum);
2355#endif
2356#ifdef FEAT_SYN_HL
2357 // Let the syntax stuff know we skipped a few lines.
2358 if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum
2359 && syntax_present(wp))
2360 syntax_end_parsing(syntax_last_parsed + 1);
2361#endif
2362
2363 // Display one line.
2364 row = win_line(wp, lnum, srow, wp->w_height,
2365 mod_top == 0, FALSE);
2366
2367#ifdef FEAT_FOLDING
2368 wp->w_lines[idx].wl_folded = FALSE;
2369 wp->w_lines[idx].wl_lastlnum = lnum;
2370#endif
2371#ifdef FEAT_SYN_HL
2372 did_update = DID_LINE;
2373 syntax_last_parsed = lnum;
2374#endif
2375 }
2376
2377 wp->w_lines[idx].wl_lnum = lnum;
2378 wp->w_lines[idx].wl_valid = TRUE;
2379
2380 // Past end of the window or end of the screen. Note that after
2381 // resizing wp->w_height may be end up too big. That's a problem
2382 // elsewhere, but prevent a crash here.
2383 if (row > wp->w_height || row + wp->w_winrow >= Rows)
2384 {
2385 // we may need the size of that too long line later on
2386 if (dollar_vcol == -1)
2387 wp->w_lines[idx].wl_size = plines_win(wp, lnum, TRUE);
2388 ++idx;
2389 break;
2390 }
2391 if (dollar_vcol == -1)
2392 wp->w_lines[idx].wl_size = row - srow;
2393 ++idx;
2394#ifdef FEAT_FOLDING
2395 lnum += fold_count + 1;
2396#else
2397 ++lnum;
2398#endif
2399 }
2400 else
2401 {
2402 if (wp->w_p_rnu)
2403 {
2404#ifdef FEAT_FOLDING
2405 // 'relativenumber' set: The text doesn't need to be drawn, but
2406 // the number column nearly always does.
2407 fold_count = foldedCount(wp, lnum, &win_foldinfo);
2408 if (fold_count != 0)
2409 fold_line(wp, fold_count, &win_foldinfo, lnum, row);
2410 else
2411#endif
2412 (void)win_line(wp, lnum, srow, wp->w_height, TRUE, TRUE);
2413 }
2414
2415 // This line does not need to be drawn, advance to the next one.
2416 row += wp->w_lines[idx++].wl_size;
2417 if (row > wp->w_height) // past end of screen
2418 break;
2419#ifdef FEAT_FOLDING
2420 lnum = wp->w_lines[idx - 1].wl_lastlnum + 1;
2421#else
2422 ++lnum;
2423#endif
2424#ifdef FEAT_SYN_HL
2425 did_update = DID_NONE;
2426#endif
2427 }
2428
2429 if (lnum > buf->b_ml.ml_line_count)
2430 {
2431 eof = TRUE;
2432 break;
2433 }
2434 }
2435
2436 // End of loop over all window lines.
2437
2438#ifdef FEAT_VTP
2439 // Rewrite the character at the end of the screen line.
2440 if (use_vtp())
2441 {
2442 int i;
2443
2444 for (i = 0; i < Rows; ++i)
2445 if (enc_utf8)
2446 if ((*mb_off2cells)(LineOffset[i] + Columns - 2,
2447 LineOffset[i] + screen_Columns) > 1)
2448 screen_draw_rectangle(i, Columns - 2, 1, 2, FALSE);
2449 else
2450 screen_draw_rectangle(i, Columns - 1, 1, 1, FALSE);
2451 else
2452 screen_char(LineOffset[i] + Columns - 1, i, Columns - 1);
2453 }
2454#endif
2455
2456 if (idx > wp->w_lines_valid)
2457 wp->w_lines_valid = idx;
2458
2459#ifdef FEAT_SYN_HL
2460 // Let the syntax stuff know we stop parsing here.
2461 if (syntax_last_parsed != 0 && syntax_present(wp))
2462 syntax_end_parsing(syntax_last_parsed + 1);
2463#endif
2464
2465 // If we didn't hit the end of the file, and we didn't finish the last
2466 // line we were working on, then the line didn't fit.
2467 wp->w_empty_rows = 0;
2468#ifdef FEAT_DIFF
2469 wp->w_filler_rows = 0;
2470#endif
2471 if (!eof && !didline)
2472 {
2473 if (lnum == wp->w_topline)
2474 {
2475 // Single line that does not fit!
2476 // Don't overwrite it, it can be edited.
2477 wp->w_botline = lnum + 1;
2478 }
2479#ifdef FEAT_DIFF
2480 else if (diff_check_fill(wp, lnum) >= wp->w_height - srow)
2481 {
2482 // Window ends in filler lines.
2483 wp->w_botline = lnum;
2484 wp->w_filler_rows = wp->w_height - srow;
2485 }
2486#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002487#ifdef FEAT_PROP_POPUP
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02002488 else if (WIN_IS_POPUP(wp))
2489 {
2490 // popup line that doesn't fit is left as-is
2491 wp->w_botline = lnum;
2492 }
2493#endif
2494 else if (dy_flags & DY_TRUNCATE) // 'display' has "truncate"
2495 {
2496 int scr_row = W_WINROW(wp) + wp->w_height - 1;
2497
2498 // Last line isn't finished: Display "@@@" in the last screen line.
2499 screen_puts_len((char_u *)"@@", 2, scr_row, wp->w_wincol,
2500 HL_ATTR(HLF_AT));
2501 screen_fill(scr_row, scr_row + 1,
2502 (int)wp->w_wincol + 2, (int)W_ENDCOL(wp),
2503 '@', ' ', HL_ATTR(HLF_AT));
2504 set_empty_rows(wp, srow);
2505 wp->w_botline = lnum;
2506 }
2507 else if (dy_flags & DY_LASTLINE) // 'display' has "lastline"
2508 {
2509 // Last line isn't finished: Display "@@@" at the end.
2510 screen_fill(W_WINROW(wp) + wp->w_height - 1,
2511 W_WINROW(wp) + wp->w_height,
2512 (int)W_ENDCOL(wp) - 3, (int)W_ENDCOL(wp),
2513 '@', '@', HL_ATTR(HLF_AT));
2514 set_empty_rows(wp, srow);
2515 wp->w_botline = lnum;
2516 }
2517 else
2518 {
2519 win_draw_end(wp, '@', ' ', TRUE, srow, wp->w_height, HLF_AT);
2520 wp->w_botline = lnum;
2521 }
2522 }
2523 else
2524 {
2525 draw_vsep_win(wp, row);
2526 if (eof) // we hit the end of the file
2527 {
2528 wp->w_botline = buf->b_ml.ml_line_count + 1;
2529#ifdef FEAT_DIFF
2530 j = diff_check_fill(wp, wp->w_botline);
2531 if (j > 0 && !wp->w_botfill)
2532 {
2533 // Display filler lines at the end of the file.
2534 if (char2cells(fill_diff) > 1)
2535 i = '-';
2536 else
2537 i = fill_diff;
2538 if (row + j > wp->w_height)
2539 j = wp->w_height - row;
2540 win_draw_end(wp, i, i, TRUE, row, row + (int)j, HLF_DED);
2541 row += j;
2542 }
2543#endif
2544 }
2545 else if (dollar_vcol == -1)
2546 wp->w_botline = lnum;
2547
2548 // Make sure the rest of the screen is blank
2549 // put '~'s on rows that aren't part of the file.
Bram Moolenaar1666ac92019-11-10 17:22:31 +01002550 if (WIN_IS_POPUP(wp))
2551 win_draw_end(wp, ' ', ' ', FALSE, row, wp->w_height, HLF_AT);
2552 else
2553 win_draw_end(wp, '~', ' ', FALSE, row, wp->w_height, HLF_EOB);
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02002554 }
2555
2556#ifdef SYN_TIME_LIMIT
2557 syn_set_timeout(NULL);
2558#endif
2559
2560 // Reset the type of redrawing required, the window has been updated.
2561 wp->w_redr_type = 0;
2562#ifdef FEAT_DIFF
2563 wp->w_old_topfill = wp->w_topfill;
2564 wp->w_old_botfill = wp->w_botfill;
2565#endif
2566
2567 if (dollar_vcol == -1)
2568 {
2569 // There is a trick with w_botline. If we invalidate it on each
2570 // change that might modify it, this will cause a lot of expensive
2571 // calls to plines() in update_topline() each time. Therefore the
2572 // value of w_botline is often approximated, and this value is used to
2573 // compute the value of w_topline. If the value of w_botline was
2574 // wrong, check that the value of w_topline is correct (cursor is on
2575 // the visible part of the text). If it's not, we need to redraw
2576 // again. Mostly this just means scrolling up a few lines, so it
2577 // doesn't look too bad. Only do this for the current window (where
2578 // changes are relevant).
2579 wp->w_valid |= VALID_BOTLINE;
2580 if (wp == curwin && wp->w_botline != old_botline && !recursive)
2581 {
Bram Moolenaarcbee6352019-11-12 20:49:15 +01002582 win_T *wwp;
2583#if defined(FEAT_CONCEAL)
2584 linenr_T old_topline = wp->w_topline;
2585 int new_wcol = wp->w_wcol;
2586#endif
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02002587 recursive = TRUE;
2588 curwin->w_valid &= ~VALID_TOPLINE;
2589 update_topline(); // may invalidate w_botline again
Bram Moolenaarcbee6352019-11-12 20:49:15 +01002590
2591#if defined(FEAT_CONCEAL)
2592 if (old_wcol != new_wcol && (wp->w_valid & (VALID_WCOL|VALID_WROW))
2593 != (VALID_WCOL|VALID_WROW))
2594 {
2595 // A win_line() call applied a fix to screen cursor column to
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002596 // accommodate concealment of cursor line, but in this call to
Bram Moolenaarcbee6352019-11-12 20:49:15 +01002597 // update_topline() the cursor's row or column got invalidated.
2598 // If they are left invalid, setcursor() will recompute them
2599 // but there won't be any further win_line() call to re-fix the
2600 // column and the cursor will end up misplaced. So we call
2601 // cursor validation now and reapply the fix again (or call
2602 // win_line() to do it for us).
2603 validate_cursor();
2604 if (wp->w_wcol == old_wcol && wp->w_wrow == old_wrow
2605 && old_topline == wp->w_topline)
2606 wp->w_wcol = new_wcol;
2607 else
2608 redrawWinline(wp, wp->w_cursor.lnum);
2609 }
2610#endif
2611 // New redraw either due to updated topline or due to wcol fix.
2612 if (wp->w_redr_type != 0)
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02002613 {
2614 // Don't update for changes in buffer again.
2615 i = curbuf->b_mod_set;
2616 curbuf->b_mod_set = FALSE;
Bram Moolenaarcbee6352019-11-12 20:49:15 +01002617 j = curbuf->b_mod_xlines;
2618 curbuf->b_mod_xlines = 0;
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02002619 win_update(curwin);
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02002620 curbuf->b_mod_set = i;
Bram Moolenaarcbee6352019-11-12 20:49:15 +01002621 curbuf->b_mod_xlines = j;
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02002622 }
Bram Moolenaarcbee6352019-11-12 20:49:15 +01002623 // Other windows might have w_redr_type raised in update_topline().
2624 must_redraw = 0;
2625 FOR_ALL_WINDOWS(wwp)
2626 if (wwp->w_redr_type > must_redraw)
2627 must_redraw = wwp->w_redr_type;
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02002628 recursive = FALSE;
2629 }
2630 }
2631
2632#if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA)
2633 // restore got_int, unless CTRL-C was hit while redrawing
2634 if (!got_int)
2635 got_int = save_got_int;
2636#endif
2637}
2638
2639#if defined(FEAT_NETBEANS_INTG) || defined(FEAT_GUI)
2640/*
2641 * Prepare for updating one or more windows.
2642 * Caller must check for "updating_screen" already set to avoid recursiveness.
2643 */
2644 static void
2645update_prepare(void)
2646{
2647 cursor_off();
2648 updating_screen = TRUE;
2649#ifdef FEAT_GUI
2650 // Remove the cursor before starting to do anything, because scrolling may
2651 // make it difficult to redraw the text under it.
2652 if (gui.in_use)
2653 gui_undraw_cursor();
2654#endif
2655#ifdef FEAT_SEARCH_EXTRA
2656 start_search_hl();
2657#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002658#ifdef FEAT_PROP_POPUP
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02002659 // Update popup_mask if needed.
2660 may_update_popup_mask(must_redraw);
2661#endif
2662}
2663
2664/*
2665 * Finish updating one or more windows.
2666 */
2667 static void
2668update_finish(void)
2669{
2670 if (redraw_cmdline || redraw_mode)
2671 showmode();
2672
2673# ifdef FEAT_SEARCH_EXTRA
2674 end_search_hl();
2675# endif
2676
2677 after_updating_screen(TRUE);
2678
2679# ifdef FEAT_GUI
2680 // Redraw the cursor and update the scrollbars when all screen updating is
2681 // done.
2682 if (gui.in_use)
2683 {
2684 out_flush_cursor(FALSE, FALSE);
2685 gui_update_scrollbars(FALSE);
2686 }
2687# endif
2688}
2689#endif
2690
2691#if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
2692 void
2693update_debug_sign(buf_T *buf, linenr_T lnum)
2694{
2695 win_T *wp;
2696 int doit = FALSE;
2697
2698# ifdef FEAT_FOLDING
2699 win_foldinfo.fi_level = 0;
2700# endif
2701
2702 // update/delete a specific sign
2703 redraw_buf_line_later(buf, lnum);
2704
2705 // check if it resulted in the need to redraw a window
2706 FOR_ALL_WINDOWS(wp)
2707 if (wp->w_redr_type != 0)
2708 doit = TRUE;
2709
2710 // Return when there is nothing to do, screen updating is already
2711 // happening (recursive call), messages on the screen or still starting up.
2712 if (!doit || updating_screen
2713 || State == ASKMORE || State == HITRETURN
2714 || msg_scrolled
2715#ifdef FEAT_GUI
2716 || gui.starting
2717#endif
2718 || starting)
2719 return;
2720
2721 // update all windows that need updating
2722 update_prepare();
2723
2724 FOR_ALL_WINDOWS(wp)
2725 {
2726 if (wp->w_redr_type != 0)
2727 win_update(wp);
2728 if (wp->w_redr_status)
2729 win_redr_status(wp, FALSE);
2730 }
2731
2732 update_finish();
2733}
2734#endif
2735
2736#if defined(FEAT_GUI) || defined(PROTO)
2737/*
2738 * Update a single window, its status line and maybe the command line msg.
2739 * Used for the GUI scrollbar.
2740 */
2741 void
2742updateWindow(win_T *wp)
2743{
2744 // return if already busy updating
2745 if (updating_screen)
2746 return;
2747
2748 update_prepare();
2749
2750#ifdef FEAT_CLIPBOARD
2751 // When Visual area changed, may have to update selection.
2752 if (clip_star.available && clip_isautosel_star())
2753 clip_update_selection(&clip_star);
2754 if (clip_plus.available && clip_isautosel_plus())
2755 clip_update_selection(&clip_plus);
2756#endif
2757
2758 win_update(wp);
2759
2760 // When the screen was cleared redraw the tab pages line.
2761 if (redraw_tabline)
2762 draw_tabline();
2763
2764 if (wp->w_redr_status
2765# ifdef FEAT_CMDL_INFO
2766 || p_ru
2767# endif
2768# ifdef FEAT_STL_OPT
2769 || *p_stl != NUL || *wp->w_p_stl != NUL
2770# endif
2771 )
2772 win_redr_status(wp, FALSE);
2773
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002774#ifdef FEAT_PROP_POPUP
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02002775 // Display popup windows on top of everything.
2776 update_popups(win_update);
2777#endif
2778
2779 update_finish();
2780}
2781#endif
2782
2783#if defined(FEAT_TERMRESPONSE) || defined(PROTO)
2784/*
2785 * Redraw as soon as possible. When the command line is not scrolled redraw
2786 * right away and restore what was on the command line.
2787 * Return a code indicating what happened.
2788 */
2789 int
2790redraw_asap(int type)
2791{
2792 int rows;
2793 int cols = screen_Columns;
2794 int r;
2795 int ret = 0;
2796 schar_T *screenline; // copy from ScreenLines[]
2797 sattr_T *screenattr; // copy from ScreenAttrs[]
2798 int i;
2799 u8char_T *screenlineUC = NULL; // copy from ScreenLinesUC[]
2800 u8char_T *screenlineC[MAX_MCO]; // copy from ScreenLinesC[][]
2801 schar_T *screenline2 = NULL; // copy from ScreenLines2[]
2802
2803 redraw_later(type);
2804 if (msg_scrolled || (State != NORMAL && State != NORMAL_BUSY) || exiting)
2805 return ret;
2806
2807 // Allocate space to save the text displayed in the command line area.
2808 rows = screen_Rows - cmdline_row;
2809 screenline = LALLOC_MULT(schar_T, rows * cols);
2810 screenattr = LALLOC_MULT(sattr_T, rows * cols);
2811 if (screenline == NULL || screenattr == NULL)
2812 ret = 2;
2813 if (enc_utf8)
2814 {
2815 screenlineUC = LALLOC_MULT(u8char_T, rows * cols);
2816 if (screenlineUC == NULL)
2817 ret = 2;
2818 for (i = 0; i < p_mco; ++i)
2819 {
2820 screenlineC[i] = LALLOC_MULT(u8char_T, rows * cols);
2821 if (screenlineC[i] == NULL)
2822 ret = 2;
2823 }
2824 }
2825 if (enc_dbcs == DBCS_JPNU)
2826 {
2827 screenline2 = LALLOC_MULT(schar_T, rows * cols);
2828 if (screenline2 == NULL)
2829 ret = 2;
2830 }
2831
2832 if (ret != 2)
2833 {
2834 // Save the text displayed in the command line area.
2835 for (r = 0; r < rows; ++r)
2836 {
2837 mch_memmove(screenline + r * cols,
2838 ScreenLines + LineOffset[cmdline_row + r],
2839 (size_t)cols * sizeof(schar_T));
2840 mch_memmove(screenattr + r * cols,
2841 ScreenAttrs + LineOffset[cmdline_row + r],
2842 (size_t)cols * sizeof(sattr_T));
2843 if (enc_utf8)
2844 {
2845 mch_memmove(screenlineUC + r * cols,
2846 ScreenLinesUC + LineOffset[cmdline_row + r],
2847 (size_t)cols * sizeof(u8char_T));
2848 for (i = 0; i < p_mco; ++i)
2849 mch_memmove(screenlineC[i] + r * cols,
2850 ScreenLinesC[i] + LineOffset[cmdline_row + r],
2851 (size_t)cols * sizeof(u8char_T));
2852 }
2853 if (enc_dbcs == DBCS_JPNU)
2854 mch_memmove(screenline2 + r * cols,
2855 ScreenLines2 + LineOffset[cmdline_row + r],
2856 (size_t)cols * sizeof(schar_T));
2857 }
2858
2859 update_screen(0);
2860 ret = 3;
2861
2862 if (must_redraw == 0)
2863 {
2864 int off = (int)(current_ScreenLine - ScreenLines);
2865
2866 // Restore the text displayed in the command line area.
2867 for (r = 0; r < rows; ++r)
2868 {
2869 mch_memmove(current_ScreenLine,
2870 screenline + r * cols,
2871 (size_t)cols * sizeof(schar_T));
2872 mch_memmove(ScreenAttrs + off,
2873 screenattr + r * cols,
2874 (size_t)cols * sizeof(sattr_T));
2875 if (enc_utf8)
2876 {
2877 mch_memmove(ScreenLinesUC + off,
2878 screenlineUC + r * cols,
2879 (size_t)cols * sizeof(u8char_T));
2880 for (i = 0; i < p_mco; ++i)
2881 mch_memmove(ScreenLinesC[i] + off,
2882 screenlineC[i] + r * cols,
2883 (size_t)cols * sizeof(u8char_T));
2884 }
2885 if (enc_dbcs == DBCS_JPNU)
2886 mch_memmove(ScreenLines2 + off,
2887 screenline2 + r * cols,
2888 (size_t)cols * sizeof(schar_T));
2889 screen_line(cmdline_row + r, 0, cols, cols, 0);
2890 }
2891 ret = 4;
2892 }
2893 }
2894
2895 vim_free(screenline);
2896 vim_free(screenattr);
2897 if (enc_utf8)
2898 {
2899 vim_free(screenlineUC);
2900 for (i = 0; i < p_mco; ++i)
2901 vim_free(screenlineC[i]);
2902 }
2903 if (enc_dbcs == DBCS_JPNU)
2904 vim_free(screenline2);
2905
2906 // Show the intro message when appropriate.
2907 maybe_intro_message();
2908
2909 setcursor();
2910
2911 return ret;
2912}
2913#endif
2914
2915/*
2916 * Invoked after an asynchronous callback is called.
2917 * If an echo command was used the cursor needs to be put back where
2918 * it belongs. If highlighting was changed a redraw is needed.
2919 * If "call_update_screen" is FALSE don't call update_screen() when at the
2920 * command line.
2921 */
2922 void
2923redraw_after_callback(int call_update_screen)
2924{
2925 ++redrawing_for_callback;
2926
2927 if (State == HITRETURN || State == ASKMORE)
2928 ; // do nothing
2929 else if (State & CMDLINE)
2930 {
2931 // Don't redraw when in prompt_for_number().
2932 if (cmdline_row > 0)
2933 {
2934 // Redrawing only works when the screen didn't scroll. Don't clear
2935 // wildmenu entries.
2936 if (msg_scrolled == 0
2937#ifdef FEAT_WILDMENU
2938 && wild_menu_showing == 0
2939#endif
2940 && call_update_screen)
2941 update_screen(0);
2942
2943 // Redraw in the same position, so that the user can continue
2944 // editing the command.
2945 redrawcmdline_ex(FALSE);
2946 }
2947 }
2948 else if (State & (NORMAL | INSERT | TERMINAL))
2949 {
2950 // keep the command line if possible
2951 update_screen(VALID_NO_UPDATE);
2952 setcursor();
2953 }
2954 cursor_on();
2955#ifdef FEAT_GUI
2956 if (gui.in_use && !gui_mch_is_blink_off())
2957 // Don't update the cursor when it is blinking and off to avoid
2958 // flicker.
2959 out_flush_cursor(FALSE, FALSE);
2960 else
2961#endif
2962 out_flush();
2963
2964 --redrawing_for_callback;
2965}
2966
2967/*
2968 * Redraw the current window later, with update_screen(type).
2969 * Set must_redraw only if not already set to a higher value.
2970 * E.g. if must_redraw is CLEAR, type NOT_VALID will do nothing.
2971 */
2972 void
2973redraw_later(int type)
2974{
2975 redraw_win_later(curwin, type);
2976}
2977
2978 void
2979redraw_win_later(
2980 win_T *wp,
2981 int type)
2982{
2983 if (!exiting && wp->w_redr_type < type)
2984 {
2985 wp->w_redr_type = type;
2986 if (type >= NOT_VALID)
2987 wp->w_lines_valid = 0;
2988 if (must_redraw < type) // must_redraw is the maximum of all windows
2989 must_redraw = type;
2990 }
2991}
2992
2993/*
2994 * Force a complete redraw later. Also resets the highlighting. To be used
2995 * after executing a shell command that messes up the screen.
2996 */
2997 void
2998redraw_later_clear(void)
2999{
3000 redraw_all_later(CLEAR);
3001 reset_screen_attr();
3002}
3003
3004/*
3005 * Mark all windows to be redrawn later.
3006 */
3007 void
3008redraw_all_later(int type)
3009{
3010 win_T *wp;
3011
3012 FOR_ALL_WINDOWS(wp)
3013 redraw_win_later(wp, type);
3014 // This may be needed when switching tabs.
3015 if (must_redraw < type)
3016 must_redraw = type;
3017}
3018
3019/*
3020 * Mark all windows that are editing the current buffer to be updated later.
3021 */
3022 void
3023redraw_curbuf_later(int type)
3024{
3025 redraw_buf_later(curbuf, type);
3026}
3027
3028 void
3029redraw_buf_later(buf_T *buf, int type)
3030{
3031 win_T *wp;
3032
3033 FOR_ALL_WINDOWS(wp)
3034 {
3035 if (wp->w_buffer == buf)
3036 redraw_win_later(wp, type);
3037 }
Bram Moolenaare52e0c82020-02-28 22:20:10 +01003038#if defined(FEAT_TERMINAL) && defined(FEAT_PROP_POPUP)
3039 // terminal in popup window is not in list of windows
3040 if (curwin->w_buffer == buf)
3041 redraw_win_later(curwin, type);
3042#endif
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02003043}
3044
3045#if defined(FEAT_SIGNS) || defined(PROTO)
3046 void
3047redraw_buf_line_later(buf_T *buf, linenr_T lnum)
3048{
3049 win_T *wp;
3050
3051 FOR_ALL_WINDOWS(wp)
3052 if (wp->w_buffer == buf && lnum >= wp->w_topline
3053 && lnum < wp->w_botline)
3054 redrawWinline(wp, lnum);
3055}
3056#endif
3057
3058#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
3059 void
3060redraw_buf_and_status_later(buf_T *buf, int type)
3061{
3062 win_T *wp;
3063
3064#ifdef FEAT_WILDMENU
3065 if (wild_menu_showing != 0)
3066 // Don't redraw while the command line completion is displayed, it
3067 // would disappear.
3068 return;
3069#endif
3070 FOR_ALL_WINDOWS(wp)
3071 {
3072 if (wp->w_buffer == buf)
3073 {
3074 redraw_win_later(wp, type);
3075 wp->w_redr_status = TRUE;
3076 }
3077 }
3078}
3079#endif
3080
3081/*
3082 * mark all status lines for redraw; used after first :cd
3083 */
3084 void
3085status_redraw_all(void)
3086{
3087 win_T *wp;
3088
3089 FOR_ALL_WINDOWS(wp)
3090 if (wp->w_status_height)
3091 {
3092 wp->w_redr_status = TRUE;
3093 redraw_later(VALID);
3094 }
3095}
3096
3097/*
3098 * mark all status lines of the current buffer for redraw
3099 */
3100 void
3101status_redraw_curbuf(void)
3102{
3103 win_T *wp;
3104
3105 FOR_ALL_WINDOWS(wp)
3106 if (wp->w_status_height != 0 && wp->w_buffer == curbuf)
3107 {
3108 wp->w_redr_status = TRUE;
3109 redraw_later(VALID);
3110 }
3111}
3112
3113/*
3114 * Redraw all status lines that need to be redrawn.
3115 */
3116 void
3117redraw_statuslines(void)
3118{
3119 win_T *wp;
3120
3121 FOR_ALL_WINDOWS(wp)
3122 if (wp->w_redr_status)
3123 win_redr_status(wp, FALSE);
3124 if (redraw_tabline)
3125 draw_tabline();
3126}
3127
3128#if defined(FEAT_WILDMENU) || defined(PROTO)
3129/*
3130 * Redraw all status lines at the bottom of frame "frp".
3131 */
3132 void
3133win_redraw_last_status(frame_T *frp)
3134{
3135 if (frp->fr_layout == FR_LEAF)
3136 frp->fr_win->w_redr_status = TRUE;
3137 else if (frp->fr_layout == FR_ROW)
3138 {
3139 FOR_ALL_FRAMES(frp, frp->fr_child)
3140 win_redraw_last_status(frp);
3141 }
3142 else // frp->fr_layout == FR_COL
3143 {
3144 frp = frp->fr_child;
3145 while (frp->fr_next != NULL)
3146 frp = frp->fr_next;
3147 win_redraw_last_status(frp);
3148 }
3149}
3150#endif
3151
3152/*
3153 * Changed something in the current window, at buffer line "lnum", that
3154 * requires that line and possibly other lines to be redrawn.
3155 * Used when entering/leaving Insert mode with the cursor on a folded line.
3156 * Used to remove the "$" from a change command.
3157 * Note that when also inserting/deleting lines w_redraw_top and w_redraw_bot
3158 * may become invalid and the whole window will have to be redrawn.
3159 */
3160 void
3161redrawWinline(
3162 win_T *wp,
3163 linenr_T lnum)
3164{
3165 if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum)
3166 wp->w_redraw_top = lnum;
3167 if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum)
3168 wp->w_redraw_bot = lnum;
3169 redraw_win_later(wp, VALID);
3170}