#!/usr/bin/env -S nvim -l
-- Generator for various vimdoc and Lua type files
local util = require('gen.util')
local lint = require('gen.lint')
local api_type = require('gen.api_types')
local fmt = string.format
local DEP_API_METADATA = arg[1]
local TAGS_FILE = arg[2]
local TEXT_WIDTH = 78
--- @class vim.api.metadata
--- @field name string
--- @field parameters [string,string][]
--- @field return_type string
--- @field deprecated_since integer
--- @field eval boolean
--- @field fast boolean
--- @field handler_id integer
--- @field impl_name string
--- @field lua boolean
--- @field method boolean
--- @field remote boolean
--- @field since integer
local LUA_API_RETURN_OVERRIDES = {
nvim_parse_cmd = 'vim.api.keyset.cmd_ret',
nvim_win_get_config = 'vim.api.keyset.win_config_ret',
}
local LUA_META_HEADER = {
'--- @meta _',
'--',
'-- DO NOT EDIT. GENERATED BY: src/gen/gen_eval_files.lua',
'--',
"error('Cannot require a meta file')",
}
local LUA_API_META_HEADER = vim.list_extend(vim.deepcopy(LUA_META_HEADER), {
'',
'--- This file embeds vimdoc as the function descriptions',
'--- so ignore any doc related errors.',
'--- @diagnostic disable: undefined-doc-name,luadoc-miss-symbol',
'',
'vim.api = {}',
})
local LUA_OPTION_META_HEADER = vim.list_extend(vim.deepcopy(LUA_META_HEADER), {
'',
'---@class vim.bo',
'---@field [integer] vim.bo',
'vim.bo = vim.bo',
'',
'---@class vim.wo',
'---@field [integer] vim.wo',
'vim.wo = vim.wo',
})
local LUA_VVAR_META_HEADER = vim.list_extend(vim.deepcopy(LUA_META_HEADER), {
'',
'--- @class vim.v',
'vim.v = ...',
})
local LUA_KEYWORDS = {
['and'] = true,
['end'] = true,
['function'] = true,
['or'] = true,
['if'] = true,
['while'] = true,
['repeat'] = true,
['true'] = true,
['false'] = true,
}
local OPTION_TYPES = {
boolean = 'boolean',
number = 'integer',
string = 'string',
}
--- @param s string
--- @return string
local function luaescape(s)
if LUA_KEYWORDS[s] then
return s .. '_'
end
return s
end
--- @param x string
--- @param sep? string
--- @return string[]
local function split(x, sep)
return vim.split(x, sep or '\n', { plain = true })
end
--- @param f string
--- @param params [string,string,string?][]|true
--- @return string
local function render_fun_sig(f, params)
local param_str --- @type string
if params == true then
param_str = '...'
else
param_str = table.concat(
vim.tbl_map(
--- @param v [string,string,string?]
--- @return string
function(v)
return luaescape(v[1])
end,
params
),
', '
)
end
if LUA_KEYWORDS[f] then
return fmt("vim.fn['%s'] = function(%s) end", f, param_str)
else
return fmt('function vim.fn.%s(%s) end', f, param_str)
end
end
--- Uniquify names
--- @param params [string,string,string?][]
--- @return [string,string,string?][]
local function process_params(params)
local seen = {} --- @type table<string,true>
local sfx = 1
for _, p in ipairs(params) do
if seen[p[1]] then
p[1] = p[1] .. sfx
sfx = sfx + 1
else
seen[p[1]] = true
end
end
return params
end
--- @return table<string, vim.EvalFn>
local function get_api_meta()
local ret = {} --- @type table<string, vim.EvalFn>
local cdoc_parser = require('gen.cdoc_parser')
local f = 'src/nvim/api'
local function include(fun)
if not vim.startswith(fun.name, 'nvim_') then
return false
end
if vim.tbl_contains(fun.attrs or {}, 'lua_only') then
return true
end
if vim.tbl_contains(fun.attrs or {}, 'remote_only') then
return false
end
return true
end
--- @type table<string,nvim.cdoc.parser.fun>
local functions = {}
for path, ty in vim.fs.dir(f) do
if ty == 'file' and (vim.endswith(path, '.c') or vim.endswith(path, '.h')) then
local filename = vim.fs.joinpath(f, path)
local _, funs = cdoc_parser.parse(filename)
for _, fn in ipairs(funs) do
if include(fn) then
functions[fn.name] = fn
end
end
end
end
for _, fun in pairs(functions) do
local deprecated = fun.deprecated_since ~= nil
local notes = {} --- @type string[]
for _, note in ipairs(fun.notes or {}) do
notes[#notes + 1] = note.desc
end
local sees = {} --- @type string[]
for _, see in ipairs(fun.see or {}) do
sees[#sees + 1] = see.desc
end
local params = {} --- @type [string,string,string?][]
for _, p in ipairs(fun.params) do
params[#params + 1] = {
p.name,
p.type,
not deprecated and p.desc or nil,
}
end
local r = {
signature = 'NA',
name = fun.name,
params = params,
notes = notes,
see = sees,
returns = fun.returns[1].type,
deprecated = deprecated,
}
if not deprecated then
r.desc = fun.desc
r.returns_desc = fun.returns[1].desc
end
ret[fun.name] = r
end
return ret
end
--- Convert vimdoc references to markdown literals
--- Convert vimdoc codeblocks to markdown codeblocks
---
--- Ensure code blocks have one empty line before the start fence and after the closing fence.
---
--- @param x string
--- @param special string?
--- | 'see-api-meta' Normalize `@see` for API meta docstrings.
--- @return string
local function norm_text(x, special)
|