mirror of
https://github.com/neovim/neovim
synced 2025-07-16 01:01:49 +00:00
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:
@ -151,6 +151,8 @@ EDITOR
|
|||||||
• |omnicompletion| in `help` buffer. |ft-help-omni|
|
• |omnicompletion| in `help` buffer. |ft-help-omni|
|
||||||
• Setting "'0" in 'shada' prevents storing the jumplist in the shada file.
|
• Setting "'0" in 'shada' prevents storing the jumplist in the shada file.
|
||||||
• 'shada' now correctly respects "/0" and "f0".
|
• 'shada' now correctly respects "/0" and "f0".
|
||||||
|
• |prompt-buffer| supports multiline input/paste, undo/redo, and o/O normal
|
||||||
|
commands.
|
||||||
|
|
||||||
EVENTS
|
EVENTS
|
||||||
|
|
||||||
@ -253,6 +255,7 @@ UI
|
|||||||
VIMSCRIPT
|
VIMSCRIPT
|
||||||
|
|
||||||
• |cmdcomplete_info()| gets current cmdline completion info.
|
• |cmdcomplete_info()| gets current cmdline completion info.
|
||||||
|
• |prompt_getinput()| gets current user-input in prompt-buffer.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
CHANGED FEATURES *news-changed*
|
CHANGED FEATURES *news-changed*
|
||||||
|
@ -333,6 +333,7 @@ Functions:
|
|||||||
- |tempname()| tries to recover if the Nvim |tempdir| disappears.
|
- |tempname()| tries to recover if the Nvim |tempdir| disappears.
|
||||||
- |writefile()| with "p" flag creates parent directories.
|
- |writefile()| with "p" flag creates parent directories.
|
||||||
- |searchcount()|'s maximal value is raised from 99 to 999.
|
- |searchcount()|'s maximal value is raised from 99 to 999.
|
||||||
|
- |prompt_getinput()|
|
||||||
|
|
||||||
Highlight groups:
|
Highlight groups:
|
||||||
- |highlight-blend| controls blend level for a highlight group
|
- |highlight-blend| controls blend level for a highlight group
|
||||||
|
@ -7548,6 +7548,19 @@ printf({fmt}, {expr1} ...) *printf()*
|
|||||||
Return: ~
|
Return: ~
|
||||||
(`string`)
|
(`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()*
|
prompt_getprompt({buf}) *prompt_getprompt()*
|
||||||
Returns the effective prompt text for buffer {buf}. {buf} can
|
Returns the effective prompt text for buffer {buf}. {buf} can
|
||||||
be a buffer name or number. See |prompt-buffer|.
|
be a buffer name or number. See |prompt-buffer|.
|
||||||
|
10
runtime/lua/vim/_meta/vimfn.lua
generated
10
runtime/lua/vim/_meta/vimfn.lua
generated
@ -6867,6 +6867,16 @@ function vim.fn.prevnonblank(lnum) end
|
|||||||
--- @return string
|
--- @return string
|
||||||
function vim.fn.printf(fmt, expr1) end
|
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
|
--- Returns the effective prompt text for buffer {buf}. {buf} can
|
||||||
--- be a buffer name or number. See |prompt-buffer|.
|
--- be a buffer name or number. See |prompt-buffer|.
|
||||||
---
|
---
|
||||||
|
@ -1078,7 +1078,7 @@ check_pum:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if ((mod_mask & MOD_MASK_SHIFT) == 0 && bt_prompt(curbuf)) {
|
if ((mod_mask & MOD_MASK_SHIFT) == 0 && bt_prompt(curbuf)) {
|
||||||
invoke_prompt_callback();
|
prompt_invoke_callback();
|
||||||
if (!bt_prompt(curbuf)) {
|
if (!bt_prompt(curbuf)) {
|
||||||
// buffer changed to a non-prompt buffer, get out of
|
// buffer changed to a non-prompt buffer, get out of
|
||||||
// Insert mode
|
// Insert mode
|
||||||
|
@ -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;
|
if (!bt_prompt(buf)) {
|
||||||
typval_T argv[2];
|
return NULL;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
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();
|
char *prompt = prompt_text();
|
||||||
if (strlen(text) >= strlen(prompt)) {
|
if (strlen(text) >= strlen(prompt)) {
|
||||||
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++) {
|
for (linenr_T i = lnum_start + 1; i <= lnum_last; i++) {
|
||||||
char *half_text = concat_str(full_text, "\n");
|
char *half_text = concat_str(full_text, "\n");
|
||||||
xfree(full_text);
|
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);
|
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].v_type = VAR_STRING;
|
||||||
argv[0].vval.v_string = full_text;
|
argv[0].vval.v_string = user_input;
|
||||||
argv[1].v_type = VAR_UNKNOWN;
|
argv[1].v_type = VAR_UNKNOWN;
|
||||||
|
|
||||||
callback_call(&curbuf->b_prompt_callback, 1, argv, &rettv);
|
callback_call(&curbuf->b_prompt_callback, 1, argv, &rettv);
|
||||||
|
@ -8332,6 +8332,21 @@ M.funcs = {
|
|||||||
signature = 'printf({fmt}, {expr1} ...)',
|
signature = 'printf({fmt}, {expr1} ...)',
|
||||||
returns = 'string',
|
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 = {
|
prompt_getprompt = {
|
||||||
args = 1,
|
args = 1,
|
||||||
base = 1,
|
base = 1,
|
||||||
|
@ -5364,6 +5364,26 @@ static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, EvalFuncData
|
|||||||
buf->b_prompt_text = xstrdup(text);
|
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
|
/// "pum_getpos()" function
|
||||||
static void f_pum_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
static void f_pum_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||||
{
|
{
|
||||||
|
@ -3853,7 +3853,7 @@ static void nv_down(cmdarg_T *cap)
|
|||||||
} else if (bt_prompt(curbuf) && cap->cmdchar == CAR
|
} else if (bt_prompt(curbuf) && cap->cmdchar == CAR
|
||||||
&& curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) {
|
&& curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) {
|
||||||
// In a prompt buffer a <CR> in the last line invokes the callback.
|
// In a prompt buffer a <CR> in the last line invokes the callback.
|
||||||
invoke_prompt_callback();
|
prompt_invoke_callback();
|
||||||
if (restart_edit == 0) {
|
if (restart_edit == 0) {
|
||||||
restart_edit = 'a';
|
restart_edit = 'a';
|
||||||
}
|
}
|
||||||
|
@ -249,6 +249,8 @@ describe('prompt buffer', function()
|
|||||||
|
|
||||||
it('can insert multiline text', function()
|
it('can insert multiline text', function()
|
||||||
source_script()
|
source_script()
|
||||||
|
local buf = api.nvim_get_current_buf()
|
||||||
|
|
||||||
feed('line 1<s-cr>line 2<s-cr>line 3')
|
feed('line 1<s-cr>line 2<s-cr>line 3')
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
cmd: line 1 |
|
cmd: line 1 |
|
||||||
@ -261,6 +263,9 @@ describe('prompt buffer', function()
|
|||||||
{5:-- INSERT --} |
|
{5:-- INSERT --} |
|
||||||
]])
|
]])
|
||||||
|
|
||||||
|
-- prompt_getinput works with multiline input
|
||||||
|
eq('line 1\nline 2\nline 3', fn('prompt_getinput', buf))
|
||||||
|
|
||||||
feed('<cr>')
|
feed('<cr>')
|
||||||
-- submiting multiline text works
|
-- submiting multiline text works
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
@ -274,6 +279,8 @@ describe('prompt buffer', function()
|
|||||||
{5:-- INSERT --} |
|
{5:-- INSERT --} |
|
||||||
]])
|
]])
|
||||||
|
|
||||||
|
eq('', fn('prompt_getinput', buf))
|
||||||
|
|
||||||
-- % prompt is not repeated with formatoptions+=r
|
-- % prompt is not repeated with formatoptions+=r
|
||||||
source([[
|
source([[
|
||||||
bwipeout!
|
bwipeout!
|
||||||
@ -293,6 +300,8 @@ describe('prompt buffer', function()
|
|||||||
|
|
||||||
it('can put (p) multiline text', function()
|
it('can put (p) multiline text', function()
|
||||||
source_script()
|
source_script()
|
||||||
|
local buf = api.nvim_get_current_buf()
|
||||||
|
|
||||||
fn('setreg', 'a', 'line 1\nline 2\nline 3')
|
fn('setreg', 'a', 'line 1\nline 2\nline 3')
|
||||||
feed('<esc>"ap')
|
feed('<esc>"ap')
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
@ -305,6 +314,10 @@ describe('prompt buffer', function()
|
|||||||
{1:~ }|*3
|
{1:~ }|*3
|
||||||
|
|
|
|
||||||
]])
|
]])
|
||||||
|
|
||||||
|
-- prompt_getinput works with pasted input
|
||||||
|
eq('line 1\nline 2\nline 3', fn('prompt_getinput', buf))
|
||||||
|
|
||||||
feed('i<cr>')
|
feed('i<cr>')
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
Result: "line 1 |
|
Result: "line 1 |
|
||||||
@ -335,6 +348,8 @@ describe('prompt buffer', function()
|
|||||||
|
|
||||||
it('can undo current prompt', function()
|
it('can undo current prompt', function()
|
||||||
source_script()
|
source_script()
|
||||||
|
local buf = api.nvim_get_current_buf()
|
||||||
|
|
||||||
-- text editiing alowed in current prompt
|
-- text editiing alowed in current prompt
|
||||||
feed('tests-initial<esc>')
|
feed('tests-initial<esc>')
|
||||||
feed('bimiddle-<esc>')
|
feed('bimiddle-<esc>')
|
||||||
@ -368,6 +383,9 @@ describe('prompt buffer', function()
|
|||||||
1 change; {MATCH:.*} |
|
1 change; {MATCH:.*} |
|
||||||
]])
|
]])
|
||||||
|
|
||||||
|
-- undo is reflected in prompt_getinput
|
||||||
|
eq('tests-middle-initial', fn('prompt_getinput', buf))
|
||||||
|
|
||||||
feed('u')
|
feed('u')
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
cmd: tests-^initial |
|
cmd: tests-^initial |
|
||||||
@ -406,6 +424,8 @@ describe('prompt buffer', function()
|
|||||||
|
|
||||||
it('o/O can create new lines', function()
|
it('o/O can create new lines', function()
|
||||||
source_script()
|
source_script()
|
||||||
|
local buf = api.nvim_get_current_buf()
|
||||||
|
|
||||||
feed('line 1<s-cr>line 2<s-cr>line 3')
|
feed('line 1<s-cr>line 2<s-cr>line 3')
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
cmd: line 1 |
|
cmd: line 1 |
|
||||||
@ -419,7 +439,6 @@ describe('prompt buffer', function()
|
|||||||
]])
|
]])
|
||||||
|
|
||||||
feed('<esc>koafter')
|
feed('<esc>koafter')
|
||||||
|
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
cmd: line 1 |
|
cmd: line 1 |
|
||||||
line 2 |
|
line 2 |
|
||||||
@ -431,6 +450,9 @@ describe('prompt buffer', function()
|
|||||||
{5:-- INSERT --} |
|
{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')
|
feed('<esc>kObefore')
|
||||||
|
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
@ -444,6 +466,9 @@ describe('prompt buffer', function()
|
|||||||
{5:-- INSERT --} |
|
{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>')
|
feed('<cr>')
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
line 2 |
|
line 2 |
|
||||||
@ -552,4 +577,23 @@ describe('prompt buffer', function()
|
|||||||
source('set buftype=')
|
source('set buftype=')
|
||||||
eq("Invalid mark name: ':'", t.pcall_err(api.nvim_buf_get_mark, 0, ':'))
|
eq("Invalid mark name: ':'", t.pcall_err(api.nvim_buf_get_mark, 0, ':'))
|
||||||
end)
|
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)
|
end)
|
||||||
|
Reference in New Issue
Block a user