aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/private/helpers.c18
-rw-r--r--src/nvim/api/ui.c7
-rw-r--r--src/nvim/api/ui_events.in.h13
-rw-r--r--src/nvim/eval.c7
-rw-r--r--src/nvim/ex_getln.c11
-rw-r--r--src/nvim/garray.h1
-rw-r--r--src/nvim/message.c196
-rw-r--r--src/nvim/message.h4
-rw-r--r--src/nvim/normal.c35
-rw-r--r--src/nvim/ops.c8
-rw-r--r--src/nvim/option.c6
-rw-r--r--src/nvim/quickfix.c2
-rw-r--r--src/nvim/screen.c94
-rw-r--r--src/nvim/ui.c6
-rw-r--r--src/nvim/ui.h4
15 files changed, 354 insertions, 58 deletions
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index f5cac82315..19a3368c1c 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -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
/// 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 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
/// with NUL.
///
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 91009c950f..9e9be588e3 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -132,6 +132,13 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
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));
data->channel_id = channel_id;
data->buffer = (Array)ARRAY_DICT_INIT;
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index ef3ff0f4c2..b57cf8d3ef 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -141,4 +141,17 @@ void wildmenu_select(Integer selected)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void wildmenu_hide(void)
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
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 4ab699cdb7..d63e45d3c7 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -19597,7 +19597,10 @@ void ex_echo(exarg_T *eap)
msg_puts_attr(" ", echo_attr);
}
char *tofree = encode_tv2echo(&rettv, NULL);
- msg_multiline_attr(tofree, echo_attr);
+ if (*tofree != NUL) {
+ msg_ext_set_kind("echo");
+ msg_multiline_attr(tofree, echo_attr);
+ }
xfree(tofree);
}
tv_clear(&rettv);
@@ -19689,11 +19692,13 @@ void ex_execute(exarg_T *eap)
}
if (eap->cmdidx == CMD_echomsg) {
+ msg_ext_set_kind("echomsg");
MSG_ATTR(ga.ga_data, echo_attr);
ui_flush();
} else if (eap->cmdidx == CMD_echoerr) {
/* We don't want to abort following commands, restore did_emsg. */
save_did_emsg = did_emsg;
+ msg_ext_set_kind("echoerr");
EMSG((char_u *)ga.ga_data);
if (!force_abort)
did_emsg = save_did_emsg;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index e8375cd3cc..d70b81409d 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -308,6 +308,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
gotocmdline(true);
redrawcmdprompt(); // draw prompt or indent
set_cmdspos();
+ if (!msg_scroll) {
+ msg_ext_clear(false);
+ }
}
s->xpc.xp_context = EXPAND_NOTHING;
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)) {
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--;
@@ -3613,7 +3622,7 @@ nextwild (
return FAIL;
}
- if (!ui_has(kUIWildmenu)) {
+ if (!(ui_has(kUICmdline) || ui_has(kUIWildmenu))) {
MSG_PUTS("..."); // show that we are busy
ui_flush();
}
diff --git a/src/nvim/garray.h b/src/nvim/garray.h
index 58738df691..e2cbdd4eab 100644
--- a/src/nvim/garray.h
+++ b/src/nvim/garray.h
@@ -18,6 +18,7 @@ typedef struct growarray {
} garray_T;
#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)
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 971a14974c..b22508c23f 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -34,11 +34,13 @@
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/strings.h"
+#include "nvim/syntax.h"
#include "nvim/ui.h"
#include "nvim/mouse.h"
#include "nvim/os/os.h"
#include "nvim/os/input.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
@@ -108,6 +110,19 @@ static int verbose_did_open = FALSE;
* 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
* When terminal not initialized (yet) mch_errmsg(..) is used.
@@ -256,7 +271,8 @@ msg_strtrunc (
/* May truncate message to avoid a hit-return prompt */
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);
if (msg_scrolled != 0)
/* Use all the columns. */
@@ -594,6 +610,9 @@ static bool emsg_multiline(const char *s, bool multiline)
} // wait_return has reset need_wait_return
// and a redraw is expected because
// 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.
@@ -802,6 +821,7 @@ static void add_msg_hist(const char *s, int len, int attr, bool multiline)
p->next = NULL;
p->attr = attr;
p->multiline = multiline;
+ p->kind = msg_ext_kind;
if (last_msg_hist != NULL) {
last_msg_hist->next = p;
}
@@ -856,7 +876,6 @@ void ex_messages(void *const eap_p)
return;
}
- msg_hist_off = true;
p = first_msg_hist;
@@ -874,13 +893,31 @@ 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_keep(p->msg, p->attr, false, p->multiline);
+ 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) {
+ if (p->msg != NULL) {
+ 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 (!exmode_active)
cmdline_row = msg_row;
- skip_redraw = TRUE; /* skip redraw once */
- do_redraw = FALSE;
+ skip_redraw = true; // skip redraw once
+ do_redraw = false;
+ msg_ext_keep_after_cmdline = true;
}
/*
@@ -1084,9 +1122,13 @@ void wait_return(int redraw)
if (tmpState == SETWSIZE) { /* got resize event while in vgetc() */
ui_refresh();
- } else if (!skip_redraw
- && (redraw == TRUE || (msg_scrolled != 0 && redraw != -1))) {
- redraw_later(VALID);
+ } else if (!skip_redraw) {
+ if (redraw == true || (msg_scrolled != 0 && redraw != -1)) {
+ 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 */
if (msg_didout) /* start on a new line */
msg_putchar('\n');
- if (got_int)
+ msg_ext_set_kind("return_prompt");
+ if (got_int) {
MSG_PUTS(_("Interrupt: "));
+ }
MSG_PUTS_ATTR(_("Press ENTER or type command to continue"), HL_ATTR(HLF_R));
if (!msg_use_printf()) {
@@ -1124,6 +1168,17 @@ void set_keep_msg(char_u *s, int 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.
*/
@@ -1160,6 +1215,14 @@ void msg_start(void)
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.
if (!did_return) {
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
// need_wait_return after some prompt, and then outputting something
// 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;
}
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);
}
+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().
* 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;
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) {
// We are at the end of the screen line when:
// - When outputting a newline.
@@ -2607,6 +2707,9 @@ void msg_clr_eos(void)
*/
void msg_clr_eos_force(void)
{
+ if (ui_has(kUIMessages)) {
+ return;
+ }
int msg_startcol = (cmdmsg_rl) ? 0 : msg_col;
int msg_endcol = (cmdmsg_rl) ? msg_col + 1 : (int)Columns;
@@ -2643,8 +2746,66 @@ int msg_end(void)
wait_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)
{
+ if (ui_has(kUIMessages)) {
+ return;
+ }
if (msg_row == Rows - 1 && msg_col >= sc_col) {
need_wait_return = TRUE;
redraw_cmdline = TRUE;
diff --git a/src/nvim/message.h b/src/nvim/message.h
index 41d2945b9c..7938fd91d3 100644
--- a/src/nvim/message.h
+++ b/src/nvim/message.h
@@ -5,6 +5,7 @@
#include <stdarg.h>
#include <stddef.h>
+#include "nvim/macros.h"
#include "nvim/types.h"
/*
@@ -77,6 +78,7 @@
typedef struct msg_hist {
struct msg_hist *next; ///< Next message.
char_u *msg; ///< Message text.
+ const char *kind; ///< Message kind (for msg_ext)
int attr; ///< Message highlighting.
bool multiline; ///< Multiline message.
} MessageHistoryEntry;
@@ -86,6 +88,8 @@ extern MessageHistoryEntry *first_msg_hist;
/// Last message
extern MessageHistoryEntry *last_msg_hist;
+EXTERN bool msg_ext_did_cmdline INIT(= false);
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "message.h.generated.h"
#endif
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 44e6ab46f1..d361d81ac7 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -61,6 +61,7 @@
#include "nvim/event/loop.h"
#include "nvim/os/time.h"
#include "nvim/os/input.h"
+#include "nvim/api/private/helpers.h"
typedef struct normal_state {
VimState state;
@@ -1258,8 +1259,9 @@ static void normal_redraw(NormalState *s)
maketitle();
}
- // display message after redraw
- if (keep_msg != NULL) {
+ // Display message after redraw. If an external message is still visible,
+ // 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.
// Don't reset keep_msg, msg_attr_keep() uses it to check for duplicates.
char *p = (char *)keep_msg;
@@ -3317,7 +3319,8 @@ void clear_showcmd(void)
else
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;
} else {
showcmd_buf[0] = NUL;
@@ -3370,8 +3373,9 @@ bool add_to_showcmd(int c)
STRCPY(p, "<20>");
size_t old_len = STRLEN(showcmd_buf);
size_t extra_len = STRLEN(p);
- if (old_len + extra_len > SHOWCMD_COLS) {
- size_t overflow = old_len + extra_len - SHOWCMD_COLS;
+ size_t limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN-1 : 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);
}
STRCAT(showcmd_buf, p);
@@ -3432,13 +3436,24 @@ void pop_showcmd(void)
static void display_showcmd(void)
{
int len;
-
len = (int)STRLEN(showcmd_buf);
- if (len == 0) {
- showcmd_is_clear = true;
- } else {
+ showcmd_is_clear = (len == 0);
+
+ 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);
- showcmd_is_clear = false;
}
/*
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index e9cb480647..674a9244f0 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -865,8 +865,12 @@ int do_record(int c)
* needs to be removed again to put it in a register. exec_reg then
* adds the escaping back later.
*/
- Recording = FALSE;
- MSG("");
+ Recording = false;
+ if (ui_has(kUIMessages)) {
+ showmode();
+ } else {
+ MSG("");
+ }
p = get_recorded();
if (p == NULL)
retval = FAIL;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 5517768194..fc1fab834e 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -4168,7 +4168,8 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
errmsg = e_positive;
}
} else if (pp == &p_ch) {
- if (value < 1) {
+ int minval = ui_has(kUIMessages) ? 0 : 1;
+ if (value < minval) {
errmsg = e_positive;
}
} 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;
}
} else if (pp == &p_ch) {
+ if (ui_has(kUIMessages)) {
+ p_ch = 0;
+ }
if (p_ch > Rows - min_rows() + 1) {
p_ch = Rows - min_rows() + 1;
}
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 200995bbeb..f0c37c0e38 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -44,6 +44,7 @@
#include "nvim/window.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
+#include "nvim/api/private/helpers.h"
struct dir_stack_T {
@@ -2155,6 +2156,7 @@ win_found:
} else if (!msg_scrolled && shortmess(SHM_OVERALL)) {
msg_scroll = false;
}
+ msg_ext_set_kind("quickfix");
msg_attr_keep(IObuff, 0, true, false);
msg_scroll = (int)i;
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index aa2982ec0c..5ac90ab601 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -362,6 +362,8 @@ void update_screen(int type)
need_wait_return = FALSE;
}
+ msg_ext_check_prompt();
+
/* reset cmdline_row now (may have been changed temporarily) */
compute_cmdrow();
@@ -483,8 +485,9 @@ void update_screen(int type)
/* Clear or redraw the command line. Done last, because scrolling may
* mess up the command line. */
- if (clear_cmdline || redraw_cmdline)
+ if (clear_cmdline || redraw_cmdline) {
showmode();
+ }
/* May put up an introductory message when not editing a file */
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
- if (row == Rows - 1) { // overwritten the command line
+ if (row == Rows - 1 && !ui_has(kUIMessages)) {
+ // overwritten the command line
redraw_cmdline = true;
if (start_col == 0 && end_col == Columns
&& c1 == ' ' && c2 == ' ' && attr == 0) {
@@ -6394,6 +6398,13 @@ int showmode(void)
int nwr_save;
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)
&& ((State & TERM_FOCUS)
|| (State & INSERT)
@@ -6436,9 +6447,14 @@ int showmode(void)
MSG_PUTS_ATTR("--", attr);
// CTRL-X in Insert mode
if (edit_submode != NULL && !shortmess(SHM_COMPLETIONMENU)) {
- /* These messages can get long, avoid a wrap in a narrow
- * window. Prefer showing edit_submode_extra. */
- length = (Rows - msg_row) * Columns - 3;
+ // These messages can get long, avoid a wrap in a narrow window.
+ // 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;
+ }
if (edit_submode_extra != NULL) {
length -= vim_strsize(edit_submode_extra);
}
@@ -6540,6 +6556,9 @@ int showmode(void)
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. */
if (VIsual_active)
clear_showcmd();
@@ -6581,11 +6600,13 @@ void unshowmode(bool force)
// Clear the mode message.
void clearmode(void)
{
- msg_pos_mode();
- if (Recording) {
- recording_mode(HL_ATTR(HLF_CM));
- }
- msg_clr_eos();
+ msg_ext_ui_flush();
+ msg_pos_mode();
+ if (Recording) {
+ recording_mode(HL_ATTR(HLF_CM));
+ }
+ msg_clr_eos();
+ msg_ext_flush_showmode();
}
static void recording_mode(int attr)
@@ -6894,9 +6915,12 @@ void showruler(int always)
static void win_redr_ruler(win_T *wp, int always)
{
- /* If 'ruler' off or redrawing disabled, don't do anything */
- if (!p_ru)
+ static bool did_show_ext_ruler = false;
+
+ // If 'ruler' off or redrawing disabled, don't do anything
+ if (!p_ru) {
return;
+ }
/*
* 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 attr;
int off;
+ bool part_of_status = false;
if (wp->w_status_height) {
row = W_ENDROW(wp);
fillchar = fillchar_status(&attr, wp);
off = wp->w_wincol;
width = wp->w_width;
+ part_of_status = true;
} else {
row = Rows - 1;
fillchar = ' ';
@@ -7016,23 +7042,39 @@ static void win_redr_ruler(win_T *wp, int always)
}
get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i);
}
- // Truncate at window boundary.
- o = 0;
- for (i = 0; buffer[i] != NUL; i += utfc_ptr2len(buffer + i)) {
- o += utf_ptr2cells(buffer + i);
- if (this_ru_col + o > width) {
- buffer[i] = NUL;
- break;
+
+ 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.
+ o = 0;
+ for (i = 0; buffer[i] != NUL; i += utfc_ptr2len(buffer + i)) {
+ o += utf_ptr2cells(buffer + i);
+ if (this_ru_col + o > width) {
+ buffer[i] = NUL;
+ break;
+ }
}
+
+ grid_puts(&default_grid, buffer, row, this_ru_col + off, attr);
+ i = redraw_cmdline;
+ grid_fill(&default_grid, row, row + 1,
+ this_ru_col + off + (int)STRLEN(buffer), off + width, fillchar,
+ fillchar, attr);
+ // don't redraw the cmdline because of showing the ruler
+ redraw_cmdline = i;
}
- grid_puts(&default_grid, buffer, row, this_ru_col + off, attr);
- i = redraw_cmdline;
- grid_fill(&default_grid, row, row + 1,
- this_ru_col + off + (int)STRLEN(buffer), off + width, fillchar,
- fillchar, attr);
- // don't redraw the cmdline because of showing the ruler
- redraw_cmdline = i;
wp->w_ru_cursor = wp->w_cursor;
wp->w_ru_virtcol = wp->w_virtcol;
wp->w_ru_empty = empty_line;
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 9b2f9c6fe6..16370f2d10 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -200,6 +200,10 @@ void ui_refresh(void)
screen_resize(width, height);
p_lz = save_p_lz;
+ if (ext_widgets[kUIMessages]) {
+ p_ch = 0;
+ command_height();
+ }
ui_mode_info_set();
pending_mode_update = true;
ui_cursor_shape();
@@ -380,6 +384,8 @@ void ui_flush(void)
{
cmdline_ui_flush();
win_ui_flush();
+ msg_ext_ui_flush();
+
if (pending_cursor_update) {
ui_call_grid_cursor_goto(cursor_grid_handle, cursor_row, cursor_col);
pending_cursor_update = false;
diff --git a/src/nvim/ui.h b/src/nvim/ui.h
index 49a30fe78b..490cc930b1 100644
--- a/src/nvim/ui.h
+++ b/src/nvim/ui.h
@@ -14,7 +14,8 @@ typedef enum {
kUIPopupmenu,
kUITabline,
kUIWildmenu,
-#define kUIGlobalCount (kUIWildmenu+1)
+ kUIMessages,
+#define kUIGlobalCount kUILinegrid
kUILinegrid,
kUIMultigrid,
kUIHlState,
@@ -27,6 +28,7 @@ EXTERN const char *ui_ext_names[] INIT(= {
"ext_popupmenu",
"ext_tabline",
"ext_wildmenu",
+ "ext_messages",
"ext_linegrid",
"ext_multigrid",
"ext_hlstate",