mirror of
https://github.com/neovim/neovim.git
synced 2026-05-24 05:40:08 +00:00
fix(messages): disallow source="nvim" progress msg #39315
Problem: Internal progress messages use the "nvim" source (since
ff68fd6b), plugins shouldn't be allowed to set the progress
message source to "nvim". The message ID used for internal
progress messages is not identifiable as such.
Solution: Disallow setting opts->source to "nvim" with nvim_echo().
Refactor msg_progress() and callees to bypass nvim_echo().
Prepend message id for internal progress messages with "nvim.".
This commit is contained in:
@@ -856,7 +856,11 @@ must handle.
|
||||
"list_cmd" List output for various commands (|:ls|, |:set|, …)
|
||||
"lua_error" Error in |:lua| code
|
||||
"lua_print" |print()| from |:lua| code
|
||||
"progress" Progress message emitted by |nvim_echo()|
|
||||
"progress" Progress message emitted by |nvim_echo()|.
|
||||
Internal progress messages use these ids:
|
||||
- `nvim.bufwrite "<filename>"`
|
||||
- `nvim.completion`
|
||||
- `nvim.indent`
|
||||
"quickfix" Quickfix navigation message
|
||||
"rpc_error" Error response from |rpcrequest()|
|
||||
"search_cmd" Entered search command
|
||||
|
||||
@@ -864,10 +864,10 @@ Union(Integer, String) nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Bool
|
||||
goto error;
|
||||
});
|
||||
|
||||
VALIDATE_EXP((!is_progress || strequal(opts->status.data, "success")
|
||||
|| strequal(opts->status.data, "failed")
|
||||
|| strequal(opts->status.data, "running")
|
||||
|| strequal(opts->status.data, "cancel")),
|
||||
VALIDATE_EXP(!is_progress || strequal(opts->status.data, "success")
|
||||
|| strequal(opts->status.data, "failed")
|
||||
|| strequal(opts->status.data, "running")
|
||||
|| strequal(opts->status.data, "cancel"),
|
||||
"status", "success|failed|running|cancel", opts->status.data, {
|
||||
goto error;
|
||||
});
|
||||
@@ -878,13 +878,16 @@ Union(Integer, String) nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Bool
|
||||
goto error;
|
||||
});
|
||||
|
||||
VALIDATE_R((!is_progress || opts->source.size != 0), "opts.source", {
|
||||
VALIDATE_R(!is_progress || opts->source.size != 0, "opts.source", {
|
||||
goto error;
|
||||
});
|
||||
VALIDATE_S(!is_progress || !strequal(opts->source.data, "nvim"), "source", opts->source.data, {
|
||||
goto error;
|
||||
});
|
||||
|
||||
// Message-id may be user-defined only if String, not Integer.
|
||||
VALIDATE(opts->id.type != kObjectTypeInteger || msg_id_exists(opts->id.data.integer),
|
||||
"Invalid 'id': %" PRId64, opts->id.data.integer, {
|
||||
VALIDATE_INT(opts->id.type != kObjectTypeInteger || msg_id_exists(opts->id.data.integer),
|
||||
"id", opts->id.data.integer, {
|
||||
goto error;
|
||||
});
|
||||
|
||||
@@ -917,10 +920,6 @@ Union(Integer, String) nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Bool
|
||||
verbose_stop(); // flush now
|
||||
}
|
||||
|
||||
if (is_progress) {
|
||||
do_autocmd_progress(id, hl_msg, &msg_data);
|
||||
}
|
||||
|
||||
if (!needs_clear) {
|
||||
// history takes ownership of `hl_msg`
|
||||
return id;
|
||||
|
||||
@@ -1669,6 +1669,9 @@ restore_backup:
|
||||
#endif
|
||||
if (!filtering) {
|
||||
add_quoted_fname(IObuff, IOSIZE, buf, fname);
|
||||
// Append the filename to the message ID.
|
||||
char msg_id[IOSIZE + 14] = "nvim.bufwrite ";
|
||||
strncat(msg_id + 14, IObuff, strlen(IObuff) - 1);
|
||||
bool insert_space = false;
|
||||
if (write_info.bw_conv_error) {
|
||||
xstrlcat(IObuff, _(" CONVERSION ERROR"), IOSIZE);
|
||||
@@ -1707,7 +1710,7 @@ restore_backup:
|
||||
xstrlcat(IObuff, shortmess(SHM_WRI) ? _(" [w]") : _(" written"), IOSIZE);
|
||||
}
|
||||
}
|
||||
set_keep_msg(msg_progress(IObuff, "bufwrite", "success", 0, true, true), 0);
|
||||
set_keep_msg(msg_progress(IObuff, msg_id, "success", 0, true, true), 0);
|
||||
}
|
||||
|
||||
// When written everything correctly: reset 'modified'. Unless not
|
||||
|
||||
@@ -132,7 +132,10 @@ void filemess(buf_T *buf, char *name, char *s)
|
||||
msg_scrolled_ign = true;
|
||||
// may truncate the message to avoid a hit-return prompt
|
||||
if (*s == NUL) {
|
||||
msg_progress(IObuff, "bufwrite", "running", 0, false, true);
|
||||
// Append the filename to the message ID.
|
||||
char msg_id[IOSIZE + 14] = "nvim.bufwrite ";
|
||||
strncat(msg_id + 14, IObuff, strlen(IObuff) - 1);
|
||||
msg_progress(IObuff, msg_id, "running", 0, false, true);
|
||||
} else {
|
||||
msg_outtrans(msg_may_trunc(false, IObuff), 0, false);
|
||||
}
|
||||
|
||||
@@ -1008,7 +1008,7 @@ void op_reindent(oparg_T *oap, Indenter how)
|
||||
// Restore cursor to avoid redrawing curwin in msg_show callback.
|
||||
linenr_T save_lnum = curwin->w_cursor.lnum;
|
||||
curwin->w_cursor.lnum = start_lnum;
|
||||
msg_progress(IObuff, "indent", "running", 0, true, false);
|
||||
msg_progress(IObuff, "nvim.indent", "running", 0, true, false);
|
||||
curwin->w_cursor.lnum = save_lnum;
|
||||
}
|
||||
|
||||
@@ -1050,7 +1050,7 @@ void op_reindent(oparg_T *oap, Indenter how)
|
||||
i = oap->line_count - (i + 1);
|
||||
snprintf(IObuff, IOSIZE,
|
||||
NGETTEXT("%" PRId64 " line indented ", "%" PRId64 " lines indented ", i), (int64_t)i);
|
||||
msg_progress(IObuff, "indent", "success", 0, true, false);
|
||||
msg_progress(IObuff, "nvim.indent", "success", 0, true, false);
|
||||
}
|
||||
if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
|
||||
// set '[ and '] marks
|
||||
|
||||
@@ -1945,7 +1945,7 @@ static void ins_compl_files(int count, char **files, bool thesaurus, int flags,
|
||||
FILE *fp = os_fopen(files[i], "r"); // open dictionary file
|
||||
if (flags != DICT_EXACT && !shortmess(SHM_COMPLETIONSCAN) && !compl_autocomplete) {
|
||||
vim_snprintf(IObuff, IOSIZE, _("Scanning dictionary: %s"), files[i]);
|
||||
msg_progress(IObuff, "completion", "running", HLF_R, false, true);
|
||||
msg_progress(IObuff, "nvim.completion", "running", HLF_R, false, true);
|
||||
}
|
||||
|
||||
if (fp == NULL) {
|
||||
@@ -3834,7 +3834,7 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar
|
||||
: st->ins_buf->b_sfname == NULL
|
||||
? st->ins_buf->b_fname
|
||||
: st->ins_buf->b_sfname);
|
||||
msg_progress(IObuff, "completion", "running", HLF_R, false, true);
|
||||
msg_progress(IObuff, "nvim.completion", "running", HLF_R, false, true);
|
||||
}
|
||||
} else if (*st->e_cpt == NUL) {
|
||||
status = INS_COMPL_CPT_END;
|
||||
@@ -3868,7 +3868,7 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar
|
||||
compl_type = CTRL_X_TAGS;
|
||||
if (!shortmess(SHM_COMPLETIONSCAN) && !compl_autocomplete) {
|
||||
vim_snprintf(IObuff, IOSIZE, "%s", _("Scanning tags."));
|
||||
msg_progress(IObuff, "completion", "running", HLF_R, false, true);
|
||||
msg_progress(IObuff, "nvim.completion", "running", HLF_R, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,7 +315,7 @@ static int is_multihl = 0;
|
||||
///
|
||||
/// @param hl_msg Message chunks
|
||||
/// @param msg_data Additional data for progress messages
|
||||
static HlMessage format_progress_message(HlMessage hl_msg, MessageData *msg_data)
|
||||
static bool format_progress_message(HlMessage *hl_msg, MessageData *msg_data)
|
||||
{
|
||||
HlMessage updated_msg = KV_INITIAL_VALUE;
|
||||
// progress messages are special. displayed as "title: percent% msg"
|
||||
@@ -333,28 +333,26 @@ static HlMessage format_progress_message(HlMessage hl_msg, MessageData *msg_data
|
||||
} else if (strequal(msg_data->status.data, "cancel")) {
|
||||
hl_id = syn_check_group("WarningMsg", STRLEN_LITERAL("WarningMsg"));
|
||||
}
|
||||
kv_push(updated_msg,
|
||||
((HlMessageChunk){ .text = copy_string(msg_data->title, NULL), .hl_id = hl_id }));
|
||||
kv_push(updated_msg, ((HlMessageChunk){ .text = cstr_to_string(": "), .hl_id = 0 }));
|
||||
kv_push(updated_msg, ((HlMessageChunk){ copy_string(msg_data->title, NULL), hl_id }));
|
||||
kv_push(updated_msg, ((HlMessageChunk){ cstr_to_string(": "), 0 }));
|
||||
}
|
||||
if (msg_data->percent >= 0) {
|
||||
char percent_buf[10];
|
||||
vim_snprintf(percent_buf, sizeof(percent_buf), "%3ld%% ", (long)msg_data->percent);
|
||||
String percent = cstr_to_string(percent_buf);
|
||||
int hl_id = syn_check_group("WarningMsg", STRLEN_LITERAL("WarningMsg"));
|
||||
kv_push(updated_msg, ((HlMessageChunk){ .text = percent, .hl_id = hl_id }));
|
||||
kv_push(updated_msg, ((HlMessageChunk){ percent, hl_id }));
|
||||
}
|
||||
|
||||
if (kv_size(updated_msg) != 0) {
|
||||
for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
|
||||
kv_push(updated_msg,
|
||||
((HlMessageChunk){ .text = copy_string(kv_A(hl_msg, i).text, NULL),
|
||||
.hl_id = kv_A(hl_msg, i).hl_id }));
|
||||
for (uint32_t i = 0; i < kv_size(*hl_msg); i++) {
|
||||
HlMessageChunk chunk = kv_A(*hl_msg, i);
|
||||
kv_push(updated_msg, ((HlMessageChunk){ copy_string(chunk.text, NULL), chunk.hl_id }));
|
||||
}
|
||||
return updated_msg;
|
||||
} else {
|
||||
return hl_msg;
|
||||
*hl_msg = updated_msg;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Print message chunks, each with their own highlight ID.
|
||||
@@ -377,33 +375,30 @@ MsgID msg_multihl(MsgID id, HlMessage hl_msg, const char *kind, bool history, bo
|
||||
abort();
|
||||
}
|
||||
|
||||
bool hl_msg_updated = false;
|
||||
// don't display progress message in cmd when target doesn't have cmd
|
||||
if (strequal(kind, "progress") && (progress_msg_target & PROGRESS_TARGET_CMD) == 0) {
|
||||
*needs_msg_clear = true;
|
||||
return id;
|
||||
if (strequal(kind, "progress")) {
|
||||
do_autocmd_progress(id, hl_msg, msg_data);
|
||||
if ((progress_msg_target & PROGRESS_TARGET_CMD) == 0) {
|
||||
*needs_msg_clear = true;
|
||||
return id;
|
||||
}
|
||||
if (msg_data && format_progress_message(&hl_msg, msg_data)) {
|
||||
*needs_msg_clear = true;
|
||||
hl_msg_updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
no_wait_return++;
|
||||
msg_start();
|
||||
msg_clr_eos();
|
||||
bool need_clear = false;
|
||||
bool hl_msg_updated = false;
|
||||
if (kind != NULL) {
|
||||
msg_ext_set_kind(kind);
|
||||
}
|
||||
msg_ext_skip_flush = true;
|
||||
msg_ext_id = id;
|
||||
|
||||
// progress message are special displayed as "title: percent% msg"
|
||||
if (strequal(kind, "progress") && msg_data) {
|
||||
HlMessage formated_message = format_progress_message(hl_msg, msg_data);
|
||||
if (formated_message.items != hl_msg.items) {
|
||||
*needs_msg_clear = true;
|
||||
hl_msg_updated = true;
|
||||
hl_msg = formated_message;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
|
||||
HlMessageChunk chunk = kv_A(hl_msg, i);
|
||||
is_multihl++;
|
||||
@@ -416,7 +411,7 @@ MsgID msg_multihl(MsgID id, HlMessage hl_msg, const char *kind, bool history, bo
|
||||
}
|
||||
|
||||
if (history && kv_size(hl_msg)) {
|
||||
msg_hist_add_multihl(hl_msg, false, msg_data);
|
||||
msg_hist_add_multihl(hl_msg, false);
|
||||
}
|
||||
|
||||
msg_ext_skip_flush = false;
|
||||
@@ -1101,25 +1096,22 @@ char *msg_may_trunc(bool force, char *s)
|
||||
|
||||
char *msg_progress(char *s, char *id, char *status, int hl_id, bool hist, bool trunc)
|
||||
{
|
||||
Error err = ERROR_INIT;
|
||||
Dict(echo_opts) opts = {
|
||||
.kind = cstr_as_string("progress"),
|
||||
.source = cstr_as_string("nvim"),
|
||||
.status = cstr_as_string(status),
|
||||
.id = CSTR_AS_OBJ(id),
|
||||
};
|
||||
if (hist && (!trunc || ui_has(kUIMessages))) {
|
||||
msg_hist_add(s, -1, 0);
|
||||
}
|
||||
if (trunc) {
|
||||
s = msg_may_trunc(false, s);
|
||||
}
|
||||
MAXSIZE_TEMP_ARRAY(chunk, 2);
|
||||
MAXSIZE_TEMP_ARRAY(chunks, 1);
|
||||
ADD_C(chunk, CSTR_AS_OBJ(s));
|
||||
ADD_C(chunk, INTEGER_OBJ(hl_id));
|
||||
ADD_C(chunks, ARRAY_OBJ(chunk));
|
||||
nvim_echo(chunks, false, &opts, &err);
|
||||
bool clear = false;
|
||||
MessageData data = {
|
||||
.source = cstr_as_string("nvim"),
|
||||
.status = cstr_as_string(status),
|
||||
.percent = -1,
|
||||
};
|
||||
HlMessage chunks = KV_INITIAL_VALUE;
|
||||
kv_push(chunks, ((HlMessageChunk){ cstr_as_string(s), hl_id }));
|
||||
msg_multihl(CSTR_AS_OBJ(id), chunks, "progress", false, false, &data, &clear);
|
||||
kv_destroy(chunks);
|
||||
ui_flush();
|
||||
return s;
|
||||
}
|
||||
@@ -1153,7 +1145,7 @@ static void msg_hist_add(const char *s, int len, int hl_id)
|
||||
|
||||
HlMessage msg = KV_INITIAL_VALUE;
|
||||
kv_push(msg, ((HlMessageChunk){ text, hl_id }));
|
||||
msg_hist_add_multihl(msg, false, NULL);
|
||||
msg_hist_add_multihl(msg, false);
|
||||
}
|
||||
|
||||
static bool do_clear_hist_temp = true;
|
||||
@@ -1190,7 +1182,7 @@ void do_autocmd_progress(MsgID msg_id, HlMessage msg, MessageData *msg_data)
|
||||
kv_destroy(messages);
|
||||
}
|
||||
|
||||
static void msg_hist_add_multihl(HlMessage msg, bool temp, MessageData *msg_data)
|
||||
static void msg_hist_add_multihl(HlMessage msg, bool temp)
|
||||
{
|
||||
if (do_clear_hist_temp) {
|
||||
msg_hist_clear_temp();
|
||||
@@ -3417,7 +3409,7 @@ void msg_ext_ui_flush(void)
|
||||
xfree(chunk);
|
||||
}
|
||||
xfree(tofree->items);
|
||||
msg_hist_add_multihl(msg, true, NULL);
|
||||
msg_hist_add_multihl(msg, true);
|
||||
}
|
||||
xfree(tofree);
|
||||
msg_ext_overwrite = false;
|
||||
|
||||
@@ -3888,6 +3888,8 @@ describe('API', function()
|
||||
eq("Invalid 'id': -1", pcall_err(api.nvim_echo, { { 'foo' } }, false, { id = -1 }))
|
||||
-- String ids are always allowed (user-defined).
|
||||
eq('my.msg.id', api.nvim_echo({ { 'foo' } }, false, { id = 'my.msg.id' }))
|
||||
local opts = { kind = 'progress', source = 'nvim', status = 'success' }
|
||||
eq("Invalid 'source': 'nvim'", pcall_err(api.nvim_echo, { { '' } }, 1, opts))
|
||||
end)
|
||||
|
||||
it('should clear cmdline message before echo', function()
|
||||
|
||||
@@ -95,7 +95,12 @@ describe('ui/ext_messages', function()
|
||||
{1:~ }|*3
|
||||
]],
|
||||
messages = {
|
||||
{ content = { { writemsg } }, history = true, id = 'bufwrite', kind = 'progress' },
|
||||
{
|
||||
content = { { writemsg } },
|
||||
history = true,
|
||||
id = 'nvim.bufwrite "Xtest_functional_ui_messages_spec"',
|
||||
kind = 'progress',
|
||||
},
|
||||
{
|
||||
content = { { 'W10: Warning: Changing a readonly file', 19, 'WarningMsg' } },
|
||||
history = true,
|
||||
@@ -587,7 +592,12 @@ describe('ui/ext_messages', function()
|
||||
{1:~ }|*2
|
||||
]],
|
||||
messages = {
|
||||
{ content = { { '3 lines indented ' } }, history = true, id = 'indent', kind = 'progress' },
|
||||
{
|
||||
content = { { '3 lines indented ' } },
|
||||
kind = 'progress',
|
||||
id = 'nvim.indent',
|
||||
history = true,
|
||||
},
|
||||
},
|
||||
})
|
||||
end)
|
||||
@@ -1398,7 +1408,7 @@ stack traceback:
|
||||
{
|
||||
content = { { string.format('"%s" [New] 0L, 0B written', fname) } },
|
||||
kind = 'progress',
|
||||
id = 'bufwrite',
|
||||
id = 'nvim.bufwrite "Xtest_functional_ui_messages_spec"',
|
||||
history = true,
|
||||
},
|
||||
},
|
||||
@@ -1645,7 +1655,7 @@ stack traceback:
|
||||
{
|
||||
content = { { 'Scanning tags.', 6, 'Question' } },
|
||||
kind = 'progress',
|
||||
id = 'completion',
|
||||
id = 'nvim.completion',
|
||||
},
|
||||
},
|
||||
showmode = {
|
||||
|
||||
Reference in New Issue
Block a user