File: Completion\KeywordRecommenders\UsingKeywordRecommender.cs
Web Access
Project: ..\..\..\src\Features\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Features.csproj (Microsoft.CodeAnalysis.CSharp.Features)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.CSharp.Syntax;
 
namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders
{
    internal class UsingKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
    {
        public UsingKeywordRecommender()
            : base(SyntaxKind.UsingKeyword)
        {
        }
 
        protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
        {
            // cases:
            //  using (goo) { }
            //  using Goo;
            //  using Goo = Bar;
            //  await using (goo) { }
            return
                context.IsStatementContext ||
                context.IsGlobalStatementContext ||
                IsUsingDirectiveContext(context, forGlobalKeyword: false, cancellationToken) ||
                context.IsAwaitStatementContext(position, cancellationToken);
        }
 
        internal static bool IsUsingDirectiveContext(CSharpSyntaxContext context, bool forGlobalKeyword, CancellationToken cancellationToken)
        {
            // cases:
            // root: |
 
            // root: u|
 
            // extern alias a;
            // |
 
            // extern alias a;
            // u|
 
            // using Goo;
            // |
 
            // using Goo;
            // u|
 
            // using Goo = Bar;
            // |
 
            // using Goo = Bar;
            // u|
 
            // t valid:
            // namespace N {}
            // |
 
            // namespace N {}
            // u|
 
            // class C {}
            // |
 
            // class C {}
            // u|
 
            // |
            // extern alias a;
 
            // u|
            // extern alias a;
 
            var originalToken = context.LeftToken;
            var token = context.TargetToken;
 
            // root: u|
 
            // namespace N { u|
 
            // namespace N; u|
 
            // extern alias a;
            // u|
 
            // using Goo;
            // u|
 
            // using Goo = Bar;
            // u|
 
            // root: |
            if (token.Kind() == SyntaxKind.None)
            {
                // root namespace
 
                return IsValidContextAtTheRoot(context, originalToken, cancellationToken);
            }
 
            if ((token.Kind() == SyntaxKind.OpenBraceToken && token.Parent.IsKind(SyntaxKind.NamespaceDeclaration))
                || (token.Kind() == SyntaxKind.SemicolonToken && token.Parent.IsKind(SyntaxKind.FileScopedNamespaceDeclaration)))
            {
                // a child using can't come before externs
                var nextToken = originalToken.GetNextToken(includeSkipped: true);
                if (nextToken.Kind() == SyntaxKind.ExternKeyword)
                {
                    return false;
                }
 
                return true;
            }
 
            // extern alias a;
            // |
 
            // using Goo;
            // |
            if (token.Kind() == SyntaxKind.SemicolonToken)
            {
                if (token.Parent is (kind: SyntaxKind.ExternAliasDirective or SyntaxKind.UsingDirective))
                {
                    return true;
                }
            }
 
            // extern alias a;
            // global |
 
            // global using Goo;
            // global |
 
            // global |
            if (!forGlobalKeyword)
            {
                var previousToken = token.GetPreviousToken(includeSkipped: true);
                if (previousToken.Kind() == SyntaxKind.None)
                {
                    // root namespace
                    if (token.Kind() == SyntaxKind.GlobalKeyword)
                    {
                        return true;
                    }
                    else if (token.Kind() == SyntaxKind.IdentifierToken && SyntaxFacts.GetContextualKeywordKind((string)token.Value!) == SyntaxKind.GlobalKeyword)
                    {
                        return IsValidContextAtTheRoot(context, originalToken, cancellationToken);
                    }
                }
                else if (previousToken.Kind() == SyntaxKind.SemicolonToken &&
                    previousToken.Parent is (kind: SyntaxKind.ExternAliasDirective or SyntaxKind.UsingDirective))
                {
                    if (token.Kind() == SyntaxKind.GlobalKeyword)
                    {
                        return true;
                    }
                    else if (token.Kind() == SyntaxKind.IdentifierToken && SyntaxFacts.GetContextualKeywordKind((string)token.Value!) == SyntaxKind.GlobalKeyword)
                    {
                        return true;
                    }
                }
            }
 
            return false;
 
            static bool IsValidContextAtTheRoot(CSharpSyntaxContext context, SyntaxToken originalToken, CancellationToken cancellationToken)
            {
                // a using can't come before externs
                var nextToken = originalToken.GetNextToken(includeSkipped: true);
                if (nextToken.Kind() == SyntaxKind.ExternKeyword ||
                    ((CompilationUnitSyntax)context.SyntaxTree.GetRoot(cancellationToken)).Externs.Count > 0)
                {
                    return false;
                }
 
                return true;
            }
        }
    }
}