mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	api: nvim_echo
This commit is contained in:
		@@ -663,6 +663,17 @@ nvim_del_var({name})                                          *nvim_del_var()*
 | 
			
		||||
                Parameters: ~
 | 
			
		||||
                    {name}  Variable name
 | 
			
		||||
 | 
			
		||||
nvim_echo({chunks}, {history}, {opts})                           *nvim_echo()*
 | 
			
		||||
                Echo a message.
 | 
			
		||||
 | 
			
		||||
                Parameters: ~
 | 
			
		||||
                    {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.
 | 
			
		||||
                    {history}  if true, add to |message-history|.
 | 
			
		||||
                    {opts}     Optional parameters. Reserved for future use.
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
///
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
 
 | 
			
		||||
@@ -2002,4 +2002,63 @@ describe('API', function()
 | 
			
		||||
      }, meths.get_option_info'showcmd')
 | 
			
		||||
    end)
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  describe('nvim_echo', function()
 | 
			
		||||
    local screen
 | 
			
		||||
 | 
			
		||||
    before_each(function()
 | 
			
		||||
      clear()
 | 
			
		||||
      screen = Screen.new(40, 8)
 | 
			
		||||
      screen:attach()
 | 
			
		||||
      screen:set_default_attr_ids({
 | 
			
		||||
        [0] = {bold=true, foreground=Screen.colors.Blue},
 | 
			
		||||
        [1] = {bold = true, foreground = Screen.colors.SeaGreen},
 | 
			
		||||
        [2] = {bold = true, reverse = true},
 | 
			
		||||
        [3] = {foreground = Screen.colors.Brown, bold = true}, -- Statement
 | 
			
		||||
        [4] = {foreground = Screen.colors.SlateBlue}, -- Special
 | 
			
		||||
      })
 | 
			
		||||
      command('highlight Statement gui=bold guifg=Brown')
 | 
			
		||||
      command('highlight Special guifg=SlateBlue')
 | 
			
		||||
    end)
 | 
			
		||||
 | 
			
		||||
    it('can show highlighted line', function()
 | 
			
		||||
      nvim_async("echo", {{"msg_a"}, {"msg_b", "Statement"}, {"msg_c", "Special"}}, true, {})
 | 
			
		||||
      screen:expect{grid=[[
 | 
			
		||||
        ^                                        |
 | 
			
		||||
        {0:~                                       }|
 | 
			
		||||
        {0:~                                       }|
 | 
			
		||||
        {0:~                                       }|
 | 
			
		||||
        {0:~                                       }|
 | 
			
		||||
        {0:~                                       }|
 | 
			
		||||
        {0:~                                       }|
 | 
			
		||||
        msg_a{3:msg_b}{4:msg_c}                         |
 | 
			
		||||
      ]]}
 | 
			
		||||
    end)
 | 
			
		||||
 | 
			
		||||
    it('can show highlighted multiline', function()
 | 
			
		||||
      nvim_async("echo", {{"msg_a\nmsg_a", "Statement"}, {"msg_b", "Special"}}, true, {})
 | 
			
		||||
      screen:expect{grid=[[
 | 
			
		||||
                                                |
 | 
			
		||||
        {0:~                                       }|
 | 
			
		||||
        {0:~                                       }|
 | 
			
		||||
        {0:~                                       }|
 | 
			
		||||
        {2:                                        }|
 | 
			
		||||
        {3:msg_a}                                   |
 | 
			
		||||
        {3:msg_a}{4:msg_b}                              |
 | 
			
		||||
        {1:Press ENTER or type command to continue}^ |
 | 
			
		||||
      ]]}
 | 
			
		||||
    end)
 | 
			
		||||
 | 
			
		||||
    it('can save message history', function()
 | 
			
		||||
      nvim('command', 'set cmdheight=2') -- suppress Press ENTER
 | 
			
		||||
      nvim("echo", {{"msg\nmsg"}, {"msg"}}, true, {})
 | 
			
		||||
      eq("msg\nmsgmsg", meths.exec('messages', true))
 | 
			
		||||
    end)
 | 
			
		||||
 | 
			
		||||
    it('can disable saving message history', function()
 | 
			
		||||
      nvim('command', 'set cmdheight=2') -- suppress Press ENTER
 | 
			
		||||
      nvim_async("echo", {{"msg\nmsg"}, {"msg"}}, false, {})
 | 
			
		||||
      eq("", meths.exec("messages", true))
 | 
			
		||||
    end)
 | 
			
		||||
  end)
 | 
			
		||||
end)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user