diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/edit.c | 17 | ||||
-rw-r--r-- | src/nvim/eval.lua | 1 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 72 | ||||
-rw-r--r-- | src/nvim/testdir/test_excmd.vim | 2 | ||||
-rw-r--r-- | src/nvim/testdir/test_prompt_buffer.vim | 195 |
5 files changed, 251 insertions, 36 deletions
diff --git a/src/nvim/edit.c b/src/nvim/edit.c index f2fddc89fe..999cc74185 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1604,13 +1604,20 @@ void edit_putchar(int c, bool highlight) } } +/// Return the effective prompt for the specified buffer. +char_u *buf_prompt_text(const buf_T *const buf) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (buf->b_prompt_text == NULL) { + return (char_u *)"% "; + } + return buf->b_prompt_text; +} + // Return the effective prompt for the current buffer. -char_u *prompt_text(void) +char_u *prompt_text(void) FUNC_ATTR_WARN_UNUSED_RESULT { - if (curbuf->b_prompt_text == NULL) { - return (char_u *)"% "; - } - return curbuf->b_prompt_text; + return buf_prompt_text(curbuf); } // Prepare for prompt mode: Make sure the last line has the prompt text. diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index b10e99fc08..77e7c7b3a9 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -251,6 +251,7 @@ return { pow={args=2}, prevnonblank={args=1}, printf={args=varargs(1)}, + prompt_getprompt={args=1}, prompt_setcallback={args={2, 2}}, prompt_setinterrupt={args={2, 2}}, prompt_setprompt={args={2, 2}}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 0d288e2cc2..6d328953f6 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -602,12 +602,7 @@ static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type == VAR_UNKNOWN) { buf = curbuf; } else { - if (!tv_check_str_or_nr(&argvars[0])) { - return; - } - emsg_off++; - buf = tv_get_buf(&argvars[0], false); - emsg_off--; + buf = tv_get_buf_from_arg(&argvars[0]); } if (buf != NULL && buf->b_fname != NULL) { rettv->vval.v_string = (char_u *)xstrdup((char *)buf->b_fname); @@ -627,6 +622,9 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type == VAR_UNKNOWN) { buf = curbuf; } else { + // Don't use tv_get_buf_from_arg(); we continue if the buffer wasn't found + // and the second argument isn't zero, but we want to return early if the + // first argument isn't a string or number so only one error is shown. if (!tv_check_str_or_nr(&argvars[0])) { return; } @@ -653,18 +651,12 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void buf_win_common(typval_T *argvars, typval_T *rettv, bool get_nr) { - if (!tv_check_str_or_nr(&argvars[0])) { + const buf_T *const buf = tv_get_buf_from_arg(&argvars[0]); + if (buf == NULL) { // no need to search if invalid arg or buffer not found rettv->vval.v_number = -1; return; } - emsg_off++; - buf_T *buf = tv_get_buf(&argvars[0], true); - if (buf == NULL) { // no need to search if buffer was not found - rettv->vval.v_number = -1; - goto end; - } - int winnr = 0; int winid; bool found_buf = false; @@ -677,8 +669,6 @@ static void buf_win_common(typval_T *argvars, typval_T *rettv, bool get_nr) } } rettv->vval.v_number = (found_buf ? (get_nr ? winnr : winid) : -1); -end: - emsg_off--; } /// "bufwinid(nr)" function @@ -731,6 +721,18 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only) return buf; } +/// Like tv_get_buf() but give an error message if the type is wrong. +buf_T *tv_get_buf_from_arg(typval_T *const tv) FUNC_ATTR_NONNULL_ALL +{ + if (!tv_check_str_or_nr(tv)) { + return NULL; + } + emsg_off++; + buf_T *const buf = tv_get_buf(tv, false); + emsg_off--; + return buf; +} + /// Get the buffer from "arg" and give an error and return NULL if it is not /// valid. buf_T * get_buf_arg(typval_T *arg) @@ -2799,13 +2801,9 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type != VAR_UNKNOWN) { // Information about one buffer. Argument specifies the buffer - if (tv_check_num(&argvars[0])) { // issue errmsg if type error - emsg_off++; - argbuf = tv_get_buf(&argvars[0], false); - emsg_off--; - if (argbuf == NULL) { - return; - } + argbuf = tv_get_buf_from_arg(&argvars[0]); + if (argbuf == NULL) { + return; } } @@ -2875,13 +2873,7 @@ static void get_buffer_lines(buf_T *buf, */ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - buf_T *buf = NULL; - - if (tv_check_str_or_nr(&argvars[0])) { - emsg_off++; - buf = tv_get_buf(&argvars[0], false); - emsg_off--; - } + buf_T *const buf = tv_get_buf_from_arg(&argvars[0]); const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf); const linenr_T end = (argvars[2].v_type == VAR_UNKNOWN @@ -6499,6 +6491,26 @@ static void f_prompt_setinterrupt(typval_T *argvars, buf->b_prompt_interrupt= interrupt_callback; } +/// "prompt_getprompt({buffer})" function +void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr) + FUNC_ATTR_NONNULL_ALL +{ + // return an empty string by default, e.g. it's not a prompt buffer + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + buf_T *const buf = tv_get_buf_from_arg(&argvars[0]); + if (buf == NULL) { + return; + } + + if (!bt_prompt(buf)) { + return; + } + + rettv->vval.v_string = vim_strsave(buf_prompt_text(buf)); +} + // "prompt_setprompt({buffer}, {text})" function static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr) diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim index 98a3e60368..15557056ee 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -47,7 +47,7 @@ func Test_buffers_lastused() endfor call assert_equal(['bufb', 'bufa', 'bufc'], names) - call assert_match('[0-2] seconds ago', bufs[1][1]) + call assert_match('[0-2] seconds\= ago', bufs[1][1]) bwipeout bufa bwipeout bufb diff --git a/src/nvim/testdir/test_prompt_buffer.vim b/src/nvim/testdir/test_prompt_buffer.vim new file mode 100644 index 0000000000..6fc5850be3 --- /dev/null +++ b/src/nvim/testdir/test_prompt_buffer.vim @@ -0,0 +1,195 @@ +" Tests for setting 'buftype' to "prompt" + +source check.vim +" Nvim's channel implementation differs from Vim's +" CheckFeature channel + +source shared.vim +source screendump.vim + +func CanTestPromptBuffer() + " We need to use a terminal window to be able to feed keys without leaving + " Insert mode. + " Nvim's terminal implementation differs from Vim's + " CheckFeature terminal + + " TODO: make the tests work on MS-Windows + CheckNotMSWindows +endfunc + +func WriteScript(name) + call writefile([ + \ 'func TextEntered(text)', + \ ' if a:text == "exit"', + \ ' " Reset &modified to allow the buffer to be closed.', + \ ' set nomodified', + \ ' stopinsert', + \ ' close', + \ ' else', + \ ' " Add the output above the current prompt.', + \ ' call append(line("$") - 1, "Command: \"" . a:text . "\"")', + \ ' " Reset &modified to allow the buffer to be closed.', + \ ' set nomodified', + \ ' call timer_start(20, {id -> TimerFunc(a:text)})', + \ ' endif', + \ 'endfunc', + \ '', + \ 'func TimerFunc(text)', + \ ' " Add the output above the current prompt.', + \ ' call append(line("$") - 1, "Result: \"" . a:text . "\"")', + \ ' " Reset &modified to allow the buffer to be closed.', + \ ' set nomodified', + \ 'endfunc', + \ '', + \ 'call setline(1, "other buffer")', + \ 'set nomodified', + \ 'new', + \ 'set buftype=prompt', + \ 'call prompt_setcallback(bufnr(""), function("TextEntered"))', + \ 'eval bufnr("")->prompt_setprompt("cmd: ")', + \ 'startinsert', + \ ], a:name) +endfunc + +func Test_prompt_basic() + throw 'skipped: TODO' + call CanTestPromptBuffer() + let scriptName = 'XpromptscriptBasic' + call WriteScript(scriptName) + + let buf = RunVimInTerminal('-S ' . scriptName, {}) + call WaitForAssert({-> assert_equal('cmd:', term_getline(buf, 1))}) + + call term_sendkeys(buf, "hello\<CR>") + call WaitForAssert({-> assert_equal('cmd: hello', term_getline(buf, 1))}) + call WaitForAssert({-> assert_equal('Command: "hello"', term_getline(buf, 2))}) + call WaitForAssert({-> assert_equal('Result: "hello"', term_getline(buf, 3))}) + + call term_sendkeys(buf, "exit\<CR>") + call WaitForAssert({-> assert_equal('other buffer', term_getline(buf, 1))}) + + call StopVimInTerminal(buf) + call delete(scriptName) +endfunc + +func Test_prompt_editing() + throw 'skipped: TODO' + call CanTestPromptBuffer() + let scriptName = 'XpromptscriptEditing' + call WriteScript(scriptName) + + let buf = RunVimInTerminal('-S ' . scriptName, {}) + call WaitForAssert({-> assert_equal('cmd:', term_getline(buf, 1))}) + + let bs = "\<BS>" + call term_sendkeys(buf, "hello" . bs . bs) + call WaitForAssert({-> assert_equal('cmd: hel', term_getline(buf, 1))}) + + let left = "\<Left>" + call term_sendkeys(buf, left . left . left . bs . '-') + call WaitForAssert({-> assert_equal('cmd: -hel', term_getline(buf, 1))}) + + let end = "\<End>" + call term_sendkeys(buf, end . "x") + call WaitForAssert({-> assert_equal('cmd: -helx', term_getline(buf, 1))}) + + call term_sendkeys(buf, "\<C-U>exit\<CR>") + call WaitForAssert({-> assert_equal('other buffer', term_getline(buf, 1))}) + + call StopVimInTerminal(buf) + call delete(scriptName) +endfunc + +func Test_prompt_garbage_collect() + func MyPromptCallback(x, text) + " NOP + endfunc + func MyPromptInterrupt(x) + " NOP + endfunc + + new + set buftype=prompt + " Nvim doesn't support method call syntax yet. + " eval bufnr('')->prompt_setcallback(function('MyPromptCallback', [{}])) + " eval bufnr('')->prompt_setinterrupt(function('MyPromptInterrupt', [{}])) + eval prompt_setcallback(bufnr(''), function('MyPromptCallback', [{}])) + eval prompt_setinterrupt(bufnr(''), function('MyPromptInterrupt', [{}])) + call test_garbagecollect_now() + " Must not crash + call feedkeys("\<CR>\<C-C>", 'xt') + call assert_true(v:true) + + call assert_fails("call prompt_setcallback(bufnr(), [])", 'E921:') + call assert_equal(0, prompt_setcallback({}, '')) + call assert_fails("call prompt_setinterrupt(bufnr(), [])", 'E921:') + call assert_equal(0, prompt_setinterrupt({}, '')) + + delfunc MyPromptCallback + bwipe! +endfunc + +" Test for editing the prompt buffer +func Test_prompt_buffer_edit() + new + set buftype=prompt + 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') + call assert_beeps('normal! ra') + call assert_beeps('normal! s') + call assert_beeps('normal! S') + call assert_beeps("normal! \<C-A>") + call assert_beeps("normal! \<C-X>") + " pressing CTRL-W in the prompt buffer should trigger the window commands + call assert_equal(1, winnr()) + " In Nvim, CTRL-W commands aren't usable from insert mode in a prompt buffer + " exe "normal A\<C-W>\<C-W>" + " call assert_equal(2, winnr()) + " wincmd w + close! + call assert_equal(0, prompt_setprompt([], '')) +endfunc + +func Test_prompt_buffer_getbufinfo() + new + call assert_equal('', prompt_getprompt('%')) + call assert_equal('', prompt_getprompt(bufnr('%'))) + let another_buffer = bufnr('%') + + set buftype=prompt + call assert_equal('% ', prompt_getprompt('%')) + call prompt_setprompt( bufnr( '%' ), 'This is a test: ' ) + call assert_equal('This is a test: ', prompt_getprompt('%')) + + call prompt_setprompt( bufnr( '%' ), '' ) + " Nvim doesn't support method call syntax yet. + " call assert_equal('', '%'->prompt_getprompt()) + call assert_equal('', prompt_getprompt('%')) + + call prompt_setprompt( bufnr( '%' ), 'Another: ' ) + call assert_equal('Another: ', prompt_getprompt('%')) + let another = bufnr('%') + + new + + call assert_equal('', prompt_getprompt('%')) + call assert_equal('Another: ', prompt_getprompt(another)) + + " Doesn't exist + let buffers_before = len( getbufinfo() ) + call assert_equal('', prompt_getprompt( bufnr('$') + 1)) + call assert_equal(buffers_before, len( getbufinfo())) + + " invalid type + call assert_fails('call prompt_getprompt({})', 'E728:') + + %bwipe! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab |