describe('render.show', function() before_each(function() package.loaded['bugabinga.cmdline.state'] = nil package.loaded['bugabinga.cmdline.format'] = nil package.loaded['bugabinga.cmdline.position'] = nil package.loaded['bugabinga.cmdline.cursor'] = nil package.loaded['bugabinga.cmdline.render'] = nil end) after_each(function() local ok, render = pcall(require, 'bugabinga.cmdline.render') if ok then render.hide() end local ok2, pos = pcall(require, 'bugabinga.cmdline.position') if ok2 then pos.set_cmdline_anchor(nil) end end) it('creates a floating window', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state = state.new({ { {}, ':hello' }, }, 1, ':', '', 0, 1) render.show(cmdline_state) local pos = require('bugabinga.cmdline.position') local anchor = assert(pos.get_cmdline_anchor()) assert.is_not_nil(anchor) assert.is_not_nil(anchor.win) render.hide() end) it('sets buffer text without icon prefix (icon via extmark)', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state = state.new({ { {}, ':test' }, }, 1, ':', '', 0, 1) render.show(cmdline_state) local pos = require('bugabinga.cmdline.position') local anchor = assert(pos.get_cmdline_anchor()) local buf = anchor.buf local lines = vim.api.nvim_buf_get_lines(buf, 0, 1, false) assert.is_true(#lines > 0) assert.is_true(lines[1]:find('󰘳') == nil) assert.is_true(lines[1]:find(':test') ~= nil) local ns = vim.api.nvim_create_namespace 'bugabinga.cmdline' local extmarks = vim.api.nvim_buf_get_extmarks(buf, ns, 0, -1, { details = true }) assert.is_true(#extmarks > 0) render.hide() end) it('has leading and trailing space for icon and cursor positioning', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state = state.new({ { {}, ':vim' }, }, 4, ':', '', 0, 1) render.show(cmdline_state) local pos = require('bugabinga.cmdline.position') local anchor = assert(pos.get_cmdline_anchor()) local buf = anchor.buf local lines = vim.api.nvim_buf_get_lines(buf, 0, 1, false) assert.equal(' :vim ', lines[1]) local cursor_byte = cmdline_state:cursor_byte() local expected_col = 1 + cursor_byte assert.equal(1 + 4, expected_col) assert.equal(7, #lines[1]) render.hide() end) it('hides safely when no window exists', function() local render = require 'bugabinga.cmdline.render' local ok = pcall(function() render.hide() render.hide() end) assert.is_true(ok) end) it('updates winhighlight when format changes between show calls', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state1 = state.new({ { {}, '' }, }, 1, ':', '', 0, 1) render.show(cmdline_state1) local pos = require('bugabinga.cmdline.position') local anchor1 = assert(pos.get_cmdline_anchor()) local win1 = anchor1.win local wh1 = vim.wo[win1].winhighlight local cmdline_state2 = state.new({ { {}, 'h' }, }, 2, ':', '', 0, 1) render.show(cmdline_state2) local anchor2 = assert(pos.get_cmdline_anchor()) local win2 = anchor2.win local wh2 = vim.wo[win2].winhighlight assert(wh1 ~= wh2, 'winhighlight should differ between cmdline and help formats') render.hide() end) end) describe('render.hide', function() before_each(function() package.loaded['bugabinga.cmdline.state'] = nil package.loaded['bugabinga.cmdline.format'] = nil package.loaded['bugabinga.cmdline.position'] = nil package.loaded['bugabinga.cmdline.cursor'] = nil package.loaded['bugabinga.cmdline.render'] = nil end) after_each(function() local ok, render = pcall(require, 'bugabinga.cmdline.render') if ok then render.hide() end local ok2, pos = pcall(require, 'bugabinga.cmdline.position') if ok2 then pos.set_cmdline_anchor(nil) end end) it('clears anchor after hide', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state = state.new({ { {}, ':test' }, }, 1, ':', '', 0, 1) render.show(cmdline_state) render.hide() local pos = require('bugabinga.cmdline.position') local anchor = pos.get_cmdline_anchor() assert.is_nil(anchor) end) end) describe('render corner highlights', function() before_each(function() package.loaded['bugabinga.cmdline.state'] = nil package.loaded['bugabinga.cmdline.format'] = nil package.loaded['bugabinga.cmdline.position'] = nil package.loaded['bugabinga.cmdline.cursor'] = nil package.loaded['bugabinga.cmdline.render'] = nil end) after_each(function() local ok, render = pcall(require, 'bugabinga.cmdline.render') if ok then render.hide() end local ok2, pos = pcall(require, 'bugabinga.cmdline.position') if ok2 then pos.set_cmdline_anchor(nil) end end) it('winhighlight contains different border and corner highlights', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state = state.new({ { {}, ':test' }, }, 1, ':', '', 0, 1) render.show(cmdline_state) local pos = require('bugabinga.cmdline.position') local anchor = assert(pos.get_cmdline_anchor()) local wh = vim.wo[anchor.win].winhighlight local border_hl = wh:match 'FloatBorder:(%w+)' local corner_hl = wh:match 'FloatBorderCorner:(%w+)' assert(border_hl ~= nil, 'FloatBorder highlight not found in: ' .. wh) assert(corner_hl ~= nil, 'FloatBorderCorner highlight not found in: ' .. wh) assert(border_hl ~= corner_hl, 'border and corner should differ') render.hide() end) it('corner highlight is CmdlineBorderCornerCmdline for cmdline format', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state = state.new({ { {}, '' }, }, 1, ':', '', 0, 1) render.show(cmdline_state) local pos = require('bugabinga.cmdline.position') local anchor = assert(pos.get_cmdline_anchor()) local wh = vim.wo[anchor.win].winhighlight local corner_hl = wh:match 'FloatBorderCorner:(%w+)' assert.equal('CmdlineBorderCornerCmdline', corner_hl) render.hide() end) it('corner highlight is CmdlineBorderCornerHelp for help format', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state = state.new({ { {}, 'h' }, }, 2, ':', '', 0, 1) render.show(cmdline_state) local pos = require('bugabinga.cmdline.position') local anchor = assert(pos.get_cmdline_anchor()) local wh = vim.wo[anchor.win].winhighlight local corner_hl = wh:match 'FloatBorderCorner:(%w+)' assert.equal('CmdlineBorderCornerHelp', corner_hl) render.hide() end) it('corner highlight updates when format changes between show calls', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state1 = state.new({ { {}, '' }, }, 1, ':', '', 0, 1) render.show(cmdline_state1) local pos = require('bugabinga.cmdline.position') local anchor1 = assert(pos.get_cmdline_anchor()) local wh1 = vim.wo[anchor1.win].winhighlight local corner1 = wh1:match 'FloatBorderCorner:(%w+)' local cmdline_state2 = state.new({ { {}, 'h' }, }, 2, ':', '', 0, 1) render.show(cmdline_state2) local anchor2 = assert(pos.get_cmdline_anchor()) local wh2 = vim.wo[anchor2.win].winhighlight local corner2 = wh2:match 'FloatBorderCorner:(%w+)' assert(corner1 ~= corner2, 'corner should change: ' .. corner1 .. ' vs ' .. corner2) render.hide() end) end) describe('render.show with input prompt', function() before_each(function() package.loaded['bugabinga.cmdline.state'] = nil package.loaded['bugabinga.cmdline.format'] = nil package.loaded['bugabinga.cmdline.position'] = nil package.loaded['bugabinga.cmdline.cursor'] = nil package.loaded['bugabinga.cmdline.highlight'] = nil package.loaded['bugabinga.cmdline.render'] = nil end) after_each(function() local ok, render = pcall(require, 'bugabinga.cmdline.render') if ok then render.hide() end local ok2, pos = pcall(require, 'bugabinga.cmdline.position') if ok2 then pos.set_cmdline_anchor(nil) end end) it('input with short prompt does not put prompt in buffer text', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state = state.new({ { {}, 'my input' }, }, 8, ':', 'Name: ', 0, 1) render.show(cmdline_state) local pos = require('bugabinga.cmdline.position') local anchor = assert(pos.get_cmdline_anchor()) local buf = anchor.buf local lines = vim.api.nvim_buf_get_lines(buf, 0, 1, false) assert(lines[1]:find('Name:') == nil, 'prompt should not appear in buffer: ' .. lines[1]) assert(lines[1]:find('my input') ~= nil, 'user input should appear in buffer') render.hide() end) it('input with empty prompt uses normal rendering', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state = state.new({ { {}, ':hello' }, }, 6, ':', '', 0, 1) render.show(cmdline_state) local pos = require('bugabinga.cmdline.position') local anchor = assert(pos.get_cmdline_anchor()) local buf = anchor.buf local lines = vim.api.nvim_buf_get_lines(buf, 0, 1, false) assert.is_true(lines[1]:find(':hello') ~= nil) render.hide() end) it('input format uses input icon (prompt_prefix)', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state = state.new({ { {}, '' }, }, 0, ':', 'Prompt: ', 0, 1) render.show(cmdline_state) local fmt_type = render.get_format_type() assert.equal('input', fmt_type) render.hide() end) it('cursor column is offset by 2 for input', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state = state.new({ { {}, 'test' }, }, 2, ':', 'Prompt: ', 0, 1) render.show(cmdline_state) local pos = require('bugabinga.cmdline.position') local anchor = assert(pos.get_cmdline_anchor()) local cursor = vim.api.nvim_win_get_cursor(anchor.win) assert.equal(2 + 2, cursor[2], 'cursor should be 2 + cursor_byte') render.hide() end) end) describe('render.show with input prompt - multi-line (virt_lines)', function() before_each(function() package.loaded['bugabinga.cmdline.state'] = nil package.loaded['bugabinga.cmdline.format'] = nil package.loaded['bugabinga.cmdline.position'] = nil package.loaded['bugabinga.cmdline.cursor'] = nil package.loaded['bugabinga.cmdline.highlight'] = nil package.loaded['bugabinga.cmdline.render'] = nil end) after_each(function() local ok, render = pcall(require, 'bugabinga.cmdline.render') if ok then render.hide() end local ok2, pos = pcall(require, 'bugabinga.cmdline.position') if ok2 then pos.set_cmdline_anchor(nil) end end) it('multi-line prompt sets virt_lines in buffer', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state = state.new({ { {}, 'input' }, }, 5, ':', 'Line 1\nLine 2\nLine 3', 0, 1) render.show(cmdline_state) local pos = require('bugabinga.cmdline.position') local anchor = assert(pos.get_cmdline_anchor()) local buf = anchor.buf local prompt_ns = vim.api.nvim_create_namespace('bugabinga.cmdline.prompt') local extmarks = vim.api.nvim_buf_get_extmarks(buf, prompt_ns, 0, -1, { details = true }) assert(#extmarks > 0, 'should have virt_lines extmark for multi-line prompt') local mark = assert(extmarks[1]) local opts = assert(mark[4]) assert(opts.virt_lines ~= nil, 'extmark should have virt_lines') assert(opts.virt_lines_above, 'virt_lines should be above') render.hide() end) it('multi-line prompt window height is prompt_chunks + 1', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state = state.new({ { {}, 'x' }, }, 1, ':', 'Line 1\nLine 2', 0, 1) render.show(cmdline_state) local pos = require('bugabinga.cmdline.position') local anchor = assert(pos.get_cmdline_anchor()) local height = vim.api.nvim_win_get_config(anchor.win).height assert.equal(2 + 1, height, 'height should be 2 (prompt lines) + 1 (input line)') render.hide() end) it('multi-line prompt splits on newlines', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state = state.new({ { {}, 'x' }, }, 1, ':', 'first line\nsecond line', 0, 1) render.show(cmdline_state) local pos = require('bugabinga.cmdline.position') local anchor = assert(pos.get_cmdline_anchor()) local buf = anchor.buf local prompt_ns = vim.api.nvim_create_namespace('bugabinga.cmdline.prompt') local extmarks = vim.api.nvim_buf_get_extmarks(buf, prompt_ns, 0, -1, { details = true }) local mark = assert(extmarks[1]) local opts = assert(mark[4]) local virt_lines = assert(opts.virt_lines) assert.equal(2, #virt_lines, 'should have 2 virt_lines for 2 prompt lines') render.hide() end) it('double newline creates blank virt_line', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state = state.new({ { {}, 'x' }, }, 1, ':', 'a\n\nb', 0, 1) render.show(cmdline_state) local pos = require('bugabinga.cmdline.position') local anchor = assert(pos.get_cmdline_anchor()) local buf = anchor.buf local prompt_ns = vim.api.nvim_create_namespace('bugabinga.cmdline.prompt') local extmarks = vim.api.nvim_buf_get_extmarks(buf, prompt_ns, 0, -1, { details = true }) local mark = assert(extmarks[1]) local opts = assert(mark[4]) local virt_lines = assert(opts.virt_lines) assert.equal(3, #virt_lines, 'should have 3 virt_lines: a, blank, b') render.hide() end) end) describe('render.hide clears prompt state', function() before_each(function() package.loaded['bugabinga.cmdline.state'] = nil package.loaded['bugabinga.cmdline.format'] = nil package.loaded['bugabinga.cmdline.position'] = nil package.loaded['bugabinga.cmdline.cursor'] = nil package.loaded['bugabinga.cmdline.highlight'] = nil package.loaded['bugabinga.cmdline.render'] = nil end) after_each(function() local ok, render = pcall(require, 'bugabinga.cmdline.render') if ok then render.hide() end local ok2, pos = pcall(require, 'bugabinga.cmdline.position') if ok2 then pos.set_cmdline_anchor(nil) end end) it('hide after input clears prompt namespace', function() local state = require 'bugabinga.cmdline.state' local render = require 'bugabinga.cmdline.render' local cmdline_state = state.new({ { {}, 'x' }, }, 1, ':', 'Line 1\nLine 2', 0, 1) render.show(cmdline_state) local pos = require('bugabinga.cmdline.position') local anchor = assert(pos.get_cmdline_anchor()) local buf = anchor.buf render.hide() local prompt_ns = vim.api.nvim_create_namespace('bugabinga.cmdline.prompt') local extmarks = vim.api.nvim_buf_get_extmarks(buf, prompt_ns, 0, -1, {}) assert.equal(0, #extmarks, 'prompt namespace should be empty after hide') end) end)