AbstractMoveToNamespaceService.cs 6.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ChangeNamespace;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Notification;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.MoveToNamespace
{
    internal abstract class AbstractMoveToNamespaceService : ILanguageService
    {
        internal abstract Task<ImmutableArray<MoveToNamespaceCodeAction>> GetCodeActionsAsync(Document document, TextSpan span, CancellationToken cancellationToken);
        internal abstract Task<MoveToNamespaceAnalysisResult> AnalyzeTypeAtPositionAsync(Document document, int position, CancellationToken cancellationToken);
        public abstract Task<MoveToNamespaceResult> MoveToNamespaceAsync(MoveToNamespaceAnalysisResult analysisResult, string targetNamespace, CancellationToken cancellationToken);
A
Andrew Hall (METAL) 已提交
21
        public abstract Task<MoveToNamespaceOptionsResult> GetOptionsAsync(Document document, string defaultNamespace, CancellationToken cancellationToken);
22 23 24 25 26 27 28
    }

    internal abstract class AbstractMoveToNamespaceService<TCompilationSyntax, TNamespaceDeclarationSyntax>
        : AbstractMoveToNamespaceService
    {
        private IMoveToNamespaceOptionsService _moveToNamespaceOptionsService;

29 30
        protected abstract string GetNamespaceName(TNamespaceDeclarationSyntax syntax);

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
        public AbstractMoveToNamespaceService(IMoveToNamespaceOptionsService moveToNamespaceOptionsService)
        {
            _moveToNamespaceOptionsService = moveToNamespaceOptionsService;
        }

        internal override async Task<ImmutableArray<MoveToNamespaceCodeAction>> GetCodeActionsAsync(
            Document document,
            TextSpan span,
            CancellationToken cancellationToken)
        {
            var typeAnalysisResult = await AnalyzeTypeAtPositionAsync(document, span.Start, cancellationToken).ConfigureAwait(false);

            return typeAnalysisResult.CanPerform
                ? ImmutableArray.Create(new MoveToNamespaceCodeAction(this, typeAnalysisResult))
                : ImmutableArray<MoveToNamespaceCodeAction>.Empty;
        }

        internal override async Task<MoveToNamespaceAnalysisResult> AnalyzeTypeAtPositionAsync(
            Document document,
            int position,
            CancellationToken cancellationToken)
        {
            var root = await document.GetSyntaxRootAsync().ConfigureAwait(false);
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var token = root.FindToken(position);
            var node = token.Parent;

            var symbolInfo = semanticModel.GetSymbolInfo(node, cancellationToken: cancellationToken);
            var symbol = symbolInfo.Symbol;

            if (symbol is INamespaceSymbol namespaceSymbol)
            {
                node = node.FirstAncestorOrSelf<SyntaxNode>(a => a is TNamespaceDeclarationSyntax);
            }

            if (node is TNamespaceDeclarationSyntax declarationSyntax)
            {
                if (ContainsNamespaceDeclaration(node))
                {
                    return new MoveToNamespaceAnalysisResult("Container contains nested namespace declaration");
                }

74 75
                var @namespace = symbol?.Name ?? GetNamespaceName(declarationSyntax);
                return new MoveToNamespaceAnalysisResult(document, node, @namespace);
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
            }

            return new MoveToNamespaceAnalysisResult("Not a valid position");
        }

        private bool ContainsNamespaceDeclaration(SyntaxNode node)
            => node.DescendantNodes(n => n is TCompilationSyntax || n is TNamespaceDeclarationSyntax)
                        .OfType<TNamespaceDeclarationSyntax>().Any();

        public override async Task<MoveToNamespaceResult> MoveToNamespaceAsync(
            MoveToNamespaceAnalysisResult analysisResult,
            string targetNamespace,
            CancellationToken cancellationToken)
        {
            if (!analysisResult.CanPerform)
            {
                return MoveToNamespaceResult.Failed;
            }

            var changeNamespaceService = analysisResult.Document.GetLanguageService<IChangeNamespaceService>();
            if (changeNamespaceService == null)
            {
                return MoveToNamespaceResult.Failed;
            }

            var changedSolution = await changeNamespaceService.ChangeNamespaceAsync(
                analysisResult.Document,
                analysisResult.Container,
                targetNamespace,
                cancellationToken).ConfigureAwait(false);

            return new MoveToNamespaceResult(changedSolution, analysisResult.Document.Id);
        }

110 111 112 113
        private static SymbolDisplayFormat QualifiedNamespaceFormat = new SymbolDisplayFormat(
            globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
            typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces);

A
Andrew Hall (METAL) 已提交
114
        public override async Task<MoveToNamespaceOptionsResult> GetOptionsAsync(
115 116 117 118 119 120
            Document document,
            string defaultNamespace,
            CancellationToken cancellationToken)
        {
            var syntaxFactsService = document.GetLanguageService<ISyntaxFactsService>();
            var notificationService = document.Project.Solution.Workspace.Services.GetService<INotificationService>();
A
Andrew Hall (METAL) 已提交
121
            var compilation = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
122 123 124 125

            var namespaces = compilation.GlobalNamespace.GetAllNamespaces(cancellationToken)
                .Where(n => n.NamespaceKind == NamespaceKind.Module && n.ContainingAssembly == compilation.Assembly)
                .Select(n => n.ToDisplayString(QualifiedNamespaceFormat));
126

A
Andrew Hall (METAL) 已提交
127
            return await _moveToNamespaceOptionsService.GetChangeNamespaceOptionsAsync(
128 129 130
                syntaxFactsService,
                notificationService,
                defaultNamespace,
A
Andrew Hall (METAL) 已提交
131 132
                namespaces.ToImmutableArray(),
                cancellationToken).ConfigureAwait(false);
133 134 135
        }
    }
}