mirror of
https://github.com/neovim/neovim
synced 2025-07-15 16:51:49 +00:00
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:
@ -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
|
||||
the job and the job output is directly displayed in the window.
|
||||
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.
|
||||
|
||||
A prompt buffer is created by setting 'buftype' to "prompt". You would
|
||||
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
|
||||
buffer. When pressing Enter in the prompt line the callback set with
|
||||
|prompt_setcallback()| is invoked. It would 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).
|
||||
The user can edit and enter text at the very last line of the buffer. When
|
||||
pressing Enter in the prompt line the callback set with |prompt_setcallback()|
|
||||
is invoked. To enter multiple lines user can use Shift+Enter that'd add a new
|
||||
line. The final Enter submits the lines to |prompt_setcallback()|. It would
|
||||
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
|
||||
buffer is not modifiable with Normal mode commands. It can be modified by
|
||||
calling functions, such as |append()|. Using other commands may mess up the
|
||||
buffer.
|
||||
Only the text after the last prompt, is editable. The rest of the buffer is
|
||||
not modifiable with Normal mode commands. It can be modified by calling
|
||||
functions, such as |append()|. Using other commands may mess up the buffer.
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
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*
|
||||
:[range]ma[rk] {a-zA-Z'}
|
||||
Set mark {a-zA-Z'} at last line number in [range],
|
||||
|
@ -472,6 +472,12 @@ Variables:
|
||||
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.
|
||||
|
||||
Prompt-Buffer:
|
||||
- supports multiline inputs.
|
||||
- supports multiline paste.
|
||||
- supports undo/redo on current prompt.
|
||||
- supports normal o/O operations.
|
||||
|
||||
Vimscript:
|
||||
- |:redir| nested in |execute()| works.
|
||||
|
||||
|
@ -878,6 +878,7 @@ static void free_buffer(buf_T *buf)
|
||||
clear_fmark(&buf->b_last_cursor, 0);
|
||||
clear_fmark(&buf->b_last_insert, 0);
|
||||
clear_fmark(&buf->b_last_change, 0);
|
||||
clear_fmark(&buf->b_prompt_start, 0);
|
||||
for (size_t i = 0; i < NMARKS; 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_interrupt.type = kCallbackNone;
|
||||
buf->b_prompt_text = NULL;
|
||||
clear_fmark(&buf->b_prompt_start, 0);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
@ -699,6 +699,7 @@ struct file_buffer {
|
||||
Callback b_prompt_interrupt; // set by prompt_setinterrupt()
|
||||
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.
|
||||
|
||||
synblock_T b_s; // Info related to syntax highlighting. w_s
|
||||
// normally points to this, but some windows
|
||||
|
@ -1746,7 +1746,27 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
|
||||
|
||||
curbuf_splice_pending++;
|
||||
old_cursor = curwin->w_cursor;
|
||||
int old_cmod_flags = cmdmod.cmod_flags;
|
||||
char *prompt_moved = NULL;
|
||||
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--;
|
||||
}
|
||||
if ((State & VREPLACE_FLAG) == 0 || old_cursor.lnum >= orig_line_count) {
|
||||
@ -1936,6 +1956,8 @@ theend:
|
||||
xfree(saved_line);
|
||||
xfree(next_line);
|
||||
xfree(allocated);
|
||||
xfree(prompt_moved);
|
||||
cmdmod.cmod_flags = old_cmod_flags;
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -1077,7 +1077,7 @@ check_pum:
|
||||
cmdwin_result = CAR;
|
||||
return 0;
|
||||
}
|
||||
if (bt_prompt(curbuf)) {
|
||||
if ((mod_mask & MOD_MASK_SHIFT) == 0 && bt_prompt(curbuf)) {
|
||||
invoke_prompt_callback();
|
||||
if (!bt_prompt(curbuf)) {
|
||||
// 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();
|
||||
|
||||
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
|
||||
if (curwin->w_cursor.lnum < curbuf->b_prompt_start.mark.lnum) {
|
||||
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
|
||||
coladvance(curwin, MAXCOL);
|
||||
}
|
||||
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
|
||||
if (*text == NUL) {
|
||||
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.
|
||||
if (Insstart_orig.lnum != curwin->w_cursor.lnum || Insstart_orig.col != (colnr_T)strlen(prompt)) {
|
||||
Insstart.lnum = curwin->w_cursor.lnum;
|
||||
if (Insstart_orig.lnum != curbuf->b_prompt_start.mark.lnum
|
||||
|| Insstart_orig.col != (colnr_T)strlen(prompt)) {
|
||||
Insstart.lnum = curbuf->b_prompt_start.mark.lnum;
|
||||
Insstart.col = (colnr_T)strlen(prompt);
|
||||
Insstart_orig = Insstart;
|
||||
Insstart_textlen = Insstart.col;
|
||||
@ -1559,7 +1565,9 @@ static void init_prompt(int cmdchar_todo)
|
||||
if (cmdchar_todo == 'A') {
|
||||
coladvance(curwin, MAXCOL);
|
||||
}
|
||||
curwin->w_cursor.col = MAX(curwin->w_cursor.col, (colnr_T)strlen(prompt));
|
||||
if (curbuf->b_prompt_start.mark.lnum == curwin->w_cursor.lnum) {
|
||||
curwin->w_cursor.col = MAX(curwin->w_cursor.col, (colnr_T)strlen(prompt));
|
||||
}
|
||||
// Make sure the cursor is in a valid position.
|
||||
check_cursor(curwin);
|
||||
}
|
||||
@ -1568,8 +1576,9 @@ static void init_prompt(int cmdchar_todo)
|
||||
bool prompt_curpos_editable(void)
|
||||
FUNC_ATTR_PURE
|
||||
{
|
||||
return curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count
|
||||
&& curwin->w_cursor.col >= (int)strlen(prompt_text());
|
||||
return curwin->w_cursor.lnum > curbuf->b_prompt_start.mark.lnum
|
||||
|| (curwin->w_cursor.lnum == curbuf->b_prompt_start.mark.lnum
|
||||
&& curwin->w_cursor.col >= (int)strlen(prompt_text()));
|
||||
}
|
||||
|
||||
// Undo the previous edit_putchar().
|
||||
|
@ -78,6 +78,7 @@
|
||||
#include "nvim/strings.h"
|
||||
#include "nvim/tag.h"
|
||||
#include "nvim/types_defs.h"
|
||||
#include "nvim/undo.h"
|
||||
#include "nvim/version.h"
|
||||
#include "nvim/vim_defs.h"
|
||||
#include "nvim/window.h"
|
||||
@ -8663,30 +8664,46 @@ void invoke_prompt_callback(void)
|
||||
{
|
||||
typval_T rettv;
|
||||
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
|
||||
// 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;
|
||||
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) {
|
||||
return;
|
||||
goto theend;
|
||||
}
|
||||
char *text = ml_get(lnum);
|
||||
char *text = ml_get(lnum_start);
|
||||
char *prompt = prompt_text();
|
||||
if (strlen(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].vval.v_string = xstrdup(text);
|
||||
argv[0].vval.v_string = full_text;
|
||||
argv[1].v_type = VAR_UNKNOWN;
|
||||
|
||||
callback_call(&curbuf->b_prompt_callback, 1, argv, &rettv);
|
||||
tv_clear(&argv[0]);
|
||||
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.
|
||||
|
@ -469,6 +469,9 @@ fmark_T *mark_get_local(buf_T *buf, win_T *win, int name)
|
||||
// to where last change was made
|
||||
} else if (name == '.') {
|
||||
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 {, }, (, ), ...
|
||||
} else {
|
||||
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_last_insert.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.
|
||||
pos_T *startp = &curbuf->b_visual.vi_start;
|
||||
@ -1030,6 +1036,9 @@ void ex_delmarks(exarg_T *eap)
|
||||
case '^':
|
||||
clear_fmark(&curbuf->b_last_insert, timestamp);
|
||||
break;
|
||||
case ':':
|
||||
// Readonly mark. No deletion allowed.
|
||||
break;
|
||||
case '.':
|
||||
clear_fmark(&curbuf->b_last_change, timestamp);
|
||||
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));
|
||||
}
|
||||
|
||||
// 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
|
||||
for (int i = 0; i < buf->b_changelistlen; i++) {
|
||||
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);
|
||||
} else if (name == '^') {
|
||||
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 == '.') {
|
||||
fm_tgt = &(buf->b_last_change);
|
||||
} else {
|
||||
|
@ -4458,10 +4458,6 @@ static void nv_kundo(cmdarg_T *cap)
|
||||
return;
|
||||
}
|
||||
|
||||
if (bt_prompt(curbuf)) {
|
||||
clearopbeep(cap->oap);
|
||||
return;
|
||||
}
|
||||
u_undo(cap->count1);
|
||||
curwin->w_set_curswant = true;
|
||||
}
|
||||
@ -6481,8 +6477,15 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent)
|
||||
}
|
||||
|
||||
if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
|
||||
clearopbeep(cap->oap);
|
||||
return;
|
||||
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);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (fix_indent) {
|
||||
@ -6613,7 +6616,7 @@ static void nv_open(cmdarg_T *cap)
|
||||
} else if (VIsual_active) {
|
||||
// switch start and end of visual/
|
||||
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);
|
||||
} else {
|
||||
n_opencmd(cap);
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "nvim/indent_c.h"
|
||||
#include "nvim/insexpand.h"
|
||||
#include "nvim/macros_defs.h"
|
||||
#include "nvim/mark.h"
|
||||
#include "nvim/mbyte.h"
|
||||
#include "nvim/memline.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) {
|
||||
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()) {
|
||||
win->w_redr_status = true;
|
||||
redraw_later(win, UPD_VALID);
|
||||
|
@ -3,6 +3,7 @@ local n = require('test.functional.testnvim')()
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
|
||||
local feed = n.feed
|
||||
local fn = n.call
|
||||
local source = n.source
|
||||
local clear = n.clear
|
||||
local command = n.command
|
||||
@ -31,7 +32,7 @@ describe('prompt buffer', function()
|
||||
close
|
||||
else
|
||||
" 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.
|
||||
set nomodified
|
||||
call timer_start(20, {id -> TimerFunc(a:text)})
|
||||
@ -40,7 +41,7 @@ describe('prompt buffer', function()
|
||||
|
||||
func TimerFunc(text)
|
||||
" 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.
|
||||
set nomodified
|
||||
endfunc
|
||||
@ -245,4 +246,280 @@ describe('prompt buffer', function()
|
||||
Leave
|
||||
Close]])
|
||||
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)
|
||||
|
@ -166,11 +166,12 @@ func Test_prompt_buffer_edit()
|
||||
normal! i
|
||||
call assert_beeps('normal! dd')
|
||||
call assert_beeps('normal! ~')
|
||||
call assert_beeps('normal! o')
|
||||
call assert_beeps('normal! O')
|
||||
call assert_beeps('normal! p')
|
||||
call assert_beeps('normal! P')
|
||||
call assert_beeps('normal! u')
|
||||
" Nvim: these operations are supported
|
||||
" call assert_beeps('normal! o')
|
||||
" call assert_beeps('normal! O')
|
||||
" call assert_beeps('normal! p')
|
||||
" call assert_beeps('normal! P')
|
||||
" call assert_beeps('normal! u')
|
||||
call assert_beeps('normal! ra')
|
||||
call assert_beeps('normal! s')
|
||||
call assert_beeps('normal! S')
|
||||
|
Reference in New Issue
Block a user