blob: d8a060f0c253ddf65cd781c350f0762832b6e782 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
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/*
Bram Moolenaar7528d1f2019-09-19 23:06:20 +020011 * screen.c: Lower level code for displaying on the screen.
Bram Moolenaar071d4272004-06-13 20:20:40 +000012 *
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.
zeertzjq99941602023-08-19 13:08:50 +020020 * ScreenCols[off] Contains the virtual columns in the line. -1 means not
zeertzjqdeb22042024-03-17 19:44:30 +010021 * available or before buffer text.
Bram Moolenaarb9081882022-07-09 04:56:24 +010022 *
23 * LineOffset[row] Contains the offset into ScreenLines*[], ScreenAttrs[]
24 * and ScreenCols[] for each line.
Bram Moolenaar071d4272004-06-13 20:20:40 +000025 * LineWraps[row] Flag for each line whether it wraps to the next line.
26 *
27 * For double-byte characters, two consecutive bytes in ScreenLines[] can form
28 * one character which occupies two display cells.
29 * For UTF-8 a multi-byte character is converted to Unicode and stored in
30 * ScreenLinesUC[]. ScreenLines[] contains the first byte only. For an ASCII
Bram Moolenaar70c49c12010-03-23 15:36:35 +010031 * character without composing chars ScreenLinesUC[] will be 0 and
32 * ScreenLinesC[][] is not used. When the character occupies two display
33 * cells the next byte in ScreenLines[] is 0.
Bram Moolenaar362e1a32006-03-06 23:29:24 +000034 * ScreenLinesC[][] contain up to 'maxcombine' composing characters
Bram Moolenaar70c49c12010-03-23 15:36:35 +010035 * (drawn on top of the first character). There is 0 after the last one used.
Bram Moolenaar071d4272004-06-13 20:20:40 +000036 * ScreenLines2[] is only used for euc-jp to store the second byte if the
37 * first byte is 0x8e (single-width character).
38 *
39 * The screen_*() functions write to the screen and handle updating
40 * ScreenLines[].
Bram Moolenaar071d4272004-06-13 20:20:40 +000041 */
42
43#include "vim.h"
44
45/*
46 * The attributes that are actually active for writing to the screen.
47 */
48static int screen_attr = 0;
49
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010050static void screen_char_2(unsigned off, int row, int col);
Bram Moolenaar838b7462022-09-26 15:19:56 +010051static int screenclear2(int doclear);
Bram Moolenaarcfce7172017-08-17 20:31:48 +020052static void lineclear(unsigned off, int width, int attr);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010053static void lineinvalid(unsigned off, int width);
Bram Moolenaarcfce7172017-08-17 20:31:48 +020054static int win_do_lines(win_T *wp, int row, int line_count, int mayclear, int del, int clear_attr);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010055static void win_rest_invalid(win_T *wp);
56static void msg_pos_mode(void);
57static void recording_mode(int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000058
Bram Moolenaar63d9e732019-12-05 21:10:38 +010059// Ugly global: overrule attribute used by screen_char()
Bram Moolenaar071d4272004-06-13 20:20:40 +000060static int screen_char_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000061
Bram Moolenaar860cae12010-06-05 23:22:07 +020062#if defined(FEAT_CONCEAL) || defined(PROTO)
Bram Moolenaarf5963f72010-07-23 22:10:27 +020063/*
64 * Return TRUE if the cursor line in window "wp" may be concealed, according
65 * to the 'concealcursor' option.
66 */
67 int
Bram Moolenaar05540972016-01-30 20:31:25 +010068conceal_cursor_line(win_T *wp)
Bram Moolenaarf5963f72010-07-23 22:10:27 +020069{
70 int c;
71
72 if (*wp->w_p_cocu == NUL)
73 return FALSE;
Bram Moolenaar24959102022-05-07 20:01:16 +010074 if (get_real_state() & MODE_VISUAL)
Bram Moolenaarf5963f72010-07-23 22:10:27 +020075 c = 'v';
Bram Moolenaar24959102022-05-07 20:01:16 +010076 else if (State & MODE_INSERT)
Bram Moolenaarf5963f72010-07-23 22:10:27 +020077 c = 'i';
Bram Moolenaar24959102022-05-07 20:01:16 +010078 else if (State & MODE_NORMAL)
Bram Moolenaarf5963f72010-07-23 22:10:27 +020079 c = 'n';
Bram Moolenaar24959102022-05-07 20:01:16 +010080 else if (State & MODE_CMDLINE)
Bram Moolenaarca8c9862010-07-24 15:00:38 +020081 c = 'c';
Bram Moolenaarf5963f72010-07-23 22:10:27 +020082 else
83 return FALSE;
84 return vim_strchr(wp->w_p_cocu, c) != NULL;
85}
86
87/*
88 * Check if the cursor line needs to be redrawn because of 'concealcursor'.
Bram Moolenaarea042672021-06-29 20:22:32 +020089 * To be called after changing the state, "was_concealed" is the value of
90 * "conceal_cursor_line()" before the change.
91 * "
Bram Moolenaarf5963f72010-07-23 22:10:27 +020092 */
93 void
Bram Moolenaarea042672021-06-29 20:22:32 +020094conceal_check_cursor_line(int was_concealed)
Bram Moolenaarf5963f72010-07-23 22:10:27 +020095{
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +000096 if (curwin->w_p_cole <= 0 || conceal_cursor_line(curwin) == was_concealed)
97 return;
Bram Moolenaarea042672021-06-29 20:22:32 +020098
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +000099 int wcol = curwin->w_wcol;
Bram Moolenaarea042672021-06-29 20:22:32 +0200100
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000101 need_cursor_line_redraw = TRUE;
102 // Need to recompute cursor column, e.g., when starting Visual mode
103 // without concealing.
104 curs_columns(TRUE);
105
106 // When concealing now w_wcol will be computed wrong, keep the previous
107 // value, it will be updated in win_line().
108 if (!was_concealed)
109 curwin->w_wcol = wcol;
Bram Moolenaarf5963f72010-07-23 22:10:27 +0200110}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000111#endif
112
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200113/*
114 * Get 'wincolor' attribute for window "wp". If not set and "wp" is a popup
115 * window then get the "Pmenu" highlight attribute.
116 */
Bram Moolenaara540f8a2019-06-14 19:23:57 +0200117 int
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200118get_wcr_attr(win_T *wp)
119{
120 int wcr_attr = 0;
121
122 if (*wp->w_p_wcr != NUL)
123 wcr_attr = syn_name2attr(wp->w_p_wcr);
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100124#ifdef FEAT_PROP_POPUP
Bram Moolenaarc363fe12019-08-04 18:13:46 +0200125 else if (WIN_IS_POPUP(wp))
Bram Moolenaar62a0cb42019-08-18 16:35:23 +0200126 {
127 if (wp->w_popup_flags & POPF_INFO)
128 wcr_attr = HL_ATTR(HLF_PSI); // PmenuSel
129 else
130 wcr_attr = HL_ATTR(HLF_PNI); // Pmenu
131 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200132#endif
133 return wcr_attr;
134}
135
Bram Moolenaar071d4272004-06-13 20:20:40 +0000136/*
Bram Moolenaar8ee4c012019-03-29 18:08:18 +0100137 * Call screen_fill() with the columns adjusted for 'rightleft' if needed.
138 * Return the new offset.
139 */
140 static int
141screen_fill_end(
142 win_T *wp,
143 int c1,
144 int c2,
145 int off,
146 int width,
147 int row,
148 int endrow,
149 int attr)
150{
151 int nn = off + width;
152
153 if (nn > wp->w_width)
154 nn = wp->w_width;
155#ifdef FEAT_RIGHTLEFT
156 if (wp->w_p_rl)
157 {
158 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200159 W_ENDCOL(wp) - nn + TPL_LCOL(wp), (int)W_ENDCOL(wp) - off + TPL_LCOL(wp),
Bram Moolenaar8ee4c012019-03-29 18:08:18 +0100160 c1, c2, attr);
161 }
162 else
163#endif
164 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200165 wp->w_wincol + off + TPL_LCOL(wp), (int)wp->w_wincol + nn + TPL_LCOL(wp),
Bram Moolenaar8ee4c012019-03-29 18:08:18 +0100166 c1, c2, attr);
167 return nn;
168}
169
170/*
171 * Clear lines near the end the window and mark the unused lines with "c1".
172 * use "c2" as the filler character.
173 * When "draw_margin" is TRUE then draw the sign, fold and number columns.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000174 */
Bram Moolenaar7528d1f2019-09-19 23:06:20 +0200175 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100176win_draw_end(
177 win_T *wp,
178 int c1,
179 int c2,
Bram Moolenaar8ee4c012019-03-29 18:08:18 +0100180 int draw_margin,
Bram Moolenaar05540972016-01-30 20:31:25 +0100181 int row,
182 int endrow,
183 hlf_T hl)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000184{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000185 int n = 0;
Bram Moolenaar193ffd12019-05-25 22:57:30 +0200186 int attr = HL_ATTR(hl);
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200187 int wcr_attr = get_wcr_attr(wp);
Bram Moolenaar193ffd12019-05-25 22:57:30 +0200188
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200189 attr = hl_combine_attr(wcr_attr, attr);
Bram Moolenaar8ee4c012019-03-29 18:08:18 +0100190
191 if (draw_margin)
192 {
Bram Moolenaar1c934292015-01-27 16:39:29 +0100193#ifdef FEAT_FOLDING
Bram Moolenaar8ee4c012019-03-29 18:08:18 +0100194 int fdc = compute_foldcolumn(wp, 0);
195
196 if (fdc > 0)
197 // draw the fold column
198 n = screen_fill_end(wp, ' ', ' ', n, fdc,
Bram Moolenaar193ffd12019-05-25 22:57:30 +0200199 row, endrow, hl_combine_attr(wcr_attr, HL_ATTR(HLF_FC)));
Bram Moolenaar1c934292015-01-27 16:39:29 +0100200#endif
Bram Moolenaar8ee4c012019-03-29 18:08:18 +0100201#ifdef FEAT_SIGNS
202 if (signcolumn_on(wp))
203 // draw the sign column
204 n = screen_fill_end(wp, ' ', ' ', n, 2,
Bram Moolenaar193ffd12019-05-25 22:57:30 +0200205 row, endrow, hl_combine_attr(wcr_attr, HL_ATTR(HLF_SC)));
Bram Moolenaar8ee4c012019-03-29 18:08:18 +0100206#endif
207 if ((wp->w_p_nu || wp->w_p_rnu)
208 && vim_strchr(p_cpo, CPO_NUMCOL) == NULL)
209 // draw the number column
210 n = screen_fill_end(wp, ' ', ' ', n, number_width(wp) + 1,
Bram Moolenaar193ffd12019-05-25 22:57:30 +0200211 row, endrow, hl_combine_attr(wcr_attr, HL_ATTR(HLF_N)));
Bram Moolenaar8ee4c012019-03-29 18:08:18 +0100212 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000213
214#ifdef FEAT_RIGHTLEFT
215 if (wp->w_p_rl)
216 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000217 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200218 wp->w_wincol + TPL_LCOL(wp), W_ENDCOL(wp) - 1 - n +
219 TPL_LCOL(wp), c2, c2, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000220 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200221 W_ENDCOL(wp) - 1 - n + TPL_LCOL(wp), W_ENDCOL(wp) - n +
222 TPL_LCOL(wp), c1, c2, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000223 }
224 else
225#endif
226 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000227 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow,
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200228 wp->w_wincol + n + TPL_LCOL(wp), (int)W_ENDCOL(wp) + TPL_LCOL(wp),
Bram Moolenaar193ffd12019-05-25 22:57:30 +0200229 c1, c2, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000230 }
Bram Moolenaar8ee4c012019-03-29 18:08:18 +0100231
Bram Moolenaar071d4272004-06-13 20:20:40 +0000232 set_empty_rows(wp, row);
233}
234
Bram Moolenaar7528d1f2019-09-19 23:06:20 +0200235#if defined(FEAT_FOLDING) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000236/*
Bram Moolenaar1c934292015-01-27 16:39:29 +0100237 * Compute the width of the foldcolumn. Based on 'foldcolumn' and how much
238 * space is available for window "wp", minus "col".
239 */
Bram Moolenaar7528d1f2019-09-19 23:06:20 +0200240 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100241compute_foldcolumn(win_T *wp, int col)
Bram Moolenaar1c934292015-01-27 16:39:29 +0100242{
Bram Moolenaar1c934292015-01-27 16:39:29 +0100243 int wmw = wp == curwin && p_wmw == 0 ? 1 : p_wmw;
John Marriottc15de972025-01-17 13:54:49 +0100244 int n = wp->w_width - (col + wmw);
Bram Moolenaar1c934292015-01-27 16:39:29 +0100245
John Marriottc15de972025-01-17 13:54:49 +0100246 return MIN(wp->w_p_fdc, n);
Bram Moolenaar1c934292015-01-27 16:39:29 +0100247}
248
249/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000250 * Fill the foldcolumn at "p" for window "wp".
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000251 * Only to be called when 'foldcolumn' > 0.
Bram Moolenaar4fa11752021-03-03 13:26:02 +0100252 * Returns the number of bytes stored in 'p'. When non-multibyte characters are
253 * used for the fold column markers, this is equal to 'fdc' setting. Otherwise,
254 * this will be greater than 'fdc'.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000255 */
Bram Moolenaar4fa11752021-03-03 13:26:02 +0100256 size_t
Bram Moolenaar05540972016-01-30 20:31:25 +0100257fill_foldcolumn(
258 char_u *p,
259 win_T *wp,
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100260 int closed, // TRUE of FALSE
261 linenr_T lnum) // current line number
Bram Moolenaar071d4272004-06-13 20:20:40 +0000262{
263 int i = 0;
264 int level;
265 int first_level;
Bram Moolenaar578b49e2005-09-10 19:22:57 +0000266 int empty;
Bram Moolenaar1c934292015-01-27 16:39:29 +0100267 int fdc = compute_foldcolumn(wp, 0);
Bram Moolenaar4fa11752021-03-03 13:26:02 +0100268 size_t byte_counter = 0;
269 int symbol = 0;
270 int len = 0;
John Marriottc15de972025-01-17 13:54:49 +0100271 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000272
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100273 // Init to all spaces.
Bram Moolenaar4fa11752021-03-03 13:26:02 +0100274 vim_memset(p, ' ', MAX_MCO * fdc + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000275
276 level = win_foldinfo.fi_level;
Bram Moolenaar4fa11752021-03-03 13:26:02 +0100277 empty = (fdc == 1) ? 0 : 1;
278
279 // If the column is too narrow, we start at the lowest level that
Bram Moolenaar008bff92021-03-04 21:55:58 +0100280 // fits and use numbers to indicate the depth.
Bram Moolenaar4fa11752021-03-03 13:26:02 +0100281 first_level = level - fdc - closed + 1 + empty;
282 if (first_level < 1)
283 first_level = 1;
284
John Marriottc15de972025-01-17 13:54:49 +0100285 n = MIN(fdc, level); // evaluate this once
286 for (i = 0; i < n; i++)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000287 {
Bram Moolenaar4fa11752021-03-03 13:26:02 +0100288 if (win_foldinfo.fi_lnum == lnum
289 && first_level + i >= win_foldinfo.fi_low_level)
Bram Moolenaar96ba25a2022-07-04 17:34:33 +0100290 symbol = wp->w_fill_chars.foldopen;
Bram Moolenaar4fa11752021-03-03 13:26:02 +0100291 else if (first_level == 1)
Bram Moolenaar96ba25a2022-07-04 17:34:33 +0100292 symbol = wp->w_fill_chars.foldsep;
Bram Moolenaar4fa11752021-03-03 13:26:02 +0100293 else if (first_level + i <= 9)
294 symbol = '0' + first_level + i;
295 else
296 symbol = '>';
Bram Moolenaar578b49e2005-09-10 19:22:57 +0000297
Bram Moolenaar4fa11752021-03-03 13:26:02 +0100298 len = utf_char2bytes(symbol, &p[byte_counter]);
299 byte_counter += len;
300 if (first_level + i >= level)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000301 {
Bram Moolenaar4fa11752021-03-03 13:26:02 +0100302 i++;
303 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000304 }
305 }
Bram Moolenaar4fa11752021-03-03 13:26:02 +0100306
Bram Moolenaar071d4272004-06-13 20:20:40 +0000307 if (closed)
Bram Moolenaar4fa11752021-03-03 13:26:02 +0100308 {
309 if (symbol != 0)
Bram Moolenaar196a1f72021-03-21 14:39:19 +0100310 {
311 // rollback length and the character
Bram Moolenaar4fa11752021-03-03 13:26:02 +0100312 byte_counter -= len;
Bram Moolenaar196a1f72021-03-21 14:39:19 +0100313 if (len > 1)
314 // for a multibyte character, erase all the bytes
315 vim_memset(p + byte_counter, ' ', len);
316 }
Bram Moolenaar96ba25a2022-07-04 17:34:33 +0100317 symbol = wp->w_fill_chars.foldclosed;
Bram Moolenaar4fa11752021-03-03 13:26:02 +0100318 len = utf_char2bytes(symbol, &p[byte_counter]);
319 byte_counter += len;
320 }
321
322 return MAX(byte_counter + (fdc - i), (size_t)fdc);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000323}
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100324#endif // FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +0000325
Bram Moolenaar362e1a32006-03-06 23:29:24 +0000326/*
327 * Return if the composing characters at "off_from" and "off_to" differ.
Bram Moolenaar70c49c12010-03-23 15:36:35 +0100328 * Only to be used when ScreenLinesUC[off_from] != 0.
Bram Moolenaar362e1a32006-03-06 23:29:24 +0000329 */
330 static int
Bram Moolenaar05540972016-01-30 20:31:25 +0100331comp_char_differs(int off_from, int off_to)
Bram Moolenaar362e1a32006-03-06 23:29:24 +0000332{
333 int i;
334
335 for (i = 0; i < Screen_mco; ++i)
336 {
337 if (ScreenLinesC[i][off_from] != ScreenLinesC[i][off_to])
338 return TRUE;
339 if (ScreenLinesC[i][off_from] == 0)
340 break;
341 }
342 return FALSE;
343}
Bram Moolenaar362e1a32006-03-06 23:29:24 +0000344
Bram Moolenaar071d4272004-06-13 20:20:40 +0000345/*
346 * Check whether the given character needs redrawing:
347 * - the (first byte of the) character is different
348 * - the attributes are different
349 * - the character is multi-byte and the next byte is different
Bram Moolenaar88f3d3a2008-06-21 12:14:30 +0000350 * - the character is two cells wide and the second cell differs.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000351 */
352 static int
Bram Moolenaar05540972016-01-30 20:31:25 +0100353char_needs_redraw(int off_from, int off_to, int cols)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000354{
355 if (cols > 0
356 && ((ScreenLines[off_from] != ScreenLines[off_to]
357 || ScreenAttrs[off_from] != ScreenAttrs[off_to])
Bram Moolenaar071d4272004-06-13 20:20:40 +0000358 || (enc_dbcs != 0
359 && MB_BYTE2LEN(ScreenLines[off_from]) > 1
360 && (enc_dbcs == DBCS_JPNU && ScreenLines[off_from] == 0x8e
361 ? ScreenLines2[off_from] != ScreenLines2[off_to]
362 : (cols > 1 && ScreenLines[off_from + 1]
363 != ScreenLines[off_to + 1])))
364 || (enc_utf8
365 && (ScreenLinesUC[off_from] != ScreenLinesUC[off_to]
366 || (ScreenLinesUC[off_from] != 0
Bram Moolenaar88f3d3a2008-06-21 12:14:30 +0000367 && comp_char_differs(off_from, off_to))
Bram Moolenaar451cf632012-08-23 18:58:14 +0200368 || ((*mb_off2cells)(off_from, off_from + cols) > 1
369 && ScreenLines[off_from + 1]
Bram Moolenaara12a1612019-01-24 16:39:02 +0100370 != ScreenLines[off_to + 1])))))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000371 return TRUE;
372 return FALSE;
373}
374
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200375#if defined(FEAT_TERMINAL) || defined(PROTO)
376/*
377 * Return the index in ScreenLines[] for the current screen line.
378 */
379 int
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000380screen_get_current_line_off(void)
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200381{
382 return (int)(current_ScreenLine - ScreenLines);
383}
384#endif
385
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100386#ifdef FEAT_PROP_POPUP
Bram Moolenaarc662ec92019-06-23 00:15:57 +0200387/*
388 * Return TRUE if this position has a higher level popup or this cell is
389 * transparent in the current popup.
390 */
391 static int
392blocked_by_popup(int row, int col)
393{
394 int off;
395
396 if (!popup_visible)
397 return FALSE;
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200398 if (col < TPL_LCOL(NULL))
399 return FALSE;
400 off = row * screen_Columns + col - TPL_LCOL(NULL);
Bram Moolenaarc662ec92019-06-23 00:15:57 +0200401 return popup_mask[off] > screen_zindex || popup_transparent[off];
402}
403#endif
404
Bram Moolenaar071d4272004-06-13 20:20:40 +0000405/*
Bram Moolenaar7528d1f2019-09-19 23:06:20 +0200406 * Reset the highlighting. Used before clearing the screen.
407 */
408 void
409reset_screen_attr(void)
410{
411#ifdef FEAT_GUI
412 if (gui.in_use)
413 // Use a code that will reset gui.highlight_mask in
414 // gui_stop_highlight().
415 screen_attr = HL_ALL + 1;
416 else
417#endif
418 // Use attributes that is very unlikely to appear in text.
419 screen_attr = HL_BOLD | HL_UNDERLINE | HL_INVERSE | HL_STRIKETHROUGH;
420}
421
422/*
Bram Moolenaar393f8d62022-10-02 14:28:30 +0100423 * Return TRUE if the character at "row" / "col" is under the popup menu and it
424 * will be redrawn soon or it is under another popup.
425 */
426 static int
427skip_for_popup(int row, int col)
428{
429 // Popup windows with zindex higher than POPUPMENU_ZINDEX go on top.
430 if (pum_under_menu(row, col, TRUE)
431#ifdef FEAT_PROP_POPUP
432 && screen_zindex <= POPUPMENU_ZINDEX
433#endif
434 )
435 return TRUE;
436#ifdef FEAT_PROP_POPUP
437 if (blocked_by_popup(row, col))
438 return TRUE;
439#endif
440 return FALSE;
441}
442
443/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000444 * Move one "cooked" screen line to the screen, but only the characters that
445 * have actually changed. Handle insert/delete character.
446 * "coloff" gives the first column on the screen for this line.
447 * "endcol" gives the columns where valid characters are.
448 * "clear_width" is the width of the window. It's > 0 if the rest of the line
449 * needs to be cleared, negative otherwise.
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200450 * "flags" can have bits:
451 * SLF_POPUP popup window
452 * SLF_RIGHTLEFT rightleft window:
Bram Moolenaar071d4272004-06-13 20:20:40 +0000453 * When TRUE and "clear_width" > 0, clear columns 0 to "endcol"
454 * When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width"
zeertzjqd0c1b772024-03-16 15:03:33 +0100455 * SLF_INC_VCOL:
456 * When FALSE, use "last_vcol" for ScreenCols[] of the columns to clear.
457 * When TRUE, use an increasing sequence starting from "last_vcol + 1" for
458 * ScreenCols[] of the columns to clear.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000459 */
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200460 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100461screen_line(
Bram Moolenaar96ba25a2022-07-04 17:34:33 +0100462 win_T *wp,
463 int row,
464 int coloff,
465 int endcol,
466 int clear_width,
zeertzjqd0c1b772024-03-16 15:03:33 +0100467 colnr_T last_vcol,
Bram Moolenaar96ba25a2022-07-04 17:34:33 +0100468 int flags UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000469{
470 unsigned off_from;
471 unsigned off_to;
Bram Moolenaar367329b2007-08-30 11:53:22 +0000472 unsigned max_off_from;
473 unsigned max_off_to;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000474 int col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475 int hl;
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100476 int force = FALSE; // force update rest of the line
477 int redraw_this // bool: does character need redraw?
Bram Moolenaar071d4272004-06-13 20:20:40 +0000478#ifdef FEAT_GUI
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100479 = TRUE // For GUI when while-loop empty
Bram Moolenaar071d4272004-06-13 20:20:40 +0000480#endif
481 ;
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100482 int redraw_next; // redraw_this for next character
Bram Moolenaar0c502d22022-10-11 12:48:44 +0100483#ifdef FEAT_GUI_MSWIN
484 int changed_this; // TRUE if character changed
485 int changed_next; // TRUE if next character changed
486#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000487 int clear_next = FALSE;
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100488 int char_cells; // 1: normal char
489 // 2: occupies two display cells
Bram Moolenaar071d4272004-06-13 20:20:40 +0000490
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100491 // Check for illegal row and col, just in case.
Bram Moolenaar5ad15df2012-03-16 19:07:58 +0100492 if (row >= Rows)
493 row = Rows - 1;
494 if (endcol > Columns)
495 endcol = Columns;
496
Bram Moolenaar071d4272004-06-13 20:20:40 +0000497# ifdef FEAT_CLIPBOARD
498 clip_may_clear_selection(row, row);
499# endif
500
501 off_from = (unsigned)(current_ScreenLine - ScreenLines);
502 off_to = LineOffset[row] + coloff;
Bram Moolenaar367329b2007-08-30 11:53:22 +0000503 max_off_from = off_from + screen_Columns;
504 max_off_to = LineOffset[row] + screen_Columns;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000505
506#ifdef FEAT_RIGHTLEFT
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200507 if (flags & SLF_RIGHTLEFT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000508 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100509 // Clear rest first, because it's left of the text.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000510 if (clear_width > 0)
511 {
zeertzjqdeb22042024-03-17 19:44:30 +0100512 int clear_start = col;
513
Bram Moolenaar071d4272004-06-13 20:20:40 +0000514 while (col <= endcol && ScreenLines[off_to] == ' '
515 && ScreenAttrs[off_to] == 0
Bram Moolenaara12a1612019-01-24 16:39:02 +0100516 && (!enc_utf8 || ScreenLinesUC[off_to] == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000517 {
518 ++off_to;
519 ++col;
520 }
521 if (col <= endcol)
522 screen_fill(row, row + 1, col + coloff,
523 endcol + coloff + 1, ' ', ' ', 0);
zeertzjqdeb22042024-03-17 19:44:30 +0100524
525 for (int i = endcol; i >= clear_start; i--)
526 ScreenCols[off_to + (i - col)] =
527 (flags & SLF_INC_VCOL) ? ++last_vcol : last_vcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000528 }
529 col = endcol + 1;
530 off_to = LineOffset[row] + col + coloff;
531 off_from += col;
532 endcol = (clear_width > 0 ? clear_width : -clear_width);
533 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100534#endif // FEAT_RIGHTLEFT
Bram Moolenaar071d4272004-06-13 20:20:40 +0000535
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100536#ifdef FEAT_PROP_POPUP
Bram Moolenaar92e25ab2019-11-26 22:39:10 +0100537 // First char of a popup window may go on top of the right half of a
538 // double-wide character. Clear the left half to avoid it getting the popup
539 // window background color.
Bram Moolenaar927495b2020-11-06 17:58:35 +0100540 if (coloff > 0 && enc_utf8
541 && ScreenLines[off_to] == 0
Bram Moolenaardc0cf1d2020-08-23 15:09:36 +0200542 && ScreenLinesUC[off_to - 1] != 0
543 && (*mb_char2cells)(ScreenLinesUC[off_to - 1]) > 1)
Bram Moolenaar92e25ab2019-11-26 22:39:10 +0100544 {
545 ScreenLines[off_to - 1] = ' ';
546 ScreenLinesUC[off_to - 1] = 0;
547 screen_char(off_to - 1, row, col + coloff - 1);
548 }
549#endif
550
Bram Moolenaar071d4272004-06-13 20:20:40 +0000551 redraw_next = char_needs_redraw(off_from, off_to, endcol - col);
Bram Moolenaar0c502d22022-10-11 12:48:44 +0100552#ifdef FEAT_GUI_MSWIN
553 changed_next = redraw_next;
554#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000555
556 while (col < endcol)
557 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000558 if (has_mbyte && (col + 1 < endcol))
Bram Moolenaar367329b2007-08-30 11:53:22 +0000559 char_cells = (*mb_off2cells)(off_from, max_off_from);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000560 else
561 char_cells = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000562
563 redraw_this = redraw_next;
Bram Moolenaarb9081882022-07-09 04:56:24 +0100564 redraw_next = force || char_needs_redraw(off_from + char_cells,
565 off_to + char_cells, endcol - col - char_cells);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000566
567#ifdef FEAT_GUI
Bram Moolenaar0c502d22022-10-11 12:48:44 +0100568# ifdef FEAT_GUI_MSWIN
569 changed_this = changed_next;
570 changed_next = redraw_next;
571# endif
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100572 // If the next character was bold, then redraw the current character to
573 // remove any pixels that might have spilt over into us. This only
574 // happens in the GUI.
Bram Moolenaar0c502d22022-10-11 12:48:44 +0100575 // With MS-Windows antialiasing may also cause pixels to spill over
576 // from a previous character, no matter attributes, always redraw if a
577 // character changed.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000578 if (redraw_next && gui.in_use)
579 {
Bram Moolenaar0c502d22022-10-11 12:48:44 +0100580# ifndef FEAT_GUI_MSWIN
Bram Moolenaarb9081882022-07-09 04:56:24 +0100581 hl = ScreenAttrs[off_to + char_cells];
Bram Moolenaar600dddc2006-03-12 22:05:10 +0000582 if (hl > HL_ALL)
583 hl = syn_attr2attr(hl);
584 if (hl & HL_BOLD)
Bram Moolenaar0c502d22022-10-11 12:48:44 +0100585# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000586 redraw_this = TRUE;
587 }
588#endif
Bram Moolenaar393f8d62022-10-02 14:28:30 +0100589 // Do not redraw if under the popup menu.
590 if (redraw_this && skip_for_popup(row, col + coloff))
Bram Moolenaar33796b32019-06-08 16:01:13 +0200591 redraw_this = FALSE;
Bram Moolenaar393f8d62022-10-02 14:28:30 +0100592
Bram Moolenaar071d4272004-06-13 20:20:40 +0000593 if (redraw_this)
594 {
595 /*
596 * Special handling when 'xs' termcap flag set (hpterm):
597 * Attributes for characters are stored at the position where the
598 * cursor is when writing the highlighting code. The
599 * start-highlighting code must be written with the cursor on the
600 * first highlighted character. The stop-highlighting code must
601 * be written with the cursor just after the last highlighted
602 * character.
Bram Moolenaarc4568ab2018-11-16 16:21:05 +0100603 * Overwriting a character doesn't remove its highlighting. Need
Bram Moolenaar071d4272004-06-13 20:20:40 +0000604 * to clear the rest of the line, and force redrawing it
605 * completely.
606 */
607 if ( p_wiv
608 && !force
609#ifdef FEAT_GUI
610 && !gui.in_use
611#endif
612 && ScreenAttrs[off_to] != 0
613 && ScreenAttrs[off_from] != ScreenAttrs[off_to])
614 {
615 /*
616 * Need to remove highlighting attributes here.
617 */
618 windgoto(row, col + coloff);
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100619 out_str(T_CE); // clear rest of this screen line
620 screen_start(); // don't know where cursor is now
621 force = TRUE; // force redraw of rest of the line
622 redraw_next = TRUE; // or else next char would miss out
Bram Moolenaar071d4272004-06-13 20:20:40 +0000623
624 /*
625 * If the previous character was highlighted, need to stop
626 * highlighting at this character.
627 */
628 if (col + coloff > 0 && ScreenAttrs[off_to - 1] != 0)
629 {
630 screen_attr = ScreenAttrs[off_to - 1];
631 term_windgoto(row, col + coloff);
632 screen_stop_highlight();
633 }
634 else
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100635 screen_attr = 0; // highlighting has stopped
Bram Moolenaar071d4272004-06-13 20:20:40 +0000636 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000637 if (enc_dbcs != 0)
638 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100639 // Check if overwriting a double-byte with a single-byte or
640 // the other way around requires another character to be
641 // redrawn. For UTF-8 this isn't needed, because comparing
642 // ScreenLinesUC[] is sufficient.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000643 if (char_cells == 1
644 && col + 1 < endcol
Bram Moolenaar367329b2007-08-30 11:53:22 +0000645 && (*mb_off2cells)(off_to, max_off_to) > 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000646 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100647 // Writing a single-cell character over a double-cell
648 // character: need to redraw the next cell.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000649 ScreenLines[off_to + 1] = 0;
650 redraw_next = TRUE;
651 }
652 else if (char_cells == 2
653 && col + 2 < endcol
Bram Moolenaar367329b2007-08-30 11:53:22 +0000654 && (*mb_off2cells)(off_to, max_off_to) == 1
655 && (*mb_off2cells)(off_to + 1, max_off_to) > 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000656 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100657 // Writing the second half of a double-cell character over
658 // a double-cell character: need to redraw the second
659 // cell.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000660 ScreenLines[off_to + 2] = 0;
661 redraw_next = TRUE;
662 }
663
664 if (enc_dbcs == DBCS_JPNU)
665 ScreenLines2[off_to] = ScreenLines2[off_from];
666 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100667 // When writing a single-width character over a double-width
668 // character and at the end of the redrawn text, need to clear out
Dominique Pelleaf4a61a2021-12-27 17:21:41 +0000669 // the right half of the old character.
670 // Also required when writing the right half of a double-width
671 // char over the left half of an existing one.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000672 if (has_mbyte && col + char_cells == endcol
673 && ((char_cells == 1
Bram Moolenaar367329b2007-08-30 11:53:22 +0000674 && (*mb_off2cells)(off_to, max_off_to) > 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000675 || (char_cells == 2
Bram Moolenaar367329b2007-08-30 11:53:22 +0000676 && (*mb_off2cells)(off_to, max_off_to) == 1
677 && (*mb_off2cells)(off_to + 1, max_off_to) > 1)))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000678 clear_next = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000679
680 ScreenLines[off_to] = ScreenLines[off_from];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000681 if (enc_utf8)
682 {
683 ScreenLinesUC[off_to] = ScreenLinesUC[off_from];
684 if (ScreenLinesUC[off_from] != 0)
685 {
Bram Moolenaar362e1a32006-03-06 23:29:24 +0000686 int i;
687
688 for (i = 0; i < Screen_mco; ++i)
689 ScreenLinesC[i][off_to] = ScreenLinesC[i][off_from];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000690 }
691 }
692 if (char_cells == 2)
693 ScreenLines[off_to + 1] = ScreenLines[off_from + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694
695#if defined(FEAT_GUI) || defined(UNIX)
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100696 // The bold trick makes a single column of pixels appear in the
697 // next character. When a bold character is removed, the next
698 // character should be redrawn too. This happens for our own GUI
699 // and for some xterms.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000700 if (
701# ifdef FEAT_GUI
702 gui.in_use
703# endif
704# if defined(FEAT_GUI) && defined(UNIX)
705 ||
706# endif
707# ifdef UNIX
708 term_is_xterm
709# endif
710 )
711 {
712 hl = ScreenAttrs[off_to];
Bram Moolenaar600dddc2006-03-12 22:05:10 +0000713 if (hl > HL_ALL)
714 hl = syn_attr2attr(hl);
715 if (hl & HL_BOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000716 redraw_next = TRUE;
717 }
718#endif
Bram Moolenaar0c502d22022-10-11 12:48:44 +0100719#ifdef FEAT_GUI_MSWIN
720 // MS-Windows antialiasing may spill over to the next character,
721 // redraw that one if this one changed, no matter attributes.
722 if (gui.in_use && changed_this)
723 redraw_next = TRUE;
724#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000725 ScreenAttrs[off_to] = ScreenAttrs[off_from];
Bram Moolenaara12a1612019-01-24 16:39:02 +0100726
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100727 // For simplicity set the attributes of second half of a
728 // double-wide character equal to the first half.
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000729 if (char_cells == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000730 ScreenAttrs[off_to + 1] = ScreenAttrs[off_from];
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000731
732 if (enc_dbcs != 0 && char_cells == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000733 screen_char_2(off_to, row, col + coloff);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000734 else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000735 screen_char(off_to, row, col + coloff);
736 }
737 else if ( p_wiv
738#ifdef FEAT_GUI
739 && !gui.in_use
740#endif
741 && col + coloff > 0)
742 {
743 if (ScreenAttrs[off_to] == ScreenAttrs[off_to - 1])
744 {
745 /*
746 * Don't output stop-highlight when moving the cursor, it will
747 * stop the highlighting when it should continue.
748 */
749 screen_attr = 0;
750 }
751 else if (screen_attr != 0)
752 screen_stop_highlight();
753 }
754
Bram Moolenaarb9081882022-07-09 04:56:24 +0100755 ScreenCols[off_to] = ScreenCols[off_from];
756 if (char_cells == 2)
zeertzjq99941602023-08-19 13:08:50 +0200757 ScreenCols[off_to + 1] = ScreenCols[off_from + 1];
Bram Moolenaarb9081882022-07-09 04:56:24 +0100758
759 off_to += char_cells;
760 off_from += char_cells;
761 col += char_cells;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000762 }
763
Bram Moolenaarff85d4a2022-10-02 15:21:04 +0100764 if (clear_next && !skip_for_popup(row, col + coloff))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000765 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100766 // Clear the second half of a double-wide character of which the left
767 // half was overwritten with a single-wide character.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000768 ScreenLines[off_to] = ' ';
769 if (enc_utf8)
770 ScreenLinesUC[off_to] = 0;
771 screen_char(off_to, row, col + coloff);
772 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000773
774 if (clear_width > 0
775#ifdef FEAT_RIGHTLEFT
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200776 && !(flags & SLF_RIGHTLEFT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000777#endif
778 )
779 {
780#ifdef FEAT_GUI
781 int startCol = col;
782#endif
783
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100784 // blank out the rest of the line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000785 while (col < clear_width && ScreenLines[off_to] == ' '
786 && ScreenAttrs[off_to] == 0
Bram Moolenaara12a1612019-01-24 16:39:02 +0100787 && (!enc_utf8 || ScreenLinesUC[off_to] == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000788 {
zeertzjqd0c1b772024-03-16 15:03:33 +0100789 ScreenCols[off_to] =
790 (flags & SLF_INC_VCOL) ? ++last_vcol : last_vcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000791 ++off_to;
792 ++col;
793 }
794 if (col < clear_width)
795 {
796#ifdef FEAT_GUI
797 /*
798 * In the GUI, clearing the rest of the line may leave pixels
799 * behind if the first character cleared was bold. Some bold
800 * fonts spill over the left. In this case we redraw the previous
801 * character too. If we didn't skip any blanks above, then we
802 * only redraw if the character wasn't already redrawn anyway.
803 */
Bram Moolenaar9c697322006-10-09 20:11:17 +0000804 if (gui.in_use && (col > startCol || !redraw_this))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000805 {
806 hl = ScreenAttrs[off_to];
807 if (hl > HL_ALL || (hl & HL_BOLD))
Bram Moolenaar9c697322006-10-09 20:11:17 +0000808 {
809 int prev_cells = 1;
Bram Moolenaara12a1612019-01-24 16:39:02 +0100810
Bram Moolenaar9c697322006-10-09 20:11:17 +0000811 if (enc_utf8)
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100812 // for utf-8, ScreenLines[char_offset + 1] == 0 means
813 // that its width is 2.
Bram Moolenaar9c697322006-10-09 20:11:17 +0000814 prev_cells = ScreenLines[off_to - 1] == 0 ? 2 : 1;
815 else if (enc_dbcs != 0)
816 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100817 // find previous character by counting from first
818 // column and get its width.
Bram Moolenaar9c697322006-10-09 20:11:17 +0000819 unsigned off = LineOffset[row];
Bram Moolenaar367329b2007-08-30 11:53:22 +0000820 unsigned max_off = LineOffset[row] + screen_Columns;
Bram Moolenaar9c697322006-10-09 20:11:17 +0000821
822 while (off < off_to)
823 {
Bram Moolenaar367329b2007-08-30 11:53:22 +0000824 prev_cells = (*mb_off2cells)(off, max_off);
Bram Moolenaar9c697322006-10-09 20:11:17 +0000825 off += prev_cells;
826 }
827 }
828
Bram Moolenaarff85d4a2022-10-02 15:21:04 +0100829 if (!skip_for_popup(row, col + coloff - prev_cells))
830 {
831 if (enc_dbcs != 0 && prev_cells > 1)
832 screen_char_2(off_to - prev_cells, row,
Bram Moolenaar9c697322006-10-09 20:11:17 +0000833 col + coloff - prev_cells);
Bram Moolenaarff85d4a2022-10-02 15:21:04 +0100834 else
835 screen_char(off_to - prev_cells, row,
Bram Moolenaar9c697322006-10-09 20:11:17 +0000836 col + coloff - prev_cells);
Bram Moolenaarff85d4a2022-10-02 15:21:04 +0100837 }
Bram Moolenaar9c697322006-10-09 20:11:17 +0000838 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000839 }
840#endif
841 screen_fill(row, row + 1, col + coloff, clear_width + coloff,
842 ' ', ' ', 0);
Bram Moolenaarb9081882022-07-09 04:56:24 +0100843 while (col < clear_width)
844 {
zeertzjqd0c1b772024-03-16 15:03:33 +0100845 ScreenCols[off_to++]
846 = (flags & SLF_INC_VCOL) ? ++last_vcol : last_vcol;
Bram Moolenaarb9081882022-07-09 04:56:24 +0100847 ++col;
848 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000849 }
850 }
851
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200852 if (clear_width > 0
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100853#ifdef FEAT_PROP_POPUP
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200854 && !(flags & SLF_POPUP) // no separator for popup window
855#endif
856 )
Bram Moolenaar071d4272004-06-13 20:20:40 +0000857 {
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200858 // For a window that has a right neighbor, draw the separator char
Bram Moolenaaraef5c622019-06-08 17:25:33 +0200859 // right of the window contents. But not on top of a popup window.
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200860 if (coloff + col < TPL_LCOL(NULL) + COLUMNS_WITHOUT_TPL())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000861 {
Bram Moolenaarff85d4a2022-10-02 15:21:04 +0100862 if (!skip_for_popup(row, col + coloff))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000863 {
Bram Moolenaaraef5c622019-06-08 17:25:33 +0200864 int c;
865
Bram Moolenaar96ba25a2022-07-04 17:34:33 +0100866 c = fillchar_vsep(&hl, wp);
Bram Moolenaaraef5c622019-06-08 17:25:33 +0200867 if (ScreenLines[off_to] != (schar_T)c
868 || (enc_utf8 && (int)ScreenLinesUC[off_to]
869 != (c >= 0x80 ? c : 0))
870 || ScreenAttrs[off_to] != hl)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000871 {
Bram Moolenaaraef5c622019-06-08 17:25:33 +0200872 ScreenLines[off_to] = c;
873 ScreenAttrs[off_to] = hl;
874 if (enc_utf8)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000875 {
Bram Moolenaaraef5c622019-06-08 17:25:33 +0200876 if (c >= 0x80)
877 {
878 ScreenLinesUC[off_to] = c;
879 ScreenLinesC[0][off_to] = 0;
880 }
881 else
882 ScreenLinesUC[off_to] = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000883 }
Bram Moolenaaraef5c622019-06-08 17:25:33 +0200884 screen_char(off_to, row, col + coloff);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000885 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000886 }
887 }
888 else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000889 LineWraps[row] = FALSE;
890 }
891}
892
Bram Moolenaar0fa313a2005-08-10 21:07:57 +0000893#if defined(FEAT_RIGHTLEFT) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000894/*
Bram Moolenaar0fa313a2005-08-10 21:07:57 +0000895 * Mirror text "str" for right-left displaying.
896 * Only works for single-byte characters (e.g., numbers).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000897 */
Bram Moolenaar0fa313a2005-08-10 21:07:57 +0000898 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100899rl_mirror(char_u *str)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000900{
901 char_u *p1, *p2;
902 int t;
903
904 for (p1 = str, p2 = str + STRLEN(str) - 1; p1 < p2; ++p1, --p2)
905 {
906 t = *p1;
907 *p1 = *p2;
908 *p2 = t;
909 }
910}
911#endif
912
Bram Moolenaar071d4272004-06-13 20:20:40 +0000913/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000914 * Draw the verticap separator right of window "wp" starting with line "row".
915 */
Bram Moolenaar7528d1f2019-09-19 23:06:20 +0200916 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100917draw_vsep_win(win_T *wp, int row)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000918{
919 int hl;
920 int c;
921
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000922 if (!wp->w_vsep_width)
923 return;
924
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200925 if (COLUMNS_WITHOUT_TPL() <= W_ENDCOL(wp) + 1)
926 return;
927
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000928 // draw the vertical separator right of this window
929 c = fillchar_vsep(&hl, wp);
930 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + wp->w_height,
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200931 W_ENDCOL(wp) + TPL_LCOL(wp), W_ENDCOL(wp) + 1 + TPL_LCOL(wp),
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000932 c, ' ', hl);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000933}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000934
Bram Moolenaar071d4272004-06-13 20:20:40 +0000935/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000936 * Return TRUE if the status line of window "wp" is connected to the status
937 * line of the window right of it. If not, then it's a vertical separator.
938 * Only call if (wp->w_vsep_width != 0).
939 */
940 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100941stl_connected(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000942{
943 frame_T *fr;
944
945 fr = wp->w_frame;
946 while (fr->fr_parent != NULL)
947 {
948 if (fr->fr_parent->fr_layout == FR_COL)
949 {
950 if (fr->fr_next != NULL)
951 break;
952 }
953 else
954 {
955 if (fr->fr_next != NULL)
956 return TRUE;
957 }
958 fr = fr->fr_parent;
959 }
960 return FALSE;
961}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000962
Bram Moolenaar071d4272004-06-13 20:20:40 +0000963
Bram Moolenaar071d4272004-06-13 20:20:40 +0000964/*
965 * Get the value to show for the language mappings, active 'keymap'.
966 */
967 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100968get_keymap_str(
969 win_T *wp,
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100970 char_u *fmt, // format string containing one %s item
971 char_u *buf, // buffer for the result
972 int len) // length of buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000973{
974 char_u *p;
John Marriotta21240b2025-01-08 20:10:59 +0100975 int plen;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000976
977 if (wp->w_buffer->b_p_iminsert != B_IMODE_LMAP)
John Marriotta21240b2025-01-08 20:10:59 +0100978 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000979
Bram Moolenaar071d4272004-06-13 20:20:40 +0000980#ifdef FEAT_EVAL
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000981 buf_T *old_curbuf = curbuf;
982 win_T *old_curwin = curwin;
John Marriotta21240b2025-01-08 20:10:59 +0100983 char_u to_evaluate[] = "b:keymap_name";
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000984 char_u *s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000985
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000986 curbuf = wp->w_buffer;
987 curwin = wp;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000988 ++emsg_skip;
John Marriotta21240b2025-01-08 20:10:59 +0100989 s = p = eval_to_string(to_evaluate, FALSE, FALSE);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000990 --emsg_skip;
991 curbuf = old_curbuf;
992 curwin = old_curwin;
993 if (p == NULL || *p == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000994#endif
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000995 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000996#ifdef FEAT_KEYMAP
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000997 if (wp->w_buffer->b_kmap_state & KEYMAP_LOADED)
998 p = wp->w_buffer->b_p_keymap;
999 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001000#endif
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001001 p = (char_u *)"lang";
Bram Moolenaar071d4272004-06-13 20:20:40 +00001002 }
John Marriotta21240b2025-01-08 20:10:59 +01001003 plen = vim_snprintf((char *)buf, len, (char *)fmt, p);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001004#ifdef FEAT_EVAL
1005 vim_free(s);
1006#endif
John Marriotta21240b2025-01-08 20:10:59 +01001007 if (plen < 0 || plen > len - 1)
1008 {
1009 buf[0] = NUL;
1010 plen = 0;
1011 }
1012
1013 return plen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001014}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001015
1016#if defined(FEAT_STL_OPT) || defined(PROTO)
1017/*
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00001018 * Redraw the status line or ruler of window "wp".
1019 * When "wp" is NULL redraw the tab pages line from 'tabline'.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001020 */
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001021 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001022win_redr_custom(
1023 win_T *wp,
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001024 int draw_ruler) // TRUE or FALSE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001025{
Bram Moolenaar1d633412013-12-11 15:52:01 +01001026 static int entered = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001027 int attr;
1028 int curattr;
1029 int row;
1030 int col = 0;
1031 int maxwidth;
1032 int width;
1033 int n;
1034 int len;
1035 int fillchar;
1036 char_u buf[MAXPATHL];
Bram Moolenaar362f3562009-11-03 16:20:34 +00001037 char_u *stl;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001038 char_u *p;
Luuk van Baal7b224fd2022-11-07 12:16:51 +00001039 char_u *opt_name;
Bram Moolenaar24fe33a2022-11-24 00:09:02 +00001040 int opt_scope = 0;
Bram Moolenaar8133cc62020-10-26 21:05:27 +01001041 stl_hlrec_T *hltab;
1042 stl_hlrec_T *tabtab;
Bram Moolenaar61452852011-02-01 18:01:11 +01001043 win_T *ewp;
1044 int p_crb_save;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001045
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001046 // There is a tiny chance that this gets called recursively: When
1047 // redrawing a status line triggers redrawing the ruler or tabline.
1048 // Avoid trouble by not allowing recursion.
Bram Moolenaar1d633412013-12-11 15:52:01 +01001049 if (entered)
1050 return;
1051 entered = TRUE;
1052
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001053 // setup environment for the task at hand
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00001054 if (wp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001055 {
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001056 // Use 'tabline'. Always at the first line of the screen.
Bram Moolenaar362f3562009-11-03 16:20:34 +00001057 stl = p_tal;
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00001058 row = 0;
Bram Moolenaar65c923a2006-03-03 22:56:30 +00001059 fillchar = ' ';
Bram Moolenaar8820b482017-03-16 17:23:31 +01001060 attr = HL_ATTR(HLF_TPF);
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02001061 maxwidth = COLUMNS_WITHOUT_TPL();
Luuk van Baal7b224fd2022-11-07 12:16:51 +00001062 opt_name = (char_u *)"tabline";
Bram Moolenaar071d4272004-06-13 20:20:40 +00001063 }
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00001064 else
1065 {
Bram Moolenaar49c51b82021-04-01 16:16:18 +02001066 row = statusline_row(wp);
Bram Moolenaar3633cf52017-07-31 22:29:35 +02001067 fillchar = fillchar_status(&attr, wp);
Sean Dewarfc8a6012023-04-17 16:41:20 +01001068 int in_status_line = wp->w_status_height != 0;
1069 maxwidth = in_status_line ? wp->w_width : Columns;
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00001070
1071 if (draw_ruler)
1072 {
Bram Moolenaar362f3562009-11-03 16:20:34 +00001073 stl = p_ruf;
Luuk van Baal7b224fd2022-11-07 12:16:51 +00001074 opt_name = (char_u *)"rulerformat";
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001075 // advance past any leading group spec - implicit in ru_col
Bram Moolenaar362f3562009-11-03 16:20:34 +00001076 if (*stl == '%')
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00001077 {
Bram Moolenaar362f3562009-11-03 16:20:34 +00001078 if (*++stl == '-')
1079 stl++;
1080 if (atoi((char *)stl))
1081 while (VIM_ISDIGIT(*stl))
1082 stl++;
1083 if (*stl++ != '(')
1084 stl = p_ruf;
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00001085 }
Sean Dewarfc8a6012023-04-17 16:41:20 +01001086 col = ru_col - (Columns - maxwidth);
1087 if (col < (maxwidth + 1) / 2)
1088 col = (maxwidth + 1) / 2;
1089 maxwidth -= col;
1090 if (!in_status_line)
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00001091 {
1092 row = Rows - 1;
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001093 --maxwidth; // writing in last column may cause scrolling
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00001094 fillchar = ' ';
1095 attr = 0;
1096 }
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00001097 }
1098 else
1099 {
Luuk van Baal7b224fd2022-11-07 12:16:51 +00001100 opt_name = (char_u *)"statusline";
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00001101 if (*wp->w_p_stl != NUL)
Luuk van Baal7b224fd2022-11-07 12:16:51 +00001102 {
Bram Moolenaar362f3562009-11-03 16:20:34 +00001103 stl = wp->w_p_stl;
Luuk van Baal7b224fd2022-11-07 12:16:51 +00001104 opt_scope = OPT_LOCAL;
1105 }
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00001106 else
Bram Moolenaar362f3562009-11-03 16:20:34 +00001107 stl = p_stl;
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00001108 }
1109
Sean Dewarfc8a6012023-04-17 16:41:20 +01001110 if (in_status_line)
1111 col += wp->w_wincol;
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00001112 }
1113
Bram Moolenaar071d4272004-06-13 20:20:40 +00001114 if (maxwidth <= 0)
Bram Moolenaar1d633412013-12-11 15:52:01 +01001115 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001116
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001117 // Temporarily reset 'cursorbind', we don't want a side effect from moving
1118 // the cursor away and back.
Bram Moolenaar61452852011-02-01 18:01:11 +01001119 ewp = wp == NULL ? curwin : wp;
1120 p_crb_save = ewp->w_p_crb;
1121 ewp->w_p_crb = FALSE;
1122
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001123 // Make a copy, because the statusline may include a function call that
1124 // might change the option value and free the memory.
Bram Moolenaar362f3562009-11-03 16:20:34 +00001125 stl = vim_strsave(stl);
Bram Moolenaar61452852011-02-01 18:01:11 +01001126 width = build_stl_str_hl(ewp, buf, sizeof(buf),
John Marriottc15de972025-01-17 13:54:49 +01001127 (stl == NULL) ? (char_u *)"" : stl, opt_name, opt_scope,
Bram Moolenaar8133cc62020-10-26 21:05:27 +01001128 fillchar, maxwidth, &hltab, &tabtab);
Bram Moolenaar362f3562009-11-03 16:20:34 +00001129 vim_free(stl);
Bram Moolenaar61452852011-02-01 18:01:11 +01001130 ewp->w_p_crb = p_crb_save;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001131
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001132 // Make all characters printable.
Bram Moolenaar7c5676b2010-12-08 19:56:58 +01001133 p = transstr(buf);
1134 if (p != NULL)
1135 {
John Marriottc15de972025-01-17 13:54:49 +01001136 len = vim_snprintf((char *)buf, sizeof(buf), "%s", p);
Bram Moolenaar7c5676b2010-12-08 19:56:58 +01001137 vim_free(p);
1138 }
John Marriottc15de972025-01-17 13:54:49 +01001139 else
1140 len = (int)STRLEN(buf);
Bram Moolenaar7c5676b2010-12-08 19:56:58 +01001141
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001142 // fill up with "fillchar"
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00001143 while (width < maxwidth && len < (int)sizeof(buf) - 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001144 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001145 len += (*mb_char2bytes)(fillchar, buf + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001146 ++width;
1147 }
1148 buf[len] = NUL;
1149
Bram Moolenaard1f56e62006-02-22 21:25:37 +00001150 /*
1151 * Draw each snippet with the specified highlighting.
1152 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001153 curattr = attr;
1154 p = buf;
Bram Moolenaard1f56e62006-02-22 21:25:37 +00001155 for (n = 0; hltab[n].start != NULL; n++)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001156 {
Bram Moolenaard1f56e62006-02-22 21:25:37 +00001157 len = (int)(hltab[n].start - p);
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02001158 screen_puts_len(p, len, row, col + TPL_LCOL(wp), curattr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001159 col += vim_strnsize(p, len);
Bram Moolenaard1f56e62006-02-22 21:25:37 +00001160 p = hltab[n].start;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001161
Bram Moolenaard1f56e62006-02-22 21:25:37 +00001162 if (hltab[n].userhl == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001163 curattr = attr;
Bram Moolenaard1f56e62006-02-22 21:25:37 +00001164 else if (hltab[n].userhl < 0)
1165 curattr = syn_id2attr(-hltab[n].userhl);
Bram Moolenaar4033c552017-09-16 20:54:51 +02001166#ifdef FEAT_TERMINAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02001167 else if (wp != NULL && wp != curwin && bt_terminal(wp->w_buffer)
1168 && wp->w_status_height != 0)
1169 curattr = highlight_stltermnc[hltab[n].userhl - 1];
Bram Moolenaarbce4f622017-08-13 21:37:43 +02001170 else if (wp != NULL && bt_terminal(wp->w_buffer)
1171 && wp->w_status_height != 0)
1172 curattr = highlight_stlterm[hltab[n].userhl - 1];
Bram Moolenaar4033c552017-09-16 20:54:51 +02001173#endif
Bram Moolenaar238a5642006-02-21 22:12:05 +00001174 else if (wp != NULL && wp != curwin && wp->w_status_height != 0)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00001175 curattr = highlight_stlnc[hltab[n].userhl - 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176 else
Bram Moolenaard1f56e62006-02-22 21:25:37 +00001177 curattr = highlight_user[hltab[n].userhl - 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001178 }
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02001179 screen_puts(p, row, col + TPL_LCOL(wp), curattr);
Bram Moolenaard1f56e62006-02-22 21:25:37 +00001180
1181 if (wp == NULL)
1182 {
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001183 // Fill the TabPageIdxs[] array for clicking in the tab pagesline.
Bram Moolenaard1f56e62006-02-22 21:25:37 +00001184 col = 0;
1185 len = 0;
1186 p = buf;
1187 fillchar = 0;
1188 for (n = 0; tabtab[n].start != NULL; n++)
1189 {
1190 len += vim_strnsize(p, (int)(tabtab[n].start - p));
1191 while (col < len)
1192 TabPageIdxs[col++] = fillchar;
1193 p = tabtab[n].start;
1194 fillchar = tabtab[n].userhl;
1195 }
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02001196 while (col < COLUMNS_WITHOUT_TPL())
Bram Moolenaard1f56e62006-02-22 21:25:37 +00001197 TabPageIdxs[col++] = fillchar;
1198 }
Bram Moolenaar1d633412013-12-11 15:52:01 +01001199
1200theend:
1201 entered = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001202}
1203
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001204#endif // FEAT_STL_OPT
Bram Moolenaar071d4272004-06-13 20:20:40 +00001205
1206/*
1207 * Output a single character directly to the screen and update ScreenLines.
1208 */
1209 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001210screen_putchar(int c, int row, int col, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001211{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001212 char_u buf[MB_MAXBYTES + 1];
1213
Bram Moolenaar9a920d82012-06-01 15:21:02 +02001214 if (has_mbyte)
1215 buf[(*mb_char2bytes)(c, buf)] = NUL;
1216 else
Bram Moolenaar9a920d82012-06-01 15:21:02 +02001217 {
1218 buf[0] = c;
1219 buf[1] = NUL;
1220 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001221 screen_puts(buf, row, col, attr);
1222}
1223
1224/*
zeertzjq47eec672023-06-01 20:26:55 +01001225 * Get a single character directly from ScreenLines into "bytes", which must
1226 * have a size of "MB_MAXBYTES + 1".
1227 * If "attrp" is not NULL, return the character's attribute in "*attrp".
Bram Moolenaar071d4272004-06-13 20:20:40 +00001228 */
1229 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001230screen_getbytes(int row, int col, char_u *bytes, int *attrp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001231{
1232 unsigned off;
1233
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001234 // safety check
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001235 if (ScreenLines == NULL || row >= screen_Rows || col >= screen_Columns)
1236 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001237
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001238 off = LineOffset[row] + col;
zeertzjq47eec672023-06-01 20:26:55 +01001239 if (attrp != NULL)
1240 *attrp = ScreenAttrs[off];
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001241 bytes[0] = ScreenLines[off];
1242 bytes[1] = NUL;
1243
1244 if (enc_utf8 && ScreenLinesUC[off] != 0)
1245 bytes[utfc_char2bytes(off, bytes)] = NUL;
1246 else if (enc_dbcs == DBCS_JPNU && ScreenLines[off] == 0x8e)
1247 {
1248 bytes[0] = ScreenLines[off];
1249 bytes[1] = ScreenLines2[off];
1250 bytes[2] = NUL;
1251 }
1252 else if (enc_dbcs && MB_BYTE2LEN(bytes[0]) > 1)
1253 {
1254 bytes[1] = ScreenLines[off + 1];
1255 bytes[2] = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001256 }
1257}
1258
Bram Moolenaar362e1a32006-03-06 23:29:24 +00001259/*
1260 * Return TRUE if composing characters for screen posn "off" differs from
1261 * composing characters in "u8cc".
Bram Moolenaar70c49c12010-03-23 15:36:35 +01001262 * Only to be used when ScreenLinesUC[off] != 0.
Bram Moolenaar362e1a32006-03-06 23:29:24 +00001263 */
1264 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001265screen_comp_differs(int off, int *u8cc)
Bram Moolenaar362e1a32006-03-06 23:29:24 +00001266{
1267 int i;
1268
1269 for (i = 0; i < Screen_mco; ++i)
1270 {
1271 if (ScreenLinesC[i][off] != (u8char_T)u8cc[i])
1272 return TRUE;
1273 if (u8cc[i] == 0)
1274 break;
1275 }
1276 return FALSE;
1277}
Bram Moolenaar362e1a32006-03-06 23:29:24 +00001278
Bram Moolenaar071d4272004-06-13 20:20:40 +00001279/*
1280 * Put string '*text' on the screen at position 'row' and 'col', with
1281 * attributes 'attr', and update ScreenLines[] and ScreenAttrs[].
1282 * Note: only outputs within one row, message is truncated at screen boundary!
1283 * Note: if ScreenLines[], row and/or col is invalid, nothing is done.
1284 */
1285 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001286screen_puts(
1287 char_u *text,
1288 int row,
1289 int col,
1290 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001291{
1292 screen_puts_len(text, -1, row, col, attr);
1293}
1294
1295/*
1296 * Like screen_puts(), but output "text[len]". When "len" is -1 output up to
1297 * a NUL.
1298 */
1299 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001300screen_puts_len(
1301 char_u *text,
1302 int textlen,
1303 int row,
1304 int col,
Bram Moolenaar35d8c202022-03-03 11:46:00 +00001305 int attr_arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001306{
Bram Moolenaar35d8c202022-03-03 11:46:00 +00001307 int attr = attr_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001308 unsigned off;
1309 char_u *ptr = text;
Bram Moolenaare4c21e62014-05-22 16:05:19 +02001310 int len = textlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001311 int c;
Bram Moolenaar367329b2007-08-30 11:53:22 +00001312 unsigned max_off;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001313 int mbyte_blen = 1;
1314 int mbyte_cells = 1;
1315 int u8c = 0;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00001316 int u8cc[MAX_MCO];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001317 int clear_next_cell = FALSE;
Bram Moolenaara12a1612019-01-24 16:39:02 +01001318#ifdef FEAT_ARABIC
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001319 int prev_c = 0; // previous Arabic character
Bram Moolenaar362e1a32006-03-06 23:29:24 +00001320 int pc, nc, nc1;
1321 int pcc[MAX_MCO];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001322#endif
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001323 int force_redraw_this;
1324 int force_redraw_next = FALSE;
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001325 int need_redraw;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001326
Bram Moolenaar0b4c9ed2019-06-03 22:04:23 +02001327 // Safety check. The check for negative row and column is to fix issue
1328 // #4102. TODO: find out why row/col could be negative.
1329 if (ScreenLines == NULL
1330 || row >= screen_Rows || row < 0
1331 || col >= screen_Columns || col < 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001332 return;
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001333 off = LineOffset[row] + col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001334
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00001335 // When drawing over the right half of a double-wide char clear out the
1336 // left half. Only needed in a terminal.
Bram Moolenaar7693ec62008-07-24 18:29:37 +00001337 if (has_mbyte && col > 0 && col < screen_Columns
Bram Moolenaara12a1612019-01-24 16:39:02 +01001338#ifdef FEAT_GUI
Bram Moolenaarc236c162008-07-13 17:41:49 +00001339 && !gui.in_use
Bram Moolenaara12a1612019-01-24 16:39:02 +01001340#endif
Bram Moolenaarc236c162008-07-13 17:41:49 +00001341 && mb_fix_col(col, row) != col)
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001342 {
Bram Moolenaarff85d4a2022-10-02 15:21:04 +01001343 if (!skip_for_popup(row, col - 1))
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001344 {
Bram Moolenaarff85d4a2022-10-02 15:21:04 +01001345 ScreenLines[off - 1] = ' ';
1346 ScreenAttrs[off - 1] = 0;
1347 if (enc_utf8)
1348 {
1349 ScreenLinesUC[off - 1] = 0;
1350 ScreenLinesC[0][off - 1] = 0;
1351 }
1352 // redraw the previous cell, make it empty
1353 screen_char(off - 1, row, col - 1);
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001354 }
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001355 // force the cell at "col" to be redrawn
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001356 force_redraw_next = TRUE;
1357 }
Bram Moolenaarc236c162008-07-13 17:41:49 +00001358
Bram Moolenaar367329b2007-08-30 11:53:22 +00001359 max_off = LineOffset[row] + screen_Columns;
Bram Moolenaara064ac82007-08-05 18:10:54 +00001360 while (col < screen_Columns
1361 && (len < 0 || (int)(ptr - text) < len)
1362 && *ptr != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001363 {
1364 c = *ptr;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001365 // check if this is the first byte of a multibyte
Bram Moolenaar071d4272004-06-13 20:20:40 +00001366 if (has_mbyte)
1367 {
zeertzjq4dc513a2022-07-25 19:42:02 +01001368 mbyte_blen = enc_utf8 && len > 0
1369 ? utfc_ptr2len_len(ptr, (int)((text + len) - ptr))
1370 : (*mb_ptr2len)(ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001371 if (enc_dbcs == DBCS_JPNU && c == 0x8e)
1372 mbyte_cells = 1;
1373 else if (enc_dbcs != 0)
1374 mbyte_cells = mbyte_blen;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001375 else // enc_utf8
Bram Moolenaar071d4272004-06-13 20:20:40 +00001376 {
zeertzjq4dc513a2022-07-25 19:42:02 +01001377 u8c = len >= 0
1378 ? utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr))
1379 : utfc_ptr2char(ptr, u8cc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001380 mbyte_cells = utf_char2cells(u8c);
Bram Moolenaara12a1612019-01-24 16:39:02 +01001381#ifdef FEAT_ARABIC
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382 if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c))
1383 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001384 // Do Arabic shaping.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001385 if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len)
1386 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001387 // Past end of string to be displayed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001388 nc = NUL;
1389 nc1 = NUL;
1390 }
1391 else
Bram Moolenaar362e1a32006-03-06 23:29:24 +00001392 {
zeertzjq4dc513a2022-07-25 19:42:02 +01001393 nc = len >= 0
1394 ? utfc_ptr2char_len(ptr + mbyte_blen, pcc,
1395 (int)((text + len) - ptr - mbyte_blen))
1396 : utfc_ptr2char(ptr + mbyte_blen, pcc);
Bram Moolenaar362e1a32006-03-06 23:29:24 +00001397 nc1 = pcc[0];
1398 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001399 pc = prev_c;
1400 prev_c = u8c;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00001401 u8c = arabic_shape(u8c, &c, &u8cc[0], nc, nc1, pc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001402 }
1403 else
1404 prev_c = u8c;
Bram Moolenaara12a1612019-01-24 16:39:02 +01001405#endif
Bram Moolenaare4ebd292010-01-19 17:40:46 +01001406 if (col + mbyte_cells > screen_Columns)
1407 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001408 // Only 1 cell left, but character requires 2 cells:
1409 // display a '>' in the last column to avoid wrapping.
Bram Moolenaare4ebd292010-01-19 17:40:46 +01001410 c = '>';
1411 mbyte_cells = 1;
1412 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001413 }
1414 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001415
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001416 force_redraw_this = force_redraw_next;
1417 force_redraw_next = FALSE;
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001418
1419 need_redraw = ScreenLines[off] != c
Bram Moolenaar071d4272004-06-13 20:20:40 +00001420 || (mbyte_cells == 2
1421 && ScreenLines[off + 1] != (enc_dbcs ? ptr[1] : 0))
1422 || (enc_dbcs == DBCS_JPNU
1423 && c == 0x8e
1424 && ScreenLines2[off] != ptr[1])
1425 || (enc_utf8
Bram Moolenaar70c49c12010-03-23 15:36:35 +01001426 && (ScreenLinesUC[off] !=
1427 (u8char_T)(c < 0x80 && u8cc[0] == 0 ? 0 : u8c)
1428 || (ScreenLinesUC[off] != 0
1429 && screen_comp_differs(off, u8cc))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001430 || ScreenAttrs[off] != attr
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001431 || exmode_active;
1432
Bram Moolenaarff85d4a2022-10-02 15:21:04 +01001433 if ((need_redraw || force_redraw_this) && !skip_for_popup(row, col))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001434 {
1435#if defined(FEAT_GUI) || defined(UNIX)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001436 // The bold trick makes a single row of pixels appear in the next
1437 // character. When a bold character is removed, the next
1438 // character should be redrawn too. This happens for our own GUI
1439 // and for some xterms.
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001440 if (need_redraw && ScreenLines[off] != ' ' && (
Bram Moolenaar071d4272004-06-13 20:20:40 +00001441# ifdef FEAT_GUI
1442 gui.in_use
1443# endif
1444# if defined(FEAT_GUI) && defined(UNIX)
1445 ||
1446# endif
1447# ifdef UNIX
1448 term_is_xterm
1449# endif
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001450 ))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001451 {
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001452 int n = ScreenAttrs[off];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001453
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001454 if (n > HL_ALL)
1455 n = syn_attr2attr(n);
1456 if (n & HL_BOLD)
1457 force_redraw_next = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001458 }
1459#endif
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001460 // When at the end of the text and overwriting a two-cell
1461 // character with a one-cell character, need to clear the next
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00001462 // cell. Also when overwriting the left half of a two-cell char
1463 // with the right half of a two-cell char. Do this only once
1464 // (mb_off2cells() may return 2 on the right half).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001465 if (clear_next_cell)
1466 clear_next_cell = FALSE;
1467 else if (has_mbyte
1468 && (len < 0 ? ptr[mbyte_blen] == NUL
1469 : ptr + mbyte_blen >= text + len)
Bram Moolenaar367329b2007-08-30 11:53:22 +00001470 && ((mbyte_cells == 1 && (*mb_off2cells)(off, max_off) > 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001471 || (mbyte_cells == 2
Bram Moolenaar367329b2007-08-30 11:53:22 +00001472 && (*mb_off2cells)(off, max_off) == 1
1473 && (*mb_off2cells)(off + 1, max_off) > 1)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001474 clear_next_cell = TRUE;
1475
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001476 // Make sure we never leave a second byte of a double-byte behind,
1477 // it confuses mb_off2cells().
Bram Moolenaar071d4272004-06-13 20:20:40 +00001478 if (enc_dbcs
Bram Moolenaar367329b2007-08-30 11:53:22 +00001479 && ((mbyte_cells == 1 && (*mb_off2cells)(off, max_off) > 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001480 || (mbyte_cells == 2
Bram Moolenaar367329b2007-08-30 11:53:22 +00001481 && (*mb_off2cells)(off, max_off) == 1
1482 && (*mb_off2cells)(off + 1, max_off) > 1)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001483 ScreenLines[off + mbyte_blen] = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001484 ScreenLines[off] = c;
1485 ScreenAttrs[off] = attr;
Bram Moolenaarb9081882022-07-09 04:56:24 +01001486 ScreenCols[off] = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001487 if (enc_utf8)
1488 {
Bram Moolenaar362e1a32006-03-06 23:29:24 +00001489 if (c < 0x80 && u8cc[0] == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001490 ScreenLinesUC[off] = 0;
1491 else
1492 {
Bram Moolenaar362e1a32006-03-06 23:29:24 +00001493 int i;
1494
Bram Moolenaar071d4272004-06-13 20:20:40 +00001495 ScreenLinesUC[off] = u8c;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00001496 for (i = 0; i < Screen_mco; ++i)
1497 {
1498 ScreenLinesC[i][off] = u8cc[i];
1499 if (u8cc[i] == 0)
1500 break;
1501 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001502 }
1503 if (mbyte_cells == 2)
1504 {
1505 ScreenLines[off + 1] = 0;
1506 ScreenAttrs[off + 1] = attr;
Bram Moolenaarb9081882022-07-09 04:56:24 +01001507 ScreenCols[off + 1] = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508 }
1509 screen_char(off, row, col);
1510 }
1511 else if (mbyte_cells == 2)
1512 {
1513 ScreenLines[off + 1] = ptr[1];
1514 ScreenAttrs[off + 1] = attr;
Bram Moolenaarb9081882022-07-09 04:56:24 +01001515 ScreenCols[off + 1] = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001516 screen_char_2(off, row, col);
1517 }
1518 else if (enc_dbcs == DBCS_JPNU && c == 0x8e)
1519 {
1520 ScreenLines2[off] = ptr[1];
1521 screen_char(off, row, col);
1522 }
1523 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001524 screen_char(off, row, col);
1525 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001526 if (has_mbyte)
1527 {
1528 off += mbyte_cells;
1529 col += mbyte_cells;
1530 ptr += mbyte_blen;
1531 if (clear_next_cell)
Bram Moolenaare4c21e62014-05-22 16:05:19 +02001532 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001533 // This only happens at the end, display one space next.
Bram Moolenaar35d8c202022-03-03 11:46:00 +00001534 // Keep the attribute from before.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001535 ptr = (char_u *)" ";
Bram Moolenaare4c21e62014-05-22 16:05:19 +02001536 len = -1;
Bram Moolenaar35d8c202022-03-03 11:46:00 +00001537 attr = ScreenAttrs[off];
Bram Moolenaare4c21e62014-05-22 16:05:19 +02001538 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001539 }
1540 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001541 {
1542 ++off;
1543 ++col;
1544 ++ptr;
1545 }
1546 }
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001547
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001548 // If we detected the next character needs to be redrawn, but the text
1549 // doesn't extend up to there, update the character here.
Bram Moolenaarff85d4a2022-10-02 15:21:04 +01001550 if (force_redraw_next && col < screen_Columns && !skip_for_popup(row, col))
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001551 {
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001552 if (enc_dbcs != 0 && dbcs_off2cells(off, max_off) > 1)
1553 screen_char_2(off, row, col);
1554 else
Bram Moolenaar2bea2912009-03-11 16:58:40 +00001555 screen_char(off, row, col);
1556 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001557}
1558
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001559#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001560/*
Bram Moolenaar6ee10162007-07-26 20:58:42 +00001561 * Prepare for 'hlsearch' highlighting.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001562 */
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001563 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001564start_search_hl(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001565{
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001566 if (!p_hls || no_hlsearch)
1567 return;
1568
1569 end_search_hl(); // just in case it wasn't called before
1570 last_pat_prog(&screen_search_hl.rm);
1571 screen_search_hl.attr = HL_ATTR(HLF_L);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001572}
1573
1574/*
Bram Moolenaar6ee10162007-07-26 20:58:42 +00001575 * Clean up for 'hlsearch' highlighting.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001576 */
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001577 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001578end_search_hl(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001579{
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001580 if (screen_search_hl.rm.regprog == NULL)
1581 return;
1582
1583 vim_regfree(screen_search_hl.rm.regprog);
1584 screen_search_hl.rm.regprog = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001585}
Bram Moolenaarde993ea2014-06-17 23:18:01 +02001586#endif
Bram Moolenaarb3414592014-06-17 17:48:32 +02001587
Yegappan Lakshmanane89aef32025-05-14 20:31:55 +02001588 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01001589screen_start_highlight(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001590{
1591 attrentry_T *aep = NULL;
1592
1593 screen_attr = attr;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001594 if (!full_screen
Bram Moolenaar4f974752019-02-17 17:44:42 +01001595#ifdef MSWIN
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001596 || !termcap_active
Bram Moolenaar071d4272004-06-13 20:20:40 +00001597#endif
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001598 )
1599 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001600
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001601#ifdef FEAT_GUI
1602 if (gui.in_use)
1603 {
1604 char buf[20];
1605
1606 // The GUI handles this internally.
1607 sprintf(buf, "\033|%dh", attr);
1608 OUT_STR(buf);
1609 return;
1610 }
1611#endif
1612
1613 if (attr > HL_ALL) // special HL attr.
1614 {
1615 if (IS_CTERM)
1616 aep = syn_cterm_attr2entry(attr);
1617 else
1618 aep = syn_term_attr2entry(attr);
1619 if (aep == NULL) // did ":syntax clear"
1620 attr = 0;
1621 else
1622 attr = aep->ae_attr;
1623 }
1624#if defined(FEAT_VTP) && defined(FEAT_TERMGUICOLORS)
1625 if (use_vtp())
1626 {
1627 guicolor_T defguifg, defguibg;
1628 int defctermfg, defctermbg;
1629
1630 // If FG and BG are unset, the color is undefined when
1631 // BOLD+INVERSE. Use Normal as the default value.
1632 get_default_console_color(&defctermfg, &defctermbg, &defguifg,
1633 &defguibg);
1634
1635 if (p_tgc)
1636 {
1637 if (aep == NULL || COLOR_INVALID(aep->ae_u.cterm.fg_rgb))
1638 term_fg_rgb_color(defguifg);
1639 if (aep == NULL || COLOR_INVALID(aep->ae_u.cterm.bg_rgb))
1640 term_bg_rgb_color(defguibg);
1641 }
1642 else if (t_colors >= 256)
1643 {
1644 if (aep == NULL || aep->ae_u.cterm.fg_color == 0)
1645 term_fg_color(defctermfg);
1646 if (aep == NULL || aep->ae_u.cterm.bg_color == 0)
1647 term_bg_color(defctermbg);
1648 }
1649 }
1650#endif
1651 if ((attr & HL_BOLD) && *T_MD != NUL) // bold
1652 out_str(T_MD);
1653 else if (aep != NULL && cterm_normal_fg_bold && (
1654#ifdef FEAT_TERMGUICOLORS
1655 p_tgc && aep->ae_u.cterm.fg_rgb != CTERMCOLOR
1656 ? aep->ae_u.cterm.fg_rgb != INVALCOLOR
1657 :
1658#endif
1659 t_colors > 1 && aep->ae_u.cterm.fg_color))
1660 // If the Normal FG color has BOLD attribute and the new HL
1661 // has a FG color defined, clear BOLD.
1662 out_str(T_ME);
1663 if ((attr & HL_STANDOUT) && *T_SO != NUL) // standout
1664 out_str(T_SO);
1665 if ((attr & HL_UNDERCURL) && *T_UCS != NUL) // undercurl
1666 out_str(T_UCS);
1667 if ((attr & HL_UNDERDOUBLE) && *T_USS != NUL) // double underline
1668 out_str(T_USS);
1669 if ((attr & HL_UNDERDOTTED) && *T_DS != NUL) // dotted underline
1670 out_str(T_DS);
1671 if ((attr & HL_UNDERDASHED) && *T_CDS != NUL) // dashed underline
1672 out_str(T_CDS);
1673 if (((attr & HL_UNDERLINE) // underline or undercurl, etc.
1674 || ((attr & HL_UNDERCURL) && *T_UCS == NUL)
1675 || ((attr & HL_UNDERDOUBLE) && *T_USS == NUL)
1676 || ((attr & HL_UNDERDOTTED) && *T_DS == NUL)
1677 || ((attr & HL_UNDERDASHED) && *T_CDS == NUL))
1678 && *T_US != NUL)
1679 out_str(T_US);
1680 if ((attr & HL_ITALIC) && *T_CZH != NUL) // italic
1681 out_str(T_CZH);
1682 if ((attr & HL_INVERSE) && *T_MR != NUL) // inverse (reverse)
1683 out_str(T_MR);
1684 if ((attr & HL_STRIKETHROUGH) && *T_STS != NUL) // strike
1685 out_str(T_STS);
1686
1687 /*
1688 * Output the color or start string after bold etc., in case the
1689 * bold etc. override the color setting.
1690 */
1691 if (aep != NULL)
1692 {
PMuncha606f3a2023-11-15 15:35:49 +01001693 if (aep->ae_u.cterm.font > 0 && aep->ae_u.cterm.font < 12)
1694 term_font(aep->ae_u.cterm.font);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001695#ifdef FEAT_TERMGUICOLORS
1696 // When 'termguicolors' is set but fg or bg is unset,
1697 // fall back to the cterm colors. This helps for SpellBad,
1698 // where the GUI uses a red undercurl.
1699 if (p_tgc && aep->ae_u.cterm.fg_rgb != CTERMCOLOR)
1700 {
1701 if (aep->ae_u.cterm.fg_rgb != INVALCOLOR)
1702 term_fg_rgb_color(aep->ae_u.cterm.fg_rgb);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001703 }
1704 else
1705#endif
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001706 if (t_colors > 1)
1707 {
1708 if (aep->ae_u.cterm.fg_color)
1709 term_fg_color(aep->ae_u.cterm.fg_color - 1);
1710 }
1711#ifdef FEAT_TERMGUICOLORS
1712 if (p_tgc && aep->ae_u.cterm.bg_rgb != CTERMCOLOR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001713 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001714 if (aep->ae_u.cterm.bg_rgb != INVALCOLOR)
1715 term_bg_rgb_color(aep->ae_u.cterm.bg_rgb);
1716 }
1717 else
1718#endif
1719 if (t_colors > 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001721 if (aep->ae_u.cterm.bg_color)
1722 term_bg_color(aep->ae_u.cterm.bg_color - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001723 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001724#ifdef FEAT_TERMGUICOLORS
1725 if (p_tgc && aep->ae_u.cterm.ul_rgb != CTERMCOLOR)
1726 {
1727 if (aep->ae_u.cterm.ul_rgb != INVALCOLOR)
1728 term_ul_rgb_color(aep->ae_u.cterm.ul_rgb);
1729 }
1730 else
1731#endif
1732 if (t_colors > 1)
Bram Moolenaara050b942019-12-02 21:35:31 +01001733 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001734 if (aep->ae_u.cterm.ul_color)
1735 term_ul_color(aep->ae_u.cterm.ul_color - 1);
Bram Moolenaara050b942019-12-02 21:35:31 +01001736 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001737
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001738 if (!IS_CTERM)
1739 {
1740 if (aep->ae_u.term.start != NULL)
1741 out_str(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001742 }
1743 }
1744}
1745
Yegappan Lakshmanane89aef32025-05-14 20:31:55 +02001746 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001747screen_stop_highlight(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001748{
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001749 int do_ME = FALSE; // output T_ME code
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02001750#if defined(FEAT_VTP) && defined(FEAT_TERMGUICOLORS)
Bram Moolenaar09307e32020-05-29 21:42:55 +02001751 int do_ME_fg = FALSE, do_ME_bg = FALSE;
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02001752#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001753
1754 if (screen_attr != 0
Bram Moolenaar4f974752019-02-17 17:44:42 +01001755#ifdef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00001756 && termcap_active
1757#endif
1758 )
1759 {
1760#ifdef FEAT_GUI
1761 if (gui.in_use)
1762 {
1763 char buf[20];
1764
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001765 // use internal GUI code
Bram Moolenaar424bcae2022-01-31 14:59:41 +00001766 sprintf(buf, "\033|%dH", screen_attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001767 OUT_STR(buf);
1768 }
1769 else
1770#endif
1771 {
Bram Moolenaar84f54632022-06-29 18:39:11 +01001772 int is_under;
1773
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001774 if (screen_attr > HL_ALL) // special HL attr.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001775 {
1776 attrentry_T *aep;
1777
Bram Moolenaar8a633e32016-04-21 21:10:14 +02001778 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001779 {
1780 /*
1781 * Assume that t_me restores the original colors!
1782 */
1783 aep = syn_cterm_attr2entry(screen_attr);
Bram Moolenaard4fc5772018-02-27 14:39:03 +01001784 if (aep != NULL && ((
Bram Moolenaar61be73b2016-04-29 22:59:22 +02001785#ifdef FEAT_TERMGUICOLORS
Bram Moolenaard4fc5772018-02-27 14:39:03 +01001786 p_tgc && aep->ae_u.cterm.fg_rgb != CTERMCOLOR
1787 ? aep->ae_u.cterm.fg_rgb != INVALCOLOR
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02001788# ifdef FEAT_VTP
1789 ? !(do_ME_fg = TRUE) : (do_ME_fg = FALSE)
1790# endif
Bram Moolenaard4fc5772018-02-27 14:39:03 +01001791 :
Bram Moolenaar8a633e32016-04-21 21:10:14 +02001792#endif
Bram Moolenaard4fc5772018-02-27 14:39:03 +01001793 aep->ae_u.cterm.fg_color) || (
Bram Moolenaar61be73b2016-04-29 22:59:22 +02001794#ifdef FEAT_TERMGUICOLORS
Bram Moolenaard4fc5772018-02-27 14:39:03 +01001795 p_tgc && aep->ae_u.cterm.bg_rgb != CTERMCOLOR
1796 ? aep->ae_u.cterm.bg_rgb != INVALCOLOR
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02001797# ifdef FEAT_VTP
1798 ? !(do_ME_bg = TRUE) : (do_ME_bg = FALSE)
1799# endif
Bram Moolenaard4fc5772018-02-27 14:39:03 +01001800 :
Bram Moolenaar8a633e32016-04-21 21:10:14 +02001801#endif
Bram Moolenaard4fc5772018-02-27 14:39:03 +01001802 aep->ae_u.cterm.bg_color)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001803 do_ME = TRUE;
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02001804#if defined(FEAT_VTP) && defined(FEAT_TERMGUICOLORS)
1805 if (use_vtp())
1806 {
1807 if (do_ME_fg && do_ME_bg)
1808 do_ME = TRUE;
1809
1810 // FG and BG cannot be separated in T_ME, which is not
1811 // efficient.
1812 if (!do_ME && do_ME_fg)
1813 out_str((char_u *)"\033|39m"); // restore FG
1814 if (!do_ME && do_ME_bg)
1815 out_str((char_u *)"\033|49m"); // restore BG
1816 }
1817 else
1818 {
1819 // Process FG and BG at once.
1820 if (!do_ME)
1821 do_ME = do_ME_fg | do_ME_bg;
1822 }
1823#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001824 }
1825 else
1826 {
1827 aep = syn_term_attr2entry(screen_attr);
1828 if (aep != NULL && aep->ae_u.term.stop != NULL)
1829 {
1830 if (STRCMP(aep->ae_u.term.stop, T_ME) == 0)
1831 do_ME = TRUE;
1832 else
1833 out_str(aep->ae_u.term.stop);
1834 }
1835 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001836 if (aep == NULL) // did ":syntax clear"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001837 screen_attr = 0;
1838 else
1839 screen_attr = aep->ae_attr;
1840 }
1841
1842 /*
1843 * Often all ending-codes are equal to T_ME. Avoid outputting the
1844 * same sequence several times.
1845 */
1846 if (screen_attr & HL_STANDOUT)
1847 {
1848 if (STRCMP(T_SE, T_ME) == 0)
1849 do_ME = TRUE;
1850 else
1851 out_str(T_SE);
1852 }
Bram Moolenaar84f54632022-06-29 18:39:11 +01001853 is_under = (screen_attr & (HL_UNDERCURL
1854 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED));
1855 if (is_under && *T_UCE != NUL)
Bram Moolenaar8b9e20a2017-11-28 21:25:21 +01001856 {
1857 if (STRCMP(T_UCE, T_ME) == 0)
1858 do_ME = TRUE;
1859 else
1860 out_str(T_UCE);
1861 }
Bram Moolenaar84f54632022-06-29 18:39:11 +01001862 if ((screen_attr & HL_UNDERLINE) || (is_under && *T_UCE == NUL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001863 {
1864 if (STRCMP(T_UE, T_ME) == 0)
1865 do_ME = TRUE;
1866 else
1867 out_str(T_UE);
1868 }
1869 if (screen_attr & HL_ITALIC)
1870 {
1871 if (STRCMP(T_CZR, T_ME) == 0)
1872 do_ME = TRUE;
1873 else
1874 out_str(T_CZR);
1875 }
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +02001876 if (screen_attr & HL_STRIKETHROUGH)
1877 {
1878 if (STRCMP(T_STE, T_ME) == 0)
1879 do_ME = TRUE;
1880 else
1881 out_str(T_STE);
1882 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001883 if (do_ME || (screen_attr & (HL_BOLD | HL_INVERSE)))
1884 out_str(T_ME);
1885
Bram Moolenaar61be73b2016-04-29 22:59:22 +02001886#ifdef FEAT_TERMGUICOLORS
1887 if (p_tgc)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001888 {
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02001889 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02001890 term_fg_rgb_color(cterm_normal_fg_gui_color);
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02001891 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02001892 term_bg_rgb_color(cterm_normal_bg_gui_color);
Bram Moolenaare023e882020-05-31 16:42:30 +02001893 if (cterm_normal_ul_gui_color != INVALCOLOR)
1894 term_ul_rgb_color(cterm_normal_ul_gui_color);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02001895 }
1896 else
1897#endif
1898 {
1899 if (t_colors > 1)
1900 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001901 // set Normal cterm colors
Bram Moolenaar8a633e32016-04-21 21:10:14 +02001902 if (cterm_normal_fg_color != 0)
1903 term_fg_color(cterm_normal_fg_color - 1);
1904 if (cterm_normal_bg_color != 0)
1905 term_bg_color(cterm_normal_bg_color - 1);
Bram Moolenaare023e882020-05-31 16:42:30 +02001906 if (cterm_normal_ul_color != 0)
1907 term_ul_color(cterm_normal_ul_color - 1);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02001908 if (cterm_normal_fg_bold)
1909 out_str(T_MD);
1910 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001911 }
1912 }
1913 }
1914 screen_attr = 0;
1915}
1916
1917/*
1918 * Reset the colors for a cterm. Used when leaving Vim.
1919 * The machine specific code may override this again.
1920 */
1921 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001922reset_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001923{
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001924 if (!IS_CTERM)
1925 return;
1926
1927 // set Normal cterm colors
Bram Moolenaar61be73b2016-04-29 22:59:22 +02001928#ifdef FEAT_TERMGUICOLORS
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001929 if (p_tgc ? (cterm_normal_fg_gui_color != INVALCOLOR
1930 || cterm_normal_bg_gui_color != INVALCOLOR)
1931 : (cterm_normal_fg_color > 0 || cterm_normal_bg_color > 0))
Bram Moolenaar8a633e32016-04-21 21:10:14 +02001932#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001933 if (cterm_normal_fg_color > 0 || cterm_normal_bg_color > 0)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02001934#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001935 {
1936 out_str(T_OP);
1937 screen_attr = -1;
1938 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001939 if (cterm_normal_fg_bold)
1940 {
1941 out_str(T_ME);
1942 screen_attr = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001943 }
1944}
1945
1946/*
1947 * Put character ScreenLines["off"] on the screen at position "row" and "col",
1948 * using the attributes from ScreenAttrs["off"].
1949 */
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02001950 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001951screen_char(unsigned off, int row, int col)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001952{
1953 int attr;
1954
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001955 // Check for illegal values, just in case (could happen just after
1956 // resizing).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001957 if (row >= screen_Rows || col >= screen_Columns)
1958 return;
1959
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001960 // Outputting a character in the last cell on the screen may scroll the
1961 // screen up. Only do it when the "xn" termcap property is set, otherwise
1962 // mark the character invalid (update it when scrolled up).
Bram Moolenaar494838a2015-02-10 19:20:37 +01001963 if (*T_XN == NUL
1964 && row == screen_Rows - 1 && col == screen_Columns - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001965#ifdef FEAT_RIGHTLEFT
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001966 // account for first command-line character in rightleft mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00001967 && !cmdmsg_rl
1968#endif
1969 )
1970 {
1971 ScreenAttrs[off] = (sattr_T)-1;
Bram Moolenaarb9081882022-07-09 04:56:24 +01001972 ScreenCols[off] = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001973 return;
1974 }
1975
1976 /*
1977 * Stop highlighting first, so it's easier to move the cursor.
1978 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001979 if (screen_char_attr != 0)
1980 attr = screen_char_attr;
1981 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001982 attr = ScreenAttrs[off];
1983 if (screen_attr != attr)
1984 screen_stop_highlight();
1985
1986 windgoto(row, col);
1987
1988 if (screen_attr != attr)
1989 screen_start_highlight(attr);
1990
Bram Moolenaar071d4272004-06-13 20:20:40 +00001991 if (enc_utf8 && ScreenLinesUC[off] != 0)
1992 {
1993 char_u buf[MB_MAXBYTES + 1];
1994
h-east8927c9b2024-04-20 17:57:19 +02001995 if (
1996#ifdef FEAT_GUI
1997 !gui.in_use &&
1998#endif
1999 get_cellwidth(ScreenLinesUC[off]) > 1
2000 )
mikoto2000e20fa592024-04-17 22:06:54 +02002001 {
h-east8927c9b2024-04-20 17:57:19 +02002002 // If the width is set to 2 with setcellwidths()
2003 // clear the two screen cells. If the character is actually
2004 // single width it won't change the second cell.
2005 out_str((char_u *)" ");
2006 term_windgoto(row, col);
2007 screen_cur_col = 9999;
mikoto2000e20fa592024-04-17 22:06:54 +02002008 }
2009 else if (utf_ambiguous_width(ScreenLinesUC[off]))
Bram Moolenaarfae8ed12017-12-12 22:29:30 +01002010 {
2011 if (*p_ambw == 'd'
Bram Moolenaara12a1612019-01-24 16:39:02 +01002012#ifdef FEAT_GUI
Bram Moolenaarfae8ed12017-12-12 22:29:30 +01002013 && !gui.in_use
Bram Moolenaara12a1612019-01-24 16:39:02 +01002014#endif
Bram Moolenaarfae8ed12017-12-12 22:29:30 +01002015 )
2016 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002017 // Clear the two screen cells. If the character is actually
2018 // single width it won't change the second cell.
Bram Moolenaarfae8ed12017-12-12 22:29:30 +01002019 out_str((char_u *)" ");
2020 term_windgoto(row, col);
2021 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002022 // not sure where the cursor is after drawing the ambiguous width
2023 // character
Bram Moolenaarcb070082016-04-02 22:14:51 +02002024 screen_cur_col = 9999;
Bram Moolenaarfae8ed12017-12-12 22:29:30 +01002025 }
Bram Moolenaarcb070082016-04-02 22:14:51 +02002026 else if (utf_char2cells(ScreenLinesUC[off]) > 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002027 ++screen_cur_col;
Bram Moolenaarfae8ed12017-12-12 22:29:30 +01002028
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002029 // Convert the UTF-8 character to bytes and write it.
Bram Moolenaarfae8ed12017-12-12 22:29:30 +01002030 buf[utfc_char2bytes(off, buf)] = NUL;
2031 out_str(buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002032 }
2033 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002034 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002035 out_flush_check();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002036 out_char(ScreenLines[off]);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002037 // double-byte character in single-width cell
Bram Moolenaar071d4272004-06-13 20:20:40 +00002038 if (enc_dbcs == DBCS_JPNU && ScreenLines[off] == 0x8e)
2039 out_char(ScreenLines2[off]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002040 }
2041
2042 screen_cur_col++;
2043}
2044
Bram Moolenaar071d4272004-06-13 20:20:40 +00002045/*
2046 * Used for enc_dbcs only: Put one double-wide character at ScreenLines["off"]
2047 * on the screen at position 'row' and 'col'.
2048 * The attributes of the first byte is used for all. This is required to
2049 * output the two bytes of a double-byte character with nothing in between.
2050 */
2051 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01002052screen_char_2(unsigned off, int row, int col)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002053{
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002054 // Check for illegal values (could be wrong when screen was resized).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002055 if (off + 1 >= (unsigned)(screen_Rows * screen_Columns))
2056 return;
2057
dundargocc57b5bc2022-11-02 13:30:51 +00002058 // Outputting the last character on the screen may scroll the screen up.
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002059 // Don't to it! Mark the character invalid (update it when scrolled up)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002060 if (row == screen_Rows - 1 && col >= screen_Columns - 2)
2061 {
2062 ScreenAttrs[off] = (sattr_T)-1;
Bram Moolenaarb9081882022-07-09 04:56:24 +01002063 ScreenCols[off] = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002064 return;
2065 }
2066
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002067 // Output the first byte normally (positions the cursor), then write the
2068 // second byte directly.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002069 screen_char(off, row, col);
2070 out_char(ScreenLines[off + 1]);
2071 ++screen_cur_col;
2072}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002073
Bram Moolenaar071d4272004-06-13 20:20:40 +00002074/*
2075 * Draw a rectangle of the screen, inverted when "invert" is TRUE.
2076 * This uses the contents of ScreenLines[] and doesn't change it.
2077 */
2078 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002079screen_draw_rectangle(
2080 int row,
2081 int col,
2082 int height,
2083 int width,
2084 int invert)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002085{
2086 int r, c;
2087 int off;
Bram Moolenaar367329b2007-08-30 11:53:22 +00002088 int max_off;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002089
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002090 // Can't use ScreenLines unless initialized
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00002091 if (ScreenLines == NULL)
2092 return;
2093
Bram Moolenaar071d4272004-06-13 20:20:40 +00002094 if (invert)
2095 screen_char_attr = HL_INVERSE;
2096 for (r = row; r < row + height; ++r)
2097 {
2098 off = LineOffset[r];
Bram Moolenaar367329b2007-08-30 11:53:22 +00002099 max_off = off + screen_Columns;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002100 for (c = col; c < col + width; ++c)
2101 {
Bram Moolenaar367329b2007-08-30 11:53:22 +00002102 if (enc_dbcs != 0 && dbcs_off2cells(off + c, max_off) > 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002103 {
Bram Moolenaarff85d4a2022-10-02 15:21:04 +01002104 if (!skip_for_popup(r, c))
2105 screen_char_2(off + c, r, c);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002106 ++c;
2107 }
2108 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002109 {
Bram Moolenaarff85d4a2022-10-02 15:21:04 +01002110 if (!skip_for_popup(r, c))
2111 screen_char(off + c, r, c);
Bram Moolenaar367329b2007-08-30 11:53:22 +00002112 if (utf_off2cells(off + c, max_off) > 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002113 ++c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002114 }
2115 }
2116 }
2117 screen_char_attr = 0;
2118}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002119
Bram Moolenaar071d4272004-06-13 20:20:40 +00002120/*
2121 * Redraw the characters for a vertically split window.
2122 */
2123 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01002124redraw_block(int row, int end, win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002125{
2126 int col;
2127 int width;
2128
2129# ifdef FEAT_CLIPBOARD
2130 clip_may_clear_selection(row, end - 1);
2131# endif
2132
2133 if (wp == NULL)
2134 {
2135 col = 0;
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02002136 width = COLUMNS_WITHOUT_TPL();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002137 }
2138 else
2139 {
2140 col = wp->w_wincol;
2141 width = wp->w_width;
2142 }
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02002143 screen_draw_rectangle(row, col + TPL_LCOL(wp), end - row, width, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002144}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002145
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02002146 void
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002147space_to_screenline(int off, int attr)
2148{
2149 ScreenLines[off] = ' ';
2150 ScreenAttrs[off] = attr;
Bram Moolenaarb9081882022-07-09 04:56:24 +01002151 ScreenCols[off] = -1;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002152 if (enc_utf8)
2153 ScreenLinesUC[off] = 0;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002154}
2155
Bram Moolenaar071d4272004-06-13 20:20:40 +00002156/*
Bram Moolenaarcee9c842022-04-09 12:40:13 +01002157 * Fill the screen from "start_row" to "end_row" (exclusive), from "start_col"
2158 * to "end_col" (exclusive) with character "c1" in first column followed by
2159 * "c2" in the other columns. Use attributes "attr".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002160 */
2161 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002162screen_fill(
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002163 int start_row,
2164 int end_row,
2165 int start_col,
2166 int end_col,
2167 int c1,
2168 int c2,
2169 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002170{
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002171 int row;
2172 int col;
2173 int off;
2174 int end_off;
2175 int did_delete;
2176 int c;
2177 int norm_term;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002178#if defined(FEAT_GUI) || defined(UNIX)
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002179 int force_next = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002180#endif
2181
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002182 if (end_row > screen_Rows) // safety check
Bram Moolenaar071d4272004-06-13 20:20:40 +00002183 end_row = screen_Rows;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002184 if (end_col > screen_Columns) // safety check
Bram Moolenaar071d4272004-06-13 20:20:40 +00002185 end_col = screen_Columns;
2186 if (ScreenLines == NULL
2187 || start_row >= end_row
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002188 || start_col >= end_col) // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00002189 return;
2190
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002191 // it's a "normal" terminal when not in a GUI or cterm
Bram Moolenaar071d4272004-06-13 20:20:40 +00002192 norm_term = (
2193#ifdef FEAT_GUI
2194 !gui.in_use &&
2195#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02002196 !IS_CTERM);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002197 for (row = start_row; row < end_row; ++row)
2198 {
Bram Moolenaarc236c162008-07-13 17:41:49 +00002199 if (has_mbyte
Bram Moolenaara12a1612019-01-24 16:39:02 +01002200#ifdef FEAT_GUI
Bram Moolenaarc236c162008-07-13 17:41:49 +00002201 && !gui.in_use
Bram Moolenaara12a1612019-01-24 16:39:02 +01002202#endif
Bram Moolenaarc236c162008-07-13 17:41:49 +00002203 )
2204 {
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00002205 // When drawing over the right half of a double-wide char clear
2206 // out the left half. When drawing over the left half of a
2207 // double wide-char clear out the right half. Only needed in a
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002208 // terminal.
Bram Moolenaar7693ec62008-07-24 18:29:37 +00002209 if (start_col > 0 && mb_fix_col(start_col, row) != start_col)
Bram Moolenaard91ffe92008-07-14 17:51:11 +00002210 screen_puts_len((char_u *)" ", 1, row, start_col - 1, 0);
Bram Moolenaara1aed622008-07-18 15:14:43 +00002211 if (end_col < screen_Columns && mb_fix_col(end_col, row) != end_col)
Bram Moolenaard91ffe92008-07-14 17:51:11 +00002212 screen_puts_len((char_u *)" ", 1, row, end_col, 0);
Bram Moolenaarc236c162008-07-13 17:41:49 +00002213 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002214 /*
2215 * Try to use delete-line termcap code, when no attributes or in a
2216 * "normal" terminal, where a bold/italic space is just a
2217 * space.
2218 */
2219 did_delete = FALSE;
2220 if (c2 == ' '
2221 && end_col == Columns
2222 && can_clear(T_CE)
2223 && (attr == 0
2224 || (norm_term
2225 && attr <= HL_ALL
2226 && ((attr & ~(HL_BOLD | HL_ITALIC)) == 0))))
2227 {
2228 /*
2229 * check if we really need to clear something
2230 */
2231 col = start_col;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002232 if (c1 != ' ') // don't clear first char
Bram Moolenaar071d4272004-06-13 20:20:40 +00002233 ++col;
2234
2235 off = LineOffset[row] + col;
2236 end_off = LineOffset[row] + end_col;
2237
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002238 // skip blanks (used often, keep it fast!)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002239 if (enc_utf8)
2240 while (off < end_off && ScreenLines[off] == ' '
2241 && ScreenAttrs[off] == 0 && ScreenLinesUC[off] == 0)
2242 ++off;
2243 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002244 while (off < end_off && ScreenLines[off] == ' '
2245 && ScreenAttrs[off] == 0)
2246 ++off;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002247 if (off < end_off) // something to be cleared
Bram Moolenaar071d4272004-06-13 20:20:40 +00002248 {
2249 col = off - LineOffset[row];
2250 screen_stop_highlight();
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002251 term_windgoto(row, col);// clear rest of this screen line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002252 out_str(T_CE);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002253 screen_start(); // don't know where cursor is now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002254 col = end_col - col;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002255 while (col--) // clear chars in ScreenLines
Bram Moolenaar071d4272004-06-13 20:20:40 +00002256 {
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002257 space_to_screenline(off, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002258 ++off;
2259 }
2260 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002261 did_delete = TRUE; // the chars are cleared now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002262 }
2263
2264 off = LineOffset[row] + start_col;
2265 c = c1;
2266 for (col = start_col; col < end_col; ++col)
2267 {
Bram Moolenaar33796b32019-06-08 16:01:13 +02002268 if ((ScreenLines[off] != c
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002269 || (enc_utf8 && (int)ScreenLinesUC[off]
2270 != (c >= 0x80 ? c : 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002271 || ScreenAttrs[off] != attr
Bram Moolenaar838b7462022-09-26 15:19:56 +01002272 || must_redraw == UPD_CLEAR // screen clear pending
Bram Moolenaar071d4272004-06-13 20:20:40 +00002273#if defined(FEAT_GUI) || defined(UNIX)
2274 || force_next
2275#endif
2276 )
Bram Moolenaar33796b32019-06-08 16:01:13 +02002277 // Skip if under a(nother) popup.
Bram Moolenaarff85d4a2022-10-02 15:21:04 +01002278 && !skip_for_popup(row, col))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002279 {
2280#if defined(FEAT_GUI) || defined(UNIX)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002281 // The bold trick may make a single row of pixels appear in
2282 // the next character. When a bold character is removed, the
2283 // next character should be redrawn too. This happens for our
2284 // own GUI and for some xterms.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002285 if (
2286# ifdef FEAT_GUI
2287 gui.in_use
2288# endif
2289# if defined(FEAT_GUI) && defined(UNIX)
2290 ||
2291# endif
2292# ifdef UNIX
2293 term_is_xterm
2294# endif
2295 )
2296 {
2297 if (ScreenLines[off] != ' '
2298 && (ScreenAttrs[off] > HL_ALL
2299 || ScreenAttrs[off] & HL_BOLD))
2300 force_next = TRUE;
2301 else
2302 force_next = FALSE;
2303 }
Bram Moolenaar4ba5f1d2022-10-04 14:36:29 +01002304#endif // FEAT_GUI || defined(UNIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002305 ScreenLines[off] = c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002306 if (enc_utf8)
2307 {
2308 if (c >= 0x80)
2309 {
2310 ScreenLinesUC[off] = c;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002311 ScreenLinesC[0][off] = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002312 }
2313 else
2314 ScreenLinesUC[off] = 0;
2315 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002316 ScreenAttrs[off] = attr;
2317 if (!did_delete || c != ' ')
2318 screen_char(off, row, col);
2319 }
Bram Moolenaarb9081882022-07-09 04:56:24 +01002320 ScreenCols[off] = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002321 ++off;
2322 if (col == start_col)
2323 {
2324 if (did_delete)
2325 break;
2326 c = c2;
2327 }
2328 }
2329 if (end_col == Columns)
2330 LineWraps[row] = FALSE;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002331 if (row == Rows - 1) // overwritten the command line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002332 {
2333 redraw_cmdline = TRUE;
Bram Moolenaar5bab5552018-04-13 20:41:29 +02002334 if (start_col == 0 && end_col == Columns
glepnirff159252025-03-01 16:17:00 +01002335 && c1 == ' ' && c2 == ' ' && attr == 0
2336#ifdef FEAT_PROP_POPUP
2337 && !popup_overlaps_cmdline()
2338#endif
2339 )
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002340 clear_cmdline = FALSE; // command line has been cleared
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002341 if (start_col == 0)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002342 mode_displayed = FALSE; // mode cleared or overwritten
Bram Moolenaar071d4272004-06-13 20:20:40 +00002343 }
2344 }
2345}
2346
2347/*
2348 * Check if there should be a delay. Used before clearing or redrawing the
2349 * screen or the command line.
2350 */
2351 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002352check_for_delay(int check_msg_scroll)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002353{
2354 if ((emsg_on_display || (check_msg_scroll && msg_scroll))
2355 && !did_wait_return
Bram Moolenaar28ee8922020-10-28 20:20:00 +01002356 && emsg_silent == 0
2357 && !in_assert_fails)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002358 {
2359 out_flush();
Bram Moolenaareda1da02019-11-17 17:06:33 +01002360 ui_delay(1006L, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002361 emsg_on_display = FALSE;
2362 if (check_msg_scroll)
2363 msg_scroll = FALSE;
2364 }
2365}
2366
2367/*
Bram Moolenaarca57ab52019-04-13 14:53:16 +02002368 * Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect.
2369 */
2370 static void
2371clear_TabPageIdxs(void)
2372{
2373 int scol;
2374
2375 for (scol = 0; scol < Columns; ++scol)
2376 TabPageIdxs[scol] = 0;
2377}
2378
2379/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002380 * screen_valid - allocate screen buffers if size changed
Bram Moolenaar70b2a562012-01-10 22:26:17 +01002381 * If "doclear" is TRUE: clear screen if it has been resized.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002382 * Returns TRUE if there is a valid screen to write to.
2383 * Returns FALSE when starting up and screen not initialized yet.
2384 */
2385 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002386screen_valid(int doclear)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002387{
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002388 screenalloc(doclear); // allocate screen buffers if size changed
Bram Moolenaar071d4272004-06-13 20:20:40 +00002389 return (ScreenLines != NULL);
2390}
2391
2392/*
2393 * Resize the shell to Rows and Columns.
2394 * Allocate ScreenLines[] and associated items.
2395 *
2396 * There may be some time between setting Rows and Columns and (re)allocating
2397 * ScreenLines[]. This happens when starting up and when (manually) changing
2398 * the shell size. Always use screen_Rows and screen_Columns to access items
2399 * in ScreenLines[]. Use Rows and Columns for positioning text etc. where the
2400 * final size of the shell is needed.
2401 */
2402 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002403screenalloc(int doclear)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002404{
2405 int new_row, old_row;
2406#ifdef FEAT_GUI
2407 int old_Rows;
2408#endif
2409 win_T *wp;
2410 int outofmem = FALSE;
2411 int len;
2412 schar_T *new_ScreenLines;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002413 u8char_T *new_ScreenLinesUC = NULL;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002414 u8char_T *new_ScreenLinesC[MAX_MCO];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002415 schar_T *new_ScreenLines2 = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002416 sattr_T *new_ScreenAttrs;
Bram Moolenaarb9081882022-07-09 04:56:24 +01002417 colnr_T *new_ScreenCols;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002418 unsigned *new_LineOffset;
2419 char_u *new_LineWraps;
Bram Moolenaard1f56e62006-02-22 21:25:37 +00002420 short *new_TabPageIdxs;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002421#ifdef FEAT_PROP_POPUP
Bram Moolenaar33796b32019-06-08 16:01:13 +02002422 short *new_popup_mask;
Bram Moolenaar4c063a02019-06-10 21:24:12 +02002423 short *new_popup_mask_next;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002424 char *new_popup_transparent;
Bram Moolenaar33796b32019-06-08 16:01:13 +02002425#endif
Bram Moolenaarf740b292006-02-16 22:11:02 +00002426 tabpage_T *tp;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002427 static int entered = FALSE; // avoid recursiveness
2428 static int done_outofmem_msg = FALSE; // did outofmem message
Bram Moolenaar87e817c2009-02-22 20:13:39 +00002429 int retry_count = 0;
Bram Moolenaarf86490e2022-11-28 19:11:02 +00002430 int found_null;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002431
Bram Moolenaar87e817c2009-02-22 20:13:39 +00002432retry:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002433 /*
2434 * Allocation of the screen buffers is done only when the size changes and
2435 * when Rows and Columns have been set and we have started doing full
2436 * screen stuff.
2437 */
2438 if ((ScreenLines != NULL
2439 && Rows == screen_Rows
2440 && Columns == screen_Columns
Bram Moolenaar071d4272004-06-13 20:20:40 +00002441 && enc_utf8 == (ScreenLinesUC != NULL)
2442 && (enc_dbcs == DBCS_JPNU) == (ScreenLines2 != NULL)
Bram Moolenaara12a1612019-01-24 16:39:02 +01002443 && p_mco == Screen_mco)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002444 || Rows == 0
2445 || Columns == 0
2446 || (!full_screen && ScreenLines == NULL))
2447 return;
2448
2449 /*
2450 * It's possible that we produce an out-of-memory message below, which
2451 * will cause this function to be called again. To break the loop, just
2452 * return here.
2453 */
2454 if (entered)
2455 return;
2456 entered = TRUE;
2457
Bram Moolenaara3f2ecd2006-07-11 21:01:01 +00002458 /*
2459 * Note that the window sizes are updated before reallocating the arrays,
2460 * thus we must not redraw here!
2461 */
2462 ++RedrawingDisabled;
2463
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002464 win_new_shellsize(); // fit the windows in the new sized shell
Bram Moolenaar071d4272004-06-13 20:20:40 +00002465
Bram Moolenaarb3f74062020-02-26 16:16:53 +01002466#ifdef FEAT_GUI_HAIKU
2467 vim_lock_screen(); // be safe, put it here
2468#endif
2469
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002470 comp_col(); // recompute columns for shown command and ruler
Bram Moolenaar071d4272004-06-13 20:20:40 +00002471
2472 /*
2473 * We're changing the size of the screen.
2474 * - Allocate new arrays for ScreenLines and ScreenAttrs.
2475 * - Move lines from the old arrays into the new arrays, clear extra
2476 * lines (unless the screen is going to be cleared).
2477 * - Free the old arrays.
2478 *
2479 * If anything fails, make ScreenLines NULL, so we don't do anything!
2480 * Continuing with the old ScreenLines may result in a crash, because the
2481 * size is wrong.
2482 */
Bram Moolenaarf740b292006-02-16 22:11:02 +00002483 FOR_ALL_TAB_WINDOWS(tp, wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002484 win_free_lsize(wp);
Bram Moolenaare76062c2022-11-28 18:51:43 +00002485 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
Bram Moolenaar84497cd2022-11-28 20:34:52 +00002486 if (aucmd_win[i].auc_win != NULL)
Bram Moolenaare76062c2022-11-28 18:51:43 +00002487 win_free_lsize(aucmd_win[i].auc_win);
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002488#ifdef FEAT_PROP_POPUP
Bram Moolenaar8caaf822019-06-01 18:11:22 +02002489 // global popup windows
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002490 FOR_ALL_POPUPWINS(wp)
Bram Moolenaar8caaf822019-06-01 18:11:22 +02002491 win_free_lsize(wp);
2492 // tab-local popup windows
2493 FOR_ALL_TABPAGES(tp)
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002494 FOR_ALL_POPUPWINS_IN_TAB(tp, wp)
Bram Moolenaar8caaf822019-06-01 18:11:22 +02002495 win_free_lsize(wp);
2496#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002497
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002498 new_ScreenLines = LALLOC_MULT(schar_T, (Rows + 1) * Columns);
Bram Moolenaar216b7102010-03-23 13:56:59 +01002499 vim_memset(new_ScreenLinesC, 0, sizeof(u8char_T *) * MAX_MCO);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002500 if (enc_utf8)
2501 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002502 new_ScreenLinesUC = LALLOC_MULT(u8char_T, (Rows + 1) * Columns);
Bram Moolenaare76062c2022-11-28 18:51:43 +00002503 for (int i = 0; i < p_mco; ++i)
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002504 new_ScreenLinesC[i] = LALLOC_CLEAR_MULT(u8char_T,
2505 (Rows + 1) * Columns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002506 }
2507 if (enc_dbcs == DBCS_JPNU)
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002508 new_ScreenLines2 = LALLOC_MULT(schar_T, (Rows + 1) * Columns);
2509 new_ScreenAttrs = LALLOC_MULT(sattr_T, (Rows + 1) * Columns);
dundargocc57b5bc2022-11-02 13:30:51 +00002510 // Clear ScreenCols to avoid a warning for uninitialized memory in
Bram Moolenaar18ee0fe2022-09-19 11:44:11 +01002511 // jump_to_mouse().
2512 new_ScreenCols = LALLOC_CLEAR_MULT(colnr_T, (Rows + 1) * Columns);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002513 new_LineOffset = LALLOC_MULT(unsigned, Rows);
2514 new_LineWraps = LALLOC_MULT(char_u, Rows);
2515 new_TabPageIdxs = LALLOC_MULT(short, Columns);
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002516#ifdef FEAT_PROP_POPUP
Bram Moolenaar33796b32019-06-08 16:01:13 +02002517 new_popup_mask = LALLOC_MULT(short, Rows * Columns);
Bram Moolenaar4c063a02019-06-10 21:24:12 +02002518 new_popup_mask_next = LALLOC_MULT(short, Rows * Columns);
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002519 new_popup_transparent = LALLOC_MULT(char, Rows * Columns);
Bram Moolenaar33796b32019-06-08 16:01:13 +02002520#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002521
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00002522 FOR_ALL_TAB_WINDOWS(tp, wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002523 {
2524 if (win_alloc_lines(wp) == FAIL)
2525 {
2526 outofmem = TRUE;
Bram Moolenaarbb9c7d12009-02-21 23:03:09 +00002527 goto give_up;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002528 }
2529 }
Bram Moolenaare76062c2022-11-28 18:51:43 +00002530 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
Bram Moolenaar84497cd2022-11-28 20:34:52 +00002531 if (aucmd_win[i].auc_win != NULL
Bram Moolenaare76062c2022-11-28 18:51:43 +00002532 && aucmd_win[i].auc_win->w_lines == NULL
2533 && win_alloc_lines(aucmd_win[i].auc_win) == FAIL)
2534 {
2535 outofmem = TRUE;
2536 break;
2537 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002538#ifdef FEAT_PROP_POPUP
Bram Moolenaar8caaf822019-06-01 18:11:22 +02002539 // global popup windows
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002540 FOR_ALL_POPUPWINS(wp)
Bram Moolenaar8caaf822019-06-01 18:11:22 +02002541 if (win_alloc_lines(wp) == FAIL)
2542 {
2543 outofmem = TRUE;
2544 goto give_up;
2545 }
2546 // tab-local popup windows
2547 FOR_ALL_TABPAGES(tp)
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002548 FOR_ALL_POPUPWINS_IN_TAB(tp, wp)
Bram Moolenaar8caaf822019-06-01 18:11:22 +02002549 if (win_alloc_lines(wp) == FAIL)
2550 {
2551 outofmem = TRUE;
2552 goto give_up;
2553 }
2554#endif
2555
Bram Moolenaarbb9c7d12009-02-21 23:03:09 +00002556give_up:
Bram Moolenaarf86490e2022-11-28 19:11:02 +00002557 found_null = FALSE;
Bram Moolenaare76062c2022-11-28 18:51:43 +00002558 for (int i = 0; i < p_mco; ++i)
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002559 if (new_ScreenLinesC[i] == NULL)
Bram Moolenaare76062c2022-11-28 18:51:43 +00002560 {
2561 found_null = TRUE;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002562 break;
Bram Moolenaare76062c2022-11-28 18:51:43 +00002563 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002564 if (new_ScreenLines == NULL
Bram Moolenaare76062c2022-11-28 18:51:43 +00002565 || (enc_utf8 && (new_ScreenLinesUC == NULL || found_null))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002566 || (enc_dbcs == DBCS_JPNU && new_ScreenLines2 == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002567 || new_ScreenAttrs == NULL
Bram Moolenaarb9081882022-07-09 04:56:24 +01002568 || new_ScreenCols == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002569 || new_LineOffset == NULL
2570 || new_LineWraps == NULL
Bram Moolenaarf740b292006-02-16 22:11:02 +00002571 || new_TabPageIdxs == NULL
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002572#ifdef FEAT_PROP_POPUP
Bram Moolenaar33796b32019-06-08 16:01:13 +02002573 || new_popup_mask == NULL
Bram Moolenaar4c063a02019-06-10 21:24:12 +02002574 || new_popup_mask_next == NULL
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002575 || new_popup_transparent == NULL
Bram Moolenaar33796b32019-06-08 16:01:13 +02002576#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002577 || outofmem)
2578 {
Bram Moolenaar89d40322006-08-29 15:30:07 +00002579 if (ScreenLines != NULL || !done_outofmem_msg)
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00002580 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002581 // guess the size
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00002582 do_outofmem_msg((long_u)((Rows + 1) * Columns));
2583
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002584 // Remember we did this to avoid getting outofmem messages over
2585 // and over again.
Bram Moolenaar89d40322006-08-29 15:30:07 +00002586 done_outofmem_msg = TRUE;
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00002587 }
Bram Moolenaard23a8232018-02-10 18:45:26 +01002588 VIM_CLEAR(new_ScreenLines);
Bram Moolenaard23a8232018-02-10 18:45:26 +01002589 VIM_CLEAR(new_ScreenLinesUC);
Bram Moolenaare76062c2022-11-28 18:51:43 +00002590 for (int i = 0; i < p_mco; ++i)
Bram Moolenaard23a8232018-02-10 18:45:26 +01002591 VIM_CLEAR(new_ScreenLinesC[i]);
2592 VIM_CLEAR(new_ScreenLines2);
Bram Moolenaard23a8232018-02-10 18:45:26 +01002593 VIM_CLEAR(new_ScreenAttrs);
Bram Moolenaarb9081882022-07-09 04:56:24 +01002594 VIM_CLEAR(new_ScreenCols);
Bram Moolenaard23a8232018-02-10 18:45:26 +01002595 VIM_CLEAR(new_LineOffset);
2596 VIM_CLEAR(new_LineWraps);
2597 VIM_CLEAR(new_TabPageIdxs);
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002598#ifdef FEAT_PROP_POPUP
Bram Moolenaar33796b32019-06-08 16:01:13 +02002599 VIM_CLEAR(new_popup_mask);
Bram Moolenaar4c063a02019-06-10 21:24:12 +02002600 VIM_CLEAR(new_popup_mask_next);
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002601 VIM_CLEAR(new_popup_transparent);
Bram Moolenaar33796b32019-06-08 16:01:13 +02002602#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002603 }
2604 else
2605 {
Bram Moolenaar89d40322006-08-29 15:30:07 +00002606 done_outofmem_msg = FALSE;
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00002607
Bram Moolenaar071d4272004-06-13 20:20:40 +00002608 for (new_row = 0; new_row < Rows; ++new_row)
2609 {
2610 new_LineOffset[new_row] = new_row * Columns;
2611 new_LineWraps[new_row] = FALSE;
2612
Olaf Seibertfd472652024-02-01 21:11:16 +01002613 (void)vim_memset(new_ScreenLines + new_row * Columns,
2614 ' ', (size_t)Columns * sizeof(schar_T));
2615 if (enc_utf8)
2616 {
2617 (void)vim_memset(new_ScreenLinesUC + new_row * Columns,
2618 0, (size_t)Columns * sizeof(u8char_T));
2619 for (int i = 0; i < p_mco; ++i)
2620 (void)vim_memset(new_ScreenLinesC[i]
2621 + new_row * Columns,
2622 0, (size_t)Columns * sizeof(u8char_T));
2623 }
2624 if (enc_dbcs == DBCS_JPNU)
2625 (void)vim_memset(new_ScreenLines2 + new_row * Columns,
2626 0, (size_t)Columns * sizeof(schar_T));
2627 (void)vim_memset(new_ScreenAttrs + new_row * Columns,
2628 0, (size_t)Columns * sizeof(sattr_T));
2629 (void)vim_memset(new_ScreenCols + new_row * Columns,
2630 0, (size_t)Columns * sizeof(colnr_T));
2631
Bram Moolenaar071d4272004-06-13 20:20:40 +00002632 /*
2633 * If the screen is not going to be cleared, copy as much as
2634 * possible from the old screen to the new one and clear the rest
2635 * (used when resizing the window at the "--more--" prompt or when
2636 * executing an external command, for the GUI).
2637 */
Bram Moolenaar70b2a562012-01-10 22:26:17 +01002638 if (!doclear)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002639 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002640 old_row = new_row + (screen_Rows - Rows);
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00002641 if (old_row >= 0 && ScreenLines != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002642 {
2643 if (screen_Columns < Columns)
2644 len = screen_Columns;
2645 else
2646 len = Columns;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002647 // When switching to utf-8 don't copy characters, they
2648 // may be invalid now. Also when p_mco changes.
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002649 if (!(enc_utf8 && ScreenLinesUC == NULL)
2650 && p_mco == Screen_mco)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002651 mch_memmove(new_ScreenLines + new_LineOffset[new_row],
2652 ScreenLines + LineOffset[old_row],
2653 (size_t)len * sizeof(schar_T));
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002654 if (enc_utf8 && ScreenLinesUC != NULL
2655 && p_mco == Screen_mco)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002656 {
2657 mch_memmove(new_ScreenLinesUC + new_LineOffset[new_row],
2658 ScreenLinesUC + LineOffset[old_row],
2659 (size_t)len * sizeof(u8char_T));
Bram Moolenaare76062c2022-11-28 18:51:43 +00002660 for (int i = 0; i < p_mco; ++i)
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002661 mch_memmove(new_ScreenLinesC[i]
2662 + new_LineOffset[new_row],
2663 ScreenLinesC[i] + LineOffset[old_row],
Bram Moolenaar071d4272004-06-13 20:20:40 +00002664 (size_t)len * sizeof(u8char_T));
2665 }
2666 if (enc_dbcs == DBCS_JPNU && ScreenLines2 != NULL)
2667 mch_memmove(new_ScreenLines2 + new_LineOffset[new_row],
2668 ScreenLines2 + LineOffset[old_row],
2669 (size_t)len * sizeof(schar_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002670 mch_memmove(new_ScreenAttrs + new_LineOffset[new_row],
2671 ScreenAttrs + LineOffset[old_row],
2672 (size_t)len * sizeof(sattr_T));
Bram Moolenaarb9081882022-07-09 04:56:24 +01002673 mch_memmove(new_ScreenCols + new_LineOffset[new_row],
2674 ScreenAttrs + LineOffset[old_row],
2675 (size_t)len * sizeof(colnr_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002676 }
2677 }
2678 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002679 // Use the last line of the screen for the current line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002680 current_ScreenLine = new_ScreenLines + Rows * Columns;
Bram Moolenaar6ace95e2019-08-13 23:09:49 +02002681
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002682#ifdef FEAT_PROP_POPUP
Bram Moolenaar6ace95e2019-08-13 23:09:49 +02002683 vim_memset(new_popup_mask, 0, Rows * Columns * sizeof(short));
2684 vim_memset(new_popup_transparent, 0, Rows * Columns * sizeof(char));
2685#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002686 }
2687
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00002688 free_screenlines();
2689
Bram Moolenaar6ace95e2019-08-13 23:09:49 +02002690 // NOTE: this may result in all pointers to become NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002691 ScreenLines = new_ScreenLines;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002692 ScreenLinesUC = new_ScreenLinesUC;
Bram Moolenaare76062c2022-11-28 18:51:43 +00002693 for (int i = 0; i < p_mco; ++i)
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002694 ScreenLinesC[i] = new_ScreenLinesC[i];
2695 Screen_mco = p_mco;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002696 ScreenLines2 = new_ScreenLines2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002697 ScreenAttrs = new_ScreenAttrs;
Bram Moolenaarb9081882022-07-09 04:56:24 +01002698 ScreenCols = new_ScreenCols;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002699 LineOffset = new_LineOffset;
2700 LineWraps = new_LineWraps;
Bram Moolenaarf740b292006-02-16 22:11:02 +00002701 TabPageIdxs = new_TabPageIdxs;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002702#ifdef FEAT_PROP_POPUP
Bram Moolenaar33796b32019-06-08 16:01:13 +02002703 popup_mask = new_popup_mask;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002704 popup_mask_next = new_popup_mask_next;
2705 popup_transparent = new_popup_transparent;
Bram Moolenaar33796b32019-06-08 16:01:13 +02002706 popup_mask_refresh = TRUE;
2707#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002708
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002709 // It's important that screen_Rows and screen_Columns reflect the actual
2710 // size of ScreenLines[]. Set them before calling anything.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002711#ifdef FEAT_GUI
2712 old_Rows = screen_Rows;
2713#endif
2714 screen_Rows = Rows;
2715 screen_Columns = Columns;
2716
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002717 set_must_redraw(UPD_CLEAR); // need to clear the screen later
Bram Moolenaar70b2a562012-01-10 22:26:17 +01002718 if (doclear)
Bram Moolenaarb13d3402022-08-29 13:44:28 +01002719 screenclear2(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002720#ifdef FEAT_GUI
2721 else if (gui.in_use
2722 && !gui.starting
2723 && ScreenLines != NULL
2724 && old_Rows != Rows)
2725 {
Bram Moolenaar7c003aa2020-03-28 20:44:41 +01002726 gui_redraw_block(0, 0, (int)Rows - 1, (int)Columns - 1, 0);
2727
2728 // Adjust the position of the cursor, for when executing an external
2729 // command.
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002730 if (msg_row >= Rows) // Rows got smaller
2731 msg_row = Rows - 1; // put cursor at last row
2732 else if (Rows > old_Rows) // Rows got bigger
2733 msg_row += Rows - old_Rows; // put cursor in same place
2734 if (msg_col >= Columns) // Columns got smaller
2735 msg_col = Columns - 1; // put cursor at last column
Bram Moolenaar071d4272004-06-13 20:20:40 +00002736 }
2737#endif
Bram Moolenaarca57ab52019-04-13 14:53:16 +02002738 clear_TabPageIdxs();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002739
Bram Moolenaarb3f74062020-02-26 16:16:53 +01002740#ifdef FEAT_GUI_HAIKU
2741 vim_unlock_screen();
2742#endif
2743
Bram Moolenaar071d4272004-06-13 20:20:40 +00002744 entered = FALSE;
Bram Moolenaar79cdf022023-05-20 14:07:00 +01002745 if (RedrawingDisabled > 0)
2746 --RedrawingDisabled;
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002747
Bram Moolenaar87e817c2009-02-22 20:13:39 +00002748 /*
2749 * Do not apply autocommands more than 3 times to avoid an endless loop
2750 * in case applying autocommands always changes Rows or Columns.
2751 */
2752 if (starting == 0 && ++retry_count <= 3)
2753 {
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002754 apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, FALSE, curbuf);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002755 // In rare cases, autocommands may have altered Rows or Columns,
2756 // jump back to check if we need to allocate the screen again.
Bram Moolenaar87e817c2009-02-22 20:13:39 +00002757 goto retry;
2758 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002759}
2760
2761 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002762free_screenlines(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00002763{
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002764 int i;
2765
Bram Moolenaar33796b32019-06-08 16:01:13 +02002766 VIM_CLEAR(ScreenLinesUC);
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002767 for (i = 0; i < Screen_mco; ++i)
Bram Moolenaar33796b32019-06-08 16:01:13 +02002768 VIM_CLEAR(ScreenLinesC[i]);
2769 VIM_CLEAR(ScreenLines2);
2770 VIM_CLEAR(ScreenLines);
2771 VIM_CLEAR(ScreenAttrs);
Bram Moolenaarb9081882022-07-09 04:56:24 +01002772 VIM_CLEAR(ScreenCols);
Bram Moolenaar33796b32019-06-08 16:01:13 +02002773 VIM_CLEAR(LineOffset);
2774 VIM_CLEAR(LineWraps);
2775 VIM_CLEAR(TabPageIdxs);
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002776#ifdef FEAT_PROP_POPUP
Bram Moolenaar33796b32019-06-08 16:01:13 +02002777 VIM_CLEAR(popup_mask);
Bram Moolenaar4c063a02019-06-10 21:24:12 +02002778 VIM_CLEAR(popup_mask_next);
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002779 VIM_CLEAR(popup_transparent);
Bram Moolenaar33796b32019-06-08 16:01:13 +02002780#endif
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00002781}
2782
Bram Moolenaarb13d3402022-08-29 13:44:28 +01002783/*
2784 * Clear the screen.
2785 * May delay if there is something the user should read.
2786 * Allocated the screen for resizing if needed.
Bram Moolenaara4e0b972022-10-01 19:43:52 +01002787 * Returns TRUE when the screen was actually cleared, FALSE if all display
Bram Moolenaar838b7462022-09-26 15:19:56 +01002788 * cells were marked for updating.
Bram Moolenaarb13d3402022-08-29 13:44:28 +01002789 */
Bram Moolenaar838b7462022-09-26 15:19:56 +01002790 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002791screenclear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002792{
2793 check_for_delay(FALSE);
Bram Moolenaar838b7462022-09-26 15:19:56 +01002794 screenalloc(FALSE); // allocate screen buffers if size changed
2795 return screenclear2(TRUE); // clear the screen
Bram Moolenaarb13d3402022-08-29 13:44:28 +01002796}
2797
2798/*
2799 * Do not clear the screen but mark everything for redraw.
2800 */
2801 void
2802redraw_as_cleared(void)
2803{
2804 screenclear2(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002805}
2806
Bram Moolenaar838b7462022-09-26 15:19:56 +01002807 static int
Bram Moolenaarb13d3402022-08-29 13:44:28 +01002808screenclear2(int doclear)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002809{
2810 int i;
Bram Moolenaar838b7462022-09-26 15:19:56 +01002811 int did_clear = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002812
2813 if (starting == NO_SCREEN || ScreenLines == NULL
2814#ifdef FEAT_GUI
2815 || (gui.in_use && gui.starting)
2816#endif
2817 )
Bram Moolenaar838b7462022-09-26 15:19:56 +01002818 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002819
2820#ifdef FEAT_GUI
2821 if (!gui.in_use)
2822#endif
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002823 screen_attr = -1; // force setting the Normal colors
2824 screen_stop_highlight(); // don't want highlighting here
Bram Moolenaar071d4272004-06-13 20:20:40 +00002825
2826#ifdef FEAT_CLIPBOARD
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002827 // disable selection without redrawing it
Bram Moolenaar071d4272004-06-13 20:20:40 +00002828 clip_scroll_selection(9999);
2829#endif
2830
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002831 // blank out ScreenLines
Bram Moolenaar071d4272004-06-13 20:20:40 +00002832 for (i = 0; i < Rows; ++i)
2833 {
Bram Moolenaarcfce7172017-08-17 20:31:48 +02002834 lineclear(LineOffset[i], (int)Columns, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002835 LineWraps[i] = FALSE;
2836 }
2837
Bram Moolenaarb13d3402022-08-29 13:44:28 +01002838 if (doclear && can_clear(T_CL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002839 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002840 out_str(T_CL); // clear the display
Bram Moolenaar838b7462022-09-26 15:19:56 +01002841 did_clear = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002842 clear_cmdline = FALSE;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002843 mode_displayed = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002844 }
2845 else
2846 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002847 // can't clear the screen, mark all chars with invalid attributes
Bram Moolenaar071d4272004-06-13 20:20:40 +00002848 for (i = 0; i < Rows; ++i)
2849 lineinvalid(LineOffset[i], (int)Columns);
2850 clear_cmdline = TRUE;
2851 }
2852
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002853 screen_cleared = TRUE; // can use contents of ScreenLines now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002854
Bram Moolenaarb13d3402022-08-29 13:44:28 +01002855 win_rest_invalid(firstwin); // redraw all regular windows
Bram Moolenaar071d4272004-06-13 20:20:40 +00002856 redraw_cmdline = TRUE;
Bram Moolenaar997fb4b2006-02-17 21:53:23 +00002857 redraw_tabline = TRUE;
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02002858#if defined(FEAT_TABPANEL)
2859 redraw_tabpanel = TRUE;
2860#endif
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002861 if (must_redraw == UPD_CLEAR) // no need to clear again
2862 must_redraw = UPD_NOT_VALID;
Bram Moolenaarcf0995d2022-09-11 21:36:17 +01002863 msg_scrolled = 0; // compute_cmdrow() uses this
Bram Moolenaar071d4272004-06-13 20:20:40 +00002864 compute_cmdrow();
Bram Moolenaarcf0995d2022-09-11 21:36:17 +01002865#ifdef FEAT_PROP_POPUP
2866 popup_redraw_all(); // redraw all popup windows
2867#endif
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002868 msg_row = cmdline_row; // put cursor on last line for messages
Bram Moolenaar071d4272004-06-13 20:20:40 +00002869 msg_col = 0;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002870 screen_start(); // don't know where cursor is now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002871 msg_didany = FALSE;
2872 msg_didout = FALSE;
Bram Moolenaar838b7462022-09-26 15:19:56 +01002873
2874 return did_clear;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002875}
2876
2877/*
2878 * Clear one line in ScreenLines.
2879 */
2880 static void
Bram Moolenaarcfce7172017-08-17 20:31:48 +02002881lineclear(unsigned off, int width, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002882{
2883 (void)vim_memset(ScreenLines + off, ' ', (size_t)width * sizeof(schar_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002884 if (enc_utf8)
2885 (void)vim_memset(ScreenLinesUC + off, 0,
2886 (size_t)width * sizeof(u8char_T));
Bram Moolenaarcfce7172017-08-17 20:31:48 +02002887 (void)vim_memset(ScreenAttrs + off, attr, (size_t)width * sizeof(sattr_T));
Bram Moolenaarb9081882022-07-09 04:56:24 +01002888 (void)vim_memset(ScreenCols + off, -1, (size_t)width * sizeof(colnr_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002889}
2890
2891/*
2892 * Mark one line in ScreenLines invalid by setting the attributes to an
2893 * invalid value.
2894 */
2895 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01002896lineinvalid(unsigned off, int width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002897{
2898 (void)vim_memset(ScreenAttrs + off, -1, (size_t)width * sizeof(sattr_T));
Bram Moolenaarb9081882022-07-09 04:56:24 +01002899 (void)vim_memset(ScreenCols + off, -1, (size_t)width * sizeof(colnr_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002900}
2901
Bram Moolenaar071d4272004-06-13 20:20:40 +00002902/*
Bram Moolenaar96916ac2020-07-08 23:09:28 +02002903 * To be called when characters were sent to the terminal directly, outputting
2904 * test on "screen_lnum".
2905 */
2906 void
2907line_was_clobbered(int screen_lnum)
2908{
2909 lineinvalid(LineOffset[screen_lnum], (int)Columns);
2910}
2911
2912/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002913 * Copy part of a Screenline for vertically split window "wp".
2914 */
2915 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01002916linecopy(int to, int from, win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002917{
2918 unsigned off_to = LineOffset[to] + wp->w_wincol;
2919 unsigned off_from = LineOffset[from] + wp->w_wincol;
2920
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02002921#if defined(FEAT_TABPANEL)
2922 off_to += TPL_LCOL(wp);
2923 off_from += TPL_LCOL(wp);
2924#endif
2925
Bram Moolenaar071d4272004-06-13 20:20:40 +00002926 mch_memmove(ScreenLines + off_to, ScreenLines + off_from,
2927 wp->w_width * sizeof(schar_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002928 if (enc_utf8)
2929 {
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002930 int i;
2931
Bram Moolenaar071d4272004-06-13 20:20:40 +00002932 mch_memmove(ScreenLinesUC + off_to, ScreenLinesUC + off_from,
2933 wp->w_width * sizeof(u8char_T));
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002934 for (i = 0; i < p_mco; ++i)
2935 mch_memmove(ScreenLinesC[i] + off_to, ScreenLinesC[i] + off_from,
2936 wp->w_width * sizeof(u8char_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002937 }
2938 if (enc_dbcs == DBCS_JPNU)
2939 mch_memmove(ScreenLines2 + off_to, ScreenLines2 + off_from,
2940 wp->w_width * sizeof(schar_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002941 mch_memmove(ScreenAttrs + off_to, ScreenAttrs + off_from,
2942 wp->w_width * sizeof(sattr_T));
Bram Moolenaarb9081882022-07-09 04:56:24 +01002943 mch_memmove(ScreenCols + off_to, ScreenCols + off_from,
2944 wp->w_width * sizeof(colnr_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002945}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002946
2947/*
2948 * Return TRUE if clearing with term string "p" would work.
2949 * It can't work when the string is empty or it won't set the right background.
Bram Moolenaar33796b32019-06-08 16:01:13 +02002950 * Don't clear to end-of-line when there are popups, it may cause flicker.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002951 */
2952 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002953can_clear(char_u *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002954{
2955 return (*p != NUL && (t_colors <= 1
2956#ifdef FEAT_GUI
2957 || gui.in_use
2958#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02002959#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02002960 || (p_tgc && cterm_normal_bg_gui_color == INVALCOLOR)
Bram Moolenaard18f6722016-06-17 13:18:49 +02002961 || (!p_tgc && cterm_normal_bg_color == 0)
2962#else
2963 || cterm_normal_bg_color == 0
Bram Moolenaar8a633e32016-04-21 21:10:14 +02002964#endif
Bram Moolenaar33796b32019-06-08 16:01:13 +02002965 || *T_UT != NUL)
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002966#ifdef FEAT_PROP_POPUP
Bram Moolenaar33796b32019-06-08 16:01:13 +02002967 && !(p == T_CE && popup_visible)
2968#endif
2969 );
Bram Moolenaar071d4272004-06-13 20:20:40 +00002970}
2971
2972/*
2973 * Reset cursor position. Use whenever cursor was moved because of outputting
2974 * something directly to the screen (shell commands) or a terminal control
2975 * code.
2976 */
2977 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002978screen_start(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002979{
2980 screen_cur_row = screen_cur_col = 9999;
2981}
2982
2983/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002984 * Move the cursor to position "row","col" in the screen.
2985 * This tries to find the most efficient way to move, minimizing the number of
2986 * characters sent to the terminal.
2987 */
2988 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002989windgoto(int row, int col)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002990{
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002991 sattr_T *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002992 int i;
2993 int plan;
2994 int cost;
2995 int wouldbe_col;
2996 int noinvcurs;
2997 char_u *bs;
2998 int goto_cost;
2999 int attr;
3000
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003001#define GOTO_COST 7 // assume a term_windgoto() takes about 7 chars
3002#define HIGHL_COST 5 // assume unhighlight takes 5 chars
Bram Moolenaar071d4272004-06-13 20:20:40 +00003003
3004#define PLAN_LE 1
3005#define PLAN_CR 2
3006#define PLAN_NL 3
3007#define PLAN_WRITE 4
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003008 // Can't use ScreenLines unless initialized
Bram Moolenaar071d4272004-06-13 20:20:40 +00003009 if (ScreenLines == NULL)
3010 return;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003011 if (col == screen_cur_col && row == screen_cur_row)
3012 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003013
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003014 // Check for valid position.
3015 if (row < 0) // window without text lines?
3016 row = 0;
3017 if (row >= screen_Rows)
3018 row = screen_Rows - 1;
3019 if (col >= screen_Columns)
3020 col = screen_Columns - 1;
3021
3022 // check if no cursor movement is allowed in highlight mode
3023 if (screen_attr && *T_MS == NUL)
3024 noinvcurs = HIGHL_COST;
3025 else
3026 noinvcurs = 0;
3027 goto_cost = GOTO_COST + noinvcurs;
3028
3029 /*
3030 * Plan how to do the positioning:
3031 * 1. Use CR to move it to column 0, same row.
3032 * 2. Use T_LE to move it a few columns to the left.
3033 * 3. Use NL to move a few lines down, column 0.
3034 * 4. Move a few columns to the right with T_ND or by writing chars.
3035 *
3036 * Don't do this if the cursor went beyond the last column, the cursor
3037 * position is unknown then (some terminals wrap, some don't )
3038 *
3039 * First check if the highlighting attributes allow us to write
3040 * characters to move the cursor to the right.
3041 */
3042 if (row >= screen_cur_row && screen_cur_col < Columns)
3043 {
3044 /*
3045 * If the cursor is in the same row, bigger col, we can use CR
3046 * or T_LE.
3047 */
3048 bs = NULL; // init for GCC
3049 attr = screen_attr;
3050 if (row == screen_cur_row && col < screen_cur_col)
3051 {
3052 // "le" is preferred over "bc", because "bc" is obsolete
3053 if (*T_LE)
3054 bs = T_LE; // "cursor left"
3055 else
3056 bs = T_BC; // "backspace character (old)
3057 if (*bs)
3058 cost = (screen_cur_col - col) * (int)STRLEN(bs);
3059 else
3060 cost = 999;
3061 if (col + 1 < cost) // using CR is less characters
3062 {
3063 plan = PLAN_CR;
3064 wouldbe_col = 0;
3065 cost = 1; // CR is just one character
3066 }
3067 else
3068 {
3069 plan = PLAN_LE;
3070 wouldbe_col = col;
3071 }
3072 if (noinvcurs) // will stop highlighting
3073 {
3074 cost += noinvcurs;
3075 attr = 0;
3076 }
3077 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003078
3079 /*
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003080 * If the cursor is above where we want to be, we can use CR LF.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003081 */
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003082 else if (row > screen_cur_row)
3083 {
3084 plan = PLAN_NL;
3085 wouldbe_col = 0;
3086 cost = (row - screen_cur_row) * 2; // CR LF
3087 if (noinvcurs) // will stop highlighting
3088 {
3089 cost += noinvcurs;
3090 attr = 0;
3091 }
3092 }
3093
3094 /*
3095 * If the cursor is in the same row, smaller col, just use write.
3096 */
3097 else
3098 {
3099 plan = PLAN_WRITE;
3100 wouldbe_col = screen_cur_col;
3101 cost = 0;
3102 }
3103
3104 /*
3105 * Check if any characters that need to be written have the
3106 * correct attributes. Also avoid UTF-8 characters.
3107 */
3108 i = col - wouldbe_col;
3109 if (i > 0)
3110 cost += i;
3111 if (cost < goto_cost && i > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003112 {
3113 /*
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003114 * Check if the attributes are correct without additionally
3115 * stopping highlighting.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003116 */
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003117 p = ScreenAttrs + LineOffset[row] + wouldbe_col;
3118 while (i && *p++ == attr)
3119 --i;
3120 if (i != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003121 {
3122 /*
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003123 * Try if it works when highlighting is stopped here.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003124 */
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003125 if (*--p == 0)
3126 {
3127 cost += noinvcurs;
3128 while (i && *p++ == 0)
3129 --i;
3130 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003131 if (i != 0)
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003132 cost = 999; // different attributes, don't do it
Bram Moolenaar071d4272004-06-13 20:20:40 +00003133 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003134 if (enc_utf8)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003135 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003136 // Don't use an UTF-8 char for positioning, it's slow.
3137 for (i = wouldbe_col; i < col; ++i)
3138 if (ScreenLinesUC[LineOffset[row] + i] != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003139 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003140 cost = 999;
3141 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003142 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003143 }
3144 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003145
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003146 /*
3147 * We can do it without term_windgoto()!
3148 */
3149 if (cost < goto_cost)
3150 {
3151 if (plan == PLAN_LE)
3152 {
3153 if (noinvcurs)
3154 screen_stop_highlight();
3155 while (screen_cur_col > col)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003156 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003157 out_str(bs);
3158 --screen_cur_col;
3159 }
3160 }
3161 else if (plan == PLAN_CR)
3162 {
3163 if (noinvcurs)
3164 screen_stop_highlight();
3165 out_char('\r');
3166 screen_cur_col = 0;
3167 }
3168 else if (plan == PLAN_NL)
3169 {
3170 if (noinvcurs)
3171 screen_stop_highlight();
3172 while (screen_cur_row < row)
3173 {
3174 out_char('\n');
3175 ++screen_cur_row;
3176 }
3177 screen_cur_col = 0;
3178 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003179
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003180 i = col - screen_cur_col;
3181 if (i > 0)
3182 {
3183 /*
3184 * Use cursor-right if it's one character only. Avoids
3185 * removing a line of pixels from the last bold char, when
3186 * using the bold trick in the GUI.
3187 */
3188 if (T_ND[0] != NUL && T_ND[1] == NUL)
3189 {
3190 while (i-- > 0)
3191 out_char(*T_ND);
3192 }
3193 else
3194 {
3195 int off;
3196
3197 off = LineOffset[row] + screen_cur_col;
3198 while (i-- > 0)
3199 {
3200 if (ScreenAttrs[off] != screen_attr)
3201 screen_stop_highlight();
3202 out_flush_check();
3203 out_char(ScreenLines[off]);
3204 if (enc_dbcs == DBCS_JPNU
3205 && ScreenLines[off] == 0x8e)
3206 out_char(ScreenLines2[off]);
3207 ++off;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003208 }
3209 }
3210 }
3211 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003212 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00003213 else
3214 cost = 999;
3215
3216 if (cost >= goto_cost)
3217 {
3218 if (noinvcurs)
3219 screen_stop_highlight();
3220 if (row == screen_cur_row && (col > screen_cur_col)
3221 && *T_CRI != NUL)
3222 term_cursor_right(col - screen_cur_col);
3223 else
3224 term_windgoto(row, col);
3225 }
3226 screen_cur_row = row;
3227 screen_cur_col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003228}
3229
3230/*
3231 * Set cursor to its position in the current window.
3232 */
3233 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003234setcursor(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003235{
Bram Moolenaar987723e2018-03-06 11:43:04 +01003236 setcursor_mayforce(FALSE);
3237}
3238
3239/*
3240 * Set cursor to its position in the current window.
3241 * When "force" is TRUE also when not redrawing.
3242 */
3243 void
3244setcursor_mayforce(int force)
3245{
3246 if (force || redrawing())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003247 {
3248 validate_cursor();
3249 windgoto(W_WINROW(curwin) + curwin->w_wrow,
Bram Moolenaar53f81742017-09-22 14:35:51 +02003250 curwin->w_wincol + (
Bram Moolenaar071d4272004-06-13 20:20:40 +00003251#ifdef FEAT_RIGHTLEFT
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003252 // With 'rightleft' set and the cursor on a double-wide
3253 // character, position it on the leftmost column.
Bram Moolenaara12a1612019-01-24 16:39:02 +01003254 curwin->w_p_rl ? ((int)curwin->w_width - curwin->w_wcol
3255 - ((has_mbyte
Bram Moolenaar561f9db2008-02-20 13:16:29 +00003256 && (*mb_ptr2cells)(ml_get_cursor()) == 2
Bram Moolenaara12a1612019-01-24 16:39:02 +01003257 && vim_isprintc(gchar_cursor())) ? 2 : 1)) :
Bram Moolenaar071d4272004-06-13 20:20:40 +00003258#endif
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003259 curwin->w_wcol) + TPL_LCOL(NULL));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003260 }
3261}
3262
3263
3264/*
Bram Moolenaar86033562017-07-12 20:24:41 +02003265 * Insert 'line_count' lines at 'row' in window 'wp'.
3266 * If 'invalid' is TRUE the wp->w_lines[].wl_lnum is invalidated.
3267 * If 'mayclear' is TRUE the screen will be cleared if it is faster than
Bram Moolenaar071d4272004-06-13 20:20:40 +00003268 * scrolling.
3269 * Returns FAIL if the lines are not inserted, OK for success.
3270 */
3271 int
Bram Moolenaar05540972016-01-30 20:31:25 +01003272win_ins_lines(
3273 win_T *wp,
3274 int row,
3275 int line_count,
3276 int invalid,
3277 int mayclear)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003278{
3279 int did_delete;
3280 int nextrow;
3281 int lastrow;
3282 int retval;
3283
3284 if (invalid)
3285 wp->w_lines_valid = 0;
3286
Bram Moolenaarc856ceb2022-06-21 18:10:39 +01003287 // with only a few lines it's not worth the effort
Bram Moolenaar071d4272004-06-13 20:20:40 +00003288 if (wp->w_height < 5)
3289 return FAIL;
3290
Bram Moolenaarc856ceb2022-06-21 18:10:39 +01003291 // with the popup menu visible this might not work correctly
3292 if (pum_visible())
3293 return FAIL;
3294
Bram Moolenaar071d4272004-06-13 20:20:40 +00003295 if (line_count > wp->w_height - row)
3296 line_count = wp->w_height - row;
3297
Bram Moolenaarcfce7172017-08-17 20:31:48 +02003298 retval = win_do_lines(wp, row, line_count, mayclear, FALSE, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003299 if (retval != MAYBE)
3300 return retval;
3301
3302 /*
3303 * If there is a next window or a status line, we first try to delete the
3304 * lines at the bottom to avoid messing what is after the window.
Bram Moolenaarc363fe12019-08-04 18:13:46 +02003305 * If this fails and there are following windows, don't do anything to
3306 * avoid messing up those windows, better just redraw.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003307 */
3308 did_delete = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003309 if (wp->w_next != NULL || wp->w_status_height)
3310 {
3311 if (screen_del_lines(0, W_WINROW(wp) + wp->w_height - line_count,
Bram Moolenaarcfce7172017-08-17 20:31:48 +02003312 line_count, (int)Rows, FALSE, 0, NULL) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003313 did_delete = TRUE;
3314 else if (wp->w_next)
3315 return FAIL;
3316 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003317 /*
3318 * if no lines deleted, blank the lines that will end up below the window
3319 */
3320 if (!did_delete)
3321 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003322 wp->w_redr_status = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003323 redraw_cmdline = TRUE;
Bram Moolenaare0de17d2017-09-24 16:24:34 +02003324 nextrow = W_WINROW(wp) + wp->w_height + wp->w_status_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003325 lastrow = nextrow + line_count;
3326 if (lastrow > Rows)
3327 lastrow = Rows;
3328 screen_fill(nextrow - line_count, lastrow - line_count,
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003329 wp->w_wincol + TPL_LCOL(wp), (int)W_ENDCOL(wp) + TPL_LCOL(wp),
Bram Moolenaar071d4272004-06-13 20:20:40 +00003330 ' ', ' ', 0);
3331 }
3332
Bram Moolenaarcfce7172017-08-17 20:31:48 +02003333 if (screen_ins_lines(0, W_WINROW(wp) + row, line_count, (int)Rows, 0, NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003334 == FAIL)
3335 {
Bram Moolenaarc363fe12019-08-04 18:13:46 +02003336 // deletion will have messed up other windows
Bram Moolenaar071d4272004-06-13 20:20:40 +00003337 if (did_delete)
3338 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003339 wp->w_redr_status = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003340 win_rest_invalid(W_NEXT(wp));
3341 }
3342 return FAIL;
3343 }
3344
3345 return OK;
3346}
3347
3348/*
Bram Moolenaar86033562017-07-12 20:24:41 +02003349 * Delete "line_count" window lines at "row" in window "wp".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003350 * If "invalid" is TRUE curwin->w_lines[] is invalidated.
3351 * If "mayclear" is TRUE the screen will be cleared if it is faster than
3352 * scrolling
3353 * Return OK for success, FAIL if the lines are not deleted.
3354 */
3355 int
Bram Moolenaar05540972016-01-30 20:31:25 +01003356win_del_lines(
3357 win_T *wp,
3358 int row,
3359 int line_count,
3360 int invalid,
Bram Moolenaarcfce7172017-08-17 20:31:48 +02003361 int mayclear,
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003362 int clear_attr) // for clearing lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003363{
3364 int retval;
3365
3366 if (invalid)
3367 wp->w_lines_valid = 0;
3368
3369 if (line_count > wp->w_height - row)
3370 line_count = wp->w_height - row;
3371
Bram Moolenaarcfce7172017-08-17 20:31:48 +02003372 retval = win_do_lines(wp, row, line_count, mayclear, TRUE, clear_attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003373 if (retval != MAYBE)
3374 return retval;
3375
3376 if (screen_del_lines(0, W_WINROW(wp) + row, line_count,
Bram Moolenaarcfce7172017-08-17 20:31:48 +02003377 (int)Rows, FALSE, clear_attr, NULL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003378 return FAIL;
3379
Bram Moolenaar071d4272004-06-13 20:20:40 +00003380 /*
3381 * If there are windows or status lines below, try to put them at the
3382 * correct place. If we can't do that, they have to be redrawn.
3383 */
3384 if (wp->w_next || wp->w_status_height || cmdline_row < Rows - 1)
3385 {
3386 if (screen_ins_lines(0, W_WINROW(wp) + wp->w_height - line_count,
Bram Moolenaarcfce7172017-08-17 20:31:48 +02003387 line_count, (int)Rows, clear_attr, NULL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003388 {
3389 wp->w_redr_status = TRUE;
3390 win_rest_invalid(wp->w_next);
3391 }
3392 }
3393 /*
3394 * If this is the last window and there is no status line, redraw the
3395 * command line later.
3396 */
3397 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003398 redraw_cmdline = TRUE;
3399 return OK;
3400}
3401
3402/*
3403 * Common code for win_ins_lines() and win_del_lines().
3404 * Returns OK or FAIL when the work has been done.
3405 * Returns MAYBE when not finished yet.
3406 */
3407 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01003408win_do_lines(
3409 win_T *wp,
3410 int row,
3411 int line_count,
3412 int mayclear,
Bram Moolenaarcfce7172017-08-17 20:31:48 +02003413 int del,
3414 int clear_attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003415{
3416 int retval;
3417
3418 if (!redrawing() || line_count <= 0)
3419 return FAIL;
3420
Bram Moolenaar33796b32019-06-08 16:01:13 +02003421 // When inserting lines would result in loss of command output, just redraw
3422 // the lines.
Bram Moolenaar29ae3772017-04-30 19:39:39 +02003423 if (no_win_do_lines_ins && !del)
3424 return FAIL;
3425
Bram Moolenaar33796b32019-06-08 16:01:13 +02003426 // only a few lines left: redraw is faster
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003427 if (mayclear && Rows - line_count < 5
3428 && wp->w_width == COLUMNS_WITHOUT_TPL())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003429 {
Bram Moolenaar29ae3772017-04-30 19:39:39 +02003430 if (!no_win_do_lines_ins)
Bram Moolenaar33796b32019-06-08 16:01:13 +02003431 screenclear(); // will set wp->w_lines_valid to 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00003432 return FAIL;
3433 }
3434
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003435#ifdef FEAT_PROP_POPUP
Bram Moolenaar4c063a02019-06-10 21:24:12 +02003436 // this doesn't work when there are popups visible
Bram Moolenaar33796b32019-06-08 16:01:13 +02003437 if (popup_visible)
3438 return FAIL;
3439#endif
3440
3441 // Delete all remaining lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003442 if (row + line_count >= wp->w_height)
3443 {
3444 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + wp->w_height,
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003445 wp->w_wincol + TPL_LCOL(wp), (int)W_ENDCOL(wp) + TPL_LCOL(wp),
Bram Moolenaar071d4272004-06-13 20:20:40 +00003446 ' ', ' ', 0);
3447 return OK;
3448 }
3449
3450 /*
Bram Moolenaar29ae3772017-04-30 19:39:39 +02003451 * When scrolling, the message on the command line should be cleared,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003452 * otherwise it will stay there forever.
Bram Moolenaar29ae3772017-04-30 19:39:39 +02003453 * Don't do this when avoiding to insert lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003454 */
Bram Moolenaar29ae3772017-04-30 19:39:39 +02003455 if (!no_win_do_lines_ins)
3456 clear_cmdline = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003457
3458 /*
3459 * If the terminal can set a scroll region, use that.
3460 * Always do this in a vertically split window. This will redraw from
3461 * ScreenLines[] when t_CV isn't defined. That's faster than using
3462 * win_line().
3463 * Don't use a scroll region when we are going to redraw the text, writing
Bram Moolenaar48e330a2016-02-23 14:53:34 +01003464 * a character in the lower right corner of the scroll region may cause a
3465 * scroll-up .
Bram Moolenaar071d4272004-06-13 20:20:40 +00003466 */
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003467 if (scroll_region || wp->w_width != COLUMNS_WITHOUT_TPL())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003468 {
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003469 if (scroll_region && (wp->w_width == COLUMNS_WITHOUT_TPL()
3470 || *T_CSV != NUL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003471 scroll_region_set(wp, row);
3472 if (del)
3473 retval = screen_del_lines(W_WINROW(wp) + row, 0, line_count,
Bram Moolenaarcfce7172017-08-17 20:31:48 +02003474 wp->w_height - row, FALSE, clear_attr, wp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003475 else
3476 retval = screen_ins_lines(W_WINROW(wp) + row, 0, line_count,
Bram Moolenaarcfce7172017-08-17 20:31:48 +02003477 wp->w_height - row, clear_attr, wp);
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003478 if (scroll_region && (wp->w_width == COLUMNS_WITHOUT_TPL()
3479 || *T_CSV != NUL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003480 scroll_region_reset();
3481 return retval;
3482 }
3483
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003484 if (wp->w_next != NULL && p_tf) // don't delete/insert on fast terminal
Bram Moolenaar071d4272004-06-13 20:20:40 +00003485 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003486
3487 return MAYBE;
3488}
3489
3490/*
3491 * window 'wp' and everything after it is messed up, mark it for redraw
3492 */
3493 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01003494win_rest_invalid(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003495{
Bram Moolenaar071d4272004-06-13 20:20:40 +00003496 while (wp != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003497 {
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003498 redraw_win_later(wp, UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003499 wp->w_redr_status = TRUE;
3500 wp = wp->w_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003501 }
3502 redraw_cmdline = TRUE;
3503}
3504
3505/*
3506 * The rest of the routines in this file perform screen manipulations. The
3507 * given operation is performed physically on the screen. The corresponding
3508 * change is also made to the internal screen image. In this way, the editor
3509 * anticipates the effect of editing changes on the appearance of the screen.
3510 * That way, when we call screenupdate a complete redraw isn't usually
3511 * necessary. Another advantage is that we can keep adding code to anticipate
3512 * screen changes, and in the meantime, everything still works.
3513 */
3514
3515/*
3516 * types for inserting or deleting lines
3517 */
3518#define USE_T_CAL 1
3519#define USE_T_CDL 2
3520#define USE_T_AL 3
3521#define USE_T_CE 4
3522#define USE_T_DL 5
3523#define USE_T_SR 6
3524#define USE_NL 7
3525#define USE_T_CD 8
3526#define USE_REDRAW 9
3527
3528/*
3529 * insert lines on the screen and update ScreenLines[]
Bram Moolenaar17fa2332022-04-01 19:44:47 +01003530 * "end" is the line after the scrolled part. Normally it is Rows.
3531 * When scrolling region used "off" is the offset from the top for the region.
3532 * "row" and "end" are relative to the start of the region.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003533 *
3534 * return FAIL for failure, OK for success.
3535 */
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003536 int
Bram Moolenaar05540972016-01-30 20:31:25 +01003537screen_ins_lines(
3538 int off,
3539 int row,
3540 int line_count,
3541 int end,
Bram Moolenaarcfce7172017-08-17 20:31:48 +02003542 int clear_attr,
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003543 win_T *wp) // NULL or window to use width from
Bram Moolenaar071d4272004-06-13 20:20:40 +00003544{
3545 int i;
3546 int j;
3547 unsigned temp;
3548 int cursor_row;
Bram Moolenaarbfa42462018-06-16 16:20:52 +02003549 int cursor_col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003550 int type;
3551 int result_empty;
3552 int can_ce = can_clear(T_CE);
3553
3554 /*
3555 * FAIL if
3556 * - there is no valid screen
Bram Moolenaar071d4272004-06-13 20:20:40 +00003557 * - the line count is less than one
3558 * - the line count is more than 'ttyscroll'
Bram Moolenaar17fa2332022-04-01 19:44:47 +01003559 * - "end" is more than "Rows" (safety check, should not happen)
Bram Moolenaar80dd3f92017-07-19 12:51:52 +02003560 * - redrawing for a callback and there is a modeless selection
Bram Moolenaar33796b32019-06-08 16:01:13 +02003561 * - there is a popup window
Bram Moolenaar071d4272004-06-13 20:20:40 +00003562 */
Yegappan Lakshmanane89aef32025-05-14 20:31:55 +02003563 if (!screen_valid(TRUE)
Bram Moolenaar33796b32019-06-08 16:01:13 +02003564 || line_count <= 0 || line_count > p_ttyscroll
Bram Moolenaar17fa2332022-04-01 19:44:47 +01003565 || end > Rows
Bram Moolenaar80dd3f92017-07-19 12:51:52 +02003566#ifdef FEAT_CLIPBOARD
3567 || (clip_star.state != SELECT_CLEARED
3568 && redrawing_for_callback > 0)
3569#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003570#ifdef FEAT_PROP_POPUP
Bram Moolenaar33796b32019-06-08 16:01:13 +02003571 || popup_visible
3572#endif
Bram Moolenaar80dd3f92017-07-19 12:51:52 +02003573 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003574 return FAIL;
3575
3576 /*
3577 * There are seven ways to insert lines:
3578 * 0. When in a vertically split window and t_CV isn't set, redraw the
3579 * characters from ScreenLines[].
3580 * 1. Use T_CD (clear to end of display) if it exists and the result of
3581 * the insert is just empty lines
3582 * 2. Use T_CAL (insert multiple lines) if it exists and T_AL is not
3583 * present or line_count > 1. It looks better if we do all the inserts
3584 * at once.
3585 * 3. Use T_CDL (delete multiple lines) if it exists and the result of the
3586 * insert is just empty lines and T_CE is not present or line_count >
3587 * 1.
3588 * 4. Use T_AL (insert line) if it exists.
3589 * 5. Use T_CE (erase line) if it exists and the result of the insert is
3590 * just empty lines.
3591 * 6. Use T_DL (delete line) if it exists and the result of the insert is
3592 * just empty lines.
3593 * 7. Use T_SR (scroll reverse) if it exists and inserting at row 0 and
3594 * the 'da' flag is not set or we have clear line capability.
3595 * 8. redraw the characters from ScreenLines[].
3596 *
3597 * Careful: In a hpterm scroll reverse doesn't work as expected, it moves
3598 * the scrollbar for the window. It does have insert line, use that if it
3599 * exists.
3600 */
3601 result_empty = (row + line_count >= end);
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003602 if (wp != NULL && wp->w_width != COLUMNS_WITHOUT_TPL() && *T_CSV == NUL)
Bram Moolenaar8ef69972022-04-03 13:23:22 +01003603 {
3604 // Avoid that lines are first cleared here and then redrawn, which
3605 // results in many characters updated twice. This happens with CTRL-F
3606 // in a vertically split window. With line-by-line scrolling
3607 // USE_REDRAW should be faster.
3608 if (line_count > 3)
3609 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003610 type = USE_REDRAW;
Bram Moolenaar8ef69972022-04-03 13:23:22 +01003611 }
Bram Moolenaar4033c552017-09-16 20:54:51 +02003612 else if (can_clear(T_CD) && result_empty)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003613 type = USE_T_CD;
3614 else if (*T_CAL != NUL && (line_count > 1 || *T_AL == NUL))
3615 type = USE_T_CAL;
3616 else if (*T_CDL != NUL && result_empty && (line_count > 1 || !can_ce))
3617 type = USE_T_CDL;
3618 else if (*T_AL != NUL)
3619 type = USE_T_AL;
3620 else if (can_ce && result_empty)
3621 type = USE_T_CE;
3622 else if (*T_DL != NUL && result_empty)
3623 type = USE_T_DL;
3624 else if (*T_SR != NUL && row == 0 && (*T_DA == NUL || can_ce))
3625 type = USE_T_SR;
3626 else
3627 return FAIL;
3628
3629 /*
3630 * For clearing the lines screen_del_lines() is used. This will also take
3631 * care of t_db if necessary.
3632 */
3633 if (type == USE_T_CD || type == USE_T_CDL ||
3634 type == USE_T_CE || type == USE_T_DL)
Bram Moolenaarcfce7172017-08-17 20:31:48 +02003635 return screen_del_lines(off, row, line_count, end, FALSE, 0, wp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003636
3637 /*
3638 * If text is retained below the screen, first clear or delete as many
3639 * lines at the bottom of the window as are about to be inserted so that
3640 * the deleted lines won't later surface during a screen_del_lines.
3641 */
3642 if (*T_DB)
Bram Moolenaarcfce7172017-08-17 20:31:48 +02003643 screen_del_lines(off, end - line_count, line_count, end, FALSE, 0, wp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003644
3645#ifdef FEAT_CLIPBOARD
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003646 // Remove a modeless selection when inserting lines halfway the screen
3647 // or not the full width of the screen.
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003648 if (off + row > 0 || (wp != NULL && wp->w_width != COLUMNS_WITHOUT_TPL()))
Bram Moolenaarc0885aa2012-07-10 16:49:23 +02003649 clip_clear_selection(&clip_star);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003650 else
3651 clip_scroll_selection(-line_count);
3652#endif
3653
Bram Moolenaarb3f74062020-02-26 16:16:53 +01003654#ifdef FEAT_GUI_HAIKU
3655 vim_lock_screen();
3656#endif
3657
Bram Moolenaar071d4272004-06-13 20:20:40 +00003658#ifdef FEAT_GUI
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003659 // Don't update the GUI cursor here, ScreenLines[] is invalid until the
3660 // scrolling is actually carried out.
Bram Moolenaar107abd22016-08-12 14:08:25 +02003661 gui_dont_update_cursor(row + off <= gui.cursor_row);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003662#endif
3663
Bram Moolenaarbfa42462018-06-16 16:20:52 +02003664 if (wp != NULL && wp->w_wincol != 0 && *T_CSV != NUL && *T_CCS == NUL)
3665 cursor_col = wp->w_wincol;
3666
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003667 if (*T_CCS != NUL) // cursor relative to region
Bram Moolenaar071d4272004-06-13 20:20:40 +00003668 cursor_row = row;
3669 else
3670 cursor_row = row + off;
3671
3672 /*
3673 * Shift LineOffset[] line_count down to reflect the inserted lines.
3674 * Clear the inserted lines in ScreenLines[].
3675 */
3676 row += off;
3677 end += off;
3678 for (i = 0; i < line_count; ++i)
3679 {
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003680 if (wp != NULL && wp->w_width != COLUMNS_WITHOUT_TPL())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003681 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003682 // need to copy part of a line
Bram Moolenaar071d4272004-06-13 20:20:40 +00003683 j = end - 1 - i;
3684 while ((j -= line_count) >= row)
3685 linecopy(j + line_count, j, wp);
3686 j += line_count;
3687 if (can_clear((char_u *)" "))
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003688 lineclear(LineOffset[j] + wp->w_wincol + TPL_LCOL(wp),
3689 wp->w_width, clear_attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003690 else
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003691 lineinvalid(LineOffset[j] + wp->w_wincol + TPL_LCOL(wp),
3692 wp->w_width);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003693 LineWraps[j] = FALSE;
3694 }
3695 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003696 {
3697 j = end - 1 - i;
3698 temp = LineOffset[j];
3699 while ((j -= line_count) >= row)
3700 {
3701 LineOffset[j + line_count] = LineOffset[j];
3702 LineWraps[j + line_count] = LineWraps[j];
3703 }
3704 LineOffset[j + line_count] = temp;
3705 LineWraps[j + line_count] = FALSE;
3706 if (can_clear((char_u *)" "))
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003707 lineclear(temp + TPL_LCOL(wp), COLUMNS_WITHOUT_TPL(),
3708 clear_attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003709 else
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003710 lineinvalid(temp + TPL_LCOL(wp), COLUMNS_WITHOUT_TPL());
Bram Moolenaar071d4272004-06-13 20:20:40 +00003711 }
3712 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003713
Bram Moolenaarb3f74062020-02-26 16:16:53 +01003714#ifdef FEAT_GUI_HAIKU
3715 vim_unlock_screen();
3716#endif
3717
Bram Moolenaar071d4272004-06-13 20:20:40 +00003718 screen_stop_highlight();
Bram Moolenaarbfa42462018-06-16 16:20:52 +02003719 windgoto(cursor_row, cursor_col);
Bram Moolenaarcfce7172017-08-17 20:31:48 +02003720 if (clear_attr != 0)
3721 screen_start_highlight(clear_attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003722
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003723 // redraw the characters
Bram Moolenaar071d4272004-06-13 20:20:40 +00003724 if (type == USE_REDRAW)
3725 redraw_block(row, end, wp);
Bram Moolenaar4033c552017-09-16 20:54:51 +02003726 else if (type == USE_T_CAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003727 {
3728 term_append_lines(line_count);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003729 screen_start(); // don't know where cursor is now
Bram Moolenaar071d4272004-06-13 20:20:40 +00003730 }
3731 else
3732 {
3733 for (i = 0; i < line_count; i++)
3734 {
3735 if (type == USE_T_AL)
3736 {
3737 if (i && cursor_row != 0)
Bram Moolenaarbfa42462018-06-16 16:20:52 +02003738 windgoto(cursor_row, cursor_col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003739 out_str(T_AL);
3740 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003741 else // type == USE_T_SR
Bram Moolenaar071d4272004-06-13 20:20:40 +00003742 out_str(T_SR);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003743 screen_start(); // don't know where cursor is now
Bram Moolenaar071d4272004-06-13 20:20:40 +00003744 }
3745 }
3746
3747 /*
3748 * With scroll-reverse and 'da' flag set we need to clear the lines that
3749 * have been scrolled down into the region.
3750 */
3751 if (type == USE_T_SR && *T_DA)
3752 {
3753 for (i = 0; i < line_count; ++i)
3754 {
Bram Moolenaarbfa42462018-06-16 16:20:52 +02003755 windgoto(off + i, cursor_col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003756 out_str(T_CE);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003757 screen_start(); // don't know where cursor is now
Bram Moolenaar071d4272004-06-13 20:20:40 +00003758 }
3759 }
3760
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003761#if defined(FEAT_TABPANEL)
3762 redraw_tabpanel = TRUE;
3763#endif
3764
Bram Moolenaar071d4272004-06-13 20:20:40 +00003765#ifdef FEAT_GUI
3766 gui_can_update_cursor();
3767 if (gui.in_use)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003768 out_flush(); // always flush after a scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +00003769#endif
3770 return OK;
3771}
3772
3773/*
Bram Moolenaar107abd22016-08-12 14:08:25 +02003774 * Delete lines on the screen and update ScreenLines[].
3775 * "end" is the line after the scrolled part. Normally it is Rows.
3776 * When scrolling region used "off" is the offset from the top for the region.
3777 * "row" and "end" are relative to the start of the region.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003778 *
3779 * Return OK for success, FAIL if the lines are not deleted.
3780 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003781 int
Bram Moolenaar05540972016-01-30 20:31:25 +01003782screen_del_lines(
3783 int off,
3784 int row,
3785 int line_count,
3786 int end,
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003787 int force, // even when line_count > p_ttyscroll
3788 int clear_attr, // used for clearing lines
Bram Moolenaar8ef69972022-04-03 13:23:22 +01003789 win_T *wp) // NULL or window to use width from
Bram Moolenaar071d4272004-06-13 20:20:40 +00003790{
3791 int j;
3792 int i;
3793 unsigned temp;
3794 int cursor_row;
Bram Moolenaarbfa42462018-06-16 16:20:52 +02003795 int cursor_col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003796 int cursor_end;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003797 int result_empty; // result is empty until end of region
3798 int can_delete; // deleting line codes can be used
Bram Moolenaar071d4272004-06-13 20:20:40 +00003799 int type;
3800
3801 /*
3802 * FAIL if
3803 * - there is no valid screen
3804 * - the screen has to be redrawn completely
3805 * - the line count is less than one
3806 * - the line count is more than 'ttyscroll'
Bram Moolenaar17fa2332022-04-01 19:44:47 +01003807 * - "end" is more than "Rows" (safety check, should not happen)
Bram Moolenaar80dd3f92017-07-19 12:51:52 +02003808 * - redrawing for a callback and there is a modeless selection
Bram Moolenaar071d4272004-06-13 20:20:40 +00003809 */
Bram Moolenaar17fa2332022-04-01 19:44:47 +01003810 if (!screen_valid(TRUE)
3811 || line_count <= 0
3812 || (!force && line_count > p_ttyscroll)
3813 || end > Rows
Bram Moolenaar80dd3f92017-07-19 12:51:52 +02003814#ifdef FEAT_CLIPBOARD
Bram Moolenaar17fa2332022-04-01 19:44:47 +01003815 || (clip_star.state != SELECT_CLEARED && redrawing_for_callback > 0)
Bram Moolenaar80dd3f92017-07-19 12:51:52 +02003816#endif
3817 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003818 return FAIL;
3819
3820 /*
3821 * Check if the rest of the current region will become empty.
3822 */
3823 result_empty = row + line_count >= end;
3824
3825 /*
3826 * We can delete lines only when 'db' flag not set or when 'ce' option
3827 * available.
3828 */
3829 can_delete = (*T_DB == NUL || can_clear(T_CE));
3830
3831 /*
3832 * There are six ways to delete lines:
3833 * 0. When in a vertically split window and t_CV isn't set, redraw the
3834 * characters from ScreenLines[].
3835 * 1. Use T_CD if it exists and the result is empty.
3836 * 2. Use newlines if row == 0 and count == 1 or T_CDL does not exist.
3837 * 3. Use T_CDL (delete multiple lines) if it exists and line_count > 1 or
3838 * none of the other ways work.
3839 * 4. Use T_CE (erase line) if the result is empty.
3840 * 5. Use T_DL (delete line) if it exists.
3841 * 6. redraw the characters from ScreenLines[].
3842 */
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003843 if (wp != NULL && wp->w_width != COLUMNS_WITHOUT_TPL() && *T_CSV == NUL)
Bram Moolenaar8ef69972022-04-03 13:23:22 +01003844 {
3845 // Avoid that lines are first cleared here and then redrawn, which
3846 // results in many characters updated twice. This happens with CTRL-F
3847 // in a vertically split window. With line-by-line scrolling
3848 // USE_REDRAW should be faster.
3849 if (line_count > 3)
3850 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003851 type = USE_REDRAW;
Bram Moolenaar8ef69972022-04-03 13:23:22 +01003852 }
Bram Moolenaar4033c552017-09-16 20:54:51 +02003853 else if (can_clear(T_CD) && result_empty)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003854 type = USE_T_CD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003855 else if (row == 0 && (
3856#ifndef AMIGA
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003857 // On the Amiga, somehow '\n' on the last line doesn't always scroll
3858 // up, so use delete-line command
Bram Moolenaar071d4272004-06-13 20:20:40 +00003859 line_count == 1 ||
3860#endif
3861 *T_CDL == NUL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003862 type = USE_NL;
3863 else if (*T_CDL != NUL && line_count > 1 && can_delete)
3864 type = USE_T_CDL;
3865 else if (can_clear(T_CE) && result_empty
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003866 && (wp == NULL || wp->w_width == COLUMNS_WITHOUT_TPL()))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003867 type = USE_T_CE;
3868 else if (*T_DL != NUL && can_delete)
3869 type = USE_T_DL;
3870 else if (*T_CDL != NUL && can_delete)
3871 type = USE_T_CDL;
3872 else
3873 return FAIL;
3874
3875#ifdef FEAT_CLIPBOARD
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003876 // Remove a modeless selection when deleting lines halfway the screen or
3877 // not the full width of the screen.
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003878 if (off + row > 0 || (wp != NULL && wp->w_width != COLUMNS_WITHOUT_TPL()))
Bram Moolenaarc0885aa2012-07-10 16:49:23 +02003879 clip_clear_selection(&clip_star);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003880 else
3881 clip_scroll_selection(line_count);
3882#endif
3883
Bram Moolenaar92c461e2020-04-24 22:19:00 +02003884#ifdef FEAT_GUI_HAIKU
3885 vim_lock_screen();
3886#endif
3887
Bram Moolenaar071d4272004-06-13 20:20:40 +00003888#ifdef FEAT_GUI
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003889 // Don't update the GUI cursor here, ScreenLines[] is invalid until the
3890 // scrolling is actually carried out.
Bram Moolenaar107abd22016-08-12 14:08:25 +02003891 gui_dont_update_cursor(gui.cursor_row >= row + off
3892 && gui.cursor_row < end + off);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003893#endif
3894
Bram Moolenaarbfa42462018-06-16 16:20:52 +02003895 if (wp != NULL && wp->w_wincol != 0 && *T_CSV != NUL && *T_CCS == NUL)
3896 cursor_col = wp->w_wincol;
3897
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003898 if (*T_CCS != NUL) // cursor relative to region
Bram Moolenaar071d4272004-06-13 20:20:40 +00003899 {
3900 cursor_row = row;
3901 cursor_end = end;
3902 }
3903 else
3904 {
3905 cursor_row = row + off;
3906 cursor_end = end + off;
3907 }
3908
3909 /*
3910 * Now shift LineOffset[] line_count up to reflect the deleted lines.
3911 * Clear the inserted lines in ScreenLines[].
3912 */
3913 row += off;
3914 end += off;
3915 for (i = 0; i < line_count; ++i)
3916 {
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003917 if (wp != NULL && wp->w_width != COLUMNS_WITHOUT_TPL())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003918 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003919 // need to copy part of a line
Bram Moolenaar071d4272004-06-13 20:20:40 +00003920 j = row + i;
3921 while ((j += line_count) <= end - 1)
3922 linecopy(j - line_count, j, wp);
3923 j -= line_count;
3924 if (can_clear((char_u *)" "))
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003925 lineclear(LineOffset[j] + wp->w_wincol + TPL_LCOL(wp),
3926 wp->w_width, clear_attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003927 else
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003928 lineinvalid(LineOffset[j] + wp->w_wincol + TPL_LCOL(wp),
3929 wp->w_width);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003930 LineWraps[j] = FALSE;
3931 }
3932 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003933 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003934 // whole width, moving the line pointers is faster
Bram Moolenaar071d4272004-06-13 20:20:40 +00003935 j = row + i;
3936 temp = LineOffset[j];
3937 while ((j += line_count) <= end - 1)
3938 {
3939 LineOffset[j - line_count] = LineOffset[j];
3940 LineWraps[j - line_count] = LineWraps[j];
3941 }
3942 LineOffset[j - line_count] = temp;
3943 LineWraps[j - line_count] = FALSE;
3944 if (can_clear((char_u *)" "))
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003945 lineclear(temp + TPL_LCOL(NULL), COLUMNS_WITHOUT_TPL(),
3946 clear_attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003947 else
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02003948 lineinvalid(temp + TPL_LCOL(NULL), COLUMNS_WITHOUT_TPL());
Bram Moolenaar071d4272004-06-13 20:20:40 +00003949 }
3950 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003951
Bram Moolenaarb3f74062020-02-26 16:16:53 +01003952#ifdef FEAT_GUI_HAIKU
3953 vim_unlock_screen();
3954#endif
3955
Bram Moolenaarcfce7172017-08-17 20:31:48 +02003956 if (screen_attr != clear_attr)
3957 screen_stop_highlight();
3958 if (clear_attr != 0)
3959 screen_start_highlight(clear_attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003960
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003961 // redraw the characters
Bram Moolenaar071d4272004-06-13 20:20:40 +00003962 if (type == USE_REDRAW)
3963 redraw_block(row, end, wp);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003964 else if (type == USE_T_CD) // delete the lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003965 {
Bram Moolenaarbfa42462018-06-16 16:20:52 +02003966 windgoto(cursor_row, cursor_col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003967 out_str(T_CD);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003968 screen_start(); // don't know where cursor is now
Bram Moolenaar071d4272004-06-13 20:20:40 +00003969 }
3970 else if (type == USE_T_CDL)
3971 {
Bram Moolenaarbfa42462018-06-16 16:20:52 +02003972 windgoto(cursor_row, cursor_col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003973 term_delete_lines(line_count);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003974 screen_start(); // don't know where cursor is now
Bram Moolenaar071d4272004-06-13 20:20:40 +00003975 }
3976 /*
3977 * Deleting lines at top of the screen or scroll region: Just scroll
3978 * the whole screen (scroll region) up by outputting newlines on the
3979 * last line.
3980 */
3981 else if (type == USE_NL)
3982 {
Bram Moolenaarbfa42462018-06-16 16:20:52 +02003983 windgoto(cursor_end - 1, cursor_col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003984 for (i = line_count; --i >= 0; )
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003985 out_char('\n'); // cursor will remain on same line
Bram Moolenaar071d4272004-06-13 20:20:40 +00003986 }
3987 else
3988 {
3989 for (i = line_count; --i >= 0; )
3990 {
3991 if (type == USE_T_DL)
3992 {
Bram Moolenaarbfa42462018-06-16 16:20:52 +02003993 windgoto(cursor_row, cursor_col);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003994 out_str(T_DL); // delete a line
Bram Moolenaar071d4272004-06-13 20:20:40 +00003995 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003996 else // type == USE_T_CE
Bram Moolenaar071d4272004-06-13 20:20:40 +00003997 {
Bram Moolenaarbfa42462018-06-16 16:20:52 +02003998 windgoto(cursor_row + i, cursor_col);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003999 out_str(T_CE); // erase a line
Bram Moolenaar071d4272004-06-13 20:20:40 +00004000 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004001 screen_start(); // don't know where cursor is now
Bram Moolenaar071d4272004-06-13 20:20:40 +00004002 }
4003 }
4004
4005 /*
4006 * If the 'db' flag is set, we need to clear the lines that have been
4007 * scrolled up at the bottom of the region.
4008 */
4009 if (*T_DB && (type == USE_T_DL || type == USE_T_CDL))
4010 {
4011 for (i = line_count; i > 0; --i)
4012 {
Bram Moolenaarbfa42462018-06-16 16:20:52 +02004013 windgoto(cursor_end - i, cursor_col);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004014 out_str(T_CE); // erase a line
4015 screen_start(); // don't know where cursor is now
Bram Moolenaar071d4272004-06-13 20:20:40 +00004016 }
4017 }
4018
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02004019#if defined(FEAT_TABPANEL)
4020 redraw_tabpanel = TRUE;
4021#endif
4022
Bram Moolenaar071d4272004-06-13 20:20:40 +00004023#ifdef FEAT_GUI
4024 gui_can_update_cursor();
4025 if (gui.in_use)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004026 out_flush(); // always flush after a scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +00004027#endif
4028
4029 return OK;
4030}
4031
4032/*
Bram Moolenaarcb574f42019-01-25 22:29:57 +01004033 * Return TRUE when postponing displaying the mode message: when not redrawing
4034 * or inside a mapping.
4035 */
4036 int
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +00004037skip_showmode(void)
Bram Moolenaarcb574f42019-01-25 22:29:57 +01004038{
4039 // Call char_avail() only when we are going to show something, because it
Bram Moolenaar944cc9c2022-06-27 22:17:37 +01004040 // takes a bit of time. redrawing() may also call char_avail().
Bram Moolenaarcb574f42019-01-25 22:29:57 +01004041 if (global_busy
4042 || msg_silent != 0
4043 || !redrawing()
4044 || (char_avail() && !KeyTyped))
4045 {
Bram Moolenaar4c25bd72019-04-20 23:38:07 +02004046 redraw_mode = TRUE; // show mode later
Bram Moolenaarcb574f42019-01-25 22:29:57 +01004047 return TRUE;
4048 }
4049 return FALSE;
4050}
4051
4052/*
Bram Moolenaar81226e02018-02-20 21:44:45 +01004053 * Show the current mode and ruler.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004054 *
4055 * If clear_cmdline is TRUE, clear the rest of the cmdline.
4056 * If clear_cmdline is FALSE there may be a message there that needs to be
4057 * cleared only if a mode is shown.
Bram Moolenaar4c25bd72019-04-20 23:38:07 +02004058 * If redraw_mode is TRUE show or clear the mode.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004059 * Return the length of the message (0 if no message).
4060 */
4061 int
Bram Moolenaar05540972016-01-30 20:31:25 +01004062showmode(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004063{
4064 int need_clear;
4065 int length = 0;
4066 int do_mode;
4067 int attr;
4068 int nwr_save;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004069 int sub_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004070
Bram Moolenaara2a89732022-08-31 14:46:18 +01004071 do_mode = p_smd && msg_silent == 0
Bram Moolenaar24959102022-05-07 20:01:16 +01004072 && ((State & MODE_INSERT)
Bram Moolenaar942b4542018-06-17 16:23:34 +02004073 || restart_edit != NUL
Bram Moolenaar9198de32022-08-27 21:30:03 +01004074 || VIsual_active);
Bram Moolenaar0b6d9112018-05-22 20:35:17 +02004075 if (do_mode || reg_recording != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004076 {
Bram Moolenaarcb574f42019-01-25 22:29:57 +01004077 if (skip_showmode())
4078 return 0; // show mode later
Bram Moolenaar071d4272004-06-13 20:20:40 +00004079
4080 nwr_save = need_wait_return;
4081
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004082 // wait a bit before overwriting an important message
Bram Moolenaar071d4272004-06-13 20:20:40 +00004083 check_for_delay(FALSE);
4084
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004085 // if the cmdline is more than one line high, erase top lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00004086 need_clear = clear_cmdline;
4087 if (clear_cmdline && cmdline_row < Rows - 1)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004088 msg_clr_cmdline(); // will reset clear_cmdline
Bram Moolenaar071d4272004-06-13 20:20:40 +00004089
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004090 // Position on the last line in the window, column 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00004091 msg_pos_mode();
4092 cursor_off();
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004093 attr = HL_ATTR(HLF_CM); // Highlight mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00004094 if (do_mode)
4095 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004096 msg_puts_attr("--", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004097#if defined(FEAT_XIM)
Bram Moolenaarc236c162008-07-13 17:41:49 +00004098 if (
Bram Moolenaar09092152010-08-08 16:38:42 +02004099# ifdef FEAT_GUI_GTK
Bram Moolenaarc236c162008-07-13 17:41:49 +00004100 preedit_get_status()
Bram Moolenaar09092152010-08-08 16:38:42 +02004101# else
Bram Moolenaarc236c162008-07-13 17:41:49 +00004102 im_get_status()
Bram Moolenaarc236c162008-07-13 17:41:49 +00004103# endif
Bram Moolenaar09092152010-08-08 16:38:42 +02004104 )
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004105# ifdef FEAT_GUI_GTK // most of the time, it's not XIM being used
Bram Moolenaar32526b32019-01-19 17:43:09 +01004106 msg_puts_attr(" IM", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004107# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004108 msg_puts_attr(" XIM", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004109# endif
4110#endif
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004111 // CTRL-X in Insert mode
Bram Moolenaarea389e92014-05-28 21:40:52 +02004112 if (edit_submode != NULL && !shortmess(SHM_COMPLETIONMENU))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004113 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004114 // These messages can get long, avoid a wrap in a narrow
4115 // window. Prefer showing edit_submode_extra.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004116 length = (Rows - msg_row) * Columns - 3;
4117 if (edit_submode_extra != NULL)
4118 length -= vim_strsize(edit_submode_extra);
4119 if (length > 0)
4120 {
4121 if (edit_submode_pre != NULL)
4122 length -= vim_strsize(edit_submode_pre);
4123 if (length - vim_strsize(edit_submode) > 0)
4124 {
4125 if (edit_submode_pre != NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004126 msg_puts_attr((char *)edit_submode_pre, attr);
4127 msg_puts_attr((char *)edit_submode, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004128 }
4129 if (edit_submode_extra != NULL)
4130 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004131 msg_puts_attr(" ", attr); // add a space in between
Bram Moolenaar071d4272004-06-13 20:20:40 +00004132 if ((int)edit_submode_highl < (int)HLF_COUNT)
Bram Moolenaar8820b482017-03-16 17:23:31 +01004133 sub_attr = HL_ATTR(edit_submode_highl);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004134 else
4135 sub_attr = attr;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004136 msg_puts_attr((char *)edit_submode_extra, sub_attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004137 }
4138 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004139 }
4140 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004141 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004142 if (State & VREPLACE_FLAG)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004143 msg_puts_attr(_(" VREPLACE"), attr);
Bram Moolenaar1f0bfe52018-07-29 16:09:22 +02004144 else if (State & REPLACE_FLAG)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004145 msg_puts_attr(_(" REPLACE"), attr);
Bram Moolenaar24959102022-05-07 20:01:16 +01004146 else if (State & MODE_INSERT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004147 {
4148#ifdef FEAT_RIGHTLEFT
4149 if (p_ri)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004150 msg_puts_attr(_(" REVERSE"), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004151#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01004152 msg_puts_attr(_(" INSERT"), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004153 }
Bram Moolenaar957cf672020-11-12 14:21:06 +01004154 else if (restart_edit == 'I' || restart_edit == 'i' ||
4155 restart_edit == 'a' || restart_edit == 'A')
Bram Moolenaar32526b32019-01-19 17:43:09 +01004156 msg_puts_attr(_(" (insert)"), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004157 else if (restart_edit == 'R')
Bram Moolenaar32526b32019-01-19 17:43:09 +01004158 msg_puts_attr(_(" (replace)"), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004159 else if (restart_edit == 'V')
Bram Moolenaar32526b32019-01-19 17:43:09 +01004160 msg_puts_attr(_(" (vreplace)"), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004161#ifdef FEAT_RIGHTLEFT
4162 if (p_hkmap)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004163 msg_puts_attr(_(" Hebrew"), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004164#endif
4165#ifdef FEAT_KEYMAP
Bram Moolenaar24959102022-05-07 20:01:16 +01004166 if (State & MODE_LANGMAP)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004167 {
4168# ifdef FEAT_ARABIC
4169 if (curwin->w_p_arab)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004170 msg_puts_attr(_(" Arabic"), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004171 else
4172# endif
Bram Moolenaar73ac0c42016-07-24 16:17:59 +02004173 if (get_keymap_str(curwin, (char_u *)" (%s)",
John Marriotta21240b2025-01-08 20:10:59 +01004174 NameBuff, MAXPATHL) > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004175 msg_puts_attr((char *)NameBuff, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004176 }
4177#endif
Bram Moolenaar24959102022-05-07 20:01:16 +01004178 if ((State & MODE_INSERT) && p_paste)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004179 msg_puts_attr(_(" (paste)"), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004180
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181 if (VIsual_active)
4182 {
4183 char *p;
4184
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004185 // Don't concatenate separate words to avoid translation
4186 // problems.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004187 switch ((VIsual_select ? 4 : 0)
4188 + (VIsual_mode == Ctrl_V) * 2
4189 + (VIsual_mode == 'V'))
4190 {
4191 case 0: p = N_(" VISUAL"); break;
4192 case 1: p = N_(" VISUAL LINE"); break;
4193 case 2: p = N_(" VISUAL BLOCK"); break;
4194 case 4: p = N_(" SELECT"); break;
4195 case 5: p = N_(" SELECT LINE"); break;
4196 default: p = N_(" SELECT BLOCK"); break;
4197 }
Bram Moolenaar32526b32019-01-19 17:43:09 +01004198 msg_puts_attr(_(p), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004199 }
Bram Moolenaar32526b32019-01-19 17:43:09 +01004200 msg_puts_attr(" --", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004201 }
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004202
Bram Moolenaar071d4272004-06-13 20:20:40 +00004203 need_clear = TRUE;
4204 }
Bram Moolenaar0b6d9112018-05-22 20:35:17 +02004205 if (reg_recording != 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02004206 && edit_submode == NULL) // otherwise it gets too long
Bram Moolenaar071d4272004-06-13 20:20:40 +00004207 {
Bram Moolenaara0ed84a2015-11-19 17:56:13 +01004208 recording_mode(attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004209 need_clear = TRUE;
4210 }
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004211
4212 mode_displayed = TRUE;
Bram Moolenaar4c25bd72019-04-20 23:38:07 +02004213 if (need_clear || clear_cmdline || redraw_mode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004214 msg_clr_eos();
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004215 msg_didout = FALSE; // overwrite this message
Bram Moolenaar071d4272004-06-13 20:20:40 +00004216 length = msg_col;
4217 msg_col = 0;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004218 need_wait_return = nwr_save; // never ask for hit-return for this
Bram Moolenaar071d4272004-06-13 20:20:40 +00004219 }
4220 else if (clear_cmdline && msg_silent == 0)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004221 // Clear the whole command line. Will reset "clear_cmdline".
Bram Moolenaar071d4272004-06-13 20:20:40 +00004222 msg_clr_cmdline();
Bram Moolenaar4c25bd72019-04-20 23:38:07 +02004223 else if (redraw_mode)
4224 {
4225 msg_pos_mode();
4226 msg_clr_eos();
4227 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004228
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004229 // In Visual mode the size of the selected area must be redrawn.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004230 if (VIsual_active)
4231 clear_showcmd();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004232
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004233 // If the last window has no status line, the ruler is after the mode
4234 // message and must be redrawn
Bram Moolenaar4033c552017-09-16 20:54:51 +02004235 if (redrawing() && lastwin->w_status_height == 0)
Bram Moolenaar491ac282018-06-17 14:47:55 +02004236 win_redr_ruler(lastwin, TRUE, FALSE);
Martin Tournoijba43e762022-10-13 22:12:15 +01004237
Bram Moolenaar071d4272004-06-13 20:20:40 +00004238 redraw_cmdline = FALSE;
Bram Moolenaar4c25bd72019-04-20 23:38:07 +02004239 redraw_mode = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004240 clear_cmdline = FALSE;
4241
4242 return length;
4243}
4244
4245/*
4246 * Position for a mode message.
4247 */
4248 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01004249msg_pos_mode(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004250{
4251 msg_col = 0;
4252 msg_row = Rows - 1;
4253}
4254
4255/*
4256 * Delete mode message. Used when ESC is typed which is expected to end
4257 * Insert mode (but Insert mode didn't end yet!).
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004258 * Caller should check "mode_displayed".
Bram Moolenaar071d4272004-06-13 20:20:40 +00004259 */
4260 void
Bram Moolenaar05540972016-01-30 20:31:25 +01004261unshowmode(int force)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004262{
4263 /*
Bram Moolenaare4ebd292010-01-19 17:40:46 +01004264 * Don't delete it right now, when not redrawing or inside a mapping.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004265 */
4266 if (!redrawing() || (!force && char_avail() && !KeyTyped))
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004267 redraw_cmdline = TRUE; // delete mode later
Bram Moolenaar071d4272004-06-13 20:20:40 +00004268 else
Bram Moolenaarfd773e92016-04-02 19:39:16 +02004269 clearmode();
4270}
4271
4272/*
4273 * Clear the mode message.
4274 */
4275 void
Bram Moolenaarcf089462016-06-12 21:18:43 +02004276clearmode(void)
Bram Moolenaarfd773e92016-04-02 19:39:16 +02004277{
Bram Moolenaar2abad542018-05-19 14:43:45 +02004278 int save_msg_row = msg_row;
4279 int save_msg_col = msg_col;
4280
Bram Moolenaarfd773e92016-04-02 19:39:16 +02004281 msg_pos_mode();
Bram Moolenaar0b6d9112018-05-22 20:35:17 +02004282 if (reg_recording != 0)
Bram Moolenaar8820b482017-03-16 17:23:31 +01004283 recording_mode(HL_ATTR(HLF_CM));
Bram Moolenaarfd773e92016-04-02 19:39:16 +02004284 msg_clr_eos();
Bram Moolenaar2abad542018-05-19 14:43:45 +02004285
4286 msg_col = save_msg_col;
4287 msg_row = save_msg_row;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004288}
4289
Bram Moolenaara0ed84a2015-11-19 17:56:13 +01004290 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01004291recording_mode(int attr)
Bram Moolenaara0ed84a2015-11-19 17:56:13 +01004292{
Bram Moolenaar32526b32019-01-19 17:43:09 +01004293 msg_puts_attr(_("recording"), attr);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00004294 if (shortmess(SHM_RECORDING))
4295 return;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004296
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00004297 char s[4];
4298
4299 sprintf(s, " @%c", reg_recording);
4300 msg_puts_attr(s, attr);
Bram Moolenaara0ed84a2015-11-19 17:56:13 +01004301}
4302
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004303/*
4304 * Draw the tab pages line at the top of the Vim window.
4305 */
Bram Moolenaare12bab32019-01-08 22:02:56 +01004306 void
Bram Moolenaar05540972016-01-30 20:31:25 +01004307draw_tabline(void)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004308{
4309 int tabcount = 0;
4310 tabpage_T *tp;
4311 int tabwidth;
4312 int col = 0;
Bram Moolenaar997fb4b2006-02-17 21:53:23 +00004313 int scol = 0;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004314 int attr;
4315 win_T *wp;
Bram Moolenaarf740b292006-02-16 22:11:02 +00004316 win_T *cwp;
4317 int wincount;
4318 int modified;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004319 int c;
4320 int len;
Bram Moolenaar8820b482017-03-16 17:23:31 +01004321 int attr_sel = HL_ATTR(HLF_TPS);
4322 int attr_nosel = HL_ATTR(HLF_TP);
4323 int attr_fill = HL_ATTR(HLF_TPF);
Bram Moolenaar997fb4b2006-02-17 21:53:23 +00004324 char_u *p;
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00004325 int room;
4326 int use_sep_chars = (t_colors < 8
4327#ifdef FEAT_GUI
4328 && !gui.in_use
4329#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02004330#ifdef FEAT_TERMGUICOLORS
4331 && !p_tgc
Bram Moolenaar8a633e32016-04-21 21:10:14 +02004332#endif
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00004333 );
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004334
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02004335#if defined(FEAT_TABPANEL)
4336 col = TPL_LCOL(NULL);
4337#endif
4338
Bram Moolenaarc695cec2017-01-08 20:00:04 +01004339 if (ScreenLines == NULL)
4340 return;
Bram Moolenaar997fb4b2006-02-17 21:53:23 +00004341 redraw_tabline = FALSE;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004342
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004343#ifdef FEAT_GUI_TABLINE
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004344 // Take care of a GUI tabline.
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004345 if (gui_use_tabline())
4346 {
4347 gui_update_tabline();
4348 return;
4349 }
4350#endif
4351
4352 if (tabline_height() < 1)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004353 return;
4354
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00004355#if defined(FEAT_STL_OPT)
Bram Moolenaarca57ab52019-04-13 14:53:16 +02004356 clear_TabPageIdxs();
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004357
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004358 // Use the 'tabline' option if it's set.
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00004359 if (*p_tal != NUL)
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00004360 win_redr_custom(NULL, FALSE);
Bram Moolenaar238a5642006-02-21 22:12:05 +00004361 else
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00004362#endif
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004363 {
Bram Moolenaar29323592016-07-24 22:04:11 +02004364 FOR_ALL_TABPAGES(tp)
Bram Moolenaar238a5642006-02-21 22:12:05 +00004365 ++tabcount;
Bram Moolenaarf740b292006-02-16 22:11:02 +00004366
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02004367 tabwidth = (COLUMNS_WITHOUT_TPL() - 1 + tabcount / 2) / tabcount;
Bram Moolenaar238a5642006-02-21 22:12:05 +00004368 if (tabwidth < 6)
4369 tabwidth = 6;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004370
Bram Moolenaar238a5642006-02-21 22:12:05 +00004371 attr = attr_nosel;
4372 tabcount = 0;
Bram Moolenaarfd2ac762006-03-01 22:09:21 +00004373 for (tp = first_tabpage; tp != NULL && col < Columns - 4;
4374 tp = tp->tp_next)
Bram Moolenaarf740b292006-02-16 22:11:02 +00004375 {
Bram Moolenaar238a5642006-02-21 22:12:05 +00004376 scol = col;
Bram Moolenaarf740b292006-02-16 22:11:02 +00004377
Bram Moolenaar238a5642006-02-21 22:12:05 +00004378 if (tp->tp_topframe == topframe)
4379 attr = attr_sel;
4380 if (use_sep_chars && col > 0)
4381 screen_putchar('|', 0, col++, attr);
4382
4383 if (tp->tp_topframe != topframe)
4384 attr = attr_nosel;
4385
4386 screen_putchar(' ', 0, col++, attr);
4387
4388 if (tp == curtab)
Bram Moolenaarf740b292006-02-16 22:11:02 +00004389 {
Bram Moolenaar238a5642006-02-21 22:12:05 +00004390 cwp = curwin;
4391 wp = firstwin;
4392 }
4393 else
4394 {
4395 cwp = tp->tp_curwin;
4396 wp = tp->tp_firstwin;
4397 }
4398
4399 modified = FALSE;
4400 for (wincount = 0; wp != NULL; wp = wp->w_next, ++wincount)
4401 if (bufIsChanged(wp->w_buffer))
4402 modified = TRUE;
4403 if (modified || wincount > 1)
4404 {
4405 if (wincount > 1)
4406 {
John Marriottc15de972025-01-17 13:54:49 +01004407 len = vim_snprintf((char *)NameBuff, MAXPATHL, "%d", wincount);
Bram Moolenaarfd2ac762006-03-01 22:09:21 +00004408 if (col + len >= Columns - 3)
4409 break;
Bram Moolenaar238a5642006-02-21 22:12:05 +00004410 screen_puts_len(NameBuff, len, 0, col,
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00004411#if defined(FEAT_SYN_HL)
Bram Moolenaar8820b482017-03-16 17:23:31 +01004412 hl_combine_attr(attr, HL_ATTR(HLF_T))
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00004413#else
Bram Moolenaare0f14822014-08-06 13:20:56 +02004414 attr
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00004415#endif
Bram Moolenaar238a5642006-02-21 22:12:05 +00004416 );
4417 col += len;
4418 }
4419 if (modified)
4420 screen_puts_len((char_u *)"+", 1, 0, col++, attr);
4421 screen_putchar(' ', 0, col++, attr);
4422 }
4423
4424 room = scol - col + tabwidth - 1;
4425 if (room > 0)
4426 {
John Marriottc15de972025-01-17 13:54:49 +01004427 int n;
4428
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004429 // Get buffer name in NameBuff[]
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004430 get_trans_bufname(cwp->w_buffer);
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004431 shorten_dir(NameBuff);
Bram Moolenaar238a5642006-02-21 22:12:05 +00004432 len = vim_strsize(NameBuff);
4433 p = NameBuff;
Bram Moolenaar238a5642006-02-21 22:12:05 +00004434 if (has_mbyte)
4435 while (len > room)
4436 {
4437 len -= ptr2cells(p);
Bram Moolenaar91acfff2017-03-12 19:22:36 +01004438 MB_PTR_ADV(p);
Bram Moolenaar238a5642006-02-21 22:12:05 +00004439 }
Bram Moolenaara12a1612019-01-24 16:39:02 +01004440 else if (len > room)
Bram Moolenaar238a5642006-02-21 22:12:05 +00004441 {
4442 p += len - room;
4443 len = room;
4444 }
John Marriottc15de972025-01-17 13:54:49 +01004445 n = Columns - col - 1;
4446 if (len > n)
4447 len = n;
Bram Moolenaar238a5642006-02-21 22:12:05 +00004448
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004449 screen_puts_len(p, (int)STRLEN(p), 0, col, attr);
Bram Moolenaarf740b292006-02-16 22:11:02 +00004450 col += len;
4451 }
Bram Moolenaarf740b292006-02-16 22:11:02 +00004452 screen_putchar(' ', 0, col++, attr);
Bram Moolenaar238a5642006-02-21 22:12:05 +00004453
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004454 // Store the tab page number in TabPageIdxs[], so that
4455 // jump_to_mouse() knows where each one is.
Bram Moolenaar238a5642006-02-21 22:12:05 +00004456 ++tabcount;
4457 while (scol < col)
4458 TabPageIdxs[scol++] = tabcount;
Bram Moolenaarf740b292006-02-16 22:11:02 +00004459 }
4460
Bram Moolenaar238a5642006-02-21 22:12:05 +00004461 if (use_sep_chars)
4462 c = '_';
4463 else
4464 c = ' ';
4465 screen_fill(0, 1, col, (int)Columns, c, c, attr_fill);
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004466
Luuk van Baalba936f62022-12-15 13:15:39 +00004467 // Draw the 'showcmd' information if 'showcmdloc' == "tabline".
4468 if (p_sc && *p_sloc == 't')
4469 {
John Marriottc15de972025-01-17 13:54:49 +01004470 int n = (int)Columns - col - (tabcount > 1) * 3;
4471 int width = MIN(10, n);
Luuk van Baalba936f62022-12-15 13:15:39 +00004472
4473 if (width > 0)
4474 screen_puts_len(showcmd_buf, width, 0, (int)Columns
4475 - width - (tabcount > 1) * 2, attr_nosel);
4476 }
4477
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004478 // Put an "X" for closing the current tab if there are several.
Luuk van Baalba936f62022-12-15 13:15:39 +00004479 if (tabcount > 1)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004480 {
4481 screen_putchar('X', 0, (int)Columns - 1, attr_nosel);
4482 TabPageIdxs[Columns - 1] = -999;
4483 }
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004484 }
Bram Moolenaarb21e5842006-04-16 18:30:08 +00004485
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004486 // Reset the flag here again, in case evaluating 'tabline' causes it to be
4487 // set.
Bram Moolenaarb21e5842006-04-16 18:30:08 +00004488 redraw_tabline = FALSE;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004489}
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004490
4491/*
4492 * Get buffer name for "buf" into NameBuff[].
4493 * Takes care of special buffer names and translates special characters.
4494 */
4495 void
Bram Moolenaar05540972016-01-30 20:31:25 +01004496get_trans_bufname(buf_T *buf)
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004497{
4498 if (buf_spname(buf) != NULL)
Bram Moolenaare1704ba2012-10-03 18:25:00 +02004499 vim_strncpy(NameBuff, buf_spname(buf), MAXPATHL - 1);
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004500 else
4501 home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE);
4502 trans_characters(NameBuff, MAXPATHL);
4503}
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004504
Bram Moolenaar071d4272004-06-13 20:20:40 +00004505/*
4506 * Get the character to use in a status line. Get its attributes in "*attr".
4507 */
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02004508 int
Bram Moolenaar3633cf52017-07-31 22:29:35 +02004509fillchar_status(int *attr, win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004510{
4511 int fill;
Bram Moolenaar3633cf52017-07-31 22:29:35 +02004512
4513#ifdef FEAT_TERMINAL
4514 if (bt_terminal(wp->w_buffer))
4515 {
Bram Moolenaar3633cf52017-07-31 22:29:35 +02004516 if (wp == curwin)
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02004517 {
4518 *attr = HL_ATTR(HLF_ST);
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01004519 fill = wp->w_fill_chars.stl;
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02004520 }
Bram Moolenaar3633cf52017-07-31 22:29:35 +02004521 else
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02004522 {
4523 *attr = HL_ATTR(HLF_STNC);
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01004524 fill = wp->w_fill_chars.stlnc;
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02004525 }
Bram Moolenaar3633cf52017-07-31 22:29:35 +02004526 }
4527 else
4528#endif
4529 if (wp == curwin)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004530 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004531 *attr = HL_ATTR(HLF_S);
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01004532 fill = wp->w_fill_chars.stl;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004533 }
4534 else
4535 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004536 *attr = HL_ATTR(HLF_SNC);
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01004537 fill = wp->w_fill_chars.stlnc;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004538 }
Christian Brabandt6a650bf2023-11-08 21:23:29 +01004539 return fill;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004540}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004541
Bram Moolenaar071d4272004-06-13 20:20:40 +00004542/*
4543 * Get the character to use in a separator between vertically split windows.
4544 * Get its attributes in "*attr".
4545 */
Bram Moolenaar7528d1f2019-09-19 23:06:20 +02004546 int
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01004547fillchar_vsep(int *attr, win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004548{
Bram Moolenaar8820b482017-03-16 17:23:31 +01004549 *attr = HL_ATTR(HLF_C);
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01004550 if (*attr == 0 && wp->w_fill_chars.vert == ' ')
Bram Moolenaar071d4272004-06-13 20:20:40 +00004551 return '|';
4552 else
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01004553 return wp->w_fill_chars.vert;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004554}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004555
4556/*
4557 * Return TRUE if redrawing should currently be done.
4558 */
4559 int
Bram Moolenaar05540972016-01-30 20:31:25 +01004560redrawing(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004561{
Bram Moolenaareb992cb2017-03-09 18:20:16 +01004562#ifdef FEAT_EVAL
4563 if (disable_redraw_for_testing)
4564 return 0;
4565 else
4566#endif
Bram Moolenaar79cdf022023-05-20 14:07:00 +01004567 return ((RedrawingDisabled == 0
Bram Moolenaared5a9d62018-09-06 13:14:43 +02004568#ifdef FEAT_EVAL
4569 || ignore_redraw_flag_for_testing
4570#endif
4571 ) && !(p_lz && char_avail() && !KeyTyped && !do_redraw));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004572}
4573
4574/*
4575 * Return TRUE if printing messages should currently be done.
4576 */
4577 int
Bram Moolenaar05540972016-01-30 20:31:25 +01004578messaging(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004579{
Bram Moolenaara2a89732022-08-31 14:46:18 +01004580 return (!(p_lz && char_avail() && !KeyTyped));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004581}
4582
Bram Moolenaare677df82019-09-02 22:31:11 +02004583/*
4584 * Compute columns for ruler and shown command. 'sc_col' is also used to
4585 * decide what the maximum length of a message on the status line can be.
4586 * If there is a status line for the last window, 'sc_col' is independent
4587 * of 'ru_col'.
4588 */
4589
4590#define COL_RULER 17 // columns needed by standard ruler
4591
4592 void
4593comp_col(void)
4594{
Sean Dewar876f5fb2023-08-17 22:40:05 +02004595 int last_has_status = last_stl_height(FALSE) > 0;
Bram Moolenaare677df82019-09-02 22:31:11 +02004596
4597 sc_col = 0;
4598 ru_col = 0;
4599 if (p_ru)
4600 {
Martin Tournoijba43e762022-10-13 22:12:15 +01004601#ifdef FEAT_STL_OPT
Bram Moolenaare677df82019-09-02 22:31:11 +02004602 ru_col = (ru_wid ? ru_wid : COL_RULER) + 1;
Martin Tournoijba43e762022-10-13 22:12:15 +01004603#else
Bram Moolenaare677df82019-09-02 22:31:11 +02004604 ru_col = COL_RULER + 1;
Martin Tournoijba43e762022-10-13 22:12:15 +01004605#endif
Bram Moolenaare677df82019-09-02 22:31:11 +02004606 // no last status line, adjust sc_col
4607 if (!last_has_status)
4608 sc_col = ru_col;
4609 }
Sam-programs062141b2024-02-29 17:40:29 +01004610 if (p_sc && *p_sloc == 'l')
Bram Moolenaare677df82019-09-02 22:31:11 +02004611 {
4612 sc_col += SHOWCMD_COLS;
4613 if (!p_ru || last_has_status) // no need for separating space
4614 ++sc_col;
4615 }
4616 sc_col = Columns - sc_col;
4617 ru_col = Columns - ru_col;
4618 if (sc_col <= 0) // screen too narrow, will become a mess
4619 sc_col = 1;
4620 if (ru_col <= 0)
4621 ru_col = 1;
Bram Moolenaare677df82019-09-02 22:31:11 +02004622#ifdef FEAT_EVAL
4623 set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
4624#endif
4625}
4626
Bram Moolenaar592e0a22004-07-03 16:05:59 +00004627#if defined(FEAT_LINEBREAK) || defined(PROTO)
4628/*
Bram Moolenaar64486672010-05-16 15:46:46 +02004629 * Return the width of the 'number' and 'relativenumber' column.
4630 * Caller may need to check if 'number' or 'relativenumber' is set.
Bram Moolenaar592e0a22004-07-03 16:05:59 +00004631 * Otherwise it depends on 'numberwidth' and the line count.
4632 */
4633 int
Bram Moolenaar05540972016-01-30 20:31:25 +01004634number_width(win_T *wp)
Bram Moolenaar592e0a22004-07-03 16:05:59 +00004635{
4636 int n;
4637 linenr_T lnum;
4638
Bram Moolenaar5ebc09b2013-06-04 22:13:50 +02004639 if (wp->w_p_rnu && !wp->w_p_nu)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004640 // cursor line shows "0"
Bram Moolenaar5ebc09b2013-06-04 22:13:50 +02004641 lnum = wp->w_height;
4642 else
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004643 // cursor line shows absolute line number
Bram Moolenaar5ebc09b2013-06-04 22:13:50 +02004644 lnum = wp->w_buffer->b_ml.ml_line_count;
Bram Moolenaar64486672010-05-16 15:46:46 +02004645
Bram Moolenaar6b314672015-03-20 15:42:10 +01004646 if (lnum == wp->w_nrwidth_line_count && wp->w_nuw_cached == wp->w_p_nuw)
Bram Moolenaar592e0a22004-07-03 16:05:59 +00004647 return wp->w_nrwidth_width;
4648 wp->w_nrwidth_line_count = lnum;
4649
4650 n = 0;
4651 do
4652 {
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00004653 lnum /= 10;
4654 ++n;
Bram Moolenaar592e0a22004-07-03 16:05:59 +00004655 } while (lnum > 0);
4656
Bram Moolenaar63d9e732019-12-05 21:10:38 +01004657 // 'numberwidth' gives the minimal width plus one
Bram Moolenaar592e0a22004-07-03 16:05:59 +00004658 if (n < wp->w_p_nuw - 1)
4659 n = wp->w_p_nuw - 1;
4660
Bram Moolenaare4b407f2019-07-04 11:59:28 +02004661# ifdef FEAT_SIGNS
4662 // If 'signcolumn' is set to 'number' and there is a sign to display, then
4663 // the minimal width for the number column is 2.
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01004664 if (n < 2 && get_first_valid_sign(wp) != NULL
Bram Moolenaare4b407f2019-07-04 11:59:28 +02004665 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
4666 n = 2;
4667# endif
4668
Bram Moolenaar592e0a22004-07-03 16:05:59 +00004669 wp->w_nrwidth_width = n;
Bram Moolenaar6b314672015-03-20 15:42:10 +01004670 wp->w_nuw_cached = wp->w_p_nuw;
Bram Moolenaar592e0a22004-07-03 16:05:59 +00004671 return n;
4672}
4673#endif
Bram Moolenaar9750bb12012-12-05 16:10:42 +01004674
Bram Moolenaar113e1072019-01-20 15:30:40 +01004675#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar9750bb12012-12-05 16:10:42 +01004676/*
4677 * Return the current cursor column. This is the actual position on the
4678 * screen. First column is 0.
4679 */
4680 int
Bram Moolenaar05540972016-01-30 20:31:25 +01004681screen_screencol(void)
Bram Moolenaar9750bb12012-12-05 16:10:42 +01004682{
4683 return screen_cur_col;
4684}
4685
4686/*
4687 * Return the current cursor row. This is the actual position on the screen.
4688 * First row is 0.
4689 */
4690 int
Bram Moolenaar05540972016-01-30 20:31:25 +01004691screen_screenrow(void)
Bram Moolenaar9750bb12012-12-05 16:10:42 +01004692{
4693 return screen_cur_row;
4694}
Bram Moolenaar113e1072019-01-20 15:30:40 +01004695#endif
Bram Moolenaare677df82019-09-02 22:31:11 +02004696
4697/*
Bram Moolenaar93ff6722021-10-16 17:51:40 +01004698 * Calls mb_ptr2char_adv(p) and returns the character.
4699 * If "p" starts with "\x", "\u" or "\U" the hex or unicode value is used.
4700 */
4701 static int
4702get_encoded_char_adv(char_u **p)
4703{
4704 char_u *s = *p;
4705
4706 if (s[0] == '\\' && (s[1] == 'x' || s[1] == 'u' || s[1] == 'U'))
4707 {
4708 varnumber_T num = 0;
4709 int bytes;
4710 int n;
4711
4712 for (bytes = s[1] == 'x' ? 1 : s[1] == 'u' ? 2 : 4; bytes > 0; --bytes)
4713 {
4714 *p += 2;
4715 n = hexhex2nr(*p);
4716 if (n < 0)
4717 return 0;
4718 num = num * 256 + n;
4719 }
4720 *p += 2;
4721 return num;
4722 }
4723 return mb_ptr2char_adv(p);
4724}
4725
Yee Cheng Chin900894b2023-09-29 20:42:32 +02004726struct charstab
4727{
4728 int *cp;
John Marriottc15de972025-01-17 13:54:49 +01004729 string_T name;
Yee Cheng Chin900894b2023-09-29 20:42:32 +02004730};
John Marriottc15de972025-01-17 13:54:49 +01004731
4732#define CHARSTAB_ENTRY(cp, name) \
4733 {(cp), {(char_u *)(name), STRLEN_LITERAL(name)}}
4734
Yee Cheng Chin900894b2023-09-29 20:42:32 +02004735static fill_chars_T fill_chars;
4736static struct charstab filltab[] =
4737{
John Marriottc15de972025-01-17 13:54:49 +01004738 CHARSTAB_ENTRY(&fill_chars.stl, "stl"),
4739 CHARSTAB_ENTRY(&fill_chars.stlnc, "stlnc"),
4740 CHARSTAB_ENTRY(&fill_chars.vert, "vert"),
4741 CHARSTAB_ENTRY(&fill_chars.fold, "fold"),
4742 CHARSTAB_ENTRY(&fill_chars.foldopen, "foldopen"),
4743 CHARSTAB_ENTRY(&fill_chars.foldclosed, "foldclose"),
4744 CHARSTAB_ENTRY(&fill_chars.foldsep, "foldsep"),
4745 CHARSTAB_ENTRY(&fill_chars.diff, "diff"),
4746 CHARSTAB_ENTRY(&fill_chars.eob, "eob"),
glepnirb8762042025-04-07 20:57:14 +02004747 CHARSTAB_ENTRY(&fill_chars.lastline, "lastline"),
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02004748#if defined(FEAT_TABPANEL)
4749 CHARSTAB_ENTRY(&fill_chars.tpl_vert, "tpl_vert"),
4750#endif
glepnirb8762042025-04-07 20:57:14 +02004751 CHARSTAB_ENTRY(&fill_chars.trunc, "trunc"),
glepnird4dbf822025-04-12 18:35:34 +02004752 CHARSTAB_ENTRY(&fill_chars.truncrl, "truncrl"),
Yee Cheng Chin900894b2023-09-29 20:42:32 +02004753};
4754static lcs_chars_T lcs_chars;
4755static struct charstab lcstab[] =
4756{
John Marriottc15de972025-01-17 13:54:49 +01004757 CHARSTAB_ENTRY(&lcs_chars.eol, "eol"),
4758 CHARSTAB_ENTRY(&lcs_chars.ext, "extends"),
4759 CHARSTAB_ENTRY(&lcs_chars.nbsp, "nbsp"),
4760 CHARSTAB_ENTRY(&lcs_chars.prec, "precedes"),
4761 CHARSTAB_ENTRY(&lcs_chars.space, "space"),
4762 CHARSTAB_ENTRY(&lcs_chars.tab2, "tab"),
4763 CHARSTAB_ENTRY(&lcs_chars.trail, "trail"),
4764 CHARSTAB_ENTRY(&lcs_chars.lead, "lead"),
Yee Cheng Chin900894b2023-09-29 20:42:32 +02004765#ifdef FEAT_CONCEAL
John Marriottc15de972025-01-17 13:54:49 +01004766 CHARSTAB_ENTRY(&lcs_chars.conceal, "conceal"),
Yee Cheng Chin900894b2023-09-29 20:42:32 +02004767#else
John Marriottc15de972025-01-17 13:54:49 +01004768 CHARSTAB_ENTRY(NULL, "conceal"),
Yee Cheng Chin900894b2023-09-29 20:42:32 +02004769#endif
John Marriottc15de972025-01-17 13:54:49 +01004770 CHARSTAB_ENTRY(NULL, "multispace"),
4771 CHARSTAB_ENTRY(NULL, "leadmultispace")
Yee Cheng Chin900894b2023-09-29 20:42:32 +02004772};
4773
zeertzjq6a8d2e12024-01-17 20:54:49 +01004774 static char *
John Marriottc15de972025-01-17 13:54:49 +01004775field_value_err(char *errbuf, size_t errbuflen, char *fmt, char_u *field)
zeertzjq6a8d2e12024-01-17 20:54:49 +01004776{
4777 if (errbuf == NULL)
4778 return "";
4779 vim_snprintf(errbuf, errbuflen, _(fmt), field);
4780 return errbuf;
4781}
4782
Bram Moolenaar93ff6722021-10-16 17:51:40 +01004783/*
Bram Moolenaare677df82019-09-02 22:31:11 +02004784 * Handle setting 'listchars' or 'fillchars'.
Yegappan Lakshmananc727b192023-03-03 12:26:15 +00004785 * "value" points to either the global or the window-local value.
zeertzjqd619d6a2023-05-08 15:56:21 +01004786 * "is_listchars" is TRUE for "listchars" and FALSE for "fillchars".
Bram Moolenaarb67f0c82022-07-04 21:03:36 +01004787 * When "apply" is FALSE do not store the flags, only check for errors.
Bram Moolenaareed9d462021-02-15 20:38:25 +01004788 * Assume monocell characters.
Bram Moolenaare677df82019-09-02 22:31:11 +02004789 * Returns error message, NULL if it's OK.
4790 */
Yegappan Lakshmananc6ff21e2023-03-02 14:46:48 +00004791 static char *
zeertzjq6a8d2e12024-01-17 20:54:49 +01004792set_chars_option(win_T *wp, char_u *value, int is_listchars, int apply,
4793 char *errbuf, size_t errbuflen)
Bram Moolenaare677df82019-09-02 22:31:11 +02004794{
John Marriottc15de972025-01-17 13:54:49 +01004795 int round, i, entries;
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01004796 char_u *p, *s;
4797 int c1 = 0, c2 = 0, c3 = 0;
4798 char_u *last_multispace = NULL; // Last occurrence of "multispace:"
4799 char_u *last_lmultispace = NULL; // Last occurrence of "leadmultispace:"
4800 int multispace_len = 0; // Length of lcs-multispace string
4801 int lead_multispace_len = 0; // Length of lcs-leadmultispace string
Bram Moolenaarb67f0c82022-07-04 21:03:36 +01004802
Bram Moolenaarb67f0c82022-07-04 21:03:36 +01004803 struct charstab *tab;
4804
Bram Moolenaarb67f0c82022-07-04 21:03:36 +01004805 if (is_listchars)
Bram Moolenaare677df82019-09-02 22:31:11 +02004806 {
4807 tab = lcstab;
Bram Moolenaar333bd562021-02-16 22:22:13 +01004808 CLEAR_FIELD(lcs_chars);
K.Takataeeec2542021-06-02 13:28:16 +02004809 entries = ARRAY_LENGTH(lcstab);
zeertzjqd619d6a2023-05-08 15:56:21 +01004810 if (wp->w_p_lcs[0] == NUL)
Yegappan Lakshmananc6ff21e2023-03-02 14:46:48 +00004811 value = p_lcs; // local value is empty, use the global value
Bram Moolenaare677df82019-09-02 22:31:11 +02004812 }
4813 else
4814 {
4815 tab = filltab;
K.Takataeeec2542021-06-02 13:28:16 +02004816 entries = ARRAY_LENGTH(filltab);
zeertzjqd619d6a2023-05-08 15:56:21 +01004817 if (wp->w_p_fcs[0] == NUL)
John Marriottc15de972025-01-17 13:54:49 +01004818 value = p_fcs; // local value is empty, use the global value
Bram Moolenaare677df82019-09-02 22:31:11 +02004819 }
4820
4821 // first round: check for valid value, second round: assign values
zeertzjq00624a22023-11-23 20:47:16 +01004822 for (round = 0; round <= (apply ? 1 : 0); ++round)
Bram Moolenaare677df82019-09-02 22:31:11 +02004823 {
4824 if (round > 0)
4825 {
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01004826 // After checking that the value is valid: set defaults.
Bram Moolenaarb67f0c82022-07-04 21:03:36 +01004827 if (is_listchars)
Bram Moolenaare677df82019-09-02 22:31:11 +02004828 {
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01004829 for (i = 0; i < entries; ++i)
4830 if (tab[i].cp != NULL)
4831 *(tab[i].cp) = NUL;
Bram Moolenaar333bd562021-02-16 22:22:13 +01004832 lcs_chars.tab1 = NUL;
4833 lcs_chars.tab3 = NUL;
zeertzjqb5f08012022-06-09 13:55:28 +01004834
Mike Williamsf5785cf2021-09-13 22:17:38 +02004835 if (multispace_len > 0)
zeertzjqf14b8ba2021-09-10 16:58:30 +02004836 {
4837 lcs_chars.multispace = ALLOC_MULT(int, multispace_len + 1);
Bram Moolenaarb67f0c82022-07-04 21:03:36 +01004838 if (lcs_chars.multispace != NULL)
4839 lcs_chars.multispace[multispace_len] = NUL;
zeertzjqf14b8ba2021-09-10 16:58:30 +02004840 }
4841 else
4842 lcs_chars.multispace = NULL;
Bram Moolenaaraca12fd2022-06-07 10:16:15 +01004843
4844 if (lead_multispace_len > 0)
4845 {
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01004846 lcs_chars.leadmultispace =
4847 ALLOC_MULT(int, lead_multispace_len + 1);
John Marriottc15de972025-01-17 13:54:49 +01004848 if (lcs_chars.leadmultispace != NULL)
4849 lcs_chars.leadmultispace[lead_multispace_len] = NUL;
Bram Moolenaaraca12fd2022-06-07 10:16:15 +01004850 }
4851 else
4852 lcs_chars.leadmultispace = NULL;
Bram Moolenaare677df82019-09-02 22:31:11 +02004853 }
4854 else
Bram Moolenaara98f8a22021-02-13 18:24:23 +01004855 {
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01004856 fill_chars.stl = ' ';
4857 fill_chars.stlnc = ' ';
4858 fill_chars.vert = ' ';
4859 fill_chars.fold = '-';
4860 fill_chars.foldopen = '-';
4861 fill_chars.foldclosed = '+';
4862 fill_chars.foldsep = '|';
4863 fill_chars.diff = '-';
4864 fill_chars.eob = '~';
Bram Moolenaar4ba5f1d2022-10-04 14:36:29 +01004865 fill_chars.lastline = '@';
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +02004866#if defined(FEAT_TABPANEL)
4867 fill_chars.tpl_vert = '|';
4868#endif
glepnirb8762042025-04-07 20:57:14 +02004869 fill_chars.trunc = '>';
glepnird4dbf822025-04-12 18:35:34 +02004870 fill_chars.truncrl = '<';
Bram Moolenaara98f8a22021-02-13 18:24:23 +01004871 }
Bram Moolenaare677df82019-09-02 22:31:11 +02004872 }
Bram Moolenaarb67f0c82022-07-04 21:03:36 +01004873 p = value;
Bram Moolenaare677df82019-09-02 22:31:11 +02004874 while (*p)
4875 {
4876 for (i = 0; i < entries; ++i)
4877 {
John Marriottc15de972025-01-17 13:54:49 +01004878 if (!(STRNCMP(p, tab[i].name.string, tab[i].name.length) == 0 && p[tab[i].name.length] == ':'))
zeertzjq1f025b02023-09-30 12:43:07 +02004879 continue;
Bram Moolenaare677df82019-09-02 22:31:11 +02004880
John Marriottc15de972025-01-17 13:54:49 +01004881 s = p + tab[i].name.length + 1;
glepnirb8762042025-04-07 20:57:14 +02004882
John Marriottc15de972025-01-17 13:54:49 +01004883 if (is_listchars && STRCMP(tab[i].name.string, "multispace") == 0)
zeertzjqf14b8ba2021-09-10 16:58:30 +02004884 {
zeertzjqf14b8ba2021-09-10 16:58:30 +02004885 if (round == 0)
4886 {
4887 // Get length of lcs-multispace string in first round
4888 last_multispace = p;
4889 multispace_len = 0;
4890 while (*s != NUL && *s != ',')
4891 {
Bram Moolenaar93ff6722021-10-16 17:51:40 +01004892 c1 = get_encoded_char_adv(&s);
zeertzjq60618c82021-12-18 15:32:46 +00004893 if (char2cells(c1) > 1)
zeertzjq6a8d2e12024-01-17 20:54:49 +01004894 return field_value_err(errbuf, errbuflen,
4895 e_wrong_character_width_for_field_str,
John Marriottc15de972025-01-17 13:54:49 +01004896 tab[i].name.string);
zeertzjqf14b8ba2021-09-10 16:58:30 +02004897 ++multispace_len;
4898 }
4899 if (multispace_len == 0)
4900 // lcs-multispace cannot be an empty string
zeertzjq6a8d2e12024-01-17 20:54:49 +01004901 return field_value_err(errbuf, errbuflen,
4902 e_wrong_number_of_characters_for_field_str,
John Marriottc15de972025-01-17 13:54:49 +01004903 tab[i].name.string);
zeertzjqf14b8ba2021-09-10 16:58:30 +02004904 }
4905 else
4906 {
4907 int multispace_pos = 0;
Mike Williamsf5785cf2021-09-13 22:17:38 +02004908
zeertzjqf14b8ba2021-09-10 16:58:30 +02004909 while (*s != NUL && *s != ',')
4910 {
Bram Moolenaar93ff6722021-10-16 17:51:40 +01004911 c1 = get_encoded_char_adv(&s);
John Marriottc15de972025-01-17 13:54:49 +01004912 if (p == last_multispace && lcs_chars.multispace != NULL)
zeertzjqf14b8ba2021-09-10 16:58:30 +02004913 lcs_chars.multispace[multispace_pos++] = c1;
4914 }
zeertzjqf14b8ba2021-09-10 16:58:30 +02004915 }
glepnirb8762042025-04-07 20:57:14 +02004916 p = s;
zeertzjq1f025b02023-09-30 12:43:07 +02004917 break;
zeertzjqf14b8ba2021-09-10 16:58:30 +02004918 }
Bram Moolenaaraca12fd2022-06-07 10:16:15 +01004919
John Marriottc15de972025-01-17 13:54:49 +01004920 if (is_listchars && STRCMP(tab[i].name.string, "leadmultispace") == 0)
Bram Moolenaaraca12fd2022-06-07 10:16:15 +01004921 {
Bram Moolenaaraca12fd2022-06-07 10:16:15 +01004922 if (round == 0)
4923 {
glepnirb8762042025-04-07 20:57:14 +02004924 // Get length of lcs-leadmultispace string in first
zeertzjqb5f08012022-06-09 13:55:28 +01004925 // round
Bram Moolenaaraca12fd2022-06-07 10:16:15 +01004926 last_lmultispace = p;
4927 lead_multispace_len = 0;
4928 while (*s != NUL && *s != ',')
4929 {
4930 c1 = get_encoded_char_adv(&s);
4931 if (char2cells(c1) > 1)
zeertzjq6a8d2e12024-01-17 20:54:49 +01004932 return field_value_err(errbuf, errbuflen,
4933 e_wrong_character_width_for_field_str,
John Marriottc15de972025-01-17 13:54:49 +01004934 tab[i].name.string);
Bram Moolenaaraca12fd2022-06-07 10:16:15 +01004935 ++lead_multispace_len;
4936 }
4937 if (lead_multispace_len == 0)
zeertzjqb5f08012022-06-09 13:55:28 +01004938 // lcs-leadmultispace cannot be an empty string
zeertzjq6a8d2e12024-01-17 20:54:49 +01004939 return field_value_err(errbuf, errbuflen,
4940 e_wrong_number_of_characters_for_field_str,
John Marriottc15de972025-01-17 13:54:49 +01004941 tab[i].name.string);
Bram Moolenaaraca12fd2022-06-07 10:16:15 +01004942 }
4943 else
4944 {
4945 int multispace_pos = 0;
4946
4947 while (*s != NUL && *s != ',')
4948 {
4949 c1 = get_encoded_char_adv(&s);
John Marriottc15de972025-01-17 13:54:49 +01004950 if (p == last_lmultispace && lcs_chars.leadmultispace != NULL)
Bram Moolenaaraca12fd2022-06-07 10:16:15 +01004951 lcs_chars.leadmultispace[multispace_pos++] = c1;
4952 }
Bram Moolenaaraca12fd2022-06-07 10:16:15 +01004953 }
glepnirb8762042025-04-07 20:57:14 +02004954 p = s;
zeertzjq1f025b02023-09-30 12:43:07 +02004955 break;
Bram Moolenaaraca12fd2022-06-07 10:16:15 +01004956 }
zeertzjq1f025b02023-09-30 12:43:07 +02004957
4958 c2 = c3 = 0;
zeertzjq6a8d2e12024-01-17 20:54:49 +01004959 if (*s == NUL)
4960 return field_value_err(errbuf, errbuflen,
4961 e_wrong_number_of_characters_for_field_str,
John Marriottc15de972025-01-17 13:54:49 +01004962 tab[i].name.string);
zeertzjq1f025b02023-09-30 12:43:07 +02004963 c1 = get_encoded_char_adv(&s);
4964 if (char2cells(c1) > 1)
zeertzjq6a8d2e12024-01-17 20:54:49 +01004965 return field_value_err(errbuf, errbuflen,
4966 e_wrong_character_width_for_field_str,
John Marriottc15de972025-01-17 13:54:49 +01004967 tab[i].name.string);
zeertzjq1f025b02023-09-30 12:43:07 +02004968 if (tab[i].cp == &lcs_chars.tab2)
4969 {
4970 if (*s == NUL)
zeertzjq6a8d2e12024-01-17 20:54:49 +01004971 return field_value_err(errbuf, errbuflen,
4972 e_wrong_number_of_characters_for_field_str,
John Marriottc15de972025-01-17 13:54:49 +01004973 tab[i].name.string);
zeertzjq1f025b02023-09-30 12:43:07 +02004974 c2 = get_encoded_char_adv(&s);
4975 if (char2cells(c2) > 1)
zeertzjq6a8d2e12024-01-17 20:54:49 +01004976 return field_value_err(errbuf, errbuflen,
4977 e_wrong_character_width_for_field_str,
John Marriottc15de972025-01-17 13:54:49 +01004978 tab[i].name.string);
zeertzjq1f025b02023-09-30 12:43:07 +02004979 if (!(*s == ',' || *s == NUL))
4980 {
4981 c3 = get_encoded_char_adv(&s);
4982 if (char2cells(c3) > 1)
zeertzjq6a8d2e12024-01-17 20:54:49 +01004983 return field_value_err(errbuf, errbuflen,
4984 e_wrong_character_width_for_field_str,
John Marriottc15de972025-01-17 13:54:49 +01004985 tab[i].name.string);
zeertzjq1f025b02023-09-30 12:43:07 +02004986 }
4987 }
4988
4989 if (*s == ',' || *s == NUL)
4990 {
4991 if (round > 0)
4992 {
4993 if (tab[i].cp == &lcs_chars.tab2)
4994 {
4995 lcs_chars.tab1 = c1;
4996 lcs_chars.tab2 = c2;
4997 lcs_chars.tab3 = c3;
4998 }
4999 else if (tab[i].cp != NULL)
5000 *(tab[i].cp) = c1;
5001
5002 }
5003 p = s;
5004 break;
5005 }
zeertzjq6a8d2e12024-01-17 20:54:49 +01005006 else
5007 return field_value_err(errbuf, errbuflen,
5008 e_wrong_number_of_characters_for_field_str,
John Marriottc15de972025-01-17 13:54:49 +01005009 tab[i].name.string);
zeertzjqf14b8ba2021-09-10 16:58:30 +02005010 }
5011
zeertzjq1f025b02023-09-30 12:43:07 +02005012 if (i == entries)
5013 return e_invalid_argument;
5014
Bram Moolenaare677df82019-09-02 22:31:11 +02005015 if (*p == ',')
5016 ++p;
5017 }
5018 }
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01005019
Bram Moolenaarb67f0c82022-07-04 21:03:36 +01005020 if (apply)
zeertzjqf14b8ba2021-09-10 16:58:30 +02005021 {
Bram Moolenaarb67f0c82022-07-04 21:03:36 +01005022 if (is_listchars)
5023 {
5024 vim_free(wp->w_lcs_chars.multispace);
5025 vim_free(wp->w_lcs_chars.leadmultispace);
5026 wp->w_lcs_chars = lcs_chars;
5027 }
5028 else
5029 {
5030 wp->w_fill_chars = fill_chars;
5031 }
zeertzjqf14b8ba2021-09-10 16:58:30 +02005032 }
Bram Moolenaare677df82019-09-02 22:31:11 +02005033
5034 return NULL; // no error
5035}
zeertzjq8ca29b62022-08-09 12:53:14 +01005036
5037/*
Yegappan Lakshmananc6ff21e2023-03-02 14:46:48 +00005038 * Handle the new value of 'fillchars'.
5039 */
5040 char *
zeertzjq6a8d2e12024-01-17 20:54:49 +01005041set_fillchars_option(win_T *wp, char_u *val, int apply, char *errbuf,
5042 size_t errbuflen)
Yegappan Lakshmananc6ff21e2023-03-02 14:46:48 +00005043{
zeertzjq6a8d2e12024-01-17 20:54:49 +01005044 return set_chars_option(wp, val, FALSE, apply, errbuf, errbuflen);
Yegappan Lakshmananc6ff21e2023-03-02 14:46:48 +00005045}
5046
5047/*
5048 * Handle the new value of 'listchars'.
5049 */
5050 char *
zeertzjq6a8d2e12024-01-17 20:54:49 +01005051set_listchars_option(win_T *wp, char_u *val, int apply, char *errbuf,
5052 size_t errbuflen)
Yegappan Lakshmananc6ff21e2023-03-02 14:46:48 +00005053{
zeertzjq6a8d2e12024-01-17 20:54:49 +01005054 return set_chars_option(wp, val, TRUE, apply, errbuf, errbuflen);
Yegappan Lakshmananc6ff21e2023-03-02 14:46:48 +00005055}
5056
5057/*
Yee Cheng Chin900894b2023-09-29 20:42:32 +02005058 * Function given to ExpandGeneric() to obtain possible arguments of the
5059 * 'fillchars' option.
5060 */
5061 char_u *
5062get_fillchars_name(expand_T *xp UNUSED, int idx)
5063{
John Marriottc15de972025-01-17 13:54:49 +01005064 if (idx < 0 || idx >= (int)ARRAY_LENGTH(filltab))
Yee Cheng Chin900894b2023-09-29 20:42:32 +02005065 return NULL;
5066
John Marriottc15de972025-01-17 13:54:49 +01005067 return filltab[idx].name.string;
Yee Cheng Chin900894b2023-09-29 20:42:32 +02005068}
5069
5070/*
5071 * Function given to ExpandGeneric() to obtain possible arguments of the
5072 * 'listchars' option.
5073 */
5074 char_u *
5075get_listchars_name(expand_T *xp UNUSED, int idx)
5076{
John Marriottc15de972025-01-17 13:54:49 +01005077 if (idx < 0 || idx >= (int)ARRAY_LENGTH(lcstab))
Yee Cheng Chin900894b2023-09-29 20:42:32 +02005078 return NULL;
5079
John Marriottc15de972025-01-17 13:54:49 +01005080 return lcstab[idx].name.string;
Yee Cheng Chin900894b2023-09-29 20:42:32 +02005081}
5082
5083/*
zeertzjq8ca29b62022-08-09 12:53:14 +01005084 * Check all global and local values of 'listchars' and 'fillchars'.
5085 * Return an untranslated error messages if any of them is invalid, NULL
5086 * otherwise.
5087 */
5088 char *
5089check_chars_options(void)
5090{
5091 tabpage_T *tp;
5092 win_T *wp;
5093
zeertzjq6a8d2e12024-01-17 20:54:49 +01005094 if (set_listchars_option(curwin, p_lcs, FALSE, NULL, 0) != NULL)
zeertzjq8ca29b62022-08-09 12:53:14 +01005095 return e_conflicts_with_value_of_listchars;
zeertzjq6a8d2e12024-01-17 20:54:49 +01005096 if (set_fillchars_option(curwin, p_fcs, FALSE, NULL, 0) != NULL)
zeertzjq8ca29b62022-08-09 12:53:14 +01005097 return e_conflicts_with_value_of_fillchars;
5098 FOR_ALL_TAB_WINDOWS(tp, wp)
5099 {
zeertzjq6a8d2e12024-01-17 20:54:49 +01005100 if (set_listchars_option(wp, wp->w_p_lcs, FALSE, NULL, 0) != NULL)
zeertzjq8ca29b62022-08-09 12:53:14 +01005101 return e_conflicts_with_value_of_listchars;
zeertzjq6a8d2e12024-01-17 20:54:49 +01005102 if (set_fillchars_option(wp, wp->w_p_fcs, FALSE, NULL, 0) != NULL)
zeertzjq8ca29b62022-08-09 12:53:14 +01005103 return e_conflicts_with_value_of_fillchars;
5104 }
5105 return NULL;
5106}