diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2025-02-05 23:09:29 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2025-02-05 23:09:29 +0000 |
commit | d5f194ce780c95821a855aca3c19426576d28ae0 (patch) | |
tree | d45f461b19f9118ad2bb1f440a7a08973ad18832 /src/nvim/message.c | |
parent | c5d770d311841ea5230426cc4c868e8db27300a8 (diff) | |
parent | 44740e561fc93afe3ebecfd3618bda2d2abeafb0 (diff) | |
download | rneovim-rahm.tar.gz rneovim-rahm.tar.bz2 rneovim-rahm.zip |
Diffstat (limited to 'src/nvim/message.c')
-rw-r--r-- | src/nvim/message.c | 478 |
1 files changed, 290 insertions, 188 deletions
diff --git a/src/nvim/message.c b/src/nvim/message.c index 47f33c8967..6fc102e4ff 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -2,6 +2,7 @@ #include <assert.h> #include <inttypes.h> +#include <limits.h> #include <stdarg.h> #include <stdbool.h> #include <stddef.h> @@ -26,6 +27,7 @@ #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/ex_cmds_defs.h" +#include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/fileio.h" #include "nvim/garray.h" @@ -45,6 +47,7 @@ #include "nvim/mbyte.h" #include "nvim/mbyte_defs.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/message.h" #include "nvim/mouse.h" #include "nvim/ops.h" @@ -89,11 +92,21 @@ static int confirm_msg_used = false; // displaying confirm_msg # include "message.c.generated.h" #endif static char *confirm_msg = NULL; // ":confirm" message -static char *confirm_msg_tail; // tail of confirm_msg +static char *confirm_buttons; // ":confirm" buttons sent to cmdline as prompt MessageHistoryEntry *first_msg_hist = NULL; MessageHistoryEntry *last_msg_hist = NULL; static int msg_hist_len = 0; +static int msg_hist_max = 500; // The default max value is 500 + +// args in 'messagesopt' option +#define MESSAGES_OPT_HIT_ENTER "hit-enter" +#define MESSAGES_OPT_WAIT "wait:" +#define MESSAGES_OPT_HISTORY "history:" + +// The default is "hit-enter,history:500" +static int msg_flags = kOptMoptFlagHitEnter | kOptMoptFlagHistory; +static int msg_wait = 0; static FILE *verbose_fd = NULL; static bool verbose_did_open = false; @@ -119,7 +132,7 @@ bool keep_msg_more = false; // keep_msg was set by msgmore() // msg_scrolled How many lines the screen has been scrolled (because of // messages). Used in update_screen() to scroll the screen // back. Incremented each time the screen scrolls a line. -// msg_scrolled_ign true when msg_scrolled is non-zero and msg_puts_hl_id() +// msg_scrolled_ign true when msg_scrolled is non-zero and msg_puts_hl() // writes something without scrolling should not make // need_wait_return to be set. This is a hack to make ":ts" // work without an extra prompt. @@ -140,8 +153,8 @@ static Array *msg_ext_chunks = NULL; static garray_T msg_ext_last_chunk = GA_INIT(sizeof(char), 40); static sattr_T msg_ext_last_attr = -1; static int msg_ext_last_hl_id; -static size_t msg_ext_cur_len = 0; +static bool msg_ext_history = false; ///< message was added to history static bool msg_ext_overwrite = false; ///< will overwrite last message static int msg_ext_visible = 0; ///< number of messages currently visible @@ -233,7 +246,7 @@ void msg_grid_validate(void) int verb_msg(const char *s) { verbose_enter(); - int n = msg_hl_keep(s, 0, false, false); + int n = msg_keep(s, 0, false, false); verbose_leave(); return n; @@ -246,7 +259,7 @@ int verb_msg(const char *s) bool msg(const char *s, const int hl_id) FUNC_ATTR_NONNULL_ARG(1) { - return msg_hl_keep(s, hl_id, false, false); + return msg_keep(s, hl_id, false, false); } /// Similar to msg_outtrans_len, but support newlines and tabs. @@ -279,26 +292,45 @@ void msg_multiline(String str, int hl_id, bool check_int, bool hist, bool *need_ } } -void msg_multihl(HlMessage hl_msg, const char *kind, bool history) +// Avoid starting a new message for each chunk and adding message to history in msg_keep(). +static bool is_multihl = false; + +/// Print message chunks, each with their own highlight ID. +/// +/// @param hl_msg Message chunks +/// @param kind Message kind (can be NULL to avoid setting kind) +/// @param history Whether to add message to history +/// @param err Whether to print message as an error +void msg_multihl(HlMessage hl_msg, const char *kind, bool history, bool err) { no_wait_return++; msg_start(); msg_clr_eos(); bool need_clear = false; - msg_ext_set_kind(kind); + msg_ext_history = history; + if (kind != NULL) { + msg_ext_set_kind(kind); + } + is_multihl = true; for (uint32_t i = 0; i < kv_size(hl_msg); i++) { HlMessageChunk chunk = kv_A(hl_msg, i); - msg_multiline(chunk.text, chunk.hl_id, true, false, &need_clear); + if (err) { + emsg_multiline(chunk.text.data, kind, chunk.hl_id, true); + } else { + msg_multiline(chunk.text, chunk.hl_id, true, false, &need_clear); + } + assert(!ui_has(kUIMessages) || kind == NULL || msg_ext_kind == kind); } if (history && kv_size(hl_msg)) { add_msg_hist_multihl(NULL, 0, 0, true, hl_msg); } + is_multihl = false; no_wait_return--; msg_end(); } /// @param keep set keep_msg if it doesn't scroll -bool msg_hl_keep(const char *s, int hl_id, bool keep, bool multiline) +bool msg_keep(const char *s, int hl_id, bool keep, bool multiline) FUNC_ATTR_NONNULL_ALL { static int entered = 0; @@ -328,18 +360,19 @@ bool msg_hl_keep(const char *s, int hl_id, bool keep, bool multiline) } entered++; - // Add message to history (unless it's a repeated kept message or a - // truncated message) - if (s != keep_msg - || (*s != '<' - && last_msg_hist != NULL - && last_msg_hist->msg != NULL - && strcmp(s, last_msg_hist->msg) != 0)) { + // Add message to history (unless it's a truncated, repeated kept or multihl message). + if ((s != keep_msg + || (*s != '<' + && last_msg_hist != NULL + && last_msg_hist->msg != NULL + && strcmp(s, last_msg_hist->msg) != 0)) && !is_multihl) { add_msg_hist(s, -1, hl_id, multiline); } + if (!is_multihl) { + msg_start(); + } // Truncate the message if needed. - msg_start(); char *buf = msg_strtrunc(s, false); if (buf != NULL) { s = buf; @@ -354,7 +387,10 @@ bool msg_hl_keep(const char *s, int hl_id, bool keep, bool multiline) if (need_clear) { msg_clr_eos(); } - bool retval = msg_end(); + bool retval = true; + if (!is_multihl) { + retval = msg_end(); + } if (keep && retval && vim_strsize(s) < (Rows - cmdline_row - 1) * Columns + sc_col) { set_keep_msg(s, 0); @@ -503,7 +539,7 @@ int smsg(int hl_id, const char *s, ...) return msg(IObuff, hl_id); } -int smsg_hl_keep(int hl_id, const char *s, ...) +int smsg_keep(int hl_id, const char *s, ...) FUNC_ATTR_PRINTF(2, 3) { va_list arglist; @@ -511,7 +547,7 @@ int smsg_hl_keep(int hl_id, const char *s, ...) va_start(arglist, s); vim_vsnprintf(IObuff, IOSIZE, s, arglist); va_end(arglist); - return msg_hl_keep(IObuff, hl_id, true, false); + return msg_keep(IObuff, hl_id, true, false); } // Remember the last sourcing name/lnum used in an error message, so that it @@ -604,6 +640,9 @@ void msg_source(int hl_id) msg_scroll = true; // this will take more than one line msg(p, hl_id); xfree(p); + if (is_multihl) { + msg_start(); // avoided in msg_keep() but need the "msg_didout" newline here + } } p = get_emsg_lnum(); if (p != NULL) { @@ -638,7 +677,7 @@ int emsg_not_now(void) return false; } -bool emsg_multiline(const char *s, bool multiline) +bool emsg_multiline(const char *s, const char *kind, int hl_id, bool multiline) { bool ignore = false; @@ -736,23 +775,26 @@ bool emsg_multiline(const char *s, bool multiline) } emsg_on_display = true; // remember there is an error message - int hl_id = HLF_E; // set highlight mode for error messages if (msg_scrolled != 0) { need_wait_return = true; // needed in case emsg() is called after } // wait_return() has reset need_wait_return // and a redraw is expected because // msg_scrolled is non-zero if (msg_ext_kind == NULL) { - msg_ext_set_kind("emsg"); + msg_ext_set_kind(kind); } // Display name and line number for the source of the error. msg_scroll = true; msg_source(hl_id); + if (msg_ext_kind == NULL) { + msg_ext_set_kind(kind); + } + // Display the error message itself. msg_nowait = false; // Wait for this msg. - return msg_hl_keep(s, hl_id, false, multiline); + return msg_keep(s, hl_id, false, multiline); } /// emsg() - display an error message @@ -763,7 +805,7 @@ bool emsg_multiline(const char *s, bool multiline) /// @return true if wait_return() not called bool emsg(const char *s) { - return emsg_multiline(s, false); + return emsg_multiline(s, "emsg", HLF_E, false); } void emsg_invreg(int name) @@ -803,7 +845,7 @@ bool semsg_multiline(const char *const fmt, ...) vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap); va_end(ap); - ret = emsg_multiline(errbuf, true); + ret = emsg_multiline(errbuf, "emsg", HLF_E, true); return ret; } @@ -887,7 +929,7 @@ void msg_schedule_semsg(const char *const fmt, ...) static void msg_semsg_multiline_event(void **argv) { char *s = argv[0]; - emsg_multiline(s, true); + emsg_multiline(s, "emsg", HLF_E, true); xfree(s); } @@ -973,28 +1015,24 @@ static void add_msg_hist(const char *s, int len, int hl_id, bool multiline) static void add_msg_hist_multihl(const char *s, int len, int hl_id, bool multiline, HlMessage multihl) { - if (msg_hist_off || msg_silent != 0) { + if (msg_hist_off || msg_silent != 0 || (s != NULL && *s == NUL)) { hl_msg_free(multihl); return; } - // Don't let the message history get too big - while (msg_hist_len > p_mhi) { - delete_first_msg(); - } - // allocate an entry and add the message at the end of the history struct msg_hist *p = xmalloc(sizeof(struct msg_hist)); if (s) { if (len < 0) { len = (int)strlen(s); } + assert(len > 0); // remove leading and trailing newlines - while (len > 0 && *s == '\n') { + while (*s == '\n') { s++; len--; } - while (len > 0 && s[len - 1] == '\n') { + while (s[len - 1] == '\n') { len--; } p->msg = xmemdupz(s, (size_t)len); @@ -1014,6 +1052,9 @@ static void add_msg_hist_multihl(const char *s, int len, int hl_id, bool multili first_msg_hist = last_msg_hist; } msg_hist_len++; + msg_ext_history = true; + + check_msg_hist(); } /// Delete the first (oldest) message from the history. @@ -1037,6 +1078,76 @@ int delete_first_msg(void) return OK; } +static void check_msg_hist(void) +{ + // Don't let the message history get too big + while (msg_hist_len > 0 && msg_hist_len > msg_hist_max) { + (void)delete_first_msg(); + } +} + +int messagesopt_changed(void) +{ + int messages_flags_new = 0; + int messages_wait_new = 0; + int messages_history_new = 0; + + char *p = p_mopt; + while (*p != NUL) { + if (strnequal(p, S_LEN(MESSAGES_OPT_HIT_ENTER))) { + p += STRLEN_LITERAL(MESSAGES_OPT_HIT_ENTER); + messages_flags_new |= kOptMoptFlagHitEnter; + } else if (strnequal(p, S_LEN(MESSAGES_OPT_WAIT)) + && ascii_isdigit(p[STRLEN_LITERAL(MESSAGES_OPT_WAIT)])) { + p += STRLEN_LITERAL(MESSAGES_OPT_WAIT); + messages_wait_new = getdigits_int(&p, false, INT_MAX); + messages_flags_new |= kOptMoptFlagWait; + } else if (strnequal(p, S_LEN(MESSAGES_OPT_HISTORY)) + && ascii_isdigit(p[STRLEN_LITERAL(MESSAGES_OPT_HISTORY)])) { + p += STRLEN_LITERAL(MESSAGES_OPT_HISTORY); + messages_history_new = getdigits_int(&p, false, INT_MAX); + messages_flags_new |= kOptMoptFlagHistory; + } + + if (*p != ',' && *p != NUL) { + return FAIL; + } + if (*p == ',') { + p++; + } + } + + // Either "wait" or "hit-enter" is required + if (!(messages_flags_new & (kOptMoptFlagHitEnter | kOptMoptFlagWait))) { + return FAIL; + } + + // "history" must be set + if (!(messages_flags_new & kOptMoptFlagHistory)) { + return FAIL; + } + + assert(messages_history_new >= 0); + // "history" must be <= 10000 + if (messages_history_new > 10000) { + return FAIL; + } + + assert(messages_wait_new >= 0); + // "wait" must be <= 10000 + if (messages_wait_new > 10000) { + return FAIL; + } + + msg_flags = messages_flags_new; + msg_wait = messages_wait_new; + + msg_hist_max = messages_history_new; + check_msg_hist(); + + return OK; +} + /// :messages command implementation void ex_messages(exarg_T *eap) FUNC_ATTR_NONNULL_ALL @@ -1109,9 +1220,9 @@ void ex_messages(exarg_T *eap) msg_hist_off = true; for (; p != NULL && !got_int; p = p->next) { if (kv_size(p->multihl)) { - msg_multihl(p->multihl, p->kind, false); + msg_multihl(p->multihl, p->kind, false, false); } else if (p->msg != NULL) { - msg_hl_keep(p->msg, p->hl_id, false, p->multiline); + msg_keep(p->msg, p->hl_id, false, p->multiline); } } msg_hist_off = false; @@ -1200,83 +1311,88 @@ void wait_return(int redraw) cmdline_row = Rows - 1; } - hit_return_msg(true); - - do { - // Remember "got_int", if it is set vgetc() probably returns a - // CTRL-C, but we need to loop then. - had_got_int = got_int; - - // Don't do mappings here, we put the character back in the - // typeahead buffer. - no_mapping++; - allow_keys++; - - // Temporarily disable Recording. If Recording is active, the - // character will be recorded later, since it will be added to the - // typebuf after the loop - const int save_reg_recording = reg_recording; - save_scriptout = scriptout; - reg_recording = 0; - scriptout = NULL; - c = safe_vgetc(); - if (had_got_int && !global_busy) { - got_int = false; - } - no_mapping--; - allow_keys--; - reg_recording = save_reg_recording; - scriptout = save_scriptout; - - // Allow scrolling back in the messages. - // Also accept scroll-down commands when messages fill the screen, - // to avoid that typing one 'j' too many makes the messages - // disappear. - if (p_more) { - if (c == 'b' || c == 'k' || c == 'u' || c == 'g' - || c == K_UP || c == K_PAGEUP) { - if (msg_scrolled > Rows) { - // scroll back to show older messages - do_more_prompt(c); - } else { - msg_didout = false; - c = K_IGNORE; - msg_col = 0; - } - if (quit_more) { - c = CAR; // just pretend CR was hit - quit_more = false; - got_int = false; - } else if (c != K_IGNORE) { + if (msg_flags & kOptMoptFlagHitEnter) { + hit_return_msg(true); + + do { + // Remember "got_int", if it is set vgetc() probably returns a + // CTRL-C, but we need to loop then. + had_got_int = got_int; + + // Don't do mappings here, we put the character back in the + // typeahead buffer. + no_mapping++; + allow_keys++; + + // Temporarily disable Recording. If Recording is active, the + // character will be recorded later, since it will be added to the + // typebuf after the loop + const int save_reg_recording = reg_recording; + save_scriptout = scriptout; + reg_recording = 0; + scriptout = NULL; + c = safe_vgetc(); + if (had_got_int && !global_busy) { + got_int = false; + } + no_mapping--; + allow_keys--; + reg_recording = save_reg_recording; + scriptout = save_scriptout; + + // Allow scrolling back in the messages. + // Also accept scroll-down commands when messages fill the screen, + // to avoid that typing one 'j' too many makes the messages + // disappear. + if (p_more) { + if (c == 'b' || c == 'k' || c == 'u' || c == 'g' + || c == K_UP || c == K_PAGEUP) { + if (msg_scrolled > Rows) { + // scroll back to show older messages + do_more_prompt(c); + } else { + msg_didout = false; + c = K_IGNORE; + msg_col = 0; + } + if (quit_more) { + c = CAR; // just pretend CR was hit + quit_more = false; + got_int = false; + } else if (c != K_IGNORE) { + c = K_IGNORE; + hit_return_msg(false); + } + } else if (msg_scrolled > Rows - 2 + && (c == 'j' || c == 'd' || c == 'f' + || c == K_DOWN || c == K_PAGEDOWN)) { c = K_IGNORE; - hit_return_msg(false); } - } else if (msg_scrolled > Rows - 2 - && (c == 'j' || c == 'd' || c == 'f' - || c == K_DOWN || c == K_PAGEDOWN)) { - c = K_IGNORE; } + } while ((had_got_int && c == Ctrl_C) + || c == K_IGNORE + || c == K_LEFTDRAG || c == K_LEFTRELEASE + || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE + || c == K_RIGHTDRAG || c == K_RIGHTRELEASE + || c == K_MOUSELEFT || c == K_MOUSERIGHT + || c == K_MOUSEDOWN || c == K_MOUSEUP + || c == K_MOUSEMOVE); + os_breakcheck(); + + // Avoid that the mouse-up event causes visual mode to start. + if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE + || c == K_X1MOUSE || c == K_X2MOUSE) { + jump_to_mouse(MOUSE_SETPOS, NULL, 0); + } else if (vim_strchr("\r\n ", c) == NULL && c != Ctrl_C) { + // Put the character back in the typeahead buffer. Don't use the + // stuff buffer, because lmaps wouldn't work. + ins_char_typebuf(vgetc_char, vgetc_mod_mask, true); + do_redraw = true; // need a redraw even though there is typeahead } - } while ((had_got_int && c == Ctrl_C) - || c == K_IGNORE - || c == K_LEFTDRAG || c == K_LEFTRELEASE - || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE - || c == K_RIGHTDRAG || c == K_RIGHTRELEASE - || c == K_MOUSELEFT || c == K_MOUSERIGHT - || c == K_MOUSEDOWN || c == K_MOUSEUP - || c == K_MOUSEMOVE); - os_breakcheck(); - - // Avoid that the mouse-up event causes visual mode to start. - if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE - || c == K_X1MOUSE || c == K_X2MOUSE) { - jump_to_mouse(MOUSE_SETPOS, NULL, 0); - } else if (vim_strchr("\r\n ", c) == NULL && c != Ctrl_C) { - // Put the character back in the typeahead buffer. Don't use the - // stuff buffer, because lmaps wouldn't work. - ins_char_typebuf(vgetc_char, vgetc_mod_mask, true); - do_redraw = true; // need a redraw even though there is - // typeahead + } else { + c = CAR; + // Wait to allow the user to verify the output. + do_sleep(msg_wait, true); } } redir_off = false; @@ -2016,12 +2132,12 @@ void msg_outtrans_long(const char *longstr, int hl_id) int len = (int)strlen(longstr); int slen = len; int room = Columns - msg_col; - if (len > room && room >= 20) { + if (!ui_has(kUIMessages) && len > room && room >= 20) { slen = (room - 3) / 2; msg_outtrans_len(longstr, slen, hl_id, false); msg_puts_hl("...", HLF_8, false); } - msg_outtrans_len(longstr + len - slen, slen, hl_id, len); + msg_outtrans_len(longstr + len - slen, slen, hl_id, false); } /// Basic function for writing a message with highlight id. @@ -2042,8 +2158,8 @@ void msg_puts_len(const char *const str, const ptrdiff_t len, int hl_id, bool hi // If redirection is on, also write to the redirection file. redir_write(str, len); - // Don't print anything when using ":silent cmd". - if (msg_silent != 0) { + // Don't print anything when using ":silent cmd" or empty message. + if (msg_silent != 0 || *str == NUL) { return; } @@ -2091,27 +2207,6 @@ void msg_puts_len(const char *const str, const ptrdiff_t len, int hl_id, bool hi need_fileinfo = false; } -/// Print a formatted message -/// -/// Message printed is limited by #IOSIZE. Must not be used from inside -/// msg_puts_hl_id(). -/// -/// @param[in] hl_id Highlight id. -/// @param[in] fmt Format string. -void msg_printf_hl(const int hl_id, const char *const fmt, ...) - FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_PRINTF(2, 3) -{ - static char msgbuf[IOSIZE]; - - va_list ap; - va_start(ap, fmt); - const size_t len = (size_t)vim_vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); - va_end(ap); - - msg_scroll = true; - msg_puts_len(msgbuf, (ptrdiff_t)len, hl_id, true); -} - static void msg_ext_emit_chunk(void) { if (msg_ext_chunks == NULL) { @@ -2150,7 +2245,13 @@ static void msg_puts_display(const char *str, int maxlen, int hl_id, int recurse // Concat pieces with the same highlight size_t len = maxlen < 0 ? strlen(str) : strnlen(str, (size_t)maxlen); ga_concat_len(&msg_ext_last_chunk, str, len); - msg_ext_cur_len += len; + + // Find last newline in the message and calculate the current message column + const char *lastline = strrchr(str, '\n'); + maxlen -= (int)(lastline ? (lastline - str) : 0); + const char *p = lastline ? lastline + 1 : str; + int col = (int)(maxlen < 0 ? mb_string2cells(p) : mb_string2cells_len(p, (size_t)(maxlen))); + msg_col = (lastline ? 0 : msg_col) + col; return; } @@ -2209,7 +2310,7 @@ static void msg_puts_display(const char *str, int maxlen, int hl_id, int recurse if (p_more && lines_left == 0 && State != MODE_HITRETURN && !msg_no_more && !exmode_active) { if (do_more_prompt(NUL)) { - s = confirm_msg_tail; + s = confirm_buttons; } if (quit_more) { return; @@ -2273,7 +2374,7 @@ static void msg_puts_display(const char *str, int maxlen, int hl_id, int recurse } } while (msg_col & 7); } else if (c == BELL) { // beep (from ":sh") - vim_beep(BO_SH); + vim_beep(kOptBoFlagShell); } } } @@ -2328,7 +2429,7 @@ int msg_scrollsize(void) bool msg_do_throttle(void) { - return msg_use_grid() && !(rdb_flags & RDB_NOTHROTTLE); + return msg_use_grid() && !(rdb_flags & kOptRdbFlagNothrottle); } /// Scroll the screen up one line for displaying the next message line. @@ -2593,7 +2694,7 @@ void show_sb_text(void) // weird, typing a command without output results in one line. msgchunk_T *mp = msg_sb_start(last_msgchunk); if (mp == NULL || mp->sb_prev == NULL) { - vim_beep(BO_MESS); + vim_beep(kOptBoFlagMess); } else { do_more_prompt('G'); wait_return(false); @@ -2641,7 +2742,7 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp) } /// @return true when messages should be printed to stdout/stderr: -/// - "batch mode" ("silent mode", -es/-Es) +/// - "batch mode" ("silent mode", -es/-Es/-l) /// - no UI and not embedded int msg_use_printf(void) { @@ -2701,7 +2802,7 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen) /// When at hit-enter prompt "typed_char" is the already typed character, /// otherwise it's NUL. /// -/// @return true when jumping ahead to "confirm_msg_tail". +/// @return true when jumping ahead to "confirm_buttons". static bool do_more_prompt(int typed_char) { static bool entered = false; @@ -3052,7 +3153,7 @@ static Array *msg_ext_init_chunks(void) { Array *tofree = msg_ext_chunks; msg_ext_chunks = xcalloc(1, sizeof(*msg_ext_chunks)); - msg_ext_cur_len = 0; + msg_col = 0; return tofree; } @@ -3066,13 +3167,14 @@ void msg_ext_ui_flush(void) msg_ext_emit_chunk(); if (msg_ext_chunks->size > 0) { Array *tofree = msg_ext_init_chunks(); - ui_call_msg_show(cstr_as_string(msg_ext_kind), *tofree, msg_ext_overwrite); + ui_call_msg_show(cstr_as_string(msg_ext_kind), *tofree, msg_ext_overwrite, msg_ext_history); api_free_array(*tofree); xfree(tofree); if (!msg_ext_overwrite) { msg_ext_visible++; } msg_ext_overwrite = false; + msg_ext_history = false; msg_ext_kind = NULL; } } @@ -3081,7 +3183,11 @@ void msg_ext_flush_showmode(void) { // Showmode messages doesn't interrupt normal message flow, so we use // separate event. Still reuse the same chunking logic, for simplicity. - if (ui_has(kUIMessages)) { + // This is called unconditionally; check if we are emitting, or have + // emitted non-empty "content". + static bool clear = false; + if (ui_has(kUIMessages) && (msg_ext_last_attr != -1 || clear)) { + clear = msg_ext_last_attr != -1; msg_ext_emit_chunk(); Array *tofree = msg_ext_init_chunks(); ui_call_msg_showmode(*tofree); @@ -3228,6 +3334,10 @@ int redirecting(void) || redir_reg || redir_vname || capture_ga != NULL; } +// Save and restore message kind when emitting a verbose message. +static const char *pre_verbose_kind = NULL; +static const char *verbose_kind = "verbose"; + /// Before giving verbose message. /// Must always be called paired with verbose_leave()! void verbose_enter(void) @@ -3235,6 +3345,10 @@ void verbose_enter(void) if (*p_vfile != NUL) { msg_silent++; } + if (msg_ext_kind != verbose_kind) { + pre_verbose_kind = msg_ext_kind; + msg_ext_set_kind("verbose"); + } } /// After giving verbose message. @@ -3246,14 +3360,17 @@ void verbose_leave(void) msg_silent = 0; } } + if (pre_verbose_kind != NULL) { + msg_ext_set_kind(pre_verbose_kind); + pre_verbose_kind = NULL; + } } /// Like verbose_enter() and set msg_scroll when displaying the message. void verbose_enter_scroll(void) { - if (*p_vfile != NUL) { - msg_silent++; - } else { + verbose_enter(); + if (*p_vfile == NUL) { // always scroll up, don't overwrite msg_scroll = true; } @@ -3262,11 +3379,8 @@ void verbose_enter_scroll(void) /// Like verbose_leave() and set cmdline_row when displaying the message. void verbose_leave_scroll(void) { - if (*p_vfile != NUL) { - if (--msg_silent < 0) { - msg_silent = 0; - } - } else { + verbose_leave(); + if (*p_vfile == NUL) { cmdline_row = msg_row; } } @@ -3360,14 +3474,6 @@ void msg_advance(int col) msg_col = col; // for redirection, may fill it up later return; } - if (ui_has(kUIMessages)) { - // TODO(bfredl): use byte count as a basic proxy. - // later on we might add proper support for formatted messages. - while (msg_ext_cur_len < (size_t)col) { - msg_putchar(' '); - } - return; - } col = MIN(col, Columns - 1); // not enough room while (msg_col < col) { msg_putchar(' '); @@ -3424,10 +3530,10 @@ int do_dialog(int type, const char *title, const char *message, const char *butt } // Get a typed character directly from the user. - int c = get_keystroke(NULL); + int c = prompt_for_input(confirm_buttons, HLF_M, true, NULL); switch (c) { case CAR: // User accepts default option - case NL: + case NUL: retval = dfltbutton; break; case Ctrl_C: // User aborts/cancels @@ -3436,6 +3542,7 @@ int do_dialog(int type, const char *title, const char *message, const char *butt break; default: // Could be a hotkey? if (c < 0) { // special keys are ignored here + msg_didout = msg_didany = false; continue; } if (c == ':' && ex_cmd) { @@ -3458,6 +3565,7 @@ int do_dialog(int type, const char *title, const char *message, const char *butt break; } // No hotkey match, so keep waiting + msg_didout = msg_didany = false; continue; } break; @@ -3511,19 +3619,20 @@ static char *console_dialog_alloc(const char *message, const char *buttons, bool has_hotkey[0] = false; // Compute the size of memory to allocate. - int len = 0; + int msg_len = 0; + int button_len = 0; int idx = 0; const char *r = buttons; while (*r) { if (*r == DLG_BUTTON_SEP) { - len += 3; // '\n' -> ', '; 'x' -> '(x)' + button_len += 3; // '\n' -> ', '; 'x' -> '(x)' lenhotkey += HOTK_LEN; // each button needs a hotkey if (idx < HAS_HOTKEY_LEN - 1) { has_hotkey[++idx] = false; } } else if (*r == DLG_HOTKEY_CHAR) { r++; - len++; // '&a' -> '[a]' + button_len++; // '&a' -> '[a]' if (idx < HAS_HOTKEY_LEN - 1) { has_hotkey[idx] = true; } @@ -3533,21 +3642,22 @@ static char *console_dialog_alloc(const char *message, const char *buttons, bool MB_PTR_ADV(r); } - len += (int)(strlen(message) - + 2 // for the NL's - + strlen(buttons) - + 3); // for the ": " and NUL - lenhotkey++; // for the NUL + msg_len += (int)strlen(message) + 3; // for the NL's and NUL + button_len += (int)strlen(buttons) + 3; // for the ": " and NUL + lenhotkey++; // for the NUL // If no hotkey is specified, first char is used. if (!has_hotkey[0]) { - len += 2; // "x" -> "[x]" + button_len += 2; // "x" -> "[x]" } // Now allocate space for the strings xfree(confirm_msg); - confirm_msg = xmalloc((size_t)len); - *confirm_msg = NUL; + confirm_msg = xmalloc((size_t)msg_len); + snprintf(confirm_msg, (size_t)msg_len, "\n%s\n", message); + + xfree(confirm_buttons); + confirm_buttons = xmalloc((size_t)button_len); return xmalloc((size_t)lenhotkey); } @@ -3565,42 +3675,34 @@ static char *msg_show_console_dialog(const char *message, const char *buttons, i bool has_hotkey[HAS_HOTKEY_LEN] = { false }; char *hotk = console_dialog_alloc(message, buttons, has_hotkey); - copy_hotkeys_and_msg(message, buttons, dfltbutton, has_hotkey, hotk); + copy_confirm_hotkeys(buttons, dfltbutton, has_hotkey, hotk); display_confirm_msg(); return hotk; } -/// Copies hotkeys & dialog message into the memory allocated for it +/// Copies hotkeys into the memory allocated for it /// -/// @param message Message which will be part of the confirm_msg /// @param buttons String containing button names /// @param default_button_idx Number of default button /// @param has_hotkey An element in this array is true if corresponding button /// has a hotkey /// @param[out] hotkeys_ptr Pointer to the memory location where hotkeys will be copied -static void copy_hotkeys_and_msg(const char *message, const char *buttons, int default_button_idx, +static void copy_confirm_hotkeys(const char *buttons, int default_button_idx, const bool has_hotkey[], char *hotkeys_ptr) { - *confirm_msg = '\n'; - STRCPY(confirm_msg + 1, message); - - char *msgp = confirm_msg + 1 + strlen(message); - // Define first default hotkey. Keep the hotkey string NUL // terminated to avoid reading past the end. hotkeys_ptr[copy_char(buttons, hotkeys_ptr, true)] = NUL; - // Remember where the choices start, displaying starts here when - // "hotkeys_ptr" typed at the more prompt. - confirm_msg_tail = msgp; - *msgp++ = '\n'; - bool first_hotkey = false; // Is the first char of button a hotkey if (!has_hotkey[0]) { first_hotkey = true; // If no hotkey is specified, first char is used } + // Remember where the choices start, sent as prompt to cmdline. + char *msgp = confirm_buttons; + int idx = 0; const char *r = buttons; while (*r) { |