提交 4fd3e8e7 编写于 作者: G Gen Lu

Add VB provider

上级 ade62630
......@@ -583,7 +583,7 @@ class Bat
$$
}
}";
var markup = CreateMarkupForProjecWithVBProjectReference(file2, file1, sourceLanguage: LanguageNames.CSharp, rootnamespace: "Foo");
var markup = CreateMarkupForProjecWithVBProjectReference(file2, file1, sourceLanguage: LanguageNames.CSharp, rootNamespace: "Foo");
await VerifyTypeImportItemExistsAsync(markup, "Barr", glyph: (int)Glyph.ClassPublic, inlineDescription: "Foo.Bar");
}
......@@ -609,7 +609,7 @@ class Bat
$$
}
}";
var markup = CreateMarkupForProjecWithVBProjectReference(file2, file1, sourceLanguage: LanguageNames.CSharp, rootnamespace: "");
var markup = CreateMarkupForProjecWithVBProjectReference(file2, file1, sourceLanguage: LanguageNames.CSharp, rootNamespace: "");
await VerifyTypeImportItemExistsAsync(markup, "Bar", glyph: (int)Glyph.ClassPublic, inlineDescription: "Na");
await VerifyTypeImportItemExistsAsync(markup, "Foo", glyph: (int)Glyph.ClassPublic, inlineDescription: "Na");
await VerifyTypeImportItemIsAbsentAsync(markup, "Bar", inlineDescription: "na");
......@@ -639,7 +639,7 @@ class Bat
$$
}
}";
var markup = CreateMarkupForProjecWithVBProjectReference(file2, file1, sourceLanguage: LanguageNames.CSharp, rootnamespace: "");
var markup = CreateMarkupForProjecWithVBProjectReference(file2, file1, sourceLanguage: LanguageNames.CSharp, rootNamespace: "");
await VerifyTypeImportItemIsAbsentAsync(markup, "Bar", inlineDescription: "Na");
await VerifyTypeImportItemIsAbsentAsync(markup, "Foo", inlineDescription: "Na");
await VerifyTypeImportItemIsAbsentAsync(markup, "Bar", inlineDescription: "na");
......@@ -662,7 +662,7 @@ class Bat
$$
}
}";
var markup = CreateMarkupForProjecWithVBProjectReference(file2, file1, sourceLanguage: LanguageNames.CSharp, rootnamespace: "Foo");
var markup = CreateMarkupForProjecWithVBProjectReference(file2, file1, sourceLanguage: LanguageNames.CSharp, rootNamespace: "Foo");
await VerifyTypeImportItemIsAbsentAsync(markup, "Barr", inlineDescription: "Foo.Bar");
}
......@@ -683,7 +683,7 @@ class Bat
$$
}
}";
var markup = CreateMarkupForProjecWithVBProjectReference(file2, file1, sourceLanguage: LanguageNames.CSharp, rootnamespace: "Foo");
var markup = CreateMarkupForProjecWithVBProjectReference(file2, file1, sourceLanguage: LanguageNames.CSharp, rootNamespace: "Foo");
await VerifyTypeImportItemIsAbsentAsync(markup, "Barr", inlineDescription: "Foo.Bar");
}
......@@ -790,7 +790,7 @@ class Bat
Barr$$
}
}";
var markup = CreateMarkupForProjecWithVBProjectReference(file2, file1, sourceLanguage: LanguageNames.CSharp, rootnamespace: "Foo");
var markup = CreateMarkupForProjecWithVBProjectReference(file2, file1, sourceLanguage: LanguageNames.CSharp, rootNamespace: "Foo");
await VerifyCustomCommitProviderAsync(markup, "Barr", expectedCodeAfterCommit, sourceCodeKind: kind);
}
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis.Completion
Imports Microsoft.CodeAnalysis.Completion.Providers
Imports Microsoft.CodeAnalysis.Editor.UnitTests
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Experiments
Imports Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
Imports Microsoft.VisualStudio.Composition
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.CompletionProviders
<UseExportProvider>
Public Class ExtensionMethodImportCompletionProviderTests
Inherits AbstractVisualBasicCompletionProviderTests
Public Sub New(workspaceFixture As VisualBasicTestWorkspaceFixture)
MyBase.New(workspaceFixture)
End Sub
Private Property ShowImportCompletionItemsOptionValue As Boolean = True
' -1 would disable timebox, whereas 0 means always timeout.
Private Property TimeoutInMilliseconds As Integer = -1
Protected Overrides Sub SetWorkspaceOptions(workspace As TestWorkspace)
workspace.Options = workspace.Options _
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.VisualBasic, ShowImportCompletionItemsOptionValue) _
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, TimeoutInMilliseconds)
End Sub
Protected Overrides Function GetExportProvider() As ExportProvider
Return ExportProviderCache.GetOrCreateExportProviderFactory(TestExportProvider.EntireAssemblyCatalogWithCSharpAndVisualBasic.WithPart(GetType(TestExperimentationService))).CreateExportProvider()
End Function
Friend Overrides Function CreateCompletionProvider() As CompletionProvider
Return New ExtensionMethodImportCompletionProvider()
End Function
<Fact, Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function Test() As Task
' attribute suffix isn't capitalized
Dim file1 = <Text><![CDATA[
Imports System.Runtime.CompilerServices
Namespace Foo
Module ExtensionModule
<System.Runtime.CompilerServices.Extension()>
Public Sub ExtensionMethod1(aString As String)
Console.WriteLine(aString)
End Sub
<Extension>
Public Sub ExtensionMethod2(aString As String)
Console.WriteLine(aString)
End Sub
<ExtensionAttribute>
Public Sub ExtensionMethod3(aString As String)
Console.WriteLine(aString)
End Sub
<Extension()>
Public Sub ExtensionMethod4(aString As String)
Console.WriteLine(aString)
End Sub
<System.Runtime.CompilerServices.ExtensionAttribute>
Public Sub ExtensionMethod5(aString As String)
Console.WriteLine(aString)
End Sub
Public Sub ExtensionMethod6(aString As String)
Console.WriteLine(aString)
End Sub
End Module
End Namespace]]></Text>.Value
Dim file2 = <Text><![CDATA[
Public Class Bar
Sub Main()
dim x = ""
x.$$
End Sub
End Class]]></Text>.Value
Dim markup = CreateMarkupForSingleProject(file2, file1, LanguageNames.VisualBasic)
Await VerifyItemExistsAsync(markup, "ExtensionMethod1", glyph:=Glyph.ExtensionMethodPublic, inlineDescription:="Foo")
Await VerifyItemExistsAsync(markup, "ExtensionMethod2", glyph:=Glyph.ExtensionMethodPublic, inlineDescription:="Foo")
Await VerifyItemExistsAsync(markup, "ExtensionMethod3", glyph:=Glyph.ExtensionMethodPublic, inlineDescription:="Foo")
Await VerifyItemExistsAsync(markup, "ExtensionMethod4", glyph:=Glyph.ExtensionMethodPublic, inlineDescription:="Foo")
Await VerifyItemExistsAsync(markup, "ExtensionMethod5", glyph:=Glyph.ExtensionMethodPublic, inlineDescription:="Foo")
Await VerifyItemExistsAsync(markup, "ExtensionMethod6", glyph:=Glyph.ExtensionMethodPublic, inlineDescription:="Foo")
End Function
End Class
End Namespace
......@@ -162,45 +162,57 @@ protected override bool ShouldProvideCompletion(Document document, SyntaxContext
var solution = currentProject.Solution;
var graph = currentProject.Solution.GetProjectDependencyGraph();
var dependencies = graph.GetProjectsThatThisProjectTransitivelyDependsOn(currentProject.Id);
#nullable restore
var relevantProjects = dependencies.Select(solution.GetProject)
.Where(p => p.SupportsCompilation)
.Concat(currentProject);
var results = new MultiDictionary<string, string>();
using var syntaxDisposer = ArrayBuilder<SyntaxTreeIndex>.GetInstance(out var syntaxTreeIndexBuilder);
using var symbolDisposer = ArrayBuilder<SymbolTreeInfo>.GetInstance(out var symbolTreeInfoBuilder);
var peReferences = PooledHashSet<PortableExecutableReference>.GetInstance();
// Find matching extension methods from source.
foreach (var project in relevantProjects)
{
// Transitively get all the PE references
peReferences.AddRange(project.MetadataReferences.OfType<PortableExecutableReference>());
foreach (var document in project.Documents)
{
#nullable enable
var info = await document.GetSyntaxTreeIndexAsync(cancellationToken).ConfigureAwait(false);
if (!info.ContainsExtensionMethod)
if (info.ContainsExtensionMethod)
{
continue;
syntaxTreeIndexBuilder.Add(info);
}
}
}
#nullable enable
// Add simple extension methods with matching target type name
foreach (var targetTypeName in targetTypeNames)
{
if (!info.SimpleExtensionMethodInfo.TryGetValue(targetTypeName, out var methodInfoIndices))
{
continue;
}
foreach (var peReference in peReferences)
{
var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
solution, peReference, loadOnly: true, cancellationToken).ConfigureAwait(false);
foreach (var index in methodInfoIndices)
{
if (info.TryGetDeclaredSymbolInfo(index, out var methodInfo))
{
results.Add(methodInfo.FullyQualifiedContainerName, methodInfo.Name);
}
}
if (info.ContainsExtensionMethod)
{
symbolTreeInfoBuilder.Add(info);
}
}
peReferences.Free();
var results = new MultiDictionary<string, string>();
// Find matching extension methods from source.
foreach (var info in syntaxTreeIndexBuilder)
{
// Add simple extension methods with matching target type name
foreach (var targetTypeName in targetTypeNames)
{
if (!info.SimpleExtensionMethodInfo.TryGetValue(targetTypeName, out var methodInfoIndices))
{
continue;
}
// Add all complex extension methods, we will need to completely rely on symbols to match them.
foreach (var index in info.ComplexExtensionMethodInfo)
foreach (var index in methodInfoIndices)
{
if (info.TryGetDeclaredSymbolInfo(index, out var methodInfo))
{
......@@ -208,19 +220,20 @@ protected override bool ShouldProvideCompletion(Document document, SyntaxContext
}
}
}
}
// Find matching extension methods from metadata
foreach (var peReference in currentProject.MetadataReferences.OfType<PortableExecutableReference>())
{
var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
solution, peReference, loadOnly: false, cancellationToken).ConfigureAwait(false);
if (!info.ContainsExtensionMethod)
// Add all complex extension methods, we will need to completely rely on symbols to match them.
foreach (var index in info.ComplexExtensionMethodInfo)
{
continue;
if (info.TryGetDeclaredSymbolInfo(index, out var methodInfo))
{
results.Add(methodInfo.FullyQualifiedContainerName, methodInfo.Name);
}
}
}
// Find matching extension methods from metadata
foreach (var info in symbolTreeInfoBuilder)
{
foreach (var targetTypeName in targetTypeNames)
{
foreach (var methodInfo in info.GetMatchingExtensionMethodInfo(targetTypeName))
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Collections.Immutable
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Completion.Providers
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
Friend NotInheritable Class ExtensionMethodImportCompletionProvider
Inherits AbstractExtensionMethodImportCompletionProvider
Friend Overrides Function IsInsertionTrigger(text As SourceText, characterPosition As Integer, options As OptionSet) As Boolean
Return CompletionUtilities.IsDefaultTriggerCharacterOrParen(text, characterPosition, options)
End Function
Protected Overrides Function CreateContextAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of SyntaxContext)
Return ImportCompletionProviderHelper.CreateContextAsync(document, position, cancellationToken)
End Function
Protected Overrides Function GetImportedNamespaces(location As SyntaxNode, semanticModel As SemanticModel, cancellationToken As CancellationToken) As ImmutableArray(Of String)
Return ImportCompletionProviderHelper.GetImportedNamespaces(location, semanticModel, cancellationToken)
End Function
Protected Overrides Function IsInImportsDirectiveAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of Boolean)
Return ImportCompletionProviderHelper.IsInImportsDirectiveAsync(document, position, cancellationToken)
End Function
Protected Overrides Function TryGetReceiverTypeSymbol(syntaxContext As SyntaxContext, ByRef receiverTypeSymbol As ITypeSymbol) As Boolean
If syntaxContext.TargetToken.Parent.Kind = SyntaxKind.SimpleMemberAccessExpression Then
Dim memberAccessNode = CType(syntaxContext.TargetToken.Parent, MemberAccessExpressionSyntax)
Dim symbol = syntaxContext.SemanticModel.GetSymbolInfo(memberAccessNode.Expression).Symbol
If symbol Is Nothing Or symbol.Kind <> SymbolKind.NamedType And symbol.Kind <> SymbolKind.TypeParameter Then
receiverTypeSymbol = syntaxContext.SemanticModel.GetTypeInfo(memberAccessNode.Expression).Type
Return receiverTypeSymbol IsNot Nothing
End If
End If
receiverTypeSymbol = Nothing
Return False
End Function
End Class
End Namespace
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Collections.Immutable
Imports System.Threading
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery
Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
Friend NotInheritable Class ImportCompletionProviderHelper
Public Shared Function GetImportedNamespaces(location As SyntaxNode, semanticModel As SemanticModel, cancellationToken As CancellationToken) As ImmutableArray(Of String)
Dim builder = ArrayBuilder(Of String).GetInstance()
' Get namespaces from import directives
Dim importsInScope = semanticModel.GetImportNamespacesInScope(location)
For Each import As INamespaceSymbol In importsInScope
builder.Add(import.ToDisplayString(SymbolDisplayFormats.NameFormat))
Next
' Get global imports from compilation option
Dim vbOptions = DirectCast(semanticModel.Compilation.Options, VisualBasicCompilationOptions)
For Each globalImport As GlobalImport In vbOptions.GlobalImports
builder.Add(globalImport.Name)
Next
Return builder.ToImmutableAndFree()
End Function
Public Shared Async Function CreateContextAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of SyntaxContext)
' Need regular semantic model because we will use it to get imported namespace symbols. Otherwise we will try to
' reach outside of the span And ended up with "node not within syntax tree" error from the speculative model.
Dim semanticModel = Await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(False)
Return Await VisualBasicSyntaxContext.CreateContextAsync(document.Project.Solution.Workspace, semanticModel, position, cancellationToken).ConfigureAwait(False)
End Function
Public Shared Async Function IsInImportsDirectiveAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of Boolean)
Dim syntaxTree = Await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(False)
Dim leftToken = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken, includeDirectives:=True, includeDocumentationComments:=True)
Return leftToken.GetAncestor(Of ImportsStatementSyntax)() IsNot Nothing
End Function
End Class
End Namespace
......@@ -4,10 +4,8 @@ Imports System.Collections.Immutable
Imports System.Threading
Imports Microsoft.CodeAnalysis.Completion.Providers
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
......@@ -19,35 +17,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
Return CompletionUtilities.IsDefaultTriggerCharacterOrParen(text, characterPosition, options)
End Function
Protected Overrides Async Function CreateContextAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of SyntaxContext)
' Need regular semantic model because we will use it to get imported namespace symbols. Otherwise we will try to
' reach outside of the span And ended up with "node not within syntax tree" error from the speculative model.
Dim semanticModel = Await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(False)
Return Await VisualBasicSyntaxContext.CreateContextAsync(document.Project.Solution.Workspace, semanticModel, position, cancellationToken).ConfigureAwait(False)
Protected Overrides Function CreateContextAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of SyntaxContext)
Return ImportCompletionProviderHelper.CreateContextAsync(document, position, cancellationToken)
End Function
Protected Overrides Function GetImportedNamespaces(location As SyntaxNode, semanticModel As SemanticModel, cancellationToken As CancellationToken) As ImmutableArray(Of String)
Dim builder = ArrayBuilder(Of String).GetInstance()
' Get namespaces from import directives
Dim importsInScope = semanticModel.GetImportNamespacesInScope(location)
For Each import As INamespaceSymbol In importsInScope
builder.Add(import.ToDisplayString(SymbolDisplayFormats.NameFormat))
Next
' Get global imports from compilation option
Dim vbOptions = DirectCast(semanticModel.Compilation.Options, VisualBasicCompilationOptions)
For Each globalImport As GlobalImport In vbOptions.GlobalImports
builder.Add(globalImport.Name)
Next
Return builder.ToImmutableAndFree()
Return ImportCompletionProviderHelper.GetImportedNamespaces(location, semanticModel, cancellationToken)
End Function
Protected Overrides Async Function IsInImportsDirectiveAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of Boolean)
Dim syntaxTree = Await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(False)
Dim leftToken = SyntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken, includeDirectives:=True, includeDocumentationComments:=True)
Return leftToken.GetAncestor(Of ImportsStatementSyntax)() IsNot Nothing
Protected Overrides Function IsInImportsDirectiveAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of Boolean)
Return ImportCompletionProviderHelper.IsInImportsDirectiveAsync(document, position, cancellationToken)
End Function
End Class
End Namespace
......@@ -63,6 +63,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion
End If
completionProviders = completionProviders.Add(New TypeImportCompletionProvider())
completionProviders = completionProviders.Add(New ExtensionMethodImportCompletionProvider())
_completionProviders = completionProviders
End Sub
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册