| 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>(); | ||
| } | ||
| } | ||
| } |
| 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; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
| 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); | ||
| } | ||
| } | ||
| } |
| 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); | ||
| } | ||
| } | ||
| } |
| 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; | ||
| } | ||
| } | ||
| } | ||
| } |
| 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; } | ||
| } | ||
| } |
| 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 | ||
| } | ||
| } |
| 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); | ||
| } | ||
| } | ||
| } |
| 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 | ||
| } | ||
| } |
| 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; | ||
| } | ||
| } | ||
| } |
| 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; | ||
| } | ||
| } | ||
| } |
| 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; | ||
| } | ||
| } | ||
| } |
| 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); | ||
| } | ||
| } | ||
| } |
| 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); | ||
| } | ||
| } | ||
| } |
| 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); | ||
| } | ||
| } | ||
| } |