feat(prompt): prompt_getinput() gets current input #34491

Problem:
Not easy to get user-input in prompt-buffer before the user submits the
input. Under the current system user/plugin needs to read the buffer
contents, figure out where the prompt is, then extract the text.

Solution:
- Add prompt_getinput().
- Extract prompt text extraction logic to a separate function
This commit is contained in:
Shadman
2025-06-25 01:42:16 +06:00
committed by GitHub
parent efd0fa55c8
commit 5ae41ddde3
10 changed files with 147 additions and 21 deletions

View File

@ -151,6 +151,8 @@ EDITOR
• |omnicompletion| in `help` buffer. |ft-help-omni|
• Setting "'0" in 'shada' prevents storing the jumplist in the shada file.
• 'shada' now correctly respects "/0" and "f0".
• |prompt-buffer| supports multiline input/paste, undo/redo, and o/O normal
commands.
EVENTS
@ -253,6 +255,7 @@ UI
VIMSCRIPT
• |cmdcomplete_info()| gets current cmdline completion info.
• |prompt_getinput()| gets current user-input in prompt-buffer.
==============================================================================
CHANGED FEATURES *news-changed*

View File

@ -333,6 +333,7 @@ Functions:
- |tempname()| tries to recover if the Nvim |tempdir| disappears.
- |writefile()| with "p" flag creates parent directories.
- |searchcount()|'s maximal value is raised from 99 to 999.
- |prompt_getinput()|
Highlight groups:
- |highlight-blend| controls blend level for a highlight group

View File

@ -7548,6 +7548,19 @@ printf({fmt}, {expr1} ...) *printf()*
Return: ~
(`string`)
prompt_getinput({buf}) *prompt_getinput()*
Gets the current user-input in |prompt-buffer| {buf} without invoking
prompt_callback. {buf} can be a buffer name or number.
If the buffer doesn't exist or isn't a prompt buffer, an empty
string is returned.
Parameters: ~
• {buf} (`integer|string`)
Return: ~
(`any`)
prompt_getprompt({buf}) *prompt_getprompt()*
Returns the effective prompt text for buffer {buf}. {buf} can
be a buffer name or number. See |prompt-buffer|.

View File

@ -6867,6 +6867,16 @@ function vim.fn.prevnonblank(lnum) end
--- @return string
function vim.fn.printf(fmt, expr1) end
--- Gets the current user-input in |prompt-buffer| {buf} without invoking
--- prompt_callback. {buf} can be a buffer name or number.
---
--- If the buffer doesn't exist or isn't a prompt buffer, an empty
--- string is returned.
---
--- @param buf integer|string
--- @return any
function vim.fn.prompt_getinput(buf) end
--- Returns the effective prompt text for buffer {buf}. {buf} can
--- be a buffer name or number. See |prompt-buffer|.
---

View File

