blob: db2376ecc6ae170a5b3d31af118c7149f9733e5e [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/*
11 * message.c: functions for displaying messages on the command line
12 */
13
Bram Moolenaar85a20022019-12-21 18:25:54 +010014#define MESSAGE_FILE // don't include prototype for smsg()
Bram Moolenaar071d4272004-06-13 20:20:40 +000015
16#include "vim.h"
17
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010018static void add_msg_hist(char_u *s, int len, int attr);
Christian Brabandt51d4d842024-12-06 17:26:25 +010019static void check_msg_hist(void);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010020static void hit_return_msg(void);
21static void msg_home_replace_attr(char_u *fname, int attr);
Bram Moolenaar32526b32019-01-19 17:43:09 +010022static void msg_puts_attr_len(char *str, int maxlen, int attr);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010023static void msg_puts_display(char_u *str, int maxlen, int attr, int recurse);
24static void msg_scroll_up(void);
25static void inc_msg_scrolled(void);
26static void store_sb_text(char_u **sb_str, char_u *s, int attr, int *sb_col, int finish);
27static void t_puts(int *t_col, char_u *t_s, char_u *s, int attr);
28static void msg_puts_printf(char_u *str, int maxlen);
29static int do_more_prompt(int typed_char);
30static void msg_screen_putchar(int c, int attr);
Bram Moolenaar5843f5f2019-08-20 20:13:45 +020031static void msg_moremsg(int full);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010032static int msg_check_screen(void);
33static void redir_write(char_u *s, int maxlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +000034#ifdef FEAT_CON_DIALOG
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010035static char_u *msg_show_console_dialog(char_u *message, char_u *buttons, int dfltbutton);
Bram Moolenaar85a20022019-12-21 18:25:54 +010036static int confirm_msg_used = FALSE; // displaying confirm_msg
37static char_u *confirm_msg = NULL; // ":confirm" message
38static char_u *confirm_msg_tail; // tail of confirm_msg
Bram Moolenaar5843f5f2019-08-20 20:13:45 +020039static void display_confirm_msg(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +000040#endif
Bram Moolenaar3b8c7082022-11-30 20:20:56 +000041#ifdef FEAT_EVAL
Bram Moolenaar958dc692016-12-01 15:34:12 +010042static int emsg_to_channel_log = FALSE;
43#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000044
45struct msg_hist
46{
47 struct msg_hist *next;
48 char_u *msg;
49 int attr;
50};
51
52static struct msg_hist *first_msg_hist = NULL;
53static struct msg_hist *last_msg_hist = NULL;
54static int msg_hist_len = 0;
Christian Brabandt51d4d842024-12-06 17:26:25 +010055static int msg_hist_max = 500; // The default max value is 500
56
57// flags obtained from the 'messagesopt' option
58#define MESSAGES_HIT_ENTER 0x001
59#define MESSAGES_WAIT 0x002
60#define MESSAGES_HISTORY 0x004
61
62// args in 'messagesopt' option
63#define MESSAGES_OPT_HIT_ENTER "hit-enter"
64#define MESSAGES_OPT_WAIT "wait:"
65#define MESSAGES_OPT_HISTORY "history:"
66
67// The default is "hit-enter,history:500"
68static int msg_flags = MESSAGES_HIT_ENTER | MESSAGES_HISTORY;
69static int msg_wait = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000070
Bram Moolenaarb5b5b892011-09-14 15:39:29 +020071static FILE *verbose_fd = NULL;
72static int verbose_did_open = FALSE;
73
Bram Moolenaar071d4272004-06-13 20:20:40 +000074/*
75 * When writing messages to the screen, there are many different situations.
76 * A number of variables is used to remember the current state:
77 * msg_didany TRUE when messages were written since the last time the
78 * user reacted to a prompt.
79 * Reset: After hitting a key for the hit-return prompt,
80 * hitting <CR> for the command line or input().
81 * Set: When any message is written to the screen.
82 * msg_didout TRUE when something was written to the current line.
83 * Reset: When advancing to the next line, when the current
84 * text can be overwritten.
85 * Set: When any message is written to the screen.
86 * msg_nowait No extra delay for the last drawn message.
87 * Used in normal_cmd() before the mode message is drawn.
88 * emsg_on_display There was an error message recently. Indicates that there
89 * should be a delay before redrawing.
90 * msg_scroll The next message should not overwrite the current one.
91 * msg_scrolled How many lines the screen has been scrolled (because of
92 * messages). Used in update_screen() to scroll the screen
93 * back. Incremented each time the screen scrolls a line.
94 * msg_scrolled_ign TRUE when msg_scrolled is non-zero and msg_puts_attr()
95 * writes something without scrolling should not make
96 * need_wait_return to be set. This is a hack to make ":ts"
97 * work without an extra prompt.
98 * lines_left Number of lines available for messages before the
Bram Moolenaar5d6f75e2011-12-30 14:14:29 +010099 * more-prompt is to be given. -1 when not set.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000100 * need_wait_return TRUE when the hit-return prompt is needed.
101 * Reset: After giving the hit-return prompt, when the user
102 * has answered some other prompt.
103 * Set: When the ruler or typeahead display is overwritten,
104 * scrolling the screen for some message.
105 * keep_msg Message to be displayed after redrawing the screen, in
106 * main_loop().
107 * This is an allocated string or NULL when not used.
108 */
109
110/*
111 * msg(s) - displays the string 's' on the status line
112 * When terminal not initialized (yet) mch_errmsg(..) is used.
Bram Moolenaar13608d82022-08-29 15:06:50 +0100113 * return TRUE if wait_return() not called
Bram Moolenaar071d4272004-06-13 20:20:40 +0000114 */
115 int
Bram Moolenaar32526b32019-01-19 17:43:09 +0100116msg(char *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000117{
118 return msg_attr_keep(s, 0, FALSE);
119}
120
Bram Moolenaar8b044b32005-05-31 22:05:58 +0000121/*
122 * Like msg() but keep it silent when 'verbosefile' is set.
123 */
124 int
Bram Moolenaar32526b32019-01-19 17:43:09 +0100125verb_msg(char *s)
Bram Moolenaar8b044b32005-05-31 22:05:58 +0000126{
127 int n;
128
129 verbose_enter();
130 n = msg_attr_keep(s, 0, FALSE);
131 verbose_leave();
132
133 return n;
134}
Bram Moolenaar8b044b32005-05-31 22:05:58 +0000135
Bram Moolenaar071d4272004-06-13 20:20:40 +0000136 int
Bram Moolenaar32526b32019-01-19 17:43:09 +0100137msg_attr(char *s, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000138{
139 return msg_attr_keep(s, attr, FALSE);
140}
141
142 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100143msg_attr_keep(
Bram Moolenaar32526b32019-01-19 17:43:09 +0100144 char *s,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100145 int attr,
Bram Moolenaar85a20022019-12-21 18:25:54 +0100146 int keep) // TRUE: set keep_msg if it doesn't scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +0000147{
148 static int entered = 0;
149 int retval;
150 char_u *buf = NULL;
151
Bram Moolenaar85a20022019-12-21 18:25:54 +0100152 // Skip messages not matching ":filter pattern".
153 // Don't filter when there is an error.
Bram Moolenaar32526b32019-01-19 17:43:09 +0100154 if (!emsg_on_display && message_filtered((char_u *)s))
Bram Moolenaar7b668e82016-08-23 23:51:21 +0200155 return TRUE;
156
Bram Moolenaar071d4272004-06-13 20:20:40 +0000157#ifdef FEAT_EVAL
158 if (attr == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +0100159 set_vim_var_string(VV_STATUSMSG, (char_u *)s, -1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000160#endif
161
162 /*
163 * It is possible that displaying a messages causes a problem (e.g.,
164 * when redrawing the window), which causes another message, etc.. To
165 * break this loop, limit the recursiveness to 3 levels.
166 */
167 if (entered >= 3)
168 return TRUE;
169 ++entered;
170
Bram Moolenaar85a20022019-12-21 18:25:54 +0100171 // Add message to history (unless it's a repeated kept message or a
172 // truncated message)
Bram Moolenaar32526b32019-01-19 17:43:09 +0100173 if ((char_u *)s != keep_msg
Bram Moolenaar071d4272004-06-13 20:20:40 +0000174 || (*s != '<'
175 && last_msg_hist != NULL
176 && last_msg_hist->msg != NULL
177 && STRCMP(s, last_msg_hist->msg)))
Bram Moolenaar32526b32019-01-19 17:43:09 +0100178 add_msg_hist((char_u *)s, -1, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000179
Bram Moolenaar4c5678f2022-11-30 18:12:19 +0000180#ifdef FEAT_EVAL
Bram Moolenaar958dc692016-12-01 15:34:12 +0100181 if (emsg_to_channel_log)
Bram Moolenaar85a20022019-12-21 18:25:54 +0100182 // Write message in the channel log.
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +0000183 ch_log(NULL, "ERROR: %s", s);
Bram Moolenaar958dc692016-12-01 15:34:12 +0100184#endif
185
Bram Moolenaar85a20022019-12-21 18:25:54 +0100186 // Truncate the message if needed.
Bram Moolenaar7ca30432005-09-09 19:44:31 +0000187 msg_start();
Bram Moolenaar32526b32019-01-19 17:43:09 +0100188 buf = msg_strtrunc((char_u *)s, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000189 if (buf != NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +0100190 s = (char *)buf;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000191
Bram Moolenaar32526b32019-01-19 17:43:09 +0100192 msg_outtrans_attr((char_u *)s, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000193 msg_clr_eos();
194 retval = msg_end();
195
Bram Moolenaar32526b32019-01-19 17:43:09 +0100196 if (keep && retval && vim_strsize((char_u *)s)
197 < (int)(Rows - cmdline_row - 1) * Columns + sc_col)
198 set_keep_msg((char_u *)s, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000199
Rob Pilling726f7f92022-01-20 14:44:38 +0000200 need_fileinfo = FALSE;
201
Bram Moolenaar071d4272004-06-13 20:20:40 +0000202 vim_free(buf);
203 --entered;
204 return retval;
205}
206
207/*
208 * Truncate a string such that it can be printed without causing a scroll.
209 * Returns an allocated string or NULL when no truncating is done.
210 */
211 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100212msg_strtrunc(
213 char_u *s,
Bram Moolenaar85a20022019-12-21 18:25:54 +0100214 int force) // always truncate
Bram Moolenaar071d4272004-06-13 20:20:40 +0000215{
216 char_u *buf = NULL;
217 int len;
218 int room;
219
Bram Moolenaar85a20022019-12-21 18:25:54 +0100220 // May truncate message to avoid a hit-return prompt
Bram Moolenaar7ca30432005-09-09 19:44:31 +0000221 if ((!msg_scroll && !need_wait_return && shortmess(SHM_TRUNCALL)
222 && !exmode_active && msg_silent == 0) || force)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000223 {
224 len = vim_strsize(s);
Bram Moolenaar35a4fbc2022-08-28 14:39:53 +0100225 if (msg_scrolled != 0
226#ifdef HAS_MESSAGE_WINDOW
Bram Moolenaara2a89732022-08-31 14:46:18 +0100227 || in_echowindow
Bram Moolenaar35a4fbc2022-08-28 14:39:53 +0100228#endif
229 )
Bram Moolenaar85a20022019-12-21 18:25:54 +0100230 // Use all the columns.
Bram Moolenaar7ca30432005-09-09 19:44:31 +0000231 room = (int)(Rows - msg_row) * Columns - 1;
232 else
Bram Moolenaar85a20022019-12-21 18:25:54 +0100233 // Use up to 'showcmd' column.
Bram Moolenaar7ca30432005-09-09 19:44:31 +0000234 room = (int)(Rows - msg_row - 1) * Columns + sc_col - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000235 if (len > room && room > 0)
236 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000237 if (enc_utf8)
Bram Moolenaar85a20022019-12-21 18:25:54 +0100238 // may have up to 18 bytes per cell (6 per char, up to two
239 // composing chars)
Bram Moolenaarf31b7642012-01-20 20:44:43 +0100240 len = (room + 2) * 18;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000241 else if (enc_dbcs == DBCS_JPNU)
Bram Moolenaar85a20022019-12-21 18:25:54 +0100242 // may have up to 2 bytes per cell for euc-jp
Bram Moolenaarf31b7642012-01-20 20:44:43 +0100243 len = (room + 2) * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000244 else
Bram Moolenaarf31b7642012-01-20 20:44:43 +0100245 len = room + 2;
246 buf = alloc(len);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000247 if (buf != NULL)
Bram Moolenaarf31b7642012-01-20 20:44:43 +0100248 trunc_string(s, buf, room, len);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000249 }
250 }
251 return buf;
252}
253
254/*
255 * Truncate a string "s" to "buf" with cell width "room".
256 * "s" and "buf" may be equal.
257 */
258 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100259trunc_string(
260 char_u *s,
261 char_u *buf,
Bram Moolenaard4f31dc2016-07-23 17:28:22 +0200262 int room_in,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100263 int buflen)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000264{
Bram Moolenaar85a20022019-12-21 18:25:54 +0100265 size_t room = room_in - 3; // "..." takes 3 chars
Bram Moolenaard4f31dc2016-07-23 17:28:22 +0200266 size_t half;
267 size_t len = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000268 int e;
269 int i;
270 int n;
271
Bram Moolenaar62818152021-02-14 15:37:30 +0100272 if (*s == NUL)
273 {
274 if (buflen > 0)
275 *buf = NUL;
276 return;
277 }
278
Bram Moolenaard4f31dc2016-07-23 17:28:22 +0200279 if (room_in < 3)
280 room = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000281 half = room / 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000282
Bram Moolenaar85a20022019-12-21 18:25:54 +0100283 // First part: Start of the string.
Bram Moolenaarf31b7642012-01-20 20:44:43 +0100284 for (e = 0; len < half && e < buflen; ++e)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000285 {
286 if (s[e] == NUL)
287 {
Bram Moolenaar85a20022019-12-21 18:25:54 +0100288 // text fits without truncating!
Bram Moolenaar071d4272004-06-13 20:20:40 +0000289 buf[e] = NUL;
290 return;
291 }
292 n = ptr2cells(s + e);
Bram Moolenaar502ae4b2016-07-16 19:50:13 +0200293 if (len + n > half)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000294 break;
295 len += n;
296 buf[e] = s[e];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000297 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +0000298 for (n = (*mb_ptr2len)(s + e); --n > 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +0000299 {
Bram Moolenaarf31b7642012-01-20 20:44:43 +0100300 if (++e == buflen)
301 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000302 buf[e] = s[e];
303 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000304 }
305
Bram Moolenaar85a20022019-12-21 18:25:54 +0100306 // Last part: End of the string.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000307 i = e;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000308 if (enc_dbcs != 0)
309 {
Bram Moolenaar85a20022019-12-21 18:25:54 +0100310 // For DBCS going backwards in a string is slow, but
311 // computing the cell width isn't too slow: go forward
312 // until the rest fits.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000313 n = vim_strsize(s + i);
314 while (len + n > room)
315 {
316 n -= ptr2cells(s + i);
Bram Moolenaar0fa313a2005-08-10 21:07:57 +0000317 i += (*mb_ptr2len)(s + i);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000318 }
319 }
320 else if (enc_utf8)
321 {
Bram Moolenaar85a20022019-12-21 18:25:54 +0100322 // For UTF-8 we can go backwards easily.
Bram Moolenaar362e1a32006-03-06 23:29:24 +0000323 half = i = (int)STRLEN(s);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000324 for (;;)
325 {
Bram Moolenaar362e1a32006-03-06 23:29:24 +0000326 do
Bram Moolenaarace95982017-03-29 17:30:27 +0200327 half = half - utf_head_off(s, s + half - 1) - 1;
Bram Moolenaarb9644432016-07-19 12:33:44 +0200328 while (half > 0 && utf_iscomposing(utf_ptr2char(s + half)));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000329 n = ptr2cells(s + half);
Bram Moolenaarb9644432016-07-19 12:33:44 +0200330 if (len + n > room || half == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000331 break;
332 len += n;
Bram Moolenaara5c0cc12016-07-30 16:40:39 +0200333 i = (int)half;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000334 }
335 }
336 else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000337 {
Bram Moolenaard0337e32019-12-30 17:55:34 +0100338 for (i = (int)STRLEN(s);
339 i - 1 >= 0 && len + (n = ptr2cells(s + i - 1)) <= room; --i)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000340 len += n;
341 }
342
Bram Moolenaarf6acffb2016-07-16 16:54:24 +0200343
344 if (i <= e + 3)
Bram Moolenaarf31b7642012-01-20 20:44:43 +0100345 {
Bram Moolenaar85a20022019-12-21 18:25:54 +0100346 // text fits without truncating
Bram Moolenaarf6acffb2016-07-16 16:54:24 +0200347 if (s != buf)
348 {
349 len = STRLEN(s);
Bram Moolenaard4f31dc2016-07-23 17:28:22 +0200350 if (len >= (size_t)buflen)
Bram Moolenaarf6acffb2016-07-16 16:54:24 +0200351 len = buflen - 1;
352 len = len - e + 1;
353 if (len < 1)
354 buf[e - 1] = NUL;
355 else
356 mch_memmove(buf + e, s + e, len);
357 }
358 }
359 else if (e + 3 < buflen)
360 {
Bram Moolenaar85a20022019-12-21 18:25:54 +0100361 // set the middle and copy the last part
Bram Moolenaarf31b7642012-01-20 20:44:43 +0100362 mch_memmove(buf + e, "...", (size_t)3);
Bram Moolenaard4f31dc2016-07-23 17:28:22 +0200363 len = STRLEN(s + i) + 1;
364 if (len >= (size_t)buflen - e - 3)
Bram Moolenaarf31b7642012-01-20 20:44:43 +0100365 len = buflen - e - 3 - 1;
366 mch_memmove(buf + e + 3, s + i, len);
367 buf[e + 3 + len - 1] = NUL;
368 }
369 else
370 {
Bram Moolenaar85a20022019-12-21 18:25:54 +0100371 // can't fit in the "...", just truncate it
Christian Brabandt3bd7fa12023-10-02 20:59:08 +0200372 buf[buflen - 1] = NUL;
Bram Moolenaarf31b7642012-01-20 20:44:43 +0100373 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000374}
375
376/*
377 * Automatic prototype generation does not understand this function.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100378 * Note: Caller of smsg() and smsg_attr() must check the resulting string is
Bram Moolenaar071d4272004-06-13 20:20:40 +0000379 * shorter than IOSIZE!!!
380 */
381#ifndef PROTO
Bram Moolenaar071d4272004-06-13 20:20:40 +0000382
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100383int vim_snprintf(char *str, size_t str_m, const char *fmt, ...);
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000384
Bram Moolenaar071d4272004-06-13 20:20:40 +0000385 int
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100386smsg(const char *s, ...)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000387{
Bram Moolenaare3a22cb2019-10-14 22:01:57 +0200388 if (IObuff == NULL)
389 {
390 // Very early in initialisation and already something wrong, just
391 // give the raw message so the user at least gets a hint.
392 return msg((char *)s);
393 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000394
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000395 va_list arglist;
396
397 va_start(arglist, s);
398 vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist);
399 va_end(arglist);
400 return msg((char *)IObuff);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000401}
402
403 int
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100404smsg_attr(int attr, const char *s, ...)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000405{
Bram Moolenaare3a22cb2019-10-14 22:01:57 +0200406 if (IObuff == NULL)
407 {
408 // Very early in initialisation and already something wrong, just
409 // give the raw message so the user at least gets a hint.
410 return msg_attr((char *)s, attr);
411 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000412
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000413 va_list arglist;
414
415 va_start(arglist, s);
416 vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist);
417 va_end(arglist);
418 return msg_attr((char *)IObuff, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000419}
420
Bram Moolenaare0429682018-07-01 16:44:03 +0200421 int
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100422smsg_attr_keep(int attr, const char *s, ...)
Bram Moolenaare0429682018-07-01 16:44:03 +0200423{
Bram Moolenaare3a22cb2019-10-14 22:01:57 +0200424 if (IObuff == NULL)
425 {
426 // Very early in initialisation and already something wrong, just
427 // give the raw message so the user at least gets a hint.
428 return msg_attr_keep((char *)s, attr, TRUE);
429 }
Bram Moolenaare0429682018-07-01 16:44:03 +0200430
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000431 va_list arglist;
432
433 va_start(arglist, s);
434 vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist);
435 va_end(arglist);
436 return msg_attr_keep((char *)IObuff, attr, TRUE);
Bram Moolenaare0429682018-07-01 16:44:03 +0200437}
438
Bram Moolenaar071d4272004-06-13 20:20:40 +0000439#endif
440
441/*
442 * Remember the last sourcing name/lnum used in an error message, so that it
443 * isn't printed each time when it didn't change.
444 */
445static int last_sourcing_lnum = 0;
446static char_u *last_sourcing_name = NULL;
447
448/*
449 * Reset the last used sourcing name/lnum. Makes sure it is displayed again
450 * for the next error message;
451 */
Bram Moolenaar4eec5ec2005-06-26 22:26:21 +0000452 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100453reset_last_sourcing(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000454{
Bram Moolenaard23a8232018-02-10 18:45:26 +0100455 VIM_CLEAR(last_sourcing_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000456 last_sourcing_lnum = 0;
457}
458
459/*
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100460 * Return TRUE if "SOURCING_NAME" differs from "last_sourcing_name".
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000461 */
462 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100463other_sourcing_name(void)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000464{
Bram Moolenaar0bd8d052021-11-25 13:39:28 +0000465 if (HAVE_SOURCING_INFO && SOURCING_NAME != NULL)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000466 {
467 if (last_sourcing_name != NULL)
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100468 return STRCMP(SOURCING_NAME, last_sourcing_name) != 0;
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000469 return TRUE;
470 }
471 return FALSE;
472}
473
474/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475 * Get the message about the source, as used for an error message.
476 * Returns an allocated string with room for one more character.
477 * Returns NULL when no message is to be given.
478 */
479 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100480get_emsg_source(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481{
482 char_u *Buf, *p;
483
Bram Moolenaar0bd8d052021-11-25 13:39:28 +0000484 if (HAVE_SOURCING_INFO && SOURCING_NAME != NULL && other_sourcing_name())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000485 {
Bram Moolenaar4f25b1a2020-09-10 19:25:05 +0200486 char_u *sname = estack_sfile(ESTACK_NONE);
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100487 char_u *tofree = sname;
488
489 if (sname == NULL)
490 sname = SOURCING_NAME;
491
Bram Moolenaarf4e8cdd2020-10-12 22:07:13 +0200492#ifdef FEAT_EVAL
493 if (estack_compiling)
494 p = (char_u *)_("Error detected while compiling %s:");
495 else
496#endif
497 p = (char_u *)_("Error detected while processing %s:");
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100498 Buf = alloc(STRLEN(sname) + STRLEN(p));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000499 if (Buf != NULL)
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100500 sprintf((char *)Buf, (char *)p, sname);
501 vim_free(tofree);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502 return Buf;
503 }
504 return NULL;
505}
506
507/*
508 * Get the message about the source lnum, as used for an error message.
509 * Returns an allocated string with room for one more character.
510 * Returns NULL when no message is to be given.
511 */
512 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100513get_emsg_lnum(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000514{
515 char_u *Buf, *p;
516
Bram Moolenaar85a20022019-12-21 18:25:54 +0100517 // lnum is 0 when executing a command from the command line
518 // argument, we don't want a line number then
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100519 if (SOURCING_NAME != NULL
520 && (other_sourcing_name() || SOURCING_LNUM != last_sourcing_lnum)
521 && SOURCING_LNUM != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000522 {
523 p = (char_u *)_("line %4ld:");
Bram Moolenaar964b3742019-05-24 18:54:09 +0200524 Buf = alloc(STRLEN(p) + 20);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000525 if (Buf != NULL)
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100526 sprintf((char *)Buf, (char *)p, (long)SOURCING_LNUM);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000527 return Buf;
528 }
529 return NULL;
530}
531
532/*
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000533 * Display name and line number for the source of an error.
534 * Remember the file name and line number, so that for the next error the info
535 * is only displayed if it changed.
536 */
537 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100538msg_source(int attr)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000539{
540 char_u *p;
Bram Moolenaarba8c9262021-11-25 14:43:18 +0000541 static int recursive = FALSE;
542
543 // Bail out if something called here causes an error.
544 if (recursive)
545 return;
546 recursive = TRUE;
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000547
548 ++no_wait_return;
549 p = get_emsg_source();
550 if (p != NULL)
551 {
zeertzjqbdedd2b2022-09-20 12:45:15 +0100552 msg_scroll = TRUE; // this will take more than one line
Bram Moolenaar32526b32019-01-19 17:43:09 +0100553 msg_attr((char *)p, attr);
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000554 vim_free(p);
555 }
556 p = get_emsg_lnum();
557 if (p != NULL)
558 {
Bram Moolenaar32526b32019-01-19 17:43:09 +0100559 msg_attr((char *)p, HL_ATTR(HLF_N));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000560 vim_free(p);
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100561 last_sourcing_lnum = SOURCING_LNUM; // only once for each line
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000562 }
563
Bram Moolenaar85a20022019-12-21 18:25:54 +0100564 // remember the last sourcing name printed, also when it's empty
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100565 if (SOURCING_NAME == NULL || other_sourcing_name())
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000566 {
Bram Moolenaarba8c9262021-11-25 14:43:18 +0000567 VIM_CLEAR(last_sourcing_name);
568 if (SOURCING_NAME != NULL)
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100569 last_sourcing_name = vim_strsave(SOURCING_NAME);
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000570 }
571 --no_wait_return;
Bram Moolenaarba8c9262021-11-25 14:43:18 +0000572
573 recursive = FALSE;
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000574}
575
576/*
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000577 * Return TRUE if not giving error messages right now:
578 * If "emsg_off" is set: no error messages at the moment.
579 * If "msg" is in 'debug': do error message but without side effects.
580 * If "emsg_skip" is set: never do error messages.
581 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +0200582 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100583emsg_not_now(void)
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000584{
585 if ((emsg_off > 0 && vim_strchr(p_debug, 'm') == NULL
586 && vim_strchr(p_debug, 't') == NULL)
587#ifdef FEAT_EVAL
588 || emsg_skip > 0
589#endif
590 )
591 return TRUE;
592 return FALSE;
593}
594
Bram Moolenaar5a66dfb2017-03-01 20:40:39 +0100595#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaare0c31f62017-03-01 15:07:05 +0100596static garray_T ignore_error_list = GA_EMPTY;
597
598 void
599ignore_error_for_testing(char_u *error)
600{
601 if (ignore_error_list.ga_itemsize == 0)
602 ga_init2(&ignore_error_list, sizeof(char_u *), 1);
603
Bram Moolenaar461a7fc2018-12-22 13:28:07 +0100604 if (STRCMP("RESET", error) == 0)
605 ga_clear_strings(&ignore_error_list);
606 else
Bram Moolenaar9f1a39a2022-01-08 15:39:39 +0000607 ga_copy_string(&ignore_error_list, error);
Bram Moolenaare0c31f62017-03-01 15:07:05 +0100608}
609
610 static int
Bram Moolenaar097c5372023-05-24 21:02:24 +0100611ignore_error(const char *msg)
Bram Moolenaare0c31f62017-03-01 15:07:05 +0100612{
613 int i;
614
615 for (i = 0; i < ignore_error_list.ga_len; ++i)
Bram Moolenaar097c5372023-05-24 21:02:24 +0100616 if (strstr(msg,
Bram Moolenaare0c31f62017-03-01 15:07:05 +0100617 (char *)((char_u **)(ignore_error_list.ga_data))[i]) != NULL)
618 return TRUE;
619 return FALSE;
620}
621#endif
622
Bram Moolenaarb869c0d2016-07-20 00:10:51 +0200623#if !defined(HAVE_STRERROR) || defined(PROTO)
624/*
625 * Replacement for perror() that behaves more or less like emsg() was called.
Bram Moolenaar53989552019-12-23 22:59:18 +0100626 * v:errmsg will be set and called_emsg will be incremented.
Bram Moolenaarb869c0d2016-07-20 00:10:51 +0200627 */
628 void
629do_perror(char *msg)
630{
631 perror(msg);
632 ++emsg_silent;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100633 emsg(msg);
Bram Moolenaarb869c0d2016-07-20 00:10:51 +0200634 --emsg_silent;
635}
636#endif
637
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000638/*
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100639 * emsg_core() - display an error message
Bram Moolenaar071d4272004-06-13 20:20:40 +0000640 *
641 * Rings the bell, if appropriate, and calls message() to do the real work
642 * When terminal not initialized (yet) mch_errmsg(..) is used.
643 *
Bram Moolenaar13608d82022-08-29 15:06:50 +0100644 * Return TRUE if wait_return() not called.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100645 * Note: caller must check 'emsg_not_now()' before calling this.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000646 */
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100647 static int
Bram Moolenaar097c5372023-05-24 21:02:24 +0100648emsg_core(const char *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000649{
650 int attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000651 char_u *p;
Bram Moolenaar958dc692016-12-01 15:34:12 +0100652 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000653#ifdef FEAT_EVAL
654 int ignore = FALSE;
655 int severe;
656#endif
657
Bram Moolenaare0c31f62017-03-01 15:07:05 +0100658#ifdef FEAT_EVAL
Bram Moolenaar85a20022019-12-21 18:25:54 +0100659 // When testing some errors are turned into a normal message.
Bram Moolenaare0c31f62017-03-01 15:07:05 +0100660 if (ignore_error(s))
Bram Moolenaar85a20022019-12-21 18:25:54 +0100661 // don't call msg() if it results in a dialog
Bram Moolenaar32526b32019-01-19 17:43:09 +0100662 return msg_use_printf() ? FALSE : msg((char *)s);
Bram Moolenaare0c31f62017-03-01 15:07:05 +0100663#endif
664
Bram Moolenaar53989552019-12-23 22:59:18 +0100665 ++called_emsg;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000666
Bram Moolenaar071d4272004-06-13 20:20:40 +0000667#ifdef FEAT_EVAL
Bram Moolenaar85a20022019-12-21 18:25:54 +0100668 // If "emsg_severe" is TRUE: When an error exception is to be thrown,
669 // prefer this message over previous messages for the same command.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000670 severe = emsg_severe;
671 emsg_severe = FALSE;
672#endif
673
Bram Moolenaar57657d82006-04-21 22:12:41 +0000674 if (!emsg_off || vim_strchr(p_debug, 't') != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000675 {
676#ifdef FEAT_EVAL
677 /*
678 * Cause a throw of an error exception if appropriate. Don't display
679 * the error message in this case. (If no matching catch clause will
680 * be found, the message will be displayed later on.) "ignore" is set
681 * when the message should be ignored completely (used for the
682 * interrupt message).
683 */
Bram Moolenaar097c5372023-05-24 21:02:24 +0100684 if (cause_errthrow((char_u *)s, severe, &ignore) == TRUE)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000685 {
686 if (!ignore)
Bram Moolenaar76a63452018-11-28 20:38:37 +0100687 ++did_emsg;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000688 return TRUE;
689 }
690
Bram Moolenaar28ee8922020-10-28 20:20:00 +0100691 if (in_assert_fails && emsg_assert_fails_msg == NULL)
Bram Moolenaar1d634542020-08-18 13:41:50 +0200692 {
Bram Moolenaar097c5372023-05-24 21:02:24 +0100693 emsg_assert_fails_msg = vim_strsave((char_u *)s);
Bram Moolenaar1d634542020-08-18 13:41:50 +0200694 emsg_assert_fails_lnum = SOURCING_LNUM;
Bram Moolenaar9bd5d872020-09-06 21:47:48 +0200695 vim_free(emsg_assert_fails_context);
696 emsg_assert_fails_context = vim_strsave(
697 SOURCING_NAME == NULL ? (char_u *)"" : SOURCING_NAME);
Bram Moolenaar1d634542020-08-18 13:41:50 +0200698 }
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200699
Bram Moolenaar85a20022019-12-21 18:25:54 +0100700 // set "v:errmsg", also when using ":silent! cmd"
Bram Moolenaar097c5372023-05-24 21:02:24 +0100701 set_vim_var_string(VV_ERRMSG, (char_u *)s, -1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000702#endif
703
704 /*
Bram Moolenaara7241f52008-06-24 20:39:31 +0000705 * When using ":silent! cmd" ignore error messages.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000706 * But do write it to the redirection file.
707 */
708 if (emsg_silent != 0)
709 {
Bram Moolenaar599410c2021-04-10 14:03:43 +0200710#ifdef FEAT_EVAL
711 ++did_emsg_silent;
712#endif
Bram Moolenaar79815f12016-07-09 17:07:29 +0200713 if (emsg_noredir == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000714 {
Bram Moolenaar79815f12016-07-09 17:07:29 +0200715 msg_start();
716 p = get_emsg_source();
717 if (p != NULL)
718 {
719 STRCAT(p, "\n");
720 redir_write(p, -1);
721 vim_free(p);
722 }
723 p = get_emsg_lnum();
724 if (p != NULL)
725 {
726 STRCAT(p, "\n");
727 redir_write(p, -1);
728 vim_free(p);
729 }
Bram Moolenaar097c5372023-05-24 21:02:24 +0100730 redir_write((char_u *)s, -1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000731 }
Bram Moolenaar56602ba2020-12-05 21:22:08 +0100732#ifdef FEAT_EVAL
733 // Only increment did_emsg_def when :silent! wasn't used inside the
734 // :def function.
735 if (emsg_silent == emsg_silent_def)
736 ++did_emsg_def;
737#endif
Bram Moolenaar4c5678f2022-11-30 18:12:19 +0000738#ifdef FEAT_EVAL
Bram Moolenaar097c5372023-05-24 21:02:24 +0100739 ch_log(NULL, "ERROR silent: %s", s);
Bram Moolenaar958dc692016-12-01 15:34:12 +0100740#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000741 return TRUE;
742 }
743
Bram Moolenaar2b7bc562017-01-14 19:24:52 +0100744 ex_exitval = 1;
745
Bram Moolenaar85a20022019-12-21 18:25:54 +0100746 // Reset msg_silent, an error causes messages to be switched back on.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000747 msg_silent = 0;
748 cmd_silent = FALSE;
749
Bram Moolenaar85a20022019-12-21 18:25:54 +0100750 if (global_busy) // break :global command
Bram Moolenaar071d4272004-06-13 20:20:40 +0000751 ++global_busy;
752
753 if (p_eb)
Bram Moolenaar85a20022019-12-21 18:25:54 +0100754 beep_flush(); // also includes flush_buffers()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000755 else
Bram Moolenaar6a2633b2018-10-07 23:16:36 +0200756 flush_buffers(FLUSH_MINIMAL); // flush internal buffers
Bram Moolenaar76a63452018-11-28 20:38:37 +0100757 ++did_emsg; // flag for DoOneCmd()
Bram Moolenaare723c422017-09-06 23:40:10 +0200758#ifdef FEAT_EVAL
Bram Moolenaar88c89c72021-08-14 14:01:05 +0200759 ++uncaught_emsg;
Bram Moolenaare723c422017-09-06 23:40:10 +0200760#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000761 }
762
Bram Moolenaar878e1d22022-08-28 17:53:23 +0100763#ifdef HAS_MESSAGE_WINDOW
Bram Moolenaara2a89732022-08-31 14:46:18 +0100764 if (!in_echowindow)
Bram Moolenaar878e1d22022-08-28 17:53:23 +0100765#endif
766 emsg_on_display = TRUE; // remember there is an error message
767
768 attr = HL_ATTR(HLF_E); // set highlight mode for error messages
Bram Moolenaarbb15b652005-10-03 21:52:09 +0000769 if (msg_scrolled != 0)
Bram Moolenaar85a20022019-12-21 18:25:54 +0100770 need_wait_return = TRUE; // needed in case emsg() is called after
Bram Moolenaar13608d82022-08-29 15:06:50 +0100771 // wait_return() has reset need_wait_return
Bram Moolenaar85a20022019-12-21 18:25:54 +0100772 // and a redraw is expected because
773 // msg_scrolled is non-zero
Bram Moolenaar071d4272004-06-13 20:20:40 +0000774
Bram Moolenaar958dc692016-12-01 15:34:12 +0100775#ifdef FEAT_JOB_CHANNEL
776 emsg_to_channel_log = TRUE;
777#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000778 /*
779 * Display name and line number for the source of the error.
780 */
zeertzjqbdedd2b2022-09-20 12:45:15 +0100781 msg_scroll = TRUE;
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000782 msg_source(attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000783
784 /*
785 * Display the error message itself.
786 */
Bram Moolenaar85a20022019-12-21 18:25:54 +0100787 msg_nowait = FALSE; // wait for this msg
Bram Moolenaar32526b32019-01-19 17:43:09 +0100788 r = msg_attr((char *)s, attr);
Bram Moolenaar958dc692016-12-01 15:34:12 +0100789
790#ifdef FEAT_JOB_CHANNEL
791 emsg_to_channel_log = FALSE;
792#endif
793 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000794}
795
796/*
Bram Moolenaar097c5372023-05-24 21:02:24 +0100797 * Print error message "s". Should already be translated.
798 * Return TRUE if wait_return() not called.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000799 */
800 int
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100801emsg(char *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000802{
Bram Moolenaar85a20022019-12-21 18:25:54 +0100803 // Skip this if not giving error messages at the moment.
Bram Moolenaar097c5372023-05-24 21:02:24 +0100804 if (emsg_not_now())
805 return TRUE;
806
807 return emsg_core(s);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000808}
809
Bram Moolenaar0d8562a2019-02-19 21:34:05 +0100810#ifndef PROTO // manual proto with __attribute__
Bram Moolenaar95f09602016-11-10 20:01:45 +0100811/*
Bram Moolenaar097c5372023-05-24 21:02:24 +0100812 * Print error message "s" with format string and variable arguments.
813 * "s" should already be translated.
814 * Note: caller must not use "IObuff" for "s"!
815 * Return TRUE if wait_return() not called.
Bram Moolenaar95f09602016-11-10 20:01:45 +0100816 */
817 int
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100818semsg(const char *s, ...)
Bram Moolenaar95f09602016-11-10 20:01:45 +0100819{
Bram Moolenaare3a22cb2019-10-14 22:01:57 +0200820 // Skip this if not giving error messages at the moment.
Bram Moolenaar097c5372023-05-24 21:02:24 +0100821 if (emsg_not_now())
822 return TRUE;
Bram Moolenaar95f09602016-11-10 20:01:45 +0100823
Bram Moolenaar097c5372023-05-24 21:02:24 +0100824 if (IObuff == NULL)
825 // Very early in initialisation and already something wrong, just
826 // give the raw message so the user at least gets a hint.
827 return emsg_core(s);
828
829 va_list ap;
830
831 va_start(ap, s);
832 vim_vsnprintf((char *)IObuff, IOSIZE, s, ap);
833 va_end(ap);
834 return emsg_core((char *)IObuff);
Bram Moolenaar95f09602016-11-10 20:01:45 +0100835}
Bram Moolenaar0d8562a2019-02-19 21:34:05 +0100836#endif
Bram Moolenaar95f09602016-11-10 20:01:45 +0100837
838/*
839 * Same as emsg(...), but abort on error when ABORT_ON_INTERNAL_ERROR is
840 * defined. It is used for internal errors only, so that they can be
841 * detected when fuzzing vim.
842 */
843 void
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100844iemsg(char *s)
Bram Moolenaar95f09602016-11-10 20:01:45 +0100845{
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000846 if (emsg_not_now())
847 return;
848
Bram Moolenaar097c5372023-05-24 21:02:24 +0100849 // Give a generic error which is translated. The error itself may not be
850 // translated, it almost never shows.
851 emsg_core(_(e_internal_error_please_report_a_bug));
852
853 emsg_core(s);
Bram Moolenaar096ca732022-01-01 00:55:28 +0000854#if defined(ABORT_ON_INTERNAL_ERROR) && defined(FEAT_EVAL)
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000855 set_vim_var_string(VV_ERRMSG, (char_u *)s, -1);
856 msg_putchar('\n'); // avoid overwriting the error message
857 out_flush();
858 abort();
Bram Moolenaar95f09602016-11-10 20:01:45 +0100859#endif
860}
861
Bram Moolenaar0d8562a2019-02-19 21:34:05 +0100862#ifndef PROTO // manual proto with __attribute__
Bram Moolenaar95f09602016-11-10 20:01:45 +0100863/*
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100864 * Same as semsg(...) but abort on error when ABORT_ON_INTERNAL_ERROR is
Bram Moolenaar95f09602016-11-10 20:01:45 +0100865 * defined. It is used for internal errors only, so that they can be
866 * detected when fuzzing vim.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100867 * Note: caller must not pass 'IObuff' as 1st argument.
Bram Moolenaar95f09602016-11-10 20:01:45 +0100868 */
869 void
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100870siemsg(const char *s, ...)
Bram Moolenaar95f09602016-11-10 20:01:45 +0100871{
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000872 if (emsg_not_now())
873 return;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100874
Bram Moolenaar097c5372023-05-24 21:02:24 +0100875 // Give a generic error which is translated. The error itself may not be
876 // translated, it almost never shows.
877 emsg_core(_(e_internal_error_please_report_a_bug));
878
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000879 if (IObuff == NULL)
880 {
881 // Very early in initialisation and already something wrong, just
882 // give the raw message so the user at least gets a hint.
Bram Moolenaar097c5372023-05-24 21:02:24 +0100883 emsg_core(s);
Bram Moolenaar213e70e2022-08-19 13:17:21 +0100884 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000885 else
886 {
887 va_list ap;
888
889 va_start(ap, s);
890 vim_vsnprintf((char *)IObuff, IOSIZE, s, ap);
891 va_end(ap);
Bram Moolenaar097c5372023-05-24 21:02:24 +0100892 emsg_core((char *)IObuff);
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000893 }
894# ifdef ABORT_ON_INTERNAL_ERROR
895 msg_putchar('\n'); // avoid overwriting the error message
896 out_flush();
897 abort();
898# endif
Bram Moolenaar95f09602016-11-10 20:01:45 +0100899}
Bram Moolenaar0d8562a2019-02-19 21:34:05 +0100900#endif
Bram Moolenaar95f09602016-11-10 20:01:45 +0100901
902/*
903 * Give an "Internal error" message.
904 */
905 void
906internal_error(char *where)
907{
Bram Moolenaar097c5372023-05-24 21:02:24 +0100908 // Give a generic error which is translated. The error itself may not be
909 // translated, it almost never shows.
910 emsg_core(_(e_internal_error_please_report_a_bug));
911
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000912 siemsg(_(e_internal_error_str), where);
Bram Moolenaar95f09602016-11-10 20:01:45 +0100913}
914
Dominique Pelle748b3082022-01-08 12:41:16 +0000915#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaardd589232020-02-29 17:38:12 +0100916/*
917 * Like internal_error() but do not call abort(), to avoid tests using
918 * test_unknown() and test_void() causing Vim to exit.
919 */
920 void
921internal_error_no_abort(char *where)
922{
Bram Moolenaar097c5372023-05-24 21:02:24 +0100923 // Give a generic error which is translated. The error itself may not be
924 // translated, it almost never shows.
925 emsg_core(_(e_internal_error_please_report_a_bug));
926
927 semsg(_(e_internal_error_str), where);
Bram Moolenaardd589232020-02-29 17:38:12 +0100928}
Dominique Pelle748b3082022-01-08 12:41:16 +0000929#endif
Bram Moolenaardd589232020-02-29 17:38:12 +0100930
Bram Moolenaar85a20022019-12-21 18:25:54 +0100931// emsg3() and emsgn() are in misc2.c to avoid warnings for the prototypes.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000932
Bram Moolenaardf177f62005-02-22 08:39:57 +0000933 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100934emsg_invreg(int name)
Bram Moolenaardf177f62005-02-22 08:39:57 +0000935{
cero19881d87e112023-02-16 15:03:12 +0000936 semsg(_(e_invalid_register_name_str), transchar_buf(NULL, name));
Bram Moolenaardf177f62005-02-22 08:39:57 +0000937}
938
Dominique Pelle748b3082022-01-08 12:41:16 +0000939#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000940/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100941 * Give an error message which contains %s for "name[len]".
942 */
943 void
944emsg_namelen(char *msg, char_u *name, int len)
945{
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +0000946 char_u *copy = vim_strnsave(name, len);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100947
948 semsg(msg, copy == NULL ? "NULL" : (char *)copy);
Bram Moolenaar292b90d2020-03-18 15:23:16 +0100949 vim_free(copy);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100950}
Dominique Pelle748b3082022-01-08 12:41:16 +0000951#endif
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100952
953/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000954 * Like msg(), but truncate to a single line if p_shm contains 't', or when
955 * "force" is TRUE. This truncates in another way as for normal messages.
956 * Careful: The string may be changed by msg_may_trunc()!
957 * Returns a pointer to the printed message, if wait_return() not called.
958 */
Bram Moolenaar32526b32019-01-19 17:43:09 +0100959 char *
960msg_trunc_attr(char *s, int force, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000961{
962 int n;
Bram Moolenaar32526b32019-01-19 17:43:09 +0100963 char *ts;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000964
Bram Moolenaar85a20022019-12-21 18:25:54 +0100965 // Add message to history before truncating
Bram Moolenaar32526b32019-01-19 17:43:09 +0100966 add_msg_hist((char_u *)s, -1, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000967
Bram Moolenaar32526b32019-01-19 17:43:09 +0100968 ts = (char *)msg_may_trunc(force, (char_u *)s);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000969
970 msg_hist_off = TRUE;
Bram Moolenaar32526b32019-01-19 17:43:09 +0100971 n = msg_attr(ts, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000972 msg_hist_off = FALSE;
973
974 if (n)
Bram Moolenaar32526b32019-01-19 17:43:09 +0100975 return ts;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000976 return NULL;
977}
978
979/*
980 * Check if message "s" should be truncated at the start (for filenames).
981 * Return a pointer to where the truncated message starts.
982 * Note: May change the message by replacing a character with '<'.
983 */
984 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100985msg_may_trunc(int force, char_u *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000986{
987 int n;
988 int room;
989
Bram Moolenaar9198de32022-08-27 21:30:03 +0100990 // If 'cmdheight' is zero or something unexpected happened "room" may be
991 // negative.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000992 room = (int)(Rows - cmdline_row - 1) * Columns + sc_col - 1;
Bram Moolenaar0fbc9262022-06-26 11:17:10 +0100993 if (room > 0 && (force || (shortmess(SHM_TRUNC) && !exmode_active))
Bram Moolenaara2a89732022-08-31 14:46:18 +0100994 && (n = (int)STRLEN(s) - room) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000995 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000996 if (has_mbyte)
997 {
998 int size = vim_strsize(s);
999
Bram Moolenaar85a20022019-12-21 18:25:54 +01001000 // There may be room anyway when there are multibyte chars.
Bram Moolenaarf4cd3e82005-12-22 22:47:02 +00001001 if (size <= room)
1002 return s;
1003
Bram Moolenaar071d4272004-06-13 20:20:40 +00001004 for (n = 0; size >= room; )
1005 {
1006 size -= (*mb_ptr2cells)(s + n);
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00001007 n += (*mb_ptr2len)(s + n);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001008 }
1009 --n;
1010 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001011 s += n;
1012 *s = '<';
1013 }
1014 return s;
1015}
1016
1017 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001018add_msg_hist(
1019 char_u *s,
Bram Moolenaar85a20022019-12-21 18:25:54 +01001020 int len, // -1 for undetermined length
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001021 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001022{
1023 struct msg_hist *p;
1024
1025 if (msg_hist_off || msg_silent != 0)
1026 return;
1027
Bram Moolenaar85a20022019-12-21 18:25:54 +01001028 // allocate an entry and add the message at the end of the history
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001029 p = ALLOC_ONE(struct msg_hist);
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001030 if (p == NULL)
1031 return;
1032
1033 if (len < 0)
1034 len = (int)STRLEN(s);
1035 // remove leading and trailing newlines
1036 while (len > 0 && *s == '\n')
Bram Moolenaar071d4272004-06-13 20:20:40 +00001037 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001038 ++s;
1039 --len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001040 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001041 while (len > 0 && s[len - 1] == '\n')
1042 --len;
1043 p->msg = vim_strnsave(s, len);
1044 p->next = NULL;
1045 p->attr = attr;
1046 if (last_msg_hist != NULL)
1047 last_msg_hist->next = p;
1048 last_msg_hist = p;
1049 if (first_msg_hist == NULL)
1050 first_msg_hist = last_msg_hist;
1051 ++msg_hist_len;
Shougo Matsushita9f860a12024-11-24 13:48:05 +01001052
1053 check_msg_hist();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001054}
1055
1056/*
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00001057 * Delete the first (oldest) message from the history.
1058 * Returns FAIL if there are no messages.
1059 */
1060 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001061delete_first_msg(void)
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00001062{
1063 struct msg_hist *p;
1064
1065 if (msg_hist_len <= 0)
1066 return FAIL;
1067 p = first_msg_hist;
1068 first_msg_hist = p->next;
Bram Moolenaara7241f52008-06-24 20:39:31 +00001069 if (first_msg_hist == NULL)
Bram Moolenaar85a20022019-12-21 18:25:54 +01001070 last_msg_hist = NULL; // history is empty
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00001071 vim_free(p->msg);
1072 vim_free(p);
1073 --msg_hist_len;
1074 return OK;
1075}
1076
Christian Brabandt51d4d842024-12-06 17:26:25 +01001077 static void
Shougo Matsushita9f860a12024-11-24 13:48:05 +01001078check_msg_hist(void)
1079{
1080 // Don't let the message history get too big
Christian Brabandt51d4d842024-12-06 17:26:25 +01001081 while (msg_hist_len > 0 && msg_hist_len > msg_hist_max)
Shougo Matsushita9f860a12024-11-24 13:48:05 +01001082 (void)delete_first_msg();
1083}
1084
Christian Brabandt51d4d842024-12-06 17:26:25 +01001085
1086 int
1087messagesopt_changed(void)
1088{
1089 char_u *p;
1090 int messages_flags_new = 0;
1091 int messages_wait_new = 0;
1092 int messages_history_new = 0;
1093
zeertzjq8cc43da2024-12-07 16:09:08 +01001094 p = p_mopt;
Christian Brabandt51d4d842024-12-06 17:26:25 +01001095 while (*p != NUL)
1096 {
1097 if (STRNCMP(p, MESSAGES_OPT_HIT_ENTER,
1098 STRLEN_LITERAL(MESSAGES_OPT_HIT_ENTER)) == 0)
1099 {
1100 p += STRLEN_LITERAL(MESSAGES_OPT_HIT_ENTER);
1101 messages_flags_new |= MESSAGES_HIT_ENTER;
1102 }
1103 else if (STRNCMP(p, MESSAGES_OPT_WAIT,
1104 STRLEN_LITERAL(MESSAGES_OPT_WAIT)) == 0
1105 && VIM_ISDIGIT(p[STRLEN_LITERAL(MESSAGES_OPT_WAIT)]))
1106 {
1107 p += STRLEN_LITERAL(MESSAGES_OPT_WAIT);
1108 messages_wait_new = getdigits(&p);
1109 messages_flags_new |= MESSAGES_WAIT;
1110 }
1111 else if (STRNCMP(p, MESSAGES_OPT_HISTORY,
1112 STRLEN_LITERAL(MESSAGES_OPT_HISTORY)) == 0
1113 && VIM_ISDIGIT(p[STRLEN_LITERAL(MESSAGES_OPT_HISTORY)]))
1114 {
1115 p += STRLEN_LITERAL(MESSAGES_OPT_HISTORY);
1116 messages_history_new = getdigits(&p);
1117 messages_flags_new |= MESSAGES_HISTORY;
1118 }
1119
1120 if (*p != ',' && *p != NUL)
1121 return FAIL;
1122 if (*p == ',')
1123 ++p;
1124 }
1125
1126 // Either "wait" or "hit-enter" is required
1127 if (!(messages_flags_new & (MESSAGES_HIT_ENTER | MESSAGES_WAIT)))
Naruhiko Nishinoc2a90002025-05-04 20:05:47 +02001128 return FAIL;
Christian Brabandt51d4d842024-12-06 17:26:25 +01001129
1130 // "history" must be set
1131 if (!(messages_flags_new & MESSAGES_HISTORY))
Naruhiko Nishinoc2a90002025-05-04 20:05:47 +02001132 return FAIL;
Christian Brabandt51d4d842024-12-06 17:26:25 +01001133
h-east65be8342024-12-08 10:05:26 +01001134 if (messages_history_new < 0 || messages_history_new > 10000)
Naruhiko Nishinoc2a90002025-05-04 20:05:47 +02001135 return FAIL;
Christian Brabandt51d4d842024-12-06 17:26:25 +01001136
h-east65be8342024-12-08 10:05:26 +01001137 if (messages_wait_new < 0 || messages_wait_new > 10000)
Naruhiko Nishinoc2a90002025-05-04 20:05:47 +02001138 return FAIL;
Shougo Matsushitad9e9f892024-12-07 16:00:25 +01001139
Christian Brabandt51d4d842024-12-06 17:26:25 +01001140 msg_flags = messages_flags_new;
1141 msg_wait = messages_wait_new;
1142
1143 msg_hist_max = messages_history_new;
1144 check_msg_hist();
1145
1146 return OK;
1147}
1148
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00001149/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001150 * ":messages" command.
1151 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001152 void
Bram Moolenaar52196b22016-04-14 17:52:41 +02001153ex_messages(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001154{
1155 struct msg_hist *p;
1156 char_u *s;
Bram Moolenaar451f8492016-04-14 17:16:22 +02001157 int c = 0;
1158
1159 if (STRCMP(eap->arg, "clear") == 0)
1160 {
1161 int keep = eap->addr_count == 0 ? 0 : eap->line2;
1162
1163 while (msg_hist_len > keep)
1164 (void)delete_first_msg();
1165 return;
1166 }
1167
1168 if (*eap->arg != NUL)
1169 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001170 emsg(_(e_invalid_argument));
Bram Moolenaar451f8492016-04-14 17:16:22 +02001171 return;
1172 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001173
1174 msg_hist_off = TRUE;
1175
Bram Moolenaar451f8492016-04-14 17:16:22 +02001176 p = first_msg_hist;
Bram Moolenaar451f8492016-04-14 17:16:22 +02001177 if (eap->addr_count != 0)
1178 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01001179 // Count total messages
Bram Moolenaar451f8492016-04-14 17:16:22 +02001180 for (; p != NULL && !got_int; p = p->next)
1181 c++;
1182
1183 c -= eap->line2;
1184
Bram Moolenaar85a20022019-12-21 18:25:54 +01001185 // Skip without number of messages specified
Bram Moolenaar451f8492016-04-14 17:16:22 +02001186 for (p = first_msg_hist; p != NULL && !got_int && c > 0;
1187 p = p->next, c--);
1188 }
1189
Bram Moolenaarbea1ede2016-04-14 19:44:36 +02001190 if (p == first_msg_hist)
1191 {
Bram Moolenaar41f69182020-04-25 15:45:37 +02001192#ifdef FEAT_MULTI_LANG
1193 s = get_mess_lang();
1194#else
Bram Moolenaarbea1ede2016-04-14 19:44:36 +02001195 s = mch_getenv((char_u *)"LANG");
Bram Moolenaar41f69182020-04-25 15:45:37 +02001196#endif
Bram Moolenaarbea1ede2016-04-14 19:44:36 +02001197 if (s != NULL && *s != NUL)
Bram Moolenaar0c183192018-06-28 14:54:43 +02001198 // The next comment is extracted by xgettext and put in po file for
1199 // translators to read.
Bram Moolenaar32526b32019-01-19 17:43:09 +01001200 msg_attr(
Bram Moolenaar0c183192018-06-28 14:54:43 +02001201 // Translator: Please replace the name and email address
1202 // with the appropriate text for your translation.
Christian Brabandte978b452023-08-13 10:33:05 +02001203 _("Messages maintainer: The Vim Project"),
Bram Moolenaar8820b482017-03-16 17:23:31 +01001204 HL_ATTR(HLF_T));
Bram Moolenaarbea1ede2016-04-14 19:44:36 +02001205 }
1206
Bram Moolenaar85a20022019-12-21 18:25:54 +01001207 // Display what was not skipped.
Bram Moolenaar451f8492016-04-14 17:16:22 +02001208 for (; p != NULL && !got_int; p = p->next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001209 if (p->msg != NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001210 msg_attr((char *)p->msg, p->attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001211
1212 msg_hist_off = FALSE;
1213}
1214
Bram Moolenaar7171abe2004-10-11 10:06:20 +00001215#if defined(FEAT_CON_DIALOG) || defined(FIND_REPLACE_DIALOG) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001216/*
1217 * Call this after prompting the user. This will avoid a hit-return message
1218 * and a delay.
1219 */
Bram Moolenaar7171abe2004-10-11 10:06:20 +00001220 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001221msg_end_prompt(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001222{
1223 need_wait_return = FALSE;
1224 emsg_on_display = FALSE;
1225 cmdline_row = msg_row;
1226 msg_col = 0;
1227 msg_clr_eos();
Bram Moolenaar5d6f75e2011-12-30 14:14:29 +01001228 lines_left = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001229}
1230#endif
1231
1232/*
Bram Moolenaar32526b32019-01-19 17:43:09 +01001233 * Wait for the user to hit a key (normally Enter).
1234 * If "redraw" is TRUE, clear and redraw the screen.
1235 * If "redraw" is FALSE, just redraw the screen.
1236 * If "redraw" is -1, don't redraw at all.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001237 */
1238 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001239wait_return(int redraw)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001240{
1241 int c;
1242 int oldState;
1243 int tmpState;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244 int had_got_int;
Bram Moolenaar0b6d9112018-05-22 20:35:17 +02001245 int save_reg_recording;
Bram Moolenaar332a2ca2013-11-04 02:01:01 +01001246 FILE *save_scriptout;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001247
1248 if (redraw == TRUE)
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001249 set_must_redraw(UPD_CLEAR);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001250
Bram Moolenaar85a20022019-12-21 18:25:54 +01001251 // If using ":silent cmd", don't wait for a return. Also don't set
1252 // need_wait_return to do it later.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001253 if (msg_silent != 0)
1254 return;
Bram Moolenaarcf0995d2022-09-11 21:36:17 +01001255#ifdef HAS_MESSAGE_WINDOW
1256 if (in_echowindow)
1257 return;
1258#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259
Bram Moolenaarfd30cd42011-03-22 13:07:26 +01001260 /*
1261 * When inside vgetc(), we can't wait for a typed character at all.
1262 * With the global command (and some others) we only need one return at
1263 * the end. Adjust cmdline_row to avoid the next message overwriting the
1264 * last one.
1265 */
Bram Moolenaar5555acc2006-04-07 21:33:12 +00001266 if (vgetc_busy > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001267 return;
Bram Moolenaarfd30cd42011-03-22 13:07:26 +01001268 need_wait_return = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001269 if (no_wait_return)
1270 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001271 if (!exmode_active)
1272 cmdline_row = msg_row;
1273 return;
1274 }
1275
Bram Moolenaar85a20022019-12-21 18:25:54 +01001276 redir_off = TRUE; // don't redirect this message
Bram Moolenaar071d4272004-06-13 20:20:40 +00001277 oldState = State;
1278 if (quit_more)
1279 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01001280 c = CAR; // just pretend CR was hit
Bram Moolenaar071d4272004-06-13 20:20:40 +00001281 quit_more = FALSE;
1282 got_int = FALSE;
1283 }
1284 else if (exmode_active)
1285 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01001286 msg_puts(" "); // make sure the cursor is on the right line
1287 c = CAR; // no need for a return in ex mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00001288 got_int = FALSE;
1289 }
1290 else
1291 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01001292 // Make sure the hit-return prompt is on screen when 'guioptions' was
1293 // just changed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001294 screenalloc(FALSE);
1295
Bram Moolenaar24959102022-05-07 20:01:16 +01001296 State = MODE_HITRETURN;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001297 setmouse();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001298#ifdef USE_ON_FLY_SCROLL
Bram Moolenaar85a20022019-12-21 18:25:54 +01001299 dont_scroll = TRUE; // disallow scrolling here
Bram Moolenaar071d4272004-06-13 20:20:40 +00001300#endif
Bram Moolenaarbfb96c02016-03-19 17:05:20 +01001301 cmdline_row = msg_row;
1302
Bram Moolenaar85a20022019-12-21 18:25:54 +01001303 // Avoid the sequence that the user types ":" at the hit-return prompt
1304 // to start an Ex command, but the file-changed dialog gets in the
1305 // way.
Bram Moolenaarec38d692013-04-24 15:12:32 +02001306 if (need_check_timestamps)
1307 check_timestamps(FALSE);
1308
Christian Brabandt51d4d842024-12-06 17:26:25 +01001309 if (msg_flags & MESSAGES_HIT_ENTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001310 {
Christian Brabandt51d4d842024-12-06 17:26:25 +01001311 hit_return_msg();
Bram Moolenaarf95dc3b2005-05-22 22:02:25 +00001312
Christian Brabandt51d4d842024-12-06 17:26:25 +01001313 do
1314 {
1315 // Remember "got_int", if it is set vgetc() probably returns a
1316 // CTRL-C, but we need to loop then.
1317 had_got_int = got_int;
Bram Moolenaar332a2ca2013-11-04 02:01:01 +01001318
Christian Brabandt51d4d842024-12-06 17:26:25 +01001319 // Don't do mappings here, we put the character back in the
1320 // typeahead buffer.
1321 ++no_mapping;
1322 ++allow_keys;
1323
1324 // Temporarily disable Recording. If Recording is active, the
h-east36f36712024-12-09 20:05:59 +01001325 // character will be recorded later, since it will be added to
1326 // the typebuf after the loop
Christian Brabandt51d4d842024-12-06 17:26:25 +01001327 save_reg_recording = reg_recording;
1328 save_scriptout = scriptout;
1329 reg_recording = 0;
1330 scriptout = NULL;
1331 c = safe_vgetc();
1332 if (had_got_int && !global_busy)
1333 got_int = FALSE;
1334 --no_mapping;
1335 --allow_keys;
1336 reg_recording = save_reg_recording;
1337 scriptout = save_scriptout;
Bram Moolenaarf95dc3b2005-05-22 22:02:25 +00001338
Bram Moolenaar071d4272004-06-13 20:20:40 +00001339#ifdef FEAT_CLIPBOARD
h-east36f36712024-12-09 20:05:59 +01001340 // Strange way to allow copying (yanking) a modeless selection
1341 // at the hit-enter prompt. Use CTRL-Y, because the same is
1342 // used in Cmdline-mode and it's harmless when there is no
1343 // selection.
Christian Brabandt51d4d842024-12-06 17:26:25 +01001344 if (c == Ctrl_Y && clip_star.state == SELECT_DONE)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00001345 {
Christian Brabandt51d4d842024-12-06 17:26:25 +01001346 clip_copy_modeless_selection(TRUE);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00001347 c = K_IGNORE;
Christian Brabandt51d4d842024-12-06 17:26:25 +01001348 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001349#endif
Bram Moolenaar88456cd2022-11-18 22:14:09 +00001350
Christian Brabandt51d4d842024-12-06 17:26:25 +01001351 /*
1352 * Allow scrolling back in the messages.
h-east36f36712024-12-09 20:05:59 +01001353 * Also accept scroll-down commands when messages fill the
1354 * screen, to avoid that typing one 'j' too many makes the
1355 * messages disappear.
Christian Brabandt51d4d842024-12-06 17:26:25 +01001356 */
1357 if (p_more && !p_cp)
1358 {
1359 if (c == 'b' || c == 'k' || c == 'u' || c == 'g'
h-east36f36712024-12-09 20:05:59 +01001360 || c == K_UP || c == K_PAGEUP)
Christian Brabandt51d4d842024-12-06 17:26:25 +01001361 {
1362 if (msg_scrolled > Rows)
1363 // scroll back to show older messages
1364 do_more_prompt(c);
1365 else
1366 {
1367 msg_didout = FALSE;
1368 c = K_IGNORE;
1369 msg_col =
1370#ifdef FEAT_RIGHTLEFT
1371 cmdmsg_rl ? Columns - 1 :
1372#endif
1373 0;
1374 }
1375 if (quit_more)
1376 {
1377 c = CAR; // just pretend CR was hit
1378 quit_more = FALSE;
1379 got_int = FALSE;
1380 }
1381 else if (c != K_IGNORE)
1382 {
1383 c = K_IGNORE;
1384 hit_return_msg();
1385 }
1386 }
1387 else if (msg_scrolled > Rows - 2
1388 && (c == 'j' || c == 'd' || c == 'f'
1389 || c == K_DOWN || c == K_PAGEDOWN))
1390 c = K_IGNORE;
1391 }
1392 } while ((had_got_int && c == Ctrl_C)
h-east36f36712024-12-09 20:05:59 +01001393 || c == K_IGNORE
Christian Brabandt51d4d842024-12-06 17:26:25 +01001394#ifdef FEAT_GUI
h-east36f36712024-12-09 20:05:59 +01001395 || c == K_VER_SCROLLBAR || c == K_HOR_SCROLLBAR
Christian Brabandt51d4d842024-12-06 17:26:25 +01001396#endif
h-east36f36712024-12-09 20:05:59 +01001397 || c == K_LEFTDRAG || c == K_LEFTRELEASE
1398 || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE
1399 || c == K_RIGHTDRAG || c == K_RIGHTRELEASE
1400 || c == K_MOUSELEFT || c == K_MOUSERIGHT
1401 || c == K_MOUSEDOWN || c == K_MOUSEUP
1402 || c == K_MOUSEMOVE
1403 || (!mouse_has(MOUSE_RETURN)
1404 && mouse_row < msg_row
1405 && (c == K_LEFTMOUSE
1406 || c == K_MIDDLEMOUSE
1407 || c == K_RIGHTMOUSE
1408 || c == K_X1MOUSE
1409 || c == K_X2MOUSE))
1410 );
Christian Brabandt51d4d842024-12-06 17:26:25 +01001411 ui_breakcheck();
1412
1413 // Avoid that the mouse-up event causes Visual mode to start.
1414 if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE
h-east36f36712024-12-09 20:05:59 +01001415 || c == K_X1MOUSE || c == K_X2MOUSE)
Christian Brabandt51d4d842024-12-06 17:26:25 +01001416 (void)jump_to_mouse(MOUSE_SETPOS, NULL, 0);
1417 else if (vim_strchr((char_u *)"\r\n ", c) == NULL && c != Ctrl_C)
1418 {
h-east36f36712024-12-09 20:05:59 +01001419 // Put the character back in the typeahead buffer. Don't use
1420 // the stuff buffer, because lmaps wouldn't work.
Christian Brabandt51d4d842024-12-06 17:26:25 +01001421 ins_char_typebuf(vgetc_char, vgetc_mod_mask);
h-east36f36712024-12-09 20:05:59 +01001422 do_redraw = TRUE; // need a redraw even though there is
Christian Brabandt51d4d842024-12-06 17:26:25 +01001423 // typeahead
1424 }
1425 }
1426 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001427 {
Christian Brabandt51d4d842024-12-06 17:26:25 +01001428 c = CAR;
1429 // Wait to allow the user to verify the output.
1430 do_sleep(msg_wait, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001431 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001432 }
1433 redir_off = FALSE;
1434
1435 /*
1436 * If the user hits ':', '?' or '/' we get a command line from the next
1437 * line.
1438 */
1439 if (c == ':' || c == '?' || c == '/')
1440 {
1441 if (!exmode_active)
1442 cmdline_row = msg_row;
Bram Moolenaar85a20022019-12-21 18:25:54 +01001443 skip_redraw = TRUE; // skip redraw once
Bram Moolenaar071d4272004-06-13 20:20:40 +00001444 do_redraw = FALSE;
Bram Moolenaar1d4754f2018-06-19 17:49:24 +02001445#ifdef FEAT_TERMINAL
1446 skip_term_loop = TRUE;
1447#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001448 }
1449
1450 /*
1451 * If the window size changed set_shellsize() will redraw the screen.
1452 * Otherwise the screen is only redrawn if 'redraw' is set and no ':'
1453 * typed.
1454 */
1455 tmpState = State;
Bram Moolenaar85a20022019-12-21 18:25:54 +01001456 State = oldState; // restore State before set_shellsize
Bram Moolenaar071d4272004-06-13 20:20:40 +00001457 setmouse();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001458 msg_check();
1459
1460#if defined(UNIX) || defined(VMS)
1461 /*
1462 * When switching screens, we need to output an extra newline on exit.
1463 */
1464 if (swapping_screen() && !termcap_active)
1465 newline_on_exit = TRUE;
1466#endif
1467
1468 need_wait_return = FALSE;
1469 did_wait_return = TRUE;
Bram Moolenaar85a20022019-12-21 18:25:54 +01001470 emsg_on_display = FALSE; // can delete error message now
1471 lines_left = -1; // reset lines_left at next msg_start()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001472 reset_last_sourcing();
1473 if (keep_msg != NULL && vim_strsize(keep_msg) >=
1474 (Rows - cmdline_row - 1) * Columns + sc_col)
Bram Moolenaar85a20022019-12-21 18:25:54 +01001475 VIM_CLEAR(keep_msg); // don't redisplay message, it's too long
Bram Moolenaar071d4272004-06-13 20:20:40 +00001476
Bram Moolenaar24959102022-05-07 20:01:16 +01001477 if (tmpState == MODE_SETWSIZE) // got resize event while in vgetc()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001478 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01001479 starttermcap(); // start termcap before redrawing
Bram Moolenaar071d4272004-06-13 20:20:40 +00001480 shell_resized();
1481 }
1482 else if (!skip_redraw
1483 && (redraw == TRUE || (msg_scrolled != 0 && redraw != -1)))
1484 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01001485 starttermcap(); // start termcap before redrawing
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001486 redraw_later(UPD_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001487 }
1488}
1489
1490/*
1491 * Write the hit-return prompt.
1492 */
1493 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001494hit_return_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001495{
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00001496 int save_p_more = p_more;
1497
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00001498 p_more = FALSE; // don't want to see this message when scrolling back
Bram Moolenaar85a20022019-12-21 18:25:54 +01001499 if (msg_didout) // start on a new line
Bram Moolenaar071d4272004-06-13 20:20:40 +00001500 msg_putchar('\n');
1501 if (got_int)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001502 msg_puts(_("Interrupt: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001503
Bram Moolenaar32526b32019-01-19 17:43:09 +01001504 msg_puts_attr(_("Press ENTER or type command to continue"), HL_ATTR(HLF_R));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001505 if (!msg_use_printf())
1506 msg_clr_eos();
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00001507 p_more = save_p_more;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508}
1509
1510/*
1511 * Set "keep_msg" to "s". Free the old value and check for NULL pointer.
1512 */
1513 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001514set_keep_msg(char_u *s, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001515{
1516 vim_free(keep_msg);
1517 if (s != NULL && msg_silent == 0)
1518 keep_msg = vim_strsave(s);
1519 else
1520 keep_msg = NULL;
Bram Moolenaaraab21c32005-01-25 21:46:35 +00001521 keep_msg_more = FALSE;
Bram Moolenaar030f0df2006-02-21 22:02:53 +00001522 keep_msg_attr = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001523}
1524
Bram Moolenaar030f0df2006-02-21 22:02:53 +00001525/*
1526 * If there currently is a message being displayed, set "keep_msg" to it, so
1527 * that it will be displayed again after redraw.
1528 */
1529 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001530set_keep_msg_from_hist(void)
Bram Moolenaar030f0df2006-02-21 22:02:53 +00001531{
1532 if (keep_msg == NULL && last_msg_hist != NULL && msg_scrolled == 0
Bram Moolenaar24959102022-05-07 20:01:16 +01001533 && (State & MODE_NORMAL))
Bram Moolenaar030f0df2006-02-21 22:02:53 +00001534 set_keep_msg(last_msg_hist->msg, last_msg_hist->attr);
1535}
Bram Moolenaar030f0df2006-02-21 22:02:53 +00001536
Bram Moolenaar071d4272004-06-13 20:20:40 +00001537/*
1538 * Prepare for outputting characters in the command line.
1539 */
1540 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001541msg_start(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001542{
1543 int did_return = FALSE;
1544
zeertzjq40ed6712023-11-23 20:37:01 +01001545 if (msg_row < cmdline_row)
1546 msg_row = cmdline_row;
1547
Bram Moolenaare4ce65d2010-08-04 20:12:32 +02001548 if (!msg_silent)
Rob Pilling726f7f92022-01-20 14:44:38 +00001549 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01001550 VIM_CLEAR(keep_msg);
Rob Pilling726f7f92022-01-20 14:44:38 +00001551 need_fileinfo = FALSE;
1552 }
Bram Moolenaara7241f52008-06-24 20:39:31 +00001553
1554#ifdef FEAT_EVAL
Bram Moolenaar3b474dc2022-09-01 17:01:32 +01001555 if (need_clr_eos)
Bram Moolenaara7241f52008-06-24 20:39:31 +00001556 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01001557 // Halfway an ":echo" command and getting an (error) message: clear
1558 // any text from the command.
Bram Moolenaara7241f52008-06-24 20:39:31 +00001559 need_clr_eos = FALSE;
1560 msg_clr_eos();
1561 }
1562#endif
1563
Bram Moolenaard54af2e2022-08-27 22:05:13 +01001564#ifdef HAS_MESSAGE_WINDOW
Bram Moolenaara2a89732022-08-31 14:46:18 +01001565 if (in_echowindow)
Bram Moolenaar9198de32022-08-27 21:30:03 +01001566 {
Bram Moolenaar37fef162022-08-29 18:16:32 +01001567 if (popup_message_win_visible()
1568 && ((msg_col > 0 && (msg_scroll || !full_screen))
1569 || in_echowindow))
Bram Moolenaar9198de32022-08-27 21:30:03 +01001570 {
1571 win_T *wp = popup_get_message_win();
1572
Bram Moolenaarf2fb54f2022-08-28 20:58:51 +01001573 // start a new line
Bram Moolenaar9198de32022-08-27 21:30:03 +01001574 curbuf = wp->w_buffer;
1575 ml_append(wp->w_buffer->b_ml.ml_line_count,
1576 (char_u *)"", (colnr_T)0, FALSE);
1577 curbuf = curwin->w_buffer;
1578 }
1579 msg_col = 0;
1580 }
Bram Moolenaard54af2e2022-08-27 22:05:13 +01001581 else
1582#endif
1583 if (!msg_scroll && full_screen) // overwrite last message
Bram Moolenaar071d4272004-06-13 20:20:40 +00001584 {
1585 msg_row = cmdline_row;
1586 msg_col =
1587#ifdef FEAT_RIGHTLEFT
1588 cmdmsg_rl ? Columns - 1 :
1589#endif
1590 0;
1591 }
Bram Moolenaara2a89732022-08-31 14:46:18 +01001592 else if (msg_didout || in_echowindow)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001593 {
Bram Moolenaar309c4e02022-08-29 12:23:39 +01001594 // start message on next line
Bram Moolenaar071d4272004-06-13 20:20:40 +00001595 msg_putchar('\n');
1596 did_return = TRUE;
1597 if (exmode_active != EXMODE_NORMAL)
1598 cmdline_row = msg_row;
1599 }
1600 if (!msg_didany || lines_left < 0)
1601 msg_starthere();
1602 if (msg_silent == 0)
1603 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01001604 msg_didout = FALSE; // no output on current line yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00001605 cursor_off();
1606 }
1607
Bram Moolenaar85a20022019-12-21 18:25:54 +01001608 // when redirecting, may need to start a new line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001609 if (!did_return)
1610 redir_write((char_u *)"\n", -1);
1611}
1612
1613/*
1614 * Note that the current msg position is where messages start.
1615 */
1616 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001617msg_starthere(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001618{
1619 lines_left = cmdline_row;
1620 msg_didany = FALSE;
1621}
1622
1623 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001624msg_putchar(int c)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001625{
1626 msg_putchar_attr(c, 0);
1627}
1628
1629 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001630msg_putchar_attr(int c, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001631{
Bram Moolenaar63c0ccd2019-01-19 21:06:58 +01001632 char_u buf[MB_MAXBYTES + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001633
1634 if (IS_SPECIAL(c))
1635 {
1636 buf[0] = K_SPECIAL;
1637 buf[1] = K_SECOND(c);
1638 buf[2] = K_THIRD(c);
1639 buf[3] = NUL;
1640 }
1641 else
Bram Moolenaar63c0ccd2019-01-19 21:06:58 +01001642 buf[(*mb_char2bytes)(c, buf)] = NUL;
Bram Moolenaar63c0ccd2019-01-19 21:06:58 +01001643 msg_puts_attr((char *)buf, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001644}
1645
1646 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001647msg_outnum(long n)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001648{
Bram Moolenaar32526b32019-01-19 17:43:09 +01001649 char buf[20];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001650
Bram Moolenaar32526b32019-01-19 17:43:09 +01001651 sprintf(buf, "%ld", n);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001652 msg_puts(buf);
1653}
1654
1655 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001656msg_home_replace(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001657{
1658 msg_home_replace_attr(fname, 0);
1659}
1660
1661#if defined(FEAT_FIND_ID) || defined(PROTO)
1662 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001663msg_home_replace_hl(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001664{
Bram Moolenaar8820b482017-03-16 17:23:31 +01001665 msg_home_replace_attr(fname, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001666}
1667#endif
1668
1669 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001670msg_home_replace_attr(char_u *fname, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001671{
1672 char_u *name;
1673
1674 name = home_replace_save(NULL, fname);
1675 if (name != NULL)
1676 msg_outtrans_attr(name, attr);
1677 vim_free(name);
1678}
1679
1680/*
1681 * Output 'len' characters in 'str' (including NULs) with translation
Bram Moolenaarf48ee3c2019-12-06 22:18:20 +01001682 * if 'len' is -1, output up to a NUL character.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001683 * Use attributes 'attr'.
1684 * Return the number of characters it takes on the screen.
1685 */
1686 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001687msg_outtrans(char_u *str)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001688{
1689 return msg_outtrans_attr(str, 0);
1690}
1691
1692 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001693msg_outtrans_attr(char_u *str, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001694{
1695 return msg_outtrans_len_attr(str, (int)STRLEN(str), attr);
1696}
1697
1698 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001699msg_outtrans_len(char_u *str, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001700{
1701 return msg_outtrans_len_attr(str, len, 0);
1702}
1703
1704/*
1705 * Output one character at "p". Return pointer to the next character.
1706 * Handles multi-byte characters.
1707 */
1708 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001709msg_outtrans_one(char_u *p, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001710{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001711 int l;
1712
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00001713 if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001714 {
1715 msg_outtrans_len_attr(p, l, attr);
1716 return p + l;
1717 }
cero19881d87e112023-02-16 15:03:12 +00001718 msg_puts_attr((char *)transchar_byte_buf(NULL, *p), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001719 return p + 1;
1720}
1721
1722 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001723msg_outtrans_len_attr(char_u *msgstr, int len, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001724{
1725 int retval = 0;
1726 char_u *str = msgstr;
1727 char_u *plain_start = msgstr;
1728 char_u *s;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001729 int mb_l;
1730 int c;
Bram Moolenaar3d30af82020-10-13 22:15:56 +02001731 int save_got_int = got_int;
1732
1733 // Only quit when got_int was set in here.
1734 got_int = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001735
Shougo Matsushitabe2b03c2024-04-08 22:11:50 +02001736 if (attr == 0)
1737 attr = HL_ATTR(HLF_MSG);
1738
Bram Moolenaar85a20022019-12-21 18:25:54 +01001739 // if MSG_HIST flag set, add message to history
Bram Moolenaar071d4272004-06-13 20:20:40 +00001740 if (attr & MSG_HIST)
1741 {
1742 add_msg_hist(str, len, attr);
1743 attr &= ~MSG_HIST;
1744 }
1745
Bram Moolenaar800cdbb2023-06-15 16:40:02 +01001746 // When drawing over the command line no need to clear it later or remove
1747 // the mode message.
zeertzjqfce1fa52025-02-27 19:19:36 +01001748 if (msg_silent == 0 && len > 0 && msg_row >= cmdline_row && msg_col == 0)
Bram Moolenaar800cdbb2023-06-15 16:40:02 +01001749 {
1750 clear_cmdline = FALSE;
1751 mode_displayed = FALSE;
1752 }
1753
Bram Moolenaar85a20022019-12-21 18:25:54 +01001754 // If the string starts with a composing character first draw a space on
1755 // which the composing char can be drawn.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001756 if (enc_utf8 && utf_iscomposing(utf_ptr2char(msgstr)))
Bram Moolenaar32526b32019-01-19 17:43:09 +01001757 msg_puts_attr(" ", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001758
1759 /*
1760 * Go over the string. Special characters are translated and printed.
1761 * Normal characters are printed several at a time.
1762 */
Bram Moolenaar3d30af82020-10-13 22:15:56 +02001763 while (--len >= 0 && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001764 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001765 if (enc_utf8)
Bram Moolenaar85a20022019-12-21 18:25:54 +01001766 // Don't include composing chars after the end.
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00001767 mb_l = utfc_ptr2len_len(str, len + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001768 else if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00001769 mb_l = (*mb_ptr2len)(str);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001770 else
1771 mb_l = 1;
1772 if (has_mbyte && mb_l > 1)
1773 {
1774 c = (*mb_ptr2char)(str);
1775 if (vim_isprintc(c))
Bram Moolenaar85a20022019-12-21 18:25:54 +01001776 // printable multi-byte char: count the cells.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001777 retval += (*mb_ptr2cells)(str);
1778 else
1779 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01001780 // unprintable multi-byte char: print the printable chars so
1781 // far and the translation of the unprintable char.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001782 if (str > plain_start)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001783 msg_puts_attr_len((char *)plain_start,
1784 (int)(str - plain_start), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001785 plain_start = str + mb_l;
cero19881d87e112023-02-16 15:03:12 +00001786 msg_puts_attr((char *)transchar_buf(NULL, c),
Bram Moolenaar32526b32019-01-19 17:43:09 +01001787 attr == 0 ? HL_ATTR(HLF_8) : attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001788 retval += char2cells(c);
1789 }
1790 len -= mb_l - 1;
1791 str += mb_l;
1792 }
1793 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001794 {
cero19881d87e112023-02-16 15:03:12 +00001795 s = transchar_byte_buf(NULL, *str);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001796 if (s[1] != NUL)
1797 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01001798 // unprintable char: print the printable chars so far and the
1799 // translation of the unprintable char.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001800 if (str > plain_start)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001801 msg_puts_attr_len((char *)plain_start,
1802 (int)(str - plain_start), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001803 plain_start = str + 1;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001804 msg_puts_attr((char *)s, attr == 0 ? HL_ATTR(HLF_8) : attr);
Bram Moolenaarc236c162008-07-13 17:41:49 +00001805 retval += (int)STRLEN(s);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001806 }
Bram Moolenaarb3c722a2008-07-06 17:16:02 +00001807 else
1808 ++retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001809 ++str;
1810 }
1811 }
1812
Bram Moolenaar3d30af82020-10-13 22:15:56 +02001813 if (str > plain_start && !got_int)
Bram Moolenaar85a20022019-12-21 18:25:54 +01001814 // print the printable chars at the end
Bram Moolenaar32526b32019-01-19 17:43:09 +01001815 msg_puts_attr_len((char *)plain_start, (int)(str - plain_start), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001816
Bram Moolenaar3d30af82020-10-13 22:15:56 +02001817 got_int |= save_got_int;
1818
Bram Moolenaar071d4272004-06-13 20:20:40 +00001819 return retval;
1820}
1821
1822#if defined(FEAT_QUICKFIX) || defined(PROTO)
1823 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001824msg_make(char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001825{
1826 int i;
1827 static char_u *str = (char_u *)"eeffoc", *rs = (char_u *)"Plon#dqg#vxjduB";
1828
1829 arg = skipwhite(arg);
1830 for (i = 5; *arg && i >= 0; --i)
1831 if (*arg++ != str[i])
1832 break;
1833 if (i < 0)
1834 {
1835 msg_putchar('\n');
1836 for (i = 0; rs[i]; ++i)
1837 msg_putchar(rs[i] - 3);
1838 }
1839}
1840#endif
1841
1842/*
Bram Moolenaarf48ee3c2019-12-06 22:18:20 +01001843 * Output the string 'str' up to a NUL character.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001844 * Return the number of characters it takes on the screen.
1845 *
1846 * If K_SPECIAL is encountered, then it is taken in conjunction with the
1847 * following character and shown as <F1>, <S-Up> etc. Any other character
1848 * which is not printable shown in <> form.
1849 * If 'from' is TRUE (lhs of a mapping), a space is shown as <Space>.
1850 * If a character is displayed in one of these special ways, is also
1851 * highlighted (its highlight name is '8' in the p_hl variable).
1852 * Otherwise characters are not highlighted.
1853 * This function is used to show mappings, where we want to see how to type
1854 * the character/string -- webb
1855 */
1856 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001857msg_outtrans_special(
1858 char_u *strstart,
Bram Moolenaar725310d2019-04-24 23:08:23 +02001859 int from, // TRUE for lhs of a mapping
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001860 int maxlen) // screen columns, 0 for unlimited
Bram Moolenaar071d4272004-06-13 20:20:40 +00001861{
1862 char_u *str = strstart;
1863 int retval = 0;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001864 char *text;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001865 int attr;
1866 int len;
1867
Bram Moolenaar8820b482017-03-16 17:23:31 +01001868 attr = HL_ATTR(HLF_8);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001869 while (*str != NUL)
1870 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01001871 // Leading and trailing spaces need to be displayed in <> form.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001872 if ((str == strstart || str[1] == NUL) && *str == ' ')
1873 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001874 text = "<Space>";
Bram Moolenaar071d4272004-06-13 20:20:40 +00001875 ++str;
1876 }
1877 else
zeertzjqcdc83932022-09-12 13:38:41 +01001878 text = (char *)str2special(&str, from, FALSE);
zeertzjq0519ce02022-05-09 12:16:19 +01001879 if (text[0] != NUL && text[1] == NUL)
1880 // single-byte character or illegal byte
cero19881d87e112023-02-16 15:03:12 +00001881 text = (char *)transchar_byte_buf(NULL, (char_u)text[0]);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001882 len = vim_strsize((char_u *)text);
Bram Moolenaar725310d2019-04-24 23:08:23 +02001883 if (maxlen > 0 && retval + len >= maxlen)
1884 break;
Bram Moolenaar85a20022019-12-21 18:25:54 +01001885 // Highlight special keys
Bram Moolenaar32526b32019-01-19 17:43:09 +01001886 msg_puts_attr(text, len > 1
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001887 && (*mb_ptr2len)((char_u *)text) <= 1 ? attr : 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001888 retval += len;
1889 }
1890 return retval;
1891}
1892
Martin Tournoijba43e762022-10-13 22:12:15 +01001893#if defined(FEAT_EVAL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaarbd743252010-10-20 21:23:33 +02001894/*
1895 * Return the lhs or rhs of a mapping, with the key codes turned into printable
1896 * strings, in an allocated string.
1897 */
1898 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001899str2special_save(
1900 char_u *str,
zeertzjqcdc83932022-09-12 13:38:41 +01001901 int replace_spaces, // TRUE to replace " " with "<Space>".
1902 // used for the lhs of mapping and keytrans().
1903 int replace_lt) // TRUE to replace "<" with "<lt>".
Bram Moolenaarbd743252010-10-20 21:23:33 +02001904{
1905 garray_T ga;
1906 char_u *p = str;
1907
1908 ga_init2(&ga, 1, 40);
1909 while (*p != NUL)
zeertzjqcdc83932022-09-12 13:38:41 +01001910 ga_concat(&ga, str2special(&p, replace_spaces, replace_lt));
Bram Moolenaarbd743252010-10-20 21:23:33 +02001911 ga_append(&ga, NUL);
1912 return (char_u *)ga.ga_data;
1913}
1914#endif
1915
Bram Moolenaar071d4272004-06-13 20:20:40 +00001916/*
1917 * Return the printable string for the key codes at "*sp".
zeertzjq0519ce02022-05-09 12:16:19 +01001918 * On illegal byte return a string with only that byte.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001919 * Used for translating the lhs or rhs of a mapping to printable chars.
1920 * Advances "sp" to the next code.
1921 */
1922 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001923str2special(
1924 char_u **sp,
zeertzjqcdc83932022-09-12 13:38:41 +01001925 int replace_spaces, // TRUE to replace " " with "<Space>".
1926 // used for the lhs of mapping and keytrans().
1927 int replace_lt) // TRUE to replace "<" with "<lt>".
Bram Moolenaar071d4272004-06-13 20:20:40 +00001928{
1929 int c;
1930 static char_u buf[7];
1931 char_u *str = *sp;
1932 int modifiers = 0;
1933 int special = FALSE;
1934
Bram Moolenaar071d4272004-06-13 20:20:40 +00001935 if (has_mbyte)
1936 {
1937 char_u *p;
1938
Bram Moolenaar85a20022019-12-21 18:25:54 +01001939 // Try to un-escape a multi-byte character. Return the un-escaped
1940 // string if it is a multi-byte character.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001941 p = mb_unescape(sp);
1942 if (p != NULL)
1943 return p;
1944 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001945
1946 c = *str;
zeertzjq1bed9932023-08-30 19:38:24 +02001947 if ((c == K_SPECIAL
1948#ifdef FEAT_GUI
1949 || c == CSI
1950#endif
1951 ) && str[1] != NUL && str[2] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001952 {
1953 if (str[1] == KS_MODIFIER)
1954 {
1955 modifiers = str[2];
1956 str += 3;
1957 c = *str;
1958 }
zeertzjq1bed9932023-08-30 19:38:24 +02001959 if ((c == K_SPECIAL
1960#ifdef FEAT_GUI
1961 || c == CSI
1962#endif
1963 ) && str[1] != NUL && str[2] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001964 {
1965 c = TO_SPECIAL(str[1], str[2]);
1966 str += 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001967 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01001968 if (IS_SPECIAL(c) || modifiers) // special key
Bram Moolenaar071d4272004-06-13 20:20:40 +00001969 special = TRUE;
1970 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001971
zeertzjq0519ce02022-05-09 12:16:19 +01001972 if (has_mbyte && !IS_SPECIAL(c) && MB_BYTE2LEN(c) > 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001973 {
zeertzjqac402f42022-05-04 18:51:43 +01001974 char_u *p;
Bram Moolenaarb8bf5412011-08-17 20:33:22 +02001975
zeertzjqac402f42022-05-04 18:51:43 +01001976 *sp = str;
1977 // Try to un-escape a multi-byte character after modifiers.
1978 p = mb_unescape(sp);
zeertzjq0519ce02022-05-09 12:16:19 +01001979 if (p != NULL)
1980 // Since 'special' is TRUE the multi-byte character 'c' will be
1981 // processed by get_special_key_name()
1982 c = (*mb_ptr2char)(p);
1983 else
1984 // illegal byte
1985 *sp = str + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001986 }
Bram Moolenaarb8bf5412011-08-17 20:33:22 +02001987 else
Bram Moolenaar083692d2022-06-29 21:16:58 +01001988 // single-byte character, NUL or illegal byte
1989 *sp = str + (*str == NUL ? 0 : 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001990
zeertzjq0519ce02022-05-09 12:16:19 +01001991 // Make special keys and C0 control characters in <> form, also <M-Space>.
zeertzjqcdc83932022-09-12 13:38:41 +01001992 if (special
1993 || c < ' '
1994 || (replace_spaces && c == ' ')
1995 || (replace_lt && c == '<'))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001996 return get_special_key_name(c, modifiers);
1997 buf[0] = c;
1998 buf[1] = NUL;
1999 return buf;
2000}
2001
2002/*
2003 * Translate a key sequence into special key names.
2004 */
2005 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002006str2specialbuf(char_u *sp, char_u *buf, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002007{
2008 char_u *s;
2009
2010 *buf = NUL;
2011 while (*sp)
2012 {
zeertzjqcdc83932022-09-12 13:38:41 +01002013 s = str2special(&sp, FALSE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002014 if ((int)(STRLEN(s) + STRLEN(buf)) < len)
2015 STRCAT(buf, s);
2016 }
2017}
2018
2019/*
2020 * print line for :print or :list command
2021 */
2022 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002023msg_prt_line(char_u *s, int list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002024{
2025 int c;
2026 int col = 0;
2027 int n_extra = 0;
2028 int c_extra = 0;
Bram Moolenaar83a52172019-01-16 22:41:54 +01002029 int c_final = 0;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002030 char_u *p_extra = NULL; // init to make SASC shut up
Bram Moolenaar071d4272004-06-13 20:20:40 +00002031 int n;
Bram Moolenaar56da7972007-01-16 14:46:32 +00002032 int attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002033 char_u *trail = NULL;
Bram Moolenaar91478ae2021-02-03 15:58:13 +01002034 char_u *lead = NULL;
zeertzjqf14b8ba2021-09-10 16:58:30 +02002035 int in_multispace = FALSE;
2036 int multispace_pos = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002037 int l;
2038 char_u buf[MB_MAXBYTES + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002039
Bram Moolenaardf177f62005-02-22 08:39:57 +00002040 if (curwin->w_p_list)
2041 list = TRUE;
2042
Bram Moolenaar91478ae2021-02-03 15:58:13 +01002043 if (list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002044 {
Bram Moolenaar91478ae2021-02-03 15:58:13 +01002045 // find start of trailing whitespace
Bram Moolenaareed9d462021-02-15 20:38:25 +01002046 if (curwin->w_lcs_chars.trail)
Bram Moolenaar91478ae2021-02-03 15:58:13 +01002047 {
2048 trail = s + STRLEN(s);
2049 while (trail > s && VIM_ISWHITE(trail[-1]))
2050 --trail;
2051 }
2052 // find end of leading whitespace
Bram Moolenaarb8329db2022-07-06 13:31:28 +01002053 if (curwin->w_lcs_chars.lead
2054 || curwin->w_lcs_chars.leadmultispace != NULL)
Bram Moolenaar91478ae2021-02-03 15:58:13 +01002055 {
2056 lead = s;
2057 while (VIM_ISWHITE(lead[0]))
2058 lead++;
2059 // in a line full of spaces all of them are treated as trailing
2060 if (*lead == NUL)
2061 lead = NULL;
2062 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002063 }
2064
Bram Moolenaar85a20022019-12-21 18:25:54 +01002065 // output a space for an empty line, otherwise the line will be
2066 // overwritten
Bram Moolenaareed9d462021-02-15 20:38:25 +01002067 if (*s == NUL && !(list && curwin->w_lcs_chars.eol != NUL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002068 msg_putchar(' ');
2069
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002070 while (!got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002071 {
Bram Moolenaar56da7972007-01-16 14:46:32 +00002072 if (n_extra > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002073 {
2074 --n_extra;
Bram Moolenaar83a52172019-01-16 22:41:54 +01002075 if (n_extra == 0 && c_final)
2076 c = c_final;
2077 else if (c_extra)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078 c = c_extra;
2079 else
2080 c = *p_extra++;
2081 }
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00002082 else if (has_mbyte && (l = (*mb_ptr2len)(s)) > 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002083 {
2084 col += (*mb_ptr2cells)(s);
Bram Moolenaar1cbfc992020-12-02 12:37:37 +01002085 if (l >= MB_MAXBYTES)
2086 {
Bram Moolenaar29d2f452020-12-04 19:42:52 +01002087 STRCPY(buf, "?");
Bram Moolenaar1cbfc992020-12-02 12:37:37 +01002088 }
Bram Moolenaareed9d462021-02-15 20:38:25 +01002089 else if (curwin->w_lcs_chars.nbsp != NUL && list
Bram Moolenaar73284b92015-05-04 17:28:22 +02002090 && (mb_ptr2char(s) == 160
2091 || mb_ptr2char(s) == 0x202f))
Bram Moolenaaracf17282011-02-01 17:12:25 +01002092 {
Bram Moolenaar74ac29c2022-06-15 12:12:44 +01002093 int len = mb_char2bytes(curwin->w_lcs_chars.nbsp, buf);
2094
2095 buf[len] = NUL;
Bram Moolenaaracf17282011-02-01 17:12:25 +01002096 }
2097 else
2098 {
2099 mch_memmove(buf, s, (size_t)l);
2100 buf[l] = NUL;
2101 }
Bram Moolenaar32526b32019-01-19 17:43:09 +01002102 msg_puts((char *)buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002103 s += l;
2104 continue;
2105 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002106 else
2107 {
2108 attr = 0;
2109 c = *s++;
zeertzjqabc80812023-09-24 23:32:18 +02002110 if (list)
2111 {
2112 in_multispace = c == ' ' && (*s == ' '
2113 || (col > 0 && s[-2] == ' '));
2114 if (!in_multispace)
2115 multispace_pos = 0;
2116 }
Bram Moolenaareed9d462021-02-15 20:38:25 +01002117 if (c == TAB && (!list || curwin->w_lcs_chars.tab1))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002118 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002119 // tab amount depends on current column
Bram Moolenaar04958cb2018-06-23 19:23:02 +02002120#ifdef FEAT_VARTABS
2121 n_extra = tabstop_padding(col, curbuf->b_p_ts,
2122 curbuf->b_p_vts_array) - 1;
2123#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002124 n_extra = curbuf->b_p_ts - col % curbuf->b_p_ts - 1;
Bram Moolenaar04958cb2018-06-23 19:23:02 +02002125#endif
Bram Moolenaardf177f62005-02-22 08:39:57 +00002126 if (!list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002127 {
2128 c = ' ';
2129 c_extra = ' ';
Bram Moolenaar83a52172019-01-16 22:41:54 +01002130 c_final = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002131 }
2132 else
2133 {
Bram Moolenaareed9d462021-02-15 20:38:25 +01002134 c = (n_extra == 0 && curwin->w_lcs_chars.tab3)
2135 ? curwin->w_lcs_chars.tab3
2136 : curwin->w_lcs_chars.tab1;
2137 c_extra = curwin->w_lcs_chars.tab2;
2138 c_final = curwin->w_lcs_chars.tab3;
Bram Moolenaar8820b482017-03-16 17:23:31 +01002139 attr = HL_ATTR(HLF_8);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002140 }
2141 }
Bram Moolenaareed9d462021-02-15 20:38:25 +01002142 else if (c == 160 && list && curwin->w_lcs_chars.nbsp != NUL)
Bram Moolenaaracf17282011-02-01 17:12:25 +01002143 {
Bram Moolenaareed9d462021-02-15 20:38:25 +01002144 c = curwin->w_lcs_chars.nbsp;
Bram Moolenaar8820b482017-03-16 17:23:31 +01002145 attr = HL_ATTR(HLF_8);
Bram Moolenaaracf17282011-02-01 17:12:25 +01002146 }
Bram Moolenaareed9d462021-02-15 20:38:25 +01002147 else if (c == NUL && list && curwin->w_lcs_chars.eol != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002148 {
2149 p_extra = (char_u *)"";
2150 c_extra = NUL;
Bram Moolenaar83a52172019-01-16 22:41:54 +01002151 c_final = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002152 n_extra = 1;
Bram Moolenaareed9d462021-02-15 20:38:25 +01002153 c = curwin->w_lcs_chars.eol;
Bram Moolenaar8820b482017-03-16 17:23:31 +01002154 attr = HL_ATTR(HLF_AT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002155 --s;
2156 }
2157 else if (c != NUL && (n = byte2cells(c)) > 1)
2158 {
2159 n_extra = n - 1;
cero19881d87e112023-02-16 15:03:12 +00002160 p_extra = transchar_byte_buf(NULL, c);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002161 c_extra = NUL;
Bram Moolenaar83a52172019-01-16 22:41:54 +01002162 c_final = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002163 c = *p_extra++;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002164 // Use special coloring to be able to distinguish <hex> from
2165 // the same in plain text.
Bram Moolenaar8820b482017-03-16 17:23:31 +01002166 attr = HL_ATTR(HLF_8);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002167 }
zeertzjqf14b8ba2021-09-10 16:58:30 +02002168 else if (c == ' ')
Bram Moolenaar91478ae2021-02-03 15:58:13 +01002169 {
zeertzjqabc80812023-09-24 23:32:18 +02002170 if (lead != NULL && s <= lead && in_multispace
Bram Moolenaaraca12fd2022-06-07 10:16:15 +01002171 && curwin->w_lcs_chars.leadmultispace != NULL)
2172 {
2173 c = curwin->w_lcs_chars.leadmultispace[multispace_pos++];
zeertzjqb5f08012022-06-09 13:55:28 +01002174 if (curwin->w_lcs_chars.leadmultispace[multispace_pos]
2175 == NUL)
Bram Moolenaaraca12fd2022-06-07 10:16:15 +01002176 multispace_pos = 0;
2177 attr = HL_ATTR(HLF_8);
2178 }
zeertzjqb5f08012022-06-09 13:55:28 +01002179 else if (lead != NULL && s <= lead
2180 && curwin->w_lcs_chars.lead != NUL)
zeertzjqf14b8ba2021-09-10 16:58:30 +02002181 {
2182 c = curwin->w_lcs_chars.lead;
2183 attr = HL_ATTR(HLF_8);
2184 }
2185 else if (trail != NULL && s > trail)
2186 {
2187 c = curwin->w_lcs_chars.trail;
2188 attr = HL_ATTR(HLF_8);
2189 }
zeertzjqabc80812023-09-24 23:32:18 +02002190 else if (in_multispace
zeertzjqf14b8ba2021-09-10 16:58:30 +02002191 && curwin->w_lcs_chars.multispace != NULL)
2192 {
2193 c = curwin->w_lcs_chars.multispace[multispace_pos++];
2194 if (curwin->w_lcs_chars.multispace[multispace_pos] == NUL)
2195 multispace_pos = 0;
2196 attr = HL_ATTR(HLF_8);
2197 }
2198 else if (list && curwin->w_lcs_chars.space != NUL)
2199 {
2200 c = curwin->w_lcs_chars.space;
2201 attr = HL_ATTR(HLF_8);
2202 }
Bram Moolenaar1510f992015-04-22 22:18:22 +02002203 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002204 }
2205
2206 if (c == NUL)
2207 break;
2208
2209 msg_putchar_attr(c, attr);
2210 col++;
2211 }
2212 msg_clr_eos();
2213}
2214
Bram Moolenaar071d4272004-06-13 20:20:40 +00002215/*
2216 * Use screen_puts() to output one multi-byte character.
2217 * Return the pointer "s" advanced to the next character.
2218 */
2219 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002220screen_puts_mbyte(char_u *s, int l, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002221{
2222 int cw;
2223
Bram Moolenaar85a20022019-12-21 18:25:54 +01002224 msg_didout = TRUE; // remember that line is not empty
Bram Moolenaar071d4272004-06-13 20:20:40 +00002225 cw = (*mb_ptr2cells)(s);
2226 if (cw > 1 && (
2227#ifdef FEAT_RIGHTLEFT
2228 cmdmsg_rl ? msg_col <= 1 :
2229#endif
2230 msg_col == Columns - 1))
2231 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002232 // Doesn't fit, print a highlighted '>' to fill it up.
Bram Moolenaar8820b482017-03-16 17:23:31 +01002233 msg_screen_putchar('>', HL_ATTR(HLF_AT));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002234 return s;
2235 }
2236
2237 screen_puts_len(s, l, msg_row, msg_col, attr);
2238#ifdef FEAT_RIGHTLEFT
2239 if (cmdmsg_rl)
2240 {
2241 msg_col -= cw;
2242 if (msg_col == 0)
2243 {
2244 msg_col = Columns;
2245 ++msg_row;
2246 }
2247 }
2248 else
2249#endif
2250 {
2251 msg_col += cw;
2252 if (msg_col >= Columns)
2253 {
2254 msg_col = 0;
2255 ++msg_row;
2256 }
2257 }
2258 return s + l;
2259}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002260
2261/*
2262 * Output a string to the screen at position msg_row, msg_col.
2263 * Update msg_row and msg_col for the next message.
2264 */
2265 void
Bram Moolenaar32526b32019-01-19 17:43:09 +01002266msg_puts(char *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002267{
Bram Moolenaaraeac9002016-09-06 22:15:08 +02002268 msg_puts_attr(s, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002269}
2270
2271 void
Bram Moolenaar32526b32019-01-19 17:43:09 +01002272msg_puts_title(char *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002273{
Bram Moolenaar8820b482017-03-16 17:23:31 +01002274 msg_puts_attr(s, HL_ATTR(HLF_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002275}
2276
Bram Moolenaar071d4272004-06-13 20:20:40 +00002277/*
2278 * Show a message in such a way that it always fits in the line. Cut out a
2279 * part in the middle and replace it with "..." when necessary.
2280 * Does not handle multi-byte characters!
2281 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02002282 static void
Bram Moolenaar32526b32019-01-19 17:43:09 +01002283msg_outtrans_long_len_attr(char_u *longstr, int len, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002284{
2285 int slen = len;
2286 int room;
2287
2288 room = Columns - msg_col;
2289 if (len > room && room >= 20)
2290 {
2291 slen = (room - 3) / 2;
2292 msg_outtrans_len_attr(longstr, slen, attr);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002293 msg_puts_attr("...", HL_ATTR(HLF_8));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002294 }
2295 msg_outtrans_len_attr(longstr + len - slen, slen, attr);
2296}
2297
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02002298 void
2299msg_outtrans_long_attr(char_u *longstr, int attr)
2300{
2301 msg_outtrans_long_len_attr(longstr, (int)STRLEN(longstr), attr);
2302}
2303
Bram Moolenaar071d4272004-06-13 20:20:40 +00002304/*
2305 * Basic function for writing a message with highlight attributes.
2306 */
2307 void
Bram Moolenaar32526b32019-01-19 17:43:09 +01002308msg_puts_attr(char *s, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002309{
2310 msg_puts_attr_len(s, -1, attr);
2311}
2312
2313/*
2314 * Like msg_puts_attr(), but with a maximum length "maxlen" (in bytes).
2315 * When "maxlen" is -1 there is no maximum length.
2316 * When "maxlen" is >= 0 the message is not put in the history.
2317 */
2318 static void
Bram Moolenaar32526b32019-01-19 17:43:09 +01002319msg_puts_attr_len(char *str, int maxlen, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002320{
Bram Moolenaar071d4272004-06-13 20:20:40 +00002321 /*
2322 * If redirection is on, also write to the redirection file.
2323 */
Bram Moolenaar32526b32019-01-19 17:43:09 +01002324 redir_write((char_u *)str, maxlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002325
2326 /*
2327 * Don't print anything when using ":silent cmd".
2328 */
2329 if (msg_silent != 0)
2330 return;
2331
Shougo Matsushitabe2b03c2024-04-08 22:11:50 +02002332 if (attr == 0)
2333 attr = HL_ATTR(HLF_MSG);
2334
Bram Moolenaar85a20022019-12-21 18:25:54 +01002335 // if MSG_HIST flag set, add message to history
Bram Moolenaar071d4272004-06-13 20:20:40 +00002336 if ((attr & MSG_HIST) && maxlen < 0)
2337 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002338 add_msg_hist((char_u *)str, -1, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002339 attr &= ~MSG_HIST;
2340 }
2341
Bram Moolenaare8070982019-10-12 17:07:06 +02002342 // When writing something to the screen after it has scrolled, requires a
2343 // wait-return prompt later. Needed when scrolling, resetting
2344 // need_wait_return after some prompt, and then outputting something
2345 // without scrolling
2346 // Not needed when only using CR to move the cursor.
2347 if (msg_scrolled != 0 && !msg_scrolled_ign && STRCMP(str, "\r") != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002348 need_wait_return = TRUE;
Bram Moolenaare8070982019-10-12 17:07:06 +02002349 msg_didany = TRUE; // remember that something was outputted
Bram Moolenaar071d4272004-06-13 20:20:40 +00002350
2351 /*
2352 * If there is no valid screen, use fprintf so we can see error messages.
2353 * If termcap is not active, we may be writing in an alternate console
2354 * window, cursor positioning may not work correctly (window size may be
2355 * different, e.g. for Win32 console) or we just don't know where the
2356 * cursor is.
2357 */
2358 if (msg_use_printf())
Bram Moolenaar32526b32019-01-19 17:43:09 +01002359 msg_puts_printf((char_u *)str, maxlen);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002360 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002361 msg_puts_display((char_u *)str, maxlen, attr, FALSE);
Rob Pilling726f7f92022-01-20 14:44:38 +00002362
2363 need_fileinfo = FALSE;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002364}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365
Bram Moolenaar9198de32022-08-27 21:30:03 +01002366// values for "where"
2367#define PUT_APPEND 0 // append to "lnum"
2368#define PUT_TRUNC 1 // replace "lnum"
2369#define PUT_BELOW 2 // add below "lnum"
2370 //
2371#ifdef HAS_MESSAGE_WINDOW
Bram Moolenaar9198de32022-08-27 21:30:03 +01002372/*
Bram Moolenaar24735f22022-08-30 15:44:22 +01002373 * Put text "t_s" until "end" in the message window.
Bram Moolenaar9198de32022-08-27 21:30:03 +01002374 * "where" specifies where to put the text.
2375 */
2376 static void
2377put_msg_win(win_T *wp, int where, char_u *t_s, char_u *end, linenr_T lnum)
2378{
Bram Moolenaarbeedd0a2022-08-27 21:52:52 +01002379 char_u *p;
Bram Moolenaar9198de32022-08-27 21:30:03 +01002380
Bram Moolenaar9198de32022-08-27 21:30:03 +01002381 if (where == PUT_BELOW)
Bram Moolenaarbeedd0a2022-08-27 21:52:52 +01002382 {
2383 if (*end != NUL)
2384 {
2385 p = vim_strnsave(t_s, end - t_s);
2386 if (p == NULL)
2387 return;
2388 }
2389 else
2390 p = t_s;
2391 ml_append_buf(wp->w_buffer, lnum, p, (colnr_T)0, FALSE);
2392 if (p != t_s)
2393 vim_free(p);
2394 }
Bram Moolenaar9198de32022-08-27 21:30:03 +01002395 else
2396 {
2397 char_u *newp;
2398
2399 curbuf = wp->w_buffer;
2400 if (where == PUT_APPEND)
Bram Moolenaarbeedd0a2022-08-27 21:52:52 +01002401 {
Bram Moolenaar9198de32022-08-27 21:30:03 +01002402 newp = concat_str(ml_get(lnum), t_s);
Bram Moolenaarbeedd0a2022-08-27 21:52:52 +01002403 if (newp == NULL)
2404 return;
2405 if (*end != NUL)
2406 newp[STRLEN(ml_get(lnum)) + (end - t_s)] = NUL;
2407 }
Bram Moolenaar9198de32022-08-27 21:30:03 +01002408 else
Bram Moolenaarbeedd0a2022-08-27 21:52:52 +01002409 {
Bram Moolenaar9198de32022-08-27 21:30:03 +01002410 newp = vim_strnsave(t_s, end - t_s);
Bram Moolenaarbeedd0a2022-08-27 21:52:52 +01002411 if (newp == NULL)
2412 return;
2413 }
Bram Moolenaar9198de32022-08-27 21:30:03 +01002414 ml_replace(lnum, newp, FALSE);
2415 curbuf = curwin->w_buffer;
2416 }
2417 redraw_win_later(wp, UPD_NOT_VALID);
2418
2419 // set msg_col so that a newline is written if needed
Bram Moolenaar24735f22022-08-30 15:44:22 +01002420 msg_col += (int)(end - t_s);
Bram Moolenaar9198de32022-08-27 21:30:03 +01002421}
2422#endif
2423
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002424/*
2425 * The display part of msg_puts_attr_len().
2426 * May be called recursively to display scroll-back text.
2427 */
2428 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002429msg_puts_display(
2430 char_u *str,
2431 int maxlen,
2432 int attr,
2433 int recurse)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002434{
2435 char_u *s = str;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002436 char_u *t_s = str; // string from "t_s" to "s" is still todo
2437 int t_col = 0; // screen cells todo, 0 when "t_s" not used
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002438 int l;
2439 int cw;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002440 char_u *sb_str = str;
2441 int sb_col = msg_col;
2442 int wrap;
Bram Moolenaarc27c8d52007-09-06 10:54:51 +00002443 int did_last_char;
Bram Moolenaar9198de32022-08-27 21:30:03 +01002444#ifdef HAS_MESSAGE_WINDOW
Bram Moolenaard54af2e2022-08-27 22:05:13 +01002445 int where = PUT_APPEND;
Bram Moolenaar9198de32022-08-27 21:30:03 +01002446 win_T *msg_win = NULL;
2447 linenr_T lnum = 1;
2448
Bram Moolenaara2a89732022-08-31 14:46:18 +01002449 if (in_echowindow)
Bram Moolenaar9198de32022-08-27 21:30:03 +01002450 {
2451 msg_win = popup_get_message_win();
2452
2453 if (msg_win != NULL)
2454 {
2455 if (!popup_message_win_visible())
2456 {
2457 if (*str == NL)
2458 {
2459 // When not showing the message window and the output
2460 // starts with a NL show the message normally.
2461 msg_win = NULL;
2462 }
2463 else
2464 {
2465 // currently hidden, make it empty
2466 curbuf = msg_win->w_buffer;
2467 while ((curbuf->b_ml.ml_flags & ML_EMPTY) == 0)
2468 ml_delete(1);
2469 curbuf = curwin->w_buffer;
2470 }
2471 }
2472 else
2473 {
2474 lnum = msg_win->w_buffer->b_ml.ml_line_count;
2475 if (msg_col == 0)
2476 where = PUT_TRUNC;
2477 }
2478 }
2479 }
2480#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002481
2482 did_wait_return = FALSE;
Bram Moolenaar57b7fe82007-08-05 17:20:43 +00002483 while ((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002484 {
2485 /*
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002486 * We are at the end of the screen line when:
2487 * - When outputting a newline.
2488 * - When outputting a character in the last column.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002489 */
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002490 if (!recurse && msg_row >= Rows - 1 && (*s == '\n' || (
Bram Moolenaar071d4272004-06-13 20:20:40 +00002491#ifdef FEAT_RIGHTLEFT
2492 cmdmsg_rl
2493 ? (
2494 msg_col <= 1
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01002495 || (*s == TAB && msg_col <= 7)
2496 || (has_mbyte && (*mb_ptr2cells)(s) > 1 && msg_col <= 2))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002497 :
2498#endif
Bram Moolenaar0efd1bd2019-12-11 19:00:04 +01002499 ((*s != '\r' && msg_col + t_col >= Columns - 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002500 || (*s == TAB && msg_col + t_col >= ((Columns - 1) & ~7))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002501 || (has_mbyte && (*mb_ptr2cells)(s) > 1
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01002502 && msg_col + t_col >= Columns - 2)))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002503 {
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002504 /*
2505 * The screen is scrolled up when at the last row (some terminals
2506 * scroll automatically, some don't. To avoid problems we scroll
2507 * ourselves).
2508 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002509 if (t_col > 0)
Bram Moolenaar9198de32022-08-27 21:30:03 +01002510 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002511 // output postponed text
Bram Moolenaar9198de32022-08-27 21:30:03 +01002512#ifdef HAS_MESSAGE_WINDOW
2513 if (msg_win != NULL)
2514 {
2515 put_msg_win(msg_win, where, t_s, s, lnum);
2516 t_col = 0;
2517 where = PUT_BELOW;
2518 }
2519 else
2520#endif
2521 t_puts(&t_col, t_s, s, attr);
2522 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002523
Bram Moolenaar85a20022019-12-21 18:25:54 +01002524 // When no more prompt and no more room, truncate here
Bram Moolenaar071d4272004-06-13 20:20:40 +00002525 if (msg_no_more && lines_left == 0)
2526 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002527
Bram Moolenaar9198de32022-08-27 21:30:03 +01002528#ifdef HAS_MESSAGE_WINDOW
2529 if (msg_win == NULL)
2530#endif
2531 // Scroll the screen up one line.
2532 msg_scroll_up();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002533
2534 msg_row = Rows - 2;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002535 if (msg_col >= Columns) // can happen after screen resize
Bram Moolenaar071d4272004-06-13 20:20:40 +00002536 msg_col = Columns - 1;
2537
Bram Moolenaar85a20022019-12-21 18:25:54 +01002538 // Display char in last column before showing more-prompt.
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002539 if (*s >= ' '
2540#ifdef FEAT_RIGHTLEFT
2541 && !cmdmsg_rl
2542#endif
2543 )
2544 {
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002545 if (has_mbyte)
2546 {
2547 if (enc_utf8 && maxlen >= 0)
Bram Moolenaar85a20022019-12-21 18:25:54 +01002548 // avoid including composing chars after the end
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00002549 l = utfc_ptr2len_len(s, (int)((str + maxlen) - s));
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002550 else
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00002551 l = (*mb_ptr2len)(s);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002552 s = screen_puts_mbyte(s, l, attr);
2553 }
2554 else
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002555 msg_screen_putchar(*s++, attr);
Bram Moolenaarc27c8d52007-09-06 10:54:51 +00002556 did_last_char = TRUE;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002557 }
Bram Moolenaarc27c8d52007-09-06 10:54:51 +00002558 else
2559 did_last_char = FALSE;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002560
2561 if (p_more)
Bram Moolenaar85a20022019-12-21 18:25:54 +01002562 // store text for scrolling back
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002563 store_sb_text(&sb_str, s, attr, &sb_col, TRUE);
2564
Bram Moolenaar9198de32022-08-27 21:30:03 +01002565#ifdef HAS_MESSAGE_WINDOW
2566 if (msg_win == NULL)
2567 {
2568#endif
2569 inc_msg_scrolled();
Bram Moolenaar13608d82022-08-29 15:06:50 +01002570 need_wait_return = TRUE; // may need wait_return() in main()
Bram Moolenaar9198de32022-08-27 21:30:03 +01002571 redraw_cmdline = TRUE;
2572 if (cmdline_row > 0 && !exmode_active)
2573 --cmdline_row;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002574
Bram Moolenaar9198de32022-08-27 21:30:03 +01002575 /*
2576 * If screen is completely filled and 'more' is set then wait
2577 * for a character.
2578 */
2579 if (lines_left > 0)
2580 --lines_left;
2581#ifdef HAS_MESSAGE_WINDOW
2582 }
2583#endif
Bram Moolenaar24959102022-05-07 20:01:16 +01002584 if (p_more && lines_left == 0 && State != MODE_HITRETURN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002585 && !msg_no_more && !exmode_active)
2586 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002587#ifdef FEAT_CON_DIALOG
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002588 if (do_more_prompt(NUL))
2589 s = confirm_msg_tail;
2590#else
2591 (void)do_more_prompt(NUL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002592#endif
2593 if (quit_more)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002594 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002595 }
Bram Moolenaarbb15b652005-10-03 21:52:09 +00002596
Bram Moolenaar85a20022019-12-21 18:25:54 +01002597 // When we displayed a char in last column need to check if there
2598 // is still more.
Bram Moolenaarc27c8d52007-09-06 10:54:51 +00002599 if (did_last_char)
Bram Moolenaarbb15b652005-10-03 21:52:09 +00002600 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002601 }
2602
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002603 wrap = *s == '\n'
Bram Moolenaar071d4272004-06-13 20:20:40 +00002604 || msg_col + t_col >= Columns
Bram Moolenaar071d4272004-06-13 20:20:40 +00002605 || (has_mbyte && (*mb_ptr2cells)(s) > 1
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01002606 && msg_col + t_col >= Columns - 1);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002607 if (t_col > 0 && (wrap || *s == '\r' || *s == '\b'
2608 || *s == '\t' || *s == BELL))
Bram Moolenaar9198de32022-08-27 21:30:03 +01002609 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002610 // output any postponed text
Bram Moolenaar9198de32022-08-27 21:30:03 +01002611#ifdef HAS_MESSAGE_WINDOW
2612 if (msg_win != NULL)
2613 {
2614 put_msg_win(msg_win, where, t_s, s, lnum);
2615 t_col = 0;
2616 where = PUT_BELOW;
2617 }
2618 else
2619#endif
2620 t_puts(&t_col, t_s, s, attr);
2621 }
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002622
2623 if (wrap && p_more && !recurse)
Bram Moolenaar85a20022019-12-21 18:25:54 +01002624 // store text for scrolling back
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002625 store_sb_text(&sb_str, s, attr, &sb_col, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002626
Bram Moolenaar85a20022019-12-21 18:25:54 +01002627 if (*s == '\n') // go to next line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002628 {
Bram Moolenaar9198de32022-08-27 21:30:03 +01002629#ifdef HAS_MESSAGE_WINDOW
2630 if (msg_win != NULL)
2631 {
2632 // Ignore a NL when the buffer is empty, it is used to scroll
2633 // up the text.
2634 if ((msg_win->w_buffer->b_ml.ml_flags & ML_EMPTY) == 0)
2635 {
2636 put_msg_win(msg_win, PUT_BELOW, t_s, t_s, lnum);
2637 ++lnum;
2638 }
2639 }
2640 else
2641#endif
2642 msg_didout = FALSE; // remember that line is empty
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00002643#ifdef FEAT_RIGHTLEFT
2644 if (cmdmsg_rl)
2645 msg_col = Columns - 1;
2646 else
2647#endif
2648 msg_col = 0;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002649 if (++msg_row >= Rows) // safety check
Bram Moolenaar071d4272004-06-13 20:20:40 +00002650 msg_row = Rows - 1;
2651 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01002652 else if (*s == '\r') // go to column 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002653 {
2654 msg_col = 0;
Bram Moolenaard54af2e2022-08-27 22:05:13 +01002655#ifdef HAS_MESSAGE_WINDOW
Bram Moolenaar9198de32022-08-27 21:30:03 +01002656 where = PUT_TRUNC;
Bram Moolenaard54af2e2022-08-27 22:05:13 +01002657#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002658 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01002659 else if (*s == '\b') // go to previous char
Bram Moolenaar071d4272004-06-13 20:20:40 +00002660 {
2661 if (msg_col)
2662 --msg_col;
2663 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01002664 else if (*s == TAB) // translate Tab into spaces
Bram Moolenaar071d4272004-06-13 20:20:40 +00002665 {
Bram Moolenaar9198de32022-08-27 21:30:03 +01002666#ifdef HAS_MESSAGE_WINDOW
2667 if (msg_win != NULL)
2668 msg_col = (msg_col + 7) % 8;
2669 else
2670#endif
2671 do
2672 msg_screen_putchar(' ', attr);
2673 while (msg_col & 7);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002674 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01002675 else if (*s == BELL) // beep (from ":sh")
Bram Moolenaar165bc692015-07-21 17:53:25 +02002676 vim_beep(BO_SH);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002677 else
2678 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002679 if (has_mbyte)
2680 {
2681 cw = (*mb_ptr2cells)(s);
2682 if (enc_utf8 && maxlen >= 0)
Bram Moolenaar85a20022019-12-21 18:25:54 +01002683 // avoid including composing chars after the end
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00002684 l = utfc_ptr2len_len(s, (int)((str + maxlen) - s));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002685 else
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00002686 l = (*mb_ptr2len)(s);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002687 }
2688 else
2689 {
2690 cw = 1;
2691 l = 1;
2692 }
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01002693
Bram Moolenaar85a20022019-12-21 18:25:54 +01002694 // When drawing from right to left or when a double-wide character
2695 // doesn't fit, draw a single character here. Otherwise collect
2696 // characters and draw them all at once later.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002697 if (
2698# ifdef FEAT_RIGHTLEFT
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01002699 cmdmsg_rl ||
Bram Moolenaar071d4272004-06-13 20:20:40 +00002700# endif
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01002701 (cw > 1 && msg_col + t_col >= Columns - 1))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002702 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002703 if (l > 1)
2704 s = screen_puts_mbyte(s, l, attr) - 1;
2705 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002706 msg_screen_putchar(*s, attr);
2707 }
2708 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002709 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002710 // postpone this character until later
Bram Moolenaar071d4272004-06-13 20:20:40 +00002711 if (t_col == 0)
2712 t_s = s;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002713 t_col += cw;
2714 s += l - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002715 }
2716 }
2717 ++s;
2718 }
2719
Bram Moolenaar85a20022019-12-21 18:25:54 +01002720 // output any postponed text
Bram Moolenaar071d4272004-06-13 20:20:40 +00002721 if (t_col > 0)
Bram Moolenaar9198de32022-08-27 21:30:03 +01002722 {
2723#ifdef HAS_MESSAGE_WINDOW
2724 if (msg_win != NULL)
2725 put_msg_win(msg_win, where, t_s, s, lnum);
2726 else
2727#endif
2728 t_puts(&t_col, t_s, s, attr);
2729 }
2730
2731#ifdef HAS_MESSAGE_WINDOW
2732 if (msg_win != NULL)
2733 popup_show_message_win();
2734#endif
Bram Moolenaar11901392022-09-26 19:50:44 +01002735 // Store the text for scroll back, unless it's a newline by itself.
2736 if (p_more && !recurse && !(s == sb_str + 1 && *sb_str == '\n'))
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002737 store_sb_text(&sb_str, s, attr, &sb_col, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002738
2739 msg_check();
2740}
2741
2742/*
Bram Moolenaar7b668e82016-08-23 23:51:21 +02002743 * Return TRUE when ":filter pattern" was used and "msg" does not match
2744 * "pattern".
2745 */
2746 int
2747message_filtered(char_u *msg)
2748{
Bram Moolenaard29459b2016-08-26 22:29:11 +02002749 int match;
2750
Bram Moolenaare1004402020-10-24 20:49:43 +02002751 if (cmdmod.cmod_filter_regmatch.regprog == NULL)
Bram Moolenaard29459b2016-08-26 22:29:11 +02002752 return FALSE;
Bram Moolenaare1004402020-10-24 20:49:43 +02002753 match = vim_regexec(&cmdmod.cmod_filter_regmatch, msg, (colnr_T)0);
2754 return cmdmod.cmod_filter_force ? match : !match;
Bram Moolenaar7b668e82016-08-23 23:51:21 +02002755}
2756
2757/*
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002758 * Scroll the screen up one line for displaying the next message line.
2759 */
2760 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002761msg_scroll_up(void)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002762{
Bram Moolenaar9198de32022-08-27 21:30:03 +01002763#ifdef HAS_MESSAGE_WINDOW
Bram Moolenaara2a89732022-08-31 14:46:18 +01002764 if (in_echowindow)
Bram Moolenaar9198de32022-08-27 21:30:03 +01002765 return;
2766#endif
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002767#ifdef FEAT_GUI
Bram Moolenaar85a20022019-12-21 18:25:54 +01002768 // Remove the cursor before scrolling, ScreenLines[] is going
2769 // to become invalid.
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002770 if (gui.in_use)
2771 gui_undraw_cursor();
2772#endif
Bram Moolenaar85a20022019-12-21 18:25:54 +01002773 // scrolling up always works
Bram Moolenaara338adc2018-01-31 20:51:47 +01002774 mch_disable_flush();
Bram Moolenaarcfce7172017-08-17 20:31:48 +02002775 screen_del_lines(0, 0, 1, (int)Rows, TRUE, 0, NULL);
Bram Moolenaara338adc2018-01-31 20:51:47 +01002776 mch_enable_flush();
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002777
2778 if (!can_clear((char_u *)" "))
2779 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002780 // Scrolling up doesn't result in the right background. Set the
2781 // background here. It's not efficient, but avoids that we have to do
2782 // it all over the code.
Shougo Matsushitabe2b03c2024-04-08 22:11:50 +02002783 screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns,
2784 ' ', ' ', HL_ATTR(HLF_MSG));
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002785
Bram Moolenaar85a20022019-12-21 18:25:54 +01002786 // Also clear the last char of the last but one line if it was not
2787 // cleared before to avoid a scroll-up.
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002788 if (ScreenAttrs[LineOffset[Rows - 2] + Columns - 1] == (sattr_T)-1)
2789 screen_fill((int)Rows - 2, (int)Rows - 1,
Shougo Matsushitabe2b03c2024-04-08 22:11:50 +02002790 (int)Columns - 1, (int)Columns,
2791 ' ', ' ', HL_ATTR(HLF_MSG));
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002792 }
2793}
2794
2795/*
Bram Moolenaarbb15b652005-10-03 21:52:09 +00002796 * Increment "msg_scrolled".
2797 */
2798 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002799inc_msg_scrolled(void)
Bram Moolenaarbb15b652005-10-03 21:52:09 +00002800{
2801#ifdef FEAT_EVAL
2802 if (*get_vim_var_str(VV_SCROLLSTART) == NUL)
2803 {
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002804 char_u *p = SOURCING_NAME;
Bram Moolenaarbb15b652005-10-03 21:52:09 +00002805 char_u *tofree = NULL;
2806 int len;
2807
Bram Moolenaar85a20022019-12-21 18:25:54 +01002808 // v:scrollstart is empty, set it to the script/function name and line
2809 // number
Bram Moolenaarbb15b652005-10-03 21:52:09 +00002810 if (p == NULL)
2811 p = (char_u *)_("Unknown");
2812 else
2813 {
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002814 len = (int)STRLEN(p) + 40;
Bram Moolenaarbb15b652005-10-03 21:52:09 +00002815 tofree = alloc(len);
2816 if (tofree != NULL)
2817 {
2818 vim_snprintf((char *)tofree, len, _("%s line %ld"),
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002819 p, (long)SOURCING_LNUM);
Bram Moolenaarbb15b652005-10-03 21:52:09 +00002820 p = tofree;
2821 }
2822 }
2823 set_vim_var_string(VV_SCROLLSTART, p, -1);
2824 vim_free(tofree);
2825 }
2826#endif
2827 ++msg_scrolled;
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002828 set_must_redraw(UPD_VALID);
Bram Moolenaarbb15b652005-10-03 21:52:09 +00002829}
2830
2831/*
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002832 * To be able to scroll back at the "more" and "hit-enter" prompts we need to
2833 * store the displayed text and remember where screen lines start.
2834 */
2835typedef struct msgchunk_S msgchunk_T;
2836struct msgchunk_S
2837{
2838 msgchunk_T *sb_next;
2839 msgchunk_T *sb_prev;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002840 char sb_eol; // TRUE when line ends after this text
2841 int sb_msg_col; // column in which text starts
2842 int sb_attr; // text attributes
2843 char_u sb_text[1]; // text to be displayed, actually longer
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002844};
2845
Bram Moolenaar85a20022019-12-21 18:25:54 +01002846static msgchunk_T *last_msgchunk = NULL; // last displayed text
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002847
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +01002848static msgchunk_T *msg_sb_start(msgchunk_T *mps);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002849
Bram Moolenaarf2405ed2017-03-16 19:58:25 +01002850typedef enum {
2851 SB_CLEAR_NONE = 0,
2852 SB_CLEAR_ALL,
2853 SB_CLEAR_CMDLINE_BUSY,
2854 SB_CLEAR_CMDLINE_DONE
2855} sb_clear_T;
2856
Bram Moolenaar85a20022019-12-21 18:25:54 +01002857// When to clear text on next msg.
Bram Moolenaarf2405ed2017-03-16 19:58:25 +01002858static sb_clear_T do_clear_sb_text = SB_CLEAR_NONE;
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00002859
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002860/*
2861 * Store part of a printed message for displaying when scrolling back.
2862 */
2863 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002864store_sb_text(
Bram Moolenaar85a20022019-12-21 18:25:54 +01002865 char_u **sb_str, // start of string
2866 char_u *s, // just after string
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002867 int attr,
2868 int *sb_col,
Bram Moolenaar85a20022019-12-21 18:25:54 +01002869 int finish) // line ends
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002870{
2871 msgchunk_T *mp;
2872
Bram Moolenaarf2405ed2017-03-16 19:58:25 +01002873 if (do_clear_sb_text == SB_CLEAR_ALL
2874 || do_clear_sb_text == SB_CLEAR_CMDLINE_DONE)
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00002875 {
Bram Moolenaarf2405ed2017-03-16 19:58:25 +01002876 clear_sb_text(do_clear_sb_text == SB_CLEAR_ALL);
zeertzjq46af7bc2022-07-28 12:34:09 +01002877 msg_sb_eol(); // prevent messages from overlapping
Bram Moolenaarf2405ed2017-03-16 19:58:25 +01002878 do_clear_sb_text = SB_CLEAR_NONE;
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00002879 }
2880
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002881 if (s > *sb_str)
2882 {
zeertzjq1b438a82023-02-01 13:11:15 +00002883 mp = alloc(offsetof(msgchunk_T, sb_text) + (s - *sb_str) + 1);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002884 if (mp != NULL)
2885 {
2886 mp->sb_eol = finish;
2887 mp->sb_msg_col = *sb_col;
2888 mp->sb_attr = attr;
2889 vim_strncpy(mp->sb_text, *sb_str, s - *sb_str);
2890
2891 if (last_msgchunk == NULL)
2892 {
2893 last_msgchunk = mp;
2894 mp->sb_prev = NULL;
2895 }
2896 else
2897 {
2898 mp->sb_prev = last_msgchunk;
2899 last_msgchunk->sb_next = mp;
2900 last_msgchunk = mp;
2901 }
2902 mp->sb_next = NULL;
2903 }
2904 }
2905 else if (finish && last_msgchunk != NULL)
2906 last_msgchunk->sb_eol = TRUE;
2907
2908 *sb_str = s;
2909 *sb_col = 0;
2910}
2911
2912/*
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00002913 * Finished showing messages, clear the scroll-back text on the next message.
2914 */
2915 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002916may_clear_sb_text(void)
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00002917{
Bram Moolenaarf2405ed2017-03-16 19:58:25 +01002918 do_clear_sb_text = SB_CLEAR_ALL;
2919}
2920
2921/*
zeertzjq46af7bc2022-07-28 12:34:09 +01002922 * Starting to edit the command line: do not clear messages now.
Bram Moolenaarf2405ed2017-03-16 19:58:25 +01002923 */
2924 void
2925sb_text_start_cmdline(void)
2926{
zeertzjq46af7bc2022-07-28 12:34:09 +01002927 if (do_clear_sb_text == SB_CLEAR_CMDLINE_BUSY)
2928 // Invoking command line recursively: the previous-level command line
2929 // doesn't need to be remembered as it will be redrawn when returning
2930 // to that level.
2931 sb_text_restart_cmdline();
2932 else
2933 {
2934 msg_sb_eol();
2935 do_clear_sb_text = SB_CLEAR_CMDLINE_BUSY;
2936 }
Bram Moolenaarf2405ed2017-03-16 19:58:25 +01002937}
2938
2939/*
zeertzjq46af7bc2022-07-28 12:34:09 +01002940 * Redrawing the command line: clear the last unfinished line.
2941 */
2942 void
2943sb_text_restart_cmdline(void)
2944{
2945 msgchunk_T *tofree;
2946
2947 // Needed when returning from nested command line.
2948 do_clear_sb_text = SB_CLEAR_CMDLINE_BUSY;
2949
2950 if (last_msgchunk == NULL || last_msgchunk->sb_eol)
2951 // No unfinished line: don't clear anything.
2952 return;
2953
2954 tofree = msg_sb_start(last_msgchunk);
2955 last_msgchunk = tofree->sb_prev;
2956 if (last_msgchunk != NULL)
2957 last_msgchunk->sb_next = NULL;
2958 while (tofree != NULL)
2959 {
2960 msgchunk_T *tofree_next = tofree->sb_next;
2961
2962 vim_free(tofree);
2963 tofree = tofree_next;
2964 }
2965}
2966
2967/*
2968 * Ending to edit the command line: clear old lines but the last one later.
Bram Moolenaarf2405ed2017-03-16 19:58:25 +01002969 */
2970 void
2971sb_text_end_cmdline(void)
2972{
2973 do_clear_sb_text = SB_CLEAR_CMDLINE_DONE;
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00002974}
2975
2976/*
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002977 * Clear any text remembered for scrolling back.
Bram Moolenaarf2405ed2017-03-16 19:58:25 +01002978 * When "all" is FALSE keep the last line.
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002979 * Called when redrawing the screen.
2980 */
2981 void
Bram Moolenaarf2405ed2017-03-16 19:58:25 +01002982clear_sb_text(int all)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002983{
2984 msgchunk_T *mp;
Bram Moolenaarf2405ed2017-03-16 19:58:25 +01002985 msgchunk_T **lastp;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002986
Bram Moolenaarf2405ed2017-03-16 19:58:25 +01002987 if (all)
2988 lastp = &last_msgchunk;
2989 else
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00002990 {
Bram Moolenaarf2405ed2017-03-16 19:58:25 +01002991 if (last_msgchunk == NULL)
2992 return;
zeertzjqecdc82e2022-07-25 19:50:57 +01002993 lastp = &msg_sb_start(last_msgchunk)->sb_prev;
Bram Moolenaarf2405ed2017-03-16 19:58:25 +01002994 }
2995
2996 while (*lastp != NULL)
2997 {
2998 mp = (*lastp)->sb_prev;
2999 vim_free(*lastp);
3000 *lastp = mp;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003001 }
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003002}
3003
3004/*
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00003005 * "g<" command.
3006 */
3007 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003008show_sb_text(void)
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00003009{
3010 msgchunk_T *mp;
3011
Bram Moolenaar85a20022019-12-21 18:25:54 +01003012 // Only show something if there is more than one line, otherwise it looks
3013 // weird, typing a command without output results in one line.
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00003014 mp = msg_sb_start(last_msgchunk);
3015 if (mp == NULL || mp->sb_prev == NULL)
Bram Moolenaar165bc692015-07-21 17:53:25 +02003016 vim_beep(BO_MESS);
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00003017 else
3018 {
3019 do_more_prompt('G');
3020 wait_return(FALSE);
3021 }
3022}
3023
3024/*
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003025 * Move to the start of screen line in already displayed text.
3026 */
3027 static msgchunk_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003028msg_sb_start(msgchunk_T *mps)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003029{
3030 msgchunk_T *mp = mps;
3031
3032 while (mp != NULL && mp->sb_prev != NULL && !mp->sb_prev->sb_eol)
3033 mp = mp->sb_prev;
3034 return mp;
3035}
3036
3037/*
Bram Moolenaar6df5e5a2012-03-28 16:49:29 +02003038 * Mark the last message chunk as finishing the line.
3039 */
3040 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003041msg_sb_eol(void)
Bram Moolenaar6df5e5a2012-03-28 16:49:29 +02003042{
3043 if (last_msgchunk != NULL)
3044 last_msgchunk->sb_eol = TRUE;
3045}
3046
3047/*
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003048 * Display a screen line from previously displayed text at row "row".
Bram Moolenaar838b7462022-09-26 15:19:56 +01003049 * When "clear_to_eol" is set clear the rest of the screen line.
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003050 * Returns a pointer to the text for the next line (can be NULL).
3051 */
3052 static msgchunk_T *
Bram Moolenaar838b7462022-09-26 15:19:56 +01003053disp_sb_line(int row, msgchunk_T *smp, int clear_to_eol)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003054{
3055 msgchunk_T *mp = smp;
3056 char_u *p;
3057
3058 for (;;)
3059 {
3060 msg_row = row;
3061 msg_col = mp->sb_msg_col;
3062 p = mp->sb_text;
Bram Moolenaar85a20022019-12-21 18:25:54 +01003063 if (*p == '\n') // don't display the line break
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003064 ++p;
3065 msg_puts_display(p, -1, mp->sb_attr, TRUE);
Bram Moolenaar838b7462022-09-26 15:19:56 +01003066
3067 // If clearing the screen did not work (e.g. because of a background
3068 // color and t_ut isn't set) clear until the last column here.
3069 if (clear_to_eol)
Shougo Matsushitabe2b03c2024-04-08 22:11:50 +02003070 screen_fill(row, row + 1, msg_col, (int)Columns,
3071 ' ', ' ', HL_ATTR(HLF_MSG));
Bram Moolenaar838b7462022-09-26 15:19:56 +01003072
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003073 if (mp->sb_eol || mp->sb_next == NULL)
3074 break;
3075 mp = mp->sb_next;
3076 }
3077 return mp->sb_next;
3078}
3079
3080/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003081 * Output any postponed text for msg_puts_attr_len().
3082 */
3083 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003084t_puts(
3085 int *t_col,
3086 char_u *t_s,
3087 char_u *s,
3088 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003089{
Bram Moolenaar85a20022019-12-21 18:25:54 +01003090 // output postponed text
3091 msg_didout = TRUE; // remember that line is not empty
Bram Moolenaar071d4272004-06-13 20:20:40 +00003092 screen_puts_len(t_s, (int)(s - t_s), msg_row, msg_col, attr);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003093 msg_col += *t_col;
3094 *t_col = 0;
Bram Moolenaar85a20022019-12-21 18:25:54 +01003095 // If the string starts with a composing character don't increment the
3096 // column position for it.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003097 if (enc_utf8 && utf_iscomposing(utf_ptr2char(t_s)))
3098 --msg_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003099 if (msg_col >= Columns)
3100 {
3101 msg_col = 0;
3102 ++msg_row;
3103 }
3104}
3105
Bram Moolenaar071d4272004-06-13 20:20:40 +00003106/*
3107 * Returns TRUE when messages should be printed with mch_errmsg().
3108 * This is used when there is no valid screen, so we can see error messages.
3109 * If termcap is not active, we may be writing in an alternate console
3110 * window, cursor positioning may not work correctly (window size may be
3111 * different, e.g. for Win32 console) or we just don't know where the
3112 * cursor is.
3113 */
3114 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003115msg_use_printf(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003116{
3117 return (!msg_check_screen()
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003118#if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
3119# ifdef VIMDLL
3120 || (!gui.in_use && !termcap_active)
3121# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003122 || !termcap_active
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003123# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003124#endif
3125 || (swapping_screen() && !termcap_active)
3126 );
3127}
3128
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003129/*
3130 * Print a message when there is no valid screen.
3131 */
3132 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003133msg_puts_printf(char_u *str, int maxlen)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003134{
3135 char_u *s = str;
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003136 char_u *buf = NULL;
3137 char_u *p = s;
3138
Bram Moolenaar4f974752019-02-17 17:44:42 +01003139#ifdef MSWIN
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003140 if (!(silent_mode && p_verbose == 0))
Bram Moolenaar85a20022019-12-21 18:25:54 +01003141 mch_settmode(TMODE_COOK); // handle CR and NL correctly
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003142#endif
Bram Moolenaar2321ca22016-09-09 14:17:18 +02003143 while ((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003144 {
3145 if (!(silent_mode && p_verbose == 0))
3146 {
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003147 // NL --> CR NL translation (for Unix, not for "--version")
3148 if (*s == NL)
3149 {
3150 int n = (int)(s - p);
3151
3152 buf = alloc(n + 3);
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003153 if (buf != NULL)
3154 {
3155 memcpy(buf, p, n);
3156 if (!info_message)
3157 buf[n++] = CAR;
3158 buf[n++] = NL;
3159 buf[n++] = NUL;
3160 if (info_message) // informative message, not an error
3161 mch_msg((char *)buf);
3162 else
3163 mch_errmsg((char *)buf);
3164 vim_free(buf);
3165 }
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003166 p = s + 1;
3167 }
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003168 }
3169
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003170 // primitive way to compute the current column
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003171#ifdef FEAT_RIGHTLEFT
3172 if (cmdmsg_rl)
3173 {
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003174 if (*s == CAR || *s == NL)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003175 msg_col = Columns - 1;
3176 else
3177 --msg_col;
3178 }
3179 else
3180#endif
3181 {
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003182 if (*s == CAR || *s == NL)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003183 msg_col = 0;
3184 else
3185 ++msg_col;
3186 }
3187 ++s;
3188 }
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003189
3190 if (*p != NUL && !(silent_mode && p_verbose == 0))
3191 {
Bram Moolenaara97c3632021-06-15 22:39:11 +02003192 char_u *tofree = NULL;
Bram Moolenaar97c2c052019-02-22 13:42:07 +01003193
Bram Moolenaarc32949b2023-01-04 15:56:51 +00003194 if (maxlen > 0 && vim_strlen_maxlen((char *)p, (size_t)maxlen)
3195 >= (size_t)maxlen)
Bram Moolenaar97c2c052019-02-22 13:42:07 +01003196 {
Bram Moolenaara97c3632021-06-15 22:39:11 +02003197 tofree = vim_strnsave(p, (size_t)maxlen);
3198 p = tofree;
Bram Moolenaar97c2c052019-02-22 13:42:07 +01003199 }
Bram Moolenaara97c3632021-06-15 22:39:11 +02003200 if (p != NULL)
3201 {
3202 if (info_message)
3203 mch_msg((char *)p);
3204 else
3205 mch_errmsg((char *)p);
3206 vim_free(tofree);
3207 }
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003208 }
3209
3210 msg_didout = TRUE; // assume that line is not empty
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003211
Bram Moolenaar4f974752019-02-17 17:44:42 +01003212#ifdef MSWIN
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003213 if (!(silent_mode && p_verbose == 0))
3214 mch_settmode(TMODE_RAW);
3215#endif
3216}
3217
3218/*
3219 * Show the more-prompt and handle the user response.
3220 * This takes care of scrolling back and displaying previously displayed text.
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00003221 * When at hit-enter prompt "typed_char" is the already typed character,
3222 * otherwise it's NUL.
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003223 * Returns TRUE when jumping ahead to "confirm_msg_tail".
3224 */
3225 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003226do_more_prompt(int typed_char)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003227{
Bram Moolenaarbfb96c02016-03-19 17:05:20 +01003228 static int entered = FALSE;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003229 int used_typed_char = typed_char;
3230 int oldState = State;
3231 int c;
3232#ifdef FEAT_CON_DIALOG
3233 int retval = FALSE;
3234#endif
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003235 int toscroll;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003236 msgchunk_T *mp_last = NULL;
3237 msgchunk_T *mp;
3238 int i;
Shougo Matsushitabe2b03c2024-04-08 22:11:50 +02003239 int msg_attr;
3240
3241 msg_attr = HL_ATTR(HLF_MSG);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003242
Bram Moolenaar85a20022019-12-21 18:25:54 +01003243 // We get called recursively when a timer callback outputs a message. In
3244 // that case don't show another prompt. Also when at the hit-Enter prompt
3245 // and nothing was typed.
Bram Moolenaar24959102022-05-07 20:01:16 +01003246 if (entered || (State == MODE_HITRETURN && typed_char == 0))
Bram Moolenaarbfb96c02016-03-19 17:05:20 +01003247 return FALSE;
3248 entered = TRUE;
3249
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00003250 if (typed_char == 'G')
3251 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003252 // "g<": Find first line on the last page.
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00003253 mp_last = msg_sb_start(last_msgchunk);
3254 for (i = 0; i < Rows - 2 && mp_last != NULL
3255 && mp_last->sb_prev != NULL; ++i)
3256 mp_last = msg_sb_start(mp_last->sb_prev);
3257 }
3258
Bram Moolenaar24959102022-05-07 20:01:16 +01003259 State = MODE_ASKMORE;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003260 setmouse();
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00003261 if (typed_char == NUL)
3262 msg_moremsg(FALSE);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003263 for (;;)
3264 {
3265 /*
3266 * Get a typed character directly from the user.
3267 */
3268 if (used_typed_char != NUL)
3269 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003270 c = used_typed_char; // was typed at hit-enter prompt
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003271 used_typed_char = NUL;
3272 }
3273 else
3274 c = get_keystroke();
3275
3276#if defined(FEAT_MENU) && defined(FEAT_GUI)
3277 if (c == K_MENU)
3278 {
Bram Moolenaar24959102022-05-07 20:01:16 +01003279 int idx = get_menu_index(current_menu, MODE_ASKMORE);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003280
Bram Moolenaar85a20022019-12-21 18:25:54 +01003281 // Used a menu. If it starts with CTRL-Y, it must
3282 // be a "Copy" for the clipboard. Otherwise
3283 // assume that we end
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003284 if (idx == MENU_INDEX_INVALID)
3285 continue;
3286 c = *current_menu->strings[idx];
3287 if (c != NUL && current_menu->strings[idx][1] != NUL)
3288 ins_typebuf(current_menu->strings[idx] + 1,
3289 current_menu->noremap[idx], 0, TRUE,
3290 current_menu->silent[idx]);
3291 }
3292#endif
3293
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003294 toscroll = 0;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003295 switch (c)
3296 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003297 case BS: // scroll one line back
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003298 case K_BS:
3299 case 'k':
3300 case K_UP:
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003301 toscroll = -1;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003302 break;
3303
Bram Moolenaar85a20022019-12-21 18:25:54 +01003304 case CAR: // one extra line
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003305 case NL:
3306 case 'j':
3307 case K_DOWN:
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003308 toscroll = 1;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003309 break;
3310
Bram Moolenaar85a20022019-12-21 18:25:54 +01003311 case 'u': // Up half a page
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003312 toscroll = -(Rows / 2);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003313 break;
3314
Bram Moolenaar85a20022019-12-21 18:25:54 +01003315 case 'd': // Down half a page
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003316 toscroll = Rows / 2;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003317 break;
3318
Bram Moolenaar85a20022019-12-21 18:25:54 +01003319 case 'b': // one page back
Bram Moolenaar2a9e4df2009-02-21 23:59:19 +00003320 case K_PAGEUP:
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003321 toscroll = -(Rows - 1);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003322 break;
3323
Bram Moolenaar85a20022019-12-21 18:25:54 +01003324 case ' ': // one extra page
Bram Moolenaar2a9e4df2009-02-21 23:59:19 +00003325 case 'f':
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003326 case K_PAGEDOWN:
3327 case K_LEFTMOUSE:
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003328 toscroll = Rows - 1;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003329 break;
3330
Bram Moolenaar85a20022019-12-21 18:25:54 +01003331 case 'g': // all the way back to the start
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003332 toscroll = -999999;
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00003333 break;
3334
Bram Moolenaar85a20022019-12-21 18:25:54 +01003335 case 'G': // all the way to the end
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003336 toscroll = 999999;
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00003337 lines_left = 999999;
3338 break;
3339
Bram Moolenaar85a20022019-12-21 18:25:54 +01003340 case ':': // start new command line
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003341#ifdef FEAT_CON_DIALOG
3342 if (!confirm_msg_used)
3343#endif
3344 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003345 // Since got_int is set all typeahead will be flushed, but we
3346 // want to keep this ':', remember that in a special way.
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003347 typeahead_noflush(':');
Bram Moolenaar1d4754f2018-06-19 17:49:24 +02003348#ifdef FEAT_TERMINAL
3349 skip_term_loop = TRUE;
3350#endif
Bram Moolenaar85a20022019-12-21 18:25:54 +01003351 cmdline_row = Rows - 1; // put ':' on this line
3352 skip_redraw = TRUE; // skip redraw once
3353 need_wait_return = FALSE; // don't wait in main()
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003354 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01003355 // FALLTHROUGH
3356 case 'q': // quit
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003357 case Ctrl_C:
3358 case ESC:
3359#ifdef FEAT_CON_DIALOG
3360 if (confirm_msg_used)
3361 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003362 // Jump to the choices of the dialog.
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003363 retval = TRUE;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003364 }
3365 else
3366#endif
3367 {
3368 got_int = TRUE;
3369 quit_more = TRUE;
3370 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01003371 // When there is some more output (wrapping line) display that
3372 // without another prompt.
Bram Moolenaar51306d22009-02-24 03:38:04 +00003373 lines_left = Rows - 1;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003374 break;
3375
3376#ifdef FEAT_CLIPBOARD
3377 case Ctrl_Y:
Bram Moolenaar85a20022019-12-21 18:25:54 +01003378 // Strange way to allow copying (yanking) a modeless
3379 // selection at the more prompt. Use CTRL-Y,
3380 // because the same is used in Cmdline-mode and at the
3381 // hit-enter prompt. However, scrolling one line up
3382 // might be expected...
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003383 if (clip_star.state == SELECT_DONE)
3384 clip_copy_modeless_selection(TRUE);
3385 continue;
3386#endif
Bram Moolenaar85a20022019-12-21 18:25:54 +01003387 default: // no valid response
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003388 msg_moremsg(TRUE);
3389 continue;
3390 }
3391
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003392 if (toscroll != 0)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003393 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003394 if (toscroll < 0)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003395 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003396 // go to start of last line
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003397 if (mp_last == NULL)
3398 mp = msg_sb_start(last_msgchunk);
3399 else if (mp_last->sb_prev != NULL)
3400 mp = msg_sb_start(mp_last->sb_prev);
3401 else
3402 mp = NULL;
3403
Bram Moolenaar85a20022019-12-21 18:25:54 +01003404 // go to start of line at top of the screen
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003405 for (i = 0; i < Rows - 2 && mp != NULL && mp->sb_prev != NULL;
3406 ++i)
3407 mp = msg_sb_start(mp->sb_prev);
3408
3409 if (mp != NULL && mp->sb_prev != NULL)
3410 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003411 // Find line to be displayed at top.
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003412 for (i = 0; i > toscroll; --i)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003413 {
3414 if (mp == NULL || mp->sb_prev == NULL)
3415 break;
3416 mp = msg_sb_start(mp->sb_prev);
3417 if (mp_last == NULL)
3418 mp_last = msg_sb_start(last_msgchunk);
3419 else
3420 mp_last = msg_sb_start(mp_last->sb_prev);
3421 }
3422
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003423 if (toscroll == -1 && screen_ins_lines(0, 0, 1,
Bram Moolenaarcfce7172017-08-17 20:31:48 +02003424 (int)Rows, 0, NULL) == OK)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003425 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003426 // display line at top
Bram Moolenaar838b7462022-09-26 15:19:56 +01003427 (void)disp_sb_line(0, mp, FALSE);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003428 }
3429 else
3430 {
Bram Moolenaar838b7462022-09-26 15:19:56 +01003431 int did_clear = screenclear();
3432
Bram Moolenaar85a20022019-12-21 18:25:54 +01003433 // redisplay all lines
Bram Moolenaar773560b2006-05-06 21:38:18 +00003434 for (i = 0; mp != NULL && i < Rows - 1; ++i)
3435 {
Bram Moolenaar838b7462022-09-26 15:19:56 +01003436 mp = disp_sb_line(i, mp, !did_clear);
Bram Moolenaar773560b2006-05-06 21:38:18 +00003437 ++msg_scrolled;
3438 }
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003439 }
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003440 toscroll = 0;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003441 }
3442 }
3443 else
3444 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003445 // First display any text that we scrolled back.
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003446 while (toscroll > 0 && mp_last != NULL)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003447 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003448 // scroll up, display line at bottom
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003449 msg_scroll_up();
Bram Moolenaarbb15b652005-10-03 21:52:09 +00003450 inc_msg_scrolled();
Shougo Matsushitabe2b03c2024-04-08 22:11:50 +02003451 screen_fill((int)Rows - 2, (int)Rows - 1, 0, (int)Columns,
3452 ' ', ' ', msg_attr);
Bram Moolenaar838b7462022-09-26 15:19:56 +01003453 mp_last = disp_sb_line((int)Rows - 2, mp_last, FALSE);
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003454 --toscroll;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003455 }
3456 }
3457
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003458 if (toscroll <= 0)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003459 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003460 // displayed the requested text, more prompt again
Shougo Matsushitabe2b03c2024-04-08 22:11:50 +02003461 screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns,
3462 ' ', ' ', msg_attr);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003463 msg_moremsg(FALSE);
3464 continue;
3465 }
3466
Bram Moolenaar85a20022019-12-21 18:25:54 +01003467 // display more text, return to caller
Bram Moolenaar70b2a562012-01-10 22:26:17 +01003468 lines_left = toscroll;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003469 }
3470
3471 break;
3472 }
3473
Bram Moolenaar85a20022019-12-21 18:25:54 +01003474 // clear the --more-- message
Shougo Matsushitabe2b03c2024-04-08 22:11:50 +02003475 screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', msg_attr);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003476 State = oldState;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003477 setmouse();
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003478 if (quit_more)
3479 {
3480 msg_row = Rows - 1;
3481 msg_col = 0;
3482 }
3483#ifdef FEAT_RIGHTLEFT
3484 else if (cmdmsg_rl)
3485 msg_col = Columns - 1;
3486#endif
3487
Bram Moolenaarbfb96c02016-03-19 17:05:20 +01003488 entered = FALSE;
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003489#ifdef FEAT_CON_DIALOG
3490 return retval;
3491#else
3492 return FALSE;
3493#endif
3494}
3495
Bram Moolenaar071d4272004-06-13 20:20:40 +00003496#if defined(USE_MCH_ERRMSG) || defined(PROTO)
3497
3498#ifdef mch_errmsg
3499# undef mch_errmsg
3500#endif
3501#ifdef mch_msg
3502# undef mch_msg
3503#endif
3504
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003505#if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
3506 static void
3507mch_errmsg_c(char *str)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003508{
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003509 DWORD nwrite = 0;
3510 DWORD mode = 0;
3511 HANDLE h = GetStdHandle(STD_ERROR_HANDLE);
3512
3513 if (GetConsoleMode(h, &mode) && enc_codepage >= 0
3514 && (int)GetConsoleCP() != enc_codepage)
3515 {
John Marriott031f2272025-04-23 20:56:08 +02003516 int len = (int)STRLEN(str);
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003517 WCHAR *w = enc_to_utf16((char_u *)str, &len);
3518
John Marriott031f2272025-04-23 20:56:08 +02003519 if (w != NULL)
3520 {
3521 WriteConsoleW(h, w, len, &nwrite, NULL);
3522 vim_free(w);
3523 }
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003524 }
3525 else
3526 {
3527 fprintf(stderr, "%s", str);
3528 }
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003529}
3530#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003531
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003532/*
3533 * Give an error message. To be used when the screen hasn't been initialized
3534 * yet. When stderr can't be used, collect error messages until the GUI has
3535 * started and they can be displayed in a message box.
3536 */
3537 void
3538mch_errmsg(char *str)
3539{
3540#if !defined(MSWIN) || defined(FEAT_GUI_MSWIN)
3541 int len;
3542#endif
3543
Bram Moolenaar0b75f7c2019-05-08 22:28:46 +02003544#if (defined(UNIX) || defined(FEAT_GUI)) && !defined(ALWAYS_USE_GUI) && !defined(VIMDLL)
Bram Moolenaar85a20022019-12-21 18:25:54 +01003545 // On Unix use stderr if it's a tty.
3546 // When not going to start the GUI also use stderr.
3547 // On Mac, when started from Finder, stderr is the console.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003548 if (
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003549# ifdef UNIX
3550# ifdef MACOS_X
Bram Moolenaar071d4272004-06-13 20:20:40 +00003551 (isatty(2) && strcmp("/dev/console", ttyname(2)) != 0)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003552# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003553 isatty(2)
3554# endif
3555# ifdef FEAT_GUI
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003556 ||
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003557# endif
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003558# endif
3559# ifdef FEAT_GUI
3560 !(gui.in_use || gui.starting)
3561# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003562 )
3563 {
3564 fprintf(stderr, "%s", str);
3565 return;
3566 }
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003567#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003568
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003569#if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
3570# ifdef VIMDLL
3571 if (!(gui.in_use || gui.starting))
3572# endif
3573 {
3574 mch_errmsg_c(str);
3575 return;
3576 }
3577#endif
3578
3579#if !defined(MSWIN) || defined(FEAT_GUI_MSWIN)
Bram Moolenaar85a20022019-12-21 18:25:54 +01003580 // avoid a delay for a message that isn't there
Bram Moolenaar071d4272004-06-13 20:20:40 +00003581 emsg_on_display = FALSE;
3582
3583 len = (int)STRLEN(str) + 1;
3584 if (error_ga.ga_growsize == 0)
3585 {
3586 error_ga.ga_growsize = 80;
3587 error_ga.ga_itemsize = 1;
3588 }
3589 if (ga_grow(&error_ga, len) == OK)
3590 {
3591 mch_memmove((char_u *)error_ga.ga_data + error_ga.ga_len,
3592 (char_u *)str, len);
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003593# ifdef UNIX
Bram Moolenaar85a20022019-12-21 18:25:54 +01003594 // remove CR characters, they are displayed
Bram Moolenaar071d4272004-06-13 20:20:40 +00003595 {
3596 char_u *p;
3597
3598 p = (char_u *)error_ga.ga_data + error_ga.ga_len;
3599 for (;;)
3600 {
3601 p = vim_strchr(p, '\r');
3602 if (p == NULL)
3603 break;
3604 *p = ' ';
3605 }
3606 }
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003607# endif
Bram Moolenaar85a20022019-12-21 18:25:54 +01003608 --len; // don't count the NUL at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00003609 error_ga.ga_len += len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003610 }
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003611#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003612}
3613
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003614#if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
3615 static void
3616mch_msg_c(char *str)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003617{
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003618 DWORD nwrite = 0;
3619 DWORD mode;
3620 HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
3621
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003622 if (GetConsoleMode(h, &mode) && enc_codepage >= 0
3623 && (int)GetConsoleCP() != enc_codepage)
3624 {
John Marriott031f2272025-04-23 20:56:08 +02003625 int len = (int)STRLEN(str);
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003626 WCHAR *w = enc_to_utf16((char_u *)str, &len);
3627
John Marriott031f2272025-04-23 20:56:08 +02003628 if (w != NULL)
3629 {
3630 WriteConsoleW(h, w, len, &nwrite, NULL);
3631 vim_free(w);
3632 }
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003633 }
3634 else
3635 {
3636 printf("%s", str);
3637 }
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003638}
3639#endif
3640
3641/*
3642 * Give a message. To be used when the screen hasn't been initialized yet.
3643 * When there is no tty, collect messages until the GUI has started and they
3644 * can be displayed in a message box.
3645 */
3646 void
3647mch_msg(char *str)
3648{
Bram Moolenaar0b75f7c2019-05-08 22:28:46 +02003649#if (defined(UNIX) || defined(FEAT_GUI)) && !defined(ALWAYS_USE_GUI) && !defined(VIMDLL)
Bram Moolenaar85a20022019-12-21 18:25:54 +01003650 // On Unix use stdout if we have a tty. This allows "vim -h | more" and
3651 // uses mch_errmsg() when started from the desktop.
3652 // When not going to start the GUI also use stdout.
3653 // On Mac, when started from Finder, stderr is the console.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003654 if (
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003655# ifdef UNIX
3656# ifdef MACOS_X
Bram Moolenaar071d4272004-06-13 20:20:40 +00003657 (isatty(2) && strcmp("/dev/console", ttyname(2)) != 0)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003658# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003659 isatty(2)
Bram Moolenaareae1b912019-05-09 15:12:55 +02003660# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003661# ifdef FEAT_GUI
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003662 ||
Bram Moolenaar071d4272004-06-13 20:20:40 +00003663# endif
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003664# endif
3665# ifdef FEAT_GUI
3666 !(gui.in_use || gui.starting)
3667# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003668 )
3669 {
3670 printf("%s", str);
3671 return;
3672 }
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003673#endif
3674
3675#if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
3676# ifdef VIMDLL
3677 if (!(gui.in_use || gui.starting))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003678# endif
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003679 {
3680 mch_msg_c(str);
3681 return;
3682 }
3683#endif
3684#if !defined(MSWIN) || defined(FEAT_GUI_MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003685 mch_errmsg(str);
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +01003686#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003687}
Bram Moolenaar85a20022019-12-21 18:25:54 +01003688#endif // USE_MCH_ERRMSG
Bram Moolenaar071d4272004-06-13 20:20:40 +00003689
3690/*
3691 * Put a character on the screen at the current message position and advance
3692 * to the next position. Only for printable ASCII!
3693 */
3694 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003695msg_screen_putchar(int c, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003696{
Bram Moolenaar85a20022019-12-21 18:25:54 +01003697 msg_didout = TRUE; // remember that line is not empty
Bram Moolenaar071d4272004-06-13 20:20:40 +00003698 screen_putchar(c, msg_row, msg_col, attr);
3699#ifdef FEAT_RIGHTLEFT
3700 if (cmdmsg_rl)
3701 {
3702 if (--msg_col == 0)
3703 {
3704 msg_col = Columns;
3705 ++msg_row;
3706 }
3707 }
3708 else
3709#endif
3710 {
3711 if (++msg_col >= Columns)
3712 {
3713 msg_col = 0;
3714 ++msg_row;
3715 }
3716 }
3717}
3718
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02003719 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003720msg_moremsg(int full)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003721{
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003722 int attr;
3723 char_u *s = (char_u *)_("-- More --");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003724
Bram Moolenaar8820b482017-03-16 17:23:31 +01003725 attr = HL_ATTR(HLF_M);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003726 screen_puts(s, (int)Rows - 1, 0, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003727 if (full)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003728 screen_puts((char_u *)
3729 _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "),
3730 (int)Rows - 1, vim_strsize(s), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003731}
3732
3733/*
Bram Moolenaar24959102022-05-07 20:01:16 +01003734 * Repeat the message for the current mode: MODE_ASKMORE, MODE_EXTERNCMD,
3735 * MODE_CONFIRM or exmode_active.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003736 */
3737 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003738repeat_message(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003739{
Bram Moolenaar24959102022-05-07 20:01:16 +01003740 if (State == MODE_ASKMORE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003741 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003742 msg_moremsg(TRUE); // display --more-- message again
Bram Moolenaar071d4272004-06-13 20:20:40 +00003743 msg_row = Rows - 1;
3744 }
3745#ifdef FEAT_CON_DIALOG
Bram Moolenaar24959102022-05-07 20:01:16 +01003746 else if (State == MODE_CONFIRM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003747 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003748 display_confirm_msg(); // display ":confirm" message again
Bram Moolenaar071d4272004-06-13 20:20:40 +00003749 msg_row = Rows - 1;
3750 }
3751#endif
Bram Moolenaar24959102022-05-07 20:01:16 +01003752 else if (State == MODE_EXTERNCMD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003753 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003754 windgoto(msg_row, msg_col); // put cursor back
Bram Moolenaar071d4272004-06-13 20:20:40 +00003755 }
Bram Moolenaar24959102022-05-07 20:01:16 +01003756 else if (State == MODE_HITRETURN || State == MODE_SETWSIZE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003757 {
Bram Moolenaar9f108752007-11-24 14:44:58 +00003758 if (msg_row == Rows - 1)
3759 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003760 // Avoid drawing the "hit-enter" prompt below the previous one,
3761 // overwrite it. Esp. useful when regaining focus and a
3762 // FocusGained autocmd exists but didn't draw anything.
Bram Moolenaar9f108752007-11-24 14:44:58 +00003763 msg_didout = FALSE;
3764 msg_col = 0;
3765 msg_clr_eos();
3766 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003767 hit_return_msg();
3768 msg_row = Rows - 1;
3769 }
3770}
3771
3772/*
3773 * msg_check_screen - check if the screen is initialized.
3774 * Also check msg_row and msg_col, if they are too big it may cause a crash.
3775 * While starting the GUI the terminal codes will be set for the GUI, but the
3776 * output goes to the terminal. Don't use the terminal codes then.
3777 */
3778 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003779msg_check_screen(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003780{
3781 if (!full_screen || !screen_valid(FALSE))
3782 return FALSE;
3783
3784 if (msg_row >= Rows)
3785 msg_row = Rows - 1;
3786 if (msg_col >= Columns)
3787 msg_col = Columns - 1;
3788 return TRUE;
3789}
3790
3791/*
3792 * Clear from current message position to end of screen.
3793 * Skip this when ":silent" was used, no need to clear for redirection.
3794 */
3795 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003796msg_clr_eos(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003797{
3798 if (msg_silent == 0)
3799 msg_clr_eos_force();
3800}
3801
3802/*
3803 * Clear from current message position to end of screen.
3804 * Note: msg_col is not updated, so we remember the end of the message
3805 * for msg_check().
3806 */
3807 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003808msg_clr_eos_force(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003809{
Bram Moolenaar3b474dc2022-09-01 17:01:32 +01003810#ifdef HAS_MESSAGE_WINDOW
3811 if (in_echowindow)
3812 return; // messages go into a popup
3813#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003814 if (msg_use_printf())
3815 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003816 if (full_screen) // only when termcap codes are valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00003817 {
3818 if (*T_CD)
Bram Moolenaar85a20022019-12-21 18:25:54 +01003819 out_str(T_CD); // clear to end of display
Bram Moolenaar071d4272004-06-13 20:20:40 +00003820 else if (*T_CE)
Bram Moolenaar85a20022019-12-21 18:25:54 +01003821 out_str(T_CE); // clear to end of line
Bram Moolenaar071d4272004-06-13 20:20:40 +00003822 }
3823 }
Bram Moolenaara2a89732022-08-31 14:46:18 +01003824 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003825 {
Shougo Matsushitabe2b03c2024-04-08 22:11:50 +02003826 int msg_attr;
3827
3828 msg_attr = HL_ATTR(HLF_MSG);
3829
Bram Moolenaar071d4272004-06-13 20:20:40 +00003830#ifdef FEAT_RIGHTLEFT
3831 if (cmdmsg_rl)
3832 {
Shougo Matsushitabe2b03c2024-04-08 22:11:50 +02003833 screen_fill(msg_row, msg_row + 1, 0, msg_col + 1,
3834 ' ', ' ', msg_attr);
3835 screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns,
3836 ' ', ' ', msg_attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003837 }
3838 else
3839#endif
3840 {
3841 screen_fill(msg_row, msg_row + 1, msg_col, (int)Columns,
Shougo Matsushitabe2b03c2024-04-08 22:11:50 +02003842 ' ', ' ', msg_attr);
3843 screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns,
3844 ' ', ' ', msg_attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003845 }
3846 }
3847}
3848
3849/*
3850 * Clear the command line.
3851 */
3852 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003853msg_clr_cmdline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003854{
3855 msg_row = cmdline_row;
3856 msg_col = 0;
3857 msg_clr_eos_force();
3858}
3859
3860/*
3861 * end putting a message on the screen
Bram Moolenaar13608d82022-08-29 15:06:50 +01003862 * call wait_return() if the message does not fit in the available space
3863 * return TRUE if wait_return() not called.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003864 */
3865 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003866msg_end(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003867{
3868 /*
Bram Moolenaar1a4a75c2013-07-28 16:03:06 +02003869 * If the string is larger than the window,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003870 * or the ruler option is set and we run into it,
3871 * we have to redraw the window.
3872 * Do not do this if we are abandoning the file or editing the command line.
3873 */
Bram Moolenaar24959102022-05-07 20:01:16 +01003874 if (!exiting && need_wait_return && !(State & MODE_CMDLINE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003875 {
3876 wait_return(FALSE);
3877 return FALSE;
3878 }
3879 out_flush();
3880 return TRUE;
3881}
3882
3883/*
3884 * If the written message runs into the shown command or ruler, we have to
3885 * wait for hit-return and redraw the window later.
3886 */
3887 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003888msg_check(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003889{
Bram Moolenaar35a4fbc2022-08-28 14:39:53 +01003890 if (msg_row == Rows - 1 && msg_col >= sc_col
3891#ifdef HAS_MESSAGE_WINDOW
Bram Moolenaara2a89732022-08-31 14:46:18 +01003892 && !in_echowindow
Bram Moolenaar35a4fbc2022-08-28 14:39:53 +01003893#endif
3894 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003895 {
3896 need_wait_return = TRUE;
3897 redraw_cmdline = TRUE;
3898 }
3899}
3900
3901/*
3902 * May write a string to the redirection file.
3903 * When "maxlen" is -1 write the whole string, otherwise up to "maxlen" bytes.
3904 */
3905 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003906redir_write(char_u *str, int maxlen)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003907{
3908 char_u *s = str;
3909 static int cur_col = 0;
3910
Bram Moolenaar85a20022019-12-21 18:25:54 +01003911 // Don't do anything for displaying prompts and the like.
Bram Moolenaar8b044b32005-05-31 22:05:58 +00003912 if (redir_off)
3913 return;
3914
Bram Moolenaar85a20022019-12-21 18:25:54 +01003915 // If 'verbosefile' is set prepare for writing in that file.
Bram Moolenaarb5b5b892011-09-14 15:39:29 +02003916 if (*p_vfile != NUL && verbose_fd == NULL)
3917 verbose_open();
Bram Moolenaar8b044b32005-05-31 22:05:58 +00003918
Bram Moolenaar77ab2802009-04-22 12:44:48 +00003919 if (redirecting())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003920 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003921 // If the string doesn't start with CR or NL, go to msg_col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003922 if (*s != '\n' && *s != '\r')
3923 {
3924 while (cur_col < msg_col)
3925 {
3926#ifdef FEAT_EVAL
Bram Moolenaar79815f12016-07-09 17:07:29 +02003927 if (redir_execute)
3928 execute_redir_str((char_u *)" ", -1);
Bram Moolenaarbc5d6dd2016-07-07 23:04:18 +02003929 else if (redir_reg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003930 write_reg_contents(redir_reg, (char_u *)" ", -1, TRUE);
Bram Moolenaardf177f62005-02-22 08:39:57 +00003931 else if (redir_vname)
3932 var_redir_str((char_u *)" ", -1);
Bram Moolenaarb5b5b892011-09-14 15:39:29 +02003933 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003934#endif
Bram Moolenaarb5b5b892011-09-14 15:39:29 +02003935 if (redir_fd != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003936 fputs(" ", redir_fd);
Bram Moolenaarb5b5b892011-09-14 15:39:29 +02003937 if (verbose_fd != NULL)
3938 fputs(" ", verbose_fd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003939 ++cur_col;
3940 }
3941 }
3942
3943#ifdef FEAT_EVAL
Bram Moolenaar79815f12016-07-09 17:07:29 +02003944 if (redir_execute)
3945 execute_redir_str(s, maxlen);
Bram Moolenaarbc5d6dd2016-07-07 23:04:18 +02003946 else if (redir_reg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003947 write_reg_contents(redir_reg, s, maxlen, TRUE);
Bram Moolenaarbc5d6dd2016-07-07 23:04:18 +02003948 else if (redir_vname)
Bram Moolenaardf177f62005-02-22 08:39:57 +00003949 var_redir_str(s, maxlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003950#endif
3951
Bram Moolenaar85a20022019-12-21 18:25:54 +01003952 // Write and adjust the current column.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003953 while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen))
3954 {
3955#ifdef FEAT_EVAL
Bram Moolenaar79815f12016-07-09 17:07:29 +02003956 if (!redir_reg && !redir_vname && !redir_execute)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003957#endif
Bram Moolenaarb5b5b892011-09-14 15:39:29 +02003958 if (redir_fd != NULL)
3959 putc(*s, redir_fd);
3960 if (verbose_fd != NULL)
3961 putc(*s, verbose_fd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003962 if (*s == '\r' || *s == '\n')
3963 cur_col = 0;
3964 else if (*s == '\t')
3965 cur_col += (8 - cur_col % 8);
3966 else
3967 ++cur_col;
3968 ++s;
3969 }
3970
Bram Moolenaar85a20022019-12-21 18:25:54 +01003971 if (msg_silent != 0) // should update msg_col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003972 msg_col = cur_col;
3973 }
3974}
3975
Bram Moolenaar77ab2802009-04-22 12:44:48 +00003976 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003977redirecting(void)
Bram Moolenaar77ab2802009-04-22 12:44:48 +00003978{
Bram Moolenaarb5b5b892011-09-14 15:39:29 +02003979 return redir_fd != NULL || *p_vfile != NUL
Bram Moolenaar77ab2802009-04-22 12:44:48 +00003980#ifdef FEAT_EVAL
Bram Moolenaar79815f12016-07-09 17:07:29 +02003981 || redir_reg || redir_vname || redir_execute
Bram Moolenaar77ab2802009-04-22 12:44:48 +00003982#endif
3983 ;
3984}
3985
Bram Moolenaar071d4272004-06-13 20:20:40 +00003986/*
Bram Moolenaarf1dc4962007-05-10 16:50:23 +00003987 * Before giving verbose message.
Bram Moolenaar8b044b32005-05-31 22:05:58 +00003988 * Must always be called paired with verbose_leave()!
3989 */
3990 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003991verbose_enter(void)
Bram Moolenaar8b044b32005-05-31 22:05:58 +00003992{
3993 if (*p_vfile != NUL)
3994 ++msg_silent;
3995}
3996
3997/*
3998 * After giving verbose message.
3999 * Must always be called paired with verbose_enter()!
4000 */
4001 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004002verbose_leave(void)
Bram Moolenaar8b044b32005-05-31 22:05:58 +00004003{
4004 if (*p_vfile != NUL)
4005 if (--msg_silent < 0)
4006 msg_silent = 0;
4007}
4008
4009/*
4010 * Like verbose_enter() and set msg_scroll when displaying the message.
4011 */
4012 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004013verbose_enter_scroll(void)
Bram Moolenaar8b044b32005-05-31 22:05:58 +00004014{
4015 if (*p_vfile != NUL)
4016 ++msg_silent;
4017 else
Bram Moolenaar85a20022019-12-21 18:25:54 +01004018 // always scroll up, don't overwrite
Bram Moolenaar8b044b32005-05-31 22:05:58 +00004019 msg_scroll = TRUE;
4020}
4021
4022/*
4023 * Like verbose_leave() and set cmdline_row when displaying the message.
4024 */
4025 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004026verbose_leave_scroll(void)
Bram Moolenaar8b044b32005-05-31 22:05:58 +00004027{
4028 if (*p_vfile != NUL)
4029 {
4030 if (--msg_silent < 0)
4031 msg_silent = 0;
4032 }
4033 else
4034 cmdline_row = msg_row;
4035}
4036
Bram Moolenaar8b044b32005-05-31 22:05:58 +00004037/*
4038 * Called when 'verbosefile' is set: stop writing to the file.
4039 */
4040 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004041verbose_stop(void)
Bram Moolenaar8b044b32005-05-31 22:05:58 +00004042{
4043 if (verbose_fd != NULL)
4044 {
4045 fclose(verbose_fd);
4046 verbose_fd = NULL;
4047 }
4048 verbose_did_open = FALSE;
4049}
4050
4051/*
4052 * Open the file 'verbosefile'.
4053 * Return FAIL or OK.
4054 */
4055 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004056verbose_open(void)
Bram Moolenaar8b044b32005-05-31 22:05:58 +00004057{
4058 if (verbose_fd == NULL && !verbose_did_open)
4059 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01004060 // Only give the error message once.
Bram Moolenaar8b044b32005-05-31 22:05:58 +00004061 verbose_did_open = TRUE;
4062
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +00004063 verbose_fd = mch_fopen((char *)p_vfile, "a");
Bram Moolenaar8b044b32005-05-31 22:05:58 +00004064 if (verbose_fd == NULL)
4065 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004066 semsg(_(e_cant_open_file_str), p_vfile);
Bram Moolenaar8b044b32005-05-31 22:05:58 +00004067 return FAIL;
4068 }
4069 }
4070 return OK;
4071}
4072
4073/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004074 * Give a warning message (for searching).
4075 * Use 'w' highlighting and may repeat the message after redrawing
4076 */
4077 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004078give_warning(char_u *message, int hl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004079{
Bram Moolenaarcc7eb2a2021-07-11 19:12:04 +02004080 give_warning_with_source(message, hl, FALSE);
4081}
4082
4083 void
4084give_warning_with_source(char_u *message, int hl, int with_source)
4085{
Bram Moolenaar85a20022019-12-21 18:25:54 +01004086 // Don't do this for ":silent".
Bram Moolenaar071d4272004-06-13 20:20:40 +00004087 if (msg_silent != 0)
4088 return;
4089
Bram Moolenaar85a20022019-12-21 18:25:54 +01004090 // Don't want a hit-enter prompt here.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004091 ++no_wait_return;
Bram Moolenaared203462004-06-16 11:19:22 +00004092
Bram Moolenaar071d4272004-06-13 20:20:40 +00004093#ifdef FEAT_EVAL
4094 set_vim_var_string(VV_WARNINGMSG, message, -1);
4095#endif
Bram Moolenaard23a8232018-02-10 18:45:26 +01004096 VIM_CLEAR(keep_msg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004097 if (hl)
Bram Moolenaar8820b482017-03-16 17:23:31 +01004098 keep_msg_attr = HL_ATTR(HLF_W);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004099 else
4100 keep_msg_attr = 0;
Bram Moolenaarcc7eb2a2021-07-11 19:12:04 +02004101
4102 if (with_source)
4103 {
4104 // Do what msg() does, but with a column offset if the warning should
4105 // be after the mode message.
4106 msg_start();
4107 msg_source(HL_ATTR(HLF_W));
4108 msg_puts(" ");
4109 msg_puts_attr((char *)message, HL_ATTR(HLF_W) | MSG_HIST);
4110 msg_clr_eos();
4111 (void)msg_end();
4112 }
4113 else if (msg_attr((char *)message, keep_msg_attr) && msg_scrolled == 0)
Bram Moolenaar030f0df2006-02-21 22:02:53 +00004114 set_keep_msg(message, keep_msg_attr);
Bram Moolenaarcc7eb2a2021-07-11 19:12:04 +02004115
Bram Moolenaar85a20022019-12-21 18:25:54 +01004116 msg_didout = FALSE; // overwrite this message
4117 msg_nowait = TRUE; // don't wait for this message
Bram Moolenaar071d4272004-06-13 20:20:40 +00004118 msg_col = 0;
Bram Moolenaared203462004-06-16 11:19:22 +00004119
Bram Moolenaar071d4272004-06-13 20:20:40 +00004120 --no_wait_return;
4121}
4122
Bram Moolenaar113e1072019-01-20 15:30:40 +01004123#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarf8be4612017-06-23 20:52:40 +02004124 void
4125give_warning2(char_u *message, char_u *a1, int hl)
4126{
Bram Moolenaare3a22cb2019-10-14 22:01:57 +02004127 if (IObuff == NULL)
4128 {
4129 // Very early in initialisation and already something wrong, just give
4130 // the raw message so the user at least gets a hint.
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00004131 give_warning(message, hl);
Bram Moolenaare3a22cb2019-10-14 22:01:57 +02004132 }
4133 else
4134 {
4135 vim_snprintf((char *)IObuff, IOSIZE, (char *)message, a1);
4136 give_warning(IObuff, hl);
4137 }
Bram Moolenaarf8be4612017-06-23 20:52:40 +02004138}
Bram Moolenaar113e1072019-01-20 15:30:40 +01004139#endif
Bram Moolenaarf8be4612017-06-23 20:52:40 +02004140
Bram Moolenaar071d4272004-06-13 20:20:40 +00004141/*
4142 * Advance msg cursor to column "col".
4143 */
4144 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004145msg_advance(int col)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004146{
Bram Moolenaar85a20022019-12-21 18:25:54 +01004147 if (msg_silent != 0) // nothing to advance to
Bram Moolenaar071d4272004-06-13 20:20:40 +00004148 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01004149 msg_col = col; // for redirection, may fill it up later
Bram Moolenaar071d4272004-06-13 20:20:40 +00004150 return;
4151 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01004152 if (col >= Columns) // not enough room
Bram Moolenaar071d4272004-06-13 20:20:40 +00004153 col = Columns - 1;
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004154#ifdef FEAT_RIGHTLEFT
4155 if (cmdmsg_rl)
4156 while (msg_col > Columns - col)
4157 msg_putchar(' ');
4158 else
4159#endif
4160 while (msg_col < col)
4161 msg_putchar(' ');
Bram Moolenaar071d4272004-06-13 20:20:40 +00004162}
4163
Christian Brabandt45e07042024-11-11 20:52:55 +01004164/*
4165 * Warn about missing Clipboard Support
4166 */
4167 void
4168msg_warn_missing_clipboard(void)
4169{
4170 if (!global_busy && !did_warn_clipboard)
4171 {
Christian Brabandt8b94afc2024-11-17 16:02:41 +01004172#ifdef FEAT_CLIPBOARD
Yegappan Lakshmanane89aef32025-05-14 20:31:55 +02004173 msg(_("W23: Clipboard register not available, using register 0"));
Christian Brabandt8b94afc2024-11-17 16:02:41 +01004174#else
Yegappan Lakshmanane89aef32025-05-14 20:31:55 +02004175 msg(_("W24: Clipboard register not available. See :h W24"));
Christian Brabandt8b94afc2024-11-17 16:02:41 +01004176#endif
Yegappan Lakshmanane89aef32025-05-14 20:31:55 +02004177 did_warn_clipboard = TRUE;
Christian Brabandt45e07042024-11-11 20:52:55 +01004178 }
4179}
4180
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181#if defined(FEAT_CON_DIALOG) || defined(PROTO)
4182/*
4183 * Used for "confirm()" function, and the :confirm command prefix.
4184 * Versions which haven't got flexible dialogs yet, and console
4185 * versions, get this generic handler which uses the command line.
4186 *
4187 * type = one of:
4188 * VIM_QUESTION, VIM_INFO, VIM_WARNING, VIM_ERROR or VIM_GENERIC
4189 * title = title string (can be NULL for default)
4190 * (neither used in console dialogs at the moment)
4191 *
4192 * Format of the "buttons" string:
4193 * "Button1Name\nButton2Name\nButton3Name"
4194 * The first button should normally be the default/accept
4195 * The second button should be the 'Cancel' button
4196 * Other buttons- use your imagination!
4197 * A '&' in a button name becomes a shortcut, so each '&' should be before a
4198 * different letter.
Rob Pilling8196e942022-02-11 15:12:10 +00004199 *
4200 * Returns 0 if cancelled, otherwise the nth button (1-indexed).
Bram Moolenaar071d4272004-06-13 20:20:40 +00004201 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004202 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004203do_dialog(
4204 int type UNUSED,
4205 char_u *title UNUSED,
4206 char_u *message,
4207 char_u *buttons,
4208 int dfltbutton,
Bram Moolenaar85a20022019-12-21 18:25:54 +01004209 char_u *textfield UNUSED, // IObuff for inputdialog(), NULL
4210 // otherwise
4211 int ex_cmd) // when TRUE pressing : accepts default and starts
4212 // Ex command
Bram Moolenaar071d4272004-06-13 20:20:40 +00004213{
4214 int oldState;
4215 int retval = 0;
4216 char_u *hotkeys;
4217 int c;
4218 int i;
Bram Moolenaar27321db2020-07-06 21:24:57 +02004219 tmode_T save_tmode;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004220
4221#ifndef NO_CONSOLE
Bram Moolenaar85a20022019-12-21 18:25:54 +01004222 // Don't output anything in silent mode ("ex -s")
Bram Moolenaar071d4272004-06-13 20:20:40 +00004223 if (silent_mode)
Bram Moolenaar85a20022019-12-21 18:25:54 +01004224 return dfltbutton; // return default option
Bram Moolenaar071d4272004-06-13 20:20:40 +00004225#endif
4226
4227#ifdef FEAT_GUI_DIALOG
Bram Moolenaar85a20022019-12-21 18:25:54 +01004228 // When GUI is running and 'c' not in 'guioptions', use the GUI dialog
Bram Moolenaar071d4272004-06-13 20:20:40 +00004229 if (gui.in_use && vim_strchr(p_go, GO_CONDIALOG) == NULL)
4230 {
Bram Moolenaar2d12c252022-06-13 21:42:45 +01004231 // --gui-dialog-file: write text to a file
4232 if (gui_dialog_log(title, message))
4233 c = dfltbutton;
4234 else
4235 c = gui_mch_dialog(type, title, message, buttons, dfltbutton,
Bram Moolenaard2c340a2011-01-17 20:08:11 +01004236 textfield, ex_cmd);
Bram Moolenaar85a20022019-12-21 18:25:54 +01004237 // avoid a hit-enter prompt without clearing the cmdline
Bram Moolenaar8de49e12009-02-11 17:47:54 +00004238 need_wait_return = FALSE;
4239 emsg_on_display = FALSE;
4240 cmdline_row = msg_row;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004241
Bram Moolenaar85a20022019-12-21 18:25:54 +01004242 // Flush output to avoid that further messages and redrawing is done
4243 // in the wrong order.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004244 out_flush();
4245 gui_mch_update();
4246
4247 return c;
4248 }
4249#endif
4250
4251 oldState = State;
Bram Moolenaar24959102022-05-07 20:01:16 +01004252 State = MODE_CONFIRM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004253 setmouse();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004254
Bram Moolenaar27321db2020-07-06 21:24:57 +02004255 // Ensure raw mode here.
4256 save_tmode = cur_tmode;
4257 settmode(TMODE_RAW);
4258
Bram Moolenaar071d4272004-06-13 20:20:40 +00004259 /*
4260 * Since we wait for a keypress, don't make the
4261 * user press RETURN as well afterwards.
4262 */
4263 ++no_wait_return;
4264 hotkeys = msg_show_console_dialog(message, buttons, dfltbutton);
4265
4266 if (hotkeys != NULL)
4267 {
4268 for (;;)
4269 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01004270 // Get a typed character directly from the user.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004271 c = get_keystroke();
4272 switch (c)
4273 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01004274 case CAR: // User accepts default option
Bram Moolenaar071d4272004-06-13 20:20:40 +00004275 case NL:
4276 retval = dfltbutton;
4277 break;
Bram Moolenaar85a20022019-12-21 18:25:54 +01004278 case Ctrl_C: // User aborts/cancels
Bram Moolenaar071d4272004-06-13 20:20:40 +00004279 case ESC:
4280 retval = 0;
4281 break;
Bram Moolenaar85a20022019-12-21 18:25:54 +01004282 default: // Could be a hotkey?
4283 if (c < 0) // special keys are ignored here
Bram Moolenaar071d4272004-06-13 20:20:40 +00004284 continue;
Bram Moolenaard2c340a2011-01-17 20:08:11 +01004285 if (c == ':' && ex_cmd)
4286 {
4287 retval = dfltbutton;
Bram Moolenaarb42c0d52020-05-29 22:41:41 +02004288 ins_char_typebuf(':', 0);
Bram Moolenaard2c340a2011-01-17 20:08:11 +01004289 break;
4290 }
4291
Bram Moolenaar85a20022019-12-21 18:25:54 +01004292 // Make the character lowercase, as chars in "hotkeys" are.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004293 c = MB_TOLOWER(c);
4294 retval = 1;
4295 for (i = 0; hotkeys[i]; ++i)
4296 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004297 if (has_mbyte)
4298 {
4299 if ((*mb_ptr2char)(hotkeys + i) == c)
4300 break;
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004301 i += (*mb_ptr2len)(hotkeys + i) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004302 }
4303 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004304 if (hotkeys[i] == c)
4305 break;
4306 ++retval;
4307 }
4308 if (hotkeys[i])
4309 break;
Bram Moolenaar85a20022019-12-21 18:25:54 +01004310 // No hotkey match, so keep waiting
Bram Moolenaar071d4272004-06-13 20:20:40 +00004311 continue;
4312 }
4313 break;
4314 }
4315
4316 vim_free(hotkeys);
4317 }
4318
Bram Moolenaar27321db2020-07-06 21:24:57 +02004319 settmode(save_tmode);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004320 State = oldState;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004321 setmouse();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004322 --no_wait_return;
4323 msg_end_prompt();
4324
4325 return retval;
4326}
4327
Bram Moolenaar071d4272004-06-13 20:20:40 +00004328/*
4329 * Copy one character from "*from" to "*to", taking care of multi-byte
4330 * characters. Return the length of the character in bytes.
4331 */
4332 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004333copy_char(
4334 char_u *from,
4335 char_u *to,
Bram Moolenaar85a20022019-12-21 18:25:54 +01004336 int lowercase) // make character lower case
Bram Moolenaar071d4272004-06-13 20:20:40 +00004337{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004338 int len;
4339 int c;
4340
4341 if (has_mbyte)
4342 {
4343 if (lowercase)
4344 {
4345 c = MB_TOLOWER((*mb_ptr2char)(from));
4346 return (*mb_char2bytes)(c, to);
4347 }
4348 else
4349 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004350 len = (*mb_ptr2len)(from);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004351 mch_memmove(to, from, (size_t)len);
4352 return len;
4353 }
4354 }
4355 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004356 {
4357 if (lowercase)
4358 *to = (char_u)TOLOWER_LOC(*from);
4359 else
4360 *to = *from;
4361 return 1;
4362 }
4363}
4364
4365/*
4366 * Format the dialog string, and display it at the bottom of
4367 * the screen. Return a string of hotkey chars (if defined) for
4368 * each 'button'. If a button has no hotkey defined, the first character of
4369 * the button is used.
4370 * The hotkeys can be multi-byte characters, but without combining chars.
4371 *
4372 * Returns an allocated string with hotkeys, or NULL for error.
4373 */
4374 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004375msg_show_console_dialog(
4376 char_u *message,
4377 char_u *buttons,
4378 int dfltbutton)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004379{
4380 int len = 0;
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01004381#define HOTK_LEN (has_mbyte ? MB_MAXBYTES : 1)
Bram Moolenaar85a20022019-12-21 18:25:54 +01004382 int lenhotkey = HOTK_LEN; // count first button
Bram Moolenaar071d4272004-06-13 20:20:40 +00004383 char_u *hotk = NULL;
4384 char_u *msgp = NULL;
4385 char_u *hotkp = NULL;
4386 char_u *r;
4387 int copy;
4388#define HAS_HOTKEY_LEN 30
4389 char_u has_hotkey[HAS_HOTKEY_LEN];
Bram Moolenaar85a20022019-12-21 18:25:54 +01004390 int first_hotkey = FALSE; // first char of button is hotkey
Bram Moolenaar071d4272004-06-13 20:20:40 +00004391 int idx;
4392
4393 has_hotkey[0] = FALSE;
4394
4395 /*
4396 * First loop: compute the size of memory to allocate.
4397 * Second loop: copy to the allocated memory.
4398 */
4399 for (copy = 0; copy <= 1; ++copy)
4400 {
4401 r = buttons;
4402 idx = 0;
4403 while (*r)
4404 {
4405 if (*r == DLG_BUTTON_SEP)
4406 {
4407 if (copy)
4408 {
4409 *msgp++ = ',';
Bram Moolenaar85a20022019-12-21 18:25:54 +01004410 *msgp++ = ' '; // '\n' -> ', '
Bram Moolenaar071d4272004-06-13 20:20:40 +00004411
Bram Moolenaar85a20022019-12-21 18:25:54 +01004412 // advance to next hotkey and set default hotkey
Bram Moolenaar071d4272004-06-13 20:20:40 +00004413 if (has_mbyte)
Bram Moolenaar6a516062007-07-05 08:11:42 +00004414 hotkp += STRLEN(hotkp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004415 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004416 ++hotkp;
Bram Moolenaar6a516062007-07-05 08:11:42 +00004417 hotkp[copy_char(r + 1, hotkp, TRUE)] = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004418 if (dfltbutton)
4419 --dfltbutton;
4420
Bram Moolenaar85a20022019-12-21 18:25:54 +01004421 // If no hotkey is specified first char is used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004422 if (idx < HAS_HOTKEY_LEN - 1 && !has_hotkey[++idx])
4423 first_hotkey = TRUE;
4424 }
4425 else
4426 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01004427 len += 3; // '\n' -> ', '; 'x' -> '(x)'
4428 lenhotkey += HOTK_LEN; // each button needs a hotkey
Bram Moolenaar071d4272004-06-13 20:20:40 +00004429 if (idx < HAS_HOTKEY_LEN - 1)
4430 has_hotkey[++idx] = FALSE;
4431 }
4432 }
4433 else if (*r == DLG_HOTKEY_CHAR || first_hotkey)
4434 {
4435 if (*r == DLG_HOTKEY_CHAR)
4436 ++r;
4437 first_hotkey = FALSE;
4438 if (copy)
4439 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01004440 if (*r == DLG_HOTKEY_CHAR) // '&&a' -> '&a'
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441 *msgp++ = *r;
4442 else
4443 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01004444 // '&a' -> '[a]'
Bram Moolenaar071d4272004-06-13 20:20:40 +00004445 *msgp++ = (dfltbutton == 1) ? '[' : '(';
4446 msgp += copy_char(r, msgp, FALSE);
4447 *msgp++ = (dfltbutton == 1) ? ']' : ')';
4448
Bram Moolenaar85a20022019-12-21 18:25:54 +01004449 // redefine hotkey
Bram Moolenaar6a516062007-07-05 08:11:42 +00004450 hotkp[copy_char(r, hotkp, TRUE)] = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004451 }
4452 }
4453 else
4454 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01004455 ++len; // '&a' -> '[a]'
Bram Moolenaar071d4272004-06-13 20:20:40 +00004456 if (idx < HAS_HOTKEY_LEN - 1)
4457 has_hotkey[idx] = TRUE;
4458 }
4459 }
4460 else
4461 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01004462 // everything else copy literally
Bram Moolenaar071d4272004-06-13 20:20:40 +00004463 if (copy)
4464 msgp += copy_char(r, msgp, FALSE);
4465 }
4466
Bram Moolenaar85a20022019-12-21 18:25:54 +01004467 // advance to the next character
Bram Moolenaar91acfff2017-03-12 19:22:36 +01004468 MB_PTR_ADV(r);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004469 }
4470
4471 if (copy)
4472 {
4473 *msgp++ = ':';
4474 *msgp++ = ' ';
4475 *msgp = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004476 }
4477 else
4478 {
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004479 len += (int)(STRLEN(message)
Bram Moolenaar85a20022019-12-21 18:25:54 +01004480 + 2 // for the NL's
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00004481 + STRLEN(buttons)
Bram Moolenaar85a20022019-12-21 18:25:54 +01004482 + 3); // for the ": " and NUL
4483 lenhotkey++; // for the NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484
Bram Moolenaar85a20022019-12-21 18:25:54 +01004485 // If no hotkey is specified first char is used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004486 if (!has_hotkey[0])
4487 {
4488 first_hotkey = TRUE;
Bram Moolenaar85a20022019-12-21 18:25:54 +01004489 len += 2; // "x" -> "[x]"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004490 }
4491
4492 /*
4493 * Now allocate and load the strings
4494 */
4495 vim_free(confirm_msg);
4496 confirm_msg = alloc(len);
4497 if (confirm_msg == NULL)
4498 return NULL;
4499 *confirm_msg = NUL;
4500 hotk = alloc(lenhotkey);
4501 if (hotk == NULL)
4502 return NULL;
4503
4504 *confirm_msg = '\n';
4505 STRCPY(confirm_msg + 1, message);
4506
4507 msgp = confirm_msg + 1 + STRLEN(message);
4508 hotkp = hotk;
4509
Bram Moolenaar85a20022019-12-21 18:25:54 +01004510 // Define first default hotkey. Keep the hotkey string NUL
4511 // terminated to avoid reading past the end.
Bram Moolenaar6a516062007-07-05 08:11:42 +00004512 hotkp[copy_char(buttons, hotkp, TRUE)] = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004513
Bram Moolenaar85a20022019-12-21 18:25:54 +01004514 // Remember where the choices start, displaying starts here when
4515 // "hotkp" typed at the more prompt.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004516 confirm_msg_tail = msgp;
4517 *msgp++ = '\n';
4518 }
4519 }
4520
4521 display_confirm_msg();
4522 return hotk;
4523}
4524
4525/*
4526 * Display the ":confirm" message. Also called when screen resized.
4527 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02004528 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004529display_confirm_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004530{
Bram Moolenaar85a20022019-12-21 18:25:54 +01004531 // avoid that 'q' at the more prompt truncates the message here
Bram Moolenaar071d4272004-06-13 20:20:40 +00004532 ++confirm_msg_used;
4533 if (confirm_msg != NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004534 msg_puts_attr((char *)confirm_msg, HL_ATTR(HLF_M));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004535 --confirm_msg_used;
4536}
4537
Bram Moolenaar85a20022019-12-21 18:25:54 +01004538#endif // FEAT_CON_DIALOG
Bram Moolenaar071d4272004-06-13 20:20:40 +00004539
4540#if defined(FEAT_CON_DIALOG) || defined(FEAT_GUI_DIALOG)
4541
4542 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004543vim_dialog_yesno(
4544 int type,
4545 char_u *title,
4546 char_u *message,
4547 int dflt)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004548{
4549 if (do_dialog(type,
4550 title == NULL ? (char_u *)_("Question") : title,
4551 message,
Bram Moolenaard2c340a2011-01-17 20:08:11 +01004552 (char_u *)_("&Yes\n&No"), dflt, NULL, FALSE) == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004553 return VIM_YES;
4554 return VIM_NO;
4555}
4556
4557 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004558vim_dialog_yesnocancel(
4559 int type,
4560 char_u *title,
4561 char_u *message,
4562 int dflt)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004563{
4564 switch (do_dialog(type,
4565 title == NULL ? (char_u *)_("Question") : title,
4566 message,
Bram Moolenaard2c340a2011-01-17 20:08:11 +01004567 (char_u *)_("&Yes\n&No\n&Cancel"), dflt, NULL, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004568 {
4569 case 1: return VIM_YES;
4570 case 2: return VIM_NO;
4571 }
4572 return VIM_CANCEL;
4573}
4574
4575 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004576vim_dialog_yesnoallcancel(
4577 int type,
4578 char_u *title,
4579 char_u *message,
4580 int dflt)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004581{
4582 switch (do_dialog(type,
4583 title == NULL ? (char_u *)"Question" : title,
4584 message,
4585 (char_u *)_("&Yes\n&No\nSave &All\n&Discard All\n&Cancel"),
Bram Moolenaard2c340a2011-01-17 20:08:11 +01004586 dflt, NULL, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004587 {
4588 case 1: return VIM_YES;
4589 case 2: return VIM_NO;
4590 case 3: return VIM_ALL;
4591 case 4: return VIM_DISCARDALL;
4592 }
4593 return VIM_CANCEL;
4594}
4595
Bram Moolenaar85a20022019-12-21 18:25:54 +01004596#endif // FEAT_GUI_DIALOG || FEAT_CON_DIALOG