Merge #31900 from luukvbaal/nvim_echo

This commit is contained in:
Justin M. Keyes
2025-01-09 06:36:29 -08:00
committed by GitHub
19 changed files with 190 additions and 160 deletions

View File

@@ -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);
}

View File

@@ -326,6 +326,7 @@ typedef struct {
} Dict(cmd_opts);
typedef struct {
Boolean err;
Boolean verbose;
} Dict(echo_opts);

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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);
}