mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #9520 from bfredl/errmsg2
Lua: format multiline messages / emsgf_multiline()
This commit is contained in:
commit
6e6bc3b6c0
@ -19593,24 +19593,7 @@ void ex_echo(exarg_T *eap)
|
||||
msg_puts_attr(" ", echo_attr);
|
||||
}
|
||||
char *tofree = encode_tv2echo(&rettv, NULL);
|
||||
const char *p = tofree;
|
||||
if (p != NULL) {
|
||||
for (; *p != NUL && !got_int; ++p) {
|
||||
if (*p == '\n' || *p == '\r' || *p == TAB) {
|
||||
if (*p != TAB && needclr) {
|
||||
/* remove any text still there from the command */
|
||||
msg_clr_eos();
|
||||
needclr = false;
|
||||
}
|
||||
msg_putchar_attr((uint8_t)(*p), echo_attr);
|
||||
} else {
|
||||
int i = (*mb_ptr2len)((const char_u *)p);
|
||||
|
||||
(void)msg_outtrans_len_attr((char_u *)p, i, echo_attr);
|
||||
p += i - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
msg_multiline_attr(tofree, echo_attr);
|
||||
xfree(tofree);
|
||||
}
|
||||
tv_clear(&rettv);
|
||||
|
@ -50,7 +50,7 @@ static void nlua_error(lua_State *const lstate, const char *const msg)
|
||||
size_t len;
|
||||
const char *const str = lua_tolstring(lstate, -1, &len);
|
||||
|
||||
emsgf(msg, (int)len, str);
|
||||
emsgf_multiline(msg, (int)len, str);
|
||||
|
||||
lua_pop(lstate, 1);
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ static int verbose_did_open = FALSE;
|
||||
*/
|
||||
int msg(char_u *s)
|
||||
{
|
||||
return msg_attr_keep(s, 0, FALSE);
|
||||
return msg_attr_keep(s, 0, false, false);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -126,29 +126,62 @@ int verb_msg(char_u *s)
|
||||
int n;
|
||||
|
||||
verbose_enter();
|
||||
n = msg_attr_keep(s, 0, FALSE);
|
||||
n = msg_attr_keep(s, 0, false, false);
|
||||
verbose_leave();
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int msg_attr(const char *s, const int attr) FUNC_ATTR_NONNULL_ARG(1)
|
||||
int msg_attr(const char *s, const int attr)
|
||||
FUNC_ATTR_NONNULL_ARG(1)
|
||||
{
|
||||
return msg_attr_keep((char_u *)s, attr, false);
|
||||
return msg_attr_keep((char_u *)s, attr, false, false);
|
||||
}
|
||||
|
||||
int
|
||||
msg_attr_keep (
|
||||
char_u *s,
|
||||
int attr,
|
||||
int keep /* TRUE: set keep_msg if it doesn't scroll */
|
||||
)
|
||||
FUNC_ATTR_NONNULL_ARG(1)
|
||||
/// similar to msg_outtrans_attr, but support newlines and tabs.
|
||||
void msg_multiline_attr(const char *s, int attr)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const char *next_spec = s;
|
||||
|
||||
while (next_spec != NULL) {
|
||||
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_attr((char_u *)s, next_spec - s, attr);
|
||||
|
||||
if (*next_spec != TAB) {
|
||||
msg_clr_eos();
|
||||
}
|
||||
msg_putchar_attr((uint8_t)(*next_spec), attr);
|
||||
s = next_spec + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Print the rest of the message. We know there is no special
|
||||
// character because strpbrk returned NULL
|
||||
if (*s != NUL) {
|
||||
msg_outtrans_attr((char_u *)s, attr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @param keep set keep_msg if it doesn't scroll
|
||||
bool msg_attr_keep(char_u *s, int attr, bool keep, bool multiline)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
static int entered = 0;
|
||||
int retval;
|
||||
char_u *buf = NULL;
|
||||
|
||||
if (keep && multiline) {
|
||||
// Not implemented. 'multiline' is only used by nvim-added messages,
|
||||
// which should avoid 'keep' behavior (just show the message at
|
||||
// the correct time already).
|
||||
abort();
|
||||
}
|
||||
|
||||
// Skip messages not match ":filter pattern".
|
||||
// Don't filter when there is an error.
|
||||
if (!emsg_on_display && message_filtered(s)) {
|
||||
@ -175,7 +208,7 @@ msg_attr_keep (
|
||||
&& last_msg_hist != NULL
|
||||
&& last_msg_hist->msg != NULL
|
||||
&& STRCMP(s, last_msg_hist->msg))) {
|
||||
add_msg_hist((const char *)s, -1, attr);
|
||||
add_msg_hist((const char *)s, -1, attr, multiline);
|
||||
}
|
||||
|
||||
/* When displaying keep_msg, don't let msg_start() free it, caller must do
|
||||
@ -189,13 +222,18 @@ msg_attr_keep (
|
||||
if (buf != NULL)
|
||||
s = buf;
|
||||
|
||||
msg_outtrans_attr(s, attr);
|
||||
if (multiline) {
|
||||
msg_multiline_attr((char *)s, attr);
|
||||
} else {
|
||||
msg_outtrans_attr(s, attr);
|
||||
}
|
||||
msg_clr_eos();
|
||||
retval = msg_end();
|
||||
|
||||
if (keep && retval && vim_strsize(s) < (int)(Rows - cmdline_row - 1)
|
||||
* Columns + sc_col)
|
||||
* Columns + sc_col) {
|
||||
set_keep_msg(s, 0);
|
||||
}
|
||||
|
||||
xfree(buf);
|
||||
--entered;
|
||||
@ -468,17 +506,8 @@ int emsg_not_now(void)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* emsg() - display an error message
|
||||
*
|
||||
* Rings the bell, if appropriate, and calls message() to do the real work
|
||||
* When terminal not initialized (yet) mch_errmsg(..) is used.
|
||||
*
|
||||
* return TRUE if wait_return not called
|
||||
*/
|
||||
int emsg(const char_u *s_)
|
||||
static bool emsg_multiline(const char *s, bool multiline)
|
||||
{
|
||||
const char *s = (const char *)s_;
|
||||
int attr;
|
||||
int ignore = false;
|
||||
int severe;
|
||||
@ -573,7 +602,18 @@ int emsg(const char_u *s_)
|
||||
|
||||
// Display the error message itself.
|
||||
msg_nowait = false; // Wait for this msg.
|
||||
return msg_attr(s, attr);
|
||||
return msg_attr_keep((char_u *)s, attr, false, multiline);
|
||||
}
|
||||
|
||||
/// emsg() - display an error message
|
||||
///
|
||||
/// Rings the bell, if appropriate, and calls message() to do the real work
|
||||
/// When terminal not initialized (yet) mch_errmsg(..) is used.
|
||||
///
|
||||
/// @return true if wait_return not called
|
||||
bool emsg(const char_u *s)
|
||||
{
|
||||
return emsg_multiline((const char *)s, false);
|
||||
}
|
||||
|
||||
void emsg_invreg(int name)
|
||||
@ -595,6 +635,28 @@ bool emsgf(const char *const fmt, ...)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define MULTILINE_BUFSIZE 8192
|
||||
|
||||
bool emsgf_multiline(const char *const fmt, ...)
|
||||
{
|
||||
bool ret;
|
||||
va_list ap;
|
||||
|
||||
|
||||
static char errbuf[MULTILINE_BUFSIZE];
|
||||
if (emsg_not_now()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap, NULL);
|
||||
va_end(ap);
|
||||
|
||||
ret = emsg_multiline(errbuf, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Print an error message with unknown number of arguments
|
||||
static bool emsgfv(const char *fmt, va_list ap)
|
||||
{
|
||||
@ -669,7 +731,7 @@ char_u *msg_trunc_attr(char_u *s, int force, int attr)
|
||||
int n;
|
||||
|
||||
// Add message to history before truncating.
|
||||
add_msg_hist((const char *)s, -1, attr);
|
||||
add_msg_hist((const char *)s, -1, attr, false);
|
||||
|
||||
s = msg_may_trunc(force, s);
|
||||
|
||||
@ -715,7 +777,7 @@ char_u *msg_may_trunc(int force, char_u *s)
|
||||
}
|
||||
|
||||
/// @param[in] len Length of s or -1.
|
||||
static void add_msg_hist(const char *s, int len, int attr)
|
||||
static void add_msg_hist(const char *s, int len, int attr, bool multiline)
|
||||
{
|
||||
if (msg_hist_off || msg_silent != 0)
|
||||
return;
|
||||
@ -739,12 +801,15 @@ static void add_msg_hist(const char *s, int len, int attr)
|
||||
p->msg = (char_u *)xmemdupz(s, (size_t)len);
|
||||
p->next = NULL;
|
||||
p->attr = attr;
|
||||
if (last_msg_hist != NULL)
|
||||
p->multiline = multiline;
|
||||
if (last_msg_hist != NULL) {
|
||||
last_msg_hist->next = p;
|
||||
}
|
||||
last_msg_hist = p;
|
||||
if (first_msg_hist == NULL)
|
||||
if (first_msg_hist == NULL) {
|
||||
first_msg_hist = last_msg_hist;
|
||||
++msg_hist_len;
|
||||
}
|
||||
msg_hist_len++;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -811,7 +876,7 @@ void ex_messages(void *const eap_p)
|
||||
// Display what was not skipped.
|
||||
for (; p != NULL && !got_int; p = p->next) {
|
||||
if (p->msg != NULL) {
|
||||
msg_attr((const char *)p->msg, p->attr);
|
||||
msg_attr_keep(p->msg, p->attr, false, p->multiline);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1205,7 +1270,7 @@ int msg_outtrans_len_attr(char_u *msgstr, int len, int attr)
|
||||
|
||||
/* if MSG_HIST flag set, add message to history */
|
||||
if (attr & MSG_HIST) {
|
||||
add_msg_hist(str, len, attr);
|
||||
add_msg_hist(str, len, attr, false);
|
||||
attr &= ~MSG_HIST;
|
||||
}
|
||||
|
||||
@ -1643,7 +1708,7 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr)
|
||||
|
||||
// if MSG_HIST flag set, add message to history
|
||||
if (attr & MSG_HIST) {
|
||||
add_msg_hist(str, (int)len, attr);
|
||||
add_msg_hist(str, (int)len, attr, false);
|
||||
attr &= ~MSG_HIST;
|
||||
}
|
||||
|
||||
|
@ -78,6 +78,7 @@ typedef struct msg_hist {
|
||||
struct msg_hist *next; ///< Next message.
|
||||
char_u *msg; ///< Message text.
|
||||
int attr; ///< Message highlighting.
|
||||
bool multiline; ///< Multiline message.
|
||||
} MessageHistoryEntry;
|
||||
|
||||
/// First message
|
||||
|
@ -2155,7 +2155,7 @@ win_found:
|
||||
} else if (!msg_scrolled && shortmess(SHM_OVERALL)) {
|
||||
msg_scroll = false;
|
||||
}
|
||||
msg_attr_keep(IObuff, 0, true);
|
||||
msg_attr_keep(IObuff, 0, true, false);
|
||||
msg_scroll = (int)i;
|
||||
}
|
||||
} else {
|
||||
|
@ -318,6 +318,10 @@ describe('API', function()
|
||||
eq({false, 'Error executing lua: [string "<nvim>"]:1: '..
|
||||
"attempt to call global 'bork' (a nil value)"},
|
||||
meth_pcall(meths.execute_lua, 'bork()', {}))
|
||||
|
||||
eq({false, 'Error executing lua: [string "<nvim>"]:1: '..
|
||||
"did\nthe\nfail"},
|
||||
meth_pcall(meths.execute_lua, 'error("did\\nthe\\nfail")', {}))
|
||||
end)
|
||||
end)
|
||||
|
||||
|
@ -1,13 +1,17 @@
|
||||
-- Test suite for checking :lua* commands
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
|
||||
local eq = helpers.eq
|
||||
local NIL = helpers.NIL
|
||||
local eval = helpers.eval
|
||||
local feed = helpers.feed
|
||||
local clear = helpers.clear
|
||||
local meths = helpers.meths
|
||||
local funcs = helpers.funcs
|
||||
local source = helpers.source
|
||||
local dedent = helpers.dedent
|
||||
local command = helpers.command
|
||||
local exc_exec = helpers.exc_exec
|
||||
local write_file = helpers.write_file
|
||||
local redir_exec = helpers.redir_exec
|
||||
@ -76,6 +80,64 @@ describe(':lua command', function()
|
||||
eq('', redir_exec(('lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"%s"})'):format(s)))
|
||||
eq({'', s}, curbufmeths.get_lines(0, -1, false))
|
||||
end)
|
||||
|
||||
it('can show multiline error messages', function()
|
||||
local screen = Screen.new(50,10)
|
||||
screen:attach()
|
||||
screen:set_default_attr_ids({
|
||||
[1] = {bold = true, foreground = Screen.colors.Blue1},
|
||||
[2] = {bold = true, reverse = true},
|
||||
[3] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
|
||||
[4] = {bold = true, foreground = Screen.colors.SeaGreen4},
|
||||
})
|
||||
|
||||
feed(':lua error("fail\\nmuch error\\nsuch details")<cr>')
|
||||
screen:expect([[
|
||||
|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2: }|
|
||||
{3:E5105: Error while calling lua chunk: [string "<Vi}|
|
||||
{3:mL compiled string>"]:1: fail} |
|
||||
{3:much error} |
|
||||
{3:such details} |
|
||||
{4:Press ENTER or type command to continue}^ |
|
||||
]])
|
||||
feed('<cr>')
|
||||
screen:expect([[
|
||||
^ |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
eq('E5105: Error while calling lua chunk: [string "<VimL compiled string>"]:1: fail\nmuch error\nsuch details', eval('v:errmsg'))
|
||||
|
||||
local status, err = pcall(command,'lua error("some error\\nin a\\nAPI command")')
|
||||
local expected = 'Vim(lua):E5105: Error while calling lua chunk: [string "<VimL compiled string>"]:1: some error\nin a\nAPI command'
|
||||
eq(false, status)
|
||||
eq(expected, string.sub(err, -string.len(expected)))
|
||||
|
||||
feed(':messages<cr>')
|
||||
screen:expect([[
|
||||
|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2: }|
|
||||
{3:E5105: Error while calling lua chunk: [string "<Vi}|
|
||||
{3:mL compiled string>"]:1: fail} |
|
||||
{3:much error} |
|
||||
{3:such details} |
|
||||
{4:Press ENTER or type command to continue}^ |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(':luado command', function()
|
||||
|
Loading…
Reference in New Issue
Block a user