mirror of
https://github.com/neovim/neovim.git
synced 2026-05-24 22:00:10 +00:00
Problem: There is a lot of overlap between terminal and prompt buffer, but no easy way to limit the number of lines kept above the prompt to prevent performance and other issues. This is desirable for both example use cases in current documentation, chat UI and repl/shell plugins. Solution: Use existing 'scrollback' option to limit prompt-buffer lines as well. Signed-off-by: Tomas Slusny <slusnucky@gmail.com> Co-authored-by: zeertzjq <zeertzjq@outlook.com>
1155 lines
34 KiB
Lua
1155 lines
34 KiB
Lua
local t = require('test.testutil')
|
|
local n = require('test.functional.testnvim')()
|
|
local Screen = require('test.functional.ui.screen')
|
|
|
|
local feed = n.feed
|
|
local fn = n.call
|
|
local source = n.source
|
|
local clear = n.clear
|
|
local command = n.command
|
|
local expect = n.expect
|
|
local poke_eventloop = n.poke_eventloop
|
|
local api = n.api
|
|
local eq = t.eq
|
|
local pcall_err = t.pcall_err
|
|
local neq = t.neq
|
|
local exec_lua = n.exec_lua
|
|
|
|
describe('prompt buffer', function()
|
|
local screen
|
|
|
|
before_each(function()
|
|
clear()
|
|
screen = Screen.new(25, 10)
|
|
command('set laststatus=0 nohidden')
|
|
end)
|
|
|
|
local function source_script()
|
|
source([[
|
|
func TextEntered(text)
|
|
if a:text == "exit"
|
|
stopinsert
|
|
close
|
|
else
|
|
" Add the output above the current prompt.
|
|
call prompt_appendbuf(bufnr(''), split('Command: "' . a:text . '"', '\n'))
|
|
" Reset &modified to allow the buffer to be closed.
|
|
set nomodified
|
|
call timer_start(20, {id -> TimerFunc(a:text)})
|
|
endif
|
|
endfunc
|
|
|
|
func TimerFunc(text)
|
|
" Add the output above the current prompt.
|
|
call prompt_appendbuf(bufnr(''), split('Result: "' . a:text .'"', '\n'))
|
|
" Reset &modified to allow the buffer to be closed.
|
|
set nomodified
|
|
endfunc
|
|
|
|
func SwitchWindows()
|
|
call timer_start(0, {-> execute("wincmd p", "")})
|
|
endfunc
|
|
|
|
call setline(1, "other buffer")
|
|
set nomodified
|
|
new
|
|
set buftype=prompt
|
|
call prompt_setcallback(bufnr(''), function("TextEntered"))
|
|
eval bufnr("")->prompt_setprompt("cmd: ")
|
|
startinsert
|
|
]])
|
|
screen:expect([[
|
|
cmd: ^ |
|
|
{1:~ }|*3
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
end
|
|
|
|
-- oldtest: Test_prompt_basic()
|
|
it('works', function()
|
|
source_script()
|
|
feed('hello\n')
|
|
screen:expect([[
|
|
cmd: hello |
|
|
Command: "hello" |
|
|
Result: "hello" |
|
|
cmd: ^ |
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
feed('exit\n')
|
|
screen:expect([[
|
|
^other buffer |
|
|
{1:~ }|*8
|
|
|
|
|
]])
|
|
|
|
command('new')
|
|
command('set buftype=prompt')
|
|
feed('iabc<BS><BS>')
|
|
eq('a', fn('prompt_getinput', fn('bufnr')))
|
|
command('quit')
|
|
eq(1, #api.nvim_list_wins())
|
|
|
|
command('new')
|
|
command('set buftype=prompt modified')
|
|
eq(
|
|
'Vim(quit):E37: No write since last change (add ! to override)',
|
|
t.pcall_err(command, 'quit')
|
|
)
|
|
eq(2, #api.nvim_list_wins())
|
|
end)
|
|
|
|
-- oldtest: Test_prompt_editing()
|
|
it('editing', function()
|
|
source_script()
|
|
feed('hello<BS><BS>')
|
|
screen:expect([[
|
|
cmd: hel^ |
|
|
{1:~ }|*3
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
feed('<Left><Left><Left><BS>-')
|
|
screen:expect([[
|
|
cmd: -^hel |
|
|
{1:~ }|*3
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
feed('<C-O>lz')
|
|
screen:expect([[
|
|
cmd: -hz^el |
|
|
{1:~ }|*3
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
feed('<End>x')
|
|
screen:expect([[
|
|
cmd: -hzelx^ |
|
|
{1:~ }|*3
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
-- :edit doesn't apply on prompt buffer
|
|
eq('Vim(edit):cannot :edit a prompt buffer', t.pcall_err(api.nvim_command, 'edit'))
|
|
|
|
feed('<C-U>exit\n')
|
|
screen:expect([[
|
|
^other buffer |
|
|
{1:~ }|*8
|
|
|
|
|
]])
|
|
end)
|
|
|
|
-- oldtest: Test_prompt_switch_windows()
|
|
it('switch windows', function()
|
|
source_script()
|
|
feed('<C-O>:call SwitchWindows()<CR>')
|
|
screen:expect([[
|
|
cmd: |
|
|
{1:~ }|*3
|
|
{2:[Prompt] }|
|
|
^other buffer |
|
|
{1:~ }|*3
|
|
|
|
|
]])
|
|
feed('<C-O>:call SwitchWindows()<CR>')
|
|
screen:expect([[
|
|
cmd: ^ |
|
|
{1:~ }|*3
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
feed('<Esc>')
|
|
screen:expect([[
|
|
cmd:^ |
|
|
{1:~ }|*3
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
|
|
|
]])
|
|
end)
|
|
|
|
-- oldtest: Test_prompt_while_writing_to_hidden_buffer()
|
|
it('keeps insert mode after aucmd_restbuf in callback', function()
|
|
source_script()
|
|
source [[
|
|
let s:buf = nvim_create_buf(1, 1)
|
|
call timer_start(0, {-> nvim_buf_set_lines(s:buf, -1, -1, 0, ['walrus'])})
|
|
]]
|
|
poke_eventloop()
|
|
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
|
|
end)
|
|
|
|
-- oldtest: Test_prompt_appending_while_hidden()
|
|
it('accessing hidden prompt buffer does not start insert mode', function()
|
|
local prev_win = api.nvim_get_current_win()
|
|
source([[
|
|
new prompt
|
|
set buftype=prompt
|
|
set bufhidden=hide
|
|
|
|
func s:TextEntered(text)
|
|
if a:text == 'exit'
|
|
close
|
|
endif
|
|
let g:entered = a:text
|
|
endfunc
|
|
call prompt_setcallback(bufnr(), function('s:TextEntered'))
|
|
|
|
func DoAppend(cmd_before = '')
|
|
exe a:cmd_before
|
|
call appendbufline('prompt', '$', 'Test')
|
|
return ''
|
|
endfunc
|
|
|
|
autocmd User SwitchTabPages tabprevious | tabnext
|
|
func DoAutoAll(cmd_before = '')
|
|
exe a:cmd_before
|
|
doautoall User SwitchTabPages
|
|
return ''
|
|
endfunc
|
|
]])
|
|
feed('asomething<CR>')
|
|
eq('something', api.nvim_get_var('entered'))
|
|
neq(prev_win, api.nvim_get_current_win())
|
|
feed('exit<CR>')
|
|
eq(prev_win, api.nvim_get_current_win())
|
|
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
|
|
command('call DoAppend()')
|
|
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
|
|
feed('i')
|
|
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
|
|
command('call DoAppend()')
|
|
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
|
|
command("call DoAppend('stopinsert')")
|
|
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
|
|
command("call DoAppend('startreplace')")
|
|
eq({ mode = 'R', blocking = false }, api.nvim_get_mode())
|
|
feed('<Esc>')
|
|
command('tabnew')
|
|
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
|
|
command("call DoAutoAll('startinsert')")
|
|
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
|
|
command("call DoAutoAll('stopinsert')")
|
|
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
|
|
end)
|
|
|
|
-- oldtest: Test_prompt_leave_modify_hidden()
|
|
it('modifying hidden buffer does not prevent prompt buffer mode change', function()
|
|
source([[
|
|
file hidden
|
|
set bufhidden=hide
|
|
enew
|
|
new prompt
|
|
set buftype=prompt
|
|
|
|
inoremap <buffer> w <Cmd>wincmd w<CR>
|
|
inoremap <buffer> q <Cmd>bwipe!<CR>
|
|
autocmd BufLeave prompt call appendbufline('hidden', '$', 'Leave')
|
|
autocmd BufEnter prompt call appendbufline('hidden', '$', 'Enter')
|
|
autocmd BufWinLeave prompt call appendbufline('hidden', '$', 'Close')
|
|
]])
|
|
feed('a')
|
|
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
|
|
feed('w')
|
|
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
|
|
feed('<C-W>w')
|
|
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
|
|
feed('q')
|
|
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
|
|
command('bwipe!')
|
|
expect([[
|
|
|
|
Leave
|
|
Enter
|
|
Leave
|
|
Close]])
|
|
end)
|
|
|
|
it('can insert multiline text', function()
|
|
source_script()
|
|
local buf = api.nvim_get_current_buf()
|
|
|
|
feed('line 1<s-cr>line 2<s-cr>line 3')
|
|
screen:expect([[
|
|
cmd: line 1 |
|
|
line 2 |
|
|
line 3^ |
|
|
{1:~ }|
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
-- prompt_getinput works with multiline input
|
|
eq('line 1\nline 2\nline 3', fn('prompt_getinput', buf))
|
|
|
|
feed('<cr>')
|
|
-- submitting multiline text works
|
|
screen:expect([[
|
|
Result: "line 1 |
|
|
line 2 |
|
|
line 3" |
|
|
cmd: ^ |
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
eq('', fn('prompt_getinput', buf))
|
|
|
|
-- % prompt is not repeated with formatoptions+=r
|
|
source([[
|
|
bwipeout!
|
|
set formatoptions+=r
|
|
call prompt_setprompt(bufnr(), "% ")
|
|
set buftype=prompt
|
|
]])
|
|
feed('iline1<s-cr>line2')
|
|
screen:expect([[
|
|
other buffer |
|
|
% line1 |
|
|
line2^ |
|
|
{1:~ }|*6
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
-- ensure cursor gets placed on first line of user input.
|
|
-- when insert mode is entered from read-only region of prompt buffer.
|
|
local prompt_pos = api.nvim_buf_get_mark(0, ':')
|
|
feed('<esc>')
|
|
-- works before prompt
|
|
api.nvim_win_set_cursor(0, { prompt_pos[1] - 1, 0 })
|
|
screen:expect([[
|
|
^other buffer |
|
|
% line1 |
|
|
line2 |
|
|
{1:~ }|*6
|
|
|
|
|
]])
|
|
feed('a')
|
|
feed('<esc>')
|
|
screen:expect([[
|
|
other buffer |
|
|
%^ line1 |
|
|
line2 |
|
|
{1:~ }|*6
|
|
|
|
|
]])
|
|
-- works on prompt
|
|
api.nvim_win_set_cursor(0, { prompt_pos[1], 0 })
|
|
screen:expect([[
|
|
other buffer |
|
|
^% line1 |
|
|
line2 |
|
|
{1:~ }|*6
|
|
|
|
|
]])
|
|
feed('a')
|
|
feed('<esc>')
|
|
screen:expect([[
|
|
other buffer |
|
|
%^ line1 |
|
|
line2 |
|
|
{1:~ }|*6
|
|
|
|
|
]])
|
|
|
|
-- i_<Left> i_<C-Left> i_<Home> i_<End> keys on prompt-line doesn't put cursor
|
|
-- at end of text
|
|
feed('a<Left><C-Left>')
|
|
screen:expect([[
|
|
other buffer |
|
|
% ^line1 |
|
|
line2 |
|
|
{1:~ }|*6
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
feed('<End>')
|
|
screen:expect([[
|
|
other buffer |
|
|
% line1^ |
|
|
line2 |
|
|
{1:~ }|*6
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
feed('<Home>')
|
|
screen:expect([[
|
|
other buffer |
|
|
% ^line1 |
|
|
line2 |
|
|
{1:~ }|*6
|
|
{5:-- INSERT --} |
|
|
]])
|
|
end)
|
|
|
|
it('can put (p) multiline text', function()
|
|
source_script()
|
|
local buf = api.nvim_get_current_buf()
|
|
|
|
fn('setreg', 'a', 'line 1\nline 2\nline 3')
|
|
feed('<esc>"ap')
|
|
screen:expect([[
|
|
cmd: ^line 1 |
|
|
line 2 |
|
|
line 3 |
|
|
{1:~ }|
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
|
|
|
]])
|
|
|
|
-- prompt_getinput works with pasted input
|
|
eq('line 1\nline 2\nline 3', fn('prompt_getinput', buf))
|
|
|
|
feed('i<cr>')
|
|
screen:expect([[
|
|
Result: "line 1 |
|
|
line 2 |
|
|
line 3" |
|
|
cmd: ^ |
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
end)
|
|
|
|
it('can put multiline text with nvim_paste', function()
|
|
source_script()
|
|
api.nvim_paste('line 1\nline 2\nline 3', false, -1)
|
|
screen:expect([[
|
|
cmd: line 1 |
|
|
line 2 |
|
|
line 3^ |
|
|
{1:~ }|
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
end)
|
|
|
|
it('can undo current prompt', function()
|
|
source_script()
|
|
local buf = api.nvim_get_current_buf()
|
|
|
|
-- text editing allowed in current prompt
|
|
feed('tests-initial<esc>')
|
|
feed('bimiddle-<esc>')
|
|
screen:expect([[
|
|
cmd: tests-middle^-initial|
|
|
{1:~ }|*3
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
|
|
|
]])
|
|
|
|
feed('Fdx')
|
|
screen:expect([[
|
|
cmd: tests-mid^le-initial |
|
|
{1:~ }|*3
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
|
|
|
]])
|
|
|
|
-- can undo edits until prompt has been submitted
|
|
feed('u')
|
|
screen:expect([[
|
|
cmd: tests-mid^dle-initial|
|
|
{1:~ }|*3
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
1 change; {MATCH:.*} |
|
|
]])
|
|
|
|
-- undo is reflected in prompt_getinput
|
|
eq('tests-middle-initial', fn('prompt_getinput', buf))
|
|
|
|
feed('u')
|
|
screen:expect([[
|
|
cmd: tests-^initial |
|
|
{1:~ }|*3
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
1 change; {MATCH:.*} |
|
|
]])
|
|
|
|
feed('i<cr><esc>')
|
|
screen:expect([[
|
|
cmd: tests-initial |
|
|
Command: "tests-initial" |
|
|
Result: "tests-initial" |
|
|
cmd:^ |
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
|
|
|
]])
|
|
|
|
-- after submit undo does nothing
|
|
feed('u')
|
|
screen:expect([[
|
|
cmd: tests-initial |
|
|
Command: "tests-initial" |
|
|
cmd:^ |
|
|
{1:~ }|
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
1 line {MATCH:.*} |
|
|
]])
|
|
|
|
-- "S" does not clear undo
|
|
feed('ihello<Esc>S')
|
|
screen:expect([[
|
|
cmd: tests-initial |
|
|
Command: "tests-initial" |
|
|
cmd: ^ |
|
|
{1:~ }|
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
feed('<Esc>u')
|
|
screen:expect([[
|
|
cmd: tests-initial |
|
|
Command: "tests-initial" |
|
|
^cmd: hello |
|
|
{1:~ }|
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
1 change; {MATCH:.*} |
|
|
]])
|
|
|
|
-- undo cleared if prompt changes
|
|
-- (otherwise undoing would abort it and append a new prompt, which isn't useful)
|
|
fn('prompt_setprompt', '', 'cmd > ')
|
|
feed('u')
|
|
screen:expect([[
|
|
cmd: tests-initial |
|
|
Command: "tests-initial" |
|
|
c^md > hello |
|
|
{1:~ }|
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
Already at oldest change |
|
|
]])
|
|
|
|
-- new prompt line appended to fix missing prompt also clears undo
|
|
feed('A there')
|
|
fn('setpos', "':", { 0, fn('line', '.'), 99, 0 })
|
|
feed('<Esc>u')
|
|
screen:expect([[
|
|
cmd: tests-initial |
|
|
Command: "tests-initial" |
|
|
cmd > hello there |
|
|
cmd >^ |
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
Already at oldest change |
|
|
]])
|
|
end)
|
|
|
|
it('o/O can create new lines', function()
|
|
source_script()
|
|
local buf = api.nvim_get_current_buf()
|
|
|
|
feed('line 1<s-cr>line 2<s-cr>line 3')
|
|
screen:expect([[
|
|
cmd: line 1 |
|
|
line 2 |
|
|
line 3^ |
|
|
{1:~ }|
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
feed('<esc>koafter')
|
|
screen:expect([[
|
|
cmd: line 1 |
|
|
line 2 |
|
|
after^ |
|
|
line 3 |
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
-- newline created with o is reflected in prompt_getinput
|
|
eq('line 1\nline 2\nafter\nline 3', fn('prompt_getinput', buf))
|
|
|
|
feed('<esc>kObefore')
|
|
|
|
screen:expect([[
|
|
cmd: line 1 |
|
|
before^ |
|
|
line 2 |
|
|
after |
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
-- newline created with O is reflected in prompt_getinput
|
|
eq('line 1\nbefore\nline 2\nafter\nline 3', fn('prompt_getinput', buf))
|
|
|
|
feed('<cr>')
|
|
vim.uv.sleep(20)
|
|
eq('', fn('prompt_getinput', buf))
|
|
screen:expect([[
|
|
line 2 |
|
|
after |
|
|
line 3" |
|
|
cmd: ^ |
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
feed('line 4<s-cr>line 5')
|
|
screen:expect([[
|
|
after |
|
|
line 3" |
|
|
cmd: line 4 |
|
|
line 5^ |
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
feed('<esc>k0oafter prompt')
|
|
screen:expect([[
|
|
after |
|
|
line 3" |
|
|
cmd: line 4 |
|
|
after prompt^ |
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
feed('<esc>k0Oat prompt')
|
|
screen:expect([[
|
|
after |
|
|
line 3" |
|
|
cmd: at prompt^ |
|
|
line 4 |
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
feed('<cr>')
|
|
vim.uv.sleep(20)
|
|
eq('', fn('prompt_getinput', buf))
|
|
screen:expect([[
|
|
line 4 |
|
|
after prompt |
|
|
line 5" |
|
|
cmd: ^ |
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
end)
|
|
|
|
it('deleting prompt adds it back on insert', function()
|
|
source_script()
|
|
feed('asdf')
|
|
screen:expect([[
|
|
cmd: asdf^ |
|
|
{1:~ }|*3
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
feed('<esc>ddi')
|
|
screen:expect([[
|
|
cmd: ^ |
|
|
{1:~ }|*3
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
feed('asdf')
|
|
screen:expect([[
|
|
cmd: asdf^ |
|
|
{1:~ }|*3
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
feed('<esc>cc')
|
|
screen:expect([[
|
|
cmd: ^ |
|
|
{1:~ }|*3
|
|
{3:[Prompt] }|
|
|
other buffer |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
end)
|
|
|
|
it("sets the ': mark", function()
|
|
api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
|
|
exec_lua(function()
|
|
local buf = vim.api.nvim_get_current_buf()
|
|
vim.fn.prompt_setprompt(buf, 'cmd > ')
|
|
vim.fn.prompt_setcallback(buf, function(str)
|
|
local last_line = vim.api.nvim_buf_line_count(buf)
|
|
vim.api.nvim_buf_set_lines(buf, last_line - 1, last_line - 1, true, vim.split(str, '\n'))
|
|
end)
|
|
end)
|
|
|
|
feed('asdf')
|
|
eq({ 1, 6 }, api.nvim_buf_get_mark(0, ':'))
|
|
feed('<cr>')
|
|
eq({ 3, 6 }, api.nvim_buf_get_mark(0, ':'))
|
|
-- Multiline prompt.
|
|
feed('<s-cr>line1<s-cr>line2<s-cr>line3<cr>')
|
|
eq({ 11, 6 }, api.nvim_buf_get_mark(0, ':'))
|
|
|
|
-- ': mark is only available in prompt buffer.
|
|
api.nvim_set_option_value('buftype', '', { buf = 0 })
|
|
eq("Invalid mark name: ':'", t.pcall_err(api.nvim_buf_get_mark, 0, ':'))
|
|
|
|
-- mark can be moved
|
|
api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
|
|
local last_line = api.nvim_buf_line_count(0)
|
|
eq({ last_line, 6 }, api.nvim_buf_get_mark(0, ':'))
|
|
eq(true, api.nvim_buf_set_mark(0, ':', 1, 5, {}))
|
|
eq({ 1, 5 }, api.nvim_buf_get_mark(0, ':'))
|
|
|
|
-- No crash from invalid col.
|
|
eq(true, api.nvim_buf_set_mark(0, ':', fn('line', '.'), 999, {}))
|
|
eq({ 12, 6 }, api.nvim_buf_get_mark(0, ':'))
|
|
|
|
-- Clamps lnum to at least 1. Do in one event to repro the leak.
|
|
exec_lua(function()
|
|
vim.fn.setpos("':", { 0, 0, 0, 0 })
|
|
vim.fn.prompt_setprompt('', 'bar > ')
|
|
end)
|
|
eq({ 1, 6 }, api.nvim_buf_get_mark(0, ':'))
|
|
|
|
-- No ml_get error from invalid lnum.
|
|
command('set messagesopt+=wait:0 messagesopt-=hit-enter')
|
|
fn('setpos', "':", { 0, 999, 7, 0 })
|
|
eq('', api.nvim_get_vvar('errmsg'))
|
|
command('set messagesopt&')
|
|
eq({ 13, 6 }, api.nvim_buf_get_mark(0, ':'))
|
|
end)
|
|
|
|
describe('prompt_getinput', function()
|
|
it('returns current prompts text', function()
|
|
command('new')
|
|
local bufnr = fn('bufnr')
|
|
api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
|
|
eq('', fn('prompt_getinput', bufnr))
|
|
feed('iasdf')
|
|
eq('asdf', fn('prompt_getinput', bufnr))
|
|
feed('<esc>dd')
|
|
eq('', fn('prompt_getinput', bufnr))
|
|
feed('iasdf2')
|
|
eq('asdf2', fn('prompt_getinput', bufnr))
|
|
|
|
-- returns empty string when called from non prompt buffer
|
|
api.nvim_set_option_value('buftype', '', { buf = 0 })
|
|
eq('', fn('prompt_getinput', bufnr))
|
|
end)
|
|
end)
|
|
|
|
it('programmatic (non-user) edits', function()
|
|
api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
|
|
|
|
-- with nvim_buf_set_lines
|
|
exec_lua([[
|
|
local buf = vim.api.nvim_get_current_buf()
|
|
vim.fn.prompt_setcallback(buf, function(text)
|
|
vim.api.nvim_buf_set_lines(buf, -2, -2, true, vim.split(text, '\n'))
|
|
end)
|
|
]])
|
|
feed('iset_lines<cr>')
|
|
feed('set_lines2<cr>')
|
|
screen:expect([[
|
|
% set_lines |
|
|
set_lines |
|
|
% set_lines2 |
|
|
set_lines2 |
|
|
% ^ |
|
|
{1:~ }|*4
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
feed('set_lines3(multi-1)<s-cr>set_lines3(multi-2)<cr>')
|
|
screen:expect([[
|
|
% set_lines |
|
|
set_lines |
|
|
% set_lines2 |
|
|
set_lines2 |
|
|
% set_lines3(multi-1) |
|
|
set_lines3(multi-2) |
|
|
set_lines3(multi-1) |
|
|
set_lines3(multi-2) |
|
|
% ^ |
|
|
{5:-- INSERT --} |
|
|
]])
|
|
-- with nvim_buf_set_text
|
|
source('bwipeout!')
|
|
api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
|
|
exec_lua([[
|
|
local buf = vim.api.nvim_get_current_buf()
|
|
vim.fn.prompt_setcallback(buf, function(text)
|
|
local lines = vim.split(text, '\n')
|
|
if lines[#lines] ~= '' then
|
|
table.insert(lines, '')
|
|
end
|
|
vim.api.nvim_buf_set_text(buf, -1, 0, -1, 0, lines)
|
|
end)
|
|
]])
|
|
feed('set_text<cr>')
|
|
feed('set_text2<cr>')
|
|
screen:expect([[
|
|
% set_text |
|
|
set_text |
|
|
% set_text2 |
|
|
set_text2 |
|
|
% ^ |
|
|
{1:~ }|*4
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
feed('set_text3(multi-1)<s-cr>set_text3(multi-2)<cr>')
|
|
screen:expect([[
|
|
% set_text |
|
|
set_text |
|
|
% set_text2 |
|
|
set_text2 |
|
|
% set_text3(multi-1) |
|
|
set_text3(multi-2) |
|
|
set_text3(multi-1) |
|
|
set_text3(multi-2) |
|
|
% ^ |
|
|
{5:-- INSERT --} |
|
|
]])
|
|
end)
|
|
|
|
it('works correctly with empty string as prompt', function()
|
|
api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
|
|
exec_lua(function()
|
|
local buf = vim.api.nvim_get_current_buf()
|
|
vim.fn.prompt_setprompt(buf, '')
|
|
end)
|
|
|
|
source('startinsert')
|
|
|
|
-- mark correctly set
|
|
eq({ 1, 0 }, api.nvim_buf_get_mark(0, ':'))
|
|
|
|
feed('asdf')
|
|
screen:expect([[
|
|
asdf^ |
|
|
{1:~ }|*8
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
-- can clear all of it
|
|
feed('<backspace><backspace><backspace><backspace>')
|
|
screen:expect([[
|
|
^ |
|
|
{1:~ }|*8
|
|
{5:-- INSERT --} |
|
|
]])
|
|
feed('<cr>')
|
|
|
|
eq({ 2, 0 }, api.nvim_buf_get_mark(0, ':'))
|
|
end)
|
|
|
|
it('prompt can be changed without interrupting user input', function()
|
|
api.nvim_set_option_value('buftype', 'prompt', { buf = 0 })
|
|
local buf = api.nvim_get_current_buf()
|
|
|
|
local function set_prompt(prompt, b)
|
|
fn('prompt_setprompt', b or buf, prompt)
|
|
end
|
|
|
|
set_prompt('> ')
|
|
|
|
source('startinsert')
|
|
|
|
feed('user input')
|
|
-- Move the cursor a bit to check cursor maintaining position
|
|
feed('<esc>hhi')
|
|
|
|
screen:expect([[
|
|
> user in^put |
|
|
{1:~ }|*8
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
eq({ 1, 2 }, api.nvim_buf_get_mark(0, ':'))
|
|
|
|
set_prompt('new-prompt > ')
|
|
|
|
screen:expect([[
|
|
new-prompt > user in^put |
|
|
{1:~ }|*8
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
eq({ 1, 13 }, api.nvim_buf_get_mark(0, ':'))
|
|
|
|
set_prompt('new-prompt(status) > ')
|
|
|
|
screen:expect([[
|
|
new-prompt(status) > user|
|
|
in^put |
|
|
{1:~ }|*7
|
|
{5:-- INSERT --} |
|
|
]])
|
|
eq({ 1, 21 }, api.nvim_buf_get_mark(0, ':'))
|
|
|
|
set_prompt('new-prompt > ')
|
|
|
|
screen:expect([[
|
|
new-prompt > user in^put |
|
|
{1:~ }|*8
|
|
{5:-- INSERT --} |
|
|
]])
|
|
eq({ 1, 13 }, api.nvim_buf_get_mark(0, ':'))
|
|
|
|
-- Cursor not moved when not on the prompt line.
|
|
feed('<CR>user input<Esc>k')
|
|
screen:expect([[
|
|
new-prompt > user inpu^t |
|
|
new-prompt > user input |
|
|
{1:~ }|*7
|
|
|
|
|
]])
|
|
set_prompt('<>< ')
|
|
screen:expect([[
|
|
new-prompt > user inpu^t |
|
|
<>< user input |
|
|
{1:~ }|*7
|
|
|
|
|
]])
|
|
-- Correct col when prompt has multi-cell chars.
|
|
feed('i<Left><Left>')
|
|
screen:expect([[
|
|
new-prompt > user input |
|
|
<>< user inp^ut |
|
|
{1:~ }|*7
|
|
{5:-- INSERT --} |
|
|
]])
|
|
set_prompt('\t > ')
|
|
screen:expect([[
|
|
new-prompt > user input |
|
|
> user inp^ut |
|
|
{1:~ }|*7
|
|
{5:-- INSERT --} |
|
|
]])
|
|
-- Works with 'virtualedit': coladd remains sensible. Cursor is redrawn correctly.
|
|
-- Tab size visually changes due to multiples of 'tabstop'.
|
|
command('set virtualedit=all')
|
|
feed('<C-O>Sa<Tab>b<C-O>3h')
|
|
screen:expect([[
|
|
new-prompt > user input |
|
|
> a ^ b |
|
|
{1:~ }|*7
|
|
{5:-- INSERT --} |
|
|
]])
|
|
set_prompt('😊 > ')
|
|
screen:expect([[
|
|
new-prompt > user input |
|
|
😊 > a ^ b |
|
|
{1:~ }|*7
|
|
{5:-- INSERT --} |
|
|
]])
|
|
-- Minimum col should be 1. Same event to avoid corrections from the state loop.
|
|
feed('<Esc>0')
|
|
local colnr = exec_lua(function()
|
|
vim.fn.prompt_setprompt('', '')
|
|
return vim.fn.col('.')
|
|
end)
|
|
eq(1, colnr)
|
|
-- Correct cursor adjustment when old ': col and old prompt length differs.
|
|
set_prompt('foo > ')
|
|
fn('setpos', "':", { 0, fn('line', '.'), 10, 0 })
|
|
fn('setline', '.', ' foo > hello')
|
|
feed('fh')
|
|
screen:expect([[
|
|
new-prompt > user input |
|
|
foo > ^hello |
|
|
{1:~ }|*7
|
|
|
|
|
]])
|
|
set_prompt('bar > ')
|
|
screen:expect([[
|
|
new-prompt > user input |
|
|
bar > ^hello |
|
|
{1:~ }|*7
|
|
|
|
|
]])
|
|
|
|
-- No crash when setting shorter prompt than curbuf's in other buffer.
|
|
feed('ztA')
|
|
command('set virtualedit& | new | setlocal buftype=prompt')
|
|
set_prompt('looooooooooooooooooooooooooooooooooooooooooooong > ', '') -- curbuf
|
|
set_prompt('foo > ')
|
|
screen:expect([[
|
|
loooooooooooooooooooooooo|
|
|
ooooooooooooooooooooong >|
|
|
^ |
|
|
{1:~ }|
|
|
{3:[Prompt] }|
|
|
foo > hello |
|
|
{1:~ }|*3
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
-- No prompt_setprompt crash from invalid ': col. Must happen in the same event.
|
|
exec_lua(function()
|
|
vim.cmd 'bwipeout!'
|
|
vim.api.nvim_buf_set_mark(0, ':', vim.fn.line('.'), 999, {})
|
|
vim.fn.prompt_setprompt('', 'new-prompt > ')
|
|
end)
|
|
screen:expect([[
|
|
new-prompt > ^ |
|
|
{1:~ }|*8
|
|
{5:-- INSERT --} |
|
|
]])
|
|
|
|
-- No leak if prompt_setprompt called for unloaded prompt buffer.
|
|
local unloaded_buf = fn('bufadd', '')
|
|
api.nvim_set_option_value('buftype', 'prompt', { buf = unloaded_buf })
|
|
fn('prompt_setprompt', unloaded_buf, 'hello unloaded! > ')
|
|
eq('hello unloaded! > ', fn('prompt_getprompt', unloaded_buf))
|
|
end)
|
|
|
|
it('prompt_appendbuf with multi-element list and singleline prompt', function()
|
|
command('new')
|
|
local buf = api.nvim_get_current_buf()
|
|
api.nvim_set_option_value('buftype', 'prompt', { buf = buf })
|
|
fn('prompt_setprompt', buf, 'cmd: ')
|
|
|
|
-- Single element list: appends before prompt
|
|
fn('prompt_appendbuf', buf, { 'line1' })
|
|
eq({ 'line1', 'cmd: ' }, api.nvim_buf_get_lines(buf, 0, -1, false))
|
|
|
|
-- Multi-element list: first element appended, rest inserted as new lines before prompt
|
|
fn('prompt_appendbuf', buf, { '-append', 'line2' })
|
|
eq({ 'line1-append', 'line2', 'cmd: ' }, api.nvim_buf_get_lines(buf, 0, -1, false))
|
|
|
|
-- Multi-element list after multi-element list
|
|
fn('prompt_appendbuf', buf, { '', 'line3', 'line4', 'line5', 'line6' })
|
|
eq(
|
|
{ 'line1-append', 'line2', 'line3', 'line4', 'line5', 'line6', 'cmd: ' },
|
|
api.nvim_buf_get_lines(buf, 0, -1, false)
|
|
)
|
|
end)
|
|
|
|
it('prompt_appendbuf with multi-element list and multiline prompt', function()
|
|
command('new')
|
|
local buf = api.nvim_get_current_buf()
|
|
api.nvim_set_option_value('buftype', 'prompt', { buf = buf })
|
|
fn('prompt_setprompt', buf, 'cmd: ')
|
|
source('startinsert')
|
|
|
|
-- User types multiline input in the prompt
|
|
feed('input1<s-cr>input2')
|
|
eq({ 'cmd: input1', 'input2' }, api.nvim_buf_get_lines(buf, 0, -1, false))
|
|
|
|
-- Single element list: appends before prompt
|
|
fn('prompt_appendbuf', buf, { 'line1' })
|
|
eq({ 'line1', 'cmd: input1', 'input2' }, api.nvim_buf_get_lines(buf, 0, -1, false))
|
|
|
|
-- Multi-element list: first element appended, rest inserted as new lines before prompt
|
|
fn('prompt_appendbuf', buf, { '-append', 'line2' })
|
|
eq(
|
|
{ 'line1-append', 'line2', 'cmd: input1', 'input2' },
|
|
api.nvim_buf_get_lines(buf, 0, -1, false)
|
|
)
|
|
|
|
-- Multi-element list after multi-element list
|
|
fn('prompt_appendbuf', buf, { '', 'line3', 'line4', 'line5', 'line6' })
|
|
eq(
|
|
{ 'line1-append', 'line2', 'line3', 'line4', 'line5', 'line6', 'cmd: input1', 'input2' },
|
|
api.nvim_buf_get_lines(buf, 0, -1, false)
|
|
)
|
|
end)
|
|
|
|
it("respects 'scrollback'", function()
|
|
exec_lua(function()
|
|
local buf = vim.api.nvim_create_buf(false, true)
|
|
vim.api.nvim_set_option_value('buftype', 'prompt', { buf = buf })
|
|
vim.api.nvim_set_option_value('scrollback', 3, { buf = buf })
|
|
vim.fn.prompt_setcallback(buf, function(_)
|
|
vim.fn.prompt_setprompt(buf, '> ')
|
|
end)
|
|
vim.fn.prompt_setprompt(buf, '> ')
|
|
vim.api.nvim_set_current_buf(buf)
|
|
vim.cmd('startinsert')
|
|
end)
|
|
local buf = fn('bufnr')
|
|
|
|
-- Submit 5 times, each creates a new prompt line as history
|
|
feed('a<cr>')
|
|
feed('b<cr>')
|
|
feed('c<cr>')
|
|
feed('d<cr>')
|
|
feed('e<cr>')
|
|
|
|
-- Should only keep 3 lines above the prompt (scrollback limit = 3)
|
|
local lines = api.nvim_buf_get_lines(buf, 0, -1, false)
|
|
eq({ '> c', '> d', '> e', '> ' }, lines)
|
|
end)
|
|
end)
|