mirror of
https://github.com/neovim/neovim.git
synced 2025-09-28 14:08:32 +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
|
- 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
|
||||||
|
@@ -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],
|
||||||
|
@@ -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.
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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().
|
||||||
|
@@ -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.
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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);
|
||||||
|
@@ -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);
|
||||||
|
@@ -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)
|
||||||
|
@@ -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')
|
||||||
|
Reference in New Issue
Block a user