diff options
author | Luuk van Baal <luukvbaal@gmail.com> | 2025-03-01 16:18:43 +0100 |
---|---|---|
committer | Luuk van Baal <luukvbaal@gmail.com> | 2025-03-03 11:30:15 +0100 |
commit | 124c655f56bcf31dfeaf91caee2e5f2fcbbfa089 (patch) | |
tree | c6622845253b60c35528d81cc632bba57e809504 | |
parent | f7fa6d12668ab696fb1d64e04cfac6734cf86a4d (diff) | |
download | rneovim-124c655f56bcf31dfeaf91caee2e5f2fcbbfa089.tar.gz rneovim-124c655f56bcf31dfeaf91caee2e5f2fcbbfa089.tar.bz2 rneovim-124c655f56bcf31dfeaf91caee2e5f2fcbbfa089.zip |
feat(messages): "g<" mapping for ext_messages
Problem: Cannot use "g<" mapping with ext_messages. Mapping displays
the scrollback buffer since the last command, but the
scrollback buffer is not populated with ext_messages.
Solution: With ext_messages; store messages in the history that otherwise
wouldn't be. Mark them as temporary messages to be deleted when
the scrollback buffer would be cleared. To this end, make the
message history a doubly-linked list such that messages can be
removed from an arbitrary position.
Outlook: Default ext_messages UI might not show the hit-enter prompt
so we want "g<" to work as a recommended way to show messages
for the last command (prompted by an indicator).
-rw-r--r-- | src/nvim/message.c | 64 | ||||
-rw-r--r-- | src/nvim/message_defs.h | 1 | ||||
-rw-r--r-- | test/functional/ui/messages_spec.lua | 42 |
3 files changed, 100 insertions, 7 deletions
diff --git a/src/nvim/message.c b/src/nvim/message.c index 49bad3c994..a8635f1a61 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -96,6 +96,7 @@ static char *confirm_buttons; // ":confirm" buttons sent to cmdlin MessageHistoryEntry *msg_hist_last = NULL; // Last message (extern for unittest) static MessageHistoryEntry *msg_hist_first = NULL; // First message +static MessageHistoryEntry *msg_hist_temp = NULL; // First potentially temporary message static int msg_hist_len = 0; static int msg_hist_max = 500; // The default max value is 500 @@ -1039,7 +1040,9 @@ static void msg_hist_add_multihl(HlMessage msg, bool temp) // Allocate an entry and add the message at the end of the history. MessageHistoryEntry *entry = xmalloc(sizeof(MessageHistoryEntry)); entry->msg = msg; + entry->temp = temp; entry->kind = msg_ext_kind; + entry->prev = msg_hist_last; entry->next = NULL; if (msg_hist_first == NULL) { @@ -1048,6 +1051,9 @@ static void msg_hist_add_multihl(HlMessage msg, bool temp) if (msg_hist_last != NULL) { msg_hist_last->next = entry; } + if (msg_hist_temp == NULL) { + msg_hist_temp = entry; + } msg_hist_len += !temp; msg_hist_last = entry; @@ -1058,9 +1064,18 @@ static void msg_hist_add_multihl(HlMessage msg, bool temp) static void msg_hist_free_msg(MessageHistoryEntry *entry) { if (entry->next == NULL) { - msg_hist_last = NULL; + msg_hist_last = entry->prev; + } else { + entry->next->prev = entry->prev; + } + if (entry->prev == NULL) { + msg_hist_first = entry->next; + } else { + entry->prev->next = entry->next; + } + if (entry == msg_hist_temp) { + msg_hist_temp = entry->next; } - msg_hist_first = entry->next; hl_msg_free(entry->msg); xfree(entry); } @@ -1069,11 +1084,22 @@ static void msg_hist_free_msg(MessageHistoryEntry *entry) void msg_hist_clear(int keep) { while (msg_hist_len > keep || (keep == 0 && msg_hist_first != NULL)) { - msg_hist_len--; + msg_hist_len -= !msg_hist_first->temp; msg_hist_free_msg(msg_hist_first); } } +void msg_hist_clear_temp(void) +{ + while (msg_hist_temp != NULL) { + MessageHistoryEntry *next = msg_hist_temp->next; + if (msg_hist_temp->temp) { + msg_hist_free_msg(msg_hist_temp); + } + msg_hist_temp = next; + } +} + int messagesopt_changed(void) { int messages_flags_new = 0; @@ -1151,11 +1177,11 @@ void ex_messages(exarg_T *eap) } Array entries = ARRAY_DICT_INIT; - MessageHistoryEntry *p = msg_hist_first; + MessageHistoryEntry *p = eap->skip ? msg_hist_temp : msg_hist_first; int skip = eap->addr_count ? (msg_hist_len - eap->line2) : 0; while (p != NULL) { - if (skip-- > 0) { - // Skipping over messages. + if ((p->temp && !eap->skip) || skip-- > 0) { + // Skipping over count or temporary "g<" messages. } else if (ui_has(kUIMessages)) { Array entry = ARRAY_DICT_INIT; ADD(entry, CSTR_TO_OBJ(p->kind)); @@ -2179,6 +2205,8 @@ static void msg_ext_emit_chunk(void) ADD(*msg_ext_chunks, ARRAY_OBJ(chunk)); } +static bool do_clear_hist_temp = true; + /// The display part of msg_puts_len(). /// May be called recursively to display scroll-back text. static void msg_puts_display(const char *str, int maxlen, int hl_id, int recurse) @@ -2207,6 +2235,10 @@ static void msg_puts_display(const char *str, int maxlen, int hl_id, int recurse int col = (int)(maxlen < 0 ? mb_string2cells(p) : mb_string2cells_len(p, (size_t)(maxlen))); msg_col = (lastline ? 0 : msg_col) + col; + if (do_clear_hist_temp && !strequal(msg_ext_kind, "return_prompt")) { + msg_hist_clear_temp(); + do_clear_hist_temp = false; + } return; } @@ -2573,6 +2605,7 @@ static void store_sb_text(const char **sb_str, const char *s, int hl_id, int *sb void may_clear_sb_text(void) { do_clear_sb_text = SB_CLEAR_ALL; + do_clear_hist_temp = true; } /// Starting to edit the command line: do not clear messages now. @@ -2645,6 +2678,11 @@ void clear_sb_text(bool all) /// "g<" command. void show_sb_text(void) { + if (ui_has(kUIMessages)) { + exarg_T ea = { .arg = "", .skip = true }; + ex_messages(&ea); + return; + } // Only show something if there is more than one line, otherwise it looks // weird, typing a command without output results in one line. msgchunk_T *mp = msg_sb_start(last_msgchunk); @@ -3125,7 +3163,19 @@ void msg_ext_ui_flush(void) if (msg_ext_chunks->size > 0) { Array *tofree = msg_ext_init_chunks(); ui_call_msg_show(cstr_as_string(msg_ext_kind), *tofree, msg_ext_overwrite, msg_ext_history); - api_free_array(*tofree); + if (msg_ext_history || strequal(msg_ext_kind, "return_prompt")) { + api_free_array(*tofree); + } else { + // Add to history as temporary message for "g<". + HlMessage msg = KV_INITIAL_VALUE; + for (size_t i = 0; i < kv_size(*tofree); i++) { + Object *chunk = kv_A(*tofree, i).data.array.items; + kv_push(msg, ((HlMessageChunk){ chunk[1].data.string, (int)chunk[2].data.integer })); + xfree(chunk); + } + xfree(tofree->items); + msg_hist_add_multihl(msg, true); + } xfree(tofree); if (!msg_ext_overwrite) { msg_ext_visible++; diff --git a/src/nvim/message_defs.h b/src/nvim/message_defs.h index addb80fc25..a65f0cd97a 100644 --- a/src/nvim/message_defs.h +++ b/src/nvim/message_defs.h @@ -17,4 +17,5 @@ typedef struct msg_hist { struct msg_hist *prev; ///< Previous message. HlMessage msg; ///< Highlighted message. const char *kind; ///< Message kind (for msg_ext) + bool temp; ///< Temporary message since last command ("g<") } MessageHistoryEntry; diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 4770c5e143..a6577f3ca0 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -1571,6 +1571,48 @@ stack traceback: end, }) end) + + it('g< mapping shows recent messages', function() + command('echo "foo" | echo "bar"') + local s1 = [[ + ^ | + {1:~ }|*4 + ]] + screen:expect({ + grid = s1, + messages = { + { + content = { { 'bar' } }, + history = false, + kind = 'echo', + }, + }, + }) + feed(':messages<CR>g<lt>') + screen:expect({ + grid = [[ + ^ | + {1:~ }|*4 + ]], + messages = { + { + content = { { 'Press ENTER or type command to continue', 6, 18 } }, + history = false, + kind = 'return_prompt', + }, + }, + msg_history = { + { + content = { { 'foo' } }, + kind = 'echo', + }, + { + content = { { 'bar' } }, + kind = 'echo', + }, + }, + }) + end) end) describe('ui/builtin messages', function() |