diff options
Diffstat (limited to 'src/nvim/message.c')
-rw-r--r-- | src/nvim/message.c | 825 |
1 files changed, 365 insertions, 460 deletions
diff --git a/src/nvim/message.c b/src/nvim/message.c index 7f29b19031..3268ff389a 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - // message.c: functions for displaying messages on the command line #include <assert.h> @@ -11,22 +8,23 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/types.h> #include "nvim/api/private/helpers.h" -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" #include "nvim/channel.h" #include "nvim/charset.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" -#include "nvim/eval/typval_defs.h" #include "nvim/event/defs.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_eval.h" #include "nvim/fileio.h" +#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/gettext.h" @@ -44,16 +42,20 @@ #include "nvim/mouse.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_vars.h" +#include "nvim/os/fs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" -#include "nvim/pos.h" +#include "nvim/os/time.h" +#include "nvim/pos_defs.h" #include "nvim/regexp.h" #include "nvim/runtime.h" -#include "nvim/screen.h" +#include "nvim/state_defs.h" #include "nvim/strings.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/ui_compositor.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" // To be able to scroll back at the "more" and "hit-enter" prompts we need to // store the displayed text and remember where screen lines start. @@ -64,7 +66,7 @@ struct msgchunk_S { char sb_eol; // true when line ends after this text int sb_msg_col; // column in which text starts int sb_attr; // text attributes - char sb_text[1]; // text to be displayed, actually longer + char sb_text[]; // text to be displayed }; // Magic chars used in confirm dialog strings @@ -142,7 +144,7 @@ static int msg_grid_pos_at_flush = 0; static void ui_ext_msg_set_pos(int row, bool scrolled) { - char buf[MAX_MCO + 1]; + char buf[MB_MAXCHAR + 1]; size_t size = (size_t)utf_char2bytes(curwin->w_p_fcs_chars.msgsep, buf); buf[size] = '\0'; ui_call_msg_set_pos(msg_grid.handle, row, scrolled, @@ -182,7 +184,7 @@ void msg_grid_validate(void) msg_grid.dirty_col = xcalloc((size_t)Rows, sizeof(*msg_grid.dirty_col)); // Tricky: allow resize while pager or ex mode is active - int pos = MAX(max_rows - msg_scrolled, 0); + int pos = (State & MODE_ASKMORE) ? 0 : MAX(max_rows - msg_scrolled, 0); msg_grid.throttled = false; // don't throttle in 'cmdheight' area msg_grid_set_pos(pos, msg_scrolled); ui_comp_put_grid(&msg_grid, pos, 0, msg_grid.rows, msg_grid.cols, @@ -213,17 +215,8 @@ void msg_grid_validate(void) } } -/// Displays the string 's' on the status line -/// When terminal not initialized (yet) os_errmsg(..) is used. -/// -/// @return true if wait_return() not called -int msg(char *s) -{ - return msg_attr_keep(s, 0, false, false); -} - /// Like msg() but keep it silent when 'verbosefile' is set. -int verb_msg(char *s) +int verb_msg(const char *s) { verbose_enter(); int n = msg_attr_keep(s, 0, false, false); @@ -232,14 +225,18 @@ int verb_msg(char *s) return n; } -int msg_attr(const char *s, const int attr) +/// Displays the string 's' on the status line +/// When terminal not initialized (yet) os_errmsg(..) is used. +/// +/// @return true if wait_return() not called +int msg(const char *s, const int attr) FUNC_ATTR_NONNULL_ARG(1) { return msg_attr_keep(s, attr, false, false); } -/// Similar to msg_outtrans_attr, but support newlines and tabs. -void msg_multiline_attr(const char *s, int attr, bool check_int, bool *need_clear) +/// Similar to msg_outtrans, but support newlines and tabs. +void msg_multiline(const char *s, int attr, bool check_int, bool *need_clear) FUNC_ATTR_NONNULL_ALL { const char *next_spec = s; @@ -252,7 +249,7 @@ void msg_multiline_attr(const char *s, int attr, bool check_int, bool *need_clea if (next_spec != NULL) { // Printing all char that are before the char found by strpbrk - msg_outtrans_len_attr(s, (int)(next_spec - s), attr); + msg_outtrans_len(s, (int)(next_spec - s), attr); if (*next_spec != TAB && *need_clear) { msg_clr_eos(); @@ -266,7 +263,7 @@ void msg_multiline_attr(const char *s, int attr, bool check_int, bool *need_clea // Print the rest of the message. We know there is no special // character because strpbrk returned NULL if (*s != NUL) { - msg_outtrans_attr(s, attr); + msg_outtrans(s, attr); } } @@ -279,8 +276,7 @@ void msg_multiattr(HlMessage hl_msg, const char *kind, bool history) msg_ext_set_kind(kind); for (uint32_t i = 0; i < kv_size(hl_msg); i++) { HlMessageChunk chunk = kv_A(hl_msg, i); - msg_multiline_attr((const char *)chunk.text.data, chunk.attr, - true, &need_clear); + msg_multiline(chunk.text.data, chunk.attr, true, &need_clear); } if (history && kv_size(hl_msg)) { add_msg_hist_multiattr(NULL, 0, 0, true, hl_msg); @@ -294,7 +290,6 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline) FUNC_ATTR_NONNULL_ALL { static int entered = 0; - int retval; char *buf = NULL; if (keep && multiline) { @@ -306,7 +301,7 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline) // Skip messages not match ":filter pattern". // Don't filter when there is an error. - if (!emsg_on_display && message_filtered((char *)s)) { + if (!emsg_on_display && message_filtered(s)) { return true; } @@ -334,24 +329,24 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline) // Truncate the message if needed. msg_start(); - buf = msg_strtrunc((char *)s, false); + buf = msg_strtrunc(s, false); if (buf != NULL) { s = buf; } bool need_clear = true; if (multiline) { - msg_multiline_attr(s, attr, false, &need_clear); + msg_multiline(s, attr, false, &need_clear); } else { - msg_outtrans_attr(s, attr); + msg_outtrans(s, attr); } if (need_clear) { msg_clr_eos(); } - retval = msg_end(); + int retval = msg_end(); - if (keep && retval && vim_strsize((char *)s) < (Rows - cmdline_row - 1) * Columns + sc_col) { - set_keep_msg((char *)s, 0); + if (keep && retval && vim_strsize(s) < (Rows - cmdline_row - 1) * Columns + sc_col) { + set_keep_msg(s, 0); } need_fileinfo = false; @@ -366,17 +361,16 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline) /// @return an allocated string or NULL when no truncating is done. /// /// @param force always truncate -char *msg_strtrunc(char *s, int force) +char *msg_strtrunc(const char *s, int force) { char *buf = NULL; - int len; - int room; // May truncate message to avoid a hit-return prompt if ((!msg_scroll && !need_wait_return && shortmess(SHM_TRUNCALL) && !exmode_active && msg_silent == 0 && !ui_has(kUIMessages)) || force) { - len = vim_strsize(s); + int room; + int len = vim_strsize(s); if (msg_scrolled != 0) { // Use all the columns. room = (Rows - msg_row) * Columns - 1; @@ -397,10 +391,9 @@ char *msg_strtrunc(char *s, int force) /// Truncate a string "s" to "buf" with cell width "room". /// "s" and "buf" may be equal. -void trunc_string(char *s, char *buf, int room_in, int buflen) +void trunc_string(const char *s, char *buf, int room_in, int buflen) { int room = room_in - 3; // "..." takes 3 chars - int half; int len = 0; int e; int i; @@ -416,7 +409,7 @@ void trunc_string(char *s, char *buf, int room_in, int buflen) if (room_in < 3) { room = 0; } - half = room / 2; + int half = room / 2; // First part: Start of the string. for (e = 0; len < half && e < buflen; e++) { @@ -441,7 +434,7 @@ void trunc_string(char *s, char *buf, int room_in, int buflen) // Last part: End of the string. half = i = (int)strlen(s); - for (;;) { + while (true) { do { half = half - utf_head_off(s, s + half - 1) - 1; } while (half > 0 && utf_iscomposing(utf_ptr2char(s + half))); @@ -478,26 +471,19 @@ void trunc_string(char *s, char *buf, int room_in, int buflen) buf[e + 3 + len - 1] = NUL; } else { // can't fit in the "...", just truncate it - buf[e - 1] = NUL; + buf[buflen - 1] = NUL; } } -// Note: Caller of smsg() and smsg_attr() must check the resulting string is -// shorter than IOSIZE!!! - -int smsg(const char *s, ...) - FUNC_ATTR_PRINTF(1, 2) -{ - va_list arglist; - - va_start(arglist, s); - vim_vsnprintf(IObuff, IOSIZE, s, arglist); - va_end(arglist); - - return msg(IObuff); -} - -int smsg_attr(int attr, const char *s, ...) +/// Shows a printf-style message with attributes. +/// +/// Note: Caller must check the resulting string is shorter than IOSIZE!!! +/// +/// @see semsg +/// @see swmsg +/// +/// @param s printf-style format message +int smsg(int attr, const char *s, ...) FUNC_ATTR_PRINTF(2, 3) { va_list arglist; @@ -505,7 +491,7 @@ int smsg_attr(int attr, const char *s, ...) va_start(arglist, s); vim_vsnprintf(IObuff, IOSIZE, s, arglist); va_end(arglist); - return msg_attr((const char *)IObuff, attr); + return msg(IObuff, attr); } int smsg_attr_keep(int attr, const char *s, ...) @@ -516,7 +502,7 @@ int smsg_attr_keep(int attr, const char *s, ...) va_start(arglist, s); vim_vsnprintf(IObuff, IOSIZE, s, arglist); va_end(arglist); - return msg_attr_keep((const char *)IObuff, attr, true, false); + return msg_attr_keep(IObuff, attr, true, false); } // Remember the last sourcing name/lnum used in an error message, so that it @@ -581,10 +567,10 @@ static char *get_emsg_lnum(void) if (SOURCING_NAME != NULL && (other_sourcing_name() || SOURCING_LNUM != last_sourcing_lnum) && SOURCING_LNUM != 0) { - const char *const p = _("line %4ld:"); + const char *const p = _("line %4" PRIdLINENR ":"); const size_t buf_len = 20 + strlen(p); char *const buf = xmalloc(buf_len); - snprintf(buf, buf_len, p, (long)SOURCING_LNUM); + snprintf(buf, buf_len, p, SOURCING_LNUM); return buf; } return NULL; @@ -607,12 +593,12 @@ void msg_source(int attr) char *p = get_emsg_source(); if (p != NULL) { msg_scroll = true; // this will take more than one line - msg_attr(p, attr); + msg(p, attr); xfree(p); } p = get_emsg_lnum(); if (p != NULL) { - msg_attr(p, HL_ATTR(HLF_N)); + msg(p, HL_ATTR(HLF_N)); xfree(p); last_sourcing_lnum = SOURCING_LNUM; // only once for each line } @@ -643,9 +629,8 @@ int emsg_not_now(void) return false; } -static bool emsg_multiline(const char *s, bool multiline) +bool emsg_multiline(const char *s, bool multiline) { - int attr; bool ignore = false; // Skip this if not giving error messages at the moment. @@ -666,7 +651,7 @@ static bool emsg_multiline(const char *s, bool multiline) // be found, the message will be displayed later on.) "ignore" is set // when the message should be ignored completely (used for the // interrupt message). - if (cause_errthrow(s, severe, &ignore)) { + if (cause_errthrow(s, multiline, severe, &ignore)) { if (!ignore) { did_emsg++; } @@ -707,8 +692,8 @@ static bool emsg_multiline(const char *s, bool multiline) // Log (silent) errors as debug messages. if (SOURCING_NAME != NULL && SOURCING_LNUM != 0) { - DLOG("(:silent) %s (%s (line %ld))", - s, SOURCING_NAME, (long)SOURCING_LNUM); + DLOG("(:silent) %s (%s (line %" PRIdLINENR "))", + s, SOURCING_NAME, SOURCING_LNUM); } else { DLOG("(:silent) %s", s); } @@ -718,7 +703,7 @@ static bool emsg_multiline(const char *s, bool multiline) // Log editor errors as INFO. if (SOURCING_NAME != NULL && SOURCING_LNUM != 0) { - ILOG("%s (%s (line %ld))", s, SOURCING_NAME, (long)SOURCING_LNUM); + ILOG("%s (%s (line %" PRIdLINENR "))", s, SOURCING_NAME, SOURCING_LNUM); } else { ILOG("%s", s); } @@ -742,7 +727,7 @@ static bool emsg_multiline(const char *s, bool multiline) } emsg_on_display = true; // remember there is an error message - attr = HL_ATTR(HLF_E); // set highlight mode for error messages + int attr = HL_ATTR(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 @@ -774,10 +759,12 @@ bool emsg(const char *s) void emsg_invreg(int name) { - semsg(_("E354: Invalid register name: '%s'"), transchar(name)); + semsg(_("E354: Invalid register name: '%s'"), transchar_buf(NULL, name)); } /// Print an error message with unknown number of arguments +/// +/// @return whether the message was displayed bool semsg(const char *const fmt, ...) FUNC_ATTR_PRINTF(1, 2) { @@ -864,7 +851,7 @@ void siemsg(const char *s, ...) } /// Give an "Internal error" message. -void internal_error(char *where) +void internal_error(const char *where) { siemsg(_(e_intern2), where); } @@ -888,22 +875,38 @@ void msg_schedule_semsg(const char *const fmt, ...) loop_schedule_deferred(&main_loop, event_create(msg_semsg_event, 1, s)); } +static void msg_semsg_multiline_event(void **argv) +{ + char *s = argv[0]; + (void)emsg_multiline(s, true); + xfree(s); +} + +void msg_schedule_semsg_multiline(const char *const fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vim_vsnprintf(IObuff, IOSIZE, fmt, ap); + va_end(ap); + + char *s = xstrdup(IObuff); + loop_schedule_deferred(&main_loop, event_create(msg_semsg_multiline_event, 1, s)); +} + /// Like msg(), but truncate to a single line if p_shm contains 't', or when /// "force" is true. This truncates in another way as for normal messages. /// Careful: The string may be changed by msg_may_trunc()! /// /// @return a pointer to the printed message, if wait_return() not called. -char *msg_trunc_attr(char *s, bool force, int attr) +char *msg_trunc(char *s, bool force, int attr) { - int n; - // Add message to history before truncating. add_msg_hist(s, -1, attr, false); char *ts = msg_may_trunc(force, s); msg_hist_off = true; - n = msg_attr(ts, attr); + int n = msg(ts, attr); msg_hist_off = false; if (n) { @@ -1009,12 +1012,10 @@ static void add_msg_hist_multiattr(const char *s, int len, int attr, bool multil /// @return FAIL if there are no messages. int delete_first_msg(void) { - struct msg_hist *p; - if (msg_hist_len <= 0) { return FAIL; } - p = first_msg_hist; + struct msg_hist *p = first_msg_hist; first_msg_hist = p->next; if (first_msg_hist == NULL) { // history is becoming empty assert(msg_hist_len == 1); @@ -1028,13 +1029,9 @@ int delete_first_msg(void) } /// :messages command implementation -void ex_messages(void *const eap_p) +void ex_messages(exarg_T *eap) FUNC_ATTR_NONNULL_ALL { - const exarg_T *const eap = (const exarg_T *)eap_p; - struct msg_hist *p; - int c = 0; - if (strcmp(eap->arg, "clear") == 0) { int keep = eap->addr_count == 0 ? 0 : eap->line2; @@ -1049,9 +1046,10 @@ void ex_messages(void *const eap_p) return; } - p = first_msg_hist; + struct msg_hist *p = first_msg_hist; if (eap->addr_count != 0) { + int c = 0; // Count total messages for (; p != NULL && !got_int; p = p->next) { c++; @@ -1072,7 +1070,7 @@ void ex_messages(void *const eap_p) for (; p != NULL; p = p->next) { if (kv_size(p->multiattr) || (p->msg && p->msg[0])) { Array entry = ARRAY_DICT_INIT; - ADD(entry, STRING_OBJ(cstr_to_string(p->kind))); + ADD(entry, CSTR_TO_OBJ(p->kind)); Array content = ARRAY_DICT_INIT; if (kv_size(p->multiattr)) { for (uint32_t i = 0; i < kv_size(p->multiattr); i++) { @@ -1085,7 +1083,7 @@ void ex_messages(void *const eap_p) } else if (p->msg && p->msg[0]) { Array content_entry = ARRAY_DICT_INIT; ADD(content_entry, INTEGER_OBJ(p->attr)); - ADD(content_entry, STRING_OBJ(cstr_to_string((char *)(p->msg)))); + ADD(content_entry, CSTR_TO_OBJ(p->msg)); ADD(content, ARRAY_OBJ(content_entry)); } ADD(entry, ARRAY_OBJ(content)); @@ -1130,8 +1128,6 @@ void msg_end_prompt(void) void wait_return(int redraw) { int c; - int oldState; - int tmpState; int had_got_int; FILE *save_scriptout; @@ -1165,7 +1161,7 @@ void wait_return(int redraw) } redir_off = true; // don't redirect this message - oldState = State; + int oldState = State; if (quit_more) { c = CAR; // just pretend CR was hit quit_more = false; @@ -1226,9 +1222,7 @@ void wait_return(int redraw) } else { msg_didout = false; c = K_IGNORE; - msg_col = - cmdmsg_rl ? Columns - 1 : - 0; + msg_col = 0; } if (quit_more) { c = CAR; // just pretend CR was hit @@ -1253,6 +1247,7 @@ void wait_return(int redraw) || 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) { @@ -1281,7 +1276,7 @@ void wait_return(int redraw) // If the screen size changed screen_resize() will redraw the screen. // Otherwise the screen is only redrawn if 'redraw' is set and no ':' // typed. - tmpState = State; + int tmpState = State; State = oldState; // restore State before screen_resize() setmouse(); msg_check(); @@ -1329,7 +1324,7 @@ static void hit_return_msg(void) } /// Set "keep_msg" to "s". Free the old value and check for NULL pointer. -void set_keep_msg(char *s, int attr) +void set_keep_msg(const char *s, int attr) { xfree(keep_msg); if (s != NULL && msg_silent == 0) { @@ -1341,9 +1336,17 @@ void set_keep_msg(char *s, int attr) keep_msg_attr = attr; } -void msgmore(long n) +/// Return true if printing messages should currently be done. +bool messaging(void) { - long pn; + // TODO(bfredl): with general support for "async" messages with p_ch, + // this should be re-enabled. + return !(p_lz && char_avail() && !KeyTyped) && (p_ch > 0 || ui_has(kUIMessages)); +} + +void msgmore(int n) +{ + int pn; if (global_busy // no messages now, wait until global is finished || !messaging()) { // 'lazyredraw' set, don't do messages now @@ -1366,17 +1369,17 @@ void msgmore(long n) if (pn > p_report) { if (n > 0) { vim_snprintf(msg_buf, MSG_BUF_LEN, - NGETTEXT("%ld more line", "%ld more lines", pn), + NGETTEXT("%d more line", "%d more lines", pn), pn); } else { vim_snprintf(msg_buf, MSG_BUF_LEN, - NGETTEXT("%ld line less", "%ld fewer lines", pn), + NGETTEXT("%d line less", "%d fewer lines", pn), pn); } if (got_int) { xstrlcat(msg_buf, _(" (Interrupted)"), MSG_BUF_LEN); } - if (msg(msg_buf)) { + if (msg(msg_buf, 0)) { set_keep_msg(msg_buf, 0); keep_msg_more = true; } @@ -1397,7 +1400,11 @@ void msg_ext_set_kind(const char *msg_kind) /// Prepare for outputting characters in the command line. void msg_start(void) { - int did_return = false; + bool did_return = false; + + if (msg_row < cmdline_row) { + msg_row = cmdline_row; + } if (!msg_silent) { XFREE_CLEAR(keep_msg); // don't display old message now @@ -1421,7 +1428,7 @@ void msg_start(void) if (!msg_scroll && full_screen) { // overwrite last message msg_row = cmdline_row; - msg_col = cmdmsg_rl ? Columns - 1 : 0; + msg_col = 0; } else if (msg_didout || (p_ch == 0 && !ui_has(kUIMessages))) { // start message on next line msg_putchar('\n'); did_return = true; @@ -1462,7 +1469,7 @@ void msg_putchar(int c) void msg_putchar_attr(int c, int attr) { - char buf[MB_MAXBYTES + 1]; + char buf[MB_MAXCHAR + 1]; if (IS_SPECIAL(c)) { buf[0] = (char)K_SPECIAL; @@ -1470,33 +1477,33 @@ void msg_putchar_attr(int c, int attr) buf[2] = (char)K_THIRD(c); buf[3] = NUL; } else { - buf[utf_char2bytes(c, (char *)buf)] = NUL; + buf[utf_char2bytes(c, buf)] = NUL; } - msg_puts_attr((const char *)buf, attr); + msg_puts_attr(buf, attr); } -void msg_outnum(long n) +void msg_outnum(int n) { char buf[20]; - snprintf(buf, sizeof(buf), "%ld", n); + snprintf(buf, sizeof(buf), "%d", n); msg_puts(buf); } -void msg_home_replace(char *fname) +void msg_home_replace(const char *fname) { msg_home_replace_attr(fname, 0); } -void msg_home_replace_hl(char *fname) +void msg_home_replace_hl(const char *fname) { msg_home_replace_attr(fname, HL_ATTR(HLF_D)); } -static void msg_home_replace_attr(char *fname, int attr) +static void msg_home_replace_attr(const char *fname, int attr) { char *name = home_replace_save(NULL, fname); - msg_outtrans_attr(name, attr); + msg_outtrans(name, attr); xfree(name); } @@ -1505,44 +1512,33 @@ static void msg_home_replace_attr(char *fname, int attr) /// Use attributes 'attr'. /// /// @return the number of characters it takes on the screen. -int msg_outtrans(char *str) -{ - return msg_outtrans_attr(str, 0); -} - -int msg_outtrans_attr(const char *str, int attr) -{ - return msg_outtrans_len_attr(str, (int)strlen(str), attr); -} - -int msg_outtrans_len(const char *str, int len) +int msg_outtrans(const char *str, int attr) { - return msg_outtrans_len_attr(str, len, 0); + return msg_outtrans_len(str, (int)strlen(str), attr); } /// Output one character at "p". /// Handles multi-byte characters. /// /// @return pointer to the next character. -char *msg_outtrans_one(char *p, int attr) +const char *msg_outtrans_one(const char *p, int attr) { int l; if ((l = utfc_ptr2len(p)) > 1) { - msg_outtrans_len_attr(p, l, attr); + msg_outtrans_len(p, l, attr); return p + l; } - msg_puts_attr((const char *)transchar_byte((uint8_t)(*p)), attr); + msg_puts_attr(transchar_byte_buf(NULL, (uint8_t)(*p)), attr); return p + 1; } -int msg_outtrans_len_attr(const char *msgstr, int len, int attr) +int msg_outtrans_len(const char *msgstr, int len, int attr) { int retval = 0; const char *str = msgstr; const char *plain_start = msgstr; char *s; - int mb_l; int c; int save_got_int = got_int; @@ -1555,17 +1551,18 @@ int msg_outtrans_len_attr(const char *msgstr, int len, int attr) attr &= ~MSG_HIST; } - // If the string starts with a composing character first draw a space on - // which the composing char can be drawn. - if (utf_iscomposing(utf_ptr2char((char *)msgstr))) { - msg_puts_attr(" ", attr); + // When drawing over the command line no need to clear it later or remove + // the mode message. + if (msg_row >= cmdline_row && msg_col == 0) { + clear_cmdline = false; + mode_displayed = false; } // Go over the string. Special characters are translated and printed. // Normal characters are printed several at a time. while (--len >= 0 && !got_int) { // Don't include composing chars after the end. - mb_l = utfc_ptr2len_len(str, len + 1); + int mb_l = utfc_ptr2len_len(str, len + 1); if (mb_l > 1) { c = utf_ptr2char(str); if (vim_isprintc(c)) { @@ -1575,25 +1572,24 @@ int msg_outtrans_len_attr(const char *msgstr, int len, int attr) // Unprintable multi-byte char: print the printable chars so // far and the translation of the unprintable char. if (str > plain_start) { - msg_puts_attr_len(plain_start, str - plain_start, attr); + msg_puts_len(plain_start, str - plain_start, attr); } plain_start = str + mb_l; - msg_puts_attr((const char *)transchar(c), - (attr == 0 ? HL_ATTR(HLF_8) : attr)); + msg_puts_attr(transchar_buf(NULL, c), attr == 0 ? HL_ATTR(HLF_8) : attr); retval += char2cells(c); } len -= mb_l - 1; str += mb_l; } else { - s = (char *)transchar_byte((uint8_t)(*str)); + s = transchar_byte_buf(NULL, (uint8_t)(*str)); if (s[1] != NUL) { // Unprintable char: print the printable chars so far and the // translation of the unprintable char. if (str > plain_start) { - msg_puts_attr_len(plain_start, str - plain_start, attr); + msg_puts_len(plain_start, str - plain_start, attr); } plain_start = str + 1; - msg_puts_attr((const char *)s, attr == 0 ? HL_ATTR(HLF_8) : attr); + msg_puts_attr(s, attr == 0 ? HL_ATTR(HLF_8) : attr); retval += (int)strlen(s); } else { retval++; @@ -1604,7 +1600,7 @@ int msg_outtrans_len_attr(const char *msgstr, int len, int attr) if (str > plain_start && !got_int) { // Print the printable chars at the end. - msg_puts_attr_len(plain_start, str - plain_start, attr); + msg_puts_len(plain_start, str - plain_start, attr); } got_int |= save_got_int; @@ -1612,11 +1608,11 @@ int msg_outtrans_len_attr(const char *msgstr, int len, int attr) return retval; } -void msg_make(char *arg) +void msg_make(const char *arg) { int i; - static char *str = "eeffoc"; - static char *rs = "Plon#dqg#vxjduB"; + static const char *str = "eeffoc"; + static const char *rs = "Plon#dqg#vxjduB"; arg = skipwhite(arg); for (i = 5; *arg && i >= 0; i--) { @@ -1667,9 +1663,9 @@ int msg_outtrans_special(const char *strstart, bool from, int maxlen) } if (text[0] != NUL && text[1] == NUL) { // single-byte character or illegal byte - text = (char *)transchar_byte((uint8_t)text[0]); + text = transchar_byte_buf(NULL, (uint8_t)text[0]); } - const int len = vim_strsize((char *)text); + const int len = vim_strsize(text); if (maxlen > 0 && retval + len >= maxlen) { break; } @@ -1773,7 +1769,7 @@ const char *str2special(const char **const sp, const bool replace_spaces, const || c < ' ' || (replace_spaces && c == ' ') || (replace_lt && c == '<')) { - return (const char *)get_special_key_name(c, modifiers); + return get_special_key_name(c, modifiers); } buf[0] = (char)c; buf[1] = NUL; @@ -1802,20 +1798,20 @@ void str2specialbuf(const char *sp, char *buf, size_t len) } /// print line for :print or :list command -void msg_prt_line(char *s, int list) +void msg_prt_line(const char *s, int list) { int c; int col = 0; int n_extra = 0; int c_extra = 0; int c_final = 0; - char *p_extra = NULL; // init to make SASC shut up + const char *p_extra = NULL; // init to make SASC shut up int n; int attr = 0; - char *lead = NULL; + const char *lead = NULL; bool in_multispace = false; int multispace_pos = 0; - char *trail = NULL; + const char *trail = NULL; int l; if (curwin->w_p_list) { @@ -1878,10 +1874,13 @@ void msg_prt_line(char *s, int list) continue; } else { attr = 0; - c = (unsigned char)(*s++); - in_multispace = c == ' ' && ((col > 0 && s[-2] == ' ') || *s == ' '); - if (!in_multispace) { - multispace_pos = 0; + c = (uint8_t)(*s++); + if (list) { + in_multispace = c == ' ' && (*s == ' ' + || (col > 0 && s[-2] == ' ')); + if (!in_multispace) { + multispace_pos = 0; + } } if (c == TAB && (!list || curwin->w_p_lcs_chars.tab1)) { // tab amount depends on current column @@ -1913,7 +1912,7 @@ void msg_prt_line(char *s, int list) s--; } else if (c != NUL && (n = byte2cells(c)) > 1) { n_extra = n - 1; - p_extra = (char *)transchar_byte(c); + p_extra = transchar_byte_buf(NULL, c); c_extra = NUL; c_final = NUL; c = (unsigned char)(*p_extra++); @@ -1921,7 +1920,7 @@ void msg_prt_line(char *s, int list) // the same in plain text. attr = HL_ATTR(HLF_0); } else if (c == ' ') { - if (list && lead != NULL && s <= lead && in_multispace + if (lead != NULL && s <= lead && in_multispace && curwin->w_p_lcs_chars.leadmultispace != NULL) { c = curwin->w_p_lcs_chars.leadmultispace[multispace_pos++]; if (curwin->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) { @@ -1934,7 +1933,7 @@ void msg_prt_line(char *s, int list) } else if (trail != NULL && s > trail) { c = curwin->w_p_lcs_chars.trail; attr = HL_ATTR(HLF_0); - } else if (list && in_multispace + } else if (in_multispace && curwin->w_p_lcs_chars.multispace != NULL) { c = curwin->w_p_lcs_chars.multispace[multispace_pos++]; if (curwin->w_p_lcs_chars.multispace[multispace_pos] == NUL) { @@ -1958,40 +1957,6 @@ void msg_prt_line(char *s, int list) msg_clr_eos(); } -/// Use grid_puts() to output one multi-byte character. -/// -/// @return the pointer "s" advanced to the next character. -static char *screen_puts_mbyte(char *s, int l, int attr) -{ - int cw; - attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr); - - msg_didout = true; // remember that line is not empty - cw = utf_ptr2cells(s); - if (cw > 1 - && (cmdmsg_rl ? msg_col <= 1 : msg_col == Columns - 1)) { - // Doesn't fit, print a highlighted '>' to fill it up. - msg_screen_putchar('>', HL_ATTR(HLF_AT)); - return s; - } - - grid_puts_len(&msg_grid_adj, s, l, msg_row, msg_col, attr); - if (cmdmsg_rl) { - msg_col -= cw; - if (msg_col == 0) { - msg_col = Columns; - msg_row++; - } - } else { - msg_col += cw; - if (msg_col >= Columns) { - msg_col = 0; - msg_row++; - } - } - return s + l; -} - /// Output a string to the screen at position msg_row, msg_col. /// Update msg_row and msg_col for the next message. void msg_puts(const char *s) @@ -2007,29 +1972,23 @@ void msg_puts_title(const char *s) /// Show a message in such a way that it always fits in the line. Cut out a /// part in the middle and replace it with "..." when necessary. /// Does not handle multi-byte characters! -void msg_outtrans_long_attr(char *longstr, int attr) -{ - msg_outtrans_long_len_attr(longstr, (int)strlen(longstr), attr); -} - -void msg_outtrans_long_len_attr(char *longstr, int len, int attr) +void msg_outtrans_long(const char *longstr, int attr) { + int len = (int)strlen(longstr); int slen = len; - int room; - - room = Columns - msg_col; + int room = Columns - msg_col; if (len > room && room >= 20) { slen = (room - 3) / 2; - msg_outtrans_len_attr(longstr, slen, attr); + msg_outtrans_len(longstr, slen, attr); msg_puts_attr("...", HL_ATTR(HLF_8)); } - msg_outtrans_len_attr(longstr + len - slen, slen, attr); + msg_outtrans_len(longstr + len - slen, slen, attr); } /// Basic function for writing a message with highlight attributes. void msg_puts_attr(const char *const s, const int attr) { - msg_puts_attr_len(s, -1, attr); + msg_puts_len(s, -1, attr); } /// Write a message with highlight attributes @@ -2037,7 +1996,7 @@ void msg_puts_attr(const char *const s, const int attr) /// @param[in] str NUL-terminated message string. /// @param[in] len Length of the string or -1. /// @param[in] attr Highlight attribute. -void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr) +void msg_puts_len(const char *const str, const ptrdiff_t len, int attr) FUNC_ATTR_NONNULL_ALL { assert(len < 0 || memchr(str, 0, (size_t)len) == NULL); @@ -2113,7 +2072,7 @@ void msg_printf_attr(const int attr, const char *const fmt, ...) va_end(ap); msg_scroll = true; - msg_puts_attr_len(msgbuf, (ptrdiff_t)len, attr); + msg_puts_len(msgbuf, (ptrdiff_t)len, attr); } static void msg_ext_emit_chunk(void) @@ -2130,19 +2089,13 @@ static void msg_ext_emit_chunk(void) ADD(msg_ext_chunks, ARRAY_OBJ(chunk)); } -/// The display part of msg_puts_attr_len(). +/// The display part of msg_puts_len(). /// May be called recursively to display scroll-back text. static void msg_puts_display(const char *str, int maxlen, int attr, int recurse) { const char *s = str; - const char *t_s = str; // String from "t_s" to "s" is still todo. - int t_col = 0; // Screen cells todo, 0 when "t_s" not used. - int l; - int cw; - const char *sb_str = (char *)str; + const char *sb_str = str; int sb_col = msg_col; - int wrap; - int did_last_char; did_wait_return = false; @@ -2152,197 +2105,175 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse) msg_ext_last_attr = attr; } // Concat pieces with the same highlight - size_t len = strnlen(str, (size_t)maxlen); // -V781 - ga_concat_len(&msg_ext_last_chunk, (char *)str, len); + size_t len = strnlen(str, (size_t)maxlen); + ga_concat_len(&msg_ext_last_chunk, str, len); msg_ext_cur_len += len; return; } + int print_attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr); msg_grid_validate(); cmdline_was_last_drawn = redrawing_cmdline; - while ((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL) { - // We are at the end of the screen line when: - // - When outputting a newline. - // - When outputting a character in the last column. - if (!recurse && msg_row >= Rows - 1 - && (*s == '\n' || (cmdmsg_rl - ? (msg_col <= 1 - || (*s == TAB && msg_col <= 7) - || (utf_ptr2cells(s) > 1 - && msg_col <= 2)) - : ((*s != '\r' && msg_col + t_col >= Columns - 1) - || (*s == TAB - && msg_col + t_col >= ((Columns - 1) & ~7)) - || (utf_ptr2cells(s) > 1 - && msg_col + t_col >= Columns - 2))))) { - // The screen is scrolled up when at the last row (some terminals - // scroll automatically, some don't. To avoid problems we scroll - // ourselves). - if (t_col > 0) { - // output postponed text - t_puts(&t_col, t_s, s, attr); - } + int msg_row_pending = -1; - // When no more prompt and no more room, truncate here + while (true) { + if (msg_col >= Columns) { + if (p_more && !recurse) { + // Store text for scrolling back. + store_sb_text(&sb_str, s, attr, &sb_col, true); + } if (msg_no_more && lines_left == 0) { break; } - // Scroll the screen up one line. - bool has_last_char = ((uint8_t)(*s) >= ' ' && !cmdmsg_rl); - msg_scroll_up(!has_last_char, false); + msg_col = 0; + msg_row++; + msg_didout = false; + } - msg_row = Rows - 2; - if (msg_col >= Columns) { // can happen after screen resize - msg_col = Columns - 1; - } + if (msg_row >= Rows) { + msg_row = Rows - 1; - // Display char in last column before showing more-prompt. - if (has_last_char) { - if (maxlen >= 0) { - // Avoid including composing chars after the end. - l = utfc_ptr2len_len(s, (int)((str + maxlen) - s)); - } else { - l = utfc_ptr2len(s); - } - s = screen_puts_mbyte((char *)s, l, attr); - did_last_char = true; - } else { - did_last_char = false; + // When no more prompt and no more room, truncate here + if (msg_no_more && lines_left == 0) { + break; } - // Tricky: if last cell will be written, delay the throttle until - // after the first scroll. Otherwise we would need to keep track of it. - if (has_last_char && msg_do_throttle()) { - if (!msg_grid.throttled) { - msg_grid_scroll_discount++; + if (!recurse) { + if (msg_row_pending >= 0) { + msg_line_flush(); + msg_row_pending = -1; } - msg_grid.throttled = true; - } - - if (p_more) { - // Store text for scrolling back. - store_sb_text((char **)&sb_str, (char *)s, attr, &sb_col, true); - } - inc_msg_scrolled(); - need_wait_return = true; // may need wait_return() in main() - redraw_cmdline = true; - if (cmdline_row > 0 && !exmode_active) { - cmdline_row--; - } + // Scroll the screen up one line. + msg_scroll_up(true, false); - // If screen is completely filled and 'more' is set then wait - // for a character. - if (lines_left > 0) { - lines_left--; - } - if (p_more && lines_left == 0 && State != MODE_HITRETURN - && !msg_no_more && !exmode_active) { - if (do_more_prompt(NUL)) { - s = confirm_msg_tail; + inc_msg_scrolled(); + need_wait_return = true; // may need wait_return() in main() + redraw_cmdline = true; + if (cmdline_row > 0 && !exmode_active) { + cmdline_row--; } - if (quit_more) { - return; + + // If screen is completely filled and 'more' is set then wait + // for a character. + if (lines_left > 0) { + lines_left--; } - } - // When we displayed a char in last column need to check if there - // is still more. - if (did_last_char) { - continue; + if (p_more && lines_left == 0 && State != MODE_HITRETURN + && !msg_no_more && !exmode_active) { + if (do_more_prompt(NUL)) { + s = confirm_msg_tail; + } + if (quit_more) { + return; + } + } } } - wrap = *s == '\n' - || msg_col + t_col >= Columns - || (utf_ptr2cells(s) > 1 - && msg_col + t_col >= Columns - 1) - ; - if (t_col > 0 && (wrap || *s == '\r' || *s == '\b' - || *s == '\t' || *s == BELL)) { - // Output any postponed text. - t_puts(&t_col, t_s, s, attr); + if (!((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL)) { + break; } - if (wrap && p_more && !recurse) { - // Store text for scrolling back. - store_sb_text((char **)&sb_str, (char *)s, attr, &sb_col, true); + if (msg_row != msg_row_pending && ((uint8_t)(*s) >= 0x20 || *s == TAB)) { + // TODO(bfredl): this logic is messier that it has to be. What + // messages really want is its own private linebuf_char buffer. + if (msg_row_pending >= 0) { + msg_line_flush(); + } + grid_line_start(&msg_grid_adj, msg_row); + msg_row_pending = msg_row; } - if (*s == '\n') { // go to next line - msg_didout = false; // remember that line is empty - if (cmdmsg_rl) { - msg_col = Columns - 1; - } else { - msg_col = 0; - } - if (++msg_row >= Rows) { // safety check - msg_row = Rows - 1; - } - } else if (*s == '\r') { // go to column 0 - msg_col = 0; - } else if (*s == '\b') { // go to previous char - if (msg_col) { - msg_col--; - } - } else if (*s == TAB) { // translate Tab into spaces - do { - msg_screen_putchar(' ', attr); - } while (msg_col & 7); - } else if (*s == BELL) { // beep (from ":sh") - vim_beep(BO_SH); - } else if ((uint8_t)(*s) >= 0x20) { // printable char - cw = utf_ptr2cells(s); - if (maxlen >= 0) { - // avoid including composing chars after the end - l = utfc_ptr2len_len(s, (int)((str + maxlen) - s)); + if ((uint8_t)(*s) >= 0x20) { // printable char + int cw = utf_ptr2cells(s); + // avoid including composing chars after the end + int l = (maxlen >= 0) ? utfc_ptr2len_len(s, (int)((str + maxlen) - s)) : utfc_ptr2len(s); + + if (cw > 1 && (msg_col == Columns - 1)) { + // Doesn't fit, print a highlighted '>' to fill it up. + grid_line_puts(msg_col, ">", 1, HL_ATTR(HLF_AT)); + cw = 1; } else { - l = utfc_ptr2len(s); + grid_line_puts(msg_col, s, l, print_attr); + s += l; } - // When drawing from right to left or when a double-wide character - // doesn't fit, draw a single character here. Otherwise collect - // characters and draw them all at once later. - if (cmdmsg_rl || (cw > 1 && msg_col + t_col >= Columns - 1)) { - if (l > 1) { - s = screen_puts_mbyte((char *)s, l, attr) - 1; - } else { - msg_screen_putchar(*s, attr); + msg_didout = true; // remember that line is not empty + msg_col += cw; + } else { + char c = *s++; + if (c == '\n') { // go to next line + msg_didout = false; // remember that line is empty + msg_col = 0; + msg_row++; + if (p_more && !recurse) { + // Store text for scrolling back. + store_sb_text(&sb_str, s, attr, &sb_col, true); } - } else { - // postpone this character until later - if (t_col == 0) { - t_s = s; + } else if (c == '\r') { // go to column 0 + msg_col = 0; + } else if (c == '\b') { // go to previous char + if (msg_col) { + msg_col--; } - t_col += cw; - s += l - 1; + } else if (c == TAB) { // translate Tab into spaces + do { + grid_line_puts(msg_col, " ", 1, print_attr); + msg_col += 1; + + if (msg_col == Columns) { + break; + } + } while (msg_col & 7); + } else if (c == BELL) { // beep (from ":sh") + vim_beep(BO_SH); } } - s++; } - // Output any postponed text. - if (t_col > 0) { - t_puts(&t_col, t_s, s, attr); + if (msg_row_pending >= 0) { + msg_line_flush(); } + msg_cursor_goto(msg_row, msg_col); + if (p_more && !recurse && !(s == sb_str + 1 && *sb_str == '\n')) { - store_sb_text((char **)&sb_str, (char *)s, attr, &sb_col, false); + store_sb_text(&sb_str, s, attr, &sb_col, false); } msg_check(); } +void msg_line_flush(void) +{ + if (cmdmsg_rl) { + grid_line_mirror(); + } + grid_line_flush_if_valid_row(); +} + +void msg_cursor_goto(int row, int col) +{ + ScreenGrid *grid = &msg_grid_adj; + if (cmdmsg_rl) { + col = Columns - 1 - col; + } + grid_adjust(&grid, &row, &col); + ui_grid_cursor_goto(grid->handle, row, col); +} + /// @return true when ":filter pattern" was used and "msg" does not match /// "pattern". -bool message_filtered(char *msg) +bool message_filtered(const char *msg) { if (cmdmod.cmod_filter_regmatch.regprog == NULL) { return false; } - bool match = vim_regexec(&cmdmod.cmod_filter_regmatch, msg, (colnr_T)0); + bool match = vim_regexec(&cmdmod.cmod_filter_regmatch, msg, 0); return cmdmod.cmod_filter_force ? match : !match; } @@ -2502,7 +2433,7 @@ static sb_clear_T do_clear_sb_text = SB_CLEAR_NONE; /// @param sb_str start of string /// @param s just after string /// @param finish line ends -static void store_sb_text(char **sb_str, char *s, int attr, int *sb_col, int finish) +static void store_sb_text(const char **sb_str, const char *s, int attr, int *sb_col, int finish) { msgchunk_T *mp; @@ -2510,11 +2441,14 @@ static void store_sb_text(char **sb_str, char *s, int attr, int *sb_col, int fin || do_clear_sb_text == SB_CLEAR_CMDLINE_DONE) { clear_sb_text(do_clear_sb_text == SB_CLEAR_ALL); msg_sb_eol(); // prevent messages from overlapping + if (do_clear_sb_text == SB_CLEAR_CMDLINE_DONE && s > *sb_str && **sb_str == '\n') { + (*sb_str)++; + } do_clear_sb_text = SB_CLEAR_NONE; } if (s > *sb_str) { - mp = xmalloc((sizeof(msgchunk_T) + (size_t)(s - *sb_str))); + mp = xmalloc(offsetof(msgchunk_T, sb_text) + (size_t)(s - *sb_str) + 1); mp->sb_eol = (char)finish; mp->sb_msg_col = *sb_col; mp->sb_attr = attr; @@ -2652,15 +2586,11 @@ void msg_sb_eol(void) static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp) { msgchunk_T *mp = smp; - char *p; - for (;;) { + while (true) { msg_row = row; msg_col = mp->sb_msg_col; - p = mp->sb_text; - if (*p == '\n') { // don't display the line break - p++; - } + char *p = mp->sb_text; msg_puts_display(p, -1, mp->sb_attr, true); if (mp->sb_eol || mp->sb_next == NULL) { break; @@ -2671,26 +2601,6 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp) return mp->sb_next; } -/// Output any postponed text for msg_puts_attr_len(). -static void t_puts(int *t_col, const char *t_s, const char *s, int attr) -{ - attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr); - // Output postponed text. - msg_didout = true; // Remember that line is not empty. - grid_puts_len(&msg_grid_adj, (char *)t_s, (int)(s - t_s), msg_row, msg_col, attr); - msg_col += *t_col; - *t_col = 0; - // If the string starts with a composing character don't increment the - // column position for it. - if (utf_iscomposing(utf_ptr2char(t_s))) { - msg_col--; - } - if (msg_col >= Columns) { - msg_col = 0; - msg_row++; - } -} - /// @return true when messages should be printed to stdout/stderr: /// - "batch mode" ("silent mode", -es/-Es) /// - no UI and not embedded @@ -2736,18 +2646,10 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen) int cw = utf_char2cells(utf_ptr2char(s)); // primitive way to compute the current column - if (cmdmsg_rl) { - if (*s == '\r' || *s == '\n') { - msg_col = Columns - 1; - } else { - msg_col -= cw; - } + if (*s == '\r' || *s == '\n') { + msg_col = 0; } else { - if (*s == '\r' || *s == '\n') { - msg_col = 0; - } else { - msg_col += cw; - } + msg_col += cw; } s += len; } @@ -2767,11 +2669,9 @@ static int do_more_prompt(int typed_char) int oldState = State; int c; int retval = false; - int toscroll; bool to_redraw = false; msgchunk_T *mp_last = NULL; msgchunk_T *mp; - int i; // If headless mode is enabled and no input is required, this variable // will be true. However If server mode is enabled, the message "--more--" @@ -2789,7 +2689,7 @@ static int do_more_prompt(int typed_char) if (typed_char == 'G') { // "g<": Find first line on the last page. mp_last = msg_sb_start(last_msgchunk); - for (i = 0; i < Rows - 2 && mp_last != NULL + for (int i = 0; i < Rows - 2 && mp_last != NULL && mp_last->sb_prev != NULL; i++) { mp_last = msg_sb_start(mp_last->sb_prev); } @@ -2800,7 +2700,7 @@ static int do_more_prompt(int typed_char) if (typed_char == NUL) { msg_moremsg(false); } - for (;;) { + while (true) { // Get a typed character directly from the user. if (used_typed_char != NUL) { c = used_typed_char; // was typed at hit-enter prompt @@ -2809,7 +2709,7 @@ static int do_more_prompt(int typed_char) c = get_keystroke(resize_events); } - toscroll = 0; + int toscroll = 0; switch (c) { case BS: // scroll one line back case K_BS: @@ -2907,13 +2807,13 @@ static int do_more_prompt(int typed_char) } // go to start of line at top of the screen - for (i = 0; i < Rows - 2 && mp != NULL && mp->sb_prev != NULL; i++) { + for (int i = 0; i < Rows - 2 && mp != NULL && mp->sb_prev != NULL; i++) { mp = msg_sb_start(mp->sb_prev); } if (mp != NULL && (mp->sb_prev != NULL || to_redraw)) { // Find line to be displayed at top - for (i = 0; i > toscroll; i--) { + for (int i = 0; i > toscroll; i--) { if (mp == NULL || mp->sb_prev == NULL) { break; } @@ -2937,7 +2837,7 @@ static int do_more_prompt(int typed_char) // event fragmentization, not unnecessary scroll events). grid_fill(&msg_grid_adj, 0, Rows, 0, Columns, ' ', ' ', HL_ATTR(HLF_MSG)); - for (i = 0; mp != NULL && i < Rows - 1; i++) { + for (int i = 0; mp != NULL && i < Rows - 1; i++) { mp = disp_sb_line(i, mp); msg_scrolled++; } @@ -2996,8 +2896,6 @@ static int do_more_prompt(int typed_char) if (quit_more) { msg_row = Rows - 1; msg_col = 0; - } else if (cmdmsg_rl) { - msg_col = Columns - 1; } entered = false; @@ -3038,37 +2936,17 @@ void os_msg(const char *str) } #endif // MSWIN -/// Put a character on the screen at the current message position and advance -/// to the next position. Only for printable ASCII! -static void msg_screen_putchar(int c, int attr) -{ - attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr); - msg_didout = true; // remember that line is not empty - grid_putchar(&msg_grid_adj, c, msg_row, msg_col, attr); - if (cmdmsg_rl) { - if (--msg_col == 0) { - msg_col = Columns; - msg_row++; - } - } else { - if (++msg_col >= Columns) { - msg_col = 0; - msg_row++; - } - } -} - void msg_moremsg(int full) { - int attr; - char *s = _("-- More --"); - - attr = hl_combine_attr(HL_ATTR(HLF_MSG), HL_ATTR(HLF_M)); - grid_puts(&msg_grid_adj, s, Rows - 1, 0, attr); + int attr = hl_combine_attr(HL_ATTR(HLF_MSG), HL_ATTR(HLF_M)); + grid_line_start(&msg_grid_adj, Rows - 1); + int len = grid_line_puts(0, _("-- More --"), -1, attr); if (full) { - grid_puts(&msg_grid_adj, _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "), - Rows - 1, vim_strsize(s), attr); + len += grid_line_puts(len, _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "), + -1, attr); } + grid_line_cursor_goto(len); + grid_line_flush(); } /// Repeat the message for the current mode: MODE_ASKMORE, MODE_EXTERNCMD, @@ -3115,12 +2993,15 @@ void msg_clr_eos_force(void) return; } int msg_startcol = (cmdmsg_rl) ? 0 : msg_col; - int msg_endcol = (cmdmsg_rl) ? msg_col + 1 : Columns; + int msg_endcol = (cmdmsg_rl) ? Columns - msg_col : Columns; + // TODO(bfredl): ugly, this state should already been validated at this + // point. But msg_clr_eos() is called in a lot of places. if (msg_grid.chars && msg_row < msg_grid_pos) { - // TODO(bfredl): ugly, this state should already been validated at this - // point. But msg_clr_eos() is called in a lot of places. - msg_row = msg_grid_pos; + msg_grid_validate(); + if (msg_row < msg_grid_pos) { + msg_row = msg_grid_pos; + } } grid_fill(&msg_grid_adj, msg_row, msg_row + 1, msg_startcol, msg_endcol, @@ -3129,7 +3010,7 @@ void msg_clr_eos_force(void) ' ', ' ', HL_ATTR(HLF_MSG)); redraw_cmdline = true; // overwritten the command line - if (msg_row < Rows - 1 || msg_col == (cmdmsg_rl ? Columns : 0)) { + if (msg_row < Rows - 1 || msg_col == 0) { clear_cmdline = false; // command line has been cleared mode_displayed = false; // mode cleared or overwritten } @@ -3305,7 +3186,7 @@ static void redir_write(const char *const str, const ptrdiff_t maxlen) write_reg_contents(redir_reg, s, (ssize_t)len, true); } if (redir_vname) { - var_redir_str((char *)s, (int)maxlen); + var_redir_str(s, (int)maxlen); } // Write and adjust the current column. @@ -3414,7 +3295,7 @@ int verbose_open(void) /// Give a warning message (for searching). /// Use 'w' highlighting and may repeat the message after redrawing -void give_warning(char *message, bool hl) +void give_warning(const char *message, bool hl) FUNC_ATTR_NONNULL_ARG(1) { // Don't do this for ":silent". @@ -3437,7 +3318,7 @@ void give_warning(char *message, bool hl) msg_ext_set_kind("wmsg"); } - if (msg_attr((const char *)message, keep_msg_attr) && msg_scrolled == 0) { + if (msg(message, keep_msg_attr) && msg_scrolled == 0) { set_keep_msg(message, keep_msg_attr); } msg_didout = false; // Overwrite this message. @@ -3447,9 +3328,22 @@ void give_warning(char *message, bool hl) no_wait_return--; } -void give_warning2(char *const message, char *const a1, bool hl) +/// Shows a warning, with optional highlighting. +/// +/// @param hl enable highlighting +/// @param fmt printf-style format message +/// +/// @see smsg +/// @see semsg +void swmsg(bool hl, const char *const fmt, ...) + FUNC_ATTR_PRINTF(2, 3) { - vim_snprintf(IObuff, IOSIZE, message, a1); + va_list args; + + va_start(args, fmt); + vim_vsnprintf(IObuff, IOSIZE, fmt, args); + va_end(args); + give_warning(IObuff, hl); } @@ -3471,14 +3365,8 @@ void msg_advance(int col) if (col >= Columns) { // not enough room col = Columns - 1; } - if (cmdmsg_rl) { - while (msg_col > Columns - col) { - msg_putchar(' '); - } - } else { - while (msg_col < col) { - msg_putchar(' '); - } + while (msg_col < col) { + msg_putchar(' '); } } @@ -3502,12 +3390,11 @@ void msg_advance(int col) /// @param textfiel IObuff for inputdialog(), NULL otherwise /// @param ex_cmd when true pressing : accepts default and starts Ex command /// @returns 0 if cancelled, otherwise the nth button (1-indexed). -int do_dialog(int type, char *title, char *message, char *buttons, int dfltbutton, char *textfield, - int ex_cmd) +int do_dialog(int type, const char *title, const char *message, const char *buttons, int dfltbutton, + const char *textfield, int ex_cmd) { int retval = 0; char *hotkeys; - int c; int i; if (silent_mode // No dialogs in silent mode ("ex -s") @@ -3528,9 +3415,9 @@ int do_dialog(int type, char *title, char *message, char *buttons, int dfltbutto no_wait_return++; hotkeys = msg_show_console_dialog(message, buttons, dfltbutton); - for (;;) { + while (true) { // Get a typed character directly from the user. - c = get_keystroke(NULL); + int c = get_keystroke(NULL); switch (c) { case CAR: // User accepts default option case NL: @@ -3611,7 +3498,7 @@ static int copy_char(const char *from, char *to, bool lowercase) /// corresponding button has a hotkey /// /// @return Pointer to memory allocated for storing hotkeys -static char *console_dialog_alloc(const char *message, char *buttons, bool has_hotkey[]) +static char *console_dialog_alloc(const char *message, const char *buttons, bool has_hotkey[]) { int lenhotkey = HOTK_LEN; // count first button has_hotkey[0] = false; @@ -3619,7 +3506,7 @@ static char *console_dialog_alloc(const char *message, char *buttons, bool has_h // Compute the size of memory to allocate. int len = 0; int idx = 0; - char *r = buttons; + const char *r = buttons; while (*r) { if (*r == DLG_BUTTON_SEP) { len += 3; // '\n' -> ', '; 'x' -> '(x)' @@ -3665,7 +3552,7 @@ static char *console_dialog_alloc(const char *message, char *buttons, bool has_h /// The hotkeys can be multi-byte characters, but without combining chars. /// /// @return an allocated string with hotkeys. -static char *msg_show_console_dialog(char *message, char *buttons, int dfltbutton) +static char *msg_show_console_dialog(const char *message, const char *buttons, int dfltbutton) FUNC_ATTR_NONNULL_RET { bool has_hotkey[HAS_HOTKEY_LEN] = { false }; @@ -3685,7 +3572,7 @@ static char *msg_show_console_dialog(char *message, char *buttons, int dfltbutto /// @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, char *buttons, int default_button_idx, +static void copy_hotkeys_and_msg(const char *message, const char *buttons, int default_button_idx, const bool has_hotkey[], char *hotkeys_ptr) { *confirm_msg = '\n'; @@ -3708,7 +3595,7 @@ static void copy_hotkeys_and_msg(const char *message, char *buttons, int default } int idx = 0; - char *r = buttons; + const char *r = buttons; while (*r) { if (*r == DLG_BUTTON_SEP) { *msgp++ = ','; @@ -3812,3 +3699,21 @@ int vim_dialog_yesnoallcancel(int type, char *title, char *message, int dflt) } return VIM_CANCEL; } + +/// Check if there should be a delay to allow the user to see a message. +/// +/// Used before clearing or redrawing the screen or the command line. +void msg_check_for_delay(bool check_msg_scroll) +{ + if ((emsg_on_display || (check_msg_scroll && msg_scroll)) + && !did_wait_return + && emsg_silent == 0 + && !in_assert_fails) { + ui_flush(); + os_delay(1006, true); + emsg_on_display = false; + if (check_msg_scroll) { + msg_scroll = false; + } + } +} |