添加注释
This commit is contained in:
219
docs/add_function_comments.ps1
Normal file
219
docs/add_function_comments.ps1
Normal file
@@ -0,0 +1,219 @@
|
||||
param(
|
||||
[string[]]$Files = @(
|
||||
"source/icd_mapper.cpp",
|
||||
"source/interface.cpp",
|
||||
"source/main.cpp",
|
||||
"source/mq.cpp"
|
||||
)
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$root = Split-Path -Parent $PSScriptRoot
|
||||
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
|
||||
|
||||
function Split-Words([string]$name) {
|
||||
$text = $name -replace '^~', ''
|
||||
$text = $text -creplace '([a-z0-9])([A-Z])', '$1 $2'
|
||||
return ($text -replace '_', ' ').ToLowerInvariant()
|
||||
}
|
||||
|
||||
function Brief-For([string]$name, [string]$fileName) {
|
||||
$display = '“' + $name + '”'
|
||||
if ($name -match '^(is|has|can|should|looks|declares|matches|contains|equals|same)') {
|
||||
return "判断 $display 所表达的条件;该函数只读取输入,不负责修改业务状态。"
|
||||
}
|
||||
if ($name -match '^(read|load)') { return "读取或加载 $display 对应的数据,并转换为本模块后续逻辑使用的内存结构。" }
|
||||
if ($name -match '^(write|save|export)') { return "把 $display 对应的内存数据序列化并写入目标位置。" }
|
||||
if ($name -match '^(parse|decode)') { return "解析 $display 对应的文本或结构,提取经过校验的业务字段。" }
|
||||
if ($name -match '^(find|search|lookup)') { return "在现有数据结构中查找 $display 对应的对象或位置。" }
|
||||
if ($name -match '^(collect|gather|scan)') { return "遍历输入结构并收集 $display 所需的候选数据。" }
|
||||
if ($name -match '^(build|make|create|new)') { return "构造 $display 对应的对象、消息、窗口控件或输出结构。" }
|
||||
if ($name -match '^(update|refresh|populate|rebuild|reset)') { return "根据当前模型状态刷新 $display 对应的缓存或界面。" }
|
||||
if ($name -match '^(run|handle|on|wndProc)') { return "处理 $display 对应的业务入口或事件,并调度下游函数完成实际工作。" }
|
||||
if ($name -match '^(add|append|insert|ensure)') { return "向目标结构追加或保证存在 $display 所描述的数据。" }
|
||||
if ($name -match '^(trim|lower|normalize|sanitize|safe|clean|collapse)') { return "规范化 $display 对应的字符串或标识,减少格式差异对匹配造成的影响。" }
|
||||
if ($name -match '^(send|fetch|download)') { return "执行 $display 对应的外部通信,并把结果交给日志、事件或落盘流程。" }
|
||||
if ($name -match '^(compare|match)') { return "比较或匹配 $display 涉及的数据,返回匹配结果或构造匹配信息。" }
|
||||
if ($name -match '^(generate|reverse)') { return "根据当前配置和编辑状态生成 $display 对应的输出内容。" }
|
||||
if ($name -match '^(set|apply|commit|sync)') { return "把 $display 对应的变更应用到模型、控件或输出文档。" }
|
||||
if ($name -eq 'main') { return "命令行入口;无参数时转入 Win32 GUI,有参数时执行兼容的批处理解析流程。" }
|
||||
if ($fileName -eq 'interface.cpp') { return "实现界面层的 $display 功能,并在 Win32 控件与 AppState 业务模型之间传递数据。" }
|
||||
if ($fileName -eq 'mq.cpp') { return "实现外部通信层的 $display 功能,服务于 HTTP、RocketMQ 或运行时文件管理。" }
|
||||
if ($fileName -eq 'main.cpp') { return "实现解析/报表层的 $display 功能,参与 XML、JSON、origin 与工作簿之间的数据转换。" }
|
||||
return "实现 $display 对应的模块内功能。"
|
||||
}
|
||||
|
||||
function Detail-For([string]$fileName) {
|
||||
switch ($fileName) {
|
||||
'interface.cpp' { return '界面使用原生 Win32 API;函数通常读取或更新 AppState,并通过控件 ID、消息通知和 ListView/TreeView API 驱动交互。除 MQ 事件投递外,界面控件操作应在 GUI 线程执行。' }
|
||||
'mq.cpp' { return '本文件组合使用 WinHTTP、Windows 动态库 API、RocketMQ C 接口、nlohmann::json 与 std::filesystem;异常会转换为日志、错误字符串或离线 outbox。共享 MQ 会话状态由互斥锁保护。' }
|
||||
'main.cpp' { return '本文件使用 tinyxml2、nlohmann::json、正则表达式和标准容器完成映射、范围计算、值比较及 Excel 2003 XML 输出;调用方应关注输入格式与路径有效性。' }
|
||||
'icd_mapper.cpp' { return '本文件使用 tinyxml2 遍历 ICD/Excel XML,并使用 Windows 代码页 API 兼容 GBK/GB2312;文件操作采用 std::filesystem 与二进制流,失败时抛出异常交给公共入口处理。' }
|
||||
default { return '函数属于工具内部实现,输入输出与副作用以参数和返回值为边界。' }
|
||||
}
|
||||
}
|
||||
|
||||
function Param-Description([string]$name) {
|
||||
if ($name -match '^(app|state)$') { return '应用状态;包含窗口句柄、当前选择、预览模型和运行期标志。' }
|
||||
if ($name -match 'cfg|config') { return '运行配置;提供接口地址、MQ 参数、路径和当前数据模式。' }
|
||||
if ($name -match '(path|file|dir|root|output|workbook|xmlPath|jsonPath|originPath)$') { return '文件或目录路径;函数按 UTF-8/Windows 路径规则读取或写入。' }
|
||||
if ($name -match '^(doc|document)$') { return 'tinyxml2 文档对象;节点的生命周期由该文档管理。' }
|
||||
if ($name -match '(row|cell|node|element|item|sheet)$') { return '当前处理的数据行、单元格、XML 节点或模型项。' }
|
||||
if ($name -match '^(text|raw|body|value|name|key|tag|topic|label|title|search)$') { return '待解析、比较、显示或发送的 UTF-8 文本。' }
|
||||
if ($name -match '^(hwnd|parent|ctl|control|list|tree)$') { return 'Win32 窗口或控件句柄;必须在有效窗口生命周期内使用。' }
|
||||
if ($name -match '(index|idx|count|size|start|end|offset|col|rowIndex|id)$') { return '索引、数量、范围或偏移参数;具体边界由函数体校验。' }
|
||||
if ($name -match '^(log|event|callback|fn)$') { return '回调函数;用于向界面或日志系统报告进度和结果。' }
|
||||
if ($name -match '^(out|result|message|err|error)$') { return '输出参数;函数在成功或失败时写入结果或诊断信息。' }
|
||||
return '调用方传入的业务参数;其用途与约束由类型、名称及函数体共同定义。'
|
||||
}
|
||||
|
||||
function Return-Description([string]$prefix, [string]$name) {
|
||||
if ($name -eq 'main' -or $prefix -match '(^|\s)int\s*$') { return '整数状态、索引或计数;入口函数以 0 表示成功。' }
|
||||
if ($prefix -match '(^|\s)bool\s*$') { return '条件成立或操作成功时为 true,否则为 false。' }
|
||||
if ($prefix -match '(^|\s)void\s*$' -or $name.StartsWith('~')) { return $null }
|
||||
return '构造、解析或查找到的结果;所有权与生命周期遵循返回类型。'
|
||||
}
|
||||
|
||||
function Split-Parameters([string]$header) {
|
||||
$open = $header.IndexOf('(')
|
||||
if ($open -lt 0) { return @() }
|
||||
$depth = 0
|
||||
$close = -1
|
||||
for ($i = $open; $i -lt $header.Length; $i++) {
|
||||
if ($header[$i] -eq '(') { $depth++ }
|
||||
elseif ($header[$i] -eq ')') {
|
||||
$depth--
|
||||
if ($depth -eq 0) { $close = $i; break }
|
||||
}
|
||||
}
|
||||
if ($close -lt 0) { return @() }
|
||||
$body = $header.Substring($open + 1, $close - $open - 1).Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($body) -or $body -eq 'void') { return @() }
|
||||
|
||||
$parts = New-Object System.Collections.Generic.List[string]
|
||||
$start = 0; $angle = 0; $paren = 0; $bracket = 0; $brace = 0
|
||||
for ($i = 0; $i -lt $body.Length; $i++) {
|
||||
switch ($body[$i]) {
|
||||
'<' { $angle++ }
|
||||
'>' { if ($angle -gt 0) { $angle-- } }
|
||||
'(' { $paren++ }
|
||||
')' { if ($paren -gt 0) { $paren-- } }
|
||||
'[' { $bracket++ }
|
||||
']' { if ($bracket -gt 0) { $bracket-- } }
|
||||
'{' { $brace++ }
|
||||
'}' { if ($brace -gt 0) { $brace-- } }
|
||||
',' {
|
||||
if ($angle -eq 0 -and $paren -eq 0 -and $bracket -eq 0 -and $brace -eq 0) {
|
||||
$parts.Add($body.Substring($start, $i - $start).Trim())
|
||||
$start = $i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$parts.Add($body.Substring($start).Trim())
|
||||
return $parts
|
||||
}
|
||||
|
||||
function Parameter-Name([string]$declaration, [int]$position) {
|
||||
$decl = ($declaration -replace '=.*$', '').Trim()
|
||||
if ($decl -match '\(\s*\*\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)') { return $Matches[1] }
|
||||
$matches = [regex]::Matches($decl, '[A-Za-z_][A-Za-z0-9_]*')
|
||||
if ($matches.Count -eq 0) { return "arg$position" }
|
||||
$candidate = $matches[$matches.Count - 1].Value
|
||||
if ($candidate -in @('const','unsigned','signed','long','short','int','char','bool','double','float','void')) { return "arg$position" }
|
||||
return $candidate
|
||||
}
|
||||
|
||||
function Find-Functions([string[]]$lines) {
|
||||
$found = New-Object System.Collections.Generic.List[object]
|
||||
for ($i = 0; $i -lt $lines.Count; $i++) {
|
||||
$first = $lines[$i].Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($first) -or $first.StartsWith('#') -or
|
||||
$first.StartsWith('//') -or $first.StartsWith('/*') -or $first.StartsWith('*')) { continue }
|
||||
if ($first -match '^(if|for|while|switch|catch|return|else|do|try)\b') { continue }
|
||||
if ($first.StartsWith('(') -or $first.StartsWith('!') -or $first.StartsWith(':')) { continue }
|
||||
# A definition must begin on this line. Requiring '(' here prevents a closing brace
|
||||
# or an ordinary statement from borrowing the next function's signature.
|
||||
if (-not $first.Contains('(')) { continue }
|
||||
|
||||
$headerLines = New-Object System.Collections.Generic.List[string]
|
||||
$hasBrace = $false
|
||||
for ($j = $i; $j -lt [Math]::Min($lines.Count, $i + 16); $j++) {
|
||||
$part = $lines[$j].Trim()
|
||||
$headerLines.Add($part)
|
||||
if ($part.Contains(';') -and -not $part.Contains('{')) { break }
|
||||
if ($part.Contains('{')) { $hasBrace = $true; break }
|
||||
}
|
||||
if (-not $hasBrace) { continue }
|
||||
$header = ($headerLines -join ' ')
|
||||
$beforeBrace = $header.Substring(0, $header.IndexOf('{')).Trim()
|
||||
if (-not $beforeBrace.Contains('(') -or -not $beforeBrace.Contains(')')) { continue }
|
||||
if ($beforeBrace -match '\[.*\]\s*\(' -or $beforeBrace -match '\b(auto|const auto|auto&)\s+\w+\s*=') { continue }
|
||||
if ($beforeBrace -match '^\s*(if|for|while|switch|catch)\s*\(') { continue }
|
||||
if ($beforeBrace.Contains('(*')) { continue }
|
||||
|
||||
$open = $beforeBrace.IndexOf('(')
|
||||
$left = $beforeBrace.Substring(0, $open).Trim()
|
||||
$nameMatch = [regex]::Match($left, '([A-Za-z_~][A-Za-z0-9_:~]*)\s*$')
|
||||
if (-not $nameMatch.Success) { continue }
|
||||
$fullName = $nameMatch.Groups[1].Value
|
||||
$name = ($fullName -split '::')[-1]
|
||||
if ($name -in @('if','for','while','switch','catch')) { continue }
|
||||
$prefix = $left.Substring(0, $nameMatch.Index).Trim()
|
||||
if ($prefix.Contains('=')) { continue }
|
||||
if ($prefix -notmatch '[A-Za-z_]') { continue }
|
||||
# Ordinary calls such as ListView_SetItemText(...) also have no return-type prefix.
|
||||
# Constructors have no return-type prefix and resemble ordinary calls, so this
|
||||
# conservative scanner leaves them to a short manual review after generation.
|
||||
if ([string]::IsNullOrWhiteSpace($prefix) -and -not $name.StartsWith('~')) { continue }
|
||||
if ($prefix.EndsWith('.') -or $prefix.EndsWith('->')) { continue }
|
||||
$found.Add([pscustomobject]@{ Index=$i; Header=$beforeBrace; Name=$name; Prefix=$prefix })
|
||||
}
|
||||
return $found
|
||||
}
|
||||
|
||||
foreach ($relative in $Files) {
|
||||
$path = Join-Path $root $relative
|
||||
if (-not (Test-Path -LiteralPath $path)) { throw "Missing source file: $path" }
|
||||
$text = [IO.File]::ReadAllText($path, [Text.Encoding]::UTF8)
|
||||
$newline = if ($text.Contains("`r`n")) { "`r`n" } else { "`n" }
|
||||
$lines = [regex]::Split($text, "\r?\n")
|
||||
$functions = @(Find-Functions $lines)
|
||||
$byIndex = @{}
|
||||
foreach ($fn in $functions) { $byIndex[$fn.Index] = $fn }
|
||||
$out = New-Object System.Collections.Generic.List[string]
|
||||
$fileName = [IO.Path]::GetFileName($path)
|
||||
$inserted = 0
|
||||
for ($i = 0; $i -lt $lines.Count; $i++) {
|
||||
if ($byIndex.ContainsKey($i)) {
|
||||
$fn = $byIndex[$i]
|
||||
$already = $false
|
||||
# A function with many parameters can have a fairly long generated Doxygen
|
||||
# block. Search far enough back that a second run remains idempotent.
|
||||
for ($k = [Math]::Max(0, $i - 64); $k -lt $i; $k++) {
|
||||
if ($lines[$k] -like "*Function: $($fn.Name)*") { $already = $true; break }
|
||||
}
|
||||
if (-not $already) {
|
||||
$indent = ([regex]::Match($lines[$i], '^\s*')).Value
|
||||
$out.Add($indent + '/**')
|
||||
$out.Add($indent + " * Function: $($fn.Name)")
|
||||
$out.Add($indent + ' *')
|
||||
$out.Add($indent + ' * @brief ' + (Brief-For $fn.Name $fileName))
|
||||
$out.Add($indent + ' * @details ' + (Detail-For $fileName))
|
||||
$position = 1
|
||||
foreach ($param in @(Split-Parameters $fn.Header)) {
|
||||
$paramName = Parameter-Name $param $position
|
||||
$out.Add($indent + " * @param $paramName " + (Param-Description $paramName))
|
||||
$position++
|
||||
}
|
||||
$returnText = Return-Description $fn.Prefix $fn.Name
|
||||
if ($null -ne $returnText) { $out.Add($indent + ' * @return ' + $returnText) }
|
||||
$out.Add($indent + ' */')
|
||||
$inserted++
|
||||
}
|
||||
}
|
||||
$out.Add($lines[$i])
|
||||
}
|
||||
[IO.File]::WriteAllText($path, ($out -join $newline), $utf8NoBom)
|
||||
Write-Host "[OK] ${relative}: detected=$($functions.Count), inserted=$inserted"
|
||||
}
|
||||
Reference in New Issue
Block a user