api: nvim_echo

This commit is contained in:
notomo
2021-01-11 15:18:51 +09:00
committed by Björn Linse
parent 1785ac3e37
commit 8e86f5e460
6 changed files with 191 additions and 0 deletions

View File

@@ -1645,6 +1645,43 @@ bool api_object_to_bool(Object obj, const char *what,
}
}
HlMessage parse_hl_msg(Array chunks, Error *err)
{
HlMessage hl_msg = KV_INITIAL_VALUE;
for (size_t i = 0; i < chunks.size; i++) {
if (chunks.items[i].type != kObjectTypeArray) {
api_set_error(err, kErrorTypeValidation, "Chunk is not an array");
goto free_exit;
}
Array chunk = chunks.items[i].data.array;
if (chunk.size == 0 || chunk.size > 2
|| chunk.items[0].type != kObjectTypeString
|| (chunk.size == 2 && chunk.items[1].type != kObjectTypeString)) {
api_set_error(err, kErrorTypeValidation,
"Chunk is not an array with one or two strings");
goto free_exit;
}
String str = copy_string(chunk.items[0].data.string);
int attr = 0;
if (chunk.size == 2) {
String hl = chunk.items[1].data.string;
if (hl.size > 0) {
int hl_id = syn_check_group((char_u *)hl.data, (int)hl.size);
attr = hl_id > 0 ? syn_id2attr(hl_id) : 0;
}
}
kv_push(hl_msg, ((HlMessageChunk){ .text = str, .attr = attr }));
}
return hl_msg;
free_exit:
clear_hl_msg(&hl_msg);
return hl_msg;
}
const char *describe_ns(NS ns_id)
{
String name;

View File

@@ -990,6 +990,47 @@ void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err)
set_option_to(channel_id, NULL, SREQ_GLOBAL, name, value, err);
}
/// Echo a message.
///
/// @param chunks A list of [text, hl_group] arrays, each representing a
/// text chunk with specified highlight. `hl_group` element
/// can be omitted for no highlight.
/// @param history if true, add to |message-history|.
/// @param opts Optional parameters. Reserved for future use.
void nvim_echo(Array chunks, Boolean history, Dictionary opts, Error *err)
FUNC_API_SINCE(7)
{
HlMessage hl_msg = parse_hl_msg(chunks, err);
if (ERROR_SET(err)) {
goto error;
}
if (opts.size > 0) {
api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
goto error;
}
no_wait_return++;
bool need_clear = true;
msg_start();
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,
false, &need_clear);
}
if (history) {
msg_ext_set_kind("echomsg");
add_hl_msg_hist(hl_msg);
} else {
msg_ext_set_kind("echo");
}
no_wait_return--;
msg_end();
error:
clear_hl_msg(&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.
///

View File

@@ -890,6 +890,40 @@ char_u *msg_may_trunc(int force, char_u *s)
return s;
}
void clear_hl_msg(HlMessage *hl_msg)
{
for (size_t i = 0; i < kv_size(*hl_msg); i++) {
xfree(kv_A(*hl_msg, i).text.data);
}
kv_destroy(*hl_msg);
*hl_msg = (HlMessage)KV_INITIAL_VALUE;
}
#define LINE_BUFFER_SIZE 4096
void add_hl_msg_hist(HlMessage hl_msg)
{
// TODO(notomo): support multi highlighted message history
size_t pos = 0;
char buf[LINE_BUFFER_SIZE];
for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
HlMessageChunk chunk = kv_A(hl_msg, i);
for (uint32_t j = 0; j < chunk.text.size; j++) {
if (pos == LINE_BUFFER_SIZE - 1) {
buf[pos] = NUL;
add_msg_hist((const char *)buf, -1, MSG_HIST, true);
pos = 0;
continue;
}
buf[pos++] = chunk.text.data[j];
}
}
if (pos != 0) {
buf[pos] = NUL;
add_msg_hist((const char *)buf, -1, MSG_HIST, true);
}
}
/// @param[in] len Length of s or -1.
static void add_msg_hist(const char *s, int len, int attr, bool multiline)
{

View File

@@ -8,6 +8,8 @@
#include "nvim/macros.h"
#include "nvim/types.h"
#include "nvim/grid_defs.h"
#include "nvim/api/private/defs.h"
#include "nvim/lib/kvec.h"
/*
* Types of dialogs passed to do_dialog().
@@ -75,6 +77,13 @@
/// Like #MSG_PUTS_ATTR, but if middle part of long messages will be replaced
#define MSG_PUTS_LONG_ATTR(s, a) msg_puts_long_attr((char_u *)(s), (a))
typedef struct {
String text;
int attr;
} HlMessageChunk;
typedef kvec_t(HlMessageChunk) HlMessage;
/// Message history for `:messages`
typedef struct msg_hist {
struct msg_hist *next; ///< Next message.