mirror of
https://github.com/neovim/neovim.git
synced 2026-03-27 19:02:02 +00:00
feat(prompt): prompt_appendbuf() appends to prompt buffer #37763
Problem: Currently, we recommend always inserting text above prompt-line in prompt-buffer. This can be done using the `:` mark. However, although we recommend it this way it can sometimes get confusing how to do it best. Solution: Provide an api to append text to prompt buffer. This is a common use-case for things using prompt-buffer.
This commit is contained in:
@@ -235,8 +235,8 @@ command, displaying shell output above the prompt: >lua
|
||||
-- Handles output from the shell.
|
||||
local function on_output(_, msg, _)
|
||||
-- Add shell output above the prompt.
|
||||
local input_start = vim.api.nvim_buf_get_mark(0, ":")[1]
|
||||
vim.fn.append(input_start - 1, msg)
|
||||
local buf = vim.api.nvim_get_current_buf()
|
||||
vim.fn.prompt_appendbuf(buf, msg)
|
||||
end
|
||||
|
||||
-- Handles the shell exit.
|
||||
|
||||
@@ -488,6 +488,7 @@ VIMSCRIPT
|
||||
• |prompt_getinput()| gets current user-input in prompt-buffer.
|
||||
• |wildtrigger()| triggers command-line expansion.
|
||||
• |v:vim_did_init| is set after sourcing |init.vim| but before |load-plugins|.
|
||||
• |prompt_appendbuf()| appends text to prompt-buffer.
|
||||
|
||||
==============================================================================
|
||||
CHANGED FEATURES *news-changed*
|
||||
|
||||
@@ -355,6 +355,7 @@ Functions:
|
||||
- |tempname()| tries to recover if the Nvim |tempdir| disappears.
|
||||
- |writefile()| with "p" flag creates parent directories.
|
||||
- |prompt_getinput()|
|
||||
- |prompt_appendbuf()|
|
||||
|
||||
Highlight groups:
|
||||
- |highlight-blend| controls blend level for a highlight group
|
||||
|
||||
@@ -7575,6 +7575,31 @@ printf({fmt}, {expr1} ...) *printf()*
|
||||
Return: ~
|
||||
(`string`)
|
||||
|
||||
prompt_appendbuf({buf}, {text}) *prompt_appendbuf()*
|
||||
Appends text to prompt buffer before current prompt. When {text} is
|
||||
a |List|: Append each item of the |List| as a text line above
|
||||
prompt-line in the buffer. Any type of item is accepted and converted
|
||||
to a String. Returns 1 for failure ({buf} not a prmopt buffer),
|
||||
0 for success. When {text} is an empty list zero is returned.
|
||||
|
||||
Example: >vim
|
||||
func TextEntered(text)
|
||||
call prompt_appendbuf(bufnr(''), split('Entered: "' . a:text . '"', '\n'))
|
||||
endfunc
|
||||
|
||||
set buftype=prompt
|
||||
call prompt_setcallback(bufnr(''), function("TextEntered"))
|
||||
eval bufnr("")->prompt_setprompt("cmd: ")
|
||||
startinsert
|
||||
<
|
||||
|
||||
Parameters: ~
|
||||
• {buf} (`integer|string`)
|
||||
• {text} (`string|string[]`)
|
||||
|
||||
Return: ~
|
||||
(`0|1`)
|
||||
|
||||
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.
|
||||
|
||||
22
runtime/lua/vim/_meta/vimfn.lua
generated
22
runtime/lua/vim/_meta/vimfn.lua
generated
@@ -6893,6 +6893,28 @@ function vim.fn.prevnonblank(lnum) end
|
||||
--- @return string
|
||||
function vim.fn.printf(fmt, expr1) end
|
||||
|
||||
--- Appends text to prompt buffer before current prompt. When {text} is
|
||||
--- a |List|: Append each item of the |List| as a text line above
|
||||
--- prompt-line in the buffer. Any type of item is accepted and converted
|
||||
--- to a String. Returns 1 for failure ({buf} not a prmopt buffer),
|
||||
--- 0 for success. When {text} is an empty list zero is returned.
|
||||
---
|
||||
--- Example: >vim
|
||||
--- func TextEntered(text)
|
||||
--- call prompt_appendbuf(bufnr(''), split('Entered: "' . a:text . '"', '\n'))
|
||||
--- endfunc
|
||||
---
|
||||
--- set buftype=prompt
|
||||
--- call prompt_setcallback(bufnr(''), function("TextEntered"))
|
||||
--- eval bufnr("")->prompt_setprompt("cmd: ")
|
||||
--- startinsert
|
||||
--- <
|
||||
---
|
||||
--- @param buf integer|string
|
||||
--- @param text string|string[]
|
||||
--- @return 0|1
|
||||
function vim.fn.prompt_appendbuf(buf, text) end
|
||||
|
||||
--- Gets the current user-input in |prompt-buffer| {buf} without invoking
|
||||
--- prompt_callback. {buf} can be a buffer name or number.
|
||||
---
|
||||
|
||||
@@ -2107,6 +2107,7 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
|
||||
buf->b_prompt_text = NULL;
|
||||
buf->b_prompt_start = (fmark_T)INIT_FMARK;
|
||||
buf->b_prompt_start.mark.col = 2; // default prompt is "% "
|
||||
buf->b_prompt_append_new_line = true;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -715,6 +715,7 @@ struct file_buffer {
|
||||
char *b_prompt_text; // set by prompt_setprompt()
|
||||
Callback b_prompt_callback; // set by prompt_setcallback()
|
||||
Callback b_prompt_interrupt; // set by prompt_setinterrupt()
|
||||
bool b_prompt_append_new_line; // prompt_appendlines() should start a newline
|
||||
int b_prompt_insert; // value for restart_edit when entering
|
||||
// a prompt buffer window.
|
||||
fmark_T b_prompt_start; // Start of the editable area of a prompt buffer.
|
||||
|
||||
@@ -1594,8 +1594,12 @@ static void init_prompt(int cmdchar_todo)
|
||||
int prompt_len = (int)strlen(prompt);
|
||||
|
||||
// In case the mark is set to a nonexistent line.
|
||||
curbuf->b_prompt_start.mark.lnum = MAX(1, MIN(curbuf->b_prompt_start.mark.lnum,
|
||||
curbuf->b_ml.ml_line_count));
|
||||
if (curbuf->b_prompt_start.mark.lnum < 1
|
||||
|| curbuf->b_prompt_start.mark.lnum > curbuf->b_ml.ml_line_count) {
|
||||
curbuf->b_prompt_start.mark.lnum = MAX(1, MIN(curbuf->b_prompt_start.mark.lnum,
|
||||
curbuf->b_ml.ml_line_count));
|
||||
curbuf->b_prompt_append_new_line = true;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -1615,6 +1619,7 @@ static void init_prompt(int cmdchar_todo)
|
||||
ml_append(lnum, prompt, 0, false);
|
||||
appended_lines_mark(lnum, 1);
|
||||
curbuf->b_prompt_start.mark.lnum = curbuf->b_ml.ml_line_count;
|
||||
curbuf->b_prompt_append_new_line = true;
|
||||
// Like submitting, undo history was relevant to the old prompt.
|
||||
u_clearallandblockfree(curbuf);
|
||||
}
|
||||
|
||||
@@ -6725,6 +6725,7 @@ theend:
|
||||
u_clearallandblockfree(curbuf);
|
||||
|
||||
curbuf->b_prompt_start.mark.lnum = curbuf->b_ml.ml_line_count;
|
||||
curbuf->b_prompt_append_new_line = true;
|
||||
}
|
||||
|
||||
/// @return true when the interrupt callback was invoked.
|
||||
|
||||
@@ -8371,6 +8371,32 @@ M.funcs = {
|
||||
signature = 'printf({fmt}, {expr1} ...)',
|
||||
returns = 'string',
|
||||
},
|
||||
prompt_appendbuf = {
|
||||
args = 2,
|
||||
base = 2,
|
||||
desc = [=[
|
||||
Appends text to prompt buffer before current prompt. When {text} is
|
||||
a |List|: Append each item of the |List| as a text line above
|
||||
prompt-line in the buffer. Any type of item is accepted and converted
|
||||
to a String. Returns 1 for failure ({buf} not a prmopt buffer),
|
||||
0 for success. When {text} is an empty list zero is returned.
|
||||
|
||||
Example: >vim
|
||||
func TextEntered(text)
|
||||
call prompt_appendbuf(bufnr(''), split('Entered: "' . a:text . '"', '\n'))
|
||||
endfunc
|
||||
|
||||
set buftype=prompt
|
||||
call prompt_setcallback(bufnr(''), function("TextEntered"))
|
||||
eval bufnr("")->prompt_setprompt("cmd: ")
|
||||
startinsert
|
||||
<
|
||||
]=],
|
||||
name = 'prompt_appendbuf',
|
||||
params = { { 'buf', 'integer|string' }, { 'text', 'string|string[]' } },
|
||||
returns = '0|1',
|
||||
signature = 'prompt_appendbuf({buf}, {text})',
|
||||
},
|
||||
prompt_getinput = {
|
||||
args = 1,
|
||||
base = 1,
|
||||
|
||||
@@ -274,6 +274,74 @@ void f_appendbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
buf_set_append_line(argvars, rettv, true);
|
||||
}
|
||||
|
||||
/// "prompt_appendbuf({buffer}, string/list)" function
|
||||
void f_prompt_appendbuf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const int did_emsg_before = did_emsg;
|
||||
|
||||
// Return an 1 by default, e.g. append failed or not a prompt buffer
|
||||
rettv->v_type = VAR_NUMBER;
|
||||
rettv->vval.v_number = 1;
|
||||
|
||||
buf_T *const buf = tv_get_buf_from_arg(&argvars[0]);
|
||||
if (buf == NULL || !bt_prompt(buf)) {
|
||||
return;
|
||||
}
|
||||
|
||||
linenr_T lnum = MAX(0, buf->b_prompt_start.mark.lnum - 1);
|
||||
typval_T *lines = &argvars[1];
|
||||
if (!buf->b_prompt_append_new_line) {
|
||||
// Since we are not creating a new line we need to append input to current line
|
||||
const char *text = (lnum > 0) ? (const char *)ml_get_buf(buf, lnum) : "";
|
||||
if (lines->v_type == VAR_LIST) {
|
||||
list_T *l = lines->vval.v_list;
|
||||
if (l != NULL && tv_list_len(l) > 0) {
|
||||
listitem_T *li = tv_list_first(l);
|
||||
const char *str = tv_get_string(&li->li_tv);
|
||||
char *new_str = concat_str(text, str);
|
||||
tv_clear(&li->li_tv);
|
||||
li->li_tv.v_type = VAR_STRING;
|
||||
li->li_tv.vval.v_string = new_str;
|
||||
}
|
||||
} else if (lines->v_type == VAR_STRING) {
|
||||
const char *str = tv_get_string(lines);
|
||||
char *new_str = concat_str(text, str);
|
||||
tv_clear(lines);
|
||||
lines->v_type = VAR_STRING;
|
||||
lines->vval.v_string = new_str;
|
||||
}
|
||||
}
|
||||
|
||||
if (did_emsg == did_emsg_before) {
|
||||
set_buffer_lines(buf, lnum, buf->b_prompt_append_new_line, lines, rettv);
|
||||
}
|
||||
|
||||
if (rettv->vval.v_number == 0) {
|
||||
// Ok we've inserted the lines successfully now check if last string ended with '\n'
|
||||
// to determine if we need to insert a new line before next append
|
||||
buf->b_prompt_append_new_line = false;
|
||||
if (lines->v_type == VAR_LIST) {
|
||||
list_T *l = lines->vval.v_list;
|
||||
if (l != NULL && tv_list_len(l) > 0) {
|
||||
listitem_T *li = tv_list_last(l);
|
||||
const char *str = tv_get_string(&li->li_tv);
|
||||
size_t len = strlen(str);
|
||||
if (len > 0 && str[len - 1] == '\n') {
|
||||
buf->b_prompt_append_new_line = true;
|
||||
}
|
||||
}
|
||||
} else if (lines->v_type == VAR_STRING) {
|
||||
const char *str = tv_get_string(lines);
|
||||
size_t len = strlen(str);
|
||||
|
||||
if (len > 0 && str[len - 1] == '\n') {
|
||||
buf->b_prompt_append_new_line = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// "bufadd(expr)" function
|
||||
void f_bufadd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
{
|
||||
@@ -778,8 +846,12 @@ void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
// even while user is editing their input.
|
||||
if (bt_prompt(buf) && buf->b_ml.ml_mfp != NULL) {
|
||||
// In case the mark is set to a nonexistent line.
|
||||
buf->b_prompt_start.mark.lnum = MAX(1, MIN(buf->b_prompt_start.mark.lnum,
|
||||
buf->b_ml.ml_line_count));
|
||||
if (buf->b_prompt_start.mark.lnum < 1
|
||||
|| buf->b_prompt_start.mark.lnum > curbuf->b_ml.ml_line_count) {
|
||||
buf->b_prompt_start.mark.lnum = MAX(1, MIN(buf->b_prompt_start.mark.lnum,
|
||||
buf->b_ml.ml_line_count));
|
||||
curbuf->b_prompt_append_new_line = true;
|
||||
}
|
||||
|
||||
linenr_T prompt_lno = buf->b_prompt_start.mark.lnum;
|
||||
char *old_prompt = buf_prompt_text(buf);
|
||||
|
||||
@@ -31,14 +31,18 @@ describe('prompt buffer', function()
|
||||
close
|
||||
else
|
||||
" Add the output above the current prompt.
|
||||
call append(line("$") - 1, split('Command: "' . a:text . '"', '\n'))
|
||||
call prompt_appendbuf(bufnr(''), split('Command: "' . a:text . '"', '\n'))
|
||||
" Reset &modified to allow the buffer to be closed.
|
||||
set nomodified
|
||||
call timer_start(20, {id -> TimerFunc(a:text)})
|
||||
endif
|
||||
endfunc
|
||||
|
||||
func TimerFunc(text)
|
||||
" Add the output above the current prompt.
|
||||
call append(line("$") - 1, split('Result: "' . a:text .'"', '\n'))
|
||||
call prompt_appendbuf(bufnr(''), split('Result: "' . a:text .'"', '\n'))
|
||||
" Reset &modified to allow the buffer to be closed.
|
||||
set nomodified
|
||||
endfunc
|
||||
|
||||
func SwitchWindows()
|
||||
|
||||
Reference in New Issue
Block a user