diff --git a/luacs-docs/.editorconfig b/luacs-docs/.editorconfig index 74e06f2a2..b07ab6eb8 100644 --- a/luacs-docs/.editorconfig +++ b/luacs-docs/.editorconfig @@ -3,7 +3,7 @@ end_of_line = lf [*.cs] indent_style = space -indent_size = 2 +indent_size = 4 csharp_prefer_braces = when_multiline:warning csharp_indent_case_contents_when_block = false # One True Brace style diff --git a/luacs-docs/lua/scripts/LuaDocsGenerator/Program.cs b/luacs-docs/lua/scripts/LuaDocsGenerator/Program.cs index d46105a3a..3b29b0cf4 100644 --- a/luacs-docs/lua/scripts/LuaDocsGenerator/Program.cs +++ b/luacs-docs/lua/scripts/LuaDocsGenerator/Program.cs @@ -6,190 +6,226 @@ using System.Text.RegularExpressions; using Barotrauma; using Barotrauma.Networking; -string NormalizeGenericTypeName(string s) { - var idx = s.LastIndexOf('`'); +string NormalizeGenericTypeName(string s) +{ + var idx = s.LastIndexOf('`'); - if (idx != -1) { - return s[..idx]; - } + if (idx != -1) + { + return s[..idx]; + } - return s; + return s; } -string TypeToString(Type type, bool useLuaTypes = true) { - // Return "T" for unresolved type params - if (type.IsGenericParameter) { - return type.Name; - } - - var genericType = type.IsGenericType - ? type.GetGenericTypeDefinition() - : null; - - if (type == typeof(bool)) { - return "bool"; - } - - if (type == typeof(string)) { - return "string"; - } - - if (useLuaTypes) { - if (type == typeof(sbyte) - || type == typeof(byte) - || type == typeof(short) - || type == typeof(ushort) - || type == typeof(int) - || type == typeof(uint) - || type == typeof(long) - || type == typeof(ulong) - || type == typeof(float) - || type == typeof(double)) { - return "number"; +string TypeToString(Type type, bool useLuaTypes = true) +{ + // Return "T" for unresolved type params + if (type.IsGenericParameter) + { + return type.Name; } - if (genericType == typeof(List<>) - || genericType == typeof(Dictionary<,>)) { - return "table"; + var genericType = type.IsGenericType + ? type.GetGenericTypeDefinition() + : null; + + if (type == typeof(bool)) + { + return "bool"; } - if (genericType == typeof(Action<,>) - || genericType == typeof(Func<,>)) { - return "function"; + if (type == typeof(string)) + { + return "string"; } - } - var nsToRemove = new[] { + if (useLuaTypes) + { + if (type == typeof(sbyte) + || type == typeof(byte) + || type == typeof(short) + || type == typeof(ushort) + || type == typeof(int) + || type == typeof(uint) + || type == typeof(long) + || type == typeof(ulong) + || type == typeof(float) + || type == typeof(double)) + { + return "number"; + } + + if (genericType == typeof(List<>) + || genericType == typeof(Dictionary<,>)) + { + return "table"; + } + + if (genericType == typeof(Action<,>) + || genericType == typeof(Func<,>)) + { + return "function"; + } + } + + var nsToRemove = new[] { "Barotrauma", "System", "System.Collections", "System.Collections.Generic", }; - string Namespaced(string typeName) { - if (type.Namespace == null) { - return typeName; - } - - // Full namespace match - if (nsToRemove.Contains(type.Namespace)) { - return typeName; - } - - // Partial namespace match - foreach (var ns in nsToRemove) { - if (ns == type.Namespace) { - return typeName; - } - - if (type.Namespace.StartsWith(ns + ".")) { - var shortNs = type.Namespace.Remove(0, ns.Length + 1); - return $"{shortNs}.{typeName}"; - } - } - - return $"{type.Namespace}.{typeName}"; - } - - string Impl(string? ns) { - if (type.IsGenericType) { - var genericTypeDef = type.GetGenericTypeDefinition(); - var genericTypeName = NormalizeGenericTypeName(genericTypeDef.Name); - - var genericArgs = type.GetGenericArguments(); - - // Use the `T?` notation instead of Nullable - if (genericTypeDef == typeof(Nullable<>)) { - // ldoc supports the "?string" notation, which expands to "?|nil|string" - if (useLuaTypes) { - return Namespaced("?" + TypeToString(genericArgs[0], useLuaTypes: false)); - } else { - return Namespaced(TypeToString(genericArgs[0], useLuaTypes: false) + "?"); + string Namespaced(string typeName) + { + if (type.Namespace == null) + { + return typeName; } - } - var sb = new StringBuilder(); - sb.Append(genericTypeName); - sb.Append("<"); - foreach (var genericArgType in genericArgs) { - sb.Append(TypeToString(genericArgType, useLuaTypes: false)); - sb.Append(","); - } - // Remove the last separator - sb.Length--; - sb.Append(">"); + // Full namespace match + if (nsToRemove.Contains(type.Namespace)) + { + return typeName; + } - return Namespaced(sb.ToString()); + // Partial namespace match + foreach (var ns in nsToRemove) + { + if (ns == type.Namespace) + { + return typeName; + } + + if (type.Namespace.StartsWith(ns + ".")) + { + var shortNs = type.Namespace.Remove(0, ns.Length + 1); + return $"{shortNs}.{typeName}"; + } + } + + return $"{type.Namespace}.{typeName}"; } - return Namespaced(type.Name); - } + string Impl(string? ns) + { + if (type.IsGenericType) + { + var genericTypeDef = type.GetGenericTypeDefinition(); + var genericTypeName = NormalizeGenericTypeName(genericTypeDef.Name); - return Impl(type.Namespace); -} + var genericArgs = type.GetGenericArguments(); -static (bool Success, string Output, string Error) TryRunGitCommand(string args) { - static string? GetGitBinary() { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - return Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process) - ?.Split(';') - .Select(x => Path.Join(x, "git.exe")) - .FirstOrDefault(File.Exists); - } else { - return Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process) - ?.Split(':') - .Select(x => Path.Join(x, "git")) - .FirstOrDefault(File.Exists); + // Use the `T?` notation instead of Nullable + if (genericTypeDef == typeof(Nullable<>)) + { + // ldoc supports the "?string" notation, which expands to "?|nil|string" + if (useLuaTypes) + { + return Namespaced("?" + TypeToString(genericArgs[0], useLuaTypes: false)); + } + else + { + return Namespaced(TypeToString(genericArgs[0], useLuaTypes: false) + "?"); + } + } + + var sb = new StringBuilder(); + sb.Append(genericTypeName); + sb.Append("<"); + foreach (var genericArgType in genericArgs) + { + sb.Append(TypeToString(genericArgType, useLuaTypes: false)); + sb.Append(","); + } + // Remove the last separator + sb.Length--; + sb.Append(">"); + + return Namespaced(sb.ToString()); + } + + return Namespaced(type.Name); } - } - var gitBinary = GetGitBinary(); - if (gitBinary == null) { - throw new InvalidOperationException("Failed to find git binary in PATH"); - } - - using var process = Process.Start(new ProcessStartInfo(gitBinary, args) { - WindowStyle = ProcessWindowStyle.Hidden, - CreateNoWindow = true, - UseShellExecute = false, - RedirectStandardInput = true, - RedirectStandardError = true, - RedirectStandardOutput = true, - }); - - if (process == null) - throw new InvalidOperationException($"Failed to run git command: {args}"); - - process.Start(); - - var stdOut = process.StandardOutput.ReadToEndAsync(); - var stdErr = process.StandardError.ReadToEndAsync(); - Task.WhenAll(stdOut, stdErr).GetAwaiter().GetResult(); - process.WaitForExit(); - - return (process.ExitCode == 0, stdOut.Result.TrimEnd('\r', '\n'), stdErr.Result); + return Impl(type.Namespace); } -var gitDir = (new Func(() => { - var (success, gitDir, error) = TryRunGitCommand("rev-parse --show-toplevel"); - if (!success) { - throw new InvalidDataException($"Failed to determine the root of the git repo: {error}"); - } +static (bool Success, string Output, string Error) TryRunGitCommand(string args) +{ + static string? GetGitBinary() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process) + ?.Split(';') + .Select(x => Path.Join(x, "git.exe")) + .FirstOrDefault(File.Exists); + } + else + { + return Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process) + ?.Split(':') + .Select(x => Path.Join(x, "git")) + .FirstOrDefault(File.Exists); + } + } - return gitDir; + var gitBinary = GetGitBinary(); + if (gitBinary == null) + { + throw new InvalidOperationException("Failed to find git binary in PATH"); + } + + using var process = Process.Start(new ProcessStartInfo(gitBinary, args) + { + WindowStyle = ProcessWindowStyle.Hidden, + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardInput = true, + RedirectStandardError = true, + RedirectStandardOutput = true, + }); + + if (process == null) + throw new InvalidOperationException($"Failed to run git command: {args}"); + + process.Start(); + + var stdOut = process.StandardOutput.ReadToEndAsync(); + var stdErr = process.StandardError.ReadToEndAsync(); + Task.WhenAll(stdOut, stdErr).GetAwaiter().GetResult(); + process.WaitForExit(); + + return (process.ExitCode == 0, stdOut.Result.TrimEnd('\r', '\n'), stdErr.Result); +} + +var gitDir = (new Func(() => +{ + var (success, gitDir, error) = TryRunGitCommand("rev-parse --show-toplevel"); + if (!success) + { + throw new InvalidDataException($"Failed to determine the root of the git repo: {error}"); + } + + return gitDir; }))(); -void GenerateDocsImpl(Type type, string baseFile, string outFile, string? categoryName = null) { - categoryName ??= type.Name; - var sb = new StringBuilder(); +void GenerateDocsImpl(Type type, string baseFile, string outFile, string? categoryName = null) +{ + categoryName ??= type.Name; + var sb = new StringBuilder(); - Console.WriteLine($"Generating docs for {type}"); + Console.WriteLine($"Generating docs for {type}"); - string baseLuaText; - try { - baseLuaText = File.ReadAllText(baseFile); - } catch (FileNotFoundException) { - baseLuaText = @$"-- luacheck: ignore 111 + string baseLuaText; + try + { + baseLuaText = File.ReadAllText(baseFile); + } + catch (FileNotFoundException) + { + baseLuaText = @$"-- luacheck: ignore 111 --[[-- {type.FullName} @@ -198,148 +234,172 @@ void GenerateDocsImpl(Type type, string baseFile, string outFile, string? catego -- @pragma nostrip local {type.Name} = {{}}".ReplaceLineEndings("\n"); - File.WriteAllText(baseFile, baseLuaText); - } - - var removeTagPattern = new Regex("^-- @remove (.*)$", RegexOptions.Multiline); - var removed = new HashSet(); - var matches = removeTagPattern.Matches(baseLuaText); - - foreach (var match in matches.Cast()) { - removed.Add(match.Value); - } - - sb.Append(baseLuaText); - sb.AppendLine(); - sb.AppendLine(); - - var members = type.GetMembers(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); - foreach (var member in members) { - static string EscapeName(string n) { - return n switch { - "end" => "endparam", - _ => n - }; + File.WriteAllText(baseFile, baseLuaText); } - switch (member.MemberType) { - case MemberTypes.Method: { - var method = (MethodInfo)member; + var removeTagPattern = new Regex("^-- @remove (.*)$", RegexOptions.Multiline); + var removed = new HashSet(); + var matches = removeTagPattern.Matches(baseLuaText); - // Exclude property getters/setters - if (method.IsSpecialName) { - continue; - } - - var paramNames = new StringBuilder(); - foreach (var parameter in method.GetParameters()) { - paramNames.Append(EscapeName(parameter.Name!)); - paramNames.Append(", "); - } - if (paramNames.Length > 0) { - // Remove the last separator - paramNames.Length -= 2; - } - - string functionDecoration; - if (method.IsStatic) { - functionDecoration = $"function {type.Name}.{method.Name}({paramNames}) end"; - } else { - functionDecoration = $"function {method.Name}({paramNames}) end"; - } - - if (removed.Contains(functionDecoration)) { - continue; - } - - Console.WriteLine($" - METHOD: {method}"); - - sb.AppendLine($"--- {method.Name}"); - sb.AppendLine("-- @realm shared"); - - foreach (var parameter in method.GetParameters()) { - sb.AppendLine($"-- @tparam {TypeToString(parameter.ParameterType)} {EscapeName(parameter.Name!)}"); - } - - if (method.ReturnType != typeof(void)) { - sb.AppendLine($"-- @treturn {TypeToString(method.ReturnType)}"); - } - - sb.AppendLine(functionDecoration); - sb.AppendLine(); - break; - } - - case MemberTypes.Field: { - var field = (FieldInfo)member; - - var name = EscapeName(field.Name); - var returnName = TypeToString(field.FieldType); - - if (field.IsStatic) { - name = type.Name + "." + field.Name; - } - - if (removed.Contains(name)) { - continue; - } - - Console.WriteLine($" - FIELD: {name}"); - - sb.AppendLine("---"); - sb.Append("-- "); - sb.Append(name); - sb.AppendLine($", field of type {returnName}"); - sb.AppendLine("-- @realm shared"); - sb.AppendLine($"-- @field {name}"); - - sb.AppendLine(); - break; - } - - case MemberTypes.Property: { - var property = (PropertyInfo)member; - - var name = EscapeName(property.Name); - var returnName = TypeToString(property.PropertyType); - - if (property.GetGetMethod()?.IsStatic == true || property.GetSetMethod()?.IsStatic == true) { - name = type.Name + "." + property.Name; - } - - if (removed.Contains(name)) { - continue; - } - - Console.WriteLine($" - PROPERTY: {name}"); - - sb.AppendLine("---"); - sb.Append("-- "); - sb.Append(name); - sb.AppendLine($", field of type {returnName}"); - sb.AppendLine("-- @realm shared"); - sb.AppendLine($"-- @field {name}"); - - sb.AppendLine(); - break; - } + foreach (var match in matches.Cast()) + { + removed.Add(match.Value); } - } - new FileInfo(outFile).Directory.Create(); - File.WriteAllText(outFile, sb.ToString()); + sb.Append(baseLuaText); + sb.AppendLine(); + sb.AppendLine(); + + var members = type.GetMembers(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); + foreach (var member in members) + { + static string EscapeName(string n) + { + return n switch + { + "end" => "endparam", + _ => n + }; + } + + switch (member.MemberType) + { + case MemberTypes.Method: + { + var method = (MethodInfo)member; + + // Exclude property getters/setters + if (method.IsSpecialName) + { + continue; + } + + var paramNames = new StringBuilder(); + foreach (var parameter in method.GetParameters()) + { + paramNames.Append(EscapeName(parameter.Name!)); + paramNames.Append(", "); + } + if (paramNames.Length > 0) + { + // Remove the last separator + paramNames.Length -= 2; + } + + string functionDecoration; + if (method.IsStatic) + { + functionDecoration = $"function {type.Name}.{method.Name}({paramNames}) end"; + } + else + { + functionDecoration = $"function {method.Name}({paramNames}) end"; + } + + if (removed.Contains(functionDecoration)) + { + continue; + } + + Console.WriteLine($" - METHOD: {method}"); + + sb.AppendLine($"--- {method.Name}"); + sb.AppendLine("-- @realm shared"); + + foreach (var parameter in method.GetParameters()) + { + sb.AppendLine($"-- @tparam {TypeToString(parameter.ParameterType)} {EscapeName(parameter.Name!)}"); + } + + if (method.ReturnType != typeof(void)) + { + sb.AppendLine($"-- @treturn {TypeToString(method.ReturnType)}"); + } + + sb.AppendLine(functionDecoration); + sb.AppendLine(); + break; + } + + case MemberTypes.Field: + { + var field = (FieldInfo)member; + + var name = EscapeName(field.Name); + var returnName = TypeToString(field.FieldType); + + if (field.IsStatic) + { + name = type.Name + "." + field.Name; + } + + if (removed.Contains(name)) + { + continue; + } + + Console.WriteLine($" - FIELD: {name}"); + + sb.AppendLine("---"); + sb.Append("-- "); + sb.Append(name); + sb.AppendLine($", field of type {returnName}"); + sb.AppendLine("-- @realm shared"); + sb.AppendLine($"-- @field {name}"); + + sb.AppendLine(); + break; + } + + case MemberTypes.Property: + { + var property = (PropertyInfo)member; + + var name = EscapeName(property.Name); + var returnName = TypeToString(property.PropertyType); + + if (property.GetGetMethod()?.IsStatic == true || property.GetSetMethod()?.IsStatic == true) + { + name = type.Name + "." + property.Name; + } + + if (removed.Contains(name)) + { + continue; + } + + Console.WriteLine($" - PROPERTY: {name}"); + + sb.AppendLine("---"); + sb.Append("-- "); + sb.Append(name); + sb.AppendLine($", field of type {returnName}"); + sb.AppendLine("-- @realm shared"); + sb.AppendLine($"-- @field {name}"); + + sb.AppendLine(); + break; + } + } + } + + new FileInfo(outFile).Directory.Create(); + File.WriteAllText(outFile, sb.ToString()); } var basePath = $"{gitDir}/luacs-docs/lua"; var generatedDir = $"{basePath}/lua/generated"; var baseLuaDir = $"{basePath}/baseluadocs"; -void GenerateDocs(Type type, string file, string? categoryName = null) { - GenerateDocsImpl(type, $"{baseLuaDir}/{file}", $"{generatedDir}/{file}", categoryName); +void GenerateDocs(Type type, string file, string? categoryName = null) +{ + GenerateDocsImpl(type, $"{baseLuaDir}/{file}", $"{generatedDir}/{file}", categoryName); } -try { - Directory.Delete(generatedDir, true); -} catch (DirectoryNotFoundException) { } +try +{ + Directory.Delete(generatedDir, true); +} +catch (DirectoryNotFoundException) { } Directory.CreateDirectory(generatedDir); Directory.CreateDirectory(baseLuaDir);