diff --git a/Src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTokenExtensions.cs b/Src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTokenExtensions.cs index 8a0a37c25ea72ad9f03c60b15e78ce62701950bf..53b3a925fd141a892caeb2f059875066f757c2b5 100644 --- a/Src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTokenExtensions.cs +++ b/Src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTokenExtensions.cs @@ -2,11 +2,9 @@ using System.Linq; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery @@ -20,6 +18,36 @@ public static bool IsUsingOrExternKeyword(this SyntaxToken token) token.CSharpKind() == SyntaxKind.ExternKeyword; } + public static bool IsUsingKeywordInUsingDirective(this SyntaxToken token) + { + if (token.IsKind(SyntaxKind.UsingKeyword)) + { + var usingDirective = token.GetAncestor(); + if (usingDirective != null && + usingDirective.UsingKeyword == token) + { + return true; + } + } + + return false; + } + + public static bool IsStaticKeywordInUsingDirective(this SyntaxToken token) + { + if (token.IsKind(SyntaxKind.StaticKeyword)) + { + var usingDirective = token.GetAncestor(); + if (usingDirective != null && + usingDirective.StaticKeyword == token) + { + return true; + } + } + + return false; + } + public static bool IsBeginningOfStatementContext(this SyntaxToken token) { // cases: diff --git a/Src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTreeExtensions.cs b/Src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTreeExtensions.cs index 5a7090e3f477cc6a17e4d06e6072cc18db41fed0..cd70d20499fd1833aece38f2d222a8411d91ca2f 100644 --- a/Src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTreeExtensions.cs +++ b/Src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTreeExtensions.cs @@ -506,6 +506,12 @@ public static bool IsAttributeNameContext(this SyntaxTree syntaxTree, int positi return true; } + // using static | is never a type declaration context + if (token.IsStaticKeywordInUsingDirective()) + { + return false; + } + var modifierTokens = contextOpt != null ? contextOpt.PrecedingModifiers : syntaxTree.GetPrecedingModifiers(position, leftToken, cancellationToken); @@ -577,6 +583,12 @@ public static bool IsAttributeNameContext(this SyntaxTree syntaxTree, int positi } } + // using static | + if (token.IsStaticKeywordInUsingDirective()) + { + return true; + } + // if it is not using directive location, most of places where // type can appear, namespace can appear as well return syntaxTree.IsTypeContext(position, cancellationToken, semanticModelOpt); @@ -625,6 +637,7 @@ public static bool IsDefinitelyNotTypeContext(this SyntaxTree syntaxTree, int po syntaxTree.IsStatementContext(position, tokenOnLeftOfPosition, cancellationToken) || syntaxTree.IsTypeParameterConstraintContext(position, tokenOnLeftOfPosition, cancellationToken) || syntaxTree.IsUsingAliasContext(position, cancellationToken) || + syntaxTree.IsUsingStaticContext(position, cancellationToken) || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || syntaxTree.IsMemberDeclarationContext( position, @@ -671,6 +684,16 @@ public static bool IsUsingAliasContext(this SyntaxTree syntaxTree, int position, return false; } + public static bool IsUsingStaticContext(this SyntaxTree syntaxTree, int position, CancellationToken cancellationToken) + { + // using static | + + var token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken); + token = token.GetPreviousTokenIfTouchingWord(position); + + return token.IsStaticKeywordInUsingDirective(); + } + public static bool IsTypeArgumentOfConstraintClause( this SyntaxTree syntaxTree, int position, CancellationToken cancellationToken) { diff --git a/Src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationService.cs b/Src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationService.cs index 0a397823ad7c232cfc7a51a9781f8d3f07d0f4ae..b7fe511234063f0276800e759e9167d0c3543711 100644 --- a/Src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationService.cs +++ b/Src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationService.cs @@ -220,6 +220,16 @@ internal class CSharpRecommendationService : AbstractRecommendationService { var symbols = context.SemanticModel.LookupNamespacesAndTypes(context.LeftToken.SpanStart); + if (context.TargetToken.IsUsingKeywordInUsingDirective()) + { + return symbols.Where(s => s.IsNamespace()); + } + + if (context.TargetToken.IsStaticKeywordInUsingDirective()) + { + return symbols.Where(s => !s.IsDelegateType() && !s.IsInterfaceType()); + } + return symbols; } @@ -248,6 +258,9 @@ internal class CSharpRecommendationService : AbstractRecommendationService ? context.SemanticModel.LookupStaticMembers(context.LeftToken.SpanStart) : context.SemanticModel.LookupSymbols(context.LeftToken.SpanStart); + // Filter out any extension methods that might be imported by a using static directive. + symbols = symbols.Where(symbol => !symbol.IsExtensionMethod()); + // The symbols may include local variables that are declared later in the method and // should not be included in the completion list, so remove those. Filter them away, // unless we're in the debugger, where we show all locals in scope. @@ -301,15 +314,16 @@ internal class CSharpRecommendationService : AbstractRecommendationService // Filter the types when in a using directive, but not an alias. // // Cases: - // using | -- Show namespaces (and static types in C# v6) + // using | -- Show namespaces + // using A.| -- Show namespaces + // using static | -- Show namespace and types // using A = B.| -- Show namespace and types var usingDirective = name.GetAncestorOrThis(); if (usingDirective != null && usingDirective.Alias == null) { - // Do we also have inclusion of static types? - if (((CSharpParseOptions)context.SyntaxTree.Options).LanguageVersion >= LanguageVersion.CSharp6) + if (usingDirective.StaticKeyword.IsKind(SyntaxKind.StaticKeyword)) { - symbols = symbols.Where(s => s.IsNamespace() || s.IsStaticType()).ToList(); + return symbols.Where(s => !s.IsDelegateType() && !s.IsInterfaceType()); } else { diff --git a/Src/Workspaces/CSharp/Portable/Utilities/TokenComparer.cs b/Src/Workspaces/CSharp/Portable/Utilities/TokenComparer.cs index 73a46555ac4e9b73426cc14dcbffd6f8215d670f..b7b343a4593b31ed6c18c7079217d26876ae3ff8 100644 --- a/Src/Workspaces/CSharp/Portable/Utilities/TokenComparer.cs +++ b/Src/Workspaces/CSharp/Portable/Utilities/TokenComparer.cs @@ -1,12 +1,8 @@ // Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Collections.Generic; using System.Globalization; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.CSharp.Extensions; namespace Microsoft.CodeAnalysis.CSharp.Utilities { @@ -32,8 +28,8 @@ private static bool IsSystem(string s) public int Compare(SyntaxToken x, SyntaxToken y) { if (specialCaseSystem && - x.GetPreviousToken(includeSkipped: true).CSharpKind() == SyntaxKind.UsingKeyword && - y.GetPreviousToken(includeSkipped: true).CSharpKind() == SyntaxKind.UsingKeyword) + x.GetPreviousToken(includeSkipped: true).IsKind(SyntaxKind.UsingKeyword, SyntaxKind.StaticKeyword) && + y.GetPreviousToken(includeSkipped: true).IsKind(SyntaxKind.UsingKeyword, SyntaxKind.StaticKeyword)) { var token1IsSystem = IsSystem(x.ValueText); var token2IsSystem = IsSystem(y.ValueText); diff --git a/Src/Workspaces/CSharp/Portable/Utilities/UsingsAndExternAliasesDirectiveComparer.cs b/Src/Workspaces/CSharp/Portable/Utilities/UsingsAndExternAliasesDirectiveComparer.cs index 1199559cf0358cfea51b01595387efdfd51789e0..92d31c6a0da727235082f85658b4737c58231e06 100644 --- a/Src/Workspaces/CSharp/Portable/Utilities/UsingsAndExternAliasesDirectiveComparer.cs +++ b/Src/Workspaces/CSharp/Portable/Utilities/UsingsAndExternAliasesDirectiveComparer.cs @@ -1,11 +1,7 @@ // Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Collections.Generic; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Utilities @@ -48,13 +44,21 @@ public int Compare(SyntaxNode directive1, SyntaxNode directive2) var directive1IsExtern = extern1 != null; var directive2IsExtern = extern2 != null; - var directive1IsNamespace = using1 != null && using1.Alias == null; - var directive2IsNamespace = using2 != null && using2.Alias == null; + var directive1IsNamespace = using1 != null && using1.Alias == null && !using1.StaticKeyword.IsKind(SyntaxKind.StaticKeyword); + var directive2IsNamespace = using2 != null && using2.Alias == null && !using2.StaticKeyword.IsKind(SyntaxKind.StaticKeyword); + + var directive1IsUsingStatic = using1 != null && using1.StaticKeyword.IsKind(SyntaxKind.StaticKeyword); + var directive2IsUsingStatic = using2 != null && using2.StaticKeyword.IsKind(SyntaxKind.StaticKeyword); var directive1IsAlias = using1 != null && using1.Alias != null; var directive2IsAlias = using2 != null && using2.Alias != null; // different types of usings get broken up into groups. + // * externs + // * usings + // * using statics + // * aliases + if (directive1IsExtern && !directive2IsExtern) { return -1; @@ -71,6 +75,14 @@ public int Compare(SyntaxNode directive1, SyntaxNode directive2) { return 1; } + else if (directive1IsUsingStatic && !directive2IsUsingStatic) + { + return -1; + } + else if (directive2IsUsingStatic && !directive1IsUsingStatic) + { + return 1; + } else if (directive1IsAlias && !directive2IsAlias) { return -1; diff --git a/Src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs b/Src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs index 23825f423b040c06a1b16a0f1673c05eee3ad084..345dfaacd8bcf1ceb23d2f9f944aee3b4913f3c5 100644 --- a/Src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs +++ b/Src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs @@ -162,6 +162,13 @@ public static bool IsModuleType(this ISymbol symbol) ((ITypeSymbol)symbol).TypeKind == TypeKind.Module; } + public static bool IsInterfaceType(this ISymbol symbol) + { + return + symbol is ITypeSymbol && + ((ITypeSymbol)symbol).TypeKind == TypeKind.Interface; + } + public static bool IsArrayType(this ISymbol symbol) { return