mirror of
https://github.com/neovim/neovim.git
synced 2025-09-13 14:58:18 +00:00
ui: implement ext_messages
Co-Author: Dongdong Zhou <dzhou121@gmail.com>
This commit is contained in:
@@ -32,6 +32,7 @@ a dictionary with these (optional) keys:
|
|||||||
`ext_tabline` Externalize the tabline. |ui-tabline|
|
`ext_tabline` Externalize the tabline. |ui-tabline|
|
||||||
`ext_cmdline` Externalize the cmdline. |ui-cmdline|
|
`ext_cmdline` Externalize the cmdline. |ui-cmdline|
|
||||||
`ext_wildmenu` Externalize the wildmenu. |ui-wildmenu|
|
`ext_wildmenu` Externalize the wildmenu. |ui-wildmenu|
|
||||||
|
`ext_messages` Externalize messages. |ui-messages|
|
||||||
`ext_linegrid` Use new revision of the grid events. |ui-linegrid|
|
`ext_linegrid` Use new revision of the grid events. |ui-linegrid|
|
||||||
`ext_multigrid` Use per-window grid based events. |ui-multigrid|
|
`ext_multigrid` Use per-window grid based events. |ui-multigrid|
|
||||||
`ext_hlstate` Use detailed highlight state. |ui-hlstate|
|
`ext_hlstate` Use detailed highlight state. |ui-hlstate|
|
||||||
@@ -572,7 +573,7 @@ Only sent if `ext_tabline` option is set in |ui-options|
|
|||||||
==============================================================================
|
==============================================================================
|
||||||
Cmdline Events *ui-cmdline*
|
Cmdline Events *ui-cmdline*
|
||||||
|
|
||||||
Only sent if `ext_cmdline` option is set in |ui-options|
|
Only sent if `ext_cmdline` option is set in |ui-options|.
|
||||||
|
|
||||||
["cmdline_show", content, pos, firstc, prompt, indent, level]
|
["cmdline_show", content, pos, firstc, prompt, indent, level]
|
||||||
content: List of [attrs, string]
|
content: List of [attrs, string]
|
||||||
@@ -644,5 +645,70 @@ Only sent if `ext_wildmenu` option is set in |ui-options|
|
|||||||
["wildmenu_hide"]
|
["wildmenu_hide"]
|
||||||
Hide the wildmenu.
|
Hide the wildmenu.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
Message Events *ui-messages*
|
||||||
|
|
||||||
|
Only sent if `ext_messages` option is set in |ui-options|. This option implies
|
||||||
|
`ext_linegrid` and `ext_cmdline` also being set. |ui-linegrid| and |ui-cmdline| events
|
||||||
|
will thus also be sent.
|
||||||
|
|
||||||
|
This extension allows the UI to control the display of messages that otherwise
|
||||||
|
would have been displayed in the message/cmdline area in the bottom of the
|
||||||
|
screen.
|
||||||
|
|
||||||
|
Activating this extension means that Nvim will allocate no screen space for
|
||||||
|
the cmdline or messages, and 'cmdheight' will be set to zero. Attempting to
|
||||||
|
change 'cmdheight' will silently be ignored. |ui-cmdline| events will be used
|
||||||
|
to represent the state of the cmdline.
|
||||||
|
|
||||||
|
["msg_show", kind, content, replace_last]
|
||||||
|
Display a message to the user.
|
||||||
|
|
||||||
|
`kind` will be one of the following
|
||||||
|
`emsg`: Internal error message
|
||||||
|
`echo`: temporary message from plugin (|:echo|)
|
||||||
|
`echomsg`: ordinary message from plugin (|:echomsg|)
|
||||||
|
`echoerr`: error message from plugin (|:echoerr|)
|
||||||
|
`return_prompt`: |press-enter| prompt after a group of messages
|
||||||
|
`quickfix`: Quickfix navigation message
|
||||||
|
`kind` can also be the empty string. The message is then some internal
|
||||||
|
informative or warning message, that hasn't yet been assigned a kind.
|
||||||
|
New message kinds can be added in later versions; clients should
|
||||||
|
handle messages of an unknown kind just like empty kind.
|
||||||
|
|
||||||
|
`content` will be an array of `[attr_id, text_chunk]` tuples,
|
||||||
|
building up the message text of chunks of different highlights.
|
||||||
|
No extra spacing should be added between chunks, the `text_chunk` by
|
||||||
|
itself should contain any necessary whitespace. Messages can contain
|
||||||
|
line breaks `"\n"`.
|
||||||
|
|
||||||
|
`replace_last` controls how multiple messages should be displayed.
|
||||||
|
If `replace_last` is false, this message should be displayed together
|
||||||
|
with all previous messages that are still visible. If `replace_last`
|
||||||
|
is true, this message should replace the message in the most recent
|
||||||
|
`msg_show` call, but any other visible message should still remain.
|
||||||
|
|
||||||
|
["msg_clear"]
|
||||||
|
Clear all messages currently displayed by "msg_show". (Messages sent
|
||||||
|
by other "msg_" events below will not be affected).
|
||||||
|
|
||||||
|
["msg_showmode", content]
|
||||||
|
Shows 'showmode' and |recording| messages. `content` has the same
|
||||||
|
format as in "msg_show". This event is sent with empty `content` to
|
||||||
|
hide the last message.
|
||||||
|
|
||||||
|
["msg_showcmd", content]
|
||||||
|
Shows 'showcmd' messages. `content` has the same format as in "msg_show".
|
||||||
|
This event is sent with empty `content` to hide the last message.
|
||||||
|
|
||||||
|
["msg_ruler", content]
|
||||||
|
Used to display 'ruler' when there is no space for the ruler in a
|
||||||
|
statusline. `content` has the same format as in "msg_show". This event is
|
||||||
|
sent with empty `content` to hide the last message.
|
||||||
|
|
||||||
|
["msg_history_show", entries]
|
||||||
|
Sent when |:messages| command is invoked. History is sent as a list of
|
||||||
|
entries, where each entry is a `[kind, content]` tuple.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
vim:tw=78:ts=8:noet:ft=help:norl:
|
||||||
|
@@ -712,6 +712,12 @@ String cbuf_to_string(const char *buf, size_t size)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String cstrn_to_string(const char *str, size_t maxsize)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
return cbuf_to_string(str, strnlen(str, maxsize));
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a String using the given C string. Unlike
|
/// Creates a String using the given C string. Unlike
|
||||||
/// cstr_to_string this function DOES NOT copy the C string.
|
/// cstr_to_string this function DOES NOT copy the C string.
|
||||||
///
|
///
|
||||||
@@ -726,6 +732,18 @@ String cstr_as_string(char *str) FUNC_ATTR_PURE
|
|||||||
return (String){ .data = str, .size = strlen(str) };
|
return (String){ .data = str, .size = strlen(str) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the owned memory of a ga as a String
|
||||||
|
///
|
||||||
|
/// Reinitializes the ga to a valid empty state.
|
||||||
|
String ga_take_string(garray_T *ga)
|
||||||
|
{
|
||||||
|
String str = { .data = (char *)ga->ga_data, .size = (size_t)ga->ga_len };
|
||||||
|
ga->ga_data = NULL;
|
||||||
|
ga->ga_len = 0;
|
||||||
|
ga->ga_maxlen = 0;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
/// Collects `n` buffer lines into array `l`, optionally replacing newlines
|
/// Collects `n` buffer lines into array `l`, optionally replacing newlines
|
||||||
/// with NUL.
|
/// with NUL.
|
||||||
///
|
///
|
||||||
|
@@ -132,6 +132,13 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
|
|||||||
ui->ui_ext[kUILinegrid] = true;
|
ui->ui_ext[kUILinegrid] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ui->ui_ext[kUIMessages]) {
|
||||||
|
// This uses attribute indicies, so ext_linegrid is needed.
|
||||||
|
ui->ui_ext[kUILinegrid] = true;
|
||||||
|
// Cmdline uses the messages area, so it should be externalized too.
|
||||||
|
ui->ui_ext[kUICmdline] = true;
|
||||||
|
}
|
||||||
|
|
||||||
UIData *data = xmalloc(sizeof(UIData));
|
UIData *data = xmalloc(sizeof(UIData));
|
||||||
data->channel_id = channel_id;
|
data->channel_id = channel_id;
|
||||||
data->buffer = (Array)ARRAY_DICT_INIT;
|
data->buffer = (Array)ARRAY_DICT_INIT;
|
||||||
|
@@ -141,4 +141,17 @@ void wildmenu_select(Integer selected)
|
|||||||
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
|
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
|
||||||
void wildmenu_hide(void)
|
void wildmenu_hide(void)
|
||||||
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
|
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
|
||||||
|
|
||||||
|
void msg_show(String kind, Array content, Boolean replace_last)
|
||||||
|
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
||||||
|
void msg_clear(void)
|
||||||
|
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
||||||
|
void msg_showcmd(Array content)
|
||||||
|
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
||||||
|
void msg_showmode(Array content)
|
||||||
|
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
||||||
|
void msg_ruler(Array content)
|
||||||
|
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
||||||
|
void msg_history_show(Array entries)
|
||||||
|
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
||||||
#endif // NVIM_API_UI_EVENTS_IN_H
|
#endif // NVIM_API_UI_EVENTS_IN_H
|
||||||
|
@@ -19597,7 +19597,10 @@ void ex_echo(exarg_T *eap)
|
|||||||
msg_puts_attr(" ", echo_attr);
|
msg_puts_attr(" ", echo_attr);
|
||||||
}
|
}
|
||||||
char *tofree = encode_tv2echo(&rettv, NULL);
|
char *tofree = encode_tv2echo(&rettv, NULL);
|
||||||
|
if (*tofree != NUL) {
|
||||||
|
msg_ext_set_kind("echo");
|
||||||
msg_multiline_attr(tofree, echo_attr);
|
msg_multiline_attr(tofree, echo_attr);
|
||||||
|
}
|
||||||
xfree(tofree);
|
xfree(tofree);
|
||||||
}
|
}
|
||||||
tv_clear(&rettv);
|
tv_clear(&rettv);
|
||||||
@@ -19689,11 +19692,13 @@ void ex_execute(exarg_T *eap)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (eap->cmdidx == CMD_echomsg) {
|
if (eap->cmdidx == CMD_echomsg) {
|
||||||
|
msg_ext_set_kind("echomsg");
|
||||||
MSG_ATTR(ga.ga_data, echo_attr);
|
MSG_ATTR(ga.ga_data, echo_attr);
|
||||||
ui_flush();
|
ui_flush();
|
||||||
} else if (eap->cmdidx == CMD_echoerr) {
|
} else if (eap->cmdidx == CMD_echoerr) {
|
||||||
/* We don't want to abort following commands, restore did_emsg. */
|
/* We don't want to abort following commands, restore did_emsg. */
|
||||||
save_did_emsg = did_emsg;
|
save_did_emsg = did_emsg;
|
||||||
|
msg_ext_set_kind("echoerr");
|
||||||
EMSG((char_u *)ga.ga_data);
|
EMSG((char_u *)ga.ga_data);
|
||||||
if (!force_abort)
|
if (!force_abort)
|
||||||
did_emsg = save_did_emsg;
|
did_emsg = save_did_emsg;
|
||||||
|
@@ -308,6 +308,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
|
|||||||
gotocmdline(true);
|
gotocmdline(true);
|
||||||
redrawcmdprompt(); // draw prompt or indent
|
redrawcmdprompt(); // draw prompt or indent
|
||||||
set_cmdspos();
|
set_cmdspos();
|
||||||
|
if (!msg_scroll) {
|
||||||
|
msg_ext_clear(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s->xpc.xp_context = EXPAND_NOTHING;
|
s->xpc.xp_context = EXPAND_NOTHING;
|
||||||
s->xpc.xp_backslash = XP_BS_NONE;
|
s->xpc.xp_backslash = XP_BS_NONE;
|
||||||
@@ -496,6 +499,12 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
|
|||||||
|
|
||||||
if (ui_has(kUICmdline)) {
|
if (ui_has(kUICmdline)) {
|
||||||
ui_call_cmdline_hide(ccline.level);
|
ui_call_cmdline_hide(ccline.level);
|
||||||
|
if (msg_ext_is_visible()) {
|
||||||
|
msg_ext_did_cmdline = true;
|
||||||
|
if (must_redraw < VALID) {
|
||||||
|
must_redraw = VALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdline_level--;
|
cmdline_level--;
|
||||||
@@ -3613,7 +3622,7 @@ nextwild (
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ui_has(kUIWildmenu)) {
|
if (!(ui_has(kUICmdline) || ui_has(kUIWildmenu))) {
|
||||||
MSG_PUTS("..."); // show that we are busy
|
MSG_PUTS("..."); // show that we are busy
|
||||||
ui_flush();
|
ui_flush();
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,7 @@ typedef struct growarray {
|
|||||||
} garray_T;
|
} garray_T;
|
||||||
|
|
||||||
#define GA_EMPTY_INIT_VALUE { 0, 0, 0, 1, NULL }
|
#define GA_EMPTY_INIT_VALUE { 0, 0, 0, 1, NULL }
|
||||||
|
#define GA_INIT(itemsize, growsize) { 0, 0, (itemsize), (growsize), NULL }
|
||||||
|
|
||||||
#define GA_EMPTY(ga_ptr) ((ga_ptr)->ga_len <= 0)
|
#define GA_EMPTY(ga_ptr) ((ga_ptr)->ga_len <= 0)
|
||||||
|
|
||||||
|
@@ -34,11 +34,13 @@
|
|||||||
#include "nvim/regexp.h"
|
#include "nvim/regexp.h"
|
||||||
#include "nvim/screen.h"
|
#include "nvim/screen.h"
|
||||||
#include "nvim/strings.h"
|
#include "nvim/strings.h"
|
||||||
|
#include "nvim/syntax.h"
|
||||||
#include "nvim/ui.h"
|
#include "nvim/ui.h"
|
||||||
#include "nvim/mouse.h"
|
#include "nvim/mouse.h"
|
||||||
#include "nvim/os/os.h"
|
#include "nvim/os/os.h"
|
||||||
#include "nvim/os/input.h"
|
#include "nvim/os/input.h"
|
||||||
#include "nvim/os/time.h"
|
#include "nvim/os/time.h"
|
||||||
|
#include "nvim/api/private/helpers.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To be able to scroll back at the "more" and "hit-enter" prompts we need to
|
* To be able to scroll back at the "more" and "hit-enter" prompts we need to
|
||||||
@@ -108,6 +110,19 @@ static int verbose_did_open = FALSE;
|
|||||||
* This is an allocated string or NULL when not used.
|
* This is an allocated string or NULL when not used.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Extended msg state, currently used for external UIs with ext_messages
|
||||||
|
static const char *msg_ext_kind = NULL;
|
||||||
|
static Array msg_ext_chunks = ARRAY_DICT_INIT;
|
||||||
|
static garray_T msg_ext_last_chunk = GA_INIT(sizeof(char), 40);
|
||||||
|
static sattr_T msg_ext_last_attr = -1;
|
||||||
|
|
||||||
|
static bool msg_ext_overwrite = false; ///< will overwrite last message
|
||||||
|
static int msg_ext_visible = 0; ///< number of messages currently visible
|
||||||
|
|
||||||
|
/// Shouldn't clear message after leaving cmdline
|
||||||
|
static bool msg_ext_keep_after_cmdline = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* msg(s) - displays the string 's' on the status line
|
* msg(s) - displays the string 's' on the status line
|
||||||
* When terminal not initialized (yet) mch_errmsg(..) is used.
|
* When terminal not initialized (yet) mch_errmsg(..) is used.
|
||||||
@@ -256,7 +271,8 @@ msg_strtrunc (
|
|||||||
|
|
||||||
/* May truncate message to avoid a hit-return prompt */
|
/* May truncate message to avoid a hit-return prompt */
|
||||||
if ((!msg_scroll && !need_wait_return && shortmess(SHM_TRUNCALL)
|
if ((!msg_scroll && !need_wait_return && shortmess(SHM_TRUNCALL)
|
||||||
&& !exmode_active && msg_silent == 0) || force) {
|
&& !exmode_active && msg_silent == 0 && !ui_has(kUIMessages))
|
||||||
|
|| force) {
|
||||||
len = vim_strsize(s);
|
len = vim_strsize(s);
|
||||||
if (msg_scrolled != 0)
|
if (msg_scrolled != 0)
|
||||||
/* Use all the columns. */
|
/* Use all the columns. */
|
||||||
@@ -594,6 +610,9 @@ static bool emsg_multiline(const char *s, bool multiline)
|
|||||||
} // wait_return has reset need_wait_return
|
} // wait_return has reset need_wait_return
|
||||||
// and a redraw is expected because
|
// and a redraw is expected because
|
||||||
// msg_scrolled is non-zero
|
// msg_scrolled is non-zero
|
||||||
|
if (msg_ext_kind == NULL) {
|
||||||
|
msg_ext_set_kind("emsg");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Display name and line number for the source of the error.
|
* Display name and line number for the source of the error.
|
||||||
@@ -802,6 +821,7 @@ static void add_msg_hist(const char *s, int len, int attr, bool multiline)
|
|||||||
p->next = NULL;
|
p->next = NULL;
|
||||||
p->attr = attr;
|
p->attr = attr;
|
||||||
p->multiline = multiline;
|
p->multiline = multiline;
|
||||||
|
p->kind = msg_ext_kind;
|
||||||
if (last_msg_hist != NULL) {
|
if (last_msg_hist != NULL) {
|
||||||
last_msg_hist->next = p;
|
last_msg_hist->next = p;
|
||||||
}
|
}
|
||||||
@@ -856,7 +876,6 @@ void ex_messages(void *const eap_p)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
msg_hist_off = true;
|
|
||||||
|
|
||||||
p = first_msg_hist;
|
p = first_msg_hist;
|
||||||
|
|
||||||
@@ -874,13 +893,31 @@ void ex_messages(void *const eap_p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Display what was not skipped.
|
// Display what was not skipped.
|
||||||
|
if (ui_has(kUIMessages)) {
|
||||||
|
Array entries = ARRAY_DICT_INIT;
|
||||||
|
for (; p != NULL; p = p->next) {
|
||||||
|
if (p->msg != NULL && p->msg[0] != NUL) {
|
||||||
|
Array entry = ARRAY_DICT_INIT;
|
||||||
|
ADD(entry, STRING_OBJ(cstr_to_string(p->kind)));
|
||||||
|
Array content_entry = ARRAY_DICT_INIT;
|
||||||
|
ADD(content_entry, INTEGER_OBJ(p->attr));
|
||||||
|
ADD(content_entry, STRING_OBJ(cstr_to_string((char *)(p->msg))));
|
||||||
|
Array content = ARRAY_DICT_INIT;
|
||||||
|
ADD(content, ARRAY_OBJ(content_entry));
|
||||||
|
ADD(entry, ARRAY_OBJ(content));
|
||||||
|
ADD(entries, ARRAY_OBJ(entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui_call_msg_history_show(entries);
|
||||||
|
} else {
|
||||||
|
msg_hist_off = true;
|
||||||
for (; p != NULL && !got_int; p = p->next) {
|
for (; p != NULL && !got_int; p = p->next) {
|
||||||
if (p->msg != NULL) {
|
if (p->msg != NULL) {
|
||||||
msg_attr_keep(p->msg, p->attr, false, p->multiline);
|
msg_attr_keep(p->msg, p->attr, false, p->multiline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
msg_hist_off = false;
|
msg_hist_off = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1058,8 +1095,9 @@ void wait_return(int redraw)
|
|||||||
if (c == ':' || c == '?' || c == '/') {
|
if (c == ':' || c == '?' || c == '/') {
|
||||||
if (!exmode_active)
|
if (!exmode_active)
|
||||||
cmdline_row = msg_row;
|
cmdline_row = msg_row;
|
||||||
skip_redraw = TRUE; /* skip redraw once */
|
skip_redraw = true; // skip redraw once
|
||||||
do_redraw = FALSE;
|
do_redraw = false;
|
||||||
|
msg_ext_keep_after_cmdline = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1084,10 +1122,14 @@ void wait_return(int redraw)
|
|||||||
|
|
||||||
if (tmpState == SETWSIZE) { /* got resize event while in vgetc() */
|
if (tmpState == SETWSIZE) { /* got resize event while in vgetc() */
|
||||||
ui_refresh();
|
ui_refresh();
|
||||||
} else if (!skip_redraw
|
} else if (!skip_redraw) {
|
||||||
&& (redraw == TRUE || (msg_scrolled != 0 && redraw != -1))) {
|
if (redraw == true || (msg_scrolled != 0 && redraw != -1)) {
|
||||||
redraw_later(VALID);
|
redraw_later(VALID);
|
||||||
}
|
}
|
||||||
|
if (ui_has(kUIMessages)) {
|
||||||
|
msg_ext_clear(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1100,8 +1142,10 @@ static void hit_return_msg(void)
|
|||||||
p_more = FALSE; /* don't want see this message when scrolling back */
|
p_more = FALSE; /* don't want see this message when scrolling back */
|
||||||
if (msg_didout) /* start on a new line */
|
if (msg_didout) /* start on a new line */
|
||||||
msg_putchar('\n');
|
msg_putchar('\n');
|
||||||
if (got_int)
|
msg_ext_set_kind("return_prompt");
|
||||||
|
if (got_int) {
|
||||||
MSG_PUTS(_("Interrupt: "));
|
MSG_PUTS(_("Interrupt: "));
|
||||||
|
}
|
||||||
|
|
||||||
MSG_PUTS_ATTR(_("Press ENTER or type command to continue"), HL_ATTR(HLF_R));
|
MSG_PUTS_ATTR(_("Press ENTER or type command to continue"), HL_ATTR(HLF_R));
|
||||||
if (!msg_use_printf()) {
|
if (!msg_use_printf()) {
|
||||||
@@ -1124,6 +1168,17 @@ void set_keep_msg(char_u *s, int attr)
|
|||||||
keep_msg_attr = attr;
|
keep_msg_attr = attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void msg_ext_set_kind(const char *msg_kind)
|
||||||
|
{
|
||||||
|
// Don't change the label of an existing batch:
|
||||||
|
msg_ext_ui_flush();
|
||||||
|
|
||||||
|
// TODO(bfredl): would be nice to avoid dynamic scoping, but that would
|
||||||
|
// need refactoring the msg_ interface to not be "please pretend nvim is
|
||||||
|
// a terminal for a moment"
|
||||||
|
msg_ext_kind = msg_kind;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prepare for outputting characters in the command line.
|
* Prepare for outputting characters in the command line.
|
||||||
*/
|
*/
|
||||||
@@ -1160,6 +1215,14 @@ void msg_start(void)
|
|||||||
msg_didout = FALSE; /* no output on current line yet */
|
msg_didout = FALSE; /* no output on current line yet */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ui_has(kUIMessages)) {
|
||||||
|
msg_ext_ui_flush();
|
||||||
|
if (!msg_scroll && msg_ext_visible) {
|
||||||
|
// Will overwrite last message.
|
||||||
|
msg_ext_overwrite = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// When redirecting, may need to start a new line.
|
// When redirecting, may need to start a new line.
|
||||||
if (!did_return) {
|
if (!did_return) {
|
||||||
redir_write("\n", 1);
|
redir_write("\n", 1);
|
||||||
@@ -1727,7 +1790,18 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr)
|
|||||||
// wait-return prompt later. Needed when scrolling, resetting
|
// wait-return prompt later. Needed when scrolling, resetting
|
||||||
// need_wait_return after some prompt, and then outputting something
|
// need_wait_return after some prompt, and then outputting something
|
||||||
// without scrolling
|
// without scrolling
|
||||||
if (msg_scrolled != 0 && !msg_scrolled_ign) {
|
bool overflow = false;
|
||||||
|
if (ui_has(kUIMessages)) {
|
||||||
|
int count = msg_ext_visible + (msg_ext_overwrite ? 0 : 1);
|
||||||
|
// TODO(bfredl): possible extension point, let external UI control this
|
||||||
|
if (count > 1) {
|
||||||
|
overflow = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
overflow = msg_scrolled != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overflow && !msg_scrolled_ign) {
|
||||||
need_wait_return = true;
|
need_wait_return = true;
|
||||||
}
|
}
|
||||||
msg_didany = true; // remember that something was outputted
|
msg_didany = true; // remember that something was outputted
|
||||||
@@ -1765,6 +1839,20 @@ void msg_printf_attr(const int attr, const char *const fmt, ...)
|
|||||||
msg_puts_attr_len(msgbuf, (ptrdiff_t)len, attr);
|
msg_puts_attr_len(msgbuf, (ptrdiff_t)len, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void msg_ext_emit_chunk(void)
|
||||||
|
{
|
||||||
|
// Color was changed or a message flushed, end current chunk.
|
||||||
|
if (msg_ext_last_attr == -1) {
|
||||||
|
return; // no chunk
|
||||||
|
}
|
||||||
|
Array chunk = ARRAY_DICT_INIT;
|
||||||
|
ADD(chunk, INTEGER_OBJ(msg_ext_last_attr));
|
||||||
|
msg_ext_last_attr = -1;
|
||||||
|
String text = ga_take_string(&msg_ext_last_chunk);
|
||||||
|
ADD(chunk, STRING_OBJ(text));
|
||||||
|
ADD(msg_ext_chunks, ARRAY_OBJ(chunk));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The display part of msg_puts_attr_len().
|
* The display part of msg_puts_attr_len().
|
||||||
* May be called recursively to display scroll-back text.
|
* May be called recursively to display scroll-back text.
|
||||||
@@ -1783,6 +1871,18 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr,
|
|||||||
int did_last_char;
|
int did_last_char;
|
||||||
|
|
||||||
did_wait_return = false;
|
did_wait_return = false;
|
||||||
|
|
||||||
|
if (ui_has(kUIMessages)) {
|
||||||
|
if (attr != msg_ext_last_attr) {
|
||||||
|
msg_ext_emit_chunk();
|
||||||
|
msg_ext_last_attr = attr;
|
||||||
|
}
|
||||||
|
// Concat pieces with the same highlight
|
||||||
|
ga_concat_len(&msg_ext_last_chunk, (char *)str,
|
||||||
|
strnlen((char *)str, maxlen));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while ((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL) {
|
while ((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL) {
|
||||||
// We are at the end of the screen line when:
|
// We are at the end of the screen line when:
|
||||||
// - When outputting a newline.
|
// - When outputting a newline.
|
||||||
@@ -2607,6 +2707,9 @@ void msg_clr_eos(void)
|
|||||||
*/
|
*/
|
||||||
void msg_clr_eos_force(void)
|
void msg_clr_eos_force(void)
|
||||||
{
|
{
|
||||||
|
if (ui_has(kUIMessages)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
int msg_startcol = (cmdmsg_rl) ? 0 : msg_col;
|
int msg_startcol = (cmdmsg_rl) ? 0 : msg_col;
|
||||||
int msg_endcol = (cmdmsg_rl) ? msg_col + 1 : (int)Columns;
|
int msg_endcol = (cmdmsg_rl) ? msg_col + 1 : (int)Columns;
|
||||||
|
|
||||||
@@ -2643,8 +2746,66 @@ int msg_end(void)
|
|||||||
wait_return(FALSE);
|
wait_return(FALSE);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
ui_flush();
|
|
||||||
return TRUE;
|
// @TODO(bfredl): calling flush here inhibits substantial performance
|
||||||
|
// improvements. Caller should call ui_flush before waiting on user input or
|
||||||
|
// CPU busywork.
|
||||||
|
ui_flush(); // calls msg_ext_ui_flush
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void msg_ext_ui_flush(void)
|
||||||
|
{
|
||||||
|
if (!ui_has(kUIMessages)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_ext_emit_chunk();
|
||||||
|
if (msg_ext_chunks.size > 0) {
|
||||||
|
ui_call_msg_show(cstr_to_string(msg_ext_kind),
|
||||||
|
msg_ext_chunks, msg_ext_overwrite);
|
||||||
|
if (!msg_ext_overwrite) {
|
||||||
|
msg_ext_visible++;
|
||||||
|
}
|
||||||
|
msg_ext_kind = NULL;
|
||||||
|
msg_ext_chunks = (Array)ARRAY_DICT_INIT;
|
||||||
|
msg_ext_overwrite = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void msg_ext_flush_showmode(void)
|
||||||
|
{
|
||||||
|
// Showmode messages doesn't interrupt normal message flow, so we use
|
||||||
|
// separate event. Still reuse the same chunking logic, for simplicity.
|
||||||
|
msg_ext_emit_chunk();
|
||||||
|
ui_call_msg_showmode(msg_ext_chunks);
|
||||||
|
msg_ext_chunks = (Array)ARRAY_DICT_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void msg_ext_clear(bool force)
|
||||||
|
{
|
||||||
|
if (msg_ext_visible && (!msg_ext_keep_after_cmdline || force)) {
|
||||||
|
ui_call_msg_clear();
|
||||||
|
msg_ext_visible = 0;
|
||||||
|
msg_ext_overwrite = false; // nothing to overwrite
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only keep once.
|
||||||
|
msg_ext_keep_after_cmdline = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void msg_ext_check_prompt(void)
|
||||||
|
{
|
||||||
|
// Redraw after cmdline is expected to clear messages.
|
||||||
|
if (msg_ext_did_cmdline) {
|
||||||
|
msg_ext_clear(true);
|
||||||
|
msg_ext_did_cmdline = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool msg_ext_is_visible(void)
|
||||||
|
{
|
||||||
|
return ui_has(kUIMessages) && msg_ext_visible > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2653,6 +2814,9 @@ int msg_end(void)
|
|||||||
*/
|
*/
|
||||||
void msg_check(void)
|
void msg_check(void)
|
||||||
{
|
{
|
||||||
|
if (ui_has(kUIMessages)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (msg_row == Rows - 1 && msg_col >= sc_col) {
|
if (msg_row == Rows - 1 && msg_col >= sc_col) {
|
||||||
need_wait_return = TRUE;
|
need_wait_return = TRUE;
|
||||||
redraw_cmdline = TRUE;
|
redraw_cmdline = TRUE;
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "nvim/macros.h"
|
||||||
#include "nvim/types.h"
|
#include "nvim/types.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -77,6 +78,7 @@
|
|||||||
typedef struct msg_hist {
|
typedef struct msg_hist {
|
||||||
struct msg_hist *next; ///< Next message.
|
struct msg_hist *next; ///< Next message.
|
||||||
char_u *msg; ///< Message text.
|
char_u *msg; ///< Message text.
|
||||||
|
const char *kind; ///< Message kind (for msg_ext)
|
||||||
int attr; ///< Message highlighting.
|
int attr; ///< Message highlighting.
|
||||||
bool multiline; ///< Multiline message.
|
bool multiline; ///< Multiline message.
|
||||||
} MessageHistoryEntry;
|
} MessageHistoryEntry;
|
||||||
@@ -86,6 +88,8 @@ extern MessageHistoryEntry *first_msg_hist;
|
|||||||
/// Last message
|
/// Last message
|
||||||
extern MessageHistoryEntry *last_msg_hist;
|
extern MessageHistoryEntry *last_msg_hist;
|
||||||
|
|
||||||
|
EXTERN bool msg_ext_did_cmdline INIT(= false);
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "message.h.generated.h"
|
# include "message.h.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
@@ -61,6 +61,7 @@
|
|||||||
#include "nvim/event/loop.h"
|
#include "nvim/event/loop.h"
|
||||||
#include "nvim/os/time.h"
|
#include "nvim/os/time.h"
|
||||||
#include "nvim/os/input.h"
|
#include "nvim/os/input.h"
|
||||||
|
#include "nvim/api/private/helpers.h"
|
||||||
|
|
||||||
typedef struct normal_state {
|
typedef struct normal_state {
|
||||||
VimState state;
|
VimState state;
|
||||||
@@ -1258,8 +1259,9 @@ static void normal_redraw(NormalState *s)
|
|||||||
maketitle();
|
maketitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
// display message after redraw
|
// Display message after redraw. If an external message is still visible,
|
||||||
if (keep_msg != NULL) {
|
// it contains the kept message already.
|
||||||
|
if (keep_msg != NULL && !msg_ext_is_visible()) {
|
||||||
// msg_attr_keep() will set keep_msg to NULL, must free the string here.
|
// msg_attr_keep() will set keep_msg to NULL, must free the string here.
|
||||||
// Don't reset keep_msg, msg_attr_keep() uses it to check for duplicates.
|
// Don't reset keep_msg, msg_attr_keep() uses it to check for duplicates.
|
||||||
char *p = (char *)keep_msg;
|
char *p = (char *)keep_msg;
|
||||||
@@ -3317,7 +3319,8 @@ void clear_showcmd(void)
|
|||||||
else
|
else
|
||||||
sprintf((char *)showcmd_buf, "%d-%d", chars, bytes);
|
sprintf((char *)showcmd_buf, "%d-%d", chars, bytes);
|
||||||
}
|
}
|
||||||
showcmd_buf[SHOWCMD_COLS] = NUL; /* truncate */
|
int limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN-1 : SHOWCMD_COLS;
|
||||||
|
showcmd_buf[limit] = NUL; // truncate
|
||||||
showcmd_visual = true;
|
showcmd_visual = true;
|
||||||
} else {
|
} else {
|
||||||
showcmd_buf[0] = NUL;
|
showcmd_buf[0] = NUL;
|
||||||
@@ -3370,8 +3373,9 @@ bool add_to_showcmd(int c)
|
|||||||
STRCPY(p, "<20>");
|
STRCPY(p, "<20>");
|
||||||
size_t old_len = STRLEN(showcmd_buf);
|
size_t old_len = STRLEN(showcmd_buf);
|
||||||
size_t extra_len = STRLEN(p);
|
size_t extra_len = STRLEN(p);
|
||||||
if (old_len + extra_len > SHOWCMD_COLS) {
|
size_t limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN-1 : SHOWCMD_COLS;
|
||||||
size_t overflow = old_len + extra_len - SHOWCMD_COLS;
|
if (old_len + extra_len > limit) {
|
||||||
|
size_t overflow = old_len + extra_len - limit;
|
||||||
memmove(showcmd_buf, showcmd_buf + overflow, old_len - overflow + 1);
|
memmove(showcmd_buf, showcmd_buf + overflow, old_len - overflow + 1);
|
||||||
}
|
}
|
||||||
STRCAT(showcmd_buf, p);
|
STRCAT(showcmd_buf, p);
|
||||||
@@ -3432,13 +3436,24 @@ void pop_showcmd(void)
|
|||||||
static void display_showcmd(void)
|
static void display_showcmd(void)
|
||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
len = (int)STRLEN(showcmd_buf);
|
len = (int)STRLEN(showcmd_buf);
|
||||||
if (len == 0) {
|
showcmd_is_clear = (len == 0);
|
||||||
showcmd_is_clear = true;
|
|
||||||
} else {
|
if (ui_has(kUIMessages)) {
|
||||||
|
Array content = ARRAY_DICT_INIT;
|
||||||
|
if (len > 0) {
|
||||||
|
Array chunk = ARRAY_DICT_INIT;
|
||||||
|
// placeholder for future highlight support
|
||||||
|
ADD(chunk, INTEGER_OBJ(0));
|
||||||
|
ADD(chunk, STRING_OBJ(cstr_to_string((char *)showcmd_buf)));
|
||||||
|
ADD(content, ARRAY_OBJ(chunk));
|
||||||
|
}
|
||||||
|
ui_call_msg_showcmd(content);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!showcmd_is_clear) {
|
||||||
grid_puts(&default_grid, showcmd_buf, (int)Rows - 1, sc_col, 0);
|
grid_puts(&default_grid, showcmd_buf, (int)Rows - 1, sc_col, 0);
|
||||||
showcmd_is_clear = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -865,8 +865,12 @@ int do_record(int c)
|
|||||||
* needs to be removed again to put it in a register. exec_reg then
|
* needs to be removed again to put it in a register. exec_reg then
|
||||||
* adds the escaping back later.
|
* adds the escaping back later.
|
||||||
*/
|
*/
|
||||||
Recording = FALSE;
|
Recording = false;
|
||||||
|
if (ui_has(kUIMessages)) {
|
||||||
|
showmode();
|
||||||
|
} else {
|
||||||
MSG("");
|
MSG("");
|
||||||
|
}
|
||||||
p = get_recorded();
|
p = get_recorded();
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
retval = FAIL;
|
retval = FAIL;
|
||||||
|
@@ -4168,7 +4168,8 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
|
|||||||
errmsg = e_positive;
|
errmsg = e_positive;
|
||||||
}
|
}
|
||||||
} else if (pp == &p_ch) {
|
} else if (pp == &p_ch) {
|
||||||
if (value < 1) {
|
int minval = ui_has(kUIMessages) ? 0 : 1;
|
||||||
|
if (value < minval) {
|
||||||
errmsg = e_positive;
|
errmsg = e_positive;
|
||||||
}
|
}
|
||||||
} else if (pp == &p_tm) {
|
} else if (pp == &p_tm) {
|
||||||
@@ -4276,6 +4277,9 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
|
|||||||
p_window = Rows - 1;
|
p_window = Rows - 1;
|
||||||
}
|
}
|
||||||
} else if (pp == &p_ch) {
|
} else if (pp == &p_ch) {
|
||||||
|
if (ui_has(kUIMessages)) {
|
||||||
|
p_ch = 0;
|
||||||
|
}
|
||||||
if (p_ch > Rows - min_rows() + 1) {
|
if (p_ch > Rows - min_rows() + 1) {
|
||||||
p_ch = Rows - min_rows() + 1;
|
p_ch = Rows - min_rows() + 1;
|
||||||
}
|
}
|
||||||
|
@@ -44,6 +44,7 @@
|
|||||||
#include "nvim/window.h"
|
#include "nvim/window.h"
|
||||||
#include "nvim/os/os.h"
|
#include "nvim/os/os.h"
|
||||||
#include "nvim/os/input.h"
|
#include "nvim/os/input.h"
|
||||||
|
#include "nvim/api/private/helpers.h"
|
||||||
|
|
||||||
|
|
||||||
struct dir_stack_T {
|
struct dir_stack_T {
|
||||||
@@ -2155,6 +2156,7 @@ win_found:
|
|||||||
} else if (!msg_scrolled && shortmess(SHM_OVERALL)) {
|
} else if (!msg_scrolled && shortmess(SHM_OVERALL)) {
|
||||||
msg_scroll = false;
|
msg_scroll = false;
|
||||||
}
|
}
|
||||||
|
msg_ext_set_kind("quickfix");
|
||||||
msg_attr_keep(IObuff, 0, true, false);
|
msg_attr_keep(IObuff, 0, true, false);
|
||||||
msg_scroll = (int)i;
|
msg_scroll = (int)i;
|
||||||
}
|
}
|
||||||
|
@@ -362,6 +362,8 @@ void update_screen(int type)
|
|||||||
need_wait_return = FALSE;
|
need_wait_return = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg_ext_check_prompt();
|
||||||
|
|
||||||
/* reset cmdline_row now (may have been changed temporarily) */
|
/* reset cmdline_row now (may have been changed temporarily) */
|
||||||
compute_cmdrow();
|
compute_cmdrow();
|
||||||
|
|
||||||
@@ -483,8 +485,9 @@ void update_screen(int type)
|
|||||||
|
|
||||||
/* Clear or redraw the command line. Done last, because scrolling may
|
/* Clear or redraw the command line. Done last, because scrolling may
|
||||||
* mess up the command line. */
|
* mess up the command line. */
|
||||||
if (clear_cmdline || redraw_cmdline)
|
if (clear_cmdline || redraw_cmdline) {
|
||||||
showmode();
|
showmode();
|
||||||
|
}
|
||||||
|
|
||||||
/* May put up an introductory message when not editing a file */
|
/* May put up an introductory message when not editing a file */
|
||||||
if (!did_intro)
|
if (!did_intro)
|
||||||
@@ -5864,7 +5867,8 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO(bfredl): The relevant caller should do this
|
// TODO(bfredl): The relevant caller should do this
|
||||||
if (row == Rows - 1) { // overwritten the command line
|
if (row == Rows - 1 && !ui_has(kUIMessages)) {
|
||||||
|
// overwritten the command line
|
||||||
redraw_cmdline = true;
|
redraw_cmdline = true;
|
||||||
if (start_col == 0 && end_col == Columns
|
if (start_col == 0 && end_col == Columns
|
||||||
&& c1 == ' ' && c2 == ' ' && attr == 0) {
|
&& c1 == ' ' && c2 == ' ' && attr == 0) {
|
||||||
@@ -6394,6 +6398,13 @@ int showmode(void)
|
|||||||
int nwr_save;
|
int nwr_save;
|
||||||
int sub_attr;
|
int sub_attr;
|
||||||
|
|
||||||
|
if (ui_has(kUIMessages) && clear_cmdline) {
|
||||||
|
msg_ext_clear(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't make non-flushed message part of the showmode
|
||||||
|
msg_ext_ui_flush();
|
||||||
|
|
||||||
do_mode = ((p_smd && msg_silent == 0)
|
do_mode = ((p_smd && msg_silent == 0)
|
||||||
&& ((State & TERM_FOCUS)
|
&& ((State & TERM_FOCUS)
|
||||||
|| (State & INSERT)
|
|| (State & INSERT)
|
||||||
@@ -6436,9 +6447,14 @@ int showmode(void)
|
|||||||
MSG_PUTS_ATTR("--", attr);
|
MSG_PUTS_ATTR("--", attr);
|
||||||
// CTRL-X in Insert mode
|
// CTRL-X in Insert mode
|
||||||
if (edit_submode != NULL && !shortmess(SHM_COMPLETIONMENU)) {
|
if (edit_submode != NULL && !shortmess(SHM_COMPLETIONMENU)) {
|
||||||
/* These messages can get long, avoid a wrap in a narrow
|
// These messages can get long, avoid a wrap in a narrow window.
|
||||||
* window. Prefer showing edit_submode_extra. */
|
// Prefer showing edit_submode_extra. With external messages there
|
||||||
|
// is no imposed limit.
|
||||||
|
if (ui_has(kUIMessages)) {
|
||||||
|
length = INT_MAX;
|
||||||
|
} else {
|
||||||
length = (Rows - msg_row) * Columns - 3;
|
length = (Rows - msg_row) * Columns - 3;
|
||||||
|
}
|
||||||
if (edit_submode_extra != NULL) {
|
if (edit_submode_extra != NULL) {
|
||||||
length -= vim_strsize(edit_submode_extra);
|
length -= vim_strsize(edit_submode_extra);
|
||||||
}
|
}
|
||||||
@@ -6540,6 +6556,9 @@ int showmode(void)
|
|||||||
msg_clr_cmdline();
|
msg_clr_cmdline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NB: also handles clearing the showmode if it was emtpy or disabled
|
||||||
|
msg_ext_flush_showmode();
|
||||||
|
|
||||||
/* In Visual mode the size of the selected area must be redrawn. */
|
/* In Visual mode the size of the selected area must be redrawn. */
|
||||||
if (VIsual_active)
|
if (VIsual_active)
|
||||||
clear_showcmd();
|
clear_showcmd();
|
||||||
@@ -6581,11 +6600,13 @@ void unshowmode(bool force)
|
|||||||
// Clear the mode message.
|
// Clear the mode message.
|
||||||
void clearmode(void)
|
void clearmode(void)
|
||||||
{
|
{
|
||||||
|
msg_ext_ui_flush();
|
||||||
msg_pos_mode();
|
msg_pos_mode();
|
||||||
if (Recording) {
|
if (Recording) {
|
||||||
recording_mode(HL_ATTR(HLF_CM));
|
recording_mode(HL_ATTR(HLF_CM));
|
||||||
}
|
}
|
||||||
msg_clr_eos();
|
msg_clr_eos();
|
||||||
|
msg_ext_flush_showmode();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void recording_mode(int attr)
|
static void recording_mode(int attr)
|
||||||
@@ -6894,9 +6915,12 @@ void showruler(int always)
|
|||||||
|
|
||||||
static void win_redr_ruler(win_T *wp, int always)
|
static void win_redr_ruler(win_T *wp, int always)
|
||||||
{
|
{
|
||||||
/* If 'ruler' off or redrawing disabled, don't do anything */
|
static bool did_show_ext_ruler = false;
|
||||||
if (!p_ru)
|
|
||||||
|
// If 'ruler' off or redrawing disabled, don't do anything
|
||||||
|
if (!p_ru) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if cursor.lnum is valid, since win_redr_ruler() may be called
|
* Check if cursor.lnum is valid, since win_redr_ruler() may be called
|
||||||
@@ -6951,12 +6975,14 @@ static void win_redr_ruler(win_T *wp, int always)
|
|||||||
int fillchar;
|
int fillchar;
|
||||||
int attr;
|
int attr;
|
||||||
int off;
|
int off;
|
||||||
|
bool part_of_status = false;
|
||||||
|
|
||||||
if (wp->w_status_height) {
|
if (wp->w_status_height) {
|
||||||
row = W_ENDROW(wp);
|
row = W_ENDROW(wp);
|
||||||
fillchar = fillchar_status(&attr, wp);
|
fillchar = fillchar_status(&attr, wp);
|
||||||
off = wp->w_wincol;
|
off = wp->w_wincol;
|
||||||
width = wp->w_width;
|
width = wp->w_width;
|
||||||
|
part_of_status = true;
|
||||||
} else {
|
} else {
|
||||||
row = Rows - 1;
|
row = Rows - 1;
|
||||||
fillchar = ' ';
|
fillchar = ' ';
|
||||||
@@ -7016,6 +7042,20 @@ static void win_redr_ruler(win_T *wp, int always)
|
|||||||
}
|
}
|
||||||
get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i);
|
get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ui_has(kUIMessages) && !part_of_status) {
|
||||||
|
Array content = ARRAY_DICT_INIT;
|
||||||
|
Array chunk = ARRAY_DICT_INIT;
|
||||||
|
ADD(chunk, INTEGER_OBJ(attr));
|
||||||
|
ADD(chunk, STRING_OBJ(cstr_to_string((char *)buffer)));
|
||||||
|
ADD(content, ARRAY_OBJ(chunk));
|
||||||
|
ui_call_msg_ruler(content);
|
||||||
|
did_show_ext_ruler = true;
|
||||||
|
} else {
|
||||||
|
if (did_show_ext_ruler) {
|
||||||
|
ui_call_msg_ruler((Array)ARRAY_DICT_INIT);
|
||||||
|
did_show_ext_ruler = false;
|
||||||
|
}
|
||||||
// Truncate at window boundary.
|
// Truncate at window boundary.
|
||||||
o = 0;
|
o = 0;
|
||||||
for (i = 0; buffer[i] != NUL; i += utfc_ptr2len(buffer + i)) {
|
for (i = 0; buffer[i] != NUL; i += utfc_ptr2len(buffer + i)) {
|
||||||
@@ -7033,6 +7073,8 @@ static void win_redr_ruler(win_T *wp, int always)
|
|||||||
fillchar, attr);
|
fillchar, attr);
|
||||||
// don't redraw the cmdline because of showing the ruler
|
// don't redraw the cmdline because of showing the ruler
|
||||||
redraw_cmdline = i;
|
redraw_cmdline = i;
|
||||||
|
}
|
||||||
|
|
||||||
wp->w_ru_cursor = wp->w_cursor;
|
wp->w_ru_cursor = wp->w_cursor;
|
||||||
wp->w_ru_virtcol = wp->w_virtcol;
|
wp->w_ru_virtcol = wp->w_virtcol;
|
||||||
wp->w_ru_empty = empty_line;
|
wp->w_ru_empty = empty_line;
|
||||||
|
@@ -200,6 +200,10 @@ void ui_refresh(void)
|
|||||||
screen_resize(width, height);
|
screen_resize(width, height);
|
||||||
p_lz = save_p_lz;
|
p_lz = save_p_lz;
|
||||||
|
|
||||||
|
if (ext_widgets[kUIMessages]) {
|
||||||
|
p_ch = 0;
|
||||||
|
command_height();
|
||||||
|
}
|
||||||
ui_mode_info_set();
|
ui_mode_info_set();
|
||||||
pending_mode_update = true;
|
pending_mode_update = true;
|
||||||
ui_cursor_shape();
|
ui_cursor_shape();
|
||||||
@@ -380,6 +384,8 @@ void ui_flush(void)
|
|||||||
{
|
{
|
||||||
cmdline_ui_flush();
|
cmdline_ui_flush();
|
||||||
win_ui_flush();
|
win_ui_flush();
|
||||||
|
msg_ext_ui_flush();
|
||||||
|
|
||||||
if (pending_cursor_update) {
|
if (pending_cursor_update) {
|
||||||
ui_call_grid_cursor_goto(cursor_grid_handle, cursor_row, cursor_col);
|
ui_call_grid_cursor_goto(cursor_grid_handle, cursor_row, cursor_col);
|
||||||
pending_cursor_update = false;
|
pending_cursor_update = false;
|
||||||
|
@@ -14,7 +14,8 @@ typedef enum {
|
|||||||
kUIPopupmenu,
|
kUIPopupmenu,
|
||||||
kUITabline,
|
kUITabline,
|
||||||
kUIWildmenu,
|
kUIWildmenu,
|
||||||
#define kUIGlobalCount (kUIWildmenu+1)
|
kUIMessages,
|
||||||
|
#define kUIGlobalCount kUILinegrid
|
||||||
kUILinegrid,
|
kUILinegrid,
|
||||||
kUIMultigrid,
|
kUIMultigrid,
|
||||||
kUIHlState,
|
kUIHlState,
|
||||||
@@ -27,6 +28,7 @@ EXTERN const char *ui_ext_names[] INIT(= {
|
|||||||
"ext_popupmenu",
|
"ext_popupmenu",
|
||||||
"ext_tabline",
|
"ext_tabline",
|
||||||
"ext_wildmenu",
|
"ext_wildmenu",
|
||||||
|
"ext_messages",
|
||||||
"ext_linegrid",
|
"ext_linegrid",
|
||||||
"ext_multigrid",
|
"ext_multigrid",
|
||||||
"ext_hlstate",
|
"ext_hlstate",
|
||||||
|
@@ -1277,8 +1277,9 @@ describe('API', function()
|
|||||||
ext_wildmenu = false,
|
ext_wildmenu = false,
|
||||||
ext_linegrid = screen._options.ext_linegrid or false,
|
ext_linegrid = screen._options.ext_linegrid or false,
|
||||||
ext_multigrid = false,
|
ext_multigrid = false,
|
||||||
ext_hlstate=false,
|
ext_hlstate = false,
|
||||||
ext_termcolors=false,
|
ext_termcolors = false,
|
||||||
|
ext_messages = false,
|
||||||
height = 4,
|
height = 4,
|
||||||
rgb = true,
|
rgb = true,
|
||||||
width = 20,
|
width = 20,
|
||||||
|
632
test/functional/ui/messages_spec.lua
Normal file
632
test/functional/ui/messages_spec.lua
Normal file
@@ -0,0 +1,632 @@
|
|||||||
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
|
local Screen = require('test.functional.ui.screen')
|
||||||
|
local clear, feed = helpers.clear, helpers.feed
|
||||||
|
local eval = helpers.eval
|
||||||
|
local eq = helpers.eq
|
||||||
|
local command = helpers.command
|
||||||
|
|
||||||
|
|
||||||
|
describe('ui/ext_messages', function()
|
||||||
|
local screen
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
screen = Screen.new(25, 5)
|
||||||
|
screen:attach({rgb=true, ext_messages=true, ext_popupmenu=true})
|
||||||
|
screen:set_default_attr_ids({
|
||||||
|
[1] = {bold = true, foreground = Screen.colors.Blue1},
|
||||||
|
[2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
|
||||||
|
[3] = {bold = true},
|
||||||
|
[4] = {bold = true, foreground = Screen.colors.SeaGreen4},
|
||||||
|
[5] = {foreground = Screen.colors.Blue1},
|
||||||
|
[6] = {bold = true, reverse = true},
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('supports :echoerr', function()
|
||||||
|
feed(':echoerr "raa"<cr>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], messages={{
|
||||||
|
content = {{"raa", 2}},
|
||||||
|
kind = "echoerr",
|
||||||
|
}}}
|
||||||
|
|
||||||
|
-- cmdline in a later input cycle clears error message
|
||||||
|
feed(':')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], cmdline={{
|
||||||
|
firstc = ":",
|
||||||
|
content = {{ "" }},
|
||||||
|
pos = 0,
|
||||||
|
}}}
|
||||||
|
|
||||||
|
|
||||||
|
feed('echoerr "bork" | echoerr "fail"<cr>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], messages={{
|
||||||
|
content = {{ "bork", 2 }},
|
||||||
|
kind = "echoerr"
|
||||||
|
}, {
|
||||||
|
content = {{ "fail", 2 }},
|
||||||
|
kind = "echoerr"
|
||||||
|
}, {
|
||||||
|
content = {{ "Press ENTER or type command to continue", 4 }},
|
||||||
|
kind = "return_prompt"
|
||||||
|
}}}
|
||||||
|
|
||||||
|
feed(':echoerr "extrafail"<cr>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], messages={{
|
||||||
|
content = { { "bork", 2 } },
|
||||||
|
kind = "echoerr"
|
||||||
|
}, {
|
||||||
|
content = { { "fail", 2 } },
|
||||||
|
kind = "echoerr"
|
||||||
|
}, {
|
||||||
|
content = { { "extrafail", 2 } },
|
||||||
|
kind = "echoerr"
|
||||||
|
}, {
|
||||||
|
content = { { "Press ENTER or type command to continue", 4 } },
|
||||||
|
kind = "return_prompt"
|
||||||
|
}}}
|
||||||
|
|
||||||
|
feed('<cr>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]]}
|
||||||
|
|
||||||
|
-- cmdline without interleaving wait/display keeps the error message
|
||||||
|
feed(':echoerr "problem" | let x = input("foo> ")<cr>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], messages={{
|
||||||
|
content = {{ "problem", 2 }},
|
||||||
|
kind = "echoerr"
|
||||||
|
}}, cmdline={{
|
||||||
|
prompt = "foo> ",
|
||||||
|
content = {{ "" }},
|
||||||
|
pos = 0,
|
||||||
|
}}}
|
||||||
|
|
||||||
|
feed('solution<cr>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]]}
|
||||||
|
eq('solution', eval('x'))
|
||||||
|
|
||||||
|
feed(":messages<cr>")
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], messages={
|
||||||
|
{kind="echoerr", content={{"raa", 2}}},
|
||||||
|
{kind="echoerr", content={{"bork", 2}}},
|
||||||
|
{kind="echoerr", content={{"fail", 2}}},
|
||||||
|
{kind="echoerr", content={{"extrafail", 2}}},
|
||||||
|
{kind="echoerr", content={{"problem", 2}}}
|
||||||
|
}}
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('supports showmode', function()
|
||||||
|
command('imap <f2> <cmd>echomsg "stuff"<cr>')
|
||||||
|
feed('i')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], showmode={{"-- INSERT --", 3}}}
|
||||||
|
|
||||||
|
feed('alphpabet<cr>alphanum<cr>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
alphpabet |
|
||||||
|
alphanum |
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], showmode={ { "-- INSERT --", 3 } }}
|
||||||
|
|
||||||
|
feed('<c-x>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
alphpabet |
|
||||||
|
alphanum |
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], showmode={ { "-- ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)", 3 } }}
|
||||||
|
|
||||||
|
feed('<c-p>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
alphpabet |
|
||||||
|
alphanum |
|
||||||
|
alphanum^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], popupmenu={
|
||||||
|
anchor = { 2, 0 },
|
||||||
|
items = { { "alphpabet", "", "", "" }, { "alphanum", "", "", "" } },
|
||||||
|
pos = 1
|
||||||
|
}, showmode={ { "-- Keyword Local completion (^N^P) ", 3 }, { "match 1 of 2", 4 } }}
|
||||||
|
|
||||||
|
-- echomsg and showmode don't overwrite each other, this is the same
|
||||||
|
-- as the TUI behavior with cmdheight=2 or larger.
|
||||||
|
feed('<f2>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
alphpabet |
|
||||||
|
alphanum |
|
||||||
|
alphanum^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], popupmenu={
|
||||||
|
anchor = { 2, 0 },
|
||||||
|
items = { { "alphpabet", "", "", "" }, { "alphanum", "", "", "" } },
|
||||||
|
pos = 1
|
||||||
|
}, messages={ {
|
||||||
|
content = { { "stuff" } },
|
||||||
|
kind = "echomsg"
|
||||||
|
} }, showmode={ { "-- Keyword Local completion (^N^P) ", 3 }, { "match 1 of 2", 4 } }}
|
||||||
|
|
||||||
|
feed('<c-p>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
alphpabet |
|
||||||
|
alphanum |
|
||||||
|
alphpabet^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], popupmenu={
|
||||||
|
anchor = { 2, 0 },
|
||||||
|
items = { { "alphpabet", "", "", "" }, { "alphanum", "", "", "" } },
|
||||||
|
pos = 0
|
||||||
|
}, messages={ {
|
||||||
|
content = { { "stuff" } },
|
||||||
|
kind = "echomsg"
|
||||||
|
} }, showmode={ { "-- Keyword Local completion (^N^P) ", 3 }, { "match 2 of 2", 4 } }}
|
||||||
|
|
||||||
|
feed("<esc>:messages<cr>")
|
||||||
|
screen:expect{grid=[[
|
||||||
|
alphpabet |
|
||||||
|
alphanum |
|
||||||
|
alphpabe^t |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], messages={
|
||||||
|
{kind="echomsg", content={{"stuff"}}},
|
||||||
|
}}
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('supports showmode with recording message', function()
|
||||||
|
feed('qq')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], showmode={ { "recording @q", 3 } }}
|
||||||
|
|
||||||
|
feed('i')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], showmode={ { "-- INSERT --recording @q", 3 } }}
|
||||||
|
|
||||||
|
feed('<esc>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], showmode={ { "recording @q", 3 } }}
|
||||||
|
|
||||||
|
feed('q')
|
||||||
|
screen:expect([[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('shows recording message with noshowmode', function()
|
||||||
|
command("set noshowmode")
|
||||||
|
feed('qq')
|
||||||
|
-- also check mode to avoid immediate success
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], showmode={ { "recording @q", 3 } }, mode="normal"}
|
||||||
|
|
||||||
|
feed('i')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], showmode={ { "recording @q", 3 } }, mode="insert"}
|
||||||
|
|
||||||
|
feed('<esc>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], showmode={ { "recording @q", 3 } }, mode="normal"}
|
||||||
|
|
||||||
|
feed('q')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], mode="normal"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('supports showcmd and ruler', function()
|
||||||
|
command('set showcmd ruler')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], ruler={ { "0,0-1 All" } }}
|
||||||
|
feed('i')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], showmode={ { "-- INSERT --", 3 } }, ruler={ { "0,1 All" } }}
|
||||||
|
feed('abcde<cr>12345<esc>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
abcde |
|
||||||
|
1234^5 |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], ruler={ { "2,5 All" } }}
|
||||||
|
feed('d')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
abcde |
|
||||||
|
1234^5 |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], showcmd={ { "d" } }, ruler={ { "2,5 All" } }}
|
||||||
|
feed('<esc>^')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
abcde |
|
||||||
|
^12345 |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], ruler={ { "2,1 All" } }}
|
||||||
|
feed('d')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
abcde |
|
||||||
|
^12345 |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], showcmd={ { "d" } }, ruler={ { "2,1 All" } }}
|
||||||
|
feed('i')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
abcde |
|
||||||
|
^12345 |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], showcmd={ { "di" } }, ruler={ { "2,1 All" } }}
|
||||||
|
feed('w')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
abcde |
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], ruler={ { "2,0-1 All" } }}
|
||||||
|
|
||||||
|
-- when ruler is part of statusline it is not externalized.
|
||||||
|
-- this will be added as part of future ext_statusline support
|
||||||
|
command("set laststatus=2")
|
||||||
|
screen:expect([[
|
||||||
|
abcde |
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{6:<o Name] [+] 2,0-1 All}|
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('keeps history of message of different kinds', function()
|
||||||
|
feed(':echomsg "howdy"<cr>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], messages={{
|
||||||
|
content = {{ "howdy" }}, kind = "echomsg"}
|
||||||
|
}}
|
||||||
|
|
||||||
|
-- always test a message without kind. If this one gets promoted to a
|
||||||
|
-- category, add a new message without kind.
|
||||||
|
feed('<c-c>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], messages={{
|
||||||
|
content = {{ "Type :qa! and press <Enter> to abandon all changes and exit Nvim" }},
|
||||||
|
kind = ""}
|
||||||
|
}}
|
||||||
|
|
||||||
|
feed(':echoerr "bork"<cr>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], messages={{
|
||||||
|
content = {{ "bork", 2 }}, kind = "echoerr"}
|
||||||
|
}}
|
||||||
|
|
||||||
|
feed(':echo "xyz"<cr>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], messages={{
|
||||||
|
content = {{ "xyz" }}, kind = "echo"}
|
||||||
|
}}
|
||||||
|
|
||||||
|
feed(':call nosuchfunction()<cr>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], messages={{
|
||||||
|
content = {{ "E117: Unknown function: nosuchfunction", 2 }},
|
||||||
|
kind = "emsg"}
|
||||||
|
}}
|
||||||
|
|
||||||
|
feed(':messages<cr>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], messages={
|
||||||
|
{kind="echomsg", content={{"howdy"}}},
|
||||||
|
{kind="", content={{"Type :qa! and press <Enter> to abandon all changes and exit Nvim"}}},
|
||||||
|
{kind="echoerr", content={{"bork", 2}}},
|
||||||
|
{kind="emsg", content={{"E117: Unknown function: nosuchfunction", 2}}}
|
||||||
|
}}
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('implies ext_cmdline and ignores cmdheight', function()
|
||||||
|
eq(0, eval('&cmdheight'))
|
||||||
|
feed(':set cmdheight=1')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], cmdline={{
|
||||||
|
content = { { "set cmdheight=1" } },
|
||||||
|
firstc = ":",
|
||||||
|
pos = 15 }
|
||||||
|
}}
|
||||||
|
|
||||||
|
feed('<cr>')
|
||||||
|
screen:expect([[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]])
|
||||||
|
eq(0, eval('&cmdheight'))
|
||||||
|
|
||||||
|
-- normally this would be an error
|
||||||
|
feed(':set cmdheight=0')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], cmdline={{
|
||||||
|
content = { { "set cmdheight=0" } },
|
||||||
|
firstc = ":",
|
||||||
|
pos = 15 }
|
||||||
|
}}
|
||||||
|
feed('<cr>')
|
||||||
|
screen:expect([[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]])
|
||||||
|
eq(0, eval('&cmdheight'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('supports multiline messages', function()
|
||||||
|
feed(':lua error("such\\nmultiline\\nerror")<cr>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]], messages={{
|
||||||
|
content = {{'E5105: Error while calling lua chunk: [string "<VimL compiled string>"]:1: such\nmultiline\nerror', 2}},
|
||||||
|
kind = "emsg"
|
||||||
|
}}}
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('ui/ext_messages', function()
|
||||||
|
local screen
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
clear{headless=false, args={"--cmd", "set shortmess-=I"}}
|
||||||
|
screen = Screen.new(80, 24)
|
||||||
|
screen:attach({rgb=true, ext_messages=true, ext_popupmenu=true})
|
||||||
|
screen:set_default_attr_ids({
|
||||||
|
[1] = {bold = true, foreground = Screen.colors.Blue1},
|
||||||
|
[2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
|
||||||
|
[3] = {bold = true},
|
||||||
|
[4] = {bold = true, foreground = Screen.colors.SeaGreen4},
|
||||||
|
[5] = {foreground = Screen.colors.Blue1},
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('supports intro screen', function()
|
||||||
|
-- intro message is not externalized. But check that it still works.
|
||||||
|
-- Note parts of it depends on version or is indeterministic. We ignore those parts.
|
||||||
|
screen:expect([[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{IGNORE}|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }Nvim is open source and freely distributable{1: }|
|
||||||
|
{1:~ }https://neovim.io/#chat{1: }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }type :help nvim{5:<Enter>} if you are new! {1: }|
|
||||||
|
{1:~ }type :checkhealth{5:<Enter>} to optimize Nvim{1: }|
|
||||||
|
{1:~ }type :q{5:<Enter>} to exit {1: }|
|
||||||
|
{1:~ }type :help{5:<Enter>} for help {1: }|
|
||||||
|
{1:~ }|
|
||||||
|
{IGNORE}|
|
||||||
|
{IGNORE}|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]])
|
||||||
|
|
||||||
|
feed("<c-l>")
|
||||||
|
screen:expect([[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
]])
|
||||||
|
|
||||||
|
feed(":intro<cr>")
|
||||||
|
screen:expect{grid=[[
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
{IGNORE}|
|
||||||
|
|
|
||||||
|
Nvim is open source and freely distributable |
|
||||||
|
https://neovim.io/#chat |
|
||||||
|
|
|
||||||
|
type :help nvim{5:<Enter>} if you are new! |
|
||||||
|
type :checkhealth{5:<Enter>} to optimize Nvim |
|
||||||
|
type :q{5:<Enter>} to exit |
|
||||||
|
type :help{5:<Enter>} for help |
|
||||||
|
|
|
||||||
|
{IGNORE}|
|
||||||
|
{IGNORE}|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
]], messages={
|
||||||
|
{content = { { "Press ENTER or type command to continue", 4 } }, kind = "return_prompt" }
|
||||||
|
}}
|
||||||
|
end)
|
||||||
|
end)
|
@@ -28,6 +28,7 @@ describe('ui receives option updates', function()
|
|||||||
ext_linegrid=false,
|
ext_linegrid=false,
|
||||||
ext_hlstate=false,
|
ext_hlstate=false,
|
||||||
ext_multigrid=false,
|
ext_multigrid=false,
|
||||||
|
ext_messages=false,
|
||||||
ext_termcolors=false,
|
ext_termcolors=false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -159,6 +159,11 @@ function Screen.new(width, height)
|
|||||||
wildmenu_selected = nil,
|
wildmenu_selected = nil,
|
||||||
win_position = {},
|
win_position = {},
|
||||||
_session = nil,
|
_session = nil,
|
||||||
|
messages = {},
|
||||||
|
msg_history = {},
|
||||||
|
showmode = {},
|
||||||
|
showcmd = {},
|
||||||
|
ruler = {},
|
||||||
_default_attr_ids = nil,
|
_default_attr_ids = nil,
|
||||||
_default_attr_ignore = nil,
|
_default_attr_ignore = nil,
|
||||||
_mouse_enabled = true,
|
_mouse_enabled = true,
|
||||||
@@ -250,7 +255,8 @@ end
|
|||||||
|
|
||||||
-- canonical order of ext keys, used to generate asserts
|
-- canonical order of ext keys, used to generate asserts
|
||||||
local ext_keys = {
|
local ext_keys = {
|
||||||
'popupmenu', 'cmdline', 'cmdline_block', 'wildmenu_items', 'wildmenu_pos'
|
'popupmenu', 'cmdline', 'cmdline_block', 'wildmenu_items', 'wildmenu_pos',
|
||||||
|
'messages', 'showmode', 'showcmd', 'ruler',
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Asserts that the screen state eventually matches an expected state
|
-- Asserts that the screen state eventually matches an expected state
|
||||||
@@ -392,7 +398,7 @@ function Screen:expect(expected, attr_ids, attr_ignore)
|
|||||||
.. ') differs from configured height(' .. #actual_rows .. ') of Screen.'
|
.. ') differs from configured height(' .. #actual_rows .. ') of Screen.'
|
||||||
end
|
end
|
||||||
for i = 1, #actual_rows do
|
for i = 1, #actual_rows do
|
||||||
if expected_rows[i] ~= actual_rows[i] then
|
if expected_rows[i] ~= actual_rows[i] and expected_rows[i] ~= "{IGNORE}|" then
|
||||||
local msg_expected_rows = {}
|
local msg_expected_rows = {}
|
||||||
for j = 1, #expected_rows do
|
for j = 1, #expected_rows do
|
||||||
msg_expected_rows[j] = expected_rows[j]
|
msg_expected_rows[j] = expected_rows[j]
|
||||||
@@ -917,7 +923,7 @@ function Screen:_handle_option_set(name, value)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Screen:_handle_popupmenu_show(items, selected, row, col)
|
function Screen:_handle_popupmenu_show(items, selected, row, col)
|
||||||
self.popupmenu = {items=items,pos=selected, anchor={row, col}}
|
self.popupmenu = {items=items, pos=selected, anchor={row, col}}
|
||||||
end
|
end
|
||||||
|
|
||||||
function Screen:_handle_popupmenu_select(selected)
|
function Screen:_handle_popupmenu_select(selected)
|
||||||
@@ -973,6 +979,34 @@ function Screen:_handle_wildmenu_hide()
|
|||||||
self.wildmenu_items, self.wildmenu_pos = nil, nil
|
self.wildmenu_items, self.wildmenu_pos = nil, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Screen:_handle_msg_show(kind, chunks, replace_last)
|
||||||
|
local pos = #self.messages
|
||||||
|
if not replace_last or pos == 0 then
|
||||||
|
pos = pos + 1
|
||||||
|
end
|
||||||
|
self.messages[pos] = {kind=kind, content=chunks}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:_handle_msg_clear()
|
||||||
|
self.messages = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:_handle_msg_showcmd(msg)
|
||||||
|
self.showcmd = msg
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:_handle_msg_showmode(msg)
|
||||||
|
self.showmode = msg
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:_handle_msg_ruler(msg)
|
||||||
|
self.ruler = msg
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:_handle_msg_history_show(entries)
|
||||||
|
self.msg_history = entries
|
||||||
|
end
|
||||||
|
|
||||||
function Screen:_clear_block(grid, top, bot, left, right)
|
function Screen:_clear_block(grid, top, bot, left, right)
|
||||||
for i = top, bot do
|
for i = top, bot do
|
||||||
self:_clear_row_section(grid, i, left, right)
|
self:_clear_row_section(grid, i, left, right)
|
||||||
@@ -1057,12 +1091,27 @@ function Screen:_extstate_repr(attr_state)
|
|||||||
cmdline_block[i] = self:_chunks_repr(entry, attr_state)
|
cmdline_block[i] = self:_chunks_repr(entry, attr_state)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local messages = {}
|
||||||
|
for i, entry in ipairs(self.messages) do
|
||||||
|
messages[i] = {kind=entry.kind, content=self:_chunks_repr(entry.content, attr_state)}
|
||||||
|
end
|
||||||
|
|
||||||
|
local msg_history = {}
|
||||||
|
for i, entry in ipairs(self.msg_history) do
|
||||||
|
messages[i] = {kind=entry[1], content=self:_chunks_repr(entry[2], attr_state)}
|
||||||
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
popupmenu=self.popupmenu,
|
popupmenu=self.popupmenu,
|
||||||
cmdline=cmdline,
|
cmdline=cmdline,
|
||||||
cmdline_block=cmdline_block,
|
cmdline_block=cmdline_block,
|
||||||
wildmenu_items=self.wildmenu_items,
|
wildmenu_items=self.wildmenu_items,
|
||||||
wildmenu_pos=self.wildmenu_pos,
|
wildmenu_pos=self.wildmenu_pos,
|
||||||
|
messages=messages,
|
||||||
|
showmode=self:_chunks_repr(self.showmode, attr_state),
|
||||||
|
showcmd=self:_chunks_repr(self.showcmd, attr_state),
|
||||||
|
ruler=self:_chunks_repr(self.ruler, attr_state),
|
||||||
|
msg_history=msg_history,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user