commit ead2b4f76999234fbe6280cfa92e78507f604e01
parent f8d59cfab9585a538760e404789029be743b61dc
Author: Justin M. Keyes <justinkz@gmail.com>
Date: Sun, 15 Feb 2026 03:57:23 -0500
Merge #37870 from seandewar/prompt-crash
Diffstat:
4 files changed, 74 insertions(+), 16 deletions(-)
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
@@ -1593,12 +1593,17 @@ static void init_prompt(int cmdchar_todo)
char *prompt = prompt_text();
int prompt_len = (int)strlen(prompt);
- if (curwin->w_cursor.lnum < curbuf->b_prompt_start.mark.lnum) {
- curwin->w_cursor.lnum = curbuf->b_prompt_start.mark.lnum;
- }
+ // In case the mark is set to a nonexistent line.
+ curbuf->b_prompt_start.mark.lnum = MIN(curbuf->b_prompt_start.mark.lnum,
+ curbuf->b_ml.ml_line_count);
+
+ curwin->w_cursor.lnum = MAX(curwin->w_cursor.lnum, curbuf->b_prompt_start.mark.lnum);
char *text = ml_get(curbuf->b_prompt_start.mark.lnum);
+ colnr_T text_len = ml_get_len(curbuf->b_prompt_start.mark.lnum);
+
if ((curbuf->b_prompt_start.mark.lnum == curwin->w_cursor.lnum
&& (curbuf->b_prompt_start.mark.col < prompt_len
+ || curbuf->b_prompt_start.mark.col > text_len
|| !strnequal(text + curbuf->b_prompt_start.mark.col - prompt_len, prompt,
(size_t)prompt_len)))) {
// prompt is missing, insert it or append a line with it
diff --git a/src/nvim/eval/buffer.c b/src/nvim/eval/buffer.c
@@ -712,7 +712,7 @@ void restore_buffer(bufref_T *save_curbuf)
/// "prompt_setcallback({buffer}, {callback})" function
void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- Callback prompt_callback = { .type = kCallbackNone };
+ Callback prompt_callback = CALLBACK_INIT;
if (check_secure()) {
return;
@@ -735,7 +735,7 @@ void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "prompt_setinterrupt({buffer}, {callback})" function
void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- Callback interrupt_callback = { .type = kCallbackNone };
+ Callback interrupt_callback = CALLBACK_INIT;
if (check_secure()) {
return;
@@ -772,22 +772,20 @@ void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
// Update the prompt-text and prompt-marks if a plugin calls prompt_setprompt()
// even while user is editing their input.
if (bt_prompt(buf)) {
- if (buf->b_prompt_start.mark.lnum > buf->b_ml.ml_line_count) {
- // In case the mark is set to a nonexistent line.
- buf->b_prompt_start.mark.lnum = buf->b_ml.ml_line_count;
- }
+ // In case the mark is set to a nonexistent line.
+ buf->b_prompt_start.mark.lnum = MIN(buf->b_prompt_start.mark.lnum, buf->b_ml.ml_line_count);
linenr_T prompt_lno = buf->b_prompt_start.mark.lnum;
char *old_prompt = buf_prompt_text(buf);
char *old_line = ml_get_buf(buf, prompt_lno);
- old_line = old_line != NULL ? old_line : "";
+ colnr_T old_line_len = ml_get_buf_len(buf, prompt_lno);
int old_prompt_len = (int)strlen(old_prompt);
colnr_T cursor_col = curwin->w_cursor.col;
if (buf->b_prompt_start.mark.col < old_prompt_len
- || curbuf->b_prompt_start.mark.col < old_prompt_len
- || !strnequal(old_prompt, old_line + curbuf->b_prompt_start.mark.col - old_prompt_len,
+ || buf->b_prompt_start.mark.col > old_line_len
+ || !strnequal(old_prompt, old_line + buf->b_prompt_start.mark.col - old_prompt_len,
(size_t)old_prompt_len)) {
// If for some odd reason the old prompt is missing,
// replace prompt line with new-prompt (discards user-input).
@@ -802,7 +800,7 @@ void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
cursor_col += new_prompt_len - old_prompt_len;
}
- if (curwin->w_buffer == buf) {
+ if (curwin->w_buffer == buf && curwin->w_cursor.lnum == prompt_lno) {
coladvance(curwin, cursor_col);
}
changed_lines_redraw_buf(buf, prompt_lno, prompt_lno + 1, 0);
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
@@ -4883,7 +4883,7 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
typval_T *cancelreturn = NULL;
typval_T cancelreturn_strarg2 = TV_INITIAL_VALUE;
const char *xp_name = NULL;
- Callback input_callback = { .type = kCallbackNone };
+ Callback input_callback = CALLBACK_INIT;
char prompt_buf[NUMBUFLEN];
char defstr_buf[NUMBUFLEN];
char cancelreturn_buf[NUMBUFLEN];
diff --git a/test/functional/legacy/prompt_buffer_spec.lua b/test/functional/legacy/prompt_buffer_spec.lua
@@ -667,6 +667,17 @@ describe('prompt buffer', function()
eq({ last_line, 6 }, api.nvim_buf_get_mark(0, ':'))
eq(true, api.nvim_buf_set_mark(0, ':', 1, 5, {}))
eq({ 1, 5 }, api.nvim_buf_get_mark(0, ':'))
+
+ -- No crash from invalid col.
+ eq(true, api.nvim_buf_set_mark(0, ':', fn('line', '.'), 999, {}))
+ eq({ 12, 6 }, api.nvim_buf_get_mark(0, ':'))
+
+ -- No ml_get error from invalid lnum.
+ command('set messagesopt+=wait:0 messagesopt-=hit-enter')
+ fn('setpos', "':", { 0, 999, 7, 0 })
+ eq('', api.nvim_get_vvar('errmsg'))
+ command('set messagesopt&')
+ eq({ 12, 6 }, api.nvim_buf_get_mark(0, ':'))
end)
describe('prompt_getinput', function()
@@ -798,8 +809,8 @@ describe('prompt buffer', function()
api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
local buf = api.nvim_get_current_buf()
- local function set_prompt(prompt)
- fn('prompt_setprompt', buf, prompt)
+ local function set_prompt(prompt, b)
+ fn('prompt_setprompt', b or buf, prompt)
end
set_prompt('> ')
@@ -846,5 +857,49 @@ describe('prompt buffer', function()
{5:-- INSERT --} |
]])
eq({ 1, 13 }, api.nvim_buf_get_mark(0, ':'))
+
+ -- Cursor not moved when not on the prompt line.
+ feed('<CR>user input<Esc>k')
+ screen:expect([[
+ new-prompt > user inpu^t |
+ new-prompt > user input |
+ {1:~ }|*7
+ |
+ ]])
+ set_prompt('<>< ')
+ screen:expect([[
+ new-prompt > user inpu^t |
+ <>< user input |
+ {1:~ }|*7
+ |
+ ]])
+
+ -- No crash when setting shorter prompt than curbuf's in other buffer.
+ feed('i<C-O>zt')
+ command('new | setlocal buftype=prompt')
+ set_prompt('looooooooooooooooooooooooooooooooooooooooooooong > ', '') -- curbuf
+ set_prompt('foo > ')
+ screen:expect([[
+ loooooooooooooooooooooooo|
+ ooooooooooooooooooooong >|
+ ^ |
+ {1:~ }|
+ {3:[Prompt] [+] }|
+ foo > user input |
+ {1:~ }|*3
+ {5:-- INSERT --} |
+ ]])
+
+ -- No prompt_setprompt crash from invalid ': col. Must happen in the same event.
+ exec_lua(function()
+ vim.cmd 'bwipeout!'
+ vim.api.nvim_buf_set_mark(0, ':', vim.fn.line('.'), 999, {})
+ vim.fn.prompt_setprompt('', 'new-prompt > ')
+ end)
+ screen:expect([[
+ new-prompt > ^ |
+ {1:~ }|*8
+ {5:-- INSERT --} |
+ ]])
end)
end)