diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index a873b6b..be28e25 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "docfx": { - "version": "2.67.5", + "version": "2.78.3", "commands": [ "docfx" ] diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..16e1a86 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,201 @@ +# editorconfig.org + +# top-most EditorConfig file +root = true + +# Default settings: +# A newline ending every file +# Use 4 spaces as indentation +[*] +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +# Specify UTF-8 without byte-order mark +[*.{csproj,locproj,nativeproj,proj,resx,slnx,vbproj}] +charset = utf-8 + +# Generated code +[*{_AssemblyInfo.cs,.notsupported.cs,AsmOffsets.cs}] +generated_code = true + +# C# files +[*.cs] +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = false +csharp_indent_switch_labels = true +csharp_indent_labels = one_less_than_current + +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async:suggestion + +# avoid this. unless absolutely necessary +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# Types: use keywords instead of BCL types, and permit var only when the type is clear +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = false:none +csharp_style_var_elsewhere = false:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# name all constant fields using PascalCase +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.required_modifiers = const +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# static fields should have s_ prefix +dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion +dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields +dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.required_modifiers = static +dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected +dotnet_naming_style.static_prefix_style.required_prefix = s_ +dotnet_naming_style.static_prefix_style.capitalization = camel_case + +# internal and private fields should be _camelCase +dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion +dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields +dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style +dotnet_naming_symbols.private_internal_fields.applicable_kinds = field +dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal +dotnet_naming_style.camel_case_underscore_style.required_prefix = _ +dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case + +# Code style defaults +csharp_using_directive_placement = outside_namespace:suggestion +dotnet_sort_system_directives_first = true +csharp_prefer_braces = true:silent +csharp_preserve_single_line_blocks = true:none +csharp_preserve_single_line_statements = false:none +csharp_prefer_static_local_function = true:suggestion +csharp_prefer_simple_using_statement = false:none +csharp_style_prefer_switch_expression = true:suggestion +dotnet_style_readonly_field = true:suggestion + +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_collection_expression = when_types_exactly_match +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +csharp_prefer_simple_default_expression = true:suggestion + +# Expression-bodied members +csharp_style_expression_bodied_methods = true:silent +csharp_style_expression_bodied_constructors = true:silent +csharp_style_expression_bodied_operators = true:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = true:silent + +# Pattern matching +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion + +# Null checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Other features +csharp_style_prefer_index_operator = false:none +csharp_style_prefer_range_operator = false:none +csharp_style_pattern_local_over_anonymous_function = false:none + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = do_not_ignore +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# License header +file_header_template = + +[src/libraries/System.Net.Http/src/System/Net/Http/{SocketsHttpHandler/Http3RequestStream.cs,BrowserHttpHandler/BrowserHttpHandler.cs}] +# disable CA2025, the analyzer throws a NullReferenceException when processing this file: https://github.com/dotnet/roslyn-analyzers/issues/7652 +dotnet_diagnostic.CA2025.severity = none + +# C++ Files +[*.{cpp,h,in}] +curly_bracket_next_line = true +indent_brace_style = Allman + +# Xml project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] +indent_size = 2 + +# Xml build files +[*.builds] +indent_size = 2 + +# Xml files +[*.{resx,ruleset,slnx,stylecop,xml}] +indent_size = 2 + +# Xml resource files +[*.resx] +# match Visual Studio behavior +insert_final_newline = false +trim_trailing_whitespace = false + +# Xml config files +[*.{props,targets,config,nuspec}] +indent_size = 2 + +# Data serialization +[*.{json,yaml,yml}] +indent_size = 2 + +# Shell scripts +[*.sh] +end_of_line = lf +[*.{cmd,bat}] +end_of_line = crlf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ab74a5b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,22 @@ +--- +name: ci + +on: + push: + branches: + - main + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Install DocFX + run: dotnet tool restore + + - name: Build & test (Release) + run: dotnet test -c Release diff --git a/.github/workflows/tool.yml b/.github/workflows/tool.yml new file mode 100644 index 0000000..bb18d7a --- /dev/null +++ b/.github/workflows/tool.yml @@ -0,0 +1,52 @@ +--- +name: tool + +on: + push: + branches: + - main + tags: + - '*' + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Should Deploy? + if: ${{ success() && github.event_name == 'push' }} + shell: pwsh + run: | + if ("${{github.ref}}" -match "^refs/tags/([0-9]+\.[0-9]+\.[0-9]+)") + { + $version = $Matches[1] + + echo "UNITY_XREF_MAPS_DEPLOY=1" >> $env:GITHUB_ENV + echo "UNITY_XREF_MAPS_VERSION=$version" >> $env:GITHUB_ENV + } + + - name: Pack + if: ${{ success() && env.UNITY_XREF_MAPS_DEPLOY != 1 }} + run: dotnet pack UnityXrefMaps/UnityXrefMaps.csproj -c Release -o ${{ runner.temp }} + + - name: Pack (Release) + if: ${{ success() && env.UNITY_XREF_MAPS_DEPLOY == 1 }} + run: dotnet pack UnityXrefMaps/UnityXrefMaps.csproj -c Release -p:PackageVersion=$UNITY_XREF_MAPS_VERSION -o ${{ runner.temp }} + + - name: NuGet login + if: ${{ success() && env.UNITY_XREF_MAPS_DEPLOY == 1 }} + uses: NuGet/login@v1 + id: login + with: + user: ${{ secrets.NUGET_USER }} + + - name: Publish (Release) + if: ${{ success() && env.UNITY_XREF_MAPS_DEPLOY == 1 }} + run: | + dotnet nuget push ${{ runner.temp }}/UnityXrefMaps.${{ env.UNITY_XREF_MAPS_VERSION }}.nupkg \ + --source https://api.nuget.org/v3/index.json \ + --api-key ${{ steps.login.outputs.NUGET_API_KEY }} diff --git a/.github/workflows/unity-xref-maps.yml b/.github/workflows/unity-xref-maps.yml index 2dcda65..6f416f1 100644 --- a/.github/workflows/unity-xref-maps.yml +++ b/.github/workflows/unity-xref-maps.yml @@ -1,3 +1,4 @@ +--- name: Unity xref maps on: @@ -10,25 +11,29 @@ on: jobs: build: - runs-on: windows-latest + runs-on: ubuntu-latest + steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v5 - name: Install DocFX run: dotnet tool restore - - name: Cache UnityCsReference - uses: actions/cache@v3 - with: - path: UnityCsReference - key: unitycsreference + - name: Restore + run: dotnet restore + working-directory: UnityXrefMaps + + - name: Build + run: dotnet build --no-restore --configuration Release --output ${{ runner.temp }}/UnityXrefMaps + working-directory: UnityXrefMaps - name: Run - run: dotnet run + run: ${{ runner.temp }}/UnityXrefMaps/UnityXrefMaps.dll + working-directory: UnityXrefMaps - name: Deploy - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: _site diff --git a/.gitignore b/.gitignore index c58b5fd..ba7a514 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,428 @@ -.vscode/ -_site/ -bin/ -gh-pages/ -obj/ -Temp/ -UnityCsReference/ -*.cache \ No newline at end of file +## 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 + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates +*.env + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ + +[Dd]ebug/x64/ +[Dd]ebugPublic/x64/ +[Rr]elease/x64/ +[Rr]eleases/x64/ +bin/x64/ +obj/x64/ + +[Dd]ebug/x86/ +[Dd]ebugPublic/x86/ +[Rr]elease/x86/ +[Rr]eleases/x86/ +bin/x86/ +obj/x86/ + +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +[Aa][Rr][Mm]64[Ee][Cc]/ +bld/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Build results on 'Bin' directories +**/[Bb]in/* +# Uncomment if you have tasks that rely on *.refresh files to move binaries +# (https://github.com/github/gitignore/pull/3736) +#!**/[Bb]in/*.refresh + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* +*.trx + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Approval Tests result files +*.received.* + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.idb +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +# but not Directory.Build.rsp, as it configures directory-level build defaults +!Directory.Build.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +**/.paket/paket.exe +paket-files/ + +# FAKE - F# Make +**/.fake/ + +# CodeRush personal settings +**/.cr/personal + +# Python Tools for Visual Studio (PTVS) +**/__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +#tools/** +#!tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog +MSBuild_Logs/ + +# AWS SAM Build and Temporary Artifacts folder +.aws-sam + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +**/.mfractor/ + +# Local History for Visual Studio +**/.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +**/.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp \ No newline at end of file diff --git a/Git.cs b/Git.cs deleted file mode 100644 index 628e837..0000000 --- a/Git.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.IO; -using LibGit2Sharp; - -namespace DocFxForUnity -{ - public sealed class Git - { - /// - /// Fetches changes and hard resets the specified repository to the latest commit of a specified branch. If no - /// repository is found, it will be cloned before. - /// - /// The url of the repository. - /// The directory path where to find/clone the repository. - /// The branch use on the repository. - /// The synced repository on the latest commit of the specified branch. - public static Repository GetSyncRepository(string sourceUrl, string path, string branch = "main") - { - // Clone this repository to the specified branch if it doesn't exist - bool clone = !Directory.Exists(path); - if (clone) - { - Console.WriteLine($"Cloning {sourceUrl} to {path}"); - - var options = new CloneOptions() { BranchName = branch }; - Repository.Clone(sourceUrl, path, options); - } - - var repository = new Repository(path); - - // Otherwise fetch changes and checkout to the specified branch - if (!clone) - { - Console.WriteLine($"Hard reset '{path}' to HEAD"); - repository.Reset(ResetMode.Hard); - repository.RemoveUntrackedFiles(); - - Console.WriteLine($"Fetching changes from 'origin' in '{path}'"); - var remote = repository.Network.Remotes["origin"]; - Commands.Fetch(repository, remote.Name, Array.Empty(), null, null); // WTF is this API libgit2sharp? - - Console.WriteLine($"Checking out '{path}' to '{branch}' branch"); - var remoteBranch = $"origin/{branch}"; - Commands.Checkout(repository, remoteBranch); - } - - Console.WriteLine(); - - return repository; - } - } -} \ No newline at end of file diff --git a/LICENSE b/LICENSE.txt similarity index 100% rename from LICENSE rename to LICENSE.txt diff --git a/Program.cs b/Program.cs deleted file mode 100644 index 66b2c59..0000000 --- a/Program.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using LibGit2Sharp; - -namespace DocFxForUnity -{ - /// - /// Generates the xref maps of the APIs of all the Unity versions. - /// - /// Usage: Generate - /// - /// - /// - /// [.NET](https://dotnet.microsoft.com) >= 7.0 and [DocFX](https://dotnet.github.io/docfx/) must be installed - /// on your system. - /// - partial class Program - { - /// - /// The path where the documentation of the Unity repository will be generated. - /// - private const string GeneratedDocsPath = $"{UnityRepoPath}/_site"; - - /// - /// The path of the xref map generated by DocFX. - /// - private static readonly string GeneratedXrefMapPath = Path.Combine(GeneratedDocsPath, XrefMapFileName); - - /// - /// The path of the default xref map, pointing at . - /// - private static readonly string DefaultXrefMapPath = Path.Combine(XrefMapsPath, XrefMapFileName); - - /// - /// Gets the URL of the online API documentation of Unity. - /// - private const string UnityApiUrl = "https://docs.unity3d.com/ScriptReference/"; - - [GeneratedRegex("\\d{4}\\.\\d")] - private static partial Regex UnityVersionRegex(); - - /// - /// The path of the Unity repository. - /// - private const string UnityRepoPath = "UnityCsReference"; - - /// - /// The URL of the Unity repository. - /// - private const string UnityRepoUrl = "https://github.com/Unity-Technologies/UnityCsReference.git"; - - /// - /// The xref map filename. - /// - private const string XrefMapFileName = "xrefmap.yml"; - - /// - /// The path where to copy the xref maps. - /// - private const string XrefMapsPath = "_site"; - - /// - /// Entry point of this program. - /// - public static void Main() - { - Console.WriteLine($"Sync the Unity repository in '{UnityRepoPath}'"); - using var unityRepo = Git.GetSyncRepository(UnityRepoUrl, UnityRepoPath, branch: "master"); - - var versions = GetLatestVersions(unityRepo); - var latestVersion = versions - .OrderByDescending(version => version.name) - .First(version => version.release.Contains('f')); - - foreach (var version in versions) - { - Console.WriteLine($"Generating Unity '{version.name}' xref map"); - unityRepo.HardReset(version.release); - string xrefMapPath = Path.Combine(XrefMapsPath, version.name, XrefMapFileName); // .//xrefmap.yml - - Console.WriteLine($"Running DocFX on '{version.release}'"); - Utils.RunCommand("dotnet", "docfx", Console.WriteLine, Console.WriteLine); - - if (!File.Exists(GeneratedXrefMapPath)) - { - Console.WriteLine($"Error: '{GeneratedXrefMapPath}' for Unity '{version.name}' not generated"); - Console.WriteLine("\n"); - continue; - } - - Console.WriteLine($"Fixing hrefs in '{xrefMapPath}'"); - Utils.CopyFile(GeneratedXrefMapPath, xrefMapPath); - var xrefMap = XrefMap.Load(xrefMapPath); - xrefMap.FixHrefs(apiUrl: $"https://docs.unity3d.com/{version.name}/Documentation/ScriptReference/"); - xrefMap.Save(xrefMapPath); - - // Set the last version's xref map as the default one - if (version == latestVersion) - { - Console.WriteLine($"Fixing hrefs in '{DefaultXrefMapPath}'"); - Utils.CopyFile(GeneratedXrefMapPath, DefaultXrefMapPath); - xrefMap = XrefMap.Load(DefaultXrefMapPath); - xrefMap.FixHrefs(UnityApiUrl); - xrefMap.Save(DefaultXrefMapPath); - } - - Console.WriteLine("\n"); - } - } - - /// - /// Returns a collection of the latest versions of a specified repository of Unity. - /// - /// The repository of Unity to use. - /// The latest versions. - private static IEnumerable<(string name, string release)> GetLatestVersions(Repository unityRepository) - { - return unityRepository - .GetTags() - .Select(release => (name: UnityVersionRegex().Match(release).Value, release)) - .GroupBy(version => version.name) - .Select(version => version.First()); - } - } -} diff --git a/README.md b/README.md index 75a83a0..13c105c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ > Automatically add clickable links to the Unity API on a DocFX documentation Generates references of the Unity API to use with DocFX (the -[cross reference maps](https://dotnet.github.io/docfx/tutorial/links_and_cross_references.html#cross-reference-between-projects)). +[cross reference maps](https://dotnet.github.io/docfx/docs/links-and-cross-references.html#cross-reference-to-net-basic-class-library)). DocFX will set clickable all the references of the Unity API on your documentation. ## Usage @@ -54,7 +54,7 @@ DocFX will set clickable all the references of the Unity API on your documentati - To run this program: 1. Install Visual Studio 2022. - 2. Install [.NET 7.0](https://dotnet.microsoft.com/download/dotnet) SDK. + 2. Install [.NET 9.0](https://dotnet.microsoft.com/download/dotnet) SDK. 3. Clone this repository on your computer. 4. Open a terminal on the cloned repository and run: diff --git a/RepositoryExtensions.cs b/RepositoryExtensions.cs deleted file mode 100644 index 8eb8b10..0000000 --- a/RepositoryExtensions.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using LibGit2Sharp; - -namespace DocFxForUnity; - -/// -/// Extension methods for . -/// -public static class RepositoryExtensions -{ - /// - /// Returns a collection of the latest tags of a specified . - /// - /// The to use. - /// The collection of tags. - public static IEnumerable GetTags(this Repository repository) - { - return repository.Tags - .OrderByDescending(tag => (tag.Target as Commit).Author.When) - .Select(tag => tag.FriendlyName); - } - - /// - /// Hard resets the specified to the specified commit. - /// - /// The to hard reset to . - /// The name of the commit where to reset . - public static void HardReset(this Repository repository, string commit) - { - repository.Reset(ResetMode.Hard, commit); - - try - { - repository.RemoveUntrackedFiles(); - } - catch (System.Exception) { } - } -} \ No newline at end of file diff --git a/UnityXrefMaps.Tests/CommandTests.cs b/UnityXrefMaps.Tests/CommandTests.cs new file mode 100644 index 0000000..2101d20 --- /dev/null +++ b/UnityXrefMaps.Tests/CommandTests.cs @@ -0,0 +1,384 @@ +using Meziantou.Extensions.Logging.Xunit.v3; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Testing; +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using UnityXrefMaps.Commands; + +namespace UnityXrefMaps.Tests +{ + public class CommandTests : IAsyncLifetime, IAsyncDisposable + { + private string? _repositoryDirectoryPath; + private string? _xrefDirectoryPath; + private string? _docFxFilePath; + private string? _docFxFilterFilePath; + + private class CustomStringWriter : StringWriter + { + private readonly ILogger logger; + private readonly LogLevel logLevel; + + public CustomStringWriter(ILogger logger, LogLevel logLevel) + { + this.logger = logger; + this.logLevel = logLevel; + } + + public override void Write(string? value) + { + base.Write(value); + + logger.Log(logLevel, "{Message}", value); + } + } + + private readonly ITestOutputHelper output; + + public CommandTests(ITestOutputHelper output) + { + this.output = output; + } + + [Fact] + public async Task UnityEditor_BuildTest_Success() + { + string docFxFileContent = await File.ReadAllTextAsync("docfx.json", TestContext.Current.CancellationToken); + + docFxFileContent = docFxFileContent.Replace("UnityCsReference/", _repositoryDirectoryPath + '/'); + docFxFileContent = docFxFileContent.Replace("filterConfig.yml", _docFxFilterFilePath); + + await File.WriteAllTextAsync(_docFxFilePath!, docFxFileContent, TestContext.Current.CancellationToken); + + string docFxFilterConfigContent = """ +### YamlMime:ManagedReference +--- +apiRules: + - include: + uidRegex: ^UnityEngine\.Vector2$ + - include: + uidRegex: ^UnityEngine\.Vector3$ + - exclude: + uidRegex: .* +"""; + + await File.WriteAllTextAsync(_docFxFilterFilePath!, docFxFilterConfigContent, TestContext.Current.CancellationToken); + + var serviceCollection = new ServiceCollection(); + + serviceCollection.AddLogging(builder => + { + builder.SetMinimumLevel(LogLevel.Trace); + builder.AddFakeLogging(); + builder.Services.AddSingleton(new XUnitLoggerProvider(output, appendScope: false)); + }); + + await using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); + + FakeLogCollector fakeLogCollector = serviceProvider.GetFakeLogCollector(); + + ILoggerFactory loggerFactory = serviceProvider.GetRequiredService(); + + ILogger logger = loggerFactory.CreateLogger(); + + InvocationConfiguration invocationConfiguration = new() + { + Error = new CustomStringWriter(logger, LogLevel.Error), + Output = new CustomStringWriter(logger, LogLevel.Information), + }; + + string[] testedVersions = ["6000.0.1f1", "6000.1.1f1"]; + + BuildCommand buildCommand = new(loggerFactory.CreateLogger()); + + string xrefDirectoryName = "test"; + string xrefFileName = "test2.yml"; + + string[] buildArgs = [ + "--repositoryPath", + _repositoryDirectoryPath!, + "--docFxConfigurationFilePath", + _docFxFilePath!, + "--xrefMapsPath", + $"{_xrefDirectoryPath}/{xrefDirectoryName}/{{0}}/{xrefFileName}", + "--trimNamespaces", + "UnityEngine" + ]; + + buildArgs = [.. buildArgs, .. testedVersions.SelectMany(v => new string[] { "--repositoryTags", v })]; + + Assert.Equal( + 0, + await buildCommand + .Parse(buildArgs) + .InvokeAsync( + invocationConfiguration, + TestContext.Current.CancellationToken)); + + IReadOnlyList logRecords = fakeLogCollector.GetSnapshot(); + + Assert.Equal(2, logRecords.Count(l => l.Message.Contains("XRef map exported.", StringComparison.OrdinalIgnoreCase))); + + foreach (string testedVersion in testedVersions) + { + string xrefFilePath = $"{_xrefDirectoryPath}/{xrefDirectoryName}/{testedVersion}/{xrefFileName}"; + + Assert.True(File.Exists(xrefFilePath)); + + string xrefFileContent = await File.ReadAllTextAsync(xrefFilePath, TestContext.Current.CancellationToken); + + logger.LogInformation("{FilePath}:\n\n{FileContent}", xrefFilePath, xrefFileContent); + } + + TestCommand testCommand = new(loggerFactory.CreateLogger()); + + foreach (string testedVersion in testedVersions) + { + fakeLogCollector.Clear(); + + string xrefFilePath = $"{_xrefDirectoryPath}/{xrefDirectoryName}/{testedVersion}/{xrefFileName}"; + + XrefMap xrefMap = await XrefMap.Load(xrefFilePath, TestContext.Current.CancellationToken); + + Assert.Equal(3, xrefMap.References!.Length); + + Assert.Equal("UnityEngine", xrefMap.References[0].Uid); + Assert.Equal("UnityEngine.Vector2", xrefMap.References[1].Uid); + Assert.Equal("UnityEngine.Vector3", xrefMap.References[2].Uid); + + string[] testArgs = [ + "--xrefPath", + xrefFilePath + ]; + + Assert.Equal( + 0, + await testCommand + .Parse(testArgs) + .InvokeAsync( + invocationConfiguration, + TestContext.Current.CancellationToken)); + + Assert.Equal(0, fakeLogCollector.Count); + } + } + + [Fact] + public async Task UnityPackage_BuildTest_Success() + { + string docFxFileContent = await File.ReadAllTextAsync("docfx.json", TestContext.Current.CancellationToken); + + docFxFileContent = docFxFileContent.Replace("UnityCsReference/Projects/CSharp/*.csproj", "UnityCsReference/InputSystem/**/*.cs"); + docFxFileContent = docFxFileContent.Replace("UnityCsReference/", _repositoryDirectoryPath + '/'); + docFxFileContent = docFxFileContent.Replace("filterConfig.yml", _docFxFilterFilePath); + + await File.WriteAllTextAsync(_docFxFilePath!, docFxFileContent, TestContext.Current.CancellationToken); + + string docFxFilterConfigContent = """ +### YamlMime:ManagedReference +--- +apiRules: + - include: + uidRegex: ^UnityEngine\.InputSystem\.InputSystem$ + - include: + uidRegex: ^UnityEngine\.InputSystem\.InputActionAsset$ + - exclude: + uidRegex: .* +"""; + + await File.WriteAllTextAsync(_docFxFilterFilePath!, docFxFilterConfigContent, TestContext.Current.CancellationToken); + + var serviceCollection = new ServiceCollection(); + + serviceCollection.AddLogging(builder => + { + builder.SetMinimumLevel(LogLevel.Trace); + builder.AddFakeLogging(); + builder.Services.AddSingleton(new XUnitLoggerProvider(output, appendScope: false)); + }); + + await using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); + + FakeLogCollector fakeLogCollector = serviceProvider.GetFakeLogCollector(); + + ILoggerFactory loggerFactory = serviceProvider.GetRequiredService(); + + ILogger logger = loggerFactory.CreateLogger(); + + InvocationConfiguration invocationConfiguration = new() + { + Error = new CustomStringWriter(logger, LogLevel.Error), + Output = new CustomStringWriter(logger, LogLevel.Information), + }; + + string[] testedVersions = ["1.14.0", "1.14.2"]; + + BuildCommand buildCommand = new(loggerFactory.CreateLogger()); + + string xrefDirectoryName = "test"; + string xrefFileName = "test2.yml"; + + string[] buildArgs = [ + "--repositoryUrl", + "https://github.com/needle-mirror/com.unity.inputsystem.git", + "--repositoryPath", + _repositoryDirectoryPath!, + "--apiUrl", + "https://docs.unity3d.com/Packages/com.unity.inputsystem@{0}/api/", + "--docFxConfigurationFilePath", + _docFxFilePath!, + "--xrefMapsPath", + $"{_xrefDirectoryPath}/{xrefDirectoryName}/{{0}}/{xrefFileName}" + ]; + + buildArgs = [.. buildArgs, .. testedVersions.SelectMany(v => new string[] { "--repositoryTags", v })]; + + Assert.Equal( + 0, + await buildCommand + .Parse(buildArgs) + .InvokeAsync( + invocationConfiguration, + TestContext.Current.CancellationToken)); + + IReadOnlyList logRecords = fakeLogCollector.GetSnapshot(); + + Assert.Equal(2, logRecords.Count(l => l.Message.Contains("XRef map exported.", StringComparison.OrdinalIgnoreCase))); + + foreach (string testedVersion in testedVersions) + { + string xrefFilePath = $"{_xrefDirectoryPath}/{xrefDirectoryName}/{testedVersion}/{xrefFileName}"; + + Assert.True(File.Exists(xrefFilePath)); + + string xrefFileContent = await File.ReadAllTextAsync(xrefFilePath, TestContext.Current.CancellationToken); + + logger.LogInformation("{FilePath}:\n\n{FileContent}", xrefFilePath, xrefFileContent); + } + + TestCommand testCommand = new(loggerFactory.CreateLogger()); + + foreach (string testedVersion in testedVersions) + { + fakeLogCollector.Clear(); + + string xrefFilePath = $"{_xrefDirectoryPath}/{xrefDirectoryName}/{testedVersion}/{xrefFileName}"; + + XrefMap xrefMap = await XrefMap.Load(xrefFilePath, TestContext.Current.CancellationToken); + + Assert.Equal(3, xrefMap.References!.Length); + + Assert.Equal("UnityEngine.InputSystem", xrefMap.References[0].Uid); + Assert.Equal("UnityEngine.InputSystem.InputActionAsset", xrefMap.References[1].Uid); + Assert.Equal("UnityEngine.InputSystem.InputSystem", xrefMap.References[2].Uid); + + string[] testArgs = [ + "--xrefPath", + xrefFilePath + ]; + + Assert.Equal( + 0, + await testCommand + .Parse(testArgs) + .InvokeAsync( + invocationConfiguration, + TestContext.Current.CancellationToken)); + + Assert.Equal(0, fakeLogCollector.Count); + } + } + + // https://stackoverflow.com/a/1702920 + private static void DeleteDirectory(string targetDir) + { + File.SetAttributes(targetDir, FileAttributes.Normal); + + string[] files = Directory.GetFiles(targetDir); + string[] dirs = Directory.GetDirectories(targetDir); + + foreach (string file in files) + { + File.SetAttributes(file, FileAttributes.Normal); + File.Delete(file); + } + + foreach (string dir in dirs) + { + DeleteDirectory(dir); + } + + Directory.Delete(targetDir, false); + } + + public ValueTask InitializeAsync() + { + _repositoryDirectoryPath = Guid.NewGuid().ToString(); + _xrefDirectoryPath = Guid.NewGuid().ToString(); + _docFxFilePath = Guid.NewGuid().ToString() + "_config.json"; + _docFxFilterFilePath = Guid.NewGuid().ToString() + "_filter_config.yml"; + + return ValueTask.CompletedTask; + } + + public async ValueTask DisposeAsync() + { + await DisposeAsyncCore().ConfigureAwait(false); + + GC.SuppressFinalize(this); + } + + protected virtual async ValueTask DisposeAsyncCore() + { + File.Delete(_docFxFilePath!); + File.Delete(_docFxFilterFilePath!); + + const int maxRetries = 3; + const int delay = 500; + + await DeleteDirectoryWithRetries(_repositoryDirectoryPath!, maxRetries, delay); + await DeleteDirectoryWithRetries(_xrefDirectoryPath!, maxRetries, delay); + } + + private async Task DeleteDirectoryWithRetries(string directoryPath, int maxRetries, int delay) + { + int retries = 0; + + while (retries < maxRetries) + { + try + { + output.WriteLine($"Trying to delete directory: {directoryPath}"); + + DeleteDirectory(directoryPath); + + output.WriteLine($"Directory deleted: {directoryPath}"); + + break; + } + catch (Exception e) + { + output.WriteLine($"Error deleting directory: {directoryPath}. Error: {e}"); + + retries++; + + if (retries < maxRetries) + { + output.WriteLine($"Retrying in {delay}ms..."); + + await Task.Delay(delay); + } + else + { + output.WriteLine("Retry limit reached."); + } + } + } + } + } +} diff --git a/UnityXrefMaps.Tests/UnityXrefMaps.Tests.csproj b/UnityXrefMaps.Tests/UnityXrefMaps.Tests.csproj new file mode 100644 index 0000000..fc52459 --- /dev/null +++ b/UnityXrefMaps.Tests/UnityXrefMaps.Tests.csproj @@ -0,0 +1,39 @@ + + + + Exe + net9.0 + enable + false + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + PreserveNewest + + + + + + + + + + + + diff --git a/UnityXrefMaps.csproj b/UnityXrefMaps.csproj deleted file mode 100644 index 4dca7f2..0000000 --- a/UnityXrefMaps.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - Exe - net7.0 - $(DefaultItemExcludes);UnityCsReference*\** - - - - - - - - diff --git a/UnityXrefMaps.sln b/UnityXrefMaps.sln new file mode 100644 index 0000000..ae261f7 --- /dev/null +++ b/UnityXrefMaps.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36511.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityXrefMaps", "UnityXrefMaps\UnityXrefMaps.csproj", "{97BE34CD-6988-1F05-D1B2-EBF906DAA098}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityXrefMaps.Tests", "UnityXrefMaps.Tests\UnityXrefMaps.Tests.csproj", "{2A7A5BA2-A0E4-D775-C22B-0C5A6B68260B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {97BE34CD-6988-1F05-D1B2-EBF906DAA098}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {97BE34CD-6988-1F05-D1B2-EBF906DAA098}.Debug|Any CPU.Build.0 = Debug|Any CPU + {97BE34CD-6988-1F05-D1B2-EBF906DAA098}.Release|Any CPU.ActiveCfg = Release|Any CPU + {97BE34CD-6988-1F05-D1B2-EBF906DAA098}.Release|Any CPU.Build.0 = Release|Any CPU + {2A7A5BA2-A0E4-D775-C22B-0C5A6B68260B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A7A5BA2-A0E4-D775-C22B-0C5A6B68260B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A7A5BA2-A0E4-D775-C22B-0C5A6B68260B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A7A5BA2-A0E4-D775-C22B-0C5A6B68260B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FCDA8159-7FB5-4653-8EBE-A68917137BBC} + EndGlobalSection +EndGlobal diff --git a/UnityXrefMaps/AssemblyInfo.cs b/UnityXrefMaps/AssemblyInfo.cs new file mode 100644 index 0000000..08d26de --- /dev/null +++ b/UnityXrefMaps/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("UnityXrefMaps.Tests")] diff --git a/UnityXrefMaps/Commands/BuildCommand.cs b/UnityXrefMaps/Commands/BuildCommand.cs new file mode 100644 index 0000000..0eea3fe --- /dev/null +++ b/UnityXrefMaps/Commands/BuildCommand.cs @@ -0,0 +1,152 @@ +using System.CommandLine; +using System.IO; +using System.Text.Json; +using System.Text.RegularExpressions; +using LibGit2Sharp; +using Microsoft.Extensions.Logging; +using UnityXrefMaps.DocFX; + +namespace UnityXrefMaps.Commands; + +internal sealed partial class BuildCommand : RootCommand +{ + public BuildCommand(ILogger logger) + { + Option repositoryUrlOption = new("--repositoryUrl") + { + Description = "The Git repository url.", + DefaultValueFactory = _ => Constants.DefaultUnityRepositoryUrl + }; + Option repositoryBranchOption = new("--repositoryBranch") + { + Description = "The Git repository branch.", + DefaultValueFactory = _ => Constants.DefaultUnityRepositoryBranch + }; + Option repositoryPathOption = new("--repositoryPath") + { + Description = "The Git repository path to git clone. " + + "If the clone has already been made, it will reset so that the clone can be reused.", + DefaultValueFactory = _ => Constants.DefaultUnityRepositoryPath + }; + Option repositoryTagsOption = new("--repositoryTags") + { + Description = "The repository tags to use to generate the documentation. " + + "That is, the versions of the Unity editor (6000.0.1f1, 6000.1.1f1) or the versions of the Unity package (1.0.0, 1.1.0).", + Required = true + }; + Option apiUrlOption = new("--apiUrl") + { + Description = "The root path of the Unity editor API documentation or the Unity package. " + + "{0} is replaced with the Unity editor short version (example: https://docs.unity3d.com/6000.0/Documentation/ScriptReference/) or " + + "the Unity package short version (example: https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/api/)}) in the case of a package.", + DefaultValueFactory = _ => Constants.DefaultUnityApiUrl + }; + Option docFxConfigurationFilePathOption = new("--docFxConfigurationFilePath") + { + Description = "The path to the DocFX configuration file.", + DefaultValueFactory = _ => Constants.DefaultDocFxConfigurationFilePath + }; + Option xrefMapsPathOption = new("--xrefMapsPath") + { + Description = $"The path where the final {Constants.DefaultXrefMapFileName} files will be generated. " + + $"{{0}} is replaced with the Unity editor version (example: {string.Format(Constants.DefaultXrefMapsPath, "6000.0.1f1")}) or " + + $"the Unity package version (example: {string.Format(Constants.DefaultXrefMapsPath, "1.0.0")}) in the case of a package.", + DefaultValueFactory = _ => Constants.DefaultXrefMapsPath + }; + Option trimNamespacesOption = new("--trimNamespaces") + { + Description = "Namespaces for trimming." + }; + + Options.Add(repositoryUrlOption); + Options.Add(repositoryBranchOption); + Options.Add(repositoryPathOption); + Options.Add(repositoryTagsOption); + Options.Add(apiUrlOption); + Options.Add(docFxConfigurationFilePathOption); + Options.Add(xrefMapsPathOption); + Options.Add(trimNamespacesOption); + + SetAction(async (parseResult, cancellationToken) => + { + bool result = true; + + string? repositoryUrl = parseResult.GetValue(repositoryUrlOption); + string? repositoryBranch = parseResult.GetValue(repositoryBranchOption); + string? repositoryPath = parseResult.GetValue(repositoryPathOption); + string[]? repositoryTags = parseResult.GetValue(repositoryTagsOption); + string? apiUrl = parseResult.GetValue(apiUrlOption); + string? docFxFilePath = parseResult.GetValue(docFxConfigurationFilePathOption); + string? xrefMapsPath = parseResult.GetValue(xrefMapsPathOption); + string[]? trimNamespaces = parseResult.GetValue(trimNamespacesOption); + + using Stream docFxStream = File.OpenRead(docFxFilePath!); + + DocFxConfiguration? docFxConfiguration = await JsonSerializer.DeserializeAsync(docFxStream, cancellationToken: cancellationToken); + + string generatedDocsPath = docFxConfiguration!.Build!.Destination!; + string generatedXrefMapPath = Path.Combine(generatedDocsPath, Constants.DefaultXrefMapFileName!); + + logger.LogInformation("Sync the Unity repository in '{RepositoryPath}'", Path.GetFullPath(repositoryPath!)); + + using Repository repository = Git.GetSyncRepository(repositoryUrl!, repositoryPath!, repositoryBranch!, logger); + + foreach (string repositoryTag in repositoryTags!) + { + Match versionMatch = VersionRegex().Match(repositoryTag); + + string shortVersion = $"{versionMatch.Groups["majorVersion"].Value}.{versionMatch.Groups["minorVersion"].Value}"; + + logger.LogInformation("Generating Unity '{ShortVersion}' xref map", shortVersion); + + repository.HardReset(repositoryTag, logger); + + string xrefMapPath = string.Format(xrefMapsPath!, repositoryTag); // .//xrefmap.yml + + logger.LogInformation("Running DocFX on '{RepositoryTag}'", repositoryTag); + + await Utils.RunCommand( + "dotnet", $"docfx {docFxFilePath}", + value => + { + if (!string.IsNullOrEmpty(value)) + { + logger.LogInformation("{Message}", value); + } + }, + value => + { + if (!string.IsNullOrEmpty(value)) + { + logger.LogError("{Message}", value); + } + }, + cancellationToken); + + if (!File.Exists(generatedXrefMapPath)) + { + result = false; + + logger.LogError("Error: '{XrefMapFilePath}' for Unity '{RepositoryTag}' not generated", generatedXrefMapPath, repositoryTag); + + continue; + } + + logger.LogInformation("Fixing hrefs in '{XrefMapFilePath}'", Path.GetFullPath(xrefMapPath)); + + await Utils.CopyFile(generatedXrefMapPath, xrefMapPath, cancellationToken); + + XrefMap xrefMap = await XrefMap.Load(xrefMapPath, cancellationToken); + + xrefMap.FixHrefs(string.Format(apiUrl!, shortVersion), trimNamespaces!); + + await xrefMap.Save(xrefMapPath, cancellationToken); + } + + return result ? 0 : 1; + }); + } + + [GeneratedRegex(@"(?\d+)\.(?\d+)\.(?\d+)")] + private static partial Regex VersionRegex(); +} diff --git a/UnityXrefMaps/Commands/TestCommand.cs b/UnityXrefMaps/Commands/TestCommand.cs new file mode 100644 index 0000000..0ffe91d --- /dev/null +++ b/UnityXrefMaps/Commands/TestCommand.cs @@ -0,0 +1,39 @@ +using System.CommandLine; +using Microsoft.Extensions.Logging; + +namespace UnityXrefMaps.Commands; + +internal sealed class TestCommand : Command +{ + public TestCommand(ILogger logger) : base("test", $"Check that the links in the {Constants.DefaultXrefMapFileName} file are valid.") + { + Option xrefPathOption = new("--xrefPath") + { + Description = $"The path to the {Constants.DefaultXrefMapFileName} file.", + Required = true + }; + + Options.Add(xrefPathOption); + + SetAction(async (parseResult, cancellationToken) => + { + bool result = true; + + string? xrefPath = parseResult.GetValue(xrefPathOption); + + XrefMap xrefMap = await XrefMap.Load(xrefPath!, cancellationToken); + + foreach (XrefMapReference reference in xrefMap.References!) + { + if (!await Utils.TestUriExists(reference.Href, logger, cancellationToken)) + { + result = false; + + logger.LogWarning("Invalid URL {Href} for {Uid} uid", reference.Href, reference.Uid); + } + } + + return result ? 0 : 1; + }); + } +} diff --git a/UnityXrefMaps/Constants.cs b/UnityXrefMaps/Constants.cs new file mode 100644 index 0000000..d6af748 --- /dev/null +++ b/UnityXrefMaps/Constants.cs @@ -0,0 +1,40 @@ +namespace UnityXrefMaps; + +internal static class Constants +{ + /// + /// Gets the default URL of the online API documentation of Unity. + /// + public const string DefaultUnityApiUrl = "https://docs.unity3d.com/{0}/Documentation/ScriptReference/"; + + /// + /// The default path of the Unity repository. + /// + public const string DefaultUnityRepositoryPath = "UnityCsReference"; + + /// + /// The default URL of the Unity repository. + /// + public const string DefaultUnityRepositoryUrl = "https://github.com/Unity-Technologies/UnityCsReference.git"; + + /// + /// The default branch of the Unity repository. + /// + public const string DefaultUnityRepositoryBranch = "master"; + + // https://github.com/dotnet/docfx/blob/1c4e9ff4a2d236206eee04066847a98343c6a3f7/src/Docfx.Build/XRefMaps/XRefArchive.cs#L14 + /// + /// The default xref map filename. + /// + public const string DefaultXrefMapFileName = "xrefmap.yml"; + + /// + /// The default path where to copy the xref maps. + /// + public const string DefaultXrefMapsPath = $"_site/{{0}}/{DefaultXrefMapFileName}"; + + /// + /// The default DocFX config file path. + /// + public const string DefaultDocFxConfigurationFilePath = "docfx.json"; +} diff --git a/UnityXrefMaps/DocFX/DocFxConfiguration.cs b/UnityXrefMaps/DocFX/DocFxConfiguration.cs new file mode 100644 index 0000000..8ce9db5 --- /dev/null +++ b/UnityXrefMaps/DocFX/DocFxConfiguration.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace UnityXrefMaps.DocFX; + +// https://github.com/dotnet/docfx/blob/main/src/Docfx.App/Config/DocfxConfig.cs +public class DocFxConfiguration +{ + [JsonPropertyName("build")] + public DocFxConfigurationBuild? Build { get; set; } +} diff --git a/UnityXrefMaps/DocFX/DocFxConfigurationBuild.cs b/UnityXrefMaps/DocFX/DocFxConfigurationBuild.cs new file mode 100644 index 0000000..455e495 --- /dev/null +++ b/UnityXrefMaps/DocFX/DocFxConfigurationBuild.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace UnityXrefMaps.DocFX; + +// https://github.com/dotnet/docfx/blob/main/src/Docfx.App/Config/BuildJsonConfig.cs +public class DocFxConfigurationBuild +{ + [JsonPropertyName("dest")] + public string? Destination { get; set; } +} diff --git a/UnityXrefMaps/Git.cs b/UnityXrefMaps/Git.cs new file mode 100644 index 0000000..1852aa6 --- /dev/null +++ b/UnityXrefMaps/Git.cs @@ -0,0 +1,49 @@ +using System.IO; +using LibGit2Sharp; +using Microsoft.Extensions.Logging; + +namespace UnityXrefMaps; + +internal static class Git +{ + /// + /// Fetches changes and hard resets the specified repository to the latest commit of a specified branch. If no + /// repository is found, it will be cloned before. + /// + /// The url of the repository. + /// The directory path where to find/clone the repository. + /// The branch use on the repository. + /// The synced repository on the latest commit of the specified branch. + public static Repository GetSyncRepository(string sourceUrl, string path, string branch, ILogger logger) + { + // Clone this repository to the specified branch if it doesn't exist + bool clone = !Directory.Exists(path); + if (clone) + { + logger.LogInformation("Cloning {SourceUrl} to {Path}", sourceUrl, path); + + var options = new CloneOptions() { BranchName = branch }; + Repository.Clone(sourceUrl, path, options); + } + + var repository = new Repository(path); + + // Otherwise fetch changes and checkout to the specified branch + if (!clone) + { + logger.LogInformation("Hard reset '{Path}' to HEAD", path); + repository.Reset(ResetMode.Hard); + repository.RemoveUntrackedFiles(); + + logger.LogInformation("Fetching changes from 'origin' in '{Path}'", path); + Remote remote = repository.Network.Remotes["origin"]; + LibGit2Sharp.Commands.Fetch(repository, remote.Name, [], null, null); // WTF is this API libgit2sharp? + + logger.LogInformation("Checking out '{Path}' to '{Branch}' branch", path, branch); + string remoteBranch = $"origin/{branch}"; + LibGit2Sharp.Commands.Checkout(repository, remoteBranch); + } + + return repository; + } +} diff --git a/UnityXrefMaps/Program.cs b/UnityXrefMaps/Program.cs new file mode 100644 index 0000000..d1d809a --- /dev/null +++ b/UnityXrefMaps/Program.cs @@ -0,0 +1,13 @@ +using System.CommandLine; +using Microsoft.Extensions.Logging; +using UnityXrefMaps.Commands; + +ILoggerFactory factory = LoggerFactory.Create(builder => +{ + builder.AddConsole(); +}); + +RootCommand rootCommand = new BuildCommand(factory.CreateLogger()); +rootCommand.Subcommands.Add(new TestCommand(factory.CreateLogger())); + +await rootCommand.Parse(args).InvokeAsync(); diff --git a/UnityXrefMaps/Properties/launchSettings.json b/UnityXrefMaps/Properties/launchSettings.json new file mode 100644 index 0000000..34c8bae --- /dev/null +++ b/UnityXrefMaps/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "DocFxForUnity": { + "commandName": "Project", + "commandLineArgs": "--repositoryTags 6000.0.1f1 --trimNamespaces UnityEditor --trimNamespaces UnityEngine" + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json" +} diff --git a/UnityXrefMaps/RepositoryExtensions.cs b/UnityXrefMaps/RepositoryExtensions.cs new file mode 100644 index 0000000..67a42ab --- /dev/null +++ b/UnityXrefMaps/RepositoryExtensions.cs @@ -0,0 +1,31 @@ +using System; +using LibGit2Sharp; +using Microsoft.Extensions.Logging; + +namespace UnityXrefMaps; + +/// +/// Extension methods for . +/// +internal static class RepositoryExtensions +{ + /// + /// Hard resets the specified to the specified commit. + /// + /// The to hard reset to . + /// The name of the commit where to reset . + public static void HardReset(this Repository repository, string commit, ILogger logger) + { + logger.LogInformation("Hard reset to {Commit}", commit); + + repository.Reset(ResetMode.Hard, commit); + + try + { + logger.LogInformation($"Removing untracked files"); + + repository.RemoveUntrackedFiles(); + } + catch (Exception) { } + } +} diff --git a/UnityXrefMaps/UnityXrefMaps.csproj b/UnityXrefMaps/UnityXrefMaps.csproj new file mode 100644 index 0000000..d116153 --- /dev/null +++ b/UnityXrefMaps/UnityXrefMaps.csproj @@ -0,0 +1,36 @@ + + + + Exe + net9.0 + enable + true + unityXrefMaps + LICENSE.txt + README.md + https://github.com/NormandErwan/UnityXrefMaps + $(DefaultItemExcludes);UnityCsReference*\** + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/UnityXrefMaps/Utils.cs b/UnityXrefMaps/Utils.cs new file mode 100644 index 0000000..8b88a27 --- /dev/null +++ b/UnityXrefMaps/Utils.cs @@ -0,0 +1,89 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +namespace UnityXrefMaps; + +internal static class Utils +{ + /// + /// Client for send HTTP requests and receiving HTTP responses. + /// + private static readonly HttpClient s_httpClient = new(); + + /// + /// Copy a source file to a destination file. Intermediate folders will be automatically created. + /// + /// The path of the source file to copy. + /// The destination path of the copied file. + public static async Task CopyFile(string sourcePath, string destPath, CancellationToken cancellationToken = default) + { + string? destDirectoryPath = Path.GetDirectoryName(destPath); + + Directory.CreateDirectory(destDirectoryPath!); + + using Stream source = File.OpenRead(sourcePath); + using Stream destination = File.Create(destPath); + + await source.CopyToAsync(destination, cancellationToken); + } + + /// + /// Run a command in a hidden window and returns its output. + /// + /// The command to run. + /// The arguments of the command. + /// The function to call with the output data of the command. + /// The function to call with the error data of the command. + public static async Task RunCommand(string command, string arguments, Action output, Action error, CancellationToken cancellationToken = default) + { + using var process = new Process(); + process.StartInfo = new ProcessStartInfo(command, arguments) + { + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardOutput = true, + RedirectStandardError = true + }; + + process.OutputDataReceived += (sender, args) => output(args.Data); + process.ErrorDataReceived += (sender, args) => error(args.Data); + + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + await process.WaitForExitAsync(cancellationToken); + } + + /// + /// Requests the specified URI with and returns if the response status code is in the + /// range 200-299. + /// + /// The URI to request. + /// true if the response status code is in the range 200-299. + public static async Task TestUriExists(string? uri, ILogger logger, CancellationToken cancellationToken = default) + { + try + { + HttpResponseMessage response = await s_httpClient.SendAsync(new(HttpMethod.Head, uri), cancellationToken); + + if (!response.IsSuccessStatusCode && response.StatusCode != System.Net.HttpStatusCode.NotFound) + { + logger.LogError("HTTP response code on {Uri} is {StatusCode}", uri, response.StatusCode); + } + + return response.IsSuccessStatusCode; + } + catch (HttpRequestException e) + { + logger.LogError(e, "Exception on {Uri}", uri); + + return false; + } + } +} diff --git a/UnityXrefMaps/XrefMap.cs b/UnityXrefMaps/XrefMap.cs new file mode 100644 index 0000000..a253920 --- /dev/null +++ b/UnityXrefMaps/XrefMap.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using YamlDotNet.Serialization; + +namespace UnityXrefMaps; + +/// +/// Represents a xref map file of Unity. +/// +public sealed partial class XrefMap +{ + private static readonly Deserializer s_deserializer = new(); + private static readonly Serializer s_serializer = new(); + + [YamlMember(Alias = "sorted")] + public bool Sorted { get; set; } + + [YamlMember(Alias = "references")] + public XrefMapReference[]? References { get; set; } + + /// + /// Loads a from a file. + /// + /// The path of the file. + /// The loaded from . + public static async Task Load(string filePath, CancellationToken cancellationToken = default) + { + string xrefMapText = await File.ReadAllTextAsync(filePath, cancellationToken); + + // Remove `0:` strings on the xrefmap that make crash Deserializer + xrefMapText = ZeroStringsRegex().Replace(xrefMapText, "$1"); + + return s_deserializer.Deserialize(xrefMapText); + } + + /// + /// Fix the of of this . + /// + /// The URL of the online API documentation of Unity. + public void FixHrefs(string apiUrl, IEnumerable hrefNamespacesToTrim) + { + var fixedReferences = new List(); + + foreach (XrefMapReference reference in References!) + { + if (!reference.IsValid) + { + continue; + } + + reference.FixHref(apiUrl, hrefNamespacesToTrim); + fixedReferences.Add(reference); + } + + References = [.. fixedReferences]; + } + + /// + /// Saves this to a file. + /// + /// The path of the file. + public async Task Save(string filePath, CancellationToken cancellationToken = default) + { + string xrefMapText = "### YamlMime:XRefMap\n" + s_serializer.Serialize(this); + + await File.WriteAllTextAsync(filePath, xrefMapText, cancellationToken); + } + + [GeneratedRegex(@"(\d):")] + private static partial Regex ZeroStringsRegex(); +} diff --git a/UnityXrefMaps/XrefMapReference.cs b/UnityXrefMaps/XrefMapReference.cs new file mode 100644 index 0000000..330f12d --- /dev/null +++ b/UnityXrefMaps/XrefMapReference.cs @@ -0,0 +1,113 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; +using YamlDotNet.Serialization; + +namespace UnityXrefMaps; + +/// +/// Represents a reference item on a . +/// +public sealed partial class XrefMapReference +{ + [YamlMember(Alias = "uid")] + public string? Uid { get; set; } + + [YamlMember(Alias = "name")] + public string? Name { get; set; } + + [YamlMember(Alias = "name.vb")] + public string? NameVb { get; set; } + + [YamlMember(Alias = "href")] + public string? Href { get; set; } + + [YamlMember(Alias = "commentId")] + public string? CommentId { get; set; } + + [YamlMember(Alias = "isSpec")] + public string? IsSpec { get; set; } + + [YamlMember(Alias = "fullName")] + public string? FullName { get; set; } + + [YamlMember(Alias = "fullName.vb")] + public string? FullNameVb { get; set; } + + [YamlMember(Alias = "nameWithType")] + public string? NameWithType { get; set; } + + [YamlMember(Alias = "nameWithType.vb")] + public string? NameWithTypeVb { get; set; } + + /// + /// Gets if this is valid or not. + /// + [YamlIgnore] + public bool IsValid => !CommentId!.Contains("Overload:"); + + /// + /// Sets to link to the online API documentation of Unity. + /// + /// The URL of the online API documentation of Unity. + public void FixHref(string apiUrl, IEnumerable hrefNamespacesToTrim) + { + // Namespaces point to documentation index + if (CommentId!.StartsWith("N:")) + { + Href = "index"; + } + else + { + Href = Uid; + + foreach (string hrefNamespaceToTrim in hrefNamespacesToTrim) + { + Href = Href!.Replace(hrefNamespaceToTrim + ".", string.Empty); + } + + // Fix href of constructors + Href = Href!.Replace(".#ctor", "-ctor"); + + // Fix href of generics + Href = GenericHrefRegex().Replace(Href!, string.Empty); + Href = Href.Replace("`", "_"); + + // Fix href of methods + Href = MethodHrefPointerRegex().Replace(Href, string.Empty); + Href = MethodHrefRegex().Replace(Href, string.Empty); + + // Fix href of operator + if (CommentId.StartsWith("M:") && CommentId.Contains(".op_")) + { + Href = Href.Replace(".op_", ".operator_"); + + Href = Href.Replace(".operator_Subtraction", ".operator_subtract"); + Href = Href.Replace(".operator_Multiply", ".operator_multiply"); + Href = Href.Replace(".operator_Division", ".operator_divide"); + Href = Href.Replace(".operator_Addition", ".operator_add"); + Href = Href.Replace(".operator_Equality", ".operator_eq"); + Href = Href.Replace(".operator_Implicit~", ".operator_"); + } + + // Fix href of properties + if (CommentId.StartsWith("F:") || CommentId.StartsWith("M:") || CommentId.StartsWith("P:")) + { + Href = PropertyHrefRegex().Replace(Href, "-$1"); + } + } + + Href = apiUrl + Href + ".html"; + } + + [GeneratedRegex(@"`{2}\d")] + private static partial Regex GenericHrefRegex(); + + [GeneratedRegex(@"\*$")] + private static partial Regex MethodHrefPointerRegex(); + + [GeneratedRegex(@"\(.*\)")] + private static partial Regex MethodHrefRegex(); + + [GeneratedRegex(@"\.([a-z].*)$")] + private static partial Regex PropertyHrefRegex(); +} diff --git a/docfx.json b/UnityXrefMaps/docfx.json similarity index 70% rename from docfx.json rename to UnityXrefMaps/docfx.json index ca2924d..eefbc5e 100644 --- a/docfx.json +++ b/UnityXrefMaps/docfx.json @@ -8,12 +8,14 @@ ], "filter": "filterConfig.yml", "dest": "UnityCsReference/_api", - "disableGitFeatures": true + "disableGitFeatures": true, + "allowCompilationErrors": true } ], - "build": - { - "xrefService": [ "https://xref.docs.microsoft.com/query?uid={uid}" ], + "build": { + "xref": [ + "https://learn.microsoft.com/en-us/dotnet/.xrefmap.json" + ], "content": [ { "src": "UnityCsReference/_api", @@ -23,4 +25,4 @@ ], "dest": "UnityCsReference/_site" } -} \ No newline at end of file +} diff --git a/UnityXrefMaps/filterConfig.yml b/UnityXrefMaps/filterConfig.yml new file mode 100644 index 0000000..b70045a --- /dev/null +++ b/UnityXrefMaps/filterConfig.yml @@ -0,0 +1,24 @@ +### YamlMime:ManagedReference +--- +apiRules: + - exclude: + uidRegex: ^AOT + - exclude: + uidRegex: ^JetBrains + - exclude: + uidRegex: ^TreeEditor + - exclude: + uidRegex: ^Unity\.CodeEditor + - exclude: + uidRegex: ^UnityEditorInternal + - exclude: + uidRegex: ^UnityEditor\.InspectorMode + - exclude: + uidRegex: ^UnityEngineInternal + - exclude: + uidRegex: ^UnityEngine.Internal + - exclude: + uidRegex: Finalize$ + - exclude: + hasAttribute: + uid: UnityEngine.Internal.ExcludeFromDocsAttribute diff --git a/Utils.cs b/Utils.cs deleted file mode 100644 index b4ba405..0000000 --- a/Utils.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Net.Http; -using System.Threading.Tasks; - -namespace DocFxForUnity -{ - public sealed class Utils - { - /// - /// Client for send HTTP requests and receiving HTTP responses. - /// - private static readonly HttpClient httpClient = new(); - - /// - /// Copy a source file to a destination file. Intermediate folders will be automatically created. - /// - /// The path of the source file to copy. - /// The destination path of the copied file. - public static void CopyFile(string sourcePath, string destPath) - { - var destDirectoryPath = Path.GetDirectoryName(destPath); - Directory.CreateDirectory(destDirectoryPath); - - File.Copy(sourcePath, destPath, overwrite: true); - } - - /// - /// Deletes the specified directories if they exist. - /// - /// The path of the directories to delete. - public static void DeleteDirectories(params string[] paths) - { - foreach (var path in paths) - { - if (Directory.Exists(path)) - { - Directory.Delete(path, recursive: true); - } - } - } - - /// - /// Run a command in a hidden window and returns its output. - /// - /// The command to run. - /// The arguments of the command. - /// The function to call with the output data of the command. - /// The function to call with the error data of the command. - public static void RunCommand(string command, string arguments, Action output, Action error) - { - using var process = new Process(); - process.StartInfo = new ProcessStartInfo(command, arguments) - { - UseShellExecute = false, - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true - }; - - process.OutputDataReceived += (sender, args) => output(args.Data); - process.ErrorDataReceived += (sender, args) => error(args.Data); - - process.Start(); - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - - process.WaitForExit(); - } - - /// - /// Requests the specified URI with and returns if the response status code is in the - /// range 200-299. - /// - /// The URI to request. - /// true if the response status code is in the range 200-299. - public static async Task TestUriExists(string uri) - { - try - { - var headRequest = new HttpRequestMessage(HttpMethod.Head, uri); - var response = await httpClient.SendAsync(headRequest); - if (!response.IsSuccessStatusCode && response.StatusCode != System.Net.HttpStatusCode.NotFound) - { - Console.Error.WriteLine($"Error: HTTP response code on {uri} is {response.StatusCode}"); - } - return response.IsSuccessStatusCode; - } - catch (HttpRequestException e) - { - Console.WriteLine($"Exception on {uri}: {e.Message}"); - return false; - } - } - } -} \ No newline at end of file diff --git a/XrefMap.cs b/XrefMap.cs deleted file mode 100644 index dfb6338..0000000 --- a/XrefMap.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text.RegularExpressions; -using YamlDotNet.Serialization; - -namespace DocFxForUnity -{ - /// - /// Represents a xref map file of Unity. - /// - public sealed partial class XrefMap - { - private static readonly Deserializer Deserializer = new(); - private static readonly Serializer Serializer = new(); - - [GeneratedRegex("(\\d):")] - private static partial Regex ZeroStringsRegex(); - - public bool sorted { get; set; } - - public XrefMapReference[] references { get; set; } - - /// - /// Loads a from a file. - /// - /// The path of the file. - /// The loaded from . - public static XrefMap Load(string filePath) - { - string xrefMapText = File.ReadAllText(filePath); - - // Remove `0:` strings on the xrefmap that make crash Deserializer - xrefMapText = ZeroStringsRegex().Replace(xrefMapText, "$1"); - - return Deserializer.Deserialize(xrefMapText); - } - - /// - /// Fix the of of this . - /// - /// The URL of the online API documentation of Unity. - public void FixHrefs(string apiUrl, bool testUrls = false) - { - var fixedReferences = new List(); - foreach (var reference in references) - { - if (!reference.IsValid) - { - continue; - } - - reference.FixHref(apiUrl); - fixedReferences.Add(reference); - - if (testUrls && !Utils.TestUriExists(reference.href).Result) - { - Console.WriteLine("Warning: invalid URL " + reference.href + " for " + reference.uid + " uid"); - } - } - references = fixedReferences.ToArray(); - } - - /// - /// Saves this to a file. - /// - /// The path of the file. - public void Save(string filePath) - { - string xrefMapText = "### YamlMime:XRefMap\n" + Serializer.Serialize(this); - File.WriteAllText(filePath, xrefMapText); - } - } -} \ No newline at end of file diff --git a/XrefMapReference.cs b/XrefMapReference.cs deleted file mode 100644 index 7f8acf0..0000000 --- a/XrefMapReference.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; -using YamlDotNet.Serialization; - -namespace DocFxForUnity -{ - /// - /// Represents a reference item on a . - /// - public sealed partial class XrefMapReference - { - /// - /// The online API documentation of Unity doesn't show some namespaces. - /// - private static readonly List HrefNamespacesToTrim = new() { "UnityEditor", "UnityEngine" }; - - [GeneratedRegex("`{2}\\d")] - private static partial Regex GenericHrefRegex(); - - [GeneratedRegex("\\*$")] - private static partial Regex MethodHrefPointerRegex(); - - [GeneratedRegex("\\(.*\\)")] - private static partial Regex MethodHrefRegex(); - - [GeneratedRegex("\\.([a-z].*)$")] - private static partial Regex PropertyHrefRegex(); - - public string uid { get; set; } - - public string name { get; set; } - - [YamlMember(Alias = "name.vb")] - public string nameVb { get; set; } - - public string href { get; set; } - - public string commentId { get; set; } - - public string isSpec { get; set; } - - public string fullName { get; set; } - - [YamlMember(Alias = "fullName.vb")] - public string fullNameVb { get; set; } - - public string nameWithType { get; set; } - - [YamlMember(Alias = "nameWithType.vb")] - public string nameWithTypeVb { get; set; } - - /// - /// Gets if this is valid or not. - /// - public bool IsValid => !commentId.Contains("Overload:"); - - /// - /// Sets to link to the online API documentation of Unity. - /// - /// The URL of the online API documentation of Unity. - public void FixHref(string apiUrl) - { - // Namespaces point to documentation index - if (commentId.Contains("N:")) - { - href = "index"; - } - else - { - href = uid; - - // Trim UnityEngine and UnityEditor namespaces from href - foreach (var hrefNamespaceToTrim in HrefNamespacesToTrim) - { - href = href.Replace(hrefNamespaceToTrim + ".", ""); - } - - // Fix href of constructors - href = href.Replace(".#ctor", "-ctor"); - - // Fix href of generics - href = GenericHrefRegex().Replace(href, ""); - href = href.Replace("`", "_"); - - // Fix href of methods - href = MethodHrefPointerRegex().Replace(href, ""); - href = MethodHrefRegex().Replace(href, ""); - - // Fix href of properties - if (commentId.Contains("P:") || commentId.Contains("M:")) - { - href = PropertyHrefRegex().Replace(href, "-$1"); - } - } - - href = apiUrl + href + ".html"; - } - } -} \ No newline at end of file diff --git a/filterConfig.yml b/filterConfig.yml deleted file mode 100644 index e4aef4c..0000000 --- a/filterConfig.yml +++ /dev/null @@ -1,22 +0,0 @@ -apiRules: -- exclude: - uidRegex: ^AOT -- exclude: - uidRegex: ^JetBrains -- exclude: - uidRegex: ^TreeEditor -- exclude: - uidRegex: ^Unity\.CodeEditor -- exclude: - uidRegex: ^UnityEditorInternal -- exclude: - uidRegex: ^UnityEditor\.InspectorMode -- exclude: - uidRegex: ^UnityEngineInternal -- exclude: - uidRegex: ^UnityEngine.Internal -- exclude: - uidRegex: Finalize$ -- exclude: - hasAttribute: - uid: UnityEngine.Internal.ExcludeFromDocsAttribute \ No newline at end of file