This commit is contained in:
2026-06-03 05:23:07 +02:00
commit a67da8c917
102 changed files with 9238 additions and 0 deletions
+224
View File
@@ -0,0 +1,224 @@
local M = {}
-- C/kernel-doc style: /** ... @param: ... Return: ... */
local function generate_c_doc(bufnr, row, line)
-- strip static/inline/extern prefixes
local stripped = line:gsub("^%s*static%s+", ""):gsub("^%s*inline%s+", ""):gsub("^%s*extern%s+", "")
-- match: type *func_name(params) or type* func_name(params)
local ret, name, params = stripped:match("^%s*([%w_]+%s*%**)%s*([%w_]+)%s*%((.*)%)%s*{?%s*$")
if not name then
return nil, "No C function signature found on current line"
end
local doc = { "/**", " * " .. name .. "() - " }
-- parse parameters
if params and params:match("%S") and not params:match("^%s*void%s*$") then
for param in params:gmatch("([^,]+)") do
local pname = param:match("([%w_]+)%s*$")
or param:match("%*%s*([%w_]+)")
or param:match("([%w_]+)%s*%[")
if pname then
table.insert(doc, " * @" .. pname .. ": ")
end
end
end
table.insert(doc, " *")
-- add Return: if not void
ret = ret and ret:gsub("%s+", " "):gsub("^%s*", ""):gsub("%s*$", "") or ""
if ret ~= "void" and ret ~= "" then
table.insert(doc, " * Return: ")
end
table.insert(doc, " */")
return doc, nil
end
-- Go style: // FunctionName does something.
local function generate_go_doc(bufnr, row, line)
-- match: func (receiver) name(params) return or func name(params) return
local name, params, ret
-- method with receiver: func (r *Receiver) Name(params) return
name, params, ret = line:match("^%s*func%s+%([^)]+%)%s+([%w_]+)%s*%((.-)%)%s*(.-)%s*{?%s*$")
-- regular function: func Name(params) return
if not name then
name, params, ret = line:match("^%s*func%s+([%w_]+)%s*%((.-)%)%s*(.-)%s*{?%s*$")
end
if not name then
return nil, "No Go function signature found on current line"
end
local doc = { "// " .. name .. " " }
-- add parameter hints if present
if params and params:match("%S") then
local param_names = {}
for param in params:gmatch("([^,]+)") do
-- Go params: name type or name, name2 type
local pname = param:match("^%s*([%w_]+)")
if pname then
table.insert(param_names, pname)
end
end
if #param_names > 0 then
table.insert(doc, "//")
table.insert(doc, "// Parameters:")
for _, pname in ipairs(param_names) do
table.insert(doc, "// - " .. pname .. ": ")
end
end
end
-- add return hint if present
ret = ret and ret:gsub("^%s*", ""):gsub("%s*$", "") or ""
if ret ~= "" and ret ~= "error" then
table.insert(doc, "//")
table.insert(doc, "// Returns: ")
end
return doc, nil
end
-- Rust style: /// Description
local function generate_rust_doc(bufnr, row, line)
-- match: fn name(params) -> return or pub fn name...
local name, params, ret = line:match("^%s*pub%s+fn%s+([%w_]+)%s*%((.-)%)%s*%->%s*(.-)%s*{?%s*$")
if not name then
name, params, ret = line:match("^%s*fn%s+([%w_]+)%s*%((.-)%)%s*%->%s*(.-)%s*{?%s*$")
end
if not name then
name, params = line:match("^%s*pub%s+fn%s+([%w_]+)%s*%((.-)%)%s*{?%s*$")
end
if not name then
name, params = line:match("^%s*fn%s+([%w_]+)%s*%((.-)%)%s*{?%s*$")
end
if not name then
return nil, "No Rust function signature found on current line"
end
local doc = { "/// " }
-- add parameter hints if present
if params and params:match("%S") then
local param_names = {}
for param in params:gmatch("([^,]+)") do
local pname = param:match("^%s*([%w_]+)%s*:")
if pname and pname ~= "self" and pname ~= "&self" and pname ~= "&mut" then
table.insert(param_names, pname)
end
end
if #param_names > 0 then
table.insert(doc, "///")
table.insert(doc, "/// # Arguments")
table.insert(doc, "///")
for _, pname in ipairs(param_names) do
table.insert(doc, "/// * `" .. pname .. "` - ")
end
end
end
-- add return hint if present
ret = ret and ret:gsub("^%s*", ""):gsub("%s*$", "") or ""
if ret ~= "" then
table.insert(doc, "///")
table.insert(doc, "/// # Returns")
table.insert(doc, "///")
table.insert(doc, "/// ")
end
return doc, nil
end
-- Python style: """docstring"""
local function generate_python_doc(bufnr, row, line)
local name, params = line:match("^%s*def%s+([%w_]+)%s*%((.-)%)%s*:?%s*$")
if not name then
name, params = line:match("^%s*async%s+def%s+([%w_]+)%s*%((.-)%)%s*:?%s*$")
end
if not name then
return nil, "No Python function signature found on current line"
end
local indent = line:match("^(%s*)") or ""
local doc = { indent .. ' """' }
-- parse parameters
if params and params:match("%S") then
local param_names = {}
for param in params:gmatch("([^,]+)") do
local pname = param:match("^%s*([%w_]+)")
if pname and pname ~= "self" and pname ~= "cls" then
table.insert(param_names, pname)
end
end
if #param_names > 0 then
table.insert(doc, indent .. "")
table.insert(doc, indent .. " Args:")
for _, pname in ipairs(param_names) do
table.insert(doc, indent .. " " .. pname .. ": ")
end
end
end
table.insert(doc, indent .. "")
table.insert(doc, indent .. " Returns:")
table.insert(doc, indent .. " ")
table.insert(doc, indent .. ' """')
return doc, nil
end
-- filetype to generator mapping
local generators = {
c = generate_c_doc,
cpp = generate_c_doc,
h = generate_c_doc,
go = generate_go_doc,
rust = generate_rust_doc,
python = generate_python_doc,
}
function M.generate_doc()
local bufnr = vim.api.nvim_get_current_buf()
local row = vim.api.nvim_win_get_cursor(0)[1]
local line = vim.api.nvim_buf_get_lines(bufnr, row - 1, row, false)[1]
local ft = vim.bo[bufnr].filetype
local generator = generators[ft]
if not generator then
vim.notify("No doc generator for filetype: " .. ft, vim.log.levels.WARN)
return
end
local doc, err = generator(bufnr, row, line)
if err then
vim.notify(err, vim.log.levels.ERROR)
return
end
vim.api.nvim_buf_set_lines(bufnr, row - 1, row - 1, false, doc)
-- position cursor at first empty description spot
local cursor_row = row
local cursor_col = #doc[1]
for i, docline in ipairs(doc) do
if docline:match("%s$") or docline:match(":%s*$") or docline:match("%-%s*$") then
cursor_row = row + i - 1
cursor_col = #docline
break
end
end
vim.api.nvim_win_set_cursor(0, { cursor_row, cursor_col })
vim.cmd("startinsert!")
end
vim.keymap.set("n", "<leader>dg", M.generate_doc)