AbstractMoveToNamespaceService.cs 6.6 KB
Newer Older
1 2
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

3
using System;
4 5
using System.Collections.Immutable;
using System.Linq;
6
using System.Text.RegularExpressions;
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
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) 已提交
23
        public abstract Task<MoveToNamespaceOptionsResult> GetOptionsAsync(Document document, string defaultNamespace, CancellationToken cancellationToken);
24 25
    }

A
Andrew Hall (METAL) 已提交
26
    internal abstract class AbstractMoveToNamespaceService<TCompilationSyntax, TNamespaceDeclarationSyntax, TNamedTypeDeclarationSyntax>
27 28 29 30
        : AbstractMoveToNamespaceService
    {
        private IMoveToNamespaceOptionsService _moveToNamespaceOptionsService;

31
        protected abstract string GetNamespaceName(TNamespaceDeclarationSyntax syntax);
A
Andrew Hall (METAL) 已提交
32
        protected abstract string GetNamespaceName(TNamedTypeDeclarationSyntax syntax);
33

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
        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)
        {
A
Andrew Hall (METAL) 已提交
56
#if DEBUG // TODO: remove once the feature is done
57 58 59 60 61 62 63 64
            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;
A
Andrew Hall (METAL) 已提交
65
            var @namespace = symbol?.Name;
66 67 68 69

            if (symbol is INamespaceSymbol namespaceSymbol)
            {
                node = node.FirstAncestorOrSelf<SyntaxNode>(a => a is TNamespaceDeclarationSyntax);
A
Andrew Hall (METAL) 已提交
70
                @namespace = GetQualifiedName(namespaceSymbol);
71 72 73 74 75 76
            }

            if (node is TNamespaceDeclarationSyntax declarationSyntax)
            {
                if (ContainsNamespaceDeclaration(node))
                {
A
Andrew Hall (METAL) 已提交
77
                    return new MoveToNamespaceAnalysisResult("Namespace container contains nested namespace declaration");
78 79
                }

A
Andrew Hall (METAL) 已提交
80 81 82 83 84 85 86 87 88 89 90 91 92 93
                @namespace = @namespace ?? GetNamespaceName(declarationSyntax);
                return new MoveToNamespaceAnalysisResult(document, node, @namespace, MoveToNamespaceAnalysisResult.ContainerType.Namespace);
            }

            if (symbol is INamedTypeSymbol namedTypeSymbol)
            {
                node = node.FirstAncestorOrSelf<SyntaxNode>(a => a is TNamedTypeDeclarationSyntax);
                @namespace = GetQualifiedName(namedTypeSymbol.ContainingNamespace);
            }

            if (node is TNamedTypeDeclarationSyntax namedTypeDeclarationSyntax)
            {
                @namespace = @namespace ?? GetNamespaceName(namedTypeDeclarationSyntax);
                return new MoveToNamespaceAnalysisResult(document, node, @namespace, MoveToNamespaceAnalysisResult.ContainerType.NamedType);
94 95 96
            }

            return new MoveToNamespaceAnalysisResult("Not a valid position");
A
Andrew Hall (METAL) 已提交
97 98 99
#else 
            return new MoveToNamespaceAnalysisResult("Feature is not complete yet");
#endif
100 101 102 103 104 105
        }

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

A
Andrew Hall (METAL) 已提交
106
        public override Task<MoveToNamespaceResult> MoveToNamespaceAsync(
107 108 109 110
            MoveToNamespaceAnalysisResult analysisResult,
            string targetNamespace,
            CancellationToken cancellationToken)
        {
A
Andrew Hall (METAL) 已提交
111 112
            // TODO: Implementation will be in a separate PR
            return Task.FromResult(MoveToNamespaceResult.Failed);
113 114
        }

115 116 117 118
        private static SymbolDisplayFormat QualifiedNamespaceFormat = new SymbolDisplayFormat(
            globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
            typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces);

A
Andrew Hall (METAL) 已提交
119 120 121
        protected static string GetQualifiedName(INamespaceSymbol namespaceSymbol)
            => namespaceSymbol.ToDisplayString(QualifiedNamespaceFormat);

A
Andrew Hall (METAL) 已提交
122
        public override async Task<MoveToNamespaceOptionsResult> GetOptionsAsync(
123 124 125 126
            Document document,
            string defaultNamespace,
            CancellationToken cancellationToken)
        {
A
Andrew Hall (METAL) 已提交
127
            var compilation = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
128 129 130

            var namespaces = compilation.GlobalNamespace.GetAllNamespaces(cancellationToken)
                .Where(n => n.NamespaceKind == NamespaceKind.Module && n.ContainingAssembly == compilation.Assembly)
A
Andrew Hall (METAL) 已提交
131
                .Select(GetQualifiedName);
132

A
Andrew Hall (METAL) 已提交
133
            return await _moveToNamespaceOptionsService.GetChangeNamespaceOptionsAsync(
134
                defaultNamespace,
A
Andrew Hall (METAL) 已提交
135 136
                namespaces.ToImmutableArray(),
                cancellationToken).ConfigureAwait(false);
137 138 139
        }
    }
}