From f4d5d5250a105b5593e3119f4ee37ea20272a34b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 1 Apr 2017 23:57:34 +0300 Subject: [PATCH 01/11] eval: Refactor get_user_input to support dictionary --- runtime/doc/eval.txt | 16 +- src/nvim/eval.c | 170 +++++++++------ src/nvim/eval/typval.c | 25 +++ test/functional/eval/input_spec.lua | 310 +++++++++++++++++++++++++++- test/unit/eval/typval_spec.lua | 49 +++++ 5 files changed, 494 insertions(+), 76 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 911fe43819..5a2db1825f 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4665,10 +4665,23 @@ index({list}, {expr} [, {start} [, {ic}]]) *index()* input({prompt} [, {text} [, {completion}]]) *input()* +input({opts}) The result is a String, which is whatever the user typed on the command-line. The {prompt} argument is either a prompt string, or a blank string (for no prompt). A '\n' can be used in the prompt to start a new line. + + In the second form it accepts a single dictionary with the + following keys, any of which may be omitted: + + Key Default Description ~ + prompt "" Same as {prompt} in the first form. + default "" Same as {text} in the first form. + completion nothing Same as {completion} in the first form. + cancelreturn "" Same as {cancelreturn} from + |inputdialog()|. Also works with + input(). + The highlighting set with |:echohl| is used for the prompt. The input is entered just like a command-line, with the same editing commands and mappings. There is a separate history @@ -4710,6 +4723,7 @@ input({prompt} [, {text} [, {completion}]]) *input()* :endfunction inputdialog({prompt} [, {text} [, {cancelreturn}]]) *inputdialog()* +inputdialog({opts}) Like |input()|, but when the GUI is running and text dialogs are supported, a dialog window pops up to input the text. Example: > @@ -4721,7 +4735,7 @@ inputdialog({prompt} [, {text} [, {cancelreturn}]]) *inputdialog()* omitted an empty string is returned. Hitting works like pressing the OK button. Hitting works like pressing the Cancel button. - NOTE: Command-line completion is not supported. + NOTE: Command-line completion is not supported in the GUI. inputlist({textlist}) *inputlist()* {textlist} must be a |List| of strings. This |List| is diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 56a6632fad..780a33eced 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10978,81 +10978,123 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const bool inputdialog) FUNC_ATTR_NONNULL_ALL { - const char *prompt = tv_get_string_chk(&argvars[0]); - int cmd_silent_save = cmd_silent; - int xp_type = EXPAND_NOTHING; - char_u *xp_arg = NULL; - rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - cmd_silent = FALSE; /* Want to see the prompt. */ - if (prompt != NULL) { - // Only the part of the message after the last NL is considered as - // prompt for the command line. - const char *p = strrchr(prompt, '\n'); - if (p == NULL) { - p = prompt; - } else { - p++; - msg_start(); - msg_clr_eos(); - msg_puts_attr_len(prompt, p - prompt, echo_attr); - msg_didout = false; - msg_starthere(); - } - cmdline_row = msg_row; - - const char *defstr = ""; - char buf[NUMBUFLEN]; + const char *prompt = ""; + const char *defstr = ""; + const char *cancelreturn = NULL; + const char *xp_name = NULL; + char prompt_buf[NUMBUFLEN]; + char defstr_buf[NUMBUFLEN]; + char cancelreturn_buf[NUMBUFLEN]; + char xp_name_buf[NUMBUFLEN]; + if (argvars[0].v_type == VAR_DICT) { if (argvars[1].v_type != VAR_UNKNOWN) { - defstr = tv_get_string_buf_chk(&argvars[1], buf); - if (defstr != NULL) { - stuffReadbuffSpec(defstr); + emsgf( + _("E5050: When providing {opts} argument no more arguments follow")); + return; + } + const dict_T *const dict = argvars[0].vval.v_dict; + prompt = tv_dict_get_string_buf_chk(dict, S_LEN("prompt"), prompt_buf, ""); + if (prompt == NULL) { + return; + } + defstr = tv_dict_get_string_buf_chk(dict, S_LEN("default"), defstr_buf, ""); + if (defstr == NULL) { + return; + } + char def[1] = { 0 }; + cancelreturn = tv_dict_get_string_buf_chk(dict, S_LEN("cancelreturn"), + cancelreturn_buf, def); + if (cancelreturn == NULL) { // error + return; + } + if (*cancelreturn == NUL) { + cancelreturn = NULL; + } + xp_name = tv_dict_get_string_buf_chk(dict, S_LEN("completion"), + xp_name_buf, def); + if (xp_name == NULL) { // error + return; + } + if (xp_name == def) { // default to NULL + xp_name = NULL; + } + } else { + prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf); + if (prompt == NULL) { + return; + } + if (argvars[1].v_type != VAR_UNKNOWN) { + defstr = tv_get_string_buf_chk(&argvars[1], defstr_buf); + if (defstr == NULL) { + return; } - - if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) { - char buf2[NUMBUFLEN]; - // input() with a third argument: completion - rettv->vval.v_string = NULL; - - const char *const xp_name = tv_get_string_buf_chk(&argvars[2], buf2); - if (xp_name == NULL) { + if (argvars[2].v_type != VAR_UNKNOWN) { + const char *const arg2 = tv_get_string_buf_chk(&argvars[2], + cancelreturn_buf); + if (arg2 == NULL) { return; } - - const int xp_namelen = (int)strlen(xp_name); - - uint32_t argt; - if (parse_compl_arg((char_u *)xp_name, xp_namelen, &xp_type, &argt, - &xp_arg) == FAIL) { - return; + if (inputdialog) { + cancelreturn = arg2; + } else { + xp_name = arg2; } } } - - if (defstr != NULL) { - int save_ex_normal_busy = ex_normal_busy; - ex_normal_busy = 0; - rettv->vval.v_string = - getcmdline_prompt(inputsecret_flag ? NUL : '@', (char_u *)p, echo_attr, - xp_type, xp_arg); - ex_normal_busy = save_ex_normal_busy; - } - if (inputdialog && rettv->vval.v_string == NULL - && argvars[1].v_type != VAR_UNKNOWN - && argvars[2].v_type != VAR_UNKNOWN) { - char buf[NUMBUFLEN]; - rettv->vval.v_string = (char_u *)xstrdup(tv_get_string_buf( - &argvars[2], buf)); - } - - xfree(xp_arg); - - /* since the user typed this, no need to wait for return */ - need_wait_return = FALSE; - msg_didout = FALSE; } + + int xp_type = EXPAND_NOTHING; + char *xp_arg = NULL; + if (xp_name != NULL) { + // input() with a third argument: completion + const int xp_namelen = (int)strlen(xp_name); + + uint32_t argt; + if (parse_compl_arg((char_u *)xp_name, xp_namelen, &xp_type, + &argt, (char_u **)&xp_arg) == FAIL) { + return; + } + } + + int cmd_silent_save = cmd_silent; + + cmd_silent = false; // Want to see the prompt. + // Only the part of the message after the last NL is considered as + // prompt for the command line. + const char *p = strrchr(prompt, '\n'); + if (p == NULL) { + p = prompt; + } else { + p++; + msg_start(); + msg_clr_eos(); + msg_puts_attr_len(prompt, p - prompt, echo_attr); + msg_didout = false; + msg_starthere(); + } + cmdline_row = msg_row; + + stuffReadbuffSpec(defstr); + + int save_ex_normal_busy = ex_normal_busy; + ex_normal_busy = 0; + rettv->vval.v_string = + getcmdline_prompt(inputsecret_flag ? NUL : '@', (char_u *)p, echo_attr, + xp_type, (char_u *)xp_arg); + ex_normal_busy = save_ex_normal_busy; + + if (rettv->vval.v_string == NULL && cancelreturn != NULL) { + rettv->vval.v_string = (char_u *)xstrdup(cancelreturn); + } + + xfree(xp_arg); + + // Since the user typed this, no need to wait for return. + need_wait_return = false; + msg_didout = false; cmd_silent = cmd_silent_save; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 786b766689..e721ebe345 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1225,6 +1225,31 @@ const char *tv_dict_get_string_buf(const dict_T *const d, const char *const key, return tv_get_string_buf(&di->di_tv, numbuf); } +/// Get a string item from a dictionary +/// +/// @param[in] d Dictionary to get item from. +/// @param[in] key Dictionary key. +/// @param[in] key_len Key length. +/// @param[in] numbuf Numbuf for. +/// @param[in] def Default return when key does not exist. +/// +/// @return `def` when key does not exist, +/// NULL in case of type error, +/// string item value in case of success. +const char *tv_dict_get_string_buf_chk(const dict_T *const d, + const char *const key, + const ptrdiff_t key_len, + char *const numbuf, + const char *const def) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + const dictitem_T *const di = tv_dict_find(d, key, key_len); + if (di == NULL) { + return def; + } + return tv_get_string_buf_chk(&di->di_tv, numbuf); +} + /// Get a function from a dictionary /// /// @param[in] d Dictionary to get callback from. diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index 393fc10175..d655d9eb4a 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -1,9 +1,14 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local eq = helpers.eq +local wait = helpers.wait local feed = helpers.feed +local meths = helpers.meths local clear = helpers.clear +local source = helpers.source local command = helpers.command +local exc_exec = helpers.exc_exec local screen @@ -11,28 +16,311 @@ before_each(function() clear() screen = Screen.new(25, 5) screen:attach() + source([[ + hi Test ctermfg=Red guifg=Red term=bold + function CustomCompl(...) + return 'TEST' + endfunction + function CustomListCompl(...) + return ['FOO'] + endfunction + ]]) + screen:set_default_attr_ids({ + EOB={bold = true, foreground = Screen.colors.Blue1}, + T={foreground=Screen.colors.Red}, + }) end) describe('input()', function() it('works correctly with multiline prompts', function() feed([[:call input("Test\nFoo")]]) screen:expect([[ - {1:~ }| - {1:~ }| - {1:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| Test | Foo^ | - ]], {{bold=true, foreground=Screen.colors.Blue}}) + ]]) end) it('works correctly with multiline prompts and :echohl', function() - command('hi Test ctermfg=Red guifg=Red term=bold') feed([[:echohl Test | call input("Test\nFoo")]]) screen:expect([[ - {1:~ }| - {1:~ }| - {1:~ }| - {2:Test} | - {2:Foo}^ | - ]], {{bold=true, foreground=Screen.colors.Blue}, {foreground=Screen.colors.Red}}) + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Test} | + {T:Foo}^ | + ]]) + wait() + command('redraw!') + wait() + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo}^ | + ]]) + end) + it('works correctly with multiple numeric arguments (many args)', function() + command('hi Test ctermfg=Red guifg=Red term=bold') + feed([[:echohl Test | call input(1, 2)]]) + wait() -- Without wait() it first shows `12` line and then empty line. + command('redraw!') -- Without this it shows two `12` lines. + wait() + -- None of the above problems happen when testing manually. + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:1}2^ | + ]]) + feed('') + wait() + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:1}^ | + ]]) + end) + it('works correctly with multiple numeric arguments (dict arg)', function() + feed([[:echohl Test | echo input({"prompt": 1, "default": 2, "cancelreturn": 3})]]) + wait() -- Without wait() it first shows `12` line and then empty line. + command('redraw!') -- Without this it shows two `12` lines. + -- None of the above problems happen when testing manually. + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:1}2^ | + ]]) + feed('') + wait() + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:1}^ | + ]]) + feed('') + wait() + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:3} | + ]]) + end) + it('allows omitting everything with dictionary argument', function() + feed(':echohl Test | echo input({})') + wait() + command('redraw!') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + ^ | + ]]) + end) + it('supports completion', function() + feed(':let var = input("", "", "custom,CustomCompl")') + wait() + feed('') + eq('TEST', meths.get_var('var')) + + feed(':let var = input({"completion": "customlist,CustomListCompl"})') + wait() + feed('') + wait() + eq('FOO', meths.get_var('var')) + end) + it('supports cancelreturn', function() + feed(':let var = input({"cancelreturn": "BAR"})') + wait() + feed('') + wait() + eq('BAR', meths.get_var('var')) + end) + it('supports default string', function() + feed(':let var = input("", "DEF1")') + wait() + feed('') + eq('DEF1', meths.get_var('var')) + + feed(':let var = input({"default": "DEF2"})') + wait() + feed('') + wait() + eq('DEF2', meths.get_var('var')) + end) + it('errors out on invalid inputs', function() + eq('Vim(call):E730: using List as a String', + exc_exec('call input([])')) + eq('Vim(call):E730: using List as a String', + exc_exec('call input("", [])')) + eq('Vim(call):E730: using List as a String', + exc_exec('call input("", "", [])')) + eq('Vim(call):E730: using List as a String', + exc_exec('call input({"prompt": []})')) + eq('Vim(call):E730: using List as a String', + exc_exec('call input({"cancelreturn": []})')) + eq('Vim(call):E730: using List as a String', + exc_exec('call input({"default": []})')) + eq('Vim(call):E730: using List as a String', + exc_exec('call input({"completion": []})')) + end) +end) +describe('inputdialog()', function() + it('works correctly with multiline prompts', function() + feed([[:call inputdialog("Test\nFoo")]]) + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + Test | + Foo^ | + ]]) + end) + it('works correctly with multiline prompts and :echohl', function() + feed([[:echohl Test | call inputdialog("Test\nFoo")]]) + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Test} | + {T:Foo}^ | + ]]) + wait() + command('redraw!') + wait() + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo}^ | + ]]) + end) + it('works correctly with multiple numeric arguments (many args)', function() + command('hi Test ctermfg=Red guifg=Red term=bold') + feed([[:echohl Test | call inputdialog(1, 2)]]) + wait() -- Without wait() it first shows `12` line and then empty line. + command('redraw!') -- Without this it shows two `12` lines. + wait() + -- None of the above problems happen when testing manually. + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:1}2^ | + ]]) + feed('') + wait() + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:1}^ | + ]]) + end) + it('works correctly with multiple numeric arguments (dict arg)', function() + feed([[:echohl Test | echo inputdialog({"prompt": 1, "default": 2, "cancelreturn": 3})]]) + wait() -- Without wait() it first shows `12` line and then empty line. + command('redraw!') -- Without this it shows two `12` lines. + -- None of the above problems happen when testing manually. + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:1}2^ | + ]]) + feed('') + wait() + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:1}^ | + ]]) + feed('') + wait() + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:3} | + ]]) + end) + it('allows omitting everything with dictionary argument', function() + feed(':echohl Test | echo inputdialog({})') + wait() + command('redraw!') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + ^ | + ]]) + end) + it('supports completion', function() + feed(':let var = inputdialog({"completion": "customlist,CustomListCompl"})') + wait() + feed('') + wait() + eq('FOO', meths.get_var('var')) + end) + it('supports cancelreturn', function() + feed(':let var = inputdialog("", "", "CR1")') + wait() + feed('') + wait() + eq('CR1', meths.get_var('var')) + + feed(':let var = inputdialog({"cancelreturn": "BAR"})') + wait() + feed('') + wait() + eq('BAR', meths.get_var('var')) + end) + it('supports default string', function() + feed(':let var = inputdialog("", "DEF1")') + wait() + feed('') + eq('DEF1', meths.get_var('var')) + + feed(':let var = inputdialog({"default": "DEF2"})') + wait() + feed('') + wait() + eq('DEF2', meths.get_var('var')) + end) + it('errors out on invalid inputs', function() + eq('Vim(call):E730: using List as a String', + exc_exec('call inputdialog([])')) + eq('Vim(call):E730: using List as a String', + exc_exec('call inputdialog("", [])')) + eq('Vim(call):E730: using List as a String', + exc_exec('call inputdialog("", "", [])')) + eq('Vim(call):E730: using List as a String', + exc_exec('call inputdialog({"prompt": []})')) + eq('Vim(call):E730: using List as a String', + exc_exec('call inputdialog({"cancelreturn": []})')) + eq('Vim(call):E730: using List as a String', + exc_exec('call inputdialog({"default": []})')) + eq('Vim(call):E730: using List as a String', + exc_exec('call inputdialog({"completion": []})')) end) end) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 6308ae7367..5d543f914f 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1751,6 +1751,55 @@ describe('typval.c', function() eq('2', s) end) end) + describe('get_string_buf_chk()', function() + local function tv_dict_get_string_buf_chk(d, key, len, buf, def, emsg) + buf = buf or ffi.gc(lib.xmalloc(lib.NUMBUFLEN), lib.xfree) + def = def or ffi.gc(lib.xstrdup('DEFAULT'), lib.xfree) + len = len or #key + alloc_log:clear() + local ret = check_emsg(function() return lib.tv_dict_get_string_buf_chk(d, key, len, buf, def) end, + emsg) + local s_ret = (ret ~= nil) and ffi.string(ret) or nil + if not emsg then + alloc_log:check({}) + end + return s_ret, ret, buf, def + end + itp('works with NULL dict', function() + eq('DEFAULT', tv_dict_get_string_buf_chk(nil, 'test')) + end) + itp('works', function() + local lua_d = { + ['']={}, + t=1, + te=int(2), + tes=empty_list, + test='tset', + testt=5, + } + local d = dict(lua_d) + alloc_log:clear() + eq(lua_d, dct2tbl(d)) + alloc_log:check({}) + local s, r, b, def + s, r, b, def = tv_dict_get_string_buf_chk(d, 'test') + neq(r, b) + neq(r, def) + eq('tset', s) + s, r, b, def = tv_dict_get_string_buf_chk(d, 'test', 1, nil, nil, 'E806: using Float as a String') + neq(r, b) + neq(r, def) + eq(nil, s) + s, r, b, def = tv_dict_get_string_buf_chk(d, 'te') + eq(r, b) + neq(r, def) + eq('2', s) + s, r, b, def = tv_dict_get_string_buf_chk(d, 'TEST') + eq(r, def) + neq(r, b) + eq('DEFAULT', s) + end) + end) describe('get_callback()', function() local function tv_dict_get_callback(d, key, key_len, emsg) key_len = key_len or #key From 475cd8f0750e16863ab049b3a192a9081ee7c986 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 21:24:15 +0300 Subject: [PATCH 02/11] doc: Do not assume something is not supported in GUI --- runtime/doc/eval.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 5a2db1825f..873200cb30 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4735,7 +4735,6 @@ inputdialog({opts}) omitted an empty string is returned. Hitting works like pressing the OK button. Hitting works like pressing the Cancel button. - NOTE: Command-line completion is not supported in the GUI. inputlist({textlist}) *inputlist()* {textlist} must be a |List| of strings. This |List| is From 5e6f7e1d558fed8ef7b368a62b68d59af6766143 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 21:30:47 +0300 Subject: [PATCH 03/11] eval: Alter E5050 error message, test that --- src/nvim/eval.c | 3 +-- test/functional/eval/input_spec.lua | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 780a33eced..882b824b49 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10991,8 +10991,7 @@ void get_user_input(const typval_T *const argvars, char xp_name_buf[NUMBUFLEN]; if (argvars[0].v_type == VAR_DICT) { if (argvars[1].v_type != VAR_UNKNOWN) { - emsgf( - _("E5050: When providing {opts} argument no more arguments follow")); + emsgf(_("E5050: {opts} must be the only argument")); return; } const dict_T *const dict = argvars[0].vval.v_dict; diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index d655d9eb4a..51fd06dd90 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -175,6 +175,10 @@ describe('input()', function() exc_exec('call input({"default": []})')) eq('Vim(call):E730: using List as a String', exc_exec('call input({"completion": []})')) + eq('Vim(call):E5050: {opts} must be the only argument', + exc_exec('call input({}, "default")')) + eq('Vim(call):E118: Too many arguments for function: input', + exc_exec('call input("prompt> ", "default", "file", "extra")')) end) end) describe('inputdialog()', function() @@ -322,5 +326,9 @@ describe('inputdialog()', function() exc_exec('call inputdialog({"default": []})')) eq('Vim(call):E730: using List as a String', exc_exec('call inputdialog({"completion": []})')) + eq('Vim(call):E5050: {opts} must be the only argument', + exc_exec('call inputdialog({}, "default")')) + eq('Vim(call):E118: Too many arguments for function: inputdialog', + exc_exec('call inputdialog("prompt> ", "default", "file", "extra")')) end) end) From 4c4f741aec1c6faf4f5a06c7c21747e360a465ac Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 10 May 2017 23:05:58 +0300 Subject: [PATCH 04/11] functests: Remove all wait()s --- test/functional/eval/input_spec.lua | 36 ----------------------------- 1 file changed, 36 deletions(-) diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index 51fd06dd90..8f790320cf 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -2,7 +2,6 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local eq = helpers.eq -local wait = helpers.wait local feed = helpers.feed local meths = helpers.meths local clear = helpers.clear @@ -51,9 +50,7 @@ describe('input()', function() {T:Test} | {T:Foo}^ | ]]) - wait() command('redraw!') - wait() screen:expect([[ | {EOB:~ }| @@ -65,9 +62,7 @@ describe('input()', function() it('works correctly with multiple numeric arguments (many args)', function() command('hi Test ctermfg=Red guifg=Red term=bold') feed([[:echohl Test | call input(1, 2)]]) - wait() -- Without wait() it first shows `12` line and then empty line. command('redraw!') -- Without this it shows two `12` lines. - wait() -- None of the above problems happen when testing manually. screen:expect([[ | @@ -77,7 +72,6 @@ describe('input()', function() {T:1}2^ | ]]) feed('') - wait() screen:expect([[ | {EOB:~ }| @@ -88,7 +82,6 @@ describe('input()', function() end) it('works correctly with multiple numeric arguments (dict arg)', function() feed([[:echohl Test | echo input({"prompt": 1, "default": 2, "cancelreturn": 3})]]) - wait() -- Without wait() it first shows `12` line and then empty line. command('redraw!') -- Without this it shows two `12` lines. -- None of the above problems happen when testing manually. screen:expect([[ @@ -99,7 +92,6 @@ describe('input()', function() {T:1}2^ | ]]) feed('') - wait() screen:expect([[ | {EOB:~ }| @@ -108,7 +100,6 @@ describe('input()', function() {T:1}^ | ]]) feed('') - wait() screen:expect([[ ^ | {EOB:~ }| @@ -119,7 +110,6 @@ describe('input()', function() end) it('allows omitting everything with dictionary argument', function() feed(':echohl Test | echo input({})') - wait() command('redraw!') screen:expect([[ | @@ -131,33 +121,25 @@ describe('input()', function() end) it('supports completion', function() feed(':let var = input("", "", "custom,CustomCompl")') - wait() feed('') eq('TEST', meths.get_var('var')) feed(':let var = input({"completion": "customlist,CustomListCompl"})') - wait() feed('') - wait() eq('FOO', meths.get_var('var')) end) it('supports cancelreturn', function() feed(':let var = input({"cancelreturn": "BAR"})') - wait() feed('') - wait() eq('BAR', meths.get_var('var')) end) it('supports default string', function() feed(':let var = input("", "DEF1")') - wait() feed('') eq('DEF1', meths.get_var('var')) feed(':let var = input({"default": "DEF2"})') - wait() feed('') - wait() eq('DEF2', meths.get_var('var')) end) it('errors out on invalid inputs', function() @@ -201,9 +183,7 @@ describe('inputdialog()', function() {T:Test} | {T:Foo}^ | ]]) - wait() command('redraw!') - wait() screen:expect([[ | {EOB:~ }| @@ -215,9 +195,7 @@ describe('inputdialog()', function() it('works correctly with multiple numeric arguments (many args)', function() command('hi Test ctermfg=Red guifg=Red term=bold') feed([[:echohl Test | call inputdialog(1, 2)]]) - wait() -- Without wait() it first shows `12` line and then empty line. command('redraw!') -- Without this it shows two `12` lines. - wait() -- None of the above problems happen when testing manually. screen:expect([[ | @@ -227,7 +205,6 @@ describe('inputdialog()', function() {T:1}2^ | ]]) feed('') - wait() screen:expect([[ | {EOB:~ }| @@ -238,7 +215,6 @@ describe('inputdialog()', function() end) it('works correctly with multiple numeric arguments (dict arg)', function() feed([[:echohl Test | echo inputdialog({"prompt": 1, "default": 2, "cancelreturn": 3})]]) - wait() -- Without wait() it first shows `12` line and then empty line. command('redraw!') -- Without this it shows two `12` lines. -- None of the above problems happen when testing manually. screen:expect([[ @@ -249,7 +225,6 @@ describe('inputdialog()', function() {T:1}2^ | ]]) feed('') - wait() screen:expect([[ | {EOB:~ }| @@ -258,7 +233,6 @@ describe('inputdialog()', function() {T:1}^ | ]]) feed('') - wait() screen:expect([[ ^ | {EOB:~ }| @@ -269,7 +243,6 @@ describe('inputdialog()', function() end) it('allows omitting everything with dictionary argument', function() feed(':echohl Test | echo inputdialog({})') - wait() command('redraw!') screen:expect([[ | @@ -281,34 +254,25 @@ describe('inputdialog()', function() end) it('supports completion', function() feed(':let var = inputdialog({"completion": "customlist,CustomListCompl"})') - wait() feed('') - wait() eq('FOO', meths.get_var('var')) end) it('supports cancelreturn', function() feed(':let var = inputdialog("", "", "CR1")') - wait() feed('') - wait() eq('CR1', meths.get_var('var')) feed(':let var = inputdialog({"cancelreturn": "BAR"})') - wait() feed('') - wait() eq('BAR', meths.get_var('var')) end) it('supports default string', function() feed(':let var = inputdialog("", "DEF1")') - wait() feed('') eq('DEF1', meths.get_var('var')) feed(':let var = inputdialog({"default": "DEF2"})') - wait() feed('') - wait() eq('DEF2', meths.get_var('var')) end) it('errors out on invalid inputs', function() From 88d4a260e1e798d1ebf2b9720c0f9ca631507b71 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 10 May 2017 23:14:23 +0300 Subject: [PATCH 05/11] functests: Remove some redraw calls --- test/functional/eval/input_spec.lua | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index 8f790320cf..857bde54d3 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -60,9 +60,8 @@ describe('input()', function() ]]) end) it('works correctly with multiple numeric arguments (many args)', function() - command('hi Test ctermfg=Red guifg=Red term=bold') - feed([[:echohl Test | call input(1, 2)]]) - command('redraw!') -- Without this it shows two `12` lines. + command('echohl Test') + feed([[:call input(1, 2)]]) -- None of the above problems happen when testing manually. screen:expect([[ | @@ -82,7 +81,7 @@ describe('input()', function() end) it('works correctly with multiple numeric arguments (dict arg)', function() feed([[:echohl Test | echo input({"prompt": 1, "default": 2, "cancelreturn": 3})]]) - command('redraw!') -- Without this it shows two `12` lines. + command('redraw!') -- Without this it shows `12` on the line above. -- None of the above problems happen when testing manually. screen:expect([[ | @@ -109,8 +108,8 @@ describe('input()', function() ]]) end) it('allows omitting everything with dictionary argument', function() - feed(':echohl Test | echo input({})') - command('redraw!') + command('echohl Test') + feed([[:call input({})]]) screen:expect([[ | {EOB:~ }| @@ -193,9 +192,8 @@ describe('inputdialog()', function() ]]) end) it('works correctly with multiple numeric arguments (many args)', function() - command('hi Test ctermfg=Red guifg=Red term=bold') - feed([[:echohl Test | call inputdialog(1, 2)]]) - command('redraw!') -- Without this it shows two `12` lines. + command('echohl Test') + feed([[:call inputdialog(1, 2)]]) -- None of the above problems happen when testing manually. screen:expect([[ | @@ -215,7 +213,7 @@ describe('inputdialog()', function() end) it('works correctly with multiple numeric arguments (dict arg)', function() feed([[:echohl Test | echo inputdialog({"prompt": 1, "default": 2, "cancelreturn": 3})]]) - command('redraw!') -- Without this it shows two `12` lines. + command('redraw!') -- Without this it shows `12` on the line above. -- None of the above problems happen when testing manually. screen:expect([[ | @@ -242,8 +240,8 @@ describe('inputdialog()', function() ]]) end) it('allows omitting everything with dictionary argument', function() - feed(':echohl Test | echo inputdialog({})') - command('redraw!') + command('echohl Test') + feed(':echo inputdialog({})') screen:expect([[ | {EOB:~ }| From 33ca9f711e9a80df8edfc3e5c7519fb5fb7fff2b Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 10 May 2017 23:19:49 +0300 Subject: [PATCH 06/11] functests: Remove outdated comments --- test/functional/eval/input_spec.lua | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index 857bde54d3..a703dbc166 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -62,7 +62,6 @@ describe('input()', function() it('works correctly with multiple numeric arguments (many args)', function() command('echohl Test') feed([[:call input(1, 2)]]) - -- None of the above problems happen when testing manually. screen:expect([[ | {EOB:~ }| @@ -82,7 +81,6 @@ describe('input()', function() it('works correctly with multiple numeric arguments (dict arg)', function() feed([[:echohl Test | echo input({"prompt": 1, "default": 2, "cancelreturn": 3})]]) command('redraw!') -- Without this it shows `12` on the line above. - -- None of the above problems happen when testing manually. screen:expect([[ | {EOB:~ }| @@ -194,7 +192,6 @@ describe('inputdialog()', function() it('works correctly with multiple numeric arguments (many args)', function() command('echohl Test') feed([[:call inputdialog(1, 2)]]) - -- None of the above problems happen when testing manually. screen:expect([[ | {EOB:~ }| @@ -214,7 +211,6 @@ describe('inputdialog()', function() it('works correctly with multiple numeric arguments (dict arg)', function() feed([[:echohl Test | echo inputdialog({"prompt": 1, "default": 2, "cancelreturn": 3})]]) command('redraw!') -- Without this it shows `12` on the line above. - -- None of the above problems happen when testing manually. screen:expect([[ | {EOB:~ }| From b6d73fb7407f8d37bc6a700d09a5eed6c3129539 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 11 May 2017 12:15:41 +0300 Subject: [PATCH 07/11] =?UTF-8?q?functests:=20Get=20rid=20of=20last=20redr?= =?UTF-8?q?aws=20due=20to=20the=20=E2=80=9Cline=20above=E2=80=9D=20issue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/functional/eval/input_spec.lua | 82 +++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 4 deletions(-) diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index a703dbc166..13e93a4764 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -79,8 +79,9 @@ describe('input()', function() ]]) end) it('works correctly with multiple numeric arguments (dict arg)', function() - feed([[:echohl Test | echo input({"prompt": 1, "default": 2, "cancelreturn": 3})]]) - command('redraw!') -- Without this it shows `12` on the line above. + command('echohl Test') + meths.set_var('opts', {prompt=1, default=2, cancelreturn=3}) + feed([[:echo input(opts)]]) screen:expect([[ | {EOB:~ }| @@ -105,6 +106,42 @@ describe('input()', function() {T:3} | ]]) end) + it('works correctly with redraw', function() + command('echohl Test') + meths.set_var('opts', {prompt='Foo>', default='Bar'}) + feed([[:echo inputdialog(opts)]]) + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo>}Bar^ | + ]]) + command('redraw!') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo>}Bar^ | + ]]) + feed('') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo>}Ba^ | + ]]) + command('redraw!') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo>}Ba^ | + ]]) + end) it('allows omitting everything with dictionary argument', function() command('echohl Test') feed([[:call input({})]]) @@ -209,8 +246,9 @@ describe('inputdialog()', function() ]]) end) it('works correctly with multiple numeric arguments (dict arg)', function() - feed([[:echohl Test | echo inputdialog({"prompt": 1, "default": 2, "cancelreturn": 3})]]) - command('redraw!') -- Without this it shows `12` on the line above. + command('echohl Test') + meths.set_var('opts', {prompt=1, default=2, cancelreturn=3}) + feed([[:echo input(opts)]]) screen:expect([[ | {EOB:~ }| @@ -235,6 +273,42 @@ describe('inputdialog()', function() {T:3} | ]]) end) + it('works correctly with redraw', function() + command('echohl Test') + meths.set_var('opts', {prompt='Foo>', default='Bar'}) + feed([[:echo input(opts)]]) + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo>}Bar^ | + ]]) + command('redraw!') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo>}Bar^ | + ]]) + feed('') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo>}Ba^ | + ]]) + command('redraw!') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo>}Ba^ | + ]]) + end) it('allows omitting everything with dictionary argument', function() command('echohl Test') feed(':echo inputdialog({})') From d66ef56739f9b666e54173d7642c0b351f0c2c12 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 May 2017 17:01:22 +0300 Subject: [PATCH 08/11] eval/typval: Fix numbuf parameter documentation --- src/nvim/eval/typval.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index e721ebe345..dd3fe22694 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1210,7 +1210,8 @@ char *tv_dict_get_string(const dict_T *const d, const char *const key, /// /// @param[in] d Dictionary to get item from. /// @param[in] key Dictionary key. -/// @param[in] numbuf Numbuf for. +/// @param[in] numbuf Buffer for non-string items converted to strings, at +/// least of #NUMBUFLEN length. /// /// @return NULL if key does not exist, empty string in case of type error, /// string item value otherwise. @@ -1230,7 +1231,8 @@ const char *tv_dict_get_string_buf(const dict_T *const d, const char *const key, /// @param[in] d Dictionary to get item from. /// @param[in] key Dictionary key. /// @param[in] key_len Key length. -/// @param[in] numbuf Numbuf for. +/// @param[in] numbuf Buffer for non-string items converted to strings, at +/// least of #NUMBUFLEN length. /// @param[in] def Default return when key does not exist. /// /// @return `def` when key does not exist, From 9906db985d064e870b882f1e36f820dd7ce90215 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 May 2017 17:04:54 +0300 Subject: [PATCH 09/11] =?UTF-8?q?functests:=20Remove=20=E2=80=9Ccorrectly?= =?UTF-8?q?=E2=80=9D=20from=20non-regression=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/functional/eval/input_spec.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index 13e93a4764..2eaadad18f 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -31,7 +31,7 @@ before_each(function() end) describe('input()', function() - it('works correctly with multiline prompts', function() + it('works with multiline prompts', function() feed([[:call input("Test\nFoo")]]) screen:expect([[ {EOB:~ }| @@ -41,7 +41,7 @@ describe('input()', function() Foo^ | ]]) end) - it('works correctly with multiline prompts and :echohl', function() + it('works with multiline prompts and :echohl', function() feed([[:echohl Test | call input("Test\nFoo")]]) screen:expect([[ {EOB:~ }| @@ -106,7 +106,7 @@ describe('input()', function() {T:3} | ]]) end) - it('works correctly with redraw', function() + it('works with redraw', function() command('echohl Test') meths.set_var('opts', {prompt='Foo>', default='Bar'}) feed([[:echo inputdialog(opts)]]) @@ -198,7 +198,7 @@ describe('input()', function() end) end) describe('inputdialog()', function() - it('works correctly with multiline prompts', function() + it('works with multiline prompts', function() feed([[:call inputdialog("Test\nFoo")]]) screen:expect([[ {EOB:~ }| @@ -208,7 +208,7 @@ describe('inputdialog()', function() Foo^ | ]]) end) - it('works correctly with multiline prompts and :echohl', function() + it('works with multiline prompts and :echohl', function() feed([[:echohl Test | call inputdialog("Test\nFoo")]]) screen:expect([[ {EOB:~ }| @@ -273,7 +273,7 @@ describe('inputdialog()', function() {T:3} | ]]) end) - it('works correctly with redraw', function() + it('works with redraw', function() command('echohl Test') meths.set_var('opts', {prompt='Foo>', default='Bar'}) feed([[:echo input(opts)]]) From a59ddde72145ef37a374ce680d7e785d63aecf52 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 May 2017 17:07:31 +0300 Subject: [PATCH 10/11] functests: Reword regression test headers --- test/functional/eval/input_spec.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index 2eaadad18f..74ad32bc6c 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -59,7 +59,7 @@ describe('input()', function() {T:Foo}^ | ]]) end) - it('works correctly with multiple numeric arguments (many args)', function() + it('allows unequal numeric arguments when using multiple args', function() command('echohl Test') feed([[:call input(1, 2)]]) screen:expect([[ @@ -78,7 +78,7 @@ describe('input()', function() {T:1}^ | ]]) end) - it('works correctly with multiple numeric arguments (dict arg)', function() + it('allows unequal numeric values when using {opts} dictionary', function() command('echohl Test') meths.set_var('opts', {prompt=1, default=2, cancelreturn=3}) feed([[:echo input(opts)]]) @@ -226,7 +226,7 @@ describe('inputdialog()', function() {T:Foo}^ | ]]) end) - it('works correctly with multiple numeric arguments (many args)', function() + it('allows unequal numeric arguments when using multiple args', function() command('echohl Test') feed([[:call inputdialog(1, 2)]]) screen:expect([[ @@ -245,7 +245,7 @@ describe('inputdialog()', function() {T:1}^ | ]]) end) - it('works correctly with multiple numeric arguments (dict arg)', function() + it('allows unequal numeric values when using {opts} dictionary', function() command('echohl Test') meths.set_var('opts', {prompt=1, default=2, cancelreturn=3}) feed([[:echo input(opts)]]) From d01f140bb3d926486014ae6697c8a7cef241325b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 May 2017 18:16:41 +0300 Subject: [PATCH 11/11] doc: Add a note to vim_diff.txt --- runtime/doc/vim_diff.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index e6184fd528..ca2ab836b8 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -244,6 +244,10 @@ Lua interface (|if_lua.txt|): - Lua has direct access to Neovim api via `vim.api`. - Currently most of features are missing. +|input()| and |inputdialog()| gained support for each other’s features (return +on cancel and completion respectively) via dictionary argument (replaces all +other arguments if used). + ============================================================================== 5. Missing legacy features *nvim-features-missing* *if_lua* *if_perl* *if_mzscheme* *if_tcl*