repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
neovim/lua/bugabinga/pivi/navigate.lua
Rawlocal entry = require 'bugabinga.pivi.entry'
local transcript = require 'bugabinga.pivi.transcript'
local follow = require 'bugabinga.pivi.follow'
local tool = require 'bugabinga.pivi.tool'
local matching = function( bufnr, kind )
local found = {}
for _, item in ipairs( entry.list( bufnr ) ) do
if not kind or item.kind == kind then table.insert( found, item ) end
end
return found
end
--- The next entry strictly after a line, or the first one wrapping around.
local after = function( bufnr, line_number, kind )
local items = matching( bufnr, kind )
for _, item in ipairs( items ) do
if item.first > line_number then return item end
end
return items[1]
end
--- The previous entry strictly before a line, or the last one wrapping around.
local before = function( bufnr, line_number, kind )
local items = matching( bufnr, kind )
local found
for _, item in ipairs( items ) do
if item.first < line_number then found = item end
end
return found or items[#items]
end
--- Moves the cursor to an entry, leaving the window and the buffer alone.
local go = function( item )
if not item then return nil end
pcall( vim.api.nvim_win_set_cursor, 0, { item.first, 0, } )
return item
end
local next_entry = function( bufnr, kind )
return go( after( bufnr, vim.api.nvim_win_get_cursor( 0 )[1], kind ) )
end
local previous_entry = function( bufnr, kind )
return go( before( bufnr, vim.api.nvim_win_get_cursor( 0 )[1], kind ) )
end
--- The place an entry names, if it names one.
local location_of = function( item )
if not item then return nil end
local meta = item.meta or {}
local args = type( meta.args ) == 'table' and meta.args or {}
-- A result kept only in part still has somewhere it lives in full.
local path = meta.path or args.path or args.file_path or args.filePath or tool.full_output( meta )
if type( path ) ~= 'string' or path == '' then return nil end
if meta.cwd and not vim.startswith( path, '/' ) and not path:match '^%a:' then
path = vim.fs.normalize( meta.cwd .. '/' .. path )
end
-- An edit is worth reaching where it changed something, not at the top of a
-- file the change may be nowhere near.
local line = tool.first_changed_line( meta ) or meta.line or args.line or args.lnum
return { path = path, line = line, }
end
--- Reaches the place the entry under the cursor names.
--- Navigation reuses follow mode's surfaces, so the working window is kept.
local locate = function( bufnr, line_number )
local item = entry.at( bufnr, line_number or vim.api.nvim_win_get_cursor( 0 )[1] )
local location = location_of( item )
if not location then return nil end
return follow.visit( location )
end
--- pivi publishes intent; the configuration chooses the keys.
local bind = function( bufnr )
local motions = {
{ plug = '<Plug>(pivi-next-entry)', kind = nil, move = next_entry,
description = 'pivi next entry', },
{ plug = '<Plug>(pivi-previous-entry)', kind = nil, move = previous_entry,
description = 'pivi previous entry', },
{ plug = '<Plug>(pivi-next-tool)', kind = 'tool', move = next_entry,
description = 'pivi next tool call', },
{ plug = '<Plug>(pivi-previous-tool)', kind = 'tool', move = previous_entry,
description = 'pivi previous tool call', },
}
for _, motion in ipairs( motions ) do
vim.keymap.set( 'n', motion.plug, function() motion.move( bufnr, motion.kind ) end,
{ buffer = bufnr, desc = motion.description, } )
end
vim.keymap.set( 'n', '<Plug>(pivi-goto-location)', function() locate( bufnr ) end,
{ buffer = bufnr, desc = 'pivi go to the location of the entry', } )
end
transcript.on_buffer( bind )
return {
location_of = location_of,
locate = locate,
after = after,
before = before,
next_entry = next_entry,
previous_entry = previous_entry,
bind = bind,
}