local transcript = require 'bugabinga.pivi.transcript' local glyph = require 'std.icon' local MODES = { 'n', 'i', 'x', } --- What every published intent is for, by its name. local intents = function( bufnr ) local known = {} for _, mode in ipairs( MODES ) do for _, mapping in ipairs( vim.api.nvim_buf_get_keymap( bufnr, mode ) ) do local plug = mapping.lhs:match '^%((pivi%-[%w%-]+)%)$' if plug then known[plug] = ( mapping.desc or plug ):gsub( '^pivi ', '' ) end end end return known end --- What the keys of a pivi buffer are for. --- pivi chooses no keys, so the guide reports the ones the configuration bound --- to pivi's intents. It describes what is actually reachable rather than what --- anyone intended, and cannot drift from either. local describe = function( bufnr ) local known = intents( bufnr ) local found = {} local seen = {} local collect = function( mappings ) for _, mapping in ipairs( mappings ) do if not mapping.lhs:find( '', 1, true ) then local target = ( mapping.rhs or '' ):match '%((pivi%-[%w%-]+)%)' local what = target and known[target] or nil -- A key pivi binds itself, such as slash-command entry, is described -- by its own mapping rather than by an intent. if not what and ( mapping.desc or '' ):find( 'pivi', 1, true ) then what = mapping.desc:gsub( '^pivi ', '' ) end if what and not seen[mapping.lhs] then seen[mapping.lhs] = true table.insert( found, { keys = mapping.lhs, what = what, } ) end end end end for _, mode in ipairs( MODES ) do collect( vim.api.nvim_buf_get_keymap( bufnr, mode ) ) collect( vim.api.nvim_get_keymap( mode ) ) end table.sort( found, function( left, right ) return left.keys < right.keys end ) return found end local commands = function() local ok, command = pcall( require, 'bugabinga.pivi.command' ) if not ok then return {} end return command.names() end local lines = function( bufnr ) local written = { '# ' .. glyph.help .. ' pivi', '', } local keys = describe( bufnr ) if #keys > 0 then table.insert( written, '## ' .. glyph.keyword .. ' keys here' ) table.insert( written, '' ) for _, mapping in ipairs( keys ) do table.insert( written, ( '- `%s` %s' ):format( mapping.keys, mapping.what ) ) end table.insert( written, '' ) end local names = commands() if #names > 0 then table.insert( written, '## ' .. glyph.command .. ' commands' ) table.insert( written, '' ) for _, name in ipairs( names ) do table.insert( written, '- `:Pi ' .. name .. '`' ) end table.insert( written, '' ) table.insert( written, 'Bare `:Pi` opens the prompt.' ) table.insert( written, '' ) end if vim.bo[bufnr].filetype == 'pivi-input' then table.insert( written, '## ' .. glyph.insert .. ' while writing' ) table.insert( written, '' ) table.insert( written, 'The prompt opens in insert mode, so keys behind a leader need `` first.' ) table.insert( written, 'Set `vim.g.pivi_start_insert = false` to open in normal mode instead.' ) table.insert( written, '' ) end table.insert( written, '## ' .. glyph.list .. ' ordinary neovim' ) table.insert( written, '' ) table.insert( written, '- `za` `zo` `zc` collapse or expand the entry under the cursor' ) table.insert( written, '- `zR` / `zM` expand or collapse every entry' ) table.insert( written, '- `]c` / `[c` move between changes inside a comparison' ) table.insert( written, '' ) table.insert( written, 'Entries are real folds, so no key here replaces one you already know.' ) return written end --- Shows the guide without leaving the buffer it describes. --- @return number? bufnr --- @return number? winid local show = function( bufnr ) bufnr = bufnr or vim.api.nvim_get_current_buf() if not vim.api.nvim_buf_is_valid( bufnr ) then return nil end local ok, float, window = pcall( vim.lsp.util.open_floating_preview, lines( bufnr ), 'markdown', { border = 'rounded', focus = false, focusable = true, max_width = 80, max_height = 30, title = ' pivi ', } ) if not ok then return nil end if float and vim.api.nvim_buf_is_valid( float ) then pcall( vim.api.nvim_buf_set_name, float, 'pivi://guide' ) end return float, window end local bind = function( bufnr ) vim.keymap.set( 'n', '(pivi-guide)', function() show( bufnr ) end, { buffer = bufnr, desc = 'pivi show this guide', } ) end transcript.on_buffer( bind ) return { describe = describe, lines = lines, show = show, bind = bind, }