diff --git a/.gitignore b/.gitignore index 940794e60..e0978f64f 100644 --- a/.gitignore +++ b/.gitignore @@ -286,3 +286,4 @@ __pycache__/ *.btm.cs *.odx.cs *.xsd.cs +/.nuget diff --git a/.nuget/NuGet.Config b/.nuget/NuGet.Config deleted file mode 100644 index 67f8ea046..000000000 --- a/.nuget/NuGet.Config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.nuget/NuGet.exe b/.nuget/NuGet.exe deleted file mode 100644 index 6bb79fe53..000000000 Binary files a/.nuget/NuGet.exe and /dev/null differ diff --git a/.nuget/NuGet.targets b/.nuget/NuGet.targets deleted file mode 100644 index 3f8c37b22..000000000 --- a/.nuget/NuGet.targets +++ /dev/null @@ -1,144 +0,0 @@ - - - - $(MSBuildProjectDirectory)\..\ - - - false - - - false - - - true - - - false - - - - - - - - - - - $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) - - - - - $(SolutionDir).nuget - - - - $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config - $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config - - - - $(MSBuildProjectDirectory)\packages.config - $(PackagesProjectConfig) - - - - - $(NuGetToolsPath)\NuGet.exe - @(PackageSource) - - "$(NuGetExePath)" - mono --runtime=v4.0.30319 "$(NuGetExePath)" - - $(TargetDir.Trim('\\')) - - -RequireConsent - -NonInteractive - - "$(SolutionDir) " - "$(SolutionDir)" - - - $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) - $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols - - - - RestorePackages; - $(BuildDependsOn); - - - - - $(BuildDependsOn); - BuildPackage; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.nuget/packages.config b/.nuget/packages.config deleted file mode 100644 index 0fa4618df..000000000 --- a/.nuget/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e2fcc6e92..97d666144 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Unreleased] +* Decrease horrendousness of performance for large projects containing large files + +### API + +* IEnumerable> becomes IAsyncEnumerable +* Upgraded target framework from netstandard 1.3 to netstandard 2.0 +* Introduced cancellation token ### Vsix @@ -21,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * Exclude project file from conversion result if it hasn't changed * Further efforts to stop the roslyn library crashing Visual Studio +* Conversion tasks are now cancellable ### VB -> C# @@ -30,6 +38,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * Avoid incorrectly renaming symbols * Prevent "SyntaxTree is not part of the compilation" error [#527](https://github.com/icsharpcode/CodeConverter/issues/527) +* Avoid incorrectly renaming symbols (#524)[https://github.com/icsharpcode/CodeConverter/issues/524] ## [7.8.0] - 2020-02-15 diff --git a/CodeConverter.Web/Pages/Index.cshtml.cs b/CodeConverter.Web/Pages/Index.cshtml.cs deleted file mode 100644 index 8dd3ccdd6..000000000 --- a/CodeConverter.Web/Pages/Index.cshtml.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace CodeConverter.Web.Pages -{ - public class IndexModel : PageModel - { - public void OnGet() - { - - } - } -} diff --git a/CodeConverter.Web/Pages/Privacy.cshtml.cs b/CodeConverter.Web/Pages/Privacy.cshtml.cs deleted file mode 100644 index 22313d539..000000000 --- a/CodeConverter.Web/Pages/Privacy.cshtml.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace CodeConverter.Web.Pages -{ - public class PrivacyModel : PageModel - { - public void OnGet() - { - } - } -} diff --git a/CodeConverter.sln b/CodeConverter.sln index 33588d6a1..ac5a19be0 100644 --- a/CodeConverter.sln +++ b/CodeConverter.sln @@ -1,9 +1,9 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2010 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29721.120 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICSharpCode.CodeConverter", "ICSharpCode.CodeConverter\ICSharpCode.CodeConverter.csproj", "{7EA075C6-6406-445C-AB77-6C47AFF88D58}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeConverter", "CodeConverter\CodeConverter.csproj", "{7EA075C6-6406-445C-AB77-6C47AFF88D58}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{21DBA1CE-AF55-4159-B04B-B8C621BE8921}" EndProject @@ -14,7 +14,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeConverter.Web", "CodeConverter.Web\CodeConverter.Web.csproj", "{08A20D4F-6310-43BB-B339-6317ACF3B52E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web", "Web\Web.csproj", "{08A20D4F-6310-43BB-B339-6317ACF3B52E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/CodeConverter/AssemblyInfo.cs b/CodeConverter/AssemblyInfo.cs new file mode 100644 index 000000000..f7afff2b9 --- /dev/null +++ b/CodeConverter/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("ICSharpCode.CodeConverter.Tests")] \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/CSharp/AdditionalInitializers.cs b/CodeConverter/CSharp/AdditionalInitializers.cs similarity index 100% rename from ICSharpCode.CodeConverter/CSharp/AdditionalInitializers.cs rename to CodeConverter/CSharp/AdditionalInitializers.cs diff --git a/ICSharpCode.CodeConverter/CSharp/AdditionalLocal.cs b/CodeConverter/CSharp/AdditionalLocal.cs similarity index 91% rename from ICSharpCode.CodeConverter/CSharp/AdditionalLocal.cs rename to CodeConverter/CSharp/AdditionalLocal.cs index 7b40cf29e..0decb9205 100644 --- a/ICSharpCode.CodeConverter/CSharp/AdditionalLocal.cs +++ b/CodeConverter/CSharp/AdditionalLocal.cs @@ -1,6 +1,4 @@ using System; -using System.Text; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace ICSharpCode.CodeConverter.CSharp diff --git a/ICSharpCode.CodeConverter/CSharp/AdditionalLocals.cs b/CodeConverter/CSharp/AdditionalLocals.cs similarity index 97% rename from ICSharpCode.CodeConverter/CSharp/AdditionalLocals.cs rename to CodeConverter/CSharp/AdditionalLocals.cs index 46adc9695..1d2234320 100644 --- a/ICSharpCode.CodeConverter/CSharp/AdditionalLocals.cs +++ b/CodeConverter/CSharp/AdditionalLocals.cs @@ -1,7 +1,6 @@ using System.Collections; using System.Collections.Generic; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; namespace ICSharpCode.CodeConverter.CSharp { diff --git a/ICSharpCode.CodeConverter/CSharp/ByRefParameterVisitor.cs b/CodeConverter/CSharp/ByRefParameterVisitor.cs similarity index 99% rename from ICSharpCode.CodeConverter/CSharp/ByRefParameterVisitor.cs rename to CodeConverter/CSharp/ByRefParameterVisitor.cs index 0bd2ea8f0..eba3aa7cd 100644 --- a/ICSharpCode.CodeConverter/CSharp/ByRefParameterVisitor.cs +++ b/CodeConverter/CSharp/ByRefParameterVisitor.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/ICSharpCode.CodeConverter/CSharp/CachedReflectedDelegates.cs b/CodeConverter/CSharp/CachedReflectedDelegates.cs similarity index 99% rename from ICSharpCode.CodeConverter/CSharp/CachedReflectedDelegates.cs rename to CodeConverter/CSharp/CachedReflectedDelegates.cs index c60dd1a65..bcf1d7ed0 100644 --- a/ICSharpCode.CodeConverter/CSharp/CachedReflectedDelegates.cs +++ b/CodeConverter/CSharp/CachedReflectedDelegates.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using ICSharpCode.CodeConverter.Util; +using System.Reflection; using Microsoft.CodeAnalysis; namespace ICSharpCode.CodeConverter.CSharp diff --git a/ICSharpCode.CodeConverter/CSharp/CommentConvertingMethodBodyVisitor.cs b/CodeConverter/CSharp/CommentConvertingMethodBodyVisitor.cs similarity index 89% rename from ICSharpCode.CodeConverter/CSharp/CommentConvertingMethodBodyVisitor.cs rename to CodeConverter/CSharp/CommentConvertingMethodBodyVisitor.cs index adf943310..f80297a11 100644 --- a/ICSharpCode.CodeConverter/CSharp/CommentConvertingMethodBodyVisitor.cs +++ b/CodeConverter/CSharp/CommentConvertingMethodBodyVisitor.cs @@ -1,14 +1,10 @@ using System; -using System.Linq; using System.Threading.Tasks; -using ICSharpCode.CodeConverter.Shared; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.VisualBasic; -using Microsoft.CodeAnalysis.VisualBasic.Syntax; using CSSyntax = Microsoft.CodeAnalysis.CSharp.Syntax; using SyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using SyntaxNodeExtensions = ICSharpCode.CodeConverter.Util.SyntaxNodeExtensions; namespace ICSharpCode.CodeConverter.CSharp { diff --git a/ICSharpCode.CodeConverter/CSharp/CommentConvertingNodesVisitor.cs b/CodeConverter/CSharp/CommentConvertingNodesVisitor.cs similarity index 81% rename from ICSharpCode.CodeConverter/CSharp/CommentConvertingNodesVisitor.cs rename to CodeConverter/CSharp/CommentConvertingNodesVisitor.cs index d00d1a478..389576398 100644 --- a/ICSharpCode.CodeConverter/CSharp/CommentConvertingNodesVisitor.cs +++ b/CodeConverter/CSharp/CommentConvertingNodesVisitor.cs @@ -1,17 +1,10 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using ICSharpCode.CodeConverter.Shared; +using System.Threading.Tasks; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.VisualBasic; using VbSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; using CsSyntax = Microsoft.CodeAnalysis.CSharp.Syntax; -using SyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using SyntaxKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind; -using SyntaxNodeExtensions = ICSharpCode.CodeConverter.Util.SyntaxNodeExtensions; namespace ICSharpCode.CodeConverter.CSharp { diff --git a/ICSharpCode.CodeConverter/CSharp/CommentConvertingVisitorWrapper.cs b/CodeConverter/CSharp/CommentConvertingVisitorWrapper.cs similarity index 100% rename from ICSharpCode.CodeConverter/CSharp/CommentConvertingVisitorWrapper.cs rename to CodeConverter/CSharp/CommentConvertingVisitorWrapper.cs diff --git a/ICSharpCode.CodeConverter/CSharp/CommonConversions.cs b/CodeConverter/CSharp/CommonConversions.cs similarity index 99% rename from ICSharpCode.CodeConverter/CSharp/CommonConversions.cs rename to CodeConverter/CSharp/CommonConversions.cs index 8a80d31c2..0f4420246 100644 --- a/ICSharpCode.CodeConverter/CSharp/CommonConversions.cs +++ b/CodeConverter/CSharp/CommonConversions.cs @@ -20,7 +20,6 @@ using ArrayTypeSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.ArrayTypeSyntax; using CSharpExtensions = Microsoft.CodeAnalysis.CSharp.CSharpExtensions; using ExpressionSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax; -using ISymbolExtensions = ICSharpCode.CodeConverter.Util.ISymbolExtensions; using SyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; using SyntaxFacts = Microsoft.CodeAnalysis.CSharp.SyntaxFacts; using SyntaxKind = Microsoft.CodeAnalysis.VisualBasic.SyntaxKind; diff --git a/ICSharpCode.CodeConverter/CSharp/CsSyntaxNodeExtensions.cs b/CodeConverter/CSharp/CsSyntaxNodeExtensions.cs similarity index 78% rename from ICSharpCode.CodeConverter/CSharp/CsSyntaxNodeExtensions.cs rename to CodeConverter/CSharp/CsSyntaxNodeExtensions.cs index 901c7bb57..133a2921d 100644 --- a/ICSharpCode.CodeConverter/CSharp/CsSyntaxNodeExtensions.cs +++ b/CodeConverter/CSharp/CsSyntaxNodeExtensions.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/ICSharpCode.CodeConverter/CSharp/DeclarationNodeVisitor.cs b/CodeConverter/CSharp/DeclarationNodeVisitor.cs similarity index 99% rename from ICSharpCode.CodeConverter/CSharp/DeclarationNodeVisitor.cs rename to CodeConverter/CSharp/DeclarationNodeVisitor.cs index a0939cb24..07183d120 100644 --- a/ICSharpCode.CodeConverter/CSharp/DeclarationNodeVisitor.cs +++ b/CodeConverter/CSharp/DeclarationNodeVisitor.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading.Tasks; using ICSharpCode.CodeConverter.Shared; @@ -11,10 +9,8 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; -using Microsoft.VisualBasic.CompilerServices; using StringComparer = System.StringComparer; using SyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using SyntaxNodeExtensions = ICSharpCode.CodeConverter.Util.SyntaxNodeExtensions; using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; using VBasic = Microsoft.CodeAnalysis.VisualBasic; using SyntaxToken = Microsoft.CodeAnalysis.SyntaxToken; diff --git a/ICSharpCode.CodeConverter/CSharp/DefiniteAssignmentAnalyzer.cs b/CodeConverter/CSharp/DefiniteAssignmentAnalyzer.cs similarity index 81% rename from ICSharpCode.CodeConverter/CSharp/DefiniteAssignmentAnalyzer.cs rename to CodeConverter/CSharp/DefiniteAssignmentAnalyzer.cs index b5894d9fd..fc1724748 100644 --- a/ICSharpCode.CodeConverter/CSharp/DefiniteAssignmentAnalyzer.cs +++ b/CodeConverter/CSharp/DefiniteAssignmentAnalyzer.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; +using System.Linq; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; diff --git a/ICSharpCode.CodeConverter/CSharp/ExpressionNodeVisitor.cs b/CodeConverter/CSharp/ExpressionNodeVisitor.cs similarity index 99% rename from ICSharpCode.CodeConverter/CSharp/ExpressionNodeVisitor.cs rename to CodeConverter/CSharp/ExpressionNodeVisitor.cs index 41c22c94d..43d1eb4ca 100644 --- a/ICSharpCode.CodeConverter/CSharp/ExpressionNodeVisitor.cs +++ b/CodeConverter/CSharp/ExpressionNodeVisitor.cs @@ -1,20 +1,16 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; -using System.Linq.Expressions; using System.Threading.Tasks; using ICSharpCode.CodeConverter.Shared; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Simplification; using Microsoft.VisualBasic.CompilerServices; using IOperation = Microsoft.CodeAnalysis.IOperation; -using ISymbolExtensions = ICSharpCode.CodeConverter.Util.ISymbolExtensions; using SyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; using SyntaxKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind; using VBasic = Microsoft.CodeAnalysis.VisualBasic; diff --git a/ICSharpCode.CodeConverter/CSharp/LambdaConverter.cs b/CodeConverter/CSharp/LambdaConverter.cs similarity index 97% rename from ICSharpCode.CodeConverter/CSharp/LambdaConverter.cs rename to CodeConverter/CSharp/LambdaConverter.cs index b7de0bee3..5b4b6eaf9 100644 --- a/ICSharpCode.CodeConverter/CSharp/LambdaConverter.cs +++ b/CodeConverter/CSharp/LambdaConverter.cs @@ -5,10 +5,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Operations; -using ISymbolExtensions = ICSharpCode.CodeConverter.Util.ISymbolExtensions; -using VBasic = Microsoft.CodeAnalysis.VisualBasic; using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; namespace ICSharpCode.CodeConverter.CSharp diff --git a/ICSharpCode.CodeConverter/CSharp/LiteralConversions.cs b/CodeConverter/CSharp/LiteralConversions.cs similarity index 97% rename from ICSharpCode.CodeConverter/CSharp/LiteralConversions.cs rename to CodeConverter/CSharp/LiteralConversions.cs index 5648d9c86..c75f5dc97 100644 --- a/ICSharpCode.CodeConverter/CSharp/LiteralConversions.cs +++ b/CodeConverter/CSharp/LiteralConversions.cs @@ -1,14 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis.CSharp; using ExpressionSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax; using SyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using SyntaxKind = Microsoft.CodeAnalysis.VisualBasic.SyntaxKind; -using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; -using CSSyntax = Microsoft.CodeAnalysis.CSharp.Syntax; using CSSyntaxKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind; namespace ICSharpCode.CodeConverter.CSharp diff --git a/ICSharpCode.CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs b/CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs similarity index 99% rename from ICSharpCode.CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs rename to CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs index 68a9cac5e..3ac5832b3 100644 --- a/ICSharpCode.CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs +++ b/CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs @@ -1,9 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; -using System.Text; using System.Threading.Tasks; using ICSharpCode.CodeConverter.Shared; using ICSharpCode.CodeConverter.Util; @@ -14,7 +12,6 @@ using SyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; using VBasic = Microsoft.CodeAnalysis.VisualBasic; using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; -using static ICSharpCode.CodeConverter.CSharp.SyntaxKindExtensions; using Microsoft.CodeAnalysis.Text; namespace ICSharpCode.CodeConverter.CSharp diff --git a/ICSharpCode.CodeConverter/CSharp/MethodInfoExtensions.cs b/CodeConverter/CSharp/MethodInfoExtensions.cs similarity index 99% rename from ICSharpCode.CodeConverter/CSharp/MethodInfoExtensions.cs rename to CodeConverter/CSharp/MethodInfoExtensions.cs index 5f344935b..885f2d03f 100644 --- a/ICSharpCode.CodeConverter/CSharp/MethodInfoExtensions.cs +++ b/CodeConverter/CSharp/MethodInfoExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using System.Reflection; -using System.Text; using ICSharpCode.CodeConverter.Util; namespace ICSharpCode.CodeConverter.CSharp diff --git a/ICSharpCode.CodeConverter/CSharp/MethodWithHandles.cs b/CodeConverter/CSharp/MethodWithHandles.cs similarity index 100% rename from ICSharpCode.CodeConverter/CSharp/MethodWithHandles.cs rename to CodeConverter/CSharp/MethodWithHandles.cs diff --git a/ICSharpCode.CodeConverter/CSharp/MethodsWithHandles.cs b/CodeConverter/CSharp/MethodsWithHandles.cs similarity index 98% rename from ICSharpCode.CodeConverter/CSharp/MethodsWithHandles.cs rename to CodeConverter/CSharp/MethodsWithHandles.cs index 8d911a566..b18387c9c 100644 --- a/ICSharpCode.CodeConverter/CSharp/MethodsWithHandles.cs +++ b/CodeConverter/CSharp/MethodsWithHandles.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; diff --git a/ICSharpCode.CodeConverter/CSharp/OperationExtensions.cs b/CodeConverter/CSharp/OperationExtensions.cs similarity index 100% rename from ICSharpCode.CodeConverter/CSharp/OperationExtensions.cs rename to CodeConverter/CSharp/OperationExtensions.cs diff --git a/ICSharpCode.CodeConverter/CSharp/ProjectExtensions.cs b/CodeConverter/CSharp/ProjectExtensions.cs similarity index 62% rename from ICSharpCode.CodeConverter/CSharp/ProjectExtensions.cs rename to CodeConverter/CSharp/ProjectExtensions.cs index a831b46ca..4d383647a 100644 --- a/ICSharpCode.CodeConverter/CSharp/ProjectExtensions.cs +++ b/CodeConverter/CSharp/ProjectExtensions.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading.Tasks; using ICSharpCode.CodeConverter.Shared; using Microsoft.CodeAnalysis; @@ -9,6 +8,8 @@ namespace ICSharpCode.CodeConverter.CSharp { internal static class ProjectExtensions { + private static char[] DirSeparators = new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; + public static Project CreateReferenceOnlyProjectFromAnyOptions(this Project project, CompilationOptions baseOptions) { var options = baseOptions.WithMetadataImportOptionsAll(); @@ -41,33 +42,50 @@ public static Project ToProjectFromAnyOptions(this Project project, CompilationO public static string GetDirectoryPath(this Project proj) { string projectFilePath = proj.FilePath; - return projectFilePath != null ? Path.GetDirectoryName(projectFilePath) : null; + if (projectFilePath != null) { + return Path.GetDirectoryName(projectFilePath); + } + + string solutionPath = GetDirectoryPath(proj); + return proj.Documents + .Where(d => d.FilePath != null && d.FilePath.StartsWith(solutionPath)) + .Select(d => d.FilePath.Replace(solutionPath, "").TrimStart(DirSeparators)) + .Where(p => p.IndexOfAny(DirSeparators) > -1) + .Select(p => p.Split(DirSeparators).First()) + .OrderByDescending(p => p.Contains(proj.AssemblyName)) + .FirstOrDefault() ?? solutionPath; + } + + public static string GetDirectoryPath(this Solution soln) + { + // Find a directory for projects that don't have a projectfile (e.g. websites) Current dir if in memory + return soln.FilePath != null ? Path.GetDirectoryName(soln.FilePath) : Directory.GetCurrentDirectory(); } - public static (Project project, List<(string Path, DocumentId DocId, string[] Errors)> firstPassDocIds) - WithDocuments(this Project project, (string Path, SyntaxNode Node, string[] Errors)[] results) + public static (Project project, List> firstPassDocIds) + WithDocuments(this Project project, WipFileConversion[] results) { var firstPassDocIds = results.Select(firstPassResult => { DocumentId docId = null; - if (firstPassResult.Node != null) + if (firstPassResult.Wip != null) { - var document = project.AddDocument(firstPassResult.Path, firstPassResult.Node, + var document = project.AddDocument(firstPassResult.Path, firstPassResult.Wip, filePath: firstPassResult.Path); project = document.Project; docId = document.Id; } - return (firstPassResult.Path, docId, firstPassResult.Errors); + return WipFileConversion.Create(firstPassResult.Path, docId, firstPassResult.Errors); }).ToList(); //ToList ensures that the project returned has all documents added. We only return DocumentIds so it's easy to look up the final version of the doc later return (project, firstPassDocIds); } - public static IEnumerable<(string Path, Document Doc, string[] Errors)> GetDocuments(this Project project, List<(string treeFilePath, DocumentId docId, string[] errors)> docIds) + public static IEnumerable> GetDocuments(this Project project, List> docIds) { - return docIds.Select(f => (f.treeFilePath, f.docId != null ? project.GetDocument(f.docId) : null, f.errors)); + return docIds.Select(f => WipFileConversion.Create(f.Path, f.Wip != null ? project.GetDocument(f.Wip) : null, f.Errors)); } } } \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/CSharp/ProjectMergedDeclarationExtensions.cs b/CodeConverter/CSharp/ProjectMergedDeclarationExtensions.cs similarity index 88% rename from ICSharpCode.CodeConverter/CSharp/ProjectMergedDeclarationExtensions.cs rename to CodeConverter/CSharp/ProjectMergedDeclarationExtensions.cs index 42c8aba6a..9281e3134 100644 --- a/ICSharpCode.CodeConverter/CSharp/ProjectMergedDeclarationExtensions.cs +++ b/CodeConverter/CSharp/ProjectMergedDeclarationExtensions.cs @@ -1,16 +1,14 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection; using System.Threading.Tasks; using ICSharpCode.CodeConverter.Shared; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Rename; -using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.VisualBasic; +using System.Threading; namespace ICSharpCode.CodeConverter.CSharp { @@ -22,12 +20,12 @@ namespace ICSharpCode.CodeConverter.CSharp /// internal static class ProjectMergedDeclarationExtensions { - public static async Task WithRenamedMergedMyNamespace(this Project vbProject) + public static async Task WithRenamedMergedMyNamespace(this Project vbProject, CancellationToken cancellationToken) { string name = "MyNamespace"; - var projectDir = Path.Combine(vbProject.GetDirectoryPath() ?? vbProject.AssemblyName, "My Project"); + var projectDir = Path.Combine(vbProject.GetDirectoryPath(), "My Project"); - var compilation = await vbProject.GetCompilationAsync(); + var compilation = await vbProject.GetCompilationAsync(cancellationToken); string embeddedSourceText = (await GetAllEmbeddedSourceText(compilation)); string generatedSourceText = (await GetDynamicallyGeneratedSourceText(compilation)); @@ -114,27 +112,29 @@ private static string Renamespace(this string sourceText) .Replace("Namespace My", $"Namespace {Constants.MergedMyNamespace}"); } - public static async Task RenameMergedNamespaces(this Project project) + public static async Task RenameMergedNamespaces(this Project project, CancellationToken cancellationToken) { - project = await RenamePrefix(project, Constants.MergedMyNamespace, "My", SymbolFilter.Namespace); - project = await RenamePrefix(project, Constants.MergedMsVbNamespace, "VisualBasic", SymbolFilter.Namespace); - project = await RenamePrefix(project, Constants.MergedMyMemberPrefix, "", SymbolFilter.Member); + project = await RenamePrefix(project, Constants.MergedMyNamespace, "My", SymbolFilter.Namespace, cancellationToken); + project = await RenamePrefix(project, Constants.MergedMsVbNamespace, "VisualBasic", SymbolFilter.Namespace, cancellationToken); + project = await RenamePrefix(project, Constants.MergedMyMemberPrefix, "", SymbolFilter.Member, cancellationToken); return project; } - private static async Task RenamePrefix(Project project, string oldNamePrefix, string newNamePrefix, SymbolFilter symbolFilter) + private static async Task RenamePrefix(Project project, string oldNamePrefix, string newNamePrefix, SymbolFilter symbolFilter, CancellationToken cancellationToken) { - for (var symbolToRename = await GetFirstSymbolStartingWith(project, oldNamePrefix, symbolFilter); symbolToRename != null; symbolToRename = await GetFirstSymbolStartingWith(project, oldNamePrefix, symbolFilter)) + for (var symbolToRename = await GetFirstSymbolStartingWith(project, oldNamePrefix, symbolFilter, cancellationToken); + symbolToRename != null; + symbolToRename = await GetFirstSymbolStartingWith(project, oldNamePrefix, symbolFilter, cancellationToken)) { - var renamedSolution = await Renamer.RenameSymbolAsync(project.Solution, symbolToRename, symbolToRename.Name.Replace(oldNamePrefix, newNamePrefix), default(OptionSet)); + var renamedSolution = await Renamer.RenameSymbolAsync(project.Solution, symbolToRename, symbolToRename.Name.Replace(oldNamePrefix, newNamePrefix), default(OptionSet), cancellationToken); project = renamedSolution.GetProject(project.Id); } return project; } - private static async Task GetFirstSymbolStartingWith(Project project, string symbolPrefix, SymbolFilter symbolFilter) + private static async Task GetFirstSymbolStartingWith(Project project, string symbolPrefix, SymbolFilter symbolFilter, CancellationToken cancellationToken) { - var compilation = await project.GetCompilationAsync(); + var compilation = await project.GetCompilationAsync(cancellationToken); return compilation.GetSymbolsWithName(s => s.StartsWith(symbolPrefix), symbolFilter).FirstOrDefault(); } } diff --git a/ICSharpCode.CodeConverter/CSharp/QueryConverter.cs b/CodeConverter/CSharp/QueryConverter.cs similarity index 100% rename from ICSharpCode.CodeConverter/CSharp/QueryConverter.cs rename to CodeConverter/CSharp/QueryConverter.cs diff --git a/ICSharpCode.CodeConverter/CSharp/SemanticModelExtensions.cs b/CodeConverter/CSharp/SemanticModelExtensions.cs similarity index 93% rename from ICSharpCode.CodeConverter/CSharp/SemanticModelExtensions.cs rename to CodeConverter/CSharp/SemanticModelExtensions.cs index 133a83a5d..67a1c131f 100644 --- a/ICSharpCode.CodeConverter/CSharp/SemanticModelExtensions.cs +++ b/CodeConverter/CSharp/SemanticModelExtensions.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Reflection; -using ICSharpCode.CodeConverter.Util; +using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.VisualBasic; diff --git a/ICSharpCode.CodeConverter/CSharp/SourceTriviaMapKind.cs b/CodeConverter/CSharp/SourceTriviaMapKind.cs similarity index 100% rename from ICSharpCode.CodeConverter/CSharp/SourceTriviaMapKind.cs rename to CodeConverter/CSharp/SourceTriviaMapKind.cs diff --git a/ICSharpCode.CodeConverter/CSharp/SyntaxKindExtensions.cs b/CodeConverter/CSharp/SyntaxKindExtensions.cs similarity index 100% rename from ICSharpCode.CodeConverter/CSharp/SyntaxKindExtensions.cs rename to CodeConverter/CSharp/SyntaxKindExtensions.cs diff --git a/ICSharpCode.CodeConverter/CSharp/SyntaxNodeVisitorExtensions.cs b/CodeConverter/CSharp/SyntaxNodeVisitorExtensions.cs similarity index 87% rename from ICSharpCode.CodeConverter/CSharp/SyntaxNodeVisitorExtensions.cs rename to CodeConverter/CSharp/SyntaxNodeVisitorExtensions.cs index 08ea1ea0c..07a956794 100644 --- a/ICSharpCode.CodeConverter/CSharp/SyntaxNodeVisitorExtensions.cs +++ b/CodeConverter/CSharp/SyntaxNodeVisitorExtensions.cs @@ -1,11 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ICSharpCode.CodeConverter.Shared; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using CS = Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.VisualBasic; namespace ICSharpCode.CodeConverter.CSharp diff --git a/ICSharpCode.CodeConverter/CSharp/TypeConversionAnalyzer.cs b/CodeConverter/CSharp/TypeConversionAnalyzer.cs similarity index 99% rename from ICSharpCode.CodeConverter/CSharp/TypeConversionAnalyzer.cs rename to CodeConverter/CSharp/TypeConversionAnalyzer.cs index e35a44f79..c92115488 100644 --- a/ICSharpCode.CodeConverter/CSharp/TypeConversionAnalyzer.cs +++ b/CodeConverter/CSharp/TypeConversionAnalyzer.cs @@ -5,7 +5,6 @@ using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using CSSyntax = Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Operations; diff --git a/ICSharpCode.CodeConverter/CSharp/UsageTypeAnalyzer.cs b/CodeConverter/CSharp/UsageTypeAnalyzer.cs similarity index 96% rename from ICSharpCode.CodeConverter/CSharp/UsageTypeAnalyzer.cs rename to CodeConverter/CSharp/UsageTypeAnalyzer.cs index 138bf615d..e898b23e0 100644 --- a/ICSharpCode.CodeConverter/CSharp/UsageTypeAnalyzer.cs +++ b/CodeConverter/CSharp/UsageTypeAnalyzer.cs @@ -1,7 +1,5 @@ -using System; -using System.Linq; +using System.Linq; using System.Threading.Tasks; -using ICSharpCode.CodeConverter.Shared; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.FindSymbols; diff --git a/ICSharpCode.CodeConverter/CSharp/VBToCSConversion.cs b/CodeConverter/CSharp/VBToCSConversion.cs similarity index 93% rename from ICSharpCode.CodeConverter/CSharp/VBToCSConversion.cs rename to CodeConverter/CSharp/VBToCSConversion.cs index 123012be5..c968cc7a7 100644 --- a/ICSharpCode.CodeConverter/CSharp/VBToCSConversion.cs +++ b/CodeConverter/CSharp/VBToCSConversion.cs @@ -3,21 +3,15 @@ using System.Linq; using ICSharpCode.CodeConverter.Shared; using ICSharpCode.CodeConverter.Util; -using ICSharpCode.CodeConverter.VB; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using SyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; using CSSyntax = Microsoft.CodeAnalysis.CSharp.Syntax; using SyntaxKind = Microsoft.CodeAnalysis.VisualBasic.SyntaxKind; using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; using System.Text.RegularExpressions; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Operations; -using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.VisualBasic; -using Conversion = Microsoft.CodeAnalysis.CSharp.Conversion; using ISymbolExtensions = ICSharpCode.CodeConverter.Util.ISymbolExtensions; -using LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion; +using System.Threading; namespace ICSharpCode.CodeConverter.CSharp { @@ -29,12 +23,17 @@ public class VBToCSConversion : ILanguageConversion private const string UnresolvedNamespaceDiagnosticId = "CS0246"; private VBToCSProjectContentsConverter _vbToCsProjectContentsConverter; + private IProgress _progress; + private CancellationToken _cancellationToken; + public ConversionOptions ConversionOptions { get; set; } - public async Task CreateProjectContentsConverter(Project project) + public async Task CreateProjectContentsConverter(Project project, IProgress progress, CancellationToken cancellationToken) { - _vbToCsProjectContentsConverter = new VBToCSProjectContentsConverter(ConversionOptions); + _progress = progress; + _cancellationToken = cancellationToken; + _vbToCsProjectContentsConverter = new VBToCSProjectContentsConverter(ConversionOptions, progress, cancellationToken); await _vbToCsProjectContentsConverter.InitializeSourceAsync(project); return _vbToCsProjectContentsConverter; } @@ -144,7 +143,7 @@ public List FindSingleImportantChild(SyntaxNode annotatedNode) public async Task SingleSecondPass(Document doc) { - return await doc.SimplifyStatements(UnresolvedNamespaceDiagnosticId); + return await doc.SimplifyStatements(UnresolvedNamespaceDiagnosticId, _cancellationToken); } public SyntaxTree CreateTree(string text) diff --git a/ICSharpCode.CodeConverter/CSharp/VBToCSProjectContentsConverter.cs b/CodeConverter/CSharp/VBToCSProjectContentsConverter.cs similarity index 78% rename from ICSharpCode.CodeConverter/CSharp/VBToCSProjectContentsConverter.cs rename to CodeConverter/CSharp/VBToCSProjectContentsConverter.cs index 15529c3fa..d49890170 100644 --- a/ICSharpCode.CodeConverter/CSharp/VBToCSProjectContentsConverter.cs +++ b/CodeConverter/CSharp/VBToCSProjectContentsConverter.cs @@ -1,13 +1,13 @@ using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using ICSharpCode.CodeConverter.Shared; using ICSharpCode.CodeConverter.Util; -using ICSharpCode.CodeConverter.VB; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.VisualBasic; -using ISymbolExtensions = ICSharpCode.CodeConverter.Util.ISymbolExtensions; using LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion; +using System; namespace ICSharpCode.CodeConverter.CSharp { @@ -29,10 +29,14 @@ internal class VBToCSProjectContentsConverter : IProjectContentsConverter private static readonly CSharpParseOptions DoNotAllowImplicitDefault = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7); private Project _csharpReferenceProject; + private readonly IProgress _progress; + private readonly CancellationToken _cancellationToken; - public VBToCSProjectContentsConverter(ConversionOptions conversionOptions) + public VBToCSProjectContentsConverter(ConversionOptions conversionOptions, IProgress progress, CancellationToken cancellationToken) { _conversionOptions = conversionOptions; + _progress = progress; + _cancellationToken = cancellationToken; } public string RootNamespace => _conversionOptions.RootNamespaceOverride ?? @@ -43,8 +47,8 @@ public async Task InitializeSourceAsync(Project project) var cSharpCompilationOptions = CSharpCompiler.CreateCompilationOptions(); _convertedCsProject = project.ToProjectFromAnyOptions(cSharpCompilationOptions, DoNotAllowImplicitDefault); _csharpReferenceProject = project.CreateReferenceOnlyProjectFromAnyOptions(cSharpCompilationOptions); - _csharpViewOfVbSymbols = (CSharpCompilation) await _csharpReferenceProject.GetCompilationAsync(); - Project = await project.WithRenamedMergedMyNamespace(); + _csharpViewOfVbSymbols = (CSharpCompilation) await _csharpReferenceProject.GetCompilationAsync(_cancellationToken); + Project = await project.WithRenamedMergedMyNamespace(_cancellationToken); } string IProjectContentsConverter.LanguageVersion { get { return LanguageVersion.Default.ToDisplayString(); } } @@ -53,14 +57,14 @@ public async Task InitializeSourceAsync(Project project) public async Task SingleFirstPass(Document document) { - return await VisualBasicConverter.ConvertCompilationTree(document, _csharpViewOfVbSymbols, _csharpReferenceProject); + return await VisualBasicConverter.ConvertCompilationTree(document, _csharpViewOfVbSymbols, _csharpReferenceProject, _cancellationToken); } - public async Task<(Project project, List<(string Path, DocumentId DocId, string[] Errors)> firstPassDocIds)> - GetConvertedProject((string Path, SyntaxNode Node, string[] Errors)[] firstPassResults) + public async Task<(Project project, List> firstPassDocIds)> + GetConvertedProject(WipFileConversion[] firstPassResults) { var (project, docIds) = _convertedCsProject.WithDocuments(firstPassResults); - return (await project.RenameMergedNamespaces(), docIds); + return (await project.RenameMergedNamespaces(_cancellationToken), docIds); } } } \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/CSharp/ValidSyntaxFactory.cs b/CodeConverter/CSharp/ValidSyntaxFactory.cs similarity index 99% rename from ICSharpCode.CodeConverter/CSharp/ValidSyntaxFactory.cs rename to CodeConverter/CSharp/ValidSyntaxFactory.cs index 01a4854be..fb8c26007 100644 --- a/ICSharpCode.CodeConverter/CSharp/ValidSyntaxFactory.cs +++ b/CodeConverter/CSharp/ValidSyntaxFactory.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq.Expressions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/ICSharpCode.CodeConverter/CSharp/VbMethodSyntaxExtensions.cs b/CodeConverter/CSharp/VbMethodSyntaxExtensions.cs similarity index 100% rename from ICSharpCode.CodeConverter/CSharp/VbMethodSyntaxExtensions.cs rename to CodeConverter/CSharp/VbMethodSyntaxExtensions.cs diff --git a/ICSharpCode.CodeConverter/CSharp/VbNameExpander.cs b/CodeConverter/CSharp/VbNameExpander.cs similarity index 99% rename from ICSharpCode.CodeConverter/CSharp/VbNameExpander.cs rename to CodeConverter/CSharp/VbNameExpander.cs index 7dd2a6e99..cb2274bb6 100644 --- a/ICSharpCode.CodeConverter/CSharp/VbNameExpander.cs +++ b/CodeConverter/CSharp/VbNameExpander.cs @@ -1,6 +1,4 @@ -using System; -using System.Linq; -using System.Threading; +using System.Threading; using ICSharpCode.CodeConverter.Shared; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; diff --git a/ICSharpCode.CodeConverter/CSharp/VbSyntaxNodeExtensions.cs b/CodeConverter/CSharp/VbSyntaxNodeExtensions.cs similarity index 98% rename from ICSharpCode.CodeConverter/CSharp/VbSyntaxNodeExtensions.cs rename to CodeConverter/CSharp/VbSyntaxNodeExtensions.cs index b7866fb41..6bd9543d6 100644 --- a/ICSharpCode.CodeConverter/CSharp/VbSyntaxNodeExtensions.cs +++ b/CodeConverter/CSharp/VbSyntaxNodeExtensions.cs @@ -1,7 +1,6 @@ using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.VisualBasic; using ArgumentSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.ArgumentSyntax; using BinaryExpressionSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.BinaryExpressionSyntax; diff --git a/ICSharpCode.CodeConverter/CSharp/VisualBasicConverter.cs b/CodeConverter/CSharp/VisualBasicConverter.cs similarity index 81% rename from ICSharpCode.CodeConverter/CSharp/VisualBasicConverter.cs rename to CodeConverter/CSharp/VisualBasicConverter.cs index bdd1f3775..41c25e817 100644 --- a/ICSharpCode.CodeConverter/CSharp/VisualBasicConverter.cs +++ b/CodeConverter/CSharp/VisualBasicConverter.cs @@ -1,29 +1,27 @@ using System; -using System.Linq; using System.Threading.Tasks; using ICSharpCode.CodeConverter.Shared; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Simplification; -using VBasic = Microsoft.CodeAnalysis.VisualBasic; using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; using CSS = Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Threading; namespace ICSharpCode.CodeConverter.CSharp { internal static class VisualBasicConverter { public static async Task ConvertCompilationTree(Document document, - CSharpCompilation csharpViewOfVbSymbols, Project csharpReferenceProject) + CSharpCompilation csharpViewOfVbSymbols, Project csharpReferenceProject, CancellationToken cancellationToken) { - document = await document.WithExpandedRootAsync(); - var root = await document.GetSyntaxRootAsync() as VBSyntax.CompilationUnitSyntax ?? + document = await document.WithExpandedRootAsync(cancellationToken); + var root = await document.GetSyntaxRootAsync(cancellationToken) as VBSyntax.CompilationUnitSyntax ?? throw new InvalidOperationException(NullRootError(document)); - var compilation = await document.Project.GetCompilationAsync(); - var tree = await document.GetSyntaxTreeAsync(); + var compilation = await document.Project.GetCompilationAsync(cancellationToken); + var tree = await document.GetSyntaxTreeAsync(cancellationToken); var csSyntaxGenerator = SyntaxGenerator.GetGenerator(csharpReferenceProject); @@ -34,7 +32,7 @@ public static async Task ConvertCompilationTree(Document document, try { // This call is very expensive for large documents. Should look for a more performant version, e.g. Is NormalizeWhitespace good enough? - converted = (CSS.CompilationUnitSyntax)Formatter.Format(converted, document.Project.Solution.Workspace); + converted = (CSS.CompilationUnitSyntax)Formatter.Format(converted, document.Project.Solution.Workspace, cancellationToken: cancellationToken); return LineTriviaMapper.MapSourceTriviaToTarget(root, converted); } catch (Exception) { //TODO log return converted; diff --git a/ICSharpCode.CodeConverter/CSharp/VisualBasicEqualityComparison.cs b/CodeConverter/CSharp/VisualBasicEqualityComparison.cs similarity index 100% rename from ICSharpCode.CodeConverter/CSharp/VisualBasicEqualityComparison.cs rename to CodeConverter/CSharp/VisualBasicEqualityComparison.cs diff --git a/ICSharpCode.CodeConverter/CodeConverter.cs b/CodeConverter/CodeConverter.cs similarity index 100% rename from ICSharpCode.CodeConverter/CodeConverter.cs rename to CodeConverter/CodeConverter.cs diff --git a/ICSharpCode.CodeConverter/ICSharpCode.CodeConverter.csproj b/CodeConverter/CodeConverter.csproj similarity index 71% rename from ICSharpCode.CodeConverter/ICSharpCode.CodeConverter.csproj rename to CodeConverter/CodeConverter.csproj index c7258e055..95eb4a939 100644 --- a/ICSharpCode.CodeConverter/ICSharpCode.CodeConverter.csproj +++ b/CodeConverter/CodeConverter.csproj @@ -1,15 +1,13 @@  - netstandard1.3 + netstandard2.0 ICSharpCode.CodeConverter ICSharpCode.CodeConverter - portable-net45+win8 ICSharpCode Convert VB.NET to/from C# - - * Accurate: Full project context (through Roslyn) is used to get the most accurate conversion. - * Actively developed: User feedback helps us continuously strive for a more accurate conversion. - * Completely free and open source: Check out [GitHub](https://github.com/icsharpcode/CodeConverter#code-converter-). +* Accurate: Full project context (through Roslyn) is used to get the most accurate conversion. +* Actively developed: User feedback helps us continuously strive for a more accurate conversion. +* Completely free and open source: Check out [GitHub](https://github.com/icsharpcode/CodeConverter#code-converter-). Code Converter for C# to/from VB.NET Copyright (c) 2017-2020 AlphaSierraPapa for the CodeConverter team 7.9.0.0 @@ -23,7 +21,7 @@ Convert Converter Conversion C# CSharp CS VB VB.NET Visual Basic Code Free Roslyn Tool See https://github.com/icsharpcode/CodeConverter/blob/master/CHANGELOG.md $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - 7.3 + 8.0 $(NoWarn);1998 @@ -33,18 +31,16 @@ - - - - - - + + all runtime; build; native; contentfiles; analyzers + + diff --git a/ICSharpCode.CodeConverter/CodeWithOptions.cs b/CodeConverter/CodeWithOptions.cs similarity index 95% rename from ICSharpCode.CodeConverter/CodeWithOptions.cs rename to CodeConverter/CodeWithOptions.cs index 8f88a1cb2..ed42d2558 100644 --- a/ICSharpCode.CodeConverter/CodeWithOptions.cs +++ b/CodeConverter/CodeWithOptions.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; -using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; namespace ICSharpCode.CodeConverter @@ -22,7 +20,7 @@ public class CodeWithOptions private static IEnumerable GetRefs(IReadOnlyCollection types) { - return types.Select(type => MetadataReference.CreateFromFile(type.GetAssemblyLocation())); + return types.Select(type => MetadataReference.CreateFromFile(type.Assembly.Location)); } public string Text { get; private set; } diff --git a/ICSharpCode.CodeConverter/ConversionResult.cs b/CodeConverter/ConversionResult.cs similarity index 100% rename from ICSharpCode.CodeConverter/ConversionResult.cs rename to CodeConverter/ConversionResult.cs diff --git a/ICSharpCode.CodeConverter/ILanguageConversion.cs b/CodeConverter/ILanguageConversion.cs similarity index 89% rename from ICSharpCode.CodeConverter/ILanguageConversion.cs rename to CodeConverter/ILanguageConversion.cs index cb4e025cc..6ac86aaef 100644 --- a/ICSharpCode.CodeConverter/ILanguageConversion.cs +++ b/CodeConverter/ILanguageConversion.cs @@ -2,6 +2,8 @@ using System.Threading.Tasks; using ICSharpCode.CodeConverter.Shared; using Microsoft.CodeAnalysis; +using System.Threading; +using System; namespace ICSharpCode.CodeConverter { @@ -22,7 +24,7 @@ SyntaxNode GetSurroundedNode(IEnumerable descendantNodes, string TargetLanguage { get; } ConversionOptions ConversionOptions { get; set; } - Task CreateProjectContentsConverter(Project project); + Task CreateProjectContentsConverter(Project project, IProgress progress, CancellationToken cancellationToken); string PostTransformProjectFile(string xml); Document CreateProjectDocumentFromTree(Workspace workspace, SyntaxTree tree, diff --git a/CodeConverter/Shared/ActionDisposable.cs b/CodeConverter/Shared/ActionDisposable.cs new file mode 100644 index 000000000..d68802c57 --- /dev/null +++ b/CodeConverter/Shared/ActionDisposable.cs @@ -0,0 +1,17 @@ +using System; + +namespace ICSharpCode.CodeConverter.Shared +{ + + internal sealed class ActionDisposable : IDisposable + { + private readonly Action _onDispose; + + public ActionDisposable(Action onDispose) + { + _onDispose = onDispose; + } + + public void Dispose() => _onDispose(); + } +} \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/Shared/AnnotationConstants.cs b/CodeConverter/Shared/AnnotationConstants.cs similarity index 100% rename from ICSharpCode.CodeConverter/Shared/AnnotationConstants.cs rename to CodeConverter/Shared/AnnotationConstants.cs diff --git a/CodeConverter/Shared/AsyncEnumerableTaskExtensions.cs b/CodeConverter/Shared/AsyncEnumerableTaskExtensions.cs new file mode 100644 index 000000000..afe372102 --- /dev/null +++ b/CodeConverter/Shared/AsyncEnumerableTaskExtensions.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using System.Threading.Tasks.Dataflow; + +namespace ICSharpCode.CodeConverter.Shared +{ + internal static class AsyncEnumerableTaskExtensions + { + public static async Task SelectManyAsync(this IEnumerable nodes, + Func>> selector) + { + var selectAsync = await nodes.SelectAsync(selector); + return selectAsync.SelectMany(x => x).ToArray(); + } + + /// High throughput parallel lazy-ish method + /// + /// Inspired by https://stackoverflow.com/a/58564740/1128762 + /// + public static async IAsyncEnumerable ParallelSelectAwait(this IEnumerable source, + Func> selector, int maxDop, [EnumeratorCancellation] CancellationToken token = default) + { + var processor = new TransformBlock(selector, new ExecutionDataflowBlockOptions { + MaxDegreeOfParallelism = maxDop, + BoundedCapacity = (maxDop * 5) / 4, + CancellationToken = token + }); + + foreach (var item in source) { + while (!processor.Post(item)) { + yield return await ReceiveAsync(); + } + if (processor.TryReceive(out var result)) { + yield return result; + } + } + processor.Complete(); + + while (await processor.OutputAvailableAsync(token)) { + yield return Receive(); + } + + async Task ReceiveAsync() + { + if (!await processor.OutputAvailableAsync() && !token.IsCancellationRequested) throw new InvalidOperationException("No output available after posting output and waiting"); + return Receive(); + } + + TResult Receive() + { + token.ThrowIfCancellationRequested(); + if (!processor.TryReceive(out var result)) throw new InvalidOperationException("Nothing received even though output available"); + return result; + } + } + + public static async Task SelectAsync(this IEnumerable nodes, + Func> selector) + { + var nodesWithOrders = nodes.Select((input, originalOrder) => (input, originalOrder)); + return await nodesWithOrders.SelectAsync(nwo => selector(nwo.input, nwo.originalOrder)); + } + + public static async Task SelectAsync(this IEnumerable source, + Func> selector) + { + var partitionResults = new List(); + foreach (var partitionMember in source) { + var result = await selector(partitionMember); + partitionResults.Add(result); + } + + return partitionResults.ToArray(); + } + } +} \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/Shared/CompilationOptionsExtensions.cs b/CodeConverter/Shared/CompilationOptionsExtensions.cs similarity index 77% rename from ICSharpCode.CodeConverter/Shared/CompilationOptionsExtensions.cs rename to CodeConverter/Shared/CompilationOptionsExtensions.cs index c1df29d2d..cfbd04816 100644 --- a/ICSharpCode.CodeConverter/Shared/CompilationOptionsExtensions.cs +++ b/CodeConverter/Shared/CompilationOptionsExtensions.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; -using System.Threading.Tasks; using ICSharpCode.CodeConverter.CSharp; -using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; namespace ICSharpCode.CodeConverter.Shared @@ -17,8 +14,13 @@ public static Document CreateProjectDocumentFromTree(this CompilationOptions opt { singleDocumentAssemblyName = singleDocumentAssemblyName ?? "ProjectToBeConverted"; ProjectId projectId = ProjectId.CreateNewId(); + + string projFileExtension = parseOptions.Language == LanguageNames.CSharp ? ".csproj" : ".vbproj"; + var projectFilePath = Path.Combine(Directory.GetCurrentDirectory() + singleDocumentAssemblyName + projFileExtension); + var solution = workspace.CurrentSolution.AddProject(projectId, singleDocumentAssemblyName, - singleDocumentAssemblyName, options.Language); + singleDocumentAssemblyName, options.Language) + .WithProjectFilePath(projectId, projectFilePath); var project = solution.GetProject(projectId) .WithCompilationOptions(options) diff --git a/ICSharpCode.CodeConverter/Shared/Constants.cs b/CodeConverter/Shared/Constants.cs similarity index 100% rename from ICSharpCode.CodeConverter/Shared/Constants.cs rename to CodeConverter/Shared/Constants.cs diff --git a/ICSharpCode.CodeConverter/Shared/ConversionOptions.cs b/CodeConverter/Shared/ConversionOptions.cs similarity index 100% rename from ICSharpCode.CodeConverter/Shared/ConversionOptions.cs rename to CodeConverter/Shared/ConversionOptions.cs diff --git a/ICSharpCode.CodeConverter/Shared/ConversionProgress.cs b/CodeConverter/Shared/ConversionProgress.cs similarity index 100% rename from ICSharpCode.CodeConverter/Shared/ConversionProgress.cs rename to CodeConverter/Shared/ConversionProgress.cs diff --git a/ICSharpCode.CodeConverter/Shared/DefaultReferences.cs b/CodeConverter/Shared/DefaultReferences.cs similarity index 100% rename from ICSharpCode.CodeConverter/Shared/DefaultReferences.cs rename to CodeConverter/Shared/DefaultReferences.cs diff --git a/ICSharpCode.CodeConverter/Shared/DocumentExtensions.cs b/CodeConverter/Shared/DocumentExtensions.cs similarity index 87% rename from ICSharpCode.CodeConverter/Shared/DocumentExtensions.cs rename to CodeConverter/Shared/DocumentExtensions.cs index a08feb725..3c725a637 100644 --- a/ICSharpCode.CodeConverter/Shared/DocumentExtensions.cs +++ b/CodeConverter/Shared/DocumentExtensions.cs @@ -1,23 +1,21 @@ using System; using System.Collections.Immutable; using System.Linq; +using System.Threading; using System.Threading.Tasks; using ICSharpCode.CodeConverter.CSharp; using ICSharpCode.CodeConverter.Util; using ICSharpCode.CodeConverter.VB; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Simplification; -using VBasic = Microsoft.CodeAnalysis.VisualBasic; using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; namespace ICSharpCode.CodeConverter.Shared { internal static class DocumentExtensions { - public static async Task SimplifyStatements(this Document convertedDocument, string unresolvedTypeDiagnosticId) + public static async Task SimplifyStatements(this Document convertedDocument, string unresolvedTypeDiagnosticId, CancellationToken cancellationToken) where TUsingDirectiveSyntax : SyntaxNode where TExpressionSyntax : SyntaxNode { Func wouldBeSimplifiedIncorrectly = @@ -43,7 +41,7 @@ public static async Task SimplifyStatements WithExpandedRootAsync(this Document document) + public static async Task WithExpandedRootAsync(this Document document, CancellationToken cancellationToken) { if (document.Project.Language == LanguageNames.VisualBasic) { - document = await ExpandAsync(document, VbNameExpander.Instance); + document = await ExpandAsync(document, VbNameExpander.Instance, cancellationToken); } else { - document = await ExpandAsync(document, CsExpander.Instance); + document = await ExpandAsync(document, CsExpander.Instance, cancellationToken); } return document; } - private static async Task ExpandAsync(Document document, ISyntaxExpander expander) + private static async Task ExpandAsync(Document document, ISyntaxExpander expander, CancellationToken cancellationToken) { - var semanticModel = await document.GetSemanticModelAsync(); + var semanticModel = await document.GetSemanticModelAsync(cancellationToken); var workspace = document.Project.Solution.Workspace; - var root = await document.GetSyntaxRootAsync(); + var root = await document.GetSyntaxRootAsync(cancellationToken); try { var newRoot = root.ReplaceNodes(root.DescendantNodes(n => expander.ShouldExpandWithinNode(n, root, semanticModel)).Where(n => expander.ShouldExpandNode(n, root, semanticModel)), - (node, rewrittenNode) => TryExpandNode(expander, node, root, semanticModel, workspace) + (node, rewrittenNode) => TryExpandNode(expander, node, root, semanticModel, workspace, cancellationToken) ); return document.WithSyntaxRoot(newRoot); } catch (Exception ex) { @@ -89,8 +87,9 @@ private static async Task ExpandAsync(Document document, ISyntaxExpand } } - private static SyntaxNode TryExpandNode(ISyntaxExpander expander, SyntaxNode node, SyntaxNode root, SemanticModel semanticModel, Workspace workspace) + private static SyntaxNode TryExpandNode(ISyntaxExpander expander, SyntaxNode node, SyntaxNode root, SemanticModel semanticModel, Workspace workspace, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); try { return expander.ExpandNode(node, root, semanticModel, workspace); } catch (Exception ex) { @@ -99,14 +98,14 @@ private static SyntaxNode TryExpandNode(ISyntaxExpander expander, SyntaxNode nod } } - private static async Task WithReducedRootAsync(this Document doc, SyntaxNode syntaxRoot = null) + private static async Task WithReducedRootAsync(this Document doc, SyntaxNode syntaxRoot, CancellationToken cancellationToken) { - var root = syntaxRoot ?? await doc.GetSyntaxRootAsync(); + var root = syntaxRoot ?? await doc.GetSyntaxRootAsync(cancellationToken); var withSyntaxRoot = doc.WithSyntaxRoot(root); try { - var options = await doc.GetOptionsAsync(); + var options = await doc.GetOptionsAsync(cancellationToken); var newOptions = doc.Project.Language == LanguageNames.VisualBasic ? GetVBOptions(options) : GetCSOptions(options); - return await Simplifier.ReduceAsync(withSyntaxRoot, newOptions); + return await Simplifier.ReduceAsync(withSyntaxRoot, newOptions, cancellationToken: cancellationToken); } catch (Exception ex) { var warningText = "Conversion warning: Qualified name reduction failed for this file. " + ex; return doc.WithSyntaxRoot(WithWarningAnnotation(root, warningText)); diff --git a/ICSharpCode.CodeConverter/Shared/Env.cs b/CodeConverter/Shared/Env.cs similarity index 100% rename from ICSharpCode.CodeConverter/Shared/Env.cs rename to CodeConverter/Shared/Env.cs diff --git a/ICSharpCode.CodeConverter/Shared/IProjectContentsConverter.cs b/CodeConverter/Shared/IProjectContentsConverter.cs similarity index 67% rename from ICSharpCode.CodeConverter/Shared/IProjectContentsConverter.cs rename to CodeConverter/Shared/IProjectContentsConverter.cs index 3fc0889ee..2ce58643d 100644 --- a/ICSharpCode.CodeConverter/Shared/IProjectContentsConverter.cs +++ b/CodeConverter/Shared/IProjectContentsConverter.cs @@ -12,7 +12,7 @@ public interface IProjectContentsConverter Project Project { get; } Task SingleFirstPass(Document document); - Task<(Project project, List<(string Path, DocumentId DocId, string[] Errors)> firstPassDocIds)> - GetConvertedProject((string Path, SyntaxNode Node, string[] Errors)[] firstPassResults); + Task<(Project project, List> firstPassDocIds)> + GetConvertedProject(WipFileConversion[] firstPassResults); } } \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/Shared/ISyntaxExpander.cs b/CodeConverter/Shared/ISyntaxExpander.cs similarity index 100% rename from ICSharpCode.CodeConverter/Shared/ISyntaxExpander.cs rename to CodeConverter/Shared/ISyntaxExpander.cs diff --git a/ICSharpCode.CodeConverter/Shared/LanguageConversionExtensions.cs b/CodeConverter/Shared/LanguageConversionExtensions.cs similarity index 97% rename from ICSharpCode.CodeConverter/Shared/LanguageConversionExtensions.cs rename to CodeConverter/Shared/LanguageConversionExtensions.cs index 98e0df825..335f77cf4 100644 --- a/ICSharpCode.CodeConverter/Shared/LanguageConversionExtensions.cs +++ b/CodeConverter/Shared/LanguageConversionExtensions.cs @@ -1,5 +1,4 @@ using System.Linq; -using ICSharpCode.CodeConverter.CSharp; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; diff --git a/ICSharpCode.CodeConverter/Shared/LineTriviaMapper.cs b/CodeConverter/Shared/LineTriviaMapper.cs similarity index 99% rename from ICSharpCode.CodeConverter/Shared/LineTriviaMapper.cs rename to CodeConverter/Shared/LineTriviaMapper.cs index 333055d4e..1999a7e3c 100644 --- a/ICSharpCode.CodeConverter/Shared/LineTriviaMapper.cs +++ b/CodeConverter/Shared/LineTriviaMapper.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; diff --git a/ICSharpCode.CodeConverter/Shared/PathConverter.cs b/CodeConverter/Shared/PathConverter.cs similarity index 100% rename from ICSharpCode.CodeConverter/Shared/PathConverter.cs rename to CodeConverter/Shared/PathConverter.cs diff --git a/CodeConverter/Shared/ProjectConversion.cs b/CodeConverter/Shared/ProjectConversion.cs new file mode 100644 index 000000000..9a6a0f9ff --- /dev/null +++ b/CodeConverter/Shared/ProjectConversion.cs @@ -0,0 +1,315 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using ICSharpCode.CodeConverter.CSharp; +using ICSharpCode.CodeConverter.Util; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Text; + +namespace ICSharpCode.CodeConverter.Shared +{ + public class ProjectConversion + { + private readonly IReadOnlyCollection _documentsToConvert; + private readonly ILanguageConversion _languageConversion; + private readonly bool _showCompilationErrors = +#if DEBUG && ShowCompilationErrors + true; +#else + false; +#endif + private readonly bool _returnSelectedNode; + private static readonly string[] BannedPaths = new[] { ".AssemblyAttributes.", "\\bin\\", "\\obj\\" }; + private readonly IProjectContentsConverter _projectContentsConverter; + private readonly CancellationToken _cancellationToken; + + private ProjectConversion(IProjectContentsConverter projectContentsConverter, IEnumerable documentsToConvert, + ILanguageConversion languageConversion, CancellationToken cancellationToken, bool returnSelectedNode = false) + { + _projectContentsConverter = projectContentsConverter; + _languageConversion = languageConversion; + _documentsToConvert = documentsToConvert.ToList(); + _returnSelectedNode = returnSelectedNode; + _cancellationToken = cancellationToken; + } + + public static async Task ConvertText(string text, TextConversionOptions conversionOptions, IProgress progress = null, CancellationToken cancellationToken = default) where TLanguageConversion : ILanguageConversion, new() + { + progress = progress ?? new Progress(); + using var roslynEntryPoint = await RoslynEntryPoint(progress); + + var languageConversion = new TLanguageConversion { ConversionOptions = conversionOptions }; + var syntaxTree = languageConversion.MakeFullCompilationUnit(text, out var textSpan); + if (textSpan.HasValue) conversionOptions.SelectedTextSpan = textSpan.Value; + using var workspace = new AdhocWorkspace(); + var document = languageConversion.CreateProjectDocumentFromTree(workspace, syntaxTree, conversionOptions.References); + return await ConvertSingle(document, conversionOptions, progress, cancellationToken); + } + + public static async Task ConvertSingle(Document document, SingleConversionOptions conversionOptions, IProgress progress = null, CancellationToken cancellationToken = default) where TLanguageConversion : ILanguageConversion, new() + { + progress = progress ?? new Progress(); + using var roslynEntryPoint = await RoslynEntryPoint(progress); + + var languageConversion = new TLanguageConversion { ConversionOptions = conversionOptions }; + + bool returnSelectedNode = conversionOptions.SelectedTextSpan.Length > 0; + if (returnSelectedNode) { + document = await WithAnnotatedSelection(document, conversionOptions.SelectedTextSpan); + } + + var projectContentsConverter = await languageConversion.CreateProjectContentsConverter(document.Project, progress, cancellationToken); + + document = projectContentsConverter.Project.GetDocument(document.Id); + + var conversion = new ProjectConversion(projectContentsConverter, new[] { document }, languageConversion, cancellationToken, returnSelectedNode); + var conversionResults = await conversion.Convert(progress).ToArrayAsync(); + var codeResult = conversionResults.SingleOrDefault(x => !string.IsNullOrWhiteSpace(x.ConvertedCode)) + ?? conversionResults.First(); + codeResult.Exceptions = conversionResults.SelectMany(x => x.Exceptions).ToArray(); + return codeResult; + } + + public static async IAsyncEnumerable ConvertProject(Project project, + ILanguageConversion languageConversion, IProgress progress, [EnumeratorCancellation] CancellationToken cancellationToken, + params (string Find, string Replace, bool FirstOnly)[] replacements) + { + progress = progress ?? new Progress(); + using var roslynEntryPoint = await RoslynEntryPoint(progress); + + var sourceFilePathsWithoutExtension = project.Documents.Select(f => f.FilePath).ToImmutableHashSet(); + var projectContentsConverter = await languageConversion.CreateProjectContentsConverter(project, progress, cancellationToken); + project = projectContentsConverter.Project; + var convertProjectContents = ConvertProjectContents(projectContentsConverter, languageConversion, progress, cancellationToken); + var results = WithProjectFile(projectContentsConverter, languageConversion, sourceFilePathsWithoutExtension, convertProjectContents, replacements); + await foreach (var result in results) yield return result; + } + + /// Perf: Keep lazy so that we don't keep all files in memory at once + private static async IAsyncEnumerable WithProjectFile(IProjectContentsConverter projectContentsConverter, ILanguageConversion languageConversion, ImmutableHashSet originalSourcePaths, IAsyncEnumerable convertProjectContents, (string Find, string Replace, bool FirstOnly)[] replacements) + { + var project = projectContentsConverter.Project; + var projectDir = project.GetDirectoryPath(); + var addedTargetFiles = new List(); + + await foreach (var conversionResult in convertProjectContents) { + yield return conversionResult; + if (!originalSourcePaths.Contains(conversionResult.SourcePathOrNull)) { + var relativePath = Path.GetFullPath(conversionResult.TargetPathOrNull).Replace(projectDir + Path.DirectorySeparatorChar, ""); + addedTargetFiles.Add(relativePath); + } + } + + var replacementSpecs = replacements.Concat(new[] { + AddCompiledItemsRegexFromRelativePaths(addedTargetFiles), + ChangeRootNamespaceRegex(projectContentsConverter.RootNamespace), + ChangeLanguageVersionRegex(projectContentsConverter.LanguageVersion) + }).ToArray(); + + yield return ConvertProjectFile(project, languageConversion, replacementSpecs); + } + + public static ConversionResult ConvertProjectFile(Project project, + ILanguageConversion languageConversion, + params (string Find, string Replace, bool FirstOnly)[] textReplacements) + { + return new FileInfo(project.FilePath).ConversionResultFromReplacements(textReplacements, + languageConversion.PostTransformProjectFile); + } + + private static (string Find, string Replace, bool FirstOnly) ChangeLanguageVersionRegex(string languageVersion) { + return (Find: new Regex(@"<\s*LangVersion>(\d|\D)*").ToString(), Replace: $"{languageVersion}", FirstOnly: true); + } + + private static (string Find, string Replace, bool FirstOnly) ChangeRootNamespaceRegex(string rootNamespace) { + return (Find: new Regex(@"<\s*RootNamespace>(\d|\D)*").ToString(), Replace: $"{rootNamespace}", FirstOnly: true); + } + + private static (string Find, string Replace, bool FirstOnly) AddCompiledItemsRegexFromRelativePaths( + IEnumerable relativeFilePathsToAdd) + { + var addFilesRegex = new Regex(@"(\s*<\s*Compile\s*Include\s*=\s*"".*\.(vb|cs)"")"); + var addedFiles = string.Join("", + relativeFilePathsToAdd.OrderBy(x => x).Select(f => $@"{Environment.NewLine} ")); + var addFilesRegexSpec = (Find: addFilesRegex.ToString(), Replace: addedFiles + @"$1", FirstOnly: true); + return addFilesRegexSpec; + } + + + private static async IAsyncEnumerable ConvertProjectContents( + IProjectContentsConverter projectContentsConverter, ILanguageConversion languageConversion, + IProgress progress, [EnumeratorCancellation] CancellationToken cancellationToken) + { + var documentsWithLengths = await projectContentsConverter.Project.Documents + .Where(d => !BannedPaths.Any(d.FilePath.Contains)) + .SelectAsync(async d => (Doc: d, Length: (await d.GetTextAsync()).Length)); + + //Perf heuristic: Decrease memory pressure on the simplification phase by converting large files first https://github.com/icsharpcode/CodeConverter/issues/524#issuecomment-590301594 + var documentsToConvert = documentsWithLengths.OrderByDescending(d => d.Length).Select(d => d.Doc); + + var projectConversion = new ProjectConversion(projectContentsConverter, documentsToConvert, languageConversion, cancellationToken); + + var results = projectConversion.Convert(progress); + await foreach (var result in results) yield return result; + } + + + private async IAsyncEnumerable Convert(IProgress progress) + { + var phaseProgress = StartPhase(progress, "Phase 1 of 2:"); + var firstPassResults = _documentsToConvert.ParallelSelectAwait(d => FirstPass(d, phaseProgress), Env.MaxDop, _cancellationToken); + var (proj1, docs1) = await _projectContentsConverter.GetConvertedProject(await firstPassResults.ToArrayAsync()); + + var warnings = await GetProjectWarnings(_projectContentsConverter.Project, proj1); + if (warnings != null) { + var warningPath = Path.Combine(proj1.GetDirectoryPath(), "ConversionWarnings.txt"); + yield return new ConversionResult() { SourcePathOrNull = warningPath, Exceptions = new[] { warnings } }; + } + + phaseProgress = StartPhase(progress, "Phase 2 of 2:"); + var secondPassResults = proj1.GetDocuments(docs1).ParallelSelectAwait(d => SecondPass(d, phaseProgress), Env.MaxDop, _cancellationToken); + await foreach (var result in secondPassResults) { + yield return new ConversionResult(result.Wip?.ToFullString()) { SourcePathOrNull = result.Path, Exceptions = result.Errors.ToList() }; + }; + } + + private static Progress StartPhase(IProgress progress, string phaseTitle) + { + progress.Report(new ConversionProgress(phaseTitle)); + var strProgress = new Progress(m => progress.Report(new ConversionProgress(m, 1))); + return strProgress; + } + + private async Task> SecondPass(WipFileConversion firstPassResult, IProgress progress) + { + if (firstPassResult.Wip != null) { + LogProgress(firstPassResult, "Simplifying", progress); + var (convertedNode, errors) = await SingleSecondPassHandled(firstPassResult.Wip); + return (firstPassResult.Path, convertedNode, firstPassResult.Errors.Concat(errors).Union(GetErrorsFromAnnotations(convertedNode)).ToArray()); + } + + return (firstPassResult.Path, null, firstPassResult.Errors); + } + + private async Task<(SyntaxNode convertedDoc, string[] errors)> SingleSecondPassHandled(Document convertedDocument) + { + SyntaxNode selectedNode = null; + string[] errors = Array.Empty(); + try { + Document document = await _languageConversion.SingleSecondPass(convertedDocument); + if (_returnSelectedNode) { + selectedNode = await GetSelectedNode(document); + var extraLeadingTrivia = selectedNode.GetFirstToken().GetPreviousToken().TrailingTrivia; + var extraTrailingTrivia = selectedNode.GetLastToken().GetNextToken().LeadingTrivia; + selectedNode = Formatter.Format(selectedNode, document.Project.Solution.Workspace); + if (extraLeadingTrivia.Any(t => !t.IsWhitespaceOrEndOfLine())) selectedNode = selectedNode.WithPrependedLeadingTrivia(extraLeadingTrivia); + if (extraTrailingTrivia.Any(t => !t.IsWhitespaceOrEndOfLine())) selectedNode = selectedNode.WithAppendedTrailingTrivia(extraTrailingTrivia); + } else { + selectedNode = await document.GetSyntaxRootAsync(); + selectedNode = Formatter.Format(selectedNode, document.Project.Solution.Workspace); + var convertedDoc = document.WithSyntaxRoot(selectedNode); + selectedNode = await convertedDoc.GetSyntaxRootAsync(); + } + } catch (Exception e) { + errors = new[] { e.ToString() }; + } + + var convertedNode = selectedNode ?? await convertedDocument.GetSyntaxRootAsync(); + return (convertedNode, errors); + } + + private async Task GetProjectWarnings(Project source, Project converted) + { + if (!_showCompilationErrors) return null; + + var sourceCompilation = await source.GetCompilationAsync(); + var convertedCompilation = await converted.GetCompilationAsync(); + return CompilationWarnings.WarningsForCompilation(sourceCompilation, "source") + CompilationWarnings.WarningsForCompilation(convertedCompilation, "target"); + } + + private async Task> FirstPass(Document document, IProgress progress) + { + var treeFilePath = document.FilePath ?? ""; + progress.Report(treeFilePath); + try { + var convertedNode = await _projectContentsConverter.SingleFirstPass(document); + string[] errors = GetErrorsFromAnnotations(convertedNode); + + return (treeFilePath, convertedNode, errors); + } catch (Exception e) { + return (treeFilePath, null, new[] { e.ToString() }); + } + } + + private static string[] GetErrorsFromAnnotations(SyntaxNode convertedNode) + { + var errorAnnotations = convertedNode.GetAnnotations(AnnotationConstants.ConversionErrorAnnotationKind).ToList(); + string[] errors = errorAnnotations.Select(a => a.Data).ToArray(); + return errors; + } + + private static async Task WithAnnotatedSelection(Document document, TextSpan selected) + { + var root = await document.GetSyntaxRootAsync(); + var selectedNode = root.FindNode(selected); + var withAnnotatedSelection = await root.WithAnnotatedNode(selectedNode, AnnotationConstants.SelectedNodeAnnotationKind).GetRootAsync(); + return document.WithSyntaxRoot(withAnnotatedSelection); + } + + private async Task GetSelectedNode(Document document) + { + var resultNode = await document.GetSyntaxRootAsync(); + var selectedNode = resultNode.GetAnnotatedNodes(AnnotationConstants.SelectedNodeAnnotationKind) + .FirstOrDefault(); + if (selectedNode != null) { + var children = _languageConversion.FindSingleImportantChild(selectedNode); + if (selectedNode.GetAnnotations(AnnotationConstants.SelectedNodeAnnotationKind) + .Any(n => n.Data == AnnotationConstants.AnnotatedNodeIsParentData) + && children.Count == 1) { + selectedNode = children.Single(); + } + } + + return selectedNode ?? resultNode; + } + + private void LogProgress(WipFileConversion convertedFile, string action, IProgress progress) + { + var indentedException = string.Join(Environment.NewLine, convertedFile.Errors) + .Replace(Environment.NewLine, Environment.NewLine + " ").TrimEnd(); + var relativePath = PathRelativeToSolutionDir(convertedFile.Path ?? "unknown"); + + var containsErrors = !string.IsNullOrWhiteSpace(indentedException); + string output; + if (convertedFile.Wip == null) { + output = $"Failed {action.ToLower()} {relativePath}:{Environment.NewLine} {indentedException}"; + } else if (containsErrors) { + output = $"Error {action.ToLower()} {relativePath}:{Environment.NewLine} {indentedException}"; + } else { + output = $"{action} {relativePath}"; + } + + progress.Report(output); + } + + private string PathRelativeToSolutionDir(string path) + { + return path.Replace(this._projectContentsConverter.Project.Solution.GetDirectoryPath() + Path.DirectorySeparatorChar, ""); + } + + private static async Task RoslynEntryPoint(IProgress progress) + { + await new SynchronizationContextRemover(); + return RoslynCrashPreventer.Create(LogError); + + void LogError(object e) => progress.Report(new ConversionProgress($"https://github.com/dotnet/roslyn threw an exception: {e}")); + } + } +} \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/Shared/ProjectTypeGuids.cs b/CodeConverter/Shared/ProjectTypeGuids.cs similarity index 100% rename from ICSharpCode.CodeConverter/Shared/ProjectTypeGuids.cs rename to CodeConverter/Shared/ProjectTypeGuids.cs diff --git a/CodeConverter/Shared/RoslynCrashPreventer.cs b/CodeConverter/Shared/RoslynCrashPreventer.cs new file mode 100644 index 000000000..cb29edc25 --- /dev/null +++ b/CodeConverter/Shared/RoslynCrashPreventer.cs @@ -0,0 +1,53 @@ +using System; +using System.Reflection; +using Microsoft.CodeAnalysis; + +namespace ICSharpCode.CodeConverter.Shared +{ + internal static class RoslynCrashPreventer + { + private static object _exchangeLock = new object(); + + /// + /// Use this to stop the library exiting the process without telling us. + /// https://github.com/dotnet/roslyn/issues/41724 + /// + /// + /// The simplification code in particular is quite buggy, scattered with "throw ExceptionUtilities.Unreachable" with no particular reasoning for why the code wouldn't be reachable. + /// It then uses FatalError.ReportUnlessCanceled rather than FatalError.ReportWithoutCrashUnlessCanceled causing fatal crashes with Environment.FailFast + /// While this presumably allows them to get good low-level debugging info from the windows error reports caused, it just means that people come to this project complaining about VS crashes. + /// See https://github.com/icsharpcode/CodeConverter/issues/521 and https://github.com/icsharpcode/CodeConverter/issues/484 + /// There are other ways to find these bugs - just run the expander/reducer on a couple of whole open source projects and the bugs will pile up. + /// + public static IDisposable Create(Action logError) + { + var FirstHandlerContainingType = (typeof(Compilation).Assembly, "Microsoft.CodeAnalysis.FatalError"); + var SecondHandlerContainingType = (typeof(WorkspaceDiagnostic).Assembly, "Microsoft.CodeAnalysis.ErrorReporting.FatalError"); + + var codeAnalysisErrorHandler = ExchangeFatalErrorHandler(logError, FirstHandlerContainingType); + var codeAnalysisErrorReportingErrorHandler = ExchangeFatalErrorHandler(logError, SecondHandlerContainingType); + return new ActionDisposable(() => { + ExchangeFatalErrorHandler(codeAnalysisErrorHandler, FirstHandlerContainingType); + ExchangeFatalErrorHandler(codeAnalysisErrorReportingErrorHandler, SecondHandlerContainingType); + }); + } + + private static Action ExchangeFatalErrorHandler(Action errorHandler, (Assembly assembly, string containingType) container, Action errorHanderToReplace = null) + { + if (errorHandler == null) return null; + try { + var fataErrorType = container.assembly.GetType(container.containingType); + var fatalHandlerField = fataErrorType.GetField("s_fatalHandler", BindingFlags.NonPublic | BindingFlags.Static); + lock (_exchangeLock) { + var originalHandler = (Action)fatalHandlerField.GetValue(null); + if (originalHandler != null && errorHanderToReplace == null || originalHandler == errorHanderToReplace) { + fatalHandlerField.SetValue(null, errorHandler); + } + return originalHandler; + } + } catch (Exception) { + return null; + } + } + } +} \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/Shared/SharedSemanticModelExtensions.cs b/CodeConverter/Shared/SharedSemanticModelExtensions.cs similarity index 93% rename from ICSharpCode.CodeConverter/Shared/SharedSemanticModelExtensions.cs rename to CodeConverter/Shared/SharedSemanticModelExtensions.cs index 23e154b8f..440ae8df4 100644 --- a/ICSharpCode.CodeConverter/Shared/SharedSemanticModelExtensions.cs +++ b/CodeConverter/Shared/SharedSemanticModelExtensions.cs @@ -1,5 +1,4 @@ using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; namespace ICSharpCode.CodeConverter.Shared { diff --git a/ICSharpCode.CodeConverter/Shared/SingleConversionOptions.cs b/CodeConverter/Shared/SingleConversionOptions.cs similarity index 100% rename from ICSharpCode.CodeConverter/Shared/SingleConversionOptions.cs rename to CodeConverter/Shared/SingleConversionOptions.cs diff --git a/ICSharpCode.CodeConverter/Shared/SolutionConverter.cs b/CodeConverter/Shared/SolutionConverter.cs similarity index 84% rename from ICSharpCode.CodeConverter/Shared/SolutionConverter.cs rename to CodeConverter/Shared/SolutionConverter.cs index c3fa6faa7..6eef745c6 100644 --- a/ICSharpCode.CodeConverter/Shared/SolutionConverter.cs +++ b/CodeConverter/Shared/SolutionConverter.cs @@ -3,8 +3,8 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; -using System.Threading.Tasks; -using ICSharpCode.CodeConverter.CSharp; +using System.Threading; +using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; namespace ICSharpCode.CodeConverter.Shared @@ -17,22 +17,24 @@ public class SolutionConverter private readonly List<(string Find, string Replace, bool FirstOnly)> _projectReferenceReplacements; private readonly IProgress _progress; private readonly ILanguageConversion _languageConversion; + private readonly CancellationToken _cancellationToken; public static SolutionConverter CreateFor(IReadOnlyCollection projectsToConvert, ConversionOptions conversionOptions = default, - IProgress progress = null) where TLanguageConversion : ILanguageConversion, new() + IProgress progress = null, + CancellationToken cancellationToken = default) where TLanguageConversion : ILanguageConversion, new() { var conversion = new TLanguageConversion {ConversionOptions = conversionOptions ?? new ConversionOptions() }; var solutionFilePath = projectsToConvert.First().Solution.FilePath; var sourceSolutionContents = File.ReadAllText(solutionFilePath); var projectReferenceReplacements = GetProjectReferenceReplacements(projectsToConvert, sourceSolutionContents); - return new SolutionConverter(solutionFilePath, sourceSolutionContents, projectsToConvert, projectReferenceReplacements, progress ?? new Progress(), conversion); + return new SolutionConverter(solutionFilePath, sourceSolutionContents, projectsToConvert, projectReferenceReplacements, progress ?? new Progress(), cancellationToken, conversion); } private SolutionConverter(string solutionFilePath, string sourceSolutionContents, IReadOnlyCollection projectsToConvert, List<(string Find, string Replace, bool FirstOnly)> projectReferenceReplacements, IProgress showProgressMessage, - ILanguageConversion languageConversion) + CancellationToken cancellationToken, ILanguageConversion languageConversion) { _solutionFilePath = solutionFilePath; _sourceSolutionContents = sourceSolutionContents; @@ -40,29 +42,28 @@ private SolutionConverter(string solutionFilePath, _projectReferenceReplacements = projectReferenceReplacements; _progress = showProgressMessage; _languageConversion = languageConversion; + _cancellationToken = cancellationToken; } - public async Task> Convert() + public IAsyncEnumerable Convert() { var projectsToUpdateReferencesOnly = _projectsToConvert.First().Solution.Projects.Except(_projectsToConvert); - var projectContents = await ConvertProjects(); - return projectContents.SelectMany(x => x) - .Concat(UpdateProjectReferences(projectsToUpdateReferencesOnly)) - .Concat(new[] { ConvertSolutionFile() }); + return ConvertProjects() + .Concat(UpdateProjectReferences(projectsToUpdateReferencesOnly).Concat(ConvertSolutionFile().Yield()).ToAsyncEnumerable()); } - private Task[]> ConvertProjects() + private IAsyncEnumerable ConvertProjects() { var projectFileReplacementRegexes = _languageConversion.GetProjectFileReplacementRegexes().Concat(_languageConversion.GetProjectTypeGuidMappings()) .Select(m => (m.Item1, m.Item2, false)); - return _projectsToConvert.SelectAsync(project => ConvertProject(projectFileReplacementRegexes, project)); + return _projectsToConvert.ToAsyncEnumerable().SelectMany(project => ConvertProject(projectFileReplacementRegexes, project)); } - private async Task> ConvertProject(IEnumerable<(string Find, string Replace, bool FirstOnly)> projectFileReplacementRegexes, Project project) + private IAsyncEnumerable ConvertProject(IEnumerable<(string Find, string Replace, bool FirstOnly)> projectFileReplacementRegexes, Project project) { var replacements = _projectReferenceReplacements.Concat(projectFileReplacementRegexes).ToArray(); _progress.Report(new ConversionProgress($"Converting {project.Name}...")); - return await ProjectConversion.ConvertProject(project, _languageConversion, _progress, replacements); + return ProjectConversion.ConvertProject(project, _languageConversion, _progress, _cancellationToken, replacements); } private IEnumerable UpdateProjectReferences(IEnumerable projectsToUpdateReferencesOnly) diff --git a/ICSharpCode.CodeConverter/Shared/SynchronizationContextRemover.cs b/CodeConverter/Shared/SynchronizationContextRemover.cs similarity index 100% rename from ICSharpCode.CodeConverter/Shared/SynchronizationContextRemover.cs rename to CodeConverter/Shared/SynchronizationContextRemover.cs diff --git a/ICSharpCode.CodeConverter/Shared/TextConversionOptions.cs b/CodeConverter/Shared/TextConversionOptions.cs similarity index 100% rename from ICSharpCode.CodeConverter/Shared/TextConversionOptions.cs rename to CodeConverter/Shared/TextConversionOptions.cs diff --git a/ICSharpCode.CodeConverter/Shared/TextLineExtensions.cs b/CodeConverter/Shared/TextLineExtensions.cs similarity index 96% rename from ICSharpCode.CodeConverter/Shared/TextLineExtensions.cs rename to CodeConverter/Shared/TextLineExtensions.cs index 98691a240..397e8baa3 100644 --- a/ICSharpCode.CodeConverter/Shared/TextLineExtensions.cs +++ b/CodeConverter/Shared/TextLineExtensions.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Linq; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; diff --git a/ICSharpCode.CodeConverter/Shared/TextReplacementConverter.cs b/CodeConverter/Shared/TextReplacementConverter.cs similarity index 100% rename from ICSharpCode.CodeConverter/Shared/TextReplacementConverter.cs rename to CodeConverter/Shared/TextReplacementConverter.cs diff --git a/ICSharpCode.CodeConverter/Shared/TokenContext.cs b/CodeConverter/Shared/TokenContext.cs similarity index 100% rename from ICSharpCode.CodeConverter/Shared/TokenContext.cs rename to CodeConverter/Shared/TokenContext.cs diff --git a/ICSharpCode.CodeConverter/Shared/TriviaMapping.cs b/CodeConverter/Shared/TriviaMapping.cs similarity index 93% rename from ICSharpCode.CodeConverter/Shared/TriviaMapping.cs rename to CodeConverter/Shared/TriviaMapping.cs index 8d0901e55..06eb21dc9 100644 --- a/ICSharpCode.CodeConverter/Shared/TriviaMapping.cs +++ b/CodeConverter/Shared/TriviaMapping.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using ICSharpCode.CodeConverter.Util; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis; namespace ICSharpCode.CodeConverter.Shared { diff --git a/CodeConverter/Shared/WipConversion.cs b/CodeConverter/Shared/WipConversion.cs new file mode 100644 index 000000000..cce57fb9c --- /dev/null +++ b/CodeConverter/Shared/WipConversion.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; + +namespace ICSharpCode.CodeConverter.Shared +{ + internal struct WipFileConversion + { + public static WipFileConversion Create(string path, TWip wip, string[] errors) + { + return new WipFileConversion(path, wip, errors); + } + } + + public struct WipFileConversion + { + public string Path; + public TWip Wip; + public string[] Errors; + + internal WipFileConversion(string path, TWip wip, string[] errors) + { + Path = path; + Wip = wip; + Errors = errors; + } + + public override bool Equals(object obj) + { + return obj is WipFileConversion other && + Path == other.Path && + EqualityComparer.Default.Equals(Wip, other.Wip) && + EqualityComparer.Default.Equals(Errors, other.Errors); + } + + public override int GetHashCode() + { + return Path.GetHashCode(); + } + + public void Deconstruct(out string path, out TWip node, out string[] errors) + { + path = Path; + node = Wip; + errors = Errors; + } + + public static implicit operator (string Path, TWip Node, string[] Errors)(WipFileConversion value) + { + return (value.Path, value.Wip, value.Errors); + } + + public static implicit operator WipFileConversion((string Path, TWip Wip, string[] Errors) value) + { + return new WipFileConversion(value.Path, value.Wip, value.Errors); + } + } +} \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/Util/CSharpCompiler.cs b/CodeConverter/Util/CSharpCompiler.cs similarity index 97% rename from ICSharpCode.CodeConverter/Util/CSharpCompiler.cs rename to CodeConverter/Util/CSharpCompiler.cs index df2875744..c1c39caa1 100644 --- a/ICSharpCode.CodeConverter/Util/CSharpCompiler.cs +++ b/CodeConverter/Util/CSharpCompiler.cs @@ -4,7 +4,6 @@ using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Text; namespace ICSharpCode.CodeConverter.Util { diff --git a/ICSharpCode.CodeConverter/Util/CSharpUtil.cs b/CodeConverter/Util/CSharpUtil.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/CSharpUtil.cs rename to CodeConverter/Util/CSharpUtil.cs diff --git a/ICSharpCode.CodeConverter/Util/CompilationWarnings.cs b/CodeConverter/Util/CompilationWarnings.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/CompilationWarnings.cs rename to CodeConverter/Util/CompilationWarnings.cs diff --git a/CodeConverter/Util/EnumerableExtensions.cs b/CodeConverter/Util/EnumerableExtensions.cs new file mode 100644 index 000000000..52a0ec43a --- /dev/null +++ b/CodeConverter/Util/EnumerableExtensions.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace ICSharpCode.CodeConverter.Util +{ +#if NR6 + public +#endif + internal static partial class EnumerableExtensions + { + public static IEnumerable Do(this IEnumerable source, Action action) + { + if (source == null) { + throw new ArgumentNullException(nameof(source)); + } + + if (action == null) { + throw new ArgumentNullException(nameof(action)); + } + + // perf optimization. try to not use enumerator if possible + var list = source as IList; + if (list != null) { + for (int i = 0, count = list.Count; i < count; i++) { + action(list[i]); + } + } else { + foreach (var value in source) { + action(value); + } + } + + return source; + } + + public static IReadOnlyCollection ToReadOnlyCollection(this IEnumerable source) + { + if (source == null) { + throw new ArgumentNullException(nameof(source)); + } + + return new ReadOnlyCollection(source.ToList()); + } + + public static IEnumerable Concat(this IEnumerable source, T value) + { + if (source == null) { + throw new ArgumentNullException(nameof(source)); + } + + return source.ConcatWorker(value); + } + + private static IEnumerable ConcatWorker(this IEnumerable source, T value) + { + foreach (var v in source) { + yield return v; + } + + yield return value; + } + + public static bool IsEmpty(this IEnumerable source) + { + var readOnlyCollection = source as IReadOnlyCollection; + if (readOnlyCollection != null) { + return readOnlyCollection.Count == 0; + } + + var genericCollection = source as ICollection; + if (genericCollection != null) { + return genericCollection.Count == 0; + } + + var collection = source as ICollection; + if (collection != null) { + return collection.Count == 0; + } + + var str = source as string; + if (str != null) { + return str.Length == 0; + } + + foreach (var t in source) { + return false; + } + + return true; + } + + public static bool IsEmpty(this IReadOnlyCollection source) + { + return source.Count == 0; + } + + private static readonly Func s_notNullTest = x => x != null; + + public static IEnumerable WhereNotNull(this IEnumerable source) + where T : class + { + if (source == null) { + return SpecializedCollections.EmptyEnumerable(); + } + + return source.Where((Func)s_notNullTest); + } + + public static IEnumerable Yield(this T singleElement) + { + yield return singleElement; + } + + public static bool Contains(this IEnumerable sequence, Func predicate) + { + return sequence.Any(predicate); + } + } +} diff --git a/ICSharpCode.CodeConverter/Util/ExceptionExtensions.cs b/CodeConverter/Util/ExceptionExtensions.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/ExceptionExtensions.cs rename to CodeConverter/Util/ExceptionExtensions.cs diff --git a/ICSharpCode.CodeConverter/Util/ExceptionWithNodeInformation.cs b/CodeConverter/Util/ExceptionWithNodeInformation.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/ExceptionWithNodeInformation.cs rename to CodeConverter/Util/ExceptionWithNodeInformation.cs diff --git a/ICSharpCode.CodeConverter/Util/ExpressionSyntaxExtensions.cs b/CodeConverter/Util/ExpressionSyntaxExtensions.cs similarity index 98% rename from ICSharpCode.CodeConverter/Util/ExpressionSyntaxExtensions.cs rename to CodeConverter/Util/ExpressionSyntaxExtensions.cs index 9df116723..1e5f3975d 100644 --- a/ICSharpCode.CodeConverter/Util/ExpressionSyntaxExtensions.cs +++ b/CodeConverter/Util/ExpressionSyntaxExtensions.cs @@ -1,12 +1,8 @@ using System.Collections.Generic; using System.Linq; -using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Simplification; -using Microsoft.CodeAnalysis.Text; using SpecialType = Microsoft.CodeAnalysis.SpecialType; using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; using VBasic = Microsoft.CodeAnalysis.VisualBasic; diff --git a/ICSharpCode.CodeConverter/Util/FindTokenHelper.cs b/CodeConverter/Util/FindTokenHelper.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/FindTokenHelper.cs rename to CodeConverter/Util/FindTokenHelper.cs diff --git a/ICSharpCode.CodeConverter/Util/FromRoslyn/CompilationExtensions.cs b/CodeConverter/Util/FromRoslyn/CompilationExtensions.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/FromRoslyn/CompilationExtensions.cs rename to CodeConverter/Util/FromRoslyn/CompilationExtensions.cs diff --git a/ICSharpCode.CodeConverter/Util/FromRoslyn/ExceptionUtilities.cs b/CodeConverter/Util/FromRoslyn/ExceptionUtilities.cs similarity index 94% rename from ICSharpCode.CodeConverter/Util/FromRoslyn/ExceptionUtilities.cs rename to CodeConverter/Util/FromRoslyn/ExceptionUtilities.cs index afedacabb..5ac718ff4 100644 --- a/ICSharpCode.CodeConverter/Util/FromRoslyn/ExceptionUtilities.cs +++ b/CodeConverter/Util/FromRoslyn/ExceptionUtilities.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using Microsoft.CodeAnalysis; namespace ICSharpCode.CodeConverter.Util.FromRoslyn { diff --git a/ICSharpCode.CodeConverter/Util/FromRoslyn/NamedTypeSymbolExtensions.cs b/CodeConverter/Util/FromRoslyn/NamedTypeSymbolExtensions.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/FromRoslyn/NamedTypeSymbolExtensions.cs rename to CodeConverter/Util/FromRoslyn/NamedTypeSymbolExtensions.cs diff --git a/ICSharpCode.CodeConverter/Util/FromRoslyn/RoslynISymbolExtensions.cs b/CodeConverter/Util/FromRoslyn/RoslynISymbolExtensions.cs similarity index 99% rename from ICSharpCode.CodeConverter/Util/FromRoslyn/RoslynISymbolExtensions.cs rename to CodeConverter/Util/FromRoslyn/RoslynISymbolExtensions.cs index c6fd3319e..329fe9b08 100644 --- a/ICSharpCode.CodeConverter/Util/FromRoslyn/RoslynISymbolExtensions.cs +++ b/CodeConverter/Util/FromRoslyn/RoslynISymbolExtensions.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Microsoft.CodeAnalysis; namespace ICSharpCode.CodeConverter.Util.FromRoslyn diff --git a/ICSharpCode.CodeConverter/Util/FromRoslyn/RoslynITypeSymbolExtensions.cs b/CodeConverter/Util/FromRoslyn/RoslynITypeSymbolExtensions.cs similarity index 95% rename from ICSharpCode.CodeConverter/Util/FromRoslyn/RoslynITypeSymbolExtensions.cs rename to CodeConverter/Util/FromRoslyn/RoslynITypeSymbolExtensions.cs index d19640a90..f6dc7be2b 100644 --- a/ICSharpCode.CodeConverter/Util/FromRoslyn/RoslynITypeSymbolExtensions.cs +++ b/CodeConverter/Util/FromRoslyn/RoslynITypeSymbolExtensions.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using Microsoft.CodeAnalysis; namespace ICSharpCode.CodeConverter.Util.FromRoslyn diff --git a/CodeConverter/Util/GeneratedCodeAnalysisExtensions.cs b/CodeConverter/Util/GeneratedCodeAnalysisExtensions.cs new file mode 100644 index 000000000..2c2821738 --- /dev/null +++ b/CodeConverter/Util/GeneratedCodeAnalysisExtensions.cs @@ -0,0 +1,15 @@ +using System.Text.RegularExpressions; + +namespace ICSharpCode.CodeConverter.Util +{ + /// + /// Copied from https://github.com/code-cracker/code-cracker/blob/330f7ad217d6aae17a07a0675f52bcaa9a03d956/src/Common/CodeCracker.Common/Extensions/GeneratedCodeAnalysisExtensions.cs + /// Apache-2.0 license + /// + public static class GeneratedCodeAnalysisExtensions + { + public static bool IsGeneratedFile(this string filePath) => + Regex.IsMatch(filePath, @"(\\service|\\TemporaryGeneratedFile_.*|\\assemblyinfo|\\assemblyattributes|\.(g\.i|g|designer|generated|assemblyattributes))\.(cs|vb)$", + RegexOptions.IgnoreCase); + } +} \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/Util/IAssemblySymbolExtensions.cs b/CodeConverter/Util/IAssemblySymbolExtensions.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/IAssemblySymbolExtensions.cs rename to CodeConverter/Util/IAssemblySymbolExtensions.cs diff --git a/ICSharpCode.CodeConverter/Util/ICompiler.cs b/CodeConverter/Util/ICompiler.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/ICompiler.cs rename to CodeConverter/Util/ICompiler.cs diff --git a/ICSharpCode.CodeConverter/Util/IMethodSymbolExtensions.cs b/CodeConverter/Util/IMethodSymbolExtensions.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/IMethodSymbolExtensions.cs rename to CodeConverter/Util/IMethodSymbolExtensions.cs diff --git a/ICSharpCode.CodeConverter/Util/INamespaceOrTypeSymbolExtensions.cs b/CodeConverter/Util/INamespaceOrTypeSymbolExtensions.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/INamespaceOrTypeSymbolExtensions.cs rename to CodeConverter/Util/INamespaceOrTypeSymbolExtensions.cs diff --git a/ICSharpCode.CodeConverter/Util/ISymbolExtensions.cs b/CodeConverter/Util/ISymbolExtensions.cs similarity index 97% rename from ICSharpCode.CodeConverter/Util/ISymbolExtensions.cs rename to CodeConverter/Util/ISymbolExtensions.cs index 3dc72a681..87d8f0dcc 100644 --- a/ICSharpCode.CodeConverter/Util/ISymbolExtensions.cs +++ b/CodeConverter/Util/ISymbolExtensions.cs @@ -1,11 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; -using ICSharpCode.CodeConverter.CSharp; -using ICSharpCode.CodeConverter.Shared; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Editing; namespace ICSharpCode.CodeConverter.Util { diff --git a/ICSharpCode.CodeConverter/Util/ITypeParameterSymbolExtensions.cs b/CodeConverter/Util/ITypeParameterSymbolExtensions.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/ITypeParameterSymbolExtensions.cs rename to CodeConverter/Util/ITypeParameterSymbolExtensions.cs diff --git a/ICSharpCode.CodeConverter/Util/ITypeSymbolExtensions.cs b/CodeConverter/Util/ITypeSymbolExtensions.cs similarity index 99% rename from ICSharpCode.CodeConverter/Util/ITypeSymbolExtensions.cs rename to CodeConverter/Util/ITypeSymbolExtensions.cs index 22eecacbb..41436d0b2 100644 --- a/ICSharpCode.CodeConverter/Util/ITypeSymbolExtensions.cs +++ b/CodeConverter/Util/ITypeSymbolExtensions.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; namespace ICSharpCode.CodeConverter.Util { diff --git a/ICSharpCode.CodeConverter/Util/NameGenerator.cs b/CodeConverter/Util/NameGenerator.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/NameGenerator.cs rename to CodeConverter/Util/NameGenerator.cs diff --git a/ICSharpCode.CodeConverter/Util/ObjectExtensions.cs b/CodeConverter/Util/ObjectExtensions.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/ObjectExtensions.cs rename to CodeConverter/Util/ObjectExtensions.cs diff --git a/ICSharpCode.CodeConverter/Util/ProjectFileTextEditor.cs b/CodeConverter/Util/ProjectFileTextEditor.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/ProjectFileTextEditor.cs rename to CodeConverter/Util/ProjectFileTextEditor.cs diff --git a/ICSharpCode.CodeConverter/Util/SpecializedCollections.cs b/CodeConverter/Util/SpecializedCollections.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/SpecializedCollections.cs rename to CodeConverter/Util/SpecializedCollections.cs diff --git a/CodeConverter/Util/StringExtensions.cs b/CodeConverter/Util/StringExtensions.cs new file mode 100644 index 000000000..e8ad85e5d --- /dev/null +++ b/CodeConverter/Util/StringExtensions.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ICSharpCode.CodeConverter.Util +{ +#if NR6 + public +#endif + internal static class StringExtensions + { + public static string ConsistentNewlines(this string str) + { + return str.Replace("\r\n", "\n").Replace("\n", "\r\n"); + } + + public static bool LooksLikeInterfaceName(this string name) + { + return name.Length >= 3 && name[0] == 'I' && Char.IsUpper(name[1]) && Char.IsLower(name[2]); + } + + public static bool LooksLikeTypeParameterName(this string name) + { + return name.Length >= 3 && name[0] == 'T' && Char.IsUpper(name[1]) && Char.IsLower(name[2]); + } + + private static readonly Func s_toLower = Char.ToLower; + private static readonly Func s_toUpper = Char.ToUpper; + + public static string ToPascalCase( + this string shortName, + bool trimLeadingTypePrefix = true) + { + return ConvertCase(shortName, trimLeadingTypePrefix, s_toUpper); + } + + public static string ToCamelCase( + this string shortName, + bool trimLeadingTypePrefix = true) + { + return ConvertCase(shortName, trimLeadingTypePrefix, s_toLower); + } + + private static string ConvertCase( + this string shortName, + bool trimLeadingTypePrefix, + Func convert) + { + // Special case the common .net pattern of "IFoo" as a type name. In this case we + // want to generate "foo" as the parameter name. + if (!String.IsNullOrEmpty(shortName)) { + if (trimLeadingTypePrefix && (shortName.LooksLikeInterfaceName() || shortName.LooksLikeTypeParameterName())) { + return convert(shortName[1]) + shortName.Substring(2); + } + + if (convert(shortName[0]) != shortName[0]) { + return convert(shortName[0]) + shortName.Substring(1); + } + } + + return shortName; + } + + // String isn't IEnumerable in the current Portable profile. + internal static bool All(this string arg, Predicate predicate) + { + foreach (char c in arg) { + if (!predicate(c)) { + return false; + } + } + + return true; + } + + public static string ReplaceEnd(this string originalContainingReplacement, KeyValuePair replacement) + { + return originalContainingReplacement.Substring(0, originalContainingReplacement.Length - replacement.Key.Length) + replacement.Value; + } + + public static string WithHalfWidthLatinCharacters(this string str) + { + return str.Normalize(NormalizationForm.FormKD); + } + } +} diff --git a/ICSharpCode.CodeConverter/Util/SymbolExtensions.cs b/CodeConverter/Util/SymbolExtensions.cs similarity index 72% rename from ICSharpCode.CodeConverter/Util/SymbolExtensions.cs rename to CodeConverter/Util/SymbolExtensions.cs index 89922fb18..8ad41147a 100644 --- a/ICSharpCode.CodeConverter/Util/SymbolExtensions.cs +++ b/CodeConverter/Util/SymbolExtensions.cs @@ -11,44 +11,6 @@ namespace ICSharpCode.CodeConverter.Util #endif internal static class SymbolExtensions { - /// - /// Returns true if the symbol wasn't tagged with - /// [System.ComponentModel.BrowsableAttribute (false)] - /// - /// true if is designer browsable the specified symbol; otherwise, false. - /// Symbol. - public static bool IsDesignerBrowsable(this ISymbol symbol) - { - if (symbol == null) - throw new ArgumentNullException("symbol"); - var browsableState = symbol.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.Name == "BrowsableAttribute" && attr.AttributeClass.ContainingNamespace.MetadataName == "System.ComponentModel"); - if (browsableState != null && browsableState.ConstructorArguments.Length == 1) { - try { - return (bool)browsableState.ConstructorArguments[0].Value; - } catch { - } - } - return true; - } - - /// - /// Returns the component category. - /// [System.ComponentModel.CategoryAttribute (CATEGORY)] - /// - /// Symbol. - public static string GetComponentCategory(this ISymbol symbol) - { - if (symbol == null) - throw new ArgumentNullException("symbol"); - var browsableState = symbol.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.Name == "CategoryAttribute" && attr.AttributeClass.ContainingNamespace.MetadataName == "System.ComponentModel"); - if (browsableState != null && browsableState.ConstructorArguments.Length == 1) { - try { - return (string)browsableState.ConstructorArguments[0].Value; - } catch { - } - } - return null; - } public static ImmutableArray GetParameters(this ISymbol symbol) { @@ -66,27 +28,6 @@ public static ImmutableArray GetParameters(this ISymbol symbol return ImmutableArray.Empty; } - public static ImmutableArray GetTypeParameters(this ISymbol symbol) - { - if (symbol == null) - throw new ArgumentNullException("symbol"); - var type = symbol as INamedTypeSymbol; - if (type != null) - return type.TypeParameters; - var method = symbol as IMethodSymbol; - if (method != null) - return method.TypeParameters; - return ImmutableArray.Empty; - } - - public static bool IsAnyConstructor(this ISymbol symbol) - { - if (symbol == null) - throw new ArgumentNullException("symbol"); - var method = symbol as IMethodSymbol; - return method != null && (method.MethodKind == MethodKind.Constructor || method.MethodKind == MethodKind.StaticConstructor); - } - public static bool IsConstructor(this ISymbol symbol) { if (symbol == null) @@ -101,13 +42,6 @@ public static bool IsStaticConstructor(this ISymbol symbol) return symbol is IMethodSymbol && ((IMethodSymbol)symbol).MethodKind == MethodKind.StaticConstructor; } - public static bool IsDestructor(this ISymbol symbol) - { - if (symbol == null) - throw new ArgumentNullException("symbol"); - return symbol is IMethodSymbol && ((IMethodSymbol)symbol).MethodKind == MethodKind.Destructor; - } - public static bool IsDelegateType(this ISymbol symbol) { if (symbol == null) @@ -158,23 +92,12 @@ public static bool IsAccessorMethod(this ISymbol symbol) accessorSymbol.MethodKind == MethodKind.EventRemove || accessorSymbol.MethodKind == MethodKind.EventAdd); } - public static bool IsAccessorPropertySet(this ISymbol symbol) - { - var accessorSymbol = symbol as IMethodSymbol; - return accessorSymbol != null && accessorSymbol.MethodKind == MethodKind.PropertySet; - } - public static bool IsAccessorWithValueInCsharp(this ISymbol symbol) { return symbol is IMethodSymbol ms && new[] { MethodKind.PropertySet, MethodKind.EventAdd, MethodKind.EventRemove }.Contains(ms.MethodKind); } - public static bool IsPublic(this ISymbol symbol) - { - return symbol.DeclaredAccessibility == Accessibility.Public; - } - public static bool IsErrorType(this ISymbol symbol) { return @@ -182,16 +105,6 @@ symbol is ITypeSymbol && ((ITypeSymbol)symbol).TypeKind == TypeKind.Error; } - public static bool IsIndexer(this ISymbol symbol) - { - return (symbol as IPropertySymbol)?.IsIndexer == true; - } - - public static bool IsUserDefinedOperator(this ISymbol symbol) - { - return (symbol as IMethodSymbol)?.MethodKind == MethodKind.UserDefinedOperator; - } - public static SymbolVisibility GetResultantVisibility(this ISymbol symbol) { // Start by assuming it's visible. @@ -275,56 +188,6 @@ public static ImmutableArray ExplicitInterfaceImplementations(this ISym _ => ImmutableArray.Create()); } - public static bool IsOverridable(this ISymbol symbol) - { - return - symbol != null && - symbol.ContainingType != null && - symbol.ContainingType.TypeKind == TypeKind.Class && - (symbol.IsVirtual || symbol.IsAbstract || symbol.IsOverride) && - !symbol.IsSealed; - } - - public static bool IsImplementable(this ISymbol symbol) - { - if (symbol != null && - symbol.ContainingType != null && - symbol.ContainingType.TypeKind == TypeKind.Interface) { - if (symbol.Kind == SymbolKind.Event) { - return true; - } - - if (symbol.Kind == SymbolKind.Property) { - return true; - } - - if (symbol.Kind == SymbolKind.Method && ((IMethodSymbol)symbol).MethodKind == MethodKind.Ordinary) { - return true; - } - } - - return false; - } - - public static INamedTypeSymbol GetContainingTypeOrThis(this ISymbol symbol) - { - if (symbol is INamedTypeSymbol) { - return (INamedTypeSymbol)symbol; - } - - return symbol.ContainingType; - } - - public static bool IsPointerType(this ISymbol symbol) - { - return symbol is IPointerTypeSymbol; - } - - public static bool IsModuleType(this ISymbol symbol) - { - return (symbol as ITypeSymbol)?.IsModuleType() == true; - } - public static bool IsInterfaceType(this ISymbol symbol) { return (symbol as ITypeSymbol)?.IsInterfaceType() == true; @@ -452,39 +315,6 @@ public static int GetArity(this ISymbol symbol) } } - public static ISymbol GetOriginalUnreducedDefinition(this ISymbol symbol) - { - if (symbol.IsReducedExtension()) { - // note: ReducedFrom is only a method definition and includes no type arguments. - symbol = ((IMethodSymbol)symbol).GetConstructedReducedFrom(); - } - - if (symbol.IsFunctionValue()) { - var method = symbol.ContainingSymbol as IMethodSymbol; - if (method != null) { - symbol = method; - - if (method.AssociatedSymbol != null) { - symbol = method.AssociatedSymbol; - } - } - } - - if (symbol.IsNormalAnonymousType() || symbol.IsAnonymousTypeProperty()) { - return symbol; - } - - var parameter = symbol as IParameterSymbol; - if (parameter != null) { - var method = parameter.ContainingSymbol as IMethodSymbol; - if (method?.IsReducedExtension() == true) { - symbol = method.GetConstructedReducedFrom().Parameters[parameter.Ordinal + 1]; - } - } - - return symbol?.OriginalDefinition; - } - public static bool IsFunctionValue(this ISymbol symbol) { return symbol is ILocalSymbol && ((ILocalSymbol)symbol).IsFunctionValue; diff --git a/ICSharpCode.CodeConverter/Util/SyntaxExtensions.cs b/CodeConverter/Util/SyntaxExtensions.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/SyntaxExtensions.cs rename to CodeConverter/Util/SyntaxExtensions.cs diff --git a/CodeConverter/Util/SyntaxNodeExtensions.cs b/CodeConverter/Util/SyntaxNodeExtensions.cs new file mode 100644 index 000000000..58a86d6a0 --- /dev/null +++ b/CodeConverter/Util/SyntaxNodeExtensions.cs @@ -0,0 +1,700 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using ICSharpCode.CodeConverter.Shared; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.VisualBasic; +using Microsoft.CodeAnalysis.VisualBasic.Syntax; +using CompilationUnitSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax; +using CSharpExtensions = Microsoft.CodeAnalysis.CSharp.CSharpExtensions; +using VBSyntaxFactory = Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory; +using VBSyntaxKind = Microsoft.CodeAnalysis.VisualBasic.SyntaxKind; +using CSSyntaxKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind; +using FieldDeclarationSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.FieldDeclarationSyntax; +using SyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using TypeSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax; + +namespace ICSharpCode.CodeConverter.Util +{ + internal static class SyntaxNodeExtensions + { + + public static IEnumerable GetAncestors(this SyntaxNode node) + { + var current = node.Parent; + + while (current != null) { + yield return current; + + current = current is IStructuredTriviaSyntax + ? ((IStructuredTriviaSyntax)current).ParentTrivia.Token.Parent + : current.Parent; + } + } + + public static IEnumerable GetAncestors(this SyntaxNode node) + where TNode : SyntaxNode + { + var current = node.Parent; + while (current != null) { + if (current is TNode) { + yield return (TNode)current; + } + + current = current is IStructuredTriviaSyntax + ? ((IStructuredTriviaSyntax)current).ParentTrivia.Token.Parent + : current.Parent; + } + } + + public static TNode GetAncestor(this SyntaxNode node) + where TNode : SyntaxNode + { + if (node == null) { + return default(TNode); + } + + return node.GetAncestors().FirstOrDefault(); + } + + public static TNode GetAncestorOrThis(this SyntaxNode node) + where TNode : SyntaxNode + { + if (node == null) { + return default(TNode); + } + + return node.GetAncestorsOrThis().FirstOrDefault(); + } + + public static IEnumerable GetAncestorsOrThis(this SyntaxNode node) + where TNode : SyntaxNode + { + var current = node; + while (current != null) { + if (current is TNode) { + yield return (TNode)current; + } + + current = current is IStructuredTriviaSyntax + ? ((IStructuredTriviaSyntax)current).ParentTrivia.Token.Parent + : current.Parent; + } + } + + public static bool HasAncestor(this SyntaxNode node) + where TNode : SyntaxNode + { + return node.GetAncestors().Any(); + } + + public static ISymbol GetEnclosingDeclaredTypeSymbol(this SyntaxNode node, SemanticModel semanticModel) + { + var typeBlockSyntax = (SyntaxNode)node.GetAncestor() + ?? node.GetAncestor(); + if (typeBlockSyntax == null) return null; + return semanticModel.GetDeclaredSymbol(typeBlockSyntax); + } + + public static SyntaxList WithVbSourceMappingFrom(this SyntaxList converted, SyntaxNode node) where T : CSharpSyntaxNode + { + if (converted.Count != 1) return WithSourceMappingFrom(converted, node); + var single = converted.Single(); + return converted.Replace(single, single.WithVbSourceMappingFrom(node)); + } + + public static SyntaxList WithCsSourceMappingFrom(this SyntaxList converted, SyntaxNode node) where T : VisualBasicSyntaxNode + { + if (converted.Count != 1) return WithSourceMappingFrom(converted, node); + var single = converted.Single(); + return converted.Replace(single, single.WithCsSourceMappingFrom(node)); + } + + private static SyntaxList WithSourceMappingFrom(this SyntaxList converted, SyntaxNode node) where T : SyntaxNode + { + if (!converted.Any()) return converted; + var origLinespan = node.SyntaxTree.GetLineSpan(node.Span); + var first = converted.First(); + converted = converted.Replace(first, node.CopyAnnotationsTo(first).WithSourceStartLineAnnotation(origLinespan)); + var last = converted.Last(); + return converted.Replace(last, last.WithSourceEndLineAnnotation(origLinespan)); + } + + public static T WithVbSourceMappingFrom(this T converted, SyntaxNodeOrToken fromSource) where T : CSharpSyntaxNode + { + if (converted == null) return null; + var lastCsConvertedToken = converted.GetLastToken(); + if (lastCsConvertedToken.IsKind(CSSyntaxKind.CloseBraceToken) && IsBlockParent(converted, lastCsConvertedToken) && fromSource.AsNode()?.ChildNodes().LastOrDefault() is EndBlockStatementSyntax lastVbSourceNode) { + converted = converted.ReplaceToken(lastCsConvertedToken, lastCsConvertedToken.WithSourceMappingFrom(lastVbSourceNode)); + } + return converted.WithSourceMappingFrom(fromSource); + } + + public static T WithCsSourceMappingFrom(this T converted, SyntaxNodeOrToken fromSource) where T : VisualBasicSyntaxNode + { + if (converted == null) return null; + var lastCsSourceToken = fromSource.AsNode()?.GetLastToken(); + if (lastCsSourceToken?.IsKind(CSSyntaxKind.CloseBraceToken) == true && IsBlockParent(fromSource.AsNode(), lastCsSourceToken.Value) && converted.ChildNodes().LastOrDefault() is EndBlockStatementSyntax lastVbConvertedNode) { + converted = converted.ReplaceNode(lastVbConvertedNode, lastVbConvertedNode.WithSourceMappingFrom(lastCsSourceToken.Value)); + } + return converted.WithSourceMappingFrom(fromSource); + } + + private static T WithSourceMappingFrom(this T converted, SyntaxNodeOrToken fromSource) where T : SyntaxNode + { + if (converted == null) return null; + var linespan = fromSource.SyntaxTree.GetLineSpan(fromSource.Span); + return converted.WithSourceStartLineAnnotation(linespan).WithSourceEndLineAnnotation(linespan); + } + + public static T WithSourceStartLineAnnotation(this T node, FileLinePositionSpan sourcePosition) where T : SyntaxNode + { + return node.WithAdditionalAnnotations(AnnotationConstants.SourceStartLine(sourcePosition)); + } + + public static T WithSourceEndLineAnnotation(this T node, FileLinePositionSpan sourcePosition) where T : SyntaxNode + { + return node.WithAdditionalAnnotations(AnnotationConstants.SourceEndLine(sourcePosition)); + } + + public static T WithoutSourceMapping(this T converted) where T : SyntaxNode + { + converted = converted.ReplaceTokens(converted.DescendantTokens(), (o, r) => + r.WithoutSourceMapping() + ); + return converted.ReplaceNodes(converted.DescendantNodes(), (o, r) => + WithoutSourceMappingNonRecursive(r) + ).WithoutSourceMappingNonRecursive(); + } + + private static T WithoutSourceMappingNonRecursive(this T node) where T : SyntaxNode + { + return node.WithoutAnnotations(AnnotationConstants.SourceStartLineAnnotationKind).WithoutAnnotations(AnnotationConstants.SourceEndLineAnnotationKind); + } + + private static bool IsBlockParent(SyntaxNode converted, SyntaxToken lastCsConvertedToken) + { + return lastCsConvertedToken.Parent == converted || lastCsConvertedToken.Parent is BlockSyntax b && b.Parent == converted; + } + + public static bool IsKind(this SyntaxNode node, CSSyntaxKind kind1, CSSyntaxKind kind2) + { + if (node == null) { + return false; + } + + var csharpKind = CSharpExtensions.Kind(node); + return csharpKind == kind1 || csharpKind == kind2; + } + + public static bool IsKind(this SyntaxNode node, CSSyntaxKind kind1, CSSyntaxKind kind2, CSSyntaxKind kind3) + { + if (node == null) { + return false; + } + + var csharpKind = CSharpExtensions.Kind(node); + return csharpKind == kind1 || csharpKind == kind2 || csharpKind == kind3; + } + + public static bool IsKind(this SyntaxNode node, CSSyntaxKind kind1, CSSyntaxKind kind2, CSSyntaxKind kind3, CSSyntaxKind kind4) + { + if (node == null) { + return false; + } + + var csharpKind = CSharpExtensions.Kind(node); + return csharpKind == kind1 || csharpKind == kind2 || csharpKind == kind3 || csharpKind == kind4; + } + + public static bool IsKind(this SyntaxNode node, CSSyntaxKind kind1, CSSyntaxKind kind2, CSSyntaxKind kind3, CSSyntaxKind kind4, CSSyntaxKind kind5) + { + if (node == null) { + return false; + } + + var csharpKind = CSharpExtensions.Kind(node); + return csharpKind == kind1 || csharpKind == kind2 || csharpKind == kind3 || csharpKind == kind4 || csharpKind == kind5; + } + + /// + /// Returns the list of using directives that affect . The list will be returned in + /// top down order. + /// + public static IEnumerable GetEnclosingUsingDirectives(this SyntaxNode node) + { + return node.GetAncestorOrThis().Usings + .Concat(node.GetAncestorsOrThis() + .Reverse() + .SelectMany(n => n.Usings)); + } + + public static bool IsInStaticCsContext(this SyntaxNode node) + { + // this/base calls are always static. + if (node.FirstAncestorOrSelf() != null) { + return true; + } + + var memberDeclaration = node.FirstAncestorOrSelf(); + if (memberDeclaration == null) { + return false; + } + + switch (memberDeclaration.Kind()) { + case CSSyntaxKind.MethodDeclaration: + case CSSyntaxKind.ConstructorDeclaration: + case CSSyntaxKind.PropertyDeclaration: + case CSSyntaxKind.EventDeclaration: + case CSSyntaxKind.IndexerDeclaration: + return memberDeclaration.GetModifiers().Any(CSSyntaxKind.StaticKeyword); + + case CSSyntaxKind.FieldDeclaration: + // Inside a field one can only access static members of a type. + return true; + + case CSSyntaxKind.DestructorDeclaration: + return false; + } + + // Global statements are not a static context. + if (node.FirstAncestorOrSelf() != null) { + return false; + } + + // any other location is considered static + return true; + } + + /// + /// Returns all of the trivia to the left of this token up to the previous token (concatenates + /// the previous token's trailing trivia and this token's leading trivia). + /// + public static IEnumerable GetAllPrecedingTriviaToPreviousToken(this SyntaxToken token) + { + var prevToken = token.GetPreviousToken(includeSkipped: true); + if (CSharpExtensions.Kind(prevToken) == CSSyntaxKind.None) { + return token.LeadingTrivia; + } + + return prevToken.TrailingTrivia.Concat(token.LeadingTrivia); + } + + public static bool IsBreakableConstruct(this SyntaxNode node) + { + switch (CSharpExtensions.Kind(node)) { + case CSSyntaxKind.DoStatement: + case CSSyntaxKind.WhileStatement: + case CSSyntaxKind.SwitchStatement: + case CSSyntaxKind.ForStatement: + case CSSyntaxKind.ForEachStatement: + return true; + } + + return false; + } + + public static bool IsContinuableConstruct(this SyntaxNode node) + { + switch (CSharpExtensions.Kind(node)) { + case CSSyntaxKind.DoStatement: + case CSSyntaxKind.WhileStatement: + case CSSyntaxKind.ForStatement: + case CSSyntaxKind.ForEachStatement: + return true; + } + + return false; + } + + public static bool IsReturnableConstruct(this SyntaxNode node) + { + switch (CSharpExtensions.Kind(node)) { + case CSSyntaxKind.AnonymousMethodExpression: + case CSSyntaxKind.SimpleLambdaExpression: + case CSSyntaxKind.ParenthesizedLambdaExpression: + case CSSyntaxKind.MethodDeclaration: + case CSSyntaxKind.ConstructorDeclaration: + case CSSyntaxKind.DestructorDeclaration: + case CSSyntaxKind.GetAccessorDeclaration: + case CSSyntaxKind.SetAccessorDeclaration: + case CSSyntaxKind.OperatorDeclaration: + case CSSyntaxKind.AddAccessorDeclaration: + case CSSyntaxKind.RemoveAccessorDeclaration: + return true; + } + + return false; + } + + public static bool SpansPreprocessorDirective( + this IEnumerable list) + where TSyntaxNode : SyntaxNode + { + if (list == null || !list.Any()) { + return false; + } + + var tokens = list.SelectMany(n => n.DescendantTokens()); + + // todo: we need to dive into trivia here. + return tokens.SpansPreprocessorDirective(); + } + + public static T WithPrependedLeadingTrivia( + this T node, + params SyntaxTrivia[] trivia) where T : SyntaxNode + { + if (trivia.Length == 0) { + return node; + } + + return node.WithPrependedLeadingTrivia((IEnumerable)trivia); + } + + public static T WithPrependedLeadingTrivia( + this T node, + SyntaxTriviaList trivia) where T : SyntaxNode + { + if (trivia.Count == 0) { + return node; + } + + return node.WithLeadingTrivia(trivia.Concat(node.GetLeadingTrivia())); + } + + public static T WithPrependedLeadingTrivia( + this T node, + IEnumerable trivia) where T : SyntaxNode + { + return node.WithPrependedLeadingTrivia(Microsoft.CodeAnalysis.CSharp.SyntaxExtensions.ToSyntaxTriviaList(trivia)); + } + + public static T WithAppendedTrailingTrivia( + this T node, + params SyntaxTrivia[] trivia) where T : SyntaxNode + { + if (trivia.Length == 0) { + return node; + } + + return node.WithAppendedTrailingTrivia((IEnumerable)trivia); + } + + public static T WithAppendedTrailingTrivia( + this T node, + SyntaxTriviaList trivia) where T : SyntaxNode + { + if (trivia.Count == 0) { + return node; + } + + return node.WithTrailingTrivia(node.GetTrailingTrivia().Concat(trivia)); + } + + public static T WithAppendedTrailingTrivia( + this T node, + IEnumerable trivia) where T : SyntaxNode + { + return node.WithAppendedTrailingTrivia(Microsoft.CodeAnalysis.CSharp.SyntaxExtensions.ToSyntaxTriviaList(trivia)); + } + + public static T With( + this T node, + IEnumerable leadingTrivia, + IEnumerable trailingTrivia) where T : SyntaxNode + { + return node.WithLeadingTrivia(leadingTrivia).WithTrailingTrivia(trailingTrivia); + } + public static SyntaxToken WithConvertedTriviaFrom(this SyntaxToken node, SyntaxNode otherNode) + { + return node.WithConvertedLeadingTriviaFrom(otherNode).WithConvertedTrailingTriviaFrom(otherNode); + } + + public static T WithConvertedLeadingTriviaFrom(this T node, SyntaxToken fromToken) where T : SyntaxNode + { + var firstConvertedToken = node.GetFirstToken(); + return node.ReplaceToken(firstConvertedToken, firstConvertedToken.WithConvertedLeadingTriviaFrom(fromToken)); + } + + public static SyntaxToken WithConvertedLeadingTriviaFrom(this SyntaxToken node, SyntaxNode otherNode) + { + var firstToken = otherNode?.GetFirstToken(); + return WithConvertedLeadingTriviaFrom(node, firstToken); + } + + public static SyntaxToken WithConvertedLeadingTriviaFrom(this SyntaxToken node, SyntaxToken? sourceToken) + { + if (sourceToken == null) return node; + var convertedTrivia = ConvertTrivia(sourceToken.Value.LeadingTrivia); + return node.WithLeadingTrivia(convertedTrivia); + } + + public static T WithConvertedTrailingTriviaFrom(this T node, SyntaxToken fromToken, TriviaKinds triviaKinds = null) where T : SyntaxNode + { + var lastConvertedToken = node.GetLastToken(); + return node.ReplaceToken(lastConvertedToken, lastConvertedToken.WithConvertedTrailingTriviaFrom(fromToken, triviaKinds)); + } + + public static SyntaxToken WithConvertedTrailingTriviaFrom(this SyntaxToken node, SyntaxNode otherNode, TriviaKinds triviaKinds = null) + { + return node.WithConvertedTrailingTriviaFrom(otherNode?.GetLastToken(), triviaKinds); + } + + public static SyntaxToken WithConvertedTrailingTriviaFrom(this SyntaxToken node, SyntaxToken? otherToken, TriviaKinds triviaKinds = null) + { + triviaKinds = triviaKinds ?? TriviaKinds.All; + if (!otherToken.HasValue || !otherToken.Value.HasTrailingTrivia) return node; + var convertedTrivia = ConvertTrivia(otherToken.Value.TrailingTrivia.Where(triviaKinds.ShouldAccept).ToArray()); + return node.WithTrailingTrivia(node.ImportantTrailingTrivia().Concat(convertedTrivia)); + } + + public static IEnumerable ImportantTrailingTrivia(this SyntaxToken node) + { + return node.TrailingTrivia.Where(x => !x.IsWhitespaceOrEndOfLine()); + } + + public static bool ParentHasSameTrailingTrivia(this SyntaxNode otherNode) + { + return otherNode.Parent.GetLastToken() == otherNode.GetLastToken(); + } + + public static IEnumerable ConvertTrivia(this IReadOnlyCollection triviaToConvert) + { + try { + if (triviaToConvert.Any() && triviaToConvert.First().Language == LanguageNames.CSharp) { + return CSharpToVBCodeConverter.Util.RecursiveTriviaConverter.ConvertTopLevel(triviaToConvert).Where(x => x != default(SyntaxTrivia)); + } + return triviaToConvert.SelectMany(ConvertVBTrivia).Where(x => x != default(SyntaxTrivia)); + } catch (Exception) { + return Enumerable.Empty(); //TODO Log this somewhere, or write enough tests to be really confident it won't throw + } + } + + private static IEnumerable ConvertVBTrivia(SyntaxTrivia t) + { + if (t.IsKind(VBSyntaxKind.CommentTrivia)) { + yield return SyntaxFactory.SyntaxTrivia(CSSyntaxKind.SingleLineCommentTrivia, $"// {t.GetCommentText()}"); + yield break; + } + if (t.IsKind(VBSyntaxKind.DocumentationCommentTrivia)) { + var previousWhitespace = t.GetPreviousTrivia(t.SyntaxTree, CancellationToken.None).ToString(); + var commentTextLines = t.GetCommentText().Replace("\r\n", "\n").Replace("\r", "\n").Split('\n'); + var outputCommentText = "/// " + String.Join($"\r\n{previousWhitespace}/// ", commentTextLines) + Environment.NewLine; + yield return SyntaxFactory.SyntaxTrivia(CSSyntaxKind.SingleLineCommentTrivia, outputCommentText); //It's always single line...even when it has multiple lines + yield break; + } + + if (t.IsKind(VBSyntaxKind.WhitespaceTrivia)) { + yield return SyntaxFactory.SyntaxTrivia(CSSyntaxKind.WhitespaceTrivia, t.ToString()); + yield break; + } + + if (t.IsKind(VBSyntaxKind.EndOfLineTrivia)) { + // Mapping one to one here leads to newlines appearing where the natural line-end was in VB. + // e.g. ToString\r\n() + // Because C Sharp needs those brackets. Handling each possible case of this is far more effort than it's worth. + yield return SyntaxFactory.SyntaxTrivia(CSSyntaxKind.EndOfLineTrivia, t.ToString()); + yield break; + } + + //Each of these would need its own method to recreate for C# with the right structure probably so let's just warn about them for now. + var convertedKind = t.GetCSKind(); + yield return convertedKind.HasValue + ? SyntaxFactory.Comment($"/* TODO ERROR: Skipped {convertedKind.Value} */") + : default(SyntaxTrivia); + } + + public static SyntaxTokenList GetModifiers(this CSharpSyntaxNode member) + { + if (member != null) { + switch (CSharpExtensions.Kind(member)) { + case CSSyntaxKind.EnumDeclaration: + return ((EnumDeclarationSyntax)member).Modifiers; + case CSSyntaxKind.ClassDeclaration: + case CSSyntaxKind.InterfaceDeclaration: + case CSSyntaxKind.StructDeclaration: + return ((TypeDeclarationSyntax)member).Modifiers; + case CSSyntaxKind.DelegateDeclaration: + return ((DelegateDeclarationSyntax)member).Modifiers; + case CSSyntaxKind.FieldDeclaration: + return ((FieldDeclarationSyntax)member).Modifiers; + case CSSyntaxKind.EventFieldDeclaration: + return ((EventFieldDeclarationSyntax)member).Modifiers; + case CSSyntaxKind.ConstructorDeclaration: + return ((ConstructorDeclarationSyntax)member).Modifiers; + case CSSyntaxKind.DestructorDeclaration: + return ((DestructorDeclarationSyntax)member).Modifiers; + case CSSyntaxKind.PropertyDeclaration: + return ((PropertyDeclarationSyntax)member).Modifiers; + case CSSyntaxKind.EventDeclaration: + return ((EventDeclarationSyntax)member).Modifiers; + case CSSyntaxKind.IndexerDeclaration: + return ((IndexerDeclarationSyntax)member).Modifiers; + case CSSyntaxKind.OperatorDeclaration: + return ((OperatorDeclarationSyntax)member).Modifiers; + case CSSyntaxKind.ConversionOperatorDeclaration: + return ((ConversionOperatorDeclarationSyntax)member).Modifiers; + case CSSyntaxKind.MethodDeclaration: + return ((MethodDeclarationSyntax)member).Modifiers; + case CSSyntaxKind.GetAccessorDeclaration: + case CSSyntaxKind.SetAccessorDeclaration: + case CSSyntaxKind.AddAccessorDeclaration: + case CSSyntaxKind.RemoveAccessorDeclaration: + return ((AccessorDeclarationSyntax)member).Modifiers; + } + } + + return default(SyntaxTokenList); + } + + public static SyntaxNode WithModifiers(this SyntaxNode member, SyntaxTokenList modifiers) + { + if (member != null) { + switch (CSharpExtensions.Kind(member)) { + case CSSyntaxKind.EnumDeclaration: + return ((EnumDeclarationSyntax)member).WithModifiers(modifiers); + case CSSyntaxKind.ClassDeclaration: + case CSSyntaxKind.InterfaceDeclaration: + case CSSyntaxKind.StructDeclaration: + return ((TypeDeclarationSyntax)member).WithModifiers(modifiers); + case CSSyntaxKind.DelegateDeclaration: + return ((DelegateDeclarationSyntax)member).WithModifiers(modifiers); + case CSSyntaxKind.FieldDeclaration: + return ((FieldDeclarationSyntax)member).WithModifiers(modifiers); + case CSSyntaxKind.EventFieldDeclaration: + return ((EventFieldDeclarationSyntax)member).WithModifiers(modifiers); + case CSSyntaxKind.ConstructorDeclaration: + return ((ConstructorDeclarationSyntax)member).WithModifiers(modifiers); + case CSSyntaxKind.DestructorDeclaration: + return ((DestructorDeclarationSyntax)member).WithModifiers(modifiers); + case CSSyntaxKind.PropertyDeclaration: + return ((PropertyDeclarationSyntax)member).WithModifiers(modifiers); + case CSSyntaxKind.EventDeclaration: + return ((EventDeclarationSyntax)member).WithModifiers(modifiers); + case CSSyntaxKind.IndexerDeclaration: + return ((IndexerDeclarationSyntax)member).WithModifiers(modifiers); + case CSSyntaxKind.OperatorDeclaration: + return ((OperatorDeclarationSyntax)member).WithModifiers(modifiers); + case CSSyntaxKind.ConversionOperatorDeclaration: + return ((ConversionOperatorDeclarationSyntax)member).WithModifiers(modifiers); + case CSSyntaxKind.MethodDeclaration: + return ((MethodDeclarationSyntax)member).WithModifiers(modifiers); + case CSSyntaxKind.GetAccessorDeclaration: + case CSSyntaxKind.SetAccessorDeclaration: + case CSSyntaxKind.AddAccessorDeclaration: + case CSSyntaxKind.RemoveAccessorDeclaration: + return ((AccessorDeclarationSyntax)member).WithModifiers(modifiers); + } + } + + return null; + } + + public static TypeDeclarationSyntax WithModifiers( + this TypeDeclarationSyntax node, SyntaxTokenList modifiers) + { + switch (node.Kind()) { + case CSSyntaxKind.ClassDeclaration: + return ((ClassDeclarationSyntax)node).WithModifiers(modifiers); + case CSSyntaxKind.InterfaceDeclaration: + return ((InterfaceDeclarationSyntax)node).WithModifiers(modifiers); + case CSSyntaxKind.StructDeclaration: + return ((StructDeclarationSyntax)node).WithModifiers(modifiers); + } + + throw new InvalidOperationException(); + } + + public static SyntaxTree WithAnnotatedNode(this SyntaxNode root, SyntaxNode selectedNode, string annotationKind, string annotationData = "") + { + var annotatatedNode = + selectedNode.WithAdditionalAnnotations(new SyntaxAnnotation(annotationKind, annotationData)); + return root.ReplaceNode(selectedNode, annotatatedNode).SyntaxTree.WithFilePath(root.SyntaxTree.FilePath); + } + + public static string GetBriefNodeDescription(this SyntaxNode node) + { + var sb = new StringBuilder(); + sb.Append($"'{node.ToString().Truncate()}' at character {node.SpanStart}"); + return sb.ToString(); + } + + public static string DescribeConversionError(this SyntaxNode node, Exception e) + { + return $"Cannot convert {node.GetType().Name}, {e}{Environment.NewLine}{Environment.NewLine}" + + $"Input:{Environment.NewLine}{node.ToFullString()}{Environment.NewLine}"; + } + + public static string DescribeConversionWarning(this SyntaxNode node, string addtlInfo) + { + return $"{addtlInfo}{Environment.NewLine}" + + $"{node.NormalizeWhitespace().ToFullString()}{Environment.NewLine}"; + } + + private static string Truncate(this string input, int maxLength = 30, string truncationIndicator = "...") + { + input = input.Replace(Environment.NewLine, "\\r\\n").Replace(" ", " ").Replace("\t", " "); + if (input.Length <= maxLength) return input; + return input.Substring(0, maxLength - truncationIndicator.Length) + truncationIndicator; + } + + public static T WithCsTrailingErrorComment(this T dummyDestNode, + VisualBasicSyntaxNode sourceNode, + Exception exception) where T : CSharpSyntaxNode + { + var errorDirective = SyntaxFactory.ParseTrailingTrivia($"#error Cannot convert {sourceNode.GetType().Name} - see comment for details{Environment.NewLine}"); + var errorDescription = sourceNode.DescribeConversionError(exception); + var commentedText = "/* " + errorDescription + " */"; + var trailingTrivia = SyntaxFactory.TriviaList(errorDirective.Concat(SyntaxFactory.Comment(commentedText))); + + return dummyDestNode + .WithTrailingTrivia(trailingTrivia) + .WithAdditionalAnnotations(new SyntaxAnnotation(AnnotationConstants.ConversionErrorAnnotationKind, exception.ToString())); + } + + public static T WithCsTrailingWarningComment(this T dummyDestNode, string warning, string addtlInfo, + CSharpSyntaxNode convertedNode + ) where T : CSharpSyntaxNode + { + var warningDirective = SyntaxFactory.ParseTrailingTrivia($"#warning {warning}{Environment.NewLine}"); + var warningDescription = convertedNode.DescribeConversionWarning(addtlInfo); + var commentedText = "/* " + warningDescription + " */"; + var trailingTrivia = SyntaxFactory.TriviaList(warningDirective.Concat(SyntaxFactory.Comment(commentedText))); + + return dummyDestNode + .WithTrailingTrivia(trailingTrivia); + } + + public static T WithVbTrailingErrorComment( + this T dummyDestNode, CSharpSyntaxNode problematicSourceNode, Exception exception) where T : VisualBasicSyntaxNode + { + var errorDescription = problematicSourceNode.DescribeConversionError(exception); + var commentedText = "''' " + errorDescription.Replace("\r\n", "\r\n''' "); + return dummyDestNode + .WithTrailingTrivia(VBSyntaxFactory.CommentTrivia(commentedText)) + .WithAdditionalAnnotations(new SyntaxAnnotation(AnnotationConstants.ConversionErrorAnnotationKind, + exception.ToString())); + } + + public static bool ContainsDeclaredVisibility(this SyntaxTokenList modifiers, bool isVariableOrConst = false, bool isConstructor = false) + { + return modifiers.Any(m => m.IsCsVisibility(isVariableOrConst, isConstructor)); + } + + public static SyntaxToken FindNonZeroWidthToken(this SyntaxNode node, int position) + { + var syntaxToken = node.FindToken(position); + if (syntaxToken.FullWidth() == 0) { + return syntaxToken.GetPreviousToken(); + } else { + return syntaxToken; + } + } + } +} \ No newline at end of file diff --git a/CodeConverter/Util/SyntaxTokenExtensions.cs b/CodeConverter/Util/SyntaxTokenExtensions.cs new file mode 100644 index 000000000..f588c8c0e --- /dev/null +++ b/CodeConverter/Util/SyntaxTokenExtensions.cs @@ -0,0 +1,473 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; +using VisualBasicExtensions = Microsoft.CodeAnalysis.VisualBasic.VisualBasicExtensions; +using VBasic = Microsoft.CodeAnalysis.VisualBasic; +using ICSharpCode.CodeConverter.Shared; + +namespace ICSharpCode.CodeConverter.Util +{ +#if NR6 + public +#endif + internal static class SyntaxTokenExtensions + { + public static SyntaxNode GetAncestor(this SyntaxToken token, Func predicate) + { + return token.GetAncestor(predicate); + } + + public static T GetAncestor(this SyntaxToken token, Func predicate = null) + where T : SyntaxNode + { + return token.Parent != null + ? token.Parent.FirstAncestorOrSelf(predicate) + : default(T); + } + + public static IEnumerable GetAncestors(this SyntaxToken token) + where T : SyntaxNode + { + return token.Parent != null + ? token.Parent.AncestorsAndSelf().OfType() + : Enumerable.Empty(); + } + + public static IEnumerable GetAncestors(this SyntaxToken token, Func predicate) + { + return token.Parent != null + ? token.Parent.AncestorsAndSelf().Where(predicate) + : Enumerable.Empty(); + } + + public static int Width(this SyntaxToken token) + { + return token.Span.Length; + } + + public static int FullWidth(this SyntaxToken token) + { + return token.FullSpan.Length; + } + + private static bool IsGenericInterfaceOrDelegateTypeParameterList(SyntaxNode node) + { + if (node.IsKind(SyntaxKind.TypeParameterList)) { + if (node.IsParentKind(SyntaxKind.InterfaceDeclaration)) { + var decl = node.Parent as TypeDeclarationSyntax; + return decl.TypeParameterList == node; + } else if (node.IsParentKind(SyntaxKind.DelegateDeclaration)) { + var decl = node.Parent as DelegateDeclarationSyntax; + return decl.TypeParameterList == node; + } + } + + return false; + } + + public static bool IsKindOrHasMatchingText(this SyntaxToken token, Microsoft.CodeAnalysis.VisualBasic.SyntaxKind kind) + { + return VisualBasicExtensions.Kind(token) == kind || token.HasMatchingText(kind); + } + + public static bool HasMatchingText(this SyntaxToken token, SyntaxKind kind) + { + return token.ToString() == SyntaxFacts.GetText(kind); + } + + public static bool HasMatchingText(this SyntaxToken token, Microsoft.CodeAnalysis.VisualBasic.SyntaxKind kind) + { + return token.ToString() == VBasic.SyntaxFacts.GetText(kind); + } + + public static bool IsKind(this SyntaxToken token, SyntaxKind kind1, SyntaxKind kind2) + { + return token.Kind() == kind1 + || token.Kind() == kind2; + } + + public static bool IsKind(this SyntaxToken token, Microsoft.CodeAnalysis.VisualBasic.SyntaxKind kind1, Microsoft.CodeAnalysis.VisualBasic.SyntaxKind kind2) + { + return VisualBasicExtensions.Kind(token) == kind1 + || VisualBasicExtensions.Kind(token) == kind2; + } + + public static bool IsKind(this SyntaxToken token, SyntaxKind kind1, SyntaxKind kind2, SyntaxKind kind3) + { + return token.Kind() == kind1 + || token.Kind() == kind2 + || token.Kind() == kind3; + } + + public static bool IsKind(this SyntaxToken token, Microsoft.CodeAnalysis.VisualBasic.SyntaxKind kind1, Microsoft.CodeAnalysis.VisualBasic.SyntaxKind kind2, Microsoft.CodeAnalysis.VisualBasic.SyntaxKind kind3) + { + return VisualBasicExtensions.Kind(token) == kind1 + || VisualBasicExtensions.Kind(token) == kind2 + || VisualBasicExtensions.Kind(token) == kind3; + } + + public static bool IsKind(this SyntaxToken token, params SyntaxKind[] kinds) + { + return kinds.Contains(token.Kind()); + } + + public static bool IsKind(this SyntaxToken token, params Microsoft.CodeAnalysis.VisualBasic.SyntaxKind[] kinds) + { + return kinds.Contains(VisualBasicExtensions.Kind(token)); + } + + public static bool IsLiteral(this SyntaxToken token) + { + switch (token.Kind()) { + case SyntaxKind.CharacterLiteralToken: + case SyntaxKind.FalseKeyword: + case SyntaxKind.NumericLiteralToken: + case SyntaxKind.StringLiteralToken: + case SyntaxKind.TrueKeyword: + return true; + + default: + return false; + } + } + + public static bool IntersectsWith(this SyntaxToken token, int position) + { + return token.Span.IntersectsWith(position); + } + + public static SyntaxToken GetPreviousTokenIfTouchingWord(this SyntaxToken token, int position) + { + return token.IntersectsWith(position) && IsWord(token) + ? token.GetPreviousToken(includeSkipped: true) + : token; + } + + public static bool IsWord(this SyntaxToken token) + { + return token.IsKind(SyntaxKind.IdentifierToken) + || SyntaxFacts.IsKeywordKind(token.Kind()) + || SyntaxFacts.IsContextualKeyword(token.Kind()) + || SyntaxFacts.IsPreprocessorKeyword(token.Kind()); + } + + public static SyntaxToken GetNextNonZeroWidthCsTokenOrEndOfFile(this SyntaxToken token) + { + return token.GetNextCsTokenOrEndOfFile(); + } + + public static SyntaxToken GetNextCsTokenOrEndOfFile( + this SyntaxToken token, + bool includeZeroWidth = false, + bool includeSkipped = false, + bool includeDirectives = false, + bool includeDocumentationComments = false) + { + var nextToken = token.GetNextToken(includeZeroWidth, includeSkipped, includeDirectives, includeDocumentationComments); + + return nextToken.Kind() == SyntaxKind.None + ? token.GetAncestor().EndOfFileToken + : nextToken; + } + + public static SyntaxToken With(this SyntaxToken token, SyntaxTriviaList leading, SyntaxTriviaList trailing) + { + return token.WithLeadingTrivia(leading).WithTrailingTrivia(trailing); + } + + /// + /// Determines whether the given SyntaxToken is the first token on a line in the specified SourceText. + /// + public static bool IsFirstTokenOnLine(this SyntaxToken token, SourceText text) + { + var previousToken = token.GetPreviousToken(includeSkipped: true, includeDirectives: true, includeDocumentationComments: true); + if (previousToken.Kind() == SyntaxKind.None) { + return true; + } + + var tokenLine = text.Lines.IndexOf(token.SpanStart); + var previousTokenLine = text.Lines.IndexOf(previousToken.SpanStart); + return tokenLine > previousTokenLine; + } + + public static bool SpansPreprocessorDirective(this IEnumerable tokens) + { + // we want to check all leading trivia of all tokens (except the + // first one), and all trailing trivia of all tokens (except the + // last one). + + var first = true; + var previousToken = default(SyntaxToken); + + foreach (var token in tokens) { + if (first) { + first = false; + } else { + // check the leading trivia of this token, and the trailing trivia + // of the previous token. + if (SpansPreprocessorDirective(token.LeadingTrivia) || + SpansPreprocessorDirective(previousToken.TrailingTrivia)) { + return true; + } + } + + previousToken = token; + } + + return false; + } + + private static bool SpansPreprocessorDirective(SyntaxTriviaList list) + { + return list.Any(t => t.GetStructure() is DirectiveTriviaSyntax); + } + + public static SyntaxToken WithoutTrivia( + this SyntaxToken token, + params SyntaxTrivia[] trivia) + { + if (!token.LeadingTrivia.Any() && !token.TrailingTrivia.Any()) { + return token; + } + + return token.With(new SyntaxTriviaList(), new SyntaxTriviaList()); + } + + public static SyntaxToken WithPrependedLeadingTrivia( + this SyntaxToken token, + params SyntaxTrivia[] trivia) + { + if (trivia.Length == 0) { + return token; + } + + return token.WithPrependedLeadingTrivia((IEnumerable)trivia); + } + + public static SyntaxToken WithPrependedLeadingTrivia( + this SyntaxToken token, + SyntaxTriviaList trivia) + { + if (trivia.Count == 0) { + return token; + } + + return token.WithLeadingTrivia(trivia.Concat(token.LeadingTrivia)); + } + + public static SyntaxToken WithPrependedLeadingTrivia( + this SyntaxToken token, + IEnumerable trivia) + { + return token.WithPrependedLeadingTrivia(trivia.ToSyntaxTriviaList()); + } + + public static SyntaxToken WithAppendedTrailingTrivia( + this SyntaxToken token, + IEnumerable trivia) + { + return token.WithTrailingTrivia(token.TrailingTrivia.Concat(trivia)); + } + + /// + /// Retrieves all trivia after this token, including it's trailing trivia and + /// the leading trivia of the next token. + /// + public static IEnumerable GetAllTrailingTrivia(this SyntaxToken token) + { + foreach (var trivia in token.TrailingTrivia) { + yield return trivia; + } + + var nextToken = token.GetNextCsTokenOrEndOfFile(includeZeroWidth: true, includeSkipped: true, includeDirectives: true, includeDocumentationComments: true); + + foreach (var trivia in nextToken.LeadingTrivia) { + yield return trivia; + } + } + + public static bool TryParseGenericName(this SyntaxToken genericIdentifier, CancellationToken cancellationToken, out GenericNameSyntax genericName) + { + if (genericIdentifier.GetNextToken(includeSkipped: true).Kind() == SyntaxKind.LessThanToken) { + var lastToken = genericIdentifier.FindLastTokenOfPartialGenericName(); + + var syntaxTree = genericIdentifier.SyntaxTree; + var name = SyntaxFactory.ParseName(syntaxTree.GetText(cancellationToken).ToString(TextSpan.FromBounds(genericIdentifier.SpanStart, lastToken.Span.End))); + + genericName = name as GenericNameSyntax; + return genericName != null; + } + + genericName = null; + return false; + } + + /// + /// Lexically, find the last token that looks like it's part of this generic name. + /// + /// The "name" of the generic identifier, last token before + /// the "&" + /// The last token in the name + /// This is related to the code in + public static SyntaxToken FindLastTokenOfPartialGenericName(this SyntaxToken genericIdentifier) + { + //Contract.ThrowIfFalse(genericIdentifier.Kind() == SyntaxKind.IdentifierToken); + + // advance to the "<" token + var token = genericIdentifier.GetNextToken(includeSkipped: true); + //Contract.ThrowIfFalse(token.Kind() == SyntaxKind.LessThanToken); + + int stack = 0; + + do { + // look forward one token + { + var next = token.GetNextToken(includeSkipped: true); + if (next.Kind() == SyntaxKind.None) { + return token; + } + + token = next; + } + + if (token.Kind() == SyntaxKind.GreaterThanToken) { + if (stack == 0) { + return token; + } else { + stack--; + continue; + } + } + + switch (token.Kind()) { + case SyntaxKind.LessThanLessThanToken: + stack++; + goto case SyntaxKind.LessThanToken; + + // fall through + case SyntaxKind.LessThanToken: + stack++; + break; + + case SyntaxKind.AsteriskToken: // for int* + case SyntaxKind.QuestionToken: // for int? + case SyntaxKind.ColonToken: // for global:: (so we don't dismiss help as you type the first :) + case SyntaxKind.ColonColonToken: // for global:: + case SyntaxKind.CloseBracketToken: + case SyntaxKind.OpenBracketToken: + case SyntaxKind.DotToken: + case SyntaxKind.IdentifierToken: + case SyntaxKind.CommaToken: + break; + + // If we see a member declaration keyword, we know we've gone too far + case SyntaxKind.ClassKeyword: + case SyntaxKind.StructKeyword: + case SyntaxKind.InterfaceKeyword: + case SyntaxKind.DelegateKeyword: + case SyntaxKind.EnumKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.PublicKeyword: + case SyntaxKind.InternalKeyword: + case SyntaxKind.ProtectedKeyword: + case SyntaxKind.VoidKeyword: + return token.GetPreviousToken(includeSkipped: true); + + default: + // user might have typed "in" on the way to typing "int" + // don't want to disregard this genericname because of that + if (SyntaxFacts.IsKeywordKind(token.Kind())) { + break; + } + + // anything else and we're sunk. Go back to the token before. + return token.GetPreviousToken(includeSkipped: true); + } + } + while (true); + } + + public static bool IsRegularStringLiteral(this SyntaxToken token) + { + return token.Kind() == SyntaxKind.StringLiteralToken && !token.IsVerbatimStringLiteral(); + } + + public static bool IsValidAttributeTarget(this SyntaxToken token) + { + switch (token.Kind()) { + case SyntaxKind.AssemblyKeyword: + case SyntaxKind.ModuleKeyword: + case SyntaxKind.FieldKeyword: + case SyntaxKind.EventKeyword: + case SyntaxKind.MethodKeyword: + case SyntaxKind.ParamKeyword: + case SyntaxKind.PropertyKeyword: + case SyntaxKind.ReturnKeyword: + case SyntaxKind.TypeKeyword: + return true; + + default: + return false; + } + } + + public static bool IsIdentifierOrAccessorOrAccessibilityModifier(this SyntaxToken token) + { + switch (token.Kind()) { + case SyntaxKind.IdentifierName: + case SyntaxKind.IdentifierToken: + case SyntaxKind.GetKeyword: + case SyntaxKind.SetKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.ProtectedKeyword: + case SyntaxKind.InternalKeyword: + case SyntaxKind.PublicKeyword: + return true; + + default: + return false; + } + } + + public static bool IsVbVisibility(this SyntaxToken token, bool isVariableOrConst, bool isConstructor) + { + return token.IsKind(VBasic.SyntaxKind.PublicKeyword, VBasic.SyntaxKind.FriendKeyword, VBasic.SyntaxKind.ProtectedKeyword, VBasic.SyntaxKind.PrivateKeyword) + || isVariableOrConst && token.IsKind(VBasic.SyntaxKind.ConstKeyword) + || isConstructor && token.IsKind(VBasic.SyntaxKind.SharedKeyword); + } + + public static bool IsCsVisibility(this SyntaxToken token, bool isVariableOrConst, bool isConstructor) + { + return token.IsKind(SyntaxKind.PublicKeyword, SyntaxKind.InternalKeyword, SyntaxKind.ProtectedKeyword, SyntaxKind.PrivateKeyword) + || isVariableOrConst && token.IsKind(SyntaxKind.ConstKeyword) + || isConstructor && token.IsKind(SyntaxKind.StaticKeyword); + } + + public static SyntaxToken WithSourceMappingFrom(this SyntaxToken converted, SyntaxNodeOrToken fromToken) + { + var origLinespan = fromToken.SyntaxTree.GetLineSpan(fromToken.Span); + if (fromToken.IsToken) converted = fromToken.AsToken().CopyAnnotationsTo(converted); + return converted.WithSourceStartLineAnnotation(origLinespan).WithSourceEndLineAnnotation(origLinespan); + } + + public static SyntaxToken WithSourceStartLineAnnotation(this SyntaxToken node, FileLinePositionSpan sourcePosition) + { + return node.WithAdditionalAnnotations(AnnotationConstants.SourceStartLine(sourcePosition)); + } + + public static SyntaxToken WithSourceEndLineAnnotation(this SyntaxToken node, FileLinePositionSpan sourcePosition) + { + return node.WithAdditionalAnnotations(AnnotationConstants.SourceEndLine(sourcePosition)); + } + + public static SyntaxToken WithoutSourceMapping(this SyntaxToken token) + { + return token.WithoutAnnotations(AnnotationConstants.SourceStartLineAnnotationKind).WithoutAnnotations(AnnotationConstants.SourceEndLineAnnotationKind); + } + } +} diff --git a/ICSharpCode.CodeConverter/Util/SyntaxTriviaExtensions.cs b/CodeConverter/Util/SyntaxTriviaExtensions.cs similarity index 99% rename from ICSharpCode.CodeConverter/Util/SyntaxTriviaExtensions.cs rename to CodeConverter/Util/SyntaxTriviaExtensions.cs index 7d1e7b07d..39e309357 100644 --- a/ICSharpCode.CodeConverter/Util/SyntaxTriviaExtensions.cs +++ b/CodeConverter/Util/SyntaxTriviaExtensions.cs @@ -7,7 +7,6 @@ using CS = Microsoft.CodeAnalysis.CSharp; using VBasic = Microsoft.CodeAnalysis.VisualBasic; using CSSyntax = Microsoft.CodeAnalysis.CSharp.Syntax; -using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; namespace ICSharpCode.CodeConverter.Util { diff --git a/ICSharpCode.CodeConverter/Util/TriviaKinds.cs b/CodeConverter/Util/TriviaKinds.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/TriviaKinds.cs rename to CodeConverter/Util/TriviaKinds.cs diff --git a/ICSharpCode.CodeConverter/Util/TypeExtensions.cs b/CodeConverter/Util/TypeExtensions.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/TypeExtensions.cs rename to CodeConverter/Util/TypeExtensions.cs diff --git a/ICSharpCode.CodeConverter/Util/UnicodeNewline.cs b/CodeConverter/Util/UnicodeNewline.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/UnicodeNewline.cs rename to CodeConverter/Util/UnicodeNewline.cs diff --git a/ICSharpCode.CodeConverter/Util/VBUtil.cs b/CodeConverter/Util/VBUtil.cs similarity index 100% rename from ICSharpCode.CodeConverter/Util/VBUtil.cs rename to CodeConverter/Util/VBUtil.cs diff --git a/ICSharpCode.CodeConverter/Util/VisualBasicCompiler.cs b/CodeConverter/Util/VisualBasicCompiler.cs similarity index 98% rename from ICSharpCode.CodeConverter/Util/VisualBasicCompiler.cs rename to CodeConverter/Util/VisualBasicCompiler.cs index 5faa56eb1..65dcdb174 100644 --- a/ICSharpCode.CodeConverter/Util/VisualBasicCompiler.cs +++ b/CodeConverter/Util/VisualBasicCompiler.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Text; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.VisualBasic; namespace ICSharpCode.CodeConverter.Util diff --git a/ICSharpCode.CodeConverter/VB/CSToVBConversion.cs b/CodeConverter/VB/CSToVBConversion.cs similarity index 93% rename from ICSharpCode.CodeConverter/VB/CSToVBConversion.cs rename to CodeConverter/VB/CSToVBConversion.cs index f38297f29..7de946f30 100644 --- a/ICSharpCode.CodeConverter/VB/CSToVBConversion.cs +++ b/CodeConverter/VB/CSToVBConversion.cs @@ -1,19 +1,15 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Text.RegularExpressions; using System.Threading.Tasks; -using ICSharpCode.CodeConverter.CSharp; using ICSharpCode.CodeConverter.Shared; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.VisualBasic; -using VBSyntaxFactory = Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory; using CSSyntax = Microsoft.CodeAnalysis.CSharp.Syntax; using SyntaxKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind; using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; +using System.Threading; namespace ICSharpCode.CodeConverter.VB { @@ -26,16 +22,20 @@ public class CSToVBConversion : ILanguageConversion private CSToVBProjectContentsConverter _csToVbProjectContentsConverter; public ConversionOptions ConversionOptions { get; set; } + private IProgress _progress; + private CancellationToken _cancellationToken; - public async Task CreateProjectContentsConverter(Project project) + public async Task CreateProjectContentsConverter(Project project, IProgress progress, CancellationToken cancellationToken) { - _csToVbProjectContentsConverter = new CSToVBProjectContentsConverter(ConversionOptions); + _progress = progress; + _cancellationToken = cancellationToken; + _csToVbProjectContentsConverter = new CSToVBProjectContentsConverter(ConversionOptions, progress, cancellationToken); await _csToVbProjectContentsConverter.InitializeSourceAsync(project); return _csToVbProjectContentsConverter; } public async Task SingleSecondPass(Document doc) { - return await doc.SimplifyStatements(UnresolvedNamespaceDiagnosticId); + return await doc.SimplifyStatements(UnresolvedNamespaceDiagnosticId, _cancellationToken); } public SyntaxNode GetSurroundedNode(IEnumerable descendantNodes, diff --git a/ICSharpCode.CodeConverter/VB/CSToVBProjectContentsConverter.cs b/CodeConverter/VB/CSToVBProjectContentsConverter.cs similarity index 80% rename from ICSharpCode.CodeConverter/VB/CSToVBProjectContentsConverter.cs rename to CodeConverter/VB/CSToVBProjectContentsConverter.cs index d5b39dab7..fcfe42e1e 100644 --- a/ICSharpCode.CodeConverter/VB/CSToVBProjectContentsConverter.cs +++ b/CodeConverter/VB/CSToVBProjectContentsConverter.cs @@ -6,6 +6,8 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.VisualBasic; +using System; +using System.Threading; namespace ICSharpCode.CodeConverter.VB { @@ -20,9 +22,13 @@ internal class CSToVBProjectContentsConverter : IProjectContentsConverter private Project _convertedVbProject; private VisualBasicCompilation _vbViewOfCsSymbols; private Project _vbReferenceProject; + private readonly IProgress _progress; + private readonly CancellationToken _cancellationToken; - public CSToVBProjectContentsConverter(ConversionOptions conversionOptions) + public CSToVBProjectContentsConverter(ConversionOptions conversionOptions, IProgress progress, CancellationToken cancellationToken) { + _progress = progress; + _cancellationToken = cancellationToken; var vbCompilationOptions = (VisualBasicCompilationOptions)conversionOptions.TargetCompilationOptionsOverride ?? VisualBasicCompiler.CreateCompilationOptions(conversionOptions.RootNamespaceOverride); @@ -49,17 +55,17 @@ public async Task InitializeSourceAsync(Project project) _sourceCsProject = project; _convertedVbProject = project.ToProjectFromAnyOptions(_vbCompilationOptions, _vbParseOptions); _vbReferenceProject = project.CreateReferenceOnlyProjectFromAnyOptions(_vbCompilationOptions); - _vbViewOfCsSymbols = (VisualBasicCompilation)await _vbReferenceProject.GetCompilationAsync(); + _vbViewOfCsSymbols = (VisualBasicCompilation)await _vbReferenceProject.GetCompilationAsync(_cancellationToken); Project = project; } public async Task SingleFirstPass(Document document) { - return await CSharpConverter.ConvertCompilationTree(document, _vbViewOfCsSymbols, _vbReferenceProject); + return await CSharpConverter.ConvertCompilationTree(document, _vbViewOfCsSymbols, _vbReferenceProject, _cancellationToken); } - public async Task<(Project project, List<(string Path, DocumentId DocId, string[] Errors)> firstPassDocIds)> - GetConvertedProject((string Path, SyntaxNode Node, string[] Errors)[] firstPassResults) + public async Task<(Project project, List> firstPassDocIds)> + GetConvertedProject(WipFileConversion[] firstPassResults) { return _convertedVbProject.WithDocuments(firstPassResults); } diff --git a/ICSharpCode.CodeConverter/VB/CSharpConverter.cs b/CodeConverter/VB/CSharpConverter.cs similarity index 78% rename from ICSharpCode.CodeConverter/VB/CSharpConverter.cs rename to CodeConverter/VB/CSharpConverter.cs index f74237480..9728b1343 100644 --- a/ICSharpCode.CodeConverter/VB/CSharpConverter.cs +++ b/CodeConverter/VB/CSharpConverter.cs @@ -1,35 +1,27 @@ using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; using System.Threading.Tasks; -using ICSharpCode.CodeConverter.CSharp; using ICSharpCode.CodeConverter.Shared; -using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Rename; -using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.VisualBasic; using CS = Microsoft.CodeAnalysis.CSharp; using CSS = Microsoft.CodeAnalysis.CSharp.Syntax; using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; +using System.Threading; namespace ICSharpCode.CodeConverter.VB { internal static class CSharpConverter { public static async Task ConvertCompilationTree(Document document, - VisualBasicCompilation vbViewOfCsSymbols, Project vbReferenceProject) + VisualBasicCompilation vbViewOfCsSymbols, Project vbReferenceProject, CancellationToken cancellationToken) { - document = await document.WithExpandedRootAsync(); - var compilation = await document.Project.GetCompilationAsync(); - var tree = await document.GetSyntaxTreeAsync(); + document = await document.WithExpandedRootAsync(cancellationToken); + var compilation = await document.Project.GetCompilationAsync(cancellationToken); + var tree = await document.GetSyntaxTreeAsync(cancellationToken); var semanticModel = compilation.GetSemanticModel(tree, true); - var root = await document.GetSyntaxRootAsync() as CSS.CompilationUnitSyntax ?? + var root = await document.GetSyntaxRootAsync(cancellationToken) as CSS.CompilationUnitSyntax ?? throw new InvalidOperationException(NullRootError(document)); var vbSyntaxGenerator = SyntaxGenerator.GetGenerator(vbReferenceProject); @@ -40,7 +32,7 @@ public static async Task ConvertCompilationTree(Document document, try { // This call is very expensive for large documents. Should look for a more performant version, e.g. Is NormalizeWhitespace good enough? - converted = (VBSyntax.CompilationUnitSyntax)Formatter.Format(converted, document.Project.Solution.Workspace); + converted = (VBSyntax.CompilationUnitSyntax)Formatter.Format(converted, document.Project.Solution.Workspace, cancellationToken: cancellationToken); return LineTriviaMapper.MapSourceTriviaToTarget(root, converted); } catch (Exception) { //TODO log return converted; diff --git a/ICSharpCode.CodeConverter/VB/CSharpHelperMethodDefinition.CSharpHelperMethodDefinition.cs b/CodeConverter/VB/CSharpHelperMethodDefinition.CSharpHelperMethodDefinition.cs similarity index 100% rename from ICSharpCode.CodeConverter/VB/CSharpHelperMethodDefinition.CSharpHelperMethodDefinition.cs rename to CodeConverter/VB/CSharpHelperMethodDefinition.CSharpHelperMethodDefinition.cs diff --git a/ICSharpCode.CodeConverter/VB/CaseConflictResolver.cs b/CodeConverter/VB/CaseConflictResolver.cs similarity index 100% rename from ICSharpCode.CodeConverter/VB/CaseConflictResolver.cs rename to CodeConverter/VB/CaseConflictResolver.cs diff --git a/ICSharpCode.CodeConverter/VB/CommentConvertingMethodBodyVisitor.cs b/CodeConverter/VB/CommentConvertingMethodBodyVisitor.cs similarity index 83% rename from ICSharpCode.CodeConverter/VB/CommentConvertingMethodBodyVisitor.cs rename to CodeConverter/VB/CommentConvertingMethodBodyVisitor.cs index f7f1ce64d..1b006279a 100644 --- a/ICSharpCode.CodeConverter/VB/CommentConvertingMethodBodyVisitor.cs +++ b/CodeConverter/VB/CommentConvertingMethodBodyVisitor.cs @@ -1,15 +1,9 @@ using System; -using ICSharpCode.CodeConverter.CSharp; -using ICSharpCode.CodeConverter.Shared; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; using CS = Microsoft.CodeAnalysis.CSharp; using VBasic = Microsoft.CodeAnalysis.VisualBasic; -using CSSyntax = Microsoft.CodeAnalysis.CSharp.Syntax; -using SyntaxNodeExtensions = ICSharpCode.CodeConverter.Util.SyntaxNodeExtensions; using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; -using System.Linq; -using System.Collections; namespace ICSharpCode.CodeConverter.VB { diff --git a/ICSharpCode.CodeConverter/VB/CommentConvertingVisitorWrapper.cs b/CodeConverter/VB/CommentConvertingVisitorWrapper.cs similarity index 100% rename from ICSharpCode.CodeConverter/VB/CommentConvertingVisitorWrapper.cs rename to CodeConverter/VB/CommentConvertingVisitorWrapper.cs diff --git a/ICSharpCode.CodeConverter/VB/CommonConversions.cs b/CodeConverter/VB/CommonConversions.cs similarity index 99% rename from ICSharpCode.CodeConverter/VB/CommonConversions.cs rename to CodeConverter/VB/CommonConversions.cs index dd8ca03b4..b3189e993 100644 --- a/ICSharpCode.CodeConverter/VB/CommonConversions.cs +++ b/CodeConverter/VB/CommonConversions.cs @@ -1,8 +1,6 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text; using ICSharpCode.CodeConverter.Shared; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; @@ -11,9 +9,7 @@ using Microsoft.CodeAnalysis.VisualBasic; using Microsoft.CodeAnalysis.VisualBasic.Syntax; using AttributeListSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.AttributeListSyntax; -using BinaryExpressionSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.BinaryExpressionSyntax; using CSharpExtensions = Microsoft.CodeAnalysis.CSharp.CSharpExtensions; -using CSSyntax = Microsoft.CodeAnalysis.CSharp.Syntax; using CSSyntaxKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind; using CS = Microsoft.CodeAnalysis.CSharp; using CSS = Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/ICSharpCode.CodeConverter/VB/ConversionExtensions.cs b/CodeConverter/VB/ConversionExtensions.cs similarity index 100% rename from ICSharpCode.CodeConverter/VB/ConversionExtensions.cs rename to CodeConverter/VB/ConversionExtensions.cs diff --git a/ICSharpCode.CodeConverter/VB/CsExpander.cs b/CodeConverter/VB/CsExpander.cs similarity index 96% rename from ICSharpCode.CodeConverter/VB/CsExpander.cs rename to CodeConverter/VB/CsExpander.cs index c1a28b02c..466667f07 100644 --- a/ICSharpCode.CodeConverter/VB/CsExpander.cs +++ b/CodeConverter/VB/CsExpander.cs @@ -6,7 +6,6 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Simplification; -using QualifiedNameSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.QualifiedNameSyntax; namespace ICSharpCode.CodeConverter.VB { diff --git a/ICSharpCode.CodeConverter/VB/MethodBodyExecutableStatementVisitor.cs b/CodeConverter/VB/MethodBodyExecutableStatementVisitor.cs similarity index 99% rename from ICSharpCode.CodeConverter/VB/MethodBodyExecutableStatementVisitor.cs rename to CodeConverter/VB/MethodBodyExecutableStatementVisitor.cs index 18a6f44f7..271f8910f 100644 --- a/ICSharpCode.CodeConverter/VB/MethodBodyExecutableStatementVisitor.cs +++ b/CodeConverter/VB/MethodBodyExecutableStatementVisitor.cs @@ -1,15 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.ExceptionServices; -using ICSharpCode.CodeConverter.CSharp; using ICSharpCode.CodeConverter.Shared; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.VisualBasic; using Microsoft.CodeAnalysis.VisualBasic.Syntax; -using ArgumentSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.ArgumentSyntax; using CS = Microsoft.CodeAnalysis.CSharp; using CSS = Microsoft.CodeAnalysis.CSharp.Syntax; using ExpressionSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.ExpressionSyntax; @@ -22,7 +18,6 @@ using TypeSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.TypeSyntax; using UsingStatementSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.UsingStatementSyntax; using VariableDeclaratorSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.VariableDeclaratorSyntax; -using static ICSharpCode.CodeConverter.VB.SyntaxKindExtensions; namespace ICSharpCode.CodeConverter.VB { diff --git a/ICSharpCode.CodeConverter/VB/NodesVisitor.cs b/CodeConverter/VB/NodesVisitor.cs similarity index 99% rename from ICSharpCode.CodeConverter/VB/NodesVisitor.cs rename to CodeConverter/VB/NodesVisitor.cs index 544987e71..4b7bcbd01 100644 --- a/ICSharpCode.CodeConverter/VB/NodesVisitor.cs +++ b/CodeConverter/VB/NodesVisitor.cs @@ -1,13 +1,10 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; -using ICSharpCode.CodeConverter.CSharp; using ICSharpCode.CodeConverter.Shared; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.VisualBasic; using Microsoft.CodeAnalysis.VisualBasic.Syntax; using ArgumentListSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.ArgumentListSyntax; @@ -35,11 +32,6 @@ using TypeParameterListSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.TypeParameterListSyntax; using TypeParameterSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.TypeParameterSyntax; using TypeSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.TypeSyntax; -using VisualBasicExtensions = Microsoft.CodeAnalysis.VisualBasic.VisualBasicExtensions; -using static ICSharpCode.CodeConverter.VB.SyntaxKindExtensions; -using SyntaxNodeExtensions = ICSharpCode.CodeConverter.Util.SyntaxNodeExtensions; -using Microsoft.VisualBasic; -using System.Collections; namespace ICSharpCode.CodeConverter.VB { diff --git a/ICSharpCode.CodeConverter/VB/SemanticModelSymbolSetExtensions.cs b/CodeConverter/VB/SemanticModelSymbolSetExtensions.cs similarity index 100% rename from ICSharpCode.CodeConverter/VB/SemanticModelSymbolSetExtensions.cs rename to CodeConverter/VB/SemanticModelSymbolSetExtensions.cs diff --git a/ICSharpCode.CodeConverter/VB/SyntaxKindExtensions.cs b/CodeConverter/VB/SyntaxKindExtensions.cs similarity index 100% rename from ICSharpCode.CodeConverter/VB/SyntaxKindExtensions.cs rename to CodeConverter/VB/SyntaxKindExtensions.cs diff --git a/ICSharpCode.CodeConverter/VB/SyntaxNodeVisitorExtensions.cs b/CodeConverter/VB/SyntaxNodeVisitorExtensions.cs similarity index 51% rename from ICSharpCode.CodeConverter/VB/SyntaxNodeVisitorExtensions.cs rename to CodeConverter/VB/SyntaxNodeVisitorExtensions.cs index 4cae31ce4..ab08c1a57 100644 --- a/ICSharpCode.CodeConverter/VB/SyntaxNodeVisitorExtensions.cs +++ b/CodeConverter/VB/SyntaxNodeVisitorExtensions.cs @@ -1,15 +1,5 @@ -using System; -using ICSharpCode.CodeConverter.CSharp; -using ICSharpCode.CodeConverter.Shared; -using ICSharpCode.CodeConverter.Util; -using Microsoft.CodeAnalysis; -using CS = Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis; using VBasic = Microsoft.CodeAnalysis.VisualBasic; -using CSSyntax = Microsoft.CodeAnalysis.CSharp.Syntax; -using SyntaxNodeExtensions = ICSharpCode.CodeConverter.Util.SyntaxNodeExtensions; -using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; -using System.Linq; -using System.Collections; namespace ICSharpCode.CodeConverter.VB { diff --git a/ICSharpCode.CodeConverter/VB/Trivia/RecursiveTriviaConverter.cs b/CodeConverter/VB/Trivia/RecursiveTriviaConverter.cs similarity index 99% rename from ICSharpCode.CodeConverter/VB/Trivia/RecursiveTriviaConverter.cs rename to CodeConverter/VB/Trivia/RecursiveTriviaConverter.cs index cb6f5d4a4..89a8fe411 100644 --- a/ICSharpCode.CodeConverter/VB/Trivia/RecursiveTriviaConverter.cs +++ b/CodeConverter/VB/Trivia/RecursiveTriviaConverter.cs @@ -9,7 +9,6 @@ using CSS = Microsoft.CodeAnalysis.CSharp.Syntax; using VB = Microsoft.CodeAnalysis.VisualBasic; using VBFactory = Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory; -using VisualBasicSyntaxFactory = Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory; using VBS = Microsoft.CodeAnalysis.VisualBasic.Syntax; using Microsoft.VisualBasic.CompilerServices; using CSharpToVBCodeConverter.DestVisualBasic; diff --git a/ICSharpCode.CodeConverter/VB/Trivia/SyntaxTriviaExtensions.cs b/CodeConverter/VB/Trivia/SyntaxTriviaExtensions.cs similarity index 87% rename from ICSharpCode.CodeConverter/VB/Trivia/SyntaxTriviaExtensions.cs rename to CodeConverter/VB/Trivia/SyntaxTriviaExtensions.cs index 83d7818f1..b72526cfb 100644 --- a/ICSharpCode.CodeConverter/VB/Trivia/SyntaxTriviaExtensions.cs +++ b/CodeConverter/VB/Trivia/SyntaxTriviaExtensions.cs @@ -1,13 +1,7 @@ -using Microsoft.VisualBasic; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; +using System.Collections.Generic; using Microsoft.CodeAnalysis; using CS = Microsoft.CodeAnalysis.CSharp; using VB = Microsoft.CodeAnalysis.VisualBasic; -using VBFactory = Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory; -using VBS = Microsoft.CodeAnalysis.VisualBasic.Syntax; using ICSharpCode.CodeConverter.Util; namespace CSharpToVBCodeConverter.Util diff --git a/ICSharpCode.CodeConverter/VB/Trivia/TriviaListSupport.cs b/CodeConverter/VB/Trivia/TriviaListSupport.cs similarity index 95% rename from ICSharpCode.CodeConverter/VB/Trivia/TriviaListSupport.cs rename to CodeConverter/VB/Trivia/TriviaListSupport.cs index f2ca7eea2..663f06d39 100644 --- a/ICSharpCode.CodeConverter/VB/Trivia/TriviaListSupport.cs +++ b/CodeConverter/VB/Trivia/TriviaListSupport.cs @@ -1,13 +1,9 @@ using Microsoft.VisualBasic; -using System; using System.Collections.Generic; -using System.Diagnostics; using Microsoft.CodeAnalysis; using CS = Microsoft.CodeAnalysis.CSharp; -using VB = Microsoft.CodeAnalysis.VisualBasic; using VBFactory = Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory; using ICSharpCode.CodeConverter.Util; -using CSharpToVBCodeConverter.Util; namespace CSharpToVBCodeConverter.DestVisualBasic { diff --git a/ICSharpCode.CodeConverter/VB/Trivia/VisualBasicSyntaxFactory.cs b/CodeConverter/VB/Trivia/VisualBasicSyntaxFactory.cs similarity index 100% rename from ICSharpCode.CodeConverter/VB/Trivia/VisualBasicSyntaxFactory.cs rename to CodeConverter/VB/Trivia/VisualBasicSyntaxFactory.cs diff --git a/ICSharpCode.CodeConverter/VB/Trivia/XMLVisitor.cs b/CodeConverter/VB/Trivia/XMLVisitor.cs similarity index 98% rename from ICSharpCode.CodeConverter/VB/Trivia/XMLVisitor.cs rename to CodeConverter/VB/Trivia/XMLVisitor.cs index 0bfcea2c2..63f25129a 100644 --- a/ICSharpCode.CodeConverter/VB/Trivia/XMLVisitor.cs +++ b/CodeConverter/VB/Trivia/XMLVisitor.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using CodeConverter.Util; using ICSharpCode.CodeConverter.Util; using Microsoft.CodeAnalysis; @@ -305,11 +303,11 @@ public override VB.VisualBasicSyntaxNode VisitXmlElement(CSS.XmlElementSyntax no EndTag = EndTag.WithoutLeadingTrivia(); } } - catch (OperationCanceledException ex) + catch (OperationCanceledException) { throw; } - catch (Exception ex) + catch (Exception) { } @@ -336,11 +334,11 @@ public override VB.VisualBasicSyntaxNode VisitXmlEmptyElement(CSS.XmlEmptyElemen var ListOfAttributes = GatherAttributes(node.Attributes); return VBFactory.XmlEmptyElement(Name, ListOfAttributes); } - catch (OperationCanceledException ex) + catch (OperationCanceledException) { throw; } - catch (Exception ex) + catch (Exception) { return VBFactory.XmlText(node.GetText().ToString()); } diff --git a/ICSharpCode.CodeConverter/AssemblyInfo.cs b/ICSharpCode.CodeConverter/AssemblyInfo.cs deleted file mode 100644 index b4a29171f..000000000 --- a/ICSharpCode.CodeConverter/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("CodeConverter.Tests")] \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/Shared/AsyncEnumerableTaskExtensions.cs b/ICSharpCode.CodeConverter/Shared/AsyncEnumerableTaskExtensions.cs deleted file mode 100644 index 029531a25..000000000 --- a/ICSharpCode.CodeConverter/Shared/AsyncEnumerableTaskExtensions.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace ICSharpCode.CodeConverter.Shared -{ - internal static class AsyncEnumerableTaskExtensions - { - public static IEnumerable AsEnumerable(this IEnumerator enumerator) - { - using (enumerator) { - while (enumerator.MoveNext()) { - yield return enumerator.Current; - } - } - } - - public static async Task SelectManyAsync(this IEnumerable nodes, - Func>> selector) - { - var selectAsync = await nodes.SelectAsync(selector); - return selectAsync.SelectMany(x => x).ToArray(); - } - - public static async Task ParallelSelectAsync(this IEnumerable source, - Func> selector, byte maxDop) - { - if (maxDop <= 0) { - throw new ArgumentOutOfRangeException(nameof(maxDop), maxDop, null); - } - if (maxDop == 1) { - return await SelectAsync(source, selector); - } - - var partitionTasks = Partitioner.Create(source).GetPartitions(maxDop) - .AsParallel().Select(partition => partition.AsEnumerable().SelectAsync(selector)); - var partionedResults = await Task.WhenAll(partitionTasks); - return partionedResults.SelectMany(x => x).ToArray(); - } - - public static async Task SelectAsync(this IEnumerable nodes, - Func> selector) - { - var nodesWithOrders = nodes.Select((input, originalOrder) => (input, originalOrder)); - return await nodesWithOrders.SelectAsync(nwo => selector(nwo.input, nwo.originalOrder)); - } - - public static async Task SelectAsync(this IEnumerable source, - Func> selector) - { - var partitionResults = new List(); - foreach (var partitionMember in source) { - var result = await selector(partitionMember); - partitionResults.Add(result); - } - - return partitionResults.ToArray(); - } - } -} \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/Shared/ProjectConversion.cs b/ICSharpCode.CodeConverter/Shared/ProjectConversion.cs deleted file mode 100644 index b06198de0..000000000 --- a/ICSharpCode.CodeConverter/Shared/ProjectConversion.cs +++ /dev/null @@ -1,319 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using ICSharpCode.CodeConverter.CSharp; -using ICSharpCode.CodeConverter.Util; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Text; - -namespace ICSharpCode.CodeConverter.Shared -{ - public class ProjectConversion - { - private readonly IReadOnlyCollection _documentsToConvert; - private readonly ILanguageConversion _languageConversion; - private readonly bool _showCompilationErrors = -#if DEBUG && ShowCompilationErrors - true; -#else - false; -#endif - private readonly bool _returnSelectedNode; - private static readonly string[] BannedPaths = new[] { ".AssemblyAttributes.", "\\bin\\", "\\obj\\" }; - private readonly IProjectContentsConverter _projectContentsConverter; - - private ProjectConversion(IProjectContentsConverter projectContentsConverter, IEnumerable documentsToConvert, - ILanguageConversion languageConversion, bool returnSelectedNode = false) - { - _projectContentsConverter = projectContentsConverter; - _languageConversion = languageConversion; - _documentsToConvert = documentsToConvert.ToList(); - _returnSelectedNode = returnSelectedNode; - } - - public static async Task ConvertText(string text, TextConversionOptions conversionOptions, IProgress progress = null) where TLanguageConversion : ILanguageConversion, new() - { - progress = progress ?? new Progress(); - return await StopRoslynCrashingAsync(async () => { - var languageConversion = new TLanguageConversion { ConversionOptions = conversionOptions }; - var syntaxTree = languageConversion.MakeFullCompilationUnit(text, out var textSpan); - if (textSpan.HasValue) conversionOptions.SelectedTextSpan = textSpan.Value; - using (var workspace = new AdhocWorkspace()) { - var document = languageConversion.CreateProjectDocumentFromTree(workspace, syntaxTree, conversionOptions.References); - return await ConvertSingle(document, conversionOptions, progress); - } - }, progress); - } - - public static async Task ConvertSingle(Document document, SingleConversionOptions conversionOptions, IProgress progress = null) where TLanguageConversion : ILanguageConversion, new() - { - progress = progress ?? new Progress(); - return await StopRoslynCrashingAsync(async () => { - - var languageConversion = new TLanguageConversion { ConversionOptions = conversionOptions }; - - bool returnSelectedNode = conversionOptions.SelectedTextSpan.Length > 0; - if (returnSelectedNode) { - document = await WithAnnotatedSelection(document, conversionOptions.SelectedTextSpan); - } - - var projectContentsConverter = await languageConversion.CreateProjectContentsConverter(document.Project); - - document = projectContentsConverter.Project.GetDocument(document.Id); - - var conversion = new ProjectConversion(projectContentsConverter, new[] { document }, languageConversion, returnSelectedNode); - var conversionResults = (await ConvertProjectContents(conversion, progress ?? new Progress())).ToList(); - var codeResult = conversionResults.SingleOrDefault(x => !string.IsNullOrWhiteSpace(x.ConvertedCode)) - ?? conversionResults.First(); - codeResult.Exceptions = conversionResults.SelectMany(x => x.Exceptions).ToArray(); - return codeResult; - }, progress); - } - - public static async Task> ConvertProject(Project project, - ILanguageConversion languageConversion, IProgress progress, - params (string Find, string Replace, bool FirstOnly)[] replacements) - { - progress = progress ?? new Progress(); - return await StopRoslynCrashingAsync(async () => { - - var sourceFilePathsWithoutExtension = project.Documents.Select(f => f.FilePath).ToImmutableHashSet(); - var projectContentsConverter = await languageConversion.CreateProjectContentsConverter(project); - project = projectContentsConverter.Project; - var convertProjectContents = (await ConvertProjectContents(projectContentsConverter, languageConversion, progress)).ToArray(); - var projectPath = Path.GetFullPath(project.GetDirectoryPath()); - string[] relativeFilePathsToAdd = - convertProjectContents.Select(r => r.SourcePathOrNull).Where(p => !sourceFilePathsWithoutExtension.Contains(p)) - .Select(p => PathConverter.TogglePathExtension(Path.GetFullPath(p)).Replace(projectPath + "\\", "")) - .OrderBy(x => x).ToArray(); - - var replacementSpecs = replacements.Concat(new[] { - AddCompiledItemsRegexFromRelativePaths(relativeFilePathsToAdd), - ChangeRootNamespaceRegex(projectContentsConverter.RootNamespace), - ChangeLanguageVersionRegex(projectContentsConverter.LanguageVersion) - }).ToArray(); - - return convertProjectContents.Concat(new[] - {ConvertProjectFile(project, languageConversion, replacementSpecs)} - ); - }, progress); - } - - public static ConversionResult ConvertProjectFile(Project project, - ILanguageConversion languageConversion, - params (string Find, string Replace, bool FirstOnly)[] textReplacements) - { - return new FileInfo(project.FilePath).ConversionResultFromReplacements(textReplacements, - languageConversion.PostTransformProjectFile); - } - - private static (string Find, string Replace, bool FirstOnly) ChangeLanguageVersionRegex(string languageVersion) { - return (Find: new Regex(@"<\s*LangVersion>(\d|\D)*").ToString(), Replace: $"{languageVersion}", FirstOnly: true); - } - - private static (string Find, string Replace, bool FirstOnly) ChangeRootNamespaceRegex(string rootNamespace) { - return (Find: new Regex(@"<\s*RootNamespace>(\d|\D)*").ToString(), Replace: $"{rootNamespace}", FirstOnly: true); - } - - private static (string Find, string Replace, bool FirstOnly) AddCompiledItemsRegexFromRelativePaths( - string[] relativeFilePathsToAdd) - { - var addFilesRegex = new Regex(@"(\s*<\s*Compile\s*Include\s*=\s*"".*\.(vb|cs)"")"); - var addedFiles = string.Join("", - relativeFilePathsToAdd.Select(f => $@"{Environment.NewLine} ")); - var addFilesRegexSpec = (Find: addFilesRegex.ToString(), Replace: addedFiles + @"$1", FirstOnly: true); - return addFilesRegexSpec; - } - - - private static async Task> ConvertProjectContents( - IProjectContentsConverter projectContentsConverter, ILanguageConversion languageConversion, - IProgress progress) - { - var documentsToConvert = projectContentsConverter.Project.Documents.Where(d => !BannedPaths.Any(d.FilePath.Contains)); - var projectConversion = new ProjectConversion(projectContentsConverter, documentsToConvert, languageConversion); - - return await ConvertProjectContents(projectConversion, progress); - } - - private static async Task> ConvertProjectContents( - ProjectConversion projectConversion, IProgress progress) - { - var pathNodePairs = await projectConversion.Convert(progress); - var results = pathNodePairs.Select(pathNodePair => new ConversionResult(pathNodePair.Node?.ToFullString()) { SourcePathOrNull = pathNodePair.Path, Exceptions = pathNodePair.Errors.ToList() }); - - var warnings = await projectConversion.GetProjectWarnings(projectConversion._projectContentsConverter.Project, pathNodePairs); - if (warnings != null) { - string projectDir = projectConversion._projectContentsConverter.Project.GetDirectoryPath() - ?? projectConversion._projectContentsConverter.Project.AssemblyName; - var warningPath = Path.Combine(projectDir, "ConversionWarnings.txt"); - results = results.Concat(new[] { new ConversionResult { SourcePathOrNull = warningPath, Exceptions = new[] { warnings } } }); - } - - return results; - } - - private async Task<(string Path, SyntaxNode Node, string[] Errors)[]> Convert( - IProgress progress) - { - var firstPassResults = await ExecutePhase(_documentsToConvert, FirstPass, progress, "Phase 1 of 2:"); - var (proj1, docs1) = await _projectContentsConverter.GetConvertedProject(firstPassResults); - return await ExecutePhase(proj1.GetDocuments(docs1), SecondPass, progress, "Phase 2 of 2:"); - } - - private async static Task StopRoslynCrashingAsync(Func> func, IProgress progress) { - await new SynchronizationContextRemover(); - - var FirstHandlerContainingType = (typeof(Compilation).GetTypeInfo().Assembly, "Microsoft.CodeAnalysis.FatalError"); - var SecondHandlerContainingType = (typeof(WorkspaceDiagnostic).GetTypeInfo().Assembly, "Microsoft.CodeAnalysis.ErrorReporting.FatalError"); - - var codeAnalysisErrorHandler = ExchangeFatalErrorHandler(LogError, FirstHandlerContainingType); - var codeAnalysisErrorReportingErrorHandler = ExchangeFatalErrorHandler(LogError, SecondHandlerContainingType); - try { - return await func(); - } finally { - ExchangeFatalErrorHandler(codeAnalysisErrorHandler, FirstHandlerContainingType); - ExchangeFatalErrorHandler(codeAnalysisErrorReportingErrorHandler, SecondHandlerContainingType); - } - - void LogError(object e) => progress.Report(new ConversionProgress($"https://github.com/dotnet/roslyn threw an exception: {e}")); - } - - /// - /// Use this to stop the library exiting the process without telling us. - /// https://github.com/dotnet/roslyn/issues/41724 - /// - /// - /// The simplification code in particular is quite buggy, scattered with "throw ExceptionUtilities.Unreachable" with no particular reasoning for why the code wouldn't be reachable. - /// It then uses FatalError.ReportUnlessCanceled rather than FatalError.ReportWithoutCrashUnlessCanceled causing fatal crashes with Environment.FailFast - /// While this presumably allows them to get good low-level debugging info from the windows error reports caused, it just means that people come to this project complaining about VS crashes. - /// See https://github.com/icsharpcode/CodeConverter/issues/521 and https://github.com/icsharpcode/CodeConverter/issues/484 - /// There are other ways to find these bugs - just run the expander/reducer on a couple of whole open source projects and the bugs will pile up. - /// - private static Action ExchangeFatalErrorHandler(Action errorHandler, (Assembly assembly, string containingType) container) - { - if (errorHandler == null) return null; - try { - var fataErrorType = container.assembly.GetType(container.containingType); - var fatalHandlerField = fataErrorType.GetField("s_fatalHandler"); - var originalHandler = (Action)fatalHandlerField.GetValue(null); - if (originalHandler != null) { - fatalHandlerField.SetValue(null, errorHandler); - } - return originalHandler; - } catch (Exception) { - return null; - } - } - - private async Task<(string Path, SyntaxNode Node, string[] Errors)[]> ExecutePhase(IEnumerable parameters, Func, Task<(string treeFilePath, SyntaxNode convertedDoc, - string[] errors)>> executePass, IProgress progress, string phaseTitle) - { - progress.Report(new ConversionProgress(phaseTitle)); - var strProgress = new Progress(m => progress.Report(new ConversionProgress(m, 1))); - return await parameters.ParallelSelectAsync(d => executePass(d, strProgress), Env.MaxDop); - } - - private async Task<(string Path, SyntaxNode Node, string[] Errors)> SecondPass((string Path, Document document, string[] Errors) firstPassResult, IProgress progress) - { - if (firstPassResult.document != null) { - progress.Report(firstPassResult.Path); - var (convertedNode, errors) = await SingleSecondPassHandled(firstPassResult.document); - return (firstPassResult.Path, convertedNode, firstPassResult.Errors.Concat(errors).Union(GetErrorsFromAnnotations(convertedNode)).ToArray()); - } - - return (firstPassResult.Path, null, firstPassResult.Errors); - } - - private async Task<(SyntaxNode convertedDoc, string[] errors)> SingleSecondPassHandled(Document convertedDocument) - { - SyntaxNode selectedNode = null; - string[] errors = Array.Empty(); - try { - Document document = await _languageConversion.SingleSecondPass(convertedDocument); - if (_returnSelectedNode) { - selectedNode = await GetSelectedNode(document); - var extraLeadingTrivia = selectedNode.GetFirstToken().GetPreviousToken().TrailingTrivia; - var extraTrailingTrivia = selectedNode.GetLastToken().GetNextToken().LeadingTrivia; - selectedNode = Formatter.Format(selectedNode, document.Project.Solution.Workspace); - if (extraLeadingTrivia.Any(t => !t.IsWhitespaceOrEndOfLine())) selectedNode = selectedNode.WithPrependedLeadingTrivia(extraLeadingTrivia); - if (extraTrailingTrivia.Any(t => !t.IsWhitespaceOrEndOfLine())) selectedNode = selectedNode.WithAppendedTrailingTrivia(extraTrailingTrivia); - } else { - selectedNode = await document.GetSyntaxRootAsync(); - selectedNode = Formatter.Format(selectedNode, document.Project.Solution.Workspace); - var convertedDoc = document.WithSyntaxRoot(selectedNode); - selectedNode = await convertedDoc.GetSyntaxRootAsync(); - } - } catch (Exception e) { - errors = new[] {e.ToString()}; - } - - var convertedNode = selectedNode ?? await convertedDocument.GetSyntaxRootAsync(); - return (convertedNode, errors); - } - - private async Task GetProjectWarnings(Project source, (string Path, SyntaxNode Node, string[] Errors)[] converted) - { - if (!_showCompilationErrors) return null; - - var sourceCompilation = await source.GetCompilationAsync(); - var convertedCompilation = await (await _projectContentsConverter.GetConvertedProject(converted)).project.GetCompilationAsync(); - return CompilationWarnings.WarningsForCompilation(sourceCompilation, "source") + CompilationWarnings.WarningsForCompilation(convertedCompilation, "target"); - } - - private async Task<(string treeFilePath, SyntaxNode convertedDoc, string[] errors)> FirstPass(Document document, IProgress progress) - { - var treeFilePath = document.FilePath ?? ""; - progress.Report(treeFilePath); - try { - var convertedNode = await _projectContentsConverter.SingleFirstPass(document); - string[] errors = GetErrorsFromAnnotations(convertedNode); - - return (treeFilePath, convertedNode, errors); - } catch (Exception e) - { - return (treeFilePath, null, new[]{e.ToString()}); - } - } - - private static string[] GetErrorsFromAnnotations(SyntaxNode convertedNode) - { - var errorAnnotations = convertedNode.GetAnnotations(AnnotationConstants.ConversionErrorAnnotationKind).ToList(); - string[] errors = errorAnnotations.Select(a => a.Data).ToArray(); - return errors; - } - - private static async Task WithAnnotatedSelection(Document document, TextSpan selected) - { - var root = await document.GetSyntaxRootAsync(); - var selectedNode = root.FindNode(selected); - var withAnnotatedSelection = await root.WithAnnotatedNode(selectedNode, AnnotationConstants.SelectedNodeAnnotationKind).GetRootAsync(); - return document.WithSyntaxRoot(withAnnotatedSelection); - } - - private async Task GetSelectedNode(Document document) - { - var resultNode = await document.GetSyntaxRootAsync(); - var selectedNode = resultNode.GetAnnotatedNodes(AnnotationConstants.SelectedNodeAnnotationKind) - .FirstOrDefault(); - if (selectedNode != null) - { - var children = _languageConversion.FindSingleImportantChild(selectedNode); - if (selectedNode.GetAnnotations(AnnotationConstants.SelectedNodeAnnotationKind) - .Any(n => n.Data == AnnotationConstants.AnnotatedNodeIsParentData) - && children.Count == 1) - { - selectedNode = children.Single(); - } - } - - return selectedNode ?? resultNode; - } - } -} \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/Util/AnnotationTable.cs b/ICSharpCode.CodeConverter/Util/AnnotationTable.cs deleted file mode 100644 index b60435e26..000000000 --- a/ICSharpCode.CodeConverter/Util/AnnotationTable.cs +++ /dev/null @@ -1,266 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using Microsoft.CodeAnalysis; - -namespace ICSharpCode.CodeConverter.Util -{ - /// - /// An AnnotationTable helps you attach your own annotation types/instances to syntax. - /// - /// It maintains a map between your instances and actual SyntaxAnnotation's used to annotate the nodes - /// and offers an API that matches the true annotation API on SyntaxNode. - /// - /// The table controls the lifetime of when you can find and retrieve your annotations. You won't be able to - /// find your annotations via HasAnnotations/GetAnnotations unless you use the same annotation table for these operations - /// that you used for the WithAdditionalAnnotations operation. - /// - /// Your custom annotations are not serialized with the syntax tree, so they won't move across boundaries unless the - /// same AnnotationTable is available on both ends. - /// - /// also, note that this table is not thread safe. - /// - internal class AnnotationTable where TAnnotation : class - { - private int _globalId = 0; - - private readonly Dictionary _realAnnotationMap = new Dictionary(); - private readonly Dictionary _annotationMap = new Dictionary(); - - private readonly string _annotationKind; - - public AnnotationTable(string annotationKind) - { - _annotationKind = annotationKind; - } - - private IEnumerable GetOrCreateRealAnnotations(TAnnotation[] annotations) - { - foreach (var annotation in annotations) { - yield return this.GetOrCreateRealAnnotation(annotation); - } - } - - private SyntaxAnnotation GetOrCreateRealAnnotation(TAnnotation annotation) - { - SyntaxAnnotation realAnnotation; - if (!_realAnnotationMap.TryGetValue(annotation, out realAnnotation)) { - var id = Interlocked.Increment(ref _globalId); - var idString = id.ToString(); - - realAnnotation = new SyntaxAnnotation(_annotationKind, idString); - _annotationMap.Add(idString, annotation); - _realAnnotationMap.Add(annotation, realAnnotation); - } - - return realAnnotation; - } - - private IEnumerable GetRealAnnotations(TAnnotation[] annotations) - { - foreach (var annotation in annotations) { - var realAnnotation = this.GetRealAnnotation(annotation); - if (realAnnotation != null) { - yield return realAnnotation; - } - } - } - - private SyntaxAnnotation GetRealAnnotation(TAnnotation annotation) - { - SyntaxAnnotation realAnnotation; - _realAnnotationMap.TryGetValue(annotation, out realAnnotation); - return realAnnotation; - } - - public TSyntaxNode WithAdditionalAnnotations(TSyntaxNode node, params TAnnotation[] annotations) where TSyntaxNode : SyntaxNode - { - return node.WithAdditionalAnnotations(this.GetOrCreateRealAnnotations(annotations).ToArray()); - } - - public SyntaxToken WithAdditionalAnnotations(SyntaxToken token, params TAnnotation[] annotations) - { - return token.WithAdditionalAnnotations(this.GetOrCreateRealAnnotations(annotations).ToArray()); - } - - public SyntaxTrivia WithAdditionalAnnotations(SyntaxTrivia trivia, params TAnnotation[] annotations) - { - return trivia.WithAdditionalAnnotations(this.GetOrCreateRealAnnotations(annotations).ToArray()); - } - - public SyntaxNodeOrToken WithAdditionalAnnotations(SyntaxNodeOrToken nodeOrToken, params TAnnotation[] annotations) - { - return nodeOrToken.WithAdditionalAnnotations(this.GetOrCreateRealAnnotations(annotations).ToArray()); - } - - public TSyntaxNode WithoutAnnotations(TSyntaxNode node, params TAnnotation[] annotations) where TSyntaxNode : SyntaxNode - { - return node.WithoutAnnotations(GetRealAnnotations(annotations).ToArray()); - } - - public SyntaxToken WithoutAnnotations(SyntaxToken token, params TAnnotation[] annotations) - { - return token.WithoutAnnotations(GetRealAnnotations(annotations).ToArray()); - } - - public SyntaxTrivia WithoutAnnotations(SyntaxTrivia trivia, params TAnnotation[] annotations) - { - return trivia.WithoutAnnotations(GetRealAnnotations(annotations).ToArray()); - } - - public SyntaxNodeOrToken WithoutAnnotations(SyntaxNodeOrToken nodeOrToken, params TAnnotation[] annotations) - { - return nodeOrToken.WithoutAnnotations(GetRealAnnotations(annotations).ToArray()); - } - - private IEnumerable GetAnnotations(IEnumerable realAnnotations) - { - foreach (var ra in realAnnotations) { - TAnnotation annotation; - if (_annotationMap.TryGetValue(ra.Data, out annotation)) { - yield return annotation; - } - } - } - - public IEnumerable GetAnnotations(SyntaxNode node) - { - return GetAnnotations(node.GetAnnotations(_annotationKind)); - } - - public IEnumerable GetAnnotations(SyntaxToken token) - { - return GetAnnotations(token.GetAnnotations(_annotationKind)); - } - - public IEnumerable GetAnnotations(SyntaxTrivia trivia) - { - return GetAnnotations(trivia.GetAnnotations(_annotationKind)); - } - - public IEnumerable GetAnnotations(SyntaxNodeOrToken nodeOrToken) - { - return GetAnnotations(nodeOrToken.GetAnnotations(_annotationKind)); - } - - public IEnumerable GetAnnotations(SyntaxNode node) where TSpecificAnnotation : TAnnotation - { - return this.GetAnnotations(node).OfType(); - } - - public IEnumerable GetAnnotations(SyntaxToken token) where TSpecificAnnotation : TAnnotation - { - return this.GetAnnotations(token).OfType(); - } - - public IEnumerable GetAnnotations(SyntaxTrivia trivia) where TSpecificAnnotation : TAnnotation - { - return this.GetAnnotations(trivia).OfType(); - } - - public IEnumerable GetAnnotations(SyntaxNodeOrToken nodeOrToken) where TSpecificAnnotation : TAnnotation - { - return this.GetAnnotations(nodeOrToken).OfType(); - } - - public bool HasAnnotations(SyntaxNode node) - { - return node.HasAnnotations(_annotationKind); - } - - public bool HasAnnotations(SyntaxToken token) - { - return token.HasAnnotations(_annotationKind); - } - - public bool HasAnnotations(SyntaxTrivia trivia) - { - return trivia.HasAnnotations(_annotationKind); - } - - public bool HasAnnotations(SyntaxNodeOrToken nodeOrToken) - { - return nodeOrToken.HasAnnotations(_annotationKind); - } - - public bool HasAnnotations(SyntaxNode node) where TSpecificAnnotation : TAnnotation - { - return this.GetAnnotations(node).OfType().Any(); - } - - public bool HasAnnotations(SyntaxToken token) where TSpecificAnnotation : TAnnotation - { - return this.GetAnnotations(token).OfType().Any(); - } - - public bool HasAnnotations(SyntaxTrivia trivia) where TSpecificAnnotation : TAnnotation - { - return this.GetAnnotations(trivia).OfType().Any(); - } - - public bool HasAnnotations(SyntaxNodeOrToken nodeOrToken) where TSpecificAnnotation : TAnnotation - { - return this.GetAnnotations(nodeOrToken).OfType().Any(); - } - - public bool HasAnnotation(SyntaxNode node, TAnnotation annotation) - { - return node.HasAnnotation(this.GetRealAnnotation(annotation)); - } - - public bool HasAnnotation(SyntaxToken token, TAnnotation annotation) - { - return token.HasAnnotation(this.GetRealAnnotation(annotation)); - } - - public bool HasAnnotation(SyntaxTrivia trivia, TAnnotation annotation) - { - return trivia.HasAnnotation(this.GetRealAnnotation(annotation)); - } - - public bool HasAnnotation(SyntaxNodeOrToken nodeOrToken, TAnnotation annotation) - { - return nodeOrToken.HasAnnotation(this.GetRealAnnotation(annotation)); - } - - public IEnumerable GetAnnotatedNodesAndTokens(SyntaxNode node) - { - return node.GetAnnotatedNodesAndTokens(_annotationKind); - } - - public IEnumerable GetAnnotatedNodes(SyntaxNode node) - { - return node.GetAnnotatedNodesAndTokens(_annotationKind).Where(nt => nt.IsNode).Select(nt => nt.AsNode()); - } - - public IEnumerable GetAnnotatedTokens(SyntaxNode node) - { - return node.GetAnnotatedNodesAndTokens(_annotationKind).Where(nt => nt.IsToken).Select(nt => nt.AsToken()); - } - - public IEnumerable GetAnnotatedTrivia(SyntaxNode node) - { - return node.GetAnnotatedTrivia(_annotationKind); - } - - public IEnumerable GetAnnotatedNodesAndTokens(SyntaxNode node) where TSpecificAnnotation : TAnnotation - { - return node.GetAnnotatedNodesAndTokens(_annotationKind).Where(nt => this.HasAnnotations(nt)); - } - - public IEnumerable GetAnnotatedNodes(SyntaxNode node) where TSpecificAnnotation : TAnnotation - { - return node.GetAnnotatedNodesAndTokens(_annotationKind).Where(nt => nt.IsNode && this.HasAnnotations(nt)).Select(nt => nt.AsNode()); - } - - public IEnumerable GetAnnotatedTokens(SyntaxNode node) where TSpecificAnnotation : TAnnotation - { - return node.GetAnnotatedNodesAndTokens(_annotationKind).Where(nt => nt.IsToken && this.HasAnnotations(nt)).Select(nt => nt.AsToken()); - } - - public IEnumerable GetAnnotatedTrivia(SyntaxNode node) where TSpecificAnnotation : TAnnotation - { - return node.GetAnnotatedTrivia(_annotationKind).Where(tr => this.HasAnnotations(tr)); - } - } -} diff --git a/ICSharpCode.CodeConverter/Util/EnumerableExtensions.cs b/ICSharpCode.CodeConverter/Util/EnumerableExtensions.cs deleted file mode 100644 index 23ae9b419..000000000 --- a/ICSharpCode.CodeConverter/Util/EnumerableExtensions.cs +++ /dev/null @@ -1,330 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Linq; - -namespace ICSharpCode.CodeConverter.Util -{ -#if NR6 - public -#endif - internal static partial class EnumerableExtensions - { - public static IEnumerable Do(this IEnumerable source, Action action) - { - if (source == null) { - throw new ArgumentNullException(nameof(source)); - } - - if (action == null) { - throw new ArgumentNullException(nameof(action)); - } - - // perf optimization. try to not use enumerator if possible - var list = source as IList; - if (list != null) { - for (int i = 0, count = list.Count; i < count; i++) { - action(list[i]); - } - } else { - foreach (var value in source) { - action(value); - } - } - - return source; - } - - public static IReadOnlyCollection ToReadOnlyCollection(this IEnumerable source) - { - if (source == null) { - throw new ArgumentNullException(nameof(source)); - } - - return new ReadOnlyCollection(source.ToList()); - } - - public static IEnumerable Concat(this IEnumerable source, T value) - { - if (source == null) { - throw new ArgumentNullException(nameof(source)); - } - - return source.ConcatWorker(value); - } - - private static IEnumerable ConcatWorker(this IEnumerable source, T value) - { - foreach (var v in source) { - yield return v; - } - - yield return value; - } - - public static bool SetEquals(this IEnumerable source1, IEnumerable source2, IEqualityComparer comparer) - { - if (source1 == null) { - throw new ArgumentNullException(nameof(source1)); - } - - if (source2 == null) { - throw new ArgumentNullException(nameof(source2)); - } - - return source1.ToSet(comparer).SetEquals(source2); - } - - public static bool SetEquals(this IEnumerable source1, IEnumerable source2) - { - if (source1 == null) { - throw new ArgumentNullException(nameof(source1)); - } - - if (source2 == null) { - throw new ArgumentNullException(nameof(source2)); - } - - return source1.ToSet().SetEquals(source2); - } - - public static ISet ToSet(this IEnumerable source, IEqualityComparer comparer) - { - if (source == null) { - throw new ArgumentNullException(nameof(source)); - } - - return new HashSet(source, comparer); - } - - public static ISet ToSet(this IEnumerable source) - { - if (source == null) { - throw new ArgumentNullException(nameof(source)); - } - - return source as ISet ?? new HashSet(source); - } - - public static T? FirstOrNullable(this IEnumerable source) - where T : struct - { - if (source == null) { - throw new ArgumentNullException(nameof(source)); - } - - return source.Cast().FirstOrDefault(); - } - - public static T? FirstOrNullable(this IEnumerable source, Func predicate) - where T : struct - { - if (source == null) { - throw new ArgumentNullException(nameof(source)); - } - - return source.Cast().FirstOrDefault(v => predicate(v.Value)); - } - - public static T? LastOrNullable(this IEnumerable source) - where T : struct - { - if (source == null) { - throw new ArgumentNullException(nameof(source)); - } - - return source.Cast().LastOrDefault(); - } - - public static bool IsSingle(this IEnumerable list) - { - using (var enumerator = list.GetEnumerator()) { - return enumerator.MoveNext() && !enumerator.MoveNext(); - } - } - - public static bool IsEmpty(this IEnumerable source) - { - var readOnlyCollection = source as IReadOnlyCollection; - if (readOnlyCollection != null) { - return readOnlyCollection.Count == 0; - } - - var genericCollection = source as ICollection; - if (genericCollection != null) { - return genericCollection.Count == 0; - } - - var collection = source as ICollection; - if (collection != null) { - return collection.Count == 0; - } - - var str = source as string; - if (str != null) { - return str.Length == 0; - } - - foreach (var t in source) { - return false; - } - - return true; - } - - public static bool IsEmpty(this IReadOnlyCollection source) - { - return source.Count == 0; - } - - public static bool IsEmpty(this ICollection source) - { - return source.Count == 0; - } - - public static bool IsEmpty(this string source) - { - return source.Length == 0; - } - - /// - /// This method is necessary to avoid an ambiguity between and . - /// - public static bool IsEmpty(this T[] source) - { - return source.Length == 0; - } - - /// - /// This method is necessary to avoid an ambiguity between and . - /// - public static bool IsEmpty(this List source) - { - return source.Count == 0; - } - - private static readonly Func s_notNullTest = x => x != null; - - public static IEnumerable WhereNotNull(this IEnumerable source) - where T : class - { - if (source == null) { - return SpecializedCollections.EmptyEnumerable(); - } - - return source.Where((Func)s_notNullTest); - } - - public static bool All(this IEnumerable source) - { - if (source == null) { - throw new ArgumentNullException(nameof(source)); - } - - foreach (var b in source) { - if (!b) { - return false; - } - } - - return true; - } - - public static IEnumerable Flatten(this IEnumerable> sequence) - { - if (sequence == null) { - throw new ArgumentNullException(nameof(sequence)); - } - - return sequence.SelectMany(s => s); - } - - public static IEnumerable Yield(this T singleElement) - { - yield return singleElement; - } - - public static IEnumerable OrderBy(this IEnumerable source, IComparer comparer) - { - return source.OrderBy(t => t, comparer); - } - - // public static IEnumerable OrderBy(this IEnumerable source, Comparison compare) - // { - // return source.OrderBy(new ComparisonComparer(compare)); - // } - - // public static IEnumerable Order(this IEnumerable source) where T : IComparable - // { - // return source.OrderBy((t1, t2) => t1.CompareTo(t2)); - // } - - public static bool IsSorted(this IEnumerable enumerable, IComparer comparer) - { - using (var e = enumerable.GetEnumerator()) { - if (!e.MoveNext()) { - return true; - } - - var previous = e.Current; - while (e.MoveNext()) { - if (comparer.Compare(previous, e.Current) > 0) { - return false; - } - - previous = e.Current; - } - - return true; - } - } - - public static bool SequenceEqual(this IEnumerable first, IEnumerable second, Func comparer) - { - Debug.Assert(comparer != null); - - if (first == second) { - return true; - } - - if (first == null || second == null) { - return false; - } - - using (var enumerator = first.GetEnumerator()) - using (var enumerator2 = second.GetEnumerator()) { - while (enumerator.MoveNext()) { - if (!enumerator2.MoveNext() || !comparer(enumerator.Current, enumerator2.Current)) { - return false; - } - } - - if (enumerator2.MoveNext()) { - return false; - } - } - - return true; - } - - public static bool Contains(this IEnumerable sequence, Func predicate) - { - return sequence.Any(predicate); - } - - public static int IndexOf(this IEnumerable sequence, Func predicate) - { - if (sequence == null) - throw new ArgumentNullException(nameof(sequence)); - int index = 0; - foreach (var item in sequence) { - if (predicate(item)) - return index; - index++; - } - return -1; - } - } -} diff --git a/ICSharpCode.CodeConverter/Util/Matcher.cs b/ICSharpCode.CodeConverter/Util/Matcher.cs deleted file mode 100644 index 046ed3a0b..000000000 --- a/ICSharpCode.CodeConverter/Util/Matcher.cs +++ /dev/null @@ -1,187 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace ICSharpCode.CodeConverter.Util -{ -#if NR6 - public -#endif - internal abstract class Matcher - { - // Tries to match this matcher against the provided sequence at the given index. If the - // match succeeds, 'true' is returned, and 'index' points to the location after the match - // ends. If the match fails, then false it returned and index remains the same. Note: the - // matcher does not need to consume to the end of the sequence to succeed. - public abstract bool TryMatch(IList sequence, ref int index); - - internal static Matcher Repeat(Matcher matcher) - { - return new RepeatMatcher(matcher); - } - - internal static Matcher OneOrMore(Matcher matcher) - { - // m+ is the same as (m m*) - return Sequence(matcher, Repeat(matcher)); - } - - internal static Matcher Choice(Matcher matcher1, Matcher matcher2) - { - return new ChoiceMatcher(matcher1, matcher2); - } - - internal static Matcher Sequence(params Matcher[] matchers) - { - return new SequenceMatcher(matchers); - } - - internal static Matcher Single(Func predicate, string description) - { - return new SingleMatcher(predicate, description); - } - private class ChoiceMatcher : Matcher - { - private readonly Matcher _matcher1; - private readonly Matcher _matcher2; - - public ChoiceMatcher(Matcher matcher1, Matcher matcher2) - { - _matcher1 = matcher1; - _matcher2 = matcher2; - } - - public override bool TryMatch(IList sequence, ref int index) - { - return - _matcher1.TryMatch(sequence, ref index) || - _matcher2.TryMatch(sequence, ref index); - } - - public override string ToString() - { - return string.Format("({0}|{1})", _matcher1, _matcher2); - } - } - private class RepeatMatcher : Matcher - { - private readonly Matcher _matcher; - - public RepeatMatcher(Matcher matcher) - { - _matcher = matcher; - } - - public override bool TryMatch(IList sequence, ref int index) - { - while (_matcher.TryMatch(sequence, ref index)) { - } - - return true; - } - - public override string ToString() - { - return string.Format("({0}*)", _matcher); - } - } - private class SequenceMatcher : Matcher - { - private readonly Matcher[] _matchers; - - public SequenceMatcher(params Matcher[] matchers) - { - _matchers = matchers; - } - - public override bool TryMatch(IList sequence, ref int index) - { - var currentIndex = index; - foreach (var matcher in _matchers) { - if (!matcher.TryMatch(sequence, ref currentIndex)) { - return false; - } - } - - index = currentIndex; - return true; - } - - public override string ToString() - { - return string.Format("({0})", string.Join(",", (object[])_matchers)); - } - } - private class SingleMatcher : Matcher - { - private readonly Func _predicate; - private readonly string _description; - - public SingleMatcher(Func predicate, string description) - { - _predicate = predicate; - _description = description; - } - - public override bool TryMatch(IList sequence, ref int index) - { - if (index < sequence.Count && _predicate(sequence[index])) { - index++; - return true; - } - - return false; - } - - public override string ToString() - { - return _description; - } - } - } - -#if NR6 - public -#endif - internal class Matcher - { - /// - /// Matcher equivalent to (m*) - /// - public static Matcher Repeat(Matcher matcher) - { - return Matcher.Repeat(matcher); - } - - /// - /// Matcher equivalent to (m+) - /// - public static Matcher OneOrMore(Matcher matcher) - { - return Matcher.OneOrMore(matcher); - } - - /// - /// Matcher equivalent to (m_1|m_2) - /// - public static Matcher Choice(Matcher matcher1, Matcher matcher2) - { - return Matcher.Choice(matcher1, matcher2); - } - - /// - /// Matcher equivalent to (m_1 ... m_n) - /// - public static Matcher Sequence(params Matcher[] matchers) - { - return Matcher.Sequence(matchers); - } - - /// - /// Matcher that matches an element if the provide predicate returns true. - /// - public static Matcher Single(Func predicate, string description) - { - return Matcher.Single(predicate, description); - } - } -} diff --git a/ICSharpCode.CodeConverter/Util/ReflectionCompatibilityExtensions.cs b/ICSharpCode.CodeConverter/Util/ReflectionCompatibilityExtensions.cs deleted file mode 100644 index 00fcec7fe..000000000 --- a/ICSharpCode.CodeConverter/Util/ReflectionCompatibilityExtensions.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace ICSharpCode.CodeConverter.Util -{ - [Flags] - internal enum BindingFlags - { - Default = 0, - Instance = 1, - Static = 2, - Public = 4, - NonPublic = 8, - } - - internal static class ReflectionCompatibilityExtensions - { - public static object[] GetCustomAttributes(this Type type, bool inherit) - { - return type.GetTypeInfo().GetCustomAttributes(inherit).ToArray(); - } - - public static MethodInfo GetMethod(this Type type, string name) - { - return type.GetTypeInfo().DeclaredMethods.FirstOrDefault(m => m.Name == name); - } - - public static MethodInfo GetMethod(this Type type, string name, BindingFlags bindingFlags) - { - return type.GetTypeInfo().DeclaredMethods.FirstOrDefault(m => (m.Name == name) && IsConformWithBindingFlags(m, bindingFlags)); - } - - public static MethodInfo GetMethod(this Type type, string name, Type[] types) - { - return type.GetTypeInfo().DeclaredMethods.FirstOrDefault( - m => (m.Name == name) && TypesAreEqual(m.GetParameters().Select(p => p.ParameterType).ToArray(), types)); - } - - public static MethodInfo GetMethod(this Type type, string name, BindingFlags bindingFlags, object binder, Type[] types, object modifiers) - { - return type.GetTypeInfo().DeclaredMethods.FirstOrDefault( - m => (m.Name == name) && IsConformWithBindingFlags(m, bindingFlags) && TypesAreEqual(m.GetParameters().Select(p => p.ParameterType).ToArray(), types)); - } - - public static IEnumerable GetMethods(this Type type) - { - return type.GetTypeInfo().DeclaredMethods; - } - - public static IEnumerable GetMethods(this Type type, string name) - { - return type.GetTypeInfo().DeclaredMethods.Where(m => m.Name == name); - } - - public static IEnumerable GetMethods(this Type type, BindingFlags bindingFlags) - { - return type.GetTypeInfo().DeclaredMethods.Where(m => IsConformWithBindingFlags(m, bindingFlags)); - } - - public static FieldInfo GetField(this Type type, string name) - { - return type.GetTypeInfo().DeclaredFields.FirstOrDefault(f => f.Name == name); - } - - public static FieldInfo GetField(this Type type, string name, BindingFlags bindingFlags) - { - return type.GetTypeInfo().DeclaredFields.FirstOrDefault(f => (f.Name == name) && IsConformWithBindingFlags(f, bindingFlags)); - } - - public static PropertyInfo GetProperty(this Type type, string name, BindingFlags bindingFlags) - { - return type.GetTypeInfo().DeclaredProperties.FirstOrDefault(p => p.Name == name); - } - - public static IEnumerable GetProperties(this Type type) - { - return type.GetTypeInfo().DeclaredProperties; - } - - public static string GetAssemblyLocation(this Type type) - { - var asm = type.GetTypeInfo().Assembly; - var locationProperty = asm.GetType().GetRuntimeProperties().Single(p => p.Name == "Location"); - return (string)locationProperty.GetValue(asm); - } - - private static bool TypesAreEqual(Type[] memberTypes, Type[] searchedTypes) - { - if (((memberTypes == null) || (searchedTypes == null)) && (memberTypes != searchedTypes)) - return false; - - if (memberTypes.Length != searchedTypes.Length) - return false; - - for (int i = 0; i < memberTypes.Length; i++) { - if (memberTypes[i] != searchedTypes[i]) - return false; - } - - return true; - } - - private static bool IsConformWithBindingFlags(MethodBase method, BindingFlags bindingFlags) - { - if (method.IsPublic && !bindingFlags.HasFlag(BindingFlags.Public)) - return false; - if (method.IsPrivate && !bindingFlags.HasFlag(BindingFlags.NonPublic)) - return false; - if (method.IsStatic && !bindingFlags.HasFlag(BindingFlags.Static)) - return false; - if (!method.IsStatic && !bindingFlags.HasFlag(BindingFlags.Instance)) - return false; - - return true; - } - - private static bool IsConformWithBindingFlags(FieldInfo method, BindingFlags bindingFlags) - { - if (method.IsPublic && !bindingFlags.HasFlag(BindingFlags.Public)) - return false; - if (method.IsPrivate && !bindingFlags.HasFlag(BindingFlags.NonPublic)) - return false; - if (method.IsStatic && !bindingFlags.HasFlag(BindingFlags.Static)) - return false; - if (!method.IsStatic && !bindingFlags.HasFlag(BindingFlags.Instance)) - return false; - - return true; - } - } -} diff --git a/ICSharpCode.CodeConverter/Util/StringExtensions.cs b/ICSharpCode.CodeConverter/Util/StringExtensions.cs deleted file mode 100644 index 5fc02237e..000000000 --- a/ICSharpCode.CodeConverter/Util/StringExtensions.cs +++ /dev/null @@ -1,483 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Text; -using System.Text.RegularExpressions; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Simplification; - -namespace ICSharpCode.CodeConverter.Util -{ -#if NR6 - public -#endif - internal static class StringExtensions - { - public static string ConsistentNewlines(this string str) - { - return str.Replace("\r\n", "\n").Replace("\n", "\r\n"); - } - - public static int? GetFirstNonWhitespaceOffset(this string line) - { - for (int i = 0; i < line.Length; i++) { - if (!Char.IsWhiteSpace(line[i])) { - return i; - } - } - - return null; - } - - public static string GetLeadingWhitespace(this string lineText) - { - var firstOffset = lineText.GetFirstNonWhitespaceOffset(); - - return firstOffset.HasValue - ? lineText.Substring(0, firstOffset.Value) - : lineText; - } - - public static int GetTextColumn(this string text, int tabSize, int initialColumn) - { - var lineText = text.GetLastLineText(); - if (text != lineText) { - return lineText.GetColumnFromLineOffset(lineText.Length, tabSize); - } - - return text.ConvertTabToSpace(tabSize, initialColumn, text.Length) + initialColumn; - } - - public static int ConvertTabToSpace(this string textSnippet, int tabSize, int initialColumn, int endPosition) - { - int column = initialColumn; - - // now this will calculate indentation regardless of actual content on the buffer except TAB - for (int i = 0; i < endPosition; i++) { - if (textSnippet[i] == '\t') { - column += tabSize - column % tabSize; - } else { - column++; - } - } - - return column - initialColumn; - } - - public static int IndexOf(this string text, Func predicate) - { - if (text == null) { - return -1; - } - - for (int i = 0; i < text.Length; i++) { - if (predicate(text[i])) { - return i; - } - } - - return -1; - } - - public static string GetFirstLineText(this string text) - { - var lineBreak = text.IndexOf('\n'); - if (lineBreak < 0) { - return text; - } - - return text.Substring(0, lineBreak + 1); - } - - public static string GetLastLineText(this string text) - { - var lineBreak = text.LastIndexOf('\n'); - if (lineBreak < 0) { - return text; - } - - return text.Substring(lineBreak + 1); - } - - public static bool ContainsLineBreak(this string text) - { - foreach (char ch in text) { - if (ch == '\n' || ch == '\r') { - return true; - } - } - - return false; - } - - public static int GetNumberOfLineBreaks(this string text) - { - int lineBreaks = 0; - for (int i = 0; i < text.Length; i++) { - if (text[i] == '\n') { - lineBreaks++; - } else if (text[i] == '\r') { - if (i + 1 == text.Length || text[i + 1] != '\n') { - lineBreaks++; - } - } - } - - return lineBreaks; - } - - public static bool ContainsTab(this string text) - { - // PERF: Tried replacing this with "text.IndexOf('\t')>=0", but that was actually slightly slower - foreach (char ch in text) { - if (ch == '\t') { - return true; - } - } - - return false; - } - - public static ImmutableArray ToSymbolDisplayParts(this string text) - { - return ImmutableArray.Create(new SymbolDisplayPart(SymbolDisplayPartKind.Text, null, text)); - } - - public static int GetColumnOfFirstNonWhitespaceCharacterOrEndOfLine(this string line, int tabSize) - { - var firstNonWhitespaceChar = line.GetFirstNonWhitespaceOffset(); - - if (firstNonWhitespaceChar.HasValue) { - return line.GetColumnFromLineOffset(firstNonWhitespaceChar.Value, tabSize); - } else { - // It's all whitespace, so go to the end - return line.GetColumnFromLineOffset(line.Length, tabSize); - } - } - - public static int GetColumnFromLineOffset(this string line, int endPosition, int tabSize) - { - return ConvertTabToSpace(line, tabSize, 0, endPosition); - } - - public static int GetLineOffsetFromColumn(this string line, int column, int tabSize) - { - var currentColumn = 0; - - for (int i = 0; i < line.Length; i++) { - if (currentColumn >= column) { - return i; - } - - if (line[i] == '\t') { - currentColumn += tabSize - (currentColumn % tabSize); - } else { - currentColumn++; - } - } - - // We're asking for a column past the end of the line, so just go to the end. - return line.Length; - } - - private static ImmutableArray s_lazyNumerals; - - internal static string GetNumeral(int number) - { - var numerals = s_lazyNumerals; - if (numerals.IsDefault) { - numerals = ImmutableArray.Create("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"); - ImmutableInterlocked.InterlockedInitialize(ref s_lazyNumerals, numerals); - } - - Debug.Assert(number >= 0); - return (number < numerals.Length) ? numerals[number] : number.ToString(); - } - - public static string Join(this IEnumerable source, string separator) - { - if (source == null) { - throw new ArgumentNullException("source"); - } - - if (separator == null) { - throw new ArgumentNullException("separator"); - } - - return String.Join(separator, source); - } - - public static bool LooksLikeInterfaceName(this string name) - { - return name.Length >= 3 && name[0] == 'I' && Char.IsUpper(name[1]) && Char.IsLower(name[2]); - } - - public static bool LooksLikeTypeParameterName(this string name) - { - return name.Length >= 3 && name[0] == 'T' && Char.IsUpper(name[1]) && Char.IsLower(name[2]); - } - - private static readonly Func s_toLower = Char.ToLower; - private static readonly Func s_toUpper = Char.ToUpper; - - public static string ToPascalCase( - this string shortName, - bool trimLeadingTypePrefix = true) - { - return ConvertCase(shortName, trimLeadingTypePrefix, s_toUpper); - } - - public static string ToCamelCase( - this string shortName, - bool trimLeadingTypePrefix = true) - { - return ConvertCase(shortName, trimLeadingTypePrefix, s_toLower); - } - - private static string ConvertCase( - this string shortName, - bool trimLeadingTypePrefix, - Func convert) - { - // Special case the common .net pattern of "IFoo" as a type name. In this case we - // want to generate "foo" as the parameter name. - if (!String.IsNullOrEmpty(shortName)) { - if (trimLeadingTypePrefix && (shortName.LooksLikeInterfaceName() || shortName.LooksLikeTypeParameterName())) { - return convert(shortName[1]) + shortName.Substring(2); - } - - if (convert(shortName[0]) != shortName[0]) { - return convert(shortName[0]) + shortName.Substring(1); - } - } - - return shortName; - } - - internal static bool IsValidClrTypeName(this string name) - { - return !String.IsNullOrEmpty(name) && name.IndexOf('\0') == -1; - } - - /// - /// Checks if the given name is a sequence of valid CLR names separated by a dot. - /// - internal static bool IsValidClrNamespaceName(this string name) - { - if (String.IsNullOrEmpty(name)) { - return false; - } - - char lastChar = '.'; - foreach (char c in name) { - if (c == '\0' || (c == '.' && lastChar == '.')) { - return false; - } - - lastChar = c; - } - - return lastChar != '.'; - } - - internal static string GetWithSingleAttributeSuffix( - this string name, - bool isCaseSensitive) - { - string cleaned = name; - while ((cleaned = GetWithoutAttributeSuffix(cleaned, isCaseSensitive)) != null) { - name = cleaned; - } - - return name + "Attribute"; - } - - internal static bool TryGetWithoutAttributeSuffix( - this string name, - out string result) - { - return TryGetWithoutAttributeSuffix(name, isCaseSensitive: true, result: out result); - } - - internal static string GetWithoutAttributeSuffix( - this string name, - bool isCaseSensitive) - { - string result; - return TryGetWithoutAttributeSuffix(name, isCaseSensitive, out result) ? result : null; - } - - internal static bool TryGetWithoutAttributeSuffix( - this string name, - bool isCaseSensitive, - out string result) - { - const string AttributeSuffix = "Attribute"; - var comparison = isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; - if (name.Length > AttributeSuffix.Length && name.EndsWith(AttributeSuffix, comparison)) { - result = name.Substring(0, name.Length - AttributeSuffix.Length); - return true; - } - - result = null; - return false; - } - - internal static bool IsValidUnicodeString(this string str) - { - int i = 0; - while (i < str.Length) { - char c = str[i++]; - - // (high surrogate, low surrogate) makes a valid pair, anything else is invalid: - if (Char.IsHighSurrogate(c)) { - if (i < str.Length && Char.IsLowSurrogate(str[i])) { - i++; - } else { - // high surrogate not followed by low surrogate - return false; - } - } else if (Char.IsLowSurrogate(c)) { - // previous character wasn't a high surrogate - return false; - } - } - - return true; - } - - /// - /// Remove one set of leading and trailing double quote characters, if both are present. - /// - internal static string Unquote(this string arg) - { - bool quoted; - return Unquote(arg, out quoted); - } - - internal static string Unquote(this string arg, out bool quoted) - { - if (arg.Length > 1 && arg[0] == '"' && arg[arg.Length - 1] == '"') { - quoted = true; - return arg.Substring(1, arg.Length - 2); - } else { - quoted = false; - return arg; - } - } - - internal static int IndexOfBalancedParenthesis(this string str, int openingOffset, char closing) - { - char opening = str[openingOffset]; - - int depth = 1; - for (int i = openingOffset + 1; i < str.Length; i++) { - var c = str[i]; - if (c == opening) { - depth++; - } else if (c == closing) { - depth--; - if (depth == 0) { - return i; - } - } - } - - return -1; - } - - // String isn't IEnumerable in the current Portable profile. - internal static char First(this string arg) - { - return arg[0]; - } - - // String isn't IEnumerable in the current Portable profile. - internal static char Last(this string arg) - { - return arg[arg.Length - 1]; - } - - // String isn't IEnumerable in the current Portable profile. - internal static bool All(this string arg, Predicate predicate) - { - foreach (char c in arg) { - if (!predicate(c)) { - return false; - } - } - - return true; - } - - public static string EscapeIdentifier( - this string identifier, - bool isQueryContext = false) - { - var nullIndex = identifier.IndexOf('\0'); - if (nullIndex >= 0) { - identifier = identifier.Substring(0, nullIndex); - } - - var needsEscaping = SyntaxFacts.GetKeywordKind(identifier) != SyntaxKind.None; - - // Check if we need to escape this contextual keyword - needsEscaping = needsEscaping || (isQueryContext && SyntaxFacts.IsQueryContextualKeyword(SyntaxFacts.GetContextualKeywordKind(identifier))); - - return needsEscaping ? "@" + identifier : identifier; - } - - public static SyntaxToken ToIdentifierToken( - this string identifier, - bool isQueryContext = false) - { - var escaped = identifier.EscapeIdentifier(isQueryContext); - - if (escaped.Length == 0 || escaped[0] != '@') { - return SyntaxFactory.Identifier(escaped); - } - - var unescaped = identifier.StartsWith("@", StringComparison.Ordinal) - ? identifier.Substring(1) - : identifier; - - var token = SyntaxFactory.Identifier( - default(SyntaxTriviaList), SyntaxKind.None, "@" + unescaped, unescaped, default(SyntaxTriviaList)); - - if (!identifier.StartsWith("@", StringComparison.Ordinal)) { - token = token.WithAdditionalAnnotations(Simplifier.Annotation); - } - - return token; - } - - public static IdentifierNameSyntax ToIdentifierName(this string identifier) - { - return SyntaxFactory.IdentifierName(identifier.ToIdentifierToken()); - } - - public static string ReplaceEnd(this string originalContainingReplacement, KeyValuePair replacement) - { - return originalContainingReplacement.Substring(0, originalContainingReplacement.Length - replacement.Key.Length) + replacement.Value; - } - - public static string WithHalfWidthLatinCharacters(this string str) - { - return str.Normalize(NormalizationForm.FormKD); - } - } - - /// - /// Copied from https://github.com/code-cracker/code-cracker/blob/330f7ad217d6aae17a07a0675f52bcaa9a03d956/src/Common/CodeCracker.Common/Extensions/GeneratedCodeAnalysisExtensions.cs - /// Apache-2.0 license - /// - public static class GeneratedCodeAnalysisExtensions - { - public static bool IsGeneratedFile(this string filePath) => - Regex.IsMatch(filePath, @"(\\service|\\TemporaryGeneratedFile_.*|\\assemblyinfo|\\assemblyattributes|\.(g\.i|g|designer|generated|assemblyattributes))\.(cs|vb)$", - RegexOptions.IgnoreCase); - } -} diff --git a/ICSharpCode.CodeConverter/Util/SyntaxNodeExtensions.cs b/ICSharpCode.CodeConverter/Util/SyntaxNodeExtensions.cs deleted file mode 100644 index f5f341373..000000000 --- a/ICSharpCode.CodeConverter/Util/SyntaxNodeExtensions.cs +++ /dev/null @@ -1,1659 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using ICSharpCode.CodeConverter.Shared; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using CS = Microsoft.CodeAnalysis.CSharp; -using CSSyntax = Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.VisualBasic; -using Microsoft.CodeAnalysis.VisualBasic.Syntax; -using VBasic = Microsoft.CodeAnalysis.VisualBasic; -using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; -using AnonymousObjectCreationExpressionSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.AnonymousObjectCreationExpressionSyntax; -using ArgumentListSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax; -using ArrayRankSpecifierSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.ArrayRankSpecifierSyntax; -using AttributeListSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax; -using CastExpressionSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.CastExpressionSyntax; -using CompilationUnitSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax; -using ConditionalAccessExpressionSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.ConditionalAccessExpressionSyntax; -using CSharpExtensions = Microsoft.CodeAnalysis.CSharp.CSharpExtensions; -using VBSyntaxFactory = Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory; -using VBSyntaxKind = Microsoft.CodeAnalysis.VisualBasic.SyntaxKind; -using CSSyntaxKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind; -using DoStatementSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.DoStatementSyntax; -using EmptyStatementSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.EmptyStatementSyntax; -using EnumMemberDeclarationSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.EnumMemberDeclarationSyntax; -using FieldDeclarationSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.FieldDeclarationSyntax; -using ForEachStatementSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.ForEachStatementSyntax; -using ForStatementSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.ForStatementSyntax; -using IfStatementSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.IfStatementSyntax; -using ParameterListSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax; -using ParenthesizedExpressionSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedExpressionSyntax; -using StatementSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax; -using SyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using SyntaxFacts = Microsoft.CodeAnalysis.CSharp.SyntaxFacts; -using TypeOfExpressionSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.TypeOfExpressionSyntax; -using TypeSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax; -using UsingStatementSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.UsingStatementSyntax; -using WhileStatementSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.WhileStatementSyntax; -using VBCommonConversions = ICSharpCode.CodeConverter.VB.CommonConversions; - -namespace ICSharpCode.CodeConverter.Util -{ - internal static class SyntaxNodeExtensions - { - - public static IEnumerable GetAncestors(this SyntaxNode node) - { - var current = node.Parent; - - while (current != null) { - yield return current; - - current = current is IStructuredTriviaSyntax - ? ((IStructuredTriviaSyntax)current).ParentTrivia.Token.Parent - : current.Parent; - } - } - - public static IEnumerable GetAncestors(this SyntaxNode node) - where TNode : SyntaxNode - { - var current = node.Parent; - while (current != null) { - if (current is TNode) { - yield return (TNode)current; - } - - current = current is IStructuredTriviaSyntax - ? ((IStructuredTriviaSyntax)current).ParentTrivia.Token.Parent - : current.Parent; - } - } - - public static TNode GetAncestor(this SyntaxNode node) - where TNode : SyntaxNode - { - if (node == null) { - return default(TNode); - } - - return node.GetAncestors().FirstOrDefault(); - } - - public static TNode GetAncestorOrThis(this SyntaxNode node) - where TNode : SyntaxNode - { - if (node == null) { - return default(TNode); - } - - return node.GetAncestorsOrThis().FirstOrDefault(); - } - - public static IEnumerable GetAncestorsOrThis(this SyntaxNode node) - where TNode : SyntaxNode - { - var current = node; - while (current != null) { - if (current is TNode) { - yield return (TNode)current; - } - - current = current is IStructuredTriviaSyntax - ? ((IStructuredTriviaSyntax)current).ParentTrivia.Token.Parent - : current.Parent; - } - } - - public static bool HasAncestor(this SyntaxNode node) - where TNode : SyntaxNode - { - return node.GetAncestors().Any(); - } - - public static bool CheckParent(this SyntaxNode node, Func valueChecker) where T : SyntaxNode - { - if (node == null) { - return false; - } - - var parentNode = node.Parent as T; - if (parentNode == null) { - return false; - } - - return valueChecker(parentNode); - } - - /// - /// Returns true if is a given token is a child token of of a certain type of parent node. - /// - /// The type of the parent node. - /// The node that we are testing. - /// A function that, when given the parent node, returns the child token we are interested in. - public static bool IsChildNode(this SyntaxNode node, Func childGetter) - where TParent : SyntaxNode - { - var ancestor = node.GetAncestor(); - if (ancestor == null) { - return false; - } - - var ancestorNode = childGetter(ancestor); - - return node == ancestorNode; - } - - /// - /// Returns true if this node is found underneath the specified child in the given parent. - /// - public static bool IsFoundUnder(this SyntaxNode node, Func childGetter) - where TParent : SyntaxNode - { - var ancestor = node.GetAncestor(); - if (ancestor == null) { - return false; - } - - var child = childGetter(ancestor); - - // See if node passes through child on the way up to ancestor. - return node.GetAncestorsOrThis().Contains(child); - } - - public static SyntaxNode GetCommonRoot(this SyntaxNode node1, SyntaxNode node2) - { - //Contract.ThrowIfTrue(node1.RawKind == 0 || node2.RawKind == 0); - - // find common starting node from two nodes. - // as long as two nodes belong to same tree, there must be at least one common root (Ex, compilation unit) - var ancestors = node1.GetAncestorsOrThis(); - var set = new HashSet(node2.GetAncestorsOrThis()); - - return ancestors.First(set.Contains); - } - - public static int Width(this SyntaxNode node) - { - return node.Span.Length; - } - - public static int FullWidth(this SyntaxNode node) - { - return node.FullSpan.Length; - } - - public static SyntaxNode FindInnermostCommonNode( - this IEnumerable nodes, - Func predicate) - { - IEnumerable blocks = null; - foreach (var node in nodes) { - blocks = blocks == null - ? node.AncestorsAndSelf().Where(predicate) - : blocks.Intersect(node.AncestorsAndSelf().Where(predicate)); - } - - return blocks == null ? null : blocks.First(); - } - - public static TSyntaxNode FindInnermostCommonNode(this IEnumerable nodes) - where TSyntaxNode : SyntaxNode - { - return (TSyntaxNode)nodes.FindInnermostCommonNode(n => n is TSyntaxNode); - } - - public static ISymbol GetEnclosingDeclaredTypeSymbol(this SyntaxNode node, SemanticModel semanticModel) - { - var typeBlockSyntax = (SyntaxNode)node.GetAncestor() - ?? node.GetAncestor(); - if (typeBlockSyntax == null) return null; - return semanticModel.GetDeclaredSymbol(typeBlockSyntax); - } - - public static SyntaxList WithVbSourceMappingFrom(this SyntaxList converted, SyntaxNode node) where T : CSharpSyntaxNode - { - if (converted.Count != 1) return WithSourceMappingFrom(converted, node); - var single = converted.Single(); - return converted.Replace(single, single.WithVbSourceMappingFrom(node)); - } - - public static SyntaxList WithCsSourceMappingFrom(this SyntaxList converted, SyntaxNode node) where T : VisualBasicSyntaxNode - { - if (converted.Count != 1) return WithSourceMappingFrom(converted, node); - var single = converted.Single(); - return converted.Replace(single, single.WithCsSourceMappingFrom(node)); - } - - private static SyntaxList WithSourceMappingFrom(this SyntaxList converted, SyntaxNode node) where T : SyntaxNode - { - if (!converted.Any()) return converted; - var origLinespan = node.SyntaxTree.GetLineSpan(node.Span); - var first = converted.First(); - converted = converted.Replace(first, node.CopyAnnotationsTo(first).WithSourceStartLineAnnotation(origLinespan)); - var last = converted.Last(); - return converted.Replace(last, last.WithSourceEndLineAnnotation(origLinespan)); - } - - public static T WithVbSourceMappingFrom(this T converted, SyntaxNodeOrToken fromSource) where T : CSharpSyntaxNode - { - if (converted == null) return null; - var lastCsConvertedToken = converted.GetLastToken(); - if (lastCsConvertedToken.IsKind(CSSyntaxKind.CloseBraceToken) && IsBlockParent(converted, lastCsConvertedToken) && fromSource.AsNode()?.ChildNodes().LastOrDefault() is EndBlockStatementSyntax lastVbSourceNode) { - converted = converted.ReplaceToken(lastCsConvertedToken, lastCsConvertedToken.WithSourceMappingFrom(lastVbSourceNode)); - } - return converted.WithSourceMappingFrom(fromSource); - } - - public static T WithCsSourceMappingFrom(this T converted, SyntaxNodeOrToken fromSource) where T : VisualBasicSyntaxNode - { - if (converted == null) return null; - var lastCsSourceToken = fromSource.AsNode()?.GetLastToken(); - if (lastCsSourceToken?.IsKind(CSSyntaxKind.CloseBraceToken) == true && IsBlockParent(fromSource.AsNode(), lastCsSourceToken.Value) && converted.ChildNodes().LastOrDefault() is EndBlockStatementSyntax lastVbConvertedNode) { - converted = converted.ReplaceNode(lastVbConvertedNode, lastVbConvertedNode.WithSourceMappingFrom(lastCsSourceToken.Value)); - } - return converted.WithSourceMappingFrom(fromSource); - } - - private static T WithSourceMappingFrom(this T converted, SyntaxNodeOrToken fromSource) where T : SyntaxNode - { - if (converted == null) return null; - var linespan = fromSource.SyntaxTree.GetLineSpan(fromSource.Span); - return converted.WithSourceStartLineAnnotation(linespan).WithSourceEndLineAnnotation(linespan); - } - - public static T WithSourceStartLineAnnotation(this T node, FileLinePositionSpan sourcePosition) where T : SyntaxNode - { - return node.WithAdditionalAnnotations(AnnotationConstants.SourceStartLine(sourcePosition)); - } - - public static T WithSourceEndLineAnnotation(this T node, FileLinePositionSpan sourcePosition) where T : SyntaxNode - { - return node.WithAdditionalAnnotations(AnnotationConstants.SourceEndLine(sourcePosition)); - } - - public static T WithoutSourceMapping(this T converted) where T : SyntaxNode - { - converted = converted.ReplaceTokens(converted.DescendantTokens(), (o, r) => - r.WithoutSourceMapping() - ); - return converted.ReplaceNodes(converted.DescendantNodes(), (o, r) => - WithoutSourceMappingNonRecursive(r) - ).WithoutSourceMappingNonRecursive(); - } - - private static T WithoutSourceMappingNonRecursive(this T node) where T : SyntaxNode - { - return node.WithoutAnnotations(AnnotationConstants.SourceStartLineAnnotationKind).WithoutAnnotations(AnnotationConstants.SourceEndLineAnnotationKind); - } - - private static bool IsBlockParent(SyntaxNode converted, SyntaxToken lastCsConvertedToken) - { - return lastCsConvertedToken.Parent == converted || lastCsConvertedToken.Parent is BlockSyntax b && b.Parent == converted; - } - - /// - /// create a new root node from the given root after adding annotations to the tokens - /// - /// tokens should belong to the given root - /// - public static SyntaxNode AddAnnotations(this SyntaxNode root, IEnumerable> pairs) - { - // Contract.ThrowIfNull(root); - // Contract.ThrowIfNull(pairs); - - var tokenMap = pairs.GroupBy(p => p.Item1, p => p.Item2).ToDictionary(g => g.Key, g => g.ToArray()); - return root.ReplaceTokens(tokenMap.Keys, (o, n) => o.WithAdditionalAnnotations(tokenMap[o])); - } - - /// - /// create a new root node from the given root after adding annotations to the nodes - /// - /// nodes should belong to the given root - /// - public static SyntaxNode AddAnnotations(this SyntaxNode root, IEnumerable> pairs) - { - // Contract.ThrowIfNull(root); - // Contract.ThrowIfNull(pairs); - - var tokenMap = pairs.GroupBy(p => p.Item1, p => p.Item2).ToDictionary(g => g.Key, g => g.ToArray()); - return root.ReplaceNodes(tokenMap.Keys, (o, n) => o.WithAdditionalAnnotations(tokenMap[o])); - } - - public static TextSpan GetContainedSpan(this IEnumerable nodes) - { - TextSpan fullSpan = nodes.First().Span; - foreach (var node in nodes) { - fullSpan = TextSpan.FromBounds( - Math.Min(fullSpan.Start, node.SpanStart), - Math.Max(fullSpan.End, node.Span.End)); - } - - return fullSpan; - } - - public static IEnumerable GetContiguousSpans( - this IEnumerable nodes, Func getLastToken = null) - { - SyntaxNode lastNode = null; - TextSpan? textSpan = null; - foreach (var node in nodes) { - if (lastNode == null) { - textSpan = node.Span; - } else { - var lastToken = getLastToken == null - ? lastNode.GetLastToken() - : getLastToken(lastNode); - if (lastToken.GetNextToken(includeDirectives: true) == node.GetFirstToken()) { - // Expand the span - textSpan = TextSpan.FromBounds(textSpan.Value.Start, node.Span.End); - } else { - // Return the last span, and start a new one - yield return textSpan.Value; - textSpan = node.Span; - } - } - - lastNode = node; - } - - if (textSpan.HasValue) { - yield return textSpan.Value; - } - } - - public static IEnumerable GetAnnotatedNodes(this SyntaxNode node, SyntaxAnnotation syntaxAnnotation) where T : SyntaxNode - { - return node.GetAnnotatedNodesAndTokens(syntaxAnnotation).Select(n => n.AsNode()).OfType(); - } - - /// - /// Creates a new tree of nodes from the existing tree with the specified old nodes replaced with a newly computed nodes. - /// - /// The root of the tree that contains all the specified nodes. - /// The nodes from the tree to be replaced. - /// A function that computes a replacement node for - /// the argument nodes. The first argument is one of the original specified nodes. The second argument is - /// the same node possibly rewritten with replaced descendants. - /// - public static Task ReplaceNodesAsync( - this TRootNode root, - IEnumerable nodes, - Func> computeReplacementAsync, - CancellationToken cancellationToken) where TRootNode : SyntaxNode - { - return root.ReplaceSyntaxAsync( - nodes: nodes, computeReplacementNodeAsync: computeReplacementAsync, - tokens: null, computeReplacementTokenAsync: null, - trivia: null, computeReplacementTriviaAsync: null, - cancellationToken: cancellationToken); - } - - public static async Task ReplaceSyntaxAsync( - this TRoot root, - IEnumerable nodes, - Func> computeReplacementNodeAsync, - IEnumerable tokens, - Func> computeReplacementTokenAsync, - IEnumerable trivia, - Func> computeReplacementTriviaAsync, - CancellationToken cancellationToken) - where TRoot : SyntaxNode - { - // index all nodes, tokens and trivia by the full spans they cover - var nodesToReplace = nodes != null ? nodes.ToDictionary(n => n.FullSpan) : new Dictionary(); - var tokensToReplace = tokens != null ? tokens.ToDictionary(t => t.FullSpan) : new Dictionary(); - var triviaToReplace = trivia != null ? trivia.ToDictionary(t => t.FullSpan) : new Dictionary(); - - var nodeReplacements = new Dictionary(); - var tokenReplacements = new Dictionary(); - var triviaReplacements = new Dictionary(); - - var retryAnnotations = new AnnotationTable("RetryReplace"); - - var spans = new List(nodesToReplace.Count + tokensToReplace.Count + triviaToReplace.Count); - spans.AddRange(nodesToReplace.Keys); - spans.AddRange(tokensToReplace.Keys); - spans.AddRange(triviaToReplace.Keys); - - while (spans.Count > 0) { - // sort the spans of the items to be replaced so we can tell if any overlap - spans.Sort((x, y) => { - // order by end offset, and then by length - var d = x.End - y.End; - - if (d == 0) { - d = x.Length - y.Length; - } - - return d; - }); - - // compute replacements for all nodes that will go in the same batch - // only spans that do not overlap go in the same batch. - TextSpan previous = default(TextSpan); - foreach (var span in spans) { - // only add to replacement map if we don't intersect with the previous node. This taken with the sort order - // should ensure that parent nodes are not processed in the same batch as child nodes. - if (previous == default(TextSpan) || !previous.IntersectsWith(span)) { - SyntaxNode currentNode; - SyntaxToken currentToken; - SyntaxTrivia currentTrivia; - - if (nodesToReplace.TryGetValue(span, out currentNode)) { - var original = (SyntaxNode)retryAnnotations.GetAnnotations(currentNode).SingleOrDefault() ?? currentNode; - var newNode = await computeReplacementNodeAsync(original, currentNode, cancellationToken).ConfigureAwait(false); - nodeReplacements[currentNode] = newNode; - } else if (tokensToReplace.TryGetValue(span, out currentToken)) { - var original = (SyntaxToken)retryAnnotations.GetAnnotations(currentToken).SingleOrDefault(); - if (original == default(SyntaxToken)) { - original = currentToken; - } - - var newToken = await computeReplacementTokenAsync(original, currentToken, cancellationToken).ConfigureAwait(false); - tokenReplacements[currentToken] = newToken; - } else if (triviaToReplace.TryGetValue(span, out currentTrivia)) { - var original = (SyntaxTrivia)retryAnnotations.GetAnnotations(currentTrivia).SingleOrDefault(); - if (original == default(SyntaxTrivia)) { - original = currentTrivia; - } - - var newTrivia = await computeReplacementTriviaAsync(original, currentTrivia, cancellationToken).ConfigureAwait(false); - triviaReplacements[currentTrivia] = newTrivia; - } - } - - previous = span; - } - - bool retryNodes = false; - bool retryTokens = false; - bool retryTrivia = false; - - // replace nodes in batch - // submit all nodes so we can annotate the ones we don't replace - root = root.ReplaceSyntax( - nodes: nodesToReplace.Values, - computeReplacementNode: (original, rewritten) => { - SyntaxNode replaced; - if (rewritten != original || !nodeReplacements.TryGetValue(original, out replaced)) { - // the subtree did change, or we didn't have a replacement for it in this batch - // so we need to add an annotation so we can find this node again for the next batch. - replaced = retryAnnotations.WithAdditionalAnnotations(rewritten, original); - retryNodes = true; - } - - return replaced; - }, - tokens: tokensToReplace.Values, - computeReplacementToken: (original, rewritten) => { - SyntaxToken replaced; - if (rewritten != original || !tokenReplacements.TryGetValue(original, out replaced)) { - // the subtree did change, or we didn't have a replacement for it in this batch - // so we need to add an annotation so we can find this node again for the next batch. - replaced = retryAnnotations.WithAdditionalAnnotations(rewritten, original); - retryTokens = true; - } - - return replaced; - }, - trivia: triviaToReplace.Values, - computeReplacementTrivia: (original, rewritten) => { - SyntaxTrivia replaced; - if (!triviaReplacements.TryGetValue(original, out replaced)) { - // the subtree did change, or we didn't have a replacement for it in this batch - // so we need to add an annotation so we can find this node again for the next batch. - replaced = retryAnnotations.WithAdditionalAnnotations(rewritten, original); - retryTrivia = true; - } - - return replaced; - }); - - nodesToReplace.Clear(); - tokensToReplace.Clear(); - triviaToReplace.Clear(); - spans.Clear(); - - // prepare next batch out of all remaining annotated nodes - if (retryNodes) { - nodesToReplace = retryAnnotations.GetAnnotatedNodes(root).ToDictionary(n => n.FullSpan); - spans.AddRange(nodesToReplace.Keys); - } - - if (retryTokens) { - tokensToReplace = retryAnnotations.GetAnnotatedTokens(root).ToDictionary(t => t.FullSpan); - spans.AddRange(tokensToReplace.Keys); - } - - if (retryTrivia) { - triviaToReplace = retryAnnotations.GetAnnotatedTrivia(root).ToDictionary(t => t.FullSpan); - spans.AddRange(triviaToReplace.Keys); - } - } - - return root; - } - - public static bool IsKind(this SyntaxNode node, CSSyntaxKind kind1, CSSyntaxKind kind2) - { - if (node == null) { - return false; - } - - var csharpKind = CSharpExtensions.Kind(node); - return csharpKind == kind1 || csharpKind == kind2; - } - - public static bool IsKind(this SyntaxNode node, CSSyntaxKind kind1, CSSyntaxKind kind2, CSSyntaxKind kind3) - { - if (node == null) { - return false; - } - - var csharpKind = CSharpExtensions.Kind(node); - return csharpKind == kind1 || csharpKind == kind2 || csharpKind == kind3; - } - - public static bool IsKind(this SyntaxNode node, CSSyntaxKind kind1, CSSyntaxKind kind2, CSSyntaxKind kind3, CSSyntaxKind kind4) - { - if (node == null) { - return false; - } - - var csharpKind = CSharpExtensions.Kind(node); - return csharpKind == kind1 || csharpKind == kind2 || csharpKind == kind3 || csharpKind == kind4; - } - - public static bool IsKind(this SyntaxNode node, CSSyntaxKind kind1, CSSyntaxKind kind2, CSSyntaxKind kind3, CSSyntaxKind kind4, CSSyntaxKind kind5) - { - if (node == null) { - return false; - } - - var csharpKind = CSharpExtensions.Kind(node); - return csharpKind == kind1 || csharpKind == kind2 || csharpKind == kind3 || csharpKind == kind4 || csharpKind == kind5; - } - - /// - /// Returns the list of using directives that affect . The list will be returned in - /// top down order. - /// - public static IEnumerable GetEnclosingUsingDirectives(this SyntaxNode node) - { - return node.GetAncestorOrThis().Usings - .Concat(node.GetAncestorsOrThis() - .Reverse() - .SelectMany(n => n.Usings)); - } - - public static bool IsUnsafeContext(this SyntaxNode node) - { - if (node.GetAncestor() != null) { - return true; - } - - return node.GetAncestors().Any( - m => m.GetModifiers().Any(CSSyntaxKind.UnsafeKeyword)); - } - - public static bool IsInStaticCsContext(this SyntaxNode node) - { - // this/base calls are always static. - if (node.FirstAncestorOrSelf() != null) { - return true; - } - - var memberDeclaration = node.FirstAncestorOrSelf(); - if (memberDeclaration == null) { - return false; - } - - switch (memberDeclaration.Kind()) { - case CSSyntaxKind.MethodDeclaration: - case CSSyntaxKind.ConstructorDeclaration: - case CSSyntaxKind.PropertyDeclaration: - case CSSyntaxKind.EventDeclaration: - case CSSyntaxKind.IndexerDeclaration: - return memberDeclaration.GetModifiers().Any(CSSyntaxKind.StaticKeyword); - - case CSSyntaxKind.FieldDeclaration: - // Inside a field one can only access static members of a type. - return true; - - case CSSyntaxKind.DestructorDeclaration: - return false; - } - - // Global statements are not a static context. - if (node.FirstAncestorOrSelf() != null) { - return false; - } - - // any other location is considered static - return true; - } - - public static NamespaceDeclarationSyntax GetInnermostNamespaceDeclarationWithUsings(this SyntaxNode contextNode) - { - var usingDirectiveAncsestor = contextNode.GetAncestor(); - if (usingDirectiveAncsestor == null) { - return contextNode.GetAncestorsOrThis().FirstOrDefault(n => n.Usings.Count > 0); - } else { - // We are inside a using directive. In this case, we should find and return the first 'parent' namespace with usings. - var containingNamespace = usingDirectiveAncsestor.GetAncestor(); - if (containingNamespace == null) { - // We are inside a top level using directive (i.e. one that's directly in the compilation unit). - return null; - } else { - return containingNamespace.GetAncestors().FirstOrDefault(n => n.Usings.Count > 0); - } - } - } - - // Matches the following: - // - // (whitespace* newline)+ - private static readonly Matcher s_oneOrMoreBlankLines; - - // Matches the following: - // - // (whitespace* (single-comment|multi-comment) whitespace* newline)+ OneOrMoreBlankLines - private static readonly Matcher s_bannerMatcher; - - static SyntaxNodeExtensions() - { - var whitespace = Matcher.Repeat(Match(CSSyntaxKind.WhitespaceTrivia, "\\b")); - var endOfLine = Match(CSSyntaxKind.EndOfLineTrivia, "\\n"); - var singleBlankLine = Matcher.Sequence(whitespace, endOfLine); - - var singleLineComment = Match(CSSyntaxKind.SingleLineCommentTrivia, "//"); - var multiLineComment = Match(CSSyntaxKind.MultiLineCommentTrivia, "/**/"); - var anyCommentMatcher = Matcher.Choice(singleLineComment, multiLineComment); - - var commentLine = Matcher.Sequence(whitespace, anyCommentMatcher, whitespace, endOfLine); - - s_oneOrMoreBlankLines = Matcher.OneOrMore(singleBlankLine); - s_bannerMatcher = - Matcher.Sequence( - Matcher.OneOrMore(commentLine), - s_oneOrMoreBlankLines); - } - - private static Matcher Match(CSSyntaxKind kind, string description) - { - return Matcher.Single(t => CSharpExtensions.Kind(t) == kind, description); - } - - /// - /// Returns all of the trivia to the left of this token up to the previous token (concatenates - /// the previous token's trailing trivia and this token's leading trivia). - /// - public static IEnumerable GetAllPrecedingTriviaToPreviousToken(this SyntaxToken token) - { - var prevToken = token.GetPreviousToken(includeSkipped: true); - if (CSharpExtensions.Kind(prevToken) == CSSyntaxKind.None) { - return token.LeadingTrivia; - } - - return prevToken.TrailingTrivia.Concat(token.LeadingTrivia); - } - - public static bool IsBreakableConstruct(this SyntaxNode node) - { - switch (CSharpExtensions.Kind(node)) { - case CSSyntaxKind.DoStatement: - case CSSyntaxKind.WhileStatement: - case CSSyntaxKind.SwitchStatement: - case CSSyntaxKind.ForStatement: - case CSSyntaxKind.ForEachStatement: - return true; - } - - return false; - } - - public static bool IsContinuableConstruct(this SyntaxNode node) - { - switch (CSharpExtensions.Kind(node)) { - case CSSyntaxKind.DoStatement: - case CSSyntaxKind.WhileStatement: - case CSSyntaxKind.ForStatement: - case CSSyntaxKind.ForEachStatement: - return true; - } - - return false; - } - - public static bool IsReturnableConstruct(this SyntaxNode node) - { - switch (CSharpExtensions.Kind(node)) { - case CSSyntaxKind.AnonymousMethodExpression: - case CSSyntaxKind.SimpleLambdaExpression: - case CSSyntaxKind.ParenthesizedLambdaExpression: - case CSSyntaxKind.MethodDeclaration: - case CSSyntaxKind.ConstructorDeclaration: - case CSSyntaxKind.DestructorDeclaration: - case CSSyntaxKind.GetAccessorDeclaration: - case CSSyntaxKind.SetAccessorDeclaration: - case CSSyntaxKind.OperatorDeclaration: - case CSSyntaxKind.AddAccessorDeclaration: - case CSSyntaxKind.RemoveAccessorDeclaration: - return true; - } - - return false; - } - - public static bool SpansPreprocessorDirective( - this IEnumerable list) - where TSyntaxNode : SyntaxNode - { - if (list == null || !list.Any()) { - return false; - } - - var tokens = list.SelectMany(n => n.DescendantTokens()); - - // todo: we need to dive into trivia here. - return tokens.SpansPreprocessorDirective(); - } - - public static T WithPrependedLeadingTrivia( - this T node, - params SyntaxTrivia[] trivia) where T : SyntaxNode - { - if (trivia.Length == 0) { - return node; - } - - return node.WithPrependedLeadingTrivia((IEnumerable)trivia); - } - - public static T WithPrependedLeadingTrivia( - this T node, - SyntaxTriviaList trivia) where T : SyntaxNode - { - if (trivia.Count == 0) { - return node; - } - - return node.WithLeadingTrivia(trivia.Concat(node.GetLeadingTrivia())); - } - - public static T WithPrependedLeadingTrivia( - this T node, - IEnumerable trivia) where T : SyntaxNode - { - return node.WithPrependedLeadingTrivia(Microsoft.CodeAnalysis.CSharp.SyntaxExtensions.ToSyntaxTriviaList(trivia)); - } - - public static T WithAppendedTrailingTrivia( - this T node, - params SyntaxTrivia[] trivia) where T : SyntaxNode - { - if (trivia.Length == 0) { - return node; - } - - return node.WithAppendedTrailingTrivia((IEnumerable)trivia); - } - - public static T WithAppendedTrailingTrivia( - this T node, - SyntaxTriviaList trivia) where T : SyntaxNode - { - if (trivia.Count == 0) { - return node; - } - - return node.WithTrailingTrivia(node.GetTrailingTrivia().Concat(trivia)); - } - - public static T WithAppendedTrailingTrivia( - this T node, - IEnumerable trivia) where T : SyntaxNode - { - return node.WithAppendedTrailingTrivia(Microsoft.CodeAnalysis.CSharp.SyntaxExtensions.ToSyntaxTriviaList(trivia)); - } - - public static T With( - this T node, - IEnumerable leadingTrivia, - IEnumerable trailingTrivia) where T : SyntaxNode - { - return node.WithLeadingTrivia(leadingTrivia).WithTrailingTrivia(trailingTrivia); - } - public static SyntaxToken WithConvertedTriviaFrom(this SyntaxToken node, SyntaxNode otherNode) - { - return node.WithConvertedLeadingTriviaFrom(otherNode).WithConvertedTrailingTriviaFrom(otherNode); - } - - public static T WithConvertedLeadingTriviaFrom(this T node, SyntaxToken fromToken) where T : SyntaxNode - { - var firstConvertedToken = node.GetFirstToken(); - return node.ReplaceToken(firstConvertedToken, firstConvertedToken.WithConvertedLeadingTriviaFrom(fromToken)); - } - - public static SyntaxToken WithConvertedLeadingTriviaFrom(this SyntaxToken node, SyntaxNode otherNode) - { - var firstToken = otherNode?.GetFirstToken(); - return WithConvertedLeadingTriviaFrom(node, firstToken); - } - - public static SyntaxToken WithConvertedLeadingTriviaFrom(this SyntaxToken node, SyntaxToken? sourceToken) - { - if (sourceToken == null) return node; - var convertedTrivia = ConvertTrivia(sourceToken.Value.LeadingTrivia); - return node.WithLeadingTrivia(convertedTrivia); - } - - public static T WithConvertedTrailingTriviaFrom(this T node, SyntaxToken fromToken, TriviaKinds triviaKinds = null) where T : SyntaxNode - { - var lastConvertedToken = node.GetLastToken(); - return node.ReplaceToken(lastConvertedToken, lastConvertedToken.WithConvertedTrailingTriviaFrom(fromToken, triviaKinds)); - } - - public static SyntaxToken WithConvertedTrailingTriviaFrom(this SyntaxToken node, SyntaxNode otherNode, TriviaKinds triviaKinds = null) - { - return node.WithConvertedTrailingTriviaFrom(otherNode?.GetLastToken(), triviaKinds); - } - - public static SyntaxToken WithConvertedTrailingTriviaFrom(this SyntaxToken node, SyntaxToken? otherToken, TriviaKinds triviaKinds = null) - { - triviaKinds = triviaKinds ?? TriviaKinds.All; - if (!otherToken.HasValue || !otherToken.Value.HasTrailingTrivia) return node; - var convertedTrivia = ConvertTrivia(otherToken.Value.TrailingTrivia.Where(triviaKinds.ShouldAccept).ToArray()); - return node.WithTrailingTrivia(node.ImportantTrailingTrivia().Concat(convertedTrivia)); - } - - public static IEnumerable ImportantTrailingTrivia(this SyntaxToken node) - { - return node.TrailingTrivia.Where(x => !x.IsWhitespaceOrEndOfLine()); - } - - public static bool ParentHasSameTrailingTrivia(this SyntaxNode otherNode) - { - return otherNode.Parent.GetLastToken() == otherNode.GetLastToken(); - } - - public static IEnumerable ConvertTrivia(this IReadOnlyCollection triviaToConvert) - { - try { - if (triviaToConvert.Any() && triviaToConvert.First().Language == LanguageNames.CSharp) { - return CSharpToVBCodeConverter.Util.RecursiveTriviaConverter.ConvertTopLevel(triviaToConvert).Where(x => x != default(SyntaxTrivia)); - } - return triviaToConvert.SelectMany(ConvertVBTrivia).Where(x => x != default(SyntaxTrivia)); - } catch (Exception) { - return Enumerable.Empty(); //TODO Log this somewhere, or write enough tests to be really confident it won't throw - } - } - - private static IEnumerable ConvertVBTrivia(SyntaxTrivia t) - { - if (t.IsKind(VBSyntaxKind.CommentTrivia)) { - yield return SyntaxFactory.SyntaxTrivia(CSSyntaxKind.SingleLineCommentTrivia, $"// {t.GetCommentText()}"); - yield break; - } - if (t.IsKind(VBSyntaxKind.DocumentationCommentTrivia)) { - var previousWhitespace = t.GetPreviousTrivia(t.SyntaxTree, CancellationToken.None).ToString(); - var commentTextLines = t.GetCommentText().Replace("\r\n", "\n").Replace("\r", "\n").Split('\n'); - var outputCommentText = "/// " + String.Join($"\r\n{previousWhitespace}/// ", commentTextLines) + Environment.NewLine; - yield return SyntaxFactory.SyntaxTrivia(CSSyntaxKind.SingleLineCommentTrivia, outputCommentText); //It's always single line...even when it has multiple lines - yield break; - } - - if (t.IsKind(VBSyntaxKind.WhitespaceTrivia)) { - yield return SyntaxFactory.SyntaxTrivia(CSSyntaxKind.WhitespaceTrivia, t.ToString()); - yield break; - } - - if (t.IsKind(VBSyntaxKind.EndOfLineTrivia)) { - // Mapping one to one here leads to newlines appearing where the natural line-end was in VB. - // e.g. ToString\r\n() - // Because C Sharp needs those brackets. Handling each possible case of this is far more effort than it's worth. - yield return SyntaxFactory.SyntaxTrivia(CSSyntaxKind.EndOfLineTrivia, t.ToString()); - yield break; - } - - //Each of these would need its own method to recreate for C# with the right structure probably so let's just warn about them for now. - var convertedKind = t.GetCSKind(); - yield return convertedKind.HasValue - ? SyntaxFactory.Comment($"/* TODO ERROR: Skipped {convertedKind.Value} */") - : default(SyntaxTrivia); - } - - public static T WithoutTrailingEndOfLineTrivia(this T cSharpNode) where T : CSharpSyntaxNode - { - var lastDescendant = cSharpNode.DescendantNodesAndTokens().Last(); - var triviaWithoutNewline = lastDescendant.GetTrailingTrivia().Where(t => !t.IsKind(CSSyntaxKind.EndOfLineTrivia)); - if (lastDescendant.IsNode) { - return cSharpNode.ReplaceNode(lastDescendant.AsNode(), - lastDescendant.AsNode().WithTrailingTrivia(triviaWithoutNewline)); - } - return cSharpNode.ReplaceToken(lastDescendant.AsToken(), - lastDescendant.AsToken().WithTrailingTrivia(triviaWithoutNewline)); - } - - public static T WithOrderedTriviaFromSubTree( - this T node, - SyntaxNode subTree) where T : SyntaxNode - { - if (!subTree.Contains(node)) - throw new InvalidOperationException(nameof(node) + " must be a descendant of " + nameof(subTree)); - - var location = node.FullSpan; - var leadingTrivia = new List(); - var trailingTrivia = new List(); - - bool wasWSOrEOL = false; - - foreach (var trivia in subTree.DescendantTrivia()) { - // ignore superfluous eol - if (wasWSOrEOL && trivia.IsWhitespaceOrEndOfLine()) - continue; - if (trivia.Span.End <= location.Start) - leadingTrivia.Add(trivia); - else if (trivia.Span.Start >= location.End) - trailingTrivia.Add(trivia); - wasWSOrEOL = trivia.IsWhitespaceOrEndOfLine(); - } - - return node.With(leadingTrivia.Concat(node.GetLeadingTrivia()), node.GetTrailingTrivia().Concat(trailingTrivia)); - } - - public static TNode ConvertToSingleLine(this TNode node) - where TNode : SyntaxNode - { - if (node == null) { - return node; - } - - var rewriter = new SingleLineRewriter(); - return (TNode)rewriter.Visit(node); - } - - internal class SingleLineRewriter : CSharpSyntaxRewriter - { - private bool _lastTokenEndedInWhitespace; - - public override SyntaxToken VisitToken(SyntaxToken token) - { - if (_lastTokenEndedInWhitespace) { - token = token.WithLeadingTrivia(Enumerable.Empty()); - } else if (token.LeadingTrivia.Count > 0) { - token = token.WithLeadingTrivia(SyntaxFactory.Space); - } - - if (token.TrailingTrivia.Count > 0) { - token = token.WithTrailingTrivia(SyntaxFactory.Space); - _lastTokenEndedInWhitespace = true; - } else { - _lastTokenEndedInWhitespace = false; - } - - return token; - } - } - - public static bool IsAnyArgumentList(this SyntaxNode node) - { - return node.IsKind(CSSyntaxKind.ArgumentList) || - node.IsKind(CSSyntaxKind.AttributeArgumentList) || - node.IsKind(CSSyntaxKind.BracketedArgumentList) || - node.IsKind(CSSyntaxKind.TypeArgumentList); - } - - public static bool IsAnyLambda(this SyntaxNode node) - { - return - node.IsKind(CSSyntaxKind.ParenthesizedLambdaExpression) || - node.IsKind(CSSyntaxKind.SimpleLambdaExpression); - } - - public static bool IsAnyLambdaOrAnonymousMethod(this SyntaxNode node) - { - return node.IsAnyLambda() || node.IsKind(CSSyntaxKind.AnonymousMethodExpression); - } - - public static IEnumerable GetLeadingBannerAndPreprocessorDirectives( - this TSyntaxNode node) - where TSyntaxNode : SyntaxNode - { - IEnumerable leadingTrivia; - node.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(out leadingTrivia); - return leadingTrivia; - } - - public static TSyntaxNode GetNodeWithoutLeadingBannerAndPreprocessorDirectives( - this TSyntaxNode node) - where TSyntaxNode : SyntaxNode - { - IEnumerable strippedTrivia; - return node.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(out strippedTrivia); - } - - public static TSyntaxNode GetNodeWithoutLeadingBannerAndPreprocessorDirectives( - this TSyntaxNode node, out IEnumerable strippedTrivia) - where TSyntaxNode : SyntaxNode - { - var leadingTrivia = node.GetLeadingTrivia(); - - // Rules for stripping trivia: - // 1) If there is a pp directive, then it (and all preceding trivia) *must* be stripped. - // This rule supersedes all other rules. - // 2) If there is a doc comment, it cannot be stripped. Even if there is a doc comment, - // followed by 5 new lines, then the doc comment still must stay with the node. This - // rule does *not* supersede rule 1. - // 3) Single line comments in a group (i.e. with no blank lines between them) belong to - // the node *iff* there is no blank line between it and the following trivia. - - List leadingTriviaToStrip, leadingTriviaToKeep; - - int ppIndex = -1; - for (int i = leadingTrivia.Count - 1; i >= 0; i--) { - if (SyntaxFacts.IsPreprocessorDirective(CSharpExtensions.Kind(leadingTrivia[i]))) { - ppIndex = i; - break; - } - } - - if (ppIndex != -1) { - // We have a pp directive. it (and all all previous trivia) must be stripped. - leadingTriviaToStrip = new List(leadingTrivia.Take(ppIndex + 1)); - leadingTriviaToKeep = new List(leadingTrivia.Skip(ppIndex + 1)); - } else { - leadingTriviaToKeep = new List(leadingTrivia); - leadingTriviaToStrip = new List(); - } - - // Now, consume as many banners as we can. - var index = 0; - while ( - s_oneOrMoreBlankLines.TryMatch(leadingTriviaToKeep, ref index) || - s_bannerMatcher.TryMatch(leadingTriviaToKeep, ref index)) { - } - - leadingTriviaToStrip.AddRange(leadingTriviaToKeep.Take(index)); - - strippedTrivia = leadingTriviaToStrip; - return node.WithLeadingTrivia(leadingTriviaToKeep.Skip(index)); - } - - public static bool IsAnyAssignExpression(this SyntaxNode node) - { - return SyntaxFacts.IsAssignmentExpression(CSharpExtensions.Kind(node)); - } - - public static bool IsCompoundAssignExpression(this SyntaxNode node) - { - switch (CSharpExtensions.Kind(node)) { - case CSSyntaxKind.AddAssignmentExpression: - case CSSyntaxKind.SubtractAssignmentExpression: - case CSSyntaxKind.MultiplyAssignmentExpression: - case CSSyntaxKind.DivideAssignmentExpression: - case CSSyntaxKind.ModuloAssignmentExpression: - case CSSyntaxKind.AndAssignmentExpression: - case CSSyntaxKind.ExclusiveOrAssignmentExpression: - case CSSyntaxKind.OrAssignmentExpression: - case CSSyntaxKind.LeftShiftAssignmentExpression: - case CSSyntaxKind.RightShiftAssignmentExpression: - return true; - } - - return false; - } - - public static bool IsLeftSideOfAssignExpression(this SyntaxNode node) - { - return node.IsParentKind(CSSyntaxKind.SimpleAssignmentExpression) && - ((AssignmentExpressionSyntax)node.Parent).Left == node; - } - - public static bool IsLeftSideOfAnyAssignExpression(this SyntaxNode node) - { - return node.Parent.IsAnyAssignExpression() && - ((AssignmentExpressionSyntax)node.Parent).Left == node; - } - - public static bool IsRightSideOfAnyAssignExpression(this SyntaxNode node) - { - return node.Parent.IsAnyAssignExpression() && - ((AssignmentExpressionSyntax)node.Parent).Right == node; - } - - public static bool IsVariableDeclaratorValue(this SyntaxNode node) - { - return - node.IsParentKind(CSSyntaxKind.EqualsValueClause) && - node.Parent.IsParentKind(CSSyntaxKind.VariableDeclarator) && - ((EqualsValueClauseSyntax)node.Parent).Value == node; - } - - public static BlockSyntax FindInnermostCommonBlock(this IEnumerable nodes) - { - return nodes.FindInnermostCommonNode(); - } - - public static IEnumerable GetAncestorsOrThis(this SyntaxNode node, Func predicate) - { - var current = node; - while (current != null) { - if (predicate(current)) { - yield return current; - } - - current = current.Parent; - } - } - - /// - /// If the position is inside of token, return that token; otherwise, return the token to the right. - /// - public static SyntaxToken FindTokenOnRightOfPosition( - this SyntaxNode root, - int position, - bool includeSkipped = true, - bool includeDirectives = false, - bool includeDocumentationComments = false) - { - var skippedTokenFinder = includeSkipped ? SyntaxTriviaExtensions.s_findSkippedTokenForward : (Func)null; - - return FindTokenHelper.FindTokenOnRightOfPosition( - root, position, skippedTokenFinder, includeSkipped, includeDirectives, includeDocumentationComments); - } - - /// - /// Returns child node or token that contains given position. - /// - /// - /// This is a copy of that also returns the index of the child node. - /// - internal static SyntaxNodeOrToken ChildThatContainsPosition(this SyntaxNode self, int position, out int childIndex) - { - var childList = self.ChildNodesAndTokens(); - - int left = 0; - int right = childList.Count - 1; - - while (left <= right) { - int middle = left + ((right - left) / 2); - SyntaxNodeOrToken node = childList.ElementAt(middle); - - var span = node.FullSpan; - if (position < span.Start) { - right = middle - 1; - } else if (position >= span.End) { - left = middle + 1; - } else { - childIndex = middle; - return node; - } - } - - // we could check up front that index is within FullSpan, - // but we wan to optimize for the common case where position is valid. - Debug.Assert(!self.FullSpan.Contains(position), "Position is valid. How could we not find a child?"); - throw new ArgumentOutOfRangeException("position"); - } - - public static SyntaxNode GetParent(this SyntaxNode node) - { - return node != null ? node.Parent : null; - } - - public static ValueTuple GetBraces(this SyntaxNode node) - { - var namespaceNode = node as NamespaceDeclarationSyntax; - if (namespaceNode != null) { - return ValueTuple.Create(namespaceNode.OpenBraceToken, namespaceNode.CloseBraceToken); - } - - var baseTypeNode = node as BaseTypeDeclarationSyntax; - if (baseTypeNode != null) { - return ValueTuple.Create(baseTypeNode.OpenBraceToken, baseTypeNode.CloseBraceToken); - } - - var accessorListNode = node as AccessorListSyntax; - if (accessorListNode != null) { - return ValueTuple.Create(accessorListNode.OpenBraceToken, accessorListNode.CloseBraceToken); - } - - var blockNode = node as BlockSyntax; - if (blockNode != null) { - return ValueTuple.Create(blockNode.OpenBraceToken, blockNode.CloseBraceToken); - } - - var switchStatementNode = node as SwitchStatementSyntax; - if (switchStatementNode != null) { - return ValueTuple.Create(switchStatementNode.OpenBraceToken, switchStatementNode.CloseBraceToken); - } - - var anonymousObjectCreationExpression = node as AnonymousObjectCreationExpressionSyntax; - if (anonymousObjectCreationExpression != null) { - return ValueTuple.Create(anonymousObjectCreationExpression.OpenBraceToken, anonymousObjectCreationExpression.CloseBraceToken); - } - - var initializeExpressionNode = node as InitializerExpressionSyntax; - if (initializeExpressionNode != null) { - return ValueTuple.Create(initializeExpressionNode.OpenBraceToken, initializeExpressionNode.CloseBraceToken); - } - - return new ValueTuple(); - } - - public static ValueTuple GetParentheses(this SyntaxNode node) - { - return node.TypeSwitch( - (ParenthesizedExpressionSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (MakeRefExpressionSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (RefTypeExpressionSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (RefValueExpressionSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (CheckedExpressionSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (DefaultExpressionSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (TypeOfExpressionSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (SizeOfExpressionSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (ArgumentListSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (CastExpressionSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (WhileStatementSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (DoStatementSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (ForStatementSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (ForEachStatementSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (UsingStatementSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (FixedStatementSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (LockStatementSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (IfStatementSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (SwitchStatementSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (CatchDeclarationSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (AttributeArgumentListSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (ConstructorConstraintSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (ParameterListSyntax n) => ValueTuple.Create(n.OpenParenToken, n.CloseParenToken), - (SyntaxNode n) => default(ValueTuple)); - } - - public static ValueTuple GetBrackets(this SyntaxNode node) - { - return node.TypeSwitch( - (ArrayRankSpecifierSyntax n) => ValueTuple.Create(n.OpenBracketToken, n.CloseBracketToken), - (BracketedArgumentListSyntax n) => ValueTuple.Create(n.OpenBracketToken, n.CloseBracketToken), - (ImplicitArrayCreationExpressionSyntax n) => ValueTuple.Create(n.OpenBracketToken, n.CloseBracketToken), - (AttributeListSyntax n) => ValueTuple.Create(n.OpenBracketToken, n.CloseBracketToken), - (BracketedParameterListSyntax n) => ValueTuple.Create(n.OpenBracketToken, n.CloseBracketToken), - (SyntaxNode n) => default(ValueTuple)); - } - - public static bool IsEmbeddedStatementOwner(this SyntaxNode node) - { - return node is IfStatementSyntax || - node is ElseClauseSyntax || - node is WhileStatementSyntax || - node is ForStatementSyntax || - node is ForEachStatementSyntax || - node is UsingStatementSyntax || - node is DoStatementSyntax; - } - - public static StatementSyntax GetEmbeddedStatement(this SyntaxNode node) - { - return node.TypeSwitch( - (IfStatementSyntax n) => n.Statement, - (ElseClauseSyntax n) => n.Statement, - (WhileStatementSyntax n) => n.Statement, - (ForStatementSyntax n) => n.Statement, - (ForEachStatementSyntax n) => n.Statement, - (UsingStatementSyntax n) => n.Statement, - (DoStatementSyntax n) => n.Statement, - (SyntaxNode n) => null); - } - - public static SyntaxTokenList GetModifiers(this CSharpSyntaxNode member) - { - if (member != null) { - switch (CSharpExtensions.Kind(member)) { - case CSSyntaxKind.EnumDeclaration: - return ((EnumDeclarationSyntax)member).Modifiers; - case CSSyntaxKind.ClassDeclaration: - case CSSyntaxKind.InterfaceDeclaration: - case CSSyntaxKind.StructDeclaration: - return ((TypeDeclarationSyntax)member).Modifiers; - case CSSyntaxKind.DelegateDeclaration: - return ((DelegateDeclarationSyntax)member).Modifiers; - case CSSyntaxKind.FieldDeclaration: - return ((FieldDeclarationSyntax)member).Modifiers; - case CSSyntaxKind.EventFieldDeclaration: - return ((EventFieldDeclarationSyntax)member).Modifiers; - case CSSyntaxKind.ConstructorDeclaration: - return ((ConstructorDeclarationSyntax)member).Modifiers; - case CSSyntaxKind.DestructorDeclaration: - return ((DestructorDeclarationSyntax)member).Modifiers; - case CSSyntaxKind.PropertyDeclaration: - return ((PropertyDeclarationSyntax)member).Modifiers; - case CSSyntaxKind.EventDeclaration: - return ((EventDeclarationSyntax)member).Modifiers; - case CSSyntaxKind.IndexerDeclaration: - return ((IndexerDeclarationSyntax)member).Modifiers; - case CSSyntaxKind.OperatorDeclaration: - return ((OperatorDeclarationSyntax)member).Modifiers; - case CSSyntaxKind.ConversionOperatorDeclaration: - return ((ConversionOperatorDeclarationSyntax)member).Modifiers; - case CSSyntaxKind.MethodDeclaration: - return ((MethodDeclarationSyntax)member).Modifiers; - case CSSyntaxKind.GetAccessorDeclaration: - case CSSyntaxKind.SetAccessorDeclaration: - case CSSyntaxKind.AddAccessorDeclaration: - case CSSyntaxKind.RemoveAccessorDeclaration: - return ((AccessorDeclarationSyntax)member).Modifiers; - } - } - - return default(SyntaxTokenList); - } - - public static SyntaxNode WithModifiers(this SyntaxNode member, SyntaxTokenList modifiers) - { - if (member != null) { - switch (CSharpExtensions.Kind(member)) { - case CSSyntaxKind.EnumDeclaration: - return ((EnumDeclarationSyntax)member).WithModifiers(modifiers); - case CSSyntaxKind.ClassDeclaration: - case CSSyntaxKind.InterfaceDeclaration: - case CSSyntaxKind.StructDeclaration: - return ((TypeDeclarationSyntax)member).WithModifiers(modifiers); - case CSSyntaxKind.DelegateDeclaration: - return ((DelegateDeclarationSyntax)member).WithModifiers(modifiers); - case CSSyntaxKind.FieldDeclaration: - return ((FieldDeclarationSyntax)member).WithModifiers(modifiers); - case CSSyntaxKind.EventFieldDeclaration: - return ((EventFieldDeclarationSyntax)member).WithModifiers(modifiers); - case CSSyntaxKind.ConstructorDeclaration: - return ((ConstructorDeclarationSyntax)member).WithModifiers(modifiers); - case CSSyntaxKind.DestructorDeclaration: - return ((DestructorDeclarationSyntax)member).WithModifiers(modifiers); - case CSSyntaxKind.PropertyDeclaration: - return ((PropertyDeclarationSyntax)member).WithModifiers(modifiers); - case CSSyntaxKind.EventDeclaration: - return ((EventDeclarationSyntax)member).WithModifiers(modifiers); - case CSSyntaxKind.IndexerDeclaration: - return ((IndexerDeclarationSyntax)member).WithModifiers(modifiers); - case CSSyntaxKind.OperatorDeclaration: - return ((OperatorDeclarationSyntax)member).WithModifiers(modifiers); - case CSSyntaxKind.ConversionOperatorDeclaration: - return ((ConversionOperatorDeclarationSyntax)member).WithModifiers(modifiers); - case CSSyntaxKind.MethodDeclaration: - return ((MethodDeclarationSyntax)member).WithModifiers(modifiers); - case CSSyntaxKind.GetAccessorDeclaration: - case CSSyntaxKind.SetAccessorDeclaration: - case CSSyntaxKind.AddAccessorDeclaration: - case CSSyntaxKind.RemoveAccessorDeclaration: - return ((AccessorDeclarationSyntax)member).WithModifiers(modifiers); - } - } - - return null; - } - - public static TypeDeclarationSyntax WithModifiers( - this TypeDeclarationSyntax node, SyntaxTokenList modifiers) - { - switch (node.Kind()) { - case CSSyntaxKind.ClassDeclaration: - return ((ClassDeclarationSyntax)node).WithModifiers(modifiers); - case CSSyntaxKind.InterfaceDeclaration: - return ((InterfaceDeclarationSyntax)node).WithModifiers(modifiers); - case CSSyntaxKind.StructDeclaration: - return ((StructDeclarationSyntax)node).WithModifiers(modifiers); - } - - throw new InvalidOperationException(); - } - - public static bool CheckTopLevel(this SyntaxNode node, TextSpan span) - { - var block = node as BlockSyntax; - if (block != null) { - return block.ContainsInBlockBody(span); - } - - var field = node as FieldDeclarationSyntax; - if (field != null) { - foreach (var variable in field.Declaration.Variables) { - if (variable.Initializer != null && variable.Initializer.Span.Contains(span)) { - return true; - } - } - } - - var global = node as GlobalStatementSyntax; - if (global != null) { - return true; - } - - var constructorInitializer = node as ConstructorInitializerSyntax; - if (constructorInitializer != null) { - return constructorInitializer.ContainsInArgument(span); - } - - return false; - } - - public static bool ContainsInArgument(this ConstructorInitializerSyntax initializer, TextSpan textSpan) - { - if (initializer == null) { - return false; - } - - return initializer.ArgumentList.Arguments.Any(a => a.Span.Contains(textSpan)); - } - - public static bool ContainsInBlockBody(this BlockSyntax block, TextSpan textSpan) - { - if (block == null) { - return false; - } - - var blockSpan = TextSpan.FromBounds(block.OpenBraceToken.Span.End, block.CloseBraceToken.SpanStart); - return blockSpan.Contains(textSpan); - } - - public static IEnumerable GetMembers(this SyntaxNode node) - { - var compilation = node as CompilationUnitSyntax; - if (compilation != null) { - return compilation.Members; - } - - var @namespace = node as NamespaceDeclarationSyntax; - if (@namespace != null) { - return @namespace.Members; - } - - var type = node as TypeDeclarationSyntax; - if (type != null) { - return type.Members; - } - - var @enum = node as EnumDeclarationSyntax; - if (@enum != null) { - return @enum.Members; - } - - return SpecializedCollections.EmptyEnumerable(); - } - - public static IEnumerable GetBodies(this SyntaxNode node) - { - var constructor = node as ConstructorDeclarationSyntax; - if (constructor != null) { - var result = SpecializedCollections.SingletonEnumerable(constructor.Body).WhereNotNull(); - var initializer = constructor.Initializer; - if (initializer != null) { - result = result.Concat(initializer.ArgumentList.Arguments.Select(a => (SyntaxNode)a.Expression).WhereNotNull()); - } - - return result; - } - - var baseMethod = node as BaseMethodDeclarationSyntax; - if (baseMethod != null) { - if (baseMethod.Body != null) - return SpecializedCollections.SingletonEnumerable(baseMethod.Body); - var method = baseMethod as MethodDeclarationSyntax; - if ((method != null) && (method.ExpressionBody != null)) { - return SpecializedCollections.SingletonEnumerable(method.ExpressionBody); - } - } - - var baseProperty = node as BasePropertyDeclarationSyntax; - if (baseProperty != null) { - if (baseProperty.AccessorList != null) { - return baseProperty.AccessorList.Accessors.Select(a => a.Body).WhereNotNull(); - } - var indexer = baseProperty as IndexerDeclarationSyntax; - if ((indexer != null) && (indexer.ExpressionBody != null)) { - return SpecializedCollections.SingletonEnumerable(indexer.ExpressionBody); - } - } - - var @enum = node as EnumMemberDeclarationSyntax; - if (@enum != null) { - if (@enum.EqualsValue != null) { - return SpecializedCollections.SingletonEnumerable(@enum.EqualsValue.Value).WhereNotNull(); - } - } - - var field = node as BaseFieldDeclarationSyntax; - if (field != null) { - return field.Declaration.Variables.Where(v => v.Initializer != null).Select(v => v.Initializer.Value).WhereNotNull(); - } - - return SpecializedCollections.EmptyEnumerable(); - } - - public static ConditionalAccessExpressionSyntax GetParentConditionalAccessExpression(this SyntaxNode node) - { - var parent = node.Parent; - while (parent != null) { - // Because the syntax for conditional access is right associate, we cannot - // simply take the first ancestor ConditionalAccessExpression. Instead, we - // must walk upward until we find the ConditionalAccessExpression whose - // OperatorToken appears left of the MemberBinding. - if (parent.IsKind(CSSyntaxKind.ConditionalAccessExpression) && - ((ConditionalAccessExpressionSyntax)parent).OperatorToken.Span.End <= node.SpanStart) { - return (ConditionalAccessExpressionSyntax)parent; - } - - parent = parent.Parent; - } - - return null; - } - - public static bool IsDelegateOrConstructorOrMethodParameterList(this SyntaxNode node) - { - if (!node.IsKind(CSSyntaxKind.ParameterList)) { - return false; - } - - return - node.IsParentKind(CSSyntaxKind.MethodDeclaration) || - node.IsParentKind(CSSyntaxKind.ConstructorDeclaration) || - node.IsParentKind(CSSyntaxKind.DelegateDeclaration); - } - - public static SyntaxTree WithAnnotatedNode(this SyntaxNode root, SyntaxNode selectedNode, string annotationKind, string annotationData = "") - { - var annotatatedNode = - selectedNode.WithAdditionalAnnotations(new SyntaxAnnotation(annotationKind, annotationData)); - return root.ReplaceNode(selectedNode, annotatatedNode).SyntaxTree.WithFilePath(root.SyntaxTree.FilePath); - } - - public static string GetBriefNodeDescription(this SyntaxNode node) - { - var sb = new StringBuilder(); - sb.Append($"'{node.ToString().Truncate()}' at character {node.SpanStart}"); - return sb.ToString(); - } - - public static string DescribeConversionError(this SyntaxNode node, Exception e) - { - return $"Cannot convert {node.GetType().Name}, {e}{Environment.NewLine}{Environment.NewLine}" + - $"Input:{Environment.NewLine}{node.ToFullString()}{Environment.NewLine}"; - } - - public static string DescribeConversionWarning(this SyntaxNode node, string addtlInfo) - { - return $"{addtlInfo}{Environment.NewLine}" + - $"{node.NormalizeWhitespace().ToFullString()}{Environment.NewLine}"; - } - - private static string Truncate(this string input, int maxLength = 30, string truncationIndicator = "...") - { - input = input.Replace(Environment.NewLine, "\\r\\n").Replace(" ", " ").Replace("\t", " "); - if (input.Length <= maxLength) return input; - return input.Substring(0, maxLength - truncationIndicator.Length) + truncationIndicator; - } - - public static T WithCsTrailingErrorComment(this T dummyDestNode, - VisualBasicSyntaxNode sourceNode, - Exception exception) where T : CSharpSyntaxNode - { - var errorDirective = SyntaxFactory.ParseTrailingTrivia($"#error Cannot convert {sourceNode.GetType().Name} - see comment for details{Environment.NewLine}"); - var errorDescription = sourceNode.DescribeConversionError(exception); - var commentedText = "/* " + errorDescription + " */"; - var trailingTrivia = SyntaxFactory.TriviaList(errorDirective.Concat(SyntaxFactory.Comment(commentedText))); - - return dummyDestNode - .WithTrailingTrivia(trailingTrivia) - .WithAdditionalAnnotations(new SyntaxAnnotation(AnnotationConstants.ConversionErrorAnnotationKind, exception.ToString())); - } - - public static T WithCsTrailingWarningComment(this T dummyDestNode, string warning, string addtlInfo, - CSharpSyntaxNode convertedNode - ) where T : CSharpSyntaxNode - { - var warningDirective = SyntaxFactory.ParseTrailingTrivia($"#warning {warning}{Environment.NewLine}"); - var warningDescription = convertedNode.DescribeConversionWarning(addtlInfo); - var commentedText = "/* " + warningDescription + " */"; - var trailingTrivia = SyntaxFactory.TriviaList(warningDirective.Concat(SyntaxFactory.Comment(commentedText))); - - return dummyDestNode - .WithTrailingTrivia(trailingTrivia); - } - - public static T WithVbTrailingErrorComment( - this T dummyDestNode, CSharpSyntaxNode problematicSourceNode, Exception exception) where T : VisualBasicSyntaxNode - { - var errorDescription = problematicSourceNode.DescribeConversionError(exception); - var commentedText = "''' " + errorDescription.Replace("\r\n", "\r\n''' "); - return dummyDestNode - .WithTrailingTrivia(VBSyntaxFactory.CommentTrivia(commentedText)) - .WithAdditionalAnnotations(new SyntaxAnnotation(AnnotationConstants.ConversionErrorAnnotationKind, - exception.ToString())); - } - - public static bool ContainsDeclaredVisibility(this SyntaxTokenList modifiers, bool isVariableOrConst = false, bool isConstructor = false) - { - return modifiers.Any(m => m.IsCsVisibility(isVariableOrConst, isConstructor)); - } - - public static SyntaxToken FindNonZeroWidthToken(this SyntaxNode node, int position) - { - var syntaxToken = node.FindToken(position); - if (syntaxToken.FullWidth() == 0) { - return syntaxToken.GetPreviousToken(); - } else { - return syntaxToken; - } - } - } -} \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/Util/SyntaxTokenExtensions.cs b/ICSharpCode.CodeConverter/Util/SyntaxTokenExtensions.cs deleted file mode 100644 index d9310fce4..000000000 --- a/ICSharpCode.CodeConverter/Util/SyntaxTokenExtensions.cs +++ /dev/null @@ -1,1086 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using ICSharpCode.CodeConverter.VB; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; -using CSharpExtensions = Microsoft.CodeAnalysis.CSharp.CSharpExtensions; -using VisualBasicExtensions = Microsoft.CodeAnalysis.VisualBasic.VisualBasicExtensions; -using VBasic = Microsoft.CodeAnalysis.VisualBasic; -using ICSharpCode.CodeConverter.Shared; - -namespace ICSharpCode.CodeConverter.Util -{ -#if NR6 - public -#endif - internal static class SyntaxTokenExtensions - { - public static SyntaxNode GetAncestor(this SyntaxToken token, Func predicate) - { - return token.GetAncestor(predicate); - } - - public static T GetAncestor(this SyntaxToken token, Func predicate = null) - where T : SyntaxNode - { - return token.Parent != null - ? token.Parent.FirstAncestorOrSelf(predicate) - : default(T); - } - - public static IEnumerable GetAncestors(this SyntaxToken token) - where T : SyntaxNode - { - return token.Parent != null - ? token.Parent.AncestorsAndSelf().OfType() - : Enumerable.Empty(); - } - - public static IEnumerable GetAncestors(this SyntaxToken token, Func predicate) - { - return token.Parent != null - ? token.Parent.AncestorsAndSelf().Where(predicate) - : Enumerable.Empty(); - } - - public static SyntaxNode GetCommonRoot(this SyntaxToken token1, SyntaxToken token2) - { - // Contract.ThrowIfTrue(token1.RawKind == 0 || token2.RawKind == 0); - - // find common starting node from two tokens. - // as long as two tokens belong to same tree, there must be at least on common root (Ex, compilation unit) - if (token1.Parent == null || token2.Parent == null) { - return null; - } - - return token1.Parent.GetCommonRoot(token2.Parent); - } - - public static bool CheckParent(this SyntaxToken token, Func valueChecker) where T : SyntaxNode - { - var parentNode = token.Parent as T; - if (parentNode == null) { - return false; - } - - return valueChecker(parentNode); - } - - public static int Width(this SyntaxToken token) - { - return token.Span.Length; - } - - public static int FullWidth(this SyntaxToken token) - { - return token.FullSpan.Length; - } - - public static SyntaxToken FindTokenFromEnd(this SyntaxNode root, int position, bool includeZeroWidth = true, bool findInsideTrivia = false) - { - var token = root.FindToken(position, findInsideTrivia); - var previousToken = token.GetPreviousToken( - includeZeroWidth, findInsideTrivia, findInsideTrivia, findInsideTrivia); - - if (token.SpanStart == position && - previousToken.RawKind != 0 && - previousToken.Span.End == position) { - return previousToken; - } - - return token; - } - - public static bool IsUsingOrExternKeyword(this SyntaxToken token) - { - return - token.Kind() == SyntaxKind.UsingKeyword || - token.Kind() == SyntaxKind.ExternKeyword; - } - - public static bool IsUsingKeywordInUsingDirective(this SyntaxToken token) - { - if (token.IsKind(SyntaxKind.UsingKeyword)) { - var usingDirective = token.GetAncestor(); - if (usingDirective != null && - usingDirective.UsingKeyword == token) { - return true; - } - } - - return false; - } - - public static bool IsStaticKeywordInUsingDirective(this SyntaxToken token) - { - if (token.IsKind(SyntaxKind.StaticKeyword)) { - var usingDirective = token.GetAncestor(); - if (usingDirective != null && - usingDirective.StaticKeyword == token) { - return true; - } - } - - return false; - } - - public static bool IsBeginningOfStatementContext(this SyntaxToken token) - { - // cases: - // { - // | - - // } - // | - - // Note, the following is *not* a legal statement context: - // do { } | - - // ...; - // | - - // case 0: - // | - - // default: - // | - - // label: - // | - - // if (foo) - // | - - // while (true) - // | - - // do - // | - - // for (;;) - // | - - // foreach (var v in c) - // | - - // else - // | - - // using (expr) - // | - - // lock (expr) - // | - - // for ( ; ; Foo(), | - - if (token.Kind() == SyntaxKind.OpenBraceToken && - token.Parent.IsKind(SyntaxKind.Block)) { - return true; - } - - if (token.Kind() == SyntaxKind.SemicolonToken) { - var statement = token.GetAncestor(); - if (statement != null && !statement.IsParentKind(SyntaxKind.GlobalStatement) && - statement.GetLastToken(includeZeroWidth: true) == token) { - return true; - } - } - - if (token.Kind() == SyntaxKind.CloseBraceToken && - token.Parent.IsKind(SyntaxKind.Block)) { - if (token.Parent.Parent is StatementSyntax) { - // Most blocks that are the child of statement are places - // that we can follow with another statement. i.e.: - // if { } - // while () { } - // There are two exceptions. - // try {} - // do {} - if (!token.Parent.IsParentKind(SyntaxKind.TryStatement) && - !token.Parent.IsParentKind(SyntaxKind.DoStatement)) { - return true; - } - } else if ( - token.Parent.IsParentKind(SyntaxKind.ElseClause) || - token.Parent.IsParentKind(SyntaxKind.FinallyClause) || - token.Parent.IsParentKind(SyntaxKind.CatchClause) || - token.Parent.IsParentKind(SyntaxKind.SwitchSection)) { - return true; - } - } - - if (token.Kind() == SyntaxKind.CloseBraceToken && - token.Parent.IsKind(SyntaxKind.SwitchStatement)) { - return true; - } - - if (token.Kind() == SyntaxKind.ColonToken) { - if (token.Parent.IsKind(SyntaxKind.CaseSwitchLabel, SyntaxKind.DefaultSwitchLabel, SyntaxKind.LabeledStatement)) { - return true; - } - } - - if (token.Kind() == SyntaxKind.DoKeyword && - token.Parent.IsKind(SyntaxKind.DoStatement)) { - return true; - } - - if (token.Kind() == SyntaxKind.CloseParenToken) { - var parent = token.Parent; - if (parent.IsKind(SyntaxKind.ForStatement) || - parent.IsKind(SyntaxKind.ForEachStatement) || - parent.IsKind(SyntaxKind.WhileStatement) || - parent.IsKind(SyntaxKind.IfStatement) || - parent.IsKind(SyntaxKind.LockStatement) || - parent.IsKind(SyntaxKind.UsingStatement)) { - return true; - } - } - - if (token.Kind() == SyntaxKind.ElseKeyword) { - return true; - } - - return false; - } - - public static bool IsBeginningOfGlobalStatementContext(this SyntaxToken token) - { - // cases: - // } - // | - - // ...; - // | - - // extern alias Foo; - // using System; - // | - - // [assembly: Foo] - // | - - if (token.Kind() == SyntaxKind.CloseBraceToken) { - var memberDeclaration = token.GetAncestor(); - if (memberDeclaration != null && memberDeclaration.GetLastToken(includeZeroWidth: true) == token && - memberDeclaration.IsParentKind(SyntaxKind.CompilationUnit)) { - return true; - } - } - - if (token.Kind() == SyntaxKind.SemicolonToken) { - var globalStatement = token.GetAncestor(); - if (globalStatement != null && globalStatement.GetLastToken(includeZeroWidth: true) == token) { - return true; - } - - var memberDeclaration = token.GetAncestor(); - if (memberDeclaration != null && memberDeclaration.GetLastToken(includeZeroWidth: true) == token && - memberDeclaration.IsParentKind(SyntaxKind.CompilationUnit)) { - return true; - } - - var compUnit = token.GetAncestor(); - if (compUnit != null) { - if (compUnit.Usings.Count > 0 && compUnit.Usings.Last().GetLastToken(includeZeroWidth: true) == token) { - return true; - } - - if (compUnit.Externs.Count > 0 && compUnit.Externs.Last().GetLastToken(includeZeroWidth: true) == token) { - return true; - } - } - } - - if (token.Kind() == SyntaxKind.CloseBracketToken) { - var compUnit = token.GetAncestor(); - if (compUnit != null) { - if (compUnit.AttributeLists.Count > 0 && compUnit.AttributeLists.Last().GetLastToken(includeZeroWidth: true) == token) { - return true; - } - } - } - - return false; - } - - public static bool IsAfterPossibleCast(this SyntaxToken token) - { - if (token.Kind() == SyntaxKind.CloseParenToken) { - if (token.Parent.IsKind(SyntaxKind.CastExpression)) { - return true; - } - - if (token.Parent.IsKind(SyntaxKind.ParenthesizedExpression)) { - var parenExpr = token.Parent as ParenthesizedExpressionSyntax; - var expr = parenExpr.Expression; - - if (expr is TypeSyntax) { - return true; - } - } - } - - return false; - } - - public static bool IsLastTokenOfNode(this SyntaxToken token) - where T : SyntaxNode - { - var node = token.GetAncestor(); - return node != null && token == node.GetLastToken(includeZeroWidth: true); - } - - public static bool IsLastTokenOfQueryClause(this SyntaxToken token) - { - if (token.IsLastTokenOfNode()) { - return true; - } - - if (token.Kind() == SyntaxKind.IdentifierToken && - token.GetPreviousToken(includeSkipped: true).Kind() == SyntaxKind.IntoKeyword) { - return true; - } - - return false; - } - - public static bool IsPreProcessorExpressionContext(this SyntaxToken targetToken) - { - // cases: - // #if | - // #if foo || | - // #if foo && | - // #if ( | - // #if ! | - // Same for elif - - if (targetToken.GetAncestor() == null) { - return false; - } - - // #if - // #elif - if (targetToken.Kind() == SyntaxKind.IfKeyword || - targetToken.Kind() == SyntaxKind.ElifKeyword) { - return true; - } - - // ( | - if (targetToken.Kind() == SyntaxKind.OpenParenToken && - targetToken.Parent.IsKind(SyntaxKind.ParenthesizedExpression)) { - return true; - } - - // ! | - if (targetToken.Parent is PrefixUnaryExpressionSyntax) { - var prefix = targetToken.Parent as PrefixUnaryExpressionSyntax; - return prefix.OperatorToken == targetToken; - } - - // a && - // a || - if (targetToken.Parent is BinaryExpressionSyntax) { - var binary = targetToken.Parent as BinaryExpressionSyntax; - return binary.OperatorToken == targetToken; - } - - return false; - } - - public static bool IsOrderByDirectionContext(this SyntaxToken targetToken) - { - // cases: - // orderby a | - // orderby a a| - // orderby a, b | - // orderby a, b a| - - if (!targetToken.IsKind(SyntaxKind.IdentifierToken, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken)) { - return false; - } - - var ordering = targetToken.GetAncestor(); - if (ordering == null) { - return false; - } - - // orderby a | - // orderby a, b | - var lastToken = ordering.Expression.GetLastToken(includeSkipped: true); - - if (targetToken == lastToken) { - return true; - } - - return false; - } - - public static bool IsSwitchLabelContext(this SyntaxToken targetToken) - { - // cases: - // case X: | - // default: | - // switch (e) { | - // - // case X: Statement(); | - - if (targetToken.Kind() == SyntaxKind.OpenBraceToken && - targetToken.Parent.IsKind(SyntaxKind.SwitchStatement)) { - return true; - } - - if (targetToken.Kind() == SyntaxKind.ColonToken) { - if (targetToken.Parent.IsKind(SyntaxKind.CaseSwitchLabel, SyntaxKind.DefaultSwitchLabel)) { - return true; - } - } - - if (targetToken.Kind() == SyntaxKind.SemicolonToken || - targetToken.Kind() == SyntaxKind.CloseBraceToken) { - var section = targetToken.GetAncestor(); - if (section != null) { - foreach (var statement in section.Statements) { - if (targetToken == statement.GetLastToken(includeSkipped: true)) { - return true; - } - } - } - } - - return false; - } - - public static bool IsXmlCrefParameterModifierContext(this SyntaxToken targetToken) - { - return targetToken.IsKind(SyntaxKind.CommaToken, SyntaxKind.OpenParenToken) - && targetToken.Parent.IsKind(SyntaxKind.CrefBracketedParameterList, SyntaxKind.CrefParameterList); - } - - public static bool IsConstructorOrMethodParameterArgumentContext(this SyntaxToken targetToken) - { - // cases: - // Foo( | - // Foo(expr, | - // Foo(bar: | - // new Foo( | - // new Foo(expr, | - // new Foo(bar: | - // Foo : base( | - // Foo : base(bar: | - // Foo : this( | - // Foo : ths(bar: | - - // Foo(bar: | - if (targetToken.Kind() == SyntaxKind.ColonToken && - targetToken.Parent.IsKind(SyntaxKind.NameColon) && - targetToken.Parent.IsParentKind(SyntaxKind.Argument) && - targetToken.Parent.GetParent().IsParentKind(SyntaxKind.ArgumentList)) { - var owner = targetToken.Parent.GetParent().GetParent().GetParent(); - if (owner.IsKind(SyntaxKind.InvocationExpression) || - owner.IsKind(SyntaxKind.ObjectCreationExpression) || - owner.IsKind(SyntaxKind.BaseConstructorInitializer) || - owner.IsKind(SyntaxKind.ThisConstructorInitializer)) { - return true; - } - } - - if (targetToken.Kind() == SyntaxKind.OpenParenToken || - targetToken.Kind() == SyntaxKind.CommaToken) { - if (targetToken.Parent.IsKind(SyntaxKind.ArgumentList)) { - if (targetToken.Parent.IsParentKind(SyntaxKind.InvocationExpression) || - targetToken.Parent.IsParentKind(SyntaxKind.ObjectCreationExpression) || - targetToken.Parent.IsParentKind(SyntaxKind.BaseConstructorInitializer) || - targetToken.Parent.IsParentKind(SyntaxKind.ThisConstructorInitializer)) { - return true; - } - } - } - - return false; - } - - public static bool IsUnaryOperatorContext(this SyntaxToken targetToken) - { - if (targetToken.Kind() == SyntaxKind.OperatorKeyword && - targetToken.GetPreviousToken(includeSkipped: true).IsLastTokenOfNode()) { - return true; - } - - return false; - } - - public static bool IsUnsafeContext(this SyntaxToken targetToken) - { - return - targetToken.GetAncestors().Any(s => s.IsKind(SyntaxKind.UnsafeStatement)) || - targetToken.GetAncestors().Any(m => m.GetModifiers().Any(SyntaxKind.UnsafeKeyword)); - } - - public static bool IsAfterYieldKeyword(this SyntaxToken targetToken) - { - // yield | - // yield r| - - if (targetToken.IsKindOrHasMatchingText(SyntaxKind.YieldKeyword)) { - return true; - } - - return false; - } - - public static bool IsAccessorDeclarationContext(this SyntaxToken targetToken, int position, SyntaxKind kind = SyntaxKind.None) - where TMemberNode : SyntaxNode - { - if (!IsAccessorDeclarationContextWorker(targetToken)) { - return false; - } - - var list = targetToken.GetAncestor(); - if (list == null) { - return false; - } - - // Check if we already have this accessor. (however, don't count it - // if the user is *on* that accessor. - var existingAccessor = list.Accessors - .Select(a => a.Keyword) - .FirstOrDefault(a => !a.IsMissing && a.IsKindOrHasMatchingText(kind)); - - if (existingAccessor.Kind() != SyntaxKind.None) { - var existingAccessorSpan = existingAccessor.Span; - if (!existingAccessorSpan.IntersectsWith(position)) { - return false; - } - } - - var decl = targetToken.GetAncestor(); - return decl != null; - } - - private static bool IsAccessorDeclarationContextWorker(SyntaxToken targetToken) - { - // cases: - // int Foo { | - // int Foo { private | - // int Foo { set { } | - // int Foo { set; | - // int Foo { [Bar]| - - // Consume all preceding access modifiers - while (targetToken.Kind() == SyntaxKind.InternalKeyword || - targetToken.Kind() == SyntaxKind.PublicKeyword || - targetToken.Kind() == SyntaxKind.ProtectedKeyword || - targetToken.Kind() == SyntaxKind.PrivateKeyword) { - targetToken = targetToken.GetPreviousToken(includeSkipped: true); - } - - // int Foo { | - // int Foo { private | - if (targetToken.Kind() == SyntaxKind.OpenBraceToken && - targetToken.Parent.IsKind(SyntaxKind.AccessorList)) { - return true; - } - - // int Foo { set { } | - // int Foo { set { } private | - if (targetToken.Kind() == SyntaxKind.CloseBraceToken && - targetToken.Parent.IsKind(SyntaxKind.Block) && - targetToken.Parent.GetParent() is AccessorDeclarationSyntax) { - return true; - } - - // int Foo { set; | - if (targetToken.Kind() == SyntaxKind.SemicolonToken && - targetToken.Parent is AccessorDeclarationSyntax) { - return true; - } - - // int Foo { [Bar]| - if (targetToken.Kind() == SyntaxKind.CloseBracketToken && - targetToken.Parent.IsKind(SyntaxKind.AttributeList) && - targetToken.Parent.GetParent() is AccessorDeclarationSyntax) { - return true; - } - - return false; - } - - private static bool IsGenericInterfaceOrDelegateTypeParameterList(SyntaxNode node) - { - if (node.IsKind(SyntaxKind.TypeParameterList)) { - if (node.IsParentKind(SyntaxKind.InterfaceDeclaration)) { - var decl = node.Parent as TypeDeclarationSyntax; - return decl.TypeParameterList == node; - } else if (node.IsParentKind(SyntaxKind.DelegateDeclaration)) { - var decl = node.Parent as DelegateDeclarationSyntax; - return decl.TypeParameterList == node; - } - } - - return false; - } - - public static bool IsTypeParameterVarianceContext(this SyntaxToken targetToken) - { - // cases: - // interface IFoo<| - // interface IFoo().EndOfFileToken - : nextToken; - } - - public static SyntaxToken With(this SyntaxToken token, SyntaxTriviaList leading, SyntaxTriviaList trailing) - { - return token.WithLeadingTrivia(leading).WithTrailingTrivia(trailing); - } - - /// - /// Determines whether the given SyntaxToken is the first token on a line in the specified SourceText. - /// - public static bool IsFirstTokenOnLine(this SyntaxToken token, SourceText text) - { - var previousToken = token.GetPreviousToken(includeSkipped: true, includeDirectives: true, includeDocumentationComments: true); - if (previousToken.Kind() == SyntaxKind.None) { - return true; - } - - var tokenLine = text.Lines.IndexOf(token.SpanStart); - var previousTokenLine = text.Lines.IndexOf(previousToken.SpanStart); - return tokenLine > previousTokenLine; - } - - public static bool SpansPreprocessorDirective(this IEnumerable tokens) - { - // we want to check all leading trivia of all tokens (except the - // first one), and all trailing trivia of all tokens (except the - // last one). - - var first = true; - var previousToken = default(SyntaxToken); - - foreach (var token in tokens) { - if (first) { - first = false; - } else { - // check the leading trivia of this token, and the trailing trivia - // of the previous token. - if (SpansPreprocessorDirective(token.LeadingTrivia) || - SpansPreprocessorDirective(previousToken.TrailingTrivia)) { - return true; - } - } - - previousToken = token; - } - - return false; - } - - private static bool SpansPreprocessorDirective(SyntaxTriviaList list) - { - return list.Any(t => t.GetStructure() is DirectiveTriviaSyntax); - } - - public static SyntaxToken WithoutTrivia( - this SyntaxToken token, - params SyntaxTrivia[] trivia) - { - if (!token.LeadingTrivia.Any() && !token.TrailingTrivia.Any()) { - return token; - } - - return token.With(new SyntaxTriviaList(), new SyntaxTriviaList()); - } - - public static SyntaxToken WithPrependedLeadingTrivia( - this SyntaxToken token, - params SyntaxTrivia[] trivia) - { - if (trivia.Length == 0) { - return token; - } - - return token.WithPrependedLeadingTrivia((IEnumerable)trivia); - } - - public static SyntaxToken WithPrependedLeadingTrivia( - this SyntaxToken token, - SyntaxTriviaList trivia) - { - if (trivia.Count == 0) { - return token; - } - - return token.WithLeadingTrivia(trivia.Concat(token.LeadingTrivia)); - } - - public static SyntaxToken WithPrependedLeadingTrivia( - this SyntaxToken token, - IEnumerable trivia) - { - return token.WithPrependedLeadingTrivia(trivia.ToSyntaxTriviaList()); - } - - public static SyntaxToken WithAppendedTrailingTrivia( - this SyntaxToken token, - IEnumerable trivia) - { - return token.WithTrailingTrivia(token.TrailingTrivia.Concat(trivia)); - } - - /// - /// Retrieves all trivia after this token, including it's trailing trivia and - /// the leading trivia of the next token. - /// - public static IEnumerable GetAllTrailingTrivia(this SyntaxToken token) - { - foreach (var trivia in token.TrailingTrivia) { - yield return trivia; - } - - var nextToken = token.GetNextCsTokenOrEndOfFile(includeZeroWidth: true, includeSkipped: true, includeDirectives: true, includeDocumentationComments: true); - - foreach (var trivia in nextToken.LeadingTrivia) { - yield return trivia; - } - } - - public static bool TryParseGenericName(this SyntaxToken genericIdentifier, CancellationToken cancellationToken, out GenericNameSyntax genericName) - { - if (genericIdentifier.GetNextToken(includeSkipped: true).Kind() == SyntaxKind.LessThanToken) { - var lastToken = genericIdentifier.FindLastTokenOfPartialGenericName(); - - var syntaxTree = genericIdentifier.SyntaxTree; - var name = SyntaxFactory.ParseName(syntaxTree.GetText(cancellationToken).ToString(TextSpan.FromBounds(genericIdentifier.SpanStart, lastToken.Span.End))); - - genericName = name as GenericNameSyntax; - return genericName != null; - } - - genericName = null; - return false; - } - - /// - /// Lexically, find the last token that looks like it's part of this generic name. - /// - /// The "name" of the generic identifier, last token before - /// the "&" - /// The last token in the name - /// This is related to the code in - public static SyntaxToken FindLastTokenOfPartialGenericName(this SyntaxToken genericIdentifier) - { - //Contract.ThrowIfFalse(genericIdentifier.Kind() == SyntaxKind.IdentifierToken); - - // advance to the "<" token - var token = genericIdentifier.GetNextToken(includeSkipped: true); - //Contract.ThrowIfFalse(token.Kind() == SyntaxKind.LessThanToken); - - int stack = 0; - - do { - // look forward one token - { - var next = token.GetNextToken(includeSkipped: true); - if (next.Kind() == SyntaxKind.None) { - return token; - } - - token = next; - } - - if (token.Kind() == SyntaxKind.GreaterThanToken) { - if (stack == 0) { - return token; - } else { - stack--; - continue; - } - } - - switch (token.Kind()) { - case SyntaxKind.LessThanLessThanToken: - stack++; - goto case SyntaxKind.LessThanToken; - - // fall through - case SyntaxKind.LessThanToken: - stack++; - break; - - case SyntaxKind.AsteriskToken: // for int* - case SyntaxKind.QuestionToken: // for int? - case SyntaxKind.ColonToken: // for global:: (so we don't dismiss help as you type the first :) - case SyntaxKind.ColonColonToken: // for global:: - case SyntaxKind.CloseBracketToken: - case SyntaxKind.OpenBracketToken: - case SyntaxKind.DotToken: - case SyntaxKind.IdentifierToken: - case SyntaxKind.CommaToken: - break; - - // If we see a member declaration keyword, we know we've gone too far - case SyntaxKind.ClassKeyword: - case SyntaxKind.StructKeyword: - case SyntaxKind.InterfaceKeyword: - case SyntaxKind.DelegateKeyword: - case SyntaxKind.EnumKeyword: - case SyntaxKind.PrivateKeyword: - case SyntaxKind.PublicKeyword: - case SyntaxKind.InternalKeyword: - case SyntaxKind.ProtectedKeyword: - case SyntaxKind.VoidKeyword: - return token.GetPreviousToken(includeSkipped: true); - - default: - // user might have typed "in" on the way to typing "int" - // don't want to disregard this genericname because of that - if (SyntaxFacts.IsKeywordKind(token.Kind())) { - break; - } - - // anything else and we're sunk. Go back to the token before. - return token.GetPreviousToken(includeSkipped: true); - } - } - while (true); - } - - public static bool IsRegularStringLiteral(this SyntaxToken token) - { - return token.Kind() == SyntaxKind.StringLiteralToken && !token.IsVerbatimStringLiteral(); - } - - public static bool IsValidAttributeTarget(this SyntaxToken token) - { - switch (token.Kind()) { - case SyntaxKind.AssemblyKeyword: - case SyntaxKind.ModuleKeyword: - case SyntaxKind.FieldKeyword: - case SyntaxKind.EventKeyword: - case SyntaxKind.MethodKeyword: - case SyntaxKind.ParamKeyword: - case SyntaxKind.PropertyKeyword: - case SyntaxKind.ReturnKeyword: - case SyntaxKind.TypeKeyword: - return true; - - default: - return false; - } - } - - public static bool IsIdentifierOrAccessorOrAccessibilityModifier(this SyntaxToken token) - { - switch (token.Kind()) { - case SyntaxKind.IdentifierName: - case SyntaxKind.IdentifierToken: - case SyntaxKind.GetKeyword: - case SyntaxKind.SetKeyword: - case SyntaxKind.PrivateKeyword: - case SyntaxKind.ProtectedKeyword: - case SyntaxKind.InternalKeyword: - case SyntaxKind.PublicKeyword: - return true; - - default: - return false; - } - } - - public static bool IsVbVisibility(this SyntaxToken token, bool isVariableOrConst, bool isConstructor) - { - return token.IsKind(VBasic.SyntaxKind.PublicKeyword, VBasic.SyntaxKind.FriendKeyword, VBasic.SyntaxKind.ProtectedKeyword, VBasic.SyntaxKind.PrivateKeyword) - || isVariableOrConst && token.IsKind(VBasic.SyntaxKind.ConstKeyword) - || isConstructor && token.IsKind(VBasic.SyntaxKind.SharedKeyword); - } - - public static bool IsCsVisibility(this SyntaxToken token, bool isVariableOrConst, bool isConstructor) - { - return token.IsKind(SyntaxKind.PublicKeyword, SyntaxKind.InternalKeyword, SyntaxKind.ProtectedKeyword, SyntaxKind.PrivateKeyword) - || isVariableOrConst && token.IsKind(SyntaxKind.ConstKeyword) - || isConstructor && token.IsKind(SyntaxKind.StaticKeyword); - } - - public static SyntaxToken WithSourceMappingFrom(this SyntaxToken converted, SyntaxNodeOrToken fromToken) - { - var origLinespan = fromToken.SyntaxTree.GetLineSpan(fromToken.Span); - if (fromToken.IsToken) converted = fromToken.AsToken().CopyAnnotationsTo(converted); - return converted.WithSourceStartLineAnnotation(origLinespan).WithSourceEndLineAnnotation(origLinespan); - } - - public static SyntaxToken WithSourceStartLineAnnotation(this SyntaxToken node, FileLinePositionSpan sourcePosition) - { - return node.WithAdditionalAnnotations(AnnotationConstants.SourceStartLine(sourcePosition)); - } - - public static SyntaxToken WithSourceEndLineAnnotation(this SyntaxToken node, FileLinePositionSpan sourcePosition) - { - return node.WithAdditionalAnnotations(AnnotationConstants.SourceEndLine(sourcePosition)); - } - - public static SyntaxToken WithoutSourceMapping(this SyntaxToken token) - { - return token.WithoutAnnotations(AnnotationConstants.SourceStartLineAnnotationKind).WithoutAnnotations(AnnotationConstants.SourceEndLineAnnotationKind); - } - } -} diff --git a/IncrementVersion.ps1 b/IncrementVersion.ps1 index a9771acec..8341344cf 100644 --- a/IncrementVersion.ps1 +++ b/IncrementVersion.ps1 @@ -1,4 +1,7 @@ -Param($major = $false) +Param([Switch] $major, [Switch] $minor) +$incrementPart = 3 +if ($minor) { $incrementPart = 2 } +if ($major) { $incrementPart = 1 } # https://stackoverflow.com/a/9121679/1128762 function Get-FileEncoding($filePath) { @@ -31,9 +34,10 @@ function WriteAllText-PreservingEncoding($filePath, $contents) { [System.IO.File]::WriteAllText($filePath, $contents, (Get-FileEncoding $filePath)) } -function Increment-Version($version, $incrementMajor = $major) { - if ($incrementMajor -Or $version.Minor -ge 9) { return Create-Version $version ($version.Major + 1) 0 0 0 } - return Create-Version $version $version.Major ($version.Minor + 1) +function Increment-Version($version) { + if ($incrementPart -eq 1 -Or $version.Minor -ge 9) { return Create-Version $version ($version.Major + 1) 0 0 0 } + if ($incrementPart -eq 2 -Or $version.Build -ge 9) { return Create-Version $version $version.Major ($version.Minor + 1) 0 0} + return Create-Version $version $version.Major $version.Minor ($version.Build + 1) 0 } #Regex must contain 3 groups. 1: Text preceding version to replace, 2: Version number part to replace, 3: Text after version to replace @@ -85,13 +89,13 @@ function Update-Changelog($filePath, $newVersion) { WriteAllText-PreservingEncoding $filePath $contents } -$newVersion = Increment-VersionInFile 'appveyor.yml' '(version: )(\d+\.\d+)(\.)' -Increment-VersionInFile 'azure-pipelines.yml' '(buildVersion: .)(\d+\.\d+)(\.\$)' | Out-Null -Increment-VersionInFile 'Vsix\source.extension.vsixmanifest' '(7e2a69d6-193b-4cdf-878d-3370d5931942" Version=")(\d+\.\d+)(\.)' | Out-Null +$newVersion = Increment-VersionInFile 'azure-pipelines.yml' '(buildVersion: .)(\d+\.\d+\.\d+)(\.\$)' +Increment-VersionInFile 'appveyor.yml' '(version: )(\d+\.\d+\.\d+)(\.)' | Out-Null +Increment-VersionInFile 'Vsix\source.extension.vsixmanifest' '(7e2a69d6-193b-4cdf-878d-3370d5931942" Version=")(\d+\.\d+\.\d+)(\.)' | Out-Null Get-ChildItem -Recurse '*.csproj' | Where { -not $_.FullName.Contains("TestData")} | % { Increment-VersionInFile $_ '(\n )(\d+\.\d+)(\.)' $true | Out-Null - Increment-VersionInFile $_ '(\n )(\d+\.\d+)(\.)' $true | Out-Null - Increment-VersionInFile $_ '(\n )(\d+\.\d+)(\.)' $true | Out-Null + Increment-VersionInFile $_ '(\n )(\d+\.\d+\.\d+)(\.)' $true | Out-Null + Increment-VersionInFile $_ '(\n )(\d+\.\d+\.\d+)(\.)' $true | Out-Null } -$newVersionString = $newVersion.ToString(2) + '.0' +$newVersionString = $newVersion.ToString(3) Update-Changelog 'CHANGELOG.md' $newVersionString \ No newline at end of file diff --git a/README.md b/README.md index b63575eea..7821397ab 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Let us know what needs improving. If you want to get involved in writing the cod Currently, the VB -> C# conversion quality is higher than the C# -> VB conversion quality. This is due to demand of people raising issues and supply of developers willing to fix them. But we're very happy to support developers who want to contribute to either conversion direction. ## Other ways to use the converter -* Artifact vsix from latest "nightly" developer builds (potentially less stable and must be uninstalled to update): https://icsharpcode.visualstudio.com/icsharpcode-pipelines/_build/latest?definitionId=2&branchName=master&status=succeeded +* VSIX inside artifact drop from latest CI builds (potentially less stable): https://icsharpcode.visualstudio.com/icsharpcode-pipelines/_build/latest?definitionId=2&branchName=master&status=succeeded * Online snippet converter: [https://codeconverter.icsharpcode.net/](https://codeconverter.icsharpcode.net/) (less accurate due to lack of project context) diff --git a/Tests/CSharp/ExpressionTests.cs b/Tests/CSharp/ExpressionTests.cs index be0873fc5..6ac1d7fa4 100644 --- a/Tests/CSharp/ExpressionTests.cs +++ b/Tests/CSharp/ExpressionTests.cs @@ -1,8 +1,4 @@ -using System; -using System.Data; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading.Tasks; +using System.Threading.Tasks; using CodeConverter.Tests.TestRunners; using Xunit; diff --git a/Tests/CSharp/LinqExpressionTests.cs b/Tests/CSharp/LinqExpressionTests.cs index 2e75b5647..386cf5a60 100644 --- a/Tests/CSharp/LinqExpressionTests.cs +++ b/Tests/CSharp/LinqExpressionTests.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Threading.Tasks; using CodeConverter.Tests.TestRunners; using Xunit; diff --git a/Tests/CSharp/MemberTests.cs b/Tests/CSharp/MemberTests.cs index 260d7cc75..48e558aec 100644 --- a/Tests/CSharp/MemberTests.cs +++ b/Tests/CSharp/MemberTests.cs @@ -1,8 +1,6 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using CodeConverter.Tests.TestRunners; using ICSharpCode.CodeConverter.CSharp; -using ICSharpCode.CodeConverter.Shared; using Xunit; namespace CodeConverter.Tests.CSharp diff --git a/Tests/CSharp/MultiFileSolutionAndProjectTests.cs b/Tests/CSharp/MultiFileSolutionAndProjectTests.cs index 8175d70a4..2072e082e 100644 --- a/Tests/CSharp/MultiFileSolutionAndProjectTests.cs +++ b/Tests/CSharp/MultiFileSolutionAndProjectTests.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; +using System.Threading.Tasks; using CodeConverter.Tests.TestRunners; using ICSharpCode.CodeConverter.CSharp; using Xunit; diff --git a/Tests/CSharp/NamespaceLevelTests.cs b/Tests/CSharp/NamespaceLevelTests.cs index 40d89786f..14624ff46 100644 --- a/Tests/CSharp/NamespaceLevelTests.cs +++ b/Tests/CSharp/NamespaceLevelTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using CodeConverter.Tests.TestRunners; using Xunit; diff --git a/Tests/CSharp/SelfVerifyingTests.cs b/Tests/CSharp/SelfVerifyingTests.cs index 3da6ba187..a61637516 100644 --- a/Tests/CSharp/SelfVerifyingTests.cs +++ b/Tests/CSharp/SelfVerifyingTests.cs @@ -2,7 +2,6 @@ using System.Linq; using ICSharpCode.CodeConverter.CSharp; using Xunit; -using CodeConverter.Tests.Compilation; using System.IO; using System.Threading.Tasks; using CodeConverter.Tests.TestRunners; diff --git a/Tests/LanguageAgnostic/ProjectFileTextEditorTests.cs b/Tests/LanguageAgnostic/ProjectFileTextEditorTests.cs index 302100faf..f08b82b51 100644 --- a/Tests/LanguageAgnostic/ProjectFileTextEditorTests.cs +++ b/Tests/LanguageAgnostic/ProjectFileTextEditorTests.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using ICSharpCode.CodeConverter.CSharp; -using ICSharpCode.CodeConverter.Util; +using ICSharpCode.CodeConverter.Util; using Xunit; namespace CodeConverter.Tests.LanguageAgnostic diff --git a/Tests/TestRunners/ConverterTestBase.cs b/Tests/TestRunners/ConverterTestBase.cs index 3f40fb66b..c07cc45f3 100644 --- a/Tests/TestRunners/ConverterTestBase.cs +++ b/Tests/TestRunners/ConverterTestBase.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Text; -using System.Text.RegularExpressions; using System.Threading.Tasks; using ICSharpCode.CodeConverter; using ICSharpCode.CodeConverter.CSharp; diff --git a/Tests/TestRunners/MultiFileTestFixture.cs b/Tests/TestRunners/MultiFileTestFixture.cs index f00157316..21a93c3e5 100644 --- a/Tests/TestRunners/MultiFileTestFixture.cs +++ b/Tests/TestRunners/MultiFileTestFixture.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; -using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; @@ -91,7 +89,7 @@ public void Dispose() : LanguageNames.CSharp; var projectsToConvert = (await _solution.GetValueAsync()).Projects.Where(p => p.Language == languageNameToConvert && shouldConvertProject(p)).ToArray(); - var conversionResults = (await SolutionConverter.CreateFor(projectsToConvert).Convert()).ToDictionary(c => c.TargetPathOrNull, StringComparer.OrdinalIgnoreCase); + var conversionResults = await SolutionConverter.CreateFor(projectsToConvert).Convert().ToDictionaryAsync(c => c.TargetPathOrNull, StringComparer.OrdinalIgnoreCase); var expectedResultDirectory = GetExpectedResultDirectory(expectedResultsDirectory); try { @@ -146,11 +144,11 @@ private static MSBuildWorkspace CreateWorkspaceUnhandled() private static async Task AssertMSBuildIsWorkingAndProjectsValid( ImmutableList valueDiagnostics, IEnumerable projectsToConvert) { - var errors = await projectsToConvert.ParallelSelectAsync(async x => { + var errors = await projectsToConvert.ParallelSelectAwait(async x => { var c = await x.GetCompilationAsync(); return new[]{CompilationWarnings.WarningsForCompilation(c, c.AssemblyName)}.Concat( valueDiagnostics.Where(d => d.Kind > WorkspaceDiagnosticKind.Warning).Select(d => d.Message)); - }, Env.MaxDop); + }, Env.MaxDop, default).ToArrayAsync(); var errorString = string.Join("\r\n", errors.SelectMany(w => w).Where(w => w != null)); Assert.True(errorString == "", errorString); } diff --git a/Tests/CSharp/NamedFact.cs b/Tests/TestRunners/NamedFact.cs similarity index 90% rename from Tests/CSharp/NamedFact.cs rename to Tests/TestRunners/NamedFact.cs index 880e960ca..a0974bcf8 100644 --- a/Tests/CSharp/NamedFact.cs +++ b/Tests/TestRunners/NamedFact.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; -namespace CodeConverter.Tests.CSharp +namespace CodeConverter.Tests.TestRunners { public class NamedFact { diff --git a/Tests/TestRunners/SelfVerifyingTestFactory.cs b/Tests/TestRunners/SelfVerifyingTestFactory.cs index 40ee4b512..bee0a0aec 100644 --- a/Tests/TestRunners/SelfVerifyingTestFactory.cs +++ b/Tests/TestRunners/SelfVerifyingTestFactory.cs @@ -1,17 +1,12 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; -using System.Threading.Tasks; using CodeConverter.Tests.Compilation; -using CodeConverter.Tests.CSharp; using ICSharpCode.CodeConverter; -using ICSharpCode.CodeConverter.CSharp; using ICSharpCode.CodeConverter.Shared; using ICSharpCode.CodeConverter.Util; -using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Threading; using Xunit; using Xunit.Sdk; diff --git a/Tests/TestRunners/TestFileRewriter.cs b/Tests/TestRunners/TestFileRewriter.cs index 502fc77d4..956cbdfec 100644 --- a/Tests/TestRunners/TestFileRewriter.cs +++ b/Tests/TestRunners/TestFileRewriter.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Reflection; using System.Text; -using Xunit; namespace CodeConverter.Tests.TestRunners { diff --git a/Tests/TestRunners/XUnitFactDiscoverer.cs b/Tests/TestRunners/XUnitFactDiscoverer.cs index aa337677f..fd9e9c260 100644 --- a/Tests/TestRunners/XUnitFactDiscoverer.cs +++ b/Tests/TestRunners/XUnitFactDiscoverer.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; -using CodeConverter.Tests.CSharp; using Xunit; namespace CodeConverter.Tests.TestRunners diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index a9ea267ba..262586b85 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -2,10 +2,10 @@ net472 Library - CodeConverter.Tests - CodeConverter.Tests + ICsharpCode.CodeConverter.Tests + ICsharpCode.CodeConverter.Tests false - 7.3 + 8.0 $(NoWarn);1998 @@ -14,6 +14,7 @@ + @@ -22,13 +23,13 @@ - - - - + + + + diff --git a/Tests/VB/MemberTests.cs b/Tests/VB/MemberTests.cs index 2bb681eed..c5be12c28 100644 --- a/Tests/VB/MemberTests.cs +++ b/Tests/VB/MemberTests.cs @@ -1,11 +1,6 @@ using System.Threading.Tasks; using CodeConverter.Tests.TestRunners; -using ICSharpCode.CodeConverter; -using ICSharpCode.CodeConverter.CSharp; -using ICSharpCode.CodeConverter.Shared; using ICSharpCode.CodeConverter.VB; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.VisualBasic; using Xunit; namespace CodeConverter.Tests.VB diff --git a/Tests/VB/SpecialConversionTests.cs b/Tests/VB/SpecialConversionTests.cs index 9ea9f7629..624a60a18 100644 --- a/Tests/VB/SpecialConversionTests.cs +++ b/Tests/VB/SpecialConversionTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using CodeConverter.Tests.TestRunners; using Xunit; diff --git a/Tests/VB/StatementTests.cs b/Tests/VB/StatementTests.cs index 0c278a3bb..d97c4d41b 100644 --- a/Tests/VB/StatementTests.cs +++ b/Tests/VB/StatementTests.cs @@ -1,9 +1,6 @@ using System.Threading.Tasks; using CodeConverter.Tests.TestRunners; -using ICSharpCode.CodeConverter; -using ICSharpCode.CodeConverter.Shared; using ICSharpCode.CodeConverter.VB; -using Microsoft.CodeAnalysis; using Xunit; namespace CodeConverter.Tests.VB diff --git a/Vsix/Cancellation.cs b/Vsix/Cancellation.cs new file mode 100644 index 000000000..82602151b --- /dev/null +++ b/Vsix/Cancellation.cs @@ -0,0 +1,25 @@ +using System; +using System.Threading; + +namespace CodeConverter.VsExtension +{ + internal sealed class Cancellation : IDisposable + { + private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + private CancellationTokenSource _commandCancellationTokenSource = new CancellationTokenSource(); + + public CancellationTokenSource ResetCommandCancellation() + { + _commandCancellationTokenSource.Cancel(); + _commandCancellationTokenSource.Dispose(); + return _commandCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token); + } + + public CancellationToken CancelAll => _cancellationTokenSource.Token; + + public void Dispose() + { + _cancellationTokenSource.Cancel(); + } + } +} diff --git a/Vsix/CodeConversion.cs b/Vsix/CodeConversion.cs index 404873ea7..ba6152620 100644 --- a/Vsix/CodeConversion.cs +++ b/Vsix/CodeConversion.cs @@ -2,18 +2,16 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; +using System.Runtime.CompilerServices; +using System.Threading; using System.Threading.Tasks; using System.Windows; using ICSharpCode.CodeConverter; -using ICSharpCode.CodeConverter.CSharp; using ICSharpCode.CodeConverter.Shared; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServices; using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Threading; using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; using Project = EnvDTE.Project; @@ -30,16 +28,18 @@ internal class CodeConversion public static readonly string ConverterTitle = "Code converter"; private static readonly string Intro = Environment.NewLine + Environment.NewLine + new string(Enumerable.Repeat('-', 80).ToArray()) + Environment.NewLine; private readonly OutputWindow _outputWindow; + private readonly Cancellation _packageCancellation; + private string SolutionDir => Path.GetDirectoryName(_visualStudioWorkspace.CurrentSolution.FilePath); - public static async Task CreateAsync(REConverterPackage serviceProvider, VisualStudioWorkspace visualStudioWorkspace, Func> getOptions) + public static async Task CreateAsync(CodeConverterPackage serviceProvider, VisualStudioWorkspace visualStudioWorkspace, Func> getOptions) { - return new CodeConversion(serviceProvider, serviceProvider.JoinableTaskFactory, visualStudioWorkspace, + return new CodeConversion(serviceProvider, serviceProvider.JoinableTaskFactory, serviceProvider.PackageCancellation, visualStudioWorkspace, getOptions, await OutputWindow.CreateAsync()); } public CodeConversion(IAsyncServiceProvider serviceProvider, - JoinableTaskFactory joinableTaskFactory, VisualStudioWorkspace visualStudioWorkspace, + JoinableTaskFactory joinableTaskFactory, Cancellation packageCancellation, VisualStudioWorkspace visualStudioWorkspace, Func> getOptions, OutputWindow outputWindow) { GetOptions = getOptions; @@ -47,30 +47,42 @@ public CodeConversion(IAsyncServiceProvider serviceProvider, _joinableTaskFactory = joinableTaskFactory; _visualStudioWorkspace = visualStudioWorkspace; _outputWindow = outputWindow; + _packageCancellation = packageCancellation; } - public async Task PerformProjectConversionAsync(IReadOnlyCollection selectedProjects) where TLanguageConversion : ILanguageConversion, new() + public async Task ConvertProjectsAsync(IReadOnlyCollection selectedProjects, CancellationToken cancellationToken) where TLanguageConversion : ILanguageConversion, new() { - await _joinableTaskFactory.RunAsync(async () => { - var convertedFiles = ConvertProjectUnhandledAsync(selectedProjects); - await WriteConvertedFilesAndShowSummaryAsync(await convertedFiles); - }); + try { + await _joinableTaskFactory.RunAsync(async () => { + var convertedFiles = ConvertProjectUnhandled(selectedProjects, cancellationToken); + await WriteConvertedFilesAndShowSummaryAsync(convertedFiles); + }); + } catch (OperationCanceledException) { + if (!_packageCancellation.CancelAll.IsCancellationRequested) { + await _outputWindow.WriteToOutputWindowAsync("Previous conversion cancelled", forceShow: true); + } + } } - public async Task PerformDocumentConversionAsync(string documentFilePath, Span selected) where TLanguageConversion : ILanguageConversion, new() + public async Task ConvertDocumentAsync(string documentFilePath, Span selected, CancellationToken cancellationToken) where TLanguageConversion : ILanguageConversion, new() { - var conversionResult = await _joinableTaskFactory.RunAsync(async () => { - var result = await ConvertDocumentUnhandledAsync(documentFilePath, selected); - await WriteConvertedFilesAndShowSummaryAsync(new[] { result }); - return result; - }); - - if ((await GetOptions()).CopyResultToClipboardForSingleDocument) { - await SetClipboardTextOnUiThreadAsync(conversionResult.ConvertedCode ?? conversionResult.GetExceptionsAsString()); - await _outputWindow.WriteToOutputWindowAsync(Environment.NewLine + "Conversion result copied to clipboard."); - await VisualStudioInteraction.ShowMessageBoxAsync(_serviceProvider, "Conversion result copied to clipboard.", $"Conversion result copied to clipboard. {conversionResult.GetExceptionsAsString()}", false); + try { + var conversionResult = await _joinableTaskFactory.RunAsync(async () => { + var result = await ConvertDocumentUnhandledAsync(documentFilePath, selected, cancellationToken); + await WriteConvertedFilesAndShowSummaryAsync(new[] { result }.ToAsyncEnumerable()); + return result; + }); + + if ((await GetOptions()).CopyResultToClipboardForSingleDocument) { + await SetClipboardTextOnUiThreadAsync(conversionResult.ConvertedCode ?? conversionResult.GetExceptionsAsString()); + await _outputWindow.WriteToOutputWindowAsync(Environment.NewLine + "Conversion result copied to clipboard."); + await VisualStudioInteraction.ShowMessageBoxAsync(_serviceProvider, "Conversion result copied to clipboard.", $"Conversion result copied to clipboard. {conversionResult.GetExceptionsAsString()}", false); + } + } catch (OperationCanceledException) { + if (!_packageCancellation.CancelAll.IsCancellationRequested) { + await _outputWindow.WriteToOutputWindowAsync("Previous conversion cancelled", forceShow: true); + } } - } private static async Task SetClipboardTextOnUiThreadAsync(string conversionResultConvertedCode) @@ -80,16 +92,16 @@ private static async Task SetClipboardTextOnUiThreadAsync(string conversionResul await TaskScheduler.Default; } - private async Task WriteConvertedFilesAndShowSummaryAsync(IEnumerable convertedFiles) + private async Task WriteConvertedFilesAndShowSummaryAsync(IAsyncEnumerable convertedFiles) { - await _outputWindow.WriteToOutputWindowAsync(Intro, true, true); + await _outputWindow.WriteToOutputWindowAsync(Intro, forceShow: true); var files = new List(); var filesToOverwrite = new List(); var errors = new List(); string longestFilePath = null; var longestFileLength = -1; - foreach (var convertedFile in convertedFiles) { + await foreach (var convertedFile in convertedFiles) { if (convertedFile.SourcePathOrNull == null) continue; if (WillOverwriteSource(convertedFile)) { @@ -97,7 +109,11 @@ private async Task WriteConvertedFilesAndShowSummaryAsync(IEnumerable errors) - { - var exceptionsAsString = convertedFile.GetExceptionsAsString(); - var indentedException = exceptionsAsString.Replace(Environment.NewLine, Environment.NewLine + " ").TrimEnd(); - var targetPathRelativeToSolutionDir = PathRelativeToSolutionDir(convertedFile.TargetPathOrNull ?? "unknown"); - string output = $"* {targetPathRelativeToSolutionDir}"; - var containsErrors = !string.IsNullOrWhiteSpace(exceptionsAsString); - - if (containsErrors) { - errors.Add(exceptionsAsString); - } - - if (string.IsNullOrWhiteSpace(convertedFile.ConvertedCode)) - { - var sourcePathRelativeToSolutionDir = PathRelativeToSolutionDir(convertedFile.SourcePathOrNull ?? "unknown"); - output = $"* Failure processing {sourcePathRelativeToSolutionDir}{Environment.NewLine} {indentedException}"; - } - else if (containsErrors){ - output += $" contains errors{Environment.NewLine} {indentedException}"; - } - - await _outputWindow.WriteToOutputWindowAsync(Environment.NewLine + output); - } - private string PathRelativeToSolutionDir(string path) { return path.Replace(SolutionDir, "") @@ -218,7 +210,7 @@ private async Task GetConversionSummaryAsync(IReadOnlyCollection + Environment.NewLine; } - private async Task ConvertDocumentUnhandledAsync(string documentPath, Span selected) where TLanguageConversion : ILanguageConversion, new() + private async Task ConvertDocumentUnhandledAsync(string documentPath, Span selected, CancellationToken cancellationToken) where TLanguageConversion : ILanguageConversion, new() { await _outputWindow.WriteToOutputWindowAsync($"Converting {documentPath}...", true, true); @@ -226,14 +218,14 @@ private async Task GetConversionSummaryAsync(IReadOnlyCollection var documentId = _visualStudioWorkspace.CurrentSolution.GetDocumentIdsWithFilePath(documentPath).SingleOrDefault(); if (documentId == null) { //If document doesn't belong to any project - return await ConvertTextOnlyAsync(documentPath, selected); + return await ConvertTextOnlyAsync(documentPath, selected, cancellationToken); } var document = _visualStudioWorkspace.CurrentSolution.GetDocument(documentId); var selectedTextSpan = new TextSpan(selected.Start, selected.Length); - return await ProjectConversion.ConvertSingle(document, new SingleConversionOptions {SelectedTextSpan = selectedTextSpan}, CreateOutputWindowProgress()); + return await ProjectConversion.ConvertSingle(document, new SingleConversionOptions {SelectedTextSpan = selectedTextSpan}, CreateOutputWindowProgress(), cancellationToken); } - private async Task ConvertTextOnlyAsync(string documentPath, Span selected) + private async Task ConvertTextOnlyAsync(string documentPath, Span selected, CancellationToken cancellationToken) where TLanguageConversion : ILanguageConversion, new() { var documentText = File.ReadAllText(documentPath); @@ -242,19 +234,18 @@ private async Task ConvertTextOnlyAsync(s documentText = documentText.Substring(selected.Start, selected.Length); } - var convertTextOnly = await ProjectConversion.ConvertText(documentText, new TextConversionOptions(DefaultReferences.NetStandard2), CreateOutputWindowProgress()); + var convertTextOnly = await ProjectConversion.ConvertText(documentText, new TextConversionOptions(DefaultReferences.NetStandard2), CreateOutputWindowProgress(), cancellationToken); convertTextOnly.SourcePathOrNull = documentPath; return convertTextOnly; } - private async Task> ConvertProjectUnhandledAsync(IReadOnlyCollection selectedProjects) + private async IAsyncEnumerable ConvertProjectUnhandled(IReadOnlyCollection selectedProjects, [EnumeratorCancellation] CancellationToken cancellationToken) where TLanguageConversion : ILanguageConversion, new() { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - var projectDesc = selectedProjects.Count == 1 - ? selectedProjects.Single().Name - : selectedProjects.Count + " projects"; - await _outputWindow.WriteToOutputWindowAsync($"Converting {projectDesc}...", true, true); + if (selectedProjects.Count > 1) { + await _outputWindow.WriteToOutputWindowAsync($"Converting {selectedProjects.Count} projects...", true, true); + } var projectsByPath = _visualStudioWorkspace.CurrentSolution.Projects.ToLookup(p => p.FilePath, p => p); @@ -263,9 +254,10 @@ private async Task> ConvertProjectUnhandledAsync(projects, progress: CreateOutputWindowProgress()); + var solutionConverter = SolutionConverter.CreateFor(projects, progress: CreateOutputWindowProgress(), cancellationToken: cancellationToken); - return await solutionConverter.Convert(); + var results = solutionConverter.Convert(); + await foreach(var result in results) yield return result; } private Progress CreateOutputWindowProgress() diff --git a/Vsix/REConverterPackage.cs b/Vsix/CodeConverterPackage.cs similarity index 87% rename from Vsix/REConverterPackage.cs rename to Vsix/CodeConverterPackage.cs index b22e9f7f4..012b66fc3 100644 --- a/Vsix/REConverterPackage.cs +++ b/Vsix/CodeConverterPackage.cs @@ -10,7 +10,6 @@ using System.Threading; using System.Threading.Tasks; using System.Windows; -using Microsoft.VisualStudio; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.LanguageServices; using Microsoft.VisualStudio.Shell; @@ -65,7 +64,7 @@ namespace CodeConverter.VsExtension expression: "HasVbproj", termNames: new[] { "HasVbproj" }, termValues: new[] { "SolutionHasProjectCapability:VB" })] [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")] - public sealed class REConverterPackage : AsyncPackage + public sealed class CodeConverterPackage : AsyncPackage { /// /// ConvertCSToVBCommandPackage GUID string. @@ -82,11 +81,18 @@ public sealed class REConverterPackage : AsyncPackage public const string VbSolutionMenuVisibilityGuid = "3332e9e5-019c-4e93-b75a-2499f6f1cec6"; public const string ConvertableSolutionMenuVisibilityGuid = "8e7192d0-28b7-4fe7-8d84-82c1db98d459"; + internal Cancellation PackageCancellation { get; } = new Cancellation(); + + private readonly AssemblyName _thisAssemblyName; + private readonly AssemblyName[] _ourAssemblyNames; /// /// Initializes a new instance of package class. /// - public REConverterPackage() + public CodeConverterPackage() { + var thisAssembly = GetType().Assembly; + _thisAssemblyName = thisAssembly.GetName(); + _ourAssemblyNames = new[] { _thisAssemblyName }.Concat(thisAssembly.GetReferencedAssemblies().Where(a => a.Name.StartsWith("ICSharpCode"))).ToArray(); AppDomain.CurrentDomain.AssemblyResolve += LoadWithoutVersionForOurDependencies; // Inside this method you can place any initialization code that does not require // any Visual Studio service because at this point the package object is created but @@ -120,13 +126,13 @@ private static Assembly LoadAnyVersionOfAssembly(AssemblyName assemblyName) private bool IsThisExtensionRequestingAssembly() { - return GetPossibleRequestingAssemblies().Contains(GetType().Assembly); + return GetPossibleRequestingAssemblies().Any(requesting => _ourAssemblyNames.Any(assemblyName => Equals(requesting, assemblyName))); } - private IEnumerable GetPossibleRequestingAssemblies() + private IEnumerable GetPossibleRequestingAssemblies() { return new StackTrace().GetFrames().Select(f => f.GetMethod().DeclaringType?.Assembly) - .SkipWhile(a => a == GetType().Assembly); + .Select(a => a.GetName()).SkipWhile(a => Equals(a, _thisAssemblyName)); } /// @@ -138,12 +144,12 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke var oleMenuCommandService = await this.GetServiceAsync(); var componentModel = await this.GetServiceAsync(); - await JoinableTaskFactory.SwitchToMainThreadAsync(); + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); var visualStudioWorkspace = componentModel.GetService(); var codeConversion = await CodeConversion.CreateAsync(this, visualStudioWorkspace, this.GetDialogPageAsync); ConvertCSToVBCommand.Initialize(this, oleMenuCommandService, codeConversion); ConvertVBToCSCommand.Initialize(this, oleMenuCommandService, codeConversion); - + VisualStudioInteraction.Initialize(PackageCancellation); await TaskScheduler.Default; await base.InitializeAsync(cancellationToken, progress); } @@ -167,9 +173,15 @@ private static Version FullVsVersion { } } - internal OleMenuCommandWithBlockingStatus CreateCommand(Func callbackAsync, CommandID menuCommandId) + internal OleMenuCommandWithBlockingStatus CreateCommand(Func callbackAsync, CommandID menuCommandId) + { + return new OleMenuCommandWithBlockingStatus(JoinableTaskFactory, PackageCancellation, callbackAsync, menuCommandId); + } + + protected override void Dispose(bool disposing) { - return new OleMenuCommandWithBlockingStatus(JoinableTaskFactory, callbackAsync, menuCommandId); + PackageCancellation.Dispose(); + base.Dispose(disposing); } } } diff --git a/Vsix/ConvertCSToVBCommand.cs b/Vsix/ConvertCSToVBCommand.cs index 10f79362f..0cfb7a09f 100644 --- a/Vsix/ConvertCSToVBCommand.cs +++ b/Vsix/ConvertCSToVBCommand.cs @@ -1,16 +1,10 @@ using System; -using System.Collections.Generic; using System.ComponentModel.Design; -using System.IO; using System.Linq; -using System.Threading.Tasks; -using EnvDTE; -using ICSharpCode.CodeConverter.CSharp; +using System.Threading; using ICSharpCode.CodeConverter.VB; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Threading; using OleMenuCommand = Microsoft.VisualStudio.Shell.OleMenuCommand; using OleMenuCommandService = Microsoft.VisualStudio.Shell.OleMenuCommandService; using Task = System.Threading.Tasks.Task; @@ -37,7 +31,7 @@ internal sealed class ConvertCSToVBCommand /// /// VS Package that provides this command, not null. /// - private readonly REConverterPackage _package; + private readonly CodeConverterPackage _package; private readonly CodeConversion _codeConversion; @@ -61,7 +55,7 @@ private IAsyncServiceProvider ServiceProvider { /// /// Must be called from UI thread /// - public static void Initialize(REConverterPackage package, OleMenuCommandService menuCommandService, CodeConversion codeConversion) + public static void Initialize(CodeConverterPackage package, OleMenuCommandService menuCommandService, CodeConversion codeConversion) { ThreadHelper.ThrowIfNotOnUIThread(); Instance = new ConvertCSToVBCommand(package, codeConversion, menuCommandService); @@ -75,7 +69,7 @@ public static void Initialize(REConverterPackage package, OleMenuCommandService /// /// /// Must be called on the UI thread due to VS 2017's implementation of AddCommand which calls GetService - private ConvertCSToVBCommand(REConverterPackage package, CodeConversion codeConversion, OleMenuCommandService commandService) + private ConvertCSToVBCommand(CodeConverterPackage package, CodeConversion codeConversion, OleMenuCommandService commandService) { ThreadHelper.ThrowIfNotOnUIThread(); this._package = package ?? throw new ArgumentNullException(nameof(package)); @@ -153,37 +147,37 @@ private async Task SolutionOrProjectMenuItem_BeforeQueryStatusAsync(object sende } } - private async Task CodeEditorMenuItemCallbackAsync(object sender, EventArgs e) + private async Task CodeEditorMenuItemCallbackAsync(CancellationToken cancellationToken) { var (filePath, selection) = await VisualStudioInteraction.GetCurrentFilenameAndSelectionAsync(ServiceProvider, CodeConversion.IsCSFileName, false); if (filePath != null && selection != null) { - await ConvertDocumentAsync(filePath, selection.Value); + await ConvertDocumentAsync(filePath, selection.Value, cancellationToken); } } - private async Task ProjectItemMenuItemCallbackAsync(object sender, EventArgs e) + private async Task ProjectItemMenuItemCallbackAsync(CancellationToken cancellationToken) { string itemPath = await VisualStudioInteraction.GetSingleSelectedItemPathOrDefaultAsync(); - await ConvertDocumentAsync(itemPath, new Span(0, 0)); + await ConvertDocumentAsync(itemPath, new Span(0, 0), cancellationToken); } - private async Task SolutionOrProjectMenuItemCallbackAsync(object sender, EventArgs e) + private async Task SolutionOrProjectMenuItemCallbackAsync(CancellationToken cancellationToken) { try { var projects = VisualStudioInteraction.GetSelectedProjectsAsync(ProjectExtension); - await _codeConversion.PerformProjectConversionAsync(await projects); + await _codeConversion.ConvertProjectsAsync(await projects, cancellationToken); } catch (Exception ex) { await VisualStudioInteraction.ShowExceptionAsync(ServiceProvider, CodeConversion.ConverterTitle, ex); } } - private async Task ConvertDocumentAsync(string documentPath, Span selected) + private async Task ConvertDocumentAsync(string documentPath, Span selected, CancellationToken cancellationToken) { if (documentPath == null || !CodeConversion.IsCSFileName(documentPath)) return; try { - await _codeConversion.PerformDocumentConversionAsync(documentPath, selected); + await _codeConversion.ConvertDocumentAsync(documentPath, selected, cancellationToken); } catch (Exception ex) { await VisualStudioInteraction.ShowExceptionAsync(ServiceProvider, CodeConversion.ConverterTitle, ex); } diff --git a/Vsix/ConvertVBToCSCommand.cs b/Vsix/ConvertVBToCSCommand.cs index f5fd94c32..82f5497ef 100644 --- a/Vsix/ConvertVBToCSCommand.cs +++ b/Vsix/ConvertVBToCSCommand.cs @@ -1,15 +1,10 @@ using System; -using System.Collections.Generic; using System.ComponentModel.Design; -using System.IO; using System.Linq; -using System.Threading.Tasks; -using EnvDTE; +using System.Threading; using ICSharpCode.CodeConverter.CSharp; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Threading; using OleMenuCommand = Microsoft.VisualStudio.Shell.OleMenuCommand; using OleMenuCommandService = Microsoft.VisualStudio.Shell.OleMenuCommandService; using Task = System.Threading.Tasks.Task; @@ -36,7 +31,7 @@ internal sealed class ConvertVBToCSCommand /// /// VS Package that provides this command, not null. /// - private readonly REConverterPackage _package; + private readonly CodeConverterPackage _package; private readonly CodeConversion _codeConversion; @@ -60,7 +55,7 @@ private IAsyncServiceProvider ServiceProvider { /// /// Must be called from UI thread /// - public static void Initialize(REConverterPackage package, OleMenuCommandService menuCommandService, CodeConversion codeConversion) + public static void Initialize(CodeConverterPackage package, OleMenuCommandService menuCommandService, CodeConversion codeConversion) { ThreadHelper.ThrowIfNotOnUIThread(); Instance = new ConvertVBToCSCommand(package, codeConversion, menuCommandService); @@ -74,7 +69,7 @@ public static void Initialize(REConverterPackage package, OleMenuCommandService /// /// /// Must be called on the UI thread due to VS 2017's implementation of AddCommand which calls GetService - private ConvertVBToCSCommand(REConverterPackage package, CodeConversion codeConversion, OleMenuCommandService commandService) + private ConvertVBToCSCommand(CodeConverterPackage package, CodeConversion codeConversion, OleMenuCommandService commandService) { ThreadHelper.ThrowIfNotOnUIThread(); this._package = package ?? throw new ArgumentNullException(nameof(package)); @@ -152,37 +147,37 @@ private async Task SolutionOrProjectMenuItem_BeforeQueryStatusAsync(object sende } } - private async Task CodeEditorMenuItemCallbackAsync(object sender, EventArgs e) + private async Task CodeEditorMenuItemCallbackAsync(CancellationToken cancellationToken) { var (filePath, selection) = await VisualStudioInteraction.GetCurrentFilenameAndSelectionAsync(ServiceProvider, CodeConversion.IsVBFileName, false); if (filePath != null && selection != null) { - await ConvertDocumentAsync(filePath, selection.Value); + await ConvertDocumentAsync(filePath, selection.Value, cancellationToken); } } - private async Task ProjectItemMenuItemCallbackAsync(object sender, EventArgs e) + private async Task ProjectItemMenuItemCallbackAsync(CancellationToken cancellationToken) { string itemPath = await VisualStudioInteraction.GetSingleSelectedItemPathOrDefaultAsync(); - await ConvertDocumentAsync(itemPath, new Span(0, 0)); + await ConvertDocumentAsync(itemPath, new Span(0, 0), cancellationToken); } - private async Task SolutionOrProjectMenuItemCallbackAsync(object sender, EventArgs e) + private async Task SolutionOrProjectMenuItemCallbackAsync(CancellationToken cancellationToken) { try { var projects = VisualStudioInteraction.GetSelectedProjectsAsync(ProjectExtension); - await _codeConversion.PerformProjectConversionAsync(await projects); + await _codeConversion.ConvertProjectsAsync(await projects, cancellationToken); } catch (Exception ex) { await VisualStudioInteraction.ShowExceptionAsync(ServiceProvider, CodeConversion.ConverterTitle, ex); } } - private async Task ConvertDocumentAsync(string documentPath, Span selected) + private async Task ConvertDocumentAsync(string documentPath, Span selected, CancellationToken cancellationToken) { if (documentPath == null || !CodeConversion.IsVBFileName(documentPath)) return; try { - await _codeConversion.PerformDocumentConversionAsync(documentPath, selected); + await _codeConversion.ConvertDocumentAsync(documentPath, selected, cancellationToken); } catch (Exception ex) { await VisualStudioInteraction.ShowExceptionAsync(ServiceProvider, CodeConversion.ConverterTitle, ex); } diff --git a/Vsix/OleMenuCommandWithBlockingStatus.cs b/Vsix/OleMenuCommandWithBlockingStatus.cs index 712e98342..d5cba9da7 100644 --- a/Vsix/OleMenuCommandWithBlockingStatus.cs +++ b/Vsix/OleMenuCommandWithBlockingStatus.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.Design; +using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Threading; @@ -12,17 +13,19 @@ internal class OleMenuCommandWithBlockingStatus private readonly JoinableTaskFactory _joinableTaskFactory; private readonly OleMenuCommand _command; - public OleMenuCommandWithBlockingStatus(JoinableTaskFactory joinableTaskFactory, Func callbackAsync, CommandID menuCommandId) + public OleMenuCommandWithBlockingStatus(JoinableTaskFactory joinableTaskFactory, Cancellation _packageCancellation, Func callbackAsync, CommandID menuCommandId) { _joinableTaskFactory = joinableTaskFactory; _command = new OleMenuCommand(Execute, menuCommandId); void Execute(object sender, EventArgs eventArgs) { + var cancellationTokenSource = _packageCancellation.ResetCommandCancellation(); + async Task ExecuteAsync() { await TaskScheduler.Default; - await callbackAsync(sender, eventArgs); + await callbackAsync(cancellationTokenSource.Token); } _joinableTaskFactory.RunAsync(ExecuteAsync).Task.Forget(); } @@ -37,6 +40,7 @@ public static implicit operator OleMenuCommand(OleMenuCommandWithBlockingStatus { return oleMenuCommandWithBlockingCommand._command; } + private EventHandler WaitFor(Func task) { return (o, a) => { diff --git a/Vsix/OutputWindow.cs b/Vsix/OutputWindow.cs index edf5b8ca4..9f8cbded4 100644 --- a/Vsix/OutputWindow.cs +++ b/Vsix/OutputWindow.cs @@ -1,5 +1,4 @@ using System; -using System.Text; using System.Threading.Tasks; using EnvDTE; using Microsoft.VisualStudio.Shell; @@ -50,7 +49,7 @@ public OutputWindow(IVsOutputWindowPane outputPaneAsync) _outputPane = outputPaneAsync; _solutionEvents = VisualStudioInteraction.Dte.Events.SolutionEvents; - _solutionEvents.Opened += OnSolutionOpened; + _solutionEvents.Opened += () => OnSolutionOpenedAsync().Forget(); } public async Task ClearAsync() @@ -85,9 +84,7 @@ private void ForceShowInner() _outputPane.Activate(); } -#pragma warning disable VSTHRD100 // Avoid async void methods - fire and forget event handler - private async void OnSolutionOpened() -#pragma warning restore VSTHRD100 // Avoid async void methods + private async Task OnSolutionOpenedAsync() { if (_hasOutputSinceSolutionOpened) await ForceShowOutputPaneAsync(); _hasOutputSinceSolutionOpened = false; diff --git a/Vsix/REConverterPackage.vsct b/Vsix/REConverterPackage.vsct index b7be84574..3e8d5c0da 100644 --- a/Vsix/REConverterPackage.vsct +++ b/Vsix/REConverterPackage.vsct @@ -4,57 +4,57 @@ - + - + - + - + - - + + - - - - - - - - - -