aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/buffer.c24
-rw-r--r--src/nvim/buffer_defs.h6
-rw-r--r--src/nvim/diff.c4
-rw-r--r--src/nvim/edit.c79
-rw-r--r--src/nvim/eval.c56
-rw-r--r--src/nvim/eval.lua4
-rw-r--r--src/nvim/eval/funcs.c98
-rw-r--r--src/nvim/normal.c59
-rw-r--r--src/nvim/ops.c72
-rw-r--r--src/nvim/option.c12
-rw-r--r--src/nvim/undo.c5
11 files changed, 364 insertions, 55 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 19d0cac2d3..790609ab94 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -763,6 +763,9 @@ static void free_buffer(buf_T *buf)
unref_var_dict(buf->b_vars);
aubuflocal_remove(buf);
tv_dict_unref(buf->additional_data);
+ xfree(buf->b_prompt_text);
+ callback_free(&buf->b_prompt_callback);
+ callback_free(&buf->b_prompt_interrupt);
clear_fmark(&buf->b_last_cursor);
clear_fmark(&buf->b_last_insert);
clear_fmark(&buf->b_last_change);
@@ -1876,6 +1879,10 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
}
}
+ buf->b_prompt_callback.type = kCallbackNone;
+ buf->b_prompt_interrupt.type = kCallbackNone;
+ buf->b_prompt_text = NULL;
+
return buf;
}
@@ -4824,6 +4831,12 @@ do_arg_all(
xfree(opened);
}
+// Return TRUE if "buf" is a prompt buffer.
+int bt_prompt(buf_T *buf)
+{
+ return buf != NULL && buf->b_p_bt[0] == 'p';
+}
+
/*
* Open a window for a number of buffers.
*/
@@ -5218,14 +5231,18 @@ bool bt_nofile(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f')
- || buf->b_p_bt[0] == 'a' || buf->terminal);
+ || buf->b_p_bt[0] == 'a'
+ || buf->terminal
+ || buf->b_p_bt[0] == 'p');
}
// Return true if "buf" is a "nowrite", "nofile" or "terminal" buffer.
bool bt_dontwrite(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- return buf != NULL && (buf->b_p_bt[0] == 'n' || buf->terminal);
+ return buf != NULL && (buf->b_p_bt[0] == 'n'
+ || buf->terminal
+ || buf->b_p_bt[0] == 'p');
}
bool bt_dontwrite_msg(const buf_T *const buf)
@@ -5278,6 +5295,9 @@ char_u *buf_spname(buf_T *buf)
if (buf->b_fname != NULL) {
return buf->b_fname;
}
+ if (bt_prompt(buf)) {
+ return (char_u *)_("[Prompt]");
+ }
return (char_u *)_("[Scratch]");
}
if (buf->b_fname == NULL) {
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index a0379740b6..9cdf36e4ed 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -791,6 +791,12 @@ struct file_buffer {
// are not used! Use the B_SPELL macro to
// access b_spell without #ifdef.
+ char_u *b_prompt_text; // set by prompt_setprompt()
+ Callback b_prompt_callback; // set by prompt_setcallback()
+ Callback b_prompt_interrupt; // set by prompt_setinterrupt()
+ int b_prompt_insert; // value for restart_edit when entering
+ // a prompt buffer window.
+
synblock_T b_s; // Info related to syntax highlighting. w_s
// normally points to this, but some windows
// may use a different synblock_T.
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index c31adc01fd..04309444d9 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -2432,6 +2432,10 @@ void nv_diffgetput(bool put, size_t count)
exarg_T ea;
char buf[30];
+ if (bt_prompt(curbuf)) {
+ vim_beep(BO_OPER);
+ return;
+ }
if (count == 0) {
ea.arg = (char_u *)"";
} else {
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index e253905057..cebd08af28 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -574,6 +574,12 @@ static int insert_check(VimState *state)
foldCheckClose();
}
+ int cmdchar_todo = s->cmdchar;
+ if (bt_prompt(curbuf)) {
+ init_prompt(cmdchar_todo);
+ cmdchar_todo = NUL;
+ }
+
// If we inserted a character at the last position of the last line in the
// window, scroll the window one line up. This avoids an extra redraw. This
// is detected when the cursor column is smaller after inserting something.
@@ -817,6 +823,16 @@ static int insert_handle_key(InsertState *s)
s->nomove = true;
return 0; // exit insert mode
}
+ if (s->c == Ctrl_C && bt_prompt(curbuf)) {
+ if (invoke_prompt_interrupt()) {
+ if (!bt_prompt(curbuf)) {
+ // buffer changed to a non-prompt buffer, get out of
+ // Insert mode
+ return 0;
+ }
+ break;
+ }
+ }
// when 'insertmode' set, and not halfway through a mapping, don't leave
// Insert mode
@@ -1143,6 +1159,15 @@ check_pum:
cmdwin_result = CAR;
return 0;
}
+ if (bt_prompt(curbuf)) {
+ invoke_prompt_callback();
+ if (!bt_prompt(curbuf)) {
+ // buffer changed to a non-prompt buffer, get out of
+ // Insert mode
+ return 0;
+ }
+ break;
+ }
if (!ins_eol(s->c) && !p_im) {
return 0; // out of memory
}
@@ -1569,6 +1594,52 @@ void edit_putchar(int c, int highlight)
}
}
+// Return the effective prompt for the current buffer.
+char_u *prompt_text(void)
+{
+ if (curbuf->b_prompt_text == NULL) {
+ return (char_u *)"% ";
+ }
+ return curbuf->b_prompt_text;
+}
+
+// Prepare for prompt mode: Make sure the last line has the prompt text.
+// Move the cursor to this line.
+static void init_prompt(int cmdchar_todo)
+{
+ char_u *prompt = prompt_text();
+ char_u *text;
+
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ text = get_cursor_line_ptr();
+ if (STRNCMP(text, prompt, STRLEN(prompt)) != 0) {
+ // prompt is missing, insert it or append a line with it
+ if (*text == NUL) {
+ ml_replace(curbuf->b_ml.ml_line_count, prompt, true);
+ } else {
+ ml_append(curbuf->b_ml.ml_line_count, prompt, 0, false);
+ }
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ coladvance((colnr_T)MAXCOL);
+ changed_bytes(curbuf->b_ml.ml_line_count, 0);
+ }
+ if (cmdchar_todo == 'A') {
+ coladvance((colnr_T)MAXCOL);
+ }
+ if (cmdchar_todo == 'I' || curwin->w_cursor.col <= (int)STRLEN(prompt)) {
+ curwin->w_cursor.col = STRLEN(prompt);
+ }
+ // Make sure the cursor is in a valid position.
+ check_cursor();
+}
+
+// Return TRUE if the cursor is in the editable position of the prompt line.
+int prompt_curpos_editable(void)
+{
+ return curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count
+ && curwin->w_cursor.col >= (int)STRLEN(prompt_text());
+}
+
/*
* Undo the previous edit_putchar().
*/
@@ -8161,10 +8232,14 @@ static void ins_mouse(int c)
win_T *new_curwin = curwin;
if (curwin != old_curwin && win_valid(old_curwin)) {
- /* Mouse took us to another window. We need to go back to the
- * previous one to stop insert there properly. */
+ // Mouse took us to another window. We need to go back to the
+ // previous one to stop insert there properly.
curwin = old_curwin;
curbuf = curwin->w_buffer;
+ if (bt_prompt(curbuf)) {
+ // Restart Insert mode when re-entering the prompt buffer.
+ curbuf->b_prompt_insert = 'A';
+ }
}
start_arrow(curwin == old_curwin ? &tpos : NULL);
if (curwin != new_curwin && win_valid(new_curwin)) {
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 0dceca671b..ddafe1981f 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -5152,6 +5152,10 @@ bool garbage_collect(bool testing)
}
// buffer ShaDa additional data
ABORTING(set_ref_dict)(buf->additional_data, copyID);
+
+ // buffer callback functions
+ set_ref_in_callback(&buf->b_prompt_callback, copyID, NULL, NULL);
+ set_ref_in_callback(&buf->b_prompt_interrupt, copyID, NULL, NULL);
}
FOR_ALL_TAB_WINDOWS(tp, wp) {
@@ -7315,9 +7319,7 @@ dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr)
return dict;
}
-/*
- * Find window specified by "vp" in tabpage "tp".
- */
+// Find window specified by "vp" in tabpage "tp".
win_T *
find_win_by_nr(
typval_T *vp,
@@ -13687,3 +13689,51 @@ void ex_checkhealth(exarg_T *eap)
xfree(buf);
}
+
+void invoke_prompt_callback(void)
+{
+ typval_T rettv;
+ typval_T argv[2];
+ char_u *text;
+ char_u *prompt;
+ linenr_T lnum = 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, (char_u *)"", 0, false);
+ curwin->w_cursor.lnum = lnum + 1;
+ curwin->w_cursor.col = 0;
+
+ if (curbuf->b_prompt_callback.type == kCallbackNone) {
+ return;
+ }
+ text = ml_get(lnum);
+ prompt = prompt_text();
+ if (STRLEN(text) >= STRLEN(prompt)) {
+ text += STRLEN(prompt);
+ }
+ argv[0].v_type = VAR_STRING;
+ argv[0].vval.v_string = vim_strsave(text);
+ argv[1].v_type = VAR_UNKNOWN;
+
+ callback_call(&curbuf->b_prompt_callback, 1, argv, &rettv);
+ tv_clear(&argv[0]);
+ tv_clear(&rettv);
+}
+
+// Return true When the interrupt callback was invoked.
+bool invoke_prompt_interrupt(void)
+{
+ typval_T rettv;
+ typval_T argv[1];
+
+ if (curbuf->b_prompt_interrupt.type == kCallbackNone) {
+ return false;
+ }
+ argv[0].v_type = VAR_UNKNOWN;
+
+ got_int = false; // don't skip executing commands
+ callback_call(&curbuf->b_prompt_interrupt, 0, argv, &rettv);
+ tv_clear(&rettv);
+ return true;
+}
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index efeac70816..17d9cc56aa 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -82,6 +82,7 @@ return {
ctxset={args={1, 2}},
ctxsize={},
cursor={args={1, 3}},
+ debugbreak={args={1, 1}},
deepcopy={args={1, 2}},
delete={args={1,2}},
deletebufline={args={2,3}},
@@ -243,6 +244,9 @@ return {
pow={args=2},
prevnonblank={args=1},
printf={args=varargs(1)},
+ prompt_setcallback={args={2, 2}},
+ prompt_setinterrupt={args={2, 2}},
+ prompt_setprompt={args={2, 2}},
pum_getpos={},
pumvisible={},
py3eval={args=1},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 8232136783..36782e4223 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -1408,9 +1408,31 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = 0;
}
-/*
- * "deepcopy()" function
- */
+// "debugbreak()" function
+static void f_debugbreak(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ int pid;
+
+ rettv->vval.v_number = FAIL;
+ pid = (int)tv_get_number(&argvars[0]);
+ if (pid == 0) {
+ EMSG(_(e_invarg));
+ } else {
+#ifdef WIN32
+ HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
+
+ if (hProcess != NULL) {
+ DebugBreakProcess(hProcess);
+ CloseHandle(hProcess);
+ rettv->vval.v_number = OK;
+ }
+#else
+ uv_kill(pid, SIGINT);
+#endif
+ }
+}
+
+// "deepcopy()" function
static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int noref = 0;
@@ -6076,6 +6098,76 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+// "prompt_setcallback({buffer}, {callback})" function
+static void f_prompt_setcallback(typval_T *argvars,
+ typval_T *rettv, FunPtr fptr)
+{
+ buf_T *buf;
+ Callback prompt_callback = { .type = kCallbackNone };
+
+ if (check_secure()) {
+ return;
+ }
+ buf = tv_get_buf(&argvars[0], false);
+ if (buf == NULL) {
+ return;
+ }
+
+ if (argvars[1].v_type != VAR_STRING || *argvars[1].vval.v_string != NUL) {
+ if (!callback_from_typval(&prompt_callback, &argvars[1])) {
+ return;
+ }
+ }
+
+ callback_free(&buf->b_prompt_callback);
+ buf->b_prompt_callback = prompt_callback;
+}
+
+// "prompt_setinterrupt({buffer}, {callback})" function
+static void f_prompt_setinterrupt(typval_T *argvars,
+ typval_T *rettv, FunPtr fptr)
+{
+ buf_T *buf;
+ Callback interrupt_callback = { .type = kCallbackNone };
+
+ if (check_secure()) {
+ return;
+ }
+ buf = tv_get_buf(&argvars[0], false);
+ if (buf == NULL) {
+ return;
+ }
+
+ if (argvars[1].v_type != VAR_STRING || *argvars[1].vval.v_string != NUL) {
+ if (!callback_from_typval(&interrupt_callback, &argvars[1])) {
+ return;
+ }
+ }
+
+ callback_free(&buf->b_prompt_interrupt);
+ buf->b_prompt_interrupt= interrupt_callback;
+}
+
+// "prompt_setprompt({buffer}, {text})" function
+static void f_prompt_setprompt(typval_T *argvars,
+ typval_T *rettv, FunPtr fptr)
+{
+ buf_T *buf;
+ const char_u *text;
+
+ if (check_secure()) {
+ return;
+ }
+ buf = tv_get_buf(&argvars[0], false);
+ if (buf == NULL) {
+ return;
+ }
+
+ text = (const char_u *)tv_get_string(&argvars[1]);
+ xfree(buf->b_prompt_text);
+ buf->b_prompt_text = vim_strsave(text);
+}
+
// "pum_getpos()" function
static void f_pum_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index dac4c8f527..c69b10f99a 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -3642,7 +3642,9 @@ static void nv_help(cmdarg_T *cap)
*/
static void nv_addsub(cmdarg_T *cap)
{
- if (!VIsual_active && cap->oap->op_type == OP_NOP) {
+ if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
+ clearopbeep(cap->oap);
+ } else if (!VIsual_active && cap->oap->op_type == OP_NOP) {
prep_redo_cmd(cap);
cap->oap->op_type = cap->cmdchar == Ctrl_A ? OP_NR_ADD : OP_NR_SUB;
op_addsub(cap->oap, cap->count1, cap->arg);
@@ -5239,6 +5241,13 @@ static void nv_down(cmdarg_T *cap)
// In the cmdline window a <CR> executes the command.
if (cmdwin_type != 0 && cap->cmdchar == CAR) {
cmdwin_result = CAR;
+ } else if (bt_prompt(curbuf) && cap->cmdchar == CAR
+ && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) {
+ // In a prompt buffer a <CR> in the last line invokes the callback.
+ invoke_prompt_callback();
+ if (restart_edit == 0) {
+ restart_edit = 'a';
+ }
} else {
cap->oap->motion_type = kMTLineWise;
if (cursor_down(cap->count1, cap->oap->op_type == OP_NOP) == false) {
@@ -5831,6 +5840,10 @@ static void nv_undo(cmdarg_T *cap)
static void nv_kundo(cmdarg_T *cap)
{
if (!checkclearopq(cap->oap)) {
+ if (bt_prompt(curbuf)) {
+ clearopbeep(cap->oap);
+ return;
+ }
u_undo((int)cap->count1);
curwin->w_set_curswant = true;
}
@@ -5844,8 +5857,13 @@ static void nv_replace(cmdarg_T *cap)
char_u *ptr;
int had_ctrl_v;
- if (checkclearop(cap->oap))
+ if (checkclearop(cap->oap)) {
+ return;
+ }
+ if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
+ clearopbeep(cap->oap);
return;
+ }
/* get another character */
if (cap->nchar == Ctrl_V) {
@@ -6182,7 +6200,11 @@ static void v_visop(cmdarg_T *cap)
*/
static void nv_subst(cmdarg_T *cap)
{
- if (VIsual_active) { /* "vs" and "vS" are the same as "vc" */
+ if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
+ clearopbeep(cap->oap);
+ return;
+ }
+ if (VIsual_active) { // "vs" and "vS" are the same as "vc"
if (cap->cmdchar == 'S') {
VIsual_mode_orig = VIsual_mode;
VIsual_mode = 'V';
@@ -7120,10 +7142,15 @@ static void nv_tilde(cmdarg_T *cap)
{
if (!p_to
&& !VIsual_active
- && cap->oap->op_type != OP_TILDE)
+ && cap->oap->op_type != OP_TILDE) {
+ if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
+ clearopbeep(cap->oap);
+ return;
+ }
n_swapchar(cap);
- else
+ } else {
nv_operator(cap);
+ }
}
/*
@@ -7136,6 +7163,12 @@ static void nv_operator(cmdarg_T *cap)
op_type = get_op_type(cap->cmdchar, cap->nchar);
+ if (bt_prompt(curbuf) && op_is_change(op_type)
+ && !prompt_curpos_editable()) {
+ clearopbeep(cap->oap);
+ return;
+ }
+
if (op_type == cap->oap->op_type) /* double operator works on lines */
nv_lineop(cap);
else if (!checkclearop(cap->oap)) {
@@ -7796,8 +7829,11 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent)
clearop(cap->oap);
assert(cap->opcount >= 0);
nv_diffgetput(true, (size_t)cap->opcount);
- } else
+ } else {
clearopbeep(cap->oap);
+ }
+ } else if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
+ clearopbeep(cap->oap);
} else {
if (fix_indent) {
dir = (cap->cmdchar == ']' && cap->nchar == 'p')
@@ -7809,8 +7845,9 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent)
? BACKWARD : FORWARD;
}
prep_redo_cmd(cap);
- if (cap->cmdchar == 'g')
+ if (cap->cmdchar == 'g') {
flags |= PUT_CURSEND;
+ }
if (VIsual_active) {
/* Putting in Visual mode: The put text replaces the selected
@@ -7916,10 +7953,14 @@ static void nv_open(cmdarg_T *cap)
clearop(cap->oap);
assert(cap->opcount >= 0);
nv_diffgetput(false, (size_t)cap->opcount);
- } else if (VIsual_active) /* switch start and end of visual */
+ } else if (VIsual_active) {
+ // switch start and end of visual/
v_swap_corners(cap->cmdchar);
- else
+ } else if (bt_prompt(curbuf)) {
+ clearopbeep(cap->oap);
+ } else {
n_opencmd(cap);
+ }
}
// Calculate start/end virtual columns for operating in block mode.
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 6d327c0814..735a33ca97 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -89,6 +89,10 @@ struct block_def {
# include "ops.c.generated.h"
#endif
+// Flags for third item in "opchars".
+#define OPF_LINES 1 // operator always works on lines
+#define OPF_CHANGE 2 // operator changes text
+
/*
* The names of operators.
* IMPORTANT: Index must correspond with defines in vim.h!!!
@@ -96,36 +100,36 @@ struct block_def {
*/
static char opchars[][3] =
{
- { NUL, NUL, false }, // OP_NOP
- { 'd', NUL, false }, // OP_DELETE
- { 'y', NUL, false }, // OP_YANK
- { 'c', NUL, false }, // OP_CHANGE
- { '<', NUL, true }, // OP_LSHIFT
- { '>', NUL, true }, // OP_RSHIFT
- { '!', NUL, true }, // OP_FILTER
- { 'g', '~', false }, // OP_TILDE
- { '=', NUL, true }, // OP_INDENT
- { 'g', 'q', true }, // OP_FORMAT
- { ':', NUL, true }, // OP_COLON
- { 'g', 'U', false }, // OP_UPPER
- { 'g', 'u', false }, // OP_LOWER
- { 'J', NUL, true }, // DO_JOIN
- { 'g', 'J', true }, // DO_JOIN_NS
- { 'g', '?', false }, // OP_ROT13
- { 'r', NUL, false }, // OP_REPLACE
- { 'I', NUL, false }, // OP_INSERT
- { 'A', NUL, false }, // OP_APPEND
- { 'z', 'f', true }, // OP_FOLD
- { 'z', 'o', true }, // OP_FOLDOPEN
- { 'z', 'O', true }, // OP_FOLDOPENREC
- { 'z', 'c', true }, // OP_FOLDCLOSE
- { 'z', 'C', true }, // OP_FOLDCLOSEREC
- { 'z', 'd', true }, // OP_FOLDDEL
- { 'z', 'D', true }, // OP_FOLDDELREC
- { 'g', 'w', true }, // OP_FORMAT2
- { 'g', '@', false }, // OP_FUNCTION
- { Ctrl_A, NUL, false }, // OP_NR_ADD
- { Ctrl_X, NUL, false }, // OP_NR_SUB
+ { NUL, NUL, 0 }, // OP_NOP
+ { 'd', NUL, OPF_CHANGE }, // OP_DELETE
+ { 'y', NUL, 0 }, // OP_YANK
+ { 'c', NUL, OPF_CHANGE }, // OP_CHANGE
+ { '<', NUL, OPF_LINES | OPF_CHANGE }, // OP_LSHIFT
+ { '>', NUL, OPF_LINES | OPF_CHANGE }, // OP_RSHIFT
+ { '!', NUL, OPF_LINES | OPF_CHANGE }, // OP_FILTER
+ { 'g', '~', OPF_CHANGE }, // OP_TILDE
+ { '=', NUL, OPF_LINES | OPF_CHANGE }, // OP_INDENT
+ { 'g', 'q', OPF_LINES | OPF_CHANGE }, // OP_FORMAT
+ { ':', NUL, OPF_LINES }, // OP_COLON
+ { 'g', 'U', OPF_CHANGE }, // OP_UPPER
+ { 'g', 'u', OPF_CHANGE }, // OP_LOWER
+ { 'J', NUL, OPF_LINES | OPF_CHANGE }, // DO_JOIN
+ { 'g', 'J', OPF_LINES | OPF_CHANGE }, // DO_JOIN_NS
+ { 'g', '?', OPF_CHANGE }, // OP_ROT13
+ { 'r', NUL, OPF_CHANGE }, // OP_REPLACE
+ { 'I', NUL, OPF_CHANGE }, // OP_INSERT
+ { 'A', NUL, OPF_CHANGE }, // OP_APPEND
+ { 'z', 'f', OPF_LINES }, // OP_FOLD
+ { 'z', 'o', OPF_LINES }, // OP_FOLDOPEN
+ { 'z', 'O', OPF_LINES }, // OP_FOLDOPENREC
+ { 'z', 'c', OPF_LINES }, // OP_FOLDCLOSE
+ { 'z', 'C', OPF_LINES }, // OP_FOLDCLOSEREC
+ { 'z', 'd', OPF_LINES }, // OP_FOLDDEL
+ { 'z', 'D', OPF_LINES }, // OP_FOLDDELREC
+ { 'g', 'w', OPF_LINES | OPF_CHANGE }, // OP_FORMAT2
+ { 'g', '@', OPF_CHANGE }, // OP_FUNCTION
+ { Ctrl_A, NUL, OPF_CHANGE }, // OP_NR_ADD
+ { Ctrl_X, NUL, OPF_CHANGE }, // OP_NR_SUB
};
/*
@@ -169,7 +173,13 @@ int get_op_type(int char1, int char2)
*/
int op_on_lines(int op)
{
- return opchars[op][2];
+ return opchars[op][2] & OPF_LINES;
+}
+
+// Return TRUE if operator "op" changes text.
+int op_is_change(int op)
+{
+ return opchars[op][2] & OPF_CHANGE;
}
/*
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 15ff8414ce..100902897a 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -299,7 +299,8 @@ static char *(p_scbopt_values[]) = { "ver", "hor", "jump", NULL };
static char *(p_debug_values[]) = { "msg", "throw", "beep", NULL };
static char *(p_ead_values[]) = { "both", "ver", "hor", NULL };
static char *(p_buftype_values[]) = { "nofile", "nowrite", "quickfix",
- "help", "acwrite", "terminal", NULL };
+ "help", "acwrite", "terminal",
+ "prompt", NULL };
static char *(p_bufhidden_values[]) = { "hide", "unload", "delete",
"wipe", NULL };
@@ -7091,10 +7092,13 @@ static int check_opt_wim(void)
*/
bool can_bs(int what)
{
+ if (what == BS_START && bt_prompt(curbuf)) {
+ return false;
+ }
switch (*p_bs) {
- case '2': return true;
- case '1': return what != BS_START;
- case '0': return false;
+ case '2': return true;
+ case '1': return what != BS_START;
+ case '0': return false;
}
return vim_strchr(p_bs, what) != NULL;
}
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index 1f74bada41..df92b2c036 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -2971,7 +2971,10 @@ static char_u *u_save_line(linenr_T lnum)
bool bufIsChanged(buf_T *buf)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- return !bt_dontwrite(buf) && (buf->b_changed || file_ff_differs(buf, true));
+ // In a "prompt" buffer we do respect 'modified', so that we can control
+ // closing the window by setting or resetting that option.
+ return (!bt_dontwrite(buf) || bt_prompt(buf))
+ && (buf->b_changed || file_ff_differs(buf, true));
}
// Return true if any buffer has changes. Also buffers that are not written.