mirror of
https://github.com/opentibiabr/canary
synced 2026-08-16 06:26:09 -04:00
Add Canary's official Lua API documentation generator and keep the generated Lua API reference synchronized with the C++ Lua binding surface. Main changes: - Add LuaApiDocGenerator and LuaBindingScanner to discover Lua classes, methods, globals, constants, parameters, returns, fields, overloads, aliases, source files, and class inheritance from the C++ binding layer. - Generate docs/lua-api/lua_api.d.lua for Lua Language Server and VSCode IntelliSense. - Generate docs/lua-api/lua_api.md for human-readable API documentation. - Generate docs/lua-api/lua_api.json for structured tooling and CI metadata. - Generate docs/lua-api/lua_api_quality_baseline.json for weak-signature regression tracking. - Integrate documentation generation into the startup after loadConfigLua, controlled by generateLuaApiDocs and luaApiDocsOutputDirectory. - Add --generate-lua-api-docs-only so CI can regenerate docs without starting the game server, loading maps, connecting to the database, or running shutdown/save paths. Generator behavior: - Force documentation generation in docgen-only mode so CI sync checks cannot silently become no-ops. - Warn instead of crashing the startup when doc generation fails. - Write generated files atomically. - Skip unchanged writes. - Use deterministic ordering and normalized file endings. - Keep source paths relative and portable. - Normalize inferred C++ types into Lua and LuaLS-friendly types. - Avoid exposing raw C++ types in generated stubs. LuaLS support: - Emit LuaLS-compatible annotations, including meta, aliases, classes, fields, overloads, params, returns, inheritance, callable constructors, typed arrays, and operators for supported metamethods. - Avoid exposing internal metamethods such as __eq, __add, and __gc as normal public Lua methods. - Add explicit signature overlays with docblocks for APIs where automatic inference is not precise enough. - Add overlays for high-impact APIs such as Player, Game, Result, db async calls, Actions, TalkActions, Spells, Weapons, MoveEvents, GlobalEvents, CreatureEvents, NpcType, Position, and NetworkMessage. Editor and documentation: - Add .luarc.json so LuaLS loads docs/lua-api by default. - Raise LuaLS preload limits for lua_api.d.lua. - Exclude generated build, cache, Visual Studio, and vcpkg directories from LuaLS workspace indexing. - Add tools/setup_vscode_lua_api.ps1 to configure VSCode and LuaLS locally. - Add docs/systems/lua-api-docgen.md explaining configuration, binding documentation, docblocks, quality baselines, and CI checks. - Mention the generated Lua API docs from the README and systems index. - Add Visual Studio project and CMake integration for the generator. CI and regression protection: - Add CI sync validation that runs --generate-lua-api-docs-only and checks docs/lua-api for diffs. - Add tools/check_lua_api_quality.py to compare weak-signature metrics against the committed baseline. - Add tools/check_lua_api_binding_docs.py to require explicit docblocks when new bindings would generate weak signatures. - Exclude generated Lua API outputs from Sonar duplication/noise while keeping the generator and tooling checked. Validation: - Verified docs/lua-api/lua_api.json parses successfully. - Verified luac -p passes for docs/lua-api/lua_api.d.lua. - Verified Lua API binding docs and quality checks pass. - Verified tools/setup_vscode_lua_api.ps1 -WhatIf succeeds. - Verified VSCode and LuaLS resolve generated Canary classes and methods from lua_api.d.lua. - Checked generated docs for stale root-level references, temporary paths, local machine paths, and obvious raw C++ types. This gives Canary a repeatable source-of-truth pipeline for Lua API documentation, editor IntelliSense, and CI enforcement while keeping the generated docs synchronized with the C++ binding layer.
344 lines
9.2 KiB
PowerShell
344 lines
9.2 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Configures VSCode Lua Language Server to use Canary's generated Lua API stubs.
|
|
|
|
.DESCRIPTION
|
|
Run this script from the repository root. It updates the local
|
|
.vscode/settings.json file so LuaLS reads docs/lua-api/lua_api.d.lua through
|
|
the workspace library. If the repository has a .luarc.json file, the script also
|
|
updates that project configuration because LuaLS gives it priority over VSCode
|
|
settings.
|
|
#>
|
|
|
|
[CmdletBinding(SupportsShouldProcess = $true)]
|
|
param(
|
|
[string]$SettingsPath = ".vscode/settings.json",
|
|
[string]$LuaRcPath = ".luarc.json",
|
|
[string]$LuaApiLibraryPath = '${workspaceFolder}/docs/lua-api',
|
|
[string]$LuaRcLibraryPath = "docs/lua-api",
|
|
[string[]]$LuaRcIgnoreDirectories = @(
|
|
".git",
|
|
".vs",
|
|
"artifacts",
|
|
"build",
|
|
"cache",
|
|
"cmake-build-*",
|
|
"database_backup",
|
|
"logs",
|
|
"Release",
|
|
"RelWithDebInfo",
|
|
"Testing",
|
|
"vcproj",
|
|
"vcpkg_installed"
|
|
),
|
|
[int]$MinimumPreloadFileSizeKb = 1000
|
|
)
|
|
|
|
Set-StrictMode -Version 2.0
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
function Remove-JsonComments {
|
|
param([string]$Text)
|
|
|
|
$builder = [System.Text.StringBuilder]::new()
|
|
$inString = $false
|
|
$escape = $false
|
|
$index = 0
|
|
|
|
while ($index -lt $Text.Length) {
|
|
$char = $Text[$index]
|
|
$next = if ($index + 1 -lt $Text.Length) { $Text[$index + 1] } else { [char]0 }
|
|
|
|
if (-not $inString -and $char -eq "/" -and $next -eq "/") {
|
|
while ($index -lt $Text.Length -and $Text[$index] -ne "`n") {
|
|
$index++
|
|
}
|
|
if ($index -lt $Text.Length) {
|
|
[void]$builder.Append($Text[$index])
|
|
}
|
|
$index++
|
|
continue
|
|
}
|
|
|
|
if (-not $inString -and $char -eq "/" -and $next -eq "*") {
|
|
$index += 2
|
|
while ($index + 1 -lt $Text.Length -and -not ($Text[$index] -eq "*" -and $Text[$index + 1] -eq "/")) {
|
|
if ($Text[$index] -eq "`n") {
|
|
[void]$builder.Append("`n")
|
|
}
|
|
$index++
|
|
}
|
|
$index += 2
|
|
continue
|
|
}
|
|
|
|
[void]$builder.Append($char)
|
|
|
|
if ($char -eq '"' -and -not $escape) {
|
|
$inString = -not $inString
|
|
}
|
|
|
|
$escape = $inString -and $char -eq "\" -and -not $escape
|
|
if ($char -ne "\") {
|
|
$escape = $false
|
|
}
|
|
|
|
$index++
|
|
}
|
|
|
|
return $builder.ToString()
|
|
}
|
|
|
|
function ConvertTo-OrderedHashtable {
|
|
param([object]$Value)
|
|
|
|
if ($null -eq $Value) {
|
|
return $null
|
|
}
|
|
|
|
if ($Value -is [string] -or $Value -is [ValueType]) {
|
|
return $Value
|
|
}
|
|
|
|
if ($Value -is [System.Collections.IDictionary]) {
|
|
$hash = [ordered]@{}
|
|
foreach ($key in $Value.Keys) {
|
|
$hash[$key] = ConvertTo-OrderedHashtable $Value[$key]
|
|
}
|
|
return $hash
|
|
}
|
|
|
|
if ($Value -is [System.Collections.IEnumerable]) {
|
|
$items = [System.Collections.Generic.List[object]]::new()
|
|
foreach ($item in $Value) {
|
|
$items.Add((ConvertTo-OrderedHashtable $item))
|
|
}
|
|
return ,$items.ToArray()
|
|
}
|
|
|
|
$properties = @($Value.PSObject.Properties)
|
|
if ($properties.Count -gt 0) {
|
|
$hash = [ordered]@{}
|
|
foreach ($property in $properties) {
|
|
$hash[$property.Name] = ConvertTo-OrderedHashtable $property.Value
|
|
}
|
|
return $hash
|
|
}
|
|
|
|
return $Value
|
|
}
|
|
|
|
function Get-ArraySetting {
|
|
param(
|
|
[System.Collections.IDictionary]$Settings,
|
|
[string]$Key
|
|
)
|
|
|
|
if (-not $Settings.Contains($Key) -or $null -eq $Settings[$Key]) {
|
|
return @()
|
|
}
|
|
|
|
$value = $Settings[$Key]
|
|
if ($value -is [System.Collections.IEnumerable] -and $value -isnot [string]) {
|
|
return @($value)
|
|
}
|
|
|
|
return @($value)
|
|
}
|
|
|
|
function Add-UniqueString {
|
|
param(
|
|
[System.Collections.Generic.List[object]]$List,
|
|
[string]$Value
|
|
)
|
|
|
|
foreach ($entry in $List) {
|
|
if ($entry -is [string] -and $entry -eq $Value) {
|
|
return
|
|
}
|
|
}
|
|
|
|
$List.Add($Value)
|
|
}
|
|
|
|
function Remove-LegacyEntries {
|
|
param(
|
|
[object[]]$Entries,
|
|
[string[]]$LegacyPaths
|
|
)
|
|
|
|
$result = [System.Collections.Generic.List[object]]::new()
|
|
foreach ($entry in $Entries) {
|
|
if ($entry -is [string] -and $LegacyPaths -contains $entry) {
|
|
continue
|
|
}
|
|
$result.Add($entry)
|
|
}
|
|
return ,$result.ToArray()
|
|
}
|
|
|
|
function Resolve-RepositoryPath {
|
|
param(
|
|
[string]$RepoRoot,
|
|
[string]$Path
|
|
)
|
|
|
|
if ([System.IO.Path]::IsPathRooted($Path)) {
|
|
return $Path
|
|
}
|
|
|
|
return Join-Path $RepoRoot $Path
|
|
}
|
|
|
|
function Read-JsonSettings {
|
|
param([string]$Path)
|
|
|
|
if (-not (Test-Path $Path)) {
|
|
return [ordered]@{}
|
|
}
|
|
|
|
$rawJson = Get-Content -Raw -Path $Path
|
|
if ([string]::IsNullOrWhiteSpace($rawJson)) {
|
|
return [ordered]@{}
|
|
}
|
|
|
|
$json = Remove-JsonComments $rawJson
|
|
$json = [regex]::Replace($json, ",(\s*[\]}])", '$1')
|
|
return ConvertTo-OrderedHashtable ($json | ConvertFrom-Json)
|
|
}
|
|
|
|
function Set-LibrarySetting {
|
|
param(
|
|
[System.Collections.IDictionary]$Settings,
|
|
[string]$Key,
|
|
[string]$LibraryPath,
|
|
[string[]]$LegacyPaths
|
|
)
|
|
|
|
$libraryEntries = Remove-LegacyEntries (Get-ArraySetting $Settings $Key) $LegacyPaths
|
|
$library = [System.Collections.Generic.List[object]]::new()
|
|
foreach ($entry in $libraryEntries) {
|
|
$library.Add($entry)
|
|
}
|
|
|
|
Add-UniqueString $library $LibraryPath
|
|
$Settings[$Key] = @($library)
|
|
}
|
|
|
|
function Set-MinimumIntegerSetting {
|
|
param(
|
|
[System.Collections.IDictionary]$Settings,
|
|
[string]$Key,
|
|
[int]$Minimum
|
|
)
|
|
|
|
if (-not $Settings.Contains($Key) -or $null -eq $Settings[$Key]) {
|
|
$Settings[$Key] = $Minimum
|
|
return
|
|
}
|
|
|
|
$current = 0
|
|
if (-not [int]::TryParse([string]$Settings[$Key], [ref]$current) -or $current -lt $Minimum) {
|
|
$Settings[$Key] = $Minimum
|
|
}
|
|
}
|
|
|
|
function Set-StringArraySetting {
|
|
param(
|
|
[System.Collections.IDictionary]$Settings,
|
|
[string]$Key,
|
|
[string[]]$Values
|
|
)
|
|
|
|
$entries = Get-ArraySetting $Settings $Key
|
|
$updated = [System.Collections.Generic.List[object]]::new()
|
|
foreach ($entry in $entries) {
|
|
$updated.Add($entry)
|
|
}
|
|
|
|
foreach ($value in $Values) {
|
|
Add-UniqueString $updated $value
|
|
}
|
|
|
|
$Settings[$Key] = @($updated)
|
|
}
|
|
|
|
function Write-JsonSettings {
|
|
param(
|
|
[System.Collections.IDictionary]$Settings,
|
|
[string]$Path
|
|
)
|
|
|
|
$jsonOutput = ($Settings | ConvertTo-Json -Depth 20)
|
|
$jsonOutput = $jsonOutput.TrimEnd() + [Environment]::NewLine
|
|
Set-Content -Path $Path -Value $jsonOutput -Encoding UTF8
|
|
}
|
|
|
|
$repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..")
|
|
$fullSettingsPath = Resolve-RepositoryPath $repoRoot $SettingsPath
|
|
$settingsDirectory = Split-Path -Parent $fullSettingsPath
|
|
if (-not (Test-Path $settingsDirectory)) {
|
|
New-Item -ItemType Directory -Path $settingsDirectory | Out-Null
|
|
}
|
|
|
|
$settings = Read-JsonSettings $fullSettingsPath
|
|
$legacyLibraryPaths = @(
|
|
"./docs/lua_api.lua",
|
|
"docs/lua_api.lua",
|
|
'${workspaceFolder}/docs/lua_api.lua',
|
|
"./docs/lua-api/lua_api.lua",
|
|
"docs/lua-api/lua_api.lua",
|
|
'${workspaceFolder}/docs/lua-api/lua_api.lua'
|
|
)
|
|
|
|
Set-LibrarySetting $settings "Lua.workspace.library" $LuaApiLibraryPath $legacyLibraryPaths
|
|
|
|
$legacyRuntimePluginPaths = @(
|
|
"./docs/lua_api.json",
|
|
"docs/lua_api.json",
|
|
'${workspaceFolder}/docs/lua_api.json',
|
|
"./docs/lua-api/lua_api.json",
|
|
"docs/lua-api/lua_api.json",
|
|
'${workspaceFolder}/docs/lua-api/lua_api.json'
|
|
)
|
|
|
|
if ($settings.Contains("Lua.runtime.plugin")) {
|
|
$pluginEntries = Remove-LegacyEntries (Get-ArraySetting $settings "Lua.runtime.plugin") $legacyRuntimePluginPaths
|
|
if ($pluginEntries.Count -gt 0) {
|
|
$settings["Lua.runtime.plugin"] = @($pluginEntries)
|
|
} else {
|
|
$settings.Remove("Lua.runtime.plugin")
|
|
}
|
|
}
|
|
|
|
if (-not $settings.Contains("Lua.workspace.checkThirdParty")) {
|
|
$settings["Lua.workspace.checkThirdParty"] = $false
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess($fullSettingsPath, "Update VSCode LuaLS settings")) {
|
|
Write-JsonSettings $settings $fullSettingsPath
|
|
}
|
|
|
|
$updatedLuaRc = $false
|
|
$fullLuaRcPath = Resolve-RepositoryPath $repoRoot $LuaRcPath
|
|
if (Test-Path $fullLuaRcPath) {
|
|
$luaRcSettings = Read-JsonSettings $fullLuaRcPath
|
|
Set-LibrarySetting $luaRcSettings "workspace.library" $LuaRcLibraryPath $legacyLibraryPaths
|
|
Set-MinimumIntegerSetting $luaRcSettings "workspace.preloadFileSize" $MinimumPreloadFileSizeKb
|
|
Set-StringArraySetting $luaRcSettings "workspace.ignoreDir" $LuaRcIgnoreDirectories
|
|
if (-not $luaRcSettings.Contains("workspace.checkThirdParty")) {
|
|
$luaRcSettings["workspace.checkThirdParty"] = $false
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess($fullLuaRcPath, "Update LuaLS project settings")) {
|
|
Write-JsonSettings $luaRcSettings $fullLuaRcPath
|
|
}
|
|
$updatedLuaRc = $true
|
|
}
|
|
|
|
Write-Host "VSCode Lua API library configured: $LuaApiLibraryPath"
|
|
Write-Host "Settings file: $SettingsPath"
|
|
if ($updatedLuaRc) {
|
|
Write-Host "LuaLS project config updated: $LuaRcPath"
|
|
}
|
|
Write-Host "Install the VSCode Lua extension if it is not installed."
|