| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <!-- Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. --> | ||
| <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
| <Import Project="..\..\..\..\packages\Microsoft.Net.ToolsetCompilers.0.7.4092303-beta\build\Microsoft.Net.ToolsetCompilers.props" Condition="Exists('..\..\..\..\packages\Microsoft.Net.ToolsetCompilers.0.7.4092303-beta\build\Microsoft.Net.ToolsetCompilers.props')" /> | ||
| <ImportGroup Label="Settings"> | ||
| <Import Project="..\..\..\Tools\Microsoft.CodeAnalysis.Toolset.Open\Targets\VSL.Settings.targets" /> | ||
| </ImportGroup> | ||
| <PropertyGroup> | ||
| <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> | ||
| <OutDir>..\..\..\..\Binaries\$(Configuration)\</OutDir> | ||
| <Platform Condition="'$(Platform)' == ''">AnyCPU</Platform> | ||
| <ProjectGuid>{687DAFFD-9BD9-4331-96B7-483B941EDEAA}</ProjectGuid> | ||
| <OutputType>Library</OutputType> | ||
| <RootNamespace>Microsoft.CodeAnalysis.CSharp</RootNamespace> | ||
| <AssemblyName>Microsoft.CodeAnalysis.CSharp.Workspaces.Desktop</AssemblyName> | ||
| <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||
| <SolutionDir Condition="'$(SolutionDir)' == '' OR '$(SolutionDir)' == '*Undefined*'">..\..\..\</SolutionDir> | ||
| <RestorePackages>true</RestorePackages> | ||
| </PropertyGroup> | ||
| <ItemGroup Label="File References"> | ||
| <Reference Include="Microsoft.Build, Version=$(VisualStudioReferenceAssemblyVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> | ||
| <Reference Include="Microsoft.Build.Framework, Version=$(VisualStudioReferenceAssemblyVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> | ||
| <Reference Include="Microsoft.Build.Tasks.$(MSBuildAssemblyNameFragment), Version=$(VisualStudioReferenceAssemblyVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> | ||
| <Reference Include="System.Collections.Immutable, Version=1.1.20.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | ||
| <SpecificVersion>False</SpecificVersion> | ||
| <HintPath>..\..\..\..\packages\Microsoft.Bcl.Immutable.1.1.20-beta\lib\portable-net45+win8\System.Collections.Immutable.dll</HintPath> | ||
| </Reference> | ||
| <Reference Include="System.Composition.AttributedModel, Version=1.0.27.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | ||
| <SpecificVersion>False</SpecificVersion> | ||
| <HintPath>..\..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.AttributedModel.dll</HintPath> | ||
| </Reference> | ||
| <Reference Include="System.Composition.Convention, Version=1.0.27.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | ||
| <SpecificVersion>False</SpecificVersion> | ||
| <HintPath>..\..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Convention.dll</HintPath> | ||
| </Reference> | ||
| <Reference Include="System.Composition.Hosting, Version=1.0.27.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | ||
| <SpecificVersion>False</SpecificVersion> | ||
| <HintPath>..\..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Hosting.dll</HintPath> | ||
| </Reference> | ||
| <Reference Include="System.Composition.Runtime, Version=1.0.27.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | ||
| <SpecificVersion>False</SpecificVersion> | ||
| <HintPath>..\..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Runtime.dll</HintPath> | ||
| </Reference> | ||
| <Reference Include="System.Composition.TypedParts, Version=1.0.27.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | ||
| <SpecificVersion>False</SpecificVersion> | ||
| <HintPath>..\..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.TypedParts.dll</HintPath> | ||
| </Reference> | ||
| </ItemGroup> | ||
| <ItemGroup Label="Project References"> | ||
| <ProjectReference Include="..\..\..\Compilers\Core\Desktop\CodeAnalysis.Desktop.csproj"> | ||
| <Project>{dfa21ca1-7f96-47ee-940c-069858e81727}</Project> | ||
| <Name>CodeAnalysis.Desktop</Name> | ||
| </ProjectReference> | ||
| <ProjectReference Include="..\..\..\Compilers\Core\Portable\CodeAnalysis.csproj"> | ||
| <Project>{1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}</Project> | ||
| <Name>CodeAnalysis</Name> | ||
| </ProjectReference> | ||
| <ProjectReference Include="..\..\..\Compilers\CSharp\Desktop\CSharpCodeAnalysis.Desktop.csproj"> | ||
| <Project>{079af8ef-1058-48b6-943f-ab02d39e0641}</Project> | ||
| <Name>CSharpCodeAnalysis.Desktop</Name> | ||
| </ProjectReference> | ||
| <ProjectReference Include="..\..\..\Compilers\CSharp\Portable\CSharpCodeAnalysis.csproj"> | ||
| <Project>{B501A547-C911-4A05-AC6E-274A50DFF30E}</Project> | ||
| <Name>CSharpCodeAnalysis</Name> | ||
| </ProjectReference> | ||
| <ProjectReference Include="..\..\Core\Desktop\Workspaces.Desktop.csproj"> | ||
| <Project>{2e87fa96-50bb-4607-8676-46521599f998}</Project> | ||
| <Name>Workspaces.Desktop</Name> | ||
| </ProjectReference> | ||
| <ProjectReference Include="..\..\Core\Portable\Workspaces.csproj"> | ||
| <Project>{5F8D2414-064A-4B3A-9B42-8E2A04246BE5}</Project> | ||
| <Name>Workspaces</Name> | ||
| </ProjectReference> | ||
| <ProjectReference Include="..\Portable\CSharpWorkspace.csproj"> | ||
| <Project>{21b239d0-d144-430f-a394-c066d58ee267}</Project> | ||
| <Name>CSharpWorkspace</Name> | ||
| </ProjectReference> | ||
| </ItemGroup> | ||
| <ItemGroup> | ||
| <Compile Include="LanguageServices\CSharpCommandLineArgumentsFactoryService.cs" /> | ||
| <Compile Include="MSBuild\CSharpProjectFileLoader.cs" /> | ||
| <Compile Include="MSBuild\CSharpProjectFileLoader.CSharpProjectFile.cs" /> | ||
| <Compile Include="MSBuild\CSharpProjectFileLoaderFactory.cs" /> | ||
| </ItemGroup> | ||
| <ItemGroup> | ||
| <None Include="packages.config" /> | ||
| </ItemGroup> | ||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'"></PropertyGroup> | ||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'"></PropertyGroup> | ||
| <ImportGroup Label="Targets"> | ||
| <Import Project="..\..\..\Tools\Microsoft.CodeAnalysis.Toolset.Open\Targets\VSL.Imports.targets" /> | ||
| <Import Project="..\..\..\..\packages\StyleCop.MSBuild.4.7.48.2\build\StyleCop.MSBuild.Targets" Condition="Exists('..\..\..\..\packages\StyleCop.MSBuild.4.7.48.2\build\StyleCop.MSBuild.Targets')" /> | ||
| <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" /> | ||
| </ImportGroup> | ||
| <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> | ||
| <PropertyGroup> | ||
| <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> | ||
| </PropertyGroup> | ||
| <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" /> | ||
| <Error Condition="!Exists('..\..\..\..\packages\Microsoft.Net.ToolsetCompilers.0.7.4092303-beta\build\Microsoft.Net.ToolsetCompilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Net.ToolsetCompilers.0.7.4092303-beta\build\Microsoft.Net.ToolsetCompilers.props'))" /> | ||
| </Target> | ||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <packages> | ||
| <package id="Microsoft.Bcl.Immutable" version="1.1.20-beta" targetFramework="net45" /> | ||
| <package id="Microsoft.Composition" version="1.0.27" targetFramework="net45" /> | ||
| </packages> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| using Roslyn.Compilers.CSharp; | ||
|
|
||
| namespace Roslyn.Services.Editor.CSharp.Extensions | ||
| { | ||
| internal static class AssemblySymbolExtensions | ||
| { | ||
| public static bool HasInternalAccessTo(this AssemblySymbol fromAssembly, AssemblySymbol toAssembly) | ||
| { | ||
| if (Equals(fromAssembly, toAssembly)) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| // checks if fromAssembly has friend assembly access to the internals in toAssembly | ||
| // TODO(cyrusn): defer to the compiler function that computes this. | ||
| return false; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| using Roslyn.Compilers.CSharp; | ||
|
|
||
| namespace Roslyn.Services.CSharp.Extensions | ||
| { | ||
| internal static class AttributeDeclarationSyntaxExtensions | ||
| { | ||
| public static bool IsAssemblyAttribute(this AttributeListSyntax attribute) | ||
| { | ||
| return | ||
| attribute != null && | ||
| attribute.Target != null && | ||
| attribute.Target.Identifier.Kind == SyntaxKind.AssemblyKeyword; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| using System.Threading; | ||
| using Microsoft.CodeAnalysis.CSharp; | ||
| using Microsoft.CodeAnalysis.CSharp.Semantics; | ||
| using Microsoft.CodeAnalysis.CSharp.Symbols; | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
| using Microsoft.CodeAnalysis.Simplification; | ||
| using Microsoft.CodeAnalysis.Text; | ||
|
|
||
| namespace Microsoft.CodeAnalysis.CSharp.Extensions | ||
| { | ||
| internal static class AttributeSyntaxExtensions | ||
| { | ||
| public static AttributeSyntax MakeSemanticallyExplicit( | ||
| this AttributeSyntax statement, | ||
| Document document, | ||
| CancellationToken cancellationToken = default(CancellationToken)) | ||
| { | ||
| return (AttributeSyntax)Simplifier.Expand(document, statement, cancellationToken); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| // Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
|
||
| using Microsoft.CodeAnalysis.CSharp; | ||
| using Microsoft.CodeAnalysis.CSharp.Symbols; | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
| using Microsoft.CodeAnalysis.Text; | ||
|
|
||
| namespace Microsoft.CodeAnalysis.CSharp.Extensions | ||
| { | ||
| internal static class BinaryExpressionSyntaxExtensions | ||
| { | ||
| public static bool IsAnyAssignExpression(this BinaryExpressionSyntax binaryExpression) | ||
| { | ||
| if (binaryExpression != null) | ||
| { | ||
| switch (binaryExpression.CSharpKind()) | ||
| { | ||
| case SyntaxKind.SimpleAssignmentExpression: | ||
| case SyntaxKind.AddAssignmentExpression: | ||
| case SyntaxKind.SubtractAssignmentExpression: | ||
| case SyntaxKind.MultiplyAssignmentExpression: | ||
| case SyntaxKind.DivideAssignmentExpression: | ||
| case SyntaxKind.ModuloAssignmentExpression: | ||
| case SyntaxKind.AndAssignmentExpression: | ||
| case SyntaxKind.ExclusiveOrAssignmentExpression: | ||
| case SyntaxKind.OrAssignmentExpression: | ||
| case SyntaxKind.LeftShiftAssignmentExpression: | ||
| case SyntaxKind.RightShiftAssignmentExpression: | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| public static bool IsCompoundAssignExpression(this BinaryExpressionSyntax binaryExpression) | ||
| { | ||
| if (binaryExpression != null) | ||
| { | ||
| switch (binaryExpression.CSharpKind()) | ||
| { | ||
| case SyntaxKind.AddAssignmentExpression: | ||
| case SyntaxKind.SubtractAssignmentExpression: | ||
| case SyntaxKind.MultiplyAssignmentExpression: | ||
| case SyntaxKind.DivideAssignmentExpression: | ||
| case SyntaxKind.ModuloAssignmentExpression: | ||
| case SyntaxKind.AndAssignmentExpression: | ||
| case SyntaxKind.ExclusiveOrAssignmentExpression: | ||
| case SyntaxKind.OrAssignmentExpression: | ||
| case SyntaxKind.LeftShiftAssignmentExpression: | ||
| case SyntaxKind.RightShiftAssignmentExpression: | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| // Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
|
||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using Microsoft.CodeAnalysis.CSharp.Symbols; | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
| using Microsoft.CodeAnalysis.Shared.Extensions; | ||
|
|
||
| namespace Microsoft.CodeAnalysis.CSharp.Extensions | ||
| { | ||
| internal static class CommonSyntaxNodeExtensions | ||
| { | ||
| public static bool IsParentKind(this SyntaxNode node, SyntaxKind kind) | ||
| { | ||
| return node != null && node.Parent.IsKind(kind); | ||
| } | ||
|
|
||
| public static bool MatchesKind(this SyntaxNode node, SyntaxKind kind) | ||
| { | ||
| if (node == null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| return node.IsKind(kind); | ||
| } | ||
|
|
||
| public static bool MatchesKind(this SyntaxNode node, SyntaxKind kind1, SyntaxKind kind2) | ||
| { | ||
| if (node == null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| return node.IsKind(kind1) || node.IsKind(kind2); | ||
| } | ||
|
|
||
| public static bool MatchesKind(this SyntaxNode node, params SyntaxKind[] kinds) | ||
| { | ||
| if (node == null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| return kinds.Contains(node.CSharpKind()); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns the list of using directives that affect 'node'. The list will be returned in | ||
| /// top down order. | ||
| /// </summary> | ||
| public static IEnumerable<UsingDirectiveSyntax> GetEnclosingUsingDirectives(this SyntaxNode node) | ||
| { | ||
| return node.GetAncestorOrThis<CompilationUnitSyntax>().Usings | ||
| .Concat(node.GetAncestorsOrThis<NamespaceDeclarationSyntax>() | ||
| .Reverse() | ||
| .SelectMany(n => n.Usings)); | ||
| } | ||
|
|
||
| public static bool IsUnsafeContext(this SyntaxNode node) | ||
| { | ||
| if (node.GetAncestor<UnsafeStatementSyntax>() != null) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| return node.GetAncestors<MemberDeclarationSyntax>().Any( | ||
| m => m.GetModifiers().Any(SyntaxKind.UnsafeKeyword)); | ||
| } | ||
|
|
||
| public static bool IsInStaticContext(this SyntaxNode node) | ||
| { | ||
| // this/base calls are always static. | ||
| if (node.FirstAncestorOrSelf<ConstructorInitializerSyntax>() != null) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| var memberDeclaration = node.FirstAncestorOrSelf<MemberDeclarationSyntax>(); | ||
| if (memberDeclaration == null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| switch (memberDeclaration.CSharpKind()) | ||
| { | ||
| case SyntaxKind.MethodDeclaration: | ||
| case SyntaxKind.ConstructorDeclaration: | ||
| case SyntaxKind.PropertyDeclaration: | ||
| case SyntaxKind.EventDeclaration: | ||
| case SyntaxKind.IndexerDeclaration: | ||
| return memberDeclaration.GetModifiers().Any(SyntaxKind.StaticKeyword); | ||
|
|
||
| case SyntaxKind.FieldDeclaration: | ||
| // Inside a field one can only access static members of a type. | ||
| return true; | ||
|
|
||
| case SyntaxKind.DestructorDeclaration: | ||
| return false; | ||
| } | ||
|
|
||
| // Global statements are not a static context. | ||
| if (node.FirstAncestorOrSelf<GlobalStatementSyntax>() != null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| // any other location is considered static | ||
| return true; | ||
| } | ||
|
|
||
| public static NamespaceDeclarationSyntax GetInnermostNamespaceDeclarationWithUsings(this SyntaxNode contextNode) | ||
| { | ||
| return contextNode.GetAncestorsOrThis<NamespaceDeclarationSyntax>().FirstOrDefault(n => n.Usings.Count > 0); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| using Microsoft.CodeAnalysis.Common; | ||
|
|
||
| namespace Microsoft.CodeAnalysis.CSharp.Extensions | ||
| { | ||
| internal static class CommonSyntaxTokenExtensions | ||
| { | ||
| #if REMOVE | ||
| public static bool IsKindOrHasMatchingText(this SyntaxToken token, SyntaxKind kind) | ||
| { | ||
| return ((SyntaxToken)token).IsKindOrHasMatchingText(kind); | ||
| } | ||
|
|
||
| public static bool HasMatchingText(this SyntaxToken token, SyntaxKind kind) | ||
| { | ||
| return ((SyntaxToken)token).HasMatchingText(kind); | ||
| } | ||
|
|
||
| public static bool IsParentKind(this SyntaxToken token, SyntaxKind kind) | ||
| { | ||
| return ((SyntaxToken)token).IsParentKind(kind); | ||
| } | ||
|
|
||
| public static bool MatchesKind(this SyntaxToken token, SyntaxKind kind) | ||
| { | ||
| return ((SyntaxToken)token).MatchesKind(kind); | ||
| } | ||
|
|
||
| public static bool MatchesKind(this SyntaxToken token, SyntaxKind kind1, SyntaxKind kind2) | ||
| { | ||
| return ((SyntaxToken)token).MatchesKind(kind1, kind2); | ||
| } | ||
|
|
||
| public static bool MatchesKind(this SyntaxToken token, params SyntaxKind[] kinds) | ||
| { | ||
| return ((SyntaxToken)token).MatchesKind(kinds); | ||
| } | ||
| #endif | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using Roslyn.Compilers; | ||
| using Roslyn.Compilers.CSharp; | ||
| using Roslyn.Services.Editor.CSharp.Utilities; | ||
| using Roslyn.Services.Shared.Extensions; | ||
| using Roslyn.Utilities; | ||
|
|
||
| namespace Roslyn.Services.Editor.CSharp.Extensions | ||
| { | ||
| internal static partial class CompilationExtensions | ||
| { | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| using System.Threading; | ||
| using Microsoft.CodeAnalysis.CSharp; | ||
| using Microsoft.CodeAnalysis.CSharp.Semantics; | ||
| using Microsoft.CodeAnalysis.CSharp.Symbols; | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
| using Microsoft.CodeAnalysis.Simplification; | ||
| using Microsoft.CodeAnalysis.Text; | ||
|
|
||
| namespace Microsoft.CodeAnalysis.CSharp.Extensions | ||
| { | ||
| internal static class ConstructorInitializerSyntaxExtensions | ||
| { | ||
| public static ConstructorInitializerSyntax MakeSemanticallyExplicit( | ||
| this ConstructorInitializerSyntax statement, | ||
| Document document, | ||
| CancellationToken cancellationToken = default(CancellationToken)) | ||
| { | ||
| return (ConstructorInitializerSyntax)Simplifier.Expand(document, statement, cancellationToken); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| using System.Collections.Generic; | ||
| using Roslyn.Compilers.CSharp; | ||
|
|
||
| namespace Roslyn.Services.CSharp.Extensions | ||
| { | ||
| internal static partial class ExpressionSyntaxExtensions | ||
| { | ||
| private class LocalReferenceFinder : SyntaxWalker | ||
| { | ||
| private readonly string name; | ||
| private readonly List<IdentifierNameSyntax> references; | ||
|
|
||
| private LocalReferenceFinder(string name) | ||
| { | ||
| this.name = name; | ||
| this.references = new List<IdentifierNameSyntax>(); | ||
| } | ||
|
|
||
| public override void VisitIdentifierName(IdentifierNameSyntax node) | ||
| { | ||
| if (node.Identifier.GetText() == name) | ||
| { | ||
| references.Add(node); | ||
| } | ||
| } | ||
|
|
||
| public static IEnumerable<IdentifierNameSyntax> Search(string name, SyntaxNode scope) | ||
| { | ||
| var finder = new LocalReferenceFinder(name); | ||
| finder.Visit(scope); | ||
| return finder.references; | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| using System.Threading; | ||
| using Roslyn.Compilers.CSharp; | ||
| using Roslyn.Services; | ||
| using Roslyn.Services.Editor; | ||
|
|
||
| namespace Roslyn.Services.Editor.CSharp | ||
| { | ||
| internal interface ICSharpSemanticSnapshot | ||
| { | ||
| IDocument Document { get; } | ||
| SemanticModel GetSemanticModel(CancellationToken cancellationToken = default(CancellationToken)); | ||
| } | ||
|
|
||
| internal static class ICSharpSemanticSnapshotExtensions | ||
| { | ||
| public static SyntaxTree GetSyntaxTree(this ICSharpSemanticSnapshot snapshot, CancellationToken cancellationToken) | ||
| { | ||
| return (SyntaxTree)snapshot.Document.GetSyntaxTree(cancellationToken); | ||
| } | ||
|
|
||
| public static Compilation GetCompilation(this ICSharpSemanticSnapshot snapshot, CancellationToken cancellationToken) | ||
| { | ||
| return (Compilation)snapshot.Document.Project.GetCompilation(cancellationToken); | ||
| } | ||
|
|
||
| public static ISolution GetSolution(this ICSharpSemanticSnapshot snapshot) | ||
| { | ||
| return snapshot.Document.Project.Solution; | ||
| } | ||
|
|
||
| public static IProject GetProject(this ICSharpSemanticSnapshot snapshot) | ||
| { | ||
| return snapshot.Document.Project; | ||
| } | ||
|
|
||
| public static TService GetService<TService>(this ICSharpSemanticSnapshot snapshot) where TService : ILanguageService | ||
| { | ||
| return snapshot.Document.LanguageServices.GetService<TService>(); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| using Roslyn.Compilers; | ||
| using Roslyn.Compilers.Common; | ||
|
|
||
| namespace Roslyn.Services.CSharp.Extensions | ||
| { | ||
| internal static class INamespaceOrTypeSymbolExtensions | ||
| { | ||
| public static bool IsBuiltInType(this INamespaceOrTypeSymbol symbol) | ||
| { | ||
| var typeSymbol = symbol as ITypeSymbol; | ||
| if (typeSymbol != null) | ||
| { | ||
| switch (typeSymbol.SpecialType) | ||
| { | ||
| case SpecialType.System_Object: | ||
| case SpecialType.System_Void: | ||
| case SpecialType.System_Boolean: | ||
| case SpecialType.System_SByte: | ||
| case SpecialType.System_Byte: | ||
| case SpecialType.System_Decimal: | ||
| case SpecialType.System_Single: | ||
| case SpecialType.System_Double: | ||
| case SpecialType.System_Int16: | ||
| case SpecialType.System_Int32: | ||
| case SpecialType.System_Int64: | ||
| case SpecialType.System_Char: | ||
| case SpecialType.System_String: | ||
| case SpecialType.System_UInt16: | ||
| case SpecialType.System_UInt32: | ||
| case SpecialType.System_UInt64: | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| using System.Linq; | ||
|
|
||
| namespace Microsoft.CodeAnalysis.CSharp.Extensions | ||
| { | ||
| internal static class ISymbolExtensions | ||
| { | ||
| public static bool IsKind(this ISymbol symbol, SymbolKind kind) | ||
| { | ||
| return symbol.MatchesKind(kind); | ||
| } | ||
|
|
||
| public static bool MatchesKind(this ISymbol symbol, SymbolKind kind) | ||
| { | ||
| var csharpSymbol = symbol as Symbol; | ||
| if (csharpSymbol == null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| return csharpSymbol.Kind == kind; | ||
| } | ||
|
|
||
| public static bool MatchesKind(this ISymbol symbol, SymbolKind kind1, SymbolKind kind2) | ||
| { | ||
| var csharpSymbol = symbol as Symbol; | ||
| if (csharpSymbol == null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| return csharpSymbol.Kind == kind1 | ||
| || csharpSymbol.Kind == kind2; | ||
| } | ||
|
|
||
| public static bool MatchesKind(this ISymbol symbol, SymbolKind kind1, SymbolKind kind2, SymbolKind kind3) | ||
| { | ||
| var csharpSymbol = symbol as Symbol; | ||
| if (csharpSymbol == null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| return csharpSymbol.Kind == kind1 | ||
| || csharpSymbol.Kind == kind2 | ||
| || csharpSymbol.Kind == kind3; | ||
| } | ||
|
|
||
| public static bool MatchesKind(this ISymbol symbol, params SymbolKind[] kinds) | ||
| { | ||
| var csharpSymbol = symbol as Symbol; | ||
| if (csharpSymbol == null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| return kinds.Contains(csharpSymbol.Kind); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| using Roslyn.Compilers.Common; | ||
| using Roslyn.Compilers.CSharp; | ||
|
|
||
| namespace Roslyn.Services.CSharp.Extensions | ||
| { | ||
| internal static class InvocationExpressionSyntaxExtensions | ||
| { | ||
| public static bool IsDelegateInvocation(this InvocationExpressionSyntax invocation, ISemanticModel semanticModel) | ||
| { | ||
| var namedType = semanticModel.GetTypeInfo(invocation.Expression).Type as INamedTypeSymbol; | ||
|
|
||
| return namedType != null | ||
| && namedType.TypeKind == CommonTypeKind.Delegate; | ||
| } | ||
|
|
||
| public static IMethodSymbol GetDelegateInvokeMethod(this InvocationExpressionSyntax invocation, ISemanticModel semanticModel) | ||
| { | ||
| var namedType = semanticModel.GetTypeInfo(invocation.Expression).Type as INamedTypeSymbol; | ||
| if (namedType == null || namedType.TypeKind != CommonTypeKind.Delegate) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| return namedType.DelegateInvokeMethod; | ||
| } | ||
|
|
||
| public static bool IsExtensionMethodInstanceInvocation(this InvocationExpressionSyntax invocation, ISemanticModel semanticModel) | ||
| { | ||
| var memberAccess = invocation.Expression as MemberAccessExpressionSyntax; | ||
| if (memberAccess == null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| var semanticInfo = semanticModel.GetSymbolInfo(memberAccess.Expression); | ||
| if (semanticInfo.Symbol != null) | ||
| { | ||
| switch (semanticInfo.Symbol.Kind) | ||
| { | ||
| case CommonSymbolKind.NamedType: | ||
| case CommonSymbolKind.Alias: | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| using System.Threading; | ||
| using Microsoft.CodeAnalysis.CSharp; | ||
| using Microsoft.CodeAnalysis.CSharp.Semantics; | ||
| using Microsoft.CodeAnalysis.CSharp.Symbols; | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
| using Microsoft.CodeAnalysis.Text; | ||
|
|
||
| namespace Microsoft.CodeAnalysis.CSharp.Extensions | ||
| { | ||
| internal static class LocationExtensions | ||
| { | ||
| public static SyntaxToken FindToken(this Location location, CancellationToken cancellationToken) | ||
| { | ||
| return location.SourceTree.GetRoot(cancellationToken).FindToken(location.SourceSpan.Start); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| using System.Linq; | ||
| using Roslyn.Compilers.Common; | ||
| using Roslyn.Compilers.CSharp; | ||
|
|
||
| namespace Roslyn.Services.Editor.CSharp.Extensions | ||
| { | ||
| internal static class MethodSymbolExtensions | ||
| { | ||
| public static bool IsExtensionUsedAsInstance( | ||
| this MethodSymbol method, | ||
| SemanticModel semanticModel, | ||
| ExpressionSyntax expression) | ||
| { | ||
| if (expression is InvocationExpressionSyntax) | ||
| { | ||
| expression = ((InvocationExpressionSyntax)expression).Expression; | ||
| } | ||
|
|
||
| if (expression is MemberAccessExpressionSyntax) | ||
| { | ||
| var leftSide = ((MemberAccessExpressionSyntax)expression).Expression; | ||
| var leftSideInfo = semanticModel.GetSymbolInfo(leftSide); | ||
| return !leftSideInfo.GetBestOrAllSymbols().OfType<ITypeSymbol>().Any(); | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| // Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| using Roslyn.Compilers; | ||
| using Roslyn.Compilers.Common; | ||
| using Roslyn.Compilers.CSharp; | ||
| using Roslyn.Services.Shared.Utilities; | ||
|
|
||
| namespace Roslyn.Services.Editor.CSharp.Extensions | ||
| { | ||
| internal static partial class SymbolContentBuilderExtensions | ||
| { | ||
| public static void AppendMinimalSymbol(this SymbolContentBuilder builder, Symbol symbol, Location location, SemanticModel semanticModel, SymbolDisplayFormat format = null) | ||
| { | ||
| var parts = symbol.ToMinimalDisplayParts(location, semanticModel, format); | ||
| builder.AddParts(parts); | ||
| } | ||
|
|
||
| public static void AppendSymbol(this SymbolContentBuilder builder, Symbol symbol, SymbolDisplayFormat format = null) | ||
| { | ||
| var parts = symbol.ToDisplayParts(format); | ||
| builder.AddParts(parts); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| using Roslyn.Compilers.CSharp; | ||
|
|
||
| namespace Roslyn.Services.Editor.CSharp.Extensions | ||
| { | ||
| internal partial class SymbolExtensions | ||
| { | ||
| private class IsUnsafeVisitor : SymbolVisitor<object, bool> | ||
| { | ||
| internal static readonly IsUnsafeVisitor Instance = new IsUnsafeVisitor(); | ||
|
|
||
| private IsUnsafeVisitor() | ||
| { | ||
| } | ||
|
|
||
| public override bool VisitArrayType(ArrayTypeSymbol symbol, object argument) | ||
| { | ||
| return Visit(symbol.ElementType, argument); | ||
| } | ||
|
|
||
| public override bool VisitErrorType(ErrorTypeSymbol symbol, object argument) | ||
| { | ||
| return symbol.TypeArguments.Any(ts => Visit(ts, argument)); | ||
| } | ||
|
|
||
| public override bool VisitField(FieldSymbol symbol, object argument) | ||
| { | ||
| return Visit(symbol.Type, argument); | ||
| } | ||
|
|
||
| public override bool VisitNamedType(NamedTypeSymbol symbol, object argument) | ||
| { | ||
| return symbol.TypeArguments.Any(ts => Visit(ts, argument)); | ||
| } | ||
|
|
||
| public override bool VisitPointerType(PointerTypeSymbol symbol, object argument) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| public override bool VisitProperty(PropertySymbol symbol, object argument) | ||
| { | ||
| return | ||
| Visit(symbol.Type, argument) || | ||
| symbol.Parameters.Any(p => Visit(p.Type, argument)); | ||
| } | ||
|
|
||
| public override bool VisitTypeParameter(TypeParameterSymbol symbol, object argument) | ||
| { | ||
| return symbol.ConstraintTypes.Any(ts => Visit(ts, argument)); | ||
| } | ||
|
|
||
| public override bool VisitMethod(MethodSymbol symbol, object argument) | ||
| { | ||
| return | ||
| Visit(symbol.ReturnType, argument) || | ||
| symbol.Parameters.Any(p => Visit(p, argument)) || | ||
| symbol.TypeParameters.Any(tp => Visit(tp, argument)); | ||
| } | ||
|
|
||
| public override bool VisitParameter(ParameterSymbol symbol, object argument) | ||
| { | ||
| return Visit(symbol.Type, argument); | ||
| } | ||
|
|
||
| public override bool VisitParameter(ParameterSymbol symbol, object arg) | ||
| { | ||
| return Visit(symbol.Type, argument); | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| using System; | ||
| using System.Linq; | ||
| using Roslyn.Compilers; | ||
| using Roslyn.Compilers.CSharp; | ||
| using Roslyn.Utilities; | ||
|
|
||
| namespace Roslyn.Services.Editor.CSharp.Extensions | ||
| { | ||
| internal static partial class SymbolExtensions | ||
| { | ||
| public static bool IsDeprecated(this Symbol symbol) | ||
| { | ||
| // TODO(cyrusn): Impelement this | ||
| return false; | ||
| } | ||
|
|
||
| public static bool IsStaticType(this Symbol s) | ||
| { | ||
| return s.Kind == SymbolKind.NamedType && s.IsStatic; | ||
| } | ||
|
|
||
| public static bool IsNamespace(this Symbol s) | ||
| { | ||
| return s.Kind == SymbolKind.Namespace; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using Microsoft.CodeAnalysis.CSharp.Semantics; | ||
| using Roslyn.Utilities; | ||
|
|
||
| namespace Microsoft.CodeAnalysis.CSharp.Extensions | ||
| { | ||
| internal static class SymbolInfoExtensions | ||
| { | ||
| public static IEnumerable<Symbol> GetAllSymbols(this SymbolInfo info) | ||
| { | ||
| return GetAllSymbolsWorker(info).Distinct(); | ||
| } | ||
|
|
||
| private static IEnumerable<Symbol> GetAllSymbolsWorker(this SymbolInfo info) | ||
| { | ||
| if (info.Symbol != null) | ||
| { | ||
| yield return info.Symbol; | ||
| } | ||
|
|
||
| foreach (var symbol in info.CandidateSymbols) | ||
| { | ||
| yield return symbol; | ||
| } | ||
| } | ||
|
|
||
| public static Symbol GetAnySymbol(this SymbolInfo info) | ||
| { | ||
| return info.GetAllSymbols().FirstOrDefault(); | ||
| } | ||
|
|
||
| public static Symbol GetAnySymbol(this SymbolInfo info, params CandidateReason[] allowableReasons) | ||
| { | ||
| if (info.Symbol != null) | ||
| { | ||
| return info.Symbol; | ||
| } | ||
|
|
||
| if (allowableReasons.Contains(info.CandidateReason)) | ||
| { | ||
| return info.CandidateSymbols.FirstOrDefault(); | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| public static IEnumerable<Symbol> GetBestOrAllSymbols(this SymbolInfo info) | ||
| { | ||
| if (info.Symbol != null) | ||
| { | ||
| return SpecializedCollections.SingletonEnumerable(info.Symbol); | ||
| } | ||
| else if (info.CandidateSymbols.Length > 0) | ||
| { | ||
| return info.CandidateSymbols; | ||
| } | ||
|
|
||
| return SpecializedCollections.EmptyEnumerable<Symbol>(); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| using Microsoft.CodeAnalysis.CSharp; | ||
| using Microsoft.CodeAnalysis.CSharp.Symbols; | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
| using Microsoft.CodeAnalysis.Text; | ||
|
|
||
| namespace Microsoft.CodeAnalysis.CSharp.Extensions | ||
| { | ||
| internal static partial class SyntaxNodeExtensions | ||
| { | ||
| private class RemoveNodeRewriter : CSharpSyntaxRewriter | ||
| { | ||
| private readonly SyntaxNode arg; | ||
| internal RemoveNodeRewriter(SyntaxNode arg) | ||
| { | ||
| this.arg = arg; | ||
| } | ||
|
|
||
| public override SyntaxNode Visit(SyntaxNode node) | ||
| { | ||
| if (node == arg) | ||
| { | ||
| return null; | ||
| } | ||
| else | ||
| { | ||
| return base.Visit(node); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| using System.Collections.Generic; | ||
| using Roslyn.Compilers.CSharp; | ||
| using Roslyn.Utilities; | ||
|
|
||
| namespace Roslyn.Services.CSharp.Extensions | ||
| { | ||
| internal static partial class SyntaxNodeOrTokenExtensions | ||
| { | ||
| public static IEnumerable<SyntaxNodeOrToken> DepthFirstTraversal(this SyntaxNodeOrToken node) | ||
| { | ||
| var stack = new Stack<SyntaxNodeOrToken>(); | ||
| stack.Push(node); | ||
|
|
||
| while (!stack.IsEmpty()) | ||
| { | ||
| var current = stack.Pop(); | ||
|
|
||
| yield return current; | ||
|
|
||
| if (current.IsNode) | ||
| { | ||
| foreach (var child in current.ChildNodesAndTokens().Reverse()) | ||
| { | ||
| stack.Push(child); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
|
||
| namespace Microsoft.CodeAnalysis.CSharp.Extensions | ||
| { | ||
| internal static class SyntaxNodeOrTokenListExtensions | ||
| { | ||
| /// <summary> | ||
| /// Returns the index in <paramref name="list"/> for the given nodeOrToken. | ||
| /// </summary> | ||
| /// <param name="list">The list in which to search.</param> | ||
| /// <param name="nodeOrToken">The node or token to search for in the list.</param> | ||
| /// <returns>The index of the found nodeOrToken, or -1 if it wasn't found</returns> | ||
| public static int IndexOf(this SyntaxNodeOrTokenList list, SyntaxNodeOrToken nodeOrToken) | ||
| { | ||
| var i = 0; | ||
| foreach (var child in list) | ||
| { | ||
| if (child == nodeOrToken) | ||
| { | ||
| return i; | ||
| } | ||
|
|
||
| i++; | ||
| } | ||
|
|
||
| return -1; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| using System.Collections.Generic; | ||
| using Roslyn.Compilers.CSharp; | ||
|
|
||
| namespace Roslyn.Services.Editor.CSharp.Extensions | ||
| { | ||
| internal partial class TypeSymbolExtensions | ||
| { | ||
| private class CollectMethodTypeParameterSymbolsVisitor : SymbolVisitor<IList<TypeParameterSymbol>, object> | ||
| { | ||
| public static readonly SymbolVisitor<IList<TypeParameterSymbol>, object> Instance = new CollectMethodTypeParameterSymbolsVisitor(); | ||
|
|
||
| private CollectMethodTypeParameterSymbolsVisitor() | ||
| { | ||
| } | ||
|
|
||
| public override object VisitDynamicType(DynamicTypeSymbol symbol, IList<TypeParameterSymbol> argument) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| public override object VisitArrayType(ArrayTypeSymbol symbol, IList<TypeParameterSymbol> arg) | ||
| { | ||
| return this.Visit(symbol.ElementType, arg); | ||
| } | ||
|
|
||
| public override object VisitErrorType(ErrorTypeSymbol symbol, IList<TypeParameterSymbol> arg) | ||
| { | ||
| foreach (var child in symbol.TypeArguments) | ||
| { | ||
| Visit(child, arg); | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| public override object VisitNamedType(NamedTypeSymbol symbol, IList<TypeParameterSymbol> arg) | ||
| { | ||
| foreach (var child in symbol.TypeArguments) | ||
| { | ||
| Visit(child, arg); | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| public override object VisitPointerType(PointerTypeSymbol symbol, IList<TypeParameterSymbol> arg) | ||
| { | ||
| return Visit(symbol.PointedAtType, arg); | ||
| } | ||
|
|
||
| public override object VisitTypeParameter(TypeParameterSymbol symbol, IList<TypeParameterSymbol> arg) | ||
| { | ||
| if (symbol.ContainingSymbol is MethodSymbol) | ||
| { | ||
| if (!arg.Contains(symbol)) | ||
| { | ||
| arg.Add(symbol); | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using Roslyn.Compilers; | ||
| using Roslyn.Compilers.Common; | ||
| using Roslyn.Compilers.CSharp; | ||
|
|
||
| namespace Roslyn.Services.Editor.CSharp.Extensions | ||
| { | ||
| internal partial class TypeSymbolExtensions | ||
| { | ||
| private class SubstituteTypesVisitor<TType1, TType2> : SymbolVisitor<object, TypeSymbol> | ||
| where TType1 : TypeSymbol | ||
| where TType2 : TypeSymbol | ||
| { | ||
| private readonly Compilation compilation; | ||
| private readonly IDictionary<TType1, TType2> map; | ||
|
|
||
| internal SubstituteTypesVisitor(Compilation compilation, IDictionary<TType1, TType2> map) | ||
| { | ||
| this.compilation = compilation; | ||
| this.map = map; | ||
| } | ||
|
|
||
| private TypeSymbol VisitType(TypeSymbol symbol, object argument) | ||
| { | ||
| TType2 converted; | ||
| if (symbol is TType1 && map.TryGetValue((TType1)symbol, out converted)) | ||
| { | ||
| return converted; | ||
| } | ||
|
|
||
| return symbol; | ||
| } | ||
|
|
||
| public override TypeSymbol VisitErrorType(ErrorTypeSymbol symbol, object argument) | ||
| { | ||
| return VisitType(symbol, argument); | ||
| } | ||
|
|
||
| public override TypeSymbol VisitTypeParameter(TypeParameterSymbol symbol, object argument) | ||
| { | ||
| return VisitType(symbol, argument); | ||
| } | ||
|
|
||
| public override TypeSymbol VisitNamedType(NamedTypeSymbol symbol, object argument) | ||
| { | ||
| if (symbol.TypeArguments.Count == 0) | ||
| { | ||
| return symbol; | ||
| } | ||
|
|
||
| var substitutedArguments = symbol.TypeArguments.Select(t => Visit(t)).ToArray(); | ||
| return ((NamedTypeSymbol)symbol.OriginalDefinition).Construct(substitutedArguments); | ||
| } | ||
|
|
||
| public override TypeSymbol VisitArrayType(ArrayTypeSymbol symbol, object argument) | ||
| { | ||
| return compilation.CreateArrayTypeSymbol(Visit(symbol.ElementType), symbol.Rank); | ||
| } | ||
|
|
||
| public override TypeSymbol VisitPointerType(PointerTypeSymbol symbol, object argument) | ||
| { | ||
| return compilation.CreatePointerTypeSymbol(Visit(symbol.PointedAtType)); | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using Roslyn.Compilers; | ||
| using Roslyn.Compilers.Common; | ||
| using Roslyn.Compilers.CSharp; | ||
|
|
||
| namespace Roslyn.Services.Editor.CSharp.Extensions | ||
| { | ||
| internal static partial class TypeSymbolExtensions | ||
| { | ||
| private class UnavailableTypeParameterRemover : SymbolVisitor<object, TypeSymbol> | ||
| { | ||
| private readonly Compilation compilation; | ||
| private readonly ISet<string> availableTypeParameterNames; | ||
|
|
||
| public UnavailableTypeParameterRemover(Compilation compilation, ISet<string> availableTypeParameterNames) | ||
| { | ||
| this.compilation = compilation; | ||
| this.availableTypeParameterNames = availableTypeParameterNames; | ||
| } | ||
|
|
||
| public override TypeSymbol VisitDynamicType(DynamicTypeSymbol symbol, object argument) | ||
| { | ||
| return symbol; | ||
| } | ||
|
|
||
| public override TypeSymbol VisitErrorType(ErrorTypeSymbol symbol, object argument) | ||
| { | ||
| return VisitNamedType(symbol, argument); | ||
| } | ||
|
|
||
| public override TypeSymbol VisitArrayType(ArrayTypeSymbol symbol, object argument) | ||
| { | ||
| var elementType = Visit(symbol.ElementType); | ||
| if (elementType == symbol.ElementType) | ||
| { | ||
| return symbol; | ||
| } | ||
|
|
||
| // TODO: Code coverage | ||
| return compilation.CreateArrayTypeSymbol(elementType, symbol.Rank); | ||
| } | ||
|
|
||
| public override TypeSymbol VisitNamedType(NamedTypeSymbol symbol, object argument) | ||
| { | ||
| var arguments = symbol.TypeArguments.Select(t => Visit(t)).ToArray(); | ||
| if (arguments.SequenceEqual(symbol.TypeArguments.AsEnumerable())) | ||
| { | ||
| return symbol; | ||
| } | ||
|
|
||
| // TODO: Code coverage | ||
| return symbol.ConstructedFrom.Construct(arguments.ToArray()); | ||
| } | ||
|
|
||
| public override TypeSymbol VisitPointerType(PointerTypeSymbol symbol, object argument) | ||
| { | ||
| var elementType = Visit(symbol.PointedAtType); | ||
| if (elementType == symbol.PointedAtType) | ||
| { | ||
| return symbol; | ||
| } | ||
|
|
||
| return compilation.CreatePointerTypeSymbol(elementType); | ||
| } | ||
|
|
||
| public override TypeSymbol VisitTypeParameter(TypeParameterSymbol symbol, object argument) | ||
| { | ||
| if (availableTypeParameterNames.Contains(symbol.Name)) | ||
| { | ||
| return symbol; | ||
| } | ||
|
|
||
| return compilation.ObjectType; | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| using System.Collections.Generic; | ||
| using Roslyn.Compilers.CSharp; | ||
|
|
||
| namespace Roslyn.Services.Editor.CSharp.Extensions | ||
| { | ||
| internal static partial class TypeSymbolExtensions | ||
| { | ||
| public static IList<NamedTypeSymbol> GetAllInterfacesIncludingThis(this TypeSymbol type) | ||
| { | ||
| var allInterfaces = type.AllInterfaces; | ||
| if (type is NamedTypeSymbol) | ||
| { | ||
| var namedType = type as NamedTypeSymbol; | ||
| if (namedType.TypeKind == TypeKind.Interface && | ||
| !allInterfaces.Contains(namedType)) | ||
| { | ||
| var result = new List<NamedTypeSymbol>() { namedType }; | ||
|
|
||
| result.AddRange(allInterfaces.AsEnumerable()); | ||
| return result; | ||
| } | ||
| } | ||
|
|
||
| return allInterfaces.AsList(); | ||
| } | ||
|
|
||
| public static bool IsNullOrError(this TypeSymbol type) | ||
| { | ||
| return type == null || type.TypeKind == TypeKind.Error; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,231 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using Roslyn.Compilers; | ||
| using Roslyn.Compilers.Common; | ||
| using Roslyn.Compilers.CSharp; | ||
| using Roslyn.Services; | ||
| using Roslyn.Services.Formatting; | ||
| using Roslyn.Utilities; | ||
|
|
||
| namespace Roslyn.Services.CSharp.Formatting | ||
| { | ||
| internal class AutoGeneratedCodeOperationProvider : IFormattingOperationProvider | ||
| { | ||
| private readonly IFormattingOperationsFactory factory; | ||
| private readonly IFormattingOperationProvider provider; | ||
|
|
||
| public AutoGeneratedCodeOperationProvider(IFormattingOperationsFactory factory, IFormattingOperationProvider provider) | ||
| { | ||
| Contract.ThrowIfNull(factory); | ||
| Contract.ThrowIfNull(provider); | ||
|
|
||
| this.factory = factory; | ||
| this.provider = provider; | ||
| } | ||
|
|
||
| public void AddSuppressOperations(List<ISuppressOperation> list, CommonSyntaxNode node) | ||
| { | ||
| // anchor operation is to preserve user code style. don't do anything | ||
| } | ||
|
|
||
| public void AddAnchorIndentationOperations(List<IAnchorIndentationOperation> list, CommonSyntaxNode node) | ||
| { | ||
| // anchor operation is to preserve user code style. don't do anything | ||
| } | ||
|
|
||
| public void AddIndentBlockOperations(List<IIndentBlockOperation> list, CommonSyntaxNode node) | ||
| { | ||
| // anchor operation is to preserve user code style. don't do anything | ||
| } | ||
|
|
||
| public void AddAlignTokensOperations(List<IAlignTokensOperation> list, CommonSyntaxNode node) | ||
| { | ||
| // anchor operation is to preserve user code style. don't do anything | ||
| } | ||
|
|
||
| public IAdjustNewLinesOperation GetAdjustNewLinesOperation(CommonSyntaxToken previousToken, CommonSyntaxToken currentToken) | ||
| { | ||
| return GetAdjustNewLinesOperation((SyntaxToken)previousToken, (SyntaxToken)currentToken); | ||
| } | ||
|
|
||
| private IAdjustNewLinesOperation GetAdjustNewLinesOperation(SyntaxToken previousToken, SyntaxToken currentToken) | ||
| { | ||
| var operation = this.provider.GetAdjustNewLinesOperation(previousToken, currentToken); | ||
| if (operation == null) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| // if operation is already forced, return as it is. | ||
| if (operation.Option == AdjustNewLinesOption.ForceLines) | ||
| { | ||
| return operation; | ||
| } | ||
|
|
||
| var line = Math.Max(LineBreaksAfter(previousToken, currentToken), operation.Line); | ||
| if (line == 0) | ||
| { | ||
| return this.factory.CreateAdjustNewLinesOperation(0, AdjustNewLinesOption.PreserveLines); | ||
| } | ||
|
|
||
| return this.factory.CreateAdjustNewLinesOperation(line, AdjustNewLinesOption.ForceLines); | ||
| } | ||
|
|
||
| public IAdjustSpacesOperation GetAdjustSpacesOperation(CommonSyntaxToken previousToken, CommonSyntaxToken currentToken) | ||
| { | ||
| var operation = this.provider.GetAdjustSpacesOperation(previousToken, currentToken); | ||
| if (operation == null) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| // if operation is already forced, return as it is. | ||
| if (operation.Option == AdjustSpacesOption.ForceSpaces) | ||
| { | ||
| return operation; | ||
| } | ||
|
|
||
| // current implementation of engine gives higher priority on new line operations over space operations if | ||
| // two are conflicting. | ||
| // ex) new line operation says add 1 line between tokens, and | ||
| // space operation says give 1 space between two tokens (basically means remove new lines) | ||
| // then, engine will pick new line operation and ignore space operation | ||
|
|
||
| // make every operation forced | ||
| return this.factory.CreateAdjustSpacesOperation(Math.Max(0, operation.Space), AdjustSpacesOption.ForceSpaces); | ||
| } | ||
|
|
||
| // copied from compiler formatter to have same base forced format | ||
| private int LineBreaksAfter(SyntaxToken previousToken, SyntaxToken currentToken) | ||
| { | ||
| if (currentToken.Kind == SyntaxKind.None) | ||
| { | ||
| return 0; | ||
| } | ||
|
|
||
| switch (previousToken.Kind) | ||
| { | ||
| case SyntaxKind.None: | ||
| return 0; | ||
|
|
||
| case SyntaxKind.OpenBraceToken: | ||
| case SyntaxKind.FinallyKeyword: | ||
| return 1; | ||
|
|
||
| case SyntaxKind.CloseBraceToken: | ||
| return LineBreaksAfterCloseBrace(currentToken); | ||
|
|
||
| case SyntaxKind.CloseParenToken: | ||
| return (((previousToken.Parent is StatementSyntax) && currentToken.Parent != previousToken.Parent) | ||
| || currentToken.Kind == SyntaxKind.OpenBraceToken) ? 1 : 0; | ||
|
|
||
| case SyntaxKind.CloseBracketToken: | ||
| if (previousToken.Parent is AttributeDeclarationSyntax) | ||
| { | ||
| return 1; | ||
| } | ||
|
|
||
| break; | ||
|
|
||
| case SyntaxKind.SemicolonToken: | ||
| return LineBreaksAfterSemicolon(previousToken, currentToken); | ||
|
|
||
| case SyntaxKind.CommaToken: | ||
| return previousToken.Parent is EnumDeclarationSyntax ? 1 : 0; | ||
|
|
||
| case SyntaxKind.ElseKeyword: | ||
| return currentToken.Kind != SyntaxKind.IfKeyword ? 1 : 0; | ||
|
|
||
| case SyntaxKind.ColonToken: | ||
| if (previousToken.Parent is LabeledStatementSyntax || previousToken.Parent is SwitchLabelSyntax) | ||
| { | ||
| return 1; | ||
| } | ||
|
|
||
| break; | ||
| } | ||
|
|
||
| if ((currentToken.Kind == SyntaxKind.FromKeyword && currentToken.Parent.Kind == SyntaxKind.FromClause) || | ||
| (currentToken.Kind == SyntaxKind.LetKeyword && currentToken.Parent.Kind == SyntaxKind.LetClause) || | ||
| (currentToken.Kind == SyntaxKind.WhereKeyword && currentToken.Parent.Kind == SyntaxKind.WhereClause) || | ||
| (currentToken.Kind == SyntaxKind.JoinKeyword && currentToken.Parent.Kind == SyntaxKind.JoinClause) || | ||
| (currentToken.Kind == SyntaxKind.JoinKeyword && currentToken.Parent.Kind == SyntaxKind.JoinIntoClause) || | ||
| (currentToken.Kind == SyntaxKind.OrderByKeyword && currentToken.Parent.Kind == SyntaxKind.OrderByClause) || | ||
| (currentToken.Kind == SyntaxKind.SelectKeyword && currentToken.Parent.Kind == SyntaxKind.SelectClause) || | ||
| (currentToken.Kind == SyntaxKind.GroupKeyword && currentToken.Parent.Kind == SyntaxKind.GroupClause)) | ||
| { | ||
| return 1; | ||
| } | ||
|
|
||
| switch (currentToken.Kind) | ||
| { | ||
| case SyntaxKind.OpenBraceToken: | ||
| case SyntaxKind.CloseBraceToken: | ||
| case SyntaxKind.ElseKeyword: | ||
| case SyntaxKind.FinallyKeyword: | ||
| return 1; | ||
|
|
||
| case SyntaxKind.OpenBracketToken: | ||
| return currentToken.Parent is AttributeDeclarationSyntax ? 1 : 0; | ||
|
|
||
| case SyntaxKind.WhereKeyword: | ||
| return previousToken.Parent is TypeParameterListSyntax ? 1 : 0; | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| private static int LineBreaksAfterCloseBrace(SyntaxToken nextToken) | ||
| { | ||
| if (nextToken.Kind == SyntaxKind.CloseBraceToken) | ||
| { | ||
| return 1; | ||
| } | ||
| else if ( | ||
| nextToken.Kind == SyntaxKind.CatchKeyword || | ||
| nextToken.Kind == SyntaxKind.FinallyKeyword || | ||
| nextToken.Kind == SyntaxKind.ElseKeyword) | ||
| { | ||
| return 1; | ||
| } | ||
| else if ( | ||
| nextToken.Kind == SyntaxKind.WhileKeyword && | ||
| nextToken.Parent.Kind == SyntaxKind.DoStatement) | ||
| { | ||
| return 1; | ||
| } | ||
| else if (nextToken.Kind == SyntaxKind.EndOfFileToken) | ||
| { | ||
| return 0; | ||
| } | ||
| else | ||
| { | ||
| return 2; | ||
| } | ||
| } | ||
|
|
||
| private static int LineBreaksAfterSemicolon(SyntaxToken previousToken, SyntaxToken currentToken) | ||
| { | ||
| if (previousToken.Parent is ForStatementSyntax) | ||
| { | ||
| return 0; | ||
| } | ||
| else if (currentToken.Kind == SyntaxKind.CloseBraceToken) | ||
| { | ||
| return 1; | ||
| } | ||
| else if (previousToken.Parent is UsingDirectiveSyntax) | ||
| { | ||
| return currentToken.Parent is UsingDirectiveSyntax ? 1 : 2; | ||
| } | ||
| else if (previousToken.Parent is ExternAliasDirectiveSyntax) | ||
| { | ||
| return currentToken.Parent is ExternAliasDirectiveSyntax ? 1 : 2; | ||
| } | ||
| else | ||
| { | ||
| return 1; | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.CodeAnalysis.Formatting; | ||
| using Microsoft.CodeAnalysis.LanguageServices; | ||
| using Microsoft.CodeAnalysis.Text; | ||
|
|
||
| namespace Microsoft.CodeAnalysis.CSharp.Formatting | ||
| { | ||
| #if MEF | ||
| [ExportLanguageService(typeof(IFormattingService), LanguageNames.CSharp)] | ||
| #endif | ||
| internal class CSharpFormattingService : IFormattingService | ||
| { | ||
| public async Task<IList<TextChange>> GetFormattingChangesAsync(Document document, TextSpan? textSpan, CancellationToken cancellationToken) | ||
| { | ||
| var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); | ||
| var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); | ||
|
|
||
| var span = textSpan.HasValue ? textSpan.Value : new TextSpan(0, sourceText.Length); | ||
| return Formatter.GetFormattedTextChanges(root, new TextSpan[] { span }, document.Project.Solution.Workspace, cancellationToken: cancellationToken); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| using System.Collections.Generic; | ||
| using Roslyn.Compilers.CSharp; | ||
| using Roslyn.Services.Formatting; | ||
| using Roslyn.Utilities; | ||
|
|
||
| namespace Roslyn.Services.CSharp.Formatting | ||
| { | ||
| internal abstract class CSharpTriviaData : TriviaData | ||
| { | ||
| public CSharpTriviaData(FormattingOptions options) : | ||
| base(options) | ||
| { | ||
| } | ||
|
|
||
| public virtual List<SyntaxTrivia> TriviaList | ||
| { | ||
| get | ||
| { | ||
| return Contract.FailWithReturn<List<SyntaxTrivia>>("Should be never called"); | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,217 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using Roslyn.Compilers.CSharp; | ||
| using Roslyn.Services.Formatting; | ||
| using Roslyn.Utilities; | ||
|
|
||
| namespace Roslyn.Services.CSharp.Formatting | ||
| { | ||
| internal partial class TriviaDataFactory | ||
| { | ||
| /// <summary> | ||
| /// represents a general trivia between two tokens. slightly more expensive than others since it | ||
| /// needs to calculate stuff unlike other cases | ||
| /// </summary> | ||
| private class ComplexTriviaData : CSharpTriviaData | ||
| { | ||
| public TreeData TreeInfo { get; private set; } | ||
| public SyntaxToken Token1 { get; private set; } | ||
| public SyntaxToken Token2 { get; private set; } | ||
| public string OriginalString { get; private set; } | ||
|
|
||
| private readonly bool treatAsElastic; | ||
|
|
||
| public ComplexTriviaData(FormattingOptions options, TreeData treeInfo, SyntaxToken token1, SyntaxToken token2) : | ||
| base(options) | ||
| { | ||
| Contract.ThrowIfNull(treeInfo); | ||
|
|
||
| this.Token1 = token1; | ||
| this.Token2 = token2; | ||
|
|
||
| this.treatAsElastic = HasAnyWhitespaceElasticTrivia(token1, token2); | ||
|
|
||
| this.TreeInfo = treeInfo; | ||
| this.OriginalString = this.TreeInfo.GetTextBetween(token1, token2); | ||
|
|
||
| int lineBreaks; | ||
| int spaces; | ||
| this.OriginalString.ProcessTextBetweenTokens(this.TreeInfo, token1, this.Options.TabSize, out lineBreaks, out spaces); | ||
|
|
||
| this.LineBreaks = lineBreaks; | ||
| this.Space = spaces; | ||
| } | ||
|
|
||
| public override bool TreatAsElastic | ||
| { | ||
| get { return this.treatAsElastic; } | ||
| } | ||
|
|
||
| public override bool IsWhitespaceOnlyTrivia | ||
| { | ||
| get { return false; } | ||
| } | ||
|
|
||
| public override bool ShouldReplaceOriginalWithNewString | ||
| { | ||
| get | ||
| { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| public override string NewString | ||
| { | ||
| get | ||
| { | ||
| return Contract.FailWithReturn<string>("Should never be called"); | ||
| } | ||
| } | ||
|
|
||
| public override List<SyntaxTrivia> TriviaList | ||
| { | ||
| get | ||
| { | ||
| return Contract.FailWithReturn<List<SyntaxTrivia>>("Should never be called"); | ||
| } | ||
| } | ||
|
|
||
| public override TriviaData WithSpace(int space) | ||
| { | ||
| // two tokens are on a singleline, we dont allow changing spaces between two tokens that contain | ||
| // noisy characters between them. | ||
| if (!this.SecondTokenIsFirstTokenOnLine) | ||
| { | ||
| return this; | ||
| } | ||
|
|
||
| // okay, two tokens are on different lines, we are basically asked to remove line breaks between them | ||
| // and make them to be on a single line. well, that is not allowed when there are noisy chars between them | ||
| if (this.SecondTokenIsFirstTokenOnLine) | ||
| { | ||
| return this; | ||
| } | ||
|
|
||
| return Contract.FailWithReturn<TriviaData>("Can not reach here"); | ||
| } | ||
|
|
||
| public override TriviaData WithLine(int line, int indentation) | ||
| { | ||
| Contract.ThrowIfFalse(line > 0); | ||
|
|
||
| // if we have elastic trivia, always let it be modified | ||
| if (this.treatAsElastic) | ||
| { | ||
| return new ModifiedComplexTriviaData(this.Options, this, line, indentation); | ||
| } | ||
|
|
||
| // two tokens are on a single line, it is always allowed to put those two tokens on a different lines | ||
| if (!this.SecondTokenIsFirstTokenOnLine) | ||
| { | ||
| return new ModifiedComplexTriviaData(this.Options, this, line, indentation); | ||
| } | ||
|
|
||
| // okay, two tokens are on different lines, now we need to see whether we can add more lines or not | ||
| if (this.SecondTokenIsFirstTokenOnLine) | ||
| { | ||
| // we are asked to add more lines. sure, no problem | ||
| if (this.LineBreaks < line) | ||
| { | ||
| return new ModifiedComplexTriviaData(this.Options, this, line, indentation); | ||
| } | ||
|
|
||
| // we already has same number of lines, but it is asking changing indentation | ||
| if (this.LineBreaks == line) | ||
| { | ||
| return WithIndentation(indentation); | ||
| } | ||
|
|
||
| // sorry, we can't reduce lines if it contains noisy chars | ||
| if (this.LineBreaks > line) | ||
| { | ||
| return this; | ||
| } | ||
| } | ||
|
|
||
| return Contract.FailWithReturn<TriviaData>("Can not reach here"); | ||
| } | ||
|
|
||
| public override TriviaData WithIndentation(int indentation) | ||
| { | ||
| // if tokens are not in different line, there is nothing we can do here | ||
| if (!this.SecondTokenIsFirstTokenOnLine) | ||
| { | ||
| return this; | ||
| } | ||
|
|
||
| // well, we are already in a desired format, nothing to do. return as it is. | ||
| if (this.Space == indentation) | ||
| { | ||
| return this; | ||
| } | ||
|
|
||
| return new ModifiedComplexTriviaData(this.Options, this, this.LineBreaks, indentation); | ||
| } | ||
|
|
||
| public override void Format(FormattingContext context, Action<int, TriviaData> formattingResultApplier, int tokenPairIndex) | ||
| { | ||
| var triviaList = new TriviaList(this.Token1.TrailingTrivia, this.Token2.LeadingTrivia); | ||
| Contract.ThrowIfFalse(triviaList.Count > 0); | ||
|
|
||
| // okay, now, check whether we need or are able to format noisy tokens | ||
| if (TriviaFormatter.ContainsSkippedTokensOrText(triviaList)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| if (!ShouldFormat(context, triviaList)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| formattingResultApplier(tokenPairIndex, | ||
| new FormattedComplexTriviaData(context, this.Token1, this.Token2, this.LineBreaks, this.Space, this.OriginalString)); | ||
| } | ||
|
|
||
| private bool ShouldFormat(FormattingContext context, TriviaList triviaList) | ||
| { | ||
| if (!this.SecondTokenIsFirstTokenOnLine) | ||
| { | ||
| return TriviaFormatter.ShouldFormatTriviaOnSingleLine(triviaList); | ||
| } | ||
|
|
||
| Debug.Assert(this.SecondTokenIsFirstTokenOnLine); | ||
|
|
||
| var desiredIndentation = context.GetBaseIndentation(triviaList[0].Span.Start); | ||
| var firstTriviaInTree = this.Token1.Kind == SyntaxKind.None; | ||
| return TriviaFormatter.ShouldFormatTriviaOnMultipleLines(context.Options, firstTriviaInTree, desiredIndentation, triviaList); | ||
| } | ||
|
|
||
| private static bool HasAnyWhitespaceElasticTrivia(SyntaxToken previousToken, SyntaxToken currentToken) | ||
| { | ||
| if (!previousToken.HasTrailingTrivia && !currentToken.HasLeadingTrivia) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| return HasAnyWhitespaceElasticTrivia(previousToken.TrailingTrivia) || HasAnyWhitespaceElasticTrivia(currentToken.LeadingTrivia); | ||
| } | ||
|
|
||
| private static bool HasAnyWhitespaceElasticTrivia(SyntaxTriviaList list) | ||
| { | ||
| for (int i = 0; i < list.Count; i++) | ||
| { | ||
| var trivia = list[i]; | ||
|
|
||
| if (trivia.IsElastic) | ||
| { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using Roslyn.Compilers.CSharp; | ||
| using Roslyn.Compilers.Internal; | ||
| using Roslyn.Services.Formatting; | ||
| using Roslyn.Utilities; | ||
|
|
||
| namespace Roslyn.Services.CSharp.Formatting | ||
| { | ||
| internal partial class TriviaDataFactory | ||
| { | ||
| private class FormattedComplexTriviaData : CSharpTriviaData | ||
| { | ||
| private readonly FormattingContext context; | ||
| private readonly SyntaxToken token1; | ||
| private readonly SyntaxToken token2; | ||
| private readonly string originalString; | ||
| private readonly string newString; | ||
| private readonly bool shouldFormat; | ||
|
|
||
| public FormattedComplexTriviaData(FormattingContext context, SyntaxToken token1, SyntaxToken token2, int lineBreaks, int spaces, string originalString) : | ||
| base(context.Options) | ||
| { | ||
| Contract.ThrowIfNull(context); | ||
| Contract.ThrowIfNull(originalString); | ||
|
|
||
| this.context = context; | ||
| this.token1 = token1; | ||
| this.token2 = token2; | ||
| this.originalString = originalString; | ||
|
|
||
| this.LineBreaks = Math.Max(0, lineBreaks); | ||
| this.Space = Math.Max(0, spaces); | ||
|
|
||
| var formatter = new TriviaFormatter(this.context, this.token1, this.token2, this.LineBreaks, this.Space); | ||
| this.newString = formatter.FormatToString(); | ||
|
|
||
| this.shouldFormat = !this.originalString.Equals(this.newString); | ||
| } | ||
|
|
||
| public override bool TreatAsElastic | ||
| { | ||
| get { return false; } | ||
| } | ||
|
|
||
| public override bool IsWhitespaceOnlyTrivia | ||
| { | ||
| get { return false; } | ||
| } | ||
|
|
||
| public override bool ShouldReplaceOriginalWithNewString | ||
| { | ||
| get { return this.shouldFormat; } | ||
| } | ||
|
|
||
| public override string NewString | ||
| { | ||
| get | ||
| { | ||
| return this.newString; | ||
| } | ||
| } | ||
|
|
||
| public override List<SyntaxTrivia> TriviaList | ||
| { | ||
| get | ||
| { | ||
| var formatter = new TriviaFormatter(this.context, this.token1, this.token2, this.LineBreaks, this.Space); | ||
| return formatter.FormatToSyntaxTriviaList(); | ||
| } | ||
| } | ||
|
|
||
| public override TriviaData WithSpace(int space) | ||
| { | ||
| return Contract.FailWithReturn<TriviaData>("Shouldn't be called"); | ||
| } | ||
|
|
||
| public override TriviaData WithLine(int line, int indentation) | ||
| { | ||
| return Contract.FailWithReturn<TriviaData>("Shouldn't be called"); | ||
| } | ||
|
|
||
| public override TriviaData WithIndentation(int indentation) | ||
| { | ||
| return Contract.FailWithReturn<TriviaData>("Shouldn't be called"); | ||
| } | ||
|
|
||
| public override void Format(FormattingContext context, Action<int, TriviaData> formattingResultApplier, int tokenPairIndex) | ||
| { | ||
| Contract.Fail("Shouldn't be called"); | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using Roslyn.Compilers.CSharp; | ||
| using Roslyn.Compilers.Internal; | ||
| using Roslyn.Services.Formatting; | ||
| using Roslyn.Utilities; | ||
|
|
||
| namespace Roslyn.Services.CSharp.Formatting | ||
| { | ||
| internal partial class TriviaDataFactory | ||
| { | ||
| private class ModifiedComplexTriviaData : CSharpTriviaData | ||
| { | ||
| private readonly ComplexTriviaData original; | ||
|
|
||
| public ModifiedComplexTriviaData(FormattingOptions options, ComplexTriviaData original, int lineBreaks, int space) | ||
| : base(options) | ||
| { | ||
| Contract.ThrowIfNull(original); | ||
|
|
||
| this.original = original; | ||
|
|
||
| // linebreak and space can become negative during formatting. but it should be normalized to >= 0 | ||
| // at the end. | ||
| this.LineBreaks = lineBreaks; | ||
| this.Space = space; | ||
| } | ||
|
|
||
| public override bool ShouldReplaceOriginalWithNewString | ||
| { | ||
| get | ||
| { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| public override bool TreatAsElastic | ||
| { | ||
| get { return this.original.TreatAsElastic; } | ||
| } | ||
|
|
||
| public override bool IsWhitespaceOnlyTrivia | ||
| { | ||
| get { return false; } | ||
| } | ||
|
|
||
| public override string NewString | ||
| { | ||
| get | ||
| { | ||
| return Contract.FailWithReturn<string>("Should be never called"); | ||
| } | ||
| } | ||
|
|
||
| public override List<SyntaxTrivia> TriviaList | ||
| { | ||
| get | ||
| { | ||
| return Contract.FailWithReturn<List<SyntaxTrivia>>("Should be never called"); | ||
| } | ||
| } | ||
|
|
||
| public override TriviaData WithSpace(int space) | ||
| { | ||
| return this.original.WithSpace(space); | ||
| } | ||
|
|
||
| public override TriviaData WithLine(int line, int indentation) | ||
| { | ||
| return this.original.WithLine(line, indentation); | ||
| } | ||
|
|
||
| public override TriviaData WithIndentation(int indentation) | ||
| { | ||
| return this.original.WithIndentation(indentation); | ||
| } | ||
|
|
||
| public override void Format(FormattingContext context, Action<int, TriviaData> formattingResultApplier, int tokenPairIndex) | ||
| { | ||
| Contract.ThrowIfFalse(this.SecondTokenIsFirstTokenOnLine); | ||
|
|
||
| var triviaList = new TriviaList(this.original.Token1.TrailingTrivia, this.original.Token2.LeadingTrivia); | ||
| Contract.ThrowIfFalse(triviaList.Count > 0); | ||
|
|
||
| // okay, now, check whether we need or are able to format noisy tokens | ||
| if (TriviaFormatter.ContainsSkippedTokensOrText(triviaList)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| formattingResultApplier(tokenPairIndex, | ||
| new FormattedComplexTriviaData(context, this.original.Token1, this.original.Token2, this.LineBreaks, this.Space, this.original.OriginalString)); | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,271 @@ | ||
| using System.Diagnostics; | ||
| using Roslyn.Compilers.CSharp; | ||
| using Roslyn.Services.Formatting; | ||
| using Roslyn.Services.Shared.Extensions; | ||
| using Roslyn.Utilities; | ||
|
|
||
| namespace Roslyn.Services.CSharp.Formatting | ||
| { | ||
| internal partial class TriviaFormatter | ||
| { | ||
| private struct MultiLineAnalyzer | ||
| { | ||
| private readonly FormattingOptions options; | ||
| private readonly int desiredIndentation; | ||
| private readonly TriviaList triviaList; | ||
|
|
||
| private int indentation; | ||
| private bool hasTrailingSpace; | ||
| private int lastLineBreakIndex; | ||
| private bool touchedNoisyCharacterOnCurrentLine; | ||
|
|
||
| public static bool ShouldFormat(FormattingOptions options, bool firstTriviaInTree, int desiredIndentation, TriviaList triviaList) | ||
| { | ||
| var analyzer = new MultiLineAnalyzer(options, firstTriviaInTree, desiredIndentation, triviaList); | ||
| return analyzer.ShouldFormat(); | ||
| } | ||
|
|
||
| private MultiLineAnalyzer(FormattingOptions options, bool firstTriviaInTree, int desiredIndentation, TriviaList triviaList) | ||
| { | ||
| this.options = options; | ||
| this.desiredIndentation = desiredIndentation; | ||
| this.triviaList = triviaList; | ||
|
|
||
| this.indentation = 0; | ||
| this.hasTrailingSpace = false; | ||
| this.lastLineBreakIndex = firstTriviaInTree ? 0 : -1; | ||
| this.touchedNoisyCharacterOnCurrentLine = false; | ||
| } | ||
|
|
||
| private bool UseIndentation | ||
| { | ||
| get { return this.lastLineBreakIndex >= 0; } | ||
| } | ||
|
|
||
| private bool OnElastic(SyntaxTrivia trivia) | ||
| { | ||
| // if it contains elastic trivia, always format | ||
| return trivia.IsElastic; | ||
| } | ||
|
|
||
| private bool OnWhitespace(SyntaxTrivia trivia) | ||
| { | ||
| if (trivia.Kind != SyntaxKind.WhitespaceTrivia) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| // there was noisy char after end of line trivia | ||
| if (!this.UseIndentation || this.touchedNoisyCharacterOnCurrentLine) | ||
| { | ||
| this.hasTrailingSpace = true; | ||
| return false; | ||
| } | ||
|
|
||
| // right after end of line trivia. calculate indentation for current line | ||
| Debug.Assert(trivia.GetText() == trivia.GetFullText()); | ||
| var text = trivia.GetText(); | ||
|
|
||
| // if text contains tab, we will give up perf optimization and use more expensive one to see whether we need to replace this trivia | ||
| if (text.IndexOf('\t') >= 0) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| this.indentation += text.ConvertStringTextPositionToColumn(this.options.TabSize, this.indentation, text.Length); | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| private bool OnEndOfLine(SyntaxTrivia trivia, int currentIndex) | ||
| { | ||
| if (trivia.Kind != SyntaxKind.EndOfLineTrivia) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| // end of line trivia right after whitespace trivia | ||
| if (this.hasTrailingSpace) | ||
| { | ||
| // has trailing whitespace | ||
| return true; | ||
| } | ||
|
|
||
| // empty line with spaces. remove it. | ||
| if (this.indentation > 0 && !this.touchedNoisyCharacterOnCurrentLine) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| ResetStateAfterNewLine(currentIndex); | ||
| return false; | ||
| } | ||
|
|
||
| private void ResetStateAfterNewLine(int currentIndex) | ||
| { | ||
| // reset states for current line | ||
| this.indentation = 0; | ||
| this.touchedNoisyCharacterOnCurrentLine = false; | ||
| this.hasTrailingSpace = false; | ||
|
|
||
| // remember last line break index | ||
| this.lastLineBreakIndex = currentIndex; | ||
| } | ||
|
|
||
| private bool OnComment(SyntaxTrivia trivia, int currentIndex) | ||
| { | ||
| if (trivia.Kind != SyntaxKind.SingleLineCommentTrivia && | ||
| trivia.Kind != SyntaxKind.MultiLineCommentTrivia && | ||
| trivia.Kind != SyntaxKind.DocumentationComment) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| // check whether indentation are right | ||
| if (this.UseIndentation && this.indentation != this.desiredIndentation) | ||
| { | ||
| // comment has wrong indentation | ||
| return true; | ||
| } | ||
|
|
||
| // go deep down for single line documentation comment | ||
| if (IsSingleLineDocumentComment(trivia) && | ||
| ShouldFormatSingleLineDocumentationComment(this.indentation, this.options.TabSize, trivia)) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| private bool OnSkippedTokensOrText(SyntaxTrivia trivia) | ||
| { | ||
| if (trivia.Kind != SyntaxKind.SkippedTokens && | ||
| trivia.Kind != SyntaxKind.PreprocessingMessageTrivia) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| return Contract.FailWithReturn<bool>("This can't happen"); | ||
| } | ||
|
|
||
| private bool OnRegion(SyntaxTrivia trivia, int currentIndex) | ||
| { | ||
| if (trivia.Kind != SyntaxKind.RegionDirective && | ||
| trivia.Kind != SyntaxKind.EndRegionDirective) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| if (!this.UseIndentation) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| if (indentation != desiredIndentation) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| ResetStateAfterNewLine(currentIndex); | ||
| return false; | ||
| } | ||
|
|
||
| private bool OnPreprocessor(SyntaxTrivia trivia, int currentIndex) | ||
| { | ||
| if (!trivia.Kind.IsPreprocessorDirective()) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| if (!this.UseIndentation) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| // preprocessor must be at from column 0 | ||
| if (this.indentation != 0) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| ResetStateAfterNewLine(currentIndex); | ||
| return false; | ||
| } | ||
|
|
||
| private bool OnTouchedNoisyCharacter(SyntaxTrivia trivia) | ||
| { | ||
| if (trivia.IsElastic || | ||
| trivia.Kind == SyntaxKind.WhitespaceTrivia || | ||
| trivia.Kind == SyntaxKind.EndOfLineTrivia) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| this.touchedNoisyCharacterOnCurrentLine = true; | ||
| this.hasTrailingSpace = false; | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| private bool ShouldFormat() | ||
| { | ||
| for (int i = 0; i < triviaList.Count; i++) | ||
| { | ||
| var trivia = triviaList[i]; | ||
|
|
||
| // order in which these methods run has a side effect. don't change the order | ||
| // each method run | ||
| if (OnElastic(trivia) || | ||
| OnWhitespace(trivia) || | ||
| OnEndOfLine(trivia, i) || | ||
| OnTouchedNoisyCharacter(trivia) || | ||
| OnComment(trivia, i) || | ||
| OnSkippedTokensOrText(trivia) || | ||
| OnRegion(trivia, i) || | ||
| OnPreprocessor(trivia, i)) | ||
| { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| private static bool ShouldFormatSingleLineDocumentationComment(int indentation, int tabSize, SyntaxTrivia trivia) | ||
| { | ||
| var xmlComment = (DocumentationCommentSyntax)trivia.GetStructure(); | ||
|
|
||
| var sawFirstOne = false; | ||
| foreach (var token in xmlComment.DescendantTokens()) | ||
| { | ||
| foreach (var xmlTrivia in token.LeadingTrivia) | ||
| { | ||
| if (xmlTrivia.Kind == SyntaxKind.DocumentationCommentExteriorTrivia) | ||
| { | ||
| // skip first one since its leading whitespace will belong to syntax tree's syntax token | ||
| // not xml doc comment's token | ||
| if (!sawFirstOne) | ||
| { | ||
| sawFirstOne = true; | ||
| break; | ||
| } | ||
|
|
||
| var xmlCommentText = xmlTrivia.GetText(); | ||
|
|
||
| // "///" == 3. | ||
| if (xmlCommentText.ConvertStringTextPositionToColumn(tabSize, xmlCommentText.Length - 3) != indentation) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,395 @@ | ||
| using System.Collections.Generic; | ||
| using Roslyn.Compilers.CSharp; | ||
| using Roslyn.Services.CSharp.Extensions; | ||
| using Roslyn.Services.Formatting; | ||
| using Roslyn.Services.Shared.Extensions; | ||
| using Roslyn.Utilities; | ||
|
|
||
| namespace Roslyn.Services.CSharp.Formatting | ||
| { | ||
| internal partial class TriviaFormatter | ||
| { | ||
| private struct TriviaLineBuilder | ||
| { | ||
| private readonly FormattingContext context; | ||
| private readonly List<SyntaxTrivia> triviaOnLine; | ||
|
|
||
| private bool containsOnlyWhitespace; | ||
| private bool containsSkippedTokensOrText; | ||
|
|
||
| public TriviaLineBuilder(FormattingContext context, List<SyntaxTrivia> buffer) | ||
| { | ||
| Contract.ThrowIfNull(context); | ||
| Contract.ThrowIfNull(buffer); | ||
|
|
||
| this.context = context; | ||
| this.triviaOnLine = buffer; | ||
| this.triviaOnLine.Clear(); | ||
|
|
||
| this.containsOnlyWhitespace = true; | ||
| this.containsSkippedTokensOrText = false; | ||
| } | ||
|
|
||
| public void Add(SyntaxTrivia trivia) | ||
| { | ||
| Contract.ThrowIfTrue(trivia.Kind == SyntaxKind.EndOfLineTrivia); | ||
|
|
||
| if (trivia.Kind == SyntaxKind.WhitespaceTrivia) | ||
| { | ||
| triviaOnLine.Add(trivia); | ||
| return; | ||
| } | ||
|
|
||
| this.containsOnlyWhitespace = false; | ||
|
|
||
| if (trivia.Kind == SyntaxKind.SingleLineCommentTrivia || | ||
| trivia.Kind == SyntaxKind.DisabledTextTrivia || | ||
| trivia.Kind == SyntaxKind.RegionDirective || | ||
| trivia.Kind == SyntaxKind.EndRegionDirective) | ||
| { | ||
| triviaOnLine.Add(trivia); | ||
| } | ||
| else if (trivia.Kind == SyntaxKind.MultiLineCommentTrivia || | ||
| trivia.Kind == SyntaxKind.DocumentationComment) | ||
| { | ||
| triviaOnLine.Add(trivia); | ||
| } | ||
| else if (trivia.Kind == SyntaxKind.SkippedTokens || | ||
| trivia.Kind == SyntaxKind.PreprocessingMessageTrivia) | ||
| { | ||
| this.containsSkippedTokensOrText = true; | ||
| triviaOnLine.Add(trivia); | ||
| } | ||
| else | ||
| { | ||
| Contract.ThrowIfFalse(trivia.Kind.IsPreprocessorDirective()); | ||
| triviaOnLine.Add(trivia); | ||
| } | ||
| } | ||
|
|
||
| public void Reset() | ||
| { | ||
| this.triviaOnLine.Clear(); | ||
|
|
||
| this.containsOnlyWhitespace = true; | ||
| this.containsSkippedTokensOrText = false; | ||
| } | ||
|
|
||
| public void CommitBetweenTokens(int initialColumn, List<SyntaxTrivia> triviaList) | ||
| { | ||
| Contract.ThrowIfTrue(this.containsSkippedTokensOrText); | ||
| Contract.ThrowIfTrue(this.containsOnlyWhitespace); | ||
|
|
||
| var currentColumn = initialColumn; | ||
|
|
||
| // okay, current line should contain more than whitespaces | ||
| for (int i = 0; i < this.triviaOnLine.Count; i++) | ||
| { | ||
| if (TryProcessOneWhitespaceTrivia(i, ref currentColumn, triviaList)) | ||
| { | ||
| continue; | ||
| } | ||
|
|
||
| // the trivia is not a whitespace trivia, just add it to collection and add up full width | ||
| var trivia = this.triviaOnLine[i]; | ||
|
|
||
| triviaList.Add(trivia); | ||
| currentColumn += trivia.FullWidth(); | ||
| } | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| public int CommitLines(bool beginningOfNewLine, List<SyntaxTrivia> triviaList) | ||
| { | ||
| Contract.ThrowIfTrue(this.containsSkippedTokensOrText); | ||
|
|
||
| if (this.containsOnlyWhitespace) | ||
| { | ||
| // number of lines committed | ||
| return 0; | ||
| } | ||
|
|
||
| // start index without any leading whitespace trivia when indentation is used. otherwise start from 0 | ||
| var startIndex = beginningOfNewLine ? GetFirstNonWhitespaceTriviaIndexInBuffer(startIndex: 0) : 0; | ||
| Contract.ThrowIfFalse(startIndex >= 0); | ||
|
|
||
| var baseIndentation = this.context.GetBaseIndentation(triviaOnLine[startIndex].Span.Start); | ||
| return ProcessMultilineTrivia(beginningOfNewLine, startIndex, baseIndentation, triviaList); | ||
| } | ||
|
|
||
| public int CommitLeftOver(bool beginningOfNewLine, int indentation, List<SyntaxTrivia> triviaList) | ||
| { | ||
| Contract.ThrowIfTrue(this.containsSkippedTokensOrText); | ||
|
|
||
| if (this.containsOnlyWhitespace) | ||
| { | ||
| // number of lines committed | ||
| return 0; | ||
| } | ||
|
|
||
| // start index without any leading whitespace trivia when indentation is used. otherwise start from 0 | ||
| var startIndex = beginningOfNewLine ? GetFirstNonWhitespaceTriviaIndexInBuffer(startIndex: 0) : 0; | ||
| Contract.ThrowIfFalse(startIndex >= 0); | ||
|
|
||
| var lineBreaks = ProcessMultilineTrivia(beginningOfNewLine, startIndex, indentation, triviaList); | ||
|
|
||
| triviaList.Add(Syntax.CarriageReturnLineFeed); | ||
| return lineBreaks + 1; | ||
| } | ||
|
|
||
| private int ProcessMultilineTrivia(bool beginningOfNewLine, int startIndex, int indentation, List<SyntaxTrivia> triviaList) | ||
| { | ||
| var lineBreaks = 0; | ||
| var currentColumn = indentation; | ||
|
|
||
| var indentationDelta = beginningOfNewLine ? GetIndentationDelta(indentation) : 0; | ||
|
|
||
| // okay, current line should contain more than whitespaces | ||
| for (int i = startIndex; i < triviaOnLine.Count; i++) | ||
| { | ||
| // indentation only matters to the very first one on current line | ||
| var appendIndentation = beginningOfNewLine && (i == startIndex); | ||
| if (TryProcessOneNonWhitespaceTrivia(i, appendIndentation, indentation, indentationDelta, ref beginningOfNewLine, ref lineBreaks, triviaList)) | ||
| { | ||
| return lineBreaks; | ||
| } | ||
|
|
||
| if (TryProcessOneWhitespaceTrivia(i, ref currentColumn, triviaList)) | ||
| { | ||
| continue; | ||
| } | ||
|
|
||
| // the trivia is not a whitespace trivia, just add its full width. | ||
| currentColumn += this.triviaOnLine[i].FullWidth(); | ||
| } | ||
|
|
||
| return lineBreaks; | ||
| } | ||
|
|
||
| private bool TryProcessOneWhitespaceTrivia(int currentIndex, ref int currentColumn, List<SyntaxTrivia> triviaList) | ||
| { | ||
| var trivia = this.triviaOnLine[currentIndex]; | ||
|
|
||
| // if there is whitespace trivia between two trivia, make sure we calculate right spaces between them. | ||
| if (trivia.Kind != SyntaxKind.WhitespaceTrivia) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| // whitespace between noisy characters. convert tab to space if there is any. | ||
| // tab can only appear in indentation | ||
| var text = trivia.GetText(); | ||
| var spaces = text.ConvertStringTextPositionToColumn(this.context.Options.TabSize, currentColumn, text.Length); | ||
|
|
||
| AppendWhitespaceTrivia(GetSpaces(spaces), triviaList); | ||
|
|
||
| // add right number of spaces | ||
| currentColumn += spaces; | ||
| return true; | ||
| } | ||
|
|
||
| private bool TryProcessOneNonWhitespaceTrivia( | ||
| int currentIndex, | ||
| bool appendIndentation, | ||
| int indentation, | ||
| int indentationDelta, | ||
| ref bool beginningOfNewLine, | ||
| ref int lineBreaks, | ||
| List<SyntaxTrivia> triviaList) | ||
| { | ||
| var trivia = this.triviaOnLine[currentIndex]; | ||
|
|
||
| // well easy case first. | ||
| if (TrySimpleSingleLineOrDisabledTextCase(trivia, beginningOfNewLine, indentation, triviaList, ref lineBreaks)) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| // now complex multiline stuff | ||
| if (trivia.Kind == SyntaxKind.MultiLineCommentTrivia) | ||
| { | ||
| lineBreaks += AppendMultilineDocumentOrRegularComment(appendIndentation, indentation, indentationDelta, trivia, triviaList); | ||
|
|
||
| // only whitespace left. remove trailing whitespace | ||
| if (HasOnlyTrailingWhitespace(currentIndex + 1)) | ||
| { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| if (trivia.Kind == SyntaxKind.DocumentationComment) | ||
| { | ||
| lineBreaks += AppendMultilineDocumentOrRegularComment(appendIndentation, indentation, indentationDelta, trivia, triviaList); | ||
| if (DoneProcessingMultilineDocumentComment(trivia, currentIndex)) | ||
| { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| // no longer it is a beginning of a line | ||
| beginningOfNewLine = false; | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| private bool TrySimpleSingleLineOrDisabledTextCase( | ||
| SyntaxTrivia trivia, | ||
| bool appendIndentation, | ||
| int indentation, | ||
| List<SyntaxTrivia> | ||
| triviaList, | ||
| ref int lineBreaks) | ||
| { | ||
| // well easy case first. | ||
| if (trivia.Kind == SyntaxKind.SingleLineCommentTrivia) | ||
| { | ||
| AppendIndentationStringIfPossible(appendIndentation, indentation, triviaList); | ||
| triviaList.Add(trivia); | ||
| return true; | ||
| } | ||
| else if (trivia.Kind == SyntaxKind.RegionDirective || | ||
| trivia.Kind == SyntaxKind.EndRegionDirective) | ||
| { | ||
| AppendIndentationStringIfPossible(appendIndentation, indentation, triviaList); | ||
|
|
||
| lineBreaks += 1; | ||
| triviaList.Add(trivia); | ||
| return true; | ||
| } | ||
| else if (trivia.Kind.IsPreprocessorDirective()) | ||
| { | ||
| // for now, put it at the column 0 | ||
| lineBreaks += 1; | ||
| triviaList.Add(trivia); | ||
| return true; | ||
| } | ||
| else if (trivia.Kind == SyntaxKind.DisabledTextTrivia) | ||
| { | ||
| lineBreaks += trivia.GetFullText().GetNumberOfLineBreaks(); | ||
| triviaList.Add(trivia); | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| private bool DoneProcessingMultilineDocumentComment(SyntaxTrivia trivia, int currentIndexInBuffer) | ||
| { | ||
| if (IsSingleLineDocumentComment(trivia)) | ||
| { | ||
| // can not have anything after this | ||
| return true; | ||
| } | ||
|
|
||
| // only whitespace left. | ||
| if (HasOnlyTrailingWhitespace(currentIndexInBuffer + 1)) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| private int AppendMultilineDocumentOrRegularComment( | ||
| bool appendIndentation, | ||
| int indentation, | ||
| int indentationDelta, | ||
| SyntaxTrivia trivia, | ||
| List<SyntaxTrivia> triviaList) | ||
| { | ||
| // append indentation of the first line of the xml doc comment | ||
| AppendIndentationStringIfPossible(appendIndentation, indentation, triviaList); | ||
|
|
||
| var forceIndentation = IsSingleLineDocumentComment(trivia); | ||
| if (indentationDelta == 0 && appendIndentation && !forceIndentation) | ||
| { | ||
| // rest of them are already in good shape | ||
| triviaList.Add(trivia); | ||
| } | ||
| else | ||
| { | ||
| // create new xml doc comment | ||
| AppendReindentedText(trivia, forceIndentation && appendIndentation, indentation, indentationDelta, triviaList); | ||
| } | ||
|
|
||
| return trivia.GetFullText().GetNumberOfLineBreaks(); | ||
| } | ||
|
|
||
| private void AppendReindentedText(SyntaxTrivia trivia, bool forceIndentation, int indentation, int indentationDelta, List<SyntaxTrivia> triviaList) | ||
| { | ||
| var xmlDocumentation = trivia.GetFullText().ReindentStartOfXmlDocumentationComment( | ||
| forceIndentation, | ||
| indentation, | ||
| indentationDelta, | ||
| this.context.Options.UseTab, | ||
| this.context.Options.TabSize); | ||
|
|
||
| var parsedTrivia = Syntax.ParseLeadingTrivia(xmlDocumentation); | ||
| Contract.ThrowIfFalse(parsedTrivia.Count == 1); | ||
|
|
||
| triviaList.Add(parsedTrivia[0]); | ||
| } | ||
|
|
||
| private int GetExistingIndentation() | ||
| { | ||
| var spaces = 0; | ||
|
|
||
| for (int i = 0; i < triviaOnLine.Count; i++) | ||
| { | ||
| var trivia = triviaOnLine[i]; | ||
|
|
||
| if (trivia.Kind == SyntaxKind.WhitespaceTrivia) | ||
| { | ||
| var text = trivia.GetText(); | ||
| spaces += text.ConvertStringTextPositionToColumn(this.context.Options.TabSize, spaces, text.Length); | ||
|
|
||
| continue; | ||
| } | ||
|
|
||
| return spaces; | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| private int GetFirstNonWhitespaceTriviaIndexInBuffer(int startIndex) | ||
| { | ||
| for (int i = startIndex; i < triviaOnLine.Count; i++) | ||
| { | ||
| var trivia = triviaOnLine[i]; | ||
|
|
||
| // eat up all leading whitespaces (indentation) | ||
| if (trivia.Kind != SyntaxKind.WhitespaceTrivia) | ||
| { | ||
| return i; | ||
| } | ||
| } | ||
|
|
||
| return -1; | ||
| } | ||
|
|
||
| private bool HasOnlyTrailingWhitespace(int startIndex) | ||
| { | ||
| return GetFirstNonWhitespaceTriviaIndexInBuffer(startIndex) < 0; | ||
| } | ||
|
|
||
| private void AppendIndentationStringIfPossible(bool appendIndentation, int indentation, List<SyntaxTrivia> triviaList) | ||
| { | ||
| // apply indentation only if we are told to do so. | ||
| if (!appendIndentation) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var indentatationString = indentation.CreateIndentationString(this.context.Options.UseTab, this.context.Options.TabSize); | ||
| AppendWhitespaceTrivia(indentatationString, triviaList); | ||
| } | ||
|
|
||
| private int GetIndentationDelta(int indentation) | ||
| { | ||
| return indentation - GetExistingIndentation(); | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| using System.Collections.Concurrent; | ||
| using System.Collections.Generic; | ||
| using Roslyn.Compilers.CSharp; | ||
|
|
||
| namespace Roslyn.Services.CSharp.Formatting | ||
| { | ||
| internal partial class TriviaFormatter | ||
| { | ||
| private class TriviaListPool | ||
| { | ||
| // maximum memory used by the pool is 16*20*28 (sizeof(SyntaxTrivia)) bytes | ||
| private const int MaxPool = 16; | ||
| private const int Threshold = 20; | ||
|
|
||
| private static readonly ConcurrentQueue<List<SyntaxTrivia>> triviaListPool = | ||
| new ConcurrentQueue<List<SyntaxTrivia>>(); | ||
|
|
||
| public static List<SyntaxTrivia> Allocate() | ||
| { | ||
| List<SyntaxTrivia> result; | ||
| if (triviaListPool.TryDequeue(out result)) | ||
| { | ||
| return result; | ||
| } | ||
|
|
||
| return new List<SyntaxTrivia>(); | ||
| } | ||
|
|
||
| public static List<SyntaxTrivia> ReturnAndFree(List<SyntaxTrivia> pool) | ||
| { | ||
| var result = new List<SyntaxTrivia>(pool); | ||
| Free(pool); | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| public static void Free(List<SyntaxTrivia> pool) | ||
| { | ||
| if (triviaListPool.Count >= MaxPool || | ||
| pool.Capacity > Threshold) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| pool.Clear(); | ||
| triviaListPool.Enqueue(pool); | ||
| } | ||
| } | ||
| } | ||
| } |