aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluukvbaal <luukvbaal@gmail.com>2024-11-17 19:21:50 +0100
committerGitHub <noreply@github.com>2024-11-17 10:21:50 -0800
commite025f5a5b30a1ef92e88fed0f0c548d2240d30c0 (patch)
tree9ae569b85a6f1506a402556e44b6b6a81485efbc
parent6ea45031d5841d3227c545f213d0903b951e40be (diff)
downloadrneovim-e025f5a5b30a1ef92e88fed0f0c548d2240d30c0.tar.gz
rneovim-e025f5a5b30a1ef92e88fed0f0c548d2240d30c0.tar.bz2
rneovim-e025f5a5b30a1ef92e88fed0f0c548d2240d30c0.zip
fix(messages): proper multiline Lua print() messages #31205
Problem: Separate message emitted for each newline present in Lua print() arguments. Solution: Make msg_multiline() handle NUL bytes. Refactor print() to use msg_multiline(). Refactor vim.print() to use print().
-rw-r--r--runtime/doc/ui.txt1
-rw-r--r--runtime/lua/vim/_editor.lua13
-rw-r--r--src/nvim/eval.c2
-rw-r--r--src/nvim/ex_cmds.c4
-rw-r--r--src/nvim/lua/executor.c39
-rw-r--r--src/nvim/message.c39
-rw-r--r--test/functional/core/startup_spec.lua2
-rw-r--r--test/functional/lua/ui_event_spec.lua2
-rw-r--r--test/functional/ui/messages_spec.lua39
9 files changed, 64 insertions, 77 deletions
diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt
index 4ee8121034..4e8253c47a 100644
--- a/runtime/doc/ui.txt
+++ b/runtime/doc/ui.txt
@@ -796,6 +796,7 @@ must handle.
"echomsg" |:echomsg| message
"echoerr" |:echoerr| message
"lua_error" Error in |:lua| code
+ "lua_print" |print()| from |:lua| code
"rpc_error" Error response from |rpcrequest()|
"return_prompt" |press-enter| prompt after a multiple messages
"quickfix" Quickfix navigation message
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index b4a1e0fc15..44f17b3f85 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -1151,21 +1151,16 @@ end
--- @param ... any
--- @return any # given arguments.
function vim.print(...)
- if vim.in_fast_event() then
- print(...)
- return ...
- end
-
+ local msg = {}
for i = 1, select('#', ...) do
local o = select(i, ...)
if type(o) == 'string' then
- vim.api.nvim_out_write(o)
+ table.insert(msg, o)
else
- vim.api.nvim_out_write(vim.inspect(o, { newline = '\n', indent = ' ' }))
+ table.insert(msg, vim.inspect(o, { newline = '\n', indent = ' ' }))
end
- vim.api.nvim_out_write('\n')
end
-
+ print(table.concat(msg, '\n'))
return ...
end
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index ab6115f145..ef6e0940cf 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -7881,7 +7881,7 @@ void ex_echo(exarg_T *eap)
char *tofree = encode_tv2echo(&rettv, NULL);
if (*tofree != NUL) {
msg_ext_set_kind("echo");
- msg_multiline(tofree, echo_hl_id, true, false, &need_clear);
+ msg_multiline(cstr_as_string(tofree), echo_hl_id, true, false, &need_clear);
}
xfree(tofree);
}
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index e937961b44..8cccf08e11 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -189,7 +189,7 @@ void do_ascii(exarg_T *eap)
transchar(c), buf1, buf2, cval, cval, cval);
}
- msg_multiline(IObuff, 0, true, false, &need_clear);
+ msg_multiline(cstr_as_string(IObuff), 0, true, false, &need_clear);
off += (size_t)utf_ptr2len(data); // needed for overlong ascii?
}
@@ -224,7 +224,7 @@ void do_ascii(exarg_T *eap)
c, c, c);
}
- msg_multiline(IObuff, 0, true, false, &need_clear);
+ msg_multiline(cstr_as_string(IObuff), 0, true, false, &need_clear);
off += (size_t)utf_ptr2len(data + off); // needed for overlong ascii?
}
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 15f70fb725..c4fa8b0fff 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -954,41 +954,10 @@ static void nlua_common_free_all_mem(lua_State *lstate)
static void nlua_print_event(void **argv)
{
- char *str = argv[0];
- const size_t len = (size_t)(intptr_t)argv[1] - 1; // exclude final NUL
-
- for (size_t i = 0; i < len;) {
- if (got_int) {
- break;
- }
- const size_t start = i;
- while (i < len) {
- switch (str[i]) {
- case NUL:
- str[i] = NL;
- i++;
- continue;
- case NL:
- // TODO(bfredl): use proper multiline msg? Probably should implement
- // print() in lua in terms of nvim_message(), when it is available.
- str[i] = NUL;
- i++;
- break;
- default:
- i++;
- continue;
- }
- break;
- }
- msg(str + start, 0);
- if (msg_silent == 0) {
- msg_didout = true; // Make blank lines work properly
- }
- }
- if (len && str[len - 1] == NUL) { // Last was newline
- msg("", 0);
- }
- xfree(str);
+ 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);
}
/// Print as a Vim message
diff --git a/src/nvim/message.c b/src/nvim/message.c
index e8f20916b8..47f33c8967 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -249,35 +249,33 @@ bool msg(const char *s, const int hl_id)
return msg_hl_keep(s, hl_id, false, false);
}
-/// Similar to msg_outtrans, but support newlines and tabs.
-void msg_multiline(const char *s, int hl_id, bool check_int, bool hist, bool *need_clear)
+/// Similar to msg_outtrans_len, but support newlines and tabs.
+void msg_multiline(String str, int hl_id, bool check_int, bool hist, bool *need_clear)
FUNC_ATTR_NONNULL_ALL
{
- const char *next_spec = s;
-
- while (next_spec != NULL) {
+ const char *s = str.data;
+ const char *chunk = s;
+ while ((size_t)(s - str.data) < str.size) {
if (check_int && got_int) {
return;
}
- next_spec = strpbrk(s, "\t\n\r");
-
- if (next_spec != NULL) {
- // Printing all char that are before the char found by strpbrk
- msg_outtrans_len(s, (int)(next_spec - s), hl_id, hist);
+ if (*s == '\n' || *s == TAB || *s == '\r') {
+ // Print all chars before the delimiter
+ msg_outtrans_len(chunk, (int)(s - chunk), hl_id, hist);
- if (*next_spec != TAB && *need_clear) {
+ if (*s != TAB && *need_clear) {
msg_clr_eos();
*need_clear = false;
}
- msg_putchar_hl((uint8_t)(*next_spec), hl_id);
- s = next_spec + 1;
+ msg_putchar_hl((uint8_t)(*s), hl_id);
+ chunk = s + 1;
}
+ s++;
}
- // Print the rest of the message. We know there is no special
- // character because strpbrk returned NULL
- if (*s != NUL) {
- msg_outtrans(s, hl_id, hist);
+ // Print the rest of the message
+ if (*chunk != NUL) {
+ msg_outtrans_len(chunk, (int)(str.size - (size_t)(chunk - str.data)), hl_id, hist);
}
}
@@ -290,7 +288,7 @@ void msg_multihl(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(chunk.text.data, chunk.hl_id, true, false, &need_clear);
+ msg_multiline(chunk.text, chunk.hl_id, true, false, &need_clear);
}
if (history && kv_size(hl_msg)) {
add_msg_hist_multihl(NULL, 0, 0, true, hl_msg);
@@ -349,7 +347,7 @@ bool msg_hl_keep(const char *s, int hl_id, bool keep, bool multiline)
bool need_clear = true;
if (multiline) {
- msg_multiline(s, hl_id, false, false, &need_clear);
+ msg_multiline(cstr_as_string(s), hl_id, false, false, &need_clear);
} else {
msg_outtrans(s, hl_id, false);
}
@@ -2689,12 +2687,13 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen)
// primitive way to compute the current column
if (*s == '\r' || *s == '\n') {
msg_col = 0;
+ msg_didout = false;
} else {
msg_col += cw;
+ msg_didout = true;
}
s += len;
}
- msg_didout = true; // assume that line is not empty
}
/// Show the more-prompt and handle the user response.
diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua
index e885164c20..7062211187 100644
--- a/test/functional/core/startup_spec.lua
+++ b/test/functional/core/startup_spec.lua
@@ -277,10 +277,8 @@ describe('startup', function()
-- nvim <vim args> -l foo.lua <vim args>
assert_l_out(
- -- luacheck: ignore 611 (Line contains only whitespaces)
[[
wrap
-
bufs:
nvim args: 7
lua args: { "-c", "set wrap?",
diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua
index 6d4be4d1f2..c8616e3e11 100644
--- a/test/functional/lua/ui_event_spec.lua
+++ b/test/functional/lua/ui_event_spec.lua
@@ -142,7 +142,7 @@ describe('vim.ui_attach', function()
'msg_history_show',
{
{ 'echomsg', { { 0, 'message1', 0 } } },
- { '', { { 0, 'message2', 0 } } },
+ { 'lua_print', { { 0, 'message2', 0 } } },
{ 'echomsg', { { 0, 'message3', 0 } } },
},
},
diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua
index 25f3465d46..734877d262 100644
--- a/test/functional/ui/messages_spec.lua
+++ b/test/functional/ui/messages_spec.lua
@@ -1113,6 +1113,33 @@ stack traceback:
})
eq(showmode, 1)
end)
+
+ it('emits single message for multiline print())', function()
+ exec_lua([[print("foo\nbar\nbaz")]])
+ screen:expect({
+ messages = {
+ {
+ content = { { 'foo\nbar\nbaz' } },
+ kind = 'lua_print',
+ },
+ },
+ })
+ exec_lua([[print(vim.inspect({ foo = "bar" }))]])
+ screen:expect({
+ grid = [[
+ ^ |
+ {1:~ }|*4
+ ]],
+ messages = {
+ {
+ content = { { '{\n foo = "bar"\n}' } },
+ kind = 'lua_print',
+ },
+ },
+ })
+ exec_lua([[vim.print({ foo = "bar" })]])
+ screen:expect_unchanged()
+ end)
end)
describe('ui/builtin messages', function()
@@ -2062,8 +2089,6 @@ aliquip ex ea commodo consequat.]]
end)
it('can be quit with Lua #11224 #16537', function()
- -- NOTE: adds "4" to message history, although not displayed initially
- -- (triggered the more prompt).
screen:try_resize(40, 5)
feed(':lua for i=0,10 do print(i) end<cr>')
screen:expect {
@@ -2093,13 +2118,13 @@ aliquip ex ea commodo consequat.]]
{4:-- More --}^ |
]],
}
- feed('j')
+ feed('G')
screen:expect {
grid = [[
- 1 |
- 2 |
- 3 |
- 4 |
+ 7 |
+ 8 |
+ 9 |
+ 10 |
{4:Press ENTER or type command to continue}^ |
]],
}