repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
neovim/tests/bugabinga/pivi/editor/navigate_spec.lua
Raw---@diagnostic disable: duplicate-set-field, need-check-nil, param-type-mismatch, assign-type-mismatch, redundant-parameter
local navigate = require 'bugabinga.pivi.navigate'
local entry = require 'bugabinga.pivi.entry'
local transcript = require 'bugabinga.pivi.transcript'
local follow = require 'bugabinga.pivi.follow'
local fake = dofile 'tests/helpers/pivi_fake.lua'
local temp_file = function( lines )
local path = vim.fs.normalize( vim.fn.tempname() .. '.txt' )
vim.fn.writefile( lines, path )
return path
end
local block = function( bufnr, kind, meta, lines )
local id = entry.open( bufnr, kind, meta )
for _, text in ipairs( lines ) do entry.line( bufnr, id, text ) end
entry.close( bufnr, id )
return id
end
describe( 'pivi navigate', function()
local bufnr, win
before_each( function()
bufnr = transcript.ensure 'moving'
transcript.clear( bufnr )
block( bufnr, 'prompt', { text = 'ask', }, { 'ask', } )
block( bufnr, 'tool', { tool = 'read', }, { 'read a.lua', 'one', } )
block( bufnr, 'answer', {}, { 'reply', } )
block( bufnr, 'tool', { tool = 'edit', }, { 'edit a.lua', } )
win = transcript.show( bufnr )
vim.api.nvim_set_current_win( win )
end )
after_each( function()
follow.reset()
transcript.forget_all()
fake.close_extra_windows()
end )
describe( 'locations', function()
it( 'reads the place an entry names', function()
local located = navigate.location_of {
kind = 'tool',
meta = { args = { path = 'src/main.lua', line = 12, }, cwd = '/work', },
}
assert.equal( '/work/src/main.lua', located.path )
assert.equal( 12, located.line )
end )
it( 'keeps an absolute path as it is', function()
local located = navigate.location_of {
kind = 'tool',
meta = { args = { path = '/elsewhere/a.lua', }, cwd = '/work', },
}
assert.equal( '/elsewhere/a.lua', located.path )
end )
it( 'reaches the first changed line of an edit', function()
local located = navigate.location_of {
kind = 'tool',
meta = {
tool = 'edit',
args = { path = 'a.lua', },
result = { details = { diff = '-7 old\n+7 new', firstChangedLine = 7, }, },
},
}
assert.equal( 7, located.line )
end )
it( 'prefers the reported change over an argument line', function()
local located = navigate.location_of {
kind = 'tool',
meta = {
tool = 'edit',
args = { path = 'a.lua', line = 1, },
result = { details = { firstChangedLine = 12, }, },
},
}
assert.equal( 12, located.line )
end )
it( 'keeps the argument line when no change was reported', function()
local located = navigate.location_of {
kind = 'tool',
meta = { tool = 'read', args = { path = 'a.lua', line = 4, }, },
}
assert.equal( 4, located.line )
end )
it( 'lands on an edit at the line it changed', function()
local path = temp_file { 'one', 'two', 'three', 'four', 'five', }
block( bufnr, 'tool', {
tool = 'edit',
args = { path = path, },
result = { details = { diff = '-4 four\n+4 FOUR', firstChangedLine = 4, }, },
}, { 'edit', '-4 four', '+4 FOUR', } )
local listed = entry.list( bufnr )
local target = navigate.locate( bufnr, listed[#listed].first )
assert.is_number( target )
assert.equal( 4, vim.api.nvim_win_get_cursor( target )[1] )
end )
it( 'reaches the full output of a truncated result', function()
local located = navigate.location_of {
kind = 'tool',
meta = {
tool = 'bash',
args = { command = 'ls -R', },
result = { details = { truncation = { truncated = true, }, fullOutputPath = '/tmp/pi-bash-abc.log', }, },
},
}
assert.equal( '/tmp/pi-bash-abc.log', located.path )
end )
it( 'prefers what a tool acted on over where its output spilled', function()
local located = navigate.location_of {
kind = 'tool',
meta = {
tool = 'read',
args = { path = '/work/a.lua', },
result = { details = { fullOutputPath = '/tmp/spill.log', }, },
},
}
assert.equal( '/work/a.lua', located.path )
end )
it( 'reports no place for an entry that names none', function()
assert.is_nil( navigate.location_of { kind = 'answer', meta = {}, } )
assert.is_nil( navigate.location_of( nil ) )
end )
it( 'reaches the place an entry names without taking the working window', function()
local path = temp_file { 'one', 'two', 'three', }
block( bufnr, 'tool', { tool = 'read', args = { path = path, line = 3, }, }, { 'read', } )
local listed = entry.list( bufnr )
local tool = listed[#listed]
vim.api.nvim_win_set_cursor( win, { tool.first, 0, } )
local target = navigate.locate( bufnr, tool.first )
assert.is_number( target )
assert.is_not.equal( win, target )
assert.equal( win, vim.api.nvim_get_current_win() )
assert.equal( 3, vim.api.nvim_win_get_cursor( target )[1] )
end )
it( 'reaches nothing from an entry without a location', function()
local listed = entry.list( bufnr )
local answer = listed[3]
assert.is_nil( navigate.locate( bufnr, answer.first ) )
end )
it( 'publishes going to a location as an intent', function()
local published
for _, mapping in ipairs( vim.api.nvim_buf_get_keymap( bufnr, 'n' ) ) do
if mapping.lhs == '<Plug>(pivi-goto-location)' then published = mapping end
end
assert.is_table( published )
end )
end )
it( 'moves to the next entry regardless of kind', function()
local listed = entry.list( bufnr )
vim.api.nvim_win_set_cursor( win, { listed[1].first, 0, } )
local moved = navigate.next_entry( bufnr )
assert.equal( listed[2].id, moved.id )
assert.equal( listed[2].first, vim.api.nvim_win_get_cursor( win )[1] )
end )
it( 'moves to the previous entry', function()
local listed = entry.list( bufnr )
vim.api.nvim_win_set_cursor( win, { listed[3].first, 0, } )
local moved = navigate.previous_entry( bufnr )
assert.equal( listed[2].id, moved.id )
end )
it( 'moves between tool calls only', function()
local listed = entry.list( bufnr )
vim.api.nvim_win_set_cursor( win, { listed[1].first, 0, } )
local first_tool = navigate.next_entry( bufnr, 'tool' )
assert.equal( 'tool', first_tool.kind )
assert.equal( 'read', first_tool.meta.tool )
local second_tool = navigate.next_entry( bufnr, 'tool' )
assert.equal( 'edit', second_tool.meta.tool )
end )
it( 'moves backwards between tool calls', function()
local listed = entry.list( bufnr )
vim.api.nvim_win_set_cursor( win, { listed[4].first, 0, } )
local moved = navigate.previous_entry( bufnr, 'tool' )
assert.equal( 'read', moved.meta.tool )
end )
it( 'wraps around at both ends', function()
local listed = entry.list( bufnr )
vim.api.nvim_win_set_cursor( win, { listed[#listed].first, 0, } )
assert.equal( listed[1].id, navigate.next_entry( bufnr ).id )
vim.api.nvim_win_set_cursor( win, { listed[1].first, 0, } )
assert.equal( listed[#listed].id, navigate.previous_entry( bufnr ).id )
end )
it( 'moves nowhere in a transcript without matching entries', function()
local empty = transcript.ensure 'nothing'
transcript.clear( empty )
assert.is_nil( navigate.after( empty, 1 ) )
assert.is_nil( navigate.before( empty, 1 ) )
end )
it( 'publishes each motion as an intent', function()
local published = {}
for _, mapping in ipairs( vim.api.nvim_buf_get_keymap( bufnr, 'n' ) ) do
published[mapping.lhs] = true
end
for _, intent in ipairs {
'<Plug>(pivi-next-entry)', '<Plug>(pivi-previous-entry)',
'<Plug>(pivi-next-tool)', '<Plug>(pivi-previous-tool)',
} do
assert.is_true( published[intent], intent .. ' is not published' )
end
end )
it( 'never changes the window or the buffer while moving', function()
local listed = entry.list( bufnr )
vim.api.nvim_win_set_cursor( win, { listed[1].first, 0, } )
local before = transcript.lines( bufnr )
navigate.next_entry( bufnr )
assert.equal( win, vim.api.nvim_get_current_win() )
assert.equal( bufnr, vim.api.nvim_win_get_buf( win ) )
assert.same( before, transcript.lines( bufnr ) )
end )
end )