909 changes: 909 additions & 0 deletions src/VisualStudio/CSharp/Impl/CSharpVSResources.Designer.cs

Large diffs are not rendered by default.

402 changes: 402 additions & 0 deletions src/VisualStudio/CSharp/Impl/CSharpVSResources.resx

Large diffs are not rendered by default.

319 changes: 319 additions & 0 deletions src/VisualStudio/CSharp/Impl/CSharpVisualStudio.csproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft. 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.ComponentModel.Composition;
using System.Composition;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.VisualStudio.LanguageServices.Implementation;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel;
using Microsoft.VisualStudio.Text.Editor;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel
{
[ExportLanguageServiceFactory(typeof(ICodeModelNavigationPointService), LanguageNames.CSharp), Shared]
internal partial class CSharpCodeModelNavigationPointServiceFactory : ILanguageServiceFactory
{
public ILanguageService CreateLanguageService(HostLanguageServices provider)
{
// This interface is implemented by the ICodeModelService as well, so just grab the other one and return it
return provider.GetService<ICodeModelService>();
}
}
}

Large diffs are not rendered by default.

1,290 changes: 1,290 additions & 0 deletions src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.NodeLocator.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel
{
internal partial class CSharpCodeModelService
{
protected override AbstractNodeNameGenerator CreateNodeNameGenerator()
{
return new NodeNameGenerator();
}

private class NodeNameGenerator : AbstractNodeNameGenerator
{
protected override bool IsNameableNode(SyntaxNode node)
{
return CSharpCodeModelService.IsNameableNode(node);
}

private static void AppendName(StringBuilder builder, NameSyntax name)
{
if (name.Kind() == SyntaxKind.QualifiedName)
{
AppendName(builder, ((QualifiedNameSyntax)name).Left);
}

switch (name.Kind())
{
case SyntaxKind.IdentifierName:
AppendDotIfNeeded(builder);
builder.Append(((IdentifierNameSyntax)name).Identifier.ValueText);
break;

case SyntaxKind.GenericName:
var genericName = (GenericNameSyntax)name;
AppendDotIfNeeded(builder);
builder.Append(genericName.Identifier.ValueText);
AppendArity(builder, genericName.Arity);
break;

case SyntaxKind.AliasQualifiedName:
var aliasQualifiedName = (AliasQualifiedNameSyntax)name;
AppendName(builder, aliasQualifiedName.Alias);
builder.Append("::");
AppendName(builder, aliasQualifiedName.Name);
break;

case SyntaxKind.QualifiedName:
AppendName(builder, ((QualifiedNameSyntax)name).Right);
break;
}
}

private static void AppendTypeName(StringBuilder builder, TypeSyntax type)
{
if (type is NameSyntax)
{
AppendName(builder, (NameSyntax)type);
}
else
{
switch (type.Kind())
{
case SyntaxKind.PredefinedType:
builder.Append(((PredefinedTypeSyntax)type).Keyword.ValueText);
break;

case SyntaxKind.ArrayType:
var arrayType = (ArrayTypeSyntax)type;
AppendTypeName(builder, arrayType.ElementType);

var specifiers = arrayType.RankSpecifiers;
for (int i = 0; i < specifiers.Count; i++)
{
builder.Append('[');

var specifier = specifiers[i];
if (specifier.Rank > 1)
{
builder.Append(',', specifier.Rank - 1);
}

builder.Append(']');
}

break;

case SyntaxKind.PointerType:
AppendTypeName(builder, ((PointerTypeSyntax)type).ElementType);
builder.Append('*');
break;

case SyntaxKind.NullableType:
AppendTypeName(builder, ((NullableTypeSyntax)type).ElementType);
builder.Append('?');
break;
}
}
}

private static void AppendParameterList(StringBuilder builder, BaseParameterListSyntax parameterList)
{
builder.Append(parameterList is BracketedParameterListSyntax ? '[' : '(');

var firstSeen = false;

foreach (var parameter in parameterList.Parameters)
{
if (firstSeen)
{
builder.Append(",");
}

if (parameter.Modifiers.Any(SyntaxKind.RefKeyword))
{
builder.Append("ref ");
}
else if (parameter.Modifiers.Any(SyntaxKind.OutKeyword))
{
builder.Append("out ");
}
else if (parameter.Modifiers.Any(SyntaxKind.ParamsKeyword))
{
builder.Append("params ");
}

AppendTypeName(builder, parameter.Type);

firstSeen = true;
}

builder.Append(parameterList is BracketedParameterListSyntax ? ']' : ')');
}

private static void AppendOperatorName(StringBuilder builder, SyntaxKind kind)
{
var name = "#op_" + kind.ToString();
if (name.EndsWith("Keyword"))
{
name = name.Substring(0, name.Length - 7);
}
else if (name.EndsWith("Token"))
{
name = name.Substring(0, name.Length - 5);
}

builder.Append(name);
}

protected override void AppendNodeName(StringBuilder builder, SyntaxNode node)
{
Debug.Assert(node != null);
Debug.Assert(IsNameableNode(node));

AppendDotIfNeeded(builder);

switch (node.Kind())
{
case SyntaxKind.NamespaceDeclaration:
var namespaceDeclaration = (NamespaceDeclarationSyntax)node;
AppendName(builder, namespaceDeclaration.Name);
break;

case SyntaxKind.ClassDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.InterfaceDeclaration:
var typeDeclaration = (TypeDeclarationSyntax)node;
builder.Append(typeDeclaration.Identifier.ValueText);
AppendArity(builder, typeDeclaration.Arity);
break;

case SyntaxKind.EnumDeclaration:
var enumDeclaration = (EnumDeclarationSyntax)node;
builder.Append(enumDeclaration.Identifier.ValueText);
break;

case SyntaxKind.DelegateDeclaration:
var delegateDeclaration = (DelegateDeclarationSyntax)node;
builder.Append(delegateDeclaration.Identifier.ValueText);
AppendArity(builder, delegateDeclaration.Arity);
break;

case SyntaxKind.EnumMemberDeclaration:
var enumMemberDeclaration = (EnumMemberDeclarationSyntax)node;
builder.Append(enumMemberDeclaration.Identifier.ValueText);
break;

case SyntaxKind.VariableDeclarator:
var variableDeclarator = (VariableDeclaratorSyntax)node;
builder.Append(variableDeclarator.Identifier.ValueText);
break;

case SyntaxKind.MethodDeclaration:
var methodDeclaration = (MethodDeclarationSyntax)node;
builder.Append(methodDeclaration.Identifier.ValueText);
AppendArity(builder, methodDeclaration.Arity);
AppendParameterList(builder, methodDeclaration.ParameterList);
break;

case SyntaxKind.OperatorDeclaration:
var operatorDeclaration = (OperatorDeclarationSyntax)node;
AppendOperatorName(builder, operatorDeclaration.OperatorToken.Kind());
AppendParameterList(builder, operatorDeclaration.ParameterList);
break;

case SyntaxKind.ConversionOperatorDeclaration:
var conversionOperatorDeclaration = (ConversionOperatorDeclarationSyntax)node;
AppendOperatorName(builder, conversionOperatorDeclaration.ImplicitOrExplicitKeyword.Kind());
builder.Append('_');
AppendTypeName(builder, conversionOperatorDeclaration.Type);
AppendParameterList(builder, conversionOperatorDeclaration.ParameterList);
break;

case SyntaxKind.ConstructorDeclaration:
var constructorDeclaration = (ConstructorDeclarationSyntax)node;
builder.Append(constructorDeclaration.Modifiers.Any(SyntaxKind.StaticKeyword) ? "#sctor" : "#ctor");
AppendParameterList(builder, constructorDeclaration.ParameterList);
break;

case SyntaxKind.DestructorDeclaration:
builder.Append("#dtor()");
break;

case SyntaxKind.IndexerDeclaration:
var indexerDeclaration = (IndexerDeclarationSyntax)node;
builder.Append("#this");
AppendParameterList(builder, indexerDeclaration.ParameterList);
break;

case SyntaxKind.PropertyDeclaration:
var propertyDeclaration = (PropertyDeclarationSyntax)node;
builder.Append(propertyDeclaration.Identifier.ValueText);
break;

case SyntaxKind.EventDeclaration:
var eventDeclaration = (EventDeclarationSyntax)node;
builder.Append(eventDeclaration.Identifier.ValueText);
break;
}
}
}
}
}
3,853 changes: 3,853 additions & 0 deletions src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft. 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;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel;
using Microsoft.VisualStudio.Text.Editor;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel
{
[ExportLanguageServiceFactory(typeof(ICodeModelService), LanguageNames.CSharp), Shared]
internal partial class CSharpCodeModelServiceFactory : ILanguageServiceFactory
{
private readonly IEditorOptionsFactoryService _editorOptionsFactoryService;
private readonly IEnumerable<IRefactorNotifyService> _refactorNotifyServices;

[ImportingConstructor]
private CSharpCodeModelServiceFactory(
IEditorOptionsFactoryService editorOptionsFactoryService,
[ImportMany] IEnumerable<IRefactorNotifyService> refactorNotifyServices)
{
_editorOptionsFactoryService = editorOptionsFactoryService;
_refactorNotifyServices = refactorNotifyServices;
}

public ILanguageService CreateLanguageService(HostLanguageServices provider)
{
return new CSharpCodeModelService(provider, _editorOptionsFactoryService, _refactorNotifyServices);
}
}
}

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions src/VisualStudio/CSharp/Impl/CodeModel/CSharpProjectCodeModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel
{
internal class CSharpProjectCodeModel : AbstractProjectCodeModel
{
private readonly CSharpProjectShimWithServices _project;

public CSharpProjectCodeModel(CSharpProjectShimWithServices project, VisualStudioWorkspace visualStudioWorkspace, IServiceProvider serviceProvider)
: base(project, visualStudioWorkspace, serviceProvider)
{
_project = project;
}

internal override bool CanCreateFileCodeModelThroughProject(string fileName)
{
return _project.CanCreateFileCodeModelThroughProject(fileName);
}

internal override object CreateFileCodeModelThroughProject(string fileName)
{
return _project.CreateFileCodeModelThroughProject(fileName);
}
}
}
59 changes: 59 additions & 0 deletions src/VisualStudio/CSharp/Impl/CodeModel/EndRegionFormattingRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) Microsoft. 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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Formatting.Rules;
using Microsoft.CodeAnalysis.Options;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel
{
internal class EndRegionFormattingRule : IFormattingRule
{
public void AddSuppressOperations(List<SuppressOperation> list, SyntaxNode node, OptionSet optionSet, NextAction<SuppressOperation> nextOperation)
{
nextOperation.Invoke(list);
}

public void AddAnchorIndentationOperations(List<AnchorIndentationOperation> list, SyntaxNode node, OptionSet optionSet, NextAction<AnchorIndentationOperation> nextOperation)
{
nextOperation.Invoke(list);
}

public void AddIndentBlockOperations(List<IndentBlockOperation> list, SyntaxNode node, OptionSet optionSet, NextAction<IndentBlockOperation> nextOperation)
{
nextOperation.Invoke(list);
}

public void AddAlignTokensOperations(List<AlignTokensOperation> list, SyntaxNode node, OptionSet optionSet, NextAction<AlignTokensOperation> nextOperation)
{
nextOperation.Invoke(list);
}

private bool IsAfterEndRegionBeforeMethodDeclaration(SyntaxToken previousToken, SyntaxToken currentToken)
{
if (previousToken.Kind() == SyntaxKind.EndOfDirectiveToken)
{
var previousPreviousToken = previousToken.GetPreviousToken();
return previousPreviousToken.Kind() == SyntaxKind.EndRegionKeyword;
}

return false;
}

public AdjustNewLinesOperation GetAdjustNewLinesOperation(SyntaxToken previousToken, SyntaxToken currentToken, OptionSet optionSet, NextOperation<AdjustNewLinesOperation> nextOperation)
{
if (IsAfterEndRegionBeforeMethodDeclaration((SyntaxToken)previousToken, (SyntaxToken)currentToken))
{
return FormattingOperations.CreateAdjustNewLinesOperation(2, AdjustNewLinesOption.ForceLines);
}

return nextOperation.Invoke();
}

public AdjustSpacesOperation GetAdjustSpacesOperation(SyntaxToken previousToken, SyntaxToken currentToken, OptionSet optionSet, NextOperation<AdjustSpacesOperation> nextOperation)
{
return nextOperation.Invoke();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Runtime.InteropServices;
using Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.Interop;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel.Extenders
{
[ComVisible(true)]
[ComDefaultInterface(typeof(ICSAutoImplementedPropertyExtender))]
public class AutoImplementedPropertyExtender : ICSAutoImplementedPropertyExtender
{
internal static ICSAutoImplementedPropertyExtender Create(bool isAutoImplemented)
{
var result = new AutoImplementedPropertyExtender(isAutoImplemented);
return (ICSAutoImplementedPropertyExtender)ComAggregate.CreateAggregatedObject(result);
}

private readonly bool _isAutoImplemented;

private AutoImplementedPropertyExtender(bool isAutoImplemented)
{
_isAutoImplemented = isAutoImplemented;
}

public bool IsAutoImplemented
{
get { return _isAutoImplemented; }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Runtime.InteropServices;
using Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.Interop;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel.Extenders
{
[ComVisible(true)]
[ComDefaultInterface(typeof(ICSCodeTypeLocation))]
public class CodeTypeLocationExtender : ICSCodeTypeLocation
{
internal static ICSCodeTypeLocation Create(string externalLocation)
{
var result = new CodeTypeLocationExtender(externalLocation);
return (ICSCodeTypeLocation)ComAggregate.CreateAggregatedObject(result);
}

private readonly string _externalLocation;

private CodeTypeLocationExtender(string externalLocation)
{
_externalLocation = externalLocation;
}

public string ExternalLocation
{
get
{
return _externalLocation;
}
}
}
}
12 changes: 12 additions & 0 deletions src/VisualStudio/CSharp/Impl/CodeModel/Extenders/ExtenderNames.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel.Extenders
{
internal static class ExtenderNames
{
public static string ExternalLocation = "ExternalLocation";
public static string PartialMethod = "PartialMethod";
public static string ExtensionMethod = "ExtensionMethod";
public static string AutoImplementedProperty = "AutoImplementedProperty";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Runtime.InteropServices;
using Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.Interop;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel.Extenders
{
[ComVisible(true)]
[ComDefaultInterface(typeof(ICSExtensionMethodExtender))]
public class ExtensionMethodExtender : ICSExtensionMethodExtender
{
internal static ICSExtensionMethodExtender Create(bool isExtension)
{
var result = new ExtensionMethodExtender(isExtension);
return (ICSExtensionMethodExtender)ComAggregate.CreateAggregatedObject(result);
}

private readonly bool _isExtension;

private ExtensionMethodExtender(bool isExtension)
{
_isExtension = isExtension;
}

public bool IsExtension
{
get { return _isExtension; }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Runtime.InteropServices;
using Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.Interop;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel.Extenders
{
[ComVisible(true)]
[ComDefaultInterface(typeof(ICSPartialMethodExtender))]
public class PartialMethodExtender : ICSPartialMethodExtender
{
internal static ICSPartialMethodExtender Create(bool isPartial, bool isDeclaration, bool hasOtherPart)
{
var result = new PartialMethodExtender(isPartial, isDeclaration, hasOtherPart);
return (ICSPartialMethodExtender)ComAggregate.CreateAggregatedObject(result);
}

private readonly bool _isPartial;
private readonly bool _isDeclaration;
private readonly bool _hasOtherPart;

private PartialMethodExtender(bool isPartial, bool isDeclaration, bool hasOtherPart)
{
_isPartial = isPartial;
_isDeclaration = isDeclaration;
_hasOtherPart = hasOtherPart;
}

public bool IsPartial
{
get { return _isPartial; }
}

public bool IsDeclaration
{
get { return _isDeclaration; }
}

public bool HasOtherPart
{
get { return _hasOtherPart; }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Runtime.InteropServices;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel.Interop
{
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("b093257b-fe0c-4302-ad0f-38e276e57619")]
internal interface ICSAutoImplementedPropertyExtender
{
bool IsAutoImplemented { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Runtime.InteropServices;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel.Interop
{
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("72551468-315b-47c6-92e2-d20b3b92dc94")]
internal interface ICSCodeTypeLocation
{
string ExternalLocation { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Runtime.InteropServices;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel.Interop
{
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("f82170cc-efe8-4f5e-8209-bc2c27b3f54d")]
internal interface ICSExtensionMethodExtender
{
bool IsExtension { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Runtime.InteropServices;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel.Interop
{
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("dfcfc5f4-9404-457c-af3b-c116c7bb5c6d")]
internal interface ICSPartialMethodExtender
{
bool IsPartial { get; }
bool IsDeclaration { get; }
bool HasOtherPart { get; }
}
}
579 changes: 579 additions & 0 deletions src/VisualStudio/CSharp/Impl/CodeModel/MethodXml/MethodXmlBuilder.cs

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions src/VisualStudio/CSharp/Impl/CodeModel/ModifierFlags.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel
{
[Flags]
internal enum ModifierFlags
{
// Note: These are in the order that they appear in modifier lists as generated by Code Model.

Public = 1 << 0,
Protected = 1 << 1,
Internal = 1 << 2,
Private = 1 << 3,
Virtual = 1 << 4,
Abstract = 1 << 5,
New = 1 << 6,
Override = 1 << 7,
Sealed = 1 << 8,
Static = 1 << 9,
Extern = 1 << 10,
Volatile = 1 << 11,
ReadOnly = 1 << 12,
Const = 1 << 13,
Unsafe = 1 << 14,
Async = 1 << 15,
Partial = 1 << 16,

AccessModifierMask = Private | Protected | Internal | Public
}
}
123 changes: 123 additions & 0 deletions src/VisualStudio/CSharp/Impl/CodeModel/ModifierFlagsExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright (c) Microsoft. 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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel
{
internal static class ModifierFlagsExtensions
{
private static readonly SortedList<ModifierFlags, SyntaxKind> s_modifierDefinitions = new SortedList<ModifierFlags, SyntaxKind>
{
{ ModifierFlags.Public, SyntaxKind.PublicKeyword },
{ ModifierFlags.Protected, SyntaxKind.ProtectedKeyword },
{ ModifierFlags.Internal, SyntaxKind.InternalKeyword },
{ ModifierFlags.Private, SyntaxKind.PrivateKeyword },
{ ModifierFlags.Virtual, SyntaxKind.VirtualKeyword },
{ ModifierFlags.Abstract, SyntaxKind.AbstractKeyword },
{ ModifierFlags.New, SyntaxKind.NewKeyword },
{ ModifierFlags.Override, SyntaxKind.OverrideKeyword },
{ ModifierFlags.Sealed, SyntaxKind.SealedKeyword },
{ ModifierFlags.Static, SyntaxKind.StaticKeyword },
{ ModifierFlags.Extern, SyntaxKind.ExternKeyword },
{ ModifierFlags.ReadOnly, SyntaxKind.ReadOnlyKeyword },
{ ModifierFlags.Const, SyntaxKind.ConstKeyword },
{ ModifierFlags.Volatile, SyntaxKind.VolatileKeyword },
{ ModifierFlags.Unsafe, SyntaxKind.UnsafeKeyword },
{ ModifierFlags.Async, SyntaxKind.AsyncKeyword },
{ ModifierFlags.Partial, SyntaxKind.PartialKeyword }
};

public static ModifierFlags GetModifierFlags(this MemberDeclarationSyntax member)
{
ModifierFlags result = 0;

foreach (var modifier in member.GetModifiers())
{
switch (modifier.Kind())
{
case SyntaxKind.PublicKeyword:
result |= ModifierFlags.Public;
break;
case SyntaxKind.ProtectedKeyword:
result |= ModifierFlags.Protected;
break;
case SyntaxKind.InternalKeyword:
result |= ModifierFlags.Internal;
break;
case SyntaxKind.PrivateKeyword:
result |= ModifierFlags.Private;
break;
case SyntaxKind.VirtualKeyword:
result |= ModifierFlags.Virtual;
break;
case SyntaxKind.AbstractKeyword:
result |= ModifierFlags.Abstract;
break;
case SyntaxKind.NewKeyword:
result |= ModifierFlags.New;
break;
case SyntaxKind.OverrideKeyword:
result |= ModifierFlags.Override;
break;
case SyntaxKind.SealedKeyword:
result |= ModifierFlags.Sealed;
break;
case SyntaxKind.StaticKeyword:
result |= ModifierFlags.Static;
break;
case SyntaxKind.ExternKeyword:
result |= ModifierFlags.Extern;
break;
case SyntaxKind.ReadOnlyKeyword:
result |= ModifierFlags.ReadOnly;
break;
case SyntaxKind.ConstKeyword:
result |= ModifierFlags.Const;
break;
case SyntaxKind.VolatileKeyword:
result |= ModifierFlags.Volatile;
break;
case SyntaxKind.UnsafeKeyword:
result |= ModifierFlags.Unsafe;
break;
case SyntaxKind.AsyncKeyword:
result |= ModifierFlags.Async;
break;
case SyntaxKind.PartialKeyword:
result |= ModifierFlags.Partial;
break;
}
}

return result;
}

public static MemberDeclarationSyntax UpdateModifiers(this MemberDeclarationSyntax member, ModifierFlags flags)
{
// The starting token for this member may change, so we need to save
// the leading trivia and reattach it after updating the modifiers.
// We also need to remove it here to avoid duplicates.
var leadingTrivia = member.GetLeadingTrivia();
member = member.WithLeadingTrivia(SyntaxTriviaList.Empty);

var newModifierList = new List<SyntaxToken>();
foreach (var modifierDefinition in s_modifierDefinitions)
{
if ((flags & modifierDefinition.Key) != 0)
{
newModifierList.Add(SyntaxFactory.Token(modifierDefinition.Value));
}
}

var newModifiers = SyntaxFactory.TokenList(newModifierList);
var newMember = (MemberDeclarationSyntax)member.WithModifiers(SyntaxFactory.TokenList(newModifierList));

return newMember.WithLeadingTrivia(leadingTrivia);
}
}
}
14 changes: 14 additions & 0 deletions src/VisualStudio/CSharp/Impl/CodeModel/ParameterFlags.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel
{
[Flags]
internal enum ParameterFlags
{
Ref = 1 << 0,
Out = 1 << 1,
Params = 1 << 2
}
}
37 changes: 37 additions & 0 deletions src/VisualStudio/CSharp/Impl/CodeModel/ParameterFlagsExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) Microsoft. 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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel
{
internal static class ParameterFlagsExtensions
{
public static ParameterFlags GetParameterFlags(this ParameterSyntax parameter)
{
ParameterFlags result = 0;

foreach (var modifier in parameter.Modifiers)
{
switch (modifier.Kind())
{
case SyntaxKind.RefKeyword:
result |= ParameterFlags.Ref;
break;
case SyntaxKind.OutKeyword:
result |= ParameterFlags.Out;
break;
case SyntaxKind.ParamsKeyword:
result |= ParameterFlags.Params;
break;
}
}

return result;
}
}
}
29 changes: 29 additions & 0 deletions src/VisualStudio/CSharp/Impl/CodeModel/SyntaxListExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel
{
internal static class SyntaxListExtensions
{
public static IReadOnlyList<TNode> AsReadOnlyList<TNode>(this SyntaxList<TNode> list)
where TNode : SyntaxNode
{
return list;
}

public static IReadOnlyList<TNode> AsReadOnlyList<TNode>(this SeparatedSyntaxList<TNode> separatedList)
where TNode : SyntaxNode
{
return separatedList;
}
}
}
72 changes: 72 additions & 0 deletions src/VisualStudio/CSharp/Impl/Debugging/BreakpointGetter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.Editor.Implementation.Debugging;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.Debugging
{
internal static class BreakpointGetter
{
internal static async Task<BreakpointResolutionResult> GetBreakpointAsync(Document document, int position, CancellationToken cancellationToken)
{
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

TextSpan span;
if (!TryGetBreakpointSpan(tree, position, cancellationToken, out span))
{
return null;
}

if (span.Length == 0)
{
return BreakpointResolutionResult.CreateLineResult(document);
}

return BreakpointResolutionResult.CreateSpanResult(document, span);
}

internal static bool TryGetBreakpointSpan(SyntaxTree tree, int position, CancellationToken cancellationToken, out TextSpan breakpointSpan)
{
var source = tree.GetText(cancellationToken);

// If the line is entirely whitespace, then don't set any breakpoint there.
var line = source.Lines.GetLineFromPosition(position);
if (IsBlank(line))
{
breakpointSpan = default(TextSpan);
return false;
}

// If the user is asking for breakpoint in an inactive region, then just create a line
// breakpoint there.
if (tree.IsInInactiveRegion(position, cancellationToken))
{
breakpointSpan = default(TextSpan);
return true;
}

var root = tree.GetRoot(cancellationToken);
return root.TryGetClosestBreakpointSpan(position, out breakpointSpan);
}

private static bool IsBlank(TextLine line)
{
var text = line.ToString();

for (int i = 0; i < text.Length; i++)
{
if (!SyntaxFacts.IsWhitespace(text[i]))
{
return false;
}
}

return true;
}
}
}
102 changes: 102 additions & 0 deletions src/VisualStudio/CSharp/Impl/Debugging/BreakpointResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.VisualStudio.LanguageServices.Implementation.Debugging;
using Roslyn.Utilities;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.Debugging
{
internal class BreakpointResolver : AbstractBreakpointResolver
{
public BreakpointResolver(Solution solution, string text)
: base(solution, text, LanguageNames.CSharp, EqualityComparer<string>.Default)
{
}

protected override IEnumerable<ISymbol> GetMembers(INamedTypeSymbol type, string name)
{
var members = type.GetMembers()
.Where(m => m.Name == name ||
m.ExplicitInterfaceImplementations()
.Where(i => i.Name == name)
.Any());

return (type.Name == name) ? members.Concat(type.Constructors) : members;
}

protected override bool HasMethodBody(IMethodSymbol method, CancellationToken cancellationToken)
{
var location = method.Locations.First(loc => loc.IsInSource);
var tree = location.SourceTree;
var token = tree.GetRoot(cancellationToken).FindToken(location.SourceSpan.Start);

return token.GetAncestor<MemberDeclarationSyntax>().GetBody() != null;
}

protected override void ParseText(
out IList<NameAndArity> nameParts,
out int? parameterCount)
{
var text = this.Text;

Debug.Assert(text != null);

var name = SyntaxFactory.ParseName(text, consumeFullText: false);
var lengthOfParsedText = name.FullSpan.End;
var parameterList = SyntaxFactory.ParseParameterList(text, lengthOfParsedText, consumeFullText: false);
var foundIncompleteParameterList = false;

parameterCount = null;
if (!parameterList.IsMissing)
{
if (parameterList.OpenParenToken.IsMissing || parameterList.CloseParenToken.IsMissing)
{
foundIncompleteParameterList = true;
}
else
{
lengthOfParsedText += parameterList.FullSpan.End;
parameterCount = parameterList.Parameters.Count;
}
}

// If there is remaining text to parse, attempt to eat a trailing semicolon.
if (lengthOfParsedText < text.Length)
{
var token = SyntaxFactory.ParseToken(text, lengthOfParsedText);
if (token.IsKind(SyntaxKind.SemicolonToken))
{
lengthOfParsedText += token.FullSpan.End;
}
}

// It's not obvious, but this method can handle the case where name "IsMissing" (no suitable name was be parsed).
var parts = name.GetNameParts();

// If we could not parse a valid parameter list or there was additional trailing text that could not be
// interpreted, don't return any names or parameters.
// Also, "Break at Function" doesn't seem to support alias qualified names with the old language service,
// and aliases don't seem meaningful for the purposes of resolving symbols from source. Since we don't
// have precedent or a clear user scenario, we won't resolve any alias qualified names (alias qualified
// parameters are accepted, but we still only validate parameter count, similar to the old implementation).
if (!foundIncompleteParameterList && (lengthOfParsedText == text.Length) &&
!parts.Any(p => p.IsKind(SyntaxKind.AliasQualifiedName)))
{
nameParts = parts.Cast<SimpleNameSyntax>().Select(p => new NameAndArity(p.Identifier.ValueText, p.Arity)).ToList();
}
else
{
nameParts = SpecializedCollections.EmptyList<NameAndArity>();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Microsoft. 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 System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Implementation.Debugging;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.Debugging
{
[ExportLanguageService(typeof(IBreakpointResolutionService), LanguageNames.CSharp), Shared]
internal partial class CSharpBreakpointResolutionService : IBreakpointResolutionService
{
public Task<BreakpointResolutionResult> ResolveBreakpointAsync(
Document document, TextSpan textSpan, CancellationToken cancellationToken)
{
return BreakpointGetter.GetBreakpointAsync(document, textSpan.Start, cancellationToken);
}

public Task<IEnumerable<BreakpointResolutionResult>> ResolveBreakpointsAsync(Solution solution, string name, CancellationToken cancellationToken)
{
return new BreakpointResolver(solution, name).DoAsync(cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Implementation.Debugging;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.VisualStudio.LanguageServices.Implementation.Debugging;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.Debugging
{
[ExportLanguageService(typeof(ILanguageDebugInfoService), LanguageNames.CSharp), Shared]
internal partial class CSharpLanguageDebugInfoService : ILanguageDebugInfoService
{
public Task<DebugLocationInfo> GetLocationInfoAsync(Document document, int position, CancellationToken cancellationToken)
{
return LocationInfoGetter.GetInfoAsync(document, position, cancellationToken);
}

public Task<DebugDataTipInfo> GetDataTipInfoAsync(
Document document, int position, CancellationToken cancellationToken)
{
return DataTipInfoGetter.GetInfoAsync(document, position, cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.Debugging
{
internal partial class CSharpProximityExpressionsService
{
// Flags used for "collecting" terms for proximity expressions. The flags are somewhat
// confusing. The key point to remember is that an expression will be placed in the result
// set if and only if ValidTerm is set. ValidExpression is used to indicate that while an
// expression may not be a ValidTerm by itself, it can be used as a sub-expression of a
// larger expression that IS a ValidTerm. (Note: ValidTerm implies ValidExpression)
//
// For example, consider the expression a[b+c]. The analysis of this expression starts at
// the ElementAccessExpression. The rules for an ElementAccessExpression say that the
// expression is a ValidTerm if and only if both the LHS('a' in this case) and the
// RHS('b+c') are valid ValidExpressions. The LHS is a ValidTerm, and the RHS is a binary o
// perator-- this time AddExpression. The rules for AddExpression state that the expression
// is never a ValidTerm, but is a ValidExpression if both the LHS and the RHS are
// ValidExpressions. In this case, both 'b' and 'c' are ValidTerms (thus valid expressions),
// so 'a+b' is a ValidExpression (but not a ValidTerm), and finally 'a[b+c]' is considered a
// ValidTerm. So, the result of GetProximityExpressions for this expression would be:
//
// a
//
// b
//
// c
//
// a[b+c]
//
// (but not "b+c")
[Flags]
private enum ExpressionType
{
Invalid = 0x0,
ValidExpression = 0x1,

// Note: ValidTerm implies ValidExpression.
ValidTerm = 0x3
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright (c) Microsoft. 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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.Debugging
{
internal partial class CSharpProximityExpressionsService
{
private class RelevantExpressionsCollector : CSharpSyntaxVisitor
{
private readonly bool _includeDeclarations;
private readonly IList<string> _expressions;

public RelevantExpressionsCollector(bool includeDeclarations, IList<string> expressions)
{
_includeDeclarations = includeDeclarations;
_expressions = expressions;
}

public override void VisitLabeledStatement(LabeledStatementSyntax node)
{
AddRelevantExpressions(node.Statement, _expressions, _includeDeclarations);
}

public override void VisitExpressionStatement(ExpressionStatementSyntax node)
{
AddExpressionTerms(node.Expression, _expressions);
}

public override void VisitReturnStatement(ReturnStatementSyntax node)
{
AddExpressionTerms(node.Expression, _expressions);
}

public override void VisitThrowStatement(ThrowStatementSyntax node)
{
AddExpressionTerms(node.Expression, _expressions);
}

public override void VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node)
{
// Here, we collect expression expressions from any/all initialization expressions
AddVariableExpressions(node.Declaration.Variables, _expressions);
}

public override void VisitDoStatement(DoStatementSyntax node)
{
AddExpressionTerms(node.Condition, _expressions);
}

public override void VisitLockStatement(LockStatementSyntax node)
{
AddExpressionTerms(node.Expression, _expressions);
}

public override void VisitWhileStatement(WhileStatementSyntax node)
{
AddExpressionTerms(node.Condition, _expressions);
}

public override void VisitIfStatement(IfStatementSyntax node)
{
AddExpressionTerms(node.Condition, _expressions);
}

public override void VisitForStatement(ForStatementSyntax node)
{
if (node.Declaration != null)
{
AddVariableExpressions(node.Declaration.Variables, _expressions);
}

node.Initializers.Do(i => AddExpressionTerms(i, _expressions));
AddExpressionTerms(node.Condition, _expressions);
node.Incrementors.Do(i => AddExpressionTerms(i, _expressions));
}

public override void VisitForEachStatement(ForEachStatementSyntax node)
{
_expressions.Add(node.Identifier.ValueText);
AddExpressionTerms(node.Expression, _expressions);
}

public override void VisitUsingStatement(UsingStatementSyntax node)
{
if (node.Declaration != null)
{
AddVariableExpressions(node.Declaration.Variables, _expressions);
}

AddExpressionTerms(node.Expression, _expressions);
}

public override void VisitSwitchStatement(SwitchStatementSyntax node)
{
AddExpressionTerms(node.Expression, _expressions);
}

private void AddVariableExpressions(
SeparatedSyntaxList<VariableDeclaratorSyntax> declarators,
IList<string> expressions)
{
foreach (var declarator in declarators)
{
if (_includeDeclarations)
{
expressions.Add(declarator.Identifier.ValueText);
}

if (declarator.Initializer != null)
{
AddExpressionTerms(declarator.Initializer.Value, expressions);
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
// Copyright (c) Microsoft. 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 System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.Debugging
{
internal partial class CSharpProximityExpressionsService
{
internal class Worker
{
private readonly SyntaxTree _syntaxTree;
private readonly int _position;

private StatementSyntax _parentStatement;
private SyntaxToken _token;
private readonly List<string> _expressions = new List<string>();

public Worker(SyntaxTree syntaxTree, int position)
{
_syntaxTree = syntaxTree;
_position = position;
}

internal IList<string> Do(CancellationToken cancellationToken)
{
// First, find the containing statement. We'll want to add the expressions in this
// statement to the result.
_token = _syntaxTree.GetRoot(cancellationToken).FindToken(_position);
_parentStatement = _token.GetAncestor<StatementSyntax>();
if (_parentStatement == null)
{
return null;
}

AddRelevantExpressions(_parentStatement, _expressions, includeDeclarations: false);
AddPrecedingRelevantExpressions();
AddFollowingRelevantExpressions(cancellationToken);
AddCurrentDeclaration();
AddMethodParameters();
AddIndexerParameters();
AddCatchParameters();
AddThisExpression();
AddValueExpression();

var result = _expressions.Distinct().Where(e => e.Length > 0).ToList();
return result.Count == 0 ? null : result;
}

private void AddValueExpression()
{
// If we're in a setter/adder/remover then add "value".
if (_parentStatement.GetAncestorOrThis<AccessorDeclarationSyntax>().IsKind(SyntaxKind.SetAccessorDeclaration, SyntaxKind.AddAccessorDeclaration, SyntaxKind.RemoveAccessorDeclaration))
{
_expressions.Add("value");
}
}

private void AddThisExpression()
{
// If it's an instance member, then also add "this".
var memberDeclaration = _parentStatement.GetAncestorOrThis<MemberDeclarationSyntax>();
if (!memberDeclaration.GetModifiers().Any(SyntaxKind.StaticKeyword))
{
_expressions.Add("this");
}
}

private void AddCatchParameters()
{
var block = GetImmediatelyContainingBlock();

// if we're the start of a "catch(Foo e)" clause, then add "e".
if (block != null && block.IsParentKind(SyntaxKind.CatchClause))
{
var catchClause = (CatchClauseSyntax)block.Parent;
if (catchClause.Declaration != null && catchClause.Declaration.Identifier.Kind() != SyntaxKind.None)
{
_expressions.Add(catchClause.Declaration.Identifier.ValueText);
}
}
}

private BlockSyntax GetImmediatelyContainingBlock()
{
return IsFirstBlockStatement()
? (BlockSyntax)_parentStatement.Parent
: _parentStatement is BlockSyntax && ((BlockSyntax)_parentStatement).OpenBraceToken == _token
? (BlockSyntax)_parentStatement
: null;
}

private bool IsFirstBlockStatement()
{
var parentBlockOpt = _parentStatement.Parent as BlockSyntax;
return parentBlockOpt != null && parentBlockOpt.Statements.FirstOrDefault() == _parentStatement;
}

private void AddCurrentDeclaration()
{
if (_parentStatement is LocalDeclarationStatementSyntax)
{
AddRelevantExpressions(_parentStatement, _expressions, includeDeclarations: true);
}
}

private void AddMethodParameters()
{
// and we're the start of a method, then also add the parameters of that method to
// the proximity expressions.
var block = GetImmediatelyContainingBlock();

if (block != null && block.Parent is MemberDeclarationSyntax)
{
var parameterList = ((MemberDeclarationSyntax)block.Parent).GetParameterList();
AddParameters(parameterList);
}
}

private void AddIndexerParameters()
{
var block = GetImmediatelyContainingBlock();

// and we're the start of a method, then also add the parameters of that method to
// the proximity expressions.
if (block != null &&
block.Parent is AccessorDeclarationSyntax &&
block.Parent.Parent is AccessorListSyntax &&
block.Parent.Parent.Parent is IndexerDeclarationSyntax)
{
var parameterList = ((IndexerDeclarationSyntax)block.Parent.Parent.Parent).ParameterList;
AddParameters(parameterList);
}
}

private void AddParameters(BaseParameterListSyntax parameterList)
{
if (parameterList != null)
{
_expressions.AddRange(
from p in parameterList.Parameters
select p.Identifier.ValueText);
}
}

private void AddFollowingRelevantExpressions(CancellationToken cancellationToken)
{
var line = _syntaxTree.GetText(cancellationToken).Lines.IndexOf(_position);

// If there's are more statements following us on the same line, then add them as
// well.
for (var nextStatement = _parentStatement.GetNextStatement();
nextStatement != null && _syntaxTree.GetText(cancellationToken).Lines.IndexOf(nextStatement.SpanStart) == line;
nextStatement = nextStatement.GetNextStatement())
{
AddRelevantExpressions(nextStatement, _expressions, includeDeclarations: false);
}
}

private void AddPrecedingRelevantExpressions()
{
// If we're not the first statement in this block,
// and there's an expression or declaration statement directly above us,
// then add the expressions from that as well.

StatementSyntax previousStatement;

var block = _parentStatement as BlockSyntax;
if (block != null &&
block.CloseBraceToken == _token)
{
// If we're at the last brace of a block, use the last
// statement in the block.
previousStatement = block.Statements.LastOrDefault();
}
else
{
previousStatement = _parentStatement.GetPreviousStatement();
}

if (previousStatement != null)
{
switch (previousStatement.Kind())
{
case SyntaxKind.ExpressionStatement:
case SyntaxKind.LocalDeclarationStatement:
AddRelevantExpressions(previousStatement, _expressions, includeDeclarations: true);
break;
case SyntaxKind.DoStatement:
AddExpressionTerms((previousStatement as DoStatementSyntax).Condition, _expressions);
AddLastStatementOfConstruct(previousStatement);
break;
case SyntaxKind.ForStatement:
case SyntaxKind.ForEachStatement:
case SyntaxKind.IfStatement:
case SyntaxKind.CheckedStatement:
case SyntaxKind.UncheckedStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.LockStatement:
case SyntaxKind.SwitchStatement:
case SyntaxKind.TryStatement:
case SyntaxKind.UsingStatement:
AddRelevantExpressions(previousStatement, _expressions, includeDeclarations: false);
AddLastStatementOfConstruct(previousStatement);
break;
default:
break;
}
}
else
{
// This is the first statement of the block. Go to the nearest enclosing statement and add its expressions
var statementAncestor = _parentStatement.Ancestors().OfType<StatementSyntax>().FirstOrDefault(node => !node.IsKind(SyntaxKind.Block));
if (statementAncestor != null)
{
AddRelevantExpressions(statementAncestor, _expressions, includeDeclarations: true);
}
}
}

private void AddLastStatementOfConstruct(StatementSyntax statement)
{
if (statement == default(StatementSyntax))
{
return;
}

switch (statement.Kind())
{
case SyntaxKind.Block:
AddLastStatementOfConstruct((statement as BlockSyntax).Statements.LastOrDefault());
break;
case SyntaxKind.BreakStatement:
case SyntaxKind.ContinueStatement:
AddLastStatementOfConstruct(statement.GetPreviousStatement());
break;
case SyntaxKind.CheckedStatement:
case SyntaxKind.UncheckedStatement:
AddLastStatementOfConstruct((statement as CheckedStatementSyntax).Block);
break;
case SyntaxKind.DoStatement:
AddLastStatementOfConstruct((statement as DoStatementSyntax).Statement);
break;
case SyntaxKind.ForStatement:
AddLastStatementOfConstruct((statement as ForStatementSyntax).Statement);
break;
case SyntaxKind.ForEachStatement:
AddLastStatementOfConstruct((statement as ForEachStatementSyntax).Statement);
break;
case SyntaxKind.IfStatement:
var ifStatement = statement as IfStatementSyntax;
AddLastStatementOfConstruct(ifStatement.Statement);
if (ifStatement.Else != null)
{
AddLastStatementOfConstruct(ifStatement.Else.Statement);
}

break;
case SyntaxKind.LockStatement:
AddLastStatementOfConstruct((statement as LockStatementSyntax).Statement);
break;
case SyntaxKind.SwitchStatement:
var switchStatement = statement as SwitchStatementSyntax;
foreach (var section in switchStatement.Sections)
{
AddLastStatementOfConstruct(section.Statements.LastOrDefault());
}

break;
case SyntaxKind.TryStatement:
var tryStatement = statement as TryStatementSyntax;
if (tryStatement.Finally != null)
{
AddLastStatementOfConstruct(tryStatement.Finally.Block);
}
else
{
AddLastStatementOfConstruct(tryStatement.Block);
foreach (var catchClause in tryStatement.Catches)
{
AddLastStatementOfConstruct(catchClause.Block);
}
}

break;
case SyntaxKind.UsingStatement:
AddLastStatementOfConstruct((statement as UsingStatementSyntax).Statement);
break;
case SyntaxKind.WhileStatement:
AddLastStatementOfConstruct((statement as WhileStatementSyntax).Statement);
break;
default:
AddRelevantExpressions(statement, _expressions, includeDeclarations: false);
break;
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright (c) Microsoft. 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 System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.VisualStudio.LanguageServices.Implementation.Debugging;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.Debugging
{
/// <summary>
/// Given a position in a source file, returns the expressions in close proximity that should
/// show up in the debugger 'autos' window. In general, the expressions we place into the autos
/// window are things that appear to be 'side effect free'. Note: because we only use the syntax
/// tree for this, it's possible for us to get this wrong. However, this should only happen in
/// code that behaves unexpectedly. For example, we will assume that "a + b" is side effect free
/// (when in practice it may not be).
///
/// The general tactic we take is to add the expressions for the statements on the
/// line the debugger is currently at. We will also try to find the 'previous' statement as well
/// to add the expressions from that. The 'previous' statement is a bit of an interesting beast.
/// Consider, for example, if the user has just jumped out of a switch and is the statement
/// directly following it. What is the previous statement? Without keeping state, there's no way
/// to know. So, in this case, we treat all 'exit points' (i.e. the last statement of a switch
/// section) of the switch statement as the 'previous statement'. There are many cases like this
/// we need to handle. Basically anything that might have nested statements/blocks might
/// contribute to the 'previous statement'
/// </summary>
[ExportLanguageService(typeof(IProximityExpressionsService), LanguageNames.CSharp), Shared]
internal partial class CSharpProximityExpressionsService : IProximityExpressionsService
{
public async Task<bool> IsValidAsync(
Document document,
int position,
string expressionValue,
CancellationToken cancellationToken)
{
var expression = SyntaxFactory.ParseExpression(expressionValue);
var root = await document.GetCSharpSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var token = root.FindToken(position);
if (token.Kind() == SyntaxKind.CloseBraceToken && token.GetPreviousToken().Kind() != SyntaxKind.None)
{
token = token.GetPreviousToken();
}

var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var info = semanticModel.GetSpeculativeSymbolInfo(token.SpanStart, expression, SpeculativeBindingOption.BindAsExpression);
if (info.Symbol == null)
{
return false;
}

// We seem to have bound successfully. However, if it bound to a local, then make
// sure that that local isn't after the statement that we're currently looking at.
if (info.Symbol.Kind == SymbolKind.Local)
{
var statement = info.Symbol.Locations.First().FindToken(cancellationToken).GetAncestor<StatementSyntax>();
if (statement != null && position < statement.SpanStart)
{
return false;
}
}

return true;
}

public async Task<IList<string>> GetProximityExpressionsAsync(
Document document,
int position,
CancellationToken cancellationToken)
{
var tree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
return Do(tree, position, cancellationToken);
}

// Internal for testing purposes
internal static IList<string> Do(SyntaxTree syntaxTree, int position)
{
return Do(syntaxTree, position, CancellationToken.None);
}

private static IList<string> Do(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken)
{
return new Worker(syntaxTree, position).Do(cancellationToken);
}

private static void AddRelevantExpressions(
StatementSyntax statement,
IList<string> expressions,
bool includeDeclarations)
{
new RelevantExpressionsCollector(includeDeclarations, expressions).Visit(statement);
}
}
}

Large diffs are not rendered by default.

73 changes: 73 additions & 0 deletions src/VisualStudio/CSharp/Impl/Debugging/DataTipInfoGetter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editor.Implementation.Debugging;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.VisualStudio.LanguageServices.Implementation.Debugging;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.Debugging
{
internal static class DataTipInfoGetter
{
internal static async Task<DebugDataTipInfo> GetInfoAsync(Document document, int position, CancellationToken cancellationToken)
{
var root = await document.GetCSharpSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var token = root.FindToken(position);

var expression = token.Parent as ExpressionSyntax;
if (expression == null)
{
return token.IsKind(SyntaxKind.IdentifierToken)
? new DebugDataTipInfo(token.Span, text: null)
: default(DebugDataTipInfo);
}

if (expression.IsAnyLiteralExpression())
{
// If the user hovers over a literal, give them a DataTip for the type of the
// literal they're hovering over.
// Partial semantics should always be sufficient because the (unconverted) type
// of a literal can always easily be determined.
var partialDocument = await document.WithFrozenPartialSemanticsAsync(cancellationToken).ConfigureAwait(false);
var semanticModel = await partialDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var type = semanticModel.GetTypeInfo(expression, cancellationToken).Type;
return type == null
? default(DebugDataTipInfo)
: new DebugDataTipInfo(expression.Span, type.ToNameDisplayString());
}

if (expression.IsRightSideOfDotOrArrow())
{
expression = (ExpressionSyntax)expression.Parent;
}

// NOTE(cyrusn): This behavior is to mimic what we did in Dev10, i'm not sure if it's
// necessary or not.
if (expression.IsKind(SyntaxKind.InvocationExpression))
{
expression = ((InvocationExpressionSyntax)expression).Expression;
}

string textOpt = null;
var typeSyntax = expression as TypeSyntax;
if (typeSyntax != null && typeSyntax.IsVar)
{
// If the user is hovering over 'var', then pass back the full type name that 'var'
// binds to.
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var type = semanticModel.GetTypeInfo(typeSyntax, cancellationToken).Type;
if (type != null)
{
textOpt = type.ToNameDisplayString();
}
}

return new DebugDataTipInfo(expression.Span, textOpt);
}
}
}
94 changes: 94 additions & 0 deletions src/VisualStudio/CSharp/Impl/Debugging/LocationInfoGetter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editor.Implementation.Debugging;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.VisualStudio.LanguageServices.Implementation.Debugging;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.Debugging
{
internal static class LocationInfoGetter
{
private static readonly SymbolDisplayFormat s_nameFormat =
new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
propertyStyle: SymbolDisplayPropertyStyle.NameOnly,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance,
memberOptions: SymbolDisplayMemberOptions.IncludeContainingType | SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeExplicitInterface,
parameterOptions:
SymbolDisplayParameterOptions.IncludeParamsRefOut |
SymbolDisplayParameterOptions.IncludeExtensionThis |
SymbolDisplayParameterOptions.IncludeType |
SymbolDisplayParameterOptions.IncludeName,
miscellaneousOptions:
SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers |
SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

internal static async Task<DebugLocationInfo> GetInfoAsync(Document document, int position, CancellationToken cancellationToken)
{
string name = null;
int lineOffset = 0;

// Note that we get the current partial solution here. Technically, this means that we may
// not fully understand the signature of the member. But that's ok. We just need this
// symbol so we can create a display string to put into the debugger. If we try to
// find the document in the "CurrentSolution" then when we try to get the semantic
// model below then it might take a *long* time as all dependent compilations are built.
var tree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false);
var token = root.FindToken(position);
SyntaxNode memberDecl = token.GetAncestor<MemberDeclarationSyntax>();

// field or event field declarations may contain multiple variable declarators. Try finding the correct one.
// If the position does not point to one, try using the first one.
if (memberDecl != null &&
(memberDecl.Kind() == SyntaxKind.FieldDeclaration || memberDecl.Kind() == SyntaxKind.EventFieldDeclaration))
{
SeparatedSyntaxList<VariableDeclaratorSyntax> variableDeclarators = ((BaseFieldDeclarationSyntax)memberDecl).Declaration.Variables;

foreach (var declarator in variableDeclarators)
{
if (declarator.FullSpan.Contains(token.FullSpan))
{
memberDecl = declarator;
break;
}
}

if (memberDecl == null)
{
memberDecl = variableDeclarators.Count > 0 ? variableDeclarators[0] : null;
}
}

if (memberDecl != null)
{
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var memberSymbol = semanticModel.GetDeclaredSymbol(memberDecl, cancellationToken);

if (memberSymbol != null)
{
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
var lineNumber = text.Lines.GetLineFromPosition(position).LineNumber;

var accessor = token.GetAncestor<AccessorDeclarationSyntax>();
var memberLine = accessor == null
? text.Lines.GetLineFromPosition(memberDecl.SpanStart).LineNumber
: text.Lines.GetLineFromPosition(accessor.SpanStart).LineNumber;

name = memberSymbol.ToDisplayString(s_nameFormat);
lineOffset = lineNumber - memberLine;
return new DebugLocationInfo(name, lineOffset);
}
}

return default(DebugLocationInfo);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribute;
using Microsoft.VisualStudio.Shell;
using Roslyn.Utilities;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.DesignerAttribute
{
[ExportPerLanguageIncrementalAnalyzerProvider(DesignerAttributeIncrementalAnalyzerProvider.Name, LanguageNames.CSharp), Shared]
internal class CSharpDesignerAttributeIncrementalAnalyzerProvider : IPerLanguageIncrementalAnalyzerProvider
{
private readonly IServiceProvider _serviceProvider;
private readonly IForegroundNotificationService _notificationService;
private readonly IEnumerable<Lazy<IAsynchronousOperationListener, FeatureMetadata>> _asyncListeners;

[ImportingConstructor]
public CSharpDesignerAttributeIncrementalAnalyzerProvider(
SVsServiceProvider serviceProvider,
IForegroundNotificationService notificationService,
[ImportMany] IEnumerable<Lazy<IAsynchronousOperationListener, FeatureMetadata>> asyncListeners)
{
_serviceProvider = serviceProvider;
_notificationService = notificationService;
_asyncListeners = asyncListeners;
}

public IIncrementalAnalyzer CreatePerLanguageIncrementalAnalyzer(Workspace workspace, IIncrementalAnalyzerProvider provider)
{
var optionService = workspace.Services.GetService<IOptionService>();
return new DesignerAttributeIncrementalAnalyzer(_serviceProvider, optionService, _notificationService, _asyncListeners);
}

private class DesignerAttributeIncrementalAnalyzer : AbstractDesignerAttributeIncrementalAnalyzer
{
public DesignerAttributeIncrementalAnalyzer(
IServiceProvider serviceProvider,
IOptionService optionService,
IForegroundNotificationService notificationService,
IEnumerable<Lazy<IAsynchronousOperationListener, FeatureMetadata>> asyncListeners) :
base(serviceProvider, optionService, notificationService, asyncListeners)
{
}

protected override IEnumerable<SyntaxNode> GetAllTopLevelTypeDefined(SyntaxNode node)
{
var compilationUnit = node as CompilationUnitSyntax;
if (compilationUnit == null)
{
return SpecializedCollections.EmptyEnumerable<SyntaxNode>();
}

return compilationUnit.Members.SelectMany(GetAllTopLevelTypeDefined);
}

private IEnumerable<SyntaxNode> GetAllTopLevelTypeDefined(MemberDeclarationSyntax member)
{
var namespaceMember = member as NamespaceDeclarationSyntax;
if (namespaceMember != null)
{
return namespaceMember.Members.SelectMany(GetAllTopLevelTypeDefined);
}

var type = member as ClassDeclarationSyntax;
if (type != null)
{
return SpecializedCollections.SingletonEnumerable<SyntaxNode>(type);
}

return SpecializedCollections.EmptyEnumerable<SyntaxNode>();
}

protected override bool ProcessOnlyFirstTypeDefined()
{
return true;
}

protected override bool HasAttributesOrBaseTypeOrIsPartial(SyntaxNode typeNode)
{
var classNode = typeNode as ClassDeclarationSyntax;
if (classNode != null)
{
return classNode.AttributeLists.Count > 0 ||
classNode.BaseList != null ||
classNode.Modifiers.Any(SyntaxKind.PartialKeyword);
}

return false;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Editor.CSharp.EventHookup;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio.Utilities;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.EventHookup
{
/// <summary>
/// Need to trick ShimQuickInfoController into leaving Quick Info Sessions created by
/// EventHookupCommandHandler visible even through buffer changed events. We must set its
/// private _session field via reflection.
/// </summary>
[Export(typeof(IHACK_EventHookupDismissalOnBufferChangePreventerService))]
internal sealed class HACK_EventHookupDismissalOnBufferChangePreventerService : IHACK_EventHookupDismissalOnBufferChangePreventerService
{
public void HACK_EnsureQuickInfoSessionNotDismissedPrematurely(ITextView textView)
{
// Need an IQuickInfoSession with Properties containing an IVsTextTipData
HACK_SetShimQuickInfoSessionWorker(textView, new HACK_QuickInfoSession());
}

public void HACK_OnQuickInfoSessionDismissed(ITextView textView)
{
HACK_SetShimQuickInfoSessionWorker(textView, null);
}

private void HACK_SetShimQuickInfoSessionWorker(ITextView textView, IQuickInfoSession quickInfoSession)
{
var properties = textView.Properties.PropertyList;
var shimController = properties.Single(p => p.Value != null && p.Value.GetType().Name == "ShimQuickInfoController").Value;
var sessionField = shimController.GetType().GetField("_session", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
sessionField.SetValue(shimController, quickInfoSession);
}

/// <summary>
/// The Properties property must contain an IVsTextTipData (which is never used), but no
/// other methods need to be implemented.
/// </summary>
private class HACK_QuickInfoSession : IQuickInfoSession
{
#pragma warning disable 67
public event EventHandler Dismissed;
public event EventHandler Recalculated;
public event EventHandler ApplicableToSpanChanged;
public event EventHandler PresenterChanged;
#pragma warning restore 67

public PropertyCollection Properties
{
get
{
var collection = new PropertyCollection();
collection.AddProperty(typeof(IVsTextTipData), new HACK_VsTextTipData());
return collection;
}
}

public Microsoft.VisualStudio.Text.ITrackingSpan ApplicableToSpan
{
get { throw new NotImplementedException(); }
}

public BulkObservableCollection<object> QuickInfoContent
{
get { throw new NotImplementedException(); }
}

public bool TrackMouse
{
get { throw new NotImplementedException(); }
}

public void Collapse()
{
throw new NotImplementedException();
}

public void Dismiss()
{
throw new NotImplementedException();
}

public Microsoft.VisualStudio.Text.SnapshotPoint? GetTriggerPoint(Microsoft.VisualStudio.Text.ITextSnapshot textSnapshot)
{
throw new NotImplementedException();
}

public Microsoft.VisualStudio.Text.ITrackingPoint GetTriggerPoint(Microsoft.VisualStudio.Text.ITextBuffer textBuffer)
{
throw new NotImplementedException();
}

public bool IsDismissed
{
get { throw new NotImplementedException(); }
}

public bool Match()
{
throw new NotImplementedException();
}

public IIntellisensePresenter Presenter
{
get { throw new NotImplementedException(); }
}

public void Recalculate()
{
throw new NotImplementedException();
}

public void Start()
{
throw new NotImplementedException();
}

public ITextView TextView
{
get { throw new NotImplementedException(); }
}
}

/// <summary>
/// None of the methods need to be implemented.
/// </summary>
private class HACK_VsTextTipData : IVsTextTipData
{
public int GetContextStream(out int pos, out int length)
{
throw new NotImplementedException();
}

public int GetTipFontInfo(int chars, uint[] pdwFontAttr)
{
throw new NotImplementedException();
}

public int GetTipText(string[] pbstrText, out int getFontInfo)
{
throw new NotImplementedException();
}

public void OnDismiss()
{
throw new NotImplementedException();
}

public void UpdateView()
{
throw new NotImplementedException();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Runtime.InteropServices;
using Microsoft.VisualStudio.LanguageServices.Implementation;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService
{
[Guid(Guids.CSharpCodePageEditorFactoryIdString)]
internal class CSharpCodePageEditorFactory : AbstractCodePageEditorFactory
{
public CSharpCodePageEditorFactory(AbstractEditorFactory editorFactory)
: base(editorFactory)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.LanguageServices.Implementation.DebuggerIntelliSense;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Projection;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio.Utilities;
using Roslyn.Utilities;
using TextSpan = Microsoft.VisualStudio.TextManager.Interop.TextSpan;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService
{
internal class CSharpDebuggerIntelliSenseContext : AbstractDebuggerIntelliSenseContext
{
public CSharpDebuggerIntelliSenseContext(
IWpfTextView view,
IVsTextView vsTextView,
IVsTextLines debuggerBuffer,
ITextBuffer contextBuffer,
TextSpan[] currentStatementSpan,
IComponentModel componentModel,
IServiceProvider serviceProvider)
: base(view,
vsTextView,
debuggerBuffer,
contextBuffer,
currentStatementSpan,
componentModel,
serviceProvider,
componentModel.GetService<IContentTypeRegistryService>().GetContentType(ContentTypeNames.CSharpContentType))
{
}

// Test constructor
internal CSharpDebuggerIntelliSenseContext(
IWpfTextView view,
ITextBuffer contextBuffer,
TextSpan[] currentStatementSpan,
IComponentModel componentModel,
bool immediateWindow)
: base(view,
contextBuffer,
currentStatementSpan,
componentModel,
componentModel.GetService<IContentTypeRegistryService>().GetContentType(ContentTypeNames.CSharpContentType),
immediateWindow)
{
}

protected override int GetAdjustedContextPoint(int contextPoint, Document document)
{
// Determine the position in the buffer at which to end the tracking span representing
// the part of the imagininary buffer before the text in the view.
var tree = document.GetCSharpSyntaxTreeAsync(CancellationToken.None).WaitAndGetResult(CancellationToken.None);
var token = tree.FindTokenOnLeftOfPosition(contextPoint, CancellationToken.None);

// Special case to handle class designer because it asks for debugger IntelliSense using
// spans between members.
if (contextPoint > token.Span.End &&
token.IsKindOrHasMatchingText(SyntaxKind.CloseBraceToken) &&
token.Parent.IsKind(SyntaxKind.Block) &&
token.Parent.Parent is MemberDeclarationSyntax)
{
return contextPoint;
}

if (token.IsKindOrHasMatchingText(SyntaxKind.CloseBraceToken) &&
token.Parent.IsKind(SyntaxKind.Block))
{
return token.SpanStart;
}

return token.FullSpan.End;
}

protected override ITrackingSpan GetPreviousStatementBufferAndSpan(int contextPoint, Document document)
{
var previousTrackingSpan = ContextBuffer.CurrentSnapshot.CreateTrackingSpan(Span.FromBounds(0, contextPoint), SpanTrackingMode.EdgeNegative);

// terminate the previous expression/statement
var buffer = ProjectionBufferFactoryService.CreateProjectionBuffer(
projectionEditResolver: null,
sourceSpans: new object[] { previousTrackingSpan, this.StatementTerminator },
options: ProjectionBufferOptions.None,
contentType: this.ContentType);

return buffer.CurrentSnapshot.CreateTrackingSpan(0, buffer.CurrentSnapshot.Length, SpanTrackingMode.EdgeNegative);
}

public override bool CompletionStartsOnQuestionMark
{
get { return false; }
}

protected override string StatementTerminator
{
get { return ";"; }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft. 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.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServices.Implementation;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService
{
[ExcludeFromCodeCoverage]
[Guid(Guids.CSharpEditorFactoryIdString)]
internal class CSharpEditorFactory : AbstractEditorFactory
{
public CSharpEditorFactory(CSharpPackage package)
: base(package)
{
}

protected override string ContentTypeName
{
get { return "CSharp"; }
}

protected override IList<TextChange> GetFormattedTextChanges(VisualStudioWorkspace workspace, string filePath, SourceText text, CancellationToken cancellationToken)
{
var root = SyntaxFactory.ParseSyntaxTree(text, path: filePath, cancellationToken: cancellationToken).GetRoot(cancellationToken);
return Formatter.GetFormattedTextChanges(root, workspace, cancellationToken: cancellationToken);
}
}
}