├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── dotnet.yml ├── .gitignore ├── CODE-OF-CONDUCT.md ├── LICENSE ├── README.md ├── build.proj ├── docs ├── PortToDocs.md └── PortToTripleSlash.md ├── install-as-tool.ps1 ├── nuget.config └── src ├── Common ├── src │ └── Log.cs └── tests │ ├── BasePortTests.cs │ ├── FileTestData.cs │ └── TestDirectory.cs ├── Directory.Build.props ├── PortToDocs ├── PortToDocs.sln ├── src │ ├── app │ │ ├── PortToDocs.cs │ │ └── PortToDocs.csproj │ └── libraries │ │ ├── Configuration.cs │ │ ├── Docs │ │ ├── APIKind.cs │ │ ├── DocsAPI.cs │ │ ├── DocsAssemblyInfo.cs │ │ ├── DocsAttribute.cs │ │ ├── DocsCommentsContainer.cs │ │ ├── DocsException.cs │ │ ├── DocsMember.cs │ │ ├── DocsMemberSignature.cs │ │ ├── DocsParam.cs │ │ ├── DocsParameter.cs │ │ ├── DocsRelated.cs │ │ ├── DocsSeeAlso.cs │ │ ├── DocsType.cs │ │ ├── DocsTypeParam.cs │ │ ├── DocsTypeParameter.cs │ │ ├── DocsTypeSignature.cs │ │ └── IDocsAPI.cs │ │ ├── Extensions.cs │ │ ├── IntelliSenseXml │ │ ├── IntelliSenseXmlCommentsContainer.cs │ │ ├── IntelliSenseXmlException.cs │ │ ├── IntelliSenseXmlMember.cs │ │ ├── IntelliSenseXmlParam.cs │ │ ├── IntelliSenseXmlSeeAlso.cs │ │ └── IntelliSenseXmlTypeParam.cs │ │ ├── ToDocsPorter.cs │ │ ├── XmlHelper.cs │ │ └── libraries.csproj └── tests │ ├── PortToDocs.FileSystem.Tests.cs │ ├── PortToDocs.Strings.Tests.cs │ ├── StringTestData.cs │ ├── TestData │ ├── AssemblyAndNamespaceDifferent │ │ ├── intellisense │ │ │ └── MyAssembly │ │ │ │ └── MyAssembly.xml │ │ ├── xml │ │ │ └── MyAssembly │ │ │ │ └── MyType.xml │ │ └── xml_expected │ │ │ └── MyAssembly │ │ │ └── MyType.xml │ ├── AssemblyAndNamespaceSame │ │ ├── intellisense │ │ │ └── MyAssembly │ │ │ │ └── MyAssembly.xml │ │ ├── xml │ │ │ └── MyAssembly │ │ │ │ └── MyType.xml │ │ └── xml_expected │ │ │ └── MyAssembly │ │ │ └── MyType.xml │ ├── Basic │ │ ├── intellisense │ │ │ └── MyAssembly │ │ │ │ └── MyAssembly.xml │ │ ├── xml │ │ │ └── MyAssembly │ │ │ │ └── MyType.xml │ │ └── xml_expected │ │ │ └── MyAssembly │ │ │ └── MyType.xml │ ├── DontAddMissingRemarks │ │ ├── intellisense │ │ │ └── MyAssembly │ │ │ │ └── MyAssembly.xml │ │ ├── xml │ │ │ └── MyAssembly │ │ │ │ └── MyType.xml │ │ └── xml_expected │ │ │ └── MyAssembly │ │ │ └── MyType.xml │ ├── EnumRemarks │ │ ├── intellisense │ │ │ └── MyAssembly │ │ │ │ └── MyAssembly.xml │ │ ├── xml │ │ │ └── MyAssembly │ │ │ │ └── MyType.xml │ │ └── xml_expected │ │ │ └── MyAssembly │ │ │ └── MyType.xml │ ├── Exception_ExistingCref │ │ ├── intellisense │ │ │ └── MyAssembly │ │ │ │ └── MyAssembly.xml │ │ ├── xml │ │ │ └── MyAssembly │ │ │ │ └── MyType.xml │ │ └── xml_expected │ │ │ └── MyAssembly │ │ │ └── MyType.xml │ ├── Exceptions │ │ ├── intellisense │ │ │ └── MyAssembly │ │ │ │ └── MyAssembly.xml │ │ ├── xml │ │ │ └── MyAssembly │ │ │ │ └── MyType.xml │ │ └── xml_expected │ │ │ └── MyAssembly │ │ │ └── MyType.xml │ ├── InheritDoc │ │ ├── intellisense │ │ │ ├── MyAssembly │ │ │ │ └── MyAssembly.xml │ │ │ └── System │ │ │ │ └── System.xml │ │ ├── xml │ │ │ ├── MyAssembly │ │ │ │ └── MyType.xml │ │ │ └── System │ │ │ │ └── MyParentType.xml │ │ └── xml_expected │ │ │ ├── MyAssembly │ │ │ └── MyType.xml │ │ │ └── System │ │ │ └── MyParentType.xml │ ├── Remarks_NoEII_NoInterfaceRemarks │ │ ├── intellisense │ │ │ ├── MyAssembly │ │ │ │ └── MyAssembly.xml │ │ │ └── System │ │ │ │ └── System.xml │ │ ├── xml │ │ │ └── MyAssembly │ │ │ │ └── MyType.xml │ │ └── xml_expected │ │ │ └── MyAssembly │ │ │ └── MyType.xml │ ├── Remarks_WithEII_NoInterfaceRemarks │ │ ├── intellisense │ │ │ └── MyAssembly │ │ │ │ └── MyAssembly.xml │ │ ├── xml │ │ │ └── MyAssembly │ │ │ │ ├── MyType.xml │ │ │ │ └── System │ │ │ │ └── System.xml │ │ └── xml_expected │ │ │ └── MyAssembly │ │ │ └── MyType.xml │ └── Remarks_WithEII_WithInterfaceRemarks │ │ ├── intellisense │ │ └── MyAssembly │ │ │ └── MyAssembly.xml │ │ ├── xml │ │ └── MyAssembly │ │ │ ├── MyType.xml │ │ │ └── System │ │ │ └── System.xml │ │ └── xml_expected │ │ └── MyAssembly │ │ └── MyType.xml │ └── tests.csproj └── PortToTripleSlash ├── PortToTripleSlash.sln ├── src ├── app │ ├── PortToTripleSlash.cs │ └── PortToTripleSlash.csproj └── libraries │ ├── AllTypesVisitor.cs │ ├── Configuration.cs │ ├── Docs │ ├── APIKind.cs │ ├── DocsAPI.cs │ ├── DocsAssemblyInfo.cs │ ├── DocsAttribute.cs │ ├── DocsCommentsContainer.cs │ ├── DocsException.cs │ ├── DocsMember.cs │ ├── DocsMemberSignature.cs │ ├── DocsParam.cs │ ├── DocsParameter.cs │ ├── DocsRelated.cs │ ├── DocsType.cs │ ├── DocsTypeParam.cs │ ├── DocsTypeParameter.cs │ ├── DocsTypeSignature.cs │ └── IDocsAPI.cs │ ├── Extensions.cs │ ├── MSBuildLoader.cs │ ├── ResolvedLocation.cs │ ├── ResolvedProject.cs │ ├── ResolvedWorkspace.cs │ ├── RoslynTripleSlash │ ├── DocumentationUpdater.cs │ └── TripleSlashSyntaxRewriter.cs │ ├── ToTripleSlashPorter.cs │ ├── VSLoader.cs │ ├── XmlHelper.cs │ └── libraries.csproj └── tests ├── PortToTripleSlash ├── PortToTripleSlash.FileSystem.Tests.cs ├── PortToTripleSlash.Strings.Tests.cs ├── PortToTripleSlashTestData.cs ├── StringTestData.cs └── TestData │ └── Basic │ ├── MyAssembly.csproj │ ├── MyDelegate.xml │ ├── MyEnum.xml │ ├── MyType.xml │ ├── SourceExpected.cs │ └── SourceOriginal.cs └── tests.csproj /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behavior to automatically normalize line endings. 2 | * text=auto eol=lf 3 | 4 | *.doc diff=astextplain 5 | *.DOC diff=astextplain 6 | *.docx diff=astextplain 7 | *.DOCX diff=astextplain 8 | *.dot diff=astextplain 9 | *.DOT diff=astextplain 10 | *.pdf diff=astextplain 11 | *.PDF diff=astextplain 12 | *.rtf diff=astextplain 13 | *.RTF diff=astextplain 14 | 15 | *.jpg binary 16 | *.png binary 17 | *.gif binary 18 | 19 | # Force bash scripts to always use lf line endings so that if a repo is accessed 20 | # in Unix via a file share from Windows, the scripts will work. 21 | *.in text eol=lf 22 | *.sh text eol=lf 23 | 24 | # Likewise, force cmd and batch scripts to always use crlf 25 | *.cmd text eol=crlf 26 | *.bat text eol=crlf 27 | 28 | *.cs text=auto diff=csharp 29 | *.vb text=auto 30 | *.resx text=auto 31 | *.c text=auto 32 | *.cpp text=auto 33 | *.cxx text=auto 34 | *.h text=auto 35 | *.hxx text=auto 36 | *.py text=auto 37 | *.rb text=auto 38 | *.java text=auto 39 | *.html text=auto 40 | *.htm text=auto 41 | *.css text=auto 42 | *.scss text=auto 43 | *.sass text=auto 44 | *.less text=auto 45 | *.js text=auto 46 | *.lisp text=auto 47 | *.clj text=auto 48 | *.sql text=auto 49 | *.php text=auto 50 | *.lua text=auto 51 | *.m text=auto 52 | *.asm text=auto 53 | *.erl text=auto 54 | *.fs text=auto 55 | *.fsx text=auto 56 | *.hs text=auto 57 | *.xml text=auto 58 | 59 | *.csproj text=auto 60 | *.vbproj text=auto 61 | *.fsproj text=auto 62 | *.dbproj text=auto 63 | *.sln text=auto eol=crlf 64 | 65 | # Set linguist language for .h files explicitly based on 66 | # https://github.com/github/linguist/issues/1626#issuecomment-401442069 67 | # this only affects the repo's language statistics 68 | *.h linguist-language=C 69 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: .NET 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Setup .NET 17 | uses: actions/setup-dotnet@v1 18 | with: 19 | dotnet-version: '8.0.x' 20 | - name: Restore dependencies of PortToTripleSlash 21 | run: dotnet restore src/PortToTripleSlash/PortToTripleSlash.sln 22 | - name: Build PortToTripleSlash 23 | run: dotnet build --no-restore src/PortToTripleSlash/PortToTripleSlash.sln 24 | # Re-enable when the msbuild failure is fixed, to prevent skipping the subsequent tasks 25 | - name: Test PortToTripleSlash 26 | run: dotnet test --no-build --verbosity normal src/PortToTripleSlash/PortToTripleSlash.sln 27 | - name: Restore dependencies of PortToDocs 28 | run: dotnet restore src/PortToDocs/PortToDocs.sln 29 | - name: Build PortToDocs 30 | run: dotnet build --no-restore src/PortToDocs/PortToDocs.sln 31 | - name: Test PortToDocs 32 | run: dotnet test --no-build --verbosity normal src/PortToDocs/PortToDocs.sln 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | 3 | ### VisualStudio ### 4 | 5 | # Tools directory 6 | /[Tt]ools/ 7 | .dotnet/ 8 | .packages/ 9 | 10 | # User-specific files 11 | *.suo 12 | *.user 13 | *.userosscache 14 | *.sln.docstates 15 | launchSettings.json 16 | 17 | # Live Unit Tests 18 | .lutignore 19 | *.lutconfig 20 | 21 | # Build results 22 | 23 | artifacts/ 24 | artifacts_stage_1/ 25 | [Dd]ebug/ 26 | [Rr]elease/ 27 | x64/ 28 | x86/ !eng/common/cross/x86/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | msbuild.log 32 | msbuild.err 33 | msbuild.wrn 34 | msbuild.binlog 35 | 36 | # Visual Studio 2015 37 | .vs/ 38 | 39 | # Visual Studio 2015 Pre-CTP6 40 | *.sln.ide 41 | *.ide/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | #NUNIT 48 | *.VisualState.xml 49 | TestResult.xml 50 | 51 | # ReSharper is a .NET coding add-in 52 | _ReSharper*/ 53 | *.[Rr]e[Ss]harper 54 | *.DotSettings.user 55 | 56 | # DotCover is a Code Coverage Tool 57 | *.dotCover 58 | 59 | # NuGet Packages 60 | *.nuget.props 61 | *.nuget.targets 62 | *.nupkg 63 | **/packages/* 64 | 65 | ### Windows ### 66 | 67 | # Windows image file caches 68 | Thumbs.db 69 | ehthumbs.db 70 | 71 | # Folder config file 72 | Desktop.ini 73 | 74 | # Recycle Bin used on file shares 75 | $RECYCLE.BIN/ 76 | 77 | # Windows Installer files 78 | *.cab 79 | *.msi 80 | *.msm 81 | *.msp 82 | 83 | # Windows shortcuts 84 | *.lnk 85 | 86 | ### Linux ### 87 | 88 | *~ 89 | 90 | # KDE directory preferences 91 | .directory 92 | 93 | ### OSX ### 94 | 95 | .DS_Store 96 | .AppleDouble 97 | .LSOverride 98 | 99 | # Icon must end with two \r 100 | Icon 101 | 102 | # Thumbnails 103 | ._* 104 | 105 | # Files that might appear on external disk 106 | .Spotlight-V100 107 | .Trashes 108 | 109 | # Directories potentially created on remote AFP share 110 | .AppleDB 111 | .AppleDesktop 112 | Network Trash Folder 113 | Temporary Items 114 | .apdisk 115 | 116 | # vim temporary files 117 | [._]*.s[a-w][a-z] 118 | [._]s[a-w][a-z] 119 | *.un~ 120 | Session.vim 121 | .netrwhist 122 | *~ 123 | 124 | # Visual Studio Code 125 | .vscode/ 126 | 127 | # Private test configuration and binaries. 128 | config.ps1 129 | **/IISApplications 130 | 131 | 132 | # Node.js modules 133 | node_modules/ 134 | 135 | # Python Compile Outputs 136 | 137 | *.pyc 138 | -------------------------------------------------------------------------------- /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This project has adopted the code of conduct defined by the Contributor Covenant 4 | to clarify expected behavior in our community. 5 | For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) .NET Foundation and Contributors 4 | 5 | All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # API Docs Sync 2 | 3 | This repo contains two tools that allow porting documentation in two directions: 4 | 5 | - IntelliSense xml files to MS Docs xml files. [Instructions](docs/PortToDocs.md). 6 | 7 | - MS Docs xml files to triple slash comments in source code. [Instructions](docs/PortToTripleSlash.md). 8 | 9 | ## Requirements 10 | 11 | - [.NET 8.0 SDK](https://get.dot.net/) 12 | - A local git clone of a dotnet repo with source code whose APIs live in an API docs repo. Examples: 13 | - [dotnet/runtime](https://github.com/dotnet/runtime) 14 | - [dotnet/winforms](https://github.com/dotnet/winforms) 15 | - [dotnet/wpf](https://github.com/dotnet/wpf) 16 | - [dotnet/wcf](https://github.com/dotnet/wcf) 17 | - A local git clone of the API docs repo where the above project hosts its documentation. For example, all the repos listed above host their documentation in the [dotnet-api-docs repo](https://github.com/dotnet/dotnet-api-docs). 18 | 19 | ## Install as dotnet tools 20 | 21 | To install the two tools as global dotnet tools in your `$PATH`, run the `install-as-tool.ps1` script. 22 | 23 | Documentation for global dotnet tools: https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-tool-install 24 | 25 | Remember to update the tool periodically to collect the latest changes. Updating instructions: https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-tool-update 26 | -------------------------------------------------------------------------------- /build.proj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/PortToDocs.md: -------------------------------------------------------------------------------- 1 | # PortToDocs 2 | 3 | This tool ports intellisense xml documentation to API docs. 4 | 5 | The way it works is the following: 6 | 7 | - You specify the source dotnet repo (for example: runtime, winforms, wpf, wcf, etc.). 8 | - You specify the API docs target repo (for example: dotnet-api-docs). 9 | - You specify a list of assemblies to port, and optionally, a list of namespaces and/or types. 10 | - The tool will then find all APIs that match the specified filters, both among the intellisense xml files of the dotnet repo and in the xml files of the API docs repo. 11 | - If an API docs xml item is still undocumented (it has the `To be added.` boilerplate message), then the tool will copy all the documentation it can find for that API in its intellisense xml file, and paste it in the API docs xml item. 12 | 13 | ## Instructions 14 | 15 | 1. Clone and build your source code dotnet repo (for example: runtime, winforms, wpf, wcf, etc.). 16 | 2. Clone the API docs repo (for example: dotnet-api-docs). No need to build it. 17 | 3. Clone this repo. 18 | 4. Run the command to port documentation: 19 | 20 | ```cmd 21 | -Docs 22 | -IntelliSense [,,...,] 23 | -IncludedAssemblies [,,...] 24 | -IncludedNamespaces [,,...,] 25 | -Save true 26 | ``` 27 | 28 | Example: 29 | 30 | ```cmd 31 | PortToDocs \ 32 | -Docs D:\dotnet-api-docs\xml \ 33 | -IntelliSense D:\runtime\artifacts\bin\System.IO.FileSystem\ \ 34 | -IncludedAssemblies System.IO.FileSystem \ 35 | -IncludedNamespaces System.IO \ 36 | -Save true 37 | ``` 38 | 39 | To view all the available CLI arguments, run: 40 | 41 | ```cmd 42 | PortToDocs -h 43 | ``` 44 | -------------------------------------------------------------------------------- /install-as-tool.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | Function InstallAsTool 4 | { 5 | Param( 6 | [string] 7 | [ValidateNotNullOrEmpty()] 8 | $APP_NAME 9 | ) 10 | 11 | Write-Output "Installing '$APP_NAME' as tool..." 12 | 13 | $ARTIFACTS_DIR = "artifacts/$APP_NAME" 14 | $BUILD_CONFIGURATION = "Release" 15 | $EXE_PROJECT = "src/$APP_NAME/src/app/$APP_NAME.csproj" 16 | 17 | Write-Output "Cleaning..." 18 | $COMMAND="dotnet clean -c $BUILD_CONFIGURATION; Remove-Item -Recurse -ErrorAction Ignore $ARTIFACTS_DIR" 19 | Write-Output $COMMAND 20 | Invoke-Expression -Command $COMMAND 21 | 22 | Write-Output "Packing..." 23 | $COMMAND="dotnet pack -c $BUILD_CONFIGURATION -o $ARTIFACTS_DIR $EXE_PROJECT" 24 | Write-Output $COMMAND 25 | Invoke-Expression -Command $COMMAND 26 | 27 | If ($LASTEXITCODE -ne 0) 28 | { 29 | Write-Output "$APP_NAME will not be installed/upgraded." 30 | Exit 31 | } 32 | 33 | Write-Output "Updating tool..." 34 | $COMMAND="dotnet tool update --global --add-source $ARTIFACTS_DIR $APP_NAME" 35 | Write-Output $COMMAND 36 | Invoke-Expression -Command $COMMAND 37 | } 38 | 39 | $ErrorActionPreference = "Stop" 40 | Push-Location $(Split-Path $MyInvocation.MyCommand.Path) 41 | 42 | InstallAsTool "PortToTripleSlash" 43 | InstallAsTool "PortToDocs" -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Common/src/Log.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | 6 | namespace ApiDocsSync 7 | { 8 | public static class Log 9 | { 10 | public static void Print(bool endline, ConsoleColor foregroundColor, string format, params object[]? args) 11 | { 12 | ConsoleColor originalColor = Console.ForegroundColor; 13 | Console.ForegroundColor = foregroundColor; 14 | 15 | string msg = args != null ? (args.Length > 0 ? string.Format(format, args) : format) : format; 16 | if (endline) 17 | { 18 | Console.WriteLine(msg); 19 | } 20 | else 21 | { 22 | Console.Write(msg); 23 | } 24 | Console.ForegroundColor = originalColor; 25 | } 26 | 27 | public static void Info(string format) 28 | { 29 | Info(format, null); 30 | } 31 | 32 | public static void Info(string format, params object[]? args) 33 | { 34 | Info(true, format, args); 35 | } 36 | 37 | public static void Info(bool endline, string format, params object[]? args) 38 | { 39 | Print(endline, ConsoleColor.White, format, args); 40 | } 41 | 42 | public static void Success(string format) 43 | { 44 | Success(format, null); 45 | } 46 | 47 | public static void Success(string format, params object[]? args) 48 | { 49 | Success(true, format, args); 50 | } 51 | 52 | public static void Success(bool endline, string format, params object[]? args) 53 | { 54 | Print(endline, ConsoleColor.Green, format, args); 55 | } 56 | 57 | public static void Warning(string format) 58 | { 59 | Warning(format, null); 60 | } 61 | 62 | public static void Warning(string format, params object[]? args) 63 | { 64 | Warning(true, format, args); 65 | } 66 | 67 | public static void Warning(bool endline, string format, params object[]? args) 68 | { 69 | Print(endline, ConsoleColor.Yellow, format, args); 70 | } 71 | 72 | public static void Error(string format) 73 | { 74 | Error(format, null); 75 | } 76 | 77 | public static void Error(string format, params object[]? args) 78 | { 79 | Error(true, format, args); 80 | } 81 | 82 | public static void Error(bool endline, string format, params object[]? args) 83 | { 84 | Print(endline, ConsoleColor.Red, format, args); 85 | } 86 | 87 | public static void Cyan(string format) 88 | { 89 | Cyan(format, null); 90 | } 91 | 92 | public static void Cyan(string format, params object[]? args) 93 | { 94 | Cyan(true, format, args); 95 | } 96 | 97 | public static void Cyan(bool endline, string format, params object[]? args) 98 | { 99 | Print(endline, ConsoleColor.Cyan, format, args); 100 | } 101 | 102 | public static void Magenta(bool endline, string format, params object[]? args) 103 | { 104 | Print(endline, ConsoleColor.Magenta, format, args); 105 | } 106 | 107 | public static void Magenta(string format) 108 | { 109 | Magenta(format, null); 110 | } 111 | 112 | public static void Magenta(string format, params object[]? args) 113 | { 114 | Magenta(true, format, args); 115 | } 116 | 117 | public static void DarkYellow(bool endline, string format, params object[]? args) 118 | { 119 | Print(endline, ConsoleColor.DarkYellow, format, args); 120 | } 121 | 122 | public static void DarkYellow(string format) 123 | { 124 | DarkYellow(format, null); 125 | } 126 | 127 | public static void DarkYellow(string format, params object[]? args) 128 | { 129 | DarkYellow(true, format, args); 130 | } 131 | 132 | public static void Assert(bool condition, string format) 133 | { 134 | Assert(true, condition, format, null); 135 | } 136 | 137 | public static void Assert(bool condition, string format, params object[]? args) 138 | { 139 | Assert(true, condition, format, args); 140 | } 141 | 142 | public static void Assert(bool endline, bool condition, string format, params object[]? args) 143 | { 144 | if (condition) 145 | { 146 | Success(endline, format, args); 147 | } 148 | else 149 | { 150 | string msg = args != null ? string.Format(format, args) : format; 151 | throw new Exception(msg); 152 | } 153 | } 154 | 155 | public static void Line() 156 | { 157 | Print(endline: true, Console.ForegroundColor, "", null); 158 | } 159 | 160 | public delegate void PrintHelpFunction(); 161 | 162 | public static void ErrorAndExit(string format, params object[]? args) 163 | { 164 | Error(format, args); 165 | Cyan("Use the -h|-help argument to view the usage instructions."); 166 | Environment.Exit(-1); 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/Common/tests/BasePortTests.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using Xunit.Abstractions; 5 | 6 | namespace ApiDocsSync.Tests 7 | { 8 | public abstract class BasePortTests 9 | { 10 | protected ITestOutputHelper Output { get; private set; } 11 | 12 | public BasePortTests(ITestOutputHelper output) => Output = output; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Common/tests/FileTestData.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.IO; 5 | 6 | namespace ApiDocsSync.Tests 7 | { 8 | internal class FileTestData 9 | { 10 | internal const string TestAssembly = "MyAssembly"; 11 | internal const string TestNamespace = "MyNamespace"; 12 | internal const string TestType = "MyType"; 13 | internal const string DocsDirName = "Docs"; 14 | 15 | internal string ExpectedFilePath { get; set; } 16 | internal string ActualFilePath { get; set; } 17 | internal DirectoryInfo DocsDir { get; set; } 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Common/tests/TestDirectory.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.IO; 6 | using Xunit; 7 | 8 | namespace ApiDocsSync.Tests 9 | { 10 | public class TestDirectory : IDisposable 11 | { 12 | private readonly DirectoryInfo DirInfo; 13 | 14 | public string FullPath => DirInfo.FullName; 15 | 16 | public TestDirectory() 17 | { 18 | string path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); 19 | DirInfo = new DirectoryInfo(path); 20 | DirInfo.Create(); 21 | Assert.True(DirInfo.Exists, "Verify root test directory exists."); 22 | } 23 | 24 | public DirectoryInfo CreateSubdirectory(string dirName) 25 | { 26 | return DirInfo.CreateSubdirectory(dirName); 27 | } 28 | 29 | public void Dispose() 30 | { 31 | try 32 | { 33 | DirInfo.Delete(recursive: true); 34 | } 35 | catch 36 | { 37 | } 38 | GC.SuppressFinalize(this); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/PortToDocs/PortToDocs.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32220.68 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PortToDocs", "src\app\PortToDocs.csproj", "{E92246CD-548D-4C08-BA43-594663E78100}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "libraries", "src\libraries\libraries.csproj", "{87BBF4FD-260C-4AC4-802B-7D2B29629C07}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "tests", "tests\tests.csproj", "{81FEFEA4-8FF5-482E-A33D-D3F351D3F7B6}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FCF7315B-C7FA-4D89-96A1-AA9081B16D3B}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {E92246CD-548D-4C08-BA43-594663E78100}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {E92246CD-548D-4C08-BA43-594663E78100}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {E92246CD-548D-4C08-BA43-594663E78100}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {E92246CD-548D-4C08-BA43-594663E78100}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {87BBF4FD-260C-4AC4-802B-7D2B29629C07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {87BBF4FD-260C-4AC4-802B-7D2B29629C07}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {87BBF4FD-260C-4AC4-802B-7D2B29629C07}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {87BBF4FD-260C-4AC4-802B-7D2B29629C07}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {81FEFEA4-8FF5-482E-A33D-D3F351D3F7B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {81FEFEA4-8FF5-482E-A33D-D3F351D3F7B6}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {81FEFEA4-8FF5-482E-A33D-D3F351D3F7B6}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {81FEFEA4-8FF5-482E-A33D-D3F351D3F7B6}.Release|Any CPU.Build.0 = Release|Any CPU 32 | EndGlobalSection 33 | GlobalSection(SolutionProperties) = preSolution 34 | HideSolutionNode = FALSE 35 | EndGlobalSection 36 | GlobalSection(ExtensibilityGlobals) = postSolution 37 | SolutionGuid = {0AE9A370-CB75-4D51-A4A3-B7ADA38970B8} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /src/PortToDocs/src/app/PortToDocs.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace ApiDocsSync.PortToDocs 5 | { 6 | class PortToDocs 7 | { 8 | public static void Main(string[] args) 9 | { 10 | Configuration config = Configuration.GetCLIArguments(args); 11 | ToDocsPorter porter = new(config); 12 | porter.CollectFiles(); 13 | porter.Start(); 14 | porter.SaveToDisk(); 15 | porter.PrintSummary(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/PortToDocs/src/app/PortToDocs.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | ApiDocsSync.PortToDocs.PortToDocs 7 | enable 8 | true 9 | true 10 | 1.5 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/Docs/APIKind.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace ApiDocsSync.PortToDocs.Docs 5 | { 6 | internal enum APIKind 7 | { 8 | Type, 9 | Member 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/Docs/DocsAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Xml.Linq; 7 | 8 | namespace ApiDocsSync.PortToDocs.Docs 9 | { 10 | internal class DocsAssemblyInfo 11 | { 12 | private readonly XElement XEAssemblyInfo; 13 | public string AssemblyName 14 | { 15 | get 16 | { 17 | return XmlHelper.GetChildElementValue(XEAssemblyInfo, "AssemblyName"); 18 | } 19 | } 20 | 21 | private List? _assemblyVersions; 22 | public List AssemblyVersions 23 | { 24 | get 25 | { 26 | if (_assemblyVersions == null) 27 | { 28 | _assemblyVersions = XEAssemblyInfo.Elements("AssemblyVersion").Select(x => XmlHelper.GetNodesInPlainText(x)).ToList(); 29 | } 30 | return _assemblyVersions; 31 | } 32 | } 33 | 34 | public DocsAssemblyInfo(XElement xeAssemblyInfo) 35 | { 36 | XEAssemblyInfo = xeAssemblyInfo; 37 | } 38 | 39 | public override string ToString() => AssemblyName; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/Docs/DocsAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToDocs.Docs 7 | { 8 | internal class DocsAttribute 9 | { 10 | private readonly XElement XEAttribute; 11 | 12 | public string FrameworkAlternate 13 | { 14 | get 15 | { 16 | return XmlHelper.GetAttributeValue(XEAttribute, "FrameworkAlternate"); 17 | } 18 | } 19 | public string AttributeName 20 | { 21 | get 22 | { 23 | return XmlHelper.GetChildElementValue(XEAttribute, "AttributeName"); 24 | } 25 | } 26 | 27 | public DocsAttribute(XElement xeAttribute) 28 | { 29 | XEAttribute = xeAttribute; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/Docs/DocsException.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Xml.Linq; 7 | 8 | namespace ApiDocsSync.PortToDocs.Docs 9 | { 10 | internal class DocsException 11 | { 12 | private readonly XElement XEException; 13 | 14 | public IDocsAPI ParentAPI 15 | { 16 | get; private set; 17 | } 18 | 19 | public string Cref => XmlHelper.GetAttributeValue(XEException, "cref"); 20 | 21 | public string Value 22 | { 23 | get => XmlHelper.GetNodesInPlainText(XEException); 24 | private set => XmlHelper.SaveFormattedAsXml(XEException, value); 25 | } 26 | 27 | public string OriginalValue { get; private set; } 28 | 29 | public DocsException(IDocsAPI parentAPI, XElement xException) 30 | { 31 | ParentAPI = parentAPI; 32 | XEException = xException; 33 | OriginalValue = Value; 34 | } 35 | 36 | public void AppendException(string toAppend) 37 | { 38 | XmlHelper.AppendFormattedAsXml(XEException, $"\n\n-or-\n\n{toAppend}", removeUndesiredEndlines: false); 39 | ParentAPI.Changed = true; 40 | } 41 | 42 | public bool WordCountCollidesAboveThreshold(string intelliSenseXmlValue, int threshold) 43 | { 44 | Dictionary hashIntelliSenseXml = GetHash(intelliSenseXmlValue); 45 | Dictionary hashDocs = GetHash(Value); 46 | 47 | int collisions = 0; 48 | // Iterate all the words of the IntelliSense xml exception string 49 | foreach (KeyValuePair word in hashIntelliSenseXml) 50 | { 51 | // Check if the existing Docs string contained that word 52 | if (hashDocs.ContainsKey(word.Key)) 53 | { 54 | // If the total found in Docs is >= than the total found in IntelliSense xml 55 | // then consider it a collision 56 | if (hashDocs[word.Key] >= word.Value) 57 | { 58 | collisions++; 59 | } 60 | } 61 | } 62 | 63 | // If the number of word collisions is above the threshold, it probably means 64 | // that part of the original TS string was included in the Docs string 65 | double collisionPercentage = (collisions * 100 / (double)hashIntelliSenseXml.Count); 66 | return collisionPercentage >= threshold; 67 | } 68 | 69 | public override string ToString() 70 | { 71 | return $"{Cref} - {Value}"; 72 | } 73 | 74 | // Gets a dictionary with the count of each character found in the string. 75 | private Dictionary GetHash(string value) 76 | { 77 | Dictionary hash = new Dictionary(); 78 | string[] words = value.Split(new char[] { ' ', '\'', '"', '\r', '\n', '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); 79 | 80 | foreach (string word in words) 81 | { 82 | if (hash.ContainsKey(word)) 83 | { 84 | hash[word]++; 85 | } 86 | else 87 | { 88 | hash.Add(word, 1); 89 | } 90 | } 91 | return hash; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/Docs/DocsMember.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Xml.Linq; 8 | 9 | namespace ApiDocsSync.PortToDocs.Docs 10 | { 11 | internal class DocsMember : DocsAPI 12 | { 13 | private string? _memberName; 14 | private List? _memberSignatures; 15 | private List? _exceptions; 16 | 17 | public DocsMember(string filePath, DocsType parentType, XElement xeMember) 18 | : base(xeMember) 19 | { 20 | FilePath = filePath; 21 | ParentType = parentType; 22 | AssemblyInfos.AddRange(XERoot.Elements("AssemblyInfo").Select(x => new DocsAssemblyInfo(x))); 23 | } 24 | 25 | public DocsType ParentType { get; private set; } 26 | 27 | public override bool Changed 28 | { 29 | get => ParentType.Changed; 30 | set => ParentType.Changed |= value; 31 | } 32 | public bool IsProperty => MemberType == "Property"; 33 | 34 | public bool IsMethod => MemberType == "Method"; 35 | 36 | public string MemberName 37 | { 38 | get 39 | { 40 | if (_memberName == null) 41 | { 42 | _memberName = XmlHelper.GetAttributeValue(XERoot, "MemberName"); 43 | } 44 | return _memberName; 45 | } 46 | } 47 | 48 | public List MemberSignatures 49 | { 50 | get 51 | { 52 | if (_memberSignatures == null) 53 | { 54 | _memberSignatures = XERoot.Elements("MemberSignature").Select(x => new DocsMemberSignature(x)).ToList(); 55 | } 56 | return _memberSignatures; 57 | } 58 | } 59 | 60 | public string MemberType 61 | { 62 | get 63 | { 64 | return XmlHelper.GetChildElementValue(XERoot, "MemberType"); 65 | } 66 | } 67 | 68 | public string ImplementsInterfaceMember 69 | { 70 | get 71 | { 72 | XElement? xeImplements = XERoot.Element("Implements"); 73 | return (xeImplements != null) ? XmlHelper.GetChildElementValue(xeImplements, "InterfaceMember") : string.Empty; 74 | } 75 | } 76 | 77 | public override string ReturnType 78 | { 79 | get 80 | { 81 | XElement? xeReturnValue = XERoot.Element("ReturnValue"); 82 | if (xeReturnValue != null) 83 | { 84 | return XmlHelper.GetChildElementValue(xeReturnValue, "ReturnType"); 85 | } 86 | return string.Empty; 87 | } 88 | } 89 | 90 | public override string Returns 91 | { 92 | get 93 | { 94 | return (ReturnType != "System.Void") ? GetNodesInPlainText("returns") : string.Empty; 95 | } 96 | set 97 | { 98 | if (ReturnType != "System.Void") 99 | { 100 | SaveFormattedAsXml("returns", value, addIfMissing: false); 101 | } 102 | else 103 | { 104 | Log.Warning($"Attempted to save a returns item for a method that returns System.Void: {DocId}"); 105 | } 106 | } 107 | } 108 | 109 | public override string Summary 110 | { 111 | get 112 | { 113 | return GetNodesInPlainText("summary"); 114 | } 115 | set 116 | { 117 | SaveFormattedAsXml("summary", value, addIfMissing: true); 118 | } 119 | } 120 | 121 | public override string Remarks 122 | { 123 | get 124 | { 125 | return GetNodesInPlainText("remarks"); 126 | } 127 | set 128 | { 129 | SaveAsIs("remarks", value, addIfMissing: !value.IsDocsEmpty()); 130 | } 131 | } 132 | 133 | public string Value 134 | { 135 | get 136 | { 137 | return (IsProperty) ? GetNodesInPlainText("value") : string.Empty; 138 | } 139 | set 140 | { 141 | if (IsProperty) 142 | { 143 | SaveFormattedAsXml("value", value, addIfMissing: true); 144 | } 145 | else 146 | { 147 | Log.Warning($"Attempted to save a value element for an API that is not a property: {DocId}"); 148 | } 149 | } 150 | } 151 | 152 | public List Exceptions 153 | { 154 | get 155 | { 156 | if (_exceptions == null) 157 | { 158 | if (Docs != null) 159 | { 160 | _exceptions = Docs.Elements("exception").Select(x => new DocsException(this, x)).ToList(); 161 | } 162 | else 163 | { 164 | _exceptions = new List(); 165 | } 166 | } 167 | return _exceptions; 168 | } 169 | } 170 | 171 | public override string ToString() 172 | { 173 | return DocId; 174 | } 175 | 176 | public DocsException AddException(string cref, string value) 177 | { 178 | XElement exception = new XElement("exception"); 179 | exception.SetAttributeValue("cref", cref); 180 | XmlHelper.SaveFormattedAsXml(exception, value, removeUndesiredEndlines: false); 181 | Docs.Add(exception); 182 | Changed = true; 183 | return new DocsException(this, exception); 184 | } 185 | 186 | protected override string GetApiSignatureDocId() 187 | { 188 | DocsMemberSignature? dts = MemberSignatures.FirstOrDefault(x => x.Language == "DocId"); 189 | if (dts == null) 190 | { 191 | throw new FormatException($"DocId TypeSignature not found for {MemberName}"); 192 | } 193 | return dts.Value; 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/Docs/DocsMemberSignature.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToDocs.Docs 7 | { 8 | internal class DocsMemberSignature 9 | { 10 | private readonly XElement XEMemberSignature; 11 | 12 | public string Language 13 | { 14 | get 15 | { 16 | return XmlHelper.GetAttributeValue(XEMemberSignature, "Language"); 17 | } 18 | } 19 | 20 | public string Value 21 | { 22 | get 23 | { 24 | return XmlHelper.GetAttributeValue(XEMemberSignature, "Value"); 25 | } 26 | } 27 | 28 | public DocsMemberSignature(XElement xeMemberSignature) 29 | { 30 | XEMemberSignature = xeMemberSignature; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/Docs/DocsParam.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToDocs.Docs 7 | { 8 | internal class DocsParam 9 | { 10 | private readonly XElement XEDocsParam; 11 | public IDocsAPI ParentAPI 12 | { 13 | get; private set; 14 | } 15 | public string Name 16 | { 17 | get 18 | { 19 | return XmlHelper.GetAttributeValue(XEDocsParam, "name"); 20 | } 21 | } 22 | public string Value 23 | { 24 | get 25 | { 26 | return XmlHelper.GetNodesInPlainText(XEDocsParam); 27 | } 28 | set 29 | { 30 | XmlHelper.SaveFormattedAsXml(XEDocsParam, value); 31 | ParentAPI.Changed = true; 32 | } 33 | } 34 | public DocsParam(IDocsAPI parentAPI, XElement xeDocsParam) 35 | { 36 | ParentAPI = parentAPI; 37 | XEDocsParam = xeDocsParam; 38 | } 39 | public override string ToString() 40 | { 41 | return Name; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/Docs/DocsParameter.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToDocs.Docs 7 | { 8 | internal class DocsParameter 9 | { 10 | private readonly XElement XEParameter; 11 | public string Name 12 | { 13 | get 14 | { 15 | return XmlHelper.GetAttributeValue(XEParameter, "Name"); 16 | } 17 | } 18 | public string Type 19 | { 20 | get 21 | { 22 | return XmlHelper.GetAttributeValue(XEParameter, "Type"); 23 | } 24 | } 25 | public DocsParameter(XElement xeParameter) 26 | { 27 | XEParameter = xeParameter; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/Docs/DocsRelated.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToDocs.Docs 7 | { 8 | internal class DocsRelated 9 | { 10 | private readonly XElement XERelatedArticle; 11 | 12 | public IDocsAPI ParentAPI 13 | { 14 | get; private set; 15 | } 16 | 17 | public string ArticleType => XmlHelper.GetAttributeValue(XERelatedArticle, "type"); 18 | 19 | public string Href => XmlHelper.GetAttributeValue(XERelatedArticle, "href"); 20 | 21 | public string Value 22 | { 23 | get => XmlHelper.GetNodesInPlainText(XERelatedArticle); 24 | set 25 | { 26 | XmlHelper.SaveFormattedAsXml(XERelatedArticle, value); 27 | ParentAPI.Changed = true; 28 | } 29 | } 30 | 31 | public DocsRelated(IDocsAPI parentAPI, XElement xeRelatedArticle) 32 | { 33 | ParentAPI = parentAPI; 34 | XERelatedArticle = xeRelatedArticle; 35 | } 36 | 37 | public override string ToString() 38 | { 39 | return Value; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/Docs/DocsSeeAlso.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToDocs.Docs; 7 | 8 | internal class DocsSeeAlso 9 | { 10 | private readonly XElement XESeeAlso; 11 | 12 | public IDocsAPI ParentAPI 13 | { 14 | get; private set; 15 | } 16 | 17 | public string Cref => XmlHelper.GetAttributeValue(XESeeAlso, "cref"); 18 | 19 | public DocsSeeAlso(IDocsAPI parentAPI, XElement xSeeAlso) 20 | { 21 | ParentAPI = parentAPI; 22 | XESeeAlso = xSeeAlso; 23 | } 24 | 25 | public override string ToString() => $"seealso cref={Cref}"; 26 | } 27 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/Docs/DocsType.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Xml.Linq; 9 | 10 | namespace ApiDocsSync.PortToDocs.Docs 11 | { 12 | /// 13 | /// Represents the root xml element (unique) of a Docs xml file, called Type. 14 | /// 15 | internal class DocsType : DocsAPI 16 | { 17 | private string? _typeName; 18 | private string? _name; 19 | private string? _fullName; 20 | private string? _namespace; 21 | private string? _baseTypeName; 22 | private List? _interfaceNames; 23 | private List? _attributes; 24 | private List? _typesSignatures; 25 | 26 | public DocsType(string filePath, XDocument xDoc, XElement xeRoot, Encoding encoding) 27 | : base(xeRoot) 28 | { 29 | FilePath = filePath; 30 | XDoc = xDoc; 31 | FileEncoding = encoding; 32 | AssemblyInfos.AddRange(XERoot.Elements("AssemblyInfo").Select(x => new DocsAssemblyInfo(x))); 33 | } 34 | 35 | public XDocument XDoc { get; set; } 36 | 37 | public override bool Changed { get; set; } 38 | 39 | public Encoding FileEncoding { get; internal set; } 40 | 41 | public string TypeName 42 | { 43 | get 44 | { 45 | if (_typeName == null) 46 | { 47 | // DocId uses ` notation for generic types, but it uses . for nested types 48 | // Name uses + for nested types, but it uses <T> for generic types 49 | // We need ` notation for generic types and + notation for nested types 50 | // Only filename gives us that format, but we have to prepend the namespace 51 | if (DocId.Contains('`') || Name.Contains('+')) 52 | { 53 | _typeName = Namespace + "." + System.IO.Path.GetFileNameWithoutExtension(FilePath); 54 | } 55 | else 56 | { 57 | _typeName = FullName; 58 | } 59 | } 60 | return _typeName; 61 | } 62 | } 63 | 64 | public string Name 65 | { 66 | get 67 | { 68 | if (_name == null) 69 | { 70 | _name = XmlHelper.GetAttributeValue(XERoot, "Name"); 71 | } 72 | return _name; 73 | } 74 | } 75 | 76 | public string FullName 77 | { 78 | get 79 | { 80 | if (_fullName == null) 81 | { 82 | _fullName = XmlHelper.GetAttributeValue(XERoot, "FullName"); 83 | } 84 | return _fullName; 85 | } 86 | } 87 | 88 | public string Namespace 89 | { 90 | get 91 | { 92 | if (_namespace == null) 93 | { 94 | int lastDotPosition = FullName.LastIndexOf('.'); 95 | _namespace = lastDotPosition < 0 ? FullName : FullName.Substring(0, lastDotPosition); 96 | } 97 | return _namespace; 98 | } 99 | } 100 | 101 | public List TypeSignatures 102 | { 103 | get 104 | { 105 | if (_typesSignatures == null) 106 | { 107 | _typesSignatures = XERoot.Elements("TypeSignature").Select(x => new DocsTypeSignature(x)).ToList(); 108 | } 109 | return _typesSignatures; 110 | } 111 | } 112 | 113 | public XElement? Base 114 | { 115 | get 116 | { 117 | return XERoot.Element("Base"); 118 | } 119 | } 120 | 121 | public string BaseTypeName 122 | { 123 | get 124 | { 125 | if (Base == null) 126 | { 127 | _baseTypeName = string.Empty; 128 | } 129 | else if (_baseTypeName == null) 130 | { 131 | _baseTypeName = XmlHelper.GetChildElementValue(Base, "BaseTypeName"); 132 | } 133 | return _baseTypeName; 134 | } 135 | } 136 | 137 | public XElement? Interfaces 138 | { 139 | get 140 | { 141 | return XERoot.Element("Interfaces"); 142 | } 143 | } 144 | 145 | public List InterfaceNames 146 | { 147 | get 148 | { 149 | if (Interfaces == null) 150 | { 151 | _interfaceNames = new List(); 152 | } 153 | else if (_interfaceNames == null) 154 | { 155 | _interfaceNames = Interfaces.Elements("Interface").Select(x => XmlHelper.GetChildElementValue(x, "InterfaceName")).ToList(); 156 | } 157 | return _interfaceNames; 158 | } 159 | } 160 | 161 | public List Attributes 162 | { 163 | get 164 | { 165 | if (_attributes == null) 166 | { 167 | XElement? e = XERoot.Element("Attributes"); 168 | if (e == null) 169 | { 170 | _attributes = new(); 171 | } 172 | else 173 | { 174 | _attributes = (e != null) ? e.Elements("Attribute").Select(x => new DocsAttribute(x)).ToList() : new List(); 175 | } 176 | } 177 | return _attributes; 178 | } 179 | } 180 | 181 | public override string Summary 182 | { 183 | get 184 | { 185 | return GetNodesInPlainText("summary"); 186 | } 187 | set 188 | { 189 | SaveFormattedAsXml("summary", value, addIfMissing: true); 190 | } 191 | } 192 | 193 | /// 194 | /// Only available when the type is a delegate. 195 | /// 196 | public override string ReturnType 197 | { 198 | get 199 | { 200 | XElement? xeReturnValue = XERoot.Element("ReturnValue"); 201 | if (xeReturnValue != null) 202 | { 203 | return XmlHelper.GetChildElementValue(xeReturnValue, "ReturnType"); 204 | } 205 | return string.Empty; 206 | } 207 | } 208 | 209 | /// 210 | /// Only available when the type is a delegate. 211 | /// 212 | public override string Returns 213 | { 214 | get 215 | { 216 | return (ReturnType != "System.Void") ? GetNodesInPlainText("returns") : string.Empty; 217 | } 218 | set 219 | { 220 | if (ReturnType != "System.Void") 221 | { 222 | SaveFormattedAsXml("returns", value, addIfMissing: false); 223 | } 224 | else 225 | { 226 | Log.Warning($"Attempted to save a returns item for a method that returns System.Void: {DocId}"); 227 | } 228 | } 229 | } 230 | 231 | public override string Remarks 232 | { 233 | get 234 | { 235 | return GetNodesInPlainText("remarks"); 236 | } 237 | set 238 | { 239 | SaveAsIs("remarks", value, addIfMissing: !value.IsDocsEmpty()); 240 | } 241 | } 242 | 243 | public override string ToString() 244 | { 245 | return FullName; 246 | } 247 | 248 | protected override string GetApiSignatureDocId() 249 | { 250 | DocsTypeSignature? dts = TypeSignatures.FirstOrDefault(x => x.Language == "DocId"); 251 | if (dts == null) 252 | { 253 | throw new FormatException($"DocId TypeSignature not found for {FullName}"); 254 | } 255 | return dts.Value; 256 | } 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/Docs/DocsTypeParam.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToDocs.Docs 7 | { 8 | /// 9 | /// Each one of these typeparam objects live inside the Docs section inside the Member object. 10 | /// 11 | internal class DocsTypeParam 12 | { 13 | private readonly XElement XEDocsTypeParam; 14 | public IDocsAPI ParentAPI 15 | { 16 | get; private set; 17 | } 18 | 19 | public string Name 20 | { 21 | get 22 | { 23 | return XmlHelper.GetAttributeValue(XEDocsTypeParam, "name"); 24 | } 25 | } 26 | 27 | public string Value 28 | { 29 | get 30 | { 31 | return XmlHelper.GetNodesInPlainText(XEDocsTypeParam); 32 | } 33 | set 34 | { 35 | XmlHelper.SaveFormattedAsXml(XEDocsTypeParam, value); 36 | ParentAPI.Changed = true; 37 | } 38 | } 39 | 40 | public DocsTypeParam(IDocsAPI parentAPI, XElement xeDocsTypeParam) 41 | { 42 | ParentAPI = parentAPI; 43 | XEDocsTypeParam = xeDocsTypeParam; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/Docs/DocsTypeParameter.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Xml.Linq; 7 | 8 | namespace ApiDocsSync.PortToDocs.Docs 9 | { 10 | /// 11 | /// Each one of these TypeParameter objects islocated inside the TypeParameters section inside the Member. 12 | /// 13 | internal class DocsTypeParameter 14 | { 15 | private readonly XElement XETypeParameter; 16 | public string Name 17 | { 18 | get 19 | { 20 | return XmlHelper.GetAttributeValue(XETypeParameter, "Name"); 21 | } 22 | } 23 | private XElement? Constraints 24 | { 25 | get 26 | { 27 | return XETypeParameter.Element("Constraints"); 28 | } 29 | } 30 | private List? _constraintsParameterAttributes; 31 | public List ConstraintsParameterAttributes 32 | { 33 | get 34 | { 35 | if (_constraintsParameterAttributes == null) 36 | { 37 | if (Constraints != null) 38 | { 39 | _constraintsParameterAttributes = Constraints.Elements("ParameterAttribute").Select(x => XmlHelper.GetNodesInPlainText(x)).ToList(); 40 | } 41 | else 42 | { 43 | _constraintsParameterAttributes = new List(); 44 | } 45 | } 46 | return _constraintsParameterAttributes; 47 | } 48 | } 49 | 50 | public string ConstraintsBaseTypeName 51 | { 52 | get 53 | { 54 | if (Constraints != null) 55 | { 56 | return XmlHelper.GetChildElementValue(Constraints, "BaseTypeName"); 57 | } 58 | return string.Empty; 59 | } 60 | } 61 | 62 | public DocsTypeParameter(XElement xeTypeParameter) 63 | { 64 | XETypeParameter = xeTypeParameter; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/Docs/DocsTypeSignature.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToDocs.Docs 7 | { 8 | internal class DocsTypeSignature 9 | { 10 | private readonly XElement XETypeSignature; 11 | 12 | public string Language 13 | { 14 | get 15 | { 16 | return XmlHelper.GetAttributeValue(XETypeSignature, "Language"); 17 | } 18 | } 19 | 20 | public string Value 21 | { 22 | get 23 | { 24 | return XmlHelper.GetAttributeValue(XETypeSignature, "Value"); 25 | } 26 | } 27 | 28 | public DocsTypeSignature(XElement xeTypeSignature) 29 | { 30 | XETypeSignature = xeTypeSignature; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/Docs/IDocsAPI.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Collections.Generic; 5 | using System.Xml.Linq; 6 | 7 | namespace ApiDocsSync.PortToDocs.Docs 8 | { 9 | internal interface IDocsAPI 10 | { 11 | public abstract APIKind Kind { get; } 12 | public abstract bool IsUndocumented { get; } 13 | public abstract bool InheritDoc { get; } 14 | public abstract bool Changed { get; set; } 15 | public abstract string FilePath { get; set; } 16 | public abstract string DocId { get; } 17 | public abstract string DocIdUnprefixed { get; } 18 | public abstract string InheritDocCref { get; } 19 | public abstract XElement Docs { get; } 20 | public abstract List Parameters { get; } 21 | public abstract List Params { get; } 22 | public abstract List TypeParameters { get; } 23 | public abstract List TypeParams { get; } 24 | public abstract string Summary { get; set; } 25 | public abstract string ReturnType { get; } 26 | public abstract string Returns { get; set; } 27 | public abstract string Remarks { get; set; } 28 | public abstract DocsParam SaveParam(XElement xeCoreFXParam); 29 | public abstract DocsTypeParam AddTypeParam(string name, string value); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/Extensions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace ApiDocsSync.PortToDocs 5 | { 6 | // Provides generic extension methods. 7 | internal static class Extensions 8 | { 9 | // Removes the specified substrings from another string 10 | public static string RemoveSubstrings(this string oldString, params string[] stringsToRemove) 11 | { 12 | string newString = oldString; 13 | foreach (string toRemove in stringsToRemove) 14 | { 15 | if (newString.Contains(toRemove)) 16 | { 17 | newString = newString.Replace(toRemove, string.Empty); 18 | } 19 | } 20 | return newString; 21 | } 22 | 23 | public static bool ContainsStrings(this string text, string[] strings) 24 | { 25 | foreach (string str in strings) 26 | { 27 | if (text.Contains(str)) 28 | { 29 | return true; 30 | } 31 | } 32 | 33 | return false; 34 | } 35 | 36 | // Some API DocIDs with types contain "{" and "}" to enclose the typeparam, which causes 37 | // an exception to be thrown when trying to embed the string in a formatted string. 38 | public static string AsEscapedDocId(this string docId) => 39 | docId 40 | .Replace("{", "{{") 41 | .Replace("}", "}}") 42 | .Replace("<", "{{") 43 | .Replace(">", "}}") 44 | .Replace("<", "{{") 45 | .Replace(">", "}}"); 46 | 47 | // Checks if the passed string is considered "empty" according to the Docs repo rules. 48 | public static bool IsDocsEmpty(this string? s) => 49 | string.IsNullOrWhiteSpace(s) || s == Configuration.ToBeAdded; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/IntelliSenseXml/IntelliSenseXmlCommentsContainer.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics.CodeAnalysis; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Xml.Linq; 10 | 11 | /* 12 | The IntelliSense xml comments files for... 13 | A) Libraries - They are saved in: 14 | /artifacts/bin//net-/.xml 15 | B) coreclr - They saved in: 16 | artifacts/bin/coreclr/../IL/System.Private.CoreLib.xml 17 | 18 | Each xml file represents a namespace. 19 | The files are structured like this: 20 | 21 | root 22 | assembly (1) 23 | name (1) 24 | members (many) 25 | member(0:M) 26 | summary (0:1) 27 | param (0:M) 28 | returns (0:1) 29 | exception (0:M) 30 | Note: The exception value may contain xml nodes. 31 | */ 32 | namespace ApiDocsSync.PortToDocs.IntelliSenseXml 33 | { 34 | internal class IntelliSenseXmlCommentsContainer 35 | { 36 | private Configuration Config { get; set; } 37 | 38 | // The IntelliSense xml files do not separate types from members, like ECMA xml files do - Everything is a member. 39 | public Dictionary Members = new(); 40 | 41 | public IntelliSenseXmlCommentsContainer(Configuration config) => Config = config; 42 | 43 | internal IEnumerable EnumerateFiles() 44 | { 45 | foreach (DirectoryInfo dirInfo in Config.DirsIntelliSense) 46 | { 47 | // 1) Find all the xml files inside all the subdirectories inside the IntelliSense xml directory 48 | foreach (DirectoryInfo subDir in dirInfo.EnumerateDirectories("*", SearchOption.TopDirectoryOnly)) 49 | { 50 | if (!Configuration.ForbiddenBinSubdirectories.Contains(subDir.Name) && 51 | !subDir.Name.EndsWith(".Tests") && 52 | !subDir.Name.StartsWith("microsoft.netcore.app.runtime.")) 53 | { 54 | foreach (FileInfo fileInfo in subDir.EnumerateFiles("*.xml", SearchOption.AllDirectories)) 55 | { 56 | yield return fileInfo; 57 | } 58 | } 59 | } 60 | 61 | // 2) Find all the xml files in the top directory 62 | foreach (FileInfo fileInfo in dirInfo.EnumerateFiles("*.xml", SearchOption.TopDirectoryOnly)) 63 | { 64 | yield return fileInfo; 65 | } 66 | } 67 | } 68 | 69 | internal void LoadIntellisenseXmlFile(XDocument xDoc, string filePath) 70 | { 71 | if (!TryGetAssemblyName(xDoc, filePath, out string? assembly)) 72 | { 73 | return; 74 | } 75 | 76 | int totalAdded = 0; 77 | if (XmlHelper.TryGetChildElement(xDoc.Root!, "members", out XElement? xeMembers) && xeMembers != null) 78 | { 79 | foreach (XElement xeMember in xeMembers.Elements("member")) 80 | { 81 | IntelliSenseXmlMember member = new(xeMember, assembly); 82 | 83 | if (Config.IncludedAssemblies.Any(included => member.Assembly.StartsWith(included, StringComparison.InvariantCultureIgnoreCase)) && 84 | !Config.ExcludedAssemblies.Any(excluded => member.Assembly.StartsWith(excluded, StringComparison.InvariantCultureIgnoreCase))) 85 | { 86 | // No namespaces provided by the user means they want to port everything from that assembly 87 | if (!Config.IncludedNamespaces.Any() || 88 | (Config.IncludedNamespaces.Any(included => member.Namespace.StartsWith(included, StringComparison.InvariantCultureIgnoreCase)) && 89 | !Config.ExcludedNamespaces.Any(excluded => member.Namespace.StartsWith(excluded, StringComparison.InvariantCultureIgnoreCase)))) 90 | { 91 | totalAdded++; 92 | Members.TryAdd(member.Name, member); // is it OK this encounters duplicates? 93 | } 94 | } 95 | } 96 | } 97 | 98 | if (totalAdded > 0) 99 | { 100 | Log.Success($"{totalAdded} IntelliSense xml member(s) added from xml file '{filePath}'"); 101 | } 102 | } 103 | 104 | // Verifies the file is properly formed while attempting to retrieve the assembly name. 105 | private static bool TryGetAssemblyName(XDocument? xDoc, string fileName, [NotNullWhen(returnValue: true)] out string? assembly) 106 | { 107 | assembly = null; 108 | 109 | if (xDoc == null) 110 | { 111 | Log.Error($"The XDocument was null: {fileName}"); 112 | return false; 113 | } 114 | 115 | if (xDoc.Root == null) 116 | { 117 | Log.Error($"The IntelliSense xml file does not contain a root element: {fileName}"); 118 | return false; 119 | } 120 | 121 | if (xDoc.Root.Name == "linker" || xDoc.Root.Name == "FileList") 122 | { 123 | // This is a linker suppression file or a framework list 124 | return false; 125 | } 126 | 127 | if (xDoc.Root.Name != "doc") 128 | { 129 | Log.Error($"The IntelliSense xml file does not contain a doc element: {fileName}"); 130 | return false; 131 | } 132 | 133 | if (!xDoc.Root.HasElements) 134 | { 135 | Log.Error($"The IntelliSense xml file doc element not have any children: {fileName}"); 136 | return false; 137 | } 138 | 139 | if (xDoc.Root.Elements("assembly").Count() != 1) 140 | { 141 | Log.Error($"The IntelliSense xml file does not contain exactly 1 'assembly' element: {fileName}"); 142 | return false; 143 | } 144 | 145 | if (xDoc.Root.Elements("members").Count() != 1) 146 | { 147 | Log.Error($"The IntelliSense xml file does not contain exactly 1 'members' element: {fileName}"); 148 | return false; 149 | } 150 | 151 | XElement? xAssembly = xDoc.Root.Element("assembly"); 152 | if (xAssembly == null) 153 | { 154 | Log.Error($"The assembly xElement is null: {fileName}"); 155 | return false; 156 | } 157 | if (xAssembly.Elements("name").Count() != 1) 158 | { 159 | Log.Error($"The IntelliSense xml file assembly element does not contain exactly 1 'name' element: {fileName}"); 160 | return false; 161 | } 162 | 163 | assembly = xAssembly.Element("name")!.Value; 164 | if (string.IsNullOrEmpty(assembly)) 165 | { 166 | Log.Error($"The IntelliSense xml file assembly string is null or empty: {fileName}"); 167 | return false; 168 | } 169 | 170 | // The System.Private.CoreLib xml file should be mapped to the System.Runtime assembly 171 | if (assembly.ToUpperInvariant() == "SYSTEM.PRIVATE.CORELIB") 172 | { 173 | assembly = "System.Runtime"; 174 | } 175 | 176 | return true; 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/IntelliSenseXml/IntelliSenseXmlException.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToDocs.IntelliSenseXml 7 | { 8 | internal class IntelliSenseXmlException 9 | { 10 | public XElement XEException 11 | { 12 | get; 13 | private set; 14 | } 15 | 16 | private string _cref = string.Empty; 17 | public string Cref 18 | { 19 | get 20 | { 21 | if (string.IsNullOrWhiteSpace(_cref)) 22 | { 23 | _cref = XmlHelper.GetAttributeValue(XEException, "cref"); 24 | } 25 | return _cref; 26 | } 27 | } 28 | 29 | private string _value = string.Empty; 30 | public string Value 31 | { 32 | get 33 | { 34 | if (string.IsNullOrWhiteSpace(_value)) 35 | { 36 | _value = XmlHelper.GetNodesInPlainText(XEException); 37 | } 38 | return _value; 39 | } 40 | } 41 | 42 | public IntelliSenseXmlException(XElement xeException) 43 | { 44 | XEException = xeException; 45 | } 46 | 47 | public override string ToString() 48 | { 49 | return $"{Cref} - {Value}"; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/IntelliSenseXml/IntelliSenseXmlMember.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Xml.Linq; 8 | 9 | namespace ApiDocsSync.PortToDocs.IntelliSenseXml 10 | { 11 | internal class IntelliSenseXmlMember 12 | { 13 | private readonly XElement XEMember; 14 | 15 | private XElement? _xInheritDoc = null; 16 | private XElement? XInheritDoc => _xInheritDoc ??= XEMember.Elements("inheritdoc").FirstOrDefault(); 17 | 18 | public string Assembly { get; private set; } 19 | 20 | private string? _inheritDocCref = null; 21 | public string InheritDocCref 22 | { 23 | get 24 | { 25 | if (_inheritDocCref == null) 26 | { 27 | _inheritDocCref = string.Empty; 28 | if (InheritDoc && XInheritDoc != null) 29 | { 30 | XAttribute? xInheritDocCref = XInheritDoc.Attribute("cref"); 31 | if (xInheritDocCref != null) 32 | { 33 | _inheritDocCref = xInheritDocCref.Value; 34 | } 35 | } 36 | } 37 | return _inheritDocCref; 38 | } 39 | } 40 | 41 | public bool InheritDoc 42 | { 43 | get => XInheritDoc != null; 44 | } 45 | 46 | private string _namespace = string.Empty; 47 | public string Namespace 48 | { 49 | get 50 | { 51 | if (string.IsNullOrWhiteSpace(_namespace)) 52 | { 53 | string[] splittedParenthesis = Name.Split('(', StringSplitOptions.RemoveEmptyEntries); 54 | string withoutParenthesisAndPrefix = splittedParenthesis[0][2..]; // Exclude the "X:" prefix 55 | string[] splittedDots = withoutParenthesisAndPrefix.Split('.', StringSplitOptions.RemoveEmptyEntries); 56 | 57 | _namespace = string.Join('.', splittedDots.Take(splittedDots.Length - 1)); 58 | } 59 | 60 | return _namespace; 61 | } 62 | } 63 | 64 | private string? _name; 65 | 66 | /// 67 | /// The API DocId. 68 | /// 69 | public string Name => _name ??= XmlHelper.GetAttributeValue(XEMember, "name"); 70 | 71 | private List? _params; 72 | public List Params 73 | { 74 | get 75 | { 76 | if (_params == null) 77 | { 78 | _params = XEMember.Elements("param").Select(x => new IntelliSenseXmlParam(x)).ToList(); 79 | } 80 | return _params; 81 | } 82 | } 83 | 84 | private List? _typeParams; 85 | public List TypeParams 86 | { 87 | get 88 | { 89 | if (_typeParams == null) 90 | { 91 | _typeParams = XEMember.Elements("typeparam").Select(x => new IntelliSenseXmlTypeParam(x)).ToList(); 92 | } 93 | return _typeParams; 94 | } 95 | } 96 | 97 | private List? _exceptions; 98 | public IEnumerable Exceptions 99 | { 100 | get 101 | { 102 | if (_exceptions == null) 103 | { 104 | _exceptions = XEMember.Elements("exception").Select(x => new IntelliSenseXmlException(x)).ToList(); 105 | } 106 | return _exceptions; 107 | } 108 | } 109 | 110 | private List? _seeAlsos; 111 | public IEnumerable SeeAlsos 112 | { 113 | get 114 | { 115 | if (_seeAlsos == null) 116 | { 117 | _seeAlsos = XEMember.Elements("seealso").Select(x => new IntelliSenseXmlSeeAlso(x)).ToList(); 118 | } 119 | return _seeAlsos; 120 | } 121 | } 122 | 123 | private string? _summary; 124 | public string Summary 125 | { 126 | get 127 | { 128 | if (_summary == null) 129 | { 130 | XElement? xElement = XEMember.Element("summary"); 131 | _summary = (xElement != null) ? XmlHelper.GetNodesInPlainText(xElement) : string.Empty; 132 | _summary = XmlHelper.ReplaceSeeAlsos(_summary); 133 | } 134 | return _summary; 135 | } 136 | } 137 | 138 | public string? _value; 139 | public string Value 140 | { 141 | get 142 | { 143 | if (_value == null) 144 | { 145 | XElement? xElement = XEMember.Element("value"); 146 | _value = (xElement != null) ? XmlHelper.GetNodesInPlainText(xElement) : string.Empty; 147 | } 148 | return _value; 149 | } 150 | } 151 | 152 | private string? _returns; 153 | public string Returns 154 | { 155 | get 156 | { 157 | if (_returns == null) 158 | { 159 | XElement? xElement = XEMember.Element("returns"); 160 | _returns = (xElement != null) ? XmlHelper.GetNodesInPlainText(xElement) : string.Empty; 161 | } 162 | return _returns; 163 | } 164 | } 165 | 166 | private string? _remarks; 167 | public string Remarks 168 | { 169 | get 170 | { 171 | if (_remarks == null) 172 | { 173 | XElement? xElement = XEMember.Element("remarks"); 174 | _remarks = (xElement != null) ? XmlHelper.GetNodesInPlainText(xElement) : string.Empty; 175 | } 176 | return _remarks; 177 | } 178 | } 179 | 180 | public IntelliSenseXmlMember(XElement xeMember, string assembly) 181 | { 182 | if (string.IsNullOrEmpty(assembly)) 183 | { 184 | throw new ArgumentNullException(nameof(assembly)); 185 | } 186 | 187 | XEMember = xeMember ?? throw new ArgumentNullException(nameof(xeMember)); 188 | Assembly = assembly.Trim(); 189 | } 190 | 191 | public override string ToString() 192 | { 193 | return Name; 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/IntelliSenseXml/IntelliSenseXmlParam.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToDocs.IntelliSenseXml 7 | { 8 | internal class IntelliSenseXmlParam 9 | { 10 | public XElement XEParam 11 | { 12 | get; 13 | private set; 14 | } 15 | 16 | private string _name = string.Empty; 17 | public string Name 18 | { 19 | get 20 | { 21 | if (string.IsNullOrWhiteSpace(_name)) 22 | { 23 | _name = XmlHelper.GetAttributeValue(XEParam, "name"); 24 | } 25 | return _name; 26 | } 27 | } 28 | 29 | private string _value = string.Empty; 30 | public string Value 31 | { 32 | get 33 | { 34 | if (string.IsNullOrWhiteSpace(_value)) 35 | { 36 | _value = XmlHelper.GetNodesInPlainText(XEParam); 37 | } 38 | return _value; 39 | } 40 | } 41 | 42 | public IntelliSenseXmlParam(XElement xeParam) 43 | { 44 | XEParam = xeParam; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/IntelliSenseXml/IntelliSenseXmlSeeAlso.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToDocs.IntelliSenseXml; 7 | 8 | internal class IntelliSenseXmlSeeAlso(XElement xeSeeAlso) 9 | { 10 | public XElement XESeeAlso 11 | { 12 | get; 13 | private set; 14 | } = xeSeeAlso; 15 | 16 | private string _cref = string.Empty; 17 | public string Cref 18 | { 19 | get 20 | { 21 | if (string.IsNullOrWhiteSpace(_cref)) 22 | { 23 | _cref = XmlHelper.GetAttributeValue(XESeeAlso, "cref"); 24 | } 25 | return _cref; 26 | } 27 | } 28 | 29 | public override string ToString() => $"SeeAlso cref={Cref}"; 30 | } 31 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/IntelliSenseXml/IntelliSenseXmlTypeParam.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToDocs.IntelliSenseXml 7 | { 8 | internal class IntelliSenseXmlTypeParam 9 | { 10 | public XElement XETypeParam; 11 | 12 | private string _name = string.Empty; 13 | public string Name 14 | { 15 | get 16 | { 17 | if (string.IsNullOrWhiteSpace(_name)) 18 | { 19 | _name = XmlHelper.GetAttributeValue(XETypeParam, "name"); 20 | } 21 | return _name; 22 | } 23 | } 24 | 25 | private string _value = string.Empty; 26 | public string Value 27 | { 28 | get 29 | { 30 | if (string.IsNullOrWhiteSpace(_value)) 31 | { 32 | _value = XmlHelper.GetNodesInPlainText(XETypeParam); 33 | } 34 | return _value; 35 | } 36 | } 37 | 38 | public IntelliSenseXmlTypeParam(XElement xeTypeParam) 39 | { 40 | XETypeParam = xeTypeParam; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/PortToDocs/src/libraries/libraries.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | net8.0 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/StringTestData.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.IO; 5 | using System.Text; 6 | using System.Xml; 7 | using System.Xml.Linq; 8 | 9 | namespace ApiDocsSync.PortToDocs.Tests 10 | { 11 | internal class StringTestData 12 | { 13 | public StringTestData(string original, string expected) 14 | { 15 | Original = original; 16 | Expected = expected; 17 | XDoc = XDocument.Parse(original); 18 | } 19 | 20 | public string Original { get; } 21 | public string Expected { get; } 22 | public XDocument XDoc { get; } 23 | public string Actual 24 | { 25 | get 26 | { 27 | XmlWriterSettings xws = new() 28 | { 29 | Encoding = Encoding.UTF8, 30 | OmitXmlDeclaration = true, 31 | Indent = true, 32 | CheckCharacters = true, 33 | NewLineChars = Configuration.NewLine, 34 | NewLineHandling = NewLineHandling.Replace 35 | }; 36 | using MemoryStream ms = new(); 37 | using (XmlWriter xw = XmlWriter.Create(ms, xws)) 38 | { 39 | XDoc.Save(xw); 40 | } 41 | ms.Position = 0; 42 | using StreamReader sr = new(ms, Encoding.UTF8, detectEncodingFromByteOrderMarks: true); 43 | return sr.ReadToEnd(); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/AssemblyAndNamespaceDifferent/intellisense/MyAssembly/MyAssembly.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 6 | 7 | 8 | This is the summary of MyNamespace.MyType. The namespace is not the same as the assembly. 9 | 10 | 11 | This is the summary of MyNamespace.MyMethod. The namespace is not the same as the assembly. 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/AssemblyAndNamespaceDifferent/xml/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | To be added. 13 | To be added. 14 | 15 | 16 | 17 | 18 | Method 19 | 20 | 21 | MyAssembly 22 | 4.0.0.0 23 | 24 | 25 | System.Void 26 | 27 | 28 | 29 | To be added. 30 | To be added. 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/AssemblyAndNamespaceDifferent/xml_expected/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | This is the summary of MyNamespace.MyType. The namespace is not the same as the assembly. 13 | To be added. 14 | 15 | 16 | 17 | 18 | Method 19 | 20 | 21 | MyAssembly 22 | 4.0.0.0 23 | 24 | 25 | System.Void 26 | 27 | 28 | 29 | This is the summary of MyNamespace.MyMethod. The namespace is not the same as the assembly. 30 | To be added. 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/AssemblyAndNamespaceSame/intellisense/MyAssembly/MyAssembly.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 6 | 7 | 8 | This is the summary of MyAssembly.MyType. The namespace is the same as the assembly. 9 | 10 | 11 | This is the summary of MyAssembly.MyMethod. The namespace is the same as the assembly. 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/AssemblyAndNamespaceSame/xml/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | To be added. 13 | To be added. 14 | 15 | 16 | 17 | 18 | Method 19 | 20 | 21 | MyAssembly 22 | 4.0.0.0 23 | 24 | 25 | System.Void 26 | 27 | 28 | 29 | To be added. 30 | To be added. 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/AssemblyAndNamespaceSame/xml_expected/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | This is the summary of MyAssembly.MyType. The namespace is the same as the assembly. 13 | To be added. 14 | 15 | 16 | 17 | 18 | Method 19 | 20 | 21 | MyAssembly 22 | 4.0.0.0 23 | 24 | 25 | System.Void 26 | 27 | 28 | 29 | This is the summary of MyAssembly.MyMethod. The namespace is the same as the assembly. 30 | To be added. 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Basic/intellisense/MyAssembly/MyAssembly.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 6 | 7 | 8 | This is the first Type param value (like with delegates). 9 | This is the second Type param value (like with delegates). 10 | This is the first Type typeparam value. 11 | This is the second Type typeparam value. 12 | This is the type summary. It has a reference to . It contains the word null which should be transformed. 13 | 14 | 15 | This is the first Method param value. 16 | This is the second Method param value. 17 | This is the first Method typeparam. 18 | This is the second Method typeparam. 19 | This is the method summary. It has a reference to . 20 | This is the return value of MyMethod. 21 | This is the original text of ArgumentNullException thrown for MyMethod. 22 | This is the original text of IndexOutOfRangeException thrown for MyMethod. 23 | 24 | 25 | This is the property summary. 26 | This is the property value. 27 | 28 | 29 | The typeparam of MyTypeParamMethod. 30 | An element of type . 31 | The equality comparer of type . 32 | The signature of this method would be: public void Add<TValue> (TValue value, System.Collections.Generic.IEqualityComparer<TValue> comparer); 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Basic/xml/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | To be added. 13 | To be added. 14 | To be added. 15 | To be added. 16 | To be added. 17 | To be added. 18 | 19 | 20 | 21 | 22 | Method 23 | 24 | 25 | MyAssembly 26 | 4.0.0.0 27 | 28 | 29 | System.Int32 30 | 31 | 32 | 33 | To be added. 34 | To be added. 35 | To be added. 36 | To be added. 37 | To be added. 38 | To be added. 39 | To be added. 40 | 41 | 42 | 43 | 44 | Property 45 | 46 | MyAssembly 47 | 4.0.0.0 48 | 49 | 50 | System.Int32 51 | 52 | 53 | To be added. 54 | To be added. 55 | To be added. 56 | 57 | 58 | 59 | 60 | Method 61 | 62 | MyAssembly 63 | 4.0.0.0 64 | 65 | 66 | System.Void 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | To be added. 77 | To be added. 78 | To be added. 79 | To be added. 80 | To be added. 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Basic/xml_expected/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | This is the type summary. It has a reference to . It contains the word which should be transformed. 13 | This is the first Type param value (like with delegates). 14 | This is the second Type param value (like with delegates). 15 | This is the first Type typeparam value. 16 | This is the second Type typeparam value. 17 | To be added. 18 | 19 | 20 | 21 | 22 | Method 23 | 24 | 25 | MyAssembly 26 | 4.0.0.0 27 | 28 | 29 | System.Int32 30 | 31 | 32 | 33 | This is the first Method param value. 34 | This is the second Method param value. 35 | This is the first Method typeparam. 36 | This is the second Method typeparam. 37 | This is the method summary. It has a reference to . 38 | This is the return value of MyMethod. 39 | To be added. 40 | This is the original text of ArgumentNullException thrown for MyMethod. 41 | This is the original text of IndexOutOfRangeException thrown for MyMethod. 42 | 43 | 44 | 45 | 46 | Property 47 | 48 | MyAssembly 49 | 4.0.0.0 50 | 51 | 52 | System.Int32 53 | 54 | 55 | This is the property summary. 56 | This is the property value. 57 | To be added. 58 | 59 | 60 | 61 | 62 | Method 63 | 64 | MyAssembly 65 | 4.0.0.0 66 | 67 | 68 | System.Void 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | The typeparam of MyTypeParamMethod. 79 | An element of type . 80 | The equality comparer of type . 81 | The signature of this method would be: public void Add<TValue> (TValue value, System.Collections.Generic.IEqualityComparer<TValue> comparer); 82 | To be added. 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/DontAddMissingRemarks/intellisense/MyAssembly/MyAssembly.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 6 | 7 | 8 | This is the enum type summary. 9 | This is the enum type remark. 10 | 11 | 12 | This is the first option of MyEnum. Notice it has no remark. 13 | 14 | 15 | This is the second option of MyEnum. Notice it has a remark. 16 | This is the second option remark. 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/DontAddMissingRemarks/xml/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | To be added. 13 | To be added. 14 | 15 | 16 | 17 | 18 | Field 19 | 20 | MyNamespace.MyEnum 21 | 22 | 0 23 | 24 | To be added. 25 | 26 | 27 | 28 | 29 | Field 30 | 31 | MyNamespace.MyEnum 32 | 33 | 1 34 | 35 | To be added. 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/DontAddMissingRemarks/xml_expected/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | This is the enum type summary. 13 | 14 | 21 | 22 | 23 | 24 | 25 | 26 | Field 27 | 28 | MyNamespace.MyEnum 29 | 30 | 0 31 | 32 | This is the first option of MyEnum. Notice it has no remark. 33 | 34 | 35 | 36 | 37 | Field 38 | 39 | MyNamespace.MyEnum 40 | 41 | 1 42 | 43 | This is the second option of MyEnum. Notice it has a remark. 44 | 45 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/EnumRemarks/intellisense/MyAssembly/MyAssembly.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 6 | 7 | 8 | The summary of MyEnum. 9 | These are the enum remarks and it's fine if they are ported. 10 | 11 | 12 | The summary of MyEnum.MyField. 13 | These are field enum remarks that should not be ported. 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/EnumRemarks/xml/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Enum 9 | 10 | 11 | 12 | To be added. 13 | To be added. 14 | 15 | 16 | 17 | 18 | Field 19 | 20 | 21 | MyAssembly 22 | 4.0.0.0 23 | 24 | 25 | MyNamespace.MyEnum 26 | 27 | 0 28 | 29 | 30 | To be added. 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/EnumRemarks/xml_expected/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Enum 9 | 10 | 11 | 12 | The summary of MyEnum. 13 | 14 | 21 | 22 | 23 | 24 | 25 | 26 | Field 27 | 28 | 29 | MyAssembly 30 | 4.0.0.0 31 | 32 | 33 | MyNamespace.MyEnum 34 | 35 | 0 36 | 37 | 38 | The summary of MyEnum.MyField. 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Exception_ExistingCref/intellisense/MyAssembly/MyAssembly.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 6 | 7 | 8 | This is the type summary. 9 | 10 | 11 | This is the method summary. 12 | Word1 Word2 Word3 Word4 Word5 Word6 Word7 Word8 Word9 Word10. 13 | Word1 Word2 Word3 Word4 Word5 Word6 Word7 Word8 Word9 Word10. 14 | Word1 Word2 Word3 Word4 Word5 Word6 Word7 Word8 Word9 Word10. 15 | Word1 Word2 Word3 Word4 Word5 Word6 Word7 Word8 Word9 Word10. 16 | Word1 Word2 Word3 Word4 Word5 Word6 Word7 Word8 Word9 Word10. 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Exception_ExistingCref/xml/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | This is the type summary. 13 | To be added. 14 | 15 | 16 | 17 | 18 | Method 19 | 20 | 21 | MyAssembly 22 | 4.0.0.0 23 | 24 | 25 | System.Void 26 | 27 | 28 | 29 | For this test, the exceptions should be ported and appended if the threshold is below 60%. 30 | To be added. 31 | Word1 Word2 Word3 Word4 Word5 Word6 Word7 Word8 Word9. 32 | Word1 Word2 Word3 Word4 Word5 Word6 Word7 Word8. 33 | Word1 Word2 Word3 Word4 Word5 Word6 Word7. 34 | Word1 Word2 Word3 Word4 Word5 Word6. 35 | Word1 Word2 Word3 Word4 Word5. 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Exception_ExistingCref/xml_expected/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | This is the type summary. 13 | To be added. 14 | 15 | 16 | 17 | 18 | Method 19 | 20 | 21 | MyAssembly 22 | 4.0.0.0 23 | 24 | 25 | System.Void 26 | 27 | 28 | 29 | For this test, the exceptions should be ported and appended if the threshold is below 60%. 30 | To be added. 31 | Word1 Word2 Word3 Word4 Word5 Word6 Word7 Word8 Word9. 32 | Word1 Word2 Word3 Word4 Word5 Word6 Word7 Word8. 33 | Word1 Word2 Word3 Word4 Word5 Word6 Word7. 34 | Word1 Word2 Word3 Word4 Word5 Word6. 35 | 36 | Word1 Word2 Word3 Word4 Word5. 37 | -or- 38 | Word1 Word2 Word3 Word4 Word5 Word6 Word7 Word8 Word9 Word10. 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Exceptions/intellisense/MyAssembly/MyAssembly.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 6 | 7 | 8 | This is the type summary. 9 | 10 | 11 | This is the method summary. 12 | This is the original text of ArgumentNullException thrown for MyMethod. 13 | This is the original text of IndexOutOfRangeException thrown for MyMethod. 14 | 15 | -or- 16 | 17 | A proper alternative. - or - An improper alternative. 18 | 19 | -or- 20 | 21 | A somewhat proper alternative. 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Exceptions/xml/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | To be added. 13 | To be added. 14 | 15 | 16 | 17 | 18 | Method 19 | 20 | 21 | MyAssembly 22 | 4.0.0.0 23 | 24 | 25 | System.Void 26 | 27 | 28 | 29 | To be added. 30 | To be added. 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Exceptions/xml_expected/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | This is the type summary. 13 | To be added. 14 | 15 | 16 | 17 | 18 | Method 19 | 20 | 21 | MyAssembly 22 | 4.0.0.0 23 | 24 | 25 | System.Void 26 | 27 | 28 | 29 | This is the method summary. 30 | To be added. 31 | This is the original text of ArgumentNullException thrown for MyMethod. 32 | 33 | This is the original text of IndexOutOfRangeException thrown for MyMethod. 34 | -or- 35 | A proper alternative. 36 | -or- 37 | An improper alternative. 38 | -or- 39 | A somewhat proper alternative. 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/InheritDoc/intellisense/MyAssembly/MyAssembly.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/InheritDoc/intellisense/System/System.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | System 5 | 6 | 7 | 8 | This is the summary of the MyParentType class. 9 | These are the remarks of the MyParentType class. 10 | 11 | 12 | This is the summary of the MyParentType.MyMethod method. 13 | These are the remarks of the MyParentType.MyMethod method. 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/InheritDoc/xml/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.MyParentType 9 | 10 | 11 | 12 | To be added. 13 | To be added. 14 | 15 | 16 | 17 | 18 | Method 19 | 20 | 21 | MyAssembly 22 | 4.0.0.0 23 | 24 | 25 | System.Void 26 | 27 | 28 | 29 | To be added. 30 | To be added. 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/InheritDoc/xml/System/MyParentType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | System 5 | 4.0.0.0 6 | 7 | 8 | System.MyParentType 9 | 10 | 11 | 12 | To be added. 13 | To be added. 14 | 15 | 16 | 17 | 18 | Method 19 | 20 | 21 | System 22 | 4.0.0.0 23 | 24 | 25 | System.Void 26 | 27 | 28 | 29 | To be added. 30 | To be added. 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/InheritDoc/xml_expected/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.MyParentType 9 | 10 | 11 | 12 | To be added. 13 | To be added. 14 | 15 | 16 | 17 | 18 | 19 | Method 20 | 21 | 22 | MyAssembly 23 | 4.0.0.0 24 | 25 | 26 | System.Void 27 | 28 | 29 | 30 | To be added. 31 | To be added. 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/InheritDoc/xml_expected/System/MyParentType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | System 5 | 4.0.0.0 6 | 7 | 8 | System.MyParentType 9 | 10 | 11 | 12 | This is the summary of the MyParentType class. 13 | 14 | 21 | 22 | 23 | 24 | 25 | 26 | Method 27 | 28 | 29 | System 30 | 4.0.0.0 31 | 32 | 33 | System.Void 34 | 35 | 36 | 37 | This is the summary of the MyParentType.MyMethod method. 38 | 39 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Remarks_NoEII_NoInterfaceRemarks/intellisense/MyAssembly/MyAssembly.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 6 | 7 | 8 | This is the type summary. 9 | These are the type remarks. They also have a cref link: . 10 | 11 | 12 | This is the method summary. 13 | These are the method remarks. They are pointing to a param: . 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Remarks_NoEII_NoInterfaceRemarks/intellisense/System/System.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | System.Runtime 5 | 4.0.0.0 6 | 7 | 8 | 9 | 10 | Original interface type summary. 11 | 12 | 19 | 20 | 21 | 22 | 23 | 24 | Method 25 | 26 | System.Runtime 27 | 28 | 29 | System.MyReturnType 30 | 31 | 32 | 33 | Original interface method summary. 34 | Original interface method returns. 35 | 36 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Remarks_NoEII_NoInterfaceRemarks/xml/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | To be added. 13 | To be added. 14 | 15 | 16 | 17 | 18 | Method 19 | 20 | 21 | MyAssembly 22 | 4.0.0.0 23 | 24 | 25 | System.Void 26 | 27 | 28 | 29 | To be added. 30 | To be added. 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Remarks_NoEII_NoInterfaceRemarks/xml_expected/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | This is the type summary. 13 | 14 | . 19 | 20 | ]]> 21 | 22 | 23 | 24 | 25 | 26 | Method 27 | 28 | 29 | MyAssembly 30 | 4.0.0.0 31 | 32 | 33 | System.Void 34 | 35 | 36 | 37 | This is the method summary. 38 | 39 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Remarks_WithEII_NoInterfaceRemarks/intellisense/MyAssembly/MyAssembly.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 6 | 7 | 8 | This is the type summary. 9 | These are the type remarks. They also have a cref link: . 10 | 11 | 12 | This is the method summary. 13 | These are the method remarks. They are pointing to a param: . 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Remarks_WithEII_NoInterfaceRemarks/xml/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | System.IMyInterface 13 | 14 | 15 | 16 | To be added. 17 | To be added. 18 | 19 | 20 | 21 | 22 | Method 23 | 24 | 25 | MyAssembly 26 | 4.0.0.0 27 | 28 | 29 | System.Void 30 | 31 | 32 | 33 | To be added. 34 | To be added. 35 | 36 | 37 | 38 | 39 | Method 40 | 41 | M:System.IMyInterface.MyInterfaceMethod 42 | 43 | 44 | MyAssembly 45 | 46 | 47 | System.MyReturnType 48 | 49 | 50 | 51 | To be added. 52 | To be added. 53 | To be added. 54 | 55 | 56 | 57 | 58 | Method 59 | 60 | M:System.IMyInterface.MyInterfaceMethod 61 | 62 | 63 | MyAssembly 64 | 4.0.0.0 65 | 66 | 67 | System.MyReturnType 68 | 69 | 70 | 71 | To be added. 72 | To be added. 73 | To be added. 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Remarks_WithEII_NoInterfaceRemarks/xml/MyAssembly/System/System.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | System.Runtime 5 | 4.0.0.0 6 | 7 | 8 | 9 | 10 | Original interface type summary. 11 | 12 | 19 | 20 | 21 | 22 | 23 | 24 | Method 25 | 26 | System.Runtime 27 | 28 | 29 | System.MyReturnType 30 | 31 | 32 | 33 | Original interface method summary. 34 | Original interface method returns. 35 | 36 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Remarks_WithEII_NoInterfaceRemarks/xml_expected/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | System.IMyInterface 13 | 14 | 15 | 16 | This is the type summary. 17 | 18 | . 23 | 24 | ]]> 25 | 26 | 27 | 28 | 29 | 30 | Method 31 | 32 | 33 | MyAssembly 34 | 4.0.0.0 35 | 36 | 37 | System.Void 38 | 39 | 40 | 41 | This is the method summary. 42 | 43 | 50 | 51 | 52 | 53 | 54 | 55 | Method 56 | 57 | M:System.IMyInterface.MyInterfaceMethod 58 | 59 | 60 | MyAssembly 61 | 62 | 63 | System.MyReturnType 64 | 65 | 66 | 67 | Original interface method summary. 68 | Original interface method returns. 69 | To be added. 70 | 71 | 72 | 73 | 74 | Method 75 | 76 | M:System.IMyInterface.MyInterfaceMethod 77 | 78 | 79 | MyAssembly 80 | 4.0.0.0 81 | 82 | 83 | System.MyReturnType 84 | 85 | 86 | 87 | Original interface method summary. 88 | Original interface method returns. 89 | 90 | instance is cast to an interface. 95 | 96 | ]]> 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Remarks_WithEII_WithInterfaceRemarks/intellisense/MyAssembly/MyAssembly.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 6 | 7 | 8 | This is the type summary. 9 | These are the type remarks. They also have a cref link: . 10 | 11 | 12 | This is the method summary. 13 | These are the method remarks. They are pointing to a param: . 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Remarks_WithEII_WithInterfaceRemarks/xml/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | System.IMyInterface 13 | 14 | 15 | 16 | To be added. 17 | To be added. 18 | 19 | 20 | 21 | 22 | Method 23 | 24 | 25 | MyAssembly 26 | 4.0.0.0 27 | 28 | 29 | System.Void 30 | 31 | 32 | 33 | To be added. 34 | To be added. 35 | 36 | 37 | 38 | 39 | Method 40 | 41 | M:System.IMyInterface.MyInterfaceMethod 42 | 43 | 44 | MyAssembly 45 | 46 | 47 | System.MyReturnType 48 | 49 | 50 | 51 | To be added. 52 | To be added. 53 | To be added. 54 | 55 | 56 | 57 | 58 | Method 59 | 60 | M:System.IMyInterface.MyInterfaceMethod 61 | 62 | 63 | MyAssembly 64 | 4.0.0.0 65 | 66 | 67 | System.MyReturnType 68 | 69 | 70 | 71 | To be added. 72 | To be added. 73 | To be added. 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Remarks_WithEII_WithInterfaceRemarks/xml/MyAssembly/System/System.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | System.Runtime 5 | 4.0.0.0 6 | 7 | 8 | 9 | 10 | Original interface type summary. 11 | 12 | 19 | 20 | 21 | 22 | 23 | 24 | Method 25 | 26 | System.Runtime 27 | 28 | 29 | System.MyReturnType 30 | 31 | 32 | 33 | Original interface method summary. 34 | Original interface method returns. 35 | 36 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/TestData/Remarks_WithEII_WithInterfaceRemarks/xml_expected/MyAssembly/MyType.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 4.0.0.0 6 | 7 | 8 | System.Object 9 | 10 | 11 | 12 | System.IMyInterface 13 | 14 | 15 | 16 | This is the type summary. 17 | 18 | . 23 | 24 | ]]> 25 | 26 | 27 | 28 | 29 | 30 | Method 31 | 32 | 33 | MyAssembly 34 | 4.0.0.0 35 | 36 | 37 | System.Void 38 | 39 | 40 | 41 | This is the method summary. 42 | 43 | 50 | 51 | 52 | 53 | 54 | 55 | Method 56 | 57 | M:System.IMyInterface.MyInterfaceMethod 58 | 59 | 60 | MyAssembly 61 | 62 | 63 | System.MyReturnType 64 | 65 | 66 | 67 | Original interface method summary. 68 | Original interface method returns. 69 | To be added. 70 | 71 | 72 | 73 | 74 | Method 75 | 76 | M:System.IMyInterface.MyInterfaceMethod 77 | 78 | 79 | MyAssembly 80 | 4.0.0.0 81 | 82 | 83 | System.MyReturnType 84 | 85 | 86 | 87 | Original interface method summary. 88 | Original interface method returns. 89 | 90 | instance is cast to an interface. 95 | 96 | Original interface method remarks that should show up in interface implementations if -skipInterfaceRemarks is set to `false`. 97 | 98 | ]]> 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/PortToDocs/tests/tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | all 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/PortToTripleSlash.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32220.68 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PortToTripleSlash", "src\app\PortToTripleSlash.csproj", "{59FE1032-97B5-48BE-BB5A-A0428916271F}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "libraries", "src\libraries\libraries.csproj", "{4E3F11D4-4661-4E71-B19D-6EFA898E14D5}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "tests", "tests\tests.csproj", "{63E78F91-1824-4E2D-BD73-9A0ED9C7B570}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{14364EF5-DFAA-40FA-A313-4C89088FEA3D}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {59FE1032-97B5-48BE-BB5A-A0428916271F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {59FE1032-97B5-48BE-BB5A-A0428916271F}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {59FE1032-97B5-48BE-BB5A-A0428916271F}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {59FE1032-97B5-48BE-BB5A-A0428916271F}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {4E3F11D4-4661-4E71-B19D-6EFA898E14D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {4E3F11D4-4661-4E71-B19D-6EFA898E14D5}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {4E3F11D4-4661-4E71-B19D-6EFA898E14D5}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {4E3F11D4-4661-4E71-B19D-6EFA898E14D5}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {63E78F91-1824-4E2D-BD73-9A0ED9C7B570}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {63E78F91-1824-4E2D-BD73-9A0ED9C7B570}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {63E78F91-1824-4E2D-BD73-9A0ED9C7B570}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {63E78F91-1824-4E2D-BD73-9A0ED9C7B570}.Release|Any CPU.Build.0 = Release|Any CPU 32 | EndGlobalSection 33 | GlobalSection(SolutionProperties) = preSolution 34 | HideSolutionNode = FALSE 35 | EndGlobalSection 36 | GlobalSection(ExtensibilityGlobals) = postSolution 37 | SolutionGuid = {E1DDEAA8-4441-4EE3-80A7-C791C517C245} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/app/PortToTripleSlash.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApiDocsSync.PortToTripleSlash 8 | { 9 | public class PortToTripleSlash 10 | { 11 | public static async Task Main(string[] args) 12 | { 13 | Configuration config = Configuration.GetCLIArguments(args); 14 | 15 | VSLoader.LoadVSInstance(); 16 | 17 | CancellationTokenSource cts = new(); 18 | config.Loader = new MSBuildLoader(config.BinLogPath); 19 | await config.Loader.LoadMainProjectAsync(config.CsProj, config.IsMono, cts.Token).ConfigureAwait(false); 20 | 21 | ToTripleSlashPorter porter = new(config); 22 | await porter.StartAsync(cts.Token).ConfigureAwait(false); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/app/PortToTripleSlash.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | ApiDocsSync.PortToTripleSlash.PortToTripleSlash 7 | enable 8 | true 9 | true 10 | 1.3 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/AllTypesVisitor.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using Microsoft.CodeAnalysis; 7 | 8 | namespace ApiDocsSync.PortToTripleSlash 9 | { 10 | 11 | internal class AllTypesVisitor : SymbolVisitor 12 | { 13 | public readonly List AllTypesSymbols = new(); 14 | public override void VisitNamedType(INamedTypeSymbol symbol) 15 | { 16 | if (symbol.DeclaredAccessibility != Accessibility.Protected && symbol.DeclaredAccessibility != Accessibility.Public && symbol.DeclaredAccessibility != Accessibility.NotApplicable) 17 | { 18 | return; 19 | } 20 | 21 | AllTypesSymbols.Add(symbol); 22 | // Visit all nested types too, including delegates 23 | foreach (INamedTypeSymbol typeMember in symbol.GetTypeMembers()) 24 | { 25 | Visit(typeMember); 26 | } 27 | } 28 | public override void VisitNamespace(INamespaceSymbol symbol) 29 | { 30 | foreach (INamespaceOrTypeSymbol s in symbol.GetMembers()) 31 | { 32 | s.Accept(this); 33 | } 34 | } 35 | public override void VisitDynamicType(IDynamicTypeSymbol symbol) => AllTypesSymbols.Add(symbol); 36 | public override void VisitFunctionPointerType(IFunctionPointerTypeSymbol symbol) => AllTypesSymbols.Add(symbol); 37 | public override void VisitAlias(IAliasSymbol symbol) => AllTypesSymbols.Add(symbol); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/Docs/APIKind.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace ApiDocsSync.PortToTripleSlash.Docs 5 | { 6 | internal enum APIKind 7 | { 8 | Type, 9 | Member 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/Docs/DocsAPI.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics.CodeAnalysis; 7 | using System.Linq; 8 | using System.Xml.Linq; 9 | 10 | namespace ApiDocsSync.PortToTripleSlash.Docs 11 | { 12 | internal abstract class DocsAPI : IDocsAPI 13 | { 14 | private string? _docId; 15 | private string? _docIdUnprefixed; 16 | private List? _params; 17 | private List? _parameters; 18 | private List? _typeParameters; 19 | private List? _typeParams; 20 | private List? _assemblyInfos; 21 | private List? _seeAlsoCrefs; 22 | private List? _altMemberCrefs; 23 | private List? _relateds; 24 | 25 | protected readonly XElement XERoot; 26 | 27 | protected DocsAPI(XElement xeRoot) => XERoot = xeRoot; 28 | 29 | public bool IsUndocumented => 30 | Summary.IsDocsEmpty() || 31 | Returns.IsDocsEmpty() || 32 | Params.Any(p => p.Value.IsDocsEmpty()) || 33 | TypeParams.Any(tp => tp.Value.IsDocsEmpty()); 34 | 35 | public string FilePath { get; set; } = string.Empty; 36 | 37 | public string DocId => _docId ??= GetApiSignatureDocId(); 38 | 39 | public string DocIdUnprefixed => _docIdUnprefixed ??= DocId[2..]; 40 | 41 | /// 42 | /// The Parameter elements found inside the Parameters section. 43 | /// 44 | public List Parameters 45 | { 46 | get 47 | { 48 | if (_parameters == null) 49 | { 50 | XElement? xeParameters = XERoot.Element("Parameters"); 51 | _parameters = xeParameters == null ? (List)new() : xeParameters.Elements("Parameter").Select(x => new DocsParameter(x)).ToList(); 52 | } 53 | return _parameters; 54 | } 55 | } 56 | 57 | /// 58 | /// The TypeParameter elements found inside the TypeParameters section. 59 | /// 60 | public List TypeParameters 61 | { 62 | get 63 | { 64 | if (_typeParameters == null) 65 | { 66 | XElement? xeTypeParameters = XERoot.Element("TypeParameters"); 67 | _typeParameters = xeTypeParameters == null ? (List)new() : xeTypeParameters.Elements("TypeParameter").Select(x => new DocsTypeParameter(x)).ToList(); 68 | } 69 | return _typeParameters; 70 | } 71 | } 72 | 73 | public XElement Docs => XERoot.Element("Docs") ?? throw new NullReferenceException($"Docs section was null in {FilePath}"); 74 | 75 | /// 76 | /// The param elements found inside the Docs section. 77 | /// 78 | public List Params => _params ??= Docs != null ? Docs.Elements("param").Select(x => new DocsParam(this, x)).ToList() : new List(); 79 | 80 | /// 81 | /// The typeparam elements found inside the Docs section. 82 | /// 83 | public List TypeParams => _typeParams ??= Docs != null ? Docs.Elements("typeparam").Select(x => new DocsTypeParam(this, x)).ToList() : (List)new(); 84 | 85 | public List SeeAlsoCrefs => _seeAlsoCrefs ??= Docs != null ? Docs.Elements("seealso").Select(x => XmlHelper.GetAttributeValue(x, "cref").DocIdEscaped()).ToList() : (List)new(); 86 | 87 | public List AltMembers => _altMemberCrefs ??= Docs != null ? Docs.Elements("altmember").Select(x => XmlHelper.GetAttributeValue(x, "cref").DocIdEscaped()).ToList() : (List)new(); 88 | 89 | public List Relateds => _relateds ??= Docs != null ? Docs.Elements("related").Select(x => new DocsRelated(this, x)).ToList() : (List)new(); 90 | 91 | public abstract string Summary { get; } 92 | 93 | public abstract string Value { get; } 94 | 95 | public abstract string ReturnType { get; } 96 | 97 | public abstract string Returns { get; } 98 | 99 | public abstract string Remarks { get; } 100 | 101 | public abstract List Exceptions { get; } 102 | 103 | public List AssemblyInfos => _assemblyInfos ??= new List(); 104 | 105 | public APIKind Kind => this switch 106 | { 107 | DocsMember _ => APIKind.Member, 108 | DocsType _ => APIKind.Type, 109 | _ => throw new ArgumentException("Unrecognized IDocsAPI object") 110 | }; 111 | 112 | // For Types, these elements are called TypeSignature. 113 | // For Members, these elements are called MemberSignature. 114 | protected abstract string GetApiSignatureDocId(); 115 | 116 | protected string GetNodesInPlainText(string name) => TryGetElement(name, out XElement? element) ? XmlHelper.GetNodesInPlainText(name, element) : string.Empty; 117 | 118 | // Returns true if the element existed or had to be created with "To be added." as value. Returns false the element was not found and a new one was not created. 119 | private bool TryGetElement(string name, [NotNullWhen(returnValue: true)] out XElement? element) 120 | { 121 | element = null; 122 | 123 | if (Docs == null) 124 | { 125 | return false; 126 | } 127 | 128 | element = Docs.Element(name); 129 | 130 | return element != null; 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/Docs/DocsAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Xml.Linq; 7 | 8 | namespace ApiDocsSync.PortToTripleSlash.Docs 9 | { 10 | internal class DocsAssemblyInfo 11 | { 12 | private readonly XElement XEAssemblyInfo; 13 | 14 | public string AssemblyName => XmlHelper.GetChildElementValue(XEAssemblyInfo, "AssemblyName"); 15 | 16 | private List? _assemblyVersions; 17 | public List AssemblyVersions => _assemblyVersions ??= XEAssemblyInfo.Elements("AssemblyVersion").Select(x => XmlHelper.GetNodesInPlainText("AssemblyVersion", x)).ToList(); 18 | 19 | public DocsAssemblyInfo(XElement xeAssemblyInfo) => XEAssemblyInfo = xeAssemblyInfo; 20 | 21 | public override string ToString() => AssemblyName; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/Docs/DocsAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToTripleSlash.Docs 7 | { 8 | internal class DocsAttribute 9 | { 10 | private readonly XElement XEAttribute; 11 | 12 | public string FrameworkAlternate => XmlHelper.GetAttributeValue(XEAttribute, "FrameworkAlternate"); 13 | 14 | public string AttributeName => XmlHelper.GetChildElementValue(XEAttribute, "AttributeName"); 15 | 16 | public DocsAttribute(XElement xeAttribute) => XEAttribute = xeAttribute; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/Docs/DocsException.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToTripleSlash.Docs 7 | { 8 | internal class DocsException 9 | { 10 | private readonly XElement XEException; 11 | 12 | public IDocsAPI ParentAPI { get; } 13 | 14 | public string Cref => XmlHelper.GetAttributeValue(XEException, "cref").DocIdEscaped(); 15 | 16 | public string Value => XmlHelper.GetNodesInPlainText("exception", XEException); 17 | 18 | public string OriginalValue { get; private set; } 19 | 20 | public DocsException(IDocsAPI parentAPI, XElement xException) 21 | { 22 | ParentAPI = parentAPI; 23 | XEException = xException; 24 | OriginalValue = Value; 25 | } 26 | 27 | public override string ToString() => $"{Cref} - {Value}"; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/Docs/DocsMember.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Xml.Linq; 8 | 9 | namespace ApiDocsSync.PortToTripleSlash.Docs 10 | { 11 | internal class DocsMember : DocsAPI 12 | { 13 | private string? _memberName; 14 | private List? _memberSignatures; 15 | private List? _exceptions; 16 | 17 | public DocsMember(string filePath, DocsType parentType, XElement xeMember) 18 | : base(xeMember) 19 | { 20 | FilePath = filePath; 21 | ParentType = parentType; 22 | AssemblyInfos.AddRange(XERoot.Elements("AssemblyInfo").Select(x => new DocsAssemblyInfo(x))); 23 | } 24 | 25 | public DocsType ParentType { get; private set; } 26 | 27 | public string MemberName => _memberName ??= XmlHelper.GetAttributeValue(XERoot, "MemberName"); 28 | 29 | public List MemberSignatures => _memberSignatures ??= XERoot.Elements("MemberSignature").Select(x => new DocsMemberSignature(x)).ToList(); 30 | 31 | public string MemberType => XmlHelper.GetChildElementValue(XERoot, "MemberType"); 32 | 33 | public string ImplementsInterfaceMember 34 | { 35 | get 36 | { 37 | XElement? xeImplements = XERoot.Element("Implements"); 38 | return (xeImplements != null) ? XmlHelper.GetChildElementValue(xeImplements, "InterfaceMember") : string.Empty; 39 | } 40 | } 41 | 42 | public override string ReturnType 43 | { 44 | get 45 | { 46 | XElement? xeReturnValue = XERoot.Element("ReturnValue"); 47 | return xeReturnValue != null ? XmlHelper.GetChildElementValue(xeReturnValue, "ReturnType") : string.Empty; 48 | } 49 | } 50 | 51 | public override string Returns => (ReturnType != "System.Void") ? GetNodesInPlainText("returns") : string.Empty; 52 | 53 | public override string Summary => GetNodesInPlainText("summary"); 54 | 55 | public override string Remarks => GetNodesInPlainText("remarks"); 56 | 57 | public override string Value => (MemberType == "Property") ? GetNodesInPlainText("value") : string.Empty; 58 | 59 | public override List Exceptions 60 | { 61 | get 62 | { 63 | if (_exceptions == null) 64 | { 65 | if (Docs != null) 66 | { 67 | _exceptions = Docs.Elements("exception").Select(x => new DocsException(this, x)).ToList(); 68 | } 69 | else 70 | { 71 | _exceptions = new List(); 72 | } 73 | } 74 | return _exceptions; 75 | } 76 | } 77 | 78 | public override string ToString() => DocId; 79 | 80 | protected override string GetApiSignatureDocId() 81 | { 82 | DocsMemberSignature? dts = MemberSignatures.FirstOrDefault(x => x.Language == "DocId"); 83 | return dts != null ? dts.Value : throw new FormatException($"DocId TypeSignature not found for {MemberName}"); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/Docs/DocsMemberSignature.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToTripleSlash.Docs 7 | { 8 | internal class DocsMemberSignature 9 | { 10 | private readonly XElement XEMemberSignature; 11 | 12 | public string Language => XmlHelper.GetAttributeValue(XEMemberSignature, "Language"); 13 | 14 | public string Value => XmlHelper.GetAttributeValue(XEMemberSignature, "Value"); 15 | 16 | public DocsMemberSignature(XElement xeMemberSignature) => XEMemberSignature = xeMemberSignature; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/Docs/DocsParam.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToTripleSlash.Docs 7 | { 8 | internal class DocsParam 9 | { 10 | private readonly XElement XEDocsParam; 11 | 12 | public IDocsAPI ParentAPI { get; } 13 | 14 | public string Name => XmlHelper.GetAttributeValue(XEDocsParam, "name"); 15 | 16 | public string Value => XmlHelper.GetNodesInPlainText("param", XEDocsParam); 17 | 18 | public DocsParam(IDocsAPI parentAPI, XElement xeDocsParam) 19 | { 20 | ParentAPI = parentAPI; 21 | XEDocsParam = xeDocsParam; 22 | } 23 | 24 | public override string ToString() => Name; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/Docs/DocsParameter.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToTripleSlash.Docs 7 | { 8 | internal class DocsParameter 9 | { 10 | private readonly XElement XEParameter; 11 | 12 | public string Name => XmlHelper.GetAttributeValue(XEParameter, "Name"); 13 | 14 | public string Type => XmlHelper.GetAttributeValue(XEParameter, "Type"); 15 | 16 | public DocsParameter(XElement xeParameter) => XEParameter = xeParameter; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/Docs/DocsRelated.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToTripleSlash.Docs 7 | { 8 | internal class DocsRelated 9 | { 10 | private readonly XElement XERelatedArticle; 11 | 12 | public IDocsAPI ParentAPI { get; } 13 | 14 | public string ArticleType => XmlHelper.GetAttributeValue(XERelatedArticle, "type"); 15 | 16 | public string Href => XmlHelper.GetAttributeValue(XERelatedArticle, "href"); 17 | 18 | public string Value => XmlHelper.GetNodesInPlainText("related", XERelatedArticle); 19 | 20 | public DocsRelated(IDocsAPI parentAPI, XElement xeRelatedArticle) 21 | { 22 | ParentAPI = parentAPI; 23 | XERelatedArticle = xeRelatedArticle; 24 | } 25 | 26 | public override string ToString() => Value; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/Docs/DocsType.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Xml.Linq; 9 | using Microsoft.CodeAnalysis; 10 | 11 | namespace ApiDocsSync.PortToTripleSlash.Docs 12 | { 13 | /// 14 | /// Represents the root xml element (unique) of a Docs xml file, called Type. 15 | /// 16 | internal class DocsType : DocsAPI 17 | { 18 | private string? _typeName; 19 | private string? _name; 20 | private string? _fullName; 21 | private string? _namespace; 22 | private string? _baseTypeName; 23 | private List? _interfaceNames; 24 | private List? _attributes; 25 | private List? _typesSignatures; 26 | 27 | public DocsType(string filePath, XDocument xDoc, XElement xeRoot, Encoding encoding) 28 | : base(xeRoot) 29 | { 30 | FilePath = filePath; 31 | XDoc = xDoc; 32 | FileEncoding = encoding; 33 | AssemblyInfos.AddRange(XERoot.Elements("AssemblyInfo").Select(x => new DocsAssemblyInfo(x))); 34 | } 35 | 36 | private List? _symbolLocations; 37 | public List SymbolLocations => _symbolLocations ??= new(); 38 | 39 | public XDocument XDoc { get; } 40 | 41 | public Encoding FileEncoding { get; } 42 | 43 | public string TypeName 44 | { 45 | get 46 | { 47 | if (_typeName == null) 48 | { 49 | // DocId uses ` notation for generic types, but it uses . for nested types 50 | // Name uses + for nested types, but it uses <T> for generic types 51 | // We need ` notation for generic types and + notation for nested types 52 | // Only filename gives us that format, but we have to prepend the namespace 53 | if (DocId.Contains('`') || Name.Contains('+')) 54 | { 55 | _typeName = Namespace + "." + System.IO.Path.GetFileNameWithoutExtension(FilePath); 56 | } 57 | else 58 | { 59 | _typeName = FullName; 60 | } 61 | } 62 | return _typeName; 63 | } 64 | } 65 | 66 | public string Name => _name ??= XmlHelper.GetAttributeValue(XERoot, "Name"); 67 | 68 | public string FullName => _fullName ??= XmlHelper.GetAttributeValue(XERoot, "FullName"); 69 | 70 | public string Namespace 71 | { 72 | get 73 | { 74 | if (_namespace == null) 75 | { 76 | int lastDotPosition = FullName.LastIndexOf('.'); 77 | _namespace = lastDotPosition < 0 ? FullName : FullName.Substring(0, lastDotPosition); 78 | } 79 | return _namespace; 80 | } 81 | } 82 | 83 | public List TypeSignatures => _typesSignatures ??= XERoot.Elements("TypeSignature").Select(x => new DocsTypeSignature(x)).ToList(); 84 | 85 | public XElement? Base => XERoot.Element("Base"); 86 | 87 | public string BaseTypeName 88 | { 89 | get 90 | { 91 | if (Base == null) 92 | { 93 | _baseTypeName = string.Empty; 94 | } 95 | else if (_baseTypeName == null) 96 | { 97 | _baseTypeName = XmlHelper.GetChildElementValue(Base, "BaseTypeName"); 98 | } 99 | return _baseTypeName; 100 | } 101 | } 102 | 103 | public XElement? Interfaces => XERoot.Element("Interfaces"); 104 | 105 | public List InterfaceNames 106 | { 107 | get 108 | { 109 | if (Interfaces == null) 110 | { 111 | _interfaceNames = new(); 112 | } 113 | else if (_interfaceNames == null) 114 | { 115 | _interfaceNames = Interfaces.Elements("Interface").Select(x => XmlHelper.GetChildElementValue(x, "InterfaceName")).ToList(); 116 | } 117 | return _interfaceNames; 118 | } 119 | } 120 | 121 | public List Attributes 122 | { 123 | get 124 | { 125 | if (_attributes == null) 126 | { 127 | XElement? e = XERoot.Element("Attributes"); 128 | if (e == null) 129 | { 130 | _attributes = new(); 131 | } 132 | else 133 | { 134 | _attributes = (e != null) ? e.Elements("Attribute").Select(x => new DocsAttribute(x)).ToList() : new List(); 135 | } 136 | } 137 | return _attributes; 138 | } 139 | } 140 | 141 | public override string Summary => GetNodesInPlainText("summary"); 142 | 143 | public override string Value => string.Empty; 144 | 145 | /// 146 | /// Only available when the type is a delegate. 147 | /// 148 | public override string ReturnType 149 | { 150 | get 151 | { 152 | XElement? xeReturnValue = XERoot.Element("ReturnValue"); 153 | if (xeReturnValue != null) 154 | { 155 | return XmlHelper.GetChildElementValue(xeReturnValue, "ReturnType"); 156 | } 157 | return string.Empty; 158 | } 159 | } 160 | 161 | /// 162 | /// Only available when the type is a delegate. 163 | /// 164 | public override string Returns => (ReturnType != "System.Void") ? GetNodesInPlainText("returns") : string.Empty; 165 | 166 | public override string Remarks => GetNodesInPlainText("remarks"); 167 | 168 | public override List Exceptions { get; } = new(); 169 | 170 | public override string ToString() => FullName; 171 | 172 | protected override string GetApiSignatureDocId() 173 | { 174 | DocsTypeSignature? dts = TypeSignatures.FirstOrDefault(x => x.Language == "DocId"); 175 | return dts != null ? dts.Value : throw new FormatException($"DocId TypeSignature not found for {FullName}"); 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/Docs/DocsTypeParam.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToTripleSlash.Docs 7 | { 8 | /// 9 | /// Each one of these typeparam objects live inside the Docs section inside the Member object. 10 | /// 11 | internal class DocsTypeParam 12 | { 13 | private readonly XElement XEDocsTypeParam; 14 | 15 | public IDocsAPI ParentAPI { get; } 16 | 17 | public string Name => XmlHelper.GetAttributeValue(XEDocsTypeParam, "name"); 18 | 19 | public string Value => XmlHelper.GetNodesInPlainText("typeparam", XEDocsTypeParam); 20 | 21 | public DocsTypeParam(IDocsAPI parentAPI, XElement xeDocsTypeParam) 22 | { 23 | ParentAPI = parentAPI; 24 | XEDocsTypeParam = xeDocsTypeParam; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/Docs/DocsTypeParameter.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Xml.Linq; 7 | 8 | namespace ApiDocsSync.PortToTripleSlash.Docs 9 | { 10 | /// 11 | /// Each one of these TypeParameter objects islocated inside the TypeParameters section inside the Member. 12 | /// 13 | internal class DocsTypeParameter 14 | { 15 | private readonly XElement XETypeParameter; 16 | 17 | public string Name => XmlHelper.GetAttributeValue(XETypeParameter, "Name"); 18 | 19 | private XElement? Constraints => XETypeParameter.Element("Constraints"); 20 | 21 | private List? _constraintsParameterAttributes; 22 | public List ConstraintsParameterAttributes => _constraintsParameterAttributes ??= Constraints != null 23 | ? Constraints.Elements("ParameterAttribute").Select(x => XmlHelper.GetNodesInPlainText("ParameterAttribute", x)).ToList() 24 | : new List(); 25 | 26 | public string ConstraintsBaseTypeName => Constraints != null ? XmlHelper.GetChildElementValue(Constraints, "BaseTypeName") : string.Empty; 27 | 28 | public DocsTypeParameter(XElement xeTypeParameter) => XETypeParameter = xeTypeParameter; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/Docs/DocsTypeSignature.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Xml.Linq; 5 | 6 | namespace ApiDocsSync.PortToTripleSlash.Docs 7 | { 8 | internal class DocsTypeSignature 9 | { 10 | private readonly XElement XETypeSignature; 11 | 12 | public string Language => XmlHelper.GetAttributeValue(XETypeSignature, "Language"); 13 | 14 | public string Value => XmlHelper.GetAttributeValue(XETypeSignature, "Value"); 15 | 16 | public DocsTypeSignature(XElement xeTypeSignature) => XETypeSignature = xeTypeSignature; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/Docs/IDocsAPI.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Collections.Generic; 5 | using System.Xml.Linq; 6 | 7 | namespace ApiDocsSync.PortToTripleSlash.Docs 8 | { 9 | internal interface IDocsAPI 10 | { 11 | public abstract APIKind Kind { get; } 12 | public abstract bool IsUndocumented { get; } 13 | public abstract string FilePath { get; set; } 14 | public abstract string DocId { get; } 15 | public abstract string DocIdUnprefixed { get; } 16 | public abstract XElement Docs { get; } 17 | public abstract List Parameters { get; } 18 | public abstract List Params { get; } 19 | public abstract List TypeParameters { get; } 20 | public abstract List TypeParams { get; } 21 | public abstract string Summary { get; } 22 | public abstract string Value { get; } 23 | public abstract string ReturnType { get; } 24 | public abstract string Returns { get; } 25 | public abstract string Remarks { get; } 26 | public abstract List Exceptions { get; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/Extensions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | namespace ApiDocsSync.PortToTripleSlash 8 | { 9 | // Provides generic extension methods. 10 | internal static class Extensions 11 | { 12 | // Adds a string to a list of strings if the element is not there yet. The method makes sure to escape unexpected curly brackets to prevent formatting exceptions. 13 | public static void AddIfNotExists(this List list, string element) 14 | { 15 | string cleanedElement = element.DocIdEscaped(); 16 | if (!list.Contains(cleanedElement)) 17 | { 18 | list.Add(cleanedElement); 19 | } 20 | } 21 | 22 | // Removes the specified substrings from another string 23 | public static string RemoveSubstrings(this string oldString, params string[] stringsToRemove) 24 | { 25 | string newString = oldString; 26 | foreach (string toRemove in stringsToRemove) 27 | { 28 | if (newString.Contains(toRemove)) 29 | { 30 | newString = newString.Replace(toRemove, string.Empty); 31 | } 32 | } 33 | return newString; 34 | } 35 | 36 | public static bool ContainsStrings(this string text, string[] strings) 37 | { 38 | foreach (string str in strings) 39 | { 40 | if (text.Contains(str)) 41 | { 42 | return true; 43 | } 44 | } 45 | 46 | return false; 47 | } 48 | 49 | // Some API DocIDs with types contain "{" and "}" to enclose the typeparam, which causes 50 | // an exception to be thrown when trying to embed the string in a formatted string. 51 | public static string DocIdEscaped(this string docId) => 52 | docId 53 | .Replace("{", "{{") 54 | .Replace("}", "}}") 55 | .Replace("<", "{{") 56 | .Replace(">", "}}") 57 | .Replace("<", "{{") 58 | .Replace(">", "}}"); 59 | 60 | // Checks if the passed string is considered "empty" according to the Docs repo rules. 61 | public static bool IsDocsEmpty(this string? s) => 62 | string.IsNullOrWhiteSpace(s) || s == Configuration.ToBeAdded; 63 | 64 | public static bool HasItems(this IEnumerable src) => src?.Any() ?? false; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/ResolvedLocation.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using Microsoft.CodeAnalysis; 5 | 6 | namespace ApiDocsSync.PortToTripleSlash 7 | { 8 | public class ResolvedLocation 9 | { 10 | public string TypeName { get; } 11 | public Compilation Compilation { get; } 12 | public Location Location { get; } 13 | public SyntaxTree Tree { get; } 14 | public SemanticModel Model { get; } 15 | public SyntaxNode? NewNode { get; set; } 16 | 17 | public ResolvedLocation(string typeName, Compilation compilation, Location location, SyntaxTree tree) 18 | { 19 | TypeName = typeName; 20 | Compilation = compilation; 21 | Location = location; 22 | Tree = tree; 23 | Model = Compilation.GetSemanticModel(Tree); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/ResolvedProject.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using Microsoft.CodeAnalysis; 5 | 6 | namespace ApiDocsSync.PortToTripleSlash 7 | { 8 | public class ResolvedProject 9 | { 10 | public ResolvedWorkspace ResolvedWorkspace { get; } 11 | public Project Project { get; } 12 | public Compilation Compilation { get; } 13 | public string ProjectPath { get; } 14 | 15 | public ResolvedProject(ResolvedWorkspace resolvedWorkspace, string projectPath, Project project, Compilation compilation) 16 | { 17 | ResolvedWorkspace = resolvedWorkspace; 18 | Project = project; 19 | Compilation = compilation; 20 | ProjectPath = projectPath; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/ResolvedWorkspace.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Collections.Generic; 5 | using Microsoft.CodeAnalysis; 6 | using Microsoft.CodeAnalysis.Editing; 7 | using Microsoft.CodeAnalysis.MSBuild; 8 | 9 | namespace ApiDocsSync.PortToTripleSlash 10 | { 11 | public class ResolvedWorkspace 12 | { 13 | public MSBuildWorkspace Workspace { get; } 14 | public List ResolvedProjects { get; } 15 | public SyntaxGenerator Generator { get; } 16 | 17 | public ResolvedWorkspace(MSBuildWorkspace workspace) 18 | { 19 | Workspace = workspace; 20 | ResolvedProjects = new List(); 21 | Generator = SyntaxGenerator.GetGenerator(workspace, LanguageNames.CSharp); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/VSLoader.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics.CodeAnalysis; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Reflection; 10 | using System.Runtime.Loader; 11 | using Microsoft.Build.Locator; 12 | 13 | namespace ApiDocsSync.PortToTripleSlash 14 | { 15 | // Per the documentation: https://docs.microsoft.com/en-us/visualstudio/msbuild/updating-an-existing-application 16 | // Do not call any of these APIs from the same context where Microsoft.Build APIs are being called. 17 | public static class VSLoader 18 | { 19 | private static readonly string[] s_candidateExtensions = new[] { "ni.dll", "ni.exe", "dll", "exe" }; 20 | private static readonly Dictionary s_pathsToAssemblies = new(StringComparer.OrdinalIgnoreCase); 21 | private static readonly Dictionary s_namesToAssemblies = new(); 22 | private static readonly object s_guard = new(); 23 | 24 | public static VisualStudioInstance? VSInstance { get; private set; } 25 | 26 | // Per the documentation: https://docs.microsoft.com/en-us/visualstudio/msbuild/updating-an-existing-application 27 | // Cannot reference any MSBuild types (from Microsoft.Build namespace) in the same method that calls MSBuildLocator. 28 | public static void LoadVSInstance() 29 | { 30 | Log.Info("Querying for all Visual Studio instances..."); 31 | IEnumerable vsBuildInstances = MSBuildLocator.QueryVisualStudioInstances(); 32 | 33 | if (!vsBuildInstances.Any()) 34 | { 35 | throw new Exception("No VS instances found."); 36 | } 37 | 38 | Log.Info("Looking for the latest stable instance of Visual Studio, if there is one..."); 39 | VSInstance = vsBuildInstances.Where(b => !b.MSBuildPath.Contains("-preview")) 40 | .OrderByDescending(b => b.Version) 41 | .FirstOrDefault() ?? 42 | vsBuildInstances.First(); 43 | Log.Success($"Selected instance:{Environment.NewLine} - MSBuildPath: {VSInstance.MSBuildPath}{Environment.NewLine} - Version: {VSInstance.Version}"); 44 | 45 | // Unit tests execute this multiple times, ensure we only register once 46 | if (MSBuildLocator.CanRegister) 47 | { 48 | Log.Info("Attempting to register assembly loader..."); 49 | RegisterAssemblyLoader(VSInstance.MSBuildPath); 50 | Log.Info("Attempting to register Visual Studio instance"); 51 | MSBuildLocator.RegisterInstance(VSInstance); 52 | Log.Success("Successful Visual Studio load!"); 53 | } 54 | else 55 | { 56 | Log.Error("Could not register the Visual Studio instance (CanRegister=false)."); 57 | } 58 | } 59 | 60 | // Register an assembly loader that will load assemblies with higher version than what was requested. 61 | private static void RegisterAssemblyLoader(string searchPath) 62 | { 63 | AssemblyLoadContext.Default.Resolving += (AssemblyLoadContext context, AssemblyName assemblyName) => 64 | { 65 | lock (s_guard) 66 | { 67 | if (s_namesToAssemblies.TryGetValue(assemblyName.FullName, out Assembly? cachedAssembly)) 68 | { 69 | return cachedAssembly; 70 | } 71 | 72 | if (TryResolveAssemblyFromPaths(context, assemblyName, searchPath, out Assembly? assembly)) 73 | { 74 | // Cache assembly 75 | string? name = assembly.FullName; 76 | if (name is null) 77 | { 78 | throw new Exception($"Could not get name for assembly '{assembly}'"); 79 | } 80 | 81 | s_pathsToAssemblies[assembly.Location] = assembly; 82 | s_namesToAssemblies[name] = assembly; 83 | 84 | return assembly; 85 | } 86 | 87 | return null; 88 | } 89 | }; 90 | } 91 | 92 | // Tries to find and return the specified assembly by looking in all the known locations where it could be found. 93 | private static bool TryResolveAssemblyFromPaths(AssemblyLoadContext context, AssemblyName assemblyName, string searchPath, [NotNullWhen(returnValue: true)] out Assembly? resolvedAssembly) 94 | { 95 | resolvedAssembly = null; 96 | foreach (string cultureSubfolder in GetCultureSubfolders(assemblyName)) 97 | { 98 | foreach (string extension in s_candidateExtensions) 99 | { 100 | string candidatePath = Path.Combine(searchPath, cultureSubfolder, $"{assemblyName.Name}.{extension}"); 101 | if (s_pathsToAssemblies.ContainsKey(candidatePath) || !File.Exists(candidatePath)) 102 | { 103 | continue; 104 | } 105 | 106 | AssemblyName candidateAssemblyName = AssemblyLoadContext.GetAssemblyName(candidatePath); 107 | if (candidateAssemblyName.Version < assemblyName.Version) 108 | { 109 | continue; 110 | } 111 | 112 | try 113 | { 114 | resolvedAssembly = context.LoadFromAssemblyPath(candidatePath); 115 | return resolvedAssembly != null; 116 | } 117 | catch 118 | { 119 | if (assemblyName.Name != null) 120 | { 121 | // We were unable to load the assembly from the file path. It is likely that 122 | // a different version of the assembly has already been loaded into the context. 123 | // Be forgiving and attempt to load assembly by name without specifying a version. 124 | resolvedAssembly = context.LoadFromAssemblyName(new AssemblyName(assemblyName.Name)); 125 | return resolvedAssembly != null; 126 | } 127 | } 128 | } 129 | } 130 | 131 | return false; 132 | } 133 | 134 | private static IEnumerable GetCultureSubfolders(AssemblyName assemblyName) 135 | { 136 | if (!string.IsNullOrEmpty(assemblyName.CultureName)) 137 | { 138 | // Search for satellite assemblies in culture subdirectories of the assembly search 139 | // directories, but fall back to the bare search directory if that fails. 140 | yield return assemblyName.CultureName; 141 | } 142 | // If no culture is specified, attempt to load directly from the known dependency paths. 143 | yield return string.Empty; 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/XmlHelper.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Text.RegularExpressions; 8 | using System.Xml; 9 | using System.Xml.Linq; 10 | 11 | namespace ApiDocsSync.PortToTripleSlash 12 | { 13 | internal class XmlHelper 14 | { 15 | private static readonly (string, string)[] ReplaceableMarkdownPatterns = new[] 16 | { 17 | (@"\s*\s*", ""), 19 | (@"\s*##\s*Remarks\s*", ""), 20 | (@"`(?'keyword'null|false|true)`", ""), 21 | (@"(?'keyword'null|false|true)", ""), 22 | (@"\?\,]+)>", ""), 23 | (@"%601", "{T}") 24 | }; 25 | 26 | public static string GetAttributeValue(XElement parent, string name) 27 | { 28 | if (parent == null) 29 | { 30 | throw new Exception($"A null parent was passed when attempting to get attribute '{name}'"); 31 | } 32 | else 33 | { 34 | XAttribute? attr = parent.Attribute(name); 35 | if (attr != null) 36 | { 37 | return attr.Value.Trim(); 38 | } 39 | } 40 | return string.Empty; 41 | } 42 | 43 | public static bool TryGetChildElement(XElement parent, string name, out XElement? child) 44 | { 45 | child = null; 46 | 47 | if (parent == null || string.IsNullOrWhiteSpace(name)) 48 | return false; 49 | 50 | child = parent.Element(name); 51 | 52 | return child != null; 53 | } 54 | 55 | public static string GetChildElementValue(XElement parent, string childName) 56 | { 57 | XElement? child = parent.Element(childName); 58 | 59 | if (child != null) 60 | { 61 | return GetNodesInPlainText(childName, child); 62 | } 63 | 64 | return string.Empty; 65 | } 66 | 67 | public static string GetNodesInPlainText(string name, XElement element) 68 | { 69 | if (element == null) 70 | { 71 | throw new Exception("A null element was passed when attempting to retrieve the nodes in plain text."); 72 | } 73 | 74 | if (name == "remarks") 75 | { 76 | XElement? formatElement = element.Element("format"); 77 | if (formatElement != null) 78 | { 79 | element = formatElement; 80 | } 81 | } 82 | // string.Join("", element.Nodes()) is very slow. 83 | // 84 | // The following is twice as fast (although still slow) 85 | // but does not produce the same spacing. That may be OK. 86 | // 87 | using XmlReader reader = element.CreateReader(); 88 | reader.MoveToContent(); 89 | string actualValue = reader.ReadInnerXml().Trim(); 90 | 91 | if (name == "remarks") 92 | { 93 | actualValue = ReplaceMarkdown(actualValue); 94 | } 95 | 96 | return actualValue.IsDocsEmpty() ? string.Empty : actualValue; 97 | } 98 | 99 | private static string ReplaceMarkdown(string value) 100 | { 101 | foreach ((string bad, string good) in ReplaceableMarkdownPatterns) 102 | { 103 | value = Regex.Replace(value, bad, good); 104 | } 105 | 106 | return string.Join(Environment.NewLine, value.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/src/libraries/libraries.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | net8.0 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | all 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/tests/PortToTripleSlash/PortToTripleSlash.FileSystem.Tests.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.IO; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using ApiDocsSync.Tests; 8 | using Xunit; 9 | using Xunit.Abstractions; 10 | 11 | namespace ApiDocsSync.PortToTripleSlash.Tests 12 | { 13 | public class PortToTripleSlash_FileSystem_Tests : BasePortTests 14 | { 15 | public PortToTripleSlash_FileSystem_Tests(ITestOutputHelper output) : base(output) 16 | { 17 | } 18 | 19 | //[Fact] 20 | // TODO: Need to fix the remark conversion from markdown to xml. 21 | private Task Port_Basic() => PortToTripleSlashAsync("Basic"); 22 | 23 | private static async Task PortToTripleSlashAsync( 24 | string testDataDir, 25 | bool skipInterfaceImplementations = true, 26 | string assemblyName = FileTestData.TestAssembly, 27 | string namespaceName = FileTestData.TestNamespace) 28 | { 29 | using TestDirectory tempDir = new(); 30 | 31 | PortToTripleSlashTestData testData = new( 32 | tempDir, 33 | testDataDir, 34 | assemblyName, 35 | namespaceName); 36 | 37 | Configuration c = new() 38 | { 39 | CsProj = Path.GetFullPath(testData.ProjectFilePath), 40 | SkipInterfaceImplementations = skipInterfaceImplementations, 41 | BinLogPath = testData.BinLogPath, 42 | SkipRemarks = false 43 | }; 44 | 45 | c.IncludedAssemblies.Add(assemblyName); 46 | 47 | if (!string.IsNullOrEmpty(namespaceName)) 48 | { 49 | c.IncludedNamespaces.Add(namespaceName); 50 | } 51 | 52 | c.DirsDocsXml.Add(testData.DocsDir); 53 | 54 | CancellationTokenSource cts = new(); 55 | 56 | VSLoader.LoadVSInstance(); 57 | c.Loader = new MSBuildLoader(c.BinLogPath); 58 | 59 | await c.Loader.LoadMainProjectAsync(c.CsProj, c.IsMono, cts.Token); 60 | 61 | ToTripleSlashPorter porter = new(c); 62 | porter.CollectFiles(); 63 | 64 | await porter.MatchSymbolsAsync(c.Loader.MainProject.Compilation, isMSBuildProject: true, cts.Token); 65 | await porter.PortAsync(isMSBuildProject: true, cts.Token); 66 | 67 | Verify(testData); 68 | } 69 | 70 | private static void Verify(PortToTripleSlashTestData testData) 71 | { 72 | string[] expectedLines = File.ReadAllLines(testData.ExpectedFilePath); 73 | string[] actualLines = File.ReadAllLines(testData.ActualFilePath); 74 | 75 | for (int i = 0; i < expectedLines.Length; i++) 76 | { 77 | string expectedLine = expectedLines[i]; 78 | string actualLine = actualLines[i]; 79 | if (System.Diagnostics.Debugger.IsAttached) 80 | { 81 | if (expectedLine != actualLine) 82 | { 83 | System.Diagnostics.Debugger.Break(); 84 | } 85 | } 86 | Assert.Equal(expectedLine, actualLine); 87 | } 88 | 89 | Assert.Equal(expectedLines.Length, actualLines.Length); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/tests/PortToTripleSlash/PortToTripleSlashTestData.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.IO; 5 | using ApiDocsSync.Tests; 6 | using Xunit; 7 | 8 | namespace ApiDocsSync.PortToTripleSlash.Tests 9 | { 10 | internal class PortToTripleSlashTestData : FileTestData 11 | { 12 | private const string BinLogFileName = "output.binlog"; 13 | private const string SourceOriginal = "SourceOriginal.cs"; 14 | private const string SourceExpected = "SourceExpected.cs"; 15 | private const string ProjectDirName = "Project"; 16 | private const string TestDataRootDirPath = @"../../../PortToTripleSlash/TestData"; 17 | 18 | private DirectoryInfo ProjectDir { get; set; } 19 | internal string ProjectFilePath { get; set; } 20 | internal string BinLogPath { get; set; } 21 | 22 | internal PortToTripleSlashTestData( 23 | TestDirectory tempDir, 24 | string testDataDir, 25 | string assemblyName, 26 | string namespaceName) 27 | { 28 | Assert.False(string.IsNullOrWhiteSpace(assemblyName)); 29 | 30 | namespaceName = string.IsNullOrEmpty(namespaceName) ? assemblyName : namespaceName; 31 | 32 | ProjectDir = tempDir.CreateSubdirectory(ProjectDirName); 33 | 34 | DocsDir = tempDir.CreateSubdirectory(DocsDirName); 35 | DirectoryInfo docsAssemblyDir = DocsDir.CreateSubdirectory(namespaceName); 36 | 37 | string testDataPath = Path.Combine(TestDataRootDirPath, testDataDir); 38 | 39 | foreach (string origin in Directory.EnumerateFiles(testDataPath, "*.xml")) 40 | { 41 | string fileName = Path.GetFileName(origin); 42 | string destination = Path.Combine(docsAssemblyDir.FullName, fileName); 43 | File.Copy(origin, destination); 44 | } 45 | 46 | string originCsOriginal = Path.Combine(testDataPath, SourceOriginal); 47 | ActualFilePath = Path.Combine(ProjectDir.FullName, SourceOriginal); 48 | File.Copy(originCsOriginal, ActualFilePath); 49 | 50 | string originCsExpected = Path.Combine(testDataPath, SourceExpected); 51 | ExpectedFilePath = Path.Combine(tempDir.FullPath, SourceExpected); 52 | File.Copy(originCsExpected, ExpectedFilePath); 53 | 54 | string originCsproj = Path.Combine(testDataPath, $"{assemblyName}.csproj"); 55 | ProjectFilePath = Path.Combine(ProjectDir.FullName, $"{assemblyName}.csproj"); 56 | File.Copy(originCsproj, ProjectFilePath); 57 | 58 | BinLogPath = Path.Combine(ProjectDir.FullName, BinLogFileName); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/tests/PortToTripleSlash/StringTestData.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Collections.Generic; 5 | using System.Xml.Linq; 6 | 7 | namespace ApiDocsSync.PortToTripleSlash.Tests; 8 | 9 | internal class StringTestData 10 | { 11 | public StringTestData(IEnumerable docFiles, IEnumerable originalCodeFiles, Dictionary expectedCodeFiles, bool addMsCorLibReferences) 12 | { 13 | OriginalCodeFiles = originalCodeFiles; 14 | ExpectedCodeFiles = expectedCodeFiles; 15 | XDocs = new List(); 16 | foreach (string docFile in docFiles) 17 | { 18 | XDocs.Add(XDocument.Parse(docFile)); 19 | } 20 | AddMsCorLibReferences = addMsCorLibReferences; 21 | } 22 | public bool AddMsCorLibReferences { get; } 23 | public List XDocs { get; } 24 | public IEnumerable OriginalCodeFiles { get; } 25 | public Dictionary ExpectedCodeFiles { get; } 26 | } 27 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/tests/PortToTripleSlash/TestData/Basic/MyAssembly.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | This is MyNamespace description. 6 | net7.0 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/tests/PortToTripleSlash/TestData/Basic/MyDelegate.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 6 | 7 | This is the sender parameter. 8 | This is the MyDelegate summary. 9 | 10 | 11 | remarks. There is a code example, which should be moved to its own examples section: 16 | 17 | ## Examples 18 | 19 | Here is some text in the examples section. There is an that should be converted to xml. 20 | 21 | The snippet links below should be inserted in markdown. 22 | 23 | [!code-csharp[MyExample#1](~/samples/snippets/example.cs)] 24 | [!code-vb[MyExample#2](~/samples/snippets/example.vb)] 25 | [!code-cpp[MyExample#3](~/samples/snippets/example.cpp)] 26 | 27 | This text should be outside the cdata in xml: . 28 | 29 | ]]> 30 | 31 | 32 | 33 | The .NET Runtime repo. 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/tests/PortToTripleSlash/TestData/Basic/MyEnum.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MyAssembly 5 | 6 | 7 | This is the MyEnum enum summary. 8 | 9 | 10 | enum remarks. They contain an [!INCLUDE[MyInclude](~/includes/MyInclude.md)] which should prevent converting markdown to xml. 15 | 16 | URL entities: %23%28%2C%29 must remain unconverted. 17 | 18 | ]]> 19 | 20 | 21 | 22 | 23 | 24 | Field 25 | 26 | MyAssembly 27 | 28 | 29 | MyNamespace.MyEnum 30 | 31 | 0 32 | 33 | This is the MyEnumValue0 member summary. There is no public modifier. 34 | 35 | 36 | 37 | 38 | Field 39 | 40 | MyAssembly 41 | 42 | 43 | MyNamespace.MyEnum 44 | 45 | 1 46 | 47 | This is the MyEnumValue1 member summary. There is no public modifier. 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/tests/PortToTripleSlash/TestData/Basic/SourceOriginal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MyNamespace 4 | { 5 | // Original MyEnum enum comments with information for maintainers, must stay. 6 | public enum MyEnum 7 | { 8 | MyEnumValue0 = 0, 9 | 10 | MyEnumValue1 = 1 11 | } 12 | 13 | // Original MyType class comments with information for maintainers, must stay. 14 | public class MyType 15 | { 16 | // Original MyType constructor double slash comments on top of triple slash, with information for maintainers, must stay but after triple slash. 17 | /// 18 | /// Original triple slash comments. They should be replaced. 19 | /// 20 | // Original MyType constructor double slash comments on bottom of triple slash, with information for maintainers, must stay. 21 | public MyType() 22 | { 23 | } /* Trailing comments should remain untouched */ 24 | 25 | // Original double slash comments, must stay (internal method). 26 | internal MyType(int myProperty) 27 | { 28 | _myProperty = myProperty; 29 | } // Trailing comments should remain untouched 30 | 31 | /// 32 | /// Triple slash comments above private members should remain untouched. 33 | /// 34 | private int _otherProperty; 35 | 36 | // Double slash comments above private members should remain untouched. 37 | private int _myProperty; 38 | 39 | /// 40 | /// Original triple slash comments. They should be replaced. 41 | /// 42 | // Original MyProperty property double slash comments with information for maintainers, must stay. 43 | // This particular example has two rows of double slash comments and both should stay. 44 | public int MyProperty 45 | { 46 | get { return _myProperty; /* Internal comments should remain untouched. */ } 47 | set { _myProperty = value; } // Internal comments should remain untouched 48 | } 49 | 50 | public int MyField = 1; 51 | 52 | public int MyIntMethod(int param1, int param2) 53 | { 54 | // Internal comments should remain untouched. 55 | return MyField + param1 + param2; 56 | } 57 | 58 | public void MyVoidMethod() 59 | { 60 | } 61 | 62 | /// 63 | /// This method simulates a newly added API that did not have documentation in the docs xml. 64 | /// The developer added the documentation in triple slash comments, so they should be preserved 65 | /// and considered the source of truth. 66 | /// 67 | /// 68 | /// These remarks are the source of truth. 69 | /// 70 | public void UndocumentedMethod() 71 | { 72 | // Set MyEvent to a method of the shape of MyDelegate 73 | MyEvent = (object sender) => { if (sender is int i) { _otherProperty = i; } }; // Use _otherProperty to remove the unused warning 74 | if (MyEvent == null) { } // Use MyEvent to remove the unused warning 75 | } 76 | 77 | public void MyTypeParamMethod(int param1) 78 | { 79 | } 80 | 81 | // Original MyDelegate delegate comments with information for maintainers, must stay. 82 | public delegate void MyDelegate(object sender); 83 | 84 | public event MyDelegate MyEvent; 85 | 86 | // Original operator + method comments with information for maintainers, must stay. 87 | public static MyType operator +(MyType value1, MyType value2) => value1; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/PortToTripleSlash/tests/tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | all 33 | runtime; build; native; contentfiles; analyzers; buildtransitive 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | --------------------------------------------------------------------------------