---@diagnostic disable: duplicate-set-field, need-check-nil, param-type-mismatch, assign-type-mismatch local command = require 'bugabinga.pivi.command' local session = require 'bugabinga.pivi.session' local transcript = require 'bugabinga.pivi.transcript' local publish = require 'bugabinga.pivi.publish' local fake = dofile 'tests/helpers/pivi_fake.lua' local notices describe( 'pivi command', function() local original_notify, original_select before_each( function() notices = {} original_notify = vim.notify original_select = vim.ui.select vim.notify = function( message, level ) table.insert( notices, { message, level, } ) end end ) after_each( function() fake.drain() vim.notify = original_notify vim.ui.select = original_select session.stop_all() transcript.forget_all() publish.reset() fake.close_extra_windows() vim.cmd 'silent! %bwipeout!' pcall( vim.api.nvim_del_user_command, 'Pi' ) end ) describe( 'slash entry', function() it( 'opens only at the first character of a pivi input buffer', function() local bufnr = vim.api.nvim_create_buf( false, true ) vim.bo[bufnr].filetype = command.input_filetype assert.is_true( command.slash_opens_commands( bufnr, 0 ) ) assert.is_false( command.slash_opens_commands( bufnr, 1 ) ) assert.is_false( command.slash_opens_commands( bufnr, 7 ) ) end ) it( 'gives the prompt window an identity and its submit hint', function() local channel = fake.new() local current = session.create { spawn = channel.spawn, name = 'spec', extension = false, skill = false, env = {}, } local bufnr = command.open_prompt( current ) local win = vim.fn.bufwinid( bufnr ) assert.is_true( win > 0 ) assert.is_true( vim.wo[win].winbar:find( 'spec', 1, true ) ~= nil ) assert.is_true( vim.wo[win].winbar:find( 'send', 1, true ) ~= nil ) assert.is_true( vim.api.nvim_buf_get_name( bufnr ):find( 'pivi://', 1, true ) ~= nil ) assert.equal( 'spec', vim.b[bufnr].pivi_session ) vim.cmd.stopinsert() end ) it( 'shows the transcript after a prompt submission', function() local channel = fake.new() local current = session.create { spawn = channel.spawn, name = 'spec', extension = false, skill = false, env = {}, } local bufnr = command.open_prompt( current ) vim.cmd.stopinsert() vim.api.nvim_buf_set_lines( bufnr, 0, -1, false, { 'do the thing', } ) local mapping for _, entry in ipairs( vim.api.nvim_buf_get_keymap( bufnr, 'n' ) ) do if entry.lhs == '(pivi-submit)' then mapping = entry end end assert.is_table( mapping ) mapping.callback() assert.is_true( vim.fn.bufwinid( current.buffer ) > 0 ) assert.same( { '', }, vim.api.nvim_buf_get_lines( bufnr, 0, -1, false ) ) end ) it( 'reuses the prompt of a session and keeps an unfinished draft', function() local channel = fake.new() local current = session.create { spawn = channel.spawn, name = 'spec', extension = false, skill = false, env = {}, } local first = command.open_prompt( current ) vim.cmd.stopinsert() vim.api.nvim_buf_set_lines( first, 0, -1, false, { 'my unfinished draft', } ) local again = command.open_prompt( current ) vim.cmd.stopinsert() assert.equal( first, again ) assert.same( { 'my unfinished draft', }, vim.api.nvim_buf_get_lines( again, 0, -1, false ) ) local alive = 0 for _, bufnr in ipairs( vim.api.nvim_list_bufs() ) do if vim.bo[bufnr].filetype == command.input_filetype then alive = alive + 1 end end assert.equal( 1, alive ) end ) it( 'never opens in an ordinary buffer', function() vim.cmd 'enew' local bufnr = vim.api.nvim_get_current_buf() assert.is_false( command.slash_opens_commands( bufnr, 0 ) ) end ) it( 'never opens in an invalid buffer', function() local bufnr = vim.api.nvim_create_buf( false, true ) vim.api.nvim_buf_delete( bufnr, { force = true, } ) assert.is_false( command.slash_opens_commands( bufnr, 0 ) ) end ) it( 'keeps ordinary search behavior away from the first character', function() local channel = fake.new() local current = session.create { spawn = channel.spawn, name = 'spec', extension = false, skill = false, env = {}, } local bufnr = command.open_prompt( current ) vim.api.nvim_buf_set_lines( bufnr, 0, -1, false, { 'hello', } ) vim.api.nvim_win_set_cursor( 0, { 1, 3, } ) local mapping for _, entry in ipairs( vim.api.nvim_buf_get_keymap( bufnr, 'n' ) ) do if entry.lhs == '/' then mapping = entry end end assert.is_table( mapping ) assert.equal( '/', mapping.callback() ) end ) end ) describe( 'dispatch', function() it( 'registers the user command and its subcommand completion', function() command.setup() assert.is_table( vim.api.nvim_get_commands( {} ).Pi ) local names = command.names() assert.is_true( vim.list_contains( names, 'ask' ) ) assert.is_true( vim.list_contains( names, 'help' ) ) assert.is_true( vim.list_contains( names, 'command' ) ) end ) it( 'opens the prompt when given no subcommand', function() local channel = fake.new() session.create { spawn = channel.spawn, name = 'spec', extension = false, skill = false, env = {}, } command.dispatch { args = '', } local opened for _, bufnr in ipairs( vim.api.nvim_list_bufs() ) do if vim.bo[bufnr].filetype == command.input_filetype then opened = bufnr end end assert.is_number( opened ) assert.is_true( vim.fn.bufwinid( opened ) > 0 ) vim.cmd.stopinsert() end ) it( 'reports an unknown subcommand', function() command.dispatch { args = 'levitate', } assert.equal( 1, #notices ) assert.equal( vim.log.levels.ERROR, notices[1][2] ) end ) it( 'refuses an empty question', function() command.dispatch { args = 'help', } assert.equal( vim.log.levels.WARN, notices[1][2] ) end ) it( 'reports that nothing is running when no session exists', function() command.dispatch { args = 'abort', } assert.equal( vim.log.levels.WARN, notices[1][2] ) end ) end ) describe( 'slash commands', function() it( 'refuses a name pi does not offer, instead of saying it to the model', function() local channel = fake.new() local current = session.create { spawn = channel.spawn, name = 'spec', extension = false, skill = false, env = {}, } command.subcommands.command 'model' local request for _, entry in ipairs( channel.commands ) do if entry.type == 'get_commands' then request = entry end end assert.is_table( request ) channel.frame { id = request.id, type = 'response', command = 'get_commands', success = true, data = { commands = { { name = 'compact', source = 'extension', }, }, }, } assert.is_true( channel.settle( function() return #notices > 0 end ) ) -- Nothing was said to the model. for _, entry in ipairs( channel.commands ) do assert.is_not.equal( 'prompt', entry.type ) end assert.equal( vim.log.levels.WARN, notices[#notices][2] ) assert.is_true( notices[#notices][1]:find( 'model', 1, true ) ~= nil ) end ) it( 'runs a name pi does offer', function() local channel = fake.new() local current = session.create { spawn = channel.spawn, name = 'spec', extension = false, skill = false, env = {}, } command.subcommands.command 'compact focus on edits' local request for _, entry in ipairs( channel.commands ) do if entry.type == 'get_commands' then request = entry end end channel.frame { id = request.id, type = 'response', command = 'get_commands', success = true, data = { commands = { { name = 'compact', source = 'extension', }, }, }, } assert.is_true( channel.settle( function() for _, entry in ipairs( channel.commands ) do if entry.type == 'prompt' then return true end end return false end ) ) local prompts = {} for _, entry in ipairs( channel.commands ) do if entry.type == 'prompt' then table.insert( prompts, entry ) end end assert.equal( 1, #prompts ) assert.equal( '/compact focus on edits', prompts[1].message ) end ) it( 'filters the picker on the command name alone', function() local channel = fake.new() local current = session.create { spawn = channel.spawn, name = 'spec', extension = false, skill = false, env = {}, } local offered vim.ui.select = function( items, options, on_choice ) offered = vim.tbl_map( options.format_item, items ) on_choice( nil ) end command.subcommands.command '' local request for _, entry in ipairs( channel.commands ) do if entry.type == 'get_commands' then request = entry end end channel.frame { id = request.id, type = 'response', command = 'get_commands', success = true, data = { commands = { { name = 'compact', description = 'Compact the conversation context', source = 'extension', }, }, }, } assert.is_true( channel.settle( function() return offered ~= nil end ) ) assert.same( { '/compact', }, offered ) end ) it( 'reports when pi offers no commands', function() local channel = fake.new() session.create { spawn = channel.spawn, name = 'spec', extension = false, skill = false, env = {}, } command.subcommands.command '' local request for _, entry in ipairs( channel.commands ) do if entry.type == 'get_commands' then request = entry end end channel.frame { id = request.id, type = 'response', command = 'get_commands', success = true, data = { commands = {}, }, } assert.is_true( channel.settle( function() return #notices > 0 end ) ) assert.equal( vim.log.levels.WARN, notices[#notices][2] ) end ) end ) describe( 'completion', function() it( 'completes subcommands at the first word', function() command.setup() local offered = vim.fn.getcompletion( 'Pi ', 'cmdline' ) assert.is_true( vim.list_contains( offered, 'command' ) ) assert.is_true( vim.list_contains( offered, 'model' ) ) end ) it( 'completes the names pi actually offers after `command`', function() command.setup() local channel = fake.new() local current = session.create { spawn = channel.spawn, name = 'spec', extension = false, skill = false, env = {}, } current.commands = { { name = 'compact', source = 'extension', }, { name = 'skill:wiki', source = 'skill', }, } local offered = vim.fn.getcompletion( 'Pi command ', 'cmdline' ) assert.is_true( vim.list_contains( offered, 'compact' ) ) assert.is_true( vim.list_contains( offered, 'skill:wiki' ) ) end ) it( 'completes follow modes', function() command.setup() local offered = vim.fn.getcompletion( 'Pi follow ', 'cmdline' ) assert.is_true( vim.list_contains( offered, 'window' ) ) assert.is_true( vim.list_contains( offered, 'tab' ) ) end ) end ) describe( 'follow', function() it( 'toggles follow mode and reports it', function() command.subcommands.follow '' assert.is_true( notices[1][1]:find( 'window', 1, true ) ~= nil ) command.subcommands.follow '' assert.is_true( notices[2][1]:find( 'off', 1, true ) ~= nil ) end ) end ) end )