|
// 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;
using System.Collections.Immutable;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using Microsoft.CodeAnalysis.AddImport;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
namespace Microsoft.CodeAnalysis.CSharp.AddImport;
[ExportLanguageService(typeof(IAddImportsService), LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class CSharpAddImportsService() : AbstractAddImportsService<
CompilationUnitSyntax, BaseNamespaceDeclarationSyntax, UsingDirectiveSyntax, ExternAliasDirectiveSyntax>
{
protected override string Language
=> LanguageNames.CSharp;
public override CodeStyleOption2<AddImportPlacement> GetUsingDirectivePlacementCodeStyleOption(IOptionsReader configOptions)
=> configOptions.GetOption(CSharpCodeStyleOptions.PreferredUsingDirectivePlacement);
protected override ImmutableArray<SyntaxNode> GetGlobalImports(
SemanticModel semanticModel, SyntaxNode? contextLocation, SyntaxGenerator generator, CancellationToken cancellationToken)
{
using var result = TemporaryArray<SyntaxNode>.Empty;
if (semanticModel != null)
{
var importScopes = semanticModel.GetImportScopes(contextLocation?.SpanStart ?? 0, cancellationToken);
foreach (var scope in importScopes)
{
foreach (var usingNode in scope.Imports)
{
if (usingNode.DeclaringSyntaxReference?.GetSyntax(cancellationToken) is UsingDirectiveSyntax usingDirective &&
usingDirective.GlobalKeyword != default)
{
result.Add(usingDirective.WithGlobalKeyword(default));
}
}
}
}
return result.ToImmutableAndClear();
}
protected override SyntaxNode? GetAlias(UsingDirectiveSyntax usingOrAlias)
=> usingOrAlias.Alias;
protected override bool IsStaticUsing(UsingDirectiveSyntax usingOrAlias)
=> usingOrAlias.StaticKeyword != default;
protected override SyntaxNode Rewrite(
ExternAliasDirectiveSyntax[] externAliases,
UsingDirectiveSyntax[] usingDirectives,
UsingDirectiveSyntax[] staticUsingDirectives,
UsingDirectiveSyntax[] aliasDirectives,
SyntaxNode externContainer,
SyntaxNode usingContainer,
SyntaxNode staticUsingContainer,
SyntaxNode aliasContainer,
AddImportPlacementOptions options,
SyntaxNode root,
CancellationToken cancellationToken)
{
var rewriter = new Rewriter(
externAliases, usingDirectives, staticUsingDirectives, aliasDirectives,
externContainer, usingContainer, staticUsingContainer, aliasContainer,
options, cancellationToken);
var newRoot = rewriter.Visit(root);
return newRoot;
}
protected override SyntaxList<UsingDirectiveSyntax> GetUsingsAndAliases(SyntaxNode node)
=> node switch
{
CompilationUnitSyntax c => c.Usings,
BaseNamespaceDeclarationSyntax n => n.Usings,
_ => default,
};
protected override SyntaxList<ExternAliasDirectiveSyntax> GetExterns(SyntaxNode node)
=> node switch
{
CompilationUnitSyntax c => c.Externs,
BaseNamespaceDeclarationSyntax n => n.Externs,
_ => default,
};
protected override bool IsEquivalentImport(SyntaxNode a, SyntaxNode b)
=> SyntaxFactory.AreEquivalent(a, b, kind => kind == SyntaxKind.NullableDirectiveTrivia);
private sealed class Rewriter(
ExternAliasDirectiveSyntax[] externAliases,
UsingDirectiveSyntax[] usingDirectives,
UsingDirectiveSyntax[] staticUsingDirectives,
UsingDirectiveSyntax[] aliasDirectives,
SyntaxNode externContainer,
SyntaxNode usingContainer,
SyntaxNode aliasContainer,
SyntaxNode staticUsingContainer,
AddImportPlacementOptions options,
CancellationToken cancellationToken) : CSharpSyntaxRewriter
{
[return: NotNullIfNotNull(nameof(node))]
public override SyntaxNode? Visit(SyntaxNode? node)
=> base.Visit(node);
public override SyntaxNode VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
=> VisitBaseNamespaceDeclaration(node, (BaseNamespaceDeclarationSyntax?)base.VisitNamespaceDeclaration(node));
public override SyntaxNode? VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node)
=> VisitBaseNamespaceDeclaration(node, (BaseNamespaceDeclarationSyntax?)base.VisitFileScopedNamespaceDeclaration(node));
private BaseNamespaceDeclarationSyntax VisitBaseNamespaceDeclaration(
BaseNamespaceDeclarationSyntax node, BaseNamespaceDeclarationSyntax? rewritten)
{
Contract.ThrowIfNull(rewritten);
// recurse downwards so we visit inner namespaces first.
if (!node.CanAddUsingDirectives(options.AllowInHiddenRegions, cancellationToken))
return rewritten;
rewritten = node == aliasContainer ? rewritten.AddUsingDirectives(aliasDirectives, options.PlaceSystemNamespaceFirst) : rewritten;
rewritten = node == usingContainer ? rewritten.AddUsingDirectives(usingDirectives, options.PlaceSystemNamespaceFirst) : rewritten;
rewritten = node == staticUsingContainer ? rewritten.AddUsingDirectives(staticUsingDirectives, options.PlaceSystemNamespaceFirst) : rewritten;
rewritten = node == externContainer ? rewritten.AddExterns(externAliases) : rewritten;
return rewritten;
}
public override SyntaxNode VisitCompilationUnit(CompilationUnitSyntax node)
{
// recurse downwards so we visit inner namespaces first.
var rewritten = (CompilationUnitSyntax)(base.VisitCompilationUnit(node) ?? throw ExceptionUtilities.Unreachable());
if (!node.CanAddUsingDirectives(options.AllowInHiddenRegions, cancellationToken))
return rewritten;
rewritten = node == aliasContainer ? rewritten.AddUsingDirectives(aliasDirectives, options.PlaceSystemNamespaceFirst) : rewritten;
rewritten = node == usingContainer ? rewritten.AddUsingDirectives(usingDirectives, options.PlaceSystemNamespaceFirst) : rewritten;
rewritten = node == staticUsingContainer ? rewritten.AddUsingDirectives(staticUsingDirectives, options.PlaceSystemNamespaceFirst) : rewritten;
rewritten = node == externContainer ? rewritten.AddExterns(externAliases) : rewritten;
return rewritten;
}
}
}
|