neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

input_spec.lua (16316B)


      1 local t = require('test.testutil')
      2 local n = require('test.functional.testnvim')()
      3 local Screen = require('test.functional.ui.screen')
      4 
      5 local eq = t.eq
      6 local feed = n.feed
      7 local api = n.api
      8 local clear = n.clear
      9 local source = n.source
     10 local command = n.command
     11 local exc_exec = n.exc_exec
     12 local async_meths = n.async_meths
     13 local NIL = vim.NIL
     14 
     15 local screen
     16 
     17 before_each(function()
     18  clear()
     19  screen = Screen.new(25, 5)
     20  source([[
     21    hi Test ctermfg=Red guifg=Red term=bold
     22    function CustomCompl(...)
     23      return 'TEST'
     24    endfunction
     25    function CustomListCompl(...)
     26      return ['FOO']
     27    endfunction
     28 
     29    highlight RBP1 guibg=Red
     30    highlight RBP2 guibg=Yellow
     31    highlight RBP3 guibg=Green
     32    highlight RBP4 guibg=Blue
     33    let g:NUM_LVLS = 4
     34    function Redraw()
     35      redraw!
     36      return ''
     37    endfunction
     38    cnoremap <expr> {REDRAW} Redraw()
     39    function RainBowParens(cmdline)
     40      let ret = []
     41      let i = 0
     42      let lvl = 0
     43      while i < len(a:cmdline)
     44        if a:cmdline[i] is# '('
     45          call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)])
     46          let lvl += 1
     47        elseif a:cmdline[i] is# ')'
     48          let lvl -= 1
     49          call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)])
     50        endif
     51        let i += 1
     52      endwhile
     53      return ret
     54    endfunction
     55  ]])
     56  screen:set_default_attr_ids({
     57    EOB = { bold = true, foreground = Screen.colors.Blue1 },
     58    T = { foreground = Screen.colors.Red },
     59    RBP1 = { background = Screen.colors.Red },
     60    RBP2 = { background = Screen.colors.Yellow },
     61    RBP3 = { background = Screen.colors.Green },
     62    RBP4 = { background = Screen.colors.Blue },
     63    SEP = { bold = true, reverse = true },
     64    CONFIRM = { bold = true, foreground = Screen.colors.SeaGreen4 },
     65  })
     66 end)
     67 
     68 describe('input()', function()
     69  it('works with multiline prompts', function()
     70    feed([[:call input("Test\nFoo")<CR>]])
     71    screen:expect([[
     72                               |
     73      {EOB:~                        }|
     74      {SEP:                         }|
     75      Test                     |
     76      Foo^                      |
     77    ]])
     78  end)
     79  it('works with multiline prompts and :echohl', function()
     80    feed([[:echohl Test | call input("Test\nFoo")<CR>]])
     81    screen:expect([[
     82                               |
     83      {EOB:~                        }|
     84      {SEP:                         }|
     85      {T:Test}                     |
     86      {T:Foo}^                      |
     87    ]])
     88    command('redraw!')
     89    screen:expect([[
     90                               |
     91      {EOB:~                        }|*3
     92      {T:Foo}^                      |
     93    ]])
     94  end)
     95  it('allows unequal numeric arguments when using multiple args', function()
     96    command('echohl Test')
     97    feed([[:call input(1, 2)<CR>]])
     98    screen:expect([[
     99                               |
    100      {EOB:~                        }|*3
    101      {T:1}2^                       |
    102    ]])
    103    feed('<BS>')
    104    screen:expect([[
    105                               |
    106      {EOB:~                        }|*3
    107      {T:1}^                        |
    108    ]])
    109  end)
    110  it('allows unequal numeric values when using {opts} dict', function()
    111    command('echohl Test')
    112    api.nvim_set_var('opts', { prompt = 1, default = 2, cancelreturn = 3 })
    113    feed([[:echo input(opts)<CR>]])
    114    screen:expect([[
    115                               |
    116      {EOB:~                        }|*3
    117      {T:1}2^                       |
    118    ]])
    119    feed('<BS>')
    120    screen:expect([[
    121                               |
    122      {EOB:~                        }|*3
    123      {T:1}^                        |
    124    ]])
    125    feed('<Esc>')
    126    screen:expect([[
    127      ^                         |
    128      {EOB:~                        }|*3
    129      {T:3}                        |
    130    ]])
    131  end)
    132  it('works with redraw', function()
    133    command('echohl Test')
    134    api.nvim_set_var('opts', { prompt = 'Foo>', default = 'Bar' })
    135    feed([[:echo inputdialog(opts)<CR>]])
    136    screen:expect([[
    137                               |
    138      {EOB:~                        }|*3
    139      {T:Foo>}Bar^                  |
    140    ]])
    141    command('mode')
    142    screen:expect {
    143      grid = [[
    144                               |
    145      {EOB:~                        }|*3
    146      {T:Foo>}Bar^                  |
    147    ]],
    148      reset = true,
    149    }
    150    feed('<BS>')
    151    screen:expect([[
    152                               |
    153      {EOB:~                        }|*3
    154      {T:Foo>}Ba^                   |
    155    ]])
    156    command('mode')
    157    screen:expect {
    158      grid = [[
    159                               |
    160      {EOB:~                        }|*3
    161      {T:Foo>}Ba^                   |
    162    ]],
    163      reset = true,
    164    }
    165  end)
    166  it('allows omitting everything with dict argument', function()
    167    command('echohl Test')
    168    feed([[:call input({})<CR>]])
    169    screen:expect([[
    170                               |
    171      {EOB:~                        }|*3
    172      ^                         |
    173    ]])
    174  end)
    175  it('supports completion', function()
    176    feed(':let var = input("", "", "custom,CustomCompl")<CR>')
    177    feed('<Tab><CR>')
    178    eq('TEST', api.nvim_get_var('var'))
    179 
    180    feed(':let var = input({"completion": "customlist,CustomListCompl"})<CR>')
    181    feed('<Tab><CR>')
    182    eq('FOO', api.nvim_get_var('var'))
    183  end)
    184  it('supports cancelreturn', function()
    185    feed(':let var = input({"cancelreturn": "BAR"})<CR>')
    186    feed('<Esc>')
    187    eq('BAR', api.nvim_get_var('var'))
    188    feed(':let var = input({"cancelreturn": []})<CR>')
    189    feed('<Esc>')
    190    eq({}, api.nvim_get_var('var'))
    191    feed(':let var = input({"cancelreturn": v:false})<CR>')
    192    feed('<Esc>')
    193    eq(false, api.nvim_get_var('var'))
    194    feed(':let var = input({"cancelreturn": v:null})<CR>')
    195    feed('<Esc>')
    196    eq(NIL, api.nvim_get_var('var'))
    197  end)
    198  it('supports default string', function()
    199    feed(':let var = input("", "DEF1")<CR>')
    200    feed('<CR>')
    201    eq('DEF1', api.nvim_get_var('var'))
    202 
    203    feed(':let var = input({"default": "DEF2"})<CR>')
    204    feed('<CR>')
    205    eq('DEF2', api.nvim_get_var('var'))
    206  end)
    207  it('errors out on invalid inputs', function()
    208    eq('Vim(call):E730: Using a List as a String', exc_exec('call input([])'))
    209    eq('Vim(call):E730: Using a List as a String', exc_exec('call input("", [])'))
    210    eq('Vim(call):E730: Using a List as a String', exc_exec('call input("", "", [])'))
    211    eq('Vim(call):E730: Using a List as a String', exc_exec('call input({"prompt": []})'))
    212    eq('Vim(call):E730: Using a List as a String', exc_exec('call input({"default": []})'))
    213    eq('Vim(call):E730: Using a List as a String', exc_exec('call input({"completion": []})'))
    214    eq('Vim(call):E5050: {opts} must be the only argument', exc_exec('call input({}, "default")'))
    215    eq(
    216      'Vim(call):E118: Too many arguments for function: input',
    217      exc_exec('call input("prompt> ", "default", "file", "extra")')
    218    )
    219  end)
    220  it('supports highlighting', function()
    221    command('nnoremap <expr> X input({"highlight": "RainBowParens"})[-1]')
    222    feed([[X]])
    223    feed('(())')
    224    screen:expect([[
    225                               |
    226      {EOB:~                        }|*3
    227      {RBP1:(}{RBP2:()}{RBP1:)}^                     |
    228    ]])
    229  end)
    230  it('is not hidden by :silent', function()
    231    feed([[:silent call input('Foo: ')<CR>]])
    232    screen:expect([[
    233                               |
    234      {EOB:~                        }|
    235      {SEP:                         }|
    236      Foo: ^                    |
    237                               |
    238    ]])
    239    feed('Bar')
    240    screen:expect([[
    241                               |
    242      {EOB:~                        }|
    243      {SEP:                         }|
    244      Foo: Bar^                 |
    245                               |
    246    ]])
    247    feed('<CR>')
    248  end)
    249 end)
    250 describe('inputdialog()', function()
    251  it('works with multiline prompts', function()
    252    feed([[:call inputdialog("Test\nFoo")<CR>]])
    253    screen:expect([[
    254                               |
    255      {EOB:~                        }|
    256      {SEP:                         }|
    257      Test                     |
    258      Foo^                      |
    259    ]])
    260  end)
    261  it('works with multiline prompts and :echohl', function()
    262    feed([[:echohl Test | call inputdialog("Test\nFoo")<CR>]])
    263    screen:expect([[
    264                               |
    265      {EOB:~                        }|
    266      {SEP:                         }|
    267      {T:Test}                     |
    268      {T:Foo}^                      |
    269    ]])
    270    command('redraw!')
    271    screen:expect([[
    272                               |
    273      {EOB:~                        }|*3
    274      {T:Foo}^                      |
    275    ]])
    276  end)
    277  it('allows unequal numeric arguments when using multiple args', function()
    278    command('echohl Test')
    279    feed([[:call inputdialog(1, 2)<CR>]])
    280    screen:expect([[
    281                               |
    282      {EOB:~                        }|*3
    283      {T:1}2^                       |
    284    ]])
    285    feed('<BS>')
    286    screen:expect([[
    287                               |
    288      {EOB:~                        }|*3
    289      {T:1}^                        |
    290    ]])
    291  end)
    292  it('allows unequal numeric values when using {opts} dict', function()
    293    command('echohl Test')
    294    api.nvim_set_var('opts', { prompt = 1, default = 2, cancelreturn = 3 })
    295    feed([[:echo input(opts)<CR>]])
    296    screen:expect([[
    297                               |
    298      {EOB:~                        }|*3
    299      {T:1}2^                       |
    300    ]])
    301    feed('<BS>')
    302    screen:expect([[
    303                               |
    304      {EOB:~                        }|*3
    305      {T:1}^                        |
    306    ]])
    307    feed('<Esc>')
    308    screen:expect([[
    309      ^                         |
    310      {EOB:~                        }|*3
    311      {T:3}                        |
    312    ]])
    313  end)
    314  it('works with redraw', function()
    315    command('echohl Test')
    316    api.nvim_set_var('opts', { prompt = 'Foo>', default = 'Bar' })
    317    feed([[:echo input(opts)<CR>]])
    318    screen:expect([[
    319                               |
    320      {EOB:~                        }|*3
    321      {T:Foo>}Bar^                  |
    322    ]])
    323    command('mode')
    324    screen:expect {
    325      grid = [[
    326                               |
    327      {EOB:~                        }|*3
    328      {T:Foo>}Bar^                  |
    329    ]],
    330      reset = true,
    331    }
    332    feed('<BS>')
    333    screen:expect([[
    334                               |
    335      {EOB:~                        }|*3
    336      {T:Foo>}Ba^                   |
    337    ]])
    338    command('mode')
    339    screen:expect {
    340      grid = [[
    341                               |
    342      {EOB:~                        }|*3
    343      {T:Foo>}Ba^                   |
    344    ]],
    345      reset = true,
    346    }
    347  end)
    348  it('allows omitting everything with dict argument', function()
    349    command('echohl Test')
    350    feed(':echo inputdialog({})<CR>')
    351    screen:expect([[
    352                               |
    353      {EOB:~                        }|*3
    354      ^                         |
    355    ]])
    356  end)
    357  it('supports completion', function()
    358    feed(':let var = inputdialog({"completion": "customlist,CustomListCompl"})<CR>')
    359    feed('<Tab><CR>')
    360    eq('FOO', api.nvim_get_var('var'))
    361  end)
    362  it('supports cancelreturn', function()
    363    feed(':let var = inputdialog("", "", "CR1")<CR>')
    364    feed('<Esc>')
    365    eq('CR1', api.nvim_get_var('var'))
    366 
    367    feed(':let var = inputdialog({"cancelreturn": "BAR"})<CR>')
    368    feed('<Esc>')
    369    eq('BAR', api.nvim_get_var('var'))
    370  end)
    371  it('supports default string', function()
    372    feed(':let var = inputdialog("", "DEF1")<CR>')
    373    feed('<CR>')
    374    eq('DEF1', api.nvim_get_var('var'))
    375 
    376    feed(':let var = inputdialog({"default": "DEF2"})<CR>')
    377    feed('<CR>')
    378    eq('DEF2', api.nvim_get_var('var'))
    379  end)
    380  it('errors out on invalid inputs', function()
    381    eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog([])'))
    382    eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog("", [])'))
    383    eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog("", "", [])'))
    384    eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog({"prompt": []})'))
    385    eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog({"default": []})'))
    386    eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog({"completion": []})'))
    387    eq(
    388      'Vim(call):E5050: {opts} must be the only argument',
    389      exc_exec('call inputdialog({}, "default")')
    390    )
    391    eq(
    392      'Vim(call):E118: Too many arguments for function: inputdialog',
    393      exc_exec('call inputdialog("prompt> ", "default", "file", "extra")')
    394    )
    395  end)
    396  it('supports highlighting', function()
    397    command('nnoremap <expr> X inputdialog({"highlight": "RainBowParens"})[-1]')
    398    feed([[X]])
    399    feed('(())')
    400    screen:expect([[
    401                               |
    402      {EOB:~                        }|*3
    403      {RBP1:(}{RBP2:()}{RBP1:)}^                     |
    404    ]])
    405  end)
    406 end)
    407 
    408 describe('confirm()', function()
    409  it('works', function()
    410    api.nvim_set_option_value('more', false, {}) -- Avoid hit-enter prompt
    411    api.nvim_set_option_value('laststatus', 2, {})
    412    -- screen:expect() calls are needed to avoid feeding input too early
    413    screen:expect({ any = '%[No Name%]' })
    414 
    415    async_meths.nvim_command([[let a = confirm('Press O to proceed')]])
    416    screen:expect({ any = '{CONFIRM:.+: }' })
    417    feed('o')
    418    screen:expect({ any = '%[No Name%]' })
    419    eq(1, api.nvim_get_var('a'))
    420 
    421    async_meths.nvim_command([[let a = 'Are you sure?'->confirm("&Yes\n&No")]])
    422    screen:expect({ any = '{CONFIRM:.+: }' })
    423    feed('y')
    424    screen:expect({ any = '%[No Name%]' })
    425    eq(1, api.nvim_get_var('a'))
    426 
    427    async_meths.nvim_command([[let a = confirm('Are you sure?', "&Yes\n&No")]])
    428    screen:expect({ any = '{CONFIRM:.+: }' })
    429    feed('n')
    430    screen:expect({ any = '%[No Name%]' })
    431    eq(2, api.nvim_get_var('a'))
    432 
    433    -- Not possible to match Vim's CTRL-C test here as CTRL-C always sets got_int in Nvim.
    434 
    435    -- confirm() should return 0 when pressing ESC.
    436    async_meths.nvim_command([[let a = confirm('Are you sure?', "&Yes\n&No")]])
    437    screen:expect({ any = '{CONFIRM:.+: }' })
    438    feed('<Esc>')
    439    screen:expect({ any = '%[No Name%]' })
    440    eq(0, api.nvim_get_var('a'))
    441 
    442    -- Default choice is returned when pressing <CR>.
    443    async_meths.nvim_command([[let a = confirm('Are you sure?', "&Yes\n&No")]])
    444    screen:expect({ any = '{CONFIRM:.+: }' })
    445    feed('<CR>')
    446    screen:expect({ any = '%[No Name%]' })
    447    eq(1, api.nvim_get_var('a'))
    448 
    449    async_meths.nvim_command([[let a = confirm('Are you sure?', "&Yes\n&No", 2)]])
    450    screen:expect({ any = '{CONFIRM:.+: }' })
    451    feed('<CR>')
    452    screen:expect({ any = '%[No Name%]' })
    453    eq(2, api.nvim_get_var('a'))
    454 
    455    async_meths.nvim_command([[let a = confirm('Are you sure?', "&Yes\n&No", 0)]])
    456    screen:expect({ any = '{CONFIRM:.+: }' })
    457    feed('<CR>')
    458    screen:expect({ any = '%[No Name%]' })
    459    eq(0, api.nvim_get_var('a'))
    460 
    461    -- Test with the {type} 4th argument
    462    for _, type in ipairs({ 'Error', 'Question', 'Info', 'Warning', 'Generic' }) do
    463      async_meths.nvim_command(
    464        ([[let a = confirm('Are you sure?', "&Yes\n&No", 1, '%s')]]):format(type)
    465      )
    466      screen:expect({ any = '{CONFIRM:.+: }' })
    467      feed('y')
    468      screen:expect({ any = '%[No Name%]' })
    469      eq(1, api.nvim_get_var('a'))
    470    end
    471  end)
    472 
    473  it('shows dialog even if :silent #8788', function()
    474    command("autocmd BufNewFile * call confirm('test')")
    475 
    476    local function check_and_clear(edit_line)
    477      screen:expect([[
    478                                 |
    479        {SEP:                         }|
    480        ]] .. edit_line .. [[
    481        {CONFIRM:test}                     |
    482        {CONFIRM:[O]k: }^                   |
    483      ]])
    484      feed('<cr>')
    485      command('redraw')
    486      command('bdelete!')
    487    end
    488 
    489    -- With shortmess-=F
    490    command('set shortmess-=F')
    491    feed(':edit foo<cr>')
    492    check_and_clear('"foo" [New]              |\n')
    493 
    494    -- With shortmess+=F
    495    command('set shortmess+=F')
    496    feed(':edit foo<cr>')
    497    check_and_clear(':edit foo                |\n')
    498 
    499    -- With :silent
    500    feed(':silent edit foo<cr>')
    501    check_and_clear(':silent edit foo         |\n')
    502 
    503    -- With API (via eval/Vimscript) call and shortmess+=F
    504    feed(':call nvim_command("edit x")<cr>')
    505    check_and_clear(':call nvim_command("edit |\n')
    506 
    507    async_meths.nvim_command('edit x')
    508    check_and_clear('                         |\n')
    509  end)
    510 end)