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.
120 lines
3.6 KiB
Python
120 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
|
import argparse
|
|
import json
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
DOC_PATH = Path("docs/lua-api/lua_api.json")
|
|
BASELINE_PATH = Path("docs/lua-api/lua_api_quality_baseline.json")
|
|
METRIC_KEYS = (
|
|
"param_any",
|
|
"param_argn",
|
|
"param_vararg",
|
|
"return_any",
|
|
"return_plain_table",
|
|
)
|
|
PARAMETER_METRICS = (
|
|
("param_any", re.compile(r":\s*any\b")),
|
|
("param_argn", re.compile(r"\barg\d+\b")),
|
|
)
|
|
RETURN_METRICS = {
|
|
"any": "return_any",
|
|
"table": "return_plain_table",
|
|
}
|
|
|
|
|
|
def iter_functions(data):
|
|
for class_name, methods in data.get("classes", {}).items():
|
|
for method in methods:
|
|
yield f"{class_name}.{method.get('name', '')}", method
|
|
|
|
for function in data.get("globals", []):
|
|
yield function.get("name", ""), function
|
|
|
|
|
|
def collect_metrics(data):
|
|
metrics = dict.fromkeys(METRIC_KEYS, 0)
|
|
examples = {key: [] for key in METRIC_KEYS}
|
|
|
|
for function_name, function in iter_functions(data):
|
|
collect_parameter_metrics(function_name, function.get("params", []), metrics, examples)
|
|
collect_return_metrics(function_name, function.get("return", ""), metrics, examples)
|
|
|
|
return metrics, examples
|
|
|
|
|
|
def collect_parameter_metrics(function_name, parameters, metrics, examples):
|
|
for parameter in parameters:
|
|
for metric_key, pattern in PARAMETER_METRICS:
|
|
if pattern.search(parameter):
|
|
increment_metric(metric_key, metrics, examples, function_name, parameter)
|
|
if parameter.startswith("..."):
|
|
increment_metric("param_vararg", metrics, examples, function_name, parameter)
|
|
|
|
|
|
def collect_return_metrics(function_name, return_type, metrics, examples):
|
|
metric_key = RETURN_METRICS.get(return_type)
|
|
if metric_key:
|
|
increment_metric(metric_key, metrics, examples, function_name, return_type)
|
|
|
|
|
|
def increment_metric(metric_key, metrics, examples, function_name, value):
|
|
metrics[metric_key] += 1
|
|
append_example(examples[metric_key], function_name, value)
|
|
|
|
|
|
def append_example(examples, function_name, value):
|
|
if len(examples) < 5:
|
|
examples.append(f"{function_name}: {value}")
|
|
|
|
|
|
def load_json(path):
|
|
with path.open("r", encoding="utf-8") as file:
|
|
return json.load(file)
|
|
|
|
|
|
def write_json(path, data):
|
|
path.write_text(json.dumps(data, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Check generated Lua API documentation quality metrics.")
|
|
parser.add_argument(
|
|
"--update-baseline",
|
|
action="store_true",
|
|
help="write the current metrics to docs/lua-api/lua_api_quality_baseline.json",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
current, examples = collect_metrics(load_json(DOC_PATH))
|
|
if args.update_baseline:
|
|
write_json(BASELINE_PATH, current)
|
|
print(f"Updated Lua API quality baseline: {current}")
|
|
return 0
|
|
|
|
if not BASELINE_PATH.exists():
|
|
print(f"::error::{BASELINE_PATH} is missing. Run tools/check_lua_api_quality.py --update-baseline and commit it.")
|
|
return 1
|
|
|
|
baseline = load_json(BASELINE_PATH)
|
|
failed = False
|
|
for key in METRIC_KEYS:
|
|
value = current[key]
|
|
allowed = baseline.get(key, value)
|
|
if value > allowed:
|
|
print(f"::error::Lua API quality regression: {key} increased from {allowed} to {value}")
|
|
for example in examples[key]:
|
|
print(f" {example}")
|
|
failed = True
|
|
|
|
if failed:
|
|
return 1
|
|
|
|
print(f"Lua API quality check passed: {current}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|