neovim

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

prompt_buffer_spec.lua (31159B)


      1 local t = require('test.testutil')
      2 local n = require('test.functional.testnvim')()
      3 local Screen = require('test.functional.ui.screen')
      4 
      5 local feed = n.feed
      6 local fn = n.call
      7 local source = n.source
      8 local clear = n.clear
      9 local command = n.command
     10 local expect = n.expect
     11 local poke_eventloop = n.poke_eventloop
     12 local api = n.api
     13 local eq = t.eq
     14 local neq = t.neq
     15 local exec_lua = n.exec_lua
     16 
     17 describe('prompt buffer', function()
     18  local screen
     19 
     20  before_each(function()
     21    clear()
     22    screen = Screen.new(25, 10)
     23    command('set laststatus=0 nohidden')
     24  end)
     25 
     26  local function source_script()
     27    source([[
     28      func TextEntered(text)
     29        if a:text == "exit"
     30          " Reset &modified to allow the buffer to be closed.
     31          set nomodified
     32          stopinsert
     33          close
     34        else
     35          " Add the output above the current prompt.
     36          call append(line("$") - 1, split('Command: "' . a:text . '"', '\n'))
     37          " Reset &modified to allow the buffer to be closed.
     38          set nomodified
     39          call timer_start(20, {id -> TimerFunc(a:text)})
     40        endif
     41      endfunc
     42 
     43      func TimerFunc(text)
     44        " Add the output above the current prompt.
     45        call append(line("$") - 1, split('Result: "' . a:text .'"', '\n'))
     46        " Reset &modified to allow the buffer to be closed.
     47        set nomodified
     48      endfunc
     49 
     50      func SwitchWindows()
     51        call timer_start(0, {-> execute("wincmd p", "")})
     52      endfunc
     53 
     54      call setline(1, "other buffer")
     55      set nomodified
     56      new
     57      set buftype=prompt
     58      call prompt_setcallback(bufnr(''), function("TextEntered"))
     59      eval bufnr("")->prompt_setprompt("cmd: ")
     60      startinsert
     61    ]])
     62    screen:expect([[
     63      cmd: ^                    |
     64      {1:~                        }|*3
     65      {3:[Prompt] [+]             }|
     66      other buffer             |
     67      {1:~                        }|*3
     68      {5:-- INSERT --}             |
     69    ]])
     70  end
     71 
     72  -- oldtest: Test_prompt_basic()
     73  it('works', function()
     74    source_script()
     75    feed('hello\n')
     76    screen:expect([[
     77      cmd: hello               |
     78      Command: "hello"         |
     79      Result: "hello"          |
     80      cmd: ^                    |
     81      {3:[Prompt]                 }|
     82      other buffer             |
     83      {1:~                        }|*3
     84      {5:-- INSERT --}             |
     85    ]])
     86    feed('exit\n')
     87    screen:expect([[
     88      ^other buffer             |
     89      {1:~                        }|*8
     90                               |
     91    ]])
     92  end)
     93 
     94  -- oldtest: Test_prompt_editing()
     95  it('editing', function()
     96    source_script()
     97    feed('hello<BS><BS>')
     98    screen:expect([[
     99      cmd: hel^                 |
    100      {1:~                        }|*3
    101      {3:[Prompt] [+]             }|
    102      other buffer             |
    103      {1:~                        }|*3
    104      {5:-- INSERT --}             |
    105    ]])
    106    feed('<Left><Left><Left><BS>-')
    107    screen:expect([[
    108      cmd: -^hel                |
    109      {1:~                        }|*3
    110      {3:[Prompt] [+]             }|
    111      other buffer             |
    112      {1:~                        }|*3
    113      {5:-- INSERT --}             |
    114    ]])
    115    feed('<C-O>lz')
    116    screen:expect([[
    117      cmd: -hz^el               |
    118      {1:~                        }|*3
    119      {3:[Prompt] [+]             }|
    120      other buffer             |
    121      {1:~                        }|*3
    122      {5:-- INSERT --}             |
    123    ]])
    124    feed('<End>x')
    125    screen:expect([[
    126      cmd: -hzelx^              |
    127      {1:~                        }|*3
    128      {3:[Prompt] [+]             }|
    129      other buffer             |
    130      {1:~                        }|*3
    131      {5:-- INSERT --}             |
    132    ]])
    133 
    134    -- :edit doesn't apply on prompt buffer
    135    eq('Vim(edit):cannot :edit a prompt buffer', t.pcall_err(api.nvim_command, 'edit'))
    136 
    137    feed('<C-U>exit\n')
    138    screen:expect([[
    139      ^other buffer             |
    140      {1:~                        }|*8
    141                               |
    142    ]])
    143  end)
    144 
    145  -- oldtest: Test_prompt_switch_windows()
    146  it('switch windows', function()
    147    source_script()
    148    feed('<C-O>:call SwitchWindows()<CR>')
    149    screen:expect([[
    150      cmd:                     |
    151      {1:~                        }|*3
    152      {2:[Prompt] [+]             }|
    153      ^other buffer             |
    154      {1:~                        }|*3
    155                               |
    156    ]])
    157    feed('<C-O>:call SwitchWindows()<CR>')
    158    screen:expect([[
    159      cmd: ^                    |
    160      {1:~                        }|*3
    161      {3:[Prompt] [+]             }|
    162      other buffer             |
    163      {1:~                        }|*3
    164      {5:-- INSERT --}             |
    165    ]])
    166    feed('<Esc>')
    167    screen:expect([[
    168      cmd:^                     |
    169      {1:~                        }|*3
    170      {3:[Prompt] [+]             }|
    171      other buffer             |
    172      {1:~                        }|*3
    173                               |
    174    ]])
    175  end)
    176 
    177  -- oldtest: Test_prompt_while_writing_to_hidden_buffer()
    178  it('keeps insert mode after aucmd_restbuf in callback', function()
    179    source_script()
    180    source [[
    181      let s:buf = nvim_create_buf(1, 1)
    182      call timer_start(0, {-> nvim_buf_set_lines(s:buf, -1, -1, 0, ['walrus'])})
    183    ]]
    184    poke_eventloop()
    185    eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
    186  end)
    187 
    188  -- oldtest: Test_prompt_appending_while_hidden()
    189  it('accessing hidden prompt buffer does not start insert mode', function()
    190    local prev_win = api.nvim_get_current_win()
    191    source([[
    192      new prompt
    193      set buftype=prompt
    194      set bufhidden=hide
    195 
    196      func s:TextEntered(text)
    197          if a:text == 'exit'
    198              close
    199          endif
    200          let g:entered = a:text
    201      endfunc
    202      call prompt_setcallback(bufnr(), function('s:TextEntered'))
    203 
    204      func DoAppend(cmd_before = '')
    205        exe a:cmd_before
    206        call appendbufline('prompt', '$', 'Test')
    207        return ''
    208      endfunc
    209 
    210      autocmd User SwitchTabPages tabprevious | tabnext
    211      func DoAutoAll(cmd_before = '')
    212        exe a:cmd_before
    213        doautoall User SwitchTabPages
    214        return ''
    215      endfunc
    216    ]])
    217    feed('asomething<CR>')
    218    eq('something', api.nvim_get_var('entered'))
    219    neq(prev_win, api.nvim_get_current_win())
    220    feed('exit<CR>')
    221    eq(prev_win, api.nvim_get_current_win())
    222    eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
    223    command('call DoAppend()')
    224    eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
    225    feed('i')
    226    eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
    227    command('call DoAppend()')
    228    eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
    229    command("call DoAppend('stopinsert')")
    230    eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
    231    command("call DoAppend('startreplace')")
    232    eq({ mode = 'R', blocking = false }, api.nvim_get_mode())
    233    feed('<Esc>')
    234    command('tabnew')
    235    eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
    236    command("call DoAutoAll('startinsert')")
    237    eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
    238    command("call DoAutoAll('stopinsert')")
    239    eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
    240  end)
    241 
    242  -- oldtest: Test_prompt_leave_modify_hidden()
    243  it('modifying hidden buffer does not prevent prompt buffer mode change', function()
    244    source([[
    245      file hidden
    246      set bufhidden=hide
    247      enew
    248      new prompt
    249      set buftype=prompt
    250 
    251      inoremap <buffer> w <Cmd>wincmd w<CR>
    252      inoremap <buffer> q <Cmd>bwipe!<CR>
    253      autocmd BufLeave prompt call appendbufline('hidden', '$', 'Leave')
    254      autocmd BufEnter prompt call appendbufline('hidden', '$', 'Enter')
    255      autocmd BufWinLeave prompt call appendbufline('hidden', '$', 'Close')
    256    ]])
    257    feed('a')
    258    eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
    259    feed('w')
    260    eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
    261    feed('<C-W>w')
    262    eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
    263    feed('q')
    264    eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
    265    command('bwipe!')
    266    expect([[
    267 
    268      Leave
    269      Enter
    270      Leave
    271      Close]])
    272  end)
    273 
    274  it('can insert multiline text', function()
    275    source_script()
    276    local buf = api.nvim_get_current_buf()
    277 
    278    feed('line 1<s-cr>line 2<s-cr>line 3')
    279    screen:expect([[
    280      cmd: line 1              |
    281      line 2                   |
    282      line 3^                   |
    283      {1:~                        }|
    284      {3:[Prompt] [+]             }|
    285      other buffer             |
    286      {1:~                        }|*3
    287      {5:-- INSERT --}             |
    288    ]])
    289 
    290    -- prompt_getinput works with multiline input
    291    eq('line 1\nline 2\nline 3', fn('prompt_getinput', buf))
    292 
    293    feed('<cr>')
    294    -- submitting multiline text works
    295    screen:expect([[
    296      Result: "line 1          |
    297      line 2                   |
    298      line 3"                  |
    299      cmd: ^                    |
    300      {3:[Prompt]                 }|
    301      other buffer             |
    302      {1:~                        }|*3
    303      {5:-- INSERT --}             |
    304    ]])
    305 
    306    eq('', fn('prompt_getinput', buf))
    307 
    308    -- % prompt is not repeated with formatoptions+=r
    309    source([[
    310      bwipeout!
    311      set formatoptions+=r
    312      call prompt_setprompt(bufnr(), "% ")
    313      set buftype=prompt
    314    ]])
    315    feed('iline1<s-cr>line2')
    316    screen:expect([[
    317      other buffer             |
    318      % line1                  |
    319      line2^                    |
    320      {1:~                        }|*6
    321      {5:-- INSERT --}             |
    322    ]])
    323 
    324    -- ensure cursor gets placed on first line of user input.
    325    -- when insert mode is entered from read-only region of prompt buffer.
    326    local prompt_pos = api.nvim_buf_get_mark(0, ':')
    327    feed('<esc>')
    328    -- works before prompt
    329    api.nvim_win_set_cursor(0, { prompt_pos[1] - 1, 0 })
    330    screen:expect([[
    331      ^other buffer             |
    332      % line1                  |
    333      line2                    |
    334      {1:~                        }|*6
    335                               |
    336    ]])
    337    feed('a')
    338    feed('<esc>')
    339    screen:expect([[
    340      other buffer             |
    341      %^ line1                  |
    342      line2                    |
    343      {1:~                        }|*6
    344                               |
    345    ]])
    346    -- works on prompt
    347    api.nvim_win_set_cursor(0, { prompt_pos[1], 0 })
    348    screen:expect([[
    349      other buffer             |
    350      ^% line1                  |
    351      line2                    |
    352      {1:~                        }|*6
    353                               |
    354    ]])
    355    feed('a')
    356    feed('<esc>')
    357    screen:expect([[
    358      other buffer             |
    359      %^ line1                  |
    360      line2                    |
    361      {1:~                        }|*6
    362                               |
    363    ]])
    364 
    365    -- i_<Left> i_<C-Left> i_<Home> i_<End> keys on prompt-line doesn't put cursor
    366    -- at end of text
    367    feed('a<Left><C-Left>')
    368    screen:expect([[
    369      other buffer             |
    370      % ^line1                  |
    371      line2                    |
    372      {1:~                        }|*6
    373      {5:-- INSERT --}             |
    374    ]])
    375 
    376    feed('<End>')
    377    screen:expect([[
    378      other buffer             |
    379      % line1^                  |
    380      line2                    |
    381      {1:~                        }|*6
    382      {5:-- INSERT --}             |
    383    ]])
    384 
    385    feed('<Home>')
    386    screen:expect([[
    387      other buffer             |
    388      % ^line1                  |
    389      line2                    |
    390      {1:~                        }|*6
    391      {5:-- INSERT --}             |
    392    ]])
    393  end)
    394 
    395  it('can put (p) multiline text', function()
    396    source_script()
    397    local buf = api.nvim_get_current_buf()
    398 
    399    fn('setreg', 'a', 'line 1\nline 2\nline 3')
    400    feed('<esc>"ap')
    401    screen:expect([[
    402      cmd: ^line 1              |
    403      line 2                   |
    404      line 3                   |
    405      {1:~                        }|
    406      {3:[Prompt] [+]             }|
    407      other buffer             |
    408      {1:~                        }|*3
    409                               |
    410    ]])
    411 
    412    -- prompt_getinput works with pasted input
    413    eq('line 1\nline 2\nline 3', fn('prompt_getinput', buf))
    414 
    415    feed('i<cr>')
    416    screen:expect([[
    417      Result: "line 1          |
    418      line 2                   |
    419      line 3"                  |
    420      cmd: ^                    |
    421      {3:[Prompt]                 }|
    422      other buffer             |
    423      {1:~                        }|*3
    424      {5:-- INSERT --}             |
    425    ]])
    426  end)
    427 
    428  it('can put multiline text with nvim_paste', function()
    429    source_script()
    430    api.nvim_paste('line 1\nline 2\nline 3', false, -1)
    431    screen:expect([[
    432      cmd: line 1              |
    433      line 2                   |
    434      line 3^                   |
    435      {1:~                        }|
    436      {3:[Prompt] [+]             }|
    437      other buffer             |
    438      {1:~                        }|*3
    439      {5:-- INSERT --}             |
    440    ]])
    441  end)
    442 
    443  it('can undo current prompt', function()
    444    source_script()
    445    local buf = api.nvim_get_current_buf()
    446 
    447    -- text editing allowed in current prompt
    448    feed('tests-initial<esc>')
    449    feed('bimiddle-<esc>')
    450    screen:expect([[
    451      cmd: tests-middle^-initial|
    452      {1:~                        }|*3
    453      {3:[Prompt] [+]             }|
    454      other buffer             |
    455      {1:~                        }|*3
    456                               |
    457    ]])
    458 
    459    feed('Fdx')
    460    screen:expect([[
    461      cmd: tests-mid^le-initial |
    462      {1:~                        }|*3
    463      {3:[Prompt] [+]             }|
    464      other buffer             |
    465      {1:~                        }|*3
    466                               |
    467    ]])
    468 
    469    -- can undo edits until prompt has been submitted
    470    feed('u')
    471    screen:expect([[
    472      cmd: tests-mid^dle-initial|
    473      {1:~                        }|*3
    474      {3:[Prompt] [+]             }|
    475      other buffer             |
    476      {1:~                        }|*3
    477      1 change; {MATCH:.*} |
    478    ]])
    479 
    480    -- undo is reflected in prompt_getinput
    481    eq('tests-middle-initial', fn('prompt_getinput', buf))
    482 
    483    feed('u')
    484    screen:expect([[
    485      cmd: tests-^initial       |
    486      {1:~                        }|*3
    487      {3:[Prompt] [+]             }|
    488      other buffer             |
    489      {1:~                        }|*3
    490      1 change; {MATCH:.*} |
    491    ]])
    492 
    493    feed('i<cr><esc>')
    494    screen:expect([[
    495      cmd: tests-initial       |
    496      Command: "tests-initial" |
    497      Result: "tests-initial"  |
    498      cmd:^                     |
    499      {3:[Prompt]                 }|
    500      other buffer             |
    501      {1:~                        }|*3
    502                               |
    503    ]])
    504 
    505    -- after submit undo does nothing
    506    feed('u')
    507    screen:expect([[
    508      cmd: tests-initial       |
    509      Command: "tests-initial" |
    510      cmd:^                     |
    511      {1:~                        }|
    512      {3:[Prompt] [+]             }|
    513      other buffer             |
    514      {1:~                        }|*3
    515      1 line {MATCH:.*} |
    516    ]])
    517 
    518    -- "S" does not clear undo
    519    feed('ihello<Esc>S')
    520    screen:expect([[
    521      cmd: tests-initial       |
    522      Command: "tests-initial" |
    523      cmd: ^                    |
    524      {1:~                        }|
    525      {3:[Prompt] [+]             }|
    526      other buffer             |
    527      {1:~                        }|*3
    528      {5:-- INSERT --}             |
    529    ]])
    530    feed('<Esc>u')
    531    screen:expect([[
    532      cmd: tests-initial       |
    533      Command: "tests-initial" |
    534      ^cmd: hello               |
    535      {1:~                        }|
    536      {3:[Prompt] [+]             }|
    537      other buffer             |
    538      {1:~                        }|*3
    539      1 change; {MATCH:.*} |
    540    ]])
    541 
    542    -- undo cleared if prompt changes
    543    -- (otherwise undoing would abort it and append a new prompt, which isn't useful)
    544    fn('prompt_setprompt', '', 'cmd > ')
    545    feed('u')
    546    screen:expect([[
    547      cmd: tests-initial       |
    548      Command: "tests-initial" |
    549      c^md > hello              |
    550      {1:~                        }|
    551      {3:[Prompt] [+]             }|
    552      other buffer             |
    553      {1:~                        }|*3
    554      Already at oldest change |
    555    ]])
    556 
    557    -- new prompt line appended to fix missing prompt also clears undo
    558    feed('A there')
    559    fn('setpos', "':", { 0, fn('line', '.'), 99, 0 })
    560    feed('<Esc>u')
    561    screen:expect([[
    562      cmd: tests-initial       |
    563      Command: "tests-initial" |
    564      cmd > hello there        |
    565      cmd >^                    |
    566      {3:[Prompt] [+]             }|
    567      other buffer             |
    568      {1:~                        }|*3
    569      Already at oldest change |
    570    ]])
    571  end)
    572 
    573  it('o/O can create new lines', function()
    574    source_script()
    575    local buf = api.nvim_get_current_buf()
    576 
    577    feed('line 1<s-cr>line 2<s-cr>line 3')
    578    screen:expect([[
    579      cmd: line 1              |
    580      line 2                   |
    581      line 3^                   |
    582      {1:~                        }|
    583      {3:[Prompt] [+]             }|
    584      other buffer             |
    585      {1:~                        }|*3
    586      {5:-- INSERT --}             |
    587    ]])
    588 
    589    feed('<esc>koafter')
    590    screen:expect([[
    591      cmd: line 1              |
    592      line 2                   |
    593      after^                    |
    594      line 3                   |
    595      {3:[Prompt] [+]             }|
    596      other buffer             |
    597      {1:~                        }|*3
    598      {5:-- INSERT --}             |
    599    ]])
    600 
    601    -- newline created with o is reflected in prompt_getinput
    602    eq('line 1\nline 2\nafter\nline 3', fn('prompt_getinput', buf))
    603 
    604    feed('<esc>kObefore')
    605 
    606    screen:expect([[
    607      cmd: line 1              |
    608      before^                   |
    609      line 2                   |
    610      after                    |
    611      {3:[Prompt] [+]             }|
    612      other buffer             |
    613      {1:~                        }|*3
    614      {5:-- INSERT --}             |
    615    ]])
    616 
    617    -- newline created with O is reflected in prompt_getinput
    618    eq('line 1\nbefore\nline 2\nafter\nline 3', fn('prompt_getinput', buf))
    619 
    620    feed('<cr>')
    621    screen:expect([[
    622      line 2                   |
    623      after                    |
    624      line 3"                  |
    625      cmd: ^                    |
    626      {3:[Prompt]                 }|
    627      other buffer             |
    628      {1:~                        }|*3
    629      {5:-- INSERT --}             |
    630    ]])
    631 
    632    feed('line 4<s-cr>line 5')
    633 
    634    feed('<esc>k0oafter prompt')
    635    screen:expect([[
    636      after                    |
    637      line 3"                  |
    638      cmd: line 4              |
    639      after prompt^             |
    640      {3:[Prompt] [+]             }|
    641      other buffer             |
    642      {1:~                        }|*3
    643      {5:-- INSERT --}             |
    644    ]])
    645 
    646    feed('<esc>k0Oat prompt')
    647    screen:expect([[
    648      after                    |
    649      line 3"                  |
    650      cmd: at prompt^           |
    651      line 4                   |
    652      {3:[Prompt] [+]             }|
    653      other buffer             |
    654      {1:~                        }|*3
    655      {5:-- INSERT --}             |
    656    ]])
    657 
    658    feed('<cr>')
    659    screen:expect([[
    660      line 4                   |
    661      after prompt             |
    662      line 5"                  |
    663      cmd: ^                    |
    664      {3:[Prompt]                 }|
    665      other buffer             |
    666      {1:~                        }|*3
    667      {5:-- INSERT --}             |
    668    ]])
    669  end)
    670 
    671  it('deleting prompt adds it back on insert', function()
    672    source_script()
    673    feed('asdf')
    674    screen:expect([[
    675      cmd: asdf^                |
    676      {1:~                        }|*3
    677      {3:[Prompt] [+]             }|
    678      other buffer             |
    679      {1:~                        }|*3
    680      {5:-- INSERT --}             |
    681    ]])
    682 
    683    feed('<esc>ddi')
    684    screen:expect([[
    685      cmd: ^                    |
    686      {1:~                        }|*3
    687      {3:[Prompt] [+]             }|
    688      other buffer             |
    689      {1:~                        }|*3
    690      {5:-- INSERT --}             |
    691    ]])
    692 
    693    feed('asdf')
    694    screen:expect([[
    695      cmd: asdf^                |
    696      {1:~                        }|*3
    697      {3:[Prompt] [+]             }|
    698      other buffer             |
    699      {1:~                        }|*3
    700      {5:-- INSERT --}             |
    701    ]])
    702 
    703    feed('<esc>cc')
    704    screen:expect([[
    705      cmd: ^                    |
    706      {1:~                        }|*3
    707      {3:[Prompt] [+]             }|
    708      other buffer             |
    709      {1:~                        }|*3
    710      {5:-- INSERT --}             |
    711    ]])
    712  end)
    713 
    714  it("sets the ': mark", function()
    715    api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
    716    exec_lua(function()
    717      local buf = vim.api.nvim_get_current_buf()
    718      vim.fn.prompt_setprompt(buf, 'cmd > ')
    719      vim.fn.prompt_setcallback(buf, function(str)
    720        local last_line = vim.api.nvim_buf_line_count(buf)
    721        vim.api.nvim_buf_set_lines(buf, last_line - 1, last_line - 1, true, vim.split(str, '\n'))
    722      end)
    723    end)
    724 
    725    feed('asdf')
    726    eq({ 1, 6 }, api.nvim_buf_get_mark(0, ':'))
    727    feed('<cr>')
    728    eq({ 3, 6 }, api.nvim_buf_get_mark(0, ':'))
    729    -- Multiline prompt.
    730    feed('<s-cr>line1<s-cr>line2<s-cr>line3<cr>')
    731    eq({ 11, 6 }, api.nvim_buf_get_mark(0, ':'))
    732 
    733    -- ': mark is only available in prompt buffer.
    734    api.nvim_set_option_value('buftype', '', { buf = 0 })
    735    eq("Invalid mark name: ':'", t.pcall_err(api.nvim_buf_get_mark, 0, ':'))
    736 
    737    -- mark can be moved
    738    api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
    739    local last_line = api.nvim_buf_line_count(0)
    740    eq({ last_line, 6 }, api.nvim_buf_get_mark(0, ':'))
    741    eq(true, api.nvim_buf_set_mark(0, ':', 1, 5, {}))
    742    eq({ 1, 5 }, api.nvim_buf_get_mark(0, ':'))
    743 
    744    -- No crash from invalid col.
    745    eq(true, api.nvim_buf_set_mark(0, ':', fn('line', '.'), 999, {}))
    746    eq({ 12, 6 }, api.nvim_buf_get_mark(0, ':'))
    747 
    748    -- Clamps lnum to at least 1. Do in one event to repro the leak.
    749    exec_lua(function()
    750      vim.fn.setpos("':", { 0, 0, 0, 0 })
    751      vim.fn.prompt_setprompt('', 'bar > ')
    752    end)
    753    eq({ 1, 6 }, api.nvim_buf_get_mark(0, ':'))
    754 
    755    -- No ml_get error from invalid lnum.
    756    command('set messagesopt+=wait:0 messagesopt-=hit-enter')
    757    fn('setpos', "':", { 0, 999, 7, 0 })
    758    eq('', api.nvim_get_vvar('errmsg'))
    759    command('set messagesopt&')
    760    eq({ 13, 6 }, api.nvim_buf_get_mark(0, ':'))
    761  end)
    762 
    763  describe('prompt_getinput', function()
    764    it('returns current prompts text', function()
    765      command('new')
    766      local bufnr = fn('bufnr')
    767      api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
    768      eq('', fn('prompt_getinput', bufnr))
    769      feed('iasdf')
    770      eq('asdf', fn('prompt_getinput', bufnr))
    771      feed('<esc>dd')
    772      eq('', fn('prompt_getinput', bufnr))
    773      feed('iasdf2')
    774      eq('asdf2', fn('prompt_getinput', bufnr))
    775 
    776      -- returns empty string when called from non prompt buffer
    777      api.nvim_set_option_value('buftype', '', { buf = 0 })
    778      eq('', fn('prompt_getinput', bufnr))
    779    end)
    780  end)
    781 
    782  it('programmatic (non-user) edits', function()
    783    api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
    784 
    785    -- with nvim_buf_set_lines
    786    exec_lua([[
    787      local buf = vim.api.nvim_get_current_buf()
    788      vim.fn.prompt_setcallback(buf, function(text)
    789        vim.api.nvim_buf_set_lines(buf, -2, -2, true, vim.split(text, '\n'))
    790      end)
    791    ]])
    792    feed('iset_lines<cr>')
    793    feed('set_lines2<cr>')
    794    screen:expect([[
    795      % set_lines              |
    796      set_lines                |
    797      % set_lines2             |
    798      set_lines2               |
    799      % ^                       |
    800      {1:~                        }|*4
    801      {5:-- INSERT --}             |
    802    ]])
    803 
    804    feed('set_lines3(multi-1)<s-cr>set_lines3(multi-2)<cr>')
    805    screen:expect([[
    806      % set_lines              |
    807      set_lines                |
    808      % set_lines2             |
    809      set_lines2               |
    810      % set_lines3(multi-1)    |
    811      set_lines3(multi-2)      |
    812      set_lines3(multi-1)      |
    813      set_lines3(multi-2)      |
    814      % ^                       |
    815      {5:-- INSERT --}             |
    816    ]])
    817    -- with nvim_buf_set_text
    818    source('bwipeout!')
    819    api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
    820    exec_lua([[
    821      local buf = vim.api.nvim_get_current_buf()
    822      vim.fn.prompt_setcallback(buf, function(text)
    823        local lines = vim.split(text, '\n')
    824        if lines[#lines] ~= '' then
    825          table.insert(lines, '')
    826        end
    827        vim.api.nvim_buf_set_text(buf, -1, 0, -1, 0, lines)
    828      end)
    829    ]])
    830    feed('set_text<cr>')
    831    feed('set_text2<cr>')
    832    screen:expect([[
    833      % set_text               |
    834      set_text                 |
    835      % set_text2              |
    836      set_text2                |
    837      % ^                       |
    838      {1:~                        }|*4
    839      {5:-- INSERT --}             |
    840    ]])
    841 
    842    feed('set_text3(multi-1)<s-cr>set_text3(multi-2)<cr>')
    843    screen:expect([[
    844      % set_text               |
    845      set_text                 |
    846      % set_text2              |
    847      set_text2                |
    848      % set_text3(multi-1)     |
    849      set_text3(multi-2)       |
    850      set_text3(multi-1)       |
    851      set_text3(multi-2)       |
    852      % ^                       |
    853      {5:-- INSERT --}             |
    854    ]])
    855  end)
    856 
    857  it('works correctly with empty string as prompt', function()
    858    api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
    859    exec_lua(function()
    860      local buf = vim.api.nvim_get_current_buf()
    861      vim.fn.prompt_setprompt(buf, '')
    862    end)
    863 
    864    source('startinsert')
    865 
    866    -- mark correctly set
    867    eq({ 1, 0 }, api.nvim_buf_get_mark(0, ':'))
    868 
    869    feed('asdf')
    870    screen:expect([[
    871      asdf^                     |
    872      {1:~                        }|*8
    873      {5:-- INSERT --}             |
    874    ]])
    875 
    876    -- can clear all of it
    877    feed('<backspace><backspace><backspace><backspace>')
    878    screen:expect([[
    879      ^                         |
    880      {1:~                        }|*8
    881      {5:-- INSERT --}             |
    882    ]])
    883    feed('<cr>')
    884 
    885    eq({ 2, 0 }, api.nvim_buf_get_mark(0, ':'))
    886  end)
    887 
    888  it('prompt can be changed without interrupting user input', function()
    889    api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
    890    local buf = api.nvim_get_current_buf()
    891 
    892    local function set_prompt(prompt, b)
    893      fn('prompt_setprompt', b or buf, prompt)
    894    end
    895 
    896    set_prompt('> ')
    897 
    898    source('startinsert')
    899 
    900    feed('user input')
    901    -- Move the cursor a bit to check cursor maintaining position
    902    feed('<esc>hhi')
    903 
    904    screen:expect([[
    905      > user in^put             |
    906      {1:~                        }|*8
    907      {5:-- INSERT --}             |
    908    ]])
    909 
    910    eq({ 1, 2 }, api.nvim_buf_get_mark(0, ':'))
    911 
    912    set_prompt('new-prompt > ')
    913 
    914    screen:expect([[
    915      new-prompt > user in^put  |
    916      {1:~                        }|*8
    917      {5:-- INSERT --}             |
    918    ]])
    919 
    920    eq({ 1, 13 }, api.nvim_buf_get_mark(0, ':'))
    921 
    922    set_prompt('new-prompt(status) > ')
    923 
    924    screen:expect([[
    925      new-prompt(status) > user|
    926       in^put                   |
    927      {1:~                        }|*7
    928      {5:-- INSERT --}             |
    929    ]])
    930    eq({ 1, 21 }, api.nvim_buf_get_mark(0, ':'))
    931 
    932    set_prompt('new-prompt > ')
    933 
    934    screen:expect([[
    935      new-prompt > user in^put  |
    936      {1:~                        }|*8
    937      {5:-- INSERT --}             |
    938    ]])
    939    eq({ 1, 13 }, api.nvim_buf_get_mark(0, ':'))
    940 
    941    -- Cursor not moved when not on the prompt line.
    942    feed('<CR>user input<Esc>k')
    943    screen:expect([[
    944      new-prompt > user inpu^t  |
    945      new-prompt > user input  |
    946      {1:~                        }|*7
    947                               |
    948    ]])
    949    set_prompt('<>< ')
    950    screen:expect([[
    951      new-prompt > user inpu^t  |
    952      <>< user input           |
    953      {1:~                        }|*7
    954                               |
    955    ]])
    956    -- Correct col when prompt has multi-cell chars.
    957    feed('i<Left><Left>')
    958    screen:expect([[
    959      new-prompt > user input  |
    960      <>< user inp^ut           |
    961      {1:~                        }|*7
    962      {5:-- INSERT --}             |
    963    ]])
    964    set_prompt('\t > ')
    965    screen:expect([[
    966      new-prompt > user input  |
    967               > user inp^ut    |
    968      {1:~                        }|*7
    969      {5:-- INSERT --}             |
    970    ]])
    971    -- Works with 'virtualedit': coladd remains sensible. Cursor is redrawn correctly.
    972    -- Tab size visually changes due to multiples of 'tabstop'.
    973    command('set virtualedit=all')
    974    feed('<C-O>Sa<Tab>b<C-O>3h')
    975    screen:expect([[
    976      new-prompt > user input  |
    977               > a  ^  b        |
    978      {1:~                        }|*7
    979      {5:-- INSERT --}             |
    980    ]])
    981    set_prompt('😊 > ')
    982    screen:expect([[
    983      new-prompt > user input  |
    984      😊 > a ^ b                |
    985      {1:~                        }|*7
    986      {5:-- INSERT --}             |
    987    ]])
    988    -- Minimum col should be 1. Same event to avoid corrections from the state loop.
    989    feed('<Esc>0')
    990    local colnr = exec_lua(function()
    991      vim.fn.prompt_setprompt('', '')
    992      return vim.fn.col('.')
    993    end)
    994    eq(1, colnr)
    995    -- Correct cursor adjustment when old ': col and old prompt length differs.
    996    set_prompt('foo > ')
    997    fn('setpos', "':", { 0, fn('line', '.'), 10, 0 })
    998    fn('setline', '.', '   foo > hello')
    999    feed('fh')
   1000    screen:expect([[
   1001      new-prompt > user input  |
   1002         foo > ^hello           |
   1003      {1:~                        }|*7
   1004                               |
   1005    ]])
   1006    set_prompt('bar > ')
   1007    screen:expect([[
   1008      new-prompt > user input  |
   1009      bar > ^hello              |
   1010      {1:~                        }|*7
   1011                               |
   1012    ]])
   1013 
   1014    -- No crash when setting shorter prompt than curbuf's in other buffer.
   1015    feed('ztA')
   1016    command('set virtualedit& | new | setlocal buftype=prompt')
   1017    set_prompt('looooooooooooooooooooooooooooooooooooooooooooong > ', '') -- curbuf
   1018    set_prompt('foo > ')
   1019    screen:expect([[
   1020      loooooooooooooooooooooooo|
   1021      ooooooooooooooooooooong >|
   1022       ^                        |
   1023      {1:~                        }|
   1024      {3:[Prompt] [+]             }|
   1025      foo > hello              |
   1026      {1:~                        }|*3
   1027      {5:-- INSERT --}             |
   1028    ]])
   1029 
   1030    -- No prompt_setprompt crash from invalid ': col. Must happen in the same event.
   1031    exec_lua(function()
   1032      vim.cmd 'bwipeout!'
   1033      vim.api.nvim_buf_set_mark(0, ':', vim.fn.line('.'), 999, {})
   1034      vim.fn.prompt_setprompt('', 'new-prompt > ')
   1035    end)
   1036    screen:expect([[
   1037      new-prompt > ^            |
   1038      {1:~                        }|*8
   1039      {5:-- INSERT --}             |
   1040    ]])
   1041 
   1042    -- No leak if prompt_setprompt called for unloaded prompt buffer.
   1043    local unloaded_buf = fn('bufadd', '')
   1044    api.nvim_set_option_value('buftype', 'prompt', { buf = unloaded_buf })
   1045    fn('prompt_setprompt', unloaded_buf, 'hello unloaded! > ')
   1046    eq('hello unloaded! > ', fn('prompt_getprompt', unloaded_buf))
   1047  end)
   1048 end)