Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

neovim/lua/bugabinga/pivi/docs.lua

Raw
local MAX_LINES = 80
local MAX_CANDIDATES = 24
local SEPARATOR = '^=====+'

local tag_files = function()
  return vim.api.nvim_get_runtime_file( 'doc/tags', true )
end

--- Resolves a help tag against the tag files of the whole runtime path,
--- so plugin documentation answers exactly like `:help` does here.
--- @param tag string
--- @return table? location file and tag
local locate = function( tag )
  vim.validate( 'tag', tag, 'string' )
  for _, tags_path in ipairs( tag_files() ) do
    local directory = vim.fs.dirname( tags_path )
    local ok, lines = pcall( vim.fn.readfile, tags_path )
    if ok then
      for _, line in ipairs( lines ) do
        local found, file = line:match '^(%S+)\t(%S+)\t'
        if found == tag then
          return { tag = found, path = vim.fs.normalize( directory .. '/' .. file ), }
        end
      end
    end
  end
  return nil
end

local candidates = function( prefix )
  local ok, found = pcall( vim.fn.getcompletion, prefix, 'help' )
  if not ok then return {} end
  return vim.list_slice( found, 1, math.min( #found, MAX_CANDIDATES ) )
end

local section = function( lines, first )
  local collected = {}
  for index = first, math.min( #lines, first + MAX_LINES - 1 ) do
    local line = lines[index]
    if index > first + 4 and line:match( SEPARATOR ) then break end
    table.insert( collected, line )
  end
  while #collected > 0 and collected[#collected] == '' do
    table.remove( collected )
  end
  return collected
end

--- Reads the help text for a tag.
--- @param tag string
--- @return table result found flag, text, source file, or candidate tags
local read = function( tag )
  vim.validate( 'tag', tag, 'string' )

  local location = locate( tag )
  if not location then
    return { found = false, tag = tag, candidates = candidates( tag ), }
  end

  local ok, lines = pcall( vim.fn.readfile, location.path )
  if not ok then
    return { found = false, tag = tag, candidates = candidates( tag ), }
  end

  local needle = '*' .. tag .. '*'
  local first
  for index, line in ipairs( lines ) do
    if line:find( needle, 1, true ) then
      first = index
      break
    end
  end
  if not first then first = 1 end

  return {
    found = true,
    tag = tag,
    path = location.path,
    file = vim.fs.basename( location.path ),
    line = first,
    text = table.concat( section( lines, first ), '\n' ),
  }
end

return {
  max_lines = MAX_LINES,
  locate = locate,
  candidates = candidates,
  read = read,
}