提交 d20421ec 编写于 作者: L lorcanmooney

Add namespace-name suggestions for C#

上级 7e4f3149
......@@ -590,6 +590,22 @@ void foo()
await VerifyBuilderAsync(markup);
}
[WorkItem(7213, "https://github.com/dotnet/roslyn/issues/7213")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task NamespaceDeclaration_Unqualified()
{
var markup = @"namespace $$";
await VerifyBuilderAsync(markup);
}
[WorkItem(7213, "https://github.com/dotnet/roslyn/issues/7213")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task NamespaceDeclaration_Qualified()
{
var markup = @"namespace A.$$";
await VerifyBuilderAsync(markup);
}
private async Task VerifyNotBuilderAsync(string markup)
{
await VerifyWorkerAsync(markup, isBuilder: false);
......
......@@ -370,20 +370,166 @@ public async Task MethodParamAttribute()
await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"System");
}
[WorkItem(7213, "https://github.com/dotnet/roslyn/issues/7213")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task NamespaceName1()
public async Task NamespaceName_Unqualified_TopLevelNoPeers()
{
await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"namespace $$"), @"String");
await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"namespace $$"), @"System");
var source = @"using System;
namespace $$";
await VerifyItemExistsAsync(source, "System");
await VerifyItemIsAbsentAsync(source, "String");
}
[WorkItem(7213, "https://github.com/dotnet/roslyn/issues/7213")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task NamespaceName_Unqualified_TopLevelWithPeer()
{
var source = @"
namespace A { }
namespace $$";
await VerifyItemExistsAsync(source, "A");
}
[WorkItem(7213, "https://github.com/dotnet/roslyn/issues/7213")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task NamespaceName2()
public async Task NamespaceName_Unqualified_NestedWithNoPeers()
{
await VerifyItemIsAbsentAsync(@"namespace $$", @"String");
await VerifyItemIsAbsentAsync(@"namespace $$", @"System");
var source = @"
namespace A
{
namespace $$
}";
await VerifyNoItemsExistAsync(source);
}
[WorkItem(7213, "https://github.com/dotnet/roslyn/issues/7213")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task NamespaceName_Unqualified_NestedWithPeer()
{
var source = @"
namespace A
{
namespace B { }
namespace $$
}";
await VerifyItemIsAbsentAsync(source, "A");
await VerifyItemExistsAsync(source, "B");
}
[WorkItem(7213, "https://github.com/dotnet/roslyn/issues/7213")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task NamespaceName_Unqualified_ExcludesCurrentDeclaration()
{
var source = @"namespace N$$S";
await VerifyItemIsAbsentAsync(source, "NS");
}
// [WorkItem(7213, "https://github.com/dotnet/roslyn/issues/7213")]
// [Fact, Trait(Traits.Feature, Traits.Features.Completion)]
// public async Task NamespaceName_Unqualified_IncompleteDeclaration()
// {
// var source = @"
//namespace A.B.C0 { }
//
//namespace A
//{
// namespace B
// {
// namespace C$$
//
// namespace C1.X { }
// }
//
// namespace B.C2.Y { }
//}
//
//namespace A.B.C3.Z { }";
//
// await VerifyItemIsAbsentAsync(source, "C0");
// await VerifyItemExistsAsync(source, "C1");
// await VerifyItemExistsAsync(source, "C2");
// await VerifyItemExistsAsync(source, "C3");
// await VerifyItemIsAbsentAsync(source, "X");
// await VerifyItemIsAbsentAsync(source, "Y");
// await VerifyItemIsAbsentAsync(source, "Z");
// }
[WorkItem(7213, "https://github.com/dotnet/roslyn/issues/7213")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task NamespaceName_Qualified_NoPeers()
{
var source = @"namespace A.$$";
await VerifyNoItemsExistAsync(source);
}
[WorkItem(7213, "https://github.com/dotnet/roslyn/issues/7213")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task NamespaceName_Qualified_TopLevelWithPeer()
{
var source = @"
namespace A.B { }
namespace A.$$";
await VerifyItemExistsAsync(source, "B");
}
[WorkItem(7213, "https://github.com/dotnet/roslyn/issues/7213")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task NamespaceName_Qualified_NestedWithPeer()
{
var source = @"
namespace A
{
namespace B.C { }
namespace B.$$
}";
await VerifyItemIsAbsentAsync(source, "A");
await VerifyItemIsAbsentAsync(source, "B");
await VerifyItemExistsAsync(source, "C");
}
// [WorkItem(7213, "https://github.com/dotnet/roslyn/issues/7213")]
// [Fact, Trait(Traits.Feature, Traits.Features.Completion)]
// public async Task NamespaceName_Qualified_IncompleteDeclaration()
// {
// var source = @"
//namespace A.B.C.D0 { }
//
//namespace A
//{
// namespace B
// {
// namespace C.D$$
//
// namespace C.D1.X { }
// }
//
// namespace B.C.D2.Y { }
//}
//
//namespace A.B.C.D3.Z { }";
//
// await VerifyItemIsAbsentAsync(source, "D0");
// await VerifyItemExistsAsync(source, "D1");
// await VerifyItemExistsAsync(source, "D2");
// await VerifyItemExistsAsync(source, "D3");
// await VerifyItemIsAbsentAsync(source, "X");
// await VerifyItemIsAbsentAsync(source, "Y");
// await VerifyItemIsAbsentAsync(source, "Z");
// }
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task UnderNamespace()
{
......
......@@ -110,7 +110,7 @@ private async Task<IEnumerable<CompletionItem>> GetSnippetsForDocumentAsync(Docu
return SpecializedCollections.EmptyEnumerable<CompletionItem>();
}
return await GetSnippetCompletionItemsAsync(workspace, semanticModel, position, itemSpan, isPreProcessorContext: true, cancellationToken: cancellationToken).ConfigureAwait(false);
return await GetSnippetCompletionItemsAsync(workspace, semanticModel, itemSpan, isPreProcessorContext: true, cancellationToken: cancellationToken).ConfigureAwait(false);
}
if (semanticFacts.IsGlobalStatementContext(semanticModel, position, cancellationToken) ||
......@@ -119,16 +119,17 @@ private async Task<IEnumerable<CompletionItem>> GetSnippetsForDocumentAsync(Docu
semanticFacts.IsTypeContext(semanticModel, position, cancellationToken) ||
semanticFacts.IsTypeDeclarationContext(semanticModel, position, cancellationToken) ||
semanticFacts.IsNamespaceContext(semanticModel, position, cancellationToken) ||
semanticFacts.IsNamespaceDeclarationNameContext(semanticModel, position, cancellationToken) ||
semanticFacts.IsMemberDeclarationContext(semanticModel, position, cancellationToken) ||
semanticFacts.IsLabelContext(semanticModel, position, cancellationToken))
{
return await GetSnippetCompletionItemsAsync(workspace, semanticModel, position, itemSpan, isPreProcessorContext: false, cancellationToken: cancellationToken).ConfigureAwait(false);
return await GetSnippetCompletionItemsAsync(workspace, semanticModel, itemSpan, isPreProcessorContext: false, cancellationToken: cancellationToken).ConfigureAwait(false);
}
return SpecializedCollections.EmptyEnumerable<CompletionItem>();
}
private async Task<IEnumerable<CompletionItem>> GetSnippetCompletionItemsAsync(Workspace workspace, SemanticModel semanticModel, int position, TextSpan itemSpan, bool isPreProcessorContext, CancellationToken cancellationToken)
private async Task<IEnumerable<CompletionItem>> GetSnippetCompletionItemsAsync(Workspace workspace, SemanticModel semanticModel, TextSpan itemSpan, bool isPreProcessorContext, CancellationToken cancellationToken)
{
var service = _snippetInfoService ?? workspace.Services.GetLanguageServices(semanticModel.Language).GetService<ISnippetInfoService>();
if (service == null)
......
......@@ -59,6 +59,10 @@ protected override async Task<CompletionItem> GetSuggestionModeItemAsync(Documen
{
return CreateSuggestionModeItem(CSharpFeaturesResources.RangeVariable, itemSpan, CSharpFeaturesResources.AutoselectDisabledDueToPotentialRangeVariableDecl);
}
else if (tree.IsNamespaceDeclarationNameContext(position, cancellationToken))
{
return CreateEmptySuggestionModeItem(itemSpan);
}
}
return null;
......
......@@ -114,13 +114,13 @@ private static bool BaseTypeDeclarationContainsPosition(BaseTypeDeclarationSynta
public static bool IsNamespaceDeclarationNameContext(this SyntaxTree syntaxTree, int position, CancellationToken cancellationToken)
{
var token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken);
var namespaceName = token.GetAncestor<NamespaceDeclarationSyntax>();
if (namespaceName == null)
var namespaceDeclaration = token.GetAncestor<NamespaceDeclarationSyntax>();
if (namespaceDeclaration == null)
{
return false;
}
return namespaceName.Name.Span.IntersectsWith(position);
return namespaceDeclaration.Name.Span.IntersectsWith(position) || token == namespaceDeclaration.NamespaceKeyword;
}
public static bool IsRightOfDotOrArrowOrColonColon(this SyntaxTree syntaxTree, int position, CancellationToken cancellationToken)
......
......@@ -60,6 +60,11 @@ public bool IsNamespaceContext(SemanticModel semanticModel, int position, Cancel
return semanticModel.SyntaxTree.IsNamespaceContext(position, cancellationToken, semanticModel);
}
public bool IsNamespaceDeclarationNameContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken)
{
return semanticModel.SyntaxTree.IsNamespaceDeclarationNameContext(position, cancellationToken);
}
public bool IsTypeDeclarationContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken)
{
return semanticModel.SyntaxTree.IsTypeDeclarationContext(
......
......@@ -7,6 +7,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.CSharp.Symbols;
......@@ -48,13 +49,6 @@ internal class CSharpRecommendationService : AbstractRecommendationService
return SpecializedCollections.EmptyEnumerable<ISymbol>();
}
// TODO: don't show completion set at namespace name part to match Dev10 behavior
// if we want to provide new feature that shows all existing namespaces later, remove this
if (context.IsNamespaceDeclarationNameContext)
{
return SpecializedCollections.EmptyEnumerable<ISymbol>();
}
if (context.IsRightOfNameSeparator)
{
return GetSymbolsOffOfContainer(context, cancellationToken);
......@@ -100,6 +94,10 @@ internal class CSharpRecommendationService : AbstractRecommendationService
{
return SpecializedCollections.SingletonEnumerable(context.SemanticModel.GetDeclaredSymbol(context.ContainingTypeOrEnumDeclaration, cancellationToken));
}
else if (context.IsNamespaceDeclarationNameContext)
{
return GetSymbolsForNamespaceDeclarationNameContext(context, cancellationToken);
}
return SpecializedCollections.EmptyEnumerable<ISymbol>();
}
......@@ -238,6 +236,22 @@ internal class CSharpRecommendationService : AbstractRecommendationService
return symbols;
}
private static IEnumerable<ISymbol> GetSymbolsForNamespaceDeclarationNameContext(
CSharpSyntaxContext context,
CancellationToken cancellationToken)
{
var namespaceDeclaration = context.TargetToken.GetAncestor<NamespaceDeclarationSyntax>();
var declaredNamespaceSymbol = context.SemanticModel.GetDeclaredSymbol(namespaceDeclaration);
var containingNamespaceSymbol = context.SemanticModel.Compilation.GetCompilationNamespace(declaredNamespaceSymbol.ContainingNamespace);
var symbols = context.SemanticModel
.LookupNamespacesAndTypes(context.LeftToken.SpanStart, containingNamespaceSymbol)
.Where(symbol => IsNonIntersectingNamespace(symbol, context));
return symbols;
}
private static IEnumerable<ISymbol> GetSymbolsForExpressionOrStatementContext(
CSharpSyntaxContext context,
bool filterOutOfScopeLocals,
......@@ -321,6 +335,11 @@ internal class CSharpRecommendationService : AbstractRecommendationService
position: name.SpanStart,
container: symbol);
if (context.IsNamespaceDeclarationNameContext)
{
return symbols.Where(s => IsNonIntersectingNamespace(s, context));
}
// Filter the types when in a using directive, but not an alias.
//
// Cases:
......@@ -350,6 +369,11 @@ internal class CSharpRecommendationService : AbstractRecommendationService
return SpecializedCollections.EmptyEnumerable<ISymbol>();
}
private static bool IsNonIntersectingNamespace(ISymbol symbol, CSharpSyntaxContext context)
{
return symbol.IsNamespace() && symbol.Locations.Any(location => !context.IntersectsWith(location));
}
private static IEnumerable<ISymbol> GetSymbolsOffOfExpression(
CSharpSyntaxContext context,
ExpressionSyntax originalExpression,
......
......@@ -44,6 +44,7 @@ internal interface ISemanticFactsService : ILanguageService
bool IsStatementContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken);
bool IsTypeContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken);
bool IsNamespaceContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken);
bool IsNamespaceDeclarationNameContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken);
bool IsTypeDeclarationContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken);
bool IsMemberDeclarationContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken);
bool IsPreProcessorDirectiveContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken);
......
// 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.Threading;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageServices;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery
......@@ -106,5 +106,15 @@ public ISet<INamedTypeSymbol> GetOuterTypes(CancellationToken cancellationToken)
{
return this.Workspace.Services.GetService<TService>();
}
public bool IntersectsWith(Location location)
{
if (location == null)
{
throw new ArgumentNullException(nameof(location));
}
return location.SourceTree == SyntaxTree && location.SourceSpan.IntersectsWith(Position);
}
}
}
......@@ -243,5 +243,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Public Function IsNameOfContext(semanticModel As SemanticModel, position As Integer, cancellationToken As CancellationToken) As Boolean Implements ISemanticFactsService.IsNameOfContext
Return semanticModel.SyntaxTree.IsNameOfContext(position, cancellationToken)
End Function
Public Function IsNamespaceDeclarationNameContext(semanticModel As SemanticModel, position As Integer, cancellationToken As CancellationToken) As Boolean Implements ISemanticFactsService.IsNamespaceDeclarationNameContext
'
' TODO: part of https://github.com/dotnet/roslyn/issues/7213
'
Return False
End Function
End Class
End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册