fix(progress): simplify ui-event, introduce default presentation #35527

Problem:
`msg_show` has "progress" info (title, status, percent) which is not presented
by default.

Solution:
Format TUI messages as `{title}: {msg}...{percent}%`. This also gets sent to UI.

- With specific formatting sent to UI we can remove the `progress` item from
  `msg_show` event. It can be added if needed in the future. Also, having
  a default presentation makes the feature more useful.
- For `vim._extui` we just need to implement the replace-msg-with-same-id
  behavior.
- If any UI/plugin wants to do anything fancier, they can handle the `Progress`
  event.
This commit is contained in:
Shadman
2025-08-28 19:33:41 +06:00
committed by GitHub
parent c10e36fc01
commit bc6737250d
5 changed files with 45 additions and 56 deletions

View File

@@ -825,7 +825,7 @@ will be set to zero, but can be changed and used for the replacing cmdline or
message window. Cmdline state is emitted as |ui-cmdline| events, which the UI message window. Cmdline state is emitted as |ui-cmdline| events, which the UI
must handle. must handle.
["msg_show", kind, content, replace_last, history, append, msg_id, progress] ~ ["msg_show", kind, content, replace_last, history, append, msg_id] ~
Display a message to the user. Display a message to the user.
kind kind
@@ -886,18 +886,6 @@ must handle.
Unique identifier for the message. It can either be an integer or Unique identifier for the message. It can either be an integer or
string. When message of same id appears it should replace the older message. string. When message of same id appears it should replace the older message.
progress
Progress-message properties:
• title: Title string of the progress message.
• status: Status of the progress message. Can contain one of
the following values
• success: The progress item completed successfully
• running: The progress is ongoing
• failed: The progress item failed
• cancel: The progressing process should be canceled.
• percent: How much progress is done on the progress
message
["msg_clear"] ~ ["msg_clear"] ~
Clear all messages currently displayed by "msg_show", emitted after Clear all messages currently displayed by "msg_show", emitted after
clearing the screen (messages sent by other "msg_" events below should clearing the screen (messages sent by other "msg_" events below should

View File

@@ -39,6 +39,11 @@ TREESITTER
• todo • todo
UI
• `progress` attribute removed form |ui-messages| msg_show event
============================================================================== ==============================================================================
BREAKING CHANGES *news-breaking* BREAKING CHANGES *news-breaking*
@@ -190,7 +195,7 @@ EVENTS
• |CmdlineLeavePre| triggered before preparing to leave the command line. • |CmdlineLeavePre| triggered before preparing to leave the command line.
• New `append` paremeter for |ui-messages| `msg_show` event. • New `append` paremeter for |ui-messages| `msg_show` event.
• New `msg_id` and `progress` paremeter for |ui-messages| `msg_show` event. • New `msg_id` paremeter for |ui-messages| `msg_show` event.
• Creating or updating a progress message with |nvim_echo()| triggers a |Progress| event. • Creating or updating a progress message with |nvim_echo()| triggers a |Progress| event.
HIGHLIGHTS HIGHLIGHTS

View File

@@ -165,7 +165,7 @@ 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, Boolean history, Boolean append, void msg_show(String kind, Array content, Boolean replace_last, Boolean history, Boolean append,
Object id, Dict progress) Object id)
FUNC_API_SINCE(6) FUNC_API_FAST FUNC_API_REMOTE_ONLY; FUNC_API_SINCE(6) FUNC_API_FAST FUNC_API_REMOTE_ONLY;
void msg_clear(void) void msg_clear(void)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;

View File

@@ -152,7 +152,6 @@ bool keep_msg_more = false; // keep_msg was set by msgmore()
// Extended msg state, currently used for external UIs with ext_messages // Extended msg state, currently used for external UIs with ext_messages
static const char *msg_ext_kind = NULL; static const char *msg_ext_kind = NULL;
static MsgID msg_ext_id = { .type = kObjectTypeInteger, .data.integer = 0 }; static MsgID msg_ext_id = { .type = kObjectTypeInteger, .data.integer = 0 };
static DictOf(Object) msg_ext_progress = ARRAY_DICT_INIT;
static Array *msg_ext_chunks = NULL; static Array *msg_ext_chunks = NULL;
static garray_T msg_ext_last_chunk = GA_INIT(sizeof(char), 40); static garray_T msg_ext_last_chunk = GA_INIT(sizeof(char), 40);
static sattr_T msg_ext_last_attr = -1; static sattr_T msg_ext_last_attr = -1;
@@ -312,6 +311,7 @@ MsgID msg_multihl(MsgID id, HlMessage hl_msg, const char *kind, bool history, bo
} }
is_multihl = true; is_multihl = true;
msg_ext_skip_flush = true; msg_ext_skip_flush = true;
bool is_progress = strequal(kind, "progress");
// provide a new id if not given // provide a new id if not given
if (id.type == kObjectTypeNil) { if (id.type == kObjectTypeNil) {
@@ -323,6 +323,14 @@ MsgID msg_multihl(MsgID id, HlMessage hl_msg, const char *kind, bool history, bo
} }
} }
// progress message are special displayed as "title: msg...percent%"
if (is_progress && msg_data && msg_data->title.size != 0) {
// this block draws the "title:" before the progress-message
String title = cstr_as_string(concat_str(msg_data->title.data, ": "));
msg_multiline(title, 0, true, false, &need_clear);
api_free_string(title);
}
for (uint32_t i = 0; i < kv_size(hl_msg); i++) { for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
HlMessageChunk chunk = kv_A(hl_msg, i); HlMessageChunk chunk = kv_A(hl_msg, i);
if (err) { if (err) {
@@ -332,6 +340,13 @@ MsgID msg_multihl(MsgID id, HlMessage hl_msg, const char *kind, bool history, bo
} }
assert(!ui_has(kUIMessages) || kind == NULL || msg_ext_kind == kind); assert(!ui_has(kUIMessages) || kind == NULL || msg_ext_kind == kind);
} }
if (is_progress && msg_data && msg_data->percent > 0) {
// this block draws the "...percent%" before the progress-message
char percent_buf[10];
vim_snprintf(percent_buf, sizeof(percent_buf), "...%ld%%", (long)msg_data->percent);
msg_multiline(cstr_as_string(percent_buf), 0, true, false, &need_clear);
}
if (history && kv_size(hl_msg)) { if (history && kv_size(hl_msg)) {
msg_hist_add_multihl(id, hl_msg, false, msg_data); msg_hist_add_multihl(id, hl_msg, false, msg_data);
} }
@@ -1106,18 +1121,6 @@ static void msg_hist_add_multihl(MsgID msg_id, HlMessage msg, bool temp, Message
msg_ext_history = true; msg_ext_history = true;
msg_ext_id = msg_id; msg_ext_id = msg_id;
if (strequal(msg_ext_kind, "progress") && msg_data != NULL && ui_has(kUIMessages)) {
kv_resize(msg_ext_progress, 3);
if (msg_data->title.size != 0) {
PUT_C(msg_ext_progress, "title", STRING_OBJ(msg_data->title));
}
if (msg_data->status.size != 0) {
PUT_C(msg_ext_progress, "status", STRING_OBJ(msg_data->status));
}
if (msg_data->percent >= 0) {
PUT_C(msg_ext_progress, "percent", INTEGER_OBJ(msg_data->percent));
}
}
msg_hist_clear(msg_hist_max); msg_hist_clear(msg_hist_max);
} }
@@ -2210,8 +2213,7 @@ void msg_puts_len(const char *const str, const ptrdiff_t len, int hl_id, bool hi
if (msg_silent != 0 || *str == NUL) { if (msg_silent != 0 || *str == NUL) {
if (*str == NUL && ui_has(kUIMessages)) { if (*str == NUL && ui_has(kUIMessages)) {
ui_call_msg_show(cstr_as_string("empty"), (Array)ARRAY_DICT_INIT, false, false, false, ui_call_msg_show(cstr_as_string("empty"), (Array)ARRAY_DICT_INIT, false, false, false,
INTEGER_OBJ(-1), INTEGER_OBJ(-1));
(Dict)ARRAY_DICT_INIT);
} }
return; return;
} }
@@ -3239,7 +3241,7 @@ void msg_ext_ui_flush(void)
Array *tofree = msg_ext_init_chunks(); Array *tofree = msg_ext_init_chunks();
ui_call_msg_show(cstr_as_string(msg_ext_kind), *tofree, msg_ext_overwrite, msg_ext_history, ui_call_msg_show(cstr_as_string(msg_ext_kind), *tofree, msg_ext_overwrite, msg_ext_history,
msg_ext_append, msg_ext_id, msg_ext_progress); msg_ext_append, msg_ext_id);
// clear info after emiting message. // clear info after emiting message.
if (msg_ext_history) { if (msg_ext_history) {
api_free_array(*tofree); api_free_array(*tofree);
@@ -3260,7 +3262,6 @@ void msg_ext_ui_flush(void)
msg_ext_append = false; msg_ext_append = false;
msg_ext_kind = NULL; msg_ext_kind = NULL;
msg_ext_id = INTEGER_OBJ(0); msg_ext_id = INTEGER_OBJ(0);
kv_destroy(msg_ext_progress);
} }
} }

