#!/usr/bin/env -S nvim -l
--- Generates Nvim :help docs from Lua/C docstrings.
---
--- Usage:
--- make doc
---
--- The generated :help text for each function is formatted as follows:
--- - Max width of 78 columns (`TEXT_WIDTH`), except long function signatures.
--- - Indent with spaces (not tabs).
--- - Indent of 4 columns for body text (`INDENTATION`).
--- - Function signature and helptag on the same line: helptag right-aligned, or after two spaces if
--- the signature is too long.
--- - Subsection bodies are indented an additional 4 spaces.
--- - Body consists of function description, parameters, return description, and
--- C declaration (`INCLUDE_C_DECL`).
--- - Parameters are omitted for the `void` and `Error *` types, or if the
--- parameter is marked as [out].
--- - Each function documentation is separated by a single line.
local luacats_parser = require('gen.luacats_parser')
local cdoc_parser = require('gen.cdoc_parser')
local util = require('gen.util')
local lint = require('gen.lint')
local fmt = string.format
local md_to_vimdoc = util.md_to_vimdoc
local align_tags = util.align_tags
local TEXT_WIDTH = 78
local INDENTATION = 4
--- @class (exact) nvim.gen_vimdoc.Config
---
--- Generated documentation target, e.g. api.txt
--- @field filename string
---
--- Ordered filenames or groups of filenames to merge into one section.
--- For a group, the first filename determines the section name and help tag.
--- @field section_order (string|string[])[]
---
--- List of files/directories for doxygen to read, relative to `base_dir`.
--- @field files string[]
---
--- Section name overrides. Key: filename (e.g., vim.c)
--- @field section_name? table<string,string>
---
--- @field fn_name_pat? string
---
--- @field fn_xform? fun(fun: nvim.luacats.parser.fun)
---
--- @field brief_xform? fun(brief: string): string
---
--- For generated section names.
--- @field section_fmt fun(name: string): string
---
--- @field helptag_fmt fun(name: string): string|string[]
---
--- Per-function helptag.
--- @field fn_helptag_fmt? fun(fun: nvim.gen_vimdoc.HelptagTarget): string
---@alias nvim.gen_vimdoc.HelptagTarget
---| nvim.luacats.parser.fun
---| nvim.luacats.parser.field
---| nvim.luacats.parser.param
local function contains(t, xs)
return vim.tbl_contains(xs, t)
end
--- True if the `.` class member should render like a module function.
--- @param fun nvim.gen_vimdoc.HelptagTarget
--- @return boolean
local function is_module_fun(fun)
return fun.classvar ~= nil
and fun.member_sep == '.'
and fun.modvar ~= nil
and fun.module ~= nil
and fun.classvar == fun.modvar
end
--- @type {level:integer, prerelease:boolean}?
local nvim_api_info_
--- @return {level: integer, prerelease:boolean}
local function nvim_api_info()
if not nvim_api_info_ then
--- @type integer?, boolean?
local level, prerelease
for l in io.lines('CMakeLists.txt') do
--- @cast l string
if level and prerelease then
break
end
local m1 = l:match('^set%(NVIM_API_LEVEL%s+(%d+)%)')
if m1 then
level = tonumber(m1) --[[@as integer]]
end
local m2 = l:match('^set%(NVIM_API_PRERELEASE%s+(%w+)%)')
if m2 then
prerelease = m2 == 'true'
end
end
nvim_api_info_ = { level = assert(level), prerelease = assert(prerelease) }
end
return nvim_api_info_
end
--- @param fun nvim.gen_vimdoc.HelptagTarget
--- @return string
local function fn_helptag_fmt_common(fun)
local fn_sfx = fun.table and '' or '()'
if is_module_fun(fun) then
return fmt('%s.%s%s', fun.module, fun.name, fn_sfx)
end
if fun.classvar then
return fmt('%s:%s%s', fun.classvar, fun.name, fn_sfx)
end
if fun.module then
return fmt('%s.%s%s', fun.module, fun.name, fn_sfx)
end
return fun.name .. fn_sfx
end
--- @type table<string,nvim.gen_vimdoc.Config>
local config = {
api = {
filename = 'api.txt',
section_order = {
-- Sections at the top, in a specific order:
'events.c',
'vim.c',
'vimscript.c',
-- Sections in alphanumeric order:
'autocmd.c',
'buffer.c',
'command.c',
'extmark.c',
'options.c',
'tabpage.c',
'ui.c',
'win_config.c',
'window.c',
},
fn_name_pat = 'nvim_.*',
files = { 'src/nvim/api' },
sec
|