aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2025-01-09 06:36:29 -0800
committerGitHub <noreply@github.com>2025-01-09 06:36:29 -0800
commit5135a232199047e473e3941b0b5a738c77fbecb5 (patch)
treec6f964678048febab756f825e6cf76470017d89c
parentdcaf8bef08d094889ef5fac24d123871dd0e6a6f (diff)
parent5c92b40b4b173c7d85106689fef811e41994abb0 (diff)
downloadrneovim-5135a232199047e473e3941b0b5a738c77fbecb5.tar.gz
rneovim-5135a232199047e473e3941b0b5a738c77fbecb5.tar.bz2
rneovim-5135a232199047e473e3941b0b5a738c77fbecb5.zip
Merge #31900 from luukvbaal/nvim_echo
-rw-r--r--runtime/doc/api.txt26
-rw-r--r--runtime/doc/deprecated.txt3
-rw-r--r--runtime/doc/news.txt2
-rw-r--r--runtime/lua/vim/_defaults.lua2
-rw-r--r--runtime/lua/vim/_editor.lua10
-rw-r--r--runtime/lua/vim/_meta/api.lua21
-rw-r--r--runtime/lua/vim/_meta/api_keysets.lua1
-rw-r--r--runtime/lua/vim/lsp/client.lua6
-rw-r--r--runtime/lua/vim/lsp/handlers.lua5
-rw-r--r--src/nvim/api/deprecated.c80
-rw-r--r--src/nvim/api/keysets_defs.h1
-rw-r--r--src/nvim/api/private/helpers.c4
-rw-r--r--src/nvim/api/vim.c88
-rw-r--r--src/nvim/eval.c3
-rw-r--r--src/nvim/ex_docmd.c2
-rw-r--r--src/nvim/lua/executor.c2
-rw-r--r--src/nvim/message.c55
-rw-r--r--test/functional/api/vim_spec.lua24
-rw-r--r--test/functional/ui/messages_spec.lua15
19 files changed, 190 insertions, 160 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index c8e0dcd0c5..852037bb34 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -662,28 +662,13 @@ nvim_echo({chunks}, {history}, {opts}) *nvim_echo()*
`hl_group` element can be omitted for no highlight.
• {history} if true, add to |message-history|.
• {opts} Optional parameters.
+ • err: Treat the message like |:echoerr|. Omitted `hlgroup`
+ uses |hl-ErrorMsg| instead.
• verbose: Message is printed as a result of 'verbose'
option. If Nvim was invoked with -V3log_file, the message
will be redirected to the log_file and suppressed from
direct output.
-nvim_err_write({str}) *nvim_err_write()*
- Writes a message to the Vim error buffer. Does not append "\n", the
- message is buffered (won't display) until a linefeed is written.
-
- Parameters: ~
- • {str} Message
-
-nvim_err_writeln({str}) *nvim_err_writeln()*
- Writes a message to the Vim error buffer. Appends "\n", so the buffer is
- flushed (and displayed).
-
- Parameters: ~
- • {str} Message
-
- See also: ~
- • nvim_err_write()
-
nvim_eval_statusline({str}, {opts}) *nvim_eval_statusline()*
Evaluates statusline string.
@@ -1154,13 +1139,6 @@ nvim_open_term({buffer}, {opts}) *nvim_open_term()*
Return: ~
Channel id, or 0 on error
-nvim_out_write({str}) *nvim_out_write()*
- Writes a message to the Vim output buffer. Does not append "\n", the
- message is buffered (won't display) until a linefeed is written.
-
- Parameters: ~
- • {str} Message
-
nvim_paste({data}, {crlf}, {phase}) *nvim_paste()*
Pastes at cursor (in any mode), and sets "redo" so dot (|.|) will repeat
the input. UIs call this to implement "paste", but it's also intended for
diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt
index ec98cc844f..dbdb8f541b 100644
--- a/runtime/doc/deprecated.txt
+++ b/runtime/doc/deprecated.txt
@@ -18,6 +18,9 @@ DEPRECATED IN 0.11 *deprecated-0.11*
API
• nvim_subscribe() Plugins must maintain their own "multicast" channels list.
• nvim_unsubscribe() Plugins must maintain their own "multicast" channels list.
+• nvim_out_write() Use |nvim_echo()|.
+• nvim_err_write() Use |nvim_echo()| with `{err=true}`.
+• nvim_err_writeln() Use |nvim_echo()| with `{err=true}`.
DIAGNOSTICS
• *vim.diagnostic.goto_next()* Use |vim.diagnostic.jump()| with `{count=1, float=true}` instead.
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 931f5e117c..960b6f05f9 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -178,6 +178,8 @@ API
• Improved API "meta" docstrings and :help documentation.
• |nvim__ns_set()| can set properties for a namespace
+• |nvim_echo()| `err` field to print error messages and `chunks` accepts
+ highlight group IDs.
DEFAULTS
diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua
index 4216a2acb7..d71116117e 100644
--- a/runtime/lua/vim/_defaults.lua
+++ b/runtime/lua/vim/_defaults.lua
@@ -224,7 +224,7 @@ do
local function cmd(opts)
local ok, err = pcall(vim.api.nvim_cmd, opts, {})
if not ok then
- vim.api.nvim_err_writeln(err:sub(#'Vim:' + 1))
+ vim.api.nvim_echo({ { err:sub(#'Vim:' + 1) } }, true, { err = true })
end
end
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index 44f17b3f85..66815a967e 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -58,6 +58,7 @@ vim._extra = {
--- @private
vim.log = {
+ --- @enum vim.log.levels
levels = {
TRACE = 0,
DEBUG = 1,
@@ -620,13 +621,8 @@ end
---@param opts table|nil Optional parameters. Unused by default.
---@diagnostic disable-next-line: unused-local
function vim.notify(msg, level, opts) -- luacheck: no unused args
- if level == vim.log.levels.ERROR then
- vim.api.nvim_err_writeln(msg)
- elseif level == vim.log.levels.WARN then
- vim.api.nvim_echo({ { msg, 'WarningMsg' } }, true, {})
- else
- vim.api.nvim_echo({ { msg } }, true, {})
- end
+ local chunks = { { msg, level == vim.log.levels.WARN and 'WarningMsg' or nil } }
+ vim.api.nvim_echo(chunks, true, { err = level == vim.log.levels.ERROR })
end
do
diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index 7297c8ad38..f6d8153c27 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -1104,22 +1104,19 @@ function vim.api.nvim_del_var(name) end
--- `hl_group` element can be omitted for no highlight.
--- @param history boolean if true, add to `message-history`.
--- @param opts vim.api.keyset.echo_opts Optional parameters.
+--- - err: Treat the message like `:echoerr`. Omitted `hlgroup`
+--- uses `hl-ErrorMsg` instead.
--- - verbose: Message is printed as a result of 'verbose' option.
--- If Nvim was invoked with -V3log_file, the message will be
--- redirected to the log_file and suppressed from direct output.
function vim.api.nvim_echo(chunks, history, opts) end
---- Writes a message to the Vim error buffer. Does not append "\n", the
---- message is buffered (won't display) until a linefeed is written.
----
---- @param str string Message
+--- @deprecated
+--- @param str string
function vim.api.nvim_err_write(str) end
---- Writes a message to the Vim error buffer. Appends "\n", so the buffer is
---- flushed (and displayed).
----
---- @see vim.api.nvim_err_write
---- @param str string Message
+--- @deprecated
+--- @param str string
function vim.api.nvim_err_writeln(str) end
--- Evaluates a Vimscript `expression`. Dicts and Lists are recursively expanded.
@@ -1859,10 +1856,8 @@ function vim.api.nvim_open_term(buffer, opts) end
--- @return integer # Window handle, or 0 on error
function vim.api.nvim_open_win(buffer, enter, config) end
---- Writes a message to the Vim output buffer. Does not append "\n", the
---- message is buffered (won't display) until a linefeed is written.
----
---- @param str string Message
+--- @deprecated
+--- @param str string
function vim.api.nvim_out_write(str) end
--- Parse command line.
diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua
index c08ab0663b..98e916115e 100644
--- a/runtime/lua/vim/_meta/api_keysets.lua
+++ b/runtime/lua/vim/_meta/api_keysets.lua
@@ -88,6 +88,7 @@ error('Cannot require a meta file')
--- @field pattern? string|string[]
--- @class vim.api.keyset.echo_opts
+--- @field err? boolean
--- @field verbose? boolean
--- @class vim.api.keyset.empty
diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
index 5d11312c77..a99363d3d6 100644
--- a/runtime/lua/vim/lsp/client.lua
+++ b/runtime/lua/vim/lsp/client.lua
@@ -702,14 +702,14 @@ local wait_result_reason = { [-1] = 'timeout', [-2] = 'interrupted', [-3] = 'err
---
--- @param ... string List to write to the buffer
local function err_message(...)
- local message = table.concat(vim.iter({ ... }):flatten():totable())
+ local chunks = { { table.concat({ ... }) } }
if vim.in_fast_event() then
vim.schedule(function()
- api.nvim_err_writeln(message)
+ vim.api.nvim_echo(chunks, true, { err = true })
api.nvim_command('redraw')
end)
else
- api.nvim_err_writeln(message)
+ vim.api.nvim_echo(chunks, true, { err = true })
api.nvim_command('redraw')
end
end
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 1945040bda..3779c342e8 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -582,9 +582,8 @@ NSC['window/showMessage'] = function(_, params, ctx)
if message_type == protocol.MessageType.Error then
err_message('LSP[', client_name, '] ', message)
else
- --- @type string
- local message_type_name = protocol.MessageType[message_type]
- api.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message))
+ message = ('LSP[%s][%s] %s\n'):format(client_name, protocol.MessageType[message_type], message)
+ api.nvim_echo({ { message } }, true, { err = true })
end
return params
end
diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c
index d5eddb74de..406d5e7b4f 100644
--- a/src/nvim/api/deprecated.c
+++ b/src/nvim/api/deprecated.c
@@ -21,9 +21,11 @@
#include "nvim/lua/executor.h"
#include "nvim/memory.h"
#include "nvim/memory_defs.h"
+#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/pos_defs.h"
+#include "nvim/strings.h"
#include "nvim/types_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -812,3 +814,81 @@ void nvim_unsubscribe(uint64_t channel_id, String event)
{
// Does nothing. `rpcnotify(0,…)` broadcasts to all channels, there are no "subscriptions".
}
+
+enum { LINE_BUFFER_MIN_SIZE = 4096, };
+
+/// Writes a message to vim output or error buffer. The string is split
+/// and flushed after each newline. Incomplete lines are kept for writing
+/// later.
+///
+/// @param message Message to write
+/// @param to_err true: message is an error (uses `emsg` instead of `msg`)
+/// @param writeln Append a trailing newline
+static void write_msg(String message, bool to_err, bool writeln)
+{
+ static StringBuilder out_line_buf = KV_INITIAL_VALUE;
+ static StringBuilder err_line_buf = KV_INITIAL_VALUE;
+ StringBuilder *line_buf = to_err ? &err_line_buf : &out_line_buf;
+
+#define PUSH_CHAR(c) \
+ if (kv_max(*line_buf) == 0) { \
+ kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
+ } \
+ if (c == NL) { \
+ kv_push(*line_buf, NUL); \
+ if (to_err) { \
+ emsg(line_buf->items); \
+ } else { \
+ msg(line_buf->items, 0); \
+ } \
+ if (msg_silent == 0) { \
+ msg_didout = true; \
+ } \
+ kv_drop(*line_buf, kv_size(*line_buf)); \
+ kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
+ } else if (c == NUL) { \
+ kv_push(*line_buf, NL); \
+ } else { \
+ kv_push(*line_buf, c); \
+ }
+
+ no_wait_return++;
+ for (uint32_t i = 0; i < message.size; i++) {
+ if (got_int) {
+ break;
+ }
+ PUSH_CHAR(message.data[i]);
+ }
+ if (writeln) {
+ PUSH_CHAR(NL);
+ }
+ no_wait_return--;
+ msg_end();
+}
+
+/// @deprecated
+///
+/// @param str Message
+void nvim_out_write(String str)
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13)
+{
+ write_msg(str, false, false);
+}
+
+/// @deprecated
+///
+/// @param str Message
+void nvim_err_write(String str)
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13)
+{
+ write_msg(str, true, false);
+}
+
+/// @deprecated
+///
+/// @param str Message
+void nvim_err_writeln(String str)
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13)
+{
+ write_msg(str, true, true);
+}
diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h
index 40e89d230d..664406ab6e 100644
--- a/src/nvim/api/keysets_defs.h
+++ b/src/nvim/api/keysets_defs.h
@@ -326,6 +326,7 @@ typedef struct {
} Dict(cmd_opts);
typedef struct {
+ Boolean err;
Boolean verbose;
} Dict(echo_opts);
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 78aa7c00f7..c98635f8fd 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -776,7 +776,7 @@ char *api_typename(ObjectType t)
UNREACHABLE;
}
-HlMessage parse_hl_msg(Array chunks, Error *err)
+HlMessage parse_hl_msg(Array chunks, bool is_err, Error *err)
{
HlMessage hl_msg = KV_INITIAL_VALUE;
for (size_t i = 0; i < chunks.size; i++) {
@@ -791,7 +791,7 @@ HlMessage parse_hl_msg(Array chunks, Error *err)
String str = copy_string(chunk.items[0].data.string, NULL);
- int hl_id = 0;
+ int hl_id = is_err ? HLF_E : 0;
if (chunk.size == 2) {
hl_id = object_to_hl_id(chunk.items[1], "text highlight", err);
}
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index e3e69f4ff6..ed9edb1beb 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -86,8 +86,6 @@
#include "nvim/vim_defs.h"
#include "nvim/window.h"
-#define LINE_BUFFER_MIN_SIZE 4096
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/vim.c.generated.h"
#endif
@@ -775,13 +773,15 @@ void nvim_set_vvar(String name, Object value, Error *err)
/// `hl_group` element can be omitted for no highlight.
/// @param history if true, add to |message-history|.
/// @param opts Optional parameters.
+/// - err: Treat the message like |:echoerr|. Omitted `hlgroup`
+/// uses |hl-ErrorMsg| instead.
/// - verbose: Message is printed as a result of 'verbose' option.
/// If Nvim was invoked with -V3log_file, the message will be
/// redirected to the log_file and suppressed from direct output.
void nvim_echo(Array chunks, Boolean history, Dict(echo_opts) *opts, Error *err)
FUNC_API_SINCE(7)
{
- HlMessage hl_msg = parse_hl_msg(chunks, err);
+ HlMessage hl_msg = parse_hl_msg(chunks, opts->err, err);
if (ERROR_SET(err)) {
goto error;
}
@@ -790,7 +790,7 @@ void nvim_echo(Array chunks, Boolean history, Dict(echo_opts) *opts, Error *err)
verbose_enter();
}
- msg_multihl(hl_msg, history ? "echomsg" : "echo", history);
+ msg_multihl(hl_msg, opts->err ? "echoerr" : history ? "echomsg" : "echo", history, opts->err);
if (opts->verbose) {
verbose_leave();
@@ -806,37 +806,6 @@ error:
hl_msg_free(hl_msg);
}
-/// Writes a message to the Vim output buffer. Does not append "\n", the
-/// message is buffered (won't display) until a linefeed is written.
-///
-/// @param str Message
-void nvim_out_write(String str)
- FUNC_API_SINCE(1)
-{
- write_msg(str, false, false);
-}
-
-/// Writes a message to the Vim error buffer. Does not append "\n", the
-/// message is buffered (won't display) until a linefeed is written.
-///
-/// @param str Message
-void nvim_err_write(String str)
- FUNC_API_SINCE(1)
-{
- write_msg(str, true, false);
-}
-
-/// Writes a message to the Vim error buffer. Appends "\n", so the buffer is
-/// flushed (and displayed).
-///
-/// @param str Message
-/// @see nvim_err_write()
-void nvim_err_writeln(String str)
- FUNC_API_SINCE(1)
-{
- write_msg(str, true, true);
-}
-
/// Gets the current list of buffer handles
///
/// Includes unlisted (unloaded/deleted) buffers, like `:ls!`.
@@ -1662,55 +1631,6 @@ Array nvim_list_chans(Arena *arena)
return channel_all_info(arena);
}
-/// Writes a message to vim output or error buffer. The string is split
-/// and flushed after each newline. Incomplete lines are kept for writing
-/// later.
-///
-/// @param message Message to write
-/// @param to_err true: message is an error (uses `emsg` instead of `msg`)
-/// @param writeln Append a trailing newline
-static void write_msg(String message, bool to_err, bool writeln)
-{
- static StringBuilder out_line_buf = KV_INITIAL_VALUE;
- static StringBuilder err_line_buf = KV_INITIAL_VALUE;
- StringBuilder *line_buf = to_err ? &err_line_buf : &out_line_buf;
-
-#define PUSH_CHAR(c) \
- if (kv_max(*line_buf) == 0) { \
- kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
- } \
- if (c == NL) { \
- kv_push(*line_buf, NUL); \
- if (to_err) { \
- emsg(line_buf->items); \
- } else { \
- msg(line_buf->items, 0); \
- } \
- if (msg_silent == 0) { \
- msg_didout = true; \
- } \
- kv_drop(*line_buf, kv_size(*line_buf)); \
- kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
- } else if (c == NUL) { \
- kv_push(*line_buf, NL); \
- } else { \
- kv_push(*line_buf, c); \
- }
-
- no_wait_return++;
- for (uint32_t i = 0; i < message.size; i++) {
- if (got_int) {
- break;
- }
- PUSH_CHAR(message.data[i]);
- }
- if (writeln) {
- PUSH_CHAR(NL);
- }
- no_wait_return--;
- msg_end();
-}
-
// Functions used for testing purposes
/// Returns object given as argument.
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index a90f275713..5b91f1248f 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -7968,8 +7968,7 @@ void ex_execute(exarg_T *eap)
} else if (eap->cmdidx == CMD_echoerr) {
// We don't want to abort following commands, restore did_emsg.
int save_did_emsg = did_emsg;
- msg_ext_set_kind("echoerr");
- emsg_multiline(ga.ga_data, true);
+ emsg_multiline(ga.ga_data, "echoerr", HLF_E, true);
if (!force_abort) {
did_emsg = save_did_emsg;
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 6f9f0f07c9..137226e2ad 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -982,7 +982,7 @@ void handle_did_throw(void)
if (messages != NULL) {
do {
msglist_T *next = messages->next;
- emsg_multiline(messages->msg, messages->multiline);
+ emsg_multiline(messages->msg, "emsg", HLF_E, messages->multiline);
xfree(messages->msg);
xfree(messages->sfile);
xfree(messages);
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 0a412b4ca9..68d3af6074 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -958,7 +958,7 @@ static void nlua_print_event(void **argv)
HlMessage msg = KV_INITIAL_VALUE;
HlMessageChunk chunk = { { .data = argv[0], .size = (size_t)(intptr_t)argv[1] - 1 }, 0 };
kv_push(msg, chunk);
- msg_multihl(msg, "lua_print", true);
+ msg_multihl(msg, "lua_print", true, false);
}
/// Print as a Vim message
diff --git a/src/nvim/message.c b/src/nvim/message.c
index d45bc147cb..e288353ddc 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -293,20 +293,31 @@ 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;
+
+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_history = history;
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(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();
}
@@ -342,18 +353,19 @@ bool msg_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;
@@ -368,7 +380,10 @@ bool msg_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);
@@ -618,6 +633,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) {
@@ -652,7 +670,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;
@@ -750,14 +768,13 @@ 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.
@@ -765,7 +782,7 @@ bool emsg_multiline(const char *s, bool multiline)
msg_source(hl_id);
if (msg_ext_kind == NULL) {
- msg_ext_set_kind("emsg");
+ msg_ext_set_kind(kind);
}
// Display the error message itself.
@@ -781,7 +798,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)
@@ -821,7 +838,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;
}
@@ -905,7 +922,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);
}
@@ -1196,7 +1213,7 @@ 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_keep(p->msg, p->hl_id, false, p->multiline);
}
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 578fa361e8..e0ab31f702 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -3680,6 +3680,30 @@ describe('API', function()
async_meths.nvim_echo({ { 'msg\nmsg' }, { 'msg' } }, false, {})
eq('', exec_capture('messages'))
end)
+
+ it('can print error message', function()
+ async_meths.nvim_echo({ { 'Error\nMessage' } }, false, { err = true })
+ screen:expect([[
+ |
+ {1:~ }|*3
+ {3: }|
+ {9:Error} |
+ {9:Message} |
+ {6:Press ENTER or type command to continue}^ |
+ ]])
+ feed(':messages<CR>')
+ screen:expect([[
+ ^ |
+ {1:~ }|*6
+ |
+ ]])
+ async_meths.nvim_echo({ { 'Error' }, { 'Message', 'Special' } }, false, { err = true })
+ screen:expect([[
+ ^ |
+ {1:~ }|*6
+ {9:Error}{16:Message} |
+ ]])
+ end)
end)
describe('nvim_open_term', function()
diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua
index 77ffc475b0..06048c665c 100644
--- a/test/functional/ui/messages_spec.lua
+++ b/test/functional/ui/messages_spec.lua
@@ -315,6 +315,21 @@ describe('ui/ext_messages', function()
})
feed('<Esc>')
command('set showmode')
+
+ -- kind=echoerr for nvim_echo() err
+ feed(':call nvim_echo([["Error"], ["Message", "Special"]], 1, #{ err:1 })<CR>')
+ screen:expect({
+ cmdline = { {
+ abort = false,
+ } },
+ messages = {
+ {
+ content = { { 'Error', 9, 6 }, { 'Message', 16, 99 } },
+ history = true,
+ kind = 'echoerr',
+ },
+ },
+ })
end)
it(':echoerr', function()