File: Completion\KeywordRecommenders\UsingKeywordRecommender.cs
Web Access
Project: src\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;
        }
    }
}