local M = {} local BUF = nil local WIN = nil M.tests = { { name = 'Basic short prompt (Path A - border title)', prompt = 'What is your name, brave adventurer?' }, { name = 'Short prompt with question mark (Path A)', prompt = 'Are you sure you want to rm -rf /?' }, { name = 'Han vs Greedo (Path A)', prompt = 'Who shot first? (Han/Greedo)' }, { name = 'Vim proficiency rating (Path A)', prompt = 'Rate your Vim proficiency from 0 to :wq' }, { name = 'Semicolons in Lua (Path A)', prompt = 'Describe your feelings about semicolons in one word:' }, { name = 'Richard Stallman wisdom (Path A)', prompt = 'What would Richard Stallman do?' }, { name = 'The meaning of life (Path A)', prompt = 'The answer to life, the universe, and everything is:' }, { name = 'Long warning prompt (Path B - virt_lines)', prompt = 'You are about to perform an irreversible operation on your carefully crafted dotfiles that you have spent months perfecting. Your Neovim config, your WezTerm theme, your niri layout — all of it could vanish in a puff of smoke. Are you absolutely, positively, cross-your-heart-and-hope-to-die sure you want to proceed?' }, { name = 'Commandment prompt (Path B)', prompt = 'Thou shalt not use nano. Thou shalt not use emacs. Thou shalt use Neovim exclusively, for it is written: "In the beginning was the command line, and the command line was good."' }, { name = 'Warranty scam prompt (Path B)', prompt = 'Congratulations! You have been selected as the lucky winner of a free compilation error! To claim your prize, simply enter your credit card number, social security number, mother maiden name, and the name of your first pet. Good luck!' }, { name = 'Terms and conditions with newlines (Path B)', prompt = 'TERMS AND CONDITIONS:\n1. You will not use nano.\n2. You will not use emacs.\n3. See points 1 and 2.\n\nDo you accept?' }, { name = 'Mission briefing with newlines (Path B)', prompt = 'Your mission, should you choose to accept it:\n\n- Infiltrate the VS Code headquarters\n- Replace all extensions with Neovim plugins\n- Escape undetected\n\nEnter your codename:' }, { name = 'Lua love letter with newlines (Path B)', prompt = 'We have been trying to reach you about your cars extended warranty.\n\nJUST KIDDING.\n\nActually, we need you to type I love Lua to continue. The interpreter is watching.' }, { name = 'Empty prompt (no title)', prompt = '' }, { name = 'Emoji spirit picker', prompt = 'Pick your spirit emoji (choose wisely):' }, { name = 'Unicode smorgasbord', prompt = 'Raksmorgas heisst nicht Scoop. Enter your favorite umlaut:' }, { name = 'Exactly 85% width', prompt = nil }, { name = 'One char over 85% width', prompt = nil }, { name = 'Tab-separated columns', prompt = 'Column A\tColumn B\tColumn C' }, { name = 'Input with default (VS Code trap)', prompt = 'Your favorite editor (hint: wrong answers only):', default = 'VS Code' }, { name = 'Input with file completion', prompt = 'Pick a file to accidentally delete:', completion = 'file' }, { name = 'Rapid fire: 3 sequential inputs', prompt = nil }, { name = 'Input then immediately cancel', prompt = 'Type something then press Esc:' }, { name = 'The ultimate question', prompt = 'You have reached the end of this manual test suite. As a reward, please type the answer to life, the universe, and everything:' }, } local function get_buf() if BUF and vim.api.nvim_buf_is_valid(BUF) then return BUF end BUF = vim.api.nvim_create_buf(true, true) vim.bo[BUF].filetype = 'markdown' return BUF end local function ensure_window() local buf = get_buf() if WIN and vim.api.nvim_win_is_valid(WIN) then vim.api.nvim_win_set_buf(WIN, buf) return WIN end WIN = vim.api.nvim_open_win(buf, true, { relative = 'editor', width = math.floor(vim.o.columns * 0.7), height = math.floor(vim.o.lines * 0.65), row = math.floor(vim.o.lines * 0.33), col = math.floor(vim.o.columns * 0.15), style = 'minimal', border = 'rounded', title = ' cmdline input() manual test ', title_pos = 'center', }) vim.wo[WIN].wrap = true vim.wo[WIN].cursorline = false vim.keymap.set('n', 'q', function() M.close() end, { buf = buf, nowait = true }) return WIN end local function append_content(lines) local buf = get_buf() vim.api.nvim_buf_set_lines(buf, -1, -1, false, lines) if WIN and vim.api.nvim_win_is_valid(WIN) then local line_count = vim.api.nvim_buf_line_count(buf) vim.api.nvim_win_set_cursor(WIN, { line_count, 0 }) end end function M.set_content(lines) local buf = get_buf() vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines) ensure_window() end function M.show_menu() local lines = { '', '=== CMDLINE INPUT() MANUAL TEST SUITE ===', '', 'Path A = border title | Path B = in-popup virt_lines', '', 'Short prompts (Path A):', } for i = 1, 7 do table.insert(lines, string.format(' [%2d] %s', i, M.tests[i].name)) end table.insert(lines, '') table.insert(lines, 'Long prompts (Path B):') for i = 8, 10 do table.insert(lines, string.format(' [%2d] %s', i, M.tests[i].name)) end table.insert(lines, '') table.insert(lines, 'Multi-line prompts (Path B):') for i = 11, 13 do table.insert(lines, string.format(' [%2d] %s', i, M.tests[i].name)) end table.insert(lines, '') table.insert(lines, 'Edge cases:') for i = 14, 19 do local name = M.tests[i].name if i == 17 or i == 18 then name = name .. ' (dynamic)' end table.insert(lines, string.format(' [%2d] %s', i, name)) end table.insert(lines, '') table.insert(lines, 'Interactive:') for i = 20, 24 do table.insert(lines, string.format(' [%2d] %s', i, M.tests[i].name)) end M.set_content(lines) end function M.run_test(idx) local test = M.tests[idx] if not test then append_content({ 'Invalid test number: ' .. tostring(idx) }) vim.defer_fn(M.input_loop, 10) return end local prompt = test.prompt if idx == 17 then local max_w = math.floor(vim.o.columns * 0.85) - 3 prompt = 'Exactly 85%: ' .. string.rep('x', math.max(0, max_w)) elseif idx == 18 then local max_w = math.floor(vim.o.columns * 0.85) - 3 prompt = 'One over 85%: ' .. string.rep('x', math.max(0, max_w + 1)) end append_content({ '', '--------------------------------------------------------------------------------', 'TEST: [' .. idx .. '] ' .. test.name, 'Prompt: ' .. vim.inspect(prompt), 'Display width: ' .. vim.fn.strdisplaywidth(prompt), '--------------------------------------------------------------------------------', }) if idx == 22 then vim.ui.input({ prompt = 'First: ', default = 'one' }, function(a) append_content({ 'You entered: ' .. (a or 'nil') }) vim.ui.input({ prompt = 'Second: ', default = 'two' }, function(b) append_content({ 'You entered: ' .. (b or 'nil') }) vim.ui.input({ prompt = 'Third: ', default = 'three' }, function(c) append_content({ 'You entered: ' .. (c or 'nil') }) append_content({ 'Rapid fire complete!' }) vim.defer_fn(M.input_loop, 10) end) end) end) return end local opts = { prompt = prompt } if test.default then opts.default = test.default end if test.completion then opts.completion = test.completion end vim.ui.input(opts, function(answer) append_content({ 'You entered: ' .. (answer or 'nil') }) vim.defer_fn(M.input_loop, 10) end) end function M.input_loop() if WIN and vim.api.nvim_win_is_valid(WIN) then vim.api.nvim_set_current_win(WIN) end vim.ui.input({ prompt = 'Enter test # (1-24) or q to quit: ' }, function(answer) if not answer or answer == 'q' or answer == 'Q' then append_content({ '', 'Done! Press q to close, or :TestAgain to run more tests.' }) return end local num = tonumber(answer) if num and num >= 1 and num <= #M.tests then M.run_test(num) else if answer ~= '' then append_content({ 'Invalid selection: ' .. answer }) end vim.defer_fn(M.input_loop, 10) end end) end function M.close() if WIN and vim.api.nvim_win_is_valid(WIN) then vim.api.nvim_win_close(WIN, true) WIN = nil end end vim.api.nvim_create_user_command('TestInputClose', function() M.close() end, {}) vim.api.nvim_create_user_command('TestAgain', function() vim.defer_fn(M.input_loop, 10) end, {}) vim.defer_fn(function() M.show_menu() vim.defer_fn(M.input_loop, 100) end, 0) return M