Skip to content

Commit

Permalink
IDE Support for C# using static (changeset 1383492)
Browse files Browse the repository at this point in the history
  • Loading branch information
dustincampbell committed Dec 23, 2014
1 parent 3164ac9 commit b49f841
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 19 deletions.
Expand Up @@ -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
Expand All @@ -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<UsingDirectiveSyntax>();
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<UsingDirectiveSyntax>();
if (usingDirective != null &&
usingDirective.StaticKeyword == token)
{
return true;
}
}

return false;
}

public static bool IsBeginningOfStatementContext(this SyntaxToken token)
{
// cases:
Expand Down
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
{
Expand Down
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<UsingDirectiveSyntax>();
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
{
Expand Down
10 changes: 3 additions & 7 deletions 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
{
Expand All @@ -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);
Expand Down
@@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Expand Up @@ -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
Expand Down

0 comments on commit b49f841

Please sign in to comment.