feat(prompt): multiline prompt input #33371

Problem:
Cannot enter multiline prompts in a buftype=prompt buffer.

Solution:
- Support shift+enter (`<s-enter>`) to start a new line in the prompt.
- Pasting multiline text via OS paste, clipboard, "xp, etc.
- A/I in editable region works as usual.
- i/a/A/I outside of editable region moves cursor to end of current
  prompt.
- Support undo/redo in prompt buffer.
- Support o/O in prompt buffer.
- Expose prompt location as `':` mark.
This commit is contained in:
Shadman
2025-06-17 21:46:57 +06:00
committed by GitHub
parent 496691f985
commit 286371b4d2
13 changed files with 409 additions and 39 deletions

View File

@@ -183,22 +183,23 @@ If you want to type input for the job in a Vim window you have a few options:
- Use a terminal window. This works well if what you type goes directly to - Use a terminal window. This works well if what you type goes directly to
the job and the job output is directly displayed in the window. the job and the job output is directly displayed in the window.
See |terminal|. See |terminal|.
- Use a window with a prompt buffer. This works well when entering a line for - Use a window with a prompt buffer. This works well when entering lines for
the job in Vim while displaying (possibly filtered) output from the job. the job in Vim while displaying (possibly filtered) output from the job.
A prompt buffer is created by setting 'buftype' to "prompt". You would A prompt buffer is created by setting 'buftype' to "prompt". You would
normally only do that in a newly created buffer. normally only do that in a newly created buffer.
The user can edit and enter one line of text at the very last line of the The user can edit and enter text at the very last line of the buffer. When
buffer. When pressing Enter in the prompt line the callback set with pressing Enter in the prompt line the callback set with |prompt_setcallback()|
|prompt_setcallback()| is invoked. It would normally send the line to a job. is invoked. To enter multiple lines user can use Shift+Enter that'd add a new
Another callback would receive the output from the job and display it in the line. The final Enter submits the lines to |prompt_setcallback()|. It would
buffer, below the prompt (and above the next prompt). normally send the line to a job. Another callback would receive the output
from the job and display it in the buffer, below the prompt (and above the
next prompt).
Only the text in the last line, after the prompt, is editable. The rest of the Only the text after the last prompt, is editable. The rest of the buffer is
buffer is not modifiable with Normal mode commands. It can be modified by not modifiable with Normal mode commands. It can be modified by calling
calling functions, such as |append()|. Using other commands may mess up the functions, such as |append()|. Using other commands may mess up the buffer.
buffer.
After setting 'buftype' to "prompt" Vim does not automatically start Insert After setting 'buftype' to "prompt" Vim does not automatically start Insert
mode, use `:startinsert` if you want to enter Insert mode, so that the user mode, use `:startinsert` if you want to enter Insert mode, so that the user

View File

@@ -795,6 +795,14 @@ m< or m> Set the |'<| or |'>| mark. Useful to change what the
Note that the Visual mode cannot be set, only the Note that the Visual mode cannot be set, only the
start and end position. start and end position.
*m:*
m: Special mark for prompt buffers. It always shows the
line where current prompt starts. Text from this line
and below will be submitted when user submits.
Note: This mark is readonly. You can not modify it's
location. Also this mark is unique to prompt buffers as
a result not available in regular buffers.
*:ma* *:mark* *E191* *:ma* *:mark* *E191*
:[range]ma[rk] {a-zA-Z'} :[range]ma[rk] {a-zA-Z'}
Set mark {a-zA-Z'} at last line number in [range], Set mark {a-zA-Z'} at last line number in [range],

View File

@@ -472,6 +472,12 @@ Variables:
instead of always being strings. |v:option_old| is now the old global value instead of always being strings. |v:option_old| is now the old global value
for all global-local options, instead of just string global-local options. for all global-local options, instead of just string global-local options.
Prompt-Buffer:
- supports multiline inputs.
- supports multiline paste.
- supports undo/redo on current prompt.
- supports normal o/O operations.
Vimscript: Vimscript:
- |:redir| nested in |execute()| works. - |:redir| nested in |execute()| works.

