mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
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().
This commit is contained in:
parent
6ea45031d5
commit
e025f5a5b3
@ -796,6 +796,7 @@ must handle.
|
|||||||
"echomsg" |:echomsg| message
|
"echomsg" |:echomsg| message
|
||||||
"echoerr" |:echoerr| message
|
"echoerr" |:echoerr| message
|
||||||
"lua_error" Error in |:lua| code
|
"lua_error" Error in |:lua| code
|
||||||
|
"lua_print" |print()| from |:lua| code
|
||||||
"rpc_error" Error response from |rpcrequest()|
|
"rpc_error" Error response from |rpcrequest()|
|
||||||
"return_prompt" |press-enter| prompt after a multiple messages
|
"return_prompt" |press-enter| prompt after a multiple messages
|
||||||
"quickfix" Quickfix navigation message
|
"quickfix" Quickfix navigation message
|
||||||
|
@ -1151,21 +1151,16 @@ end
|
|||||||
--- @param ... any
|
--- @param ... any
|
||||||
--- @return any # given arguments.
|
--- @return any # given arguments.
|
||||||
function vim.print(...)
|
function vim.print(...)
|
||||||
if vim.in_fast_event() then
|
local msg = {}
|
||||||
print(...)
|
|
||||||
return ...
|
|
||||||
end
|
|
||||||
|
|
||||||
for i = 1, select('#', ...) do
|
for i = 1, select('#', ...) do
|
||||||
local o = select(i, ...)
|
local o = select(i, ...)
|
||||||
if type(o) == 'string' then
|
if type(o) == 'string' then
|
||||||
vim.api.nvim_out_write(o)
|
table.insert(msg, o)
|
||||||
else
|
else
|
||||||
vim.api.nvim_out_write(vim.inspect(o, { newline = '\n', indent = ' ' }))
|
table.insert(msg, vim.inspect(o, { newline = '\n', indent = ' ' }))
|
||||||
end
|
end
|
||||||
vim.api.nvim_out_write('\n')
|
|
||||||
end
|
end
|
||||||
|
print(table.concat(msg, '\n'))
|
||||||
return ...
|
return ...
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -7881,7 +7881,7 @@ void ex_echo(exarg_T *eap)
|
|||||||
char *tofree = encode_tv2echo(&rettv, NULL);
|
char *tofree = encode_tv2echo(&rettv, NULL);
|
||||||
if (*tofree != NUL) {
|
if (*tofree != NUL) {
|
||||||
msg_ext_set_kind("echo");
|
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);
|
xfree(tofree);
|
||||||
}
|
}
|
||||||
|
@ -189,7 +189,7 @@ void do_ascii(exarg_T *eap)
|
|||||||
transchar(c), buf1, buf2, cval, cval, cval);
|
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?
|
off += (size_t)utf_ptr2len(data); // needed for overlong ascii?
|
||||||
}
|
}
|
||||||
@ -224,7 +224,7 @@ void do_ascii(exarg_T *eap)
|
|||||||
c, c, c);
|
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?
|
off += (size_t)utf_ptr2len(data + off); // needed for overlong ascii?
|
||||||
}
|
}
|
||||||
|
@ -954,41 +954,10 @@ static void nlua_common_free_all_mem(lua_State *lstate)
|
|||||||
|
|
||||||
static void nlua_print_event(void **argv)
|
static void nlua_print_event(void **argv)
|
||||||
{
|
{
|
||||||
char *str = argv[0];
|
HlMessage msg = KV_INITIAL_VALUE;
|
||||||
const size_t len = (size_t)(intptr_t)argv[1] - 1; // exclude final NUL
|
HlMessageChunk chunk = { { .data = argv[0], .size = (size_t)(intptr_t)argv[1] - 1 }, 0 };
|
||||||
|
kv_push(msg, chunk);
|
||||||
for (size_t i = 0; i < len;) {
|
msg_multihl(msg, "lua_print", true);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Print as a Vim message
|
/// Print as a Vim message
|
||||||
|
@ -249,35 +249,33 @@ bool msg(const char *s, const int hl_id)
|
|||||||
return msg_hl_keep(s, hl_id, false, false);
|
return msg_hl_keep(s, hl_id, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Similar to msg_outtrans, but support newlines and tabs.
|
/// Similar to msg_outtrans_len, but support newlines and tabs.
|
||||||
void msg_multiline(const char *s, int hl_id, bool check_int, bool hist, bool *need_clear)
|
void msg_multiline(String str, int hl_id, bool check_int, bool hist, bool *need_clear)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
const char *next_spec = s;
|
const char *s = str.data;
|
||||||
|
const char *chunk = s;
|
||||||
while (next_spec != NULL) {
|
while ((size_t)(s - str.data) < str.size) {
|
||||||
if (check_int && got_int) {
|
if (check_int && got_int) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
next_spec = strpbrk(s, "\t\n\r");
|
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 != NULL) {
|
if (*s != TAB && *need_clear) {
|
||||||
// Printing all char that are before the char found by strpbrk
|
|
||||||
msg_outtrans_len(s, (int)(next_spec - s), hl_id, hist);
|
|
||||||
|
|
||||||
if (*next_spec != TAB && *need_clear) {
|
|
||||||
msg_clr_eos();
|
msg_clr_eos();
|
||||||
*need_clear = false;
|
*need_clear = false;
|
||||||
}
|
}
|
||||||
msg_putchar_hl((uint8_t)(*next_spec), hl_id);
|
msg_putchar_hl((uint8_t)(*s), hl_id);
|
||||||
s = next_spec + 1;
|
chunk = s + 1;
|
||||||
}
|
}
|
||||||
|
s++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the rest of the message. We know there is no special
|
// Print the rest of the message
|
||||||
// character because strpbrk returned NULL
|
if (*chunk != NUL) {
|
||||||
if (*s != NUL) {
|
msg_outtrans_len(chunk, (int)(str.size - (size_t)(chunk - str.data)), hl_id, hist);
|
||||||
msg_outtrans(s, hl_id, hist);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,7 +288,7 @@ void msg_multihl(HlMessage hl_msg, const char *kind, bool history)
|
|||||||
msg_ext_set_kind(kind);
|
msg_ext_set_kind(kind);
|
||||||
for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
|
for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
|
||||||
HlMessageChunk chunk = kv_A(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)) {
|
if (history && kv_size(hl_msg)) {
|
||||||
add_msg_hist_multihl(NULL, 0, 0, true, 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;
|
bool need_clear = true;
|
||||||
if (multiline) {
|
if (multiline) {
|
||||||
msg_multiline(s, hl_id, false, false, &need_clear);
|
msg_multiline(cstr_as_string(s), hl_id, false, false, &need_clear);
|
||||||
} else {
|
} else {
|
||||||
msg_outtrans(s, hl_id, false);
|
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
|
// primitive way to compute the current column
|
||||||
if (*s == '\r' || *s == '\n') {
|
if (*s == '\r' || *s == '\n') {
|
||||||
msg_col = 0;
|
msg_col = 0;
|
||||||
|
msg_didout = false;
|
||||||
} else {
|
} else {
|
||||||
msg_col += cw;
|
msg_col += cw;
|
||||||
|
msg_didout = true;
|
||||||
}
|
}
|
||||||
s += len;
|
s += len;
|
||||||
}
|
}
|
||||||
msg_didout = true; // assume that line is not empty
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show the more-prompt and handle the user response.
|
/// Show the more-prompt and handle the user response.
|
||||||
|
@ -277,10 +277,8 @@ describe('startup', function()
|
|||||||
|
|
||||||
-- nvim <vim args> -l foo.lua <vim args>
|
-- nvim <vim args> -l foo.lua <vim args>
|
||||||
assert_l_out(
|
assert_l_out(
|
||||||
-- luacheck: ignore 611 (Line contains only whitespaces)
|
|
||||||
[[
|
[[
|
||||||
wrap
|
wrap
|
||||||
|
|
||||||
bufs:
|
bufs:
|
||||||
nvim args: 7
|
nvim args: 7
|
||||||
lua args: { "-c", "set wrap?",
|
lua args: { "-c", "set wrap?",
|
||||||
|
@ -142,7 +142,7 @@ describe('vim.ui_attach', function()
|
|||||||
'msg_history_show',
|
'msg_history_show',
|
||||||
{
|
{
|
||||||
{ 'echomsg', { { 0, 'message1', 0 } } },
|
{ 'echomsg', { { 0, 'message1', 0 } } },
|
||||||
{ '', { { 0, 'message2', 0 } } },
|
{ 'lua_print', { { 0, 'message2', 0 } } },
|
||||||
{ 'echomsg', { { 0, 'message3', 0 } } },
|
{ 'echomsg', { { 0, 'message3', 0 } } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1113,6 +1113,33 @@ stack traceback:
|
|||||||
})
|
})
|
||||||
eq(showmode, 1)
|
eq(showmode, 1)
|
||||||
end)
|
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)
|
end)
|
||||||
|
|
||||||
describe('ui/builtin messages', function()
|
describe('ui/builtin messages', function()
|
||||||
@ -2062,8 +2089,6 @@ aliquip ex ea commodo consequat.]]
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it('can be quit with Lua #11224 #16537', function()
|
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)
|
screen:try_resize(40, 5)
|
||||||
feed(':lua for i=0,10 do print(i) end<cr>')
|
feed(':lua for i=0,10 do print(i) end<cr>')
|
||||||
screen:expect {
|
screen:expect {
|
||||||
@ -2093,13 +2118,13 @@ aliquip ex ea commodo consequat.]]
|
|||||||
{4:-- More --}^ |
|
{4:-- More --}^ |
|
||||||
]],
|
]],
|
||||||
}
|
}
|
||||||
feed('j')
|
feed('G')
|
||||||
screen:expect {
|
screen:expect {
|
||||||
grid = [[
|
grid = [[
|
||||||
1 |
|
7 |
|
||||||
2 |
|
8 |
|
||||||
3 |
|
9 |
|
||||||
4 |
|
10 |
|
||||||
{4:Press ENTER or type command to continue}^ |
|
{4:Press ENTER or type command to continue}^ |
|
||||||
]],
|
]],
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user