102 changes: 102 additions & 0 deletions Src/Workspaces/CSharp/Desktop/CSharpWorkspace.Desktop.csproj
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
@@ -1,13 +1,13 @@
// 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.Composition;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServices;

namespace Microsoft.CodeAnalysis.CSharp
{
[ExportLanguageService(typeof(ICommandLineArgumentsFactoryService), LanguageNames.CSharp)]
[ExportLanguageService(typeof(ICommandLineArgumentsFactoryService), LanguageNames.CSharp), Shared]
internal class CSharpCommandLineArgumentsFactoryService : ICommandLineArgumentsFactoryService
{
public CommandLineArguments CreateCommandLineArguments(IEnumerable<string> arguments, string baseDirectory, bool isInteractive)
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
using System;
using System.Collections.Generic;
using System.Composition;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.MSBuild;

namespace Microsoft.CodeAnalysis.CSharp
{
[ExportLanguageServiceFactory(typeof(IProjectFileLoader), LanguageNames.CSharp)]
[Shared]
[ProjectFileExtension("csproj")]
[ProjectTypeGuid("FAE04EC0-301F-11D3-BF4B-00C04F79EFBC")]
internal class CSharpProjectFileLoaderFactory : ILanguageServiceFactory
Expand Down
5 changes: 5 additions & 0 deletions Src/Workspaces/CSharp/Desktop/packages.config
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>
19 changes: 19 additions & 0 deletions Src/Workspaces/CSharp/Extensions/AssemblySymbolExtensions.cs
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;
}
}
}
21 changes: 21 additions & 0 deletions Src/Workspaces/CSharp/Extensions/AttributeSyntaxExtensions.cs
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;
}
}
}
117 changes: 117 additions & 0 deletions Src/Workspaces/CSharp/Extensions/CommonSyntaxNodeExtensions.cs
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);
}
}
}
39 changes: 39 additions & 0 deletions Src/Workspaces/CSharp/Extensions/CommonSyntaxTokenExtensions.cs
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
}
}
15 changes: 15 additions & 0 deletions Src/Workspaces/CSharp/Extensions/CompilationExtensions.cs
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;
}
}
}
}
41 changes: 41 additions & 0 deletions Src/Workspaces/CSharp/Extensions/ICSharpSemanticSnapshot.cs
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>();
}
}
}
38 changes: 38 additions & 0 deletions Src/Workspaces/CSharp/Extensions/INamespaceOrTypeSymbol.cs
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;
}
}
}
59 changes: 59 additions & 0 deletions Src/Workspaces/CSharp/Extensions/ISymbolExtensions.cs
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;
}
}
}
17 changes: 17 additions & 0 deletions Src/Workspaces/CSharp/Extensions/LocationExtensions.cs
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);
}
}
}
29 changes: 29 additions & 0 deletions Src/Workspaces/CSharp/Extensions/MethodSymbolExtensions.cs
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;
}
}
}
1 change: 1 addition & 0 deletions Src/Workspaces/CSharp/Extensions/SemanticEquivalence.cs
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.
22 changes: 22 additions & 0 deletions Src/Workspaces/CSharp/Extensions/SymbolContentBuilderExtensions.cs
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);
}
}
}
}
27 changes: 27 additions & 0 deletions Src/Workspaces/CSharp/Extensions/SymbolExtensions.cs
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;
}
}
}
62 changes: 62 additions & 0 deletions Src/Workspaces/CSharp/Extensions/SymbolInfoExtensions.cs
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);
}
}
}
}
}
30 changes: 30 additions & 0 deletions Src/Workspaces/CSharp/Extensions/SyntaxNodeOrTokenExtensions.cs
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;
}
}
}
}
32 changes: 32 additions & 0 deletions Src/Workspaces/CSharp/Extensions/TypeSymbolExtensions.cs
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;
}
}
}
231 changes: 231 additions & 0 deletions Src/Workspaces/CSharp/Formatting/AutoGeneratedCodeOperationProvider.cs
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;
}
}
}
}
27 changes: 27 additions & 0 deletions Src/Workspaces/CSharp/Formatting/CSharpFormattingService.cs
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);
}
}
}
23 changes: 23 additions & 0 deletions Src/Workspaces/CSharp/Formatting/Engine/Trivia/CSharpTriviaData.cs
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);
}
}
}
}