View File

@@ -878,6 +878,7 @@ static void free_buffer(buf_T *buf)
clear_fmark(&buf->b_last_cursor, 0); clear_fmark(&buf->b_last_cursor, 0);
clear_fmark(&buf->b_last_insert, 0); clear_fmark(&buf->b_last_insert, 0);
clear_fmark(&buf->b_last_change, 0); clear_fmark(&buf->b_last_change, 0);
clear_fmark(&buf->b_prompt_start, 0);
for (size_t i = 0; i < NMARKS; i++) { for (size_t i = 0; i < NMARKS; i++) {
free_fmark(buf->b_namedm[i]); free_fmark(buf->b_namedm[i]);
} }
@@ -2024,6 +2025,7 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
buf->b_prompt_callback.type = kCallbackNone; buf->b_prompt_callback.type = kCallbackNone;
buf->b_prompt_interrupt.type = kCallbackNone; buf->b_prompt_interrupt.type = kCallbackNone;
buf->b_prompt_text = NULL; buf->b_prompt_text = NULL;
clear_fmark(&buf->b_prompt_start, 0);
return buf; return buf;
} }

View File

@@ -699,6 +699,7 @@ struct file_buffer {
Callback b_prompt_interrupt; // set by prompt_setinterrupt() Callback b_prompt_interrupt; // set by prompt_setinterrupt()
int b_prompt_insert; // value for restart_edit when entering int b_prompt_insert; // value for restart_edit when entering
// a prompt buffer window. // a prompt buffer window.
fmark_T b_prompt_start; // Start of the editable area of a prompt buffer.
synblock_T b_s; // Info related to syntax highlighting. w_s synblock_T b_s; // Info related to syntax highlighting. w_s
// normally points to this, but some windows // normally points to this, but some windows

View File

@@ -1746,7 +1746,27 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
curbuf_splice_pending++; curbuf_splice_pending++;
old_cursor = curwin->w_cursor; old_cursor = curwin->w_cursor;
int old_cmod_flags = cmdmod.cmod_flags;
char *prompt_moved = NULL;
if (dir == BACKWARD) { if (dir == BACKWARD) {
// In case of prompt buffer, when we are applying 'normal O' operation on line of prompt,
// we can't add a new line before the prompt. In this case, we move the prompt text one
// line below and create a new prompt line as current line.
if (bt_prompt(curbuf) && curwin->w_cursor.lnum == curbuf->b_prompt_start.mark.lnum) {
char *prompt_line = ml_get(curwin->w_cursor.lnum);
char *prompt = prompt_text();
size_t prompt_len = strlen(prompt);
if (strncmp(prompt_line, prompt, prompt_len) == 0) {
STRMOVE(prompt_line, prompt_line + prompt_len);
// We are moving the lines but the b_prompt_start mark needs to stay in
// place so freezing marks before making the move.
cmdmod.cmod_flags = cmdmod.cmod_flags | CMOD_LOCKMARKS;
ml_replace(curwin->w_cursor.lnum, prompt_line, true);
prompt_moved = concat_str(prompt, p_extra);
p_extra = prompt_moved;
}
}
curwin->w_cursor.lnum--; curwin->w_cursor.lnum--;
} }
if ((State & VREPLACE_FLAG) == 0 || old_cursor.lnum >= orig_line_count) { if ((State & VREPLACE_FLAG) == 0 || old_cursor.lnum >= orig_line_count) {
@@ -1936,6 +1956,8 @@ theend:
xfree(saved_line); xfree(saved_line);
xfree(next_line); xfree(next_line);
xfree(allocated); xfree(allocated);
xfree(prompt_moved);
cmdmod.cmod_flags = old_cmod_flags;
return retval; return retval;
} }

View File

@@ -1077,7 +1077,7 @@ check_pum:
cmdwin_result = CAR; cmdwin_result = CAR;
return 0; return 0;
} }
if (bt_prompt(curbuf)) { if ((mod_mask & MOD_MASK_SHIFT) == 0 && bt_prompt(curbuf)) {
invoke_prompt_callback(); invoke_prompt_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
@@ -1532,9 +1532,14 @@ static void init_prompt(int cmdchar_todo)
{ {
char *prompt = prompt_text(); char *prompt = prompt_text();
if (curwin->w_cursor.lnum < curbuf->b_prompt_start.mark.lnum) {
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
coladvance(curwin, MAXCOL);
}
char *text = get_cursor_line_ptr(); char *text = get_cursor_line_ptr();
if (strncmp(text, prompt, strlen(prompt)) != 0) { if ((curbuf->b_prompt_start.mark.lnum == curwin->w_cursor.lnum
&& strncmp(text, prompt, strlen(prompt)) != 0)
|| curbuf->b_prompt_start.mark.lnum > curwin->w_cursor.lnum) {
// prompt is missing, insert it or append a line with it // prompt is missing, insert it or append a line with it
if (*text == NUL) { if (*text == NUL) {
ml_replace(curbuf->b_ml.ml_line_count, prompt, true); ml_replace(curbuf->b_ml.ml_line_count, prompt, true);
@@ -1547,8 +1552,9 @@ static void init_prompt(int cmdchar_todo)
} }
// Insert always starts after the prompt, allow editing text after it. // Insert always starts after the prompt, allow editing text after it.
if (Insstart_orig.lnum != curwin->w_cursor.lnum || Insstart_orig.col != (colnr_T)strlen(prompt)) { if (Insstart_orig.lnum != curbuf->b_prompt_start.mark.lnum
Insstart.lnum = curwin->w_cursor.lnum; || Insstart_orig.col != (colnr_T)strlen(prompt)) {
Insstart.lnum = curbuf->b_prompt_start.mark.lnum;
Insstart.col = (colnr_T)strlen(prompt); Insstart.col = (colnr_T)strlen(prompt);
Insstart_orig = Insstart; Insstart_orig = Insstart;
Insstart_textlen = Insstart.col; Insstart_textlen = Insstart.col;
@@ -1559,7 +1565,9 @@ static void init_prompt(int cmdchar_todo)
if (cmdchar_todo == 'A') { if (cmdchar_todo == 'A') {
coladvance(curwin, MAXCOL); coladvance(curwin, MAXCOL);
} }
if (curbuf->b_prompt_start.mark.lnum == curwin->w_cursor.lnum) {
curwin->w_cursor.col = MAX(curwin->w_cursor.col, (colnr_T)strlen(prompt)); curwin->w_cursor.col = MAX(curwin->w_cursor.col, (colnr_T)strlen(prompt));
}
// Make sure the cursor is in a valid position. // Make sure the cursor is in a valid position.
check_cursor(curwin); check_cursor(curwin);
} }
@@ -1568,8 +1576,9 @@ static void init_prompt(int cmdchar_todo)
bool prompt_curpos_editable(void) bool prompt_curpos_editable(void)
FUNC_ATTR_PURE FUNC_ATTR_PURE
{ {
return curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count return curwin->w_cursor.lnum > curbuf->b_prompt_start.mark.lnum
&& curwin->w_cursor.col >= (int)strlen(prompt_text()); || (curwin->w_cursor.lnum == curbuf->b_prompt_start.mark.lnum
&& curwin->w_cursor.col >= (int)strlen(prompt_text()));
} }
// Undo the previous edit_putchar(). // Undo the previous edit_putchar().

View File

@@ -78,6 +78,7 @@
#include "nvim/strings.h" #include "nvim/strings.h"
#include "nvim/tag.h" #include "nvim/tag.h"
#include "nvim/types_defs.h" #include "nvim/types_defs.h"
#include "nvim/undo.h"
#include "nvim/version.h" #include "nvim/version.h"
#include "nvim/vim_defs.h" #include "nvim/vim_defs.h"
#include "nvim/window.h" #include "nvim/window.h"
@@ -8663,30 +8664,46 @@ void invoke_prompt_callback(void)
{ {
typval_T rettv; typval_T rettv;
typval_T argv[2]; typval_T argv[2];
linenr_T lnum = curbuf->b_ml.ml_line_count; 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 // Add a new line for the prompt before invoking the callback, so that
// text can always be inserted above the last line. // text can always be inserted above the last line.
ml_append(lnum, "", 0, false); ml_append(lnum_last, "", 0, false);
appended_lines_mark(lnum, 1); appended_lines_mark(lnum_last, 1);
curwin->w_cursor.lnum = lnum + 1; curwin->w_cursor.lnum = lnum_last + 1;
curwin->w_cursor.col = 0; curwin->w_cursor.col = 0;
if (curbuf->b_prompt_callback.type == kCallbackNone) { if (curbuf->b_prompt_callback.type == kCallbackNone) {
return; goto theend;
} }
char *text = ml_get(lnum); char *text = ml_get(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);
} }
char *full_text = xstrdup(text);
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));
xfree(half_text);
}
argv[0].v_type = VAR_STRING; argv[0].v_type = VAR_STRING;
argv[0].vval.v_string = xstrdup(text); argv[0].vval.v_string = full_text;
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);
tv_clear(&argv[0]); tv_clear(&argv[0]);
tv_clear(&rettv); tv_clear(&rettv);
theend:
// clear undo history on submit
u_clearallandblockfree(curbuf);
pos_T next_prompt = { .lnum = curbuf->b_ml.ml_line_count, .col = 1, .coladd = 0 };
RESET_FMARK(&curbuf->b_prompt_start, next_prompt, 0, ((fmarkv_T)INIT_FMARKV));
} }
/// @return true when the interrupt callback was invoked. /// @return true when the interrupt callback was invoked.

