diff --git a/.github/workflows/box2d-docs-github.yml b/.github/workflows/box2d-docs-github.yml new file mode 100644 index 00000000..65a925e0 --- /dev/null +++ b/.github/workflows/box2d-docs-github.yml @@ -0,0 +1,44 @@ +# More GitHub Actions for Azure: https://github.com/Azure/actions + +name: Build Box2D Docs for GitHub Pages + +env: + COMMON_SETTINGS_PATH: docs/docfx.json + +on: + workflow_dispatch: + +jobs: + publish-docs: + runs-on: windows-latest + + steps: + - name: .NET SDK Setup + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 9.x + + - name: Checkout Box2D.NET + uses: actions/checkout@v4 + + - name: Run Box2D.NET.CommentConverter.csproj + run: dotnet run --project tools/Box2D.NET.CommentConverter/Box2D.NET.CommentConverter.csproj --configuration Release + + - name: Install DocFX + # This installs the latest version of DocFX and may introduce breaking changes + # run: dotnet tool update -g docfx + # This installs a specific, tested version of DocFX. + run: dotnet tool update -g docfx --version 2.78.2 + + - name: Build Box2D.NET API Docs + run: docfx metadata ${{ env.COMMON_SETTINGS_PATH }} + + - name: Build Box2D.NET Docs + run: docfx build ${{ env.COMMON_SETTINGS_PATH }} + + - name: Deploy + uses: peaceiris/actions-gh-pages@v4.0.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: docs/_site + publish_branch: gh-pages diff --git a/.gitignore b/.gitignore index e900518a..d50a7dd6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -## Ignore Visual Studio temporary files, build results, and +## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore @@ -399,3 +399,7 @@ FodyWeavers.xsd .idea/ imgui.ini settings.ini + +# DocFX API Generated Pages +docs/api/* +docs/_site/* \ No newline at end of file diff --git a/docs/docfx.json b/docs/docfx.json new file mode 100644 index 00000000..1036bbd8 --- /dev/null +++ b/docs/docfx.json @@ -0,0 +1,63 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/docfx/main/schemas/docfx.schema.json", + "metadata": [ + { + "src": [ + { + "src": "..", + "files": [ + "src/Box2D.NET/Box2D.NET.csproj" + ] + } + ], + "dest": "api" + } + ], + "build": { + "content": [ + { + "files": [ + "**/*.{md,yml}" + ], + "exclude": [ + "_site/**" + ] + } + ], + "resource": [ + { + "files": [ + "favicon-32x32.png", + "favicon-16x16.png", + "images/**" + ] + } + ], + "output": "_site", + "template": [ + "default", + "modern", + "template" + ], + "fileMetadata": { + "_appTitle": { + "api/**/*.md": "Box2D API", + "api/**/*.yml": "Box2D API" + } + }, + "globalMetadata": { + "_appName": "", + "_appTitle": "Box2D Docs", + "_appLogoPath": "images/box2d_logo.svg", + "_appFaviconPath": "favicon-32x32.png", + "_enableSearch": true, + "pdf": false, + "_gitContribute": { + "repo": "https://github.com/ikpil/Box2D.NET", + "branch": "master" + }, + "_gitUrlPattern": "github", + "_gitRepo": "https://github.com/ikpil/Box2D.NET" + } + } +} \ No newline at end of file diff --git a/docs/docs/index.md b/docs/docs/index.md new file mode 100644 index 00000000..4e7dc4ff --- /dev/null +++ b/docs/docs/index.md @@ -0,0 +1,4 @@ +--- +_disableToc: false +--- +# Box2D.NET Docs \ No newline at end of file diff --git a/docs/docs/toc.yml b/docs/docs/toc.yml new file mode 100644 index 00000000..093b7ebb --- /dev/null +++ b/docs/docs/toc.yml @@ -0,0 +1,8 @@ +- name: 📚 Docs + href: index.md +- name: Original Box2D + href: https://box2d.org/ +- name: Overview + href: https://box2d.org/documentation/index.html +- name: FAQ + href: https://box2d.org/documentation/md_faq.html \ No newline at end of file diff --git a/docs/favicon-16x16.png b/docs/favicon-16x16.png new file mode 100644 index 00000000..2ab65d23 Binary files /dev/null and b/docs/favicon-16x16.png differ diff --git a/docs/favicon-32x32.png b/docs/favicon-32x32.png new file mode 100644 index 00000000..31d602e7 Binary files /dev/null and b/docs/favicon-32x32.png differ diff --git a/docs/images/box2d_logo.svg b/docs/images/box2d_logo.svg new file mode 100644 index 00000000..f002689d --- /dev/null +++ b/docs/images/box2d_logo.svg @@ -0,0 +1,126 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..3ebcccbc --- /dev/null +++ b/docs/index.md @@ -0,0 +1,4 @@ +--- +_disableToc: false +--- +[!INCLUDE [readme](../readme.md)] \ No newline at end of file diff --git a/docs/template/public/main.css b/docs/template/public/main.css new file mode 100644 index 00000000..eb3143e5 --- /dev/null +++ b/docs/template/public/main.css @@ -0,0 +1,5 @@ +/* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. */ + +.navbar-brand #logo { + height: 56px; +} \ No newline at end of file diff --git a/docs/template/public/main.js b/docs/template/public/main.js new file mode 100644 index 00000000..9bbb8f99 --- /dev/null +++ b/docs/template/public/main.js @@ -0,0 +1,17 @@ +const app = { + languageDropdownCreated: false, + iconLinks: [ + { + icon: 'github', + href: 'https://github.com/ikpil/Box2D.NET', + title: 'GitHub' + }, + { + icon: 'discord', + href: 'https://github.com/ikpil/Box2D.NET', + title: 'Discord' + } + ] +}; + +export default app; \ No newline at end of file diff --git a/docs/toc.yml b/docs/toc.yml new file mode 100644 index 00000000..b2a2f936 --- /dev/null +++ b/docs/toc.yml @@ -0,0 +1,4 @@ +- name: 📚 Docs + href: docs/index.md +- name: 🔧 API + href: api/ \ No newline at end of file diff --git a/tools/Box2D.NET.CommentConverter/Box2D.NET.CommentConverter.csproj b/tools/Box2D.NET.CommentConverter/Box2D.NET.CommentConverter.csproj new file mode 100644 index 00000000..fd4bd08d --- /dev/null +++ b/tools/Box2D.NET.CommentConverter/Box2D.NET.CommentConverter.csproj @@ -0,0 +1,10 @@ + + + + Exe + net9.0 + enable + enable + + + diff --git a/tools/Box2D.NET.CommentConverter/Program.cs b/tools/Box2D.NET.CommentConverter/Program.cs new file mode 100644 index 00000000..50504f6d --- /dev/null +++ b/tools/Box2D.NET.CommentConverter/Program.cs @@ -0,0 +1,159 @@ +const string DoubleSlashCommentPrefix = "// "; +const string TripleSlashCommentPrefix = "///"; +const string SummaryStart = "/// "; +const string SummaryEnd = "/// "; +const string FileFilter = "*.cs"; + +var commentStarts = new HashSet { DoubleSlashCommentPrefix, TripleSlashCommentPrefix }; +var repoRoot = Directory.GetCurrentDirectory(); + +#if DEBUG +repoRoot = Path.GetFullPath(Path.Combine(repoRoot, "..", "..", "..", "..", "..")); +#endif + +var folderPath = Path.Combine(repoRoot, "src", "Box2D.NET"); + +// Tests: B2BodySim, B2World, B2WorldId +var files = Directory.GetFiles(folderPath, FileFilter, SearchOption.AllDirectories) + //.Where(w => w.Contains("B2PrismaticJointDef")) + //.Take(50) + .ToList(); + +Console.WriteLine($"Found {files.Count} C# files in the folder: {folderPath}"); + +foreach (var filePath in files) +{ + await ProcessFileAsync(filePath); +} + +Console.WriteLine("*** Processing completed ***"); + +async Task ProcessFileAsync(string filePath) +{ + Console.WriteLine($"\nProcessing file: {filePath}"); + + var content = await File.ReadAllTextAsync(filePath); + var lines = content.Split(["\r\n", "\n"], StringSplitOptions.None).ToList(); + var commentLineIndexes = ExtractCommentLineIndexes(lines); + + RemovePreNamespaceComments(lines, commentLineIndexes); + + if (commentLineIndexes.Count == 0) + { + Console.WriteLine("No comment lines found in the file."); + return; + } + + var commentBlocks = ExtractCommentBlocks(lines, commentLineIndexes); + + if (commentBlocks.Count == 0) + { + Console.WriteLine("No comment blocks found."); + return; + } + + ConvertCommentsToTripleSlash(lines, commentBlocks); + + WrapCommentsWithSummaryTags(lines, commentBlocks); + + //File.WriteAllText(filePath.Replace(".cs", ".xmlcomments.cs"), string.Join(Environment.NewLine, lines)); + File.WriteAllText(filePath, string.Join(Environment.NewLine, lines)); + + Console.WriteLine($"Output written to: {filePath}"); +} + +static void RemovePreNamespaceComments(List lines, List commentLineIndexes) +{ + var namespaceLineIndex = lines.FindIndex(line => line.TrimStart().StartsWith("namespace ")); + + commentLineIndexes.RemoveAll(index => index < namespaceLineIndex); +} + +static List ExtractCommentBlocks(List lines, List commentLineIndexes) +{ + var commentBlocks = new List(); + var startIndex = commentLineIndexes[0]; + var endIndex = commentLineIndexes[0]; + + if (commentLineIndexes.Count == 1) + { + AddBlockIfFollowedByPublic(startIndex, endIndex); + + return commentBlocks; + } + + for (int i = 1; i < commentLineIndexes.Count; i++) + { + var nextIndex = commentLineIndexes[i]; + + if (nextIndex - endIndex != 1) + { + AddBlockIfFollowedByPublic(startIndex, endIndex); + startIndex = nextIndex; + } + + endIndex = nextIndex; + + if (i == commentLineIndexes.Count - 1) + { + AddBlockIfFollowedByPublic(startIndex, endIndex); + } + } + + return commentBlocks; + + void AddBlockIfFollowedByPublic(int startIndex, int endIndex) + { + if (endIndex + 1 < lines.Count && lines[endIndex + 1].Contains("public")) + { + commentBlocks.Add(new CommentBlock(startIndex, endIndex)); + } + } +} + +List ExtractCommentLineIndexes(List lines) +{ + return lines + .Select((line, index) => (line, index)) + .Where(item => commentStarts.Any(commentStart => + item.line.TrimStart().StartsWith(commentStart))) + .Select(item => item.index) + .ToList(); +} + +static void ConvertCommentsToTripleSlash(List lines, List commentBlocks) +{ + foreach (var block in commentBlocks) + { + Console.WriteLine($"Comment block from {block.StartIndex + 1} to {block.EndIndex + 1} (length: {block.Length})"); + + for (int i = 0; i < block.Length; i++) + { + int lineIndex = block.StartIndex + i; + + if (!lines[lineIndex].TrimStart().StartsWith(TripleSlashCommentPrefix)) + { + lines[lineIndex] = lines[lineIndex].Replace(DoubleSlashCommentPrefix, "/// "); + } + } + } +} + +void WrapCommentsWithSummaryTags(List lines, List commentBlocks) +{ + for (int i = commentBlocks.Count - 1; i >= 0; i--) + { + var block = commentBlocks[i]; + var indentation = GetIndentation(lines[block.StartIndex]); + + lines.Insert(block.EndIndex + 1, $"{indentation}{SummaryEnd}"); + lines.Insert(block.StartIndex, $"{indentation}{SummaryStart}"); + } + + string GetIndentation(string line) => new(' ', line.Length - line.TrimStart().Length); +} + +record class CommentBlock(int StartIndex, int EndIndex) +{ + public int Length => EndIndex - StartIndex + 1; +}