diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index a9d9c83867..b0b684064e 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2281,8 +2281,9 @@ pow({x}, {y}) Float {x} to the power of {y} prevnonblank({lnum}) Number line nr of non-blank line <= {lnum} printf({fmt}, {expr1}...) String format text prompt_addtext({buf}, {expr}) none add text to a prompt buffer -prompt_setprompt({buf}, {text}) none set prompt text prompt_setcallback({buf}, {expr}) none set prompt callback function +prompt_setinterrupt({buf}, {text}) none set prompt interrupt function +prompt_setprompt({buf}, {text}) none set prompt text pum_getpos() Dict position and size of pum if visible pumvisible() Number whether popup menu is visible pyeval({expr}) any evaluate |Python| expression @@ -6544,17 +6545,11 @@ printf({fmt}, {expr1} ...) *printf()* of "%" items. If there are not sufficient or too many arguments an error is given. Up to 18 arguments can be used. -prompt_setprompt({buf}, {text}) *prompt_setprompt()* - Set prompt for buffer {buf} to {text}. You most likely want - {text} to end in a space. - The result is only visible if {buf} has 'buftype' set to - "prompt". Example: > - call prompt_setprompt(bufnr(''), 'command: ') - - prompt_setcallback({buf}, {expr}) *prompt_setcallback()* - Set prompt callback for buffer {buf} to {expr}. This has only + Set prompt callback for buffer {buf} to {expr}. When {expr} + is an empty string the callback is removed. This has only effect if {buf} has 'buftype' set to "prompt". + The callback is invoked when pressing Enter. The current buffer will always be the prompt buffer. A new line for a prompt is added before invoking the callback, thus the prompt @@ -6579,6 +6574,22 @@ prompt_setcallback({buf}, {expr}) *prompt_setcallback()* endif endfunc +prompt_setinterrupt({buf}, {expr}) *prompt_setinterrupt()* + Set a callback for buffer {buf} to {expr}. When {expr} is an + empty string the callback is removed. This has only effect if + {buf} has 'buftype' set to "prompt". + + This callback will be invoked when pressing CTRL-C in Insert + mode. Without setting a callback Vim will exit Insert mode, + as in any buffer. + +prompt_setprompt({buf}, {text}) *prompt_setprompt()* + Set prompt for buffer {buf} to {text}. You most likely want + {text} to end in a space. + The result is only visible if {buf} has 'buftype' set to + "prompt". Example: > + call prompt_setprompt(bufnr(''), 'command: ') + pum_getpos() *pum_getpos()* If the popup menu (see |ins-completion-menu|) is not visible, returns an empty |Dictionary|, otherwise, returns a @@ -6591,6 +6602,7 @@ pum_getpos() *pum_getpos()* scrollbar |TRUE| if visible The values are the same as in |v:event| during |CompleteChanged|. + pumvisible() *pumvisible()* Returns non-zero when the popup menu is visible, zero otherwise. See |ins-completion-menu|. diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index a9b37af917..790609ab94 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -765,6 +765,7 @@ static void free_buffer(buf_T *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); @@ -1879,6 +1880,7 @@ 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; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 40d34bd0b7..cebd08af28 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -823,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 diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b330e7161d..7b5845e18b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -13718,3 +13718,20 @@ void invoke_prompt_callback(void) 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 fb72402f0d..1ca6b75b7e 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -244,6 +244,7 @@ return { 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={}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index a89d62fdfd..48e2ef8c51 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -6101,6 +6101,31 @@ static void f_prompt_setcallback(typval_T *argvars, 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)