View File

@@ -469,6 +469,9 @@ fmark_T *mark_get_local(buf_T *buf, win_T *win, int name)
// to where last change was made // to where last change was made
} else if (name == '.') { } else if (name == '.') {
mark = &buf->b_last_change; mark = &buf->b_last_change;
// prompt start location
} else if (name == ':' && bt_prompt(buf)) {
mark = &(buf->b_prompt_start);
// Mark that are actually not marks but motions, e.g {, }, (, ), ... // Mark that are actually not marks but motions, e.g {, }, (, ), ...
} else { } else {
mark = mark_get_motion(buf, win, name); mark = mark_get_motion(buf, win, name);
@@ -908,6 +911,9 @@ void ex_marks(exarg_T *eap)
show_one_mark(']', arg, &curbuf->b_op_end, NULL, true); show_one_mark(']', arg, &curbuf->b_op_end, NULL, true);
show_one_mark('^', arg, &curbuf->b_last_insert.mark, NULL, true); show_one_mark('^', arg, &curbuf->b_last_insert.mark, NULL, true);
show_one_mark('.', arg, &curbuf->b_last_change.mark, NULL, true); show_one_mark('.', arg, &curbuf->b_last_change.mark, NULL, true);
if (bt_prompt(curbuf)) {
show_one_mark(':', arg, &curbuf->b_prompt_start.mark, NULL, true);
}
// Show the marks as where they will jump to. // Show the marks as where they will jump to.
pos_T *startp = &curbuf->b_visual.vi_start; pos_T *startp = &curbuf->b_visual.vi_start;
@@ -1030,6 +1036,9 @@ void ex_delmarks(exarg_T *eap)
case '^': case '^':
clear_fmark(&curbuf->b_last_insert, timestamp); clear_fmark(&curbuf->b_last_insert, timestamp);
break; break;
case ':':
// Readonly mark. No deletion allowed.
break;
case '.': case '.':
clear_fmark(&curbuf->b_last_change, timestamp); clear_fmark(&curbuf->b_last_change, timestamp);
break; break;
@@ -1223,6 +1232,11 @@ void mark_adjust_buf(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount
ONE_ADJUST(&(buf->b_last_cursor.mark.lnum)); ONE_ADJUST(&(buf->b_last_cursor.mark.lnum));
} }
// on prompt buffer adjust the last prompt start location mark
if (bt_prompt(curbuf)) {
ONE_ADJUST_NODEL(&(buf->b_prompt_start.mark.lnum));
}
// list of change positions // list of change positions
for (int i = 0; i < buf->b_changelistlen; i++) { for (int i = 0; i < buf->b_changelistlen; i++) {
ONE_ADJUST_NODEL(&(buf->b_changelist[i].mark.lnum)); ONE_ADJUST_NODEL(&(buf->b_changelist[i].mark.lnum));
@@ -1712,6 +1726,9 @@ bool mark_set_local(const char name, buf_T *const buf, const fmark_T fm, const b
fm_tgt = &(buf->b_last_cursor); fm_tgt = &(buf->b_last_cursor);
} else if (name == '^') { } else if (name == '^') {
fm_tgt = &(buf->b_last_insert); fm_tgt = &(buf->b_last_insert);
} else if (name == ':') {
// Readonly mark for prompt buffer. Can't be edited on user side.
return false;
} else if (name == '.') { } else if (name == '.') {
fm_tgt = &(buf->b_last_change); fm_tgt = &(buf->b_last_change);
} else { } else {

View File

@@ -4458,10 +4458,6 @@ static void nv_kundo(cmdarg_T *cap)
return; return;
} }
if (bt_prompt(curbuf)) {
clearopbeep(cap->oap);
return;
}
u_undo(cap->count1); u_undo(cap->count1);
curwin->w_set_curswant = true; curwin->w_set_curswant = true;
} }
@@ -6481,9 +6477,16 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent)
} }
if (bt_prompt(curbuf) && !prompt_curpos_editable()) { if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
if (curwin->w_cursor.lnum == curbuf->b_prompt_start.mark.lnum) {
curwin->w_cursor.col = (int)strlen(prompt_text());
// Since we've shifted the cursor to the first editable char. We want to
// paste before that.
cap->cmdchar = 'P';
} else {
clearopbeep(cap->oap); clearopbeep(cap->oap);
return; return;
} }
}
if (fix_indent) { if (fix_indent) {
dir = (cap->cmdchar == ']' && cap->nchar == 'p') dir = (cap->cmdchar == ']' && cap->nchar == 'p')
@@ -6613,7 +6616,7 @@ static void nv_open(cmdarg_T *cap)
} else if (VIsual_active) { } else if (VIsual_active) {
// switch start and end of visual/ // switch start and end of visual/
v_swap_corners(cap->cmdchar); v_swap_corners(cap->cmdchar);
} else if (bt_prompt(curbuf)) { } else if (bt_prompt(curbuf) && curwin->w_cursor.lnum < curbuf->b_prompt_start.mark.lnum) {
clearopbeep(cap->oap); clearopbeep(cap->oap);
} else { } else {
n_opencmd(cap); n_opencmd(cap);

View File

@@ -28,6 +28,7 @@
#include "nvim/indent_c.h" #include "nvim/indent_c.h"
#include "nvim/insexpand.h" #include "nvim/insexpand.h"
#include "nvim/macros_defs.h" #include "nvim/macros_defs.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h" #include "nvim/mbyte.h"
#include "nvim/memline.h" #include "nvim/memline.h"
#include "nvim/memory.h" #include "nvim/memory.h"
@@ -695,6 +696,11 @@ const char *did_set_buftype(optset_T *args)
|| opt_strings_flags(buf->b_p_bt, opt_bt_values, NULL, false) != OK) { || opt_strings_flags(buf->b_p_bt, opt_bt_values, NULL, false) != OK) {
return e_invarg; return e_invarg;
} }
// buftype=prompt: set the prompt start position to lastline.
if (buf->b_p_bt[0] == 'p') {
pos_T next_prompt = { .lnum = buf->b_ml.ml_line_count, .col = 1, .coladd = 0 };
RESET_FMARK(&buf->b_prompt_start, next_prompt, 0, ((fmarkv_T)INIT_FMARKV));
}
if (win->w_status_height || global_stl_height()) { if (win->w_status_height || global_stl_height()) {
win->w_redr_status = true; win->w_redr_status = true;
redraw_later(win, UPD_VALID); redraw_later(win, UPD_VALID);

View File

@@ -3,6 +3,7 @@ local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
local feed = n.feed local feed = n.feed
local fn = n.call
local source = n.source local source = n.source
local clear = n.clear local clear = n.clear
local command = n.command local command = n.command
@@ -31,7 +32,7 @@ describe('prompt buffer', function()
close close
else else
" Add the output above the current prompt. " Add the output above the current prompt.
call append(line("$") - 1, 'Command: "' . a:text . '"') call append(line("$") - 1, split('Command: "' . a:text . '"', '\n'))
" Reset &modified to allow the buffer to be closed. " Reset &modified to allow the buffer to be closed.
set nomodified set nomodified
call timer_start(20, {id -> TimerFunc(a:text)}) call timer_start(20, {id -> TimerFunc(a:text)})
@@ -40,7 +41,7 @@ describe('prompt buffer', function()
func TimerFunc(text) func TimerFunc(text)
" Add the output above the current prompt. " Add the output above the current prompt.
call append(line("$") - 1, 'Result: "' . a:text .'"') call append(line("$") - 1, split('Result: "' . a:text .'"', '\n'))
" Reset &modified to allow the buffer to be closed. " Reset &modified to allow the buffer to be closed.
set nomodified set nomodified
endfunc endfunc
@@ -245,4 +246,280 @@ describe('prompt buffer', function()
Leave Leave
Close]]) Close]])
end) end)
it('can insert mutli line text', function()
source_script()
feed('line 1<s-cr>line 2<s-cr>line 3')
screen:expect([[
cmd: line 1 |
line 2 |
line 3^ |
{1:~ }|
{3:[Prompt] [+] }|
other buffer |
{1:~ }|*3
{5:-- INSERT --} |
]])
feed('<cr>')
-- submiting multiline text works
screen:expect([[
Result: "line 1 |
line 2 |
line 3" |
cmd: ^ |
{3:[Prompt] }|
other buffer |
{1:~ }|*3
{5:-- INSERT --} |
]])
end)
it('can paste multiline text', function()
source_script()
fn('setreg', 'a', 'line 1\nline 2\nline 3')
feed('<esc>"ap')
screen:expect([[
cmd: ^line 1 |
line 2 |
line 3 |
{1:~ }|
{3:[Prompt] [+] }|
other buffer |
{1:~ }|*3
|
]])
feed('i<cr>')
screen:expect([[
Result: "line 1 |
line 2 |
line 3" |
cmd: ^ |
{3:[Prompt] }|
other buffer |
{1:~ }|*3
{5:-- INSERT --} |
]])
end)
it('undo works for current prompt', function()
source_script()
-- text editiing alowed in current prompt
feed('tests-initial<esc>')
feed('bimiddle-<esc>')
screen:expect([[
cmd: tests-middle^-initial|
{1:~ }|*3
{3:[Prompt] [+] }|
other buffer |
{1:~ }|*3
|
]])
feed('Fdx')
screen:expect([[
cmd: tests-mid^le-initial |
{1:~ }|*3
{3:[Prompt] [+] }|
other buffer |
{1:~ }|*3
|
]])
-- can undo edits until prompt has been submitted
feed('u')
screen:expect([[
cmd: tests-mid^dle-initial|
{1:~ }|*3
{3:[Prompt] [+] }|
other buffer |
{1:~ }|*3
1 change; {MATCH:.*} |
]])
feed('u')
screen:expect([[
cmd: tests-^initial |
{1:~ }|*3
{3:[Prompt] [+] }|
other buffer |
{1:~ }|*3
1 change; {MATCH:.*} |
]])
feed('i<cr><esc>')
screen:expect([[
cmd: tests-initial |
Command: "tests-initial" |
Result: "tests-initial" |
cmd:^ |
{3:[Prompt] }|
other buffer |
{1:~ }|*3
|
]])
-- after submit undo does nothing
feed('u')
screen:expect([[
cmd: tests-initial |
Command: "tests-initial" |
cmd:^ |
{1:~ }|
{3:[Prompt] [+] }|
other buffer |
{1:~ }|*3
1 line {MATCH:.*} |
]])
end)
it('o/O can create new lines', function()
source_script()
feed('line 1<s-cr>line 2<s-cr>line 3')
screen:expect([[
cmd: line 1 |
line 2 |
line 3^ |
{1:~ }|
{3:[Prompt] [+] }|
other buffer |
{1:~ }|*3
{5:-- INSERT --} |
]])
feed('<esc>koafter')
screen:expect([[
cmd: line 1 |
line 2 |
after^ |
line 3 |
{3:[Prompt] [+] }|
other buffer |
{1:~ }|*3
{5:-- INSERT --} |
]])
feed('<esc>kObefore')
screen:expect([[
cmd: line 1 |
before^ |
line 2 |
after |
{3:[Prompt] [+] }|
other buffer |
{1:~ }|*3
{5:-- INSERT --} |
]])
feed('<cr>')
screen:expect([[
line 2 |
after |
line 3" |
cmd: ^ |
{3:[Prompt] }|
other buffer |
{1:~ }|*3
{5:-- INSERT --} |
]])
feed('line 4<s-cr>line 5')
feed('<esc>k0oafter prompt')
screen:expect([[
after |
line 3" |
cmd: line 4 |
after prompt^ |
{3:[Prompt] [+] }|
other buffer |
{1:~ }|*3
{5:-- INSERT --} |
]])
feed('<esc>k0Oat prompt')
screen:expect([[
after |
line 3" |
cmd: at prompt^ |
line 4 |
{3:[Prompt] [+] }|
other buffer |
{1:~ }|*3
{5:-- INSERT --} |
]])
feed('<cr>')
screen:expect([[
line 4 |
after prompt |
line 5" |
cmd: ^ |
{3:[Prompt] }|
other buffer |
{1:~ }|*3
{5:-- INSERT --} |
]])
end)
it('deleting prompt adds it back on insert', function()
source_script()
feed('asdf')
screen:expect([[
cmd: asdf^ |
{1:~ }|*3
{3:[Prompt] [+] }|
other buffer |
{1:~ }|*3
{5:-- INSERT --} |
]])
feed('<esc>ddi')
screen:expect([[
cmd: ^ |
{1:~ }|*3
{3:[Prompt] [+] }|
other buffer |
{1:~ }|*3
{5:-- INSERT --} |
]])
feed('asdf')
screen:expect([[
cmd: asdf^ |
{1:~ }|*3
{3:[Prompt] [+] }|
other buffer |
{1:~ }|*3
{5:-- INSERT --} |
]])
feed('<esc>cc')
screen:expect([[
cmd: ^ |
{1:~ }|*3
{3:[Prompt] [+] }|
other buffer |
{1:~ }|*3
{5:-- INSERT --} |
]])
end)
it(': mark follows current prompt', function()
source_script()
feed('asdf')
eq({ 1, 1 }, api.nvim_buf_get_mark(0, ':'))
feed('<cr>')
eq({ 3, 1 }, api.nvim_buf_get_mark(0, ':'))
end)
it(': mark only available in prompt buffer', function()
source_script()
feed('asdf')
eq({ 1, 1 }, api.nvim_buf_get_mark(0, ':'))
source('set buftype=')
eq(false, pcall(api.nvim_buf_get_mark, 0, ':'))
end)
end) end)

View File

@@ -166,11 +166,12 @@ func Test_prompt_buffer_edit()
normal! i normal! i
call assert_beeps('normal! dd') call assert_beeps('normal! dd')
call assert_beeps('normal! ~') call assert_beeps('normal! ~')
call assert_beeps('normal! o') " Nvim: these operations are supported
call assert_beeps('normal! O') " call assert_beeps('normal! o')
call assert_beeps('normal! p') " call assert_beeps('normal! O')
call assert_beeps('normal! P') " call assert_beeps('normal! p')
call assert_beeps('normal! u') " call assert_beeps('normal! P')
" call assert_beeps('normal! u')
call assert_beeps('normal! ra') call assert_beeps('normal! ra')
call assert_beeps('normal! s') call assert_beeps('normal! s')
call assert_beeps('normal! S') call assert_beeps('normal! S')