@ -1078,7 +1078,7 @@ check_pum:
return 0;
}
if ((mod_mask & MOD_MASK_SHIFT) == 0 && bt_prompt(curbuf)) {
invoke_prompt_callback();
prompt_invoke_callback();
if (!bt_prompt(curbuf)) {
// buffer changed to a non-prompt buffer, get out of
// Insert mode

View File

@ -8660,24 +8660,16 @@ void eval_fmt_source_name_line(char *buf, size_t bufsize)
}
}
void invoke_prompt_callback(void)
/// Gets the current user-input in prompt buffer `buf`, or NULL if buffer is not a prompt buffer.
char *prompt_get_input(buf_T *buf)
{
typval_T rettv;
typval_T argv[2];
linenr_T lnum_start = curbuf->b_prompt_start.mark.lnum;
linenr_T lnum_last = curbuf->b_ml.ml_line_count;
// Add a new line for the prompt before invoking the callback, so that
// text can always be inserted above the last line.
ml_append(lnum_last, "", 0, false);
appended_lines_mark(lnum_last, 1);
curwin->w_cursor.lnum = lnum_last + 1;
curwin->w_cursor.col = 0;
if (curbuf->b_prompt_callback.type == kCallbackNone) {
goto theend;
if (!bt_prompt(buf)) {
return NULL;
}
char *text = ml_get(lnum_start);
linenr_T lnum_start = buf->b_prompt_start.mark.lnum;
linenr_T lnum_last = buf->b_ml.ml_line_count;
char *text = ml_get_buf(buf, lnum_start);
char *prompt = prompt_text();
if (strlen(text) >= strlen(prompt)) {
text += strlen(prompt);
@ -8687,11 +8679,39 @@ void invoke_prompt_callback(void)
for (linenr_T i = lnum_start + 1; i <= lnum_last; i++) {
char *half_text = concat_str(full_text, "\n");
xfree(full_text);
full_text = concat_str(half_text, ml_get(i));
full_text = concat_str(half_text, ml_get_buf(buf, i));
xfree(half_text);
}
return full_text;
}
/// Invokes the user-defined callback defined for the current prompt-buffer.
void prompt_invoke_callback(void)
{
typval_T rettv;
typval_T argv[2];
linenr_T lnum = curbuf->b_ml.ml_line_count;
char *user_input = prompt_get_input(curbuf);
if (!user_input) {
return;
}
// Add a new line for the prompt before invoking the callback, so that
// text can always be inserted above the last line.
ml_append(lnum, "", 0, false);
appended_lines_mark(lnum, 1);
curwin->w_cursor.lnum = lnum + 1;
curwin->w_cursor.col = 0;
if (curbuf->b_prompt_callback.type == kCallbackNone) {
xfree(user_input);
goto theend;
}
argv[0].v_type = VAR_STRING;
argv[0].vval.v_string = full_text;
argv[0].vval.v_string = user_input;
argv[1].v_type = VAR_UNKNOWN;
callback_call(&curbuf->b_prompt_callback, 1, argv, &rettv);

View File

@ -8332,6 +8332,21 @@ M.funcs = {
signature = 'printf({fmt}, {expr1} ...)',
returns = 'string',
},
prompt_getinput = {
args = 1,
base = 1,
desc = [=[
Gets the current user-input in |prompt-buffer| {buf} without invoking
prompt_callback. {buf} can be a buffer name or number.
If the buffer doesn't exist or isn't a prompt buffer, an empty
string is returned.
]=],
name = 'prompt_getinput',
params = { { 'buf', 'integer|string' } },
signature = 'prompt_getinput({buf})',
},
prompt_getprompt = {
args = 1,
base = 1,

View File

@ -5364,6 +5364,26 @@ static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, EvalFuncData
buf->b_prompt_text = xstrdup(text);
}
/// "prompt_getinput({buffer})" function
static void f_prompt_getinput(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
{
// return an empty string by default, e.g. it's not a prompt buffer
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
buf_T *const buf = tv_get_buf_from_arg(&argvars[0]);
if (buf == NULL) {
return;
}
if (!bt_prompt(buf)) {
return;
}
rettv->vval.v_string = prompt_get_input(buf);
}
/// "pum_getpos()" function
static void f_pum_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{

View File

@ -3853,7 +3853,7 @@ static void nv_down(cmdarg_T *cap)
} else if (bt_prompt(curbuf) && cap->cmdchar == CAR
&& curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) {
// In a prompt buffer a <CR> in the last line invokes the callback.
invoke_prompt_callback();
prompt_invoke_callback();
if (restart_edit == 0) {
restart_edit = 'a';
}

View File

@ -249,6 +249,8 @@ describe('prompt buffer', function()
it('can insert multiline text', function()
source_script()
local buf = api.nvim_get_current_buf()
feed('line 1<s-cr>line 2<s-cr>line 3')
screen:expect([[
cmd: line 1 |
@ -261,6 +263,9 @@ describe('prompt buffer', function()
{5:-- INSERT --} |
]])
-- prompt_getinput works with multiline input
eq('line 1\nline 2\nline 3', fn('prompt_getinput', buf))
feed('<cr>')
-- submiting multiline text works
screen:expect([[
@ -274,6 +279,8 @@ describe('prompt buffer', function()
{5:-- INSERT --} |
]])
eq('', fn('prompt_getinput', buf))
-- % prompt is not repeated with formatoptions+=r
source([[
bwipeout!
@ -293,6 +300,8 @@ describe('prompt buffer', function()
it('can put (p) multiline text', function()
source_script()
local buf = api.nvim_get_current_buf()
fn('setreg', 'a', 'line 1\nline 2\nline 3')
feed('<esc>"ap')
screen:expect([[
@ -305,6 +314,10 @@ describe('prompt buffer', function()
{1:~ }|*3
|
]])
-- prompt_getinput works with pasted input
eq('line 1\nline 2\nline 3', fn('prompt_getinput', buf))
feed('i<cr>')
screen:expect([[
Result: "line 1 |
@ -335,6 +348,8 @@ describe('prompt buffer', function()
it('can undo current prompt', function()
source_script()
local buf = api.nvim_get_current_buf()
-- text editiing alowed in current prompt
feed('tests-initial<esc>')
feed('bimiddle-<esc>')
@ -368,6 +383,9 @@ describe('prompt buffer', function()
1 change; {MATCH:.*} |
]])
-- undo is reflected in prompt_getinput
eq('tests-middle-initial', fn('prompt_getinput', buf))
feed('u')
screen:expect([[
cmd: tests-^initial |
@ -406,6 +424,8 @@ describe('prompt buffer', function()
it('o/O can create new lines', function()
source_script()
local buf = api.nvim_get_current_buf()
feed('line 1<s-cr>line 2<s-cr>line 3')
screen:expect([[
cmd: line 1 |
@ -419,7 +439,6 @@ describe('prompt buffer', function()
]])
feed('<esc>koafter')
screen:expect([[
cmd: line 1 |
line 2 |
@ -431,6 +450,9 @@ describe('prompt buffer', function()
{5:-- INSERT --} |
]])
-- newline created with o is reflected in prompt_getinput
eq('line 1\nline 2\nafter\nline 3', fn('prompt_getinput', buf))
feed('<esc>kObefore')
screen:expect([[
@ -444,6 +466,9 @@ describe('prompt buffer', function()
{5:-- INSERT --} |
]])
-- newline created with O is reflected in prompt_getinput
eq('line 1\nbefore\nline 2\nafter\nline 3', fn('prompt_getinput', buf))
feed('<cr>')
screen:expect([[
line 2 |
@ -552,4 +577,23 @@ describe('prompt buffer', function()
source('set buftype=')
eq("Invalid mark name: ':'", t.pcall_err(api.nvim_buf_get_mark, 0, ':'))
end)
describe('prompt_getinput', function()
it('returns current prompts text', function()
command('new')
local bufnr = fn('bufnr')
api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
eq('', fn('prompt_getinput', bufnr))
feed('iasdf')
eq('asdf', fn('prompt_getinput', bufnr))
feed('<esc>dd')
eq('', fn('prompt_getinput', bufnr))
feed('iasdf2')
eq('asdf2', fn('prompt_getinput', bufnr))
-- returns empty string when called from non prompt buffer
api.nvim_set_option_value('buftype', '', { buf = 0 })
eq('', fn('prompt_getinput', bufnr))
end)
end)
end)