View File

@@ -3197,12 +3197,7 @@ describe('progress-message', function()
]], ]],
messages = { messages = {
{ {
content = { { 'test-message' } }, content = { { 'testsuit: test-message...10%' } },
progress = {
percent = 10,
status = 'running',
title = 'testsuit',
},
history = true, history = true,
id = 1, id = 1,
kind = 'progress', kind = 'progress',
@@ -3232,12 +3227,7 @@ describe('progress-message', function()
]], ]],
messages = { messages = {
{ {
content = { { 'test-message-updated' } }, content = { { 'TestSuit: test-message-updated...50%' } },
progress = {
percent = 50,
status = 'running',
title = 'TestSuit',
},
history = true, history = true,
id = 1, id = 1,
kind = 'progress', kind = 'progress',
@@ -3294,15 +3284,10 @@ describe('progress-message', function()
]], ]],
messages = { messages = {
{ {
content = { { 'test-message' } }, content = { { 'TestSuit: test-message...10%' } },
history = true, history = true,
id = 1, id = 1,
kind = 'progress', kind = 'progress',
progress = {
percent = 10,
status = 'running',
title = 'TestSuit',
},
}, },
}, },
}) })
@@ -3491,15 +3476,10 @@ describe('progress-message', function()
]], ]],
messages = { messages = {
{ {
content = { { 'supports str-id' } }, content = { { 'TestSuit: supports str-id...30%' } },
history = true, history = true,
id = 'str-id', id = 'str-id',
kind = 'progress', kind = 'progress',
progress = {
percent = 30,
status = 'running',
title = 'TestSuit',
},
}, },
}, },
}) })
@@ -3519,4 +3499,19 @@ describe('progress-message', function()
data = {}, data = {},
}) })
end) end)
it('tui displays progress message in proper format', function()
clear()
setup_screen(false)
api.nvim_echo(
{ { 'test-message' } },
true,
{ kind = 'progress', title = 'TestSuit', percent = 10, status = 'running' }
)
screen:expect([[
^ |
{1:~ }|*3
TestSuit: test-message...10% |
]])
end)
